Elixir: Zombie process wrapper script does not exit on wrapped program exit

Created on 27 Jun 2019  路  16Comments  路  Source: elixir-lang/elixir

Hey, I am running a program through System.cmd that does not mind its stdin being closed. The System.cmd documentation points me to the recommended wrapper script on the Port docs for preventing zombie processes, but this script accomplishes its task at the cost of ignoring when the wrapped program exits

#!/bin/bash
"$@" & # There is nothing making the script exit with this process exits
pid=$!
while read line ; do
  :
done
kill -KILL $pid

Environment

Erlang/OTP 22 [erts-10.4.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

Elixir 1.9.0 (compiled with Erlang/OTP 22)

Mac OS 10.14.5

Current behavior

System.cmd("/path/to/wrapper", ~w(echo test))

Hangs indefinitely even after echo test is run

Expected behavior

The script should prevent zombie processes by exiting on stdin close, but also exit when the wrapped program finishes (preferably with the same exit code)

Most helpful comment

If you don't mind a port program made in C wrapping it the https://github.com/saleyn/erlexec (and erlang) library is what I've replaced my old scripts with. Otherwise I have a posix script on the ElixirForums somewhere in my old posts that I used to use that worked. I really recommend erlexec when you can though.

EDIT1: It's docs are at: http://saleyn.github.io/erlexec/
Similar interface to System.cmd but with more options, and the ability to send signals, kill it, etc... etc... Basically everything you'd ever need.

EDIT2: It's on hex at https://hex.pm/packages/erlexec too for note.

All 16 comments

Thank you. I will add a note that the above should only be used if you expect the child program to run forever, for something that you expect to terminate, then it won't work (which is less of a problem because it will terminate eventtually). If anyone can come up with a different script for those cases or one that works for both, please let us know!

Also If anyone can come up with a script that is POSIX compliant, it will be greatly appreciated as this is the only piece of shell script code that is not

If you don't mind a port program made in C wrapping it the https://github.com/saleyn/erlexec (and erlang) library is what I've replaced my old scripts with. Otherwise I have a posix script on the ElixirForums somewhere in my old posts that I used to use that worked. I really recommend erlexec when you can though.

EDIT1: It's docs are at: http://saleyn.github.io/erlexec/
Similar interface to System.cmd but with more options, and the ability to send signals, kill it, etc... etc... Basically everything you'd ever need.

EDIT2: It's on hex at https://hex.pm/packages/erlexec too for note.

@josevalim The process I am running is ffmpeg... so yes, it will terminate eventually
@eksperimental I don't see anything non-POSIX other than the shebang, in which case I agree
@OvermindDL1 Thanks, this seems to be more than appropriate for my use case

Fantastic, thanks everyone for the input.

Ok, so I was able to write a version of the wrapper that terminates if the program terminates, at the cost of an extra process:

#!/bin/sh

# Start the program in the background
exec "$@" &
pid1=$!

# Silence warnings from here on
exec >/dev/null 2>&1 

# Read from stdin in the background and
# kill running program when stdin closes
exec 0<&0 $(
  while read; do :; done
  kill -KILL $pid1
) &
pid2=$!

# Clean up
wait $pid1
kill -KILL $pid1
kill -KILL $pid2

shellcheck validates it OK too.

@josevalim I found that terminating the script does not kill the exec "$@" &

$ ./wrapper sh -c 'sleep 6; echo test'
^C
$ # but the 'sleep 6; echo test' is still running (ironically zombified now)

Edit: I realize this is also an issue present in the original script

We are not trapping signals. You have to send a Ctrl+D to simulate closing

stdin. Someone could add trapping but it isn鈥檛 required for the port usage.

Jos茅 Valimwww.plataformatec.com.br
http://www.plataformatec.com.br/Founder and Director of R&D

Yeah that sounds fair.
Thank you

@josevalim Is there a reason why you're killing $pid1 after waiting for it?

Ah, good catch. I will remove it.

I'll stop annoying you after this but the return code of the wait is also not forwarded. I'm not sure if this was intended..

@Javyre do you know how to forward it?

@josevalim This works for me:

# Clean up
wait $pid1
ret=$?
kill -KILL $pid2
exit $ret

Do you want to submit a PR with the last two proposed changes? Otherwise I will do it. :)

No problem, I'll do it

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vothane picture vothane  路  3Comments

LucianaMarques picture LucianaMarques  路  3Comments

sashaafm picture sashaafm  路  3Comments

whitepaperclip picture whitepaperclip  路  3Comments

alexrp picture alexrp  路  4Comments