in cmds/exec_agent/verbs.go [75:160]
func start(bin string, args []string) error {
ctx := xcontext.Background()
// signal channel for process exit. Similar to Wait
reaper := newSafeSignal()
defer reaper.Signal()
if flagTimeQuota != nil && *flagTimeQuota != 0 {
var cancel xcontext.CancelFunc
ctx, cancel = xcontext.WithTimeout(ctx, *flagTimeQuota)
defer cancel()
// skip waiting for remote to poll/wait
go func() {
<-ctx.Done()
reaper.Signal()
}()
}
cmd := exec.CommandContext(ctx, bin, args...)
var stdout, stderr SafeBuffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
log.Printf("starting command: %s", cmd.String())
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start process: %w", err)
}
// NOTE: write this only pid on stdout
fmt.Printf("%d\n", cmd.Process.Pid)
cmdErr := make(chan error, 1)
go func() {
err := cmd.Wait()
if err != nil {
log.Printf("process exited with err: %v", err)
} else {
log.Print("process finished")
}
reaper.Wait()
cmdErr <- err
}()
// start unix socket server
mon := NewMonitorServer(cmd.Process, &stdout, &stderr, reaper)
monErr := make(chan error, 1)
go func() {
monErr <- mon.Serve()
}()
// catch termination signals
sigs := make(chan os.Signal, 1)
defer close(sigs)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
for {
select {
case err := <-cmdErr:
if err := mon.Shutdown(); err != nil {
log.Printf("failed to shutdown monitor: %v", err)
}
return err
case sig := <-sigs:
if err := mon.Shutdown(); err != nil {
log.Printf("failed to shutdown monitor: %v", err)
}
if cmd.ProcessState == nil {
if err := cmd.Process.Kill(); err != nil {
log.Printf("failed to kill process on signal: %v", err)
}
}
return fmt.Errorf("signal caught: %v", sig)
case err := <-monErr:
if cmd.ProcessState == nil {
if err := cmd.Process.Kill(); err != nil {
log.Printf("failed to kill process on monitor error: %v", err)
}
}
return err
}
}
}