Skip to content

Instantly share code, notes, and snippets.

@drmalex07
Last active October 17, 2022 03:19
Show Gist options
  • Save drmalex07/8b35f2621e556be30e56 to your computer and use it in GitHub Desktop.
Save drmalex07/8b35f2621e556be30e56 to your computer and use it in GitHub Desktop.
Another example of an (sysvinit) initd script using the LSB helpers. #sysvinit #initd #initd-scripts #lsb-init #linux #daemon

README

This is a very simple example of an init.d-style daemon that uses the LSB helpers. It is a modified version of the script found at http://www.thegeekstuff.com/2012/03/lsbinit-script.

Assume we have a daemon helloworld.py which can be invoked as (running at the foreground):

/path/to/helloworld.py /another-path/to/helloworld.ini

We want to wrap this daemon, say helloworld, into a init.d-style script (e.g. to include it in the normal boot sequence). When done, the daemon will be managed in the usual manner:

invoke-rc.d helloworld {start|stop|reload|status}

The wrapper init.d script will (as usual) live under /etc/init.d/.

Install the daemon under the proper runlevels (specified into the init.d script itself), i.e. create all needed symlinks into /etc/rc*.d directories:

update-rc.d helloworld defaults

If needed, uninstall the daemon with:

update-rc.d helloworld remove
#!/bin/bash
### BEGIN INIT INFO
# Provides: helloworld
# Required-Start: networking
# Required-Stop: networking
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Run helloworld daemon
### END INIT INFO
# Source LSB helpers
. /lib/lsb/init-functions
# Process name (for display only)
NAME=helloworld
# Where is the actual executable for the daemon
DAEMON=/path/to/helloworld.py
DAEMON_ARGS="/another-path/to/helloworld.ini"
# The user:group under which the daemon must run
RUN_AS_USER=malex
# pid file for the daemon
PIDFILE=/var/run/helloworld.pid
# If the daemon is not there, then exit.
if ! [ -x ${DAEMON} ] ; then
log_failure_msg "Cannot find an executable at ${DAEMON}"
exit 1
fi
case $1 in
start)
# Check if pidfile exists
if [ -e ${PIDFILE} ]; then
# Check the actual status of process
status_of_proc -p ${PIDFILE} ${DAEMON} ${NAME} && status="0" || status="$?"
# If the status is successfull, no need to start again.
[ ${status} = "0" ] && exit 0
fi
# Start the daemon.
log_daemon_msg "Starting ${NAME}"
# Start the daemon with the help of start-stop-daemon
start-stop-daemon -S --quiet --oknodo --pidfile ${PIDFILE} --make-pidfile --background \
--chuid ${RUN_AS_USER} --startas ${DAEMON} -- ${DAEMON_ARGS}
if [ "$?" == "0" ]; then
log_end_msg 0
else
log_end_msg 1
fi
;;
stop)
if [ -e ${PIDFILE} ]; then
status_of_proc -p ${PIDFILE} ${DAEMON} ${NAME} && status="0" || status="$?"
if [ "$status" = "0" ]; then
log_daemon_msg "Stopping ${NAME}"
start-stop-daemon -K --signal TERM --quiet --oknodo --pidfile ${PIDFILE}
if [ "$?" == "0" ]; then
log_end_msg 0
rm -rf ${PIDFILE}
else
log_end_msg 1
fi
fi
else
log_daemon_msg "${NAME} is not running"
log_end_msg 0
fi
;;
restart)
$0 stop && sleep 3 && $0 start
;;
status)
# Check the status of the process.
if [ -e ${PIDFILE} ]; then
status_of_proc -p ${PIDFILE} ${DAEMON} ${NAME} && exit 0 || exit $?
else
log_daemon_msg "${NAME} is not running (no pidfile)"
log_end_msg 0
fi
;;
reload)
if [ -e ${PIDFILE} ]; then
log_daemon_msg "Reloading ${NAME}"
start-stop-daemon -K --quiet --signal HUP --pidfile ${PIDFILE}
if [ "$?" == "0" ]; then
log_end_msg 0
else
log_end_msg 1
fi
else
log_failure_msg "Cannot find pidfile at ${PIDFILE}"
fi
;;
*)
# Invalid argument, print the usage message.
echo "Usage: $0 {start|stop|restart|reload|status}"
exit 2
;;
esac
[main]
greeting = Hello (again, and again) World
[loggers]
keys = root
[handlers]
keys = syslog,console
[formatters]
keys = default
[logger_root]
level = DEBUG
handlers = syslog,console
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = default
[handler_syslog]
class = handlers.SysLogHandler
level = DEBUG
formatter = default
args = (('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_LOCAL0)
[formatter_default]
format = helloworld %(levelname)-5.5s [%(name)s] %(message)s
#!/usr/bin/env python
import sys
import threading
import logging
import logging.config
import signal
import ConfigParser as configparser
from wsgiref.simple_server import make_server
config_file = sys.argv[1]
logging.config.fileConfig(config_file)
log1 = logging.getLogger(__name__)
greeting_message = None
def read_config():
log1.info('Reading main configuration from %s', config_file)
global greeting_message
config = configparser.ConfigParser()
config.read(config_file)
greeting_message = config.get('main', 'greeting')
def handle_reload(signum, frame):
log1.info('Reloading service configuration')
read_config()
shutdown = None
def handle_stop(signum, frame):
global shutdown
log1.info('Received termination signal: Shutting down service')
shutdown.start()
def app(environ, start_response):
global greeting_message
log1.info('[client %(REMOTE_ADDR)s] Received request: '
'%(REQUEST_METHOD)s %(PATH_INFO)s', environ)
status = '200 OK'
headers = [('Content-type', 'text/plain')]
start_response(status, headers)
return [greeting_message]
if __name__ == '__main__':
# Install signal handlers
signal.signal(signal.SIGHUP, handle_reload)
signal.signal(signal.SIGTERM, handle_stop)
signal.signal(signal.SIGINT, handle_stop)
# Read configuration, instantiate server
read_config()
httpd = make_server('', 8000, app)
log1.info('Serving on port 8000...')
# Prepare a thread to handle server's shutdown
# Note: The shutdown method must be invoked from a different thread
# than the one that runs the server's main loop.
shutdown = threading.Thread(target=httpd.shutdown)
# Serve
httpd.serve_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment