Start a process in Go and detach from it

£可爱£侵袭症+ 提交于 2020-12-29 03:46:42

问题


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.


回答1:


  1. You can use process.Release to detach the child process from the parent one and make it survive after parent death
  2. 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())
    }
}



回答2:


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.



来源:https://stackoverflow.com/questions/23031752/start-a-process-in-go-and-detach-from-it

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!