def softwareUpdateList(): '''Returns a list of available updates using `/usr/sbin/softwareupdate -l`''' global CACHEDUPDATELIST if CACHEDUPDATELIST != None: return CACHEDUPDATELIST updates = [] munkicommon.display_detail( 'Getting list of available Apple Software Updates') cmd = ['/usr/sbin/softwareupdate', '-l'] proc = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, unused_err) = proc.communicate() if proc.returncode == 0: updates = [ str(item)[5:] for item in str(output).splitlines() if str(item).startswith(' * ') ] munkicommon.display_detail('softwareupdate returned %s updates' % len(updates)) CACHEDUPDATELIST = updates return CACHEDUPDATELIST
def softwareUpdateList(): '''Returns a list of available updates using `/usr/sbin/softwareupdate -l`''' global CACHEDUPDATELIST if CACHEDUPDATELIST != None: return CACHEDUPDATELIST updates = [] munkicommon.display_detail( 'Getting list of available Apple Software Updates') cmd = ['/usr/sbin/softwareupdate', '-l'] proc = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, unused_err) = proc.communicate() if proc.returncode == 0: updates = [str(item)[5:] for item in str(output).splitlines() if str(item).startswith(' * ')] munkicommon.display_detail( 'softwareupdate returned %s updates' % len(updates)) CACHEDUPDATELIST = updates return CACHEDUPDATELIST
def initDatabase(forcerebuild=False): """ Builds or rebuilds our internal package database. """ if not shouldRebuildDB(packagedb) and not forcerebuild: return True munkicommon.display_status_minor( 'Gathering information on installed packages') if os.path.exists(packagedb): try: os.remove(packagedb) except (OSError, IOError): munkicommon.display_error( "Could not remove out-of-date receipt database.") return False os_version = munkicommon.getOsVersion(as_tuple=True) pkgcount = 0 receiptsdir = "/Library/Receipts" bomsdir = "/Library/Receipts/boms" if os.path.exists(receiptsdir): receiptlist = munkicommon.listdir(receiptsdir) for item in receiptlist: if item.endswith(".pkg"): pkgcount += 1 if os.path.exists(bomsdir): bomslist = munkicommon.listdir(bomsdir) for item in bomslist: if item.endswith(".bom"): pkgcount += 1 if os_version >= (10, 6): # Snow Leopard or later pkglist = [] cmd = ['/usr/sbin/pkgutil', '--pkgs'] proc = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) while True: line = proc.stdout.readline() if not line and (proc.poll() != None): break pkglist.append(line.rstrip('\n')) pkgcount += 1 conn = sqlite3.connect(packagedb) conn.text_factory = str curs = conn.cursor() CreateTables(curs) currentpkgindex = 0 munkicommon.display_percent_done(0, pkgcount) if os.path.exists(receiptsdir): receiptlist = munkicommon.listdir(receiptsdir) for item in receiptlist: if munkicommon.stopRequested(): curs.close() conn.close() #our package db isn't valid, so we should delete it os.remove(packagedb) return False if item.endswith(".pkg"): receiptpath = os.path.join(receiptsdir, item) munkicommon.display_detail("Importing %s...", receiptpath) ImportPackage(receiptpath, curs) currentpkgindex += 1 munkicommon.display_percent_done(currentpkgindex, pkgcount) if os.path.exists(bomsdir): bomslist = munkicommon.listdir(bomsdir) for item in bomslist: if munkicommon.stopRequested(): curs.close() conn.close() #our package db isn't valid, so we should delete it os.remove(packagedb) return False if item.endswith(".bom"): bompath = os.path.join(bomsdir, item) munkicommon.display_detail("Importing %s...", bompath) ImportBom(bompath, curs) currentpkgindex += 1 munkicommon.display_percent_done(currentpkgindex, pkgcount) if os_version >= (10, 6): # Snow Leopard or later for pkg in pkglist: if munkicommon.stopRequested(): curs.close() conn.close() #our package db isn't valid, so we should delete it os.remove(packagedb) return False munkicommon.display_detail("Importing %s...", pkg) ImportFromPkgutil(pkg, curs) currentpkgindex += 1 munkicommon.display_percent_done(currentpkgindex, pkgcount) # in case we didn't quite get to 100% for some reason if currentpkgindex < pkgcount: munkicommon.display_percent_done(pkgcount, pkgcount) # commit and close the db when we're done. conn.commit() curs.close() conn.close() return True
# add any pem files in the ca_dir_path directory for item in os.listdir(ca_dir_path): if item.endswith('.pem'): certs_to_add.append(os.path.join(ca_dir_path, item)) for cert in certs_to_add: munkicommon.display_debug1('Adding CA cert %s...', cert) try: output = security('add-trusted-cert', '-d', '-k', SYSTEM_KEYCHAIN, cert) if output: munkicommon.display_debug2(output) except SecurityError, err: munkicommon.display_error( 'Could not add CA cert %s into System keychain: %s', cert, err) munkicommon.display_detail('System.keychain updated.') def make_client_keychain(cert_info=None): '''Builds a client cert keychain from existing client certs''' if not cert_info: # just grab data from Munki's preferences/defaults cert_info = get_munki_client_cert_info() client_cert_path = cert_info['client_cert_path'] client_key_path = cert_info['client_key_path'] site_urls = cert_info['site_urls'] if not client_cert_path: # no client, so nothing to do munkicommon.display_debug1(
def removeFilesystemItems(removalpaths, forcedeletebundles): """ Attempts to remove all the paths in the array removalpaths """ # we sort in reverse because we can delete from the bottom up, # clearing a directory before we try to remove the directory itself removalpaths.sort(reverse=True) removalerrors = "" removalcount = len(removalpaths) munkicommon.display_status_minor( 'Removing %s filesystem items' % removalcount) itemcount = len(removalpaths) itemindex = 0 munkicommon.display_percent_done(itemindex, itemcount) for item in removalpaths: itemindex += 1 pathtoremove = "/" + item # use os.path.lexists so broken links return true # so we can remove them if os.path.lexists(pathtoremove): munkicommon.display_detail("Removing: " + pathtoremove) if (os.path.isdir(pathtoremove) and \ not os.path.islink(pathtoremove)): diritems = munkicommon.listdir(pathtoremove) if diritems == ['.DS_Store']: # If there's only a .DS_Store file # we'll consider it empty ds_storepath = pathtoremove + "/.DS_Store" try: os.remove(ds_storepath) except (OSError, IOError): pass diritems = munkicommon.listdir(pathtoremove) if diritems == []: # directory is empty try: os.rmdir(pathtoremove) except (OSError, IOError), err: msg = "Couldn't remove directory %s - %s" % ( pathtoremove, err) munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg else: # the directory is marked for deletion but isn't empty. # if so directed, if it's a bundle (like .app), we should # remove it anyway - no use having a broken bundle hanging # around if (forcedeletebundles and isBundle(pathtoremove)): munkicommon.display_warning( "Removing non-empty bundle: %s", pathtoremove) retcode = subprocess.call(['/bin/rm', '-r', pathtoremove]) if retcode: msg = "Couldn't remove bundle %s" % pathtoremove munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg else: # if this path is inside a bundle, and we've been # directed to force remove bundles, # we don't need to warn because it's going to be # removed with the bundle. # Otherwise, we should warn about non-empty # directories. if not insideBundle(pathtoremove) or \ not forcedeletebundles: msg = \ "Did not remove %s because it is not empty." % \ pathtoremove munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg else: # not a directory, just unlink it # I was using rm instead of Python because I don't trust # handling of resource forks with Python #retcode = subprocess.call(['/bin/rm', pathtoremove]) # but man that's slow. # I think there's a lot of overhead with the # subprocess call. I'm going to use os.remove. # I hope I don't regret it. retcode = '' try: os.remove(pathtoremove) except (OSError, IOError), err: msg = "Couldn't remove item %s: %s" % (pathtoremove, err) munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg
def removeReceipts(pkgkeylist, noupdateapplepkgdb): """ Removes receipt data from /Library/Receipts, /Library/Receipts/boms, our internal package database, and optionally Apple's package database. """ munkicommon.display_status_minor('Removing receipt info') munkicommon.display_percent_done(0, 4) conn = sqlite3.connect(packagedb) curs = conn.cursor() os_version = munkicommon.getOsVersion(as_tuple=True) applepkgdb = '/Library/Receipts/db/a.receiptdb' if not noupdateapplepkgdb and os_version <= (10, 5): aconn = sqlite3.connect(applepkgdb) acurs = aconn.cursor() munkicommon.display_percent_done(1, 4) for pkgkey in pkgkeylist: pkgid = '' pkgkey_t = (pkgkey, ) row = curs.execute( 'SELECT pkgname, pkgid from pkgs where pkg_key = ?', pkgkey_t).fetchone() if row: pkgname = row[0] pkgid = row[1] receiptpath = None if os_version <= (10, 5): if pkgname.endswith('.pkg'): receiptpath = os.path.join('/Library/Receipts', pkgname) if pkgname.endswith('.bom'): receiptpath = os.path.join('/Library/Receipts/boms', pkgname) else: # clean up /Library/Receipts in case there's stuff left there receiptpath = findBundleReceiptFromID(pkgid) if receiptpath and os.path.exists(receiptpath): munkicommon.display_detail("Removing %s...", receiptpath) unused_retcode = subprocess.call( ["/bin/rm", "-rf", receiptpath]) # remove pkg info from our database munkicommon.display_detail( "Removing package data from internal database...") curs.execute('DELETE FROM pkgs_paths 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 if not noupdateapplepkgdb and pkgid: if os_version <= (10, 5): # Leopard pkgid_t = (pkgid, ) row = acurs.execute( 'SELECT pkg_key FROM pkgs where pkgid = ?', pkgid_t).fetchone() if row: munkicommon.display_detail( "Removing package data from Apple package "+ "database...") apple_pkg_key = row[0] pkgkey_t = (apple_pkg_key, ) acurs.execute( 'DELETE FROM pkgs where pkg_key = ?', pkgkey_t) acurs.execute( 'DELETE FROM pkgs_paths where pkg_key = ?', pkgkey_t) acurs.execute( 'DELETE FROM pkgs_groups where pkg_key = ?', pkgkey_t) acurs.execute( 'DELETE FROM acls where pkg_key = ?', pkgkey_t) acurs.execute( 'DELETE FROM taints where pkg_key = ?', pkgkey_t) acurs.execute( 'DELETE FROM sha1s where pkg_key = ?', pkgkey_t) acurs.execute( 'DELETE FROM oldpkgs where pkg_key = ?', pkgkey_t) else: # Snow Leopard or higher, must use pkgutil cmd = ['/usr/sbin/pkgutil', '--forget', pkgid] proc = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, unused_err) = proc.communicate() if output: munkicommon.display_detail( str(output).decode('UTF-8').rstrip('\n')) munkicommon.display_percent_done(2, 4) # now remove orphaned paths from paths table # first, Apple's database if option is passed if not noupdateapplepkgdb: if os_version <= (10, 5): munkicommon.display_detail( "Removing unused paths from Apple package database...") acurs.execute( '''DELETE FROM paths where path_key not in (select distinct path_key from pkgs_paths)''') aconn.commit() acurs.close() aconn.close() munkicommon.display_percent_done(3, 4) # we do our database last so its modtime is later than the modtime for the # Apple DB... munkicommon.display_detail("Removing unused paths from internal package " "database...") curs.execute( '''DELETE FROM paths where path_key not in (select distinct path_key from pkgs_paths)''') conn.commit() curs.close() conn.close() munkicommon.display_percent_done(4, 4)
def get_url(url, destinationpath, custom_headers=None, message=None, onlyifnewer=False, resume=False, follow_redirects=False): """Gets an HTTP or HTTPS URL and stores it in destination path. Returns a dictionary of headers, which includes http_result_code and http_result_description. Will raise CurlError if Gurl returns an error. Will raise HTTPError if HTTP Result code is not 2xx or 304. If destinationpath already exists, you can set 'onlyifnewer' to true to indicate you only want to download the file only if it's newer on the server. If you set resume to True, Gurl will attempt to resume an interrupted download.""" tempdownloadpath = destinationpath + '.download' if os.path.exists(tempdownloadpath) and not resume: if resume and not os.path.exists(destinationpath): os.remove(tempdownloadpath) cache_data = None if onlyifnewer and os.path.exists(destinationpath): # create a temporary Gurl object so we can extract the # stored caching data so we can download only if the # file has changed on the server gurl_obj = Gurl.alloc().initWithOptions_({'file': destinationpath}) cache_data = gurl_obj.get_stored_headers() del gurl_obj options = { 'url': url, 'file': tempdownloadpath, 'follow_redirects': follow_redirects, 'can_resume': resume, 'additional_headers': header_dict_from_list(custom_headers), 'download_only_if_changed': onlyifnewer, 'cache_data': cache_data, 'logging_function': munkicommon.display_debug2 } munkicommon.display_debug2('Options: %s' % options) connection = Gurl.alloc().initWithOptions_(options) stored_percent_complete = -1 stored_bytes_received = 0 connection.start() try: while True: # if we did `while not connection.isDone()` we'd miss printing # messages and displaying percentages if we exit the loop first connection_done = connection.isDone() if message and connection.status and connection.status != 304: # log always, display if verbose is 1 or more # also display in MunkiStatus detail field munkicommon.display_status_minor(message) # now clear message so we don't display it again message = None if (str(connection.status).startswith('2') and connection.percentComplete != -1): if connection.percentComplete != stored_percent_complete: # display percent done if it has changed stored_percent_complete = connection.percentComplete munkicommon.display_percent_done(stored_percent_complete, 100) elif connection.bytesReceived != stored_bytes_received: # if we don't have percent done info, log bytes received stored_bytes_received = connection.bytesReceived munkicommon.display_detail('Bytes received: %s', stored_bytes_received) if connection_done: break except (KeyboardInterrupt, SystemExit): # safely kill the connection then re-raise connection.cancel() raise except Exception, err: # too general, I know # Let us out! ... Safely! Unexpectedly quit dialogs are annoying... connection.cancel() # Re-raise the error as a GurlError raise GurlError(-1, str(err))
# size, otherwise fall back to a custom X-Download-Size # header. # This is primary for servers that use chunked transfer # encoding, when Content-Length is forbidden by # RFC2616 4.4. An example of such a server is # Google App Engine Blobstore. targetsize = ( header.get('content-length') or header.get('x-download-size')) targetsize = int(targetsize) except (ValueError, TypeError): targetsize = 0 if header.get('http_result_code') == '206': # partial content because we're resuming munkicommon.display_detail( 'Resuming partial download for %s' % os.path.basename(destinationpath)) contentrange = header.get('content-range') if contentrange.startswith('bytes'): try: targetsize = int(contentrange.split('/')[1]) except (ValueError, TypeError): targetsize = 0 if message and header.get('http_result_code') != '304': if message: # log always, display if verbose is 1 or more # also display in MunkiStatus detail field munkicommon.display_status_minor(message) elif targetsize and header.get('http_result_code').startswith('2'):
# default catalog for Snow Leopard catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-leopard-snowleopard.merged-1.sucatalog' elif osvers == 11: # default catalog for Lion catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-lion-snowleopard-leopard.merged-1.sucatalog.gz' else: munkicommon.display_error( 'Can\'t determine Software Update CatalogURL for Darwin ' 'version %s', osvers) return -1 if not os.path.exists(swupdCacheDir(temp=False)): try: os.makedirs(swupdCacheDir(temp=False)) except OSError, oserr: raise ReplicationError(oserr) munkicommon.display_detail('Caching CatalogURL %s', catalogURL) download_location = os.path.join(swupdCacheDir(temp=False), 'apple.sucatalog') try: file_changed = updatecheck.getResourceIfChangedAtomically( catalogURL, download_location, resume=True) extractAppleSUScatalog() return file_changed except updatecheck.MunkiDownloadError: return -1 def installedApplePackagesChanged(): '''Generates a SHA-256 checksum of the info for all packages in the receipts database whose id matches com.apple.* and compares it to a stored version of this checksum. Returns False if the checksums
# add any pem files in the ca_dir_path directory for item in os.listdir(ca_dir_path): if item.endswith('.pem'): certs_to_add.append(os.path.join(ca_dir_path, item)) for cert in certs_to_add: munkicommon.display_debug1('Adding CA cert %s...', cert) try: output = security('add-trusted-cert', '-d', '-k', SYSTEM_KEYCHAIN, cert) if output: munkicommon.display_debug2(output) except SecurityError, err: munkicommon.display_error( 'Could not add CA cert %s into System keychain: %s', cert, err) munkicommon.display_detail('System.keychain updated.') def make_client_keychain(certdata=None): '''Builds a client cert keychain from existing client certs''' if not certdata: # jusr grab data from Munki's preferences/defaults certdata = get_munki_client_cert_data() client_cert_path = certdata['client_cert_path'] client_key_path = certdata['client_key_path'] site_url = certdata['site_url'] if not client_cert_path: # no client, so nothing to do munkicommon.display_debug1('No client cert info provided, '
def get_url(url, destinationpath, custom_headers=None, message=None, onlyifnewer=False, resume=False, follow_redirects=False): """Gets an HTTP or HTTPS URL and stores it in destination path. Returns a dictionary of headers, which includes http_result_code and http_result_description. Will raise CurlError if Gurl returns an error. Will raise HTTPError if HTTP Result code is not 2xx or 304. If destinationpath already exists, you can set 'onlyifnewer' to true to indicate you only want to download the file only if it's newer on the server. If you set resume to True, Gurl will attempt to resume an interrupted download.""" tempdownloadpath = destinationpath + '.download' if os.path.exists(tempdownloadpath) and not resume: if resume and not os.path.exists(destinationpath): os.remove(tempdownloadpath) cache_data = None if onlyifnewer and os.path.exists(destinationpath): # create a temporary Gurl object so we can extract the # stored caching data so we can download only if the # file has changed on the server gurl_obj = Gurl.alloc().initWithOptions_({'file': destinationpath}) cache_data = gurl_obj.get_stored_headers() del gurl_obj options = {'url': url, 'file': tempdownloadpath, 'follow_redirects': follow_redirects, 'can_resume': resume, 'additional_headers': header_dict_from_list(custom_headers), 'download_only_if_changed': onlyifnewer, 'cache_data': cache_data, 'logging_function': munkicommon.display_debug2} munkicommon.display_debug2('Options: %s' % options) connection = Gurl.alloc().initWithOptions_(options) stored_percent_complete = -1 stored_bytes_received = 0 connection.start() try: while True: # if we did `while not connection.isDone()` we'd miss printing # messages and displaying percentages if we exit the loop first connection_done = connection.isDone() if message and connection.status and connection.status != 304: # log always, display if verbose is 1 or more # also display in MunkiStatus detail field munkicommon.display_status_minor(message) # now clear message so we don't display it again message = None if (str(connection.status).startswith('2') and connection.percentComplete != -1): if connection.percentComplete != stored_percent_complete: # display percent done if it has changed stored_percent_complete = connection.percentComplete munkicommon.display_percent_done( stored_percent_complete, 100) elif connection.bytesReceived != stored_bytes_received: # if we don't have percent done info, log bytes received stored_bytes_received = connection.bytesReceived munkicommon.display_detail( 'Bytes received: %s', stored_bytes_received) if connection_done: break except (KeyboardInterrupt, SystemExit): # safely kill the connection then re-raise connection.cancel() raise except Exception, err: # too general, I know # Let us out! ... Safely! Unexpectedly quit dialogs are annoying... connection.cancel() # Re-raise the error as a GurlError raise GurlError(-1, str(err))
break except (KeyboardInterrupt, SystemExit): # safely kill the connection then re-raise connection.cancel() raise except Exception, err: # too general, I know # Let us out! ... Safely! Unexpectedly quit dialogs are annoying... connection.cancel() # Re-raise the error as a GurlError raise GurlError(-1, str(err)) if connection.error != None: # Gurl returned an error munkicommon.display_detail( 'Download error %s: %s', connection.error.code(), connection.error.localizedDescription()) if connection.SSLerror: munkicommon.display_detail( 'SSL error detail: %s', str(connection.SSLerror)) keychain.debug_output() munkicommon.display_detail('Headers: %s', connection.headers) if os.path.exists(tempdownloadpath) and not resume: os.remove(tempdownloadpath) raise GurlError(connection.error.code(), connection.error.localizedDescription()) if connection.response != None: munkicommon.display_debug1('Status: %s', connection.status) munkicommon.display_debug1('Headers: %s', connection.headers) if connection.redirection != []:
def removeReceipts(pkgkeylist, noupdateapplepkgdb): """ Removes receipt data from /Library/Receipts, /Library/Receipts/boms, our internal package database, and optionally Apple's package database. """ munkicommon.display_status_minor('Removing receipt info') munkicommon.display_percent_done(0, 4) conn = sqlite3.connect(packagedb) curs = conn.cursor() os_version = munkicommon.getOsVersion(as_tuple=True) applepkgdb = '/Library/Receipts/db/a.receiptdb' if not noupdateapplepkgdb and os_version <= (10, 5): aconn = sqlite3.connect(applepkgdb) acurs = aconn.cursor() munkicommon.display_percent_done(1, 4) for pkgkey in pkgkeylist: pkgid = '' pkgkey_t = (pkgkey, ) row = curs.execute('SELECT pkgname, pkgid from pkgs where pkg_key = ?', pkgkey_t).fetchone() if row: pkgname = row[0] pkgid = row[1] receiptpath = None if os_version <= (10, 5): if pkgname.endswith('.pkg'): receiptpath = os.path.join('/Library/Receipts', pkgname) if pkgname.endswith('.bom'): receiptpath = os.path.join('/Library/Receipts/boms', pkgname) else: # clean up /Library/Receipts in case there's stuff left there receiptpath = findBundleReceiptFromID(pkgid) if receiptpath and os.path.exists(receiptpath): munkicommon.display_detail("Removing %s...", receiptpath) dummy_retcode = subprocess.call( ["/bin/rm", "-rf", receiptpath]) # remove pkg info from our database munkicommon.display_detail( "Removing package data from internal database...") curs.execute('DELETE FROM pkgs_paths 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 if not noupdateapplepkgdb and pkgid: if os_version <= (10, 5): # Leopard pkgid_t = (pkgid, ) row = acurs.execute('SELECT pkg_key FROM pkgs where pkgid = ?', pkgid_t).fetchone() if row: munkicommon.display_detail( "Removing package data from Apple package " + "database...") apple_pkg_key = row[0] pkgkey_t = (apple_pkg_key, ) acurs.execute('DELETE FROM pkgs where pkg_key = ?', pkgkey_t) acurs.execute('DELETE FROM pkgs_paths where pkg_key = ?', pkgkey_t) acurs.execute('DELETE FROM pkgs_groups where pkg_key = ?', pkgkey_t) acurs.execute('DELETE FROM acls where pkg_key = ?', pkgkey_t) acurs.execute('DELETE FROM taints where pkg_key = ?', pkgkey_t) acurs.execute('DELETE FROM sha1s where pkg_key = ?', pkgkey_t) acurs.execute('DELETE FROM oldpkgs where pkg_key = ?', pkgkey_t) else: # Snow Leopard or higher, must use pkgutil cmd = ['/usr/sbin/pkgutil', '--forget', pkgid] proc = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, dummy_err) = proc.communicate() if output: munkicommon.display_detail( str(output).decode('UTF-8').rstrip('\n')) munkicommon.display_percent_done(2, 4) # now remove orphaned paths from paths table # first, Apple's database if option is passed if not noupdateapplepkgdb: if os_version <= (10, 5): munkicommon.display_detail( "Removing unused paths from Apple package database...") acurs.execute('''DELETE FROM paths where path_key not in (select distinct path_key from pkgs_paths)''') aconn.commit() acurs.close() aconn.close() munkicommon.display_percent_done(3, 4) # we do our database last so its modtime is later than the modtime for the # Apple DB... munkicommon.display_detail("Removing unused paths from internal package " "database...") curs.execute('''DELETE FROM paths where path_key not in (select distinct path_key from pkgs_paths)''') conn.commit() curs.close() conn.close() munkicommon.display_percent_done(4, 4)
break except (KeyboardInterrupt, SystemExit): # safely kill the connection then re-raise connection.cancel() raise except Exception, err: # too general, I know # Let us out! ... Safely! Unexpectedly quit dialogs are annoying... connection.cancel() # Re-raise the error as a GurlError raise GurlError(-1, str(err)) if connection.error != None: # Gurl returned an error munkicommon.display_detail('Download error %s: %s', connection.error.code(), connection.error.localizedDescription()) if connection.SSLerror: munkicommon.display_detail('SSL error detail: %s', str(connection.SSLerror)) keychain.debug_output() munkicommon.display_detail('Headers: %s', connection.headers) if os.path.exists(tempdownloadpath) and not resume: os.remove(tempdownloadpath) raise GurlError(connection.error.code(), connection.error.localizedDescription()) if connection.response != None: munkicommon.display_debug1('Status: %s', connection.status) munkicommon.display_debug1('Headers: %s', connection.headers) if connection.redirection != []:
def removeFilesystemItems(removalpaths, forcedeletebundles): """ Attempts to remove all the paths in the array removalpaths """ # we sort in reverse because we can delete from the bottom up, # clearing a directory before we try to remove the directory itself removalpaths.sort(reverse=True) removalerrors = "" removalcount = len(removalpaths) munkicommon.display_status_minor('Removing %s filesystem items' % removalcount) itemcount = len(removalpaths) itemindex = 0 munkicommon.display_percent_done(itemindex, itemcount) for item in removalpaths: itemindex += 1 pathtoremove = "/" + item # use os.path.lexists so broken links return true # so we can remove them if os.path.lexists(pathtoremove): munkicommon.display_detail("Removing: " + pathtoremove) if (os.path.isdir(pathtoremove) and not os.path.islink(pathtoremove)): diritems = munkicommon.listdir(pathtoremove) if diritems == ['.DS_Store']: # If there's only a .DS_Store file # we'll consider it empty ds_storepath = pathtoremove + "/.DS_Store" try: os.remove(ds_storepath) except (OSError, IOError): pass diritems = munkicommon.listdir(pathtoremove) if diritems == []: # directory is empty try: os.rmdir(pathtoremove) except (OSError, IOError), err: msg = "Couldn't remove directory %s - %s" % ( pathtoremove, err) munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg else: # the directory is marked for deletion but isn't empty. # if so directed, if it's a bundle (like .app), we should # remove it anyway - no use having a broken bundle hanging # around if forcedeletebundles and isBundle(pathtoremove): munkicommon.display_warning( "Removing non-empty bundle: %s", pathtoremove) retcode = subprocess.call( ['/bin/rm', '-r', pathtoremove]) if retcode: msg = "Couldn't remove bundle %s" % pathtoremove munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg else: # if this path is inside a bundle, and we've been # directed to force remove bundles, # we don't need to warn because it's going to be # removed with the bundle. # Otherwise, we should warn about non-empty # directories. if not insideBundle(pathtoremove) or \ not forcedeletebundles: msg = \ "Did not remove %s because it is not empty." % \ pathtoremove munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg else: # not a directory, just unlink it # I was using rm instead of Python because I don't trust # handling of resource forks with Python #retcode = subprocess.call(['/bin/rm', pathtoremove]) # but man that's slow. # I think there's a lot of overhead with the # subprocess call. I'm going to use os.remove. # I hope I don't regret it. retcode = '' try: os.remove(pathtoremove) except (OSError, IOError), err: msg = "Couldn't remove item %s: %s" % (pathtoremove, err) munkicommon.display_error(msg) removalerrors = removalerrors + "\n" + msg