/usr/sbin/rainbow-gc is in rainbow 0.8.7-2.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | #!/usr/bin/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 make_reporter, enable_verbose_tracebacks
trace = enable_verbose_tracebacks()
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_x11_auth', 'uid_to_x11_cookie', 'uid_to_x11_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())
|