Reputation: 2570
I need to start a new process in Go with the following requirements:
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:
This needs to run on Linux only if that simplifies things.
Upvotes: 21
Views: 27212
Reputation: 2368
What I have found that seems to work cross-platform is to re-run the program with a special flag. In your main program, check for this flag. If present on startup, you're in the "fork". If not present, re-run the command with the flag.
func rerunDetached() error {
cwd, err := os.Getwd()
if err != nil {
return err
}
args := append(os.Args, "--detached")
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = cwd
err = cmd.Start()
if err != nil {
return err
}
cmd.Process.Release()
return nil
}
This will simply re-run your process with the exact parameters and append --detached
to the arguments. When your program starts, check for the --detached
flag to know if you need to call rerunDetached
or not. This is sort of like a poor mans fork()
which will work across different OS.
Upvotes: 2
Reputation: 474
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())
}
}
Upvotes: 28