Shell script behaves strangely when called through Erlang port

When invoking shell scripts from Erlang, I usually need their exit status (0 or something else), so I run them using this function:

%% in module util os_cmd_exitstatus(Action, Cmd) -> ?debug("~ts starting... Shell command: ~ts", [Action, Cmd]), try erlang:open_port({spawn, Cmd}, [exit_status, stderr_to_stdout]) of Port -> os_cmd_exitstatus_loop(Action, Port) catch _:Reason -> case Reason of badarg -> Message = "Bad input arguments"; system_limit -> Message = "All available ports in the Erlang emulator are in use"; _ -> Message = file:format_error(Reason) end, ?error("~ts: shell command error: ~ts", [Action, Message]), error end. os_cmd_exitstatus_loop(Action, Port) -> receive {Port, {data, Data}} -> ?debug("~ts... Shell output: ~ts", [Action, Data]), os_cmd_exitstatus_loop(Action, Port); {Port, {exit_status, 0}} -> ?info("~ts finished successfully", [Action]), ok; {Port, {exit_status, Status}} -> ?error("~ts failed with exit status ~p", [Action, Status]), error; {'EXIT', Port, Reason} -> ?error("~ts failed with port exit: reason ~ts", [Action, file:format_error(Reason)]), error end. 

This worked fine until I used this to run a script that drops the program and exits:

 #!/bin/sh FILENAME=$1 eog $FILENAME & exit 0 

(There are still a lot of arguments in the actual usecase, and some are massaged before they are passed to the program). When launched from the terminal, it displays an image and immediately exits as expected.

But work from Erlang, it is not. In the log file, I see that it starts normally:

 22/Mar/2011 13:38:30.518 Debug: Starting player starting... Shell command: /home/aromanov/workspace/gmcontroller/scripts.dummy/image/show-image.sh /home/aromanov/workspace/media/images/9e89471e-eb0b-43f8-8c12-97bbe598e7f7.png 

and the eog window will appear. But I do not get

 22/Mar/2011 13:47:14.709 Info: Starting player finished successfully 

until the eog process is eog (with kill or just closing the window), which is not suitable for my requirements. Why is there a difference in behavior? Is there any way to fix this?

+4
source share
2 answers

Usually, if you run the command in the background with & in the shell script, and the shell script ends before the command, then the command becomes an orphan. Perhaps erlang is trying to prevent orphaned processes in open_port and is waiting for eog complete. Usually, if you want to run something in the background during a shell script, you should put wait in the end of the script to wait for the background processes to complete. But this is exactly what you do not want to do.

You can try the following in your shell script:

 #!/bin/sh FILENAME=$1 daemon eog $FILENAME # exit 0 not needed: daemon returns 0 if everything is ok 

If your operating system has a daemon command. I checked in FreeBSD and it has one: daemon (8)

This is not a command available on all Unix-like systems, but there may be another command that does the same on your operating system.

The daemon utility disconnects from the control terminal and executes the program indicated by its arguments.

I'm not sure if this solves your problem, but I suspect that eog somehow remains attached to stdin / stdou as a kind of control terminal. In any case, it’s worth a try.

This should also solve a possible problem in which job management fails, which can also cause a problem. Since daemon really exits normally, your shell cannot try to wait for the background job to exit, because there is no shell in the view.

Having said all this: why not just keep the port open in Erlang while eog is running?

Start with:

 #!/bin/sh FILENAME=$1 exec eog $FILENAME 

Invoking with exec not fork it bu replaces the eog shell eog . The exit status that you will see in Erlang will then be the eog status when it completes. You also have the option to close the port and wrap eog from Erlang if you want to do this.

+2
source

Perhaps your /bin/sh does not support job management when it does not start interactively? At least /bin/sh (actually dash(1) !) On my system, Ubuntu mentions:

  -m monitor Turn on job control (set automatically when interactive). 

When you run the script from the terminal, the shell probably recognizes that it runs interactively and supports job management. When you run the shell script as a port, the shell probably works without job control.

+1
source

Source: https://habr.com/ru/post/1344758/


All Articles