• Top
  • Comment
  • Reply

The init.d Script

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:

  • starting a process if its not started,
  • stopping it if it is started
  • stopping then starting the script, in other words restarting

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:

  • The shell deceleration !#/bin/bash
  • BEGIN INIT INFO - This is NOT a comment, this is actual deceleration. Took me a long while to recognize this
  • Include functions - . /etc/init.d/functions and . /etc/sysconfig/myconfig (Optional)
  • Variable deceleration - proc and lockfile
  • Local Function - start() and stop
  • case to handle actions

The shell Deceleration

#!/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.

The init inbuilt functions

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

daemon()

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

killproc()

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.

status()

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

Our start() function

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.

Our stop() Function

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.

The case call

# 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.

By

19th Feb 2012
© 2011 Shahmir Javaid - http://shahmirj.com/blog/25

Logan

21st May 2012

This is definitely the best article about daemons that I have come across. I have also read your previous post that has the c++ code.

I'm having trouble understanding how status() works though... You say that functions file that we include in the shell script is about the status function; could you by any chance give an example how functions file would be like? The status command on Ubuntu prints out "Unknown Job" for me...

I believe I am asking for a template for how to make status checks but if it is no trouble I would appreciate it. Thanks for this article, it is very enlightening.

Shahmir Javaid

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.

Suryaprathap

19th Aug 2012

Hi Shamir,
Its a great work.keep it up.

tom

27th Dec 2012

the daemon() function doesn't create the PID file, it just check the existence...

sharad chhetri

23rd Jan 2014

killproc() function bring me here. The function is not available in Ubuntu . This is nice post .thanks

Shahmir Javaid

23rd Jan 2014

Have you got the `. /etc/init.d/functions` file included, If so, check to make sure that killproc is defined there.

sharad chhetri

21st Feb 2014

Yes the file is there.I got the file after posting the comment here.
I also told you to remove my previous comment,might be you have not recieved

Shahmir Javaid

21st Feb 2014

Sorry no delete functionality :P

Paul Tanner

5th Jul 2014

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

Paul Tanner

5th Jul 2014

.. but that had the same problem. If it has no & then service start never completes. if I include the & then the pidfile is not created

Shahmir Javaid

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

Paul Tanner

6th Jul 2014

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.

Shahmir Javaid

8th Jul 2014

@Paul, please share with us if you find a solution. It might help someone down the line

Jawad Soleiman

13th Sep 2014

Hello
I have this command a"find domlogs/ -type f -name *-bytes_log -exec cat {} >> access_log \;" which i want to run as daemon - real time. in which part i should add it to always run

regards

Shahmir Javaid

15th Sep 2014

You can wrap it around a script and change the `prog=mywrappedscript.sh`

Jawad Soleiman

17th Sep 2014

i did that and i put sleep 5h in the script,,, but i should keep the terminal open to keep running and it stay on Starting Server :

because their is sleep

Shahmir Javaid

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 `&`

Mark Cowan

11th Mar 2015

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.

Shahmir Javaid

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



Back to Top
All content is © copyrighted, unless stated otherwise.
Subscribe, @shahmirj, Shahmir Javaid+