Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to start and monitor and kill process with Go

Tags:

go

I am writing a program in Go that should open a portable browser to a given page and monitor it, so that if the browser is closed, it should be automatically reopened. Also the browser should be closed when the program exits.

So far I have managed to reopen the browser when the user closes it, however I am not being able to close it.

Here is my code:

package browser

import (
    "fmt"
    "os/exec"

    log "github.com/sirupsen/logrus"
)

type iBrowserHandler interface {
    Start(processListener chan bool) error
    Stop() error
    KillProcess() error
}

type browserHandler struct {
    cmd            *exec.Cmd
    pathToChromium string
    pdvEndpoint    string
}

func newBrowserHandler(pathToChromium string, pdvEndpoint string) iBrowserHandler {
    b := &browserHandler{}
    b.pathToChromium = pathToChromium
    b.pdvEndpoint = pdvEndpoint

    return b
}

func (b *browserHandler) Start(processListener chan bool) error {
    endpoint := fmt.Sprintf("--app=%s", b.pdvEndpoint)

    b.cmd = exec.Command(b.pathToChromium, endpoint, "--kiosk")
    err := b.cmd.Run()
    if err != nil {
        log.WithError(err).Error("Error with the browser process")
        processListener <- false
    } else {
        processListener <- true
    }

    return err
}

func (b *browserHandler) Stop() error {
    err := b.cmd.Process.Release()
    if err != nil {
        log.WithError(err).Fatal("Error shutting down chromium")
    }

    return err
}

func (b *browserHandler) KillProcess() error {
    err := b.cmd.Process.Kill()
    if err != nil {
        log.WithError(err).Fatal("Error killing chromium process")
    }

    return err
}

And this is the function that I am using to start the browser:

var handler iBrowserHandler

func init() {
    var pathToChromium string
    var c = config.GetInstance()
    var os = runtime.GOOS

    if os == "windows" {
        pathToChromium = "chromium-browser\\ChromiumPortable.exe"
    } else {
        pathToChromium = "chrome"
    }

    handler = newBrowserHandler(pathToChromium, c.GetConfig().PDVUrl)
}

func StartBrowser(done chan bool) {
    browserProcessListener := make(chan bool)
    defer close(browserProcessListener)
    go handler.Start(browserProcessListener)

    var tryReopenBrowser = true
    for {
        select {
        case <-browserProcessListener:
            if tryReopenBrowser {
                log.Warn("Browser process is stopped. Attempting to restart")
                go handler.Start(browserProcessListener)
            } else {
                log.Warn("Browser process is stopped. Will not attempt to restart")
            }

        case <-done:
            log.Info("Shutting down browser")
            tryReopenBrowser = false
            handler.KillProcess()
            return

        default:
        }
    }
}

When the program starts I call the StartBrowser() function. Once the browser is started, it keeps listening to see if it was closed (the cmd.Run() function blocks the execution of the thread until the process finishes).

If the browser was closed, then my program attempts to reopen it.

If the program receives a stop signal, it tries to close the browser. I tried to use both the cmd.Process.Release() and the cmd.Process.Kill() commands, but both do not close the browser. Is there any other way to do this?

like image 821
Felipe Avatar asked Nov 01 '17 13:11

Felipe


People also ask

How do you kill a go process?

The SIGINT signal is sent to a process by its controlling terminal when a user wishes to interrupt the process. This is typically initiated by pressing Ctrl+C, but on some systems, the "delete" character or "break" key can be used.

How do I terminate a process in Golang?

The provided context is used to kill the process (by calling os. Process. Kill) if the context becomes done before the command completes on its own. After the Run() completes, you can inspect ctx.


2 Answers

I found an answer! The problem is that the Go cmd.Process.Kill() command only stops the parent process, but not the child processes, so I had to kill these processes manually through the OS.

Since I am on Windows my KillProcess() method now looks like this:

func (b *browserHandler) KillProcess() error {
    log.Info("Killing browser process")
    kill := exec.Command("taskkill", "/T", "/F", "/PID", strconv.Itoa(b.cmd.Process.Pid))
    err := kill.Run()
    if err != nil {
        log.WithError(err).Error("Error killing chromium process")
    }
    log.Info("Browser process was killed")

    return err
}

Credits: this thread has an answer for both Windows and Unix systems.

like image 51
Felipe Avatar answered Nov 15 '22 18:11

Felipe


Instead of cmd.Run() use cmd.Start() see this

like image 21
tez Avatar answered Nov 15 '22 18:11

tez