25 January 2015
Init scripts are used to start and stop daemon processes on Linux systems. In turns out, that like most things in Linux, they are pretty simple. Following a few rules allows you to quickly create a script that plays well with how the system starts up and shuts down.
There is a pretty good guide that explains all the parts of the init scripts in much more detail that I will repeat here.
Your init script should be given the same name as the process you are tying to start and it should be stored in /etc/rc.d/init.d
. For example, if you are creating an init script for a process called flume, you should create a file /etc/rc.d/init.d/flume
and set its permissions to 755.
Once you do this, you can use the service command start and stop the service, for example:
# /etc/rc.d/init.d/testservice
echo "executed the service start script"
$ service testservice start
executed the service start script
For a few reasons, an init script should create a lock file in /var/lock/subsys/
It should write a pidfile (ie a file containing the PID of the parent process of the service) into /var/run. What I found is that if the process runs as a user other than root, the user starting the process probably won't have permission to write into /var/run. If that is the case, you can create a sub-directory, and store the pidfile there - /var/run/
For Redhat systems, all init scripts should contain a header line for chkconfig, normally it looks something like this:
# chkconfig: - 20 80
This means the script is off by default on all run levels, which can be changed using the chkconfig utility.
Additionally, and chkconfig description line should be included:
# description: This is a description for my service. Multiple lines \
# should be ended with a backslash as shown.
On Redhat (and Centos systems), there are a few library functions that you should include in any init script. They are stored in /etc/rc.d/init.d/functions, and should be included into the init script:
# source function library
. /etc/rc.d/init.d/functions
This library provides 4 functions.
Used to start a process that correctly daemonizes itself. Interestingly, the daemon function does not write a pidfile for the process it starts. I think it expects the process to create its own pidfile. It takes a few options in a command line like format, including the program to start and any options to pass to the program, for instance:
daemon --user=httpd --pidfile=/var/run/httpd.pid /usr/local/bin/<service> <any service start options>
The user switch is only required if the process does not run as root. The pidfile parameter is also optional - it does not actually create a pidfile for the process that is started.
Used to shutdown (or kill) a running process. Generally you pass it the pid file of the process and it shuts it down.
killproc -p /var/run/process.pid /usr/local/bin/process [-signal]
Can be used to find the pid of the procedure, if it is running:
pidofproc -p /var/run/process.pid /user/local/bin/process
Tests to see if the process is running or not:
status -p /var/run/process.pid /user/local/bin/process
Putting all these points together, gives a fairly generic init script template (taken directly from the guide I mentioned earlier:
#!/bin/sh
#
# <daemonname> <summary>
#
# chkconfig: - 20 80
# description: <description, split multiple lines with \
# a backslash>
# Source function library.
. /etc/rc.d/init.d/functions
exec="/path/to/<daemonname>"
prog="<service name>"
config="<path to major config file>"
[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
lockfile=/var/lock/subsys/$prog
start() {
[ -x $exec ] || exit 5
[ -f $config ] || exit 6
echo -n $"Starting $prog: "
# if not running, start it up here, usually something like "daemon $exec"
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}
stop() {
echo -n $"Stopping $prog: "
# stop it here, often "killproc $prog"
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}
restart() {
stop
start
}
reload() {
restart
}
force_reload() {
restart
}
rh_status() {
# run checks to determine if the service is running or use generic status
status $prog
}
rh_status_q() {
rh_status >/dev/null 2>&1
}
case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
restart
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?