modemd: add SIGHUP handler for config reload

On SIGHUP, modemd stops all ModemThreads, re-runs sim-setup, then
restarts the modem threads from fresh sysrepo config.  The pidfile is
touched with os.utime() once the reload is complete, satisfying Finit's
default pidfile-based notify so confd can signal a reload and wait for
it to finish before proceeding.

This allows operator and APN changes to take effect without a full
daemon restart.

Also extract start_modem_threads() to remove the duplicated thread
startup loop shared between initial startup and reload, and align
sighandler() to use isinstance(th, ModemThread) consistently with the
rest of the thread-management code.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-06-07 20:51:02 +02:00
parent 1168c6c8e2
commit f41898d325
+46 -19
View File
@@ -14,10 +14,12 @@ import os
import sys
import ipaddress
rundir = "/run/modemd"
smsdir = "/var/sms"
debug = False
rundir = "/run/modemd"
smsdir = "/var/sms"
pidfile = "/run/modemd.pid"
debug = False
threads = []
reload_event = threading.Event()
syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_SYSLOG)
@@ -1605,12 +1607,11 @@ def sighandler(signum, frame):
for th in threads:
th.stop()
# wait for modem threads
timeout = 0
while timeout < 3:
exited = True
for th in threads:
if th.name.startswith("modem") and not th.exited:
if isinstance(th, ModemThread) and not th.exited:
exited = False
if exited:
break
@@ -1623,6 +1624,22 @@ def sighandler(signum, frame):
fatal("Exiting on signal %d" % signum)
def start_modem_threads(cfgs):
started = []
for cfg in cfgs:
try:
th = ModemThread(cfg)
th.start()
started.append(th)
except Exception as e:
err("Failed to start modem%d thread: %s" % (cfg["index"], e))
return started
def sighup_handler(signum, frame):
reload_event.set()
def main():
global debug
syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON)
@@ -1647,8 +1664,10 @@ def main():
mkdir(rundir)
mkdir(smsdir)
fwrite(pidfile, str(os.getpid()) + "\n")
signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGHUP, sighup_handler)
try:
th = RpcThread()
@@ -1673,24 +1692,32 @@ def main():
if not runcmd(['/usr/libexec/modemd/sim-setup']):
fatal("Unable to setup SIMs")
for cfg in modems:
th = None
try:
th = ModemThread(cfg)
th.start()
except Exception as e:
if debug:
print(e)
fatal("Modem %d thread caught an exception" % cfg["index"])
if th:
threads.append(th)
threads.extend(start_modem_threads(modems))
if len(threads) == 0:
fatal("No modem threads are running")
[th.join() for th in threads]
while True:
if not reload_event.wait(timeout=60):
continue
reload_event.clear()
fatal("Abnormal exit")
info("Reloading configuration")
modem_threads = [th for th in threads if isinstance(th, ModemThread)]
for th in modem_threads:
th.stop()
for th in modem_threads:
th.join(timeout=5)
if th.is_alive():
err("modem%d thread did not stop, orphaning" % th.index)
threads[:] = [th for th in threads if th not in modem_threads]
runcmd(['/usr/libexec/modemd/sim-setup'])
new_modems = load_config()
threads.extend(start_modem_threads(new_modems))
os.utime(pidfile, None)
info("Reload complete")
if __name__ == "__main__":