From cef0f8d8bc24ccd0051c6070b749b675dabab6d9 Mon Sep 17 00:00:00 2001 From: Fredrik Eriksson Date: Sat, 27 Jul 2024 13:15:26 +0200 Subject: [PATCH] fix configurable process->service mapping on systemd --- sau/gentoo.py | 19 ++++++----- sau/services.py | 86 ++++++++++++++++++++++++++++--------------------- 2 files changed, 59 insertions(+), 46 deletions(-) diff --git a/sau/gentoo.py b/sau/gentoo.py index f3201c8..b9a6a9b 100644 --- a/sau/gentoo.py +++ b/sau/gentoo.py @@ -4,6 +4,7 @@ import re import sau import sau.helpers +import sau.services EIX_SYNC_PATH='/usr/bin/eix-sync' RC_SERVICE_PATH='/sbin/rc-service' @@ -22,11 +23,10 @@ slot_re = re.compile(r'^(\(~\))?([^\(]+)(\([^\)]+\))$') def identify_service_from_bin(exe): log = logging.getLogger(sau.LOGNAME) - with open('/proc/1/comm', 'r') as f: - if f.readline().strip() == 'systemd': - init_script_re = re.compile(r'[^/]*(.*)\.service$') - else: - init_script_re = re.compile(r'/etc/init\.d/(.*)') + if sau.services.on_systemd(): + init_script_re = re.compile(r'[^/]*(.*)\.service$') + else: + init_script_re = re.compile(r'/etc/init\.d/(.*)') cmd = [ EQUERY_PATH, '-Cq', 'b', exe ] ret, out, err = sau.helpers.exec_cmd(cmd) @@ -61,11 +61,10 @@ def identify_service_from_bin(exe): def restart_service(service): log = logging.getLogger(sau.LOGNAME) - with open('/proc/1/comm', 'r') as f: - if f.readline().strip() == 'systemd': - cmd = [ SYSTEMCTL, 'restart', service ] - else: - cmd = [ RC_SERVICE_PATH, service, 'restart' ] + if sau.services.on_systemd(): + cmd = [ SYSTEMCTL, 'restart', service ] + else: + cmd = [ RC_SERVICE_PATH, service, 'restart' ] ret, out, err = sau.helpers.exec_cmd(cmd) if ret != 0: diff --git a/sau/services.py b/sau/services.py index a47e73f..121e8d3 100644 --- a/sau/services.py +++ b/sau/services.py @@ -75,6 +75,16 @@ def _get_processes(): return check_procs +# Just return True if system is running on systemd +def on_systemd(): + try: + init_proc = psutil.Process(pid=1) + if init_proc.name() == 'systemd': + return True + except psutil.NoSuchProcess: + pass + return False + def restart_services(): log = logging.getLogger(sau.LOGNAME) platform = sau.platforms.get_platform() @@ -84,14 +94,6 @@ def restart_services(): # wait before the second test time.sleep(5) - on_systemd = False - try: - init_proc = psutil.Process(pid=1) - if init_proc.name() == 'systemd': - on_systemd = True - except psutil.NoSuchProcess: - pass - # perform a second check to remove potential false positives service_procs = set() retest_procs = set() @@ -105,7 +107,7 @@ def restart_services(): except (psutil.NoSuchProcess, psutil.ZombieProcess, psutil.AccessDenied): # either of the above exceptions means the process has quit continue - if on_systemd: + if on_systemd(): service_procs.add(proc) else: parent = _get_top_parent(proc) @@ -128,35 +130,18 @@ def restart_services(): log.debug('{} died before it could be restarted'.format(proc)) continue - if on_systemd: - if proc.pid == 1: - log.info("Upgrade of systemd detected; doing daemon-reexec") - ret, _out, _err = sau.helpers.exec_cmd([ '/usr/bin/systemctl', 'daemon-reexec' ]) - continue - ret, unit, err = sau.helpers.exec_cmd([ '/usr/bin/systemctl', 'whoami', f'{proc.pid}' ]) - unit = unit.strip() - name, unit_type = unit.split('.') - if ret != 0: - log.debug(f'Non-success ({ret}) when checking unit for process: {err}') - continue - elif unit_type != 'service': - log.warning(f'not restarting non-service unit "{unit}"; owner of {proc}') - elif name.startswith('user@'): - log.warning(f'Not restarting user service {unit}; please log out and log in again') - else: - _ret, enabled, _err = sau.helpers.exec_cmd([ '/usr/bin/systemctl', 'is-enabled', unit ]) - enabled = enabled.strip() - if enabled not in ('enabled', 'static'): - log.warning(f'Unit {name}.service has enable status: {enabled} - will only restart "enabled" services') - else: - service_name = name - else: - service_name = _get_service_from_proc(proc) + service_name = _get_service_from_proc(proc) if not service_name: log.warning('no service for process {}'.format(proc)) recommend_restart = True continue + if service_name == 'systemd': + log.info("Upgrade of systemd detected; doing daemon-reexec") + sau.helpers.exec_cmd([ '/usr/bin/systemctl', 'daemon-reexec' ]) + continue + elif service_name == '@ignore': + continue services[proc_name] = service_name processes[service_name] = [proc] @@ -209,21 +194,50 @@ def _get_service_restart_policy(service): def _get_service_from_proc(proc): conf = sau.config platform = sau.platforms.get_platform() - proc = _get_top_parent(proc) + if not on_systemd(): + proc = _get_top_parent(proc) log = logging.getLogger(sau.LOGNAME) try: proc_name = proc.name() service_exe = proc.exe() except (psutil.NoSuchProcess, psutil.ZombieProcess, psutil.AccessDenied): log.debug('{} died'.format(proc)) - return None + return '@ignore' service_name = conf.get('processes', proc_name, fallback=None) + log.debug(f'configuration of process "{proc_name}" in config: "{service_name}"') if service_name == '': log.debug('Ignoring process {}'.format(proc)) - return None + return '@ignore' if not service_name: + # Systemd has it's own way... + if on_systemd(): + if proc.pid == 1: + return 'systemd' + ret, unit, err = sau.helpers.exec_cmd([ '/usr/bin/systemctl', 'whoami', f'{proc.pid}' ]) + unit = unit.strip() + name, unit_type = unit.split('.') + if ret != 0: + log.debug(f'Non-success ({ret}) when checking unit for process: {err}') + return None + elif unit_type != 'service': + log.warning(f'not restarting non-service unit "{unit}"; owner of {proc}') + return None + elif name.startswith('user@'): + log.warning(f'Not restarting user service {unit}; please log out and log in again') + return None + else: + _ret, enabled, _err = sau.helpers.exec_cmd([ '/usr/bin/systemctl', 'is-enabled', unit ]) + enabled = enabled.strip() + if enabled not in ('enabled', 'static'): + log.warning(f'Unit {name}.service has enable status: {enabled} - will only restart "enabled" services') + return None + else: + return name + log.error(f'This should be an unreachable path when checking process {proc}') + return None + # if the exe file has been deleted since started, service_exe will be empty # and we'll have to guess if not service_exe: