I'd like a daemonizer that can turn an arbitrary, generic script or command into a daemon.
There are two common cases I'd like to deal with:
I have a script that should run forever. If it ever dies (or on reboot), restart it. Don't let there ever be two copies running at once (detect if a copy is already running and don't launch it in that case).
I have a simple script or command line command that I'd like to keep executing repeatedly forever (with a short pause between runs). Again, don't allow two copies of the script to ever be running at once.
Of course it's trivial to write a "while(true)" loop around the script in case 2 and then apply a solution for case 1, but a more general solution will just solve case 2 directly since that applies to the script in case 1 as well (you may just want a shorter or no pause if the script is not intended to ever die (of course if the script really does never die then the pause doesn't actually matter)).
Note that the solution should not involve, say, adding file-locking code or PID recording to the existing scripts.
More specifically, I'd like a program "daemonize" that I can run like
% daemonize myscript arg1 arg2
or, for example,
% daemonize 'echo `date` >> /tmp/times.txt'
which would keep a growing list of dates appended to times.txt. (Note that if the argument(s) to daemonize is a script that runs forever as in case 1 above, then daemonize will still do the right thing, restarting it when necessary.) I could then put a command like above in my .login and/or cron it hourly or minutely (depending on how worried I was about it dying unexpectedly).
NB: The daemonize script will need to remember the command string it is daemonizing so that if the same command string is daemonized again it does not launch a second copy.
Also, the solution should ideally work on both OS X and linux but solutions for one or the other are welcome.
EDIT: It's fine if you have to invoke it with sudo daemonize myscript myargs
.
(If I'm thinking of this all wrong or there are quick-and-dirty partial solutions, I'd love to hear that too.)
PS: In case it's useful, here's a similar question specific to python.
And this answer to a similar question has what appears to be a useful idiom for a quick-and-dirty demonizing of an arbitrary script:
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 &
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.
/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 your svscanboot
script, although most daemontools users are used to using /service
and will get confused if you don't use it.init
(i.e., doesn't use /etc/inittab
), you will need to use the pre-installed inittab
as a base for arranging svscanboot
to be called by init
. It's not hard, but you need to know how to configure the init
that your OS uses. svscanboot
is a script that calls svscan
, which does the main work of looking for services; it's called from init
so init
will arrange to restart it if it dies for any reason./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, and loguser
is the user to run the logger as. (Logging is explained in just a little bit.)
fghack
, although this comes at a trade-off: you can no longer control the program using svc
.run
script to ensure it's doing what you want it to. You may need to place a sleep
call at the top, if you expect your service to exit frequently./service
pointing to your service directory. (Don't put service directories directly within /service
; it makes it harder to remove the service from svscan
's watch.)mkservice
); svscan
takes care of sending log messages to the logging service.mkservice
will create auto-rotated, timestamped log files in the log/main
directory. The current log file is called current
.tai64nlocal
will translate the timestamps into a human-readable format. (TAI64N is a 64-bit atomic timestamp with a nanosecond count.)svstat
to get the status of a service. Note that the logging service is independent, and has its own status.svc
. For example, to restart your service, use svc -t /service/some-service-name
; -t
means "send SIGTERM
".-h
(SIGHUP
), -a
(SIGALRM
), -1
(SIGUSR1
), -2
(SIGUSR2
), and -k
(SIGKILL
).-d
. You can also prevent a service from automatically starting at bootup by creating a file named down
in the service directory.-u
. This is not necessary unless you've downed it previously (or set it up not to auto-start).-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 else svscan
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.Pros:
init
provides.Cons:
svc
, and cannot run the run scripts directly (since they would then not be under the control of the supervisor).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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With