mirror of
https://github.com/munki/munki.git
synced 2026-03-13 13:08:44 -05:00
unattended_install in metadata no longer applied if a RestartAction exists (in original item or metadata); unattended Apple updates are skipped if a blocking application is running; installlist, holding Apple update installation results, only holds items which are eligible for unattended installation
Mostly, this commit involves improving upon expected functionality of an unattend Apple update installation. Munki performs a "suppressed" Apple software update check post installation run if a GUI user is logged in such that the user will be prompted to install any remaining updates. This is inline with munki's behavior for munki packages. blocking_applications can now accept items which are full paths in case an admin wishes to be specific regarding the location of a running, blocking application and/or executable. Also of note is a fix for previously broken blocking_application checking for Apple updates, both in MSU and managaedsoftwareupdate.
This commit is contained in:
@@ -442,7 +442,11 @@ def getRunningBlockingApps(appnames):
|
||||
filemanager = NSFileManager.alloc().init()
|
||||
for appname in appnames:
|
||||
matching_items = []
|
||||
if appname.endswith('.app'):
|
||||
if appname.startswith('/'):
|
||||
# search by exact path
|
||||
matching_items = [item for item in proc_list
|
||||
if item == appname]
|
||||
elif appname.endswith('.app'):
|
||||
# search by filename
|
||||
matching_items = [item for item in proc_list
|
||||
if '/'+ appname + '/Contents/MacOS/' in item]
|
||||
|
||||
@@ -796,9 +796,17 @@ def main():
|
||||
mustlogout = True
|
||||
|
||||
# it's possible that we no longer have any available updates
|
||||
# so we need to check InstallInfo.plist again
|
||||
# however Apple Updates have not been affected by the
|
||||
# unattended install tasks (so that check is still valid).
|
||||
# so we need to check InstallInfo.plist and
|
||||
# AppleUpdates.plist again
|
||||
try:
|
||||
appleupdatesavailable = \
|
||||
appleupdates.appleSoftwareUpdatesAvailable(
|
||||
suppresscheck=True, client_id=options.id)
|
||||
except:
|
||||
munkicommon.display_error('Unexpected error in appleupdates:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
if appleupdatesavailable or munkiUpdatesAvailable():
|
||||
# set a flag to notify the user of available updates
|
||||
# after we conclude this run.
|
||||
|
||||
@@ -443,7 +443,9 @@ class AppleUpdates(object):
|
||||
if fileurl.startswith('file://localhost'):
|
||||
fileurl = fileurl[len('file://localhost'):]
|
||||
pathname = urllib2.unquote(fileurl).rstrip('/')
|
||||
dirname = os.path.dirname(pathname)
|
||||
executable = munkicommon.getAppBundleExecutable(pathname)
|
||||
executable = executable[len(dirname + '/'):]
|
||||
blocking_apps.append(executable or pathname)
|
||||
|
||||
return blocking_apps
|
||||
@@ -1174,15 +1176,19 @@ class AppleUpdates(object):
|
||||
# Creating an 'unattended_install' filtered catalog
|
||||
# against the existing filtered catalog is not an option as
|
||||
# cached downloads are purged if they do not exist in the
|
||||
# filtered catalog. Instead, get a list of updates
|
||||
# that are eligible for unattended_install.
|
||||
unattended_install_items = self.GetUnattendedInstalls()
|
||||
# filtered catalog. Instead, get a list of updates, and their
|
||||
# product_ids, that are eligible for unattended_install.
|
||||
unattended_install_items, unattended_install_product_ids = \
|
||||
self.GetUnattendedInstalls()
|
||||
restartneeded = False
|
||||
if not unattended_install_items:
|
||||
return False
|
||||
else:
|
||||
msg = 'Installing available Apple Software Updates...'
|
||||
restartneeded = self.IsRestartNeeded()
|
||||
|
||||
self._ResetMunkiStatusAndDisplayMessage(msg)
|
||||
|
||||
restartneeded = self.IsRestartNeeded()
|
||||
# use our filtered local catalog
|
||||
if not os.path.exists(self.local_catalog_path):
|
||||
munkicommon.display_error(
|
||||
@@ -1200,6 +1206,11 @@ class AppleUpdates(object):
|
||||
if only_unattended:
|
||||
# Append list of unattended_install items
|
||||
su_options.extend(unattended_install_items)
|
||||
# Filter installist to only include items
|
||||
# which we're attempting to install
|
||||
installlist = [item for item in installlist
|
||||
if item.get('productKey') in
|
||||
unattended_install_product_ids]
|
||||
else:
|
||||
# We're installing all available updates
|
||||
su_options.extend(['-a'])
|
||||
@@ -1373,28 +1384,50 @@ class AppleUpdates(object):
|
||||
'\tSkipping %s \'%s\', \'%s\' is preferred.'
|
||||
% (key, metadata[key], item[key]))
|
||||
continue
|
||||
elif key == 'unattended_install':
|
||||
# Don't apply unattended_install if a RestartAction exists
|
||||
# in either the original item or metadata
|
||||
if metadata.get('RestartAction', 'None') != 'None' or \
|
||||
item.get('RestartAction', 'None') != 'None':
|
||||
munkicommon.display_warning(
|
||||
'\tIgnoring unattended_install key '
|
||||
'because RestartAction is %s.'
|
||||
% metadata.get('RestartAction'))
|
||||
continue
|
||||
munkicommon.display_debug2('\tApplying %s...' % key)
|
||||
item[key] = metadata[key]
|
||||
return item
|
||||
|
||||
def GetUnattendedInstalls(self):
|
||||
"""Processes AppleUpdates.plist to return a list
|
||||
consisting of NAME-VERSION formatted items
|
||||
which have been marked as unattended_installs.
|
||||
of NAME-VERSION formatted items and a list of product_ids
|
||||
which are elgible for unattended installation.
|
||||
"""
|
||||
item_list = []
|
||||
product_ids = []
|
||||
try:
|
||||
pl_dict = FoundationPlist.readPlist(self.apple_updates_plist)
|
||||
except FoundationPlist.FoundationPlistException:
|
||||
munkicommon.display_error(
|
||||
'Error reading: %s', self.apple_updates_plist)
|
||||
return item_list
|
||||
return item_list, product_ids
|
||||
apple_updates = pl_dict.get('AppleUpdates', [])
|
||||
for item in apple_updates:
|
||||
if item.get('unattended_install'):
|
||||
install_item = item['name'] + '-' + item['version_to_install']
|
||||
item_list.append(install_item)
|
||||
return item_list
|
||||
if munkicommon.blockingApplicationsRunning(item):
|
||||
munkicommon.display_detail(
|
||||
'Skipping unattended install of %s because '
|
||||
'blocking application(s) running.'
|
||||
% item['display_name'])
|
||||
continue
|
||||
install_item = item['name'] + '-' + item['version_to_install']
|
||||
item_list.append(install_item)
|
||||
product_ids.append(item['productKey'])
|
||||
else:
|
||||
munkicommon.display_detail(
|
||||
'Skipping install of %s because it\'s not unattended.'
|
||||
% item['display_name'])
|
||||
return item_list, product_ids
|
||||
|
||||
|
||||
|
||||
|
||||
31
code/client/munkilib/munkicommon.py
Normal file → Executable file
31
code/client/munkilib/munkicommon.py
Normal file → Executable file
@@ -2240,7 +2240,11 @@ def isAppRunning(appname):
|
||||
display_detail('Checking if %s is running...' % appname)
|
||||
proc_list = getRunningProcesses()
|
||||
matching_items = []
|
||||
if appname.endswith('.app'):
|
||||
if appname.startswith('/'):
|
||||
# search by exact path
|
||||
matching_items = [item for item in proc_list
|
||||
if item == appname]
|
||||
elif appname.endswith('.app'):
|
||||
# search by filename
|
||||
matching_items = [item for item in proc_list
|
||||
if '/'+ appname + '/Contents/MacOS/' in item]
|
||||
@@ -2494,6 +2498,31 @@ def forceLogoutNow():
|
||||
display_error('Exception in forceLogoutNow(): %s' % str(e))
|
||||
|
||||
|
||||
def blockingApplicationsRunning(pkginfoitem):
|
||||
"""Returns true if any application in the blocking_applications list
|
||||
is running or, if there is no blocking_applications list, if any
|
||||
application in the installs list is running."""
|
||||
|
||||
if 'blocking_applications' in pkginfoitem:
|
||||
appnames = pkginfoitem['blocking_applications']
|
||||
else:
|
||||
# if no blocking_applications specified, get appnames
|
||||
# from 'installs' list if it exists
|
||||
appnames = [os.path.basename(item.get('path'))
|
||||
for item in pkginfoitem.get('installs', [])
|
||||
if item['type'] == 'application']
|
||||
|
||||
display_debug1("Checking for %s" % appnames)
|
||||
running_apps = [appname for appname in appnames
|
||||
if isAppRunning(appname)]
|
||||
if running_apps:
|
||||
display_detail(
|
||||
"Blocking apps for %s are running:" % pkginfoitem['name'])
|
||||
display_detail(" %s" % running_apps)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# module globals
|
||||
#debug = False
|
||||
verbose = 1
|
||||
|
||||
Reference in New Issue
Block a user