mirror of
https://github.com/munki/munki.git
synced 2026-05-02 10:19:32 -05:00
Renamed managedinstalls.py to munkilib.py.
Removed cfconsoleuser.py. Its function was added to munkilib.py. Renamed ManagedInstaller to managedinstaller.py. Next commit it will be renamed to managedinstaller. Renamed removepackages to removepackages.py Refactored removepackages.py so it can be called as a Python module as well as a command-line tool. managedinstaller.py now calls removepackages.py as a module instead of as a subprocess. Cleaned up globals in installcheck, managedinstaller, and removepackages. Removed bad copyright info from makcatalogs. git-svn-id: http://munki.googlecode.com/svn/trunk@74 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
@@ -6,10 +6,10 @@ ManagedSoftwareUpdate.app - user notification tool.
|
||||
MunkiStatus - 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.
|
||||
removepackages: used by ManagedInstaller to do package removals.
|
||||
|
||||
|
||||
|
||||
Supporting libraries:
|
||||
managedinstalls.py - shared functions
|
||||
munkistatus.py - functions to display status using MunkiStatus.app
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
"""
|
||||
cfconsoleuser.py
|
||||
|
||||
Created by Greg Neagle on 2009-04-20.
|
||||
"""
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from SystemConfiguration import *
|
||||
|
||||
def getConsoleUser():
|
||||
cfuser = SCDynamicStoreCopyConsoleUser( None, None, None )
|
||||
return cfuser[0]
|
||||
|
||||
+20
-20
@@ -40,7 +40,7 @@ import time
|
||||
import socket
|
||||
|
||||
#our lib
|
||||
import managedinstalls
|
||||
import munkilib
|
||||
|
||||
|
||||
def log(message):
|
||||
@@ -73,7 +73,7 @@ def reporterrors():
|
||||
# this needs to be expanded to support error reporting
|
||||
# via email and HTTP CGI.
|
||||
global options
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
clientidentifier = managedinstallprefs.get('client_identifier','')
|
||||
alternate_id = options.id
|
||||
hostname = os.uname()[1]
|
||||
@@ -133,8 +133,8 @@ def compareVersions(thisvers, thatvers):
|
||||
Returns 1 if thisvers is the same as thatvers
|
||||
Returns 2 if thisvers is newer than thatvers
|
||||
"""
|
||||
thisvers = managedinstalls.padVersionString(thisvers,5)
|
||||
thatvers = managedinstalls.padVersionString(thatvers,5)
|
||||
thisvers = munkilib.padVersionString(thisvers,5)
|
||||
thatvers = munkilib.padVersionString(thatvers,5)
|
||||
if version.LooseVersion(thisvers) < version.LooseVersion(thatvers):
|
||||
return -1
|
||||
elif version.LooseVersion(thisvers) == version.LooseVersion(thatvers):
|
||||
@@ -352,7 +352,7 @@ def compareReceiptVersion(item):
|
||||
return -2
|
||||
|
||||
printandlog("Looking for package %s, version %s" % (pkgid, vers), 2)
|
||||
installedvers = managedinstalls.getInstalledPackageVersion(pkgid)
|
||||
installedvers = munkilib.getInstalledPackageVersion(pkgid)
|
||||
if installedvers:
|
||||
return compareVersions(installedvers, vers)
|
||||
else:
|
||||
@@ -368,7 +368,7 @@ def getInstalledVersion(pl):
|
||||
if 'receipts' in pl:
|
||||
if len(pl['receipts']) == 1:
|
||||
pkgid = pl['receipts'][0]['packageid']
|
||||
installedvers = managedinstalls.getInstalledPackageVersion(pkgid)
|
||||
installedvers = munkilib.getInstalledPackageVersion(pkgid)
|
||||
if installedvers:
|
||||
return installedvers
|
||||
|
||||
@@ -419,7 +419,7 @@ def download_installeritem(pkgurl):
|
||||
"""
|
||||
global mytmpdir
|
||||
|
||||
managed_install_dir = managedinstalls.managed_install_dir()
|
||||
managed_install_dir = munkilib.managed_install_dir()
|
||||
mycachedir = os.path.join(managed_install_dir, "Cache")
|
||||
pkgname = os.path.basename(urlparse.urlsplit(pkgurl)[2])
|
||||
destinationpath = os.path.join(mycachedir, pkgname)
|
||||
@@ -437,7 +437,7 @@ def download_installeritem(pkgurl):
|
||||
elif not options.verbose:
|
||||
dl_message = "Downloading %s" % pkgname
|
||||
|
||||
result = managedinstalls.getfilefromhttpurl(pkgurl, tempfilepath, showprogress=not(options.quiet), ifmodifiedsince=itemmodtime, message=dl_message)
|
||||
result = munkilib.getfilefromhttpurl(pkgurl, tempfilepath, showprogress=not(options.quiet), ifmodifiedsince=itemmodtime, message=dl_message)
|
||||
if result == 0:
|
||||
os.rename(tempfilepath, destinationpath)
|
||||
return True
|
||||
@@ -511,7 +511,7 @@ def getAllMatchingItems(name,cataloglist):
|
||||
itemlist = []
|
||||
# we'll throw away any included version info
|
||||
(name, includedversion) = nameAndVersion(name)
|
||||
managedinstalldir = managedinstalls.managed_install_dir()
|
||||
managedinstalldir = munkilib.managed_install_dir()
|
||||
catalogsdir = os.path.join(managedinstalldir, 'catalogs')
|
||||
printandlog("Looking for all items matching: %s..." % name, 1)
|
||||
for catalogname in cataloglist:
|
||||
@@ -545,7 +545,7 @@ def getManifestItemDetail(name, cataloglist, version=''):
|
||||
version = includedversion
|
||||
else:
|
||||
version = 'latest'
|
||||
managedinstalldir = managedinstalls.managed_install_dir()
|
||||
managedinstalldir = munkilib.managed_install_dir()
|
||||
catalogsdir = os.path.join(managedinstalldir, 'catalogs')
|
||||
printandlog("Looking for detail for: %s, version %s..." % (name, version), 1)
|
||||
for catalogname in cataloglist:
|
||||
@@ -592,7 +592,7 @@ def enoughDiskSpace(manifestitem_pl):
|
||||
if 'installed_size' in manifestitem_pl:
|
||||
installedsize = manifestitem_pl['installed_size']
|
||||
diskspaceneeded = (installeritemsize + installedsize + fudgefactor)/1024
|
||||
availablediskspace = managedinstalls.getAvailableDiskSpace()/1024
|
||||
availablediskspace = munkilib.getAvailableDiskSpace()/1024
|
||||
if availablediskspace > diskspaceneeded:
|
||||
return True
|
||||
else:
|
||||
@@ -700,7 +700,7 @@ def processInstalls(manifestitem, cataloglist, installinfo):
|
||||
will stop the installation of a dependent item
|
||||
"""
|
||||
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
sw_repo_baseurl = managedinstallprefs['sw_repo_url']
|
||||
managed_install_dir = managedinstallprefs['managed_install_dir']
|
||||
|
||||
@@ -952,7 +952,7 @@ def processRemovals(manifestitem, cataloglist, installinfo):
|
||||
# and we're supposed to remove SomePackage--1.0.1.0.0... what do we do?
|
||||
#
|
||||
dependentitemsremoved = True
|
||||
managed_install_dir = managedinstalls.managed_install_dir()
|
||||
managed_install_dir = munkilib.managed_install_dir()
|
||||
catalogsdir = os.path.join(managed_install_dir, 'catalogs')
|
||||
|
||||
processednamesandaliases = []
|
||||
@@ -1056,7 +1056,7 @@ def getCatalogs(cataloglist):
|
||||
"""
|
||||
Retreives the catalogs from the server
|
||||
"""
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
sw_repo_baseurl = managedinstallprefs['sw_repo_url']
|
||||
catalog_dir = os.path.join(managedinstallprefs['managed_install_dir'], "catalogs")
|
||||
|
||||
@@ -1069,7 +1069,7 @@ def getCatalogs(cataloglist):
|
||||
message = None
|
||||
elif not options.verbose:
|
||||
message = "Retreiving catalog '%s'..." % catalog
|
||||
newcatalog = managedinstalls.getHTTPfileIfNewerAtomically(catalogurl,catalogpath,showprogress=not(options.quiet), message=message)
|
||||
newcatalog = munkilib.getHTTPfileIfNewerAtomically(catalogurl,catalogpath,showprogress=not(options.quiet), message=message)
|
||||
if not newcatalog:
|
||||
logerror("Could not retreive catalog %s from server." % catalog)
|
||||
|
||||
@@ -1078,7 +1078,7 @@ def getmanifest(partialurl):
|
||||
"""
|
||||
Gets a manifest from the server
|
||||
"""
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
sw_repo_baseurl = managedinstallprefs['sw_repo_url']
|
||||
manifest_dir = os.path.join(managedinstallprefs['managed_install_dir'], "manifests")
|
||||
|
||||
@@ -1099,7 +1099,7 @@ def getmanifest(partialurl):
|
||||
elif not options.verbose:
|
||||
message = "Retreiving list of software for this machine..."
|
||||
|
||||
newmanifest = managedinstalls.getHTTPfileIfNewerAtomically(manifesturl,manifestpath,showprogress=not(options.quiet), message=message)
|
||||
newmanifest = munkilib.getHTTPfileIfNewerAtomically(manifesturl,manifestpath,showprogress=not(options.quiet), message=message)
|
||||
if not newmanifest:
|
||||
logerror("Could not retreive manifest %s from the server." % partialurl)
|
||||
|
||||
@@ -1109,7 +1109,7 @@ def getPrimaryManifest(alternate_id):
|
||||
"""
|
||||
Gets the client manifest from the server
|
||||
"""
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
manifesturl = managedinstallprefs['manifest_url']
|
||||
clientidentifier = managedinstallprefs.get('client_identifier','')
|
||||
|
||||
@@ -1147,7 +1147,7 @@ def getRemovalCount(installinfo):
|
||||
|
||||
|
||||
def checkServer():
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
manifesturl = managedinstallprefs['manifest_url']
|
||||
# deconstruct URL so we can check availability
|
||||
port = 80
|
||||
@@ -1194,7 +1194,7 @@ def main():
|
||||
|
||||
if not options.quiet: print "Managed Software Check\n"
|
||||
|
||||
managedinstallprefs = managedinstalls.prefs()
|
||||
managedinstallprefs = munkilib.prefs()
|
||||
managed_install_dir = managedinstallprefs['managed_install_dir']
|
||||
logginglevel = managedinstallprefs.get('logging_level', 1)
|
||||
manifestsdir = os.path.join(managed_install_dir, "manifests")
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
makecatalogs
|
||||
|
||||
Created by Greg Neagle on 2009-03-30.
|
||||
Copyright (c) 2009 Walt Disney Animation Studios. All rights reserved.
|
||||
|
||||
Recursively scans a directory, looking for installer item info files. Builds a repo catalog from these files.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import plistlib
|
||||
import subprocess
|
||||
import hashlib
|
||||
|
||||
import managedinstalls
|
||||
import munkilib
|
||||
|
||||
|
||||
def mountdmg(dmgpath):
|
||||
@@ -115,8 +115,8 @@ def getCatalogInfo(pkgitem):
|
||||
receipts: an array of packageids that may be installed (some may be optional)
|
||||
"""
|
||||
installedsize = 0
|
||||
installerinfo = managedinstalls.getInstallerPkgInfo(pkgitem)
|
||||
info = managedinstalls.getPkgInfo(pkgitem)
|
||||
installerinfo = munkilib.getInstallerPkgInfo(pkgitem)
|
||||
info = munkilib.getPkgInfo(pkgitem)
|
||||
|
||||
highestpkgversion = "0.0"
|
||||
for infoitem in info:
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
ManagedInstaller
|
||||
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
|
||||
(containing pkgs and mpkgs) from a defined folder.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -26,12 +25,9 @@ import sys
|
||||
import time
|
||||
import plistlib
|
||||
import optparse
|
||||
import managedinstalls
|
||||
import munkilib
|
||||
import munkistatus
|
||||
import cfconsoleuser
|
||||
|
||||
pathtoremovepackages = "/Users/Shared/munki/munki/code/client/removepackages"
|
||||
|
||||
from removepackages import removepackages
|
||||
|
||||
def stopRequested():
|
||||
if options.munkistatusoutput:
|
||||
@@ -59,7 +55,6 @@ def createDirsIfNeeded(dirlist):
|
||||
|
||||
|
||||
def log(message):
|
||||
global logdir
|
||||
logfile = os.path.join(logdir,'ManagedInstaller.log')
|
||||
try:
|
||||
f = open(logfile, mode='a', buffering=1)
|
||||
@@ -75,7 +70,6 @@ def install(pkgpath):
|
||||
at pkgpath. Prints status messages to STDOUT.
|
||||
Returns the installer return code and true if a restart is needed.
|
||||
"""
|
||||
global options
|
||||
|
||||
restartneeded = False
|
||||
installeroutput = []
|
||||
@@ -271,7 +265,6 @@ def getRemovalCount(removalList):
|
||||
|
||||
|
||||
def processRemovals(removalList):
|
||||
global logdir
|
||||
restartFlag = False
|
||||
for item in removalList:
|
||||
if stopRequested():
|
||||
@@ -294,51 +287,15 @@ def processRemovals(removalList):
|
||||
print "Removing %s..." % name
|
||||
|
||||
log("Removing %s..." % name)
|
||||
cmd = [pathtoremovepackages, '-f', '--logfile', os.path.join(logdir,'ManagedInstaller.log')]
|
||||
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
|
||||
else:
|
||||
print msg
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
retcode = removepackages(item['packages'],
|
||||
munkistatusoutput=options.munkistatusoutput,
|
||||
forcedeletebundles=True,
|
||||
logfile=os.path.join(logdir,'ManagedInstaller.log'))
|
||||
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 = "-------------------------------------------------"
|
||||
if retcode == -128:
|
||||
message = "Uninstall of %s was cancelled." % name
|
||||
else:
|
||||
message = "Uninstall of %s failed." % name
|
||||
print >>sys.stderr, message
|
||||
log(message)
|
||||
else:
|
||||
@@ -437,17 +394,19 @@ def unmountdmg(mountpoint):
|
||||
|
||||
|
||||
# module (global) variables
|
||||
managedinstallbase = managedinstalls.managed_install_dir()
|
||||
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()
|
||||
logdir = None
|
||||
options = None
|
||||
|
||||
|
||||
def main():
|
||||
global installdir
|
||||
global logdir, options
|
||||
managedinstallbase = munkilib.managed_install_dir()
|
||||
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 = False
|
||||
createDirsIfNeeded([logdir])
|
||||
@@ -507,20 +466,22 @@ def main():
|
||||
log("### End managed installer session ###")
|
||||
|
||||
if needtorestart:
|
||||
if cfconsoleuser.getConsoleUser() == None or not options.munkistatusoutput:
|
||||
if munkilib.getconsoleuser() == None:
|
||||
time.sleep(5)
|
||||
cleanup()
|
||||
retcode = subprocess.call(["/sbin/shutdown", "-r", "now"])
|
||||
else:
|
||||
# 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"')
|
||||
cleanup()
|
||||
munkistatus.osascript('tell application "System Events" to restart')
|
||||
if options.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"')
|
||||
cleanup()
|
||||
munkistatus.osascript('tell application "System Events" to restart')
|
||||
else:
|
||||
print "Please restart immediately."
|
||||
else:
|
||||
cleanup()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
managedinstalls
|
||||
munkilib
|
||||
|
||||
Created by Greg Neagle on 2008-11-18.
|
||||
|
||||
Common functions used by the managedinstalls tools.
|
||||
Common functions used by the munki tools.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -34,6 +34,13 @@ import tempfile
|
||||
import shutil
|
||||
from xml.dom import minidom
|
||||
|
||||
from SystemConfiguration import *
|
||||
|
||||
|
||||
def getconsoleuser():
|
||||
cfuser = SCDynamicStoreCopyConsoleUser( None, None, None )
|
||||
return cfuser[0]
|
||||
|
||||
|
||||
#####################################################
|
||||
# managed installs preferences/metadata
|
||||
@@ -44,8 +51,8 @@ def getManagedInstallsPrefs():
|
||||
# define default values
|
||||
prefs = {}
|
||||
prefs['managed_install_dir'] = "/Library/Managed Installs"
|
||||
prefs['manifest_url'] = "http:/managedinstalls/cgi-bin/getmanifest"
|
||||
prefs['sw_repo_url'] = "http://managedinstalls/swrepo"
|
||||
prefs['manifest_url'] = "http:/munki/repo/manifests/"
|
||||
prefs['sw_repo_url'] = "http:/munki/repo"
|
||||
prefs['client_identifier'] = ""
|
||||
prefs['logging_level'] = 1
|
||||
prefsfile = "/Library/Preferences/ManagedInstalls.plist"
|
||||
@@ -518,7 +525,7 @@ def getHTTPfileIfNewerAtomically(url,destinationpath,showprogress=False, message
|
||||
|
||||
debug = False
|
||||
def main():
|
||||
pass
|
||||
print "This is a library of support tools for the Munki Suite."
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -15,12 +15,16 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
removepackages - a tool to analyze installed packages and remove
|
||||
removepackages.py
|
||||
|
||||
a tool to analyze installed packages and remove
|
||||
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.
|
||||
|
||||
Callable directly from the command-line and as a python module.
|
||||
"""
|
||||
#TO-DO: refactor this so it can be called as a library as well as a command-line script
|
||||
|
||||
|
||||
import optparse
|
||||
import os
|
||||
@@ -30,6 +34,7 @@ import plistlib
|
||||
import sqlite3
|
||||
import time
|
||||
import munkistatus
|
||||
import munkilib
|
||||
|
||||
|
||||
##################################################################
|
||||
@@ -71,7 +76,7 @@ import munkistatus
|
||||
# taint VARCHAR NOT NULL);
|
||||
#################################################################
|
||||
#################################################################
|
||||
# our db schema
|
||||
# our package db schema -- a subset of Apple's
|
||||
#
|
||||
# CREATE TABLE paths (path_key INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
# path VARCHAR NOT NULL UNIQUE )
|
||||
@@ -91,7 +96,7 @@ import munkistatus
|
||||
#################################################################
|
||||
|
||||
def stopRequested():
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
if munkistatus.getStopButtonState() == 1:
|
||||
return True
|
||||
|
||||
@@ -119,15 +124,15 @@ def display_percent_done(current,maximum):
|
||||
of Apple's tools (like softwareupdate), or tells
|
||||
MunkiStatus to display percent done via progress bar.
|
||||
"""
|
||||
if options.munkistatusoutput:
|
||||
step = getsteps(101, maximum)
|
||||
if munkistatusoutput:
|
||||
step = getsteps(21, maximum)
|
||||
if current in step:
|
||||
if current == maximum:
|
||||
percentdone = 100
|
||||
else:
|
||||
percentdone = step.index(current)
|
||||
percentdone = int(float(current)/float(maximum)*100)
|
||||
munkistatus.percent(str(percentdone))
|
||||
elif not options.verbose:
|
||||
elif not verbose:
|
||||
step = getsteps(16, maximum)
|
||||
output = ''
|
||||
indicator = ['\t0','.','.','20','.','.','40','.','.',
|
||||
@@ -146,9 +151,9 @@ def display_status(msg):
|
||||
for verbose/non-verbose and munkistatus-style output.
|
||||
"""
|
||||
log(msg)
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(msg)
|
||||
elif options.verbose:
|
||||
elif verbose:
|
||||
print "%s..." % msg
|
||||
else:
|
||||
print "%s: " % msg
|
||||
@@ -160,10 +165,10 @@ def display_info(msg):
|
||||
Displays minor info messages, formatting as needed
|
||||
for verbose/non-verbose and munkistatus-style output.
|
||||
"""
|
||||
if options.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
#munkistatus.detail(msg)
|
||||
pass
|
||||
elif options.verbose:
|
||||
elif verbose:
|
||||
print msg
|
||||
|
||||
|
||||
@@ -178,7 +183,7 @@ def display_error(msg):
|
||||
|
||||
def log(msg):
|
||||
try:
|
||||
f = open(options.logfile, mode='a', buffering=1)
|
||||
f = open(logfile, mode='a', buffering=1)
|
||||
print >>f, time.ctime(), msg
|
||||
f.close()
|
||||
except:
|
||||
@@ -383,15 +388,6 @@ def ImportBom(bompath, c):
|
||||
c.execute('INSERT INTO pkgs (timestamp, owner, pkgid, vers, ppath, pkgname) values (?, ?, ?, ?, ?, ?)', t)
|
||||
pkgkey = c.lastrowid
|
||||
|
||||
#pkgdict = {}
|
||||
#pkgdict['timestamp'] = timestamp
|
||||
#pkgdict['owner'] = owner
|
||||
#pkgdict['pkgid'] = pkgid
|
||||
#pkgdict['vers'] = vers
|
||||
#pkgdict['ppath'] = ppath
|
||||
#pkgdict['pkgname'] = pkgname
|
||||
#pkgdbarray.append(pkgdict)
|
||||
|
||||
cmd = ["/usr/bin/lsbom", bompath]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
@@ -467,8 +463,8 @@ def initDatabase(packagedb,forcerebuild=False):
|
||||
conn.close()
|
||||
#our package db isn't valid, so we should delete it
|
||||
os.remove(packagedb)
|
||||
cleanup()
|
||||
exit()
|
||||
|
||||
return False
|
||||
|
||||
if item.endswith(".pkg"):
|
||||
receiptpath = os.path.join(receiptsdir, item)
|
||||
@@ -485,8 +481,8 @@ def initDatabase(packagedb,forcerebuild=False):
|
||||
conn.close()
|
||||
#our package db isn't valid, so we should delete it
|
||||
os.remove(packagedb)
|
||||
cleanup()
|
||||
exit()
|
||||
|
||||
return False
|
||||
|
||||
if item.endswith(".bom"):
|
||||
bompath = os.path.join(bomsdir, item)
|
||||
@@ -503,12 +499,6 @@ def initDatabase(packagedb,forcerebuild=False):
|
||||
conn.commit()
|
||||
c.close()
|
||||
conn.close()
|
||||
|
||||
# create a plist with the installed packages
|
||||
#pl = {}
|
||||
#pl['packages'] = pkgdbarray
|
||||
#plistlib.writePlist(pl,'/Users/Shared/InstalledPackages.plist')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -546,7 +536,7 @@ def getpathstoremove(pkgkeylist):
|
||||
"""
|
||||
Queries our database for paths to remove.
|
||||
"""
|
||||
pkgkeys = tuple(pkgkeyslist)
|
||||
pkgkeys = tuple(pkgkeylist)
|
||||
|
||||
# open connection and cursor to our database
|
||||
conn = sqlite3.connect(packagedb)
|
||||
@@ -563,7 +553,7 @@ 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.munkistatusoutput:
|
||||
if munkistatusoutput:
|
||||
munkistatus.percent(-1)
|
||||
|
||||
c.execute(combined_query)
|
||||
@@ -577,7 +567,7 @@ def getpathstoremove(pkgkeylist):
|
||||
return removalpaths
|
||||
|
||||
|
||||
def removeReceipts(pkgkeylist):
|
||||
def removeReceipts(pkgkeylist, noupdateapplepkgdb):
|
||||
"""
|
||||
Removes receipt data from /Library/Receipts,
|
||||
/Library/Receipts/boms, our internal package database,
|
||||
@@ -590,11 +580,11 @@ def removeReceipts(pkgkeylist):
|
||||
c = conn.cursor()
|
||||
|
||||
applepkgdb = '/Library/Receipts/db/a.receiptdb'
|
||||
if not options.noupdateapplepkgdb:
|
||||
if not noupdateapplepkgdb:
|
||||
aconn = sqlite3.connect(applepkgdb)
|
||||
ac = aconn.cursor()
|
||||
|
||||
if not options.verbose:
|
||||
if not verbose:
|
||||
display_percent_done(1,4)
|
||||
|
||||
for pkgkey in pkgkeylist:
|
||||
@@ -614,18 +604,18 @@ def removeReceipts(pkgkeylist):
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", receiptpath])
|
||||
|
||||
# remove pkg info from our database
|
||||
if options.verbose:
|
||||
if verbose:
|
||||
print "Removing package data from internal database..."
|
||||
c.execute('DELETE FROM pkgs_paths where pkg_key = ?', t)
|
||||
c.execute('DELETE FROM pkgs where pkg_key = ?', t)
|
||||
|
||||
# then remove pkg info from Apple's database unless option is passed
|
||||
if not options.noupdateapplepkgdb:
|
||||
if not noupdateapplepkgdb:
|
||||
if pkgid:
|
||||
t = (pkgid, )
|
||||
row = ac.execute('SELECT pkg_key FROM pkgs where pkgid = ?', t).fetchone()
|
||||
if row:
|
||||
if options.verbose:
|
||||
if verbose:
|
||||
print "Removing package data from Apple package database..."
|
||||
apple_pkg_key = row[0]
|
||||
t = (apple_pkg_key, )
|
||||
@@ -641,7 +631,7 @@ def removeReceipts(pkgkeylist):
|
||||
|
||||
# now remove orphaned paths from paths table
|
||||
# first, Apple's database if option is passed
|
||||
if not options.noupdateapplepkgdb:
|
||||
if not noupdateapplepkgdb:
|
||||
display_info("Removing unused paths from Apple package database...")
|
||||
ac.execute('DELETE FROM paths where path_key not in (select distinct path_key from pkgs_paths)')
|
||||
aconn.commit()
|
||||
@@ -701,7 +691,7 @@ def isBundle(pathname):
|
||||
else:
|
||||
return False
|
||||
|
||||
def removeFilesystemItems(removalpaths):
|
||||
def removeFilesystemItems(removalpaths, forcedeletebundles):
|
||||
"""
|
||||
Attempts to remove all the paths in the array removalpaths
|
||||
"""
|
||||
@@ -740,17 +730,13 @@ def removeFilesystemItems(removalpaths):
|
||||
# if so directed, if it's a bundle (like .app), we should
|
||||
# remove it anyway - no use having a broken bundle hanging
|
||||
# around
|
||||
if (options.forcedeletebundles and isBundle(pathtoremove)):
|
||||
if (forcedeletebundles and isBundle(pathtoremove)):
|
||||
retcode = subprocess.call(['/bin/rm', '-rf', pathtoremove])
|
||||
if retcode:
|
||||
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
|
||||
@@ -758,95 +744,103 @@ def removeFilesystemItems(removalpaths):
|
||||
retcode = subprocess.call(['/bin/rm', pathtoremove])
|
||||
if retcode:
|
||||
display_error("ERROR: couldn't remove item %s" % pathtoremove)
|
||||
|
||||
# this should be 100%
|
||||
display_percent_done(itemindex, itemcount)
|
||||
|
||||
display_percent_done(itemindex, itemcount)
|
||||
|
||||
def cleanup():
|
||||
if options.munkistatusoutput:
|
||||
if not options.dontquitmunkistatus:
|
||||
munkistatus.quit()
|
||||
|
||||
######################################################
|
||||
# Main
|
||||
######################################################
|
||||
|
||||
# location of our internal pkg db
|
||||
packagedb = "/Users/Shared/b.receiptdb"
|
||||
pkgdbarray = []
|
||||
|
||||
# command-line options
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--forcedeletebundles', '-f', action='store_true',
|
||||
help='Delete bundles even if they aren\'t empty.')
|
||||
p.add_option('--listfiles', '-l', action='store_true',
|
||||
help='List the filesystem objects to be removed, but do not actually remove them.')
|
||||
p.add_option('--rebuildpkgdb', action='store_true',
|
||||
help='Force a rebuild of the internal package database.')
|
||||
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('--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.')
|
||||
p.add_option('--logfile', default='',
|
||||
help="Path to a log file.")
|
||||
# Get our options and our package names
|
||||
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)
|
||||
def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
|
||||
rebuildpkgdb=False, noremovereceipts=False,
|
||||
noupdateapplepkgdb=False, **kwargs):
|
||||
|
||||
if stopRequested():
|
||||
cleanup()
|
||||
exit()
|
||||
removalpaths = getpathstoremove(pkgkeyslist)
|
||||
if stopRequested():
|
||||
cleanup()
|
||||
exit()
|
||||
# we get the following arguments from the kwargs dictionary
|
||||
# so we can assign them to the globals without a name conflict
|
||||
global munkistatusoutput, verbose, verbose
|
||||
munkistatusoutput = kwargs.get('munkistatusoutput', False)
|
||||
verbose = kwargs.get('verbose', False)
|
||||
verbose = kwargs.get('verbose', '')
|
||||
|
||||
# check to see if we're root
|
||||
if os.geteuid() != 0:
|
||||
display_error("You must run this as root!")
|
||||
return -1
|
||||
|
||||
if pkgnames == []:
|
||||
display_error("You must specify at least one package to remove!")
|
||||
return -2
|
||||
|
||||
if not initDatabase(packagedb,rebuildpkgdb):
|
||||
display_error("Could not initialize receipt database.")
|
||||
return -3
|
||||
|
||||
pkgkeyslist = getpkgkeys(pkgnames)
|
||||
if len(pkgkeyslist) == 0:
|
||||
return -4
|
||||
|
||||
if stopRequested():
|
||||
return -128
|
||||
removalpaths = getpathstoremove(pkgkeyslist)
|
||||
if stopRequested():
|
||||
return -128
|
||||
|
||||
if removalpaths:
|
||||
if listfiles:
|
||||
removalpaths.sort()
|
||||
for item in removalpaths:
|
||||
print "/" + item.encode("UTF-8")
|
||||
else:
|
||||
removeFilesystemItems(removalpaths, forcedeletebundles)
|
||||
if not noremovereceipts:
|
||||
removeReceipts(pkgkeyslist, noupdateapplepkgdb)
|
||||
if munkistatusoutput:
|
||||
display_status('Package removal complete.')
|
||||
time.sleep(2)
|
||||
|
||||
if removalpaths:
|
||||
if options.listfiles:
|
||||
removalpaths.sort()
|
||||
for item in removalpaths:
|
||||
print "/" + item.encode("UTF-8")
|
||||
else:
|
||||
removeFilesystemItems(removalpaths)
|
||||
if not options.noremovereceipts:
|
||||
removeReceipts(pkgkeyslist)
|
||||
if options.munkistatusoutput:
|
||||
display_status('Package removal complete.')
|
||||
time.sleep(2)
|
||||
display_status('Nothing to remove.')
|
||||
if munkistatusoutput:
|
||||
time.sleep(2)
|
||||
|
||||
return 0
|
||||
|
||||
else:
|
||||
display_status('Nothing to remove.')
|
||||
if options.munkistatusoutput:
|
||||
time.sleep(2)
|
||||
|
||||
#cleanup
|
||||
cleanup()
|
||||
|
||||
# some globals
|
||||
packagedb = os.path.join(munkilib.managed_install_dir(), "b.receiptdb")
|
||||
munkistatusoutput = False
|
||||
verbose = False
|
||||
logfile = ''
|
||||
|
||||
|
||||
def main():
|
||||
# command-line options
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--forcedeletebundles', '-f', action='store_true',
|
||||
help='Delete bundles even if they aren\'t empty.')
|
||||
p.add_option('--listfiles', '-l', action='store_true',
|
||||
help='List the filesystem objects to be removed, but do not actually remove them.')
|
||||
p.add_option('--rebuildpkgdb', action='store_true',
|
||||
help='Force a rebuild of the internal package database.')
|
||||
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('--munkistatusoutput', '-m', action='store_true',
|
||||
help='Output is formatted for use with MunkiStatus.')
|
||||
p.add_option('--verbose', '-v', action='store_true',
|
||||
help='More verbose output.')
|
||||
p.add_option('--logfile', default='',
|
||||
help="Path to a log file.")
|
||||
# Get our options and our package names
|
||||
options, pkgnames = p.parse_args()
|
||||
retcode = removePackages(pkgnames, forcedeletebundles=options.forcedeletebundles, listfiles=options.listfiles,
|
||||
rebuildpkgdb=options.rebuildpkgdb, noremovereceipts=options.noremovereceipts,
|
||||
noupdateapplepkgdb=options.noupdateapplepkgdb, munkistatusoutput=options.munkistatusoutput,
|
||||
verbose=options.verbose, logfile=options.logfile)
|
||||
if munkistatusoutput:
|
||||
munkistatus.quit()
|
||||
exit(retcode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user