Question

NOTE: I thought I was using bash, but /bin/sh is linked to /bin/dash, which has this weird exec problem.

I have a simple bash shell script on Linux which is used to launch a server process (which I did not write or control) and would like to have the bash shell script output the launched process's PID to a pidfile.

The problem is that bash-exec command does not replace its own process with the launched server process!

So:

echo $$ | cat > /var/run/launched-server.pid

This does not work because the pid in the file will be that of bash not the server process. And if the server process exits bash may not exit leaving the stupid launch script hanging out in the process list.

Does anyone know a way to use bash (or dash maybe?) so that:

  1. Is it possible?
  2. The launched-server server process's PID will be in my pidfile?
  3. Ensure that the bash script will die when the launched-server exists and not leave defunct shell processes hanging out in the process list?

Edit: This snippet from the bash reference manual be of use...

exec
           exec [-cl] [-a name] [command [arguments]]

If command is supplied, it replaces the shell without creating a new process. If the -l option is supplied, the shell places a dash at the beginning of the zeroth argument passed to command. This is what the login program does. The -c option causes command to be executed with an empty environment. If -a is supplied, the shell passes name as the zeroth argument to command. If no command is specified, redirections may be used to affect the current shell environment. If there are no redirection errors, the return status is zero; otherwise the return status is non-zero.


Edit 2: This is the script (sanitized):

#!/bin/sh

# this is where the server expects to run its services for Daemontools
SERVICE_DIR='/var/service';

# kill stdout, stderr, stdin
exec </dev/null
exec >/dev/null
exec 2>/dev/null

logger -ip daemon.debug -- stdout, stderr, stdin redirected to /dev/null

if [ -d $SERVICE_DIR ]; then
    # sanitized...
    logger -ip daemon.debug -- services and supervisors exited
else
    logger -ip daemon.err -- $SERVICE_DIR does not exist, exiting
    exit 1;
fi

if [ -d /var/run/pid ]; then
    echo $$ | cat > /var/run/pid/launched-server.pid
    logger -ip daemon.debug -- creating launched-server pidfile
fi

# start the server process
logger -ip daemon.info -- launching server on $SERVICE_DIR
exec /usr/local/bin/launched-server

And some ps output to maybe be more clear?

me@chainsaw: ~/dev $ ps ax | grep launched-server
13896 pts/1    S+     0:00 /bin/sh ./server_boot
13905 pts/1    S+     0:00 launched-server /var/service
13938 pts/2    R+     0:00 grep --color=auto launched-server
Was it helpful?

Solution

Does your distribution include start-stop-daemon(8)? Wonderfully useful little tool, it was built specifically for starting daemons from shell scripts. (Of course, the daemons outlive the shell scripts, so it may not be a perfect match -- it depends on why you want the shell to outlive your daemon.)

Or, for something simpler:

Could your problem be solved with bash's exec command? It replaces the shell process with whatever program you ask to execute:

#!/bin/bash

echo $$ > /tmp/pidfile
exec /bin/sleep 60

$ ./show_exec.sh 
[nothing happens]

And, in another shell:

$ cat pidfile
24686
$ ps auxw | grep 24686
sarnold  24686  0.0  0.0   9728   816 pts/1    S+   04:53   0:00 /bin/sleep 60

OTHER TIPS

I now realize what the real problem was:

By using #!/bin/sh I was not invoking bash, but dash.

Dash's exec is the shell with the exec problem. If I had used #!/bin/bash from the start, it would have worked as expected.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top