Last active
October 17, 2024 15:30
-
-
Save corvuscrypto/cec8255687aa962c3562d0e5c548da37 to your computer and use it in GitHub Desktop.
Golang fork + prctl PDEATHSIG control example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"os" | |
"os/exec" | |
"os/signal" | |
"strconv" | |
"syscall" | |
) | |
// Demonstration of "forking" and setting prctl PDEATH control | |
// Note: This is not practical since you don't really want to do | |
// fork/exec in golang. That will really mess things up due to the | |
// way the thread scheduler works. | |
// PR_SET_PDEATHSIG is the signal we will use with the prctl syscall so | |
// the thread (links are made to the parent thread, not necessarily the parent | |
// process) that forked children will exit when given a SIGKILL signal. | |
// You don't have to set it to SIGKILL, but I did. | |
// #################################################### | |
// # HOW TO VERIFY CORRECT BEHAVIOR # | |
// #################################################### | |
// In a separate terminal, run 'ps -a' and verify you have 2 'main' processes. | |
// Then run 'kill <parent pid>' and verify that the child is also killed. | |
// Next, comment out line 66 of this code and rebuild/rerun and perform the | |
// same steps. You'll see that the child should survive and needs to be killed manually | |
// NOTE: Due to runtime behavior, using ^C on the terminal running this process, it will also | |
// cleanup the processes tied to this parent process. Cheers :) | |
const ( | |
PRCTL_SYSCALL = 157 | |
PR_SET_PDEATHSIG = 1 | |
) | |
func fork() { | |
cmd := exec.Command(os.Args[0]) | |
// Set the parent process id as an ENVVAR so we can handle the race condition possibility | |
// (which arises due to the delay between "forking" and setting the PDEATH control) | |
cmd.Env = append(os.Environ(), fmt.Sprintf("PARENT_PID=%d", os.Getpid())) | |
cmd.Stdout = os.Stdout | |
cmd.Start() | |
fmt.Println("Started child process", cmd.Process.Pid) | |
} | |
func setKillSignal() { | |
_, _, errno := syscall.RawSyscall(uintptr(PRCTL_SYSCALL), uintptr(PR_SET_PDEATHSIG), uintptr(syscall.SIGKILL), 0) | |
if errno != 0 { | |
os.Exit(127 + int(errno)) | |
} | |
// here's the check that prevents an orphan due to the possible race | |
// condition | |
if strconv.Itoa(os.Getppid()) != os.Getenv("PARENT_PID") { | |
os.Exit(1) | |
} | |
} | |
func main() { | |
if os.Getenv("PARENT_PID") != "" { | |
// Comment the line below | |
setKillSignal() | |
} else { | |
fmt.Println("Parent ID", os.Getpid()) | |
fork() | |
} | |
killChan := make(chan os.Signal) | |
signal.Notify(killChan, os.Kill) | |
<-killChan | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment