Run Command in Golang and Detach It from Process

How to start an app detached in separate process. The started process must run in its own session and act like a daemon

on linux go version 1.15.2, i run below code and it spawns a new process that does not die with main.

package main

import (
"log"
"os"
"os/exec"
)

func main() {
cmd := exec.Command("go", "run", "./d")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Printf("Running command and waiting for it to finish...")
err := cmd.Start()
if err != nil {
log.Fatal("cmd.Start failed: ", err)
}
err = cmd.Process.Release()
if err != nil {
log.Fatal("cmd.Process.Release failed: ", err)
}
}

where ./d/main.go is

package main

import (
"fmt"
"time"
)

func main() {
go func() {
for {
fmt.Println("hop")
<-time.After(time.Second)
}
}()
<-make(chan bool)
}

Start a process in Go and detach from it


  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())
}
}

Start a detached process on Windows using Golang

start is not a standalone application, it's an (internal) command of the Windows command line interpreter (cmd.exe) (details: Command line reference / Start), so you need a "shell" to run the start command.

Use cmd.exe with the /C parameter, and pass start and your application to run.

Like in this example:

s := []string{"cmd.exe", "/C", "start", `c:\path\to\your\app\myapp.exe`}

cmd := exec.Command(s[0], s[1:]...)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}

Or without the command slice:

cmd := exec.Command("cmd.exe", "/C", "start", `c:\path\to\your\app\myapp.exe`)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}

You may also pass the /b param to start like this if you don't want a terminal window for the launched application:

cmd := exec.Command("cmd.exe", "/C", "start", "/b", `c:\path\to\your\app\myapp.exe`)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}

Start detached command with redirect to file

Maybe you can try to use this: https://stackoverflow.com/a/28918814/2728768

Opening a file (and os.File implements io.Writer), and then passing it as the command.Stdout could do the trick:

func main() {
command := exec.Command("./tmp/test.sh")
f, err := os.OpenFile("/tmp/out", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("error opening file: %v", err)
}
defer f.Close()
// On this line you're going to redirect the output to a file
command.Stdout = f
if err := command.Start(); err != nil {
fmt.Fprintln(os.Stderr, "Command failed.", err)
os.Exit(1)
}

fmt.Println("Process ID:", command.Process.Pid)
}

Not sure this could be a viable solution for your case. I've tried it locally and it seems working... remember that your user should be able to create/update the file.

Golang exec process and to disown it

A process forked with Start() will continue even after its parent dies.

func forker() {
cmd := exec.Command("sleep", "3")
cmd.Start()
time.Sleep(2 * time.Second)
os.Exit(1)
}

Here the sleep process will happily live on for 3 seconds even if the parent process only lives for 2 seconds:

  $ forker &; while true; do ps -f; sleep 1; done

UID PID PPID C STIME TTY TIME CMD
501 71423 69892 0 3:01PM ttys003 0:00.07 forker
501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3

UID PID PPID C STIME TTY TIME CMD
501 71423 69892 0 3:01PM ttys003 0:00.07 forker
501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3

UID PID PPID C STIME TTY TIME CMD
501 71433 1 0 3:01PM ttys003 0:00.00 sleep 3

Notice how the parent process ID (PPID) of the sleep process became 1 when the parent process 71432 exited. This means that the sleep process has been orphaned.

In Golang, how to terminate an os.exec.Cmd process with a SIGTERM instead of a SIGKILL?

You can use Signal() API. The supported Syscalls are here.

So basically you might want to use

cmd.Process.Signal(syscall.SIGTERM)

Also please note as per documentation.

The only signal values guaranteed to be present in the os package on
all systems are os.Interrupt (send the process an interrupt) and
os.Kill (force the process to exit). On Windows, sending os.Interrupt
to a process with os.Process.Signal is not implemented; it will return
an error instead of sending a signal.

how to keep subprocess running after program exit in golang?

The subprocess should continue to run after your process ends, as long as it ends cleanly, which won't happen if you hit ^C.
What you can do is intercept the signals sent to your process so you can end cleanly.

sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan,
syscall.SIGINT,
syscall.SIGKILL,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
s := <-sigchan
// do anything you need to end program cleanly
}()


Related Topics



Leave a reply



Submit