User Filler Postflight Script
I've been told I would instantly become very popular if I posted this script here. I wish to put that theory to the test. Enjoy.
This script should be able to handle installing to any volume (and it will figure out and fill the users already present on the target volume, if applicable). It can also compensate for unusual home directory locations (/Volumes/Macintosh HD/var/root comes to mind, where /Volumes/Macintosh HD/var points to /private/var; it can handle this situation correctly, though it skips the root user because its UID is below 500).
[code]
#!/usr/bin/python
# Written by Ben Urban
"""
This script clones anything it finds in /private/tmp/fillUsers to all existing
non-hidden users, as well as all user template localizations. It is meant to be
used as a postflight script for packages that need this functionality, mimicking
the functionality available in JAMF's Casper Suite for DMG packages.
It requires Python 2.5 or higher (thus, Mac OS X 10.5 or later).
"""
import os, subprocess, sys
fsroot = sys.argv[3] if len(sys.argv) > 3 else '/'
src = os.path.join(fsroot, 'private/tmp/fillUsers')
defaultNode = os.path.join(fsroot, 'private/var/db/dslocal/nodes/Default')
def dscl(cmd, path, *args):
# Not sure why -f can't be used directly with any commands...
path = os.path.join('/Local/Target/', path.lstrip(os.path.sep))
cmd = ' '.join((cmd, path) + args)
result = subprocess.Popen(
['/usr/bin/dscl', '-q', '-f', defaultNode],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
).communicate(cmd)[0]
result = result.rstrip('\n').split('\n')
del result[0] # 'Entering interactive mode... (Type "help" for commands)'
return result
def dsclread(path, prop):
result = dscl('read', path, prop)
result = ''.join(result).split('%s: ' % (prop, ), 1)[1]
return result
def makeAbsPath(path):
path = os.path.normpath(path).lstrip(os.path.sep).split(os.path.sep)
newpath = []
for part in path:
subpath = os.path.join(*([fsroot] + newpath + [part]))
if os.path.islink(subpath):
link = os.readlink(subpath)
if link.startswith(os.path.sep):
newpath = [os.path.normpath(link.lstrip(os.path.sep))]
else:
newpath.append(os.path.normpath(link))
else:
newpath.append(part)
return os.path.normpath(os.path.join(fsroot, *newpath))
users = []
for line in dscl('ls', '/Users', 'UniqueID'):
user, uid = line.split(' ', 1)[0], line.rsplit(' ', 1)[1]
if int(uid) > 500:
gid = dsclread(os.path.join('/Users', user), 'PrimaryGroupID')
name = dsclread(os.path.join('/Users', user), 'RealName')
home = dsclread(os.path.join('/Users', user), 'NFSHomeDirectory')
users.append((uid, gid, name, home))
for lang in os.listdir(os.path.join(fsroot, 'System/Library/User Template')):
users.append((
'0',
'0',
'template (%s)' % (lang, ), # This is cheating, but it works
os.path.join('/System/Library/User Template/', lang),
))
for uid, gid, name, home in users:
home = makeAbsPath(home)
print "Filling %s for user %s" % (home, name)
subprocess.check_call(['/usr/sbin/chown', '-R', uid + ':' + gid, src])
subprocess.check_call(['/usr/bin/ditto', src, home])
print "Cleaning up %s" % (src, )
subprocess.check_call(['/bin/rm', '-rf', src])
[/code]