open-nomad/command/spawn_daemon_linux.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
}