Daemonizing a python script in debian

15,982

Solution 1

Assuming you may want to manage more than one daemon in the future, let me recommend Supervisord. It's much simpler than writing and managing your own init.d scripts.

For example, starting your script would be as easy as including this in the conf:

[program:myscript]
command=/usr/bin/python /path/to/myscript.py

I use an init.d script available here. Rename it to supervisord and copy it to your /etc/init.d/ then run:

sudo update-rc.d supervisord defaults

I believe that init script has supervisord run as root as default. You can have it drop to run as another user if you like. I'm not if children run as root or not, although I'd assume not. Go ahead and check, but if they don't you can stick a sudo before the python command in your supervisord.conf where you call the script.

It that doesn't run, (or if you want supervisord to run as a non-root but still want your script run as root) you can allow for anyone (or a group of users) to run the python script as root (although you should make quite certain that this script cannot be edited by anyone other than root).

edit your sudoers file with "sudo visudo" and add the following to the end:

USERS ALL=(ALL) NOPASSWD: /path/to/myscript.py

Then make sure you have a shebang at the beginning of your python script and change the command to omit the python call, i.e:

[program:myscript]
command=sudo /path/to/myscript.py

Solution 2

Here's a good blog post which deals with this question: Getting a Python script to run in the background (as a service) on boot

Solution 3

Use daemontools from djb. It is a lot easier than the other answers provided. For starters you can install daemon tools with apt-get so you do not need to worry about grabbing an unknown script from a gist and you get updates through debian like normal. daemontools also takes care of restarting the service if it dies and provides for logging. There is a description of daemontools and debian here:

http://blog.rtwilson.com/how-to-set-up-a-simple-service-to-run-in-the-background-on-a-linux-machine-using-daemontools/

djb's page aout daemontools:

http://cr.yp.to/daemontools.html

Share:
15,982
Joseph Roberts
Author by

Joseph Roberts

I am a strong all round technologist most interested in robotics and the Internet of Things, as they bring hardware and software together, enabling the digital and real worlds to combine and augment each other. Technology has always fascinated me and from an early age, I have been interested in computers and electronics. For this reason, I have been developing software and tinkering with hardware for over 15 years. I have worked with embedded devices, both Linux based single board computers and microcontrollers, writing firmware and integrating with external hardware and sensors. I have also developed applications targeting both Windows and Linux based desktop environments as well as iOS and Android platforms. My experience has brought me to work on many different projects using a variety of languages such as C++, Golang, Rust, CoffeeScript, C#, and Python. I have managed projects from start to finish, developed and debugged electronic PCBs and designed and manufactured small runs of electronic and mechanical components. Currently, I'm a Hardware Engineer at resin.io focused on bringing micro-controller support to the resin platform. To keep up to date with technology I regularly attend a Hackerspace, go to meetups and have recently been to helping to run resin hosted hackathons. I also work on projects in my spare time. At the moment I'm working on building a smaller, nimbler Quad Rotor, prototyping a small two wheeled balancing robot and developing a Rust command line tool for tracking crypto-currency prices.

Updated on June 04, 2022

Comments

  • Joseph Roberts
    Joseph Roberts almost 2 years

    I have a python script that i want to run in the background on startup. This is the script:

    #!/usr/bin/python
    from Adafruit_CharLCD import Adafruit_CharLCD
    from subprocess import * 
    from time import sleep, strftime
    from datetime import datetime
    from datetime import timedelta
    from os import system
    from os import getloadavg
    from glob import glob
    
    #Variables
    lcd = Adafruit_CharLCD() #Stores LCD object
    cmdIP = "ip addr show eth0 | grep inet | awk '{print $2}' | cut -d/ -f1" #Current IP
    cmdHD = "df -h / | awk '{print $5}'" # Available hd space
    cmdSD = "df -h /dev/sda1 | awk '{print $5}'" # Available sd space
    cmdRam = "free -h"
    temp = 0
    
    #Run shell command
    def run_cmd(cmd):
        p = Popen(cmd, shell=True, stdout=PIPE)
        output = p.communicate()[0]
        return output
    
    #Initalises temp device     
    def initialise_temp():
        #Initialise
        system("sudo modprobe w1-gpio")
        system("sudo modprobe w1-therm")
        #Find device
        devicedir = glob("/sys/bus/w1/devices/28-*")
        device = devicedir[0]+"/w1_slave"
        return device
    
    #Gets temp  
    def get_temp(device):
        f = open (device, 'r')
        sensor = f.readlines()
        f.close()
    
        #parse results from the file
        crc=sensor[0].split()[-1]
        temp=float(sensor[1].split()[-1].strip('t='))
        temp_C=(temp/1000.000)
        temp_F = ( temp_C * 9.0 / 5.0 ) + 32
    
        #output
        return temp_C
    
    #Gets time
    def get_time():
        return datetime.now().strftime('%b %d  %H:%M:%S\n')
    
    #Gets uptime
    def get_uptime():
        with open('/proc/uptime', 'r') as f:
            seconds = float(f.readline().split()[0])
            array = str(timedelta(seconds = seconds)).split('.')
            string = array[0].split(' ')
            totalString = string[0] + ":" + string[2]
        return totalString
    
    #Gets average load
    def get_load():
        array = getloadavg()
        average = 0
        for i in array:
            average += i
        average = average / 3
        average = average * 100
        average = "%.2f" % average
        return str(average + "%")
    
    #def get_ram():
    def get_ram():
        ram = run_cmd(cmdRam)
        strippedRam = ram.replace("\n"," ");
        splitRam = strippedRam.split(' ')
        totalRam = int(splitRam[52].rstrip("M"))
        usedRam = int(splitRam[59].rstrip("M"))
        percentage = "%.2f" % ((float(usedRam) / float(totalRam)) * 100)
        return percentage + "%"
    
    #Gets the SD usage
    def get_sd():
        sd = run_cmd(cmdSD)
        strippedSD = sd.lstrip("Use%\n")
        return strippedSD
    
    #Gets the HD usage
    def get_hd():
        hd = run_cmd(cmdSD)
        strippedHD = hd.lstrip("Use%\n")
        return strippedHD
    
    def scroll():
        while(1):
            lcd.scrollDisplayLeft()
            sleep(0.5)
    
    #Uptime and IP
    def screen1():
        uptime = get_uptime()
        lcd.message('Uptime %s\n' % (uptime))
        ipaddr = run_cmd(cmdIP)
        lcd.message('IP %s' % (ipaddr))
    
    #Ram and load
    def screen2():
        ram = get_ram()
        lcd.message('Ram Used %s\n' % (ram))
        load = get_load()
        lcd.message('Avg Load %s' % (load))
    
    #Temp and time
    def screen3():
        time = get_time();
        lcd.message('%s\n' % (time))
        lcd.message('Temp %s' % (temp))
    
    #HD and SD usage
    def screen4():
        sd = get_sd()
        lcd.message('SD Used %s\n' % (sd))
        hd = get_hd()
        lcd.message('HD Used %s' % (hd))
    
    #Pause and clear
    def screenPause(time):
        sleep(time)
        #In here to reduce lag
        global temp
        temp = str(get_temp(device));
        lcd.clear()
    ###########################################################################################################
    
    #Initialise
    lcd.begin(16,2)
    device = initialise_temp()
    lcd.clear()
    
    #Testing
    
    #Main loop
    while(1):
        screen1()
        screenPause(5)
        screen2()
        screenPause(5)
        screen3()
        screenPause(5)
        screen4()
        screenPause(5)
    

    I know i probably havnt done things the write way but its the first attempt. My startup script is in /etc/init.d This is the script:

    #! /bin/sh
    ### BEGIN INIT INFO
    # Provides:          LCD looping
    # Required-Start:    $remote_fs $syslog
    # Required-Stop:     $remote_fs $syslog
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: LCD daemon
    # Description:       This file should be used to construct scripts to be
    #                    placed in /etc/init.d.
    ### END INIT INFO
    
    # Author: Foo Bar <[email protected]>
    #
    # Please remove the "Author" lines above and replace them
    # with your own name if you copy and modify this script.
    
    # Do NOT "set -e"
    
    # PATH should only include /usr/* if it runs after the mountnfs.sh script
    PATH=/sbin:/usr/sbin:/bin:/usr/bin
    DESC="Loops the LCD screen through LCD.py"
    NAME=startup.py
    DAEMON=/home/pi/Programming/LCD/startup.py
    DAEMON_ARGS=""
    PIDFILE=/var/run/daemonLCD.pid
    SCRIPTNAME=/etc/init.d/daemonLCD
    
    # Exit if the package is not installed
    [ -x "$DAEMON" ] || exit 0
    
    # Read configuration variable file if it is present
    [ -r /etc/default/daemonLCD ] && . /etc/default/daemonLCD
    
    # Load the VERBOSE setting and other rcS variables
    . /lib/init/vars.sh
    
    # Define LSB log_* functions.
    # Depend on lsb-base (>= 3.2-14) to ensure that this file is present
    # and status_of_proc is working.
    . /lib/lsb/init-functions
    
    #
    # Function that starts the daemon/service
    #
    do_start()
    {
            # Return
            #   0 if daemon has been started
            #   1 if daemon was already running
            #   2 if daemon could not be started
            start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                    || return 1
            start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
                    $DAEMON_ARGS \
                    || return 2
            # Add code here, if necessary, that waits for the process to be ready
            # to handle requests from services started subsequently which depend
            # on this one.  As a last resort, sleep for some time.
    }
    
    #
    # Function that stops the daemon/service
    #
    do_stop()
    {
            # Return
            #   0 if daemon has been stopped
            #   1 if daemon was already stopped
            #   2 if daemon could not be stopped
            #   other if a failure occurred
            start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
            RETVAL="$?"
            [ "$RETVAL" = 2 ] && return 2
            # Wait for children to finish too if this is a daemon that forks
            # and if the daemon is only ever run from this initscript.
            # If the above conditions are not satisfied then add some other code
            # that waits for the process to drop all resources that could be
            # needed by services started subsequently.  A last resort is to
            # sleep for some time.
            start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
            [ "$?" = 2 ] && return 2
            # Many daemons don't delete their pidfiles when they exit.
            rm -f $PIDFILE
            return "$RETVAL"
    #
    # Function that sends a SIGHUP to the daemon/service
    #
    do_reload() {
            #
            # If the daemon can reload its configuration without
            # restarting (for example, when it is sent a SIGHUP),
            # then implement that here.
            #
            start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
            return 0
    }
    
    case "$1" in
      start)
            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
            do_start
            case "$?" in
                    0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                    2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
            esac
            ;;
      stop)
            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
            do_stop
            case "$?" in
                    0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                    2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
            esac
            ;;
      status)
            status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
            ;;
      #reload|force-reload)
            #
            # If do_reload() is not implemented then leave this commented out
            # and leave 'force-reload' as an alias for 'restart'.
            #
            #log_daemon_msg "Reloading $DESC" "$NAME"
            #do_reload
            #log_end_msg $?
            #;;
    
      restart|force-reload)
            #
            # If the "reload" option is implemented then remove the
            # 'force-reload' alias
            #
            log_daemon_msg "Restarting $DESC" "$NAME"
            do_stop
            case "$?" in
              0|1)
                    do_start
                    case "$?" in
                            0) log_end_msg 0 ;;
                            1) log_end_msg 1 ;; # Old process is still running
                            *) log_end_msg 1 ;; # Failed to start
                    esac
                    ;;
              *)
                    # Failed to stop
                    log_end_msg 1
                    ;;
            esac
            ;;
      *)
            #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
            echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
            exit 3
            ;;
    esac
    
    :
    

    Im think i have missed something as when i type daemonLCD start it says command not found. Any input would be great.

    Thanks