Skip to content

Instantly share code, notes, and snippets.

@sickel
Last active June 4, 2019 19:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sickel/9d6ff3b2ddd8ee89d873bb4b82ec5a73 to your computer and use it in GitHub Desktop.
Save sickel/9d6ff3b2ddd8ee89d873bb4b82ec5a73 to your computer and use it in GitHub Desktop.
A python deamon that spins of an extra thread and acts on signals
# Concept of daemon that spins of an extra thread and can be controlled by signals
#
# I have rewritten the script slightly after I did this log. So some output has changed a bit,
# the signals are still the same
#
# This has to be run with python3
#
# Developed and tested on debian 9.9 - all modules installed with apt
#
# morten@latlon:/home/disk0/m/GardenController$ sudo tail -f /var/log/messages &
# morten@latlon:/home/disk0/m/GardenController$ python3 controller_daemon.py && ps -u morten | grep py
# 26808 ? 00:00:00 python3
# morten@latlon:/home/disk0/m/GardenController$ Jun 2 20:42:15 latlon controller_daemon.py: starting up
# Jun 2 20:42:15 latlon controller_daemon.py: Thread started
# Jun 2 20:42:15 latlon controller_daemon.py: Thread still active
# Jun 2 20:42:30 latlon controller_daemon.py: Thread still active
# morten@latlon:/home/disk0/m/GardenController$ kill -SIGUSR1 26808
# Jun 2 20:42:40 latlon controller_daemon.py: sending message to thread
# Jun 2 20:42:45 latlon controller_daemon.py: Thread got it
# Jun 2 20:42:45 latlon controller_daemon.py: Thread still active
# morten@latlon:/home/disk0/m/GardenController$ kill -SIGUSR2 `cat /tmp/controller.pid`
# Jun 2 20:42:51 latlon controller_daemon.py: Got it!
# morten@latlon:/home/disk0/m/GardenController$ kill -SIGUSR1 26808
# Jun 2 20:42:55 latlon controller_daemon.py: sending message to thread
# Jun 2 20:43:00 latlon controller_daemon.py: Thread got it
# Jun 2 20:43:00 latlon controller_daemon.py: Thread still active
# morten@latlon:/home/disk0/m/GardenController$ kill -SIGTERM 26808
# Jun 2 20:43:20 latlon controller_daemon.py: Starting to exit
# Jun 2 20:43:20 latlon controller_daemon.py: starting to end process
# Jun 2 20:43:20 latlon controller_daemon.py: ending process
# Jun 2 20:43:30 latlon controller_daemon.py: Ending thread
# Jun 2 20:43:30 latlon controller_daemon.py: Finished
# After a signal is sent, it may take up to 5 secs before it is processed by the main deamon thread and up to 15 secs
# afterwards for the subthread. - Nothing happens during the time.sleep()s. To get the code more responsive, it is
# possible to have short sleeps and use some other checks to see if the time is due to do whatever is to be done, but this is
# meant to be an as simple as possible example.
#
import daemon
import daemon.pidfile
import time
import signal
import threading
import os
import syslog
EXITDAEMON=False
MSGTHREAD=False
def runthread(evtexit,evtmsg):
syslog.syslog(syslog.LOG_NOTICE,"Thread started")
while True:
if evtexit.isSet():
syslog.syslog(syslog.LOG_NOTICE,"Ending thread")
return()
if evtmsg.isSet():
syslog.syslog(syslog.LOG_NOTICE,"Thread got it")
syslog.syslog(syslog.LOG_NOTICE,"Thread still active")
time.sleep(15)
def do_work():
global MSGTHREAD
global EXITDAEMON
syslog.syslog(syslog.LOG_NOTICE,"starting up")
stopthread=threading.Event()
msgthread=threading.Event()
trd=threading.Thread(target=runthread,args = (stopthread,msgthread, ))
trd.start()
while True:
with open("/tmp/current_time.txt", "w") as f:
f.write("The time is now " + time.ctime())
if MSGTHREAD:
msgthread.set()
syslog.syslog(syslog.LOG_NOTICE,"sending message to thread")
MSGTHREAD=False
if EXITDAEMON:
syslog.syslog(syslog.LOG_NOTICE,"starting to end process ")
break
time.sleep(5)
# Will get here after a break caused by EXITDEAMON being true
syslog.syslog(syslog.LOG_NOTICE,"ending process")
stopthread.set() # tells the thread to finish its business
trd.join() # ends thread
syslog.syslog(syslog.LOG_NOTICE,"Finished")
def receiveSignal(signalNumber, frame):
syslog.syslog("Got it!")
def shutdown(signum,frame):
global EXITDAEMON
EXITDAEMON=True
syslog.syslog(syslog.LOG_NOTICE,"Ready to exit")
def msgthread(signum,frame):
global MSGTHREAD
MSGTHREAD=True
syslog.syslog(syslog.LOG_NOTICE,"Will send message to thread") # This was added after I did the log at the top
def run():
with daemon.DaemonContext(signal_map={
signal.SIGTERM: shutdown,
signal.SIGUSR2: receiveSignal,
signal.SIGUSR1: msgthread
},pidfile=daemon.pidfile.PIDLockFile('/tmp/controller.pid')):
# If making a daemon that is to be run as root, put the pidfile in /var/run, or maybe the users home dir
do_work()
if __name__ == "__main__":
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment