Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/bin/rainbow-gc
diff options
context:
space:
mode:
Diffstat (limited to 'bin/rainbow-gc')
-rwxr-xr-xbin/rainbow-gc101
1 files changed, 101 insertions, 0 deletions
diff --git a/bin/rainbow-gc b/bin/rainbow-gc
new file mode 100755
index 0000000..82a5cd7
--- /dev/null
+++ b/bin/rainbow-gc
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+import sys
+
+from subprocess import call, check_call, CalledProcessError
+from os import listdir, unlink
+from os.path import join, isdir, islink, exists
+from optparse import OptionParser
+from glob import glob
+
+from rainbow.util import trace, make_reporter
+
+def active_uid(uid):
+ cmd = ['/usr/bin/pgrep', '-U', uid]
+ ret = call(cmd, stdout=open('/dev/null'))
+ if ret in (0, 1):
+ return ret == 0
+ raise CalledProcessError(ret, cmd)
+
+def sticky_uid(spool, uid):
+ return exists(join(spool, "sticky_uids", uid))
+
+def gc_uid(log, spool, uid):
+ """This function conservatively attempts to garbage-collect stale uid
+ reservations.
+ """
+
+ # XXX: D-Bus caches passwd-db data!
+ # XXX: D-Bus uses a fixed 1k passwd buffer - be careful with long paths &
+ # comments
+
+ reservation = join(spool, 'uid_pool', uid)
+ assert not isdir(reservation) and not islink(reservation)
+
+ # XXX: We perform several execv()'s as root based on strings derived from
+ # this 'uid' parameter, which originates as a file-name in a user-writable
+ # directory. Better ideas for input validation would be welcome.
+ uid_num = int(uid)
+ assert uid_num >= 1000 and uid_num <= 65534 # XXX: magic numbers from util/spool.py
+
+ if active_uid(uid) or sticky_uid(spool, uid):
+ log(1, "skipped uid %s", uid)
+ return
+
+ for table in ('uid_to_instance_dir', 'uid_to_home_dir', 'uid_to_gid', 'uid_to_xephyr_auth', 'uid_to_xephyr_cookie', 'uid_to_xephyr_display'):
+ row = join(spool, table, uid)
+ # NB: it is important that rm -rf doesn't follow links. <MS>
+ cmd = ['/bin/rm', '-r', '-f', row]
+ log(2, "%s", ' '.join(cmd))
+ check_call(cmd)
+
+ for row in glob(join(spool, 'gid_to_members', '*', uid)):
+ # NB: it is important that rm -rf doesn't follow links. <MS>
+ cmd = ['/bin/rm', '-r', '-f', row]
+ log(2, "%s", ' '.join(cmd))
+ check_call(cmd)
+
+ # So long as we unlink the reservation last, we run no risk of seeing inconsistency
+ unlink(reservation)
+ log(1, "cleaned uid %s", uid)
+
+def gc_spool(log, spool):
+ ret = 0
+ uspool = join(spool, 'uid_pool')
+ if exists(uspool) and isdir(uspool):
+ for maybe_uid in listdir(uspool):
+ try: gc_uid(log, spool, maybe_uid)
+ except KeyboardInterrupt:
+ raise
+ except:
+ trace()
+ ret = 1
+ else:
+ log(1, "Skipping spool %s", spool)
+ ret = 1
+ return ret
+
+def main():
+ sys.excepthook = trace
+
+ parser = OptionParser(version='0.1')
+ parser.add_option('-v', '--verbose', default=0, action='count',
+ help='Verbosity. Repeat for more verbose output.')
+ parser.add_option('-q', '--quiet', default=False, action='store_true',
+ help='Quiet. Disable all output.')
+ parser.add_option('-s', '--spool', default="/var/spool/rainbow/2",
+ help='Location of the rainbow spool.')
+
+ opts, _ = parser.parse_args()
+
+ report = make_reporter(opts.verbose, opts.quiet, sys.stdout)
+
+ def check_spool(opts):
+ assert exists(opts.spool) and isdir(opts.spool)
+ return opts.spool
+
+ spool = check_spool(opts)
+ return gc_spool(report, spool)
+
+if __name__ == "__main__":
+ exit(main())