fix inconsistency when fail at execve

This commit is contained in:
criyle 2019-09-15 14:43:39 -07:00
parent d0b047bb08
commit dcbc99fa5d
2 changed files with 19 additions and 24 deletions

View File

@ -184,20 +184,18 @@ func handleExecve(s *unixsocket.Socket, cmd *Cmd, msg *unixsocket.Msg) error {
DropCaps: true,
SyncFunc: syncFunc,
}
// starts the runner, error is handled same as wait4 to make api simple
pid, err := r.Start()
if err != nil {
return sendErrorReply(s, "execve: %v", err)
}
// done is to signal kill goroutine exits
done := make(chan struct{})
killDone := make(chan struct{})
// waitDone is to signal kill goroutine to collect zombies
waitDone := make(chan struct{})
// recv kill
go func() {
// signal done
defer close(done)
defer close(killDone)
// msg must be kill
recvCmd(s)
// kill all
@ -206,29 +204,27 @@ func handleExecve(s *unixsocket.Socket, cmd *Cmd, msg *unixsocket.Msg) error {
<-waitDone
// collect zombies
for {
_, err = syscall.Wait4(-1, nil, syscall.WNOHANG, nil)
if err != nil {
pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil)
if err != nil || pid <= 0 {
break
}
}
}()
// wait pid
// wait pid if no error encoutered for execve
var wstatus syscall.WaitStatus
loop:
for {
if err == nil {
_, err = syscall.Wait4(pid, &wstatus, 0, nil)
// sync with kill goroutine
close(waitDone)
if err != nil {
sendErrorReply(s, "execve: wait4 %v", err)
break loop
}
}
// sync with kill goroutine
close(waitDone)
if err != nil {
sendErrorReply(s, "execve: wait4 %v", err)
} else {
switch {
case wstatus.Exited():
reply := Reply{ExitStatus: wstatus.ExitStatus()}
sendReply(s, &reply, nil)
break loop
case wstatus.Signaled():
var status types.Status
@ -245,11 +241,12 @@ loop:
}
reply := Reply{Status: status}
sendReply(s, &reply, nil)
break loop
default:
sendErrorReply(s, "execve: unknown status %v", wstatus)
}
}
// wait for kill msg and reply done for finish
<-done
<-killDone
return sendReply(s, &Reply{}, nil)
}

View File

@ -62,19 +62,18 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
// make sure goroutine not leaked (blocked) even if result is not consumed
wait := make(chan types.Result, 1)
waitDone := make(chan struct{})
killDone := make(chan struct{})
// Wait
go func() {
defer close(wait)
reply2, _, _ := m.recvReply()
close(waitDone)
<-killDone
// done signal (should recv after kill)
m.recvReply()
// emit result after all communication finish
wait <- types.Result{
ExitStatus: reply2.ExitStatus,
Status: reply2.Status,
}
// done signal (should recv after kill)
m.recvReply()
}()
// Kill (if wait is done, a kill message need to be send to collect zombies)
go func() {
@ -83,7 +82,6 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
case <-waitDone:
}
m.sendCmd(&Cmd{Cmd: cmdKill}, nil)
close(killDone)
}()
return wait, nil
}