Daemonizing a python script in debian
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:
djb's page aout daemontools
:
http://cr.yp.to/daemontools.html
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, 2022Comments
-
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