github.com/jmitchell/nomad@v0.1.3-0.20151007230021-7ab84c2862d8/command/spawn_daemon_linux.go (about)

     1  package command
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"strconv"
     9  	"strings"
    10  	"syscall"
    11  )
    12  
    13  // Configuration for the command to start as a daemon.
    14  type DaemonConfig struct {
    15  	exec.Cmd
    16  
    17  	// The paths, if not /dev/null, must be either in the tasks root directory
    18  	// or in the shared alloc directory.
    19  	StdoutFile string
    20  	StdinFile  string
    21  	StderrFile string
    22  
    23  	Chroot string
    24  }
    25  
    26  // Whether to start the user command or abort.
    27  type TaskStart bool
    28  
    29  func (c *SpawnDaemonCommand) Run(args []string) int {
    30  	flags := c.Meta.FlagSet("spawn-daemon", FlagSetClient)
    31  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    32  
    33  	if err := flags.Parse(args); err != nil {
    34  		return 1
    35  	}
    36  
    37  	// Check that we got json input.
    38  	args = flags.Args()
    39  	if len(args) != 1 {
    40  		c.Ui.Error(c.Help())
    41  		return 1
    42  	}
    43  	jsonInput, err := strconv.Unquote(args[0])
    44  	if err != nil {
    45  		return c.outputStartStatus(fmt.Errorf("Failed to unquote json input: %v", err), 1)
    46  	}
    47  
    48  	// De-serialize the passed command.
    49  	var cmd DaemonConfig
    50  	dec := json.NewDecoder(strings.NewReader(jsonInput))
    51  	if err := dec.Decode(&cmd); err != nil {
    52  		return c.outputStartStatus(err, 1)
    53  	}
    54  
    55  	// Isolate the user process.
    56  	if _, err := syscall.Setsid(); err != nil {
    57  		return c.outputStartStatus(fmt.Errorf("Failed setting sid: %v", err), 1)
    58  	}
    59  
    60  	syscall.Umask(0)
    61  
    62  	// Redirect logs.
    63  	stdo, err := os.OpenFile(cmd.StdoutFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
    64  	if err != nil {
    65  		return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stdout: %v", err), 1)
    66  	}
    67  
    68  	stde, err := os.OpenFile(cmd.StderrFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
    69  	if err != nil {
    70  		return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stderr: %v", err), 1)
    71  	}
    72  
    73  	stdi, err := os.OpenFile(cmd.StdinFile, os.O_CREATE|os.O_RDONLY, 0666)
    74  	if err != nil {
    75  		return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stdin: %v", err), 1)
    76  	}
    77  
    78  	cmd.Cmd.Stdout = stdo
    79  	cmd.Cmd.Stderr = stde
    80  	cmd.Cmd.Stdin = stdi
    81  
    82  	// Chroot jail the process and set its working directory.
    83  	if cmd.Cmd.SysProcAttr == nil {
    84  		cmd.Cmd.SysProcAttr = &syscall.SysProcAttr{}
    85  	}
    86  
    87  	cmd.Cmd.SysProcAttr.Chroot = cmd.Chroot
    88  	cmd.Cmd.Dir = "/"
    89  
    90  	// Wait to get the start command.
    91  	var start TaskStart
    92  	dec = json.NewDecoder(os.Stdin)
    93  	if err := dec.Decode(&start); err != nil {
    94  		return c.outputStartStatus(err, 1)
    95  	}
    96  
    97  	if !start {
    98  		return 0
    99  	}
   100  
   101  	// Spawn the user process.
   102  	if err := cmd.Cmd.Start(); err != nil {
   103  		return c.outputStartStatus(fmt.Errorf("Error starting user command: %v", err), 1)
   104  	}
   105  
   106  	// Indicate that the command was started successfully.
   107  	c.outputStartStatus(nil, 0)
   108  
   109  	// Wait and then output the exit status.
   110  	if err := cmd.Wait(); err != nil {
   111  		return 1
   112  	}
   113  
   114  	return 0
   115  }