4xy
4xy

Reputation: 3662

How to restart itself in Go daemon process?

I use go-daemon library to fork process and run it in background. And I need to restart the daemon process after update performed from within http handler.

The handler code is

func httpUpdate(w http.ResponseWriter, req *http.Request) {
    if !isPost(req.Method) {
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
        return
    }

    if checkAuth(req) != 200 {
        http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
        return
    }

    log.Println("INFO: Update request, checking for update...")
    var err = doUpdate(UPDATE_URL, nil, false)
    if !isError(err) {
        log.Println("INFO: Update successful, exit")
        var system = RealSystem{}
        system.Run(fmt.Sprintf("(sleep 0.3s && %s start &> /test/extra.log)&disown", appFilename()))
        system.Exit(0)

        return
    }

    w.WriteHeader(http.StatusNoContent)
}

doUpdate() returns nil if successfully replaced the executable file. RealSystem is just wrapper for exec.Command and os.Exit(). appFilename() is the executable file name. The command to start app is /path/to/app start.

I see that new process starts, but executing Context::Reborn() fails with EOF error. Looks like some intrinsic pipes used as implementation details fail with EOF (may be...).

What would be the reason? Or may be there is a better way of doing that?

For now everything happens inside docker container in the "context" of e2e test if it matters. I spent hours trying to make it work but with no success.

Upvotes: 1

Views: 2035

Answers (2)

4xy
4xy

Reputation: 3662

The issue is specific to the library. Spawn new self instance from within child process is not a problem for the system, but for that library.

To achieve this it's necessary to execute something like that. Note the _GO_DAEMON=0 variable set to zero. This makes library follow parent control flow.

var cmd = exec.Command("bash", "-c", fmt.Sprintf("sleep 0.5s; _GO_DAEMON=0 %s start", appFilename()))
var err = cmd.Start()

Also it was necessary to make small changes to the original library. Here is the fork.

Upvotes: 0

Clark McCauley
Clark McCauley

Reputation: 1457

I assume you mean restarting the currently running Go binary. You can use a syscall for unix-based systems, and use an exec.Command for Windows.

func RestartSelf() error {
    self, err := osext.Executable()
    if err != nil {
        return err
    }
    args := os.Args
    env := os.Environ()
    // Windows does not support exec syscall.
    if runtime.GOOS == "windows" {
        cmd := exec.Command(self, args[1:]...)
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        cmd.Stdin = os.Stdin
        cmd.Env = env
        err := cmd.Run()
        if err == nil {
            os.Exit(0)
        }
        return err
    }
    return syscall.Exec(self, args, env)
}

Upvotes: 2

Related Questions