diff options
| author | Paul Bütow <pbuetow@mimecast.com> | 2020-02-16 13:10:02 +0000 |
|---|---|---|
| committer | Paul Bütow <pbuetow@mimecast.com> | 2020-02-16 13:10:02 +0000 |
| commit | 1bdfd96a799726ff74b52955a25b216de62c14d2 (patch) | |
| tree | 212a93dae1ec4818121e346e51ce35741e44e0c9 | |
| parent | 221010919b210034ba75d42951caf3965f66aa9d (diff) | |
drun can execute more complex commands, e.g. small shell scripts
| -rw-r--r-- | internal/config/common.go | 27 | ||||
| -rw-r--r-- | internal/io/run/run.go | 26 | ||||
| -rw-r--r-- | internal/server/handlers/runcommand.go | 95 | ||||
| -rw-r--r-- | samples/dtail.json.sample | 1 |
4 files changed, 88 insertions, 61 deletions
diff --git a/internal/config/common.go b/internal/config/common.go index 8c07710..f0f1a94 100644 --- a/internal/config/common.go +++ b/internal/config/common.go @@ -2,27 +2,19 @@ package config // CommonConfig stores configuration keys shared by DTail server and client. type CommonConfig struct { - // The SSH server port number. - SSHPort int - // Enable experimental features. + SSHPort int ExperimentalFeaturesEnable bool `json:",omitempty"` - // Enable extra debug logging (used for deevlopment or debugging purpes only). - DebugEnable bool `json:",omitempty"` - // Enable extra trace logging (used for deevlopment or debugging purpes only). - TraceEnable bool `json:",omitempty"` + DebugEnable bool `json:",omitempty"` + TraceEnable bool `json:",omitempty"` // The log strategy to use, one of // stdout: only log to stdout (useful when used with systemd) // daily: create a log file for every day - LogStrategy string - // The log directory - LogDir string - // The cache directory - CacheDir string - // Do we want to enable pperf http server? - PProfEnable bool `json:",omitempty"` - // The HTTP port used by PProf - PProfPort int `json:",omitempty"` - // The PProf HTTP server bind address + LogStrategy string + LogDir string + CacheDir string + TmpDir string `json:",omitempty"` + PProfEnable bool `json:",omitempty"` + PProfPort int `json:",omitempty"` PProfBindAddress string `json:",omitempty"` } @@ -35,6 +27,7 @@ func newDefaultCommonConfig() *CommonConfig { ExperimentalFeaturesEnable: false, LogDir: "log", CacheDir: "cache", + TmpDir: "/tmp", PProfEnable: false, PProfPort: 6060, PProfBindAddress: "0.0.0.0", diff --git a/internal/io/run/run.go b/internal/io/run/run.go index 3cf7935..5951cde 100644 --- a/internal/io/run/run.go +++ b/internal/io/run/run.go @@ -14,16 +14,16 @@ import ( // Run is for execute a command. type Run struct { - commandPath string - args []string - cmd *exec.Cmd + command string + args []string + cmd *exec.Cmd } // New returns a new command runner. -func New(commandPath string, args []string) Run { +func New(command string, args []string) Run { return Run{ - commandPath: commandPath, - args: args, + command: command, + args: args, } } @@ -36,11 +36,11 @@ func (r Run) Start(ctx context.Context, lines chan<- line.Line) (pid int, ec int pid = -1 if len(r.args) > 0 { - logger.Debug(r.commandPath, r.args, " ") - r.cmd = exec.CommandContext(ctx, r.commandPath, r.args...) + logger.Debug(r.command, r.args, " ") + r.cmd = exec.CommandContext(ctx, r.command, r.args...) } else { - logger.Debug(r.commandPath) - r.cmd = exec.CommandContext(ctx, r.commandPath) + logger.Debug(r.command) + r.cmd = exec.CommandContext(ctx, r.command) } stdoutPipe, myErr := r.cmd.StdoutPipe() @@ -60,8 +60,10 @@ func (r Run) Start(ctx context.Context, lines chan<- line.Line) (pid int, ec int return } - pid = r.cmd.Process.Pid - ec = 0 + if r.cmd.Process != nil { + pid = r.cmd.Process.Pid + ec = 0 + } var wg sync.WaitGroup wg.Add(2) diff --git a/internal/server/handlers/runcommand.go b/internal/server/handlers/runcommand.go index b7fbb8b..9dc878f 100644 --- a/internal/server/handlers/runcommand.go +++ b/internal/server/handlers/runcommand.go @@ -3,9 +3,13 @@ package handlers import ( "context" "fmt" + "io/ioutil" + "os" "os/exec" "strings" + "time" + "github.com/mimecast/dtail/internal/config" "github.com/mimecast/dtail/internal/io/logger" "github.com/mimecast/dtail/internal/io/run" ) @@ -26,39 +30,66 @@ func (r runCommand) Start(ctx context.Context, argc int, args []string) { r.server.sendServerMessage(logger.Warn(r.server.user, commandParseWarning, args, argc)) return } - commands := strings.Split(strings.Join(args[1:], " "), ";") - r.start(ctx, commands) + + command := strings.Join(args[1:], " ") + if strings.Contains(command, ";") { + r.startScript(ctx, command) + return + } + + r.start(ctx, strings.TrimSpace(command)) } -func (r runCommand) start(ctx context.Context, commands []string) { - for _, command := range commands { - command = strings.TrimSpace(command) - if len(command) == 0 { - continue - } - splitted := strings.Split(command, " ") - path := splitted[0] - args := splitted[1:] - - qualifiedPath, err := exec.LookPath(path) - if err != nil { - logger.Error(r.server.user, err) - r.server.sendServerMessage(logger.Error(r.server.user, "Unable to execute command(s), check server logs")) - r.server.sendServerMessage(".run exitstatus 255") - return - } - - if !r.server.user.HasFilePermission(qualifiedPath, "runcommands") { - logger.Error(r.server.user, "No permission to execute path", qualifiedPath) - r.server.sendServerMessage(logger.Error(r.server.user, "Unable to execute command(s), check server logs")) - r.server.sendServerMessage(".run exitstatus 255") - return - } - - r.run = run.New(qualifiedPath, args) - pid, ec, _ := r.run.Start(ctx, r.server.lines) - - r.server.sendServerMessage(fmt.Sprintf(".run exitstatus %d", ec)) - r.server.sendServerMessage(logger.Info(fmt.Sprintf("Process %d exited with status %d", pid, ec))) +func (r runCommand) startScript(ctx context.Context, script string) { + if _, err := os.Stat(config.Common.TmpDir); os.IsNotExist(err) { + logger.Error(r.server.user, err) + r.server.sendServerMessage(logger.Error(r.server.user, "Unable to execute command(s), check server logs")) + return + } + + timestamp := time.Now().UnixNano() + scriptPath := fmt.Sprintf("%s/%s_%v.sh", config.Common.TmpDir, r.server.user.Name, timestamp) + + // TODO: On dserver startup delete all previously written scripts (there might be left overs due to a crash or so) + logger.Debug(r.server.user, "Writing temp script", scriptPath) + + script = fmt.Sprintf("#!/bin/sh\n%s", script) + if err := ioutil.WriteFile(scriptPath, []byte(script), 0700); err != nil { + logger.Error(r.server.user, err) + r.server.sendServerMessage(logger.Error(r.server.user, "Unable to execute command(s), check server logs")) + return } + + r.start(ctx, scriptPath) + os.Remove(scriptPath) +} + +func (r runCommand) start(ctx context.Context, command string) { + if len(command) == 0 { + return + } + splitted := strings.Split(command, " ") + path := splitted[0] + args := splitted[1:] + + qualifiedPath, err := exec.LookPath(path) + if err != nil { + logger.Error(r.server.user, err) + r.server.sendServerMessage(logger.Error(r.server.user, "Unable to execute command(s), check server logs")) + r.server.sendServerMessage(".run exitstatus 255") + return + } + + if !r.server.user.HasFilePermission(qualifiedPath, "runcommands") { + logger.Error(r.server.user, "No permission to execute path", qualifiedPath) + r.server.sendServerMessage(logger.Error(r.server.user, "Unable to execute command(s), check server logs")) + r.server.sendServerMessage(".run exitstatus 255") + return + } + + r.run = run.New(qualifiedPath, args) + pid, ec, _ := r.run.Start(ctx, r.server.lines) + + r.server.sendServerMessage(fmt.Sprintf(".run exitstatus %d", ec)) + r.server.sendServerMessage(logger.Info(fmt.Sprintf("Process %d exited with status %d", pid, ec))) } diff --git a/samples/dtail.json.sample b/samples/dtail.json.sample index 83925c6..4f672e9 100644 --- a/samples/dtail.json.sample +++ b/samples/dtail.json.sample @@ -29,6 +29,7 @@ "Common": { "LogDir" : "log", "CacheDir" : "cache", + "TmpDir" : "tmp", "LogStrategy": "stdout", "SSHPort": 2222, "DebugEnable": false, |
