I have two go programs. ProgA starts ProgB using cmd.Start(). From ProgA I try to kill ProgB, but ProgB shouldn't get killed immediately, it has to do some cleanup before dying. So I'm using signal.Notify in ProgB to handle sigcall.SIGKILL but whenever ProgA calls progb.Process.Kill() it doesn't seem to notify ProgB(write contents to sigc channel)
in ProgB I have the notify like this:
signal.Notify(sigc, syscall.SIGKILL)
go func() {
fmt.Println("started listening")
<-sigc
fmt.Println("sig term")
cleanUp()
os.Exit(1)
}()
someLongRunningCode()
is there something I'm missing out? I'm sure that ProgA sends a SIGKILL because cmd.Process.Kill() internally does a process.Signal(SIGKILL)
SIGKILL cannot be trapped by recieving process - kernel will force process termination. You may send SIGTERM to process and handle it on other side - it is a conventional method to stop an application.
Related
For example, a container runs the main thread and a goroutine. The main thread encounters an issue and terminates. Note that for Golang, termination of the main thread does not result in auto-termination of the goroutine.
As the main thread has been terminated, will the container be killed and re-created? Or will the container continue running due to the goroutine is still running?
If the container will be killed and re-created after the main thread has been terminated, will this result in the goroutine getting terminated as well? Or will the goroutine continue running indefinitely and there is no easy way to terminate it now?
If the main functions exists, the program is stopped. Nothing will run any more. It will release any used resource, like file descriptors and database connections.
In the below program, we will never see done being printed.
func main() {
go func() {
time.Sleep(time.Minute)
fmt.Println("done")
}()
time.Sleep(time.Second * 3)
}
https://play.golang.com/p/kPKZDdMcduS
If the program with that main function was the foreground process of the container, then the container shuts down as its standard behaviour with containers.
If you run the below example, you can observe how the container shuts down as soon as the sleep finishes.
$ docker run --name sample busybox sleep 3 && docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd4319261a0d busybox "sleep 3" 4 seconds ago Exited (0) Less than a second ago sample
If the container, the program is running in, is shut down, it's more or less as if you would pull the plug of your computer. Nothing will run on your computer any more. It's impossible.
I would encourage you to create some test scenarios yourself and validate this.
I have a Go program that consumes "live" input from a shell pipe, eg:
tail -f some/file | my-program
my-program is an interactive program built with rivo/tview. I want to be able to close my program with Ctrl-C and have it also terminate the tail -f that supplies input to it.
Currently I have to hit Ctrl-C twice to get back to my shell prompt. Any way I can get back to my prompt by hitting Ctrl-C once?
Adjusted my program per #torek's explanation of progress groups and observation that I can get the progress group ID using unix.Getpgid(pid):
import (
"os"
"golang.org/x/sys/unix"
)
func main() {
// do stuff with piped input
pid := os.Getpid()
pgid, err := unix.Getpgid(pid)
if err != nil {
log.Fatalf("could not get process group id for pid: %v\n", pid)
}
processGroup, err := os.FindProcess(pgid)
if err != nil {
log.Fatalf("could not find process for pid: %v\n", pgid)
}
processGroup.Signal(os.Interrupt)
}
This delivers my desired behavior from my original question.
I opted to not use syscall because of the warning I found:
Deprecated: this package is locked down. Callers should use the corresponding package in the golang.org/x/sys repository instead. That is also where updates required by new systems or versions should be applied. See https://golang.org/s/go1.4-syscall for more information.
I plan to update my program to detect whether or not it was given a pipe using the strategy outlined in this article, so when a pipe is detected, I'll do the above process group signaling on interrupt.
Any issues with that?
We'll assume a Unix-like system, using a shell that understands and engages in job control (and they all do now). When you run a command, the shell creates something called a process group or "pgroup" to hold each of the processes that make up the command. If the command is a pipeline (as this one is), each process in the pipeline gets the same pgroup-ID (see setpgid).
If the command is run in the forgeground (without &), the controlling terminal has this particular pgid assigned to it. Pressing one of the signal-generating keys, such as CTRL-C or CTRL-\, sends the corresponding signal (SIGINT and SIGQUIT in these cases) to the pgroup, using an internal killpg or equivalent. This sends the signal to every member of the pgroup.
(Backgrounding a process is simply *cough* a matter of taking back the pgid on the controlling tty, then restarting the processes in the pipeline. To make that happen is not so simple, though, as indicated by the "restarting" here.)
The likely source of the problem here is that an interactive program will place the controlling terminal into cbreak or raw mode and disable some or all signalling from keyboard keys, so that, for instance, CTRL-C no longer causes the kernel's tty module to send a signal at all. Instead, if you see a key that should cause suspension (CTRL-Z) or termination, the program has to do its own suspending or terminating. Programmers sometimes assume that this consists of simply suspending or terminating—but since the entire pipeline never got the signal in question, that's not the case, unless the entire shell pipeline consisted solely of the interactive program.
The fix is to have the program send the signal to its own pgroup, after doing any necessary cleanup (temporarily or permanently) of the controlling terminal.
My golang program starts a service program which is supposed to run forever, like this:
cmd := exec.Command("/path/to/service")
cmd.Start()
I do NOT want to wait for the termination of "service" because it is supposed to be running forever. However, if service starts with some error (e.g. it will terminate if another instance is already running), the child process will exit and become zombie.
My question is, after cmd.Start(), can I somehow detect if the child process is still running, rather than becomes a zombie? The preferred way might be:
if cmd.Process.IsZombie() {
... ...
}
or,
procStat := cmd.GetProcessStatus()
if procStat.Zombie == true {
... ...
}
i.e. I hope there are some way to get the status of a (child) process without waiting for its exit code, or, to "peek" its status code without blocking.
Thanks!
Judging from the docs the only way to get the process state is to call os.Process.Wait. So it seems you will have to call wait in a goroutine, and then you can easily check if that goroutine has exited yet:
var cmd exec.Cmd
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case err := <-done:
// inspect err to check if service exited normally
default:
// not done yet
}
The best solution (for me) is:
add a signal handler listen for SIGCHLD
on receiving SIGCHLD, call cmd.Wait()
This way, the zombie process will disappear.
I'm trying to replicate a shell environment. The following code runs within os.StartProcess and p.Wait(). It is able to receive C-z (SIGTSTP) and C-c (SIGINT) but not when I send SIGCONT from another shell with kill -CONT [PID].
sigChild := make(chan os.Signal)
defer close(sigChild)
signal.Notify(sigChild, syscall.SIGTSTP, syscall.SIGINT, syscall.SIGCONT)
defer signal.Stop(sigChild)
sigRcvd := <- sigChild
fmt.Println(sigRcvd)
I'm not sure if I missing something in my code.
It's an known issue in Go. There is an issue for it on GitHub.
I'm trying to send a SIGTSTP signal to a child process. The problem I'm facing is that sending SIGTSTP to the child process halts my entire program and the caller is unable to proceed with execution of the rest of the program. Here's my code
cmd := exec.Command("ping", "google.com")
stdout, _ := cmd.StdoutPipe()
cmd.Start()
io.Copy(os.Stdout, stdout)
cmd.Wait()
Running this code, I get output from ping google.com printed on the terminal. When I hit ctrl-z, the output is stopped, but the program is not longer able to accept signals or do anything else unless SIGCONT is sent to the child process. Am I missing something? How do I suspend the child process but resume execution of the caller? Thanks.
Wait waits for the command to exit. Your child process isn't exiting, it's just paused, so Wait doesn't return.