From f41898d325152c1d207ddcb77530b2c4a19d3eac Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 10 May 2026 07:48:08 +0200 Subject: [PATCH] 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 --- src/modemd/modemd/__init__.py | 65 +++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/src/modemd/modemd/__init__.py b/src/modemd/modemd/__init__.py index b3409453..35287fe4 100644 --- a/src/modemd/modemd/__init__.py +++ b/src/modemd/modemd/__init__.py @@ -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__":