mirror of
https://github.com/munki/munki.git
synced 2026-03-09 01:19:44 -05:00
Merged with munki master; manually resolved 'munkiimport' merge conflicts
This commit is contained in:
@@ -66,8 +66,8 @@ class MSUAppDelegate(NSObject):
|
||||
NSLog("MSU GUI version: %s" % ver)
|
||||
munki.log("MSU", "launched", "VER=%s" % ver)
|
||||
|
||||
runmode = NSUserDefaults.standardUserDefaults().stringForKey_("mode") or \
|
||||
os.environ.get("ManagedSoftwareUpdateMode")
|
||||
runmode = (NSUserDefaults.standardUserDefaults().stringForKey_("mode")
|
||||
or os.environ.get("ManagedSoftwareUpdateMode"))
|
||||
if runmode:
|
||||
self.runmode = runmode
|
||||
NSLog("Runmode: %s" % runmode)
|
||||
@@ -81,8 +81,8 @@ class MSUAppDelegate(NSObject):
|
||||
if NSApp.respondsToSelector_('disableRelaunchOnLogin'):
|
||||
NSApp.disableRelaunchOnLogin()
|
||||
|
||||
# register for notification messages so we can be told if available updates
|
||||
# change while we are open
|
||||
# register for notification messages so we can be told if available
|
||||
# updates change while we are open
|
||||
notification_center = NSDistributedNotificationCenter.defaultCenter()
|
||||
notification_center.addObserver_selector_name_object_suspensionBehavior_(
|
||||
self,
|
||||
@@ -199,7 +199,8 @@ class MSUAppDelegate(NSObject):
|
||||
|
||||
alertMessageText = NSLocalizedString(u"Update check failed", None)
|
||||
if self.managedsoftwareupdate_task == "installwithnologout":
|
||||
alertMessageText = NSLocalizedString(u"Install session failed", None)
|
||||
alertMessageText = NSLocalizedString(
|
||||
u"Install session failed", None)
|
||||
|
||||
if socketSessionResult == -1:
|
||||
# connection was dropped unexpectedly
|
||||
@@ -209,9 +210,13 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator.", None))
|
||||
NSLocalizedString(
|
||||
(u"There is a configuration problem with the managed "
|
||||
"software installer. The process ended unexpectedly. "
|
||||
"Contact your systems administrator."), None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
return
|
||||
|
||||
elif socketSessionResult == -2:
|
||||
@@ -222,9 +227,13 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator.", None))
|
||||
NSLocalizedString(
|
||||
(u"There is a configuration problem with the managed "
|
||||
"software installer. Could not start the process. "
|
||||
"Contact your systems administrator."), None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
return
|
||||
|
||||
if self.managedsoftwareupdate_task == "installwithnologout":
|
||||
@@ -260,9 +269,13 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"Managed Software Update cannot contact the update server at this time.\nIf this situation continues, contact your systems administrator.", None))
|
||||
NSLocalizedString(
|
||||
(u"Managed Software Update cannot contact the update "
|
||||
"server at this time.\nIf this situation continues, "
|
||||
"contact your systems administrator."), None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
elif lastCheckResult == -2:
|
||||
munki.log("MSU", "cant_update", "failed preflight")
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
@@ -270,9 +283,12 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"Managed Software Update failed its preflight check.\nTry again later.", None))
|
||||
NSLocalizedString(
|
||||
(u"Managed Software Update failed its preflight "
|
||||
"check.\nTry again later."), None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
def noUpdatesAlert(self):
|
||||
if self._optionalInstalls:
|
||||
@@ -281,7 +297,9 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
NSLocalizedString(u"Optional software...", None),
|
||||
NSLocalizedString(u"Check again", None),
|
||||
NSLocalizedString(u"There is no new software for your computer at this time.", None))
|
||||
NSLocalizedString(
|
||||
u"There is no new software for your computer at this time.",
|
||||
None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
else:
|
||||
@@ -290,9 +308,12 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
NSLocalizedString(u"Check again", None),
|
||||
NSLocalizedString(u"There is no new software for your computer at this time.", None))
|
||||
NSLocalizedString(
|
||||
u"There is no new software for your computer at this time.",
|
||||
None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
|
||||
def checkForUpdates(self):
|
||||
@@ -321,9 +342,13 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"There is a configuration problem with the managed software installer. Could not start the update check process. Contact your systems administrator.", None))
|
||||
NSLocalizedString(
|
||||
(u"There is a configuration problem with the managed "
|
||||
"software installer. Could not start the update check "
|
||||
"process. Contact your systems administrator."), None))
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
def applicationDidBecomeActive_(self, sender):
|
||||
pass
|
||||
@@ -349,70 +374,78 @@ class MSUAppDelegate(NSObject):
|
||||
def getAvailableUpdates(self):
|
||||
updatelist = []
|
||||
installinfo = munki.getInstallInfo()
|
||||
munki_updates_contain_apple_items = \
|
||||
munki.munkiUpdatesContainAppleItems()
|
||||
if installinfo:
|
||||
updatelist = installinfo.get("managed_installs", [])
|
||||
for update in updatelist:
|
||||
force_install_after_date = update.get('force_install_after_date')
|
||||
if force_install_after_date:
|
||||
# insert installation deadline into description
|
||||
local_date = munki.discardTimeZoneFromDate(force_install_after_date)
|
||||
date_str = munki.stringFromDate(local_date)
|
||||
forced_date_text = NSLocalizedString(u"This item must be installed by ", None)
|
||||
description = update["description"]
|
||||
# prepend deadline info to description. This will fail if the description is HTML...
|
||||
update["description"] = forced_date_text + date_str + "\n\n" + description
|
||||
updatelist.extend(installinfo.get('managed_installs', []))
|
||||
if not munki_updates_contain_apple_items:
|
||||
appleupdates = munki.getAppleUpdates()
|
||||
updatelist.extend(appleupdates.get("AppleUpdates", []))
|
||||
for update in updatelist:
|
||||
force_install_after_date = update.get(
|
||||
'force_install_after_date')
|
||||
if force_install_after_date:
|
||||
# insert installation deadline into description
|
||||
local_date = munki.discardTimeZoneFromDate(
|
||||
force_install_after_date)
|
||||
date_str = munki.stringFromDate(local_date)
|
||||
forced_date_text = NSLocalizedString(
|
||||
u"This item must be installed by ", None)
|
||||
description = update["description"]
|
||||
# prepend deadline info to description. This will fail if
|
||||
# the description is HTML...
|
||||
update["description"] = (forced_date_text + date_str
|
||||
+ "\n\n" + description)
|
||||
|
||||
if installinfo.get("removals"):
|
||||
removallist = installinfo.get("removals")
|
||||
restartNeeded = False
|
||||
logoutNeeded = False
|
||||
showRemovalDetail = munki.getRemovalDetailPrefs()
|
||||
for item in removallist:
|
||||
restartAction = item.get("RestartAction")
|
||||
if restartAction in ["RequireRestart", "RecommendRestart"]:
|
||||
restartNeeded = True
|
||||
if restartAction in ["RequireLogout", "RecommendLogout"]:
|
||||
logoutNeeded = True
|
||||
if showRemovalDetail:
|
||||
item["display_name"] = ((item.get("display_name") or item.get("name", ""))
|
||||
+ NSLocalizedString(u" (will be removed)", None))
|
||||
item["description"] = NSLocalizedString(u"This item will be removed.", None)
|
||||
updatelist.append(item)
|
||||
if not showRemovalDetail:
|
||||
row = {}
|
||||
row["display_name"] = NSLocalizedString(u"Software removals", None)
|
||||
row["version"] = ""
|
||||
row["description"] = NSLocalizedString(u"Scheduled removal of managed software.", None)
|
||||
if restartNeeded:
|
||||
row["RestartAction"] = "RequireRestart"
|
||||
elif logoutNeeded:
|
||||
row["RestartAction"] = "RequireLogout"
|
||||
updatelist.append(row)
|
||||
if installinfo.get("removals"):
|
||||
removallist = installinfo.get("removals")
|
||||
restartNeeded = False
|
||||
logoutNeeded = False
|
||||
showRemovalDetail = munki.getRemovalDetailPrefs()
|
||||
for item in removallist:
|
||||
restartAction = item.get("RestartAction")
|
||||
if restartAction in ["RequireRestart", "RecommendRestart"]:
|
||||
restartNeeded = True
|
||||
if restartAction in ["RequireLogout", "RecommendLogout"]:
|
||||
logoutNeeded = True
|
||||
if showRemovalDetail:
|
||||
item["display_name"] = (
|
||||
(item.get("display_name") or item.get("name", ""))
|
||||
+ NSLocalizedString(u" (will be removed)", None))
|
||||
item["description"] = NSLocalizedString(
|
||||
u"This item will be removed.", None)
|
||||
updatelist.append(item)
|
||||
if not showRemovalDetail:
|
||||
row = {}
|
||||
row["display_name"] = NSLocalizedString(
|
||||
u"Software removals", None)
|
||||
row["version"] = ""
|
||||
row["description"] = NSLocalizedString(
|
||||
u"Scheduled removal of managed software.", None)
|
||||
if restartNeeded:
|
||||
row["RestartAction"] = "RequireRestart"
|
||||
elif logoutNeeded:
|
||||
row["RestartAction"] = "RequireLogout"
|
||||
updatelist.append(row)
|
||||
|
||||
if updatelist:
|
||||
self._sortUpdateList(updatelist)
|
||||
#self._sortUpdateList(updatelist)
|
||||
self._listofupdates = updatelist
|
||||
self.enableUpdateNowBtn_(YES)
|
||||
#self.performSelector_withObject_afterDelay_("enableUpdateNowBtn:", YES, 4)
|
||||
#self.performSelector_withObject_afterDelay_(
|
||||
# "enableUpdateNowBtn:", YES, 4)
|
||||
self.getOptionalInstalls()
|
||||
else:
|
||||
appleupdates = munki.getAppleUpdates()
|
||||
if appleupdates:
|
||||
munki.log("MSU", "appleupdates")
|
||||
self._listofupdates = appleupdates.get("AppleUpdates", [])
|
||||
self.update_view_controller.updateNowBtn.setEnabled_(YES)
|
||||
self.update_view_controller.optionalSoftwareBtn.setHidden_(YES)
|
||||
else:
|
||||
self._listofupdates = []
|
||||
self.update_view_controller.updateNowBtn.setEnabled_(NO)
|
||||
self.getOptionalInstalls()
|
||||
self._listofupdates = []
|
||||
self.update_view_controller.updateNowBtn.setEnabled_(NO)
|
||||
self.getOptionalInstalls()
|
||||
|
||||
|
||||
def buildOptionalInstallsData(self):
|
||||
table = []
|
||||
selfservedata = munki.readSelfServiceManifest()
|
||||
selfserve_installs = selfservedata.get('managed_installs',[])
|
||||
selfserve_uninstalls = selfservedata.get('managed_uninstalls',[])
|
||||
selfserve_installs = selfservedata.get('managed_installs', [])
|
||||
selfserve_uninstalls = selfservedata.get('managed_uninstalls', [])
|
||||
|
||||
for item in self._optionalInstalls:
|
||||
row = {}
|
||||
@@ -424,10 +457,12 @@ class MSUAppDelegate(NSObject):
|
||||
row['original_managed'] = (item['name'] in selfserve_installs)
|
||||
row['itemname'] = item['name']
|
||||
row['name'] = item.get("display_name") or item['name']
|
||||
row['version'] = munki.trimVersionString(item.get("version_to_install"))
|
||||
row['version'] = munki.trimVersionString(
|
||||
item.get("version_to_install"))
|
||||
row['description'] = item.get("description", "")
|
||||
if item.get("installer_item_size"):
|
||||
row['size'] = munki.humanReadable(item.get("installer_item_size"))
|
||||
row['size'] = munki.humanReadable(
|
||||
item.get("installer_item_size"))
|
||||
elif item.get("installed_size"):
|
||||
row['size'] = munki.humanReadable(item.get("installed_size"))
|
||||
else:
|
||||
@@ -472,7 +507,8 @@ class MSUAppDelegate(NSObject):
|
||||
# user selected for install
|
||||
optional_install_choices['managed_installs'].append(row['itemname'])
|
||||
elif row['original_managed']:
|
||||
# was managed, but user deselected it; we should remove it if possible
|
||||
# was managed, but user deselected it; we should remove it if
|
||||
# possible
|
||||
optional_install_choices['managed_uninstalls'].append(row['itemname'])
|
||||
munki.writeSelfServiceManifest(optional_install_choices)
|
||||
self.checkForUpdates()
|
||||
@@ -485,18 +521,22 @@ class MSUAppDelegate(NSObject):
|
||||
for item in self._listofupdates:
|
||||
row = {}
|
||||
row['image'] = self._emptyImage
|
||||
if item.get("RestartAction") == "RequireRestart" or item.get("RestartAction") == "RecommendRestart":
|
||||
if (item.get("RestartAction") == "RequireRestart"
|
||||
or item.get("RestartAction") == "RecommendRestart"):
|
||||
row['image'] = self._restartImage
|
||||
self.restart_required = True
|
||||
elif item.get("RestartAction") == "RequireLogout" or item.get("RestartAction") == "RecommendLogout":
|
||||
elif (item.get("RestartAction") == "RequireLogout"
|
||||
or item.get("RestartAction") == "RecommendLogout"):
|
||||
row['image'] = self._logoutImage
|
||||
self.logout_required = True
|
||||
if item.get("force_install_after_date"):
|
||||
row['image'] = self._exclamationImage
|
||||
row['name'] = item.get("display_name") or item.get("name","")
|
||||
row['version'] = munki.trimVersionString(item.get("version_to_install"))
|
||||
row['version'] = munki.trimVersionString(
|
||||
item.get("version_to_install"))
|
||||
if item.get("installer_item_size"):
|
||||
row['size'] = munki.humanReadable(item.get("installer_item_size"))
|
||||
row['size'] = munki.humanReadable(
|
||||
item.get("installer_item_size"))
|
||||
elif item.get("installed_size"):
|
||||
row['size'] = munki.humanReadable(item.get("installed_size"))
|
||||
else:
|
||||
@@ -510,18 +550,21 @@ class MSUAppDelegate(NSObject):
|
||||
if self.restart_required:
|
||||
self.update_view_controller.restartInfoFld.setStringValue_(
|
||||
NSLocalizedString(u"Restart will be required.", None))
|
||||
self.update_view_controller.restartImageFld.setImage_(self._restartImage)
|
||||
self.update_view_controller.restartImageFld.setImage_(
|
||||
self._restartImage)
|
||||
elif self.logout_required:
|
||||
self.update_view_controller.restartInfoFld.setStringValue_(
|
||||
NSLocalizedString(u"Logout will be required.", None))
|
||||
self.update_view_controller.restartImageFld.setImage_(self._logoutImage)
|
||||
self.update_view_controller.restartImageFld.setImage_(
|
||||
self._logoutImage)
|
||||
|
||||
|
||||
def forcedLogoutWarning(self, notification_obj):
|
||||
NSApp.activateIgnoringOtherApps_(True)
|
||||
info = notification_obj.userInfo()
|
||||
moreText = NSLocalizedString(
|
||||
u"\nAll pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now.", None)
|
||||
(u"\nAll pending updates will be installed. Unsaved work will be "
|
||||
"lost.\nYou may avoid the forced logout by logging out now."), None)
|
||||
logout_time = None
|
||||
if info:
|
||||
logout_time = info.get('logout_time')
|
||||
@@ -533,13 +576,19 @@ class MSUAppDelegate(NSObject):
|
||||
if time_til_logout > 55:
|
||||
deadline_str = munki.stringFromDate(logout_time)
|
||||
munki.log("user", "forced_logout_warning_initial")
|
||||
infoText = NSLocalizedString(u"A logout will be forced at approximately %s.", None) % deadline_str + moreText
|
||||
formatString = NSLocalizedString(
|
||||
u"A logout will be forced at approximately %s.", None)
|
||||
infoText = formatString % deadline_str + moreText
|
||||
elif time_til_logout > 0:
|
||||
munki.log("user", "forced_logout_warning_%s" % time_til_logout)
|
||||
infoText = NSLocalizedString(u"A logout will be forced in less than %s minutes.", None) % time_til_logout + moreText
|
||||
formatString = NSLocalizedString(
|
||||
u"A logout will be forced in less than %s minutes.", None)
|
||||
infoText = formatString % time_til_logout + moreText
|
||||
else:
|
||||
munki.log("user", "forced_logout_warning_final")
|
||||
infoText = NSLocalizedString(u"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost.", None)
|
||||
infoText = NSLocalizedString(
|
||||
(u"A logout will be forced in less than a minute.\nAll pending "
|
||||
"updates will be installed. Unsaved work will be lost."), None)
|
||||
|
||||
# Set the OK button to default, unless less than 5 minutes to logout
|
||||
# in which case only the Logout button should be displayed.
|
||||
@@ -561,7 +610,8 @@ class MSUAppDelegate(NSObject):
|
||||
NSApp.endSheet_(self._currentAlert.window())
|
||||
self._currentAlert = None
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
NSLocalizedString(u"Forced Logout for Mandatory Install", None),
|
||||
NSLocalizedString(
|
||||
u"Forced Logout for Mandatory Install", None),
|
||||
self._force_warning_btns[NSAlertDefaultReturn],
|
||||
self._force_warning_btns[NSAlertAlternateReturn],
|
||||
objc.nil,
|
||||
@@ -576,9 +626,14 @@ class MSUAppDelegate(NSObject):
|
||||
time_til_logout = deadline.timeIntervalSinceNow()
|
||||
if time_til_logout > 0:
|
||||
deadline_str = munki.stringFromDate(deadline)
|
||||
infoText = NSLocalizedString("One or more updates must be installed by %s. A logout may be forced if you wait too long to update.", None) % deadline_str
|
||||
formatString = NSLocalizedString(
|
||||
(u"One or more updates must be installed by %s. A logout "
|
||||
"may be forced if you wait too long to update."), None)
|
||||
infoText = formatString % deadline_str
|
||||
else:
|
||||
infoText = NSLocalizedString("One or more mandatory updates are overdue for installation. A logout will be forced soon.", None)
|
||||
infoText = NSLocalizedString(
|
||||
(u"One or more mandatory updates are overdue for "
|
||||
"installation. A logout will be forced soon."), None)
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
NSLocalizedString(u"Manadatory Updates Pending", None),
|
||||
NSLocalizedString(u"Show updates", None),
|
||||
@@ -587,7 +642,8 @@ class MSUAppDelegate(NSObject):
|
||||
infoText)
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.confirmLaterAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.confirmLaterAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
else:
|
||||
munki.log("user", "exit_later_clicked")
|
||||
NSApp.terminate_(self)
|
||||
@@ -601,7 +657,11 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Cancel", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString("There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out.", None))
|
||||
NSLocalizedString(
|
||||
(u"There are other users logged into this computer.\n"
|
||||
"Updating now could cause other users to lose their "
|
||||
"work.\n\nPlease try again later after the other users "
|
||||
"have logged out."), None))
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.multipleUserAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
@@ -611,17 +671,24 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Logout and update", None),
|
||||
NSLocalizedString(u"Cancel", None),
|
||||
objc.nil,
|
||||
NSLocalizedString(u"A restart is required after updating. Please be patient as there may be a short delay at the login window. Logout and update now?", None))
|
||||
NSLocalizedString(
|
||||
(u"A restart is required after updating. Please be patient "
|
||||
"as there may be a short delay at the login window. Logout "
|
||||
"and update now?"), None))
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
elif self.logout_required or munki.installRequiresLogout():
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
NSLocalizedString(u"Logout Required", None),
|
||||
NSLocalizedString(u"Logout and update", None),
|
||||
NSLocalizedString(u"Cancel", None),
|
||||
objc.nil,
|
||||
NSLocalizedString(u"A logout is required before updating. Please be patient as there may be a short delay at the login window. Logout and update now?", None))
|
||||
NSLocalizedString(
|
||||
(u"A logout is required before updating. Please be patient "
|
||||
"as there may be a short delay at the login window. Logout "
|
||||
"and update now?"), None))
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
@@ -631,10 +698,14 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Logout and update", None),
|
||||
NSLocalizedString(u"Cancel", None),
|
||||
NSLocalizedString(u"Update without logging out", None),
|
||||
NSLocalizedString(u"A logout is recommended before updating. Please be patient as there may be a short delay at the login window. Logout and update now?", None))
|
||||
NSLocalizedString(
|
||||
(u"A logout is recommended before updating. Please be "
|
||||
"patient as there may be a short delay at the login "
|
||||
"window. Logout and update now?"), None))
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
|
||||
def alertIfBlockingAppsRunning(self):
|
||||
@@ -650,15 +721,21 @@ class MSUAppDelegate(NSObject):
|
||||
running_apps = munki.getRunningBlockingApps(apps_to_check)
|
||||
if running_apps:
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
NSLocalizedString(u"Conflicting applications running", None),
|
||||
NSLocalizedString(
|
||||
u"Conflicting applications running", None),
|
||||
NSLocalizedString(u"OK", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"You must quit the following applications before proceeding with installation:\n\n%s", None) % '\n'.join(running_apps))
|
||||
NSLocalizedString(
|
||||
(u"You must quit the following applications before "
|
||||
"proceeding with installation:\n\n%s"), None)
|
||||
% '\n'.join(running_apps))
|
||||
munki.log("MSU", "conflicting_apps", ','.join(running_apps))
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.blockingAppsRunningAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.blockingAppsRunningAlertDidEnd_returnCode_contextInfo_,
|
||||
objc.nil)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -669,16 +746,20 @@ class MSUAppDelegate(NSObject):
|
||||
if (power_info.get('PowerSource') == 'Battery Power'
|
||||
and power_info.get('BatteryCharge', 0) < 50):
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
NSLocalizedString(u"Your computer is not connected to a power source.", None),
|
||||
NSLocalizedString(u"Continue", None),
|
||||
NSLocalizedString(u"Cancel", None),
|
||||
objc.nil,
|
||||
NSLocalizedString(u"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?", None))
|
||||
NSLocalizedString(
|
||||
u"Your computer is not connected to a power source.", None),
|
||||
NSLocalizedString(u"Continue", None),
|
||||
NSLocalizedString(u"Cancel", None),
|
||||
objc.nil,
|
||||
NSLocalizedString(
|
||||
(u"For best results, you should connect your computer to a "
|
||||
"power source before updating. Are you sure you want to "
|
||||
"continue the update?"), None))
|
||||
munki.log("MSU", "alert_on_battery_power")
|
||||
# making UI consistent with Apple Software Update...
|
||||
# set Cancel button to be activated by return key
|
||||
alert.buttons()[1].setKeyEquivalent_('\r')
|
||||
# set Ccontinue button to be activated by Escape key
|
||||
# set Continue button to be activated by Escape key
|
||||
alert.buttons()[0].setKeyEquivalent_(chr(27))
|
||||
buttonPressed = alert.runModal()
|
||||
if buttonPressed == NSAlertAlternateReturn:
|
||||
@@ -692,15 +773,20 @@ class MSUAppDelegate(NSObject):
|
||||
NSLocalizedString(u"Quit", None),
|
||||
objc.nil,
|
||||
objc.nil,
|
||||
NSLocalizedString(u"There is a configuration problem with the managed software installer. Could not start the install session. Contact your systems administrator.", None))
|
||||
NSLocalizedString(
|
||||
(u"There is a configuration problem with the managed "
|
||||
"software installer. Could not start the install session. "
|
||||
"Contact your systems administrator."), None))
|
||||
munki.log("MSU", "cannot_start")
|
||||
self._currentAlert = alert
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
|
||||
self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
self.mainWindowController.theWindow, self,
|
||||
self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def logoutAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
def logoutAlertDidEnd_returnCode_contextInfo_(
|
||||
self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
if returncode == NSAlertDefaultReturn:
|
||||
if self.alertIfRunnningOnBattery():
|
||||
@@ -736,17 +822,20 @@ class MSUAppDelegate(NSObject):
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def blockingAppsRunningAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
def blockingAppsRunningAlertDidEnd_returnCode_contextInfo_(
|
||||
self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def multipleUserAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
def multipleUserAlertDidEnd_returnCode_contextInfo_(
|
||||
self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def confirmLaterAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
def confirmLaterAlertDidEnd_returnCode_contextInfo_(
|
||||
self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
if returncode == NSAlertAlternateReturn:
|
||||
munki.log("user", "exit_later_clicked")
|
||||
@@ -754,7 +843,8 @@ class MSUAppDelegate(NSObject):
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def forceLogoutWarningDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
def forceLogoutWarningDidEnd_returnCode_contextInfo_(
|
||||
self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
btn_pressed = self._force_warning_btns.get(returncode)
|
||||
if btn_pressed == self._force_warning_logout_btn:
|
||||
@@ -765,7 +855,8 @@ class MSUAppDelegate(NSObject):
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def quitAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
def quitAlertDidEnd_returnCode_contextInfo_(
|
||||
self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
if returncode == NSAlertDefaultReturn:
|
||||
munki.log("user", "quit")
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<key>pfm_default</key>
|
||||
<false/>
|
||||
<key>pfm_description</key>
|
||||
<string>Set to true to only install updates from an Apple Software Update server. No munki repository is needed or used.</string>
|
||||
<string>Set to true to only install updates from an Apple Software Update server. No Munki repository is needed or used.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<key>pfm_default</key>
|
||||
<false/>
|
||||
<key>pfm_description</key>
|
||||
<string>Set to true to install updates from an Apple Software Update server, in addition to "regular" munki updates.</string>
|
||||
<string>Set to true to install updates from an Apple Software Update server, in addition to "regular" Munki updates.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
@@ -72,7 +72,7 @@
|
||||
<key>pfm_default</key>
|
||||
<string>http://munki/repo</string>
|
||||
<key>pfm_description</key>
|
||||
<string>Base URL for munki repository</string>
|
||||
<string>Base URL for Munki repository</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
@@ -88,7 +88,7 @@
|
||||
<key>pfm_default</key>
|
||||
<string>http://munki/repo/pkgs</string>
|
||||
<key>pfm_description</key>
|
||||
<string>Base URL for munki pkgs.
|
||||
<string>Base URL for Munki pkgs.
|
||||
Useful if your packages are served from a different server than your catalogs or manifests.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
@@ -105,7 +105,7 @@ Useful if your packages are served from a different server than your catalogs or
|
||||
<key>pfm_default</key>
|
||||
<string>http://munki/repo/catalogs</string>
|
||||
<key>pfm_description</key>
|
||||
<string>Base URL for munki catalogs.
|
||||
<string>Base URL for Munki catalogs.
|
||||
Useful if your catalogs are served from a different server than your packages or manifests.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
@@ -122,7 +122,7 @@ Useful if your catalogs are served from a different server than your packages or
|
||||
<key>pfm_default</key>
|
||||
<string>http://munki/repo/manifests</string>
|
||||
<key>pfm_description</key>
|
||||
<string>Base URL for munki manifests.
|
||||
<string>Base URL for Munki manifests.
|
||||
Useful if your manifests are served from a different server than your catalogs or packages.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
@@ -139,7 +139,7 @@ Useful if your manifests are served from a different server than your catalogs o
|
||||
<key>pfm_default</key>
|
||||
<string></string>
|
||||
<key>pfm_description</key>
|
||||
<string>Identifier for munki client. Usually is the same as a manifest name on the munki server.</string>
|
||||
<string>Identifier for Munki client. Usually is the same as a manifest name on the Munki server.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
@@ -155,7 +155,7 @@ Useful if your manifests are served from a different server than your catalogs o
|
||||
<key>pfm_default</key>
|
||||
<string>/Library/Managed Installs</string>
|
||||
<key>pfm_description</key>
|
||||
<string>Folder where munki keeps its data on the client.</string>
|
||||
<string>Folder where Munki keeps its data on the client.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
@@ -220,8 +220,8 @@ Set to 0 to have Managed Software Update notify every time a background check ru
|
||||
<key>pfm_default</key>
|
||||
<false/>
|
||||
<key>pfm_description</key>
|
||||
<string>Set to true to cause munki to use an SSL client certificate when communicating with the munki server.
|
||||
Requires an https:// URL for the munki repo.
|
||||
<string>Set to true to cause Munki to use an SSL client certificate when communicating with the Munki server.
|
||||
Requires an https:// URL for the Munki repo.
|
||||
Client cert should be named "munki.pem" and stored in a "certs" directory in the Managed Installs folder (typically /Library/Managed Installs).</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
@@ -238,7 +238,7 @@ Client cert should be named "munki.pem" and stored in a "certs" directory in the
|
||||
<key>pfm_default</key>
|
||||
<false/>
|
||||
<key>pfm_description</key>
|
||||
<string>Set to true to cause munki to use the CN of the client certificate as the Client Identifier.
|
||||
<string>Set to true to cause Munki to use the CN of the client certificate as the Client Identifier.
|
||||
Used in combination with the UseClientCertificate key.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
@@ -291,7 +291,7 @@ AdditionalHttpHeaders must be an array of strings with valid HTTP header format.
|
||||
<string>hash-strict</string>
|
||||
</array>
|
||||
<key>pfm_description</key>
|
||||
<string>Controls how munki verifies the integrity of downloaded packages.
|
||||
<string>Controls how Munki verifies the integrity of downloaded packages.
|
||||
Possible values are:
|
||||
none: No integrity check is performed.
|
||||
hash: Integrity check is performed if package info contains checksum information.
|
||||
@@ -322,13 +322,29 @@ Managed Software Update can still be manually invoked to discover and install up
|
||||
<key>pfm_name</key>
|
||||
<string>SuppressAutoInstall</string>
|
||||
<key>pfm_title</key>
|
||||
<string>Suppress Auto Install at loginwindow</string>
|
||||
<string>Suppress Auto Install</string>
|
||||
<key>pfm_type</key>
|
||||
<string>boolean</string>
|
||||
<key>pfm_default</key>
|
||||
<false/>
|
||||
<key>pfm_description</key>
|
||||
<string>If true, munki will not automatically install when the machine has no users logged in.</string>
|
||||
<string>If true, Munki will not automatically install any item.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>pfm_name</key>
|
||||
<string>SuppressLoginwindowInstall</string>
|
||||
<key>pfm_title</key>
|
||||
<string>Suppress Automatic Install at loginwindow</string>
|
||||
<key>pfm_type</key>
|
||||
<string>boolean</string>
|
||||
<key>pfm_default</key>
|
||||
<false/>
|
||||
<key>pfm_description</key>
|
||||
<string>If true, Munki will not automatically install when the machine has no users logged in.</string>
|
||||
<key>pfm_targets</key>
|
||||
<array>
|
||||
<string>system-managed</string>
|
||||
|
||||
@@ -190,6 +190,20 @@ def getInstallInfo():
|
||||
return plist
|
||||
|
||||
|
||||
def munkiUpdatesContainAppleItems():
|
||||
"""Return True if there are any Apple items in the list of updates"""
|
||||
installinfo = getInstallInfo()
|
||||
# check managed_installs
|
||||
for item in installinfo.get('managed_installs', []):
|
||||
if item.get('apple_item'):
|
||||
return True
|
||||
# check removals
|
||||
for item in installinfo.get('removals', []):
|
||||
if item.get('apple_item'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def thereAreUpdatesToBeForcedSoon(hours=72):
|
||||
'''Return True if any updates need to be installed within the next
|
||||
X hours, false otherwise'''
|
||||
|
||||
@@ -169,20 +169,20 @@ def runScript(script, display_name, runtype):
|
||||
|
||||
try:
|
||||
utils.verifyFileOnlyWritableByMunkiAndRoot(script)
|
||||
except utils.VerifyFilePermissionsError, e:
|
||||
except utils.VerifyFilePermissionsError, err:
|
||||
# preflight/postflight is insecure, but if the currently executing
|
||||
# file is insecure too we are no worse off.
|
||||
try:
|
||||
utils.verifyFileOnlyWritableByMunkiAndRoot(__file__)
|
||||
except utils.VerifyFilePermissionsError, e:
|
||||
except utils.VerifyFilePermissionsError, err:
|
||||
# OK, managedsoftwareupdate is insecure anyway - warn & execute.
|
||||
munkicommon.display_warning('Multiple munki executable scripts '
|
||||
'have insecure file permissions. Executing '
|
||||
'%s anyway. Error: %s' % (display_name, e))
|
||||
'%s anyway. Error: %s' % (display_name, err))
|
||||
else:
|
||||
# Just the preflight/postflight is insecure. Do not execute.
|
||||
munkicommon.display_warning('Skipping execution of %s due to '
|
||||
'insecure file permissions. Error: %s' % (display_name, e))
|
||||
'insecure file permissions. Error: %s' % (display_name, err))
|
||||
return result
|
||||
|
||||
try:
|
||||
@@ -197,16 +197,17 @@ def runScript(script, display_name, runtype):
|
||||
munkicommon.display_info('%s stderr: %s' % (display_name, stderr))
|
||||
except utils.ScriptNotFoundError:
|
||||
pass # script is not required, so pass
|
||||
except utils.RunExternalScriptError, e:
|
||||
munkicommon.display_warning(str(e))
|
||||
except utils.RunExternalScriptError, err:
|
||||
munkicommon.display_warning(str(err))
|
||||
return result
|
||||
|
||||
|
||||
def doInstallTasks(only_unattended=False):
|
||||
def doInstallTasks(do_apple_updates, only_unattended=False):
|
||||
"""Perform our installation/removal tasks.
|
||||
|
||||
Args:
|
||||
only_unattended: Boolean. If True, only do unattended_(un)install items.
|
||||
do_apple_updates: Boolean. If True, install Apple updates
|
||||
only_unattended: Boolean. If True, only do unattended_(un)install items.
|
||||
|
||||
Returns:
|
||||
Boolean. True if a restart is required, False otherwise.
|
||||
@@ -218,31 +219,20 @@ def doInstallTasks(only_unattended=False):
|
||||
clearLastNotifiedDate()
|
||||
|
||||
need_to_restart = False
|
||||
# munki updates take priority over Apple Updates, because
|
||||
# a munki install or (especially) removal could make a
|
||||
# pending Apple update no longer necessary or even complicate
|
||||
# or prevent the removal of another item.
|
||||
# Therefore we only install Apple updates if there are no
|
||||
# pending munki updates.
|
||||
|
||||
if munkiUpdatesAvailable():
|
||||
# install munki updates
|
||||
try:
|
||||
need_to_restart = installer.run(only_unattended=only_unattended)
|
||||
except:
|
||||
munkicommon.display_error(
|
||||
'Unexpected error in munkilib.installer:')
|
||||
munkicommon.display_error('Unexpected error in munkilib.installer:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
|
||||
# clear any Apple update info since it may no longer
|
||||
# be relevant
|
||||
if not only_unattended:
|
||||
appleupdates.clearAppleUpdateInfo()
|
||||
elif ((munkicommon.pref('InstallAppleSoftwareUpdates') or
|
||||
munkicommon.pref('AppleSoftwareUpdatesOnly'))
|
||||
and not only_unattended):
|
||||
if do_apple_updates and not only_unattended:
|
||||
# (note we do not currently support doing Apple updates
|
||||
# as unattended_installs. Support may be added in the future)
|
||||
# are we supposed to handle Apple Software Updates?
|
||||
try:
|
||||
need_to_restart = appleupdates.installAppleUpdates()
|
||||
@@ -317,6 +307,28 @@ def munkiUpdatesAvailable():
|
||||
munkicommon.display_error('Install info at %s is invalid.' %
|
||||
installinfo)
|
||||
return updatesavailable
|
||||
|
||||
|
||||
def munkiUpdatesContainAppleItems():
|
||||
"""Return True if there are any Apple items in the list of updates"""
|
||||
installinfo = os.path.join(munkicommon.pref('ManagedInstallDir'),
|
||||
'InstallInfo.plist')
|
||||
if os.path.exists(installinfo):
|
||||
try:
|
||||
plist = FoundationPlist.readPlist(installinfo)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
munkicommon.display_error('Install info at %s is invalid.' %
|
||||
installinfo)
|
||||
else:
|
||||
# check managed_installs
|
||||
for item in plist.get('managed_installs', []):
|
||||
if item.get('apple_item'):
|
||||
return True
|
||||
# check removals
|
||||
for item in plist.get('removals', []):
|
||||
if item.get('apple_item'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def recordUpdateCheckResult(result):
|
||||
@@ -557,7 +569,7 @@ def main():
|
||||
munkicommon.log("### Starting managedsoftwareupdate run: %s ###" % runtype)
|
||||
if options.verbose:
|
||||
print 'Managed Software Update Tool'
|
||||
print 'Copyright 2010-2012 The Munki Project'
|
||||
print 'Copyright 2010-2013 The Munki Project'
|
||||
print 'http://code.google.com/p/munki\n'
|
||||
|
||||
munkicommon.display_status_major('Starting...')
|
||||
@@ -660,43 +672,63 @@ def main():
|
||||
|
||||
if updatecheckresult is not None:
|
||||
recordUpdateCheckResult(updatecheckresult)
|
||||
|
||||
|
||||
updatesavailable = munkiUpdatesAvailable()
|
||||
appleupdatesavailable = False
|
||||
if (not updatesavailable and not options.installonly and
|
||||
not munkicommon.stopRequested()):
|
||||
# if there are no munki updates,
|
||||
# are we supposed to check for and install Apple Software Updates?
|
||||
if ((munkicommon.pref('InstallAppleSoftwareUpdates') or
|
||||
applesoftwareupdatesonly) and not options.munkipkgsonly):
|
||||
|
||||
# should we do Apple Software updates this run?
|
||||
if applesoftwareupdatesonly:
|
||||
# admin told us to only do Apple updates this run
|
||||
should_do_apple_updates = True
|
||||
elif options.munkipkgsonly:
|
||||
# admin told us to skip Apple updates for this run
|
||||
should_do_apple_updates = False
|
||||
elif munkiUpdatesContainAppleItems():
|
||||
# shouldn't run Software Update if we're doing Apple items
|
||||
# with Munki items
|
||||
should_do_apple_updates = False
|
||||
else:
|
||||
# check the normal preferences
|
||||
should_do_apple_updates = munkicommon.pref(
|
||||
'InstallAppleSoftwareUpdates')
|
||||
|
||||
if should_do_apple_updates:
|
||||
if (not options.installonly and not munkicommon.stopRequested()):
|
||||
force_update_check = False
|
||||
if (options.manualcheck or runtype == 'checkandinstallatstartup'):
|
||||
force_update_check = True
|
||||
if (runtype == 'custom' and applesoftwareupdatesonly):
|
||||
force_update_check = True
|
||||
try:
|
||||
appleupdatesavailable = \
|
||||
appleupdates.appleSoftwareUpdatesAvailable(
|
||||
forcecheck=(options.manualcheck or
|
||||
runtype == 'checkandinstallatstartup' or
|
||||
(runtype == 'custom' and
|
||||
applesoftwareupdatesonly)))
|
||||
forcecheck=force_update_check)
|
||||
except:
|
||||
munkicommon.display_error('Unexpected error in appleupdates:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
|
||||
if (not updatesavailable and options.installonly and
|
||||
not options.munkipkgsonly and
|
||||
(munkicommon.pref('InstallAppleSoftwareUpdates') or
|
||||
applesoftwareupdatesonly)):
|
||||
# just look and see if there are already downloaded Apple updates
|
||||
# to install; don't run softwareupdate to check with Apple
|
||||
try:
|
||||
appleupdatesavailable = \
|
||||
appleupdates.appleSoftwareUpdatesAvailable(suppresscheck=True)
|
||||
except:
|
||||
munkicommon.display_error('Unexpected error in appleupdates:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
|
||||
if options.installonly:
|
||||
# just look and see if there are already downloaded Apple updates
|
||||
# to install; don't run softwareupdate to check with Apple
|
||||
try:
|
||||
appleupdatesavailable = \
|
||||
appleupdates.appleSoftwareUpdatesAvailable(
|
||||
suppresscheck=True)
|
||||
except:
|
||||
munkicommon.display_error('Unexpected error in appleupdates:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
|
||||
if not options.installonly:
|
||||
# display any available update information
|
||||
if updatecheckresult:
|
||||
updatecheck.displayUpdateInfo()
|
||||
if appleupdatesavailable:
|
||||
appleupdates.displayAppleUpdateInfo()
|
||||
|
||||
# send a notification event so MSU can update its display
|
||||
# if needed
|
||||
sendUpdateNotification()
|
||||
@@ -708,7 +740,7 @@ def main():
|
||||
if updatesavailable or appleupdatesavailable:
|
||||
if options.installonly or options.logoutinstall:
|
||||
# just install
|
||||
mustrestart = doInstallTasks()
|
||||
mustrestart = doInstallTasks(appleupdatesavailable)
|
||||
elif options.auto:
|
||||
if not munkicommon.currentGUIusers(): # no GUI users
|
||||
if munkicommon.pref('SuppressAutoInstall'):
|
||||
@@ -729,7 +761,7 @@ def main():
|
||||
munkicommon.log('Installing only items marked unattended '
|
||||
'because SuppressLoginwindowInstall is '
|
||||
'true.')
|
||||
doInstallTasks(only_unattended=True)
|
||||
doInstallTasks(appleupdatesavailable, only_unattended=True)
|
||||
elif getIdleSeconds() < 10:
|
||||
munkicommon.log('Skipping auto install at loginwindow '
|
||||
'because system is not idle '
|
||||
@@ -738,7 +770,7 @@ def main():
|
||||
# no GUI users, system is idle, so we can install
|
||||
# but first, enable status output over login window
|
||||
munkicommon.munkistatusoutput = True
|
||||
mustrestart = doInstallTasks()
|
||||
mustrestart = doInstallTasks(appleupdatesavailable)
|
||||
|
||||
else: # there are GUI users
|
||||
if munkicommon.pref('SuppressAutoInstall'):
|
||||
@@ -750,7 +782,7 @@ def main():
|
||||
# don't require a logout
|
||||
unused_action = updatecheck.checkForceInstallPackages()
|
||||
# install anything that can be done unattended
|
||||
doInstallTasks(only_unattended=True)
|
||||
doInstallTasks(appleupdatesavailable, only_unattended=True)
|
||||
|
||||
# send a notification event so MSU can update its display
|
||||
# if needed
|
||||
|
||||
@@ -572,7 +572,8 @@ def configure():
|
||||
('repo_url',
|
||||
'Repo fileshare URL (example: afp://munki.example.com/repo)'),
|
||||
('pkginfo_extension', 'pkginfo extension (Example: .plist)'),
|
||||
('editor', 'pkginfo editor (examples: /usr/bin/vi or TextMate.app)')]:
|
||||
('editor', 'pkginfo editor (examples: /usr/bin/vi or TextMate.app)'),
|
||||
('default_catalog', 'Default catalog to use (example: testing)')]:
|
||||
|
||||
_prefs[key] = raw_input_with_default('%15s: ' % prompt, pref(key))
|
||||
|
||||
@@ -653,7 +654,7 @@ def main():
|
||||
APPLEMETADATA = True
|
||||
# Verify that arguments, presumed to be for
|
||||
# 'makepkginfo' are valid and return installer_item
|
||||
installer_item = makePkgInfo(arguments, True)
|
||||
installer_item = makePkgInfo(options=arguments, test_mode=True)
|
||||
if not installer_item and not APPLEMETADATA:
|
||||
cleanupAndExit(-1)
|
||||
|
||||
@@ -686,7 +687,7 @@ def main():
|
||||
exit(-1)
|
||||
|
||||
if not APPLEMETADATA:
|
||||
if os.path.isdir(installer_item):
|
||||
if os.path.isdir(installer_item): # Start of indent
|
||||
if munkicommon.hasValidDiskImageExt(installer_item):
|
||||
# a directory named foo.dmg or foo.iso!
|
||||
print >> sys.stderr, '%s is an unknown type.' % installer_item
|
||||
@@ -703,7 +704,13 @@ def main():
|
||||
|
||||
# append the installer_item to arguments which
|
||||
# may have changed if bundle was wrapped into dmg
|
||||
arguments.append(installer_item)
|
||||
arguments.append(installer_item) # End of indent
|
||||
|
||||
# if catalog/catalogs have not been explictly specified via command-line,
|
||||
# append our default catalog
|
||||
if not '--catalog' in arguments and not '-c' in arguments:
|
||||
default_catalog = pref('default_catalog') or 'testing'
|
||||
arguments.extend(['--catalog', default_catalog])
|
||||
pkginfo = makePkgInfo(arguments, False)
|
||||
if not pkginfo:
|
||||
# makepkginfo returned an error
|
||||
|
||||
@@ -441,8 +441,8 @@ class AppleUpdates(object):
|
||||
if fileurl.startswith('file://localhost'):
|
||||
fileurl = fileurl[len('file://localhost'):]
|
||||
pathname = urllib2.unquote(fileurl).rstrip('/')
|
||||
appname = os.path.basename(pathname)
|
||||
blocking_apps.append(appname)
|
||||
executable = munkicommon.getAppBundleExecutable(pathname)
|
||||
blocking_apps.append(executable or pathname)
|
||||
|
||||
return blocking_apps
|
||||
|
||||
@@ -721,7 +721,13 @@ class AppleUpdates(object):
|
||||
munkicommon.display_info('Skipping Apple Software Update check '
|
||||
'because sucatalog is unchanged, installed Apple packages are '
|
||||
'unchanged and we recently did a full check.')
|
||||
return False
|
||||
#return False
|
||||
# instead of returning False, return True if we have cached updates
|
||||
# False otherwise
|
||||
if self.GetSoftwareUpdateInfo():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
product_ids = self.GetAvailableUpdateProductIDs()
|
||||
if not product_ids:
|
||||
@@ -840,11 +846,13 @@ class AppleUpdates(object):
|
||||
'Error reading: %s', self.apple_updates_plist)
|
||||
return
|
||||
apple_updates = pl_dict.get('AppleUpdates', [])
|
||||
if apple_updates:
|
||||
munkicommon.report['AppleUpdates'] = apple_updates
|
||||
munkicommon.display_info(
|
||||
'The following Apple Software Updates are available to '
|
||||
'install:')
|
||||
if not apple_updates:
|
||||
munkicommon.display_info('No available Apple Software Updates.')
|
||||
return
|
||||
munkicommon.report['AppleUpdates'] = apple_updates
|
||||
munkicommon.display_info(
|
||||
'The following Apple Software Updates are available to '
|
||||
'install:')
|
||||
for item in apple_updates:
|
||||
munkicommon.display_info(
|
||||
' + %s-%s' % (
|
||||
@@ -1264,11 +1272,8 @@ class AppleUpdates(object):
|
||||
return False
|
||||
if munkicommon.stopRequested():
|
||||
return False
|
||||
if self.WriteAppleUpdatesFile():
|
||||
self.DisplayAppleUpdateInfo()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return self.WriteAppleUpdatesFile()
|
||||
|
||||
|
||||
def SoftwareUpdateList(self):
|
||||
"""Returns a list of str update names using softwareupdate -l."""
|
||||
@@ -1325,3 +1330,8 @@ def appleSoftwareUpdatesAvailable(forcecheck=False, suppresscheck=False):
|
||||
"""Method for drop-in appleupdates replacement; see primary method docs."""
|
||||
return getAppleUpdatesInstance().AppleSoftwareUpdatesAvailable(
|
||||
force_check=forcecheck, suppress_check=suppresscheck)
|
||||
|
||||
|
||||
def displayAppleUpdateInfo():
|
||||
"""Method for drop-in appleupdates replacement; see primary method docs."""
|
||||
getAppleUpdatesInstance().DisplayAppleUpdateInfo()
|
||||
|
||||
@@ -1221,6 +1221,23 @@ def getVersionString(plist, key=None):
|
||||
return ''
|
||||
|
||||
|
||||
def getAppBundleExecutable(bundlepath):
|
||||
"""Returns path to the actual executable in an app bundle or None"""
|
||||
infoPlist = os.path.join(bundlepath, 'Contents', 'Info.plist')
|
||||
if os.path.exists(infoPlist):
|
||||
plist = FoundationPlist.readPlist(infoPlist)
|
||||
if 'CFBundleExecutable' in plist:
|
||||
executable = plist['CFBundleExecutable']
|
||||
elif 'CFBundleName' in plist:
|
||||
executable = plist['CFBundleName']
|
||||
else:
|
||||
executable = os.path.splitext(os.path.basename(bundlepath))[0]
|
||||
executable_path = os.path.join(bundlepath, 'Contents/MacOS', executable)
|
||||
if os.path.exists(executable_path):
|
||||
return executable_path
|
||||
return None
|
||||
|
||||
|
||||
def getBundleVersion(bundlepath, key=None):
|
||||
"""
|
||||
Returns version number from a bundle.
|
||||
|
||||
@@ -1451,6 +1451,24 @@ def lookForUpdatesForVersion(itemname, itemversion, cataloglist):
|
||||
return update_list
|
||||
|
||||
|
||||
def isAppleItem(item_pl):
|
||||
"""Returns True if the item to be installed or removed appears to be from
|
||||
Apple. If we are installing or removing any Apple items in a check/install
|
||||
cycle, we skip checking/installing Apple updates from an Apple Software
|
||||
Update server so we don't stomp on each other"""
|
||||
# check receipts
|
||||
for receipt in item_pl.get('receipts', []):
|
||||
if receipt.get('packageid', '').startswith('com.apple.'):
|
||||
return True
|
||||
# check installs items
|
||||
for install_item in item_pl.get('installs', []):
|
||||
if install_item.get('CFBundleIdentifier', '').startswith('com.apple.'):
|
||||
return True
|
||||
# if we get here, no receipts or installs items have Apple
|
||||
# identifiers
|
||||
return False
|
||||
|
||||
|
||||
def processManagedUpdate(manifestitem, cataloglist, installinfo):
|
||||
"""Process a managed_updates item to see if it is installed, and if so,
|
||||
if it needs an update.
|
||||
@@ -1746,12 +1764,19 @@ def processInstall(manifestitem, cataloglist, installinfo):
|
||||
'postinstall_script',
|
||||
'items_to_copy', # used w/ copy_from_dmg
|
||||
'copy_local', # used w/ AdobeCS5 Updaters
|
||||
'force_install_after_date']
|
||||
'force_install_after_date',
|
||||
'apple_item']
|
||||
|
||||
for key in optional_keys:
|
||||
if key in item_pl:
|
||||
iteminfo[key] = item_pl[key]
|
||||
|
||||
if not 'apple_item' in iteminfo:
|
||||
# admin did not explictly mark this item; let's determine if
|
||||
# it's from Apple
|
||||
if isAppleItem(item_pl):
|
||||
iteminfo['apple_item'] = True
|
||||
|
||||
installinfo['managed_installs'].append(iteminfo)
|
||||
|
||||
update_list = []
|
||||
@@ -2186,6 +2211,9 @@ def processRemoval(manifestitem, cataloglist, installinfo):
|
||||
for key in optionalKeys:
|
||||
if key in uninstall_item:
|
||||
iteminfo[key] = uninstall_item[key]
|
||||
|
||||
if isAppleItem(item_pl):
|
||||
iteminfo['apple_item'] = True
|
||||
|
||||
if packagesToRemove:
|
||||
# remove references for each package
|
||||
@@ -2866,10 +2894,30 @@ def check(client_id='', localmanifestpath=None):
|
||||
munkicommon.report['ItemsToRemove'] = \
|
||||
installinfo.get('removals', [])
|
||||
|
||||
munkicommon.savereport()
|
||||
munkicommon.log('### End managed software check ###')
|
||||
|
||||
installcount = len(installinfo.get('managed_installs', []))
|
||||
removalcount = len(installinfo.get('removals', []))
|
||||
|
||||
if installcount or removalcount:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def displayUpdateInfo():
|
||||
'''Prints info about available updates'''
|
||||
ManagedInstallDir = munkicommon.pref('ManagedInstallDir')
|
||||
installinfopath = os.path.join(ManagedInstallDir, 'InstallInfo.plist')
|
||||
try:
|
||||
installinfo = FoundationPlist.readPlist(installinfopath)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
installinfo = {}
|
||||
|
||||
installcount = len(installinfo.get('managed_installs', []))
|
||||
removalcount = len(installinfo.get('removals', []))
|
||||
|
||||
munkicommon.log('')
|
||||
if installcount:
|
||||
munkicommon.display_info('')
|
||||
munkicommon.display_info(
|
||||
@@ -2906,14 +2954,6 @@ def check(client_id='', localmanifestpath=None):
|
||||
munkicommon.display_info(
|
||||
'No changes to managed software are available.')
|
||||
|
||||
munkicommon.savereport()
|
||||
munkicommon.log('### End managed software check ###')
|
||||
|
||||
if installcount or removalcount:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def subtractTimeZoneOffsetFromDate(the_date):
|
||||
"""Input: NSDate object
|
||||
|
||||
@@ -949,7 +949,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.mox.StubOutWithMock(appleupdates.hashlib, 'sha256')
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'pref')
|
||||
|
||||
cmd = ['/usr/sbin/pkgutil', '--regexp', '-pkg-info-plist',
|
||||
cmd = ['/usr/sbin/pkgutil', '--regexp', '--pkg-info-plist',
|
||||
'com\.apple\.*']
|
||||
output = 'output!'
|
||||
|
||||
@@ -1109,7 +1109,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
'unchanged, installed Apple packages are unchanged and we '
|
||||
'recently did a full check.')
|
||||
self.mox.ReplayAll()
|
||||
self.assertFalse(self.au.CheckForSoftwareUpdates(force_check=False))
|
||||
self.assertTrue(self.au.CheckForSoftwareUpdates(force_check=False))
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def testCheckForSoftwareUpdatesWhenUpdateListEmpty(self):
|
||||
@@ -1508,7 +1508,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'pref')
|
||||
self.mox.StubOutWithMock(self.au, 'CheckForSoftwareUpdates')
|
||||
self.mox.StubOutWithMock(self.au, 'WriteAppleUpdatesFile')
|
||||
self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
#self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
# Cannot stub out the builtin NSDate methods, so stub the entire module.
|
||||
mock_nsdate_module = self.mox.CreateMockAnything()
|
||||
self.mox.StubOutWithMock(appleupdates, 'NSDate', mock_nsdate_module)
|
||||
@@ -1531,7 +1531,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.au.CheckForSoftwareUpdates(force_check=True).AndReturn(True)
|
||||
appleupdates.munkicommon.stopRequested().AndReturn(False)
|
||||
self.au.WriteAppleUpdatesFile().AndReturn(True)
|
||||
self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
#self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
out = self.au.AppleSoftwareUpdatesAvailable(
|
||||
@@ -1545,7 +1545,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'pref')
|
||||
self.mox.StubOutWithMock(self.au, 'CheckForSoftwareUpdates')
|
||||
self.mox.StubOutWithMock(self.au, 'WriteAppleUpdatesFile')
|
||||
self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
#self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
# Cannot stub out the builtin NSDate methods, so stub the entire module.
|
||||
mock_nsdate_module = self.mox.CreateMockAnything()
|
||||
self.mox.StubOutWithMock(appleupdates, 'NSDate', mock_nsdate_module)
|
||||
@@ -1568,7 +1568,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.au.CheckForSoftwareUpdates(force_check=False).AndReturn(True)
|
||||
appleupdates.munkicommon.stopRequested().AndReturn(False)
|
||||
self.au.WriteAppleUpdatesFile().AndReturn(True)
|
||||
self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
#self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
out = self.au.AppleSoftwareUpdatesAvailable(
|
||||
@@ -1582,7 +1582,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'pref')
|
||||
self.mox.StubOutWithMock(self.au, 'CheckForSoftwareUpdates')
|
||||
self.mox.StubOutWithMock(self.au, 'WriteAppleUpdatesFile')
|
||||
self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
#self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
|
||||
# use a date string that will not parse correctly.
|
||||
appleupdates.munkicommon.pref('LastAppleSoftwareUpdateCheck').AndReturn(
|
||||
@@ -1591,7 +1591,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.au.CheckForSoftwareUpdates(force_check=True).AndReturn(True)
|
||||
appleupdates.munkicommon.stopRequested().AndReturn(False)
|
||||
self.au.WriteAppleUpdatesFile().AndReturn(True)
|
||||
self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
#self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
out = self.au.AppleSoftwareUpdatesAvailable(
|
||||
@@ -1604,12 +1604,12 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'stopRequested')
|
||||
self.mox.StubOutWithMock(self.au, 'CheckForSoftwareUpdates')
|
||||
self.mox.StubOutWithMock(self.au, 'WriteAppleUpdatesFile')
|
||||
self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
#self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
|
||||
self.au.CheckForSoftwareUpdates(force_check=True).AndReturn(True)
|
||||
appleupdates.munkicommon.stopRequested().AndReturn(False)
|
||||
self.au.WriteAppleUpdatesFile().AndReturn(True)
|
||||
self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
#self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
out = self.au.AppleSoftwareUpdatesAvailable(
|
||||
@@ -1617,15 +1617,15 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
self.assertTrue(out)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def testAppleSoftwareUpdatesAvailableSupressCheck(self):
|
||||
def testAppleSoftwareUpdatesAvailableSuppressCheck(self):
|
||||
"""Tests AppleSoftwareUpdatesAvailable() with suppress_check=True."""
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'stopRequested')
|
||||
self.mox.StubOutWithMock(self.au, 'WriteAppleUpdatesFile')
|
||||
self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
#self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
|
||||
appleupdates.munkicommon.stopRequested().AndReturn(False)
|
||||
self.au.WriteAppleUpdatesFile().AndReturn(True)
|
||||
self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
#self.au.DisplayAppleUpdateInfo().AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
out = self.au.AppleSoftwareUpdatesAvailable(
|
||||
@@ -1637,7 +1637,7 @@ class TestAppleUpdates(mox.MoxTestBase):
|
||||
"""Tests AppleSoftwareUpdatesAvailable() when no updates available."""
|
||||
self.mox.StubOutWithMock(appleupdates.munkicommon, 'stopRequested')
|
||||
self.mox.StubOutWithMock(self.au, 'WriteAppleUpdatesFile')
|
||||
self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
#self.mox.StubOutWithMock(self.au, 'DisplayAppleUpdateInfo')
|
||||
|
||||
appleupdates.munkicommon.stopRequested().AndReturn(False)
|
||||
self.au.WriteAppleUpdatesFile().AndReturn(False)
|
||||
|
||||
Reference in New Issue
Block a user