mirror of
https://github.com/munki/munki.git
synced 2026-02-21 22:50:31 -06:00
Add MCX support, based on code contribution from Dan Roque.
Other minor clean-ups. git-svn-id: http://munki.googlecode.com/svn/trunk@973 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.11</string>
|
||||
<string>3.0.12</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.0.11</string>
|
||||
<string>3.0.12</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -135,8 +135,7 @@ class MSUAppDelegate(NSObject):
|
||||
if self._listofupdates:
|
||||
return
|
||||
# no list of updates; let's check the LastCheckResult for more info
|
||||
prefs = munki.getManagedInstallsPrefs()
|
||||
lastCheckResult = prefs.get("LastCheckResult")
|
||||
lastCheckResult = munki.pref("LastCheckResult")
|
||||
if lastCheckResult == 0:
|
||||
self.noUpdatesAlert()
|
||||
elif lastCheckResult == 1:
|
||||
|
||||
@@ -24,6 +24,8 @@ import os
|
||||
import subprocess
|
||||
import FoundationPlist
|
||||
from Foundation import NSFileManager
|
||||
from Foundation import CFPreferencesCopyAppValue
|
||||
|
||||
|
||||
UPDATECHECKLAUNCHFILE = \
|
||||
"/private/tmp/.com.googlecode.munki.updatecheck.launchd"
|
||||
@@ -37,33 +39,29 @@ def call(cmd):
|
||||
return proc.returncode
|
||||
|
||||
|
||||
def getManagedInstallsPrefs():
|
||||
'''Define default preference values;
|
||||
Read preference values from ManagedInstalls.plist if it exists.'''
|
||||
BUNDLE_ID = 'ManagedInstalls'
|
||||
|
||||
prefs = {}
|
||||
prefs['ManagedInstallDir'] = "/Library/Managed Installs"
|
||||
prefs['InstallAppleSoftwareUpdates'] = False
|
||||
prefs['ShowRemovalDetail'] = False
|
||||
prefs['InstallRequiresLogout'] = False
|
||||
|
||||
prefsfile = "/Library/Preferences/ManagedInstalls.plist"
|
||||
if os.path.exists(prefsfile):
|
||||
try:
|
||||
plist = FoundationPlist.readPlist(prefsfile)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
return prefs
|
||||
try:
|
||||
for key in plist.keys():
|
||||
if type(plist[key]).__name__ == "__NSCFDate":
|
||||
# convert NSDate/CFDates to strings
|
||||
prefs[key] = str(plist[key])
|
||||
else:
|
||||
prefs[key] = plist[key]
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return prefs
|
||||
def pref(pref_name):
|
||||
"""Return a preference. Since this uses CFPreferencesCopyAppValue,
|
||||
Preferences can be defined several places. Precedence is:
|
||||
- MCX
|
||||
- ~/Library/Preferences/ManagedInstalls.plist
|
||||
- /Library/Preferences/ManagedInstalls.plist
|
||||
- default_prefs defined here.
|
||||
"""
|
||||
default_prefs = {
|
||||
'ManagedInstallDir': '/Library/Managed Installs',
|
||||
'InstallAppleSoftwareUpdates': False,
|
||||
'ShowRemovalDetail': False,
|
||||
'InstallRequiresLogout': False
|
||||
}
|
||||
pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID)
|
||||
if pref_value == None:
|
||||
pref_value = default_prefs.get(pref_name)
|
||||
if type(pref_value).__name__ == "__NSCFDate":
|
||||
# convert NSDate/CFDates to strings
|
||||
pref_value = str(pref_value)
|
||||
return pref_value
|
||||
|
||||
|
||||
def readSelfServiceManifest():
|
||||
@@ -72,8 +70,7 @@ def readSelfServiceManifest():
|
||||
SelfServeManifest = "/Users/Shared/.SelfServeManifest"
|
||||
if not os.path.exists(SelfServeManifest):
|
||||
# no working copy, look for system copy
|
||||
prefs = getManagedInstallsPrefs()
|
||||
managedinstallbase = prefs['ManagedInstallDir']
|
||||
managedinstallbase = pref('ManagedInstallDir')
|
||||
SelfServeManifest = os.path.join(managedinstallbase, "manifests",
|
||||
"SelfServeManifest")
|
||||
if os.path.exists(SelfServeManifest):
|
||||
@@ -97,18 +94,17 @@ def writeSelfServiceManifest(optional_install_choices):
|
||||
|
||||
def getRemovalDetailPrefs():
|
||||
'''Returns preference to control display of removal detail'''
|
||||
return getManagedInstallsPrefs().get('ShowRemovalDetail', False)
|
||||
return pref('ShowRemovalDetail')
|
||||
|
||||
|
||||
def installRequiresLogout():
|
||||
'''Returns preference to force logout for all installs'''
|
||||
return getManagedInstallsPrefs().get('InstallRequiresLogout', False)
|
||||
return pref('InstallRequiresLogout')
|
||||
|
||||
|
||||
def getInstallInfo():
|
||||
'''Returns the dictionary describing the managed installs and removals'''
|
||||
prefs = getManagedInstallsPrefs()
|
||||
managedinstallbase = prefs['ManagedInstallDir']
|
||||
managedinstallbase = pref('ManagedInstallDir')
|
||||
plist = {}
|
||||
installinfo = os.path.join(managedinstallbase, 'InstallInfo.plist')
|
||||
if os.path.exists(installinfo):
|
||||
@@ -127,12 +123,11 @@ def startUpdateCheck():
|
||||
|
||||
def getAppleUpdates():
|
||||
'''Returns any available Apple updates'''
|
||||
prefs = getManagedInstallsPrefs()
|
||||
managedinstallbase = prefs['ManagedInstallDir']
|
||||
managedinstallbase = pref('ManagedInstallDir')
|
||||
plist = {}
|
||||
appleUpdatesFile = os.path.join(managedinstallbase, 'AppleUpdates.plist')
|
||||
if (os.path.exists(appleUpdatesFile) and
|
||||
prefs['InstallAppleSoftwareUpdates']):
|
||||
pref('InstallAppleSoftwareUpdates')):
|
||||
try:
|
||||
plist = FoundationPlist.readPlist(appleUpdatesFile)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
|
||||
@@ -90,17 +90,7 @@ def networkUp():
|
||||
|
||||
def clearLastNotifiedDate():
|
||||
"""Clear the last date the user was notified of updates."""
|
||||
try:
|
||||
plist = FoundationPlist.readPlist(
|
||||
munkicommon.MANAGED_INSTALLS_PLIST_PATH)
|
||||
if plist:
|
||||
if 'LastNotifiedDate' in plist:
|
||||
cmd = ['/usr/bin/defaults', 'delete',
|
||||
munkicommon.MANAGED_INSTALLS_PLIST_PATH_NO_EXT,
|
||||
'LastNotifiedDate']
|
||||
unused_retcode = subprocess.call(cmd)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
pass
|
||||
munkicommon.set_pref('LastNotifiedDate', None)
|
||||
|
||||
|
||||
def createDirsIfNeeded(dirlist):
|
||||
@@ -129,8 +119,7 @@ def initMunkiDirs():
|
||||
Returns:
|
||||
Boolean. True if all data dirs existed or were created, False otherwise.
|
||||
"""
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
ManagedInstallDir = managedinstallprefs['ManagedInstallDir']
|
||||
ManagedInstallDir = munkicommon.pref('ManagedInstallDir')
|
||||
manifestsdir = os.path.join(ManagedInstallDir, 'manifests')
|
||||
catalogsdir = os.path.join(ManagedInstallDir, 'catalogs')
|
||||
cachedir = os.path.join(ManagedInstallDir, 'Cache')
|
||||
@@ -249,14 +238,8 @@ def munkiUpdatesAvailable():
|
||||
def recordUpdateCheckResult(result):
|
||||
"""Record last check date and result"""
|
||||
now = NSDate.new()
|
||||
cmd = ['/usr/bin/defaults', 'write',
|
||||
munkicommon.MANAGED_INSTALLS_PLIST_PATH_NO_EXT,
|
||||
'LastCheckDate', '-date', str(now)]
|
||||
unused_retcode = subprocess.call(cmd)
|
||||
cmd = ['/usr/bin/defaults', 'write',
|
||||
munkicommon.MANAGED_INSTALLS_PLIST_PATH_NO_EXT,
|
||||
'LastCheckResult', '-int', str(result)]
|
||||
unused_retcode = subprocess.call(cmd)
|
||||
munkicommon.set_pref('LastCheckDate', now)
|
||||
munkicommon.set_pref('LastCheckResult', result)
|
||||
|
||||
|
||||
def notifyUserOfUpdates():
|
||||
@@ -284,11 +267,8 @@ def notifyUserOfUpdates():
|
||||
nextNotifyDate = lastNotifiedDate.dateByAddingTimeInterval_(interval)
|
||||
if now.timeIntervalSinceDate_(nextNotifyDate) > 0:
|
||||
# record current notification date
|
||||
cmd = ['/usr/bin/defaults', 'write',
|
||||
munkicommon.MANAGED_INSTALLS_PLIST_PATH_NO_EXT,
|
||||
'LastNotifiedDate', '-date', str(now)]
|
||||
unused_retcode = subprocess.call(cmd)
|
||||
|
||||
munkicommon.set_pref('LastNotifiedDate', now)
|
||||
|
||||
# Kill Managed Software Update.app if it's already
|
||||
# open so it will update its display
|
||||
# using subprocess.Popen instead of subprocess.call
|
||||
@@ -486,7 +466,7 @@ def main():
|
||||
munkicommon.cleanUpTmpDir()
|
||||
exit(-1)
|
||||
# Force a prefs refresh, in case preflight modified the prefs file.
|
||||
munkicommon.prefs(force_refresh=True)
|
||||
munkicommon.reload_prefs()
|
||||
|
||||
# create needed directories if necessary
|
||||
if not initMunkiDirs():
|
||||
|
||||
@@ -99,7 +99,7 @@ def softwareUpdateList():
|
||||
return CACHEDUPDATELIST
|
||||
|
||||
updates = []
|
||||
munkicommon.display_info(
|
||||
munkicommon.display_detail(
|
||||
'Getting list of available Apple Software Updates')
|
||||
cmd = ['/usr/sbin/softwareupdate', '-l']
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=1,
|
||||
@@ -109,7 +109,7 @@ def softwareUpdateList():
|
||||
if proc.returncode == 0:
|
||||
updates = [str(item)[5:] for item in output.splitlines()
|
||||
if str(item).startswith(' * ')]
|
||||
munkicommon.display_info(
|
||||
munkicommon.display_detail(
|
||||
'softwareupdate returned %s updates' % len(updates))
|
||||
CACHEDUPDATELIST = updates
|
||||
return CACHEDUPDATELIST
|
||||
|
||||
@@ -37,17 +37,20 @@ import urllib2
|
||||
import warnings
|
||||
from distutils import version
|
||||
from xml.dom import minidom
|
||||
|
||||
from Foundation import NSDate
|
||||
from Foundation import CFPreferencesCopyAppValue
|
||||
from Foundation import CFPreferencesSetValue
|
||||
from Foundation import CFPreferencesAppSynchronize
|
||||
from Foundation import kCFPreferencesAnyUser
|
||||
from Foundation import kCFPreferencesCurrentHost
|
||||
|
||||
import munkistatus
|
||||
import FoundationPlist
|
||||
import LaunchServices
|
||||
|
||||
|
||||
MANAGED_INSTALLS_PLIST_PATH = '/Library/Preferences/ManagedInstalls.plist'
|
||||
MANAGED_INSTALLS_PLIST_PATH_NO_EXT = '/Library/Preferences/ManagedInstalls'
|
||||
SECURE_MANAGED_INSTALLS_PLIST_PATH = (
|
||||
'/private/var/root/Library/Preferences/ManagedInstalls.plist')
|
||||
BUNDLE_ID = 'ManagedInstalls'
|
||||
ADDITIONAL_HTTP_HEADERS_KEY = 'AdditionalHttpHeaders'
|
||||
|
||||
|
||||
@@ -288,7 +291,7 @@ def format_time(timestamp=None):
|
||||
if timestamp is None:
|
||||
return str(NSDate.new())
|
||||
else:
|
||||
return str(NSDate.alloc().initWithTimeIntervalSince1970_(timestamp))
|
||||
return str(NSDate.dateWithTimeIntervalSince1970_(timestamp))
|
||||
|
||||
|
||||
def log(msg, logname=''):
|
||||
@@ -613,94 +616,65 @@ def isApplication(pathname):
|
||||
# managed installs preferences/metadata
|
||||
#####################################################
|
||||
|
||||
|
||||
def prefs(force_refresh=False):
|
||||
"""Loads and caches preferences from ManagedInstalls.plist.
|
||||
|
||||
Args:
|
||||
force_refresh: Boolean. If True, wipe prefs and reload from scratch. If
|
||||
False (default), load from cache if it's already set.
|
||||
|
||||
Returns:
|
||||
Dict of preferences.
|
||||
"""
|
||||
global _prefs
|
||||
if not _prefs or force_refresh:
|
||||
_prefs = {} # start with a clean state.
|
||||
_prefs['ManagedInstallDir'] = '/Library/Managed Installs'
|
||||
# convenience; to be replaced with CatalogURL and PackageURL
|
||||
_prefs['SoftwareRepoURL'] = 'http://munki/repo'
|
||||
# effective defaults for the following three; though if they
|
||||
# are not in the prefs plist, they are calculated relative
|
||||
# to the SoftwareRepoURL (if it exists)
|
||||
#prefs['ManifestURL'] = 'http://munki/repo/manifests/'
|
||||
#prefs['CatalogURL'] = 'http://munki/repo/catalogs/'
|
||||
#prefs['PackageURL'] = 'http://munki/repo/pkgs/'
|
||||
_prefs['ClientIdentifier'] = ''
|
||||
_prefs['LogFile'] = \
|
||||
'/Library/Managed Installs/Logs/ManagedSoftwareUpdate.log'
|
||||
_prefs['LoggingLevel'] = 1
|
||||
_prefs['InstallAppleSoftwareUpdates'] = False
|
||||
_prefs['SoftwareUpdateServerURL'] = ''
|
||||
_prefs['DaysBetweenNotifications'] = 1
|
||||
_prefs['LastNotifiedDate'] = '1970-01-01 00:00:00 -0000'
|
||||
_prefs['UseClientCertificate'] = False
|
||||
_prefs['SuppressUserNotification'] = False
|
||||
_prefs['SuppressAutoInstall'] = False
|
||||
_prefs['SuppressStopButtonOnInstall'] = False
|
||||
_prefs['PackageVerificationMode'] = 'hash'
|
||||
|
||||
# Load configs from ManagedInstalls.plist file
|
||||
if not loadPrefsFromFile(_prefs, MANAGED_INSTALLS_PLIST_PATH):
|
||||
# no prefs file, so we'll write out a 'default' prefs file
|
||||
del _prefs['LastNotifiedDate']
|
||||
FoundationPlist.writePlist(_prefs, MANAGED_INSTALLS_PLIST_PATH)
|
||||
|
||||
# Load configs from secure ManagedInstalls.plist file.
|
||||
# Note: this overwrites existing configs.
|
||||
loadPrefsFromFile(_prefs, SECURE_MANAGED_INSTALLS_PLIST_PATH)
|
||||
|
||||
return _prefs
|
||||
def reload_prefs():
|
||||
"""Uses CFPreferencesAppSynchronize(BUNDLE_ID)
|
||||
to make sure we have the latest prefs. Call this
|
||||
if you have modified /Library/Preferences/ManagedInstalls.plist
|
||||
or /var/root/Library/Preferences/ManagedInstalls.plist directly"""
|
||||
CFPreferencesAppSynchronize(BUNDLE_ID)
|
||||
|
||||
|
||||
def loadPrefsFromFile(prefs, filepath):
|
||||
"""Loads preferences from a file into the passed prefs dictionary.
|
||||
|
||||
Args:
|
||||
prefs: dictionary of configurations to update.
|
||||
filepath: str path of file to read configurations from.
|
||||
Returns:
|
||||
Boolean. True if the file exists and prefs was updated, False otherwise.
|
||||
Raises:
|
||||
Error: there was an error reading the specified preferences file.
|
||||
"""
|
||||
if not os.path.exists(filepath):
|
||||
return False
|
||||
|
||||
plist = {}
|
||||
def set_pref(pref_name, pref_value):
|
||||
"""Sets a preference, writing it to
|
||||
/Library/Preferences/ManagedInstalls.plist.
|
||||
This should normally be used only for 'bookkeeping' values;
|
||||
values that control the behavior of munki may be overridden
|
||||
elsewhere (by MCX, for example)"""
|
||||
try:
|
||||
plist = FoundationPlist.readPlist(filepath)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
display_error('ERROR: Could not read preferences file %s.' % filepath)
|
||||
raise PreferencesError(
|
||||
'Could not read preferences file %s.' % filepath)
|
||||
try:
|
||||
for key in plist.keys():
|
||||
if type(plist[key]).__name__ == '__NSCFDate':
|
||||
# convert NSDate/CFDates to strings
|
||||
_prefs[key] = str(plist[key])
|
||||
else:
|
||||
_prefs[key] = plist[key]
|
||||
except AttributeError:
|
||||
display_error('ERROR: Prefs file %s contains invalid data.' % filepath)
|
||||
raise PreferencesError('Preferences file %s invalid.' % filepath)
|
||||
return True
|
||||
CFPreferencesSetValue(
|
||||
pref_name, pref_value, BUNDLE_ID,
|
||||
kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
|
||||
CFPreferencesAppSynchronize(BUNDLE_ID)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def pref(prefname):
|
||||
"""Return a prefernce"""
|
||||
return prefs().get(prefname,'')
|
||||
|
||||
def pref(pref_name):
|
||||
"""Return a preference. Since this uses CFPreferencesCopyAppValue,
|
||||
Preferences can be defined several places. Precedence is:
|
||||
- MCX
|
||||
- /var/root/Library/Preferences/ManagedInstalls.plist
|
||||
- /Library/Preferences/ManagedInstalls.plist
|
||||
- default_prefs defined here.
|
||||
"""
|
||||
default_prefs = {
|
||||
'ManagedInstallDir': '/Library/Managed Installs',
|
||||
'SoftwareRepoURL': 'http://munki/repo',
|
||||
'ClientIdentifier': '',
|
||||
'LogFile': '/Library/Managed Installs/Logs/ManagedSoftwareUpdate.log',
|
||||
'LoggingLevel': 1,
|
||||
'InstallAppleSoftwareUpdates': False,
|
||||
'AppleSoftwareUpdatesOnly': False,
|
||||
'SoftwareUpdateServerURL': '',
|
||||
'DaysBetweenNotifications': 1,
|
||||
'LastNotifiedDate': NSDate.dateWithTimeIntervalSince1970_(0),
|
||||
'UseClientCertificate': False,
|
||||
'SuppressUserNotification': False,
|
||||
'SuppressAutoInstall': False,
|
||||
'SuppressStopButtonOnInstall': False,
|
||||
'PackageVerificationMode': 'hash'
|
||||
}
|
||||
pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID)
|
||||
if pref_value == None:
|
||||
pref_value = default_prefs.get(pref_name)
|
||||
# we're using a default value. We'll write it out to
|
||||
# /Library/Preferences/<BUNDLE_ID>.plist for admin
|
||||
# discoverability
|
||||
set_pref(pref_name, pref_value)
|
||||
if type(pref_value).__name__ == '__NSCFDate':
|
||||
# convert NSDate/CFDates to strings
|
||||
pref_value = str(pref_value)
|
||||
return pref_value
|
||||
|
||||
#####################################################
|
||||
# Apple package utilities
|
||||
@@ -1560,7 +1534,8 @@ def getAvailableDiskSpace(volumepath='/'):
|
||||
try:
|
||||
st = os.statvfs(volumepath)
|
||||
except OSError, e:
|
||||
display_error('Error getting disk space in %s: %s', volumepath, str(e))
|
||||
display_error(
|
||||
'Error getting disk space in %s: %s', volumepath, str(e))
|
||||
return 0
|
||||
|
||||
return st.f_frsize * st.f_bavail / 1024 # f_bavail matches df(1) output
|
||||
@@ -1609,7 +1584,6 @@ def listdir(path):
|
||||
verbose = 1
|
||||
munkistatusoutput = False
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
_prefs = {} # never access this directly; use prefs() instead.
|
||||
report = {}
|
||||
report['Errors'] = []
|
||||
report['Warnings'] = []
|
||||
|
||||
@@ -3,11 +3,8 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IFPkgDescriptionTitle</key>
|
||||
<string>Munki - Managed software installation for OS X</string>
|
||||
<string>munki tools</string>
|
||||
<key>IFPkgDescriptionDescription</key>
|
||||
<string>Munki is a set of tools that, used together with a webserver-based
|
||||
repository of packages and package metadata, can be used by OS X
|
||||
administrators to manage software installs (and in many cases removals)
|
||||
on OS X client machines.</string>
|
||||
<string>Software installation tools for OS X</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -25,7 +25,7 @@ http://webserver/cgi-bin/getmanifest.py?arbitrarystring
|
||||
arbitrarystring could be the hostname, a UUID, a username...
|
||||
|
||||
This could be extended to do wildcard matching, or to
|
||||
read another file that mapped hostnames/strings to catalog
|
||||
read another file that mapped hostnames/strings to manifest
|
||||
files
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user