116 lines
2.7 KiB
Go
116 lines
2.7 KiB
Go
package command
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
// Configuration for the command to start as a daemon.
|
|
type DaemonConfig struct {
|
|
exec.Cmd
|
|
|
|
// The paths, if not /dev/null, must be either in the tasks root directory
|
|
// or in the shared alloc directory.
|
|
StdoutFile string
|
|
StdinFile string
|
|
StderrFile string
|
|
|
|
Chroot string
|
|
}
|
|
|
|
// Whether to start the user command or abort.
|
|
type TaskStart bool
|
|
|
|
func (c *SpawnDaemonCommand) Run(args []string) int {
|
|
flags := c.Meta.FlagSet("spawn-daemon", FlagSetClient)
|
|
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
|
|
|
if err := flags.Parse(args); err != nil {
|
|
return 1
|
|
}
|
|
|
|
// Check that we got json input.
|
|
args = flags.Args()
|
|
if len(args) != 1 {
|
|
c.Ui.Error(c.Help())
|
|
return 1
|
|
}
|
|
jsonInput, err := strconv.Unquote(args[0])
|
|
if err != nil {
|
|
return c.outputStartStatus(fmt.Errorf("Failed to unquote json input: %v", err), 1)
|
|
}
|
|
|
|
// De-serialize the passed command.
|
|
var cmd DaemonConfig
|
|
dec := json.NewDecoder(strings.NewReader(jsonInput))
|
|
if err := dec.Decode(&cmd); err != nil {
|
|
return c.outputStartStatus(err, 1)
|
|
}
|
|
|
|
// Isolate the user process.
|
|
if _, err := syscall.Setsid(); err != nil {
|
|
return c.outputStartStatus(fmt.Errorf("Failed setting sid: %v", err), 1)
|
|
}
|
|
|
|
syscall.Umask(0)
|
|
|
|
// Redirect logs.
|
|
stdo, err := os.OpenFile(cmd.StdoutFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stdout: %v", err), 1)
|
|
}
|
|
|
|
stde, err := os.OpenFile(cmd.StderrFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stderr: %v", err), 1)
|
|
}
|
|
|
|
stdi, err := os.OpenFile(cmd.StdinFile, os.O_CREATE|os.O_RDONLY, 0666)
|
|
if err != nil {
|
|
return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stdin: %v", err), 1)
|
|
}
|
|
|
|
cmd.Cmd.Stdout = stdo
|
|
cmd.Cmd.Stderr = stde
|
|
cmd.Cmd.Stdin = stdi
|
|
|
|
// Chroot jail the process and set its working directory.
|
|
if cmd.Cmd.SysProcAttr == nil {
|
|
cmd.Cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
}
|
|
|
|
cmd.Cmd.SysProcAttr.Chroot = cmd.Chroot
|
|
cmd.Cmd.Dir = "/"
|
|
|
|
// Wait to get the start command.
|
|
var start TaskStart
|
|
dec = json.NewDecoder(os.Stdin)
|
|
if err := dec.Decode(&start); err != nil {
|
|
return c.outputStartStatus(err, 1)
|
|
}
|
|
|
|
if !start {
|
|
return 0
|
|
}
|
|
|
|
// Spawn the user process.
|
|
if err := cmd.Cmd.Start(); err != nil {
|
|
return c.outputStartStatus(fmt.Errorf("Error starting user command: %v", err), 1)
|
|
}
|
|
|
|
// Indicate that the command was started successfully.
|
|
c.outputStartStatus(nil, 0)
|
|
|
|
// Wait and then output the exit status.
|
|
if err := cmd.Wait(); err != nil {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|