This is part 2 of my first tutorial on how to create a daemon script. I have been meaning to do this for a long time, so here it is. When I was starting out in Linux and trying to understand the whole process about daemons and how they work I was really overwhelmed and the main mistake I made was, that I thought the init.d script was some humounges language that I had to learn. This is not the case. Infact an init.d script is nothing but bash program which controls the following:
Please be aware, that if you have never programmed using bash, It would be a good idea to brush up some common knowledge before carrying any further. Google should be more than sufficient.
Before starting I think its only right to begin with a full init.d script, To show the different parts which can be explained later in details. So without further ado, I introduce our Script:
#!/bin/bash
#
# Daemon Name: myprogd
#
# chkconfig: - 58 74
# description: Our Script
# Source function library.
. /etc/init.d/functions
# Source networking configuration.
. /etc/sysconfig/myconfig
prog=myprogd
lockfile=/var/lock/subsys/$prog
start() {
#Make some checks for requirements before continuing
[ "$NETWORKING" = "no" ] && exit 1
[ -x /usr/sbin/$prog ] || exit 5
# Start our daemon daemon
echo -n $"Starting $prog: "
daemon --pidfile /var/run/${proc}.pid $prog
RETVAL=$?
echo
#If all is well touch the lock file
[ $RETVAL -eq 0 ] && touch $lockfile
return $RETVAL
}
stop() {
echo -n $"Shutting down $prog: "
killproc $prog
RETVAL=$?
echo
#If all is well remove the lockfile
[ $RETVAL -eq 0 ] && rm -f $lockfile
return $RETVAL
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $prog
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 2
esac
Scarry hunh? Well lets break it down. There are 6 main parts that this init script controls:
!#/bin/bash
. /etc/init.d/functions
and . /etc/sysconfig/myconfig
(Optional)proc
and lockfile
start()
and stop
case
to handle actions#!/bin/bash
#
# Daemon Name: myprogd
#
# chkconfig: - 58
# description: A script that does nothing but run a sleep process for testing
# requires: mysqld
This is the header that tells the shell what to use when running this script. The comment here are just for visual purposes but very important. Especially if you are planning on open-sourcing your script. You can choose to put more information here and be more descriptive, but i believe the headings above (Deamon Name, chkconfig, description and requires) are the most important ones, and should always be filled in.
init.d comes with a certian set of functions that help you control a daemon without going through the headache creating a processflow to control the starting and stopping of scripts. This file includes three major functions that we will be interested in daemon
, killproc
and status
This function starts your process and creates the PID file. The PID file stores the process-id value given to a each and every process by your kernel. You can view the current running processes and their PIDs by running ps -ef
.
The daemon function can take an optional parameter --pidfile
, This is the location and the name of the PID file that the daemon will create. If the parameter is omitted by default it will create a pidfile in /var/run/$1.pid
where $1
is the program name. The variable lets you define the PID-file name yourself should you choose to
A PID file is a file that contains simply the process id, If you look inside the /var/run/
folder on your linux machine you will see many files that store a number inside them. This is used by the init script to kill that specific Process with that id. When you start your process up, the script will create a PID file with its process ID.
For our script we are going to define a custom PID file so we can see how its used in other script
To kill any process on Linux you use the kill
command, which takes a Process ID as a parameter. This function performs a similar task, however it uses the PID file to retrieve the Process ID, this is why you can stop a daemon without giving it a PID file to kill. You can see how the PID file is now becoming more and more important.
Once we perform our kill we also need to remove the PID file as the Process ID is now no longer in use. This functions also deals with the cleanup of the PID file so a new one can be generated next time around. From a PID file prespective, daemon()
function creates it and the killproc()
function deletes it.
The status function just looks at the PID file and checks its running status.
I would like to point out all of the above function leave out other major functionality, this is done so someone starting out can understand the basics. I do recommend you look at the functions manually and work your way through them to understand all the different options.
To include these function we use
# Source function library.
. /etc/init.d/functions
start() {
#Make some checks for requirements before continuing
[ "$NETWORKING" = "no" ] && exit 1
[ -x /usr/sbin/$prog ] || exit 5
# Start our daemon daemon
echo -n $"Starting $prog: "
daemon --pidfile /var/run/${proc}.pid $prog
RETVAL=$?
echo
#If all is well touch the lock file
[ $RETVAL -eq 0 ] && touch $lockfile
return $RETVAL
}
This functions starts of with checking weather the variable $NETWORK
is set to "no", if this is the case we will exit out of our starting procedures as we would like the networking to be on, before we go any where with our script. We don't actually require networking for our script, but I have left that in there just to show you how you can control dependencies for other environment in your system.
Where does the $NETWORK
variable come from you ask? Well this is included and defined using the include just after the functions call.
# Source networking configuration.
. /etc/sysconfig/myconfig
If you look inside the file /etc/sysconfig/myconfig
, you can see the following
NETWORKING_IPV6=no
HOSTNAME=local.shahmirj.com
NETWORKING=yes
GATEWAY=192.168.1.1
The next line [ -x /usr/sbin/$prog ] || exit 5
checks to see weather or not the actual daemon file exist for us to run or not. There is no point continuing further if the script we are trying to daemon-ize does not exist. You may need to change this according to where your script is located.
Continuing on, we see the echo
to show some output of the script starting followed by a call to daemon --pidfile /var/run/${proc}.pid $prog
. This call is just passing the actual starting of the script to our daemon function as explained above. Note our pidfile parameter call here is useless, because if left out it will create the exact same PID file as the parameter. I have left the pidfile parameter just for demonstration purposes.
After this call we use a $RETVAL
variable to store the return code of the $prog
. Which is relevant to what you set your daemon to return back to the shell inside your main function. Note that 0
is always the return code for success, this can be confusing but just try to remember this. This is also the reason why in many c++ examples you see main()
returning 0
at the end.
This is also another reason why we fork()
our script, because our child keeps on living where our parent process returns the exit code.
If we success in running your script, we touch a $lockfile. A lockfile is similar to a PID file however it serves a different purpose. A Lockfile in our context ensures only one script is running at one time. This prevents someone trying to start the script up twice. This is your responsibility as there may be certain circumstances where you want to keep starting new scripts. So after we succeed in running your script, we touch
a lockfile. On a plus side this lock file can also tell us when your process started, by looking at the date the file was modified.
stop() {
echo -n $"Shutting down $prog: "
killproc $prog
RETVAL=$?
echo
#If all is well remove the lockfile
[ $RETVAL -eq 0 ] && rm -f $lockfile
return $RETVAL
}
This is more or less the negative of the start function. Instead of starting the process it kills the existing process using a call to killproc
and if the killing of the process succeds it removes the lock file.
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $prog
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 2
esac
This call just handles which function to run for the right call. For example
> /etc/init.d/myprogd start
> /etc/init.d/myprogd stop
> /etc/init.d/myprogd restart
> /etc/init.d/myprogd status
Thats all there is to it folks, Leave a comment if you want me to elaborate more on a specific part that you may be lost at. Otherwise go forth and daemon-ize.
26th May 2012
Ah I just realize there is no function file in Ubunutu, The init.d scripts in Ubuntu write there own status function, The best one to look at would be /etc/init.d/apache2.
In its basic form, the status function uses the existence of a lock file to determine weather a program is being run or not, If it exists, The status echo's "running" otherwise it says "not running". The lock file can be generated manually, by using touch. Just make sure the status function looks at the right lock file. The only place where a lock file should be deleted should be in the stop function.
23rd Jan 2014
Have you got the `. /etc/init.d/functions` file included, If so, check to make sure that killproc is defined there.
21st Feb 2014
Sorry no delete functionality :P
Thanks for this. As the others have said this is a very good explanation.
My script works fine and start returns "OK", **except** that it does not set the pidfile in /var/run. As a result stop fails.
permission on /var/run is 755 and I am testing as root.
my daemon line is:
daemon --pidfile /var/run/${prog}.pid "/usr/local/bin/node $locn/$prog.js | /usr/local/sbin/cronolog /var/log/$prog/%Y-%m-%d.log&"
The & sign is evidently part of the problem but I found it would not start without it.
I am going to try with another containing shell
5th Jul 2014
Hi Paul,
The daemon command expects the running command to return and fork it self, you could try to wrap the whole command you are going for in a shell script and fork it inside using the `&`. It might help as your command can then look like
daemon --pidfile /var/run/${prog}.pid /usr/local/bin/mywrapednodecommand
Hope this helps
Thx Shamir,
Yes it does. The main thing is that daemons must exit once they have forked their process(es). There seem to be a lot of ways of doing this and I did try what you suggest and not sure why that did not work. I will try it again.
I have also found a node module "daemon" that looks perfect for demonising other node programs so have a couple of approaches to try.
8th Jul 2014
@Paul, please share with us if you find a solution. It might help someone down the line
15th Sep 2014
You can wrap it around a script and change the `prog=mywrappedscript.sh`
17th Sep 2014
Ah you need to fork your command so change your find to `find domlogs/ -type f -name *-bytes_log -exec cat {} >> access_log \; &`, with the extra `&`
Thanks for this posting. It helped me a lot in learning about writing scripts in init.d. Of course I had to tweak things a bit.
One thing - where you set the Lock file. Shouldn't be done in the start() function? Otherwise anytime someone makes a reference to the script (e.g. ./myprogd status ) the Lock file will be written.
Also - I like the design of your site - it's not often one sees white lettering on red. And it's rare that it is done right.
13th Mar 2015
@Mark, It does to it in the start function (e.g [ $RETVAL -eq 0 ] && touch $lockfile) are you seeing something else.
Thanks for the site compliment, I am not really a designer but it was a great way of learning things.
:D