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
- 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())
}
}
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
Determine Tsc Frequency on Linux
Differencebetween Module_Init and Init_Module in a Linux Kernel Module
How to Check If Two Paths Are Equal in Bash
How to Get in Script Whether Valgrind Found Memory Leaks
How to Add JSON Object to JSON File Using Shell Script
Which Stack Is Used by Interrupt Handler - Linux
Rename Multiple Directories Matching Pattern
How to Grep Exact Literal String (No Regex)
Using Find with -Exec {}, How to Count the Total
Ansible Conditional Based on Stdout of Result
Linking Boost Library in Linux
Use Sudo Inside Dockerfile (Alpine)
Iterating Over Lists in Makefiles
Configure and Build Opencv to Custom Ffmpeg Install
Is There Any Significant Difference Between Tcp_Cork and Tcp_Nodelay in This Use-Case
Use Wc on All Subdirectories to Count the Sum of Lines
Sed: How to Delete Lines Matching a Pattern That Contains Forward Slashes
Linux Join Utility Complains About Input File Not Being Sorted