mirror of https://github.com/coder/coder.git
87 lines
1.7 KiB
Go
87 lines
1.7 KiB
Go
//go:build linux
|
|
|
|
package reaper
|
|
|
|
import (
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/hashicorp/go-reap"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// IsInitProcess returns true if the current process's PID is 1.
|
|
func IsInitProcess() bool {
|
|
return os.Getpid() == 1
|
|
}
|
|
|
|
func catchSignals(pid int, sigs []os.Signal) {
|
|
if len(sigs) == 0 {
|
|
return
|
|
}
|
|
|
|
sc := make(chan os.Signal, 1)
|
|
signal.Notify(sc, sigs...)
|
|
defer signal.Stop(sc)
|
|
|
|
for {
|
|
s := <-sc
|
|
sig, ok := s.(syscall.Signal)
|
|
if ok {
|
|
_ = syscall.Kill(pid, sig)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ForkReap spawns a goroutine that reaps children. In order to avoid
|
|
// complications with spawning `exec.Commands` in the same process that
|
|
// is reaping, we forkexec a child process. This prevents a race between
|
|
// the reaper and an exec.Command waiting for its process to complete.
|
|
// The provided 'pids' channel may be nil if the caller does not care about the
|
|
// reaped children PIDs.
|
|
func ForkReap(opt ...Option) error {
|
|
opts := &options{
|
|
ExecArgs: os.Args,
|
|
}
|
|
|
|
for _, o := range opt {
|
|
o(opts)
|
|
}
|
|
|
|
go reap.ReapChildren(opts.PIDs, nil, nil, nil)
|
|
|
|
pwd, err := os.Getwd()
|
|
if err != nil {
|
|
return xerrors.Errorf("get wd: %w", err)
|
|
}
|
|
|
|
pattrs := &syscall.ProcAttr{
|
|
Dir: pwd,
|
|
Env: os.Environ(),
|
|
Sys: &syscall.SysProcAttr{
|
|
Setsid: true,
|
|
},
|
|
Files: []uintptr{
|
|
uintptr(syscall.Stdin),
|
|
uintptr(syscall.Stdout),
|
|
uintptr(syscall.Stderr),
|
|
},
|
|
}
|
|
|
|
//#nosec G204
|
|
pid, err := syscall.ForkExec(opts.ExecArgs[0], opts.ExecArgs, pattrs)
|
|
if err != nil {
|
|
return xerrors.Errorf("fork exec: %w", err)
|
|
}
|
|
|
|
go catchSignals(pid, opts.CatchSignals)
|
|
|
|
var wstatus syscall.WaitStatus
|
|
_, err = syscall.Wait4(pid, &wstatus, 0, nil)
|
|
for xerrors.Is(err, syscall.EINTR) {
|
|
_, err = syscall.Wait4(pid, &wstatus, 0, nil)
|
|
}
|
|
return err
|
|
}
|