gopptp

golang exec.Command cause a lot of defunct processes


I'm using golang to call pppd and then kill it after a while. However I got a lot of defunct proccesses in this way.

This is how I run pppd

exec.Command("sh", "-c", "pppd call vpn").CombinedOutput()

This is how I kill it.

exec.Command("sh", "-c", "pkill pppd").CombinedOutput()

Then I got a lot of this

root     31541 23536  0 10:54 ?        00:00:00 [pppd] <defunct>
root     31929 23356  0 10:55 ?        00:00:00 [pptpgw] <defunct>
root     31933 23356  0 10:55 ?        00:00:00 [pptpcm] <defunct>
root     31940 23356  0 10:55 ?        00:00:00 [pppd] <defunct>
root     31993 23536  0 10:55 ?        00:00:00 [pptpgw] <defunct>
root     31997 23536  0 10:55 ?        00:00:00 [pptpcm] <defunct>
root     31998 23536  0 10:55 ?        00:00:00 [pppd] <defunct>
root     32012 23356  0 10:55 ?        00:00:00 [pptpgw] <defunct>
root     32016 23356  0 10:55 ?        00:00:00 [pptpcm] <defunct>
root     32017 23356  0 10:56 ?        00:00:00 [pppd] <defunct>
root     32070 23536  0 10:56 ?        00:00:00 [pptpgw] <defunct>
root     32074 23536  0 10:56 ?        00:00:00 [pptpcm] <defunct>
root     32075 23536  0 10:56 ?        00:00:00 [pppd] <defunct>
root     32083 23356  0 10:56 ?        00:00:00 [pptpgw] <defunct>
root     32087 23356  0 10:56 ?        00:00:00 [pptpcm] <defunct>
root     32089 23356  0 10:56 ?        00:00:00 [pppd] <defunct>
root     32131 23536  0 10:57 ?        00:00:00 [pptpgw] <defunct>
root     32135 23536  0 10:57 ?        00:00:00 [pptpcm] <defunct>
root     32148 23536  0 10:57 ?        00:00:00 [pppd] <defunct>
root     32160 23356  0 10:57 ?        00:00:00 [pptpgw] <defunct>
root     32164 23356  0 10:57 ?        00:00:00 [pptpcm] <defunct>
root     32165 23356  0 10:57 ?        00:00:00 [pppd] <defunct>
root     32177 23536  0 10:57 ?        00:00:00 [pptpgw] <defunct>
root     32181 23536  0 10:57 ?        00:00:00 [pptpcm] <defunct>

How can I avoid defunct processes.


Solution

  • These "zombie" processes are created when a process has finished, but the parent has not read their exit status via the wait system call.

    I would guess that all you need to do is call (*Cmd).Wait() on every command structure you create. Obviously This will be less straight forward than you may like, since you probably don't want to call Wait on the first command until after the second command is finished.


    EDIT: As is pointed out in the comments, (*Cmd).CombinedOutput() calls (*Cmd).Run(), which calls (*Cmd).Wait()... So the above is wrong. The real answer in this case is that for some reason sh isn't cleaning up, and so the solution is to cut out the midle man and do the call like so:

    exec.Command("pppd", "call", "vpn").CombinedOutput()
    

    That'll teach me to read the docs a little closer next time...