change this module to use munkicommon.getOsVersion() instead of os.uname()

splitting.

(also, our whitespace filter whacked many other lines)



git-svn-id: http://munki.googlecode.com/svn/trunk@1290 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
John Randolph
2011-08-11 21:29:21 +00:00
parent a9ef0a7034
commit 0dfd915f55
+136 -138
View File
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,7 +16,7 @@
# limitations under the License. # limitations under the License.
""" """
removepackages.py removepackages.py
a tool to analyze installed packages and remove a tool to analyze installed packages and remove
files unique to the packages given at the command line. No attempt files unique to the packages given at the command line. No attempt
@@ -99,9 +99,9 @@ def local_display_percent_done(current, maximum):
'''Bump up verboseness so we get download percentage done feedback.''' '''Bump up verboseness so we get download percentage done feedback.'''
oldverbose = munkicommon.verbose oldverbose = munkicommon.verbose
munkicommon.verbose = oldverbose + 1 munkicommon.verbose = oldverbose + 1
munkicommon.display_percent_done(current, maximum) munkicommon.display_percent_done(current, maximum)
# set verboseness back. # set verboseness back.
munkicommon.verbose = oldverbose munkicommon.verbose = oldverbose
@@ -158,8 +158,8 @@ def shouldRebuildDB(pkgdbpath):
pkgpath = os.path.join(sl_receiptsdir, item) pkgpath = os.path.join(sl_receiptsdir, item)
pkg_modtime = os.stat(pkgpath).st_mtime pkg_modtime = os.stat(pkgpath).st_mtime
if (packagedb_modtime < pkg_modtime): if (packagedb_modtime < pkg_modtime):
return True return True
if os.path.exists(installhistory): if os.path.exists(installhistory):
installhistory_modtime = os.stat(installhistory).st_mtime installhistory_modtime = os.stat(installhistory).st_mtime
if packagedb_modtime < installhistory_modtime: if packagedb_modtime < installhistory_modtime:
@@ -169,7 +169,7 @@ def shouldRebuildDB(pkgdbpath):
applepkgdb_modtime = os.stat(applepkgdb).st_mtime applepkgdb_modtime = os.stat(applepkgdb).st_mtime
if packagedb_modtime < applepkgdb_modtime: if packagedb_modtime < applepkgdb_modtime:
return True return True
# if we got this far, we don't need to update the db # if we got this far, we don't need to update the db
return False return False
@@ -178,10 +178,10 @@ def CreateTables(curs):
""" """
Creates the tables needed for our internal package database. Creates the tables needed for our internal package database.
""" """
curs.execute('''CREATE TABLE paths curs.execute('''CREATE TABLE paths
(path_key INTEGER PRIMARY KEY AUTOINCREMENT, (path_key INTEGER PRIMARY KEY AUTOINCREMENT,
path VARCHAR NOT NULL UNIQUE )''') path VARCHAR NOT NULL UNIQUE )''')
curs.execute('''CREATE TABLE pkgs curs.execute('''CREATE TABLE pkgs
(pkg_key INTEGER PRIMARY KEY AUTOINCREMENT, (pkg_key INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp INTEGER NOT NULL, timestamp INTEGER NOT NULL,
owner INTEGER NOT NULL, owner INTEGER NOT NULL,
@@ -190,14 +190,14 @@ def CreateTables(curs):
ppath VARCHAR NOT NULL, ppath VARCHAR NOT NULL,
pkgname VARCHAR NOT NULL, pkgname VARCHAR NOT NULL,
replaces INTEGER )''') replaces INTEGER )''')
curs.execute('''CREATE TABLE pkgs_paths curs.execute('''CREATE TABLE pkgs_paths
(pkg_key INTEGER NOT NULL, (pkg_key INTEGER NOT NULL,
path_key INTEGER NOT NULL, path_key INTEGER NOT NULL,
uid INTEGER, uid INTEGER,
gid INTEGER, gid INTEGER,
perms INTEGER )''') perms INTEGER )''')
def findBundleReceiptFromID(pkgid): def findBundleReceiptFromID(pkgid):
'''Finds a bundle receipt in /Library/Receipts based on packageid. '''Finds a bundle receipt in /Library/Receipts based on packageid.
Some packages write bundle receipts under /Library/Receipts even on Some packages write bundle receipts under /Library/Receipts even on
@@ -212,7 +212,7 @@ def findBundleReceiptFromID(pkgid):
info = munkicommon.getOnePackageInfo(itempath) info = munkicommon.getOnePackageInfo(itempath)
if info.get('packageid') == pkgid: if info.get('packageid') == pkgid:
return itempath return itempath
#if we get here, not found #if we get here, not found
return '' return ''
@@ -232,8 +232,8 @@ def ImportPackage(packagepath, curs):
return return
if not os.path.isdir(packagepath): if not os.path.isdir(packagepath):
# Every machine I've seen has a bogus BSD.pkg, # Every machine I've seen has a bogus BSD.pkg,
# so we won't print a warning for that specific one. # so we won't print a warning for that specific one.
if pkgname != "BSD.pkg": if pkgname != "BSD.pkg":
munkicommon.display_warning( munkicommon.display_warning(
"%s is not a valid receipt. Skipping." % packagepath) "%s is not a valid receipt. Skipping." % packagepath)
@@ -242,10 +242,10 @@ def ImportPackage(packagepath, curs):
if not os.path.exists(bompath): if not os.path.exists(bompath):
# look in receipt's Resources directory # look in receipt's Resources directory
bomname = os.path.splitext(pkgname)[0] + '.bom' bomname = os.path.splitext(pkgname)[0] + '.bom'
bompath = os.path.join(packagepath, "Contents/Resources", bompath = os.path.join(packagepath, "Contents/Resources",
bomname) bomname)
if not os.path.exists(bompath): if not os.path.exists(bompath):
munkicommon.display_warning("%s has no BOM file. Skipping." % munkicommon.display_warning("%s has no BOM file. Skipping." %
packagepath) packagepath)
return return
@@ -276,23 +276,23 @@ def ImportPackage(packagepath, curs):
ppath = ppath.lstrip('./').rstrip('/') ppath = ppath.lstrip('./').rstrip('/')
else: else:
ppath = "" ppath = ""
values_t = (timestamp, owner, pkgid, vers, ppath, pkgname) values_t = (timestamp, owner, pkgid, vers, ppath, pkgname)
curs.execute( curs.execute(
'''INSERT INTO pkgs (timestamp, owner, pkgid, vers, ppath, pkgname) '''INSERT INTO pkgs (timestamp, owner, pkgid, vers, ppath, pkgname)
values (?, ?, ?, ?, ?, ?)''', values_t) values (?, ?, ?, ?, ?, ?)''', values_t)
pkgkey = curs.lastrowid pkgkey = curs.lastrowid
cmd = ["/usr/bin/lsbom", bompath] cmd = ["/usr/bin/lsbom", bompath]
proc = subprocess.Popen(cmd, shell=False, bufsize=1, proc = subprocess.Popen(cmd, shell=False, bufsize=1,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True: while True:
line = proc.stdout.readline().decode('UTF-8') line = proc.stdout.readline().decode('UTF-8')
if not line and (proc.poll() != None): if not line and (proc.poll() != None):
break break
try: try:
item = line.rstrip("\n").split("\t") item = line.rstrip("\n").split("\t")
path = item[0] path = item[0]
@@ -311,16 +311,16 @@ def ImportPackage(packagepath, curs):
# special case for MS Office 2008 installers # special case for MS Office 2008 installers
if ppath == "tmp/com.microsoft.updater/office_location": if ppath == "tmp/com.microsoft.updater/office_location":
ppath = "Applications" ppath = "Applications"
# prepend the ppath so the paths match the actual install # prepend the ppath so the paths match the actual install
# locations # locations
path = path.lstrip("./") path = path.lstrip("./")
if ppath: if ppath:
path = ppath + "/" + path path = ppath + "/" + path
values_t = (path, ) values_t = (path, )
row = curs.execute( row = curs.execute(
'SELECT path_key from paths where path = ?', 'SELECT path_key from paths where path = ?',
values_t).fetchone() values_t).fetchone()
if not row: if not row:
curs.execute( curs.execute(
@@ -328,10 +328,10 @@ def ImportPackage(packagepath, curs):
pathkey = curs.lastrowid pathkey = curs.lastrowid
else: else:
pathkey = row[0] pathkey = row[0]
values_t = (pkgkey, pathkey, uid, gid, perms) values_t = (pkgkey, pathkey, uid, gid, perms)
curs.execute( curs.execute(
'''INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms) '''INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms)
values (?, ?, ?, ?, ?)''', values_t) values (?, ?, ?, ?, ?)''', values_t)
except sqlite3.DatabaseError: except sqlite3.DatabaseError:
pass pass
@@ -346,10 +346,10 @@ def ImportBom(bompath, curs):
# If we completely trusted the accuracy of Apple's database, we wouldn't # If we completely trusted the accuracy of Apple's database, we wouldn't
# need the bom files, but in my enviroment at least, the bom files are # need the bom files, but in my enviroment at least, the bom files are
# a better indicator of what flat packages have actually been installed # a better indicator of what flat packages have actually been installed
# on the current machine. # on the current machine.
# We still need to consult Apple's package database # We still need to consult Apple's package database
# because the bom files are missing metadata about the package. # because the bom files are missing metadata about the package.
#applepkgdb = "/Library/Receipts/db/a.receiptdb" #applepkgdb = "/Library/Receipts/db/a.receiptdb"
pkgname = os.path.basename(bompath) pkgname = os.path.basename(bompath)
@@ -381,11 +381,11 @@ def ImportBom(bompath, curs):
pkgkey = curs.lastrowid pkgkey = curs.lastrowid
cmd = ["/usr/bin/lsbom", bompath] cmd = ["/usr/bin/lsbom", bompath]
proc = subprocess.Popen(cmd, shell=False, bufsize=1, proc = subprocess.Popen(cmd, shell=False, bufsize=1,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True: while True:
line = proc.stdout.readline().decode('UTF-8') line = proc.stdout.readline().decode('UTF-8')
if not line and (proc.poll() != None): if not line and (proc.poll() != None):
break break
@@ -401,7 +401,7 @@ def ImportBom(bompath, curs):
perms = "0000" perms = "0000"
uid = "0" uid = "0"
gid = "0" gid = "0"
if path != ".": if path != ".":
# special case for MS Office 2008 installers # special case for MS Office 2008 installers
if ppath == "tmp/com.microsoft.updater/office_location": if ppath == "tmp/com.microsoft.updater/office_location":
@@ -414,7 +414,7 @@ def ImportBom(bompath, curs):
values_t = (path, ) values_t = (path, )
row = curs.execute( row = curs.execute(
'SELECT path_key from paths where path = ?', 'SELECT path_key from paths where path = ?',
values_t).fetchone() values_t).fetchone()
if not row: if not row:
curs.execute( curs.execute(
@@ -433,7 +433,7 @@ def ImportFromPkgutil(pkgname, curs):
""" """
Imports package data from pkgutil into our internal package database. Imports package data from pkgutil into our internal package database.
""" """
timestamp = 0 timestamp = 0
owner = 0 owner = 0
pkgid = pkgname pkgid = pkgname
@@ -456,7 +456,7 @@ def ImportFromPkgutil(pkgname, curs):
ppath = ppath.lstrip('./').rstrip('/') ppath = ppath.lstrip('./').rstrip('/')
else: else:
# there _should_ be an install-location. If there's not, let's # there _should_ be an install-location. If there's not, let's
# check the old /Library/Receipts. # check the old /Library/Receipts.
# (Workaround for QuarkXPress 8.1 packages) # (Workaround for QuarkXPress 8.1 packages)
receiptpath = findBundleReceiptFromID(pkgid) receiptpath = findBundleReceiptFromID(pkgid)
if receiptpath: if receiptpath:
@@ -469,24 +469,24 @@ def ImportFromPkgutil(pkgname, curs):
values_t = (timestamp, owner, pkgid, vers, ppath, pkgname) values_t = (timestamp, owner, pkgid, vers, ppath, pkgname)
curs.execute( curs.execute(
'''INSERT INTO pkgs (timestamp, owner, pkgid, vers, ppath, pkgname) '''INSERT INTO pkgs (timestamp, owner, pkgid, vers, ppath, pkgname)
values (?, ?, ?, ?, ?, ?)''', values_t) values (?, ?, ?, ?, ?, ?)''', values_t)
pkgkey = curs.lastrowid pkgkey = curs.lastrowid
cmd = ["/usr/sbin/pkgutil", "--files", pkgid] cmd = ["/usr/sbin/pkgutil", "--files", pkgid]
proc = subprocess.Popen(cmd, shell=False, bufsize=1, proc = subprocess.Popen(cmd, shell=False, bufsize=1,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True: while True:
line = proc.stdout.readline().decode('UTF-8') line = proc.stdout.readline().decode('UTF-8')
if not line and (proc.poll() != None): if not line and (proc.poll() != None):
break break
path = line.rstrip("\n") path = line.rstrip("\n")
# pkgutil --files pkgid only gives us path info. We don't # pkgutil --files pkgid only gives us path info. We don't
# really need perms, uid and gid, so we'll just fake them. # really need perms, uid and gid, so we'll just fake them.
# if we needed them, we'd have to call # if we needed them, we'd have to call
# pkgutil --export-plist pkgid and iterate through the # pkgutil --export-plist pkgid and iterate through the
# plist. That would be slower, so we'll do things this way... # plist. That would be slower, so we'll do things this way...
perms = "0000" perms = "0000"
@@ -516,7 +516,7 @@ def ImportFromPkgutil(pkgname, curs):
values_t = (pkgkey, pathkey, uid, gid, perms) values_t = (pkgkey, pathkey, uid, gid, perms)
curs.execute( curs.execute(
'''INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms) '''INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms)
values (?, ?, ?, ?, ?)''', values_t) values (?, ?, ?, ?, ?)''', values_t)
@@ -526,9 +526,9 @@ def initDatabase(forcerebuild=False):
""" """
if not shouldRebuildDB(packagedb) and not forcerebuild: if not shouldRebuildDB(packagedb) and not forcerebuild:
return True return True
munkicommon.display_status('Gathering information on installed packages') munkicommon.display_status('Gathering information on installed packages')
if os.path.exists(packagedb): if os.path.exists(packagedb):
try: try:
os.remove(packagedb) os.remove(packagedb)
@@ -536,8 +536,8 @@ def initDatabase(forcerebuild=False):
munkicommon.display_error( munkicommon.display_error(
"Could not remove out-of-date receipt database.") "Could not remove out-of-date receipt database.")
return False return False
osvers = int(os.uname()[2].split('.')[0]) os_version = munkicommon.getOsVersion(as_tuple=True)
pkgcount = 0 pkgcount = 0
receiptsdir = "/Library/Receipts" receiptsdir = "/Library/Receipts"
bomsdir = "/Library/Receipts/boms" bomsdir = "/Library/Receipts/boms"
@@ -551,30 +551,30 @@ def initDatabase(forcerebuild=False):
for item in bomslist: for item in bomslist:
if item.endswith(".bom"): if item.endswith(".bom"):
pkgcount += 1 pkgcount += 1
if osvers > 9:
# Snow Leopard or later if os_version >= (10, 6): # Snow Leopard or later
pkglist = [] pkglist = []
cmd = ['/usr/sbin/pkgutil', '--pkgs'] cmd = ['/usr/sbin/pkgutil', '--pkgs']
proc = subprocess.Popen(cmd, shell=False, bufsize=1, proc = subprocess.Popen(cmd, shell=False, bufsize=1,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
while True: while True:
line = proc.stdout.readline() line = proc.stdout.readline()
if not line and (proc.poll() != None): if not line and (proc.poll() != None):
break break
pkglist.append(line.rstrip('\n')) pkglist.append(line.rstrip('\n'))
pkgcount += 1 pkgcount += 1
conn = sqlite3.connect(packagedb) conn = sqlite3.connect(packagedb)
conn.text_factory = str conn.text_factory = str
curs = conn.cursor() curs = conn.cursor()
CreateTables(curs) CreateTables(curs)
currentpkgindex = 0 currentpkgindex = 0
local_display_percent_done(0, pkgcount) local_display_percent_done(0, pkgcount)
if os.path.exists(receiptsdir): if os.path.exists(receiptsdir):
receiptlist = munkicommon.listdir(receiptsdir) receiptlist = munkicommon.listdir(receiptsdir)
for item in receiptlist: for item in receiptlist:
@@ -584,14 +584,14 @@ def initDatabase(forcerebuild=False):
#our package db isn't valid, so we should delete it #our package db isn't valid, so we should delete it
os.remove(packagedb) os.remove(packagedb)
return False return False
if item.endswith(".pkg"): if item.endswith(".pkg"):
receiptpath = os.path.join(receiptsdir, item) receiptpath = os.path.join(receiptsdir, item)
munkicommon.display_detail("Importing %s..." % receiptpath) munkicommon.display_detail("Importing %s..." % receiptpath)
ImportPackage(receiptpath, curs) ImportPackage(receiptpath, curs)
currentpkgindex += 1 currentpkgindex += 1
local_display_percent_done(currentpkgindex, pkgcount) local_display_percent_done(currentpkgindex, pkgcount)
if os.path.exists(bomsdir): if os.path.exists(bomsdir):
bomslist = munkicommon.listdir(bomsdir) bomslist = munkicommon.listdir(bomsdir)
for item in bomslist: for item in bomslist:
@@ -601,15 +601,14 @@ def initDatabase(forcerebuild=False):
#our package db isn't valid, so we should delete it #our package db isn't valid, so we should delete it
os.remove(packagedb) os.remove(packagedb)
return False return False
if item.endswith(".bom"): if item.endswith(".bom"):
bompath = os.path.join(bomsdir, item) bompath = os.path.join(bomsdir, item)
munkicommon.display_detail("Importing %s..." % bompath) munkicommon.display_detail("Importing %s..." % bompath)
ImportBom(bompath, curs) ImportBom(bompath, curs)
currentpkgindex += 1 currentpkgindex += 1
local_display_percent_done(currentpkgindex, pkgcount) local_display_percent_done(currentpkgindex, pkgcount)
if osvers > 9: if os_version >= (10, 6): # Snow Leopard or later
# Snow Leopard or later
for pkg in pkglist: for pkg in pkglist:
if munkicommon.stopRequested(): if munkicommon.stopRequested():
curs.close() curs.close()
@@ -617,16 +616,16 @@ def initDatabase(forcerebuild=False):
#our package db isn't valid, so we should delete it #our package db isn't valid, so we should delete it
os.remove(packagedb) os.remove(packagedb)
return False return False
munkicommon.display_detail("Importing %s..." % pkg) munkicommon.display_detail("Importing %s..." % pkg)
ImportFromPkgutil(pkg, curs) ImportFromPkgutil(pkg, curs)
currentpkgindex += 1 currentpkgindex += 1
local_display_percent_done(currentpkgindex, pkgcount) local_display_percent_done(currentpkgindex, pkgcount)
# in case we didn't quite get to 100% for some reason # in case we didn't quite get to 100% for some reason
if currentpkgindex < pkgcount: if currentpkgindex < pkgcount:
local_display_percent_done(pkgcount, pkgcount) local_display_percent_done(pkgcount, pkgcount)
# commit and close the db when we're done. # commit and close the db when we're done.
conn.commit() conn.commit()
curs.close() curs.close()
@@ -642,8 +641,8 @@ def getpkgkeys(pkgnames):
# open connection and cursor to our database # open connection and cursor to our database
conn = sqlite3.connect(packagedb) conn = sqlite3.connect(packagedb)
curs = conn.cursor() curs = conn.cursor()
# check package names to make sure they're all in the database, # check package names to make sure they're all in the database,
# build our list of pkg_keys # build our list of pkg_keys
pkgerror = False pkgerror = False
pkgkeyslist = [] pkgkeyslist = []
@@ -658,7 +657,7 @@ def getpkgkeys(pkgnames):
munkicommon.display_debug1( munkicommon.display_debug1(
"select pkg_key from pkgs where pkgname = %s" % pkg) "select pkg_key from pkgs where pkgname = %s" % pkg)
pkg_keys = curs.execute( pkg_keys = curs.execute(
'select pkg_key from pkgs where pkgname = ?', 'select pkg_key from pkgs where pkgname = ?',
values_t).fetchall() values_t).fetchall()
if not pkg_keys: if not pkg_keys:
munkicommon.display_error("%s not found in database." % pkg) munkicommon.display_error("%s not found in database." % pkg)
@@ -669,7 +668,7 @@ def getpkgkeys(pkgnames):
pkgkeyslist.append(row[0]) pkgkeyslist.append(row[0])
if pkgerror: if pkgerror:
pkgkeyslist = [] pkgkeyslist = []
curs.close() curs.close()
conn.close() conn.close()
munkicommon.display_debug1("pkgkeys: %s" % pkgkeyslist) munkicommon.display_debug1("pkgkeys: %s" % pkgkeyslist)
@@ -681,11 +680,11 @@ def getpathstoremove(pkgkeylist):
Queries our database for paths to remove. Queries our database for paths to remove.
""" """
pkgkeys = tuple(pkgkeylist) pkgkeys = tuple(pkgkeylist)
# open connection and cursor to our database # open connection and cursor to our database
conn = sqlite3.connect(packagedb) conn = sqlite3.connect(packagedb)
curs = conn.cursor() curs = conn.cursor()
# set up some subqueries: # set up some subqueries:
# all the paths that are referred to by the selected packages: # all the paths that are referred to by the selected packages:
if len(pkgkeys) > 1: if len(pkgkeys) > 1:
@@ -696,8 +695,8 @@ def getpathstoremove(pkgkeylist):
in_selected_packages = \ in_selected_packages = \
"select distinct path_key from pkgs_paths where pkg_key = %s" % \ "select distinct path_key from pkgs_paths where pkg_key = %s" % \
str(pkgkeys[0]) str(pkgkeys[0])
# all the paths that are referred to by every package # all the paths that are referred to by every package
# except the selected packages: # except the selected packages:
if len(pkgkeys) > 1: if len(pkgkeys) > 1:
not_in_other_packages = \ not_in_other_packages = \
@@ -707,26 +706,26 @@ def getpathstoremove(pkgkeylist):
not_in_other_packages = \ not_in_other_packages = \
"select distinct path_key from pkgs_paths where pkg_key != %s" % \ "select distinct path_key from pkgs_paths where pkg_key != %s" % \
str(pkgkeys[0]) str(pkgkeys[0])
# every path that is used by the selected packages and no other packages: # every path that is used by the selected packages and no other packages:
combined_query = \ combined_query = \
"select path from paths where " + \ "select path from paths where " + \
"(path_key in (%s) and path_key not in (%s))" % \ "(path_key in (%s) and path_key not in (%s))" % \
(in_selected_packages, not_in_other_packages) (in_selected_packages, not_in_other_packages)
munkicommon.display_status('Determining which filesystem items to remove') munkicommon.display_status('Determining which filesystem items to remove')
if munkicommon.munkistatusoutput: if munkicommon.munkistatusoutput:
munkistatus.percent(-1) munkistatus.percent(-1)
curs.execute(combined_query) curs.execute(combined_query)
results = curs.fetchall() results = curs.fetchall()
curs.close() curs.close()
conn.close() conn.close()
removalpaths = [] removalpaths = []
for item in results: for item in results:
removalpaths.append(item[0]) removalpaths.append(item[0])
return removalpaths return removalpaths
@@ -738,19 +737,19 @@ def removeReceipts(pkgkeylist, noupdateapplepkgdb):
""" """
munkicommon.display_status('Removing receipt info') munkicommon.display_status('Removing receipt info')
local_display_percent_done(0, 4) local_display_percent_done(0, 4)
conn = sqlite3.connect(packagedb) conn = sqlite3.connect(packagedb)
curs = conn.cursor() curs = conn.cursor()
osvers = int(os.uname()[2].split('.')[0]) os_version = munkicommon.getOsVersion(as_tuple=True)
applepkgdb = '/Library/Receipts/db/a.receiptdb' applepkgdb = '/Library/Receipts/db/a.receiptdb'
if not noupdateapplepkgdb and osvers < 10: if not noupdateapplepkgdb and os_version <= (10, 5):
aconn = sqlite3.connect(applepkgdb) aconn = sqlite3.connect(applepkgdb)
acurs = aconn.cursor() acurs = aconn.cursor()
local_display_percent_done(1, 4) local_display_percent_done(1, 4)
for pkgkey in pkgkeylist: for pkgkey in pkgkeylist:
pkgid = '' pkgid = ''
pkgkey_t = (pkgkey, ) pkgkey_t = (pkgkey, )
@@ -761,7 +760,7 @@ def removeReceipts(pkgkeylist, noupdateapplepkgdb):
pkgname = row[0] pkgname = row[0]
pkgid = row[1] pkgid = row[1]
receiptpath = None receiptpath = None
if osvers < 10: if os_version <= (10, 5):
if pkgname.endswith('.pkg'): if pkgname.endswith('.pkg'):
receiptpath = os.path.join('/Library/Receipts', pkgname) receiptpath = os.path.join('/Library/Receipts', pkgname)
if pkgname.endswith('.bom'): if pkgname.endswith('.bom'):
@@ -770,21 +769,21 @@ def removeReceipts(pkgkeylist, noupdateapplepkgdb):
else: else:
# clean up /Library/Receipts in case there's stuff left there # clean up /Library/Receipts in case there's stuff left there
receiptpath = findBundleReceiptFromID(pkgid) receiptpath = findBundleReceiptFromID(pkgid)
if receiptpath and os.path.exists(receiptpath): if receiptpath and os.path.exists(receiptpath):
munkicommon.display_detail("Removing %s..." % receiptpath) munkicommon.display_detail("Removing %s..." % receiptpath)
unused_retcode = subprocess.call( unused_retcode = subprocess.call(
["/bin/rm", "-rf", receiptpath]) ["/bin/rm", "-rf", receiptpath])
# remove pkg info from our database # remove pkg info from our database
munkicommon.display_detail( munkicommon.display_detail(
"Removing package data from internal database...") "Removing package data from internal database...")
curs.execute('DELETE FROM pkgs_paths where pkg_key = ?', pkgkey_t) curs.execute('DELETE FROM pkgs_paths where pkg_key = ?', pkgkey_t)
curs.execute('DELETE FROM pkgs where pkg_key = ?', pkgkey_t) curs.execute('DELETE FROM pkgs where pkg_key = ?', pkgkey_t)
# then remove pkg info from Apple's database unless option is passed # then remove pkg info from Apple's database unless option is passed
if not noupdateapplepkgdb and pkgid: if not noupdateapplepkgdb and pkgid:
if osvers < 10: if os_version <= (10, 5):
# Leopard # Leopard
pkgid_t = (pkgid, ) pkgid_t = (pkgid, )
row = acurs.execute( row = acurs.execute(
@@ -813,42 +812,42 @@ def removeReceipts(pkgkeylist, noupdateapplepkgdb):
else: else:
# Snow Leopard or higher, must use pkgutil # Snow Leopard or higher, must use pkgutil
cmd = ['/usr/sbin/pkgutil', '--forget', pkgid] cmd = ['/usr/sbin/pkgutil', '--forget', pkgid]
proc = subprocess.Popen(cmd, bufsize=1, proc = subprocess.Popen(cmd, bufsize=1,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
(output, unused_err) = proc.communicate() (output, unused_err) = proc.communicate()
if output: if output:
munkicommon.display_detail( munkicommon.display_detail(
str(output).decode('UTF-8').rstrip('\n')) str(output).decode('UTF-8').rstrip('\n'))
local_display_percent_done(2, 4) local_display_percent_done(2, 4)
# now remove orphaned paths from paths table # now remove orphaned paths from paths table
# first, Apple's database if option is passed # first, Apple's database if option is passed
if not noupdateapplepkgdb: if not noupdateapplepkgdb:
if osvers < 10: if os_version <= (10, 5):
munkicommon.display_detail( munkicommon.display_detail(
"Removing unused paths from Apple package database...") "Removing unused paths from Apple package database...")
acurs.execute( acurs.execute(
'''DELETE FROM paths where path_key not in '''DELETE FROM paths where path_key not in
(select distinct path_key from pkgs_paths)''') (select distinct path_key from pkgs_paths)''')
aconn.commit() aconn.commit()
acurs.close() acurs.close()
aconn.close() aconn.close()
local_display_percent_done(3, 4) local_display_percent_done(3, 4)
# we do our database last so its modtime is later than the modtime for the # we do our database last so its modtime is later than the modtime for the
# Apple DB... # Apple DB...
munkicommon.display_detail("Removing unused paths from internal package " munkicommon.display_detail("Removing unused paths from internal package "
"database...") "database...")
curs.execute( curs.execute(
'''DELETE FROM paths where path_key not in '''DELETE FROM paths where path_key not in
(select distinct path_key from pkgs_paths)''') (select distinct path_key from pkgs_paths)''')
conn.commit() conn.commit()
curs.close() curs.close()
conn.close() conn.close()
local_display_percent_done(4, 4) local_display_percent_done(4, 4)
@@ -892,8 +891,8 @@ def isBundle(pathname):
return False return False
else: else:
return False return False
def insideBundle(pathname): def insideBundle(pathname):
'''Check the path to see if it's inside a bundle.''' '''Check the path to see if it's inside a bundle.'''
while len(pathname) > 1: while len(pathname) > 1:
@@ -916,15 +915,15 @@ def removeFilesystemItems(removalpaths, forcedeletebundles):
removalerrors = "" removalerrors = ""
removalcount = len(removalpaths) removalcount = len(removalpaths)
munkicommon.display_status("Removing %s filesystem items" % removalcount) munkicommon.display_status("Removing %s filesystem items" % removalcount)
itemcount = len(removalpaths) itemcount = len(removalpaths)
itemindex = 0 itemindex = 0
local_display_percent_done(itemindex, itemcount) local_display_percent_done(itemindex, itemcount)
for item in removalpaths: for item in removalpaths:
itemindex += 1 itemindex += 1
pathtoremove = "/" + item pathtoremove = "/" + item
# use os.path.lexists so broken links return true # use os.path.lexists so broken links return true
# so we can remove them # so we can remove them
if os.path.lexists(pathtoremove): if os.path.lexists(pathtoremove):
munkicommon.display_detail("Removing: " + pathtoremove) munkicommon.display_detail("Removing: " + pathtoremove)
@@ -966,9 +965,9 @@ def removeFilesystemItems(removalpaths, forcedeletebundles):
else: else:
# if this path is inside a bundle, and we've been # if this path is inside a bundle, and we've been
# directed to force remove bundles, # directed to force remove bundles,
# we don't need to warn because it's going to be # we don't need to warn because it's going to be
# removed with the bundle. # removed with the bundle.
# Otherwise, we should warn about non-empty # Otherwise, we should warn about non-empty
# directories. # directories.
if not insideBundle(pathtoremove) or \ if not insideBundle(pathtoremove) or \
not forcedeletebundles: not forcedeletebundles:
@@ -977,15 +976,15 @@ def removeFilesystemItems(removalpaths, forcedeletebundles):
pathtoremove pathtoremove
munkicommon.display_error(msg) munkicommon.display_error(msg)
removalerrors = removalerrors + "\n" + msg removalerrors = removalerrors + "\n" + msg
else: else:
# not a directory, just unlink it # not a directory, just unlink it
# I was using rm instead of Python because I don't trust # I was using rm instead of Python because I don't trust
# handling of resource forks with Python # handling of resource forks with Python
#retcode = subprocess.call(['/bin/rm', pathtoremove]) #retcode = subprocess.call(['/bin/rm', pathtoremove])
# but man that's slow. # but man that's slow.
# I think there's a lot of overhead with the # I think there's a lot of overhead with the
# subprocess call. I'm going to use os.remove. # subprocess call. I'm going to use os.remove.
# I hope I don't regret it. # I hope I don't regret it.
retcode = '' retcode = ''
try: try:
@@ -994,9 +993,9 @@ def removeFilesystemItems(removalpaths, forcedeletebundles):
msg = "Couldn't remove item %s: %s" % (pathtoremove, err) msg = "Couldn't remove item %s: %s" % (pathtoremove, err)
munkicommon.display_error(msg) munkicommon.display_error(msg)
removalerrors = removalerrors + "\n" + msg removalerrors = removalerrors + "\n" + msg
local_display_percent_done(itemindex, itemcount) local_display_percent_done(itemindex, itemcount)
if removalerrors: if removalerrors:
munkicommon.display_info( munkicommon.display_info(
"---------------------------------------------------") "---------------------------------------------------")
@@ -1005,8 +1004,8 @@ def removeFilesystemItems(removalpaths, forcedeletebundles):
munkicommon.display_info( munkicommon.display_info(
"---------------------------------------------------") "---------------------------------------------------")
munkicommon.display_info(removalerrors) munkicommon.display_info(removalerrors)
def removepackages(pkgnames, forcedeletebundles=False, listfiles=False, def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
rebuildpkgdb=False, noremovereceipts=False, rebuildpkgdb=False, noremovereceipts=False,
noupdateapplepkgdb=False): noupdateapplepkgdb=False):
@@ -1018,21 +1017,21 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
munkicommon.display_error( munkicommon.display_error(
"You must specify at least one package to remove!") "You must specify at least one package to remove!")
return -2 return -2
if not initDatabase(forcerebuild=rebuildpkgdb): if not initDatabase(forcerebuild=rebuildpkgdb):
munkicommon.display_error("Could not initialize receipt database.") munkicommon.display_error("Could not initialize receipt database.")
return -3 return -3
pkgkeyslist = getpkgkeys(pkgnames) pkgkeyslist = getpkgkeys(pkgnames)
if len(pkgkeyslist) == 0: if len(pkgkeyslist) == 0:
return -4 return -4
if munkicommon.stopRequested(): if munkicommon.stopRequested():
return -128 return -128
removalpaths = getpathstoremove(pkgkeyslist) removalpaths = getpathstoremove(pkgkeyslist)
if munkicommon.stopRequested(): if munkicommon.stopRequested():
return -128 return -128
if removalpaths: if removalpaths:
if listfiles: if listfiles:
removalpaths.sort() removalpaths.sort()
@@ -1046,7 +1045,7 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
munkicommon.display_status('Nothing to remove.') munkicommon.display_status('Nothing to remove.')
if munkicommon.munkistatusoutput: if munkicommon.munkistatusoutput:
time.sleep(2) time.sleep(2)
if not listfiles: if not listfiles:
if not noremovereceipts: if not noremovereceipts:
removeReceipts(pkgkeyslist, noupdateapplepkgdb) removeReceipts(pkgkeyslist, noupdateapplepkgdb)
@@ -1054,7 +1053,7 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
munkistatus.enableStopButton() munkistatus.enableStopButton()
munkicommon.display_status('Package removal complete.') munkicommon.display_status('Package removal complete.')
time.sleep(2) time.sleep(2)
return 0 return 0
@@ -1069,40 +1068,40 @@ def main():
p.add_option('--forcedeletebundles', '-f', action='store_true', p.add_option('--forcedeletebundles', '-f', action='store_true',
help='Delete bundles even if they aren\'t empty.') help='Delete bundles even if they aren\'t empty.')
p.add_option('--listfiles', '-l', action='store_true', p.add_option('--listfiles', '-l', action='store_true',
help='''List the filesystem objects to be removed, help='''List the filesystem objects to be removed,
but do not actually remove them.''') but do not actually remove them.''')
p.add_option('--rebuildpkgdb', action='store_true', p.add_option('--rebuildpkgdb', action='store_true',
help='Force a rebuild of the internal package database.') help='Force a rebuild of the internal package database.')
p.add_option('--noremovereceipts', action='store_true', p.add_option('--noremovereceipts', action='store_true',
help='''Do not remove receipts and boms from help='''Do not remove receipts and boms from
/Library/Receipts and update internal package /Library/Receipts and update internal package
database.''') database.''')
p.add_option('--noupdateapplepkgdb', action='store_true', p.add_option('--noupdateapplepkgdb', action='store_true',
help='''Do not update Apple\'s package database. help='''Do not update Apple\'s package database.
If --noremovereceipts is also given, this is implied''') If --noremovereceipts is also given, this is implied''')
p.add_option('--munkistatusoutput', '-m', action='store_true', p.add_option('--munkistatusoutput', '-m', action='store_true',
help='Output is formatted for use with MunkiStatus.') help='Output is formatted for use with MunkiStatus.')
p.add_option('--verbose', '-v', action='count', default=1, p.add_option('--verbose', '-v', action='count', default=1,
help='''More verbose output. May be specified multiple help='''More verbose output. May be specified multiple
times.''') times.''')
# Get our options and our package names # Get our options and our package names
options, pkgnames = p.parse_args() options, pkgnames = p.parse_args()
# check to see if we're root # check to see if we're root
if os.geteuid() != 0: if os.geteuid() != 0:
munkicommon.display_error("You must run this as root!") munkicommon.display_error("You must run this as root!")
exit(-1) exit(-1)
# set the munkicommon globals # set the munkicommon globals
munkicommon.munkistatusoutput = options.munkistatusoutput munkicommon.munkistatusoutput = options.munkistatusoutput
munkicommon.verbose = options.verbose munkicommon.verbose = options.verbose
if options.munkistatusoutput: if options.munkistatusoutput:
pkgcount = len(pkgnames) pkgcount = len(pkgnames)
munkistatus.message("Removing %s packages..." % pkgcount) munkistatus.message("Removing %s packages..." % pkgcount)
munkistatus.detail("") munkistatus.detail("")
retcode = removepackages(pkgnames, retcode = removepackages(pkgnames,
forcedeletebundles=options.forcedeletebundles, forcedeletebundles=options.forcedeletebundles,
listfiles=options.listfiles, listfiles=options.listfiles,
@@ -1112,9 +1111,8 @@ def main():
if options.munkistatusoutput: if options.munkistatusoutput:
munkistatus.quit() munkistatus.quit()
exit(retcode) exit(retcode)
if __name__ == '__main__': if __name__ == '__main__':
main() main()