Exemple #1
0
def downloadAvailableUpdates():
    '''Downloads the available Apple updates using our local
    filtered sucatalog. Returns True if successful, False otherwise.'''
    msg = "Downloading available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    # use our filtered local catalog
    catalogpath = os.path.join(swupdCacheDir(),
        'content/catalogs/local_download.sucatalog')
    if not os.path.exists(catalogpath):
        munkicommon.display_error(
            'Missing local Software Update catalog at %s', catalogpath)
        return False

    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    # get the OS version
    osvers = int(os.uname()[2].split('.')[0])
    if osvers == 9:
        retcode = leopardDownloadAvailableUpdates(catalogURL)
    else:
        retcode = run_softwareupdate(['--CatalogURL', catalogURL, '-d', '-a'])

    if retcode:
        # there was an error
        munkicommon.display_error("softwareupdate error: %s" % retcode)
        return False
    return True
Exemple #2
0
def downloadAvailableUpdates():
    '''Downloads the available Apple updates using our local
    filtered sucatalog. Returns True if successful, False otherwise.'''
    msg = "Downloading available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    # use our filtered local catalog
    catalogpath = os.path.join(swupdCacheDir(),
                               'content/catalogs/local_download.sucatalog')
    if not os.path.exists(catalogpath):
        munkicommon.display_error(
            'Missing local Software Update catalog at %s', catalogpath)
        return False

    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    # get the OS version
    osvers = int(os.uname()[2].split('.')[0])
    if osvers == 9:
        retcode = leopardDownloadAvailableUpdates(catalogURL)
    else:
        retcode = run_softwareupdate(['--CatalogURL', catalogURL, '-d', '-a'])

    if retcode:
        # there was an error
        munkicommon.display_error("softwareupdate error: %s" % retcode)
        return False
    return True
Exemple #3
0
def getpathstoremove(pkgkeylist):
    """
    Queries our database for paths to remove.
    """
    pkgkeys = tuple(pkgkeylist)

    # open connection and cursor to our database
    conn = sqlite3.connect(packagedb)
    curs = conn.cursor()

    # set up some subqueries:
    # all the paths that are referred to by the selected packages:
    if len(pkgkeys) > 1:
        in_selected_packages = \
          "select distinct path_key from pkgs_paths where pkg_key in %s" % \
           str(pkgkeys)
    else:
        in_selected_packages = \
          "select distinct path_key from pkgs_paths where pkg_key = %s" % \
           str(pkgkeys[0])

    # all the paths that are referred to by every package
    # except the selected packages:
    if len(pkgkeys) > 1:
        not_in_other_packages = \
        "select distinct path_key from pkgs_paths where pkg_key not in %s" % \
         str(pkgkeys)
    else:
        not_in_other_packages = \
        "select distinct path_key from pkgs_paths where pkg_key != %s" % \
         str(pkgkeys[0])

    # every path that is used by the selected packages and no other packages:
    combined_query = \
        "select path from paths where " + \
        "(path_key in (%s) and path_key not in (%s))" % \
                                (in_selected_packages, not_in_other_packages)

    munkicommon.display_status('Determining which filesystem items to remove')
    if munkicommon.munkistatusoutput:
        munkistatus.percent(-1)

    curs.execute(combined_query)
    results = curs.fetchall()
    curs.close()
    conn.close()

    removalpaths = []
    for item in results:
        removalpaths.append(item[0])

    return removalpaths
Exemple #4
0
def getpathstoremove(pkgkeylist):
    """
    Queries our database for paths to remove.
    """
    pkgkeys = tuple(pkgkeylist)
    
    # open connection and cursor to our database
    conn = sqlite3.connect(packagedb)
    curs = conn.cursor()
    
    # set up some subqueries:
    # all the paths that are referred to by the selected packages:
    if len(pkgkeys) > 1:
        in_selected_packages = \
          "select distinct path_key from pkgs_paths where pkg_key in %s" % \
           str(pkgkeys)
    else:
        in_selected_packages = \
          "select distinct path_key from pkgs_paths where pkg_key = %s" % \
           str(pkgkeys[0])
        
    # all the paths that are referred to by every package 
    # except the selected packages:
    if len(pkgkeys) > 1:
        not_in_other_packages = \
        "select distinct path_key from pkgs_paths where pkg_key not in %s" % \
         str(pkgkeys)
    else:
        not_in_other_packages = \
        "select distinct path_key from pkgs_paths where pkg_key != %s" % \
         str(pkgkeys[0])
        
    # every path that is used by the selected packages and no other packages:
    combined_query = \
        "select path from paths where " + \
        "(path_key in (%s) and path_key not in (%s))" % \
                                (in_selected_packages, not_in_other_packages)
    
    munkicommon.display_status('Determining which filesystem items to remove')
    if munkicommon.munkistatusoutput:
        munkistatus.percent(-1)
    
    curs.execute(combined_query)
    results = curs.fetchall()
    curs.close()
    conn.close()
    
    removalpaths = []
    for item in results:
        removalpaths.append(item[0])
        
    return removalpaths
Exemple #5
0
def removepackages(pkgnames,
                   forcedeletebundles=False,
                   listfiles=False,
                   rebuildpkgdb=False,
                   noremovereceipts=False,
                   noupdateapplepkgdb=False):
    """
    Our main function, called by installer.py to remove items based on
    receipt info.
    """
    if pkgnames == []:
        munkicommon.display_error(
            "You must specify at least one package to remove!")
        return -2

    if not initDatabase(forcerebuild=rebuildpkgdb):
        munkicommon.display_error("Could not initialize receipt database.")
        return -3

    pkgkeyslist = getpkgkeys(pkgnames)
    if len(pkgkeyslist) == 0:
        return -4

    if munkicommon.stopRequested():
        return -128
    removalpaths = getpathstoremove(pkgkeyslist)
    if munkicommon.stopRequested():
        return -128

    if removalpaths:
        if listfiles:
            removalpaths.sort()
            for item in removalpaths:
                print "/" + item.encode('UTF-8')
        else:
            if munkicommon.munkistatusoutput:
                munkistatus.disableStopButton()
            removeFilesystemItems(removalpaths, forcedeletebundles)
    else:
        munkicommon.display_status('Nothing to remove.')
        if munkicommon.munkistatusoutput:
            time.sleep(2)

    if not listfiles:
        if not noremovereceipts:
            removeReceipts(pkgkeyslist, noupdateapplepkgdb)
        if munkicommon.munkistatusoutput:
            munkistatus.enableStopButton()
            munkicommon.display_status('Package removal complete.')
            time.sleep(2)

    return 0
Exemple #6
0
def removepackages(pkgnames, forcedeletebundles=False, listfiles=False,
                    rebuildpkgdb=False, noremovereceipts=False,
                    noupdateapplepkgdb=False):
    """
    Our main function, called by installer.py to remove items based on
    receipt info.
    """
    if pkgnames == []:
        munkicommon.display_error(
            "You must specify at least one package to remove!")
        return -2
        
    if not initDatabase(forcerebuild=rebuildpkgdb):
        munkicommon.display_error("Could not initialize receipt database.")
        return -3
        
    pkgkeyslist = getpkgkeys(pkgnames)
    if len(pkgkeyslist) == 0:
        return -4
        
    if munkicommon.stopRequested():
        return -128
    removalpaths = getpathstoremove(pkgkeyslist)
    if munkicommon.stopRequested():
        return -128
        
    if removalpaths:
        if listfiles:
            removalpaths.sort()
            for item in removalpaths:
                print "/" + item.encode('UTF-8')
        else:
            if munkicommon.munkistatusoutput:
                munkistatus.disableStopButton()
            removeFilesystemItems(removalpaths, forcedeletebundles)
    else:
        munkicommon.display_status('Nothing to remove.')
        if munkicommon.munkistatusoutput:
            time.sleep(2)
            
    if not listfiles:
        if not noremovereceipts:
            removeReceipts(pkgkeyslist, noupdateapplepkgdb)
        if munkicommon.munkistatusoutput:
            munkistatus.enableStopButton()
            munkicommon.display_status('Package removal complete.')
            time.sleep(2)
           
    return 0
Exemple #7
0
def getAvailableUpdates():
    '''Returns a list of product IDs of available Apple updates'''
    msg = "Checking for available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    applicable_updates = os.path.join(swupdCacheDir(),
                                      'ApplicableUpdates.plist')
    if os.path.exists(applicable_updates):
        # remove any old item
        try:
            os.unlink(applicable_updates)
        except (OSError, IOError):
            pass

    # use our locally-cached Apple catalog
    catalogpath = os.path.join(swupdCacheDir(),
                               'content/catalogs/apple_index.sucatalog')
    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    su_options = ['--CatalogURL', catalogURL, '-l', '-f', applicable_updates]

    retcode = run_softwareupdate(su_options)
    if retcode:
        # there was an error
        osvers = int(os.uname()[2].split('.')[0])
        if osvers == 9:
            # always a non-zero retcode on Leopard
            pass
        else:
            munkicommon.display_error("softwareupdate error: %s" % retcode)
            return []

    if os.path.exists(applicable_updates):
        try:
            updatelist = FoundationPlist.readPlist(applicable_updates)
            if updatelist:
                results_array = updatelist.get('phaseResultsArray', [])
                return [
                    item['productKey'] for item in results_array
                    if 'productKey' in item
                ]
        except FoundationPlist.NSPropertyListSerializationException:
            return []
    return []
Exemple #8
0
def getAvailableUpdates():
    '''Returns a list of product IDs of available Apple updates'''
    msg = "Checking for available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    applicable_updates = os.path.join(swupdCacheDir(),
                                      'ApplicableUpdates.plist')
    if os.path.exists(applicable_updates):
        # remove any old item
        try:
            os.unlink(applicable_updates)
        except (OSError, IOError):
            pass

    # use our locally-cached Apple catalog
    catalogpath = os.path.join(swupdCacheDir(),
        'content/catalogs/apple_index.sucatalog')
    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    su_options = ['--CatalogURL', catalogURL, '-l', '-f', applicable_updates]

    retcode = run_softwareupdate(su_options)
    if retcode:
        # there was an error
        osvers = int(os.uname()[2].split('.')[0])
        if osvers == 9:
            # always a non-zero retcode on Leopard
            pass
        else:
            munkicommon.display_error("softwareupdate error: %s" % retcode)
            return []

    if os.path.exists(applicable_updates):
        try:
            updatelist = FoundationPlist.readPlist(applicable_updates)
            if updatelist:
                results_array = updatelist.get('phaseResultsArray', [])
                return [item['productKey'] for item in results_array
                        if 'productKey' in item]
        except FoundationPlist.NSPropertyListSerializationException:
            return []
    return []
Exemple #9
0
def cacheSwupdMetadata():
    '''Copies ServerMetadata (.smd), Metadata (.pkm),
    and Distribution (.dist) files for the available updates
    to the local machine and writes a new sucatalog that refers
    to the local copies of these files.'''
    filtered_catalogpath = os.path.join(
        swupdCacheDir(), 'content/catalogs/filtered_index.sucatalog')
    catalog = FoundationPlist.readPlist(filtered_catalogpath)
    if 'Products' in catalog:
        product_keys = list(catalog['Products'].keys())
        for product_key in product_keys:
            munkicommon.display_status('Caching metadata for product ID %s',
                                       product_key)
            product = catalog['Products'][product_key]
            if 'ServerMetadataURL' in product:
                unused_path = replicateURLtoFilesystem(
                    product['ServerMetadataURL'], copy_only_if_missing=True)

            for package in product.get('Packages', []):
                ### not replicating the packages themselves ###
                #if 'URL' in package:
                #    unused_path = replicateURLtoFilesystem(
                #        package['URL'],
                #        copy_only_if_missing=fast_scan)
                if 'MetadataURL' in package:
                    munkicommon.display_status(
                        'Caching package metadata for product ID %s',
                        product_key)
                    unused_path = replicateURLtoFilesystem(
                        package['MetadataURL'], copy_only_if_missing=True)

            distributions = product['Distributions']
            for dist_lang in distributions.keys():
                munkicommon.display_status(
                    'Caching %s distribution for product ID %s', dist_lang,
                    product_key)
                dist_url = distributions[dist_lang]
                unused_path = replicateURLtoFilesystem(
                    dist_url, copy_only_if_missing=True)

        # rewrite URLs to point to local resources
        rewriteURLs(catalog, rewrite_pkg_urls=False)
        # write out the rewritten catalog
        localcatalogpath = os.path.join(swupdCacheDir(), 'content', 'catalogs')
        if not os.path.exists(localcatalogpath):
            try:
                os.makedirs(localcatalogpath)
            except OSError, oserr:
                raise ReplicationError(oserr)
        localcatalogpathname = os.path.join(localcatalogpath,
                                            'local_download.sucatalog')
        FoundationPlist.writePlist(catalog, localcatalogpathname)

        rewriteURLs(catalog, rewrite_pkg_urls=True)
        localcatalogpathname = os.path.join(localcatalogpath,
                                            'local_install.sucatalog')
        FoundationPlist.writePlist(catalog, localcatalogpathname)
Exemple #10
0
 elif output.startswith('Software Update Tool'):
     # don't display this
     pass
 elif output.startswith('Copyright 2'):
     # don't display this
     pass
 elif output.startswith('Installing ') and mode == 'install':
     item = output[11:]
     if item:
         if munkicommon.munkistatusoutput:
             munkistatus.message(output)
             munkistatus.detail("")
             munkistatus.percent(-1)
             munkicommon.log(output)
         else:
             munkicommon.display_status(output)
 elif output.startswith('Installed '):
     # 10.6 / 10.7. Successful install of package name.
     if mode == 'install':
         munkicommon.display_status(output)
         results['installed'].append(output[10:])
     else:
         pass
         # don't display.
         # softwareupdate logging "Installed" at the end of a
         # successful download-only session is odd.
 elif output.startswith('Done '):
     # 10.5. Successful install of package name.
     munkicommon.display_status(output)
     results['installed'].append(output[5:])
 elif output.startswith('Downloading ') and mode == 'install':
Exemple #11
0
def installAppleUpdates():
    '''Uses /usr/sbin/softwareupdate to install previously
    downloaded updates. Returns True if a restart is needed
    after install, False otherwise.'''
    msg = "Installing available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)
    restartneeded = restartNeeded()
    # use our filtered local catalog
    catalogpath = os.path.join(swupdCacheDir(),
        'content/catalogs/local_install.sucatalog')
    if not os.path.exists(catalogpath):
        munkicommon.display_error(
            'Missing local Software Update catalog at %s', catalogpath)
        # didn't do anything, so no restart needed
        return False

    installlist = getSoftwareUpdateInfo()
    installresults = {'installed':[], 'download':[]}

    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    retcode = run_softwareupdate(['--CatalogURL', catalogURL, '-i', '-a'],
                                 mode='install', results=installresults)

    if not 'InstallResults' in munkicommon.report:
        munkicommon.report['InstallResults'] = []

    for item in installlist:
        rep = {}
        rep['name'] = item.get('display_name')
        rep['version'] = item.get('version_to_install', '')
        rep['applesus'] = True
        rep['productKey'] = item.get('productKey', '')
        message = "Apple Software Update install of %s-%s: %s"
        if rep['name'] in installresults['installed']:
            rep['status'] = 0
            install_status = 'SUCCESSFUL'
        elif rep['name'] in installresults['download']:
            rep['status'] = -1
            install_status = 'FAILED due to missing package.'
            munkicommon.display_warning(
                'Apple update %s, %s failed. A sub-package was missing '
                'on disk at time of install.'
                % (rep['name'], rep['productKey']))
        else:
            rep['status'] = -2
            install_status = 'FAILED for unknown reason'
            munkicommon.display_warning(
                'Apple update %s, %s failed to install. No record of '
                'success or failure.' % (rep['name'],rep['productKey']))

        munkicommon.report['InstallResults'].append(rep)
        log_msg = message % (rep['name'], rep['version'], install_status)
        munkicommon.log(log_msg, "Install.log")

    if retcode:
        # there was an error
        munkicommon.display_error("softwareupdate error: %s" % retcode)
    # clean up our now stale local cache
    cachedir = os.path.join(swupdCacheDir())
    if os.path.exists(cachedir):
        unused_retcode = subprocess.call(['/bin/rm', '-rf', cachedir])
    # remove the now invalid appleUpdatesFile
    try:
        os.unlink(appleUpdatesFile())
    except OSError:
        pass
    # Also clear our pref value for last check date. We may have
    # just installed an update which is a pre-req for some other update.
    # Let's check again soon.
    munkicommon.set_pref('LastAppleSoftwareUpdateCheck', None)

    return restartneeded
Exemple #12
0
def cacheSwupdMetadata():
    '''Copies ServerMetadata (.smd), Metadata (.pkm),
    and Distribution (.dist) files for the available updates
    to the local machine and writes a new sucatalog that refers
    to the local copies of these files.'''
    filtered_catalogpath = os.path.join(swupdCacheDir(),
            'content/catalogs/filtered_index.sucatalog')
    catalog = FoundationPlist.readPlist(filtered_catalogpath)
    if 'Products' in catalog:
        product_keys = list(catalog['Products'].keys())
        for product_key in product_keys:
            munkicommon.display_status(
                'Caching metadata for product ID %s', product_key)
            product = catalog['Products'][product_key]
            if 'ServerMetadataURL' in product:
                unused_path = replicateURLtoFilesystem(
                    product['ServerMetadataURL'],
                    copy_only_if_missing=True)

            for package in product.get('Packages', []):
                ### not replicating the packages themselves ###
                #if 'URL' in package:
                #    unused_path = replicateURLtoFilesystem(
                #        package['URL'],
                #        copy_only_if_missing=fast_scan)
                if 'MetadataURL' in package:
                    munkicommon.display_status(
                        'Caching package metadata for product ID %s',
                         product_key)
                    unused_path = replicateURLtoFilesystem(
                        package['MetadataURL'],
                        copy_only_if_missing=True)

            distributions = product['Distributions']
            for dist_lang in distributions.keys():
                munkicommon.display_status(
                    'Caching %s distribution for product ID %s',
                    dist_lang, product_key)
                dist_url = distributions[dist_lang]
                unused_path = replicateURLtoFilesystem(
                    dist_url,
                    copy_only_if_missing=True)

        # rewrite URLs to point to local resources
        rewriteURLs(catalog, rewrite_pkg_urls=False)
        # write out the rewritten catalog
        localcatalogpath = os.path.join(swupdCacheDir(),
                                        'content', 'catalogs')
        if not os.path.exists(localcatalogpath):
            try:
                os.makedirs(localcatalogpath)
            except OSError, oserr:
                raise ReplicationError(oserr)
        localcatalogpathname = os.path.join(localcatalogpath,
                                            'local_download.sucatalog')
        FoundationPlist.writePlist(catalog, localcatalogpathname)

        rewriteURLs(catalog, rewrite_pkg_urls=True)
        localcatalogpathname = os.path.join(localcatalogpath,
                                            'local_install.sucatalog')
        FoundationPlist.writePlist(catalog, localcatalogpathname)
Exemple #13
0
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('Removing receipt info')
    local_display_percent_done(0, 4)
    
    conn = sqlite3.connect(packagedb)
    curs = conn.cursor()
    
    osvers = int(os.uname()[2].split('.')[0])
    
    applepkgdb = '/Library/Receipts/db/a.receiptdb'
    if not noupdateapplepkgdb and osvers < 10:
        aconn = sqlite3.connect(applepkgdb)
        acurs = aconn.cursor()
    
    local_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 osvers < 10:
                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 osvers < 10:
                # 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'))
                    
    local_display_percent_done(2, 4)
    
    # now remove orphaned paths from paths table
    # first, Apple's database if option is passed
    if not noupdateapplepkgdb:
        if osvers < 10:
            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()
    
    local_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()
    
    local_display_percent_done(4, 4)
Exemple #14
0
def initDatabase(forcerebuild=False):
    """
    Builds or rebuilds our internal package database.
    """
    if not shouldRebuildDB(packagedb) and not forcerebuild:
        return True
        
    munkicommon.display_status('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
            
    osvers = int(os.uname()[2].split('.')[0])
    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 osvers > 9:
        # 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
    local_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
                local_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
                local_display_percent_done(currentpkgindex, pkgcount)
    if osvers > 9:
        # 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
            local_display_percent_done(currentpkgindex, pkgcount)
            
    # in case we didn't quite get to 100% for some reason
    if currentpkgindex < pkgcount:
        local_display_percent_done(pkgcount, pkgcount)
        
    # commit and close the db when we're done.
    conn.commit()
    curs.close()
    conn.close()
    return True
Exemple #15
0
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("Removing %s filesystem items" % removalcount)

    itemcount = len(removalpaths)
    itemindex = 0
    local_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
Exemple #16
0
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('Removing receipt info')
    local_display_percent_done(0, 4)

    conn = sqlite3.connect(packagedb)
    curs = conn.cursor()

    osvers = int(os.uname()[2].split('.')[0])

    applepkgdb = '/Library/Receipts/db/a.receiptdb'
    if not noupdateapplepkgdb and osvers < 10:
        aconn = sqlite3.connect(applepkgdb)
        acurs = aconn.cursor()

    local_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 osvers < 10:
                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 osvers < 10:
                # 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'))

    local_display_percent_done(2, 4)

    # now remove orphaned paths from paths table
    # first, Apple's database if option is passed
    if not noupdateapplepkgdb:
        if osvers < 10:
            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()

    local_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()

    local_display_percent_done(4, 4)
Exemple #17
0
def installAppleUpdates():
    '''Uses /usr/sbin/softwareupdate to install previously
    downloaded updates. Returns True if a restart is needed
    after install, False otherwise.'''
    msg = "Installing available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)
    restartneeded = restartNeeded()
    # use our filtered local catalog
    catalogpath = os.path.join(swupdCacheDir(),
                               'content/catalogs/local_install.sucatalog')
    if not os.path.exists(catalogpath):
        munkicommon.display_error(
            'Missing local Software Update catalog at %s', catalogpath)
        # didn't do anything, so no restart needed
        return False

    installlist = getSoftwareUpdateInfo()
    installresults = {'installed': [], 'download': []}

    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    retcode = run_softwareupdate(['--CatalogURL', catalogURL, '-i', '-a'],
                                 mode='install',
                                 results=installresults)

    if not 'InstallResults' in munkicommon.report:
        munkicommon.report['InstallResults'] = []

    for item in installlist:
        rep = {}
        rep['name'] = item.get('display_name')
        rep['version'] = item.get('version_to_install', '')
        rep['applesus'] = True
        rep['productKey'] = item.get('productKey', '')
        message = "Apple Software Update install of %s-%s: %s"
        if rep['name'] in installresults['installed']:
            rep['status'] = 0
            install_status = 'SUCCESSFUL'
        elif rep['name'] in installresults['download']:
            rep['status'] = -1
            install_status = 'FAILED due to missing package.'
            munkicommon.display_warning(
                'Apple update %s, %s failed. A sub-package was missing '
                'on disk at time of install.' %
                (rep['name'], rep['productKey']))
        else:
            rep['status'] = -2
            install_status = 'FAILED for unknown reason'
            munkicommon.display_warning(
                'Apple update %s, %s failed to install. No record of '
                'success or failure.' % (rep['name'], rep['productKey']))

        munkicommon.report['InstallResults'].append(rep)
        log_msg = message % (rep['name'], rep['version'], install_status)
        munkicommon.log(log_msg, "Install.log")

    if retcode:
        # there was an error
        munkicommon.display_error("softwareupdate error: %s" % retcode)
    # clean up our now stale local cache
    cachedir = os.path.join(swupdCacheDir())
    if os.path.exists(cachedir):
        unused_retcode = subprocess.call(['/bin/rm', '-rf', cachedir])
    # remove the now invalid appleUpdatesFile
    try:
        os.unlink(appleUpdatesFile())
    except OSError:
        pass
    # Also clear our pref value for last check date. We may have
    # just installed an update which is a pre-req for some other update.
    # Let's check again soon.
    munkicommon.set_pref('LastAppleSoftwareUpdateCheck', None)

    return restartneeded
Exemple #18
0
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("Removing %s filesystem items" % removalcount)
    
    itemcount = len(removalpaths)
    itemindex = 0
    local_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
Exemple #19
0
 elif output.startswith('Software Update Tool'):
     # don't display this
     pass
 elif output.startswith('Copyright 2'):
     # don't display this
     pass
 elif output.startswith('Installing ') and mode == 'install':
     item = output[11:]
     if item:
         if munkicommon.munkistatusoutput:
             munkistatus.message(output)
             munkistatus.detail("")
             munkistatus.percent(-1)
             munkicommon.log(output)
         else:
             munkicommon.display_status(output)
 elif output.startswith('Installed '):
     # 10.6 / 10.7. Successful install of package name.
     if mode == 'install':
         munkicommon.display_status(output)
         results['installed'].append(output[10:])
     else:
         pass
         # don't display.
         # softwareupdate logging "Installed" at the end of a
         # successful download-only session is odd.
 elif output.startswith('Done '):
     # 10.5. Successful install of package name.
     munkicommon.display_status(output)
     results['installed'].append(output[5:])
 elif output.startswith('Downloading ') and mode == 'install':
Exemple #20
0
def initDatabase(forcerebuild=False):
    """
    Builds or rebuilds our internal package database.
    """
    if not shouldRebuildDB(packagedb) and not forcerebuild:
        return True

    munkicommon.display_status('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

    osvers = int(os.uname()[2].split('.')[0])
    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 osvers > 9:
        # 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
    local_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
                local_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
                local_display_percent_done(currentpkgindex, pkgcount)
    if osvers > 9:
        # 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
            local_display_percent_done(currentpkgindex, pkgcount)

    # in case we didn't quite get to 100% for some reason
    if currentpkgindex < pkgcount:
        local_display_percent_done(pkgcount, pkgcount)

    # commit and close the db when we're done.
    conn.commit()
    curs.close()
    conn.close()
    return True