6

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.

xren
  • 1,381
  • 5
  • 14
  • 29
  • 1
    Perhaps the shell is not reaping the child process, although I don't knw why it wouldn't. Try invoking the commands directly: `exec.Command("pppd", "call", "vpn").CombinedOutput()`. – Charlie Tumahai Sep 19 '17 at 16:15
  • This is [issue 18874](https://golang.org/issue/18874). The latest docs make note of this: `Wait waits for the command to exit and waits for any copying to stdin or copying from stdout or stderr to complete` – JimB Sep 20 '17 at 03:41

3 Answers3

10

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...

Milo Christiansen
  • 3,204
  • 2
  • 23
  • 36
  • 2
    Actually .CombinedOutput() already has called wait(). I guess the wait() is onlying waiting for `sh` instead of the child processes sh created. Do you know how to resolve this ? – xren Sep 19 '17 at 13:48
  • If that is the case, no. Well, yes. Start the command directly, without `sh`, eg `exec.Command("pppd call vpn").CombinedOutput()`. – Milo Christiansen Sep 19 '17 at 19:45
0

A simpler way to cancel your command would be to use exec.CommandContext. e.g.

ctx, cancel := context.WithCancel(context.Background())
exec.CommandContext(ctx, "pppd", "call", "vpn").CombinedOutput()

// in some other goroutine...
cancel()

Maybe this would solve your zombie problem?

Jonathan B
  • 109
  • 3
0

run subprocess in a new thread

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

kill subprocess

exec.Command("pkill", "pppd").CombinedOutput().CombinedOutput()
levinit
  • 394
  • 3
  • 7