I need to start a new process in Go with the following requirements:
- The starting process should run even after the Go process is terminated
- I need to be able to set the Unix user/group that’s running it
- I need to be able to set the environment variables inherited
- I need control over std in/out/err
Here is an attempt:
var attr = os.ProcAttr { Dir: "/bin", Env: os.Environ(), Files: []*os.File{ os.Stdin, "stdout.log", "stderr.log", }, } process, err := os.StartProcess("sleep", []string{"1"}, &attr)
This works fine but has the following shortcomings from the requirements:
- No way to set Unix user/group
- The started process ends when the Go process (parent) stops
This needs to run on Linux only if that simplifies things.
Advertisement
Answer
- You can use process.Release to detach the child process from the parent one and make it survive after parent death
- Look at the definition of *os.ProcAttr.Sys.Credentials attribute : it looks like using the attribute you can set process user and group ID.
Here is a working version of your example (I did not check if process ID’s where actually the one set )
package main import "fmt" import "os" import "syscall" const ( UID = 501 GUID = 100 ) func main() { // The Credential fields are used to set UID, GID and attitional GIDS of the process // You need to run the program as root to do this var cred = &syscall.Credential{ UID, GUID, []uint32{} } // the Noctty flag is used to detach the process from parent tty var sysproc = &syscall.SysProcAttr{ Credential:cred, Noctty:true } var attr = os.ProcAttr{ Dir: ".", Env: os.Environ(), Files: []*os.File{ os.Stdin, nil, nil, }, Sys:sysproc, } process, err := os.StartProcess("/bin/sleep", []string{"/bin/sleep", "100"}, &attr) if err == nil { // It is not clear from docs, but Realease actually detaches the process err = process.Release(); if err != nil { fmt.Println(err.Error()) } } else { fmt.Println(err.Error()) } }