--- /dev/null
+#!/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',
+))
+
+
+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()
+ if (path == '/[aio]' or
+ path.startswith('/SYSV') or
+ path.startswith('/dev/shm/')):
+ 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/snmpd': 'snmpd',
+ '/usr/sbin/sshd': 'sshd',
+ '/usr/sbin/syslog-ng': 'syslog-ng',
+ '/usr/sbin/xinetd': 'xinetd',
+ '/usr/bin/distccd': 'distccd',
+ '/usr/bin/monit': 'monit',
+ '/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(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]))
+ os.kill(pid, signal.SIGTERM)
+ for init in restart:
+ print('restarting %s' % init)
+ os.system('/etc/init.d/%s -q restart' % init)
+
+ 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__)
+ return parser
+
+
+def main(argv):
+ parser = get_parser()
+ opts = parser.parse_args(argv)
+
+ svcs = find_svcs()
+ svcs = auto_restart(svcs)
+ summarize(svcs)
+
+
+if __name__ == '__main__':
+ exit(main(sys.argv[1:]))