mirror of
https://github.com/munki/munki.git
synced 2026-04-21 03:58:20 -05:00
Merge branch 'master' into curl-refactor
This commit is contained in:
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.2</string>
|
||||
<string>3.3.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.2</string>
|
||||
<string>3.3.0</string>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<true/>
|
||||
<key>NSMainNibFile</key>
|
||||
|
||||
@@ -636,6 +636,29 @@ class MSUAppDelegate(NSObject):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def alertIfRunnningOnBattery(self):
|
||||
power_info = munki.getPowerInfo()
|
||||
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))
|
||||
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
|
||||
alert.buttons()[0].setKeyEquivalent_(chr(27))
|
||||
buttonPressed = alert.runModal()
|
||||
if buttonPressed == NSAlertAlternateReturn:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def installSessionErrorAlert(self):
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
||||
NSLocalizedString(u"Cannot start installation session", None),
|
||||
@@ -653,6 +676,9 @@ class MSUAppDelegate(NSObject):
|
||||
def logoutAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
self._currentAlert = None
|
||||
if returncode == NSAlertDefaultReturn:
|
||||
if self.alertIfRunnningOnBattery():
|
||||
munki.log("user", "alerted_on_battery_power_and_cancelled")
|
||||
return
|
||||
NSLog("User chose to logout")
|
||||
munki.log("user", "install_with_logout")
|
||||
result = munki.logoutAndUpdate()
|
||||
@@ -666,18 +692,20 @@ class MSUAppDelegate(NSObject):
|
||||
# another alert
|
||||
alert.window().orderOut_(self)
|
||||
if self.alertIfBlockingAppsRunning():
|
||||
pass
|
||||
return
|
||||
if self.alertIfRunnningOnBattery():
|
||||
munki.log("user", "alerted_on_battery_power_and_cancelled")
|
||||
return
|
||||
NSLog("User chose to update without logging out")
|
||||
munki.log("user", "install_without_logout")
|
||||
result = munki.justUpdate()
|
||||
if result:
|
||||
self.installSessionErrorAlert()
|
||||
else:
|
||||
NSLog("User chose to update without logging out")
|
||||
munki.log("user", "install_without_logout")
|
||||
result = munki.justUpdate()
|
||||
if result:
|
||||
self.installSessionErrorAlert()
|
||||
else:
|
||||
self.managedsoftwareupdate_task = "installwithnologout"
|
||||
self.mainWindowController.theWindow.orderOut_(self)
|
||||
self.munkiStatusController.window.makeKeyAndOrderFront_(self)
|
||||
self.munkiStatusController.startMunkiStatusSession()
|
||||
self.managedsoftwareupdate_task = "installwithnologout"
|
||||
self.mainWindowController.theWindow.orderOut_(self)
|
||||
self.munkiStatusController.window.makeKeyAndOrderFront_(self)
|
||||
self.munkiStatusController.startMunkiStatusSession()
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
|
||||
@@ -434,7 +434,7 @@ def getRunningBlockingApps(appnames):
|
||||
if not matching_items:
|
||||
# try adding '.app' to the name and check again
|
||||
matching_items = [item for item in proc_list
|
||||
if '/'+ appname + '.app/' in item]
|
||||
if '/' + appname + '.app/' in item]
|
||||
|
||||
matching_items = set(matching_items)
|
||||
for path in matching_items:
|
||||
@@ -447,6 +447,68 @@ def getRunningBlockingApps(appnames):
|
||||
return list(set(running_apps))
|
||||
|
||||
|
||||
def getPowerInfo():
|
||||
'''Returns power info in a dictionary'''
|
||||
power_dict = {}
|
||||
power_dict['PowerSource'] = 'Unknown Power'
|
||||
power_dict['BatteryCharge'] = -1
|
||||
power_dict['ChargingStatus'] = 'unknown'
|
||||
power_dict['TimeRemaining'] = -1
|
||||
cmd = ['/usr/bin/pmset', '-g', 'ps']
|
||||
proc = subprocess.Popen(cmd, bufsize=-1, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(output, unused_error) = proc.communicate()
|
||||
if proc.returncode:
|
||||
# handle error
|
||||
return power_dict
|
||||
#
|
||||
# output from `pmset -g ps` looks like:
|
||||
#
|
||||
# Currently drawing from 'AC Power'
|
||||
# -InternalBattery-0 100%; charged; 0:00 remaining
|
||||
#
|
||||
# or
|
||||
#
|
||||
# Currently drawing from 'AC Power'
|
||||
# -InternalBattery-0 98%; charging; 0:08 remaining
|
||||
#
|
||||
# or
|
||||
#
|
||||
# Currently drawing from 'Battery Power'
|
||||
# -InternalBattery-0 100%; discharging; (no estimate)
|
||||
#
|
||||
# or
|
||||
#
|
||||
# Currently drawing from 'Battery Power'
|
||||
# -InternalBattery-0 100%; discharging; 5:55 remaining
|
||||
#
|
||||
line = output.splitlines()
|
||||
if 'AC Power' in line[0]:
|
||||
power_dict['PowerSource'] = 'AC Power'
|
||||
power_dict['ChargingStatus'] = 'not applicable'
|
||||
if 'Battery Power' in line[0]:
|
||||
power_dict['PowerSource'] = 'Battery Power'
|
||||
if len(line) > 1:
|
||||
part = line[1].split()
|
||||
try:
|
||||
power_dict['BatteryCharge'] = int(part[1].rstrip('%;'))
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
try:
|
||||
power_dict['ChargingStatus'] = part[2].rstrip(';')
|
||||
except IndexError:
|
||||
pass
|
||||
try:
|
||||
time_remaining_text = part[3]
|
||||
time_part = time_remaining_text.split(':')
|
||||
minutes = 60 * int(time_part[0]) + int(time_part[1])
|
||||
power_dict['TimeRemaining'] = minutes
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
|
||||
return power_dict
|
||||
|
||||
|
||||
def setupLogging(username=None):
|
||||
"""Setup logging module.
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
managedsoftwareupdate
|
||||
"""
|
||||
|
||||
import grp
|
||||
#import grp
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
#import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
@@ -696,7 +696,7 @@ def main():
|
||||
if not munkicommon.pref('SuppressAutoInstall'):
|
||||
doInstallTasks(only_unattended=True)
|
||||
else:
|
||||
munkicommon.log('Skipping unattended installs because '
|
||||
munkicommon.log('Skipping unattended installs because '
|
||||
'SuppressAutoInstall is true.')
|
||||
# send a notification event so MSU can update its display
|
||||
# if needed
|
||||
|
||||
+16
-11
@@ -28,16 +28,17 @@ import optparse
|
||||
import os
|
||||
import readline
|
||||
|
||||
try:
|
||||
from munkilib import FoundationPlist as plistlib
|
||||
except ImportError:
|
||||
try:
|
||||
import FoundationPlist as plistlib
|
||||
except ImportError:
|
||||
# maybe we're not on an OS X machine...
|
||||
print >> sys.stderr, ("WARNING: FoundationPlist is not available, "
|
||||
"using plistlib instead.")
|
||||
import plistlib
|
||||
#try:
|
||||
# from munkilib import FoundationPlist as plistlib
|
||||
#except ImportError:
|
||||
# try:
|
||||
# import FoundationPlist as plistlib
|
||||
# except ImportError:
|
||||
# # maybe we're not on an OS X machine...
|
||||
# print >> sys.stderr, ("WARNING: FoundationPlist is not available, "
|
||||
# "using plistlib instead.")
|
||||
# import plistlib
|
||||
import plistlib
|
||||
|
||||
try:
|
||||
from munkilib.munkicommon import get_version
|
||||
@@ -357,7 +358,11 @@ def find(args):
|
||||
count = 0
|
||||
for name in getManifestNames():
|
||||
pathname = os.path.join(manifests_path, name)
|
||||
manifest = plistlib.readPlist(pathname)
|
||||
try:
|
||||
manifest = plistlib.readPlist(pathname)
|
||||
except Exception:
|
||||
print >> sys.stderr, 'Error reading %s' % pathname
|
||||
continue
|
||||
if keyname:
|
||||
if keyname in manifest:
|
||||
value = manifest[keyname]
|
||||
|
||||
@@ -22,6 +22,7 @@ munki module to automatically install pkgs, mpkgs, and dmgs
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pwd
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
@@ -79,7 +80,8 @@ def removeBundleRelocationInfo(pkgpath):
|
||||
pass
|
||||
|
||||
|
||||
def install(pkgpath, choicesXMLpath=None, suppressBundleRelocation=False):
|
||||
def install(pkgpath, choicesXMLpath=None, suppressBundleRelocation=False,
|
||||
environment=None):
|
||||
"""
|
||||
Uses the apple installer to install the package or metapackage
|
||||
at pkgpath. Prints status messages to STDOUT.
|
||||
@@ -136,12 +138,28 @@ def install(pkgpath, choicesXMLpath=None, suppressBundleRelocation=False):
|
||||
if choicesXMLpath:
|
||||
cmd.extend(['-applyChoiceChangesXML', choicesXMLpath])
|
||||
|
||||
# set up environment for installer
|
||||
env_vars = os.environ.copy()
|
||||
# get info for root
|
||||
userinfo = pwd.getpwuid(0)
|
||||
env_vars['USER'] = userinfo.pw_name
|
||||
env_vars['HOME'] = userinfo.pw_dir
|
||||
if environment:
|
||||
# Munki admin has specified custom installer environment
|
||||
for key in environment.keys():
|
||||
if key == 'USER' and environment[key] == 'CURRENT_CONSOLE_USER':
|
||||
# current console user (if there is one) 'owns' /dev/console
|
||||
userinfo = pwd.getpwuid(os.stat('/dev/console').st_uid)
|
||||
env_vars['USER'] = userinfo.pw_name
|
||||
env_vars['HOME'] = userinfo.pw_dir
|
||||
else:
|
||||
env_vars[key] = environment[key]
|
||||
munkicommon.display_debug1(
|
||||
'Using custom installer environment variables: %s', env_vars)
|
||||
|
||||
# run installer, setting the program id of the process (all child
|
||||
# processes will also use the same program id), making it easier to kill
|
||||
# not only hung installer but also any child processes it started.
|
||||
env_vars = os.environ.copy()
|
||||
if not 'USER' in env_vars:
|
||||
env_vars['USER'] = 'root'
|
||||
proc = munkicommon.Popen(cmd, shell=False, bufsize=1, env=env_vars,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
@@ -222,7 +240,8 @@ def install(pkgpath, choicesXMLpath=None, suppressBundleRelocation=False):
|
||||
return (retcode, restartneeded)
|
||||
|
||||
|
||||
def installall(dirpath, choicesXMLpath=None, suppressBundleRelocation=False):
|
||||
def installall(dirpath, choicesXMLpath=None, suppressBundleRelocation=False,
|
||||
environment=None):
|
||||
"""
|
||||
Attempts to install all pkgs and mpkgs in a given directory.
|
||||
Will mount dmg files and install pkgs and mpkgs found at the
|
||||
@@ -250,7 +269,8 @@ def installall(dirpath, choicesXMLpath=None, suppressBundleRelocation=False):
|
||||
# of the mountpoint -- call us recursively!
|
||||
(retcode, needsrestart) = installall(mountpoint,
|
||||
choicesXMLpath,
|
||||
suppressBundleRelocation)
|
||||
suppressBundleRelocation,
|
||||
environment)
|
||||
if needsrestart:
|
||||
restartflag = True
|
||||
if retcode:
|
||||
@@ -262,7 +282,8 @@ def installall(dirpath, choicesXMLpath=None, suppressBundleRelocation=False):
|
||||
|
||||
if (item.endswith(".pkg") or item.endswith(".mpkg")):
|
||||
(retcode, needsrestart) = install(itempath, choicesXMLpath,
|
||||
suppressBundleRelocation)
|
||||
suppressBundleRelocation,
|
||||
environment)
|
||||
if needsrestart:
|
||||
restartflag = True
|
||||
if retcode:
|
||||
@@ -640,6 +661,7 @@ def installWithInfo(
|
||||
choicesXMLfile)
|
||||
else:
|
||||
choicesXMLfile = ''
|
||||
installer_environment = item.get('installer_environment')
|
||||
if itempath.endswith(".dmg"):
|
||||
munkicommon.display_status("Mounting disk image %s" %
|
||||
item["installer_item"])
|
||||
@@ -672,14 +694,16 @@ def installWithInfo(
|
||||
if os.path.exists(fullpkgpath):
|
||||
(retcode, needtorestart) = install(fullpkgpath,
|
||||
choicesXMLfile,
|
||||
suppressBundleRelocation)
|
||||
suppressBundleRelocation,
|
||||
installer_environment)
|
||||
else:
|
||||
# no relative path to pkg on dmg, so just install all
|
||||
# pkgs found at the root of the first mountpoint
|
||||
# (hopefully there's only one)
|
||||
(retcode, needtorestart) = installall(mountpoints[0],
|
||||
choicesXMLfile,
|
||||
suppressBundleRelocation)
|
||||
choicesXMLfile,
|
||||
suppressBundleRelocation,
|
||||
installer_environment)
|
||||
if (needtorestart or
|
||||
item.get("RestartAction") == "RequireRestart" or
|
||||
item.get("RestartAction") == "RecommendRestart"):
|
||||
@@ -688,8 +712,9 @@ def installWithInfo(
|
||||
elif (itempath.endswith(".pkg") or itempath.endswith(".mpkg")
|
||||
or itempath.endswith(".dist")):
|
||||
(retcode, needtorestart) = install(itempath,
|
||||
choicesXMLfile,
|
||||
suppressBundleRelocation)
|
||||
choicesXMLfile,
|
||||
suppressBundleRelocation,
|
||||
installer_environment)
|
||||
if (needtorestart or
|
||||
item.get("RestartAction") == "RequireRestart" or
|
||||
item.get("RestartAction") == "RecommendRestart"):
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -69,7 +69,7 @@ def sendCommand(command_text):
|
||||
global SOCK
|
||||
if SOCK == None:
|
||||
launchAndConnectToMunkiStatus()
|
||||
if SOCK:
|
||||
if SOCK:
|
||||
try:
|
||||
# we can send only a single line.
|
||||
messagelines = command_text.splitlines(True)
|
||||
@@ -97,17 +97,21 @@ def readResponse():
|
||||
print err, errmsg
|
||||
SOCK.close()
|
||||
SOCK = None
|
||||
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def getPIDforProcessName(processname):
|
||||
'''Returns a process ID for processname'''
|
||||
cmd = ['/bin/ps', '-eo', 'pid=,command=']
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
while True:
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=1,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
while True:
|
||||
line = proc.stdout.readline().decode('UTF-8')
|
||||
if not line and (proc.poll() != None):
|
||||
break
|
||||
@@ -121,7 +125,7 @@ def getPIDforProcessName(processname):
|
||||
else:
|
||||
if process.find(processname) != -1:
|
||||
return str(pid)
|
||||
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
@@ -147,7 +151,7 @@ def getMunkiStatusSocket():
|
||||
socketpath = "/tmp/com.googlecode.munki.munkistatus.%s" % pid
|
||||
if os.path.exists(socketpath):
|
||||
return socketpath
|
||||
|
||||
|
||||
# sleep and try again
|
||||
time.sleep(.25)
|
||||
return ""
|
||||
@@ -202,7 +206,7 @@ def showStopButton():
|
||||
|
||||
def disableStopButton():
|
||||
'''Disables (grays out) the stop button.'''
|
||||
sendCommand(u"DISABLESTOPBUTTON: \n")
|
||||
sendCommand(u"DISABLESTOPBUTTON: \n")
|
||||
|
||||
|
||||
def enableStopButton():
|
||||
|
||||
@@ -21,7 +21,7 @@ Created by Greg Neagle on 2008-11-13.
|
||||
|
||||
"""
|
||||
|
||||
#standard libs
|
||||
# standard libs
|
||||
import calendar
|
||||
import errno
|
||||
import os
|
||||
@@ -33,15 +33,16 @@ import time
|
||||
import urllib2
|
||||
import urlparse
|
||||
import xattr
|
||||
#from distutils import version
|
||||
from OpenSSL.crypto import load_certificate, FILETYPE_PEM
|
||||
|
||||
#our libs
|
||||
# our libs
|
||||
import munkicommon
|
||||
import munkistatus
|
||||
import appleupdates
|
||||
import FoundationPlist
|
||||
from Foundation import NSDate
|
||||
|
||||
# Apple's libs
|
||||
from Foundation import NSDate, NSPredicate
|
||||
|
||||
|
||||
# This many hours before a force install deadline, start notifying the user.
|
||||
@@ -1576,6 +1577,7 @@ def processInstall(manifestitem, cataloglist, installinfo):
|
||||
# optional keys
|
||||
optional_keys = ['suppress_bundle_relocation',
|
||||
'installer_choices_xml',
|
||||
'installer_environment',
|
||||
'adobe_install_info',
|
||||
'RestartAction',
|
||||
'installer_type',
|
||||
@@ -1662,58 +1664,117 @@ def processInstall(manifestitem, cataloglist, installinfo):
|
||||
return True
|
||||
|
||||
|
||||
def processManifestForKey(manifestpath, manifest_key, installinfo,
|
||||
INFO_OBJECT = {}
|
||||
def makePredicateInfoObject():
|
||||
if INFO_OBJECT:
|
||||
return
|
||||
for key in MACHINE.keys():
|
||||
INFO_OBJECT[key] = MACHINE[key]
|
||||
os_vers = MACHINE['os_vers']
|
||||
os_vers = os_vers + '.0.0'
|
||||
INFO_OBJECT['os_vers_major'] = int(os_vers.split('.')[0])
|
||||
INFO_OBJECT['os_vers_minor'] = int(os_vers.split('.')[1])
|
||||
INFO_OBJECT['os_vers_patch'] = int(os_vers.split('.')[2])
|
||||
if 'Book' in MACHINE.get('machine_model', ''):
|
||||
INFO_OBJECT['machine_type'] = 'laptop'
|
||||
else:
|
||||
INFO_OBJECT['machine_type'] = 'desktop'
|
||||
|
||||
|
||||
def predicateEvaluatesAsTrue(predicate_string):
|
||||
'''Evaluates predicate against our info object'''
|
||||
munkicommon.display_debug1('Evaluating predicate: %s' % predicate_string)
|
||||
try:
|
||||
p = NSPredicate.predicateWithFormat_(predicate_string)
|
||||
except Exception, e:
|
||||
munkicommon.display_warning('%s' % e)
|
||||
# can't parse predicate, so return False
|
||||
return False
|
||||
|
||||
result = p.evaluateWithObject_(INFO_OBJECT)
|
||||
munkicommon.display_debug1(
|
||||
'Predicate %s is %s' % (predicate_string, result))
|
||||
return result
|
||||
|
||||
|
||||
def processManifestForKey(manifest, manifest_key, installinfo,
|
||||
parentcatalogs=None):
|
||||
"""Processes keys in manifests to build the lists of items to install and
|
||||
remove.
|
||||
|
||||
Can be recursive if manifests include other manifests.
|
||||
Probably doesn't handle circular manifest references well.
|
||||
|
||||
manifest can be a path to a manifest file or a dictionary object.
|
||||
"""
|
||||
munkicommon.display_debug1(
|
||||
"** Processing manifest %s for %s" %
|
||||
(os.path.basename(manifestpath), manifest_key))
|
||||
if isinstance(manifest, basestring):
|
||||
munkicommon.display_debug1(
|
||||
"** Processing manifest %s for %s" %
|
||||
(os.path.basename(manifest), manifest_key))
|
||||
manifestdata = getManifestData(manifest)
|
||||
else:
|
||||
manifestdata = manifest
|
||||
manifest = 'embedded manifest'
|
||||
|
||||
cataloglist = getManifestValueForKey(manifestpath, 'catalogs')
|
||||
cataloglist = manifestdata.get('catalogs')
|
||||
if cataloglist:
|
||||
getCatalogs(cataloglist)
|
||||
elif parentcatalogs:
|
||||
cataloglist = parentcatalogs
|
||||
|
||||
if cataloglist:
|
||||
nestedmanifests = getManifestValueForKey(manifestpath,
|
||||
'included_manifests')
|
||||
if nestedmanifests:
|
||||
for item in nestedmanifests:
|
||||
try:
|
||||
nestedmanifestpath = getmanifest(item)
|
||||
except ManifestException:
|
||||
nestedmanifestpath = None
|
||||
if munkicommon.stopRequested():
|
||||
return {}
|
||||
if nestedmanifestpath:
|
||||
processManifestForKey(nestedmanifestpath, manifest_key,
|
||||
installinfo, cataloglist)
|
||||
|
||||
items = getManifestValueForKey(manifestpath, manifest_key)
|
||||
if items:
|
||||
for item in items:
|
||||
if munkicommon.stopRequested():
|
||||
return {}
|
||||
if manifest_key == 'managed_installs':
|
||||
unused_result = processInstall(item, cataloglist,
|
||||
installinfo)
|
||||
elif manifest_key == 'managed_updates':
|
||||
processManagedUpdate(item, cataloglist, installinfo)
|
||||
elif manifest_key == 'optional_installs':
|
||||
processOptionalInstall(item, cataloglist, installinfo)
|
||||
elif manifest_key == 'managed_uninstalls':
|
||||
unused_result = processRemoval(item, cataloglist,
|
||||
installinfo)
|
||||
|
||||
else:
|
||||
if not cataloglist:
|
||||
munkicommon.display_warning('Manifest %s has no catalogs' %
|
||||
manifestpath)
|
||||
return
|
||||
|
||||
nestedmanifests = manifestdata.get('included_manifests')
|
||||
if nestedmanifests:
|
||||
for item in nestedmanifests:
|
||||
try:
|
||||
nestedmanifestpath = getmanifest(item)
|
||||
except ManifestException:
|
||||
nestedmanifestpath = None
|
||||
if munkicommon.stopRequested():
|
||||
return {}
|
||||
if nestedmanifestpath:
|
||||
processManifestForKey(nestedmanifestpath, manifest_key,
|
||||
installinfo, cataloglist)
|
||||
|
||||
conditionalitems = manifestdata.get('conditional_items')
|
||||
if conditionalitems:
|
||||
munkicommon.display_debug1(
|
||||
'** Processing conditional_items in %s' % manifest)
|
||||
# conditionalitems should be an array of dicts
|
||||
# each dict has a predicate; the rest consists of the
|
||||
# same keys as a manifest
|
||||
for item in conditionalitems:
|
||||
try:
|
||||
predicate = item['condition']
|
||||
except (AttributeError, KeyError):
|
||||
munkicommon.display_warning(
|
||||
'Missing predicate for conditional_item %s' % item)
|
||||
continue
|
||||
INFO_OBJECT['catalogs'] = cataloglist
|
||||
if predicateEvaluatesAsTrue(predicate):
|
||||
conditionalmanifest = item
|
||||
processManifestForKey(conditionalmanifest, manifest_key,
|
||||
installinfo, cataloglist)
|
||||
|
||||
items = manifestdata.get(manifest_key)
|
||||
if items:
|
||||
for item in items:
|
||||
if munkicommon.stopRequested():
|
||||
return {}
|
||||
if manifest_key == 'managed_installs':
|
||||
unused_result = processInstall(item, cataloglist,
|
||||
installinfo)
|
||||
elif manifest_key == 'managed_updates':
|
||||
processManagedUpdate(item, cataloglist, installinfo)
|
||||
elif manifest_key == 'optional_installs':
|
||||
processOptionalInstall(item, cataloglist, installinfo)
|
||||
elif manifest_key == 'managed_uninstalls':
|
||||
unused_result = processRemoval(item, cataloglist,
|
||||
installinfo)
|
||||
|
||||
|
||||
def getReceiptsToRemove(item):
|
||||
@@ -2003,17 +2064,21 @@ def processRemoval(manifestitem, cataloglist, installinfo):
|
||||
return True
|
||||
|
||||
|
||||
def getManifestValueForKey(manifestpath, keyname):
|
||||
"""Returns a value for keyname in manifestpath"""
|
||||
def getManifestData(manifestpath):
|
||||
'''Reads a manifest file, returns a
|
||||
dictionary-like object'''
|
||||
plist = {}
|
||||
try:
|
||||
plist = FoundationPlist.readPlist(manifestpath)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
munkicommon.display_error('Could not read plist %s' % manifestpath)
|
||||
return None
|
||||
if keyname in plist:
|
||||
return plist[keyname]
|
||||
else:
|
||||
return None
|
||||
return plist
|
||||
|
||||
|
||||
def getManifestValueForKey(manifestpath, keyname):
|
||||
"""Returns a value for keyname in manifestpath"""
|
||||
plist = getManifestData(manifestpath)
|
||||
return plist.get(keyname, None)
|
||||
|
||||
|
||||
# global to hold our catalog DBs
|
||||
@@ -2811,8 +2876,26 @@ def getHTTPfileIfChangedAtomically(url, destinationpath,
|
||||
return True
|
||||
|
||||
|
||||
# we only want to call sw_vers and the like once. Since these values don't
|
||||
# change often, we store the info in MACHINE.
|
||||
def get_hardware_info():
|
||||
'''Uses system profiler to get hardware info for this machine'''
|
||||
cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml']
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, error) = proc.communicate()
|
||||
try:
|
||||
plist = FoundationPlist.readPlistFromString(output)
|
||||
# system_profiler xml is an array
|
||||
sp_dict = plist[0]
|
||||
items = sp_dict['_items']
|
||||
sp_hardware_dict = items[0]
|
||||
return sp_hardware_dict
|
||||
except Exception, e:
|
||||
return {}
|
||||
|
||||
|
||||
# we only want to call system_profiler and the like once. Since these values
|
||||
# don't change often, we store the info in MACHINE.
|
||||
MACHINE = {}
|
||||
def getMachineFacts():
|
||||
"""Gets some facts about this machine we use to determine if a given
|
||||
@@ -2822,6 +2905,8 @@ def getMachineFacts():
|
||||
MACHINE['hostname'] = os.uname()[1]
|
||||
MACHINE['arch'] = os.uname()[4]
|
||||
MACHINE['os_vers'] = munkicommon.getOsVersion(only_major_minor=False)
|
||||
hardware_info = get_hardware_info()
|
||||
MACHINE['machine_model'] = hardware_info.get('machine_model', 'UNKNOWN')
|
||||
|
||||
|
||||
def check(client_id='', localmanifestpath=None):
|
||||
@@ -2859,7 +2944,10 @@ def check(client_id='', localmanifestpath=None):
|
||||
installinfo['optional_installs'] = []
|
||||
installinfo['managed_installs'] = []
|
||||
installinfo['removals'] = []
|
||||
|
||||
|
||||
# set up INFO_OBJECT for conditional item comparisons
|
||||
makePredicateInfoObject()
|
||||
|
||||
munkicommon.display_detail('**Checking for installs**')
|
||||
processManifestForKey(mainmanifestpath, 'managed_installs',
|
||||
installinfo)
|
||||
|
||||
Reference in New Issue
Block a user