aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-10-30 19:31:06 +0700
committerShulhan <ms@kilabit.info>2023-10-30 19:31:06 +0700
commit4726a28cee89f542eb3b0569639069a05d1df6b0 (patch)
tree8ba5cd33f4f53387f183464088f98d556a9a7e45
parent5b6f533771e01e992e471d0e777d542c1526cf10 (diff)
downloadawwan-4726a28cee89f542eb3b0569639069a05d1df6b0.tar.xz
all: log all execution into file
For each script execution, a file suffixed with ".log" will be created in the same directory with the same name as script file. For example, if the script is path is "a/b/c.aww" then the log file would named "a/b/c.aww.log". This is to provides history and audit in the future.
-rw-r--r--.gitignore1
-rw-r--r--awwan.go16
-rw-r--r--awwan_local_test.go20
-rw-r--r--awwan_play_test.go32
-rw-r--r--awwan_sudo_test.go12
-rw-r--r--cmd/awwan/main.go2
-rw-r--r--http_server.go7
-rw-r--r--request.go62
-rw-r--r--session.go4
-rw-r--r--testdata/local/local_encrypted.data6
-rw-r--r--testdata/local/local_test.data9
-rw-r--r--testdata/play/awwanssh.test/play_test.data6
12 files changed, 127 insertions, 50 deletions
diff --git a/.gitignore b/.gitignore
index ffd1748..03edd56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*.lck
+*.log
/CHANGELOG.html
/README.html
/_AUR/*.tar.zst
diff --git a/awwan.go b/awwan.go
index 7d59087..8040c73 100644
--- a/awwan.go
+++ b/awwan.go
@@ -177,12 +177,6 @@ func (aww *Awwan) Local(req *Request) (err error) {
sessionDir string
)
- req.scriptPath = filepath.Clean(req.Script)
- req.scriptPath, err = filepath.Abs(req.scriptPath)
- if err != nil {
- return fmt.Errorf("%s: %w", logp, err)
- }
-
sessionDir = filepath.Dir(req.scriptPath)
ses, err = NewSession(aww, sessionDir)
@@ -210,7 +204,7 @@ func (aww *Awwan) Local(req *Request) (err error) {
log.Printf(`%s: %s`, logp, errRemove)
}
- req.mlog.Flush()
+ req.close()
}()
var pos linePosition
@@ -244,12 +238,6 @@ func (aww *Awwan) Play(req *Request) (err error) {
sshSection *config.Section
)
- req.scriptPath = filepath.Clean(req.Script)
- req.scriptPath, err = filepath.Abs(req.scriptPath)
- if err != nil {
- return fmt.Errorf("%s: %w", logp, err)
- }
-
sessionDir = filepath.Dir(req.scriptPath)
ses, err = NewSession(aww, sessionDir)
@@ -285,7 +273,7 @@ func (aww *Awwan) Play(req *Request) (err error) {
defer func() {
ses.sshc.rmdirAll(ses.sshc.dirTmp)
- req.mlog.Flush()
+ req.close()
}()
var pos linePosition
diff --git a/awwan_local_test.go b/awwan_local_test.go
index f2a1de0..c18524c 100644
--- a/awwan_local_test.go
+++ b/awwan_local_test.go
@@ -47,7 +47,10 @@ func TestAwwanLocal(t *testing.T) {
logw bytes.Buffer
)
- req = NewRequest(CommandModeLocal, scriptFile, `1-`)
+ req, err = NewRequest(CommandModeLocal, scriptFile, `1-`)
+ if err != nil {
+ t.Fatal(err)
+ }
req.registerLogWriter(`output`, &logw)
@@ -131,7 +134,10 @@ func TestAwwanLocal_Get(t *testing.T) {
var req *Request
- req = NewRequest(CommandModeLocal, script, c.lineRange)
+ req, err = NewRequest(CommandModeLocal, script, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
err = aww.Local(req)
if err != nil {
@@ -251,7 +257,10 @@ func TestAwwanLocal_Put(t *testing.T) {
var req *Request
- req = NewRequest(CommandModeLocal, script, c.lineRange)
+ req, err = NewRequest(CommandModeLocal, script, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
err = aww.Local(req)
if err != nil {
@@ -350,7 +359,10 @@ func TestAwwanLocal_withEncryption(t *testing.T) {
logw bytes.Buffer
)
- req = NewRequest(CommandModeLocal, c.script, c.lineRange)
+ req, err = NewRequest(CommandModeLocal, c.script, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
req.registerLogWriter(`output`, &logw)
diff --git a/awwan_play_test.go b/awwan_play_test.go
index e73faf4..943ddab 100644
--- a/awwan_play_test.go
+++ b/awwan_play_test.go
@@ -51,11 +51,15 @@ func TestAwwan_Play_withLocal(t *testing.T) {
}
var (
- req = NewRequest(CommandModePlay, scriptFile, `1-`)
-
+ req *Request
logw bytes.Buffer
)
+ req, err = NewRequest(CommandModePlay, scriptFile, `1-`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
req.registerLogWriter(`output`, &logw)
err = aww.Play(req)
@@ -108,6 +112,7 @@ func TestAwwan_Play_Get(t *testing.T) {
}}
var (
+ req *Request
c testCaseGetPut
fi os.FileInfo
gotContent []byte
@@ -120,7 +125,10 @@ func TestAwwan_Play_Get(t *testing.T) {
_ = os.Remove(c.fileDest)
}
- var req = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
err = aww.Play(req)
if err != nil {
@@ -191,6 +199,7 @@ func TestAwwan_Play_Put(t *testing.T) {
}}
var (
+ req *Request
c testCaseGetPut
fi os.FileInfo
gotContent []byte
@@ -203,7 +212,10 @@ func TestAwwan_Play_Put(t *testing.T) {
_ = os.Remove(c.fileDest)
}
- var req = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
err = aww.Play(req)
if err != nil {
@@ -280,6 +292,7 @@ func TestAwwan_Play_SudoGet(t *testing.T) {
var (
mockin = &mockStdin{}
+ req *Request
c testCaseGetPut
fi os.FileInfo
gotContent []byte
@@ -292,7 +305,10 @@ func TestAwwan_Play_SudoGet(t *testing.T) {
_ = os.Remove(c.fileDest)
}
- var req = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
// Mock the request stdin to read password from buffer.
mockin.buf.Reset()
@@ -369,6 +385,7 @@ func TestAwwan_Play_SudoPut(t *testing.T) {
}}
var (
+ req *Request
c testCaseGetPut
fi os.FileInfo
gotContent []byte
@@ -381,7 +398,10 @@ func TestAwwan_Play_SudoPut(t *testing.T) {
_ = os.Remove(c.fileDest)
}
- var req = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
err = aww.Play(req)
if err != nil {
diff --git a/awwan_sudo_test.go b/awwan_sudo_test.go
index b4c9d9d..d73fe09 100644
--- a/awwan_sudo_test.go
+++ b/awwan_sudo_test.go
@@ -87,6 +87,7 @@ func TestAwwan_Local_SudoGet(t *testing.T) {
script = filepath.Join(baseDir, `get.aww`)
mockin = &mockStdin{}
+ req *Request
c testCase
gotContent []byte
)
@@ -94,7 +95,10 @@ func TestAwwan_Local_SudoGet(t *testing.T) {
for _, c = range cases {
t.Log(c.desc)
- var req = NewRequest(CommandModeLocal, script, c.lineRange)
+ req, err = NewRequest(CommandModeLocal, script, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
// Mock the request stdin to read password from buffer.
mockin.buf.Reset()
@@ -180,6 +184,7 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
mockTerm = mock.ReadWriter{}
aww *Awwan
+ req *Request
c testCase
gotContent []byte
)
@@ -200,7 +205,10 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
_ = os.Remove(c.fileDest)
}
- var req = NewRequest(CommandModeLocal, script, c.lineRange)
+ req, err = NewRequest(CommandModeLocal, script, c.lineRange)
+ if err != nil {
+ t.Fatal(err)
+ }
mockin.buf.Reset()
mockin.buf.WriteString(c.sudoPass)
diff --git a/cmd/awwan/main.go b/cmd/awwan/main.go
index b44b242..35c461a 100644
--- a/cmd/awwan/main.go
+++ b/cmd/awwan/main.go
@@ -150,7 +150,7 @@ func main() {
return
case awwan.CommandModeLocal, awwan.CommandModePlay:
- req = awwan.NewRequest(cmdMode, flag.Arg(1), flag.Arg(2))
+ req, err = awwan.NewRequest(cmdMode, flag.Arg(1), flag.Arg(2))
case awwan.CommandModeServe:
if flag.NArg() <= 1 {
diff --git a/http_server.go b/http_server.go
index 4b667a4..d06647e 100644
--- a/http_server.go
+++ b/http_server.go
@@ -408,7 +408,12 @@ func (httpd *httpServer) awwanApiExecute(epr *libhttp.EndpointRequest) (resb []b
req.Script = filepath.Join(httpd.memfsBase.Opts.Root, req.Script)
req.lineRange = parseLineRange(req.LineRange)
- req.init()
+
+ err = req.init()
+ if err != nil {
+ res.Message = fmt.Sprintf(`%s: %s`, logp, err)
+ return nil, res
+ }
var (
data = &HttpResponse{
diff --git a/request.go b/request.go
index 1790a92..4e252ce 100644
--- a/request.go
+++ b/request.go
@@ -4,12 +4,19 @@
package awwan
import (
+ "fmt"
"io"
"os"
+ "path/filepath"
+ "time"
"github.com/shuLhan/share/lib/mlog"
)
+// defLogTimeFormat define the default log time format.
+// This is set as variable to make it easy overwriting it in testing.
+var defLogTimeFormat = time.RFC3339
+
// Request for executing local or remote script.
// Each request define the Mode of execution, Script file to be executed,
// and the lineRange -- list of line numbers to be executed.
@@ -21,6 +28,10 @@ type Request struct {
mlog *mlog.MultiLogger
+ // flog the log file where all input and output will be
+ // recorded.
+ flog *os.File
+
scriptPath string // The actual or cleaned up path of the Script.
script *Script
@@ -34,7 +45,7 @@ type Request struct {
// NewRequest create new Request and initialize stdout and stderr to os.Stdout
// and os.Stderr.
-func NewRequest(mode, script, lineRange string) (req *Request) {
+func NewRequest(mode, script, lineRange string) (req *Request, err error) {
req = &Request{
Mode: mode,
Script: script,
@@ -42,23 +53,62 @@ func NewRequest(mode, script, lineRange string) (req *Request) {
}
req.lineRange = parseLineRange(lineRange)
- req.init()
- return req
+ err = req.init()
+ if err != nil {
+ return nil, fmt.Errorf(`NewRequest: %w`, err)
+ }
+
+ return req, nil
+}
+
+// close flush and release all resources.
+func (req *Request) close() {
+ req.mlog.Flush()
+
+ var err = req.flog.Sync()
+ if err != nil {
+ mlog.Errf(`%s`, err)
+ }
+
+ err = req.flog.Close()
+ if err != nil {
+ mlog.Errf(`%s`, err)
+ }
}
-// init initialize all fields in Request.
-func (req *Request) init() {
+// init initialize multi loggers to write all output.
+func (req *Request) init() (err error) {
if req.mlog == nil {
var (
namedStdout = mlog.NewNamedWriter(`stdout`, os.Stdout)
namedStderr = mlog.NewNamedWriter(`stderr`, os.Stderr)
)
- req.mlog = mlog.NewMultiLogger(``, ``,
+
+ req.mlog = mlog.NewMultiLogger(defLogTimeFormat, filepath.Base(req.Script),
[]mlog.NamedWriter{namedStdout},
[]mlog.NamedWriter{namedStderr},
)
}
+
+ req.scriptPath = filepath.Clean(req.Script)
+ req.scriptPath, err = filepath.Abs(req.scriptPath)
+ if err != nil {
+ return err
+ }
+
+ // Create log file to record all input and output for audit in the
+ // future.
+ var fileLog = req.scriptPath + `.log`
+
+ req.flog, err = os.OpenFile(fileLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
+ if err != nil {
+ return err
+ }
+
+ req.registerLogWriter(`file`, req.flog)
+
+ return nil
}
// registerLogWriter register a writer w to mlog output and error.
diff --git a/session.go b/session.go
index e14cb40..7c00996 100644
--- a/session.go
+++ b/session.go
@@ -453,7 +453,7 @@ func (ses *Session) executeScriptOnLocal(req *Request, pos linePosition) (err er
continue
}
- req.mlog.Outf("\n--> local: %3d: %s", x, stmt.String())
+ req.mlog.Outf(`--> local: %3d: %s`, x, stmt.String())
switch stmt.kind {
case statementKindDefault:
@@ -496,7 +496,7 @@ func (ses *Session) executeScriptOnRemote(req *Request, pos linePosition) (err e
continue
}
- req.mlog.Outf("\n--> %s: %3d: %s", ses.sshc.conn, x, stmt.String())
+ req.mlog.Outf(`--> %s: %3d: %s`, ses.sshc.conn, x, stmt.String())
switch stmt.kind {
case statementKindDefault:
diff --git a/testdata/local/local_encrypted.data b/testdata/local/local_encrypted.data
index 5355762..2682124 100644
--- a/testdata/local/local_encrypted.data
+++ b/testdata/local/local_encrypted.data
@@ -1,6 +1,5 @@
<<< echo_encrypted
-
---> local: 3: echo this_is_a_secret
+- local_encrypted.aww --> local: 3: echo this_is_a_secret
this_is_a_secret
<<< echo_encrypted_no_pass
@@ -10,6 +9,5 @@ Local: NewScript: ParseScript: template: local_encrypted.aww:3:7: executing "loc
Local: NewSession: .awwan.env.vault: LoadPrivateKeyInteractive: x509: decryption password incorrect
<<< sub_echo_encrypted
-
---> local: 1: echo this_is_a_secret_in_sub
+- local_encrypted.aww --> local: 1: echo this_is_a_secret_in_sub
this_is_a_secret_in_sub
diff --git a/testdata/local/local_test.data b/testdata/local/local_test.data
index 8d2b0b9..d5bc811 100644
--- a/testdata/local/local_test.data
+++ b/testdata/local/local_test.data
@@ -1,11 +1,8 @@
Test on "local" command.
<<< local:output
-
---> local: 1: echo "hello"
+- local.aww --> local: 1: echo "hello"
hello
-
---> local: 4: #local: echo "nothing"
-
---> local: 6: echo "local"
+- local.aww --> local: 4: #local: echo "nothing"
+- local.aww --> local: 6: echo "local"
local
diff --git a/testdata/play/awwanssh.test/play_test.data b/testdata/play/awwanssh.test/play_test.data
index 69840c6..9f27fae 100644
--- a/testdata/play/awwanssh.test/play_test.data
+++ b/testdata/play/awwanssh.test/play_test.data
@@ -3,9 +3,7 @@ Test on "play" command.
<<< play_with_local:output
--- SSH connection: awwanssh@127.0.0.1:10022
--- SSH identity file: [/home/awwan/.ssh/id_ed25519]
-
---> awwanssh@127.0.0.1:10022: 2: pwd
+- play.aww --> awwanssh@127.0.0.1:10022: 2: pwd
/home/awwanssh
-
---> awwanssh@127.0.0.1:10022: 5: #local: pwd
+- play.aww --> awwanssh@127.0.0.1:10022: 5: #local: pwd
/home/awwan/src