#!/dcs/bin/python # uid ----- YPdomain --- password database entry # \ \ # \ -- password database entry # \ # -- YPdomain --- password database entry # \ # ------------- password database entry # # ^ ^ ^ # | | | # 1 2 3 # # The above reflects that a given uid can have replications between # YPdomain's, as well as within YPdomains. # # Level 1 (uid) is done with a dictionary. # Level 2 (YPdomain) is also done with a dictionary # Level 3 (pwent) is done with a list import sys import string import posix least_uid = 100 def test(): print 'hi' uid = {} domain = {} pwent1 = [ 'a', 'b' ] pwent2 = [ 'c', 'd' ] domain['YPee'] = pwent1 domain['YPmae'] = pwent2 uid[0] = domain print uid def usage(): sys.stderr.write('To use:\n') sys.stderr.write('\tCreate an empty directory.\n') sys.stderr.write('\tIn this directory create one file per YP domain.\n') sys.stderr.write('\tFor each domain A, create A-paths which accepts a\n') sys.stderr.write('\thomedir in argv[1] and outputs a modified homedir\n') sys.stderr.write('\ton stdout. Make sure the -paths scripts are 755.\n') sys.stderr.write('\tThen "uid-merge domaina domainb domainc"\n') sys.stderr.write('\tYou are expected to manually resolve username\n') sys.stderr.write('\tconflicts in the respective domain files.\n') sys.stderr.write('\tFirst column of output tags what kind of info the\n') sys.stderr.write('\tline holds: pwent or chown. You can easily sed\n') sys.stderr.write('\tthese into a new password file and input to chowns\n') sys.stderr.write('\tby stripping off the first word and space.\n') sys.exit() def main(): if len(sys.argv) >= 100: sys.stderr.write('too many domains\n') sys.exit(1) if len(sys.argv) == 1 or (sys.argv[1:] and sys.argv[1] == '-h'): usage() find_username_conflicts() entries_at_uid = bring_in_uid_tree() resolve_uid_conflicts(entries_at_uid) output_new_database(entries_at_uid) # the sense of this is inverted in output_new_database()! def bother_with_entry(fields): # IE, don't do system accounts if string.atoi(fields[2]) >= least_uid: return 1 else: return 0 def find_username_conflicts(): username_used = {} conflict_found = 0 for domainname in sys.argv[1:]: file = open(domainname,'r') while 1: pwent = file.readline() if not pwent: break fields = string.splitfields(pwent,':') if bother_with_entry(fields): if username_used.has_key(fields[0]): conflict_found = 1 sys.stderr.write('username conflict: '+fields[0]+'\n') else: username_used[fields[0]] = 1 file.close() if conflict_found <> 0: sys.exit(1) def bring_in_uid_tree(): entries_at_uid = {} prefix = chr(ord('A') - 1) for domainname in sys.argv[1:]: prefix = chr(ord(prefix) + 1) file = open(domainname,'r') domainname = prefix + ' ' + domainname while 1: pwent = file.readline() if not pwent: break fields = string.splitfields(pwent,':') if bother_with_entry(fields): uid = string.atoi(fields[2]) if entries_at_uid.has_key(uid): # already something at this uid if entries_at_uid[uid].has_key(domainname): # already something at this uid and this domain entries_at_uid[uid][domainname].append(pwent) else: # already something at this uid, nothing at this domain entries_at_uid[uid][domainname] = [ pwent ] else: # nothing at this uid yet entries_at_uid[uid] = {} entries_at_uid[uid][domainname] = [ pwent ] file.close() return entries_at_uid def find_free_uid(entries_at_uid): possibility = least_uid while 1: if entries_at_uid.has_key(possibility): possibility = possibility + 1 else: return possibility def resolve_uid_conflicts(entries_at_uid): for old_uid in entries_at_uid.keys(): domains = entries_at_uid[old_uid].keys() domains.sort() if len(domains) >= 2: for domain in domains[1:]: new_uid = find_free_uid(entries_at_uid) entries_at_uid[new_uid] = {} entries_at_uid[new_uid][domain] = entries_at_uid[old_uid][domain] del entries_at_uid[old_uid][domain] print 'chown',string.split(domain)[1],old_uid,new_uid def tailor_pwent(uid,domain,pwent): fields = string.splitfields(pwent,':') fields[2] = str(uid) file = posix.popen('./'+string.split(domain)[1]+'-paths '+fields[5],'r') fields[5] = file.readline()[:-1] # don't want the newline file.close() return string.joinfields(fields,':') def output_new_database(entries_at_uid): # output entries with uid < 100 in the first domain file = open(sys.argv[1],'r') while 1: pwent = file.readline() if not pwent: break fields = string.splitfields(pwent,':') if not bother_with_entry(fields): sys.stdout.write('pwent '+pwent) # output the shuffled stuff; uid's >= least_uid uids = entries_at_uid.keys() uids.sort() for uid in uids: domain = entries_at_uid[uid].keys()[0] for pwent in entries_at_uid[uid][domain]: new_pwent = tailor_pwent(uid,domain,pwent) sys.stdout.write('pwent '+new_pwent) main()