@@ -11,60 +11,73 @@ import (
1111// ReRun converts the current process into a bare-bones init, runs the current
1212// commandline as a child process, and waits for it to complete. The new child
1313// process shares stdin/stdout/stderr with the parent. When the child exits,
14- // this will return the same value as exec.Command.Run(). If there is an error
15- // in reaping children that this can not handle, it will panic .
16- func ReRun () error {
14+ // this will return the exit code. If this returns an error, the child process
15+ // may not be terminated .
16+ func ReRun () ( int , error ) {
1717 bin , err := os .Readlink ("/proc/self/exe" )
1818 if err != nil {
19- return err
19+ return 0 , err
2020 }
2121 cmd := exec .Command (bin , os .Args [1 :]... )
2222 cmd .Stdin = os .Stdin
2323 cmd .Stdout = os .Stdout
2424 cmd .Stderr = os .Stderr
2525 if err := cmd .Start (); err != nil {
26- return err
26+ return 0 , err
2727 }
28- runInit (cmd .Process .Pid )
29- return nil
28+ return runInit (cmd .Process .Pid )
3029}
3130
32- // runInit runs a bare-bones init process. This will return when firstborn
33- // exits. In case of truly unknown errors it will panic.
34- func runInit (firstborn int ) {
31+ // runInit runs a bare-bones init process. When firstborn exits, this will
32+ // return the exit code. If this returns an error, the child process may not
33+ // be terminated.
34+ func runInit (firstborn int ) (int , error ) {
3535 sigs := make (chan os.Signal , 8 )
3636 signal .Notify (sigs )
3737 for sig := range sigs {
3838 if sig != syscall .SIGCHLD {
3939 // Pass it on to the real process.
40- syscall .Kill (firstborn , sig .(syscall.Signal ))
40+ if err := syscall .Kill (firstborn , sig .(syscall.Signal )); err != nil {
41+ return 0 , err
42+ }
4143 }
4244 // Always try to reap a child - empirically, sometimes this gets missed.
43- if sigchld (firstborn ) {
44- return
45+ die , status , err := sigchld (firstborn )
46+ if err != nil {
47+ return 0 , err
48+ }
49+ if die {
50+ if status .Signaled () {
51+ return 128 + int (status .Signal ()), nil
52+ }
53+ if status .Exited () {
54+ return status .ExitStatus (), nil
55+ }
56+ return 0 , fmt .Errorf ("unhandled exit status: 0x%x\n " , status )
4557 }
4658 }
59+ return 0 , fmt .Errorf ("signal handler terminated unexpectedly" )
4760}
4861
49- // sigchld handles a SIGCHLD. This will return true when firstborn exits. In
50- // case of truly unknown errors it will panic .
51- func sigchld (firstborn int ) bool {
62+ // sigchld handles a SIGCHLD. This will return true only when firstborn exits.
63+ // For any other process this will return false and a 0 status .
64+ func sigchld (firstborn int ) ( bool , syscall. WaitStatus , error ) {
5265 // Loop to handle multiple child processes.
5366 for {
5467 var status syscall.WaitStatus
5568 pid , err := syscall .Wait4 (- 1 , & status , syscall .WNOHANG , nil )
5669 if err != nil {
57- panic ( fmt .Sprintf ( "failed to wait4(): %v\n " , err ) )
70+ return false , 0 , fmt .Errorf ( " wait4(): %v\n " , err )
5871 }
5972
6073 if pid == firstborn {
61- return true
74+ return true , status , nil
6275 }
6376 if pid <= 0 {
6477 // No more children to reap.
6578 break
6679 }
6780 // Must have found one, see if there are more.
6881 }
69- return false
82+ return false , 0 , nil
7083}
0 commit comments