mirror of
https://github.com/munki/munki.git
synced 2026-04-23 21:40:25 -05:00
Moved all support libraries into munkilib package.
Combined installcheck and managedinstaller into managedsoftwareupdate to avoid race conditions between the two tools. Removed support for Apple Software Updates (because it didn't work correctly!) git-svn-id: http://munki.googlecode.com/svn/trunk@112 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
The actual tools are:
|
||||
|
||||
installcheck - gets the manifest for the client and processes it to determine what, if anything, needs to be installed.
|
||||
managedinstaller - meant to be run as part of a logout hook or as a launchd job. Does the actual installs and removals.
|
||||
ManagedSoftwareUpdate.app - user notification tool.
|
||||
MunkiStatus.app - used by ManagedInstaller to provide user feedback on the installation and removal process.
|
||||
makepkginfo: Helper tool to help create info files for each installer item.
|
||||
makecatalogs: Creates the software catalogs from the pkginfo files.
|
||||
|
||||
|
||||
|
||||
Supporting libraries:
|
||||
munkilib.py - shared functions
|
||||
munkistatus.py - functions to display status using MunkiStatus.app. Can also be called directly as a command-line tool.
|
||||
removepackages.py: used by ManagedInstaller to do package removals. Can also be called directly as a command-line tool.
|
||||
@@ -41,7 +41,7 @@ import plistlib
|
||||
import subprocess
|
||||
import hashlib
|
||||
|
||||
import munkilib
|
||||
import munkilib.munkicommon
|
||||
|
||||
|
||||
def getCatalogInfoFromDmg(dmgpath):
|
||||
@@ -53,12 +53,12 @@ def getCatalogInfoFromDmg(dmgpath):
|
||||
To-do: handle multiple installer items on a disk image
|
||||
"""
|
||||
cataloginfo = None
|
||||
mountpoints = munkilib.mountdmg(dmgpath)
|
||||
mountpoints = munkilib.munkicommon.mountdmg(dmgpath)
|
||||
for mountpoint in mountpoints:
|
||||
for fsitem in os.listdir(mountpoint):
|
||||
itempath = os.path.join(mountpoint, fsitem)
|
||||
if itempath.endswith('.pkg') or itempath.endswith('.mpkg'):
|
||||
cataloginfo = munkilib.getPackageMetaData(itempath)
|
||||
cataloginfo = munkilib.munkicommon.getPackageMetaData(itempath)
|
||||
# get out of fsitem loop
|
||||
break
|
||||
if cataloginfo:
|
||||
@@ -67,7 +67,7 @@ def getCatalogInfoFromDmg(dmgpath):
|
||||
|
||||
#unmount all the mountpoints from the dmg
|
||||
for mountpoint in mountpoints:
|
||||
munkilib.unmountdmg(mountpoint)
|
||||
munkilib.munkicommon.unmountdmg(mountpoint)
|
||||
return cataloginfo
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ def main():
|
||||
if item.endswith('.dmg'):
|
||||
catinfo = getCatalogInfoFromDmg(item)
|
||||
elif item.endswith('.pkg') or item.endswith('.mpkg'):
|
||||
catinfo = munkilib.getPackageMetaData(item)
|
||||
catinfo = munkilib.munkicommon.getPackageMetaData(item)
|
||||
else:
|
||||
print >>sys.stderr, "%s is not an installer package!" % item
|
||||
exit(-1)
|
||||
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2009 Greg Neagle.
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
managedsoftwareupdate
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import optparse
|
||||
import datetime
|
||||
import dateutil.parser
|
||||
import subprocess
|
||||
import plistlib
|
||||
|
||||
import munkilib.munkicommon
|
||||
import munkilib.updatecheck
|
||||
import munkilib.installer
|
||||
from munkilib.munkistatus import osascript
|
||||
|
||||
def getIdleSeconds():
|
||||
# stolen from Karl Kuehn -- thanks, Karl!
|
||||
# I'd like to Python-ize it a bit better; calling awk seems unPythonic, but it works.
|
||||
commandString = "/usr/sbin/ioreg -c IOHIDSystem -d 4 | /usr/bin/awk '/Idle/ { print $4 }'"
|
||||
ioregProcess = subprocess.Popen([commandString], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if ioregProcess.wait() != 0:
|
||||
return 0
|
||||
return int(int(ioregProcess.stdout.read()) / 1000000000) # convert from Nanoseconds
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# check to see if we're root
|
||||
if os.geteuid() != 0:
|
||||
print >>sys.stderr, "You must run this as root!"
|
||||
exit(-1)
|
||||
|
||||
# check to see if another instance of this script is running
|
||||
myname = os.path.basename(sys.argv[0])
|
||||
if munkilib.munkicommon.pythonScriptRunning(myname):
|
||||
# another instance of this script is running, so we should quit
|
||||
print >>sys.stderr, "Another instance of %s is running. Exiting." % myname
|
||||
exit(0)
|
||||
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--auto', '-a', action='store_true',
|
||||
help='No user feedback or intervention. All other options ignored.')
|
||||
p.add_option('--munkistatusoutput', '-m', action='store_true',
|
||||
help='Uses MunkiStatus.app for progress feedback when installing.')
|
||||
p.add_option('--id', default='',
|
||||
help='Alternate identifier for catalog retreival')
|
||||
p.add_option('--quiet', '-q', action='store_true',
|
||||
help='Quiet mode. Logs messages, but nothing to stdout.')
|
||||
p.add_option('--verbose', '-v', action='count', default=0,
|
||||
help='More verbose output. May be specified multiple times.')
|
||||
p.add_option('--checkonly', action='store_true',
|
||||
help="Check for updates, but don't install them.")
|
||||
p.add_option('--installonly', action='store_true',
|
||||
help='Skip checking and install any pending updates.')
|
||||
|
||||
options, arguments = p.parse_args()
|
||||
|
||||
if options.auto:
|
||||
options.munkistatusoutput = True
|
||||
options.quiet = True
|
||||
options.verbose = 0
|
||||
options.checkonly = False
|
||||
options.installonly = False
|
||||
|
||||
if options.checkonly and options.installonly:
|
||||
print >>sys.stderr, "--checkonly and --installonly options are mutually exclusive!"
|
||||
exit(-1)
|
||||
|
||||
updatesavailable = False
|
||||
if not options.installonly:
|
||||
result = munkilib.updatecheck.check(id=options.id, verbosity=options.verbose, quiet=options.quiet)
|
||||
|
||||
if result > 0:
|
||||
updatesavailable = True
|
||||
|
||||
if result == -1:
|
||||
# there were errors checking for updates.
|
||||
# let's check to see if there's a InstallInfo.plist with waiting updates from
|
||||
# an earlier successful run
|
||||
installinfo = os.path.join(munkilib.munkicommon.ManagedInstallDir(), 'InstallInfo.plist')
|
||||
if os.path.exists(installinfo):
|
||||
try:
|
||||
pl = plistlib.readPlist(installinfo)
|
||||
removalcount = munkilib.installer.getRemovalCount(pl.get('removals',[]))
|
||||
installcount = munkilib.installer.getInstallCount(pl.get('managed_installs',[]))
|
||||
if removalcount or installcount:
|
||||
updatesavailable = True
|
||||
except:
|
||||
print >>sys.stderr, "Invalid %s" % installinfo
|
||||
|
||||
|
||||
if updatesavailable or options.installonly:
|
||||
if options.auto:
|
||||
if munkilib.munkicommon.getconsoleuser() == None:
|
||||
if getIdleSeconds() > 10:
|
||||
# no-one is logged in, and
|
||||
# no keyboard or mouse movement for the past 10 seconds,
|
||||
# therefore no-one is in the middle of logging in (we hope!)
|
||||
# so just install
|
||||
munkilib.installer.run(options.munkistatusoutput)
|
||||
else:
|
||||
# someone is logged in, and we have updates.
|
||||
# if we haven't notified in a (admin-configurable) while, notify:
|
||||
lastNotifiedString = munkilib.munkicommon.pref('LastNotifiedDate')
|
||||
daysBetweenNotifications = munkilib.munkicommon.pref('DaysBetweenNotifications')
|
||||
nowString = munkilib.munkicommon.NSDateNowString()
|
||||
now = dateutil.parser.parse(nowString)
|
||||
if lastNotifiedString:
|
||||
lastNotifiedDate = dateutil.parser.parse(lastNotifiedString)
|
||||
interval = datetime.timedelta(days=daysBetweenNotifications)
|
||||
nextNotifyDate = lastNotifiedDate + interval
|
||||
if now >= nextNotifyDate:
|
||||
# record current notification date
|
||||
cmd = ['/usr/bin/defaults', 'write', '/Library/Preferences/ManagedInstalls',
|
||||
'LastNotifiedDate', '-date', now.ctime()]
|
||||
retcode = subprocess.call(cmd)
|
||||
# notify user of available updates
|
||||
result = osascript('tell application "Managed Software Update" to activate')
|
||||
else:
|
||||
if options.installonly:
|
||||
# install!
|
||||
munkilib.installer.run(options.munkistatusoutput)
|
||||
else:
|
||||
print "Run %s --installonly to install the downloaded updates." % myname
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
# this is needed to make Python recognize the directory as a module package.
|
||||
Binary file not shown.
Executable → Regular
+52
-87
@@ -14,8 +14,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
managedinstaller
|
||||
Tool to automatically install pkgs, mpkgs, and dmgs
|
||||
installer.py
|
||||
munki module to automatically install pkgs, mpkgs, and dmgs
|
||||
(containing pkgs and mpkgs) from a defined folder.
|
||||
"""
|
||||
|
||||
@@ -24,13 +24,14 @@ import subprocess
|
||||
import sys
|
||||
import time
|
||||
import plistlib
|
||||
import optparse
|
||||
import munkilib
|
||||
import munkicommon
|
||||
import munkistatus
|
||||
from removepackages import removepackages
|
||||
|
||||
global munkistatusoutput
|
||||
|
||||
def stopRequested():
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
if munkistatus.getStopButtonState() == 1:
|
||||
log("### User stopped session ###")
|
||||
return True
|
||||
@@ -38,7 +39,7 @@ def stopRequested():
|
||||
|
||||
|
||||
def cleanup():
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.quit()
|
||||
|
||||
|
||||
@@ -55,7 +56,7 @@ def createDirsIfNeeded(dirlist):
|
||||
|
||||
|
||||
def log(message):
|
||||
logfile = os.path.join(logdir,'ManagedInstaller.log')
|
||||
logfile = os.path.join(logdir,'ManagedSoftwareUpdate.log')
|
||||
try:
|
||||
f = open(logfile, mode='a', buffering=1)
|
||||
print >>f, time.ctime(), message
|
||||
@@ -80,7 +81,7 @@ def install(pkgpath):
|
||||
(output, err) = p.communicate()
|
||||
packagename = output.splitlines()[0]
|
||||
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.message("Installing %s..." % packagename)
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
@@ -93,7 +94,7 @@ def install(pkgpath):
|
||||
restartaction = output.rstrip("\n")
|
||||
if restartaction == "RequireRestart":
|
||||
message = "%s requires a restart after installation." % packagename
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(message)
|
||||
else:
|
||||
print message
|
||||
@@ -117,7 +118,7 @@ def install(pkgpath):
|
||||
if msg.startswith("PHASE:"):
|
||||
phase = msg[6:]
|
||||
if phase:
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(phase)
|
||||
else:
|
||||
print phase
|
||||
@@ -125,24 +126,24 @@ def install(pkgpath):
|
||||
elif msg.startswith("STATUS:"):
|
||||
status = msg[7:]
|
||||
if status:
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(status)
|
||||
else:
|
||||
print status
|
||||
sys.stdout.flush()
|
||||
elif msg.startswith("%"):
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
percent = float(msg[1:])
|
||||
percent = int(percent * 100)
|
||||
munkistatus.percent(percent)
|
||||
elif msg.startswith(" Error"):
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(msg)
|
||||
else:
|
||||
print >>sys.stderr, msg
|
||||
log(msg)
|
||||
elif msg.startswith(" Cannot install"):
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(msg)
|
||||
else:
|
||||
print >>sys.stderr, msg
|
||||
@@ -153,7 +154,7 @@ def install(pkgpath):
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
message = "Install of %s failed." % packagename
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(message)
|
||||
print >>sys.stderr, message
|
||||
log(message)
|
||||
@@ -169,7 +170,7 @@ def install(pkgpath):
|
||||
restartneeded = False
|
||||
else:
|
||||
log("Install of %s was successful." % packagename)
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.percent(100)
|
||||
|
||||
return (retcode, restartneeded)
|
||||
@@ -188,18 +189,18 @@ def installall(dirpath):
|
||||
return restartflag
|
||||
itempath = os.path.join(dirpath, item)
|
||||
if item.endswith(".dmg"):
|
||||
if not options.munkistatusoutput:
|
||||
if not munkistatusoutput:
|
||||
print "Mounting disk image %s" % item
|
||||
log("Mounting disk image %s" % item)
|
||||
mountpoints = munkilib.mountdmg(itempath)
|
||||
mountpoints = munkicommon.mountdmg(itempath)
|
||||
if mountpoints == []:
|
||||
if not options.munkistatusoutput:
|
||||
if not munkistatusoutput:
|
||||
print >>sys.stderr, "ERROR: No filesystems mounted from %s" % item
|
||||
log("ERROR: No filesystems mounted from %s" % item)
|
||||
return restartflag
|
||||
if stopRequested():
|
||||
for mountpoint in mountpoints:
|
||||
munkilib.unmountdmg(mountpoint)
|
||||
munkicommon.unmountdmg(mountpoint)
|
||||
return restartflag
|
||||
for mountpoint in mountpoints:
|
||||
# install all the pkgs and mpkgs at the root
|
||||
@@ -207,7 +208,7 @@ def installall(dirpath):
|
||||
needtorestart = installall(mountpoint)
|
||||
if needtorestart:
|
||||
restartflag = True
|
||||
munkilib.unmountdmg(mountpoint)
|
||||
munkicommon.unmountdmg(mountpoint)
|
||||
|
||||
if (item.endswith(".pkg") or item.endswith(".mpkg")):
|
||||
(retcode, needsrestart) = install(itempath)
|
||||
@@ -244,18 +245,18 @@ def installWithInfo(dirpath, installlist):
|
||||
return restartflag
|
||||
|
||||
if itempath.endswith(".dmg"):
|
||||
if not options.munkistatusoutput:
|
||||
if not munkistatusoutput:
|
||||
print "Mounting disk image %s" % item["installer_item"]
|
||||
log("Mounting disk image %s" % item["installer_item"])
|
||||
mountpoints = munkilib.mountdmg(itempath)
|
||||
mountpoints = munkicommon.mountdmg(itempath)
|
||||
if mountpoints == []:
|
||||
if not options.munkistatusoutput:
|
||||
if not munkistatusoutput:
|
||||
print >>sys.stderr, "ERROR: No filesystems mounted from %s" % item["installer_item"]
|
||||
log("ERROR: No filesystems mounted from %s" % item["installer_item"])
|
||||
return restartflag
|
||||
if stopRequested():
|
||||
for mountpoint in mountpoints:
|
||||
munkilib.unmountdmg(mountpoint)
|
||||
munkicommon.unmountdmg(mountpoint)
|
||||
return restartflag
|
||||
for mountpoint in mountpoints:
|
||||
# install all the pkgs and mpkgs at the root
|
||||
@@ -263,9 +264,9 @@ def installWithInfo(dirpath, installlist):
|
||||
needtorestart = installall(mountpoint)
|
||||
if needtorestart:
|
||||
restartflag = True
|
||||
munkilib.unmountdmg(mountpoint)
|
||||
munkicommon.unmountdmg(mountpoint)
|
||||
else:
|
||||
itempath = munkilib.findInstallerItem(itempath)
|
||||
itempath = munkicommon.findInstallerItem(itempath)
|
||||
if (itempath.endswith(".pkg") or itempath.endswith(".mpkg") or itempath.endswith(".dist")):
|
||||
(retcode, needsrestart) = install(itempath)
|
||||
if needsrestart:
|
||||
@@ -304,7 +305,7 @@ def processRemovals(removalList):
|
||||
if 'packages' in item:
|
||||
if item.get('RestartAction') == "RequireRestart":
|
||||
restartFlag = True
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.message("Removing %s..." % name)
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
@@ -313,9 +314,9 @@ def processRemovals(removalList):
|
||||
|
||||
log("Removing %s..." % name)
|
||||
retcode = removepackages(item['packages'],
|
||||
munkistatusoutput=options.munkistatusoutput,
|
||||
munkistatusoutput=munkistatusoutput,
|
||||
forcedeletebundles=True,
|
||||
logfile=os.path.join(logdir,'ManagedInstaller.log'))
|
||||
logfile=os.path.join(logdir,'ManagedSoftwareUpdate.log'))
|
||||
if retcode:
|
||||
if retcode == -128:
|
||||
message = "Uninstall of %s was cancelled." % name
|
||||
@@ -328,7 +329,7 @@ def processRemovals(removalList):
|
||||
|
||||
elif os.path.exists(uninstallmethod[0]) and os.access(uninstallmethod[0], os.X_OK):
|
||||
# it's a script or program to uninstall
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.message("Running uninstall script for %s..." % name)
|
||||
# set indeterminate progress bar
|
||||
munkistatus.percent(-1)
|
||||
@@ -347,7 +348,7 @@ def processRemovals(removalList):
|
||||
# an error so we can dump it to the log
|
||||
uninstalleroutput.append(msg)
|
||||
msg = msg.rstrip("\n")
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
# do nothing with the output
|
||||
pass
|
||||
else:
|
||||
@@ -370,7 +371,7 @@ def processRemovals(removalList):
|
||||
else:
|
||||
log("Uninstall of %s was successful." % name)
|
||||
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
|
||||
@@ -383,56 +384,31 @@ def processRemovals(removalList):
|
||||
|
||||
# module (global) variables
|
||||
logdir = None
|
||||
options = None
|
||||
munkistatusoutput = False
|
||||
|
||||
|
||||
def main():
|
||||
global logdir, options
|
||||
def run(use_munkistatus):
|
||||
global logdir
|
||||
global munkistatusoutput
|
||||
|
||||
# check to see if we're root
|
||||
if os.geteuid() != 0:
|
||||
print >>sys.stderr, "You must run this as root!"
|
||||
exit(-1)
|
||||
if use_munkistatus:
|
||||
munkistatusoutput = True
|
||||
|
||||
managedinstallbase = munkilib.ManagedInstallDir()
|
||||
managedinstallbase = munkicommon.ManagedInstallDir()
|
||||
installdir = os.path.join(managedinstallbase , 'Cache')
|
||||
logdir = os.path.join(managedinstallbase, 'Logs')
|
||||
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--munkistatusoutput', '-m', action='store_true')
|
||||
options, arguments = p.parse_args()
|
||||
|
||||
needtorestart = removals_need_restart = installs_need_restart = appleupdates_need_restart = False
|
||||
needtorestart = removals_need_restart = installs_need_restart = False
|
||||
createDirsIfNeeded([logdir])
|
||||
log("### Beginning managed installer session ###")
|
||||
|
||||
if munkilib.pythonScriptRunning("installcheck"):
|
||||
# sleep one second and check again in case we
|
||||
# were launched by installcheck
|
||||
time.sleep(1)
|
||||
if munkilib.pythonScriptRunning("installcheck"):
|
||||
# installcheck is running, so we should quit
|
||||
print "installcheck is running. Exiting."
|
||||
log("installcheck is running. Exiting.")
|
||||
log("### End managed installer session ###")
|
||||
exit(0)
|
||||
|
||||
# check to see if another instance of this script is running
|
||||
myname = os.path.basename(sys.argv[0])
|
||||
if munkilib.pythonScriptRunning(myname):
|
||||
# another instance of this script is running, so we should quit
|
||||
print "Another instance of %s is running. Exiting." % myname
|
||||
log("Another instance of %s is running. Exiting." % myname)
|
||||
log("### End managed installer session ###")
|
||||
exit(0)
|
||||
|
||||
installinfo = os.path.join(managedinstallbase, 'InstallInfo.plist')
|
||||
if os.path.exists(installinfo):
|
||||
try:
|
||||
pl = plistlib.readPlist(installinfo)
|
||||
except:
|
||||
print >>sys.stderr, "Invalid %s" % installinfo
|
||||
exit(-1)
|
||||
return -1
|
||||
|
||||
# remove the install info file
|
||||
# it's no longer valid once we start running
|
||||
@@ -441,7 +417,7 @@ def main():
|
||||
if "removals" in pl:
|
||||
removalcount = getRemovalCount(pl['removals'])
|
||||
if removalcount:
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
if removalcount == 1:
|
||||
munkistatus.message("Removing 1 item...")
|
||||
else:
|
||||
@@ -453,10 +429,8 @@ def main():
|
||||
if "managed_installs" in pl:
|
||||
if not stopRequested():
|
||||
installcount = getInstallCount(pl['managed_installs'])
|
||||
if "apple_updates" in pl:
|
||||
installcount = installcount + len(pl['apple_updates'])
|
||||
if installcount:
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
if installcount == 1:
|
||||
munkistatus.message("Installing 1 item...")
|
||||
else:
|
||||
@@ -465,19 +439,14 @@ def main():
|
||||
munkistatus.percent(-1)
|
||||
log("Processing installs")
|
||||
installs_need_restart = installWithInfo(installdir, pl['managed_installs'])
|
||||
if "apple_updates" in pl:
|
||||
if not stopRequested():
|
||||
log("Installing Apple updates")
|
||||
swupdldir = '/var/root/Downloads'
|
||||
appleupdates_need_restart = installWithInfo(swupdldir, pl['apple_updates']) or needtorestart
|
||||
|
||||
else:
|
||||
log("No %s found." % installinfo)
|
||||
|
||||
needtorestart = removals_need_restart or installs_need_restart or appleupdates_need_restart
|
||||
needtorestart = removals_need_restart or installs_need_restart
|
||||
if needtorestart:
|
||||
log("Software installed or removed requires a restart.")
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.hideStopButton()
|
||||
munkistatus.message("Software installed or removed requires a restart.")
|
||||
munkistatus.percent(-1)
|
||||
@@ -488,22 +457,18 @@ def main():
|
||||
log("### End managed installer session ###")
|
||||
|
||||
if needtorestart:
|
||||
if munkilib.getconsoleuser() == None:
|
||||
if munkicommon.getconsoleuser() == None:
|
||||
time.sleep(5)
|
||||
cleanup()
|
||||
retcode = subprocess.call(["/sbin/shutdown", "-r", "now"])
|
||||
else:
|
||||
if options.munkistatusoutput:
|
||||
# someone is logged in and we're using MunkiStatus
|
||||
if munkistatusoutput:
|
||||
# someone is logged in and we're using munkistatus
|
||||
munkistatus.activate()
|
||||
munkistatus.osascript('tell application "MunkiStatus" to display alert "Restart Required" message "Software installed requires a restart. You will have a chance to save open documents." as critical default button "Restart"')
|
||||
munkistatus.osascript('tell application "munkistatus" to display alert "Restart Required" message "Software installed requires a restart. You will have a chance to save open documents." as critical default button "Restart"')
|
||||
cleanup()
|
||||
munkistatus.osascript('tell application "System Events" to restart')
|
||||
else:
|
||||
print "Please restart immediately."
|
||||
else:
|
||||
cleanup()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
cleanup()
|
||||
Binary file not shown.
@@ -24,6 +24,7 @@ Common functions used by the munki tools.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import plistlib
|
||||
import urllib2
|
||||
import urlparse
|
||||
@@ -56,17 +57,23 @@ def readPlist(plistfile):
|
||||
|
||||
|
||||
def getconsoleuser():
|
||||
osvers = int(os.uname()[2].split('.')[0])
|
||||
if osvers > 9:
|
||||
cmd = ['/usr/bin/who | /usr/bin/grep console']
|
||||
p = subprocess.Popen(cmd, shell=True, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, err) = p.communicate()
|
||||
return output
|
||||
else:
|
||||
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
|
||||
cfuser = SCDynamicStoreCopyConsoleUser( None, None, None )
|
||||
return cfuser[0]
|
||||
# workaround no longer needed, but leaving this here for now...
|
||||
#osvers = int(os.uname()[2].split('.')[0])
|
||||
#if osvers > 9:
|
||||
# cmd = ['/usr/bin/who | /usr/bin/grep console']
|
||||
# p = subprocess.Popen(cmd, shell=True, bufsize=1, stdin=subprocess.PIPE,
|
||||
# stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
# (output, err) = p.communicate()
|
||||
# return output
|
||||
#else:
|
||||
# from SystemConfiguration import SCDynamicStoreCopyConsoleUser
|
||||
# cfuser = SCDynamicStoreCopyConsoleUser( None, None, None )
|
||||
# return cfuser[0]
|
||||
|
||||
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
|
||||
cfuser = SCDynamicStoreCopyConsoleUser( None, None, None )
|
||||
return cfuser[0]
|
||||
|
||||
|
||||
|
||||
def pythonScriptRunning(scriptname):
|
||||
@@ -195,6 +202,7 @@ def prefs():
|
||||
#####################################################
|
||||
|
||||
def getInstallerPkgInfo(filename):
|
||||
"""Uses Apple's installer tool to get basic info about an installer item."""
|
||||
installerinfo = {}
|
||||
p = subprocess.Popen(["/usr/sbin/installer", "-pkginfo", "-verbose", "-plist", "-pkg", filename], bufsize=1,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
@@ -211,6 +219,12 @@ def getInstallerPkgInfo(filename):
|
||||
installerinfo['RestartAction'] = "RequireRestart"
|
||||
if "Title" in pl:
|
||||
installerinfo['display_name'] = pl['Title']
|
||||
|
||||
p = subprocess.Popen(["/usr/sbin/installer", "-query", "RestartAction", "-pkg", filename], bufsize=1,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
if out:
|
||||
installerinfo['RestartAction'] = out.rstrip('\n')
|
||||
|
||||
return installerinfo
|
||||
|
||||
@@ -254,9 +268,12 @@ def getExtendedVersion(bundlepath):
|
||||
return padVersionString(pl["CFBundleShortVersionString"],5)
|
||||
else:
|
||||
return "0.0.0.0.0"
|
||||
|
||||
|
||||
|
||||
def parsePkgRefs(filename):
|
||||
"""Parses a .dist file looking for pkg-ref or pkg-info tags to build a list of
|
||||
included sub-packages"""
|
||||
info = []
|
||||
dom = minidom.parse(filename)
|
||||
pkgrefs = dom.getElementsByTagName("pkg-ref")
|
||||
@@ -269,7 +286,7 @@ def parsePkgRefs(filename):
|
||||
print key, "=>", ref.attributes[key].value.encode('UTF-8')
|
||||
|
||||
pkginfo = {}
|
||||
pkginfo['id'] = ref.attributes['id'].value.encode('UTF-8')
|
||||
pkginfo['packageid'] = ref.attributes['id'].value.encode('UTF-8')
|
||||
pkginfo['version'] = padVersionString(ref.attributes['version'].value.encode('UTF-8'),5)
|
||||
if 'installKBytes' in keys:
|
||||
pkginfo['installed_size'] = int(ref.attributes['installKBytes'].value.encode('UTF-8'))
|
||||
@@ -287,7 +304,7 @@ def parsePkgRefs(filename):
|
||||
print key, "=>", ref.attributes[key].value.encode('UTF-8')
|
||||
|
||||
pkginfo = {}
|
||||
pkginfo['id'] = ref.attributes['identifier'].value.encode('UTF-8')
|
||||
pkginfo['packageid'] = ref.attributes['identifier'].value.encode('UTF-8')
|
||||
pkginfo['version'] = padVersionString(ref.attributes['version'].value.encode('UTF-8'),5)
|
||||
if not pkginfo in info:
|
||||
info.append(pkginfo)
|
||||
@@ -296,7 +313,7 @@ def parsePkgRefs(filename):
|
||||
|
||||
def getFlatPackageInfo(pkgpath):
|
||||
"""
|
||||
returns array of dictionaries with info on packages
|
||||
returns array of dictionaries with info on subpackages
|
||||
contained in the flat package
|
||||
"""
|
||||
|
||||
@@ -310,17 +327,16 @@ def getFlatPackageInfo(pkgpath):
|
||||
packageinfofile = os.path.join(currentdir, "PackageInfo")
|
||||
if os.path.exists(packageinfofile):
|
||||
infoarray = parsePkgRefs(packageinfofile)
|
||||
|
||||
else:
|
||||
distributionfile = os.path.join(currentdir, "Distribution")
|
||||
if os.path.exists(distributionfile):
|
||||
infoarray = parsePkgRefs(distributionfile)
|
||||
|
||||
shutil.rmtree(mytmpdir)
|
||||
return infoarray
|
||||
|
||||
|
||||
def getOnePackageInfo(pkgpath):
|
||||
"""Gets receipt info for a single bundle-style package"""
|
||||
pkginfo = {}
|
||||
plistpath = os.path.join(pkgpath, "Contents", "Info.plist")
|
||||
if os.path.exists(plistpath):
|
||||
@@ -328,9 +344,9 @@ def getOnePackageInfo(pkgpath):
|
||||
pl = plistlib.readPlist(plistpath)
|
||||
|
||||
if "CFBundleIdentifier" in pl:
|
||||
pkginfo['id'] = pl["CFBundleIdentifier"]
|
||||
pkginfo['packageid'] = pl["CFBundleIdentifier"]
|
||||
else:
|
||||
pkginfo['id'] = os.path.basename(pkgpath)
|
||||
pkginfo['packageid'] = os.path.basename(pkgpath)
|
||||
|
||||
if "CFBundleName" in pl:
|
||||
pkginfo['name'] = pl["CFBundleName"]
|
||||
@@ -382,7 +398,7 @@ def getBundlePackageInfo(pkgpath):
|
||||
return infoarray
|
||||
|
||||
|
||||
def getPkgInfo(p):
|
||||
def getReceiptInfo(p):
|
||||
info = []
|
||||
if p.endswith(".pkg") or p.endswith(".mpkg"):
|
||||
if debug:
|
||||
@@ -392,38 +408,13 @@ def getPkgInfo(p):
|
||||
|
||||
if os.path.isdir(p): # bundle-style package?
|
||||
info = getBundlePackageInfo(p)
|
||||
|
||||
elif p.endswith('.dist'):
|
||||
info = parsePkgRefs(p)
|
||||
info = parsePkgRefs(p)
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def examinePackage(p):
|
||||
info = []
|
||||
if p.endswith(".pkg") or p.endswith(".mpkg"):
|
||||
if debug:
|
||||
print "Examining %s" % p
|
||||
if os.path.isfile(p): # new flat package
|
||||
info = getFlatPackageInfo(p)
|
||||
|
||||
if os.path.isdir(p): # bundle-style package?
|
||||
info = getBundlePackageInfo(p)
|
||||
|
||||
if len(info) == 0:
|
||||
print >>sys.stderr, "Can't determine bundle ID of %s." % p
|
||||
return
|
||||
|
||||
# print info
|
||||
for pkg in info:
|
||||
#print pkg
|
||||
pkg_id = pkg['id']
|
||||
vers = pkg['version']
|
||||
print "packageid: %s \t version: %s" % (pkg_id, vers)
|
||||
|
||||
else:
|
||||
print >>sys.stderr, "%s doesn't appear to be an Installer package." % p
|
||||
|
||||
|
||||
def getInstalledPackageVersion(pkgid):
|
||||
"""
|
||||
Checks a package id against the receipts to
|
||||
@@ -442,7 +433,7 @@ def getInstalledPackageVersion(pkgid):
|
||||
info = getBundlePackageInfo(os.path.join(receiptsdir, item))
|
||||
if len(info):
|
||||
infoitem = info[0]
|
||||
foundbundleid = infoitem['id']
|
||||
foundbundleid = infoitem['packageid']
|
||||
foundvers = infoitem['version']
|
||||
if pkgid == foundbundleid:
|
||||
if version.LooseVersion(foundvers) > version.LooseVersion(highestversion):
|
||||
@@ -452,7 +443,7 @@ def getInstalledPackageVersion(pkgid):
|
||||
return highestversion
|
||||
|
||||
# If we got to this point, we haven't found the pkgid yet.
|
||||
# Now check new (Leopard) package database
|
||||
# Now check new (Leopard and later) package database
|
||||
p = subprocess.Popen(["/usr/sbin/pkgutil", "--pkg-info-plist", pkgid], bufsize=1,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
@@ -529,9 +520,11 @@ def getPackageMetaData(pkgitem):
|
||||
pkgitem = findInstallerItem(pkgitem)
|
||||
if pkgitem == None:
|
||||
return {}
|
||||
|
||||
|
||||
# first get the data /usr/sbin/installer will give us
|
||||
installerinfo = getInstallerPkgInfo(pkgitem)
|
||||
info = getPkgInfo(pkgitem)
|
||||
# now look for receipt/subpkg info
|
||||
receiptinfo = getReceiptInfo(pkgitem)
|
||||
|
||||
name = os.path.split(pkgitem)[1]
|
||||
shortname = os.path.splitext(name)[0]
|
||||
@@ -540,7 +533,7 @@ def getPackageMetaData(pkgitem):
|
||||
metaversion = nameAndVersion(shortname)[1]
|
||||
|
||||
highestpkgversion = "0.0"
|
||||
for infoitem in info:
|
||||
for infoitem in receiptinfo:
|
||||
if version.LooseVersion(infoitem['version']) > version.LooseVersion(highestpkgversion):
|
||||
highestpkgversion = infoitem['version']
|
||||
if "installed_size" in infoitem:
|
||||
@@ -549,35 +542,26 @@ def getPackageMetaData(pkgitem):
|
||||
|
||||
if metaversion == "0.0.0.0.0":
|
||||
metaversion = highestpkgversion
|
||||
elif len(info) == 1:
|
||||
elif len(receiptinfo) == 1:
|
||||
# there is only one package in this item
|
||||
metaversion = highestpkgversion
|
||||
elif highestpkgversion.startswith(metaversion):
|
||||
# for example, highestpkgversion is 2.0.3124.0, version in filename is 2.0
|
||||
metaversion = highestpkgversion
|
||||
|
||||
if 'installed_size' in installerinfo:
|
||||
if installerinfo['installed_size'] > 0:
|
||||
installedsize = installerinfo['installed_size']
|
||||
|
||||
|
||||
cataloginfo = {}
|
||||
cataloginfo['name'] = nameAndVersion(shortname)[0]
|
||||
cataloginfo['version'] = metaversion
|
||||
for key in ('display_name', 'RestartAction', 'description'):
|
||||
if key in installerinfo:
|
||||
cataloginfo[key] = installerinfo[key]
|
||||
|
||||
if installedsize > 0:
|
||||
cataloginfo['installed_size'] = installedsize
|
||||
|
||||
cataloginfo['receipts'] = []
|
||||
for infoitem in info:
|
||||
pkginfo = {}
|
||||
if 'filename' in infoitem:
|
||||
pkginfo['filename'] = infoitem['filename']
|
||||
pkginfo['packageid'] = infoitem['id']
|
||||
pkginfo['version'] = infoitem['version']
|
||||
cataloginfo['receipts'].append(pkginfo)
|
||||
|
||||
if 'installed_size' in installerinfo:
|
||||
if installerinfo['installed_size'] > 0:
|
||||
cataloginfo['installed_size'] = installerinfo['installed_size']
|
||||
|
||||
cataloginfo['receipts'] = receiptinfo
|
||||
|
||||
return cataloginfo
|
||||
|
||||
|
||||
@@ -731,6 +715,7 @@ def getHTTPfileIfNewerAtomically(url,destinationpath,showprogress=False, message
|
||||
Gets file from HTTP URL, only if newer on web server.
|
||||
Replaces pre-existing file only on success. (thus 'Atomically')
|
||||
"""
|
||||
err = None
|
||||
mytmpdir = tempfile.mkdtemp()
|
||||
mytemppath = os.path.join(mytmpdir,"TempDownload")
|
||||
if os.path.exists(destinationpath):
|
||||
@@ -741,21 +726,21 @@ def getHTTPfileIfNewerAtomically(url,destinationpath,showprogress=False, message
|
||||
if result == 0:
|
||||
try:
|
||||
os.rename(mytemppath, destinationpath)
|
||||
return destinationpath
|
||||
return destinationpath, err
|
||||
except:
|
||||
print >>sys.stderr, "Could not write to %s" % destinationpath
|
||||
err = "Could not write to %s" % destinationpath
|
||||
destinationpath = None
|
||||
elif result == 304:
|
||||
# not modified, return existing file
|
||||
return destinationpath
|
||||
return destinationpath, err
|
||||
else:
|
||||
print >>sys.stderr, "Error code: %s retreiving %s" % (result, url)
|
||||
err = "Error code: %s retreiving %s" % (result, url)
|
||||
destinationpath = None
|
||||
|
||||
if os.path.exists(mytemppath):
|
||||
os.remove(mytemppath)
|
||||
os.rmdir(mytmpdir)
|
||||
return destinationpath
|
||||
return destinationpath, err
|
||||
|
||||
|
||||
debug = False
|
||||
Binary file not shown.
Binary file not shown.
@@ -34,7 +34,7 @@ import plistlib
|
||||
import sqlite3
|
||||
import time
|
||||
import munkistatus
|
||||
import munkilib
|
||||
import munkicommon
|
||||
|
||||
|
||||
##################################################################
|
||||
@@ -326,32 +326,35 @@ def ImportPackage(packagepath, c):
|
||||
if not line and (p.poll() != None):
|
||||
break
|
||||
|
||||
item = line.rstrip("\n").split("\t")
|
||||
path = item[0]
|
||||
perms = item[1]
|
||||
uidgid = item[2].split("/")
|
||||
uid = uidgid[0]
|
||||
gid = uidgid[1]
|
||||
if path != ".":
|
||||
# special case for MS Office 2008 installers
|
||||
if ppath == "./tmp/com.microsoft.updater/office_location/":
|
||||
ppath = "./Applications/"
|
||||
try:
|
||||
item = line.rstrip("\n").split("\t")
|
||||
path = item[0]
|
||||
perms = item[1]
|
||||
uidgid = item[2].split("/")
|
||||
uid = uidgid[0]
|
||||
gid = uidgid[1]
|
||||
if path != ".":
|
||||
# special case for MS Office 2008 installers
|
||||
if ppath == "./tmp/com.microsoft.updater/office_location/":
|
||||
ppath = "./Applications/"
|
||||
|
||||
# prepend the ppath so the paths match the actual install locations
|
||||
path = path.lstrip("./")
|
||||
path = ppath + path
|
||||
path = path.lstrip("./")
|
||||
# prepend the ppath so the paths match the actual install locations
|
||||
path = path.lstrip("./")
|
||||
path = ppath + path
|
||||
path = path.lstrip("./")
|
||||
|
||||
t = (path, )
|
||||
row = c.execute('SELECT path_key from paths where path = ?', t).fetchone()
|
||||
if not row:
|
||||
c.execute('INSERT INTO paths (path) values (?)', t)
|
||||
pathkey = c.lastrowid
|
||||
else:
|
||||
pathkey = row[0]
|
||||
t = (path, )
|
||||
row = c.execute('SELECT path_key from paths where path = ?', t).fetchone()
|
||||
if not row:
|
||||
c.execute('INSERT INTO paths (path) values (?)', t)
|
||||
pathkey = c.lastrowid
|
||||
else:
|
||||
pathkey = row[0]
|
||||
|
||||
t = (pkgkey, pathkey, uid, gid, perms)
|
||||
c.execute('INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms) values (?, ?, ?, ?, ?)', t)
|
||||
t = (pkgkey, pathkey, uid, gid, perms)
|
||||
c.execute('INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms) values (?, ?, ?, ?, ?)', t)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def ImportBom(bompath, c):
|
||||
@@ -703,15 +706,16 @@ def removeReceipts(pkgkeylist, noupdateapplepkgdb):
|
||||
if row:
|
||||
pkgname = row[0]
|
||||
pkgid = row[1]
|
||||
receiptpath = None
|
||||
if pkgname.endswith('.pkg'):
|
||||
receiptpath = os.path.join('/Library/Receipts', pkgname)
|
||||
if pkgname.endswith('.bom'):
|
||||
receiptpath = os.path.join('/Library/Receipts/boms', pkgname)
|
||||
if receiptpath and os.path.exists(receiptpath):
|
||||
display_info("Removing %s..." % receiptpath)
|
||||
log("Removing %s..." % receiptpath)
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", receiptpath])
|
||||
if osvers < 10:
|
||||
receiptpath = None
|
||||
if pkgname.endswith('.pkg'):
|
||||
receiptpath = os.path.join('/Library/Receipts', pkgname)
|
||||
if pkgname.endswith('.bom'):
|
||||
receiptpath = os.path.join('/Library/Receipts/boms', pkgname)
|
||||
if receiptpath and os.path.exists(receiptpath):
|
||||
display_info("Removing %s..." % receiptpath)
|
||||
log("Removing %s..." % receiptpath)
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", receiptpath])
|
||||
|
||||
# remove pkg info from our database
|
||||
if verbose:
|
||||
@@ -882,7 +886,7 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
|
||||
display_error("You must specify at least one package to remove!")
|
||||
return -2
|
||||
|
||||
if not initDatabase(packagedb,rebuildpkgdb):
|
||||
if not initDatabase(packagedb,forcerebuild=rebuildpkgdb):
|
||||
display_error("Could not initialize receipt database.")
|
||||
return -3
|
||||
|
||||
@@ -918,7 +922,7 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
|
||||
|
||||
|
||||
# some globals
|
||||
packagedb = os.path.join(munkilib.ManagedInstallDir(), "b.receiptdb")
|
||||
packagedb = os.path.join(munkicommon.ManagedInstallDir(), "b.receiptdb")
|
||||
munkistatusoutput = False
|
||||
verbose = False
|
||||
logfile = ''
|
||||
Binary file not shown.
Executable → Regular
+123
-245
@@ -15,15 +15,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
installcheck
|
||||
updatecheck
|
||||
|
||||
Created by Greg Neagle on 2008-11-13.
|
||||
2009-04-27: renamed to 'installcheck'
|
||||
terminology change!
|
||||
replacing "catalog" with "manifest"
|
||||
a "catalog" is now a list of what's available on the server;
|
||||
a "manifest" is a list of what should be installed or
|
||||
uninstalled on this client
|
||||
|
||||
"""
|
||||
|
||||
#standard libs
|
||||
@@ -34,22 +29,21 @@ import tempfile
|
||||
import subprocess
|
||||
from distutils import version
|
||||
import urlparse
|
||||
import optparse
|
||||
import hashlib
|
||||
import datetime
|
||||
import dateutil.parser
|
||||
#import dateutil.parser
|
||||
import time
|
||||
import random
|
||||
#import random
|
||||
import socket
|
||||
|
||||
#our lib
|
||||
import munkilib
|
||||
import munkicommon
|
||||
from munkistatus import osascript
|
||||
|
||||
|
||||
def log(message):
|
||||
global logdir
|
||||
logfile = os.path.join(logdir,'ManagedInstallerCheck.log')
|
||||
logfile = os.path.join(logdir,'ManagedSoftwareUpdate.log')
|
||||
f = open(logfile, mode='a', buffering=1)
|
||||
if f:
|
||||
print >>f, datetime.datetime.now().ctime(), message
|
||||
@@ -66,7 +60,7 @@ def logerror(message):
|
||||
|
||||
|
||||
def printandlog(message, verbositylevel=0):
|
||||
if (options.verbose >= verbositylevel) and not options.quiet:
|
||||
if (option_verbose >= verbositylevel) and not option_quiet:
|
||||
print message
|
||||
if logginglevel >= verbositylevel:
|
||||
log(message)
|
||||
@@ -76,10 +70,10 @@ def reporterrors():
|
||||
# just a placeholder right now;
|
||||
# this needs to be expanded to support error reporting
|
||||
# via email and HTTP CGI.
|
||||
global options
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
clientidentifier = managedinstallprefs.get('ClientIdentifier','')
|
||||
alternate_id = options.id
|
||||
alternate_id = option_id
|
||||
hostname = os.uname()[1]
|
||||
|
||||
print "installcheck errors %s:" % datetime.datetime.now().ctime()
|
||||
@@ -137,8 +131,8 @@ def compareVersions(thisvers, thatvers):
|
||||
Returns 1 if thisvers is the same as thatvers
|
||||
Returns 2 if thisvers is newer than thatvers
|
||||
"""
|
||||
thisvers = munkilib.padVersionString(thisvers,5)
|
||||
thatvers = munkilib.padVersionString(thatvers,5)
|
||||
thisvers = munkicommon.padVersionString(thisvers,5)
|
||||
thatvers = munkicommon.padVersionString(thatvers,5)
|
||||
if version.LooseVersion(thisvers) < version.LooseVersion(thatvers):
|
||||
return -1
|
||||
elif version.LooseVersion(thisvers) == version.LooseVersion(thatvers):
|
||||
@@ -356,7 +350,7 @@ def compareReceiptVersion(item):
|
||||
return -2
|
||||
|
||||
printandlog("Looking for package %s, version %s" % (pkgid, vers), 2)
|
||||
installedvers = munkilib.getInstalledPackageVersion(pkgid)
|
||||
installedvers = munkicommon.getInstalledPackageVersion(pkgid)
|
||||
if installedvers:
|
||||
return compareVersions(installedvers, vers)
|
||||
else:
|
||||
@@ -373,7 +367,7 @@ def getInstalledVersion(pl):
|
||||
if 'receipts' in pl:
|
||||
maxversion = "0.0.0.0.0"
|
||||
for receipt in pl['receipts']:
|
||||
pkgvers = munkilib.getInstalledPackageVersion(receipt['packageid'])
|
||||
pkgvers = munkicommon.getInstalledPackageVersion(receipt['packageid'])
|
||||
if compareVersions(pkgvers, maxversion) == 2:
|
||||
# version is higher
|
||||
maxversion = pkgvers
|
||||
@@ -426,7 +420,7 @@ def download_installeritem(pkgurl):
|
||||
"""
|
||||
global mytmpdir
|
||||
|
||||
ManagedInstallDir = munkilib.ManagedInstallDir()
|
||||
ManagedInstallDir = munkicommon.ManagedInstallDir()
|
||||
mycachedir = os.path.join(ManagedInstallDir, "Cache")
|
||||
pkgname = os.path.basename(urlparse.urlsplit(pkgurl)[2])
|
||||
destinationpath = os.path.join(mycachedir, pkgname)
|
||||
@@ -439,12 +433,12 @@ def download_installeritem(pkgurl):
|
||||
|
||||
dl_message = "Downloading %s from %s" % (pkgname, pkgurl)
|
||||
log(dl_message)
|
||||
if options.quiet:
|
||||
if option_quiet:
|
||||
dl_message = None
|
||||
elif not options.verbose:
|
||||
elif not option_verbose:
|
||||
dl_message = "Downloading %s" % pkgname
|
||||
|
||||
result = munkilib.getfilefromhttpurl(pkgurl, tempfilepath, showprogress=not(options.quiet), ifmodifiedsince=itemmodtime, message=dl_message)
|
||||
result = munkicommon.getfilefromhttpurl(pkgurl, tempfilepath, showprogress=not(option_quiet), ifmodifiedsince=itemmodtime, message=dl_message)
|
||||
if result == 0:
|
||||
os.rename(tempfilepath, destinationpath)
|
||||
return True
|
||||
@@ -518,7 +512,7 @@ def getAllMatchingItems(name,cataloglist):
|
||||
itemlist = []
|
||||
# we'll throw away any included version info
|
||||
(name, includedversion) = nameAndVersion(name)
|
||||
managedinstalldir = munkilib.ManagedInstallDir()
|
||||
managedinstalldir = munkicommon.ManagedInstallDir()
|
||||
catalogsdir = os.path.join(managedinstalldir, 'catalogs')
|
||||
printandlog("Looking for all items matching: %s..." % name, 1)
|
||||
for catalogname in cataloglist:
|
||||
@@ -552,7 +546,7 @@ def getManifestItemDetail(name, cataloglist, version=''):
|
||||
version = includedversion
|
||||
else:
|
||||
version = 'latest'
|
||||
managedinstalldir = munkilib.ManagedInstallDir()
|
||||
managedinstalldir = munkicommon.ManagedInstallDir()
|
||||
catalogsdir = os.path.join(managedinstalldir, 'catalogs')
|
||||
printandlog("Looking for detail for: %s, version %s..." % (name, version), 1)
|
||||
for catalogname in cataloglist:
|
||||
@@ -599,7 +593,7 @@ def enoughDiskSpace(manifestitem_pl):
|
||||
if 'installed_size' in manifestitem_pl:
|
||||
installedsize = manifestitem_pl['installed_size']
|
||||
diskspaceneeded = (installeritemsize + installedsize + fudgefactor)/1024
|
||||
availablediskspace = munkilib.getAvailableDiskSpace()/1024
|
||||
availablediskspace = munkicommon.getAvailableDiskSpace()/1024
|
||||
if availablediskspace > diskspaceneeded:
|
||||
return True
|
||||
else:
|
||||
@@ -707,7 +701,7 @@ def processInstalls(manifestitem, cataloglist, installinfo):
|
||||
will stop the installation of a dependent item
|
||||
"""
|
||||
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
sw_repo_baseurl = managedinstallprefs['SoftwareRepoURL']
|
||||
ManagedInstallDir = managedinstallprefs['ManagedInstallDir']
|
||||
|
||||
@@ -959,7 +953,7 @@ def processRemovals(manifestitem, cataloglist, installinfo):
|
||||
# and we're supposed to remove SomePackage--1.0.1.0.0... what do we do?
|
||||
#
|
||||
dependentitemsremoved = True
|
||||
ManagedInstallDir = munkilib.ManagedInstallDir()
|
||||
ManagedInstallDir = munkicommon.ManagedInstallDir()
|
||||
catalogsdir = os.path.join(ManagedInstallDir, 'catalogs')
|
||||
|
||||
# make a list of the name and aliases of the current uninstall_item
|
||||
@@ -1068,7 +1062,7 @@ def getCatalogs(cataloglist):
|
||||
"""
|
||||
Retreives the catalogs from the server
|
||||
"""
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
sw_repo_baseurl = managedinstallprefs['SoftwareRepoURL']
|
||||
catalog_dir = os.path.join(managedinstallprefs['ManagedInstallDir'], "catalogs")
|
||||
|
||||
@@ -1077,20 +1071,21 @@ def getCatalogs(cataloglist):
|
||||
catalogpath = os.path.join(catalog_dir, catalog)
|
||||
message = "Getting catalog %s from %s..." % (catalog, catalogurl)
|
||||
log(message)
|
||||
if options.quiet:
|
||||
if option_quiet:
|
||||
message = None
|
||||
elif not options.verbose:
|
||||
elif not option_verbose:
|
||||
message = "Retreiving catalog '%s'..." % catalog
|
||||
newcatalog = munkilib.getHTTPfileIfNewerAtomically(catalogurl,catalogpath,showprogress=not(options.quiet), message=message)
|
||||
(newcatalog, err) = munkicommon.getHTTPfileIfNewerAtomically(catalogurl,catalogpath,showprogress=not(option_quiet), message=message)
|
||||
if not newcatalog:
|
||||
logerror("Could not retreive catalog %s from server." % catalog)
|
||||
logerror(err)
|
||||
|
||||
|
||||
def getmanifest(partialurl):
|
||||
def getmanifest(partialurl, suppress_errors=False):
|
||||
"""
|
||||
Gets a manifest from the server
|
||||
"""
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
sw_repo_baseurl = managedinstallprefs['SoftwareRepoURL']
|
||||
manifest_dir = os.path.join(managedinstallprefs['ManagedInstallDir'], "manifests")
|
||||
|
||||
@@ -1106,14 +1101,15 @@ def getmanifest(partialurl):
|
||||
manifestpath = os.path.join(manifest_dir, manifestname)
|
||||
message = "Getting manifest %s from %s..." % (manifestname, manifesturl)
|
||||
log(message)
|
||||
if options.quiet:
|
||||
if option_quiet:
|
||||
message = None
|
||||
elif not options.verbose:
|
||||
elif not option_verbose:
|
||||
message = "Retreiving list of software for this machine..."
|
||||
|
||||
newmanifest = munkilib.getHTTPfileIfNewerAtomically(manifesturl,manifestpath,showprogress=not(options.quiet), message=message)
|
||||
if not newmanifest:
|
||||
(newmanifest, err) = munkicommon.getHTTPfileIfNewerAtomically(manifesturl,manifestpath,showprogress=not(option_quiet), message=message)
|
||||
if not newmanifest and not suppress_errors:
|
||||
logerror("Could not retreive manifest %s from the server." % partialurl)
|
||||
logerror(err)
|
||||
|
||||
return newmanifest
|
||||
|
||||
@@ -1122,7 +1118,7 @@ def getPrimaryManifest(alternate_id):
|
||||
Gets the client manifest from the server
|
||||
"""
|
||||
global errors
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
manifesturl = managedinstallprefs['ManifestURL']
|
||||
clientidentifier = managedinstallprefs.get('ClientIdentifier','')
|
||||
|
||||
@@ -1137,10 +1133,10 @@ def getPrimaryManifest(alternate_id):
|
||||
else:
|
||||
# no client identifier specified, so use the hostname
|
||||
hostname = os.uname()[1]
|
||||
manifest = getmanifest(manifesturl + hostname)
|
||||
manifest = getmanifest(manifesturl + hostname,suppress_errors=True)
|
||||
if not manifest:
|
||||
# try the short hostname
|
||||
manifest = getmanifest(manifesturl + hostname.split('.')[0])
|
||||
manifest = getmanifest(manifesturl + hostname.split('.')[0], suppress_errors=True)
|
||||
if not manifest:
|
||||
# last resort - try for the site_default manifest
|
||||
manifesturl = manifesturl + "site_default"
|
||||
@@ -1148,7 +1144,7 @@ def getPrimaryManifest(alternate_id):
|
||||
if not manifest:
|
||||
manifest = getmanifest(manifesturl)
|
||||
if manifest:
|
||||
# clear out any errors we got while try to find
|
||||
# clear out any errors we got while trying to find
|
||||
# the primary manifest
|
||||
errors = ""
|
||||
|
||||
@@ -1183,86 +1179,9 @@ def isSUinstallItem(itempath):
|
||||
return False
|
||||
|
||||
|
||||
def doSoftwareUpdate(installinfo):
|
||||
installinfo['apple_updates'] = []
|
||||
if munkilib.pref('InstallAppleSoftwareUpdates'):
|
||||
|
||||
# switch to our preferred Software Update Server if supplied
|
||||
if munkilib.pref('SoftwareUpdateServerURL'):
|
||||
oldsuserver = ''
|
||||
cmd = ['/usr/bin/defaults', 'read', '/Library/Preferences/com.apple.SoftwareUpdate', 'CatalogURL']
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
if p.returncode == 0:
|
||||
oldsusserver = out.rstrip('\n')
|
||||
|
||||
cmd = ['/usr/bin/defaults', 'write', '/Library/Preferences/com.apple.SoftwareUpdate',
|
||||
'CatalogURL', munkilib.pref('SoftwareUpdateServerURL')]
|
||||
retcode = subprocess.call(cmd)
|
||||
|
||||
swupdldir = '/var/root/Downloads'
|
||||
runcheck = True
|
||||
# check downloads dir and skip checking if there's
|
||||
# anything in it
|
||||
for item in os.listdir(swupdldir):
|
||||
itempath = os.path.join(swupdldir,item)
|
||||
if isSUinstallItem(itempath):
|
||||
runcheck = False
|
||||
break
|
||||
if runcheck:
|
||||
cmd = ['/usr/sbin/softwareupdate', '-d', '-a']
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
swupdout = p.stdout.readline()
|
||||
if not swupdout and (p.poll() != None):
|
||||
#process completed and we've deal with all the output
|
||||
break
|
||||
printandlog(swupdout.rstrip("\n"))
|
||||
sys.stdout.flush()
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
# some problem occurred.
|
||||
# right now, we'll just stumble through in case some downloads
|
||||
# succeeded
|
||||
pass
|
||||
|
||||
# now check for downloads and process
|
||||
for item in os.listdir(swupdldir):
|
||||
itempath = os.path.join(swupdldir,item)
|
||||
if isSUinstallItem(itempath):
|
||||
pl = munkilib.getPackageMetaData(itempath)
|
||||
if pl:
|
||||
# check to see if there is enough free space to install
|
||||
if enoughDiskSpace(pl):
|
||||
iteminfo = {}
|
||||
iteminfo["installer_item"] = item
|
||||
iteminfo["name"] = pl.get('name', '')
|
||||
iteminfo["description"] = pl.get('description', '')
|
||||
if iteminfo["description"] == '':
|
||||
iteminfo["description"] = "Updated Apple software."
|
||||
iteminfo["version_to_install"] = pl.get('version',"UNKNOWN")
|
||||
iteminfo['display_name'] = pl.get('display_name','')
|
||||
if 'RestartAction' in pl:
|
||||
iteminfo['RestartAction'] = pl['RestartAction']
|
||||
installinfo['apple_updates'].append(iteminfo)
|
||||
|
||||
# switch back to original Software Update server
|
||||
if munkilib.pref('SoftwareUpdateServerURL'):
|
||||
if oldsuserver:
|
||||
cmd = ['/usr/bin/defaults', 'write', '/Library/Preferences/com.apple.SoftwareUpdate',
|
||||
'CatalogURL', oldsuserver]
|
||||
else:
|
||||
cmd = ['/usr/bin/defaults', 'delete', '/Library/Preferences/com.apple.SoftwareUpdate']
|
||||
retcode = subprocess.call(cmd)
|
||||
|
||||
return installinfo
|
||||
|
||||
|
||||
def checkServer():
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
manifesturl = managedinstallprefs['ManifestURL']
|
||||
# deconstruct URL so we can check availability
|
||||
port = 80
|
||||
@@ -1279,7 +1198,7 @@ def checkServer():
|
||||
s = socket.socket()
|
||||
#try:
|
||||
s.connect((host, port))
|
||||
s.close
|
||||
s.close()
|
||||
return True
|
||||
#except:
|
||||
#return False
|
||||
@@ -1291,27 +1210,26 @@ logdir = ''
|
||||
logginglevel = 1
|
||||
mytmpdir = tempfile.mkdtemp()
|
||||
errors = ''
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--id', '-i', default='',
|
||||
help='Alternate identifier for catalog retreival')
|
||||
p.add_option('--quiet', '-q', action='store_true',
|
||||
help='Quiet mode. Logs messages, but nothing to stdout.')
|
||||
p.add_option('--verbose', '-v', action='count', default=0,
|
||||
help='More verbose output. May be specified multiple times.')
|
||||
options, arguments = p.parse_args()
|
||||
option_id=''
|
||||
option_verbose=0
|
||||
option_quiet=False
|
||||
|
||||
|
||||
def main():
|
||||
global mytmpdir, options, logdir, errors
|
||||
def check(id='', verbosity=0, quiet=False):
|
||||
'''Checks for available new or updated managed software, downloading installer items
|
||||
if needed. Returns 1 if there are available updates, 0 if there are no available updates,
|
||||
and -1 if there were errors.'''
|
||||
|
||||
# check to see if we're root
|
||||
if os.geteuid() != 0:
|
||||
print >>sys.stderr, "You must run this as root!"
|
||||
exit(-1)
|
||||
global mytmpdir, logdir, errors
|
||||
global option_id, option_verbose, option_quiet
|
||||
|
||||
if not options.quiet: print "Managed Software Check\n"
|
||||
option_id =id
|
||||
option_verbose = verbosity
|
||||
option_quiet = quiet
|
||||
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
if not option_quiet: print "Managed Software Check\n"
|
||||
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
ManagedInstallDir = managedinstallprefs['ManagedInstallDir']
|
||||
logginglevel = managedinstallprefs.get('LoggingLevel', 1)
|
||||
manifestsdir = os.path.join(ManagedInstallDir, "manifests")
|
||||
@@ -1328,71 +1246,58 @@ def main():
|
||||
exit(-1)
|
||||
log("### Beginning managed software check ###")
|
||||
|
||||
if munkilib.pythonScriptRunning("managedinstaller"):
|
||||
# managedinstaller is running, so we should quit
|
||||
printandlog("managedinstaller is running. Exiting.")
|
||||
log("### End managed software check ###")
|
||||
exit(0)
|
||||
mainmanifestpath = getPrimaryManifest(option_id)
|
||||
if mainmanifestpath:
|
||||
# initialize our installinfo record
|
||||
installinfo = {}
|
||||
installinfo['managed_installs'] = []
|
||||
installinfo['removals'] = []
|
||||
|
||||
# check to see if another instance of this script is running
|
||||
myname = os.path.basename(sys.argv[0])
|
||||
if munkilib.pythonScriptRunning(myname):
|
||||
# another instance of this script is running, so we should quit
|
||||
printandlog("Another instance of %s is running. Exiting." % myname)
|
||||
log("### End managed software check ###")
|
||||
exit(0)
|
||||
printandlog("**Checking for installs**", 1)
|
||||
installinfo = processManifestForInstalls(mainmanifestpath, installinfo)
|
||||
|
||||
# clean up cache dir
|
||||
# remove any item in the install cache that isn't scheduled
|
||||
# to be installed --
|
||||
# this allows us to 'pull back' an item before it is installed
|
||||
# by removing it from the manifest
|
||||
installer_item_list = []
|
||||
for item in installinfo['managed_installs']:
|
||||
if "installer_item" in item:
|
||||
installer_item_list.append(item["installer_item"])
|
||||
|
||||
mainmanifestpath = getPrimaryManifest(options.id)
|
||||
if not mainmanifestpath:
|
||||
logerror("Could not retreive managed install primary manifest.")
|
||||
# we can't continue.
|
||||
# we need a way to notify the admin of this problem --
|
||||
# logging to a local file isn't really sufficient.
|
||||
reporterrors()
|
||||
exit(-1)
|
||||
|
||||
# initialize our installinfo record
|
||||
installinfo = {}
|
||||
installinfo['managed_installs'] = []
|
||||
installinfo['removals'] = []
|
||||
|
||||
printandlog("**Checking for installs**", 1)
|
||||
installinfo = processManifestForInstalls(mainmanifestpath, installinfo)
|
||||
|
||||
# clean up cache dir
|
||||
# remove any item in the install cache that isn't scheduled
|
||||
# to be installed --
|
||||
# this allows us to 'pull back' an item before it is installed
|
||||
# by removing it from the manifest
|
||||
installer_item_list = []
|
||||
for item in installinfo['managed_installs']:
|
||||
if "installer_item" in item:
|
||||
installer_item_list.append(item["installer_item"])
|
||||
|
||||
for item in os.listdir(cachedir):
|
||||
if item not in installer_item_list:
|
||||
if options.verbose > 1:
|
||||
print "Removing %s from cache" % item
|
||||
os.unlink(os.path.join(cachedir, item))
|
||||
for item in os.listdir(cachedir):
|
||||
if item not in installer_item_list:
|
||||
if option_verbose > 1:
|
||||
print "Removing %s from cache" % item
|
||||
os.unlink(os.path.join(cachedir, item))
|
||||
|
||||
# now generate a list of items to be uninstalled
|
||||
printandlog("**Checking for removals**", 1)
|
||||
installinfo = processManifestForRemovals(mainmanifestpath, installinfo)
|
||||
|
||||
printandlog("**Checking for Apple Software Updates**", 1)
|
||||
installinfo = doSoftwareUpdate(installinfo)
|
||||
|
||||
# need to write out install list so the autoinstaller
|
||||
# can use it to install things in the right order
|
||||
installinfochanged = True
|
||||
installinfopath = os.path.join(ManagedInstallDir, "InstallInfo.plist")
|
||||
if os.path.exists(installinfopath):
|
||||
oldinstallinfo = plistlib.readPlist(installinfopath)
|
||||
if oldinstallinfo == installinfo:
|
||||
installinfochanged = False
|
||||
printandlog("No change in InstallInfo.", 1)
|
||||
if installinfochanged:
|
||||
plistlib.writePlist(installinfo, os.path.join(ManagedInstallDir, "InstallInfo.plist"))
|
||||
# now generate a list of items to be uninstalled
|
||||
printandlog("**Checking for removals**", 1)
|
||||
installinfo = processManifestForRemovals(mainmanifestpath, installinfo)
|
||||
|
||||
# need to write out install list so the autoinstaller
|
||||
# can use it to install things in the right order
|
||||
installinfochanged = True
|
||||
installinfopath = os.path.join(ManagedInstallDir, "InstallInfo.plist")
|
||||
if os.path.exists(installinfopath):
|
||||
oldinstallinfo = plistlib.readPlist(installinfopath)
|
||||
if oldinstallinfo == installinfo:
|
||||
installinfochanged = False
|
||||
printandlog("No change in InstallInfo.", 1)
|
||||
if installinfochanged:
|
||||
plistlib.writePlist(installinfo, os.path.join(ManagedInstallDir, "InstallInfo.plist"))
|
||||
|
||||
else:
|
||||
# couldn't get a primary manifest. Check to see if we have a valid InstallList from
|
||||
# an earlier run.
|
||||
logerror("Could not retreive managed install primary manifest.")
|
||||
installinfopath = os.path.join(ManagedInstallDir, "InstallInfo.plist")
|
||||
if os.path.exists(installinfopath):
|
||||
try:
|
||||
installinfo = plistlib.readPlist(installinfopath)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
# clean up our tmp dir
|
||||
@@ -1400,11 +1305,11 @@ def main():
|
||||
except:
|
||||
# not fatal if it fails
|
||||
pass
|
||||
|
||||
|
||||
installcount = getInstallCount(installinfo)
|
||||
removalcount = getRemovalCount(installinfo)
|
||||
appleupdatecount = len(installinfo.get('apple_updates',[]))
|
||||
|
||||
|
||||
if installcount:
|
||||
printandlog("The following items will be installed or upgraded:")
|
||||
for item in installinfo['managed_installs']:
|
||||
@@ -1421,54 +1326,27 @@ def main():
|
||||
printandlog(" - %s" % item.get('name'))
|
||||
if item.get('RestartAction') == 'RequireRestart':
|
||||
printandlog(" *Restart required")
|
||||
if appleupdatecount:
|
||||
printandlog("The following Apple updates will be installed:")
|
||||
for item in installinfo['apple_updates']:
|
||||
printandlog (" + %s-%s" % (item.get('name'), item.get('version_to_install')))
|
||||
if item.get('description') != "Updated Apple software.":
|
||||
printandlog(" %s" % item['description'])
|
||||
if item.get('RestartAction') == 'RequireRestart':
|
||||
printandlog(" *Restart required")
|
||||
|
||||
|
||||
if installcount == 0 and removalcount == 0:
|
||||
printandlog("No changes to managed software scheduled.")
|
||||
|
||||
log("### End managed software check ###")
|
||||
|
||||
if errors:
|
||||
reporterrors()
|
||||
|
||||
if installcount == 0 and removalcount == 0 and appleupdatecount == 0:
|
||||
printandlog("No changes to managed software scheduled.")
|
||||
|
||||
if installcount or removalcount:
|
||||
return 1
|
||||
else:
|
||||
if munkilib.getconsoleuser() == None:
|
||||
# eventually trigger managedinstaller here
|
||||
# we could:
|
||||
# - call managedinstaller directly, but we'd have to either
|
||||
# hard-code its path or search a few paths for it
|
||||
# - call `launchctl start com.googlecode.munki-managedinstaller`, but that
|
||||
# relies on that launchd job being installed
|
||||
# - indirectly trigger `launchctl start com.googlecode.munki-managedinstaller`
|
||||
# by touching its watch file, just like Managed Software Update.app does
|
||||
#
|
||||
pass
|
||||
else:
|
||||
# some one is logged in, and we have updates.
|
||||
# if we haven't notified in a (admin-configurable) while, notify:
|
||||
lastNotifiedString = munkilib.pref('LastNotifiedDate')
|
||||
daysBetweenNotifications = munkilib.pref('DaysBetweenNotifications')
|
||||
nowString = munkilib.NSDateNowString()
|
||||
now = dateutil.parser.parse(nowString)
|
||||
if lastNotifiedString:
|
||||
lastNotifiedDate = dateutil.parser.parse(lastNotifiedString)
|
||||
interval = datetime.timedelta(days=daysBetweenNotifications)
|
||||
nextNotifyDate = lastNotifiedDate + interval
|
||||
if now >= nextNotifyDate:
|
||||
# record current notification date
|
||||
cmd = ['/usr/bin/defaults', 'write', '/Library/Preferences/ManagedInstalls',
|
||||
'LastNotifiedDate', '-date', now.ctime()]
|
||||
retcode = subprocess.call(cmd)
|
||||
# notify user of available updates
|
||||
result = osascript('tell application "Managed Software Update" to activate')
|
||||
|
||||
log("### End managed software check ###")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
Binary file not shown.
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
# this script simply sleeps a random number of seconds
|
||||
# up to MAX_DELAY_MINUTES, then runs managedsoftwareupdate --auto.
|
||||
# It's meant to spread the load on the munki server
|
||||
# so all clients don't hit it simultaneously.
|
||||
# If launchd had a way to randomize the StartInterval,
|
||||
# we wouldn't need to do this.
|
||||
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
if [ "$1" != "" ]; then
|
||||
MAX_DELAY_MINUTES="$1"
|
||||
else
|
||||
MAX_DELAY_MINUTES=60
|
||||
fi
|
||||
MAX_DELAY_SECONDS=$((MAX_DELAY_MINUTES*60))
|
||||
seconds=$(( (RANDOM%MAX_DELAY_SECONDS+1) ))
|
||||
sleep $seconds
|
||||
/usr/local/munki/managedsoftwareupdate --auto
|
||||
Reference in New Issue
Block a user