Exemple #1
0
    def loadTemplateAndReturnError_(self, path):
        if path in self.loadedTemplates:
            return "%s included recursively" % path
        else:
            self.loadedTemplates.add(path)

        plist = NSDictionary.dictionaryWithContentsOfFile_(path)
        if not plist:
            error = "Couldn't read dictionary from plist at %s" % (path)
            LogWarning("%@", error)
            return error

        templateFormat = plist.get("TemplateFormat", "1.0")

        if templateFormat not in ["1.0", "1.1"]:
            LogWarning("Unknown format version %@", templateFormat)

        for key in plist.iterkeys():
            if key == "IncludeTemplates":
                for includePath in plist["IncludeTemplates"]:
                    LogInfo("Including template %@", includePath)
                    error = self.loadTemplateAndReturnError_(includePath)
                    if error:
                        return error
            elif key == "SourcePath":
                self.setSourcePath_(plist["SourcePath"])
            elif key == "ApplyUpdates":
                self.setApplyUpdates_(plist["ApplyUpdates"])
            elif key == "AdditionalPackages":
                if not self.setAdditionalPackages_(
                        plist["AdditionalPackages"]):
                    msg = "Additional packages failed verification"
                    if self.additionalPackageError:
                        msg += ":\n" + self.additionalPackageError
                    return msg
            elif key == "OutputPath":
                self.setOutputPath_(plist["OutputPath"])
            elif key == "VolumeName":
                self.setVolumeName_(plist["VolumeName"])
            elif key == "VolumeSize":
                self.setVolumeSize_(plist["VolumeSize"])
            elif key == "FinalizeAsrImagescan":
                self.setFinalizeAsrImagescan_(plist["FinalizeAsrImagescan"])
            elif key == "Filesystem":
                if plist["Filesystem"] not in ["apfs", "hfs"]:
                    error = "Unknown Filesystem value '%s'" % plist[
                        "Filesystem"]
                    return error
                self.setFilesystem_(plist["Filesystem"])
            elif key == "TemplateFormat":
                pass

            else:
                LogWarning("Unknown key '%@' in template", key)

        return None
Exemple #2
0
 def setFilesystem_(self, filesystem):
     LogDebug("Setting filesystem for workflow to '%@'", filesystem)
     if filesystem == "hfs":
         if IEDUtil.hostMajorVersion() > 13:
             LogWarning("Ignoring filesystem setting, only apfs is supported")
     elif filesystem == "apfs":
         if IEDUtil.hostMajorVersion() < 13:
             LogWarning("Ignoring filesystem setting, only hfs is supported")
     else:
         LogWarning("Unrecognized filesystem setting '%@'", filesystem)
     self._filesystem = filesystem
Exemple #3
0
 def getInstalledPkgSize_(cls, pkgPath):
     # For apps just return the size on disk.
     name, ext = os.path.splitext(pkgPath)
     if ext == u".app":
         return cls.getPackageSize_(pkgPath)
     # For packages try to get the size requirements with installer.
     pkgFileName = os.path.os.path.basename(pkgPath)
     tempdir = tempfile.mkdtemp()
     try:
         symlinkPath = os.path.join(tempdir, pkgFileName)
         os.symlink(pkgPath, symlinkPath)
         p = subprocess.Popen([u"/usr/sbin/installer",
                               u"-pkginfo",
                               u"-verbose",
                               u"-plist",
                               u"-pkg",
                               symlinkPath],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
         out, err = p.communicate()
     finally:
         try:
             shutil.rmtree(tempdir)
         except BaseException as e:
             LogWarning(u"Unable to remove tempdir: %@", unicode(e))
     # Try to handle some common scenarios when installer fails.
     if p.returncode == -11:
         LogWarning(u"Estimating package size since installer -pkginfo "
                    u"'%@' crashed", pkgPath)
         return cls.getPackageSize_(pkgPath) * 2
     elif p.returncode != 0:
         mountPoints = IEDMountInfo.getMountPoints()
         fsInfo = mountPoints[cls.findMountPoint_(pkgPath)]
         if not fsInfo[u"islocal"]:
             LogWarning(u"Estimating package size since installer -pkginfo "
                        u"failed and '%@' is on a remote (%@) filesystem",
                        pkgPath, fsInfo[u"fstypename"])
             return cls.getPackageSize_(pkgPath) * 2
         else:
             LogError(u"installer -pkginfo -pkg '%@' failed with exit code %d", pkgPath, p.returncode)
             return None
     outData = NSData.dataWithBytes_length_(out, len(out))
     plist, format, error = NSPropertyListSerialization.propertyListWithData_options_format_error_(outData,
                                                                                                   NSPropertyListImmutable,
                                                                                                   None,
                                                                                                   None)
     if not plist:
         LogError(u"Error decoding plist: %@", error)
         return None
     LogDebug(u"%@ requires %@", pkgPath, cls.formatBytes_(int(plist[u"Size"]) * 1024))
     return int(plist[u"Size"]) * 1024
Exemple #4
0
    def pruneAndCreateSymlinks(self, symlinks):
        LogInfo(u"Pruning cache")

        self.symlinks = symlinks

        # Create a reverse dictionary and a set of filenames.
        names = dict()
        filenames = set()
        for sha1, name in symlinks.iteritems():
            names[name] = sha1
            filenames.add(name)
            filenames.add(sha1)

        for item in os.listdir(self.updateDir):
            try:
                itempath = os.path.join(self.updateDir, item)
                if item not in filenames:
                    LogInfo(u"Removing %s" % item)
                    os.unlink(itempath)
            except OSError as e:
                LogWarning(u"Cache pruning of %s failed: %s" %
                           (item, unicode(e)))
        for sha1 in symlinks.iterkeys():
            sha1Path = self.cachePath_(sha1)
            linkPath = self.updatePath_(sha1)
            name = os.path.basename(linkPath)
            if os.path.exists(sha1Path):
                if os.path.lexists(linkPath):
                    if os.readlink(linkPath) == sha1:
                        LogInfo(u"Found %s -> %s" % (name, sha1))
                        continue
                    LogInfo(u"Removing stale link %s -> %s" %
                            (name, os.readlink(linkPath)))
                    try:
                        os.unlink(linkPath)
                    except OSError as e:
                        LogWarning(u"Cache pruning of %s failed: %s" %
                                   (name, unicode(c)))
                        continue
                LogInfo(u"Creating %s -> %s" % (name, sha1))
                os.symlink(sha1, linkPath)
            else:
                if os.path.lexists(linkPath):
                    LogInfo(u"Removing stale link %s -> %s" %
                            (name, os.readlink(linkPath)))
                    try:
                        os.unlink(linkPath)
                    except OSError as e:
                        LogWarning(u"Cache pruning of %s failed: %s" %
                                   (name, unicode(c)))
Exemple #5
0
    def loadProfilesFromPlist_(self, plist):
        """Load UpdateProfiles from a plist dictionary."""

        LogInfo("Loading update profiles with PublicationDate %@",
                plist["PublicationDate"])

        try:
            # FIXME: Add profile verification.

            self.profiles = dict()
            for name, updates in plist["Profiles"].iteritems():
                profile = list()
                for update in updates:
                    profile.append(plist["Updates"][update])
                self.profiles[name] = profile

            self.publicationDate = plist["PublicationDate"]

            self.updatePaths = dict()
            for name, update in plist["Updates"].iteritems():
                filename, ext = os.path.splitext(
                    os.path.basename(update["url"]))
                self.updatePaths[update["sha1"]] = "%s(%s)%s" % (
                    filename, update["sha1"][:7], ext)

            self.deprecatedInstallerBuilds = dict()
            try:
                for replacement, builds in plist[
                        "DeprecatedInstallers"].iteritems():
                    for build in builds:
                        self.deprecatedInstallerBuilds[build] = replacement
            except KeyError:
                LogWarning("No deprecated installers in profile")

            self.deprecatedOS = False
            try:
                for osVerStr in plist["DeprecatedOSVersions"]:
                    deprecatedVerMajor = IEDUtil.splitVersion(osVerStr)[1]
                    if IEDUtil.hostMajorVersion() <= deprecatedVerMajor:
                        self.deprecatedOS = True
                        LogWarning("%@ is no longer being updated by Apple",
                                   osVerStr)
                        break
            except KeyError:
                LogWarning("No deprecated OS versions in profile")

            if self.delegate:
                self.delegate.profilesUpdated()
        except BaseException as e:
            LogError("Failed to load profile: %@", unicode(e))
Exemple #6
0
    def loadTemplateAndReturnError_(self, path):
        if path in self.loadedTemplates:
            return u"%s included recursively" % path
        else:
            self.loadedTemplates.add(path)

        plist = NSDictionary.dictionaryWithContentsOfFile_(path)
        if not plist:
            error = u"Couldn't read dictionary from plist at %s" % (path)
            LogWarning(u"%@", error)
            return error

        templateFormat = plist.get(u"TemplateFormat", u"1.0")

        if templateFormat != u"1.0":
            LogWarning(u"Unknown format version %@", templateFormat)

        for key in plist.keys():
            if key == u"IncludeTemplates":
                for includePath in plist[u"IncludeTemplates"]:
                    LogInfo(u"Including template %@", includePath)
                    error = self.loadTemplateAndReturnError_(includePath)
                    if error:
                        return error
            elif key == u"SourcePath":
                self.setSourcePath_(plist[u"SourcePath"])
            elif key == u"ApplyUpdates":
                self.setApplyUpdates_(plist[u"ApplyUpdates"])
            elif key == u"AdditionalPackages":
                if not self.setAdditionalPackages_(
                        plist[u"AdditionalPackages"]):
                    msg = u"Additional packages failed verification"
                    if self.additionalPackageError:
                        msg += u":\n" + self.additionalPackageError
                    return msg
            elif key == u"OutputPath":
                self.setOutputPath_(plist[u"OutputPath"])
            elif key == u"VolumeName":
                self.setVolumeName_(plist[u"VolumeName"])
            elif key == u"VolumeSize":
                self.setVolumeSize_(plist[u"VolumeSize"])
            elif key == u"FinalizeAsrImagescan":
                self.setFinalizeAsrImagescan_(plist[u"FinalizeAsrImagescan"])
            elif key == u"TemplateFormat":
                pass

            else:
                LogWarning(u"Unknown key '%@' in template", key)

        return None
Exemple #7
0
 def getFlatPkgInfo_(cls, pkgPath):
     tempdir = tempfile.mkdtemp()
     try:
         # Extract to tempdir, excluding all except Distribution and
         # PackageInfo.
         subprocess.check_output([
             "/usr/bin/xar", "-x", "--exclude", "^[^DP]", "--exclude",
             "Payload", "-C", tempdir, "-f", pkgPath
         ])
         distPath = os.path.join(tempdir, "Distribution")
         pkgInfoPath = os.path.join(tempdir, "PackageInfo")
         if os.path.exists(distPath):
             return cls.getSizeFromDistribution_(distPath)
         elif os.path.exists(pkgInfoPath):
             return cls.getSizeFromPackageInfo_(pkgInfoPath)
         else:
             LogError("No Distribution or PackageInfo found in '%@'",
                      pkgPath)
             return None
     except subprocess.CalledProcessError as e:
         LogError("xar failed with return code %d", e.returncode)
         return None
     finally:
         try:
             shutil.rmtree(tempdir)
         except Exception as e:
             LogWarning("Unable to remove tempdir: %@", str(e))
Exemple #8
0
    def loadProfilesFromPlist_(self, plist):
        """Load UpdateProfiles from a plist dictionary."""

        LogInfo(u"Loading update profiles with PublicationDate %@",
                plist[u"PublicationDate"])
        self.profiles = dict()
        for name, updates in plist[u"Profiles"].iteritems():
            profile = list()
            for update in updates:
                profile.append(plist[u"Updates"][update])
            self.profiles[name] = profile
        self.publicationDate = plist[u"PublicationDate"]
        self.updatePaths = dict()
        for name, update in plist[u"Updates"].iteritems():
            filename, ext = os.path.splitext(os.path.basename(update[u"url"]))
            self.updatePaths[update[u"sha1"]] = u"%s(%s)%s" % (
                filename, update[u"sha1"][:7], ext)
        self.deprecatedInstallerBuilds = dict()
        try:
            for replacement, builds in plist[
                    u"DeprecatedInstallers"].iteritems():
                for build in builds:
                    self.deprecatedInstallerBuilds[build] = replacement
        except KeyError:
            LogWarning(u"No deprecated installers")
        if self.delegate:
            self.delegate.profilesUpdated()
 def stopListening(self):
     LogDebug("stopListening")
     self.watchThread.cancel()
     try:
         os.unlink(self.socketPath)
     except BaseException as e:
         LogWarning("Couldn't remove listener socket %@: %@", self.socketPath, str(e))
Exemple #10
0
 def getvar(m):
     try:
         return variables[m.group("key")]
     except KeyError as err:
         LogWarning("Template references undefined variable: %%%@%%",
                    m.group("key"))
         return u"%%%s%%" % m.group("key")
Exemple #11
0
def gui_main():
    IEDLog.IEDLogToController = True
    IEDLog.IEDLogToSyslog = True
    IEDLog.IEDLogToStdOut = True
    IEDLog.IEDLogStdOutLogLevel = IEDLog.IEDLogLevelDebug
    try:
        logFile = os.path.join(get_log_dir(),
                               u"AutoDMG-%s.log" % get_date_string())
        IEDLog.IEDLogFileHandle = open(logFile, u"a", buffering=1)
        IEDLog.IEDLogToFile = True
    except OSError as e:
        IEDLog.IEDLogToFile = False
        LogWarning(u"Couldn't open %s for writing" %
                   (logFile)).encode(u"utf-8")

    import AppKit
    from PyObjCTools import AppHelper

    # import modules containing classes required to start application and load MainMenu.nib
    import IEDAppDelegate
    import IEDController
    import IEDSourceSelector
    import IEDAddPkgController
    import IEDAppVersionController

    # pass control to AppKit
    AppHelper.runEventLoop(unexpectedErrorAlert=gui_unexpected_error_alert)

    return os.EX_OK
Exemple #12
0
 def getInstalledPkgSizeFromInstaller_(cls, pkgPath):
     pkgFileName = os.path.os.path.basename(pkgPath)
     tempdir = tempfile.mkdtemp()
     try:
         symlinkPath = os.path.join(tempdir, pkgFileName)
         os.symlink(pkgPath, symlinkPath)
         p = subprocess.Popen([
             "/usr/sbin/installer", "-pkginfo", "-verbose", "-plist",
             "-pkg", symlinkPath
         ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
         out, err = p.communicate()
     finally:
         try:
             shutil.rmtree(tempdir)
         except BaseException as e:
             LogWarning("Unable to remove tempdir: %@", str(e))
     if p.returncode != 0:
         LogDebug(
             "/usr/sbin/installer failed to determine size requirements")
         return None
     outData = NSData.dataWithBytes_length_(out, len(out))
     plist, format, error = NSPropertyListSerialization.propertyListWithData_options_format_error_(
         outData, NSPropertyListImmutable, None, None)
     if not plist:
         LogError("Error decoding plist: %@", error)
         return None
     LogDebug("Installer says %@ requires %@", pkgPath,
              cls.formatByteSize_(int(plist["Size"]) * 1024))
     return int(plist["Size"]) * 1024
Exemple #13
0
def get_log_dir():
    logDir = os.path.expanduser(u"~/Library/Logs/AutoDMG")
    if not os.path.exists(logDir):
        try:
            os.makedirs(logDir)
        except OSError as e:
            LogWarning(u"Couldn't create %s" % (logDir))
    return logDir
Exemple #14
0
 def deleteTempDir(self):
     if self.tempDir:
         try:
             shutil.rmtree(self.tempDir)
         except OSError as e:
             LogWarning("Can't remove temporary directory '%@': %@",
                        self.tempDir, str(e))
         finally:
             self.tempDir = None
Exemple #15
0
 def calculateInstalledPkgSize_(cls, pkgPath):
     if os.path.isdir(pkgPath):
         size = cls.getBundlePkgInfo_(pkgPath)
     else:
         size = cls.getFlatPkgInfo_(pkgPath)
     if size is None:
         # If all else fails, estimate package size requirements.
         LogWarning("Estimating package size for '%@'", pkgPath)
         size = cls.getPackageSize_(pkgPath) * 2
     LogDebug("%@ needs %@", pkgPath, cls.formatByteSize_(size))
     return size
Exemple #16
0
 def loadImageTemplate_(self, mountPoint):
     LogDebug("checkTemplate:%@", mountPoint)
     try:
         path = glob.glob(os.path.join(mountPoint, "private/var/log/*.adtmpl"))[0]
     except IndexError:
         return None
     template = IEDTemplate.alloc().init()
     error = template.loadTemplateAndReturnError_(path)
     if error:
         LogWarning("Error reading %@ from image: %@", os.path.basename(path), error)
         return None
     return template
Exemple #17
0
    def attachedDMGs(self):
        dmgMounts = dict()

        LogDebug(u"Finding already attached dmgs")
        p = subprocess.Popen([u"/usr/bin/hdiutil", u"info", u"-plist"],
                             bufsize=1,
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            errstr = u"hdiutil info failed with return code %d" % p.returncode
            if err:
                errstr += u": %s" % err.decode(u"utf-8")
            LogWarning("%@", errstr)
            return dmgMounts

        # Strip EULA text.
        outXML = out[out.find("<?xml"):]
        outData = NSData.dataWithBytes_length_(outXML, len(outXML))
        plist, format, error = \
            NSPropertyListSerialization.propertyListWithData_options_format_error_(outData,
                                                                                   NSPropertyListImmutable,
                                                                                   None,
                                                                                   None)
        for dmgInfo in plist[u"images"]:
            for entity in dmgInfo.get(u"system-entities", []):
                try:
                    image_path = dmgInfo[u"image-path"]
                    alias_path = u""
                    bookmark = CFURLCreateBookmarkDataFromAliasRecord(
                        kCFAllocatorDefault, dmgInfo[u"image-alias"])
                    if bookmark:
                        url, stale, error = CFURLCreateByResolvingBookmarkData(
                            None, bookmark, kCFBookmarkResolutionWithoutUIMask,
                            None, None, None, None)
                        if url:
                            alias_path = url.path()
                        else:
                            LogDebug(u"Couldn't resolve bookmark: %@",
                                     error.localizedDescription())
                    for path in set(x for x in (image_path, alias_path) if x):
                        dmgMounts[path] = entity[u"mount-point"]
                        LogDebug(u"'%@' is already mounted at '%@'", path,
                                 entity[u"mount-point"])
                    break
                except IndexError:
                    pass
                except KeyError:
                    pass

        return dmgMounts
Exemple #18
0
    def launchScript_(self, args):
        LogDebug("launchScript:")

        if (self.authPassword() is None) and (os.getuid() != 0):
            # Use GUI dialog to elevate privileges.
            task = STPrivilegedTask.alloc().init()
            task.setLaunchPath_(args[0])
            task.setArguments_(args[1:])
            status = task.launch()
            LogNotice("Install task launched with return code: %d", status)
            if status:
                self.performSelectorOnMainThread_withObject_waitUntilDone_(
                    self.handleLaunchScriptError_, status, False)
        else:
            task = NSTask.alloc().init()
            if os.getuid() == 0:
                # No privilege elevation necessary.
                task.setLaunchPath_(args[0])
                task.setArguments_(args[1:])
            else:
                # Use sudo to elevate privileges.
                task.setLaunchPath_("/usr/bin/sudo")
                task.setArguments_(["-kSE"] + args)
                # Send password to sudo on stdin.
                passwordpipe = NSPipe.alloc().init()
                task.setStandardInput_(passwordpipe.fileHandleForReading())
                task.setStandardOutput_(
                    NSFileHandle.fileHandleWithNullDevice())
                task.setStandardError_(NSFileHandle.fileHandleWithNullDevice())
                writer = passwordpipe.fileHandleForWriting()
                pwd = NSString.stringWithString_(self.authPassword() + "\n")
                writer.writeData_(pwd.dataUsingEncoding_(NSUTF8StringEncoding))
                writer.closeFile()
            try:
                task.launch()
                LogNotice("Install task launched%@",
                          " with sudo" if os.getuid() != 0 else "")
            except BaseException as e:
                LogWarning("Install task launch failed with exception")
                self.performSelectorOnMainThread_withObject_waitUntilDone_(
                    self.handleLaunchScriptError_, str(e), False)
                return
            task.waitUntilExit()
            LogNotice("Install task finished with exit status %d",
                      task.terminationStatus())
            if task.terminationStatus() != 0:
                self.performSelectorOnMainThread_withObject_waitUntilDone_(
                    self.handleLaunchScriptError_,
                    "Install task failed with status %d" %
                    task.terminationStatus(), False)
Exemple #19
0
 def checkForAppUpdateSilently_(self, silently):
     self.checkSilently = silently
     # Create a buffer for data.
     self.plistData = NSMutableData.alloc().init()
     # Start download.
     osVer, osBuild = IEDUtil.readSystemVersion_("/")[1:3]
     appVer, appBuild = IEDUtil.getAppVersion()
     urlString = "%s?osVer=%s&osBuild=%s&appVer=%s&appBuild=%s" % (
         self.defaults.stringForKey_("AppVersionURL"), osVer, osBuild,
         appVer, appBuild)
     url = NSURL.URLWithString_(urlString)
     request = NSURLRequest.requestWithURL_(url)
     self.connection = NSURLConnection.connectionWithRequest_delegate_(
         request, self)
     LogDebug("connection = %@", self.connection)
     if not self.connection:
         LogWarning("Connection to %@ failed", url)
Exemple #20
0
 def saveTemplateAndReturnError_(self, path):
     plist = NSMutableDictionary.alloc().init()
     plist[u"TemplateFormat"] = self.templateFormat = u"1.0"
     plist[u"AdditionalPackages"] = self.additionalPackages
     plist[u"ApplyUpdates"] = self.applyUpdates
     plist[u"VolumeName"] = self.volumeName
     if self.sourcePath:
         plist[u"SourcePath"] = self.sourcePath
     if self.outputPath:
         plist[u"OutputPath"] = self.outputPath
     if self.volumeSize:
         plist[u"VolumeSize"] = self.volumeSize
     if plist.writeToFile_atomically_(path, False):
         return None
     else:
         error = u"Couldn't write dictionary to plist at %s" % (path)
         LogWarning(u"%@", error)
         return error
Exemple #21
0
 def saveTemplateAndReturnError_(self, path):
     plist = NSMutableDictionary.alloc().init()
     plist["TemplateFormat"] = self.templateFormat = "1.1"
     plist["AdditionalPackages"] = self.additionalPackages
     plist["ApplyUpdates"] = self.applyUpdates
     plist["VolumeName"] = self.volumeName
     if self.sourcePath:
         plist["SourcePath"] = self.sourcePath
     if self.outputPath:
         plist["OutputPath"] = self.outputPath
     if self.volumeSize:
         plist["VolumeSize"] = self.volumeSize
     if not self.finalizeAsrImagescan:
         plist["FinalizeAsrImagescan"] = self.finalizeAsrImagescan
     if self.filesystem:
         plist["Filesystem"] = self.filesystem
     if plist.writeToFile_atomically_(path, False):
         return None
     else:
         error = "Couldn't write dictionary to plist at %s" % (path)
         LogWarning("%@", error)
         return error
Exemple #22
0
    def updateFromURL_(self, url):
        """Download the latest UpdateProfiles.plist."""

        LogDebug("updateFromURL:%@", url)

        if self.profileUpdateWindow:
            # Show the progress window.
            self.progressBar.setIndeterminate_(True)
            self.progressBar.startAnimation_(self)
            self.profileUpdateWindow.makeKeyAndOrderFront_(self)

        # Create a buffer for data.
        self.profileUpdateData = NSMutableData.alloc().init()
        # Start download.
        request = NSURLRequest.requestWithURL_(url)
        self.connection = NSURLConnection.connectionWithRequest_delegate_(
            request, self)
        LogDebug("connection = %@", self.connection)
        if not self.connection:
            LogWarning("Connection to %@ failed", url)
            if self.profileUpdateWindow:
                self.profileUpdateWindow.orderOut_(self)
            self.delegate.profileUpdateFailed_(error)
Exemple #23
0
 def getInstalledPkgSize_(cls, pkgPath):
     # For apps just return the size on disk.
     ext = os.path.splitext(pkgPath)[1].lower()
     if ext == ".app":
         return cls.getPackageSize_(pkgPath)
     elif ext in (".pkg", ".mpkg"):
         # For packages first try to get the size requirements with
         # installer.
         size = cls.getInstalledPkgSizeFromInstaller_(pkgPath)
         if size is None:
             # If this fails, manually extract the size requirements from
             # the package.
             return cls.calculateInstalledPkgSize_(pkgPath)
         else:
             return size
     elif ext == ".plist":
         # FIXME: Implement size calculation
         LogWarning("Using hardcoded size for InstallInfo.plist")
         return 12497424
     else:
         LogError("Don't know how to calculate installed size for '%@'",
                  pkgPath)
         return None
Exemple #24
0
def cli_main(argv):
    IEDLog.IEDLogToController = False
    IEDLog.IEDLogToSyslog = True
    IEDLog.IEDLogToStdOut = True
    IEDLog.IEDLogToFile = False

    from IEDCLIController import IEDCLIController
    clicontroller = IEDCLIController.alloc().init()

    try:
        # Initialize user defaults before application starts.
        defaults = NSUserDefaults.standardUserDefaults()
        defaultsPath = NSBundle.mainBundle().pathForResource_ofType_(
            u"Defaults", u"plist")
        defaultsDict = NSDictionary.dictionaryWithContentsOfFile_(defaultsPath)
        defaults.registerDefaults_(defaultsDict)

        p = argparse.ArgumentParser()
        p.add_argument(u"-v",
                       u"--verbose",
                       action=u"store_true",
                       help=u"Verbose output")
        p.add_argument(u"-L",
                       u"--log-level",
                       type=int,
                       choices=range(0, 8),
                       default=6,
                       metavar=u"LEVEL",
                       help=u"Log level (0-7), default 6")
        p.add_argument(u"-l", u"--logfile", help=u"Log to file")
        p.add_argument(u"-r",
                       u"--root",
                       action=u"store_true",
                       help=u"Allow running as root")
        sp = p.add_subparsers(title=u"subcommands", dest=u"subcommand")

        # Populate subparser for each verb.
        for verb in clicontroller.listVerbs():
            verb_method = getattr(clicontroller, u"cmd%s_" % verb.capitalize())
            addargs_method = getattr(clicontroller,
                                     u"addargs%s_" % verb.capitalize())
            parser = sp.add_parser(verb, help=verb_method.__doc__)
            addargs_method(parser)
            parser.set_defaults(func=verb_method)

        args = p.parse_args(argv)

        if args.verbose:
            IEDLog.IEDLogStdOutLogLevel = IEDLog.IEDLogLevelInfo
        else:
            IEDLog.IEDLogStdOutLogLevel = IEDLog.IEDLogLevelNotice

        IEDLog.IEDLogFileLogLevel = args.log_level

        if args.logfile == u"-":
            # Redirect log to stdout instead.
            IEDLog.IEDLogFileHandle = sys.stdout
            IEDLog.IEDLogToFile = True
            IEDLog.IEDLogToStdOut = False
        else:
            try:
                if args.logfile:
                    logFile = args.logfile
                else:
                    logFile = os.path.join(
                        get_log_dir(), u"AutoDMG-%s.log" % get_date_string())
                IEDLog.IEDLogFileHandle = open(logFile, u"a", buffering=1)
            except OSError as e:
                print >> sys.stderr, (u"Couldn't open %s for writing" %
                                      logFile).encode(u"utf-8")
                return os.EX_CANTCREAT
            IEDLog.IEDLogToFile = True

        # Check if we're running with root.
        if os.getuid() == 0:
            if args.root:
                fm = NSFileManager.defaultManager()
                url, error = fm.URLForDirectory_inDomain_appropriateForURL_create_error_(
                    NSApplicationSupportDirectory, NSUserDomainMask, None,
                    False, None)
                LogWarning(u"Running as root, using %@",
                           os.path.join(url.path(), u"AutoDMG"))
            else:
                LogError(
                    u"Running as root isn't recommended (use -r to override)")
                return os.EX_USAGE

        # Log version info on startup.
        version, build = IEDUtil.getAppVersion()
        LogInfo(u"AutoDMG v%@ build %@", version, build)
        name, version, build = IEDUtil.readSystemVersion_(u"/")
        LogInfo(u"%@ %@ %@", name, version, build)
        LogInfo(u"%@ %@ (%@)", platform.python_implementation(),
                platform.python_version(), platform.python_compiler())
        LogInfo(u"PyObjC %@", objc.__version__)

        return args.func(args)
    finally:
        clicontroller.cleanup()
Exemple #25
0
    def openTemplateAtURL_(self, url):
        LogDebug("openTemplateAtURL:%@", url)
        self.templateURL = None
        template = IEDTemplate.alloc().init()
        error = template.loadTemplateAndReturnError_(url.path())
        if error:
            self.displayAlert_text_("Couldn't open template", error)
            return False
        self.templateURL = url
        NSDocumentController.sharedDocumentController(
        ).noteNewRecentDocumentURL_(url)
        # AdditionalPackages.
        LogDebug("Setting additional packages to %@",
                 template.additionalPackages)
        self.addPkgController.replacePackagesWithPaths_(
            template.additionalPackages)
        # ApplyUpdates.
        if template.applyUpdates:
            LogDebug("Enable updates")
            self.updateController.applyUpdatesCheckbox.setState_(NSOnState)
        else:
            LogDebug("Disable updates")
            self.updateController.applyUpdatesCheckbox.setState_(NSOffState)
        # VolumeName.
        self.volumeName.setStringValue_("")
        if template.volumeName:
            LogDebug("Setting volume name to %@", template.volumeName)
            self.volumeName.setStringValue_(template.volumeName)
        # VolumeSize.
        self.volumeSize.setStringValue_("")
        if template.volumeSize:
            LogDebug("Setting volume size to %@", template.volumeSize)
            self.volumeSize.setIntValue_(template.volumeSize)
        # Finalize task: ASR imagescan.
        self.finalizeAsrImagescan.setState_(NSOnState)
        if template.finalizeAsrImagescan == False:
            LogDebug("Setting 'Finalize: Scan for restore' to %@",
                     template.finalizeAsrImagescan)
            self.finalizeAsrImagescan.setState_(NSOffState)
        # Filesystem.
        if template.filesystem:
            if IEDUtil.hostMajorVersion() < 13:
                if template.filesystem != "hfs":
                    LogWarning(
                        "Ignoring template filesystem (%@), using hfs on 10.%d",
                        template.filesystem, IEDUtil.hostMajorVersion())
            else:
                LogDebug("Setting filesystem to %@", template.filesystem)
                for item in self.filesystem.itemArray():
                    if item.representedObject() == template.filesystem:
                        self.filesystem.selectItem_(item)
                        break
                else:
                    LogWarning("Unknown filesystem '%@'", template.filesystem)

        # SourcePath.
        if template.sourcePath:
            LogDebug("Setting source to %@", template.sourcePath)
            self.setBusy_(True)
            self.workflow.setSource_(template.sourcePath)
        return True
Exemple #26
0
    def continuePrepare(self):
        LogDebug("continuePrepare")

        # Generate a list of packages to install.
        self.packagesToInstall = list()
        if self.sourceType == IEDWorkflow.INSTALL_ESD:
            self.packagesToInstall.append(
                os.path.join(self.installerMountPoint, "Packages",
                             "OSInstall.mpkg"))
        elif self.sourceType == IEDWorkflow.INSTALL_INFO:
            self.packagesToInstall.append(
                os.path.join(self.newSourcePath, "Contents", "SharedSupport",
                             "InstallInfo.plist"))
        for package in self.additionalPackages:
            if package.path().endswith(".dmg"):
                mountPoint = self.attachedPackageDMGs[package.path()]
                LogDebug("Looking for packages and applications in %@: %@",
                         mountPoint, glob.glob(os.path.join(mountPoint, "*")))
                packagePaths = glob.glob(os.path.join(mountPoint, "*.mpkg"))
                packagePaths += glob.glob(os.path.join(mountPoint, "*.pkg"))
                packagePaths += glob.glob(os.path.join(mountPoint, "*.app"))
                if len(packagePaths) == 0:
                    self.fail_details_(
                        "Nothing found to install",
                        "No package or application found in %s" %
                        package.name())
                    return
                elif len(packagePaths) > 1:
                    LogWarning("Multiple items found in %@, using %@",
                               package.path(), packagePaths[0])
                self.packagesToInstall.append(packagePaths[0])
            else:
                self.packagesToInstall.append(package.path())
        if len(self.packagesToInstall) == 0:
            self.delegate.buildFailed_details_(
                "Nothing to do", "There are no packages to install")
            self.stop()
            return

        # Calculate disk image size requirements.
        sizeRequirement = 0
        LogInfo("%d packages to install:", len(self.packagesToInstall))
        for path in self.packagesToInstall:
            try:
                installedSize = IEDUtil.getInstalledPkgSize_(path)
            except BaseException as e:
                LogError("Size calculation of %@ failed: %@", path, unicode(e))
                installedSize = None
            if installedSize is None:
                self.delegate.buildFailed_details_(
                    "Failed to determine installed size",
                    "Unable to determine installation size requirements for %s"
                    % path)
                self.stop()
                return
            LogInfo("    %@ requires %@", path,
                    IEDUtil.formatByteSize_(installedSize))
            sizeRequirement += installedSize
        sizeReqStr = IEDUtil.formatByteSize_(sizeRequirement)
        LogInfo("Workflow requires a %@ disk image", sizeReqStr)

        if self.volumeSize() is None:
            # Calculate DMG size. Multiply package requirements by 1.1, round
            # to the nearest GB, and add 23.
            self.setVolumeSize_(
                int((float(sizeRequirement) * 1.1) /
                    (1000.0 * 1000.0 * 1000.0) + 23.5))
        else:
            # Make sure user specified image size is large enough.
            if sizeRequirement > self.volumeSize() * 1000 * 1000 * 1000:
                details = "Workflow requires %s and disk image is %d GB" % (
                    sizeReqStr, self.volumeSize())
                self.delegate.buildFailed_details_(
                    "Disk image too small for workflow", details)
                self.stop()
                return
        LogInfo("Using a %d GB disk image", self.volumeSize())

        # Task done.
        self.nextTask()