tunasync/worker/runner.go
2016-04-30 16:39:08 +08:00

104 lines
2.0 KiB
Go

package worker
import (
"errors"
"os"
"os/exec"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
)
// runner is to run os commands giving command line, env and log file
// it's an alternative to python-sh or go-sh
// TODO: cgroup excution
type cmdJob struct {
cmd *exec.Cmd
workingDir string
env map[string]string
logFile *os.File
finished chan empty
}
func newCmdJob(cmdAndArgs []string, workingDir string, env map[string]string) *cmdJob {
var cmd *exec.Cmd
if len(cmdAndArgs) == 1 {
cmd = exec.Command(cmdAndArgs[0])
} else if len(cmdAndArgs) > 1 {
c := cmdAndArgs[0]
args := cmdAndArgs[1:]
cmd = exec.Command(c, args...)
} else if len(cmdAndArgs) == 0 {
panic("Command length should be at least 1!")
}
cmd.Dir = workingDir
cmd.Env = newEnviron(env, true)
return &cmdJob{
cmd: cmd,
workingDir: workingDir,
env: env,
}
}
func (c *cmdJob) Start() error {
c.finished = make(chan empty, 1)
return c.cmd.Start()
}
func (c *cmdJob) Wait() error {
err := c.cmd.Wait()
c.finished <- empty{}
return err
}
func (c *cmdJob) SetLogFile(logFile *os.File) {
c.cmd.Stdout = logFile
c.cmd.Stderr = logFile
}
func (c *cmdJob) Terminate() error {
if c.cmd == nil {
return nil
}
if c.cmd.Process == nil {
return nil
}
err := unix.Kill(c.cmd.Process.Pid, syscall.SIGTERM)
if err != nil {
return err
}
select {
case <-time.After(2 * time.Second):
unix.Kill(c.cmd.Process.Pid, syscall.SIGKILL)
return errors.New("SIGTERM failed to kill the job")
case <-c.finished:
return nil
}
}
// Copied from go-sh
func newEnviron(env map[string]string, inherit bool) []string { //map[string]string {
environ := make([]string, 0, len(env))
if inherit {
for _, line := range os.Environ() {
// if os environment and env collapses,
// omit the os one
k := strings.Split(line, "=")[0]
if _, ok := env[k]; ok {
continue
}
environ = append(environ, line)
}
}
for k, v := range env {
environ = append(environ, k+"="+v)
}
return environ
}