How do I daemonize an arbitrary script in unix?
I apologise for the long answer (please see comments about how my answer nails the spec). I'm trying to be comprehensive, so you have as good of a leg up as possible. :-)
If you are able to install programs (have root access), and are willing to do one-time legwork to set up your script for daemon execution (i.e., more involved than simply specifying the command-line arguments to run on the command line, but only needing to be done once per service), I have a way that's more robust.
It involves using daemontools. The rest of the post describes how to set up services using daemontools.
Initial setup
- Follow the instructions in How to install daemontools. Some distributions (e.g., Debian, Ubuntu) already have packages for it, so just use that.
- Make a directory called
/service
. The installer should have already done this, but just verify, or if installing manually. If you dislike this location, you can change it in yoursvscanboot
script, although most daemontools users are used to using/service
and will get confused if you don't use it. - If you're using Ubuntu or another distro that doesn't use standard
init
(i.e., doesn't use/etc/inittab
), you will need to use the pre-installedinittab
as a base for arrangingsvscanboot
to be called byinit
. It's not hard, but you need to know how to configure theinit
that your OS uses.svscanboot
is a script that callssvscan
, which does the main work of looking for services; it's called frominit
soinit
will arrange to restart it if it dies for any reason.
Per-service setup
- Each service needs a service directory, which stores housekeeping information about the service. You can also make a location to house these service directories so they're all in one place; usually I use
/var/lib/svscan
, but any new location will be fine. I usually use a script to set up the service directory, to save lots of manual repetitive work. e.g.,
sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
where
some-service-name
is the name you want to give your service,user
is the user to run that service as, andloguser
is the user to run the logger as. (Logging is explained in just a little bit.)- Your service has to run in the foreground. If your program backgrounds by default, but has an option to disable that, then do so. If your program backgrounds without a way to disable it, read up on
fghack
, although this comes at a trade-off: you can no longer control the program usingsvc
. - Edit the
run
script to ensure it's doing what you want it to. You may need to place asleep
call at the top, if you expect your service to exit frequently. - When everything is set up right, create a symlink in
/service
pointing to your service directory. (Don't put service directories directly within/service
; it makes it harder to remove the service fromsvscan
's watch.)
Logging
- The daemontools way of logging is to have the service write log messages to standard output (or standard error, if you're using scripts generated with
mkservice
);svscan
takes care of sending log messages to the logging service. - The logging service takes the log messages from standard input. The logging service script generated by
mkservice
will create auto-rotated, timestamped log files in thelog/main
directory. The current log file is calledcurrent
. - The logging service can be started and stopped independently of the main service.
- Piping the log files through
tai64nlocal
will translate the timestamps into a human-readable format. (TAI64N is a 64-bit atomic timestamp with a nanosecond count.)
Controlling services
- Use
svstat
to get the status of a service. Note that the logging service is independent, and has its own status. - You control your service (start, stop, restart, etc.) using
svc
. For example, to restart your service, usesvc -t /service/some-service-name
;-t
means "sendSIGTERM
". - Other signals available include
-h
(SIGHUP
),-a
(SIGALRM
),-1
(SIGUSR1
),-2
(SIGUSR2
), and-k
(SIGKILL
). - To down the service, use
-d
. You can also prevent a service from automatically starting at bootup by creating a file nameddown
in the service directory. - To start the service, use
-u
. This is not necessary unless you've downed it previously (or set it up not to auto-start). - To ask the supervisor to exit, use
-x
; usually used with-d
to terminate the service as well. This is the usual way to allow a service to be removed, but you have to unlink the service from/service
first, or elsesvscan
will restart the supervisor. Also, if you created your service with a logging service (mkservice -l
), remember to also exit the logging supervisor (e.g.,svc -dx /var/lib/svscan/some-service-name/log
) before removing the service directory.
Summary
Pros:
- daemontools provides a bulletproof way to create and manage services. I use it for my servers, and I highly recommend it.
- Its logging system is very robust, as is the service auto-restart facility.
- Because it starts services with a shell script that you write/tune, you can tailor your service however you like.
- Powerful service control tools: you can send most any signal to a service, and can bring services up and down reliably.
- Your services are guaranteed a clean execution environment: they will execute with the same environment, process limits, etc., as what
init
provides.
Cons:
- Each service takes a bit of setup. Thankfully, this only needs doing once per service.
- Services must be set up to run in the foreground. Also, for best results, they should be set up to log to standard output/standard error, rather than syslog or other files.
- Steep learning curve if you're new to the daemontools way of doing things. You have to restart services using
svc
, and cannot run the run scripts directly (since they would then not be under the control of the supervisor). - Lots of housekeeping files, and lots of housekeeping processes. Each service needs its own service directory, and each service uses one supervisor process to auto-restart the service if it dies. (If you have many services, you will see lots of
supervise
processes in your process table.)
In balance, I think daemontools is an excellent system for your needs. I welcome any questions about how to set it up and maintain it.
I think you may want to try start-stop-daemon(8)
. Check out scripts in /etc/init.d
in any Linux distro for examples. It can find started processes by command line invoked or PID file, so it matches all your requirements except being a watchdog for your script. But you can always start another daemon watchdog script that just restarts your script if necessary.
You should have a look at daemonize. It allows to detect second copy (but it uses file locking mechanism). Also it works on different UNIX and Linux distributions.
If you need to automatically start your application as daemon, then you need to create appropriate init-script.
You can use the following template:
#!/bin/sh
#
# mydaemon This shell script takes care of starting and stopping
# the <mydaemon>
#
# Source function library
. /etc/rc.d/init.d/functions
# Do preliminary checks here, if any
#### START of preliminary checks #########
##### END of preliminary checks #######
# Handle manual control parameters like start, stop, status, restart, etc.
case "$1" in
start)
# Start daemons.
echo -n $"Starting <mydaemon> daemon: "
echo
daemon <mydaemon>
echo
;;
stop)
# Stop daemons.
echo -n $"Shutting down <mydaemon>: "
killproc <mydaemon>
echo
# Do clean-up works here like removing pid files from /var/run, etc.
;;
status)
status <mydaemon>
;;
restart)
$0 stop
$0 start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 1
esac
exit 0
You can daemonize any executable in Unix by using nohup and the & operator:
nohup yourScript.sh script args&
The nohup command allows you to shut down your shell session without it killing your script, while the & places your script in the background so you get a shell prompt to continue your session. The only minor problem with this is standard out and standard error both get sent to ./nohup.out, so if you start several scripts in this manor their output will be intertwined. A better command would be:
nohup yourScript.sh script args >script.out 2>script.error&
This will send standard out to the file of your choice and standard error to a different file of your choice. If you want to use just one file for both standard out and standard error you can us this:
nohup yourScript.sh script args >script.out 2>&1 &
The 2>&1 tells the shell to redirect standard error (file descriptor 2) to the same file as standard out (file descriptor 1).
To run a command only once and restart it if it dies you can use this script:
#!/bin/bash
if [[ $# < 1 ]]; then
echo "Name of pid file not given."
exit
fi
# Get the pid file's name.
PIDFILE=$1
shift
if [[ $# < 1 ]]; then
echo "No command given."
exit
fi
echo "Checking pid in file $PIDFILE."
#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
ps -p $PID >/dev/null 2>&1
if [[ $? = 0 ]]; then
echo "Command $1 already running."
exit
fi
fi
# Write our pid to file.
echo $$ >$PIDFILE
# Get command.
COMMAND=$1
shift
# Run command until we're killed.
while true; do
$COMMAND "$@"
sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done
The first argument is the name of the pid file to use. The second argument is the command. And all other arguments are the command's arguments.
If you name this script restart.sh this is how you would call it:
nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &