Best way to make a shell script daemon?
Just backgrounding your script (./myscript &
) will not daemonize it. See http://www.faqs.org/faqs/unix-faq/programmer/faq/, section 1.7, which describes what's necessary to become a daemon. You must disconnect it from the terminal so that SIGHUP
does not kill it. You can take a shortcut to make a script appear to act like a daemon;
nohup ./myscript 0<&- &>/dev/null &
will do the job. Or, to capture both stderr and stdout to a file:
nohup ./myscript 0<&- &> my.admin.log.file &
Redirection explained (see bash redirection)
0<&-
closes stdin&> file
sends stdout and stderr to a file
However, there may be further important aspects that you need to consider. For example:
- You will still have a file descriptor open to the script, which means that the directory it's mounted in would be unmountable. To be a true daemon you should
chdir("/")
(orcd /
inside your script), and fork so that the parent exits, and thus the original descriptor is closed. - Perhaps run
umask 0
. You may not want to depend on the umask of the caller of the daemon.
For an example of a script that takes all of these aspects into account, see Mike S' answer.
Some of the top-upvoted answers here are missing some important parts of what makes a daemon a daemon, as opposed to just a background process, or a background process detached from a shell.
This http://www.faqs.org/faqs/unix-faq/programmer/faq/ describes what is necessary to be a daemon. And this Run bash script as daemon implements the setsid, though it misses the chdir to root.
The original poster's question was actually more specific than "How do I create a daemon process using bash?", but since the subject and answers discuss daemonizing shell scripts generally, I think it's important to point it out (for interlopers like me looking into the fine details of creating a daemon).
Here's my rendition of a shell script that would behave according to the FAQ. Set DEBUG to true
to see pretty output (but it also exits immediately rather than looping endlessly):
#!/bin/bashDEBUG=false# This part is for fun, if you consider shell scripts fun- and I do.trap process_USR1 SIGUSR1process_USR1() { echo 'Got signal USR1' echo 'Did you notice that the signal was acted upon only after the sleep was done' echo 'in the while loop? Interesting, yes? Yes.' exit 0}# End of fun. Now on to the business end of things.print_debug() { whatiam="$1"; tty="$2" [[ "$tty" != "not a tty" ]] && { echo "" >$tty echo "$whatiam, PID $$" >$tty ps -o pid,sess,pgid -p $$ >$tty tty >$tty }}me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"me_FILE=$(basename $0)cd /#### CHILD HERE --------------------------------------------------------------------->if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again. shift; tty="$1"; shift $DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty" umask 0 $me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null & $DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty exit 0fi##### ENTRY POINT HERE -------------------------------------------------------------->if [ "$1" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts. tty=$(tty) $DEBUG && print_debug "*** PARENT" "$tty" setsid $me_DIR/$me_FILE child "$tty" "$@" & $DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty exit 0fi##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace --------------> # 3. We have been reforked. Go to work.exec >/tmp/outfileexec 2>/tmp/errfileexec 0</dev/nullshift; tty="$1"; shift$DEBUG && print_debug "*** DAEMON" "$tty" # The real stuff goes here. To exit, see fun (above)$DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty$DEBUG || {while true; do echo "Change this loop, so this silly no-op goes away." >/dev/null echo "Do something useful with your life, young padawan." >/dev/null sleep 10done}$DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$ttyexit # This may never run. Why is it here then? It's pretty. # Kind of like, "The End" at the end of a movie that you # already know is over. It's always nice.
Output looks like this when DEBUG
is set to true
. Notice how the session and process group ID (SESS, PGID) numbers change:
<shell_prompt>$ bash blahd*** PARENT, PID 5180 PID SESS PGID 5180 1708 5180/dev/pts/6PARENT OUT<shell_prompt>$ *** CHILD, NEW SESSION, NEW PGID, PID 5188 PID SESS PGID 5188 5188 5188not a ttyCHILD OUT*** DAEMON, PID 5198 PID SESS PGID 5198 5188 5188not a ttyNOT A REAL DAEMON. NOT RUNNING WHILE LOOP.DAEMON OUT