in lambda/supervisor/local_supervisor.go [56:145]
func (s *LocalSupervisor) Exec(ctx context.Context, req *model.ExecRequest) error {
if req.Domain != "runtime" {
log.Debug("Exec is a no op if domain != runtime")
return nil
}
command := exec.Command(req.Path, req.Args...)
if req.Env != nil {
envStrings := make([]string, 0, len(*req.Env))
for key, value := range *req.Env {
envStrings = append(envStrings, key+"="+value)
}
command.Env = envStrings
}
if req.Cwd != nil && *req.Cwd != "" {
command.Dir = *req.Cwd
}
if req.ExtraFiles != nil {
command.ExtraFiles = *req.ExtraFiles
}
command.Stdout = req.StdoutWriter
command.Stderr = req.StderrWriter
command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
err := command.Start()
if err != nil {
return err
// TODO Use supevisor specific error
}
pid := command.Process.Pid
termination := make(chan struct{})
s.processMapLock.Lock()
s.processMap[req.Name] = process{
pid: pid,
termination: termination,
}
s.processMapLock.Unlock()
// The first freeze thaw cycle starts on Exec() at init time
s.freezeThawCycleStart = time.Now()
go func() {
err = command.Wait()
// close the termination channel to unblock whoever's blocked on
// it (used to implement kill's blocking behaviour)
close(termination)
var cell int32
var exitStatus *int32
var signo *int32
var exitErr *exec.ExitError
if err == nil {
exitStatus = &cell
} else if errors.As(err, &exitErr) {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
if code := status.ExitStatus(); code >= 0 {
cell = int32(code)
exitStatus = &cell
} else {
cell = int32(status.Signal())
signo = &cell
}
}
}
if signo == nil && exitStatus == nil {
log.Error("Cannot convert process exit status to unix WaitStatus. This is unexpected. Assuming ExitStatus 1")
cell = 1
exitStatus = &cell
}
s.events <- model.Event{
Time: uint64(time.Now().UnixMilli()),
Event: model.EventData{
Domain: &req.Domain,
Name: &req.Name,
Signo: signo,
ExitStatus: exitStatus,
},
}
}()
return nil
}