I created a service to run a Python program and added some lines of code to create a lock to avoid launching it twice.
Unfortunately I don't know how to configure the service to stop the running program correctly. When running the stop command it doesn't delete the lock, then I can't start the service anymore. If I execute the program myself via CLI and exit with a Ctrl+C, the lock is deleted.
I've read the manual about KillMode, ExecStop and Signal. My understanding is that the default configuration was the one I needed.
Any help please?
Main program
if __name__ == '__main__':
#Creating lock to avoid launching program twice
lock = pathlib.Path("program.lock")
if not lock.exists():
lock_acquired_on = datetime.now()
with open('program.lock', 'w') as lock:
lock.write(f'Lock acquired on {lock_acquired_on}')
logger.info('Added lock file to avoid running the program twice.')
try:
while True:
#Doing stuff here
except KeyboardInterrupt:
close_program() #Close other threads
#Removing the lock file
os.remove(pathlib.Path("program.lock"))
else:
with open('program.lock', 'r') as lock:
lock_acquisition_time = str(lock.readlines()[0])
logger.info('Programme Maquette Status is already running.')
logger.info(lock_acquisition_time)
Service
[Unit]
Description=Programme Maquette IoT End-to-End
After=multi-user.target
Conflicts=getty@tty1.service
[Service]
WorkingDirectory=/home/pi/Documents/ProductionMaquette
Type=simple
ExecStart=/usr/local/bin/python3.8 /home/pi/Documents/ProductionMaquette/Lo_main.py
StandardInput=tty-force
[Install]
WantedBy=multi-user.target
Systemd sends the SIGTERM to the process - so you need to handle that.
So following little example uses the a signal handler for SIGTERM
to clean up a file. Actually it uses atexit to clean up the file, as that handles standard exit conditions as well and a signal handler to initiate in "normal" closing down of the process on receiving the SIGTERM
signal
import atexit
import signal
import os
locking_file = "/var/lock/my_service.lock"
if __name__ == '__main__':
def clean_lock():
# atexit handler to clean up a file
os.remove(locking_file)
def signal_term_handler(sigNum, frame):
# on receiving a signal initiate a normal exit
raise SystemExit('terminating')
with open("test_file.lock", "w") as lock:
while True:
lock.write("x")
time.sleep(10)
# register the cleanup handler
atexit.register(clean_lock)
# register the signal handler
signal.signal(signal.SIGTERM, signal_term_handler)
As a note: there is a file locking library you might want to look at:https://pypi.org/project/filelock/ as that should handle that use case as well.
It is not only testing for presents of a file but uses the os-file locking mechanism. I.e. not only the existence of the file is tested - but if it can be locked as well. In effect that means even if the file still exists but the previous process died it is not a problem, as the file is no longer locked.