#!/usr/bin/python # -*- coding: utf-8 -*- """Script for restarting services that refer to old/deleted libs.""" from __future__ import print_function import argparse import glob import os import signal import sys import time # Set of paths that are "OK" and safe to ignore. IGNORE_PATHS = set(( '/dev/zero', '/etc/ld.so.cache', '/usr/share/icons/Adwaita/icon-theme.cache', '/usr/share/icons/gnome/icon-theme.cache', '/usr/share/icons/hicolor/icon-theme.cache', '/usr/share/mime/mime.cache', )) def find_svcs(): """Find all programs w/deleted paths.""" svcs = {} for pid in os.listdir('/proc'): try: pid_nr = int(pid) except ValueError: continue map = '/proc/%s/maps' % pid if not os.path.exists(map): print('skipping %s' % pid) continue old_paths = set() for line in open(map): if not line.endswith(' (deleted)\n'): continue # b71c7000-b7307000 rw-s 00000000 00:04 17024337 /dev/zero (deleted) addr, perm, offset, dev, inode, path = line.split(' ', 5) # Handle paths with spaces. path = path.lstrip().rsplit(' ', 2)[0] if (path == '/[aio]' or path.startswith('/memfd:') or path.startswith('/SYSV') or path.startswith('/dev/shm/') or path.startswith('/tmp/')): continue old_paths.add(path) old_paths -= IGNORE_PATHS if not old_paths: continue cmdline = open('/proc/%s/cmdline' % pid).read().split('\0') try: while True: cmdline.remove('') except ValueError: pass svcs[pid_nr] = { 'cmdline': cmdline, 'old_paths': old_paths, } return svcs # Mapping of known programs to their init.d scripts. SERVICES = { '/usr/sbin/acpid': 'acpid', '/usr/sbin/apache2': 'apache2', '/usr/sbin/atd': 'atd', '/usr/sbin/bacula-fd': 'bacula-fd', '/usr/sbin/cron': 'vixie-cron', '/usr/sbin/crond': 'dcron', '/usr/sbin/ntpd': 'ntpd', '/usr/sbin/snmpd': 'snmpd', '/usr/sbin/sshd': 'sshd', '/usr/sbin/syslog-ng': 'syslog-ng', '/usr/sbin/xinetd': 'xinetd', '/usr/bin/daisydog': 'daisydog', '/usr/bin/distccd': 'distccd', '/usr/bin/monit': 'monit', '/usr/bin/rsync': 'rsyncd', '/usr/sbin/smbd': 'samba', '/usr/bin/stunnel': 'stunnel', '/usr/bin/tor': 'tor', '/usr/bin/transmission-daemon': 'transmission-daemon', '/usr/bin/mediatomb': 'mediatomb', '/lib/systemd/systemd-udevd': 'udev', '/usr/libexec/nrpe': 'nrpe', '//usr/libexec/postfix/master': 'postfix', 'denyhosts.py': 'denyhosts', 'dropbear': 'dropbear', 'galileo': 'galileo', 'tlsdated': 'tlsdated', } def auto_restart(opts, svcs): kill = set() restart = set() for pid, svc in svcs.items(): if svc['cmdline'][0] == '/sbin/agetty': kill.add(pid) elif 'postgres:' in svc['cmdline'][0]: p = os.path.basename(glob.glob('/etc/runlevels/default/postgresql-*')[0]) restart.add(p) elif svc['cmdline'][0].startswith('metalog'): restart.add('metalog') else: prog = svc['cmdline'][0] if prog.startswith('/usr/bin/python'): prog = os.path.basename(svc['cmdline'][1]) init = SERVICES.get(prog) if init: restart.add(init) if kill or restart: for pid in kill: print('killing %s (%s)' % (pid, svcs[pid]['cmdline'][0])) if not opts.dryrun: os.kill(pid, signal.SIGTERM) for init in restart: print('restarting %s' % init) if not opts.dryrun: os.system('/etc/init.d/%s -q restart' % init) if not opts.dryrun: time.sleep(1) svcs = find_svcs() return svcs def summarize(svcs): print() sslh = False for pid in svcs.keys(): if svcs[pid]['cmdline'][0] == '/usr/sbin/sslh': del svcs[pid] sslh = True if sslh: print('sslh needs restart') print() for pid, svc in svcs.items(): print(pid, svc['cmdline']) print('\t%s' % '\n\t'.join(svc['old_paths'])) def get_parser(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-n', '--dry-run', dest='dryrun', action='store_true', help='Show what would be restarted (and why)') return parser def main(argv): parser = get_parser() opts = parser.parse_args(argv) svcs = find_svcs() svcs = auto_restart(opts, svcs) summarize(svcs) if __name__ == '__main__': exit(main(sys.argv[1:]))