def connection_didReceiveResponse_(self, connection, response): LogDebug("%@ status code %d", self.package.name(), response.statusCode()) if response.statusCode() >= 400: connection.cancel() self.fileHandle.closeFile() error = "%s failed with HTTP %d" % (self.package.name(), response.statusCode()) self.delegate.downloadFailed_withError_(self.package, error) self.delegate.downloadAllDone()
def setSource_(self, path): LogDebug(u"setSource:%@", path) self._source = None self.newSourcePath = path if self.installerMountPoint: self.delegate.ejectingSource() self.dmgHelper.detachAll_(self.continueSetSource_) else: self.continueSetSource_({})
def detachAll_(self, selector): LogDebug("detachAll:%@", selector) self.detachAllFailed = dict() self.detachAllRemaining = len(self.dmgs) self.detachAllSelector = selector if self.dmgs: for dmgPath in self.dmgs.iterkeys(): self.performSelectorInBackground_withObject_(self.hdiutilDetach_, [dmgPath, self, self.handleDetachAllResult_]) else: if self.delegate.respondsToSelector_(selector): self.delegate.performSelector_withObject_(selector, {})
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
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
def handleSourceMountResult_(self, result): LogDebug("handleSourceMountResult:%@", result) if not result["success"]: self.delegate.sourceFailed_text_("Failed to mount %s" % result["dmg-path"], result["error-message"]) return mountPoint = result["mount-point"] # Update the icon if we find an installer app. for path in glob.glob(os.path.join(mountPoint, "Install*.app")): self.delegate.foundSourceForIcon_(path) # Don't set this again since 10.9 mounts BaseSystem.dmg after InstallESD.dmg. if self.installerMountPoint is None: # Check if the source is an InstallESD, BaseSystem or system image. if os.path.exists(os.path.join(mountPoint, "Packages", "OSInstall.mpkg")): self.installerMountPoint = mountPoint self.sourceType = IEDWorkflow.INSTALL_ESD LogDebug("sourceType = INSTALL_ESD") elif glob.glob(os.path.join(mountPoint, "Install*.app")): self.sourceType = IEDWorkflow.INSTALL_INFO LogDebug("sourceType = INSTALL_INFO") else: self.installerMountPoint = mountPoint self.sourceType = IEDWorkflow.SYSTEM_IMAGE LogDebug("sourceType = SYSTEM_IMAGE") baseSystemPath = os.path.join(mountPoint, "BaseSystem.dmg") # If we find a SystemVersion.plist we proceed to the next step. if os.path.exists(os.path.join(mountPoint, IEDUtil.VERSIONPLIST_PATH)): self.checkVersion_(mountPoint) # Otherwise check if there's a BaseSystem.dmg that we need to examine. elif os.path.exists(baseSystemPath): self.baseSystemMountedFromPath = baseSystemPath self.dmgHelper.attach_selector_(baseSystemPath, self.handleSourceMountResult_) else: self.delegate.sourceFailed_text_("Invalid source", "Couldn't find system version.")
def nextTask(self): LogDebug("nextTask, currentTask == %@", self.currentTask) if self.currentTask: if self.currentTask["phases"]: for phase in self.currentTask["phases"]: if not phase.get("optional", False): details = NSString.stringWithFormat_("Phases remaining: %@", self.currentTask["phases"]) self.fail_details_("Task finished prematurely", details) return if self.tasks: self.currentTask = self.tasks.pop(0) LogNotice("Starting task %@ with %d phases", self.currentTask["title"], len(self.currentTask["phases"])) self.nextPhase() LogDebug("Calling %@()", self.currentTask["title"]) self.currentTask["method"]() LogDebug("Returned from %@()", self.currentTask["title"]) else: LogNotice("Build finished successfully, image saved to %@", self.outputPath()) self.delegate.buildSucceeded() self.stop()
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
def countDownloads(self): LogDebug(u"countDownloads") self.downloads = list() self.downloadTotalSize = 0 for package in self.updates: if self.cache.isCached_(package.sha1()): package.setImage_(self.cachedImage) else: package.setImage_(self.uncachedImage) self.downloadTotalSize += package.size() self.downloads.append(package) self.updateTable.reloadData() self.showRemainingDownloads()
def taskPrepare(self): LogDebug("taskPrepare") # Attach any disk images containing update packages. self.attachedPackageDMGs = dict() self.numberOfDMGsToAttach = 0 for package in self.additionalPackages: if package.path().endswith(".dmg"): self.numberOfDMGsToAttach += 1 LogInfo("Attaching %@", package.path()) self.dmgHelper.attach_selector_(package.path(), self.attachPackageDMG_) if self.numberOfDMGsToAttach == 0: self.continuePrepare()
def attachPackageDMG_(self, result): LogDebug("attachPackageDMG:%@", result) if not result["success"]: self.fail_details_("Failed to attach %s" % result["dmg-path"], result["error-message"]) return # Save result in a dictionary of dmg paths and their mount points. self.attachedPackageDMGs[result["dmg-path"]] = result["mount-point"] # If this was the last image we were waiting for, continue preparing # for install. if len(self.attachedPackageDMGs) == self.numberOfDMGsToAttach: self.continuePrepare()
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
def awakeFromNib(self): LogDebug("awakeFromNib") # Initialize UI. self.buildProgressBar.setMaxValue_(100.0) self.buildProgressMessage.setStringValue_("") self.setSourcePlaceholder() # We're a delegate for the drag and drop target, protocol: # (void)acceptInstaller:(NSString *)path self.sourceBox.setDelegate_(self) self.sourceImage.setDelegate_(self) self.sourceLabel.setDelegate_(self) # We're a delegate for the update controller, protocol: # (void)updateControllerChanged if self.updateController: self.updateController.setDelegate_(self) else: alert = NSAlert.alloc().init() alert.setMessageText_("Unable to initialize update controller") alert.setInformativeText_( "AutoDMG will not function correctly, check the log for details." ) alert.runModal() # Main workflow logic. self.workflow = IEDWorkflow.alloc().initWithDelegate_(self) # Enabled state for main window. self.enabled = True # When busy is true quitting gets a confirmation prompt. self._busy = False # Currently loaded template. self.templateURL = None # Filesystem selection. self.filesystem.setAutoenablesItems_(False) self.filesystemHfs.setRepresentedObject_("hfs") self.filesystemApfs.setRepresentedObject_("apfs") if IEDUtil.hostMajorVersion() < 13: self.filesystem.selectItem_(self.filesystemHfs) self.filesystemApfs.setEnabled_(False) if IEDUtil.hostMajorVersion() == 13: self.filesystem.selectItem_(self.filesystemApfs) self.filesystemApfs.setEnabled_(True) else: self.filesystem.selectItem_(self.filesystemApfs) self.filesystemHfs.setEnabled_(False)
def continueSetSource_(self, failedUnmounts): LogDebug(u"continueSetSource:%@", failedUnmounts) self.alertFailedUnmounts_(failedUnmounts) self.installESDPath = os.path.join(self.newSourcePath, u"Contents/SharedSupport/InstallESD.dmg") if not os.path.exists(self.installESDPath): self.installESDPath = self.newSourcePath self.delegate.examiningSource_(self.newSourcePath) self.installerMountPoint = None self.baseSystemMountedFromPath = None self.dmgHelper.attach_selector_(self.installESDPath, self.handleSourceMountResult_)
def applicationShouldTerminate_(self, sender): LogDebug("applicationShouldTerminate:") if self.mainWindowController.busy(): alert = NSAlert.alloc().init() alert.setAlertStyle_(NSCriticalAlertStyle) alert.setMessageText_("Application busy") alert.setInformativeText_("Quitting now could leave the " "system in an unpredictable state.") alert.addButtonWithTitle_("Quit") alert.addButtonWithTitle_("Stay") button = alert.runModal() if button == NSAlertSecondButtonReturn: return NSTerminateCancel return NSTerminateNow
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)
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)
def setSource_(self, path): LogDebug("setSource:%@", path) if not self.checkAppleBugWarning_(path): return self._source = None self.sourceType = None self.newSourcePath = path if self.installerMountPoint: self.delegate.ejectingSource() self.dmgHelper.detachAll_(self.continueSetSource_) else: self.continueSetSource_({})
def hdiutilDetach_(self, args): try: dmgPath, target, selector = args LogDebug(u"Detaching %@", dmgPath) try: cmd = [u"/usr/bin/hdiutil", u"detach", self.dmgs[dmgPath]] except KeyError: target.performSelectorOnMainThread_withObject_waitUntilDone_(selector, {u"success": False, u"dmg-path": dmgPath, u"error-message": u"%s not mounted" % dmgPath}, False) return del self.dmgs[dmgPath] maxtries = 5 for tries in range(maxtries): if tries == maxtries >> 1: cmd.append(u"-force") p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode == 0: target.performSelectorOnMainThread_withObject_waitUntilDone_(selector, {u"success": True, u"dmg-path": dmgPath}, False) return elif tries == maxtries - 1: errstr = u"hdiutil detach failed with return code %d" % p.returncode if err: errstr += u": %s" % err.decode(u"utf-8") target.performSelectorOnMainThread_withObject_waitUntilDone_(selector, {u"success": False, u"dmg-path": dmgPath, u"error-message": errstr}, False) else: time.sleep(1) except Exception: exceptionInfo = traceback.format_exc() msg = u"Detach of %s crashed with exception:\n%s" % (dmgPath, exceptionInfo) target.performSelectorOnMainThread_withObject_waitUntilDone_(selector, {u"success": False, u"dmg-path": dmgPath, u"error-message": msg}, False)
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) # SourcePath. if template.sourcePath: LogDebug("Setting source to %@", template.sourcePath) self.setBusy_(True) self.workflow.setSource_(template.sourcePath) return True
def updateUsersProfilesIfNewer_(self, plist): """Update the user's update profiles if plist is newer. Returns whichever was the newest.""" # Load UpdateProfiles from the user's application support directory. userUpdateProfiles = NSDictionary.dictionaryWithContentsOfFile_( self.userUpdateProfilesPath) # If the bundle's plist is newer, update the user's. if (not userUpdateProfiles) or ( userUpdateProfiles[u"PublicationDate"].timeIntervalSinceDate_( plist[u"PublicationDate"]) < 0): LogDebug(u"Saving updated UpdateProfiles.plist") self.saveUsersProfiles_(plist) return plist else: return userUpdateProfiles
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)
def nextPhase(self): LogDebug("nextPhase, currentPhase == %@", self.currentPhase) if self.currentPhase: self.progress += self.currentPhase["weight"] LogInfo("Phase %@ with weight %ld finished after %.3f seconds", self.currentPhase["title"], self.currentPhase["weight"], time.time() - self.phaseStartTime) self.phaseStartTime = time.time() try: self.currentPhase = self.currentTask["phases"].pop(0) except IndexError: self.fail_details_("No phase left in task", traceback.format_stack()) return LogNotice("Starting phase: %@", self.currentPhase["title"]) self.delegate.buildSetPhase_(self.currentPhase["title"]) self.delegate.buildSetProgress_(self.progress)
def hdiutilAttach_(self, args): dmgPath, selector = args LogDebug(u"Attaching %@", dmgPath) p = subprocess.Popen([ u"/usr/bin/hdiutil", u"attach", dmgPath, u"-mountRandom", u"/tmp", u"-nobrowse", u"-noverify", u"-plist" ], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate(u"Y\n") if p.returncode != 0: errstr = u"hdiutil attach failed with return code %d" % p.returncode if err: errstr += u": %s" % err.decode(u"utf-8") self.tellDelegate_message_(selector, { u"success": False, u"dmg-path": dmgPath, u"error-message": errstr }) return # Strip EULA text. xmlStartIndex = out.find("<?xml") plist = plistlib.readPlistFromString(out[xmlStartIndex:]) for partition in plist[u"system-entities"]: if partition[u"content-hint"] == u"Apple_HFS": if u"mount-point" in partition: self.dmgs[dmgPath] = partition[u"mount-point"] break else: self.tellDelegate_message_( selector, { u"success": False, u"dmg-path": dmgPath, u"error-message": u"No mounted filesystem in %s" % dmgPath }) return self.tellDelegate_message_( selector, { u"success": True, u"dmg-path": dmgPath, u"mount-point": self.dmgs[dmgPath] })
def launchFinalize_(self, args): try: task = NSTask.alloc().init() task.setLaunchPath_(args[0]) task.setArguments_(args[1:]) task.launch() task.waitUntilExit() if task.terminationStatus() == 0: LogDebug("Finalize exited with status %d", task.terminationStatus()) else: errMsg = "Finalize task failed with status %d" % task.terminationStatus( ) self.performSelectorOnMainThread_withObject_waitUntilDone_( self.handleFinalizeError_, errMsg, False) except BaseException as e: errMsg = "Failed to launch finalize task: %s" % str(e) self.performSelectorOnMainThread_withObject_waitUntilDone_( self.handleFinalizeError_, errMsg, False)
def handleLaunchScriptError_(self, error): if isinstance(error, int): try: msg = { -60001: "The authorization rights are invalid.", -60002: "The authorization reference is invalid.", -60003: "The authorization tag is invalid.", -60004: "The returned authorization is invalid.", -60005: "The authorization was denied.", -60006: "The authorization was cancelled by the user.", -60007: "The authorization was denied since no user interaction was possible.", -60008: "Unable to obtain authorization for this operation.", -60009: "The authorization is not allowed to be converted to an external format.", -60010: "The authorization is not allowed to be created from an external format.", -60011: "The provided option flag(s) are invalid for this authorization operation.", -60031: "The specified program could not be executed.", -60032: "An invalid status was returned during execution of a privileged tool.", -60033: "The requested socket address is invalid (must be 0-1023 inclusive).", }[error] except KeyError: msg = "Unknown error (%d)." % error else: msg = error if error == -60006: LogDebug("User cancelled auth.") # User cancelled. self.stop() else: self.fail_details_("Build failed", msg)
def saveTemplateToURL_(self, url): LogDebug(u"saveTemplateToURL:%@", url) self.templateURL = url NSDocumentController.sharedDocumentController( ).noteNewRecentDocumentURL_(url) # Create a template from the current state. template = IEDTemplate.alloc().init() if self.workflow.source(): template.setSourcePath_(self.workflow.source()) if self.updateController.packagesToInstall(): template.setApplyUpdates_(True) else: template.setApplyUpdates_(False) template.setAdditionalPackages_( [x.path() for x in self.addPkgController.packagesToInstall()]) error = template.saveTemplateAndReturnError_(url.path()) if error: self.displayAlert_text_(u"Couldn't save template", error)
def awakeFromNib(self): LogDebug("awakeFromNib") # Initialize UI. self.buildProgressBar.setMaxValue_(100.0) self.buildProgressMessage.setStringValue_("") self.setSourcePlaceholder() # We're a delegate for the drag and drop target, protocol: # (void)acceptInstaller:(NSString *)path self.sourceBox.setDelegate_(self) self.sourceImage.setDelegate_(self) self.sourceLabel.setDelegate_(self) # We're a delegate for the update controller, protocol: # (void)updateControllerChanged self.updateController.setDelegate_(self) # Main workflow logic. self.workflow = IEDWorkflow.alloc().initWithDelegate_(self) # Enabled state for main window. self.enabled = True # When busy is true quitting gets a confirmation prompt. self._busy = False # Currently loaded template. self.templateURL = None # Filesystem selection. self.filesystem.setAutoenablesItems_(False) self.filesystemHfs.setRepresentedObject_("hfs") self.filesystemApfs.setRepresentedObject_("apfs") osMajor = IEDUtil.hostVersionTuple()[1] if osMajor < 13: self.filesystem.selectItem_(self.filesystemHfs) self.filesystemApfs.setEnabled_(False) else: self.filesystem.selectItem_(self.filesystemApfs) self.filesystemApfs.setEnabled_(True)
def continueSetSource_(self, failedUnmounts): LogDebug("continueSetSource:%@", failedUnmounts) self.alertFailedUnmounts_(failedUnmounts) pathCandidates = [ os.path.join(self.newSourcePath, "Contents/SharedSupport/BaseSystem.dmg"), os.path.join(self.newSourcePath, "Contents/SharedSupport/InstallESD.dmg"), self.newSourcePath, ] for path in pathCandidates: if os.path.isfile(path): dmgPath = path break else: iaToolPath = os.path.join( self.newSourcePath, "Contents/Resources/InstallAssistantTool") baseName = os.path.basename(self.newSourcePath) if os.path.exists(iaToolPath): self.delegate.sourceFailed_text_( "Incomplete installer", "Installation resources are missing from '%s'. " % baseName + "Try downloading a new installer from the App Store straight from " + "Apple without using an internal Software Update or caching server." ) else: self.delegate.sourceFailed_text_( "Failed to mount %s" % self.newSourcePath, "No system dmg found") return self.delegate.examiningSource_(self.newSourcePath) self.installerMountPoint = None self.baseSystemMountedFromPath = None self.dmgHelper.attach_selector_(dmgPath, self.handleSourceMountResult_)
def checkVersion_(self, mountPoint): LogDebug("checkVersion:%@", mountPoint) # We're now examining InstallESD.dmg for 10.7/10.8, BaseSystem.dmg for # 10.9, or a system image. name, version, build = IEDUtil.readSystemVersion_(mountPoint) if self.baseSystemMountedFromPath: self.dmgHelper.detach_selector_(self.baseSystemMountedFromPath, self.handleDetachResult_) installerVersion = IEDUtil.splitVersion(version) runningVersion = IEDUtil.hostVersionTuple() LogNotice("Found source: %@ %@ %@", name, version, build) if installerVersion[:2] != runningVersion[:2]: self.delegate.ejectingSource() majorVersion = ".".join(str(x) for x in installerVersion[:2]) self.delegate.sourceFailed_text_("OS version mismatch", "The major version of the installer and the current OS must match.\n\n" + \ "%s %s %s installer requires running %s.x to build an image." % ( name, version, build, majorVersion)) self.dmgHelper.detachAll_(self.alertFailedUnmounts_) return LogNotice("Accepted source %@: %@ %@ %@", self.newSourcePath, name, version, build) self._source = self.newSourcePath self.installerName = name self.installerVersion = version self.installerBuild = build info = { "name": name, "version": version, "build": build, "template": self.loadImageTemplate_(mountPoint), "sourceType": self.sourceType, } self.delegate.sourceSucceeded_(info) # There's no reason to keep the dmg mounted if it's not an InstallESD. if self.sourceType != IEDWorkflow.INSTALL_ESD: self.dmgHelper.detachAll_(self.ejectSystemImage_)