- This topic has 11 replies, 4 voices, and was last updated 15 years, 1 month ago by
Chris George.
-
AuthorPosts
-
May 5, 2011 at 12:04 am #380695
Ben Urban
ParticipantI’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).
#!/usr/bin/python # Written by Ben Urban <burban@imf.org> """ 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])May 5, 2011 at 9:59 pm #380699dead2sin
ParticipantVery Nice! Its always a pain to put stuff into existing user’s folders, but that looks like it’ll do the trick 🙂
Nate
May 20, 2011 at 7:23 pm #380746Chris George
ParticipantSo, if I understand this correctly… say you want something to go into all users /LIbrary/Application Support/blah folder – you create /private/tmp/fillUsers/Library/Application Support/blah/, put everything into that folder, then run this as a postflight?
May 20, 2011 at 7:58 pm #380747Ben Urban
ParticipantYup!
I was working on a new version of this that can fill user home folders the next time they log in (using a LaunchAgent), but I haven’t had time to work on it recently. (It’s actually a lot more complicated than you would think to do this – you’ll see why when you see the new version of the script.)
May 20, 2011 at 8:04 pm #380748Rusty Myers
ParticipantVery nice! Thanks for sharing. Do you have your scripts in a version control system we can follow? Githib? Thanks!
May 20, 2011 at 8:30 pm #380749Ben Urban
ParticipantI’m afraid I don’t. I should start using one sometime soon…
May 20, 2011 at 9:28 pm #380750Ben Urban
ParticipantI took another look at the script, and I think I got it working, but I am not in a position to reasonably test it. Anyone care to try?
Note that currently all it does is output what it would do; it doesn’t actually do anything. You’ll have to edit out the extra return statements to make it actually do stuff.
I also went nuts with refactoring, partly to allow me to nullify the actual effects in favor of print statements.
#!/usr/bin/python # Written by Ben Urban <burban@imf.org> """ This script clones anything it finds in /tmp/fillUsers to all existing non-hidden users, as well as all user template localizations. It also sets up a LaunchAgent to automatically extract the data for all users that don't already have it. It is meant to be used as a postflight script for packages that need this functionality, mimicking and surpassing 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 srcDir = '/tmp/fillUsers' defaultNode = '/var/db/dslocal/nodes/Default' userTemplateRoot = '/System/Library/User Template' autofillDir = '/Library/Autofill' dataDir = os.path.join(autofillDir, 'Data') launchAgentsDir = '/Library/LaunchAgents' autofillReceiptsDir = 'Library/Autofill/Receipts' autofiller_sh = '''#!/bin/sh for f in "%(dataDir)s"/* do if ! [ -f "${HOME}/%(autofillReceiptsDir)s/${f%%%%.*}" ] then /usr/bin/tar -xpf "${f}" -C "${HOME}" --strip-components 1 fi done ''' autofiller_plist = '''<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>autofiller</string> <key>ProgramArguments</key> <array> <string>%(scriptPath)s</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> ''' def writeFile(path, contents): prepDirTree(os.path.dirname(path)) print '%s:' % dest(path) print '\t' + contents.replace('\n', '\n\t') return f = open(dest(path), 'w') f.write(contents) f.close() def prepDirTree(path, mode = 0777): print 'os.mkdirs(%s, 0%o)' % (dest(path), mode) return os.makedirs(dest(path), mode) def tarCreate(archive, srcFolder): print [ '/usr/bin/tar', '-cj', '-f', dest(archive), '-C', dest(os.path.dirname(srcFolder)), os.path.basename(srcFolder), ] return subprocess.check_call([ '/usr/bin/tar', '-cj', '-f', dest(archive), '-C', dest(os.path.dirname(srcFolder)), os.path.basename(srcFolder), ]) def tarExtract(archive, dstFolder, uid, gid): print [ '/usr/bin/tar', '-x', '-f', dest(archive), '-C', dest(dstFolder), '--strip-components', '1', ], (uid, gid) return subprocess.check_call( [ '/usr/bin/tar', '-x', '-f', dest(archive), '-C', dest(dstFolder), '--strip-components', '1', ], preexec_fn = lambda: (os.setuid(uid), os.setgid(gid)), ) def chmod(item, mode): print 'chmod(%s, 0%o)' % (dest(item), mode) return os.chmod(dest(item), mode) def chown_R(item, uid, gid): print ['/usr/sbin/chown', '-R', '%s:%s' % (uid, gid), dest(item)] return subprocess.check_call(['/usr/sbin/chown', '-R', '%s:%s' % (uid, gid), dest(item)]) def rm_rf(item): print ['/bin/rm', '-rf', dest(item)] return subprocess.check_call(['/bin/rm', '-rf', dest(item)]) def dest(path): """ Convert an absolute path into an absolute path within the mountpoint specified by destroot. Symlinks in the path are followed properly. If you pass a recursive link to this function, enjoy your infinite loop! """ path = os.path.normpath(path) if path.startswith(destroot): path = path.split(destroot, 1)[1] path = path.lstrip(os.path.sep).split(os.path.sep) while True: newpath = [] noLinks = True for part in path: subpath = os.path.join(*([destroot] + newpath + [part])) if os.path.islink(subpath): noLinks = False 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) if noLinks: break path = os.path.join(*newpath).lstrip(os.path.sep).split(os.path.sep) return os.path.normpath(os.path.join(destroot, *newpath)) 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 collectReachableHomeDirs(uidFilter = lambda uid: uid > 500): for line in dscl('ls', '/Users', 'UniqueID'): user, uid = line.split(' ', 1)[0], int(line.rsplit(' ', 1)[1]) if uidFilter(uid): gid = int(dsclread(os.path.join('/Users', user), 'PrimaryGroupID')) name = dsclread(os.path.join('/Users', user), 'RealName') home = dest(dsclread(os.path.join('/Users', user), 'NFSHomeDirectory')) if os.path.exists(home): yield (int(uid), int(gid), name, home) def collectUserTemplateDirs(languages = None): # None actually means all for lang in os.listdir(dest(userTemplateRoot)): if languages is None or lang in languages: yield ( 0, 0, 'template (%s)' % (lang, ), # This is cheating, but it works os.path.join(userTemplateRoot, lang), ) def setupAutofiller(launchAgentsDir, autofillDir, autofillReceiptsDir, dataDir): print "Setting up LaunchAgent for deferred fill" scriptPath = os.path.join(autofillDir, 'autofiller.sh') writeFile(scriptPath, autofiller_sh % locals()) chmod(scriptPath, 0755) writeFile(os.path.join(launchAgentsDir, 'autofiller.plist'), autofiller_plist % locals()) def makeArchive(srcDir, archive): print "Archiving autofill data" chown_R(srcDir, 0, 0) prepDirTree(dataDir) tarCreate(archive, srcDir) return archive def cleanUp(srcDir): print "Cleaning up %s" % (dest(srcDir), ) rm_rf(srcDir) def setupDeferredFill(srcDir, pkgid): setupAutofiller(launchAgentsDir, autofillDir, autofillReceiptsDir, dataDir) writeFile(os.path.join(srcDir, autofillReceiptsDir, pkgid), '') # Create empty file archive = makeArchive(srcDir, os.path.join(dataDir, pkgid + '.tar.bz2')) cleanUp(srcDir) return archive def fillDirs(src, dirs): for uid, gid, name, home in dirs: print "Filling %s for user %s" % (dest(home), name) tarExtract(src, home, uid, gid) def getPkgID(): import time, uuid return time.strftime('%Y-%m-%d %H:%M:%S ') + str(uuid.uuid1()) def main(dest): global destroot destroot = dest pkgid = getPkgID() src = setupDeferredFill(srcDir, pkgid) dirs = [] dirs += collectReachableHomeDirs() dirs += collectUserTemplateDirs() fillDirs(src, dirs) if __name__ == '__main__': import sys main(sys.argv[3] if len(sys.argv) > 3 else '/')May 21, 2011 at 5:08 am #380751Ben Urban
ParticipantHere’s a version you can use to test it:
#!/usr/bin/python # Written by Ben Urban <burban@imf.org> from __future__ import with_statement # in case we're in Python 2.5 """ This script clones anything it finds in /tmp/fillUsers to all existing non-hidden users, as well as all user template localizations. It also sets up a LaunchAgent to automatically extract the data for all users that don't already have it. It is meant to be used as a postflight script for packages that need this functionality, mimicking and surpassing 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 srcDir = '/tmp/fillUsers' defaultNode = '/var/db/dslocal/nodes/Default' userTemplateRoot = '/System/Library/User Template' autofillDir = '/Library/Autofill' dataDir = os.path.join(autofillDir, 'Data') launchAgentsDir = '/Library/LaunchAgents' autofillReceiptsDir = 'Library/Autofill/Receipts' autofiller_sh = '''#!/bin/sh for f in "%(dataDir)s"/* do if ! [ -f "${HOME}/%(autofillReceiptsDir)s/${f%%%%.*}" ] then /usr/bin/tar -xpf "${f}" -C "${HOME}" --strip-components 1 fi done ''' autofiller_plist = '''<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>autofiller</string> <key>ProgramArguments</key> <array> <string>%(scriptPath)s</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> ''' def writeFile(path, contents): path = dest(path) os.makedirs(os.path.dirname(path)) with open(path, 'w') as f: f.write(contents) def tarCreate(archive, srcFolder): os.makedirs(dest(os.path.dirname(archive))) subprocess.check_call([ '/usr/bin/tar', '-cj', '-f', dest(archive), '-C', dest(os.path.dirname(srcFolder)), os.path.basename(srcFolder), ]) def tarExtract(archive, dstFolder, uid, gid): subprocess.check_call( [ '/usr/bin/tar', '-x', '-f', dest(archive), '-C', dest(dstFolder), '--strip-components', '1', ], preexec_fn = lambda: (os.setuid(uid), os.setgid(gid)), ) def dest(path): """ Convert an absolute path into an absolute path within the mountpoint specified by destroot. Symlinks in the path are followed properly. If you pass a recursive link to this function, enjoy your infinite loop! """ path = os.path.normpath(path) if path.startswith(destroot): path = path.split(destroot, 1)[1] path = path.lstrip(os.path.sep).split(os.path.sep) while True: newpath = [] noLinks = True for part in path: subpath = os.path.join(*([destroot] + newpath + [part])) if os.path.islink(subpath): noLinks = False 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) if noLinks: break path = os.path.join(*newpath).lstrip(os.path.sep).split(os.path.sep) return os.path.normpath(os.path.join(destroot, *newpath)) 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 collectReachableHomeDirs(uidFilter = lambda uid: uid > 500): for line in dscl('ls', '/Users', 'UniqueID'): user, uid = line.split(' ', 1)[0], int(line.rsplit(' ', 1)[1]) if uidFilter(uid): gid = int(dsclread(os.path.join('/Users', user), 'PrimaryGroupID')) name = dsclread(os.path.join('/Users', user), 'RealName') home = dest(dsclread(os.path.join('/Users', user), 'NFSHomeDirectory')) if os.path.exists(home): yield (uid, gid, name, home) def collectUserTemplateDirs(languages = None): # None actually means all for lang in os.listdir(dest(userTemplateRoot)): if languages is None or lang in languages: yield ( 0, 0, 'template (%s)' % (lang, ), # This is cheating, but it works dest(os.path.join(userTemplateRoot, lang)), ) def setupAutofiller(**kwargs): print "Setting up LaunchAgent for deferred fill" scriptPath = os.path.join(kwargs['autofillDir'], 'autofiller.sh') writeFile(kwargs['scriptPath'], autofiller_sh % kwargs) os.chmod(dest(kwargs['scriptPath']), 0755) writeFile(os.path.join(kwargs['launchAgentsDir'], 'autofiller.plist'), autofiller_plist % kwargs) def makeArchive(srcDir, archive): print "Archiving autofill data" subprocess.check_call(['/usr/sbin/chown', '-R', '0:0', dest(srcDir)]) tarCreate(archive, srcDir) return archive def cleanUp(srcDir): srcDir = dest(srcDir) print "Cleaning up %s" % (srcDir, ) subprocess.check_call(['/bin/rm', '-rf', srcDir]) def setupDeferredFill(srcDir, pkgid): setupAutofiller( launchAgentsDir = launchAgentsDir, autofillDir = autoFillDir, autofillReceiptsDir = autofillReceiptsDir, dataDir = dataDir, ) writeFile(os.path.join(srcDir, autofillReceiptsDir, pkgid), '') # Create empty file archive = makeArchive(srcDir, os.path.join(dataDir, pkgid + '.tar.bz2')) cleanUp(srcDir) return archive def fillDirs(src, dirs): for uid, gid, name, home in dirs: print "Filling %s for user %s" % (dest(home), name) tarExtract(src, home, uid, gid) def getPkgID(): import time, uuid return time.strftime('%Y-%m-%d %H:%M:%S ') + str(uuid.uuid1()) def main(_destroot): global destroot destroot = _destroot pkgid = getPkgID() src = setupDeferredFill(srcDir, pkgid) dirs = [] dirs += collectReachableHomeDirs() dirs += collectUserTemplateDirs() fillDirs(src, dirs) if __name__ == '__main__': import sys main(sys.argv[3] if len(sys.argv) > 3 else '/')May 21, 2011 at 11:38 pm #380752Chris George
ParticipantMaybe I’m not following, but I don’t get the point of this. If the files get put into all the existing home directories and the user template localizations – what does the launchagent do?
May 23, 2011 at 11:23 am #380761Ben Urban
ParticipantThe LaunchAgent takes care of those home directories that are not reachable when the package is installed. This includes home directories protected by FileVault, as well as network home directories. (Thanks to Greg Neagle for the suggestion, by the way.)
I expect the LaunchAgent’s impact once the template is already installed (by the script or the LaunchAgent itself) should be negligible. Unfortunately, I can’t test this easily, and I’m not that familiar with writing a LaunchAgent by hand (I copied one that seemed to run with similar frequency). Feedback on that part would be appreciated.
I’m also not sure this is the best way to generate the package IDs, or to store them (empty files are okay, but there must be some useful information that can go in there…). Any thoughts?
May 25, 2011 at 2:53 am #380779Ben Urban
ParticipantHmm, this forum doesn’t allow edits?
This version fixes a bug that would cause the previous version of the script to consistently crash when it is run. I also renamed autofiller.plist to com.osxdeployment.autofiller.plist (I’m told “Nate won’t mind”), and added a description.
#!/usr/bin/python # Written by Ben Urban <burban@imf.org> from __future__ import with_statement # in case we're in Python 2.5 """ This script clones anything it finds in /tmp/fillUsers to all existing non-hidden users, as well as all user template localizations. It also sets up a LaunchAgent to automatically extract the data for all users that don't already have it. It is meant to be used as a postflight script for packages that need this functionality, mimicking and surpassing 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 srcDir = '/tmp/fillUsers' defaultNode = '/var/db/dslocal/nodes/Default' userTemplateRoot = '/System/Library/User Template' autofillDir = '/Library/Autofill' dataDir = os.path.join(autofillDir, 'Data') launchAgentsDir = '/Library/LaunchAgents' autofillReceiptsDir = 'Library/Autofill/Receipts' autofiller_sh = '''#!/bin/sh for f in "%(dataDir)s"/* do if ! [ -f "${HOME}/%(autofillReceiptsDir)s/${f%%%%.*}" ] then /usr/bin/tar -xpf "${f}" -C "${HOME}" --strip-components 1 fi done ''' autofiller_plist = '''<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.osxdeployment.autofiller</string> <key>ProgramArguments</key> <array> <string>%(scriptPath)s</string> </array> <key>RunAtLoad</key> <true/> <key>ServiceDescription</key> <string>Automatically fills the home folder at login, using the tarballs in from %(dataDir).</string> </dict> </plist> ''' def writeFile(path, contents): path = dest(path) os.makedirs(os.path.dirname(path)) with open(path, 'w') as f: f.write(contents) def tarCreate(archive, srcFolder): os.makedirs(dest(os.path.dirname(archive))) subprocess.check_call([ '/usr/bin/tar', '-cj', '-f', dest(archive), '-C', dest(os.path.dirname(srcFolder)), os.path.basename(srcFolder), ]) def tarExtract(archive, dstFolder, uid, gid): subprocess.check_call( [ '/usr/bin/tar', '-x', '-f', dest(archive), '-C', dest(dstFolder), '--strip-components', '1', ], preexec_fn = lambda: (os.setuid(uid), os.setgid(gid)), ) def dest(path): """ Convert an absolute path into an absolute path within the mountpoint specified by destroot. Symlinks in the path are followed properly. If you pass a recursive link to this function, enjoy your infinite loop! """ path = os.path.normpath(path) if path.startswith(destroot): path = path.split(destroot, 1)[1] path = path.lstrip(os.path.sep).split(os.path.sep) while True: newpath = [] noLinks = True for part in path: subpath = os.path.join(*([destroot] + newpath + [part])) if os.path.islink(subpath): noLinks = False 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) if noLinks: break path = os.path.join(*newpath).lstrip(os.path.sep).split(os.path.sep) return os.path.normpath(os.path.join(destroot, *newpath)) 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 collectReachableHomeDirs(uidFilter = lambda uid: uid > 500): for line in dscl('ls', '/Users', 'UniqueID'): user, uid = line.split(' ', 1)[0], int(line.rsplit(' ', 1)[1]) if uidFilter(uid): gid = int(dsclread(os.path.join('/Users', user), 'PrimaryGroupID')) name = dsclread(os.path.join('/Users', user), 'RealName') home = dest(dsclread(os.path.join('/Users', user), 'NFSHomeDirectory')) if os.path.exists(home): yield (uid, gid, name, home) def collectUserTemplateDirs(languages = None): # None actually means all for lang in os.listdir(dest(userTemplateRoot)): if languages is None or lang in languages: yield ( 0, 0, 'template (%s)' % (lang, ), # This is cheating, but it works dest(os.path.join(userTemplateRoot, lang)), ) def setupAutofiller(**kwargs): print "Setting up LaunchAgent for deferred fill" kwargs['scriptPath'] = os.path.join(kwargs['autofillDir'], 'autofiller.sh') writeFile(kwargs['scriptPath'], autofiller_sh % kwargs) os.chmod(dest(kwargs['scriptPath']), 0755) writeFile(os.path.join(kwargs['launchAgentsDir'], 'com.osxdeployment.autofiller.plist'), autofiller_plist % kwargs) def makeArchive(srcDir, archive): print "Archiving autofill data" subprocess.check_call(['/usr/sbin/chown', '-R', '0:0', dest(srcDir)]) tarCreate(archive, srcDir) return archive def cleanUp(srcDir): srcDir = dest(srcDir) print "Cleaning up %s" % (srcDir, ) subprocess.check_call(['/bin/rm', '-rf', srcDir]) def setupDeferredFill(srcDir, pkgid): setupAutofiller( launchAgentsDir = launchAgentsDir, autofillDir = autoFillDir, autofillReceiptsDir = autofillReceiptsDir, dataDir = dataDir, ) writeFile(os.path.join(srcDir, autofillReceiptsDir, pkgid), '') # Create empty file archive = makeArchive(srcDir, os.path.join(dataDir, pkgid + '.tar.bz2')) cleanUp(srcDir) return archive def fillDirs(src, dirs): for uid, gid, name, home in dirs: print "Filling %s for user %s" % (dest(home), name) tarExtract(src, home, uid, gid) def getPkgID(): import time, uuid return time.strftime('%Y-%m-%d %H:%M:%S ') + str(uuid.uuid1()) def main(_destroot): global destroot destroot = _destroot pkgid = getPkgID() src = setupDeferredFill(srcDir, pkgid) dirs = [] dirs += collectReachableHomeDirs() dirs += collectUserTemplateDirs() fillDirs(src, dirs) if __name__ == '__main__': import sys main(sys.argv[3] if len(sys.argv) > 3 else '/')September 26, 2011 at 1:42 pm #381228Chris George
ParticipantHmm. Old thread bump time!
I just realized that, unless I’m wrong, this script doesn’t account for network users, such as those from Active Directory/Open Directory, that have local home directories, as it is reading the list of users from dscl on the local machine and only applying the changes to those users. I couldn’t figure out why an installer I made using this postflight didn’t work in our labs, and I think now that’s because we don’t have any local users aside from the admin account.
Am I wrong?
-
AuthorPosts
- You must be logged in to reply to this topic.