Added MunkiStatus.xcodeproject to build MunkiStatus.app.

Added munkistatus.py to communicate with MunkiStstus.app
Modified ManagedInstaller and removepackages to use MunkStatus.app for user feedback instead of iHook.
Removed iHook example files (100.mangagedinstalllogout.hook anbd MI_logout.hook) since we no longer use iHook for user feedback.


git-svn-id: http://munki.googlecode.com/svn/trunk@44 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
Greg Neagle
2009-04-21 21:54:25 +00:00
parent 15e145265c
commit ef6abe9460
16 changed files with 3613 additions and 185 deletions
+242 -159
View File
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
installomatic
ManagedInstaller
Tool to automatically install pkgs, mpkgs, and dmgs
(containing pkgs and mpkgs) from a defined folder. Intended
to be run as part of a logout hook, but can be run manually
@@ -27,13 +27,29 @@ import time
import plistlib
import optparse
import managedinstalls
import munkistatus
pathtoremovepackages = "/Users/Shared/munki/munki/code/client/removepackages"
def stopRequested():
if options.munkistatusoutput:
if munkistatus.getStopButtonState() == 1:
log("### User stopped session ###")
return True
return False
def cleanup():
if options.munkistatusoutput:
munkistatus.quit()
def createDirsIfNeeded(dirlist):
for dir in dirlist:
if not os.path.exists(dir):
try:
os.mkdir(dir)
os.makedirs(dir, mode=0755)
except:
print >>sys.stderr, "Could not create %s" % dir
return False
@@ -53,8 +69,7 @@ def log(message):
def countinstallcandidates(dirpath):
"""
Counts the number of pkgs, mpkgs, and dmgs
in dirpath
Get the count of items to be installed
"""
candidatecount = 0
items = os.listdir(dirpath)
@@ -70,11 +85,8 @@ def install(pkgpath):
at pkgpath. Prints status messages to STDOUT.
Returns the installer return code and true if a restart is needed.
"""
global installablecount
global currentinstallable
global options
currentinstallable += 1
restartneeded = False
installeroutput = []
@@ -84,9 +96,11 @@ def install(pkgpath):
(output, err) = p.communicate()
packagename = output.splitlines()[0]
print >>sys.stderr, "Package name is", packagename
if options.ihookoutput:
print "%TITLE Installing " + packagename + "..."
print "%%%s Item %s of %s" % (0, currentinstallable, installablecount)
if options.munkistatusoutput:
munkistatus.message("Installing %s..." % packagename)
# clear indeterminate progress bar
munkistatus.percent(0)
log("Installing %s from %s" % (packagename, os.path.basename(pkgpath)))
cmd = ['/usr/sbin/installer', '-query', 'RestartAction', '-pkg', pkgpath]
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
@@ -95,8 +109,11 @@ def install(pkgpath):
restartaction = output.rstrip("\n")
if restartaction == "RequireRestart":
message = "%s requires a restart after installation." % packagename
print message
sys.stdout.flush()
if options.munkistatusoutput:
munkistatus.detail(message)
else:
print message
sys.stdout.flush()
log(message)
restartneeded = True
@@ -114,31 +131,35 @@ def install(pkgpath):
if msg.startswith("PHASE:"):
phase = msg[6:]
if phase:
print phase
sys.stdout.flush()
if options.munkistatusoutput:
munkistatus.detail(phase)
else:
print phase
sys.stdout.flush()
elif msg.startswith("STATUS:"):
status = msg[7:]
if status:
print status
sys.stdout.flush()
if options.munkistatusoutput:
munkistatus.detail(status)
else:
print status
sys.stdout.flush()
elif msg.startswith("%"):
if options.ihookoutput:
if options.munkistatusoutput:
percent = float(msg[1:])
percent = int(percent * 100)
print "%%%s Item %s of %s" % (percent, currentinstallable, installablecount)
if percent == 100:
overallpercentage = min(100,int(currentinstallable/installablecount * 100))
print "%%%s Item %s of %s" % (overallpercentage, currentinstallable, installablecount)
sys.stdout.flush()
munkistatus.percent(percent)
elif msg.startswith(" Error"):
print msg
sys.stdout.flush()
print >>sys.stderr, msg
if options.munkistatusoutput:
munkistatus.detail(msg)
else:
print >>sys.stderr, msg
log(msg)
elif msg.startswith(" Cannot install"):
print msg
sys.stdout.flush()
print >>sys.stderr, msg
if options.munkistatusoutput:
munkistatus.detail(msg)
else:
print >>sys.stderr, msg
log(msg)
else:
print >>sys.stderr, msg
@@ -146,6 +167,8 @@ def install(pkgpath):
retcode = p.poll()
if retcode:
message = "Install of %s failed." % packagename
if options.munkistatusoutput:
munkistatus.detail(message)
print >>sys.stderr, message
log(message)
message = "-------------------------------------------------"
@@ -160,7 +183,9 @@ def install(pkgpath):
restartneeded = False
else:
log("Install of %s was successful." % packagename)
if options.munkistatusoutput:
munkistatus.percent(100)
return (retcode, restartneeded)
@@ -173,6 +198,8 @@ def installall(dirpath):
restartflag = False
installitems = os.listdir(dirpath)
for item in installitems:
if stopRequested():
return restartflag
itempath = os.path.join(dirpath, item)
if (item.endswith(".pkg") or item.endswith(".mpkg")):
(retcode, needsrestart) = install(itempath)
@@ -180,6 +207,10 @@ def installall(dirpath):
restartflag = True
if item.endswith(".dmg"):
mountpoints = mountdmg(itempath)
if stopRequested():
for mountpoint in mountpoints:
unmountdmg(mountpoint)
return restartflag
for mountpoint in mountpoints:
# install all the pkgs and mpkgs at the root
# of the mountpoint -- call us recursively!
@@ -190,6 +221,15 @@ def installall(dirpath):
return restartflag
def getInstallCount(installList):
count = 0
for item in installList:
if 'installed' in item:
if not item['installed']:
count +=1
return count
def installWithInfo(dirpath, installlist):
"""
@@ -198,17 +238,23 @@ def installWithInfo(dirpath, installlist):
"""
restartflag = False
for item in installlist:
if stopRequested():
return restartflag
if "installer_item" in item:
itempath = os.path.join(dirpath, item["installer_item"])
if not os.path.exists(itempath):
#can't install, so we should stop
return restartFlag
return restartflag
if (itempath.endswith(".pkg") or itempath.endswith(".mpkg")):
(retcode, needsrestart) = install(itempath)
if needsrestart:
restartflag = True
if itempath.endswith(".dmg"):
mountpoints = mountdmg(itempath)
if stopRequested():
for mountpoint in mountpoints:
unmountdmg(mountpoint)
return restartflag
for mountpoint in mountpoints:
# install all the pkgs and mpkgs at the root
# of the mountpoint -- call us recursively!
@@ -224,115 +270,133 @@ def installWithInfo(dirpath, installlist):
return restartflag
def getRemovalCount(removalList):
count = 0
for item in removalList:
if 'installed' in item:
if item['installed']:
count +=1
return count
def processRemovals(removalList):
restartFlag = False
for item in removalList:
name = ""
if 'name' in item:
name = item['name']
elif 'catalogitem' in item:
name = item['catalogitem']
if 'uninstall_method' in item:
uninstallmethod = item['uninstall_method'].split(' ')
if uninstallmethod[0] == "removepackages":
if 'packages' in item:
if options.ihookoutput:
print "%TITLE Removing %s.." % name
#print "%%%s Item %s of %s" % (0, currentinstallable, installablecount)
else:
print "Removing %s..." % name
cmd = ['/Users/Shared/bin/removepackages', '-f']
if options.ihookoutput:
cmd.append('-i')
for package in item['packages']:
cmd.append(package)
uninstalleroutput = []
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if stopRequested():
return restartFlag
if 'installed' in item:
if item['installed']:
name = ""
if 'name' in item:
name = item['name']
elif 'catalogitem' in item:
name = item['catalogitem']
if 'uninstall_method' in item:
uninstallmethod = item['uninstall_method'].split(' ')
if uninstallmethod[0] == "removepackages":
if 'packages' in item:
if options.munkistatusoutput:
munkistatus.message("Removing %s..." % name)
# clear indeterminate progress bar
munkistatus.percent(0)
else:
print "Removing %s..." % name
cmd = [pathtoremovepackages, '-f']
if options.munkistatusoutput:
cmd.append('-m')
cmd.append('-d')
for package in item['packages']:
cmd.append(package)
uninstalleroutput = []
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while (p.poll() == None):
msg = p.stdout.readline()
# save all uninstaller output in case there is
# an error so we can dump it to the log
uninstalleroutput.append(msg)
msg = msg.rstrip("\n")
if msg.startswith("STATUS: "):
status = msg[8:]
if status:
print status
sys.stdout.flush()
elif msg.startswith("INFO: "):
info = msg[6:]
if info:
print >>sys.stderr, info
elif msg.startswith("ERROR: "):
error = msg[7:]
if error:
print >>sys.stderr, error
while (p.poll() == None):
msg = p.stdout.readline()
# save all uninstaller output in case there is
# an error so we can dump it to the log
uninstalleroutput.append(msg)
msg = msg.rstrip("\n")
if msg.startswith("STATUS: "):
status = msg[8:]
if status:
print status
sys.stdout.flush()
elif msg.startswith("INFO: "):
info = msg[6:]
if info:
print >>sys.stderr, info
elif msg.startswith("ERROR: "):
error = msg[7:]
if error:
print >>sys.stderr, error
else:
print msg
sys.stdout.flush()
retcode = p.poll()
if retcode:
message = "Uninstall of %s failed." % name
print >>sys.stderr, message
log(message)
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
for line in uninstalleroutput:
print >>sys.stderr, " ", line.rstrip("\n")
log(line.rstrip("\n"))
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
else:
log("Uninstall of %s was successful." % name)
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:
munkistatus.message("Running uninstall script for %s..." % name)
# set indeterminate progress bar
munkistatus.percent(-1)
cmd = uninstallmethod
uninstalleroutput = []
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while (p.poll() == None):
msg = p.stdout.readline()
# save all uninstaller output in case there is
# an error so we can dump it to the log
uninstalleroutput.append(msg)
msg = msg.rstrip("\n")
if options.munkistatusoutput:
# do nothing with the output
pass
else:
print msg
retcode = p.poll()
if retcode:
message = "Uninstall of %s failed." % name
print >>sys.stderr, message
log(message)
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
for line in uninstalleroutput:
print >>sys.stderr, " ", line.rstrip("\n")
log(line.rstrip("\n"))
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
else:
print msg
sys.stdout.flush()
retcode = p.poll()
if retcode:
message = "Uninstall of %s failed." % name
print >>sys.stderr, message
log(message)
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
for line in uninstalleroutput:
print >>sys.stderr, " ", line.rstrip("\n")
log(line.rstrip("\n"))
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
log("Uninstall of %s was successful." % name)
if options.munkstatusoutput:
# clear indeterminate progress bar
munkistatus.percent(0)
else:
log("Uninstall of %s was successful." % name)
elif os.path.exists(uninstallmethod[0]) and os.access(uninstallmethod[0], os.X_OK):
# it's a script or program to uninstall
if options.ihookoutput:
print "%TITLE Running uninstall script for " + name + "..."
print "%BEGINPOLE"
sys.stdout.flush()
cmd = uninstallmethod
uninstalleroutput = []
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while (p.poll() == None):
msg = p.stdout.readline()
# save all uninstaller output in case there is
# an error so we can dump it to the log
uninstalleroutput.append(msg)
msg = msg.rstrip("\n")
if options.ihookoutput:
# redirect output to the iHook drawer
print >>sys.stderr, msg
else:
print msg
retcode = p.poll()
if retcode:
message = "Uninstall of %s failed." % name
print >>sys.stderr, message
log(message)
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
for line in uninstalleroutput:
print >>sys.stderr, " ", line.rstrip("\n")
log(line.rstrip("\n"))
message = "-------------------------------------------------"
print >>sys.stderr, message
log(message)
else:
log("Uninstall of %s was successful." % name)
else:
log("Uninstall of %s failed because there was no valid uninstall method." % name)
log("Uninstall of %s failed because there was no valid uninstall method." % name)
return restartFlag
@@ -377,30 +441,17 @@ def unmountdmg(mountpoint):
managedinstallbase = managedinstalls.managed_install_dir()
installdir = os.path.join(managedinstallbase , 'Cache')
logdir = os.path.join(managedinstallbase, 'Logs')
installablecount = 0
currentinstallable = 0
p = optparse.OptionParser()
p.add_option('--ihookoutput', '-i', action='store_true')
p.add_option('--munkistatusoutput', '-m', action='store_true')
options, arguments = p.parse_args()
def main():
global installdir
global installablecount
if options.ihookoutput:
print '%WINDOWSIZE 512 232'
print '%BACKGROUND /Users/Shared/Installer.png'
print '%BECOMEKEY'
print '%BEGINPOLE'
sys.stdout.flush()
needtorestart = False
#installablecount = countinstallcandidates(installdir)
#if installablecount:
installablecount = 5
log("### Beginning automated install session ###")
if os.path.exists(installdir):
installinfo = os.path.join(managedinstallbase, 'InstallInfo.plist')
@@ -410,30 +461,62 @@ def main():
except:
print >>sys.stderr, "Invalid %s" % installinfo
exit(-1)
# remove the install info file
# it's no longer valid once we start running
os.unlink(installinfo)
if "removals" in pl:
log("Processing removals")
needtorestart = processRemovals(pl['removals'])
removalcount = getRemovalCount(pl['removals'])
if removalcount:
if options.munkistatusoutput:
if removalcount == 1:
munkistatus.message("Removing 1 item...")
else:
munkistatus.message("Removing %i items..." % removalcount)
# set indeterminate progress bar
munkistatus.percent(-1)
log("Processing removals")
needtorestart = processRemovals(pl['removals'])
if "managed_installs" in pl:
log("Processing installs")
needtorestart = installWithInfo(installdir, pl['managed_installs'])
# remove the install info
os.unlink(installinfo)
if not stopRequested():
installcount = getInstallCount(pl['managed_installs'])
if installcount:
if options.munkistatusoutput:
if installcount == 1:
munkistatus.message("Installing 1 item...")
else:
munkistatus.message("Installing %i items..." % installcount)
# set indeterminate progress bar
munkistatus.percent(-1)
log("Processing installs")
needtorestart = installWithInfo(installdir, pl['managed_installs'])
else:
log("No %s found." % installinfo)
log("Installing everything in the cache.")
#log("Installing everything in the cache.")
# install all pkgs and mpkgs
needtorestart = installall(installdir)
#needtorestart = installall(installdir)
if needtorestart:
print "Software installed requires a restart."
log("Software installed requires a restart.")
sys.stdout.flush()
log("Software installed or removed requires a restart.")
if options.munkistatusoutput:
munkistatus.message("Software installed or removed requires a restart.")
munkistatus.detail("")
munkistatus.percent(-1)
else:
print "Software installed or removed requires a restart."
sys.stdout.flush()
log("### End automated install session ###")
if needtorestart:
time.sleep(5)
cleanup()
# uncomment this when testing is done so it will restart.
#retcode = subprocess.call(["/sbin/shutdown", "-r", "now"])
#retcode = subprocess.call(["/sbin/shutdown", "-r", "now"])
else:
cleanup()
if __name__ == '__main__':
+3 -2
View File
@@ -5,6 +5,7 @@ ManagedInstaller - meant to be run as part of a logout hook. Does the actual in
makecatalogitem.py: Helper tool to help create catalogitem plists for each installer item.
removepackages: used by ManagedInstaller to do package removals.
Supporting library:
managedinstalls.py
Supporting libraries:
managedinstalls.py - shared functions
munkistatus.py - functions to display status using MunkiStatus.app
+1 -2
View File
@@ -914,8 +914,7 @@ def main():
# now generate a list of items to be uninstalled
removallist = processCatalogForRemovals(maincatalogpath)
# need to write out install list so the autoinstaller
# can use it to install things in the right order
pldict = {}
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env python
# encoding: utf-8
#
# 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.
"""
munkistatus.py
Created by Greg Neagle on 2009-04-17.
Utility functions for using MunkiStatus.app.
Can be called as a standalone script with options,
or as a Python library
"""
import sys
import os
import optparse
import subprocess
def osascript(osastring):
cmd = ['osascript', '-e', osastring]
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:
print >>sys.stderr, "Error: ", err
if out:
return out.rstrip("\n")
def quit():
# see if MunkiStatus is running first
cmd = ['/usr/bin/killall', '-s', 'MunkiStatus']
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:
# it's running, send it a quit message
result = osascript('tell application "MunkiStatus" to quit')
def activate():
osascript('tell application "MunkiStatus" to activate')
def title(titleText):
result = osascript('tell application "MunkiStatus" to set title of window "mainWindow" to "%s"' % titleText)
def message(messageText):
result = osascript('tell application "MunkiStatus" to set contents of text field "mainTextFld" of window "mainWindow" to "%s"' % messageText)
def detail(detailsText):
result = osascript('tell application "MunkiStatus" to set contents of text field "minorTextFld" of window "mainWindow" to "%s"' % detailsText)
def percent(percentage):
# Note: this is a relatively slow operation.
# If you are calling this on every iteration of a loop,
# you may find it slows the loop down unacceptibly, as
# Your loop spends more time waiting for this operation
# than actually doing its work. You might
# instead try calling it once at the beginning of the loop,
# once every X iterations, then once at the end to speed things up.
# X might equal (number of iterations / 10 or even 20)
percent = int(float(percentage))
if percent > 100:
percent = 100
if percent < 0:
# set an indeterminate progress bar
result = osascript('tell application "MunkiStatus" to set indeterminate of progress indicator "progressBar" of window "mainWindow" to true')
result = osascript('tell application "MunkiStatus" to tell window "mainWindow" to tell progress indicator "progressBar" to start')
elif percent == 0:
# we only clear the indeterminate status when we set the percentage to 0;
# we tried always doing it, but it really slows down response times such
# that a script spends way more time updating MunkiStatus than it does
# actually performing its task
result = osascript('tell application "MunkiStatus" to set indeterminate of progress indicator "progressBar" of window "mainWindow" to false')
result = osascript('tell application "MunkiStatus" to set contents of progress indicator "progressBar" of window "mainWindow" to 0')
else:
percentStr = str(percent)
result = osascript('tell application "MunkiStatus" to set contents of progress indicator "progressBar" of window "mainWindow" to %s' % percentStr)
def getStopButtonState():
# see if MunkiStatus is running first
cmd = ['/usr/bin/killall', '-s', 'MunkiStatus']
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:
# it's running, ask it for the button state
result = osascript('tell application "MunkiStatus" to get the state of button "stopBtn" of window "mainWindow"')
return int(result)
else:
return 0
def hideStopButton():
result = osascript('tell application "MunkiStatus" to set visible of button "stopBtn" of window "mainWindow" to false')
def showStopButton():
result = osascript('tell application "MunkiStatus" to set visible of button "stopBtn" of window "mainWindow" to true')
def disableStopButton():
result = osascript('tell application "MunkiStatus" to set enabled of button "stopBtn" of window "mainWindow" to false')
def enableStopButton():
result = osascript('tell application "MunkiStatus" to set enabled of button "stopBtn" of window "mainWindow" to true')
def main():
p = optparse.OptionParser()
p.add_option('--activate', '-a', action='store_true',
help='Bring MunkiStatus to the front.')
p.add_option('--title', '-t', default='',
help='Window title.')
p.add_option('--message', '-m', default='',
help='Main message text.')
p.add_option('--detail', '-d',
help='Minor message text.')
p.add_option('--percent', '-p', default='',
help='Update progress bar to show percent done. Negative values show an indeterminate progress bar.')
p.add_option('--quit', '-q', action='store_true',
help='Tell MunkiStatus to quit.')
p.add_option('--getStopButtonState', '-g', action='store_true',
help='Returns 1 if stop button has been clicked.')
p.add_option('--hideStopButton', action='store_true',
help='Hide the stop button.')
p.add_option('--showStopButton', action='store_true',
help='Show the stop button.')
p.add_option('--disableStopButton', action='store_true',
help='Disable the stop button.')
p.add_option('--enableStopButton', action='store_true',
help='Enable the stop button.')
options, arguments = p.parse_args()
if options.quit:
quit()
exit()
if options.activate:
activate()
if options.title:
title(options.title)
if options.message:
message(options.message)
if options.detail:
detail(options.detail)
if options.percent:
percent(options.percent)
if options.getStopButtonState:
print getStopButtonState()
if options.hideStopButton:
hideStopButton()
if options.showStopButton:
showStopButton()
if options.disableStopButton:
disableStopButton()
if options.enableStopButton:
enableStopButton()
if __name__ == '__main__':
main()
+72 -22
View File
@@ -20,6 +20,7 @@ files unique to the packages given at the command line. No attempt
is made to revert to older versions of a file when uninstalling;
only file removals are done.
"""
#TO-DO: refactor this so it can be called as a library as well as a command-line script
import optparse
import os
@@ -28,6 +29,7 @@ import sys
import plistlib
import sqlite3
import time
import munkistatus
#import applereceiptutils
##################################################################
@@ -88,6 +90,14 @@ import time
# perms INTEGER )
#################################################################
def stopRequested():
if options.munkistatusoutput:
if munkistatus.getStopButtonState() == 1:
return True
return False
def getsteps(num_of_steps, limit):
"""
Helper function for display_percent_done
@@ -106,18 +116,17 @@ def getsteps(num_of_steps, limit):
def display_percent_done(current,maximum):
"""
Mimics the command-line progress meter seen in some
of Apple's tools (like softwareupdate), or prints
percent-done output in iHook directive format.
of Apple's tools (like softwareupdate), or tells
MunkiStatus to display percent done via progress bar.
"""
if options.ihookoutput:
if options.munkistatusoutput:
step = getsteps(101, maximum)
if current in step:
if current == maximum:
percentdone = 100
else:
percentdone = step.index(current)
print "%%%s %s of %s" % (str(percentdone), current, maximum)
sys.stdout.flush()
munkistatus.percent(str(percentdone))
elif not options.verbose:
step = getsteps(16, maximum)
output = ''
@@ -136,8 +145,8 @@ def display_status(msg):
Displays major status messages, formatting as needed
for verbose/non-verbose and iHook-style output.
"""
if options.ihookoutput:
print "STATUS: %s" % msg
if options.munkistatusoutput:
munkistatus.detail(msg)
elif options.verbose:
print "%s..." % msg
else:
@@ -150,9 +159,9 @@ def display_info(msg):
Displays minor info messages, formatting as needed
for verbose/non-verbose and iHook-style output.
"""
if options.ihookoutput:
#iHook puts stderr in the drawer
print >>sys.stderr, "INFO: %s" % msg
if options.munkistatusoutput:
#munkistatus.detail(msg)
pass
elif options.verbose:
print msg
@@ -162,8 +171,7 @@ def display_error(msg):
Prints msg to stderr and eventually to the log
"""
print >>sys.stderr, "ERROR: %s" % msg
if options.ihookoutput:
sys.stderr.flush()
def shouldRebuildDB(pkgdbpath):
@@ -443,6 +451,14 @@ def initDatabase(packagedb,forcerebuild=False):
if os.path.exists(receiptsdir):
receiptlist = os.listdir(receiptsdir)
for item in receiptlist:
if stopRequested():
c.close()
conn.close()
#our package db isn't valid, so we should delete it
os.remove(packagedb)
cleanup()
exit()
if item.endswith(".pkg"):
receiptpath = os.path.join(receiptsdir, item)
display_info("Importing %s..." % receiptpath)
@@ -453,6 +469,14 @@ def initDatabase(packagedb,forcerebuild=False):
if os.path.exists(bomsdir):
bomslist = os.listdir(bomsdir)
for item in bomslist:
if stopRequested():
c.close()
conn.close()
#our package db isn't valid, so we should delete it
os.remove(packagedb)
cleanup()
exit()
if item.endswith(".bom"):
bompath = os.path.join(bomsdir, item)
display_info("Importing %s..." % bompath)
@@ -528,9 +552,8 @@ def getpathstoremove(pkgkeylist):
combined_query = "select path from paths where (path_key in (%s) and path_key not in (%s))" % (in_selected_packages, not_in_other_packages)
display_status('Determining which filesystem items to remove')
if options.ihookoutput:
print '%BEGINPOLE'
sys.stdout.flush()
if options.munkistatusoutput:
munkistatus.percent(-1)
c.execute(combined_query)
results = c.fetchall()
@@ -710,6 +733,11 @@ def removeFilesystemItems(removalpaths):
display_error("ERROR: couldn't remove bundle %s" % pathtoremove)
else:
display_error("WARNING: Did not remove %s because it is not empty." % pathtoremove)
#we'll update the progress bar only on directories because we're too slow
#otherwise
display_percent_done(itemindex, itemcount)
else:
# not a directory, just unlink it
# we're using rm instead of Python because I don't trust
@@ -717,9 +745,14 @@ def removeFilesystemItems(removalpaths):
retcode = subprocess.call(['/bin/rm', pathtoremove])
if retcode:
display_error("ERROR: couldn't remove item %s" % pathtoremove)
display_percent_done(itemindex, itemcount)
# this should be 100%
display_percent_done(itemindex, itemcount)
def cleanup():
if options.munkistatusoutput:
if not options.dontquitmunkistatus:
munkistatus.quit()
######################################################
# Main
@@ -741,8 +774,10 @@ p.add_option('--noremovereceipts', action='store_true',
help='Do not remove receipts and boms from /Library/Receipts and update internal package database.')
p.add_option('--noupdateapplepkgdb', action='store_true',
help='Do not update Apple\'s package database. If --noremovereceipts is also given, this is implied')
p.add_option('--ihookoutput', '-i', action='store_true',
help='Output is formatted for use with iHook.')
p.add_option('--munkistatusoutput', '-m', action='store_true',
help='Output is formatted for use with MunkiStatus.')
p.add_option('--dontquitmunkistatus', '-d', action='store_true',
help="Don't quit MunkiStatus on exit. (For example, when called by ManagedInstaller.)")
p.add_option('--verbose', '-v', action='store_true',
help='More verbose output.')
# Get our options and our package names
@@ -751,21 +786,33 @@ options, pkgnames = p.parse_args()
# check to see if we're root
if os.geteuid() != 0:
display_error("You must run this as root!")
cleanup()
exit(1)
if pkgnames == []:
display_error("You must specify at least one package to remove!")
cleanup()
exit(1)
if not initDatabase(packagedb,options.rebuildpkgdb):
display_error("Could not initialize receipt database.")
cleanup()
exit(-1)
pkgkeyslist = getpkgkeys(pkgnames)
if len(pkgkeyslist) == 0:
cleanup()
exit(1)
if stopRequested():
cleanup()
exit()
removalpaths = getpathstoremove(pkgkeyslist)
if stopRequested():
cleanup()
exit()
if removalpaths:
if options.listfiles:
removalpaths.sort()
@@ -775,14 +822,17 @@ if removalpaths:
removeFilesystemItems(removalpaths)
if not options.noremovereceipts:
removeReceipts(pkgkeyslist)
if options.ihookoutput:
if options.munkistatusoutput:
display_status('Package removal complete.')
time.sleep(2)
else:
display_status('Nothing to remove.')
if options.ihookoutput:
if options.munkistatusoutput:
time.sleep(2)
#cleanup
cleanup()