0

Does anyone know how to run detached command in golang (Go)?

Task is: I have app1 and app2, app2 do big, long job, approx. 2 hrs. But app1 is default app which do its job in minutes, called frequently, 1-2 times per day. Practically, I need to run app1, than run app2 in app1 gorutine, app1 will finish its job and close, but app2 must still work until end of its tasks list. So could be normal situation if 2-3 app2 instances runned.

I need smth. like this, what I do in bash:

$ ./my-app &

...or in Qt:

QProcess prc;
prc.setArguments(args);
prc.setProgram(app2);
prc.setWorkingDirectory(dir);
prc.startDetached();

Qt doc:

bool QProcess::startDetached(qint64 *pid = nullptr)

Starts the program with arguments in a new process, and detaches from it ... If the calling process exits, the detached process will continue to run unaffected. The started process will run in its own session and act like a daemon. If the function is successful then *pid is set to the process identifier of the started process. Note that the child process may exit and the PID may become invalid without notice. Furthermore, after the child process exits, the same PID may be recycled and used by a completely different process.

So, I do it like this in Go:

    cmd := exec.Command("./app2", "&")
    _ = cmd.Run()

...but it isn't helps, 'app2' still is not detached. And if I kill main app1 from where run 'app2', than 'app2' also will be closed.

Also I tried this way: (in app1)

    var attr = os.ProcAttr{
        Dir: ".",
        Env: os.Environ(),
        Files: []*os.File{
            os.Stdin,
            os.Stdout,
            os.Stderr,
        },
    }
    process, err := os.StartProcess(server, []string{"/usr/local/app2"}, &attr)

    if err == nil {
        err = process.Release()
    }
    if err != nil {
        log.Println(err)
    }

and can tell, it also doesn't work.

For now I have only one solution: make app2 as server, with ListenAndServe and than in app1 run app2 no metter how, but in gorutine:

go func() {
    cmd := exec.Command("/usr/local/app2")
    _ = cmd.Run()
}()

and run app1 in bash as detached process:

./app1 &

in this case app1 will run app2, will do all its work and closes, but app2 will do all its long work.

Actually, I want to do in golang (Go language) the same what I do with QProcess::startDetached in Qt (cross-platform application development framework used to extend the C++ language)

PS. Golang (or Go, for grammar-nazis who have a heart attack in case of using Golang word): BTW, "many use the golang name, though, and it is handy as a label" https://golang.org/doc/faq#go_or_golang

xiaose
  • 616
  • 1
  • 7
  • 19
  • 1
    Basically you read up on how to start a classic daemon on a Unix-like OS and then study the OS-specific bits its imlementation of [`syscall.SysProcAttr`](https://golang.org/pkg/syscall/#SysProcAttr) allows you to set; then you set the `os/exec.Cmd.SysProcAttr` field before running your process. Please note that it's futile to ask about starting a process "in an interesting way" w/o also speficying which OS this should be done on. – kostix Dec 10 '20 at 12:10
  • 1
    Please note that [the language is called Go](https://golang.org/doc/faq#go_or_golang); the language called "Golang" does not exist. – kostix Dec 10 '20 at 12:10
  • 1
    Like in every other language: you fork. – Volker Dec 10 '20 at 12:13
  • 1
    @Volker, except in Go, you cannot — mostly due to the fact only the thread which called `fork` gets forked ;-) Discussed [here](https://stackoverflow.com/q/28370646/720999), for instance. – kostix Dec 10 '20 at 13:17
  • @kostix you writing about nothing. What does Go or Golang have to do with it? esp. when the question is completely different... – xiaose Dec 11 '20 at 16:13
  • @kostix "w/o also speficying which OS this should be done on" - any OS (operating system, software that manages computer hardware, software resources, and provides common services for computer programs) – xiaose Dec 11 '20 at 16:38

2 Answers2

3

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

I had a scenario when the spawned process got killed even using the .Process.Release(), after the parent process exited.

I found a solution based on this article: https://www.tecmint.com/run-linux-command-process-in-background-detach-process/

By using the nohup Linux command, I was able to create a process fully independent of any other processes. (It will run directly under systemd in the process tree)

Here's a sample code:

package main

import (
  "fmt"
  "os/exec"
)

func main(){
  exePath="_path_to_the_background_process"
  cmd := exec.Command("/usr/bin/nohup", exePath, "parameter1", "parameter2")
  if err := cmd.Start(); err != nil {
    fmt.Println("There was a problem running ", exePath, ":", err)
  } else {
    cmd.Process.Release()
    fmt.Println(exePath, " has been started.")
  }
}

Note: The /usr/bin/nohup is the path to nohup in my system. It could be found by running whereis nohup

BenVida
  • 1,796
  • 1
  • 16
  • 25