Exemplo n.º 1
0
def appleSoftwareUpdatesAvailable(forcecheck=False, suppresscheck=False):
    '''Checks for available Apple Software Updates, trying not to hit the SUS
    more than needed'''
    if suppresscheck:
        # typically because we're doing a logout install; if
        # there are no waiting Apple Updates we shouldn't
        # trigger a check for them.
        pass
    elif forcecheck:
        # typically because user initiated the check from
        # Managed Software Update.app
        unused_retcode = checkForSoftwareUpdates(forcecheck=True)
    else:
        # have we checked recently?  Don't want to check with
        # Apple Software Update server too frequently
        now = NSDate.new()
        nextSUcheck = now
        lastSUcheckString = munkicommon.pref('LastAppleSoftwareUpdateCheck')
        if lastSUcheckString:
            try:
                lastSUcheck = NSDate.dateWithString_(lastSUcheckString)
                interval = 24 * 60 * 60
                nextSUcheck = lastSUcheck.dateByAddingTimeInterval_(interval)
            except (ValueError, TypeError):
                pass
        if now.timeIntervalSinceDate_(nextSUcheck) >= 0:
            unused_retcode = checkForSoftwareUpdates(forcecheck=True)
        else:
            unused_retcode = checkForSoftwareUpdates(forcecheck=False)

    if writeAppleUpdatesFile():
        displayAppleUpdateInfo()
        return True
    else:
        return False
Exemplo n.º 2
0
    def addPayloadFromPlistContents(self, plist_dict, domain, manage, is_byhost=False):
        """Add one plist dict contents to the profile's payloads. domain is the
        preferences domain (ie. com.apple.finder), manage is one of 'Once', 'Often' or 'Always',
        and is_byhost is a boolean representing whether the preference is to be used as a ByHost.
        """

        payload_dict = {}

        # Frequency to apply settings, or 'state'
        if manage == 'Always':
            state = 'Forced'
        else:
            state = 'Set-Once'

        if is_byhost:
            domain += '.ByHost'

        # Yet another nested dict for the actual contents
        payload_dict[domain] = {}
        payload_dict[domain][state] = []
        payload_dict[domain][state].append({})
        payload_dict[domain][state][0]['mcx_preference_settings'] = plist_dict

        # Add a datestamp if we're managing 'Once'
        if manage == 'Once':
            now = NSDate.new()
            payload_dict[domain][state][0]['mcx_data_timestamp'] = now

        self._addPayload(payload_dict)
Exemplo n.º 3
0
def format_time(timestamp=None):
    """Return timestamp as an ISO 8601 formatted string, in the current
    timezone.
    If timestamp isn't given the current time is used."""
    if timestamp is None:
        return str(NSDate.new())
    return str(NSDate.dateWithTimeIntervalSince1970_(timestamp))
Exemplo n.º 4
0
    def addPayloadFromPlistContents(self,
                                    plist_dict,
                                    domain,
                                    manage,
                                    is_byhost=False):
        """Add one plist dict contents to the profile's payloads. domain is the
        preferences domain (ie. com.apple.finder), manage is one of 'Once', 'Often' or 'Always',
        and is_byhost is a boolean representing whether the preference is to be used as a ByHost.
        """

        payload_dict = {}

        # Frequency to apply settings, or 'state'
        if manage == 'Always':
            state = 'Forced'
        else:
            state = 'Set-Once'

        if is_byhost:
            domain += '.ByHost'

        # Yet another nested dict for the actual contents
        payload_dict[domain] = {}
        payload_dict[domain][state] = []
        payload_dict[domain][state].append({})
        payload_dict[domain][state][0]['mcx_preference_settings'] = plist_dict

        # Add a datestamp if we're managing 'Once'
        if manage == 'Once':
            now = NSDate.new()
            payload_dict[domain][state][0]['mcx_data_timestamp'] = now

        self._addPayload(payload_dict)
Exemplo n.º 5
0
Arquivo: au.py Projeto: munki/munki
    def software_updates_available(
            self, force_check=False, suppress_check=False):
        """Checks for available Apple Software Updates, trying not to hit the
        SUS more than needed.

        Args:
          force_check: Boolean. If True, forces a softwareupdate run.
          suppress_check: Boolean. If True, skips a softwareupdate run.
        Returns:
          Integer. Count of available Apple updates.
        """
        success = True
        if suppress_check:
            # don't check at all --
            # typically because we are doing a logout install
            # just return any AppleUpdates info we already have
            if not os.path.exists(self.apple_updates_plist):
                return 0
            try:
                plist = FoundationPlist.readPlist(self.apple_updates_plist)
            except FoundationPlist.FoundationPlistException:
                plist = {}
            return len(plist.get('AppleUpdates', []))
        if force_check:
            # typically because user initiated the check from
            # Managed Software Update.app
            success = self.check_for_software_updates(force_check=True)
        else:
            # have we checked recently?  Don't want to check with
            # Apple Software Update server too frequently
            now = NSDate.new()
            next_su_check = now
            last_su_check_string = prefs.pref(
                'LastAppleSoftwareUpdateCheck')
            if last_su_check_string:
                try:
                    last_su_check = NSDate.dateWithString_(
                        last_su_check_string)
                    # dateWithString_ returns None if invalid date string.
                    if not last_su_check:
                        raise ValueError
                    interval = 24 * 60 * 60
                    # only force check every 24 hours.
                    next_su_check = last_su_check.dateByAddingTimeInterval_(
                        interval)
                except (ValueError, TypeError):
                    pass
            if now.timeIntervalSinceDate_(next_su_check) >= 0:
                success = self.check_for_software_updates(force_check=True)
            else:
                success = self.check_for_software_updates(force_check=False)
        display.display_debug1(
            'CheckForSoftwareUpdates result: %s' % success)
        if success:
            count = self.write_appleupdates_file()
        else:
            self.clear_apple_update_info()
            return 0
        return count
Exemplo n.º 6
0
    def software_updates_available(
            self, force_check=False, suppress_check=False):
        """Checks for available Apple Software Updates, trying not to hit the
        SUS more than needed.

        Args:
          force_check: Boolean. If True, forces a softwareupdate run.
          suppress_check: Boolean. If True, skips a softwareupdate run.
        Returns:
          Integer. Count of available Apple updates.
        """
        success = True
        if suppress_check:
            # don't check at all --
            # typically because we are doing a logout install
            # just return any AppleUpdates info we already have
            if not os.path.exists(self.apple_updates_plist):
                return 0
            try:
                plist = FoundationPlist.readPlist(self.apple_updates_plist)
            except FoundationPlist.FoundationPlistException:
                plist = {}
            return len(plist.get('AppleUpdates', []))
        if force_check:
            # typically because user initiated the check from
            # Managed Software Update.app
            success = self.check_for_software_updates(force_check=True)
        else:
            # have we checked recently?  Don't want to check with
            # Apple Software Update server too frequently
            now = NSDate.new()
            next_su_check = now
            last_su_check_string = prefs.pref(
                'LastAppleSoftwareUpdateCheck')
            if last_su_check_string:
                try:
                    last_su_check = NSDate.dateWithString_(
                        last_su_check_string)
                    # dateWithString_ returns None if invalid date string.
                    if not last_su_check:
                        raise ValueError
                    interval = 24 * 60 * 60
                    # only force check every 24 hours.
                    next_su_check = last_su_check.dateByAddingTimeInterval_(
                        interval)
                except (ValueError, TypeError):
                    pass
            if now.timeIntervalSinceDate_(next_su_check) >= 0:
                success = self.check_for_software_updates(force_check=True)
            else:
                success = self.check_for_software_updates(force_check=False)
        display.display_debug1(
            'CheckForSoftwareUpdates result: %s' % success)
        if success:
            count = self.write_appleupdates_file()
        else:
            self.clear_apple_update_info()
            return 0
        return count
Exemplo n.º 7
0
def format_time(timestamp=None):
    """Return timestamp as an ISO 8601 formatted string, in the current
    timezone.
    If timestamp isn't given the current time is used."""
    if timestamp is None:
        return str(NSDate.new())
    else:
        return str(NSDate.dateWithTimeIntervalSince1970_(timestamp))
Exemplo n.º 8
0
def make_pkginfo_metadata():
    '''Records information about the environment in which the pkginfo was
created so we have a bit of an audit trail. Returns a dictionary.'''
    metadata = {}
    metadata['created_by'] = NSUserName()
    metadata['creation_date'] = NSDate.new()
    metadata['munki_version'] = info.get_version()
    metadata['os_version'] = osutils.getOsVersion(only_major_minor=False)
    return metadata
Exemplo n.º 9
0
def make_pkginfo_metadata():
    '''Records information about the environment in which the pkginfo was
created so we have a bit of an audit trail. Returns a dictionary.'''
    metadata = {}
    metadata['created_by'] = NSUserName()
    metadata['creation_date'] = NSDate.new()
    metadata['munki_version'] = info.get_version()
    metadata['os_version'] = osutils.getOsVersion(only_major_minor=False)
    return metadata
Exemplo n.º 10
0
    def software_updates_available(self,
                                   force_check=False,
                                   suppress_check=False):
        """Checks for available Apple Software Updates, trying not to hit the
        SUS more than needed.

        Args:
          force_check: Boolean. If True, forces a softwareupdate run.
          suppress_check: Boolean. If True, skips a softwareupdate run.
        Returns:
          Integer. Count of available Apple updates.
        """
        if suppress_check:
            # don't check at all --
            # typically because we are doing a logout install
            # just return any AppleUpdates info we already have
            return self.cached_update_count()
        if force_check:
            # typically because user initiated the check from
            # Managed Software Update.app
            updatecount = self.check_for_software_updates(force_check=True)
        else:
            # have we checked recently?  Don't want to check with
            # Apple Software Update server too frequently
            now = NSDate.new()
            next_su_check = now
            last_su_check_string = prefs.pref('LastAppleSoftwareUpdateCheck')
            if last_su_check_string:
                try:
                    last_su_check = NSDate.dateWithString_(
                        last_su_check_string)
                    # dateWithString_ returns None if invalid date string.
                    if not last_su_check:
                        raise ValueError
                    interval = 24 * 60 * 60
                    # only force check every 24 hours.
                    next_su_check = last_su_check.dateByAddingTimeInterval_(
                        interval)
                except (ValueError, TypeError):
                    pass
            if now.timeIntervalSinceDate_(next_su_check) >= 0:
                updatecount = self.check_for_software_updates(force_check=True)
            else:
                updatecount = self.check_for_software_updates(
                    force_check=False)
        display.display_debug1('CheckForSoftwareUpdates result: %s' %
                               updatecount)
        if updatecount == -1:
            # some (transient?) communications error with the su server; return
            # cached AppleInfo
            return self.cached_update_count()
        elif updatecount == 0:
            self.clear_apple_update_info()
        else:
            _ = self.write_appleupdates_file()
        return updatecount
Exemplo n.º 11
0
 def _no_nudge_all_time(self):
     if (self.time_between_notif > 0 and self.first_seen
             and self.last_seen):
         difference = _last_seen_vs_now(self.last_seen)
         if difference.seconds < self.time_between_notif:
             info = 'Last seen date is within notification threshold'
             nudgelog(f'{info}: {str(self.time_between_notif)} seconds')
             sys.exit(0)
     if not self.first_seen:
         set_app_pref('first_seen', NSDate.new())
         self.first_seen = app_pref('first_seen')
Exemplo n.º 12
0
def appleSoftwareUpdatesAvailable(forcecheck=False, suppresscheck=False):
    '''Checks for available Apple Software Updates, trying not to hit the SUS
    more than needed'''
    if suppresscheck:
        # typically because we're doing a logout install; if
        # there are no waiting Apple Updates we shouldn't
        # trigger a check for them.
        pass
    elif forcecheck:
        # typically because user initiated the check from
        # Managed Software Update.app
        unused_retcode = checkForSoftwareUpdates(forcecheck=True)
    else:
        # have we checked recently?  Don't want to check with
        # Apple Software Update server too frequently
        now = NSDate.new()
        nextSUcheck = now
        lastSUcheckString = munkicommon.pref('LastAppleSoftwareUpdateCheck')
        if lastSUcheckString:
            try:
                lastSUcheck = NSDate.dateWithString_(lastSUcheckString)
                interval = 24 * 60 * 60
                nextSUcheck = lastSUcheck.dateByAddingTimeInterval_(interval)
            except (ValueError, TypeError):
                pass
        if now.timeIntervalSinceDate_(nextSUcheck) >= 0:
            unused_retcode = checkForSoftwareUpdates(forcecheck=True)
        else:
            unused_retcode = checkForSoftwareUpdates(forcecheck=False)


    if writeAppleUpdatesFile():
        displayAppleUpdateInfo()
        return True
    else:
        return False
Exemplo n.º 13
0
 def run(self):
     '''Function that will run nudge experience'''
     set_app_pref('last_seen', NSDate.new())
     self.nudge.hidden = True
     self.nudge.run()
Exemplo n.º 14
0
    def install_apple_updates(self, only_unattended=False):
        """Uses softwareupdate to install previously downloaded updates.

        Returns:
          Boolean. True if a restart is needed after install, False otherwise.
        """
        # disable Stop button if we are presenting GUI status
        if display.munkistatusoutput:
            munkistatus.hideStopButton()

        # Get list of unattended_installs
        if only_unattended:
            msg = 'Installing unattended Apple Software Updates...'
            unattended_install_items, unattended_install_product_ids = \
                self.get_unattended_installs()
            # ensure that we don't restart for unattended installations
            restartneeded = False
            if not unattended_install_items:
                return False  # didn't find any unattended installs
        else:
            msg = 'Installing available Apple Software Updates...'
            restartneeded = self.restart_needed()

        self._display_status_major(msg)

        installlist = self.software_update_info()
        remaining_apple_updates = []
        installresults = {'installed': [], 'download': []}

        su_options = ['-i']

        if only_unattended:
            # Append list of unattended_install items
            su_options.extend(unattended_install_items)
            # Filter installist to only include items
            # which we're attempting to install
            filtered_installlist = [item for item in installlist
                                    if item.get('productKey') in
                                    unattended_install_product_ids]
            # record items we aren't planning to attempt to install
            remaining_apple_updates = [item for item in installlist
                                       if item not in filtered_installlist]
            installlist = filtered_installlist

        else:
            # We're installing all available updates; add all their names
            for item in installlist:
                su_options.append(
                    item['name'] + '-' + item['version_to_install'])

        # new in 10.11: '--no-scan' flag to tell softwareupdate to just install
        # and not rescan for available updates.
        os_version_tuple = osutils.getOsVersion(as_tuple=True)
        if os_version_tuple >= (10, 11):
            su_options.append('--no-scan')
            # 10.11 seems not to like file:// URLs, and we don't really need
            # to switch to a local file URL anyway since we now have the
            # --no-scan option
            catalog_url = None
        else:
            # use our local catalog
            if not os.path.exists(self.applesync.local_catalog_path):
                display.display_error(
                    'Missing local Software Update catalog at %s',
                    self.applesync.local_catalog_path)
                return False  # didn't do anything, so no restart needed
            catalog_url = 'file://localhost' + urllib2.quote(
                self.applesync.local_catalog_path)

        retcode = self._run_softwareupdate(
            su_options, mode='install', catalog_url=catalog_url,
            results=installresults)
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        display.display_debug1(
            'Raw Apple Update install results: %s', installresults)
        for item in installlist:
            rep = {}
            rep['name'] = item.get('apple_product_name')
            rep['version'] = item.get('version_to_install', '')
            rep['applesus'] = True
            rep['time'] = NSDate.new()
            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 ('display_name' in item and
                  item['display_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.'
                display.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'
                display.display_warning(
                    'Apple update %s, %s may have failed to install. No record '
                    'of success or failure.', rep['name'], rep['productKey'])
                if installresults['installed']:
                    display.display_warning(
                        'softwareupdate recorded these installations: %s',
                        installresults['installed'])

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

        if retcode:  # there was an error
            display.display_error('softwareupdate error: %s' % retcode)

        if not remaining_apple_updates:
            # clean up our now stale local cache
            self.applesync.clean_up_cache()
            # remove the now invalid AppleUpdates.plist
            self.clear_apple_update_info()
        else:
            # we installed some of the updates, but some are still uninstalled.
            # re-write the apple_update_info to match
            plist = {'AppleUpdates': remaining_apple_updates}
            FoundationPlist.writePlist(plist, self.apple_updates_plist)
            #TODO: clean up cached items we no longer need

        # 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.
        prefs.set_pref('LastAppleSoftwareUpdateCheck', None)

        # show stop button again
        if display.munkistatusoutput:
            munkistatus.showStopButton()

        return restartneeded
Exemplo n.º 15
0
def process_removals(removallist, only_unattended=False):
    '''processes removals from the removal list'''
    restart_flag = False
    index = 0
    skipped_removals = []
    for item in removallist:
        if only_unattended:
            if not item.get('unattended_uninstall'):
                skipped_removals.append(item)
                display.display_detail(
                    ('Skipping removal of %s because it\'s not unattended.'
                     % item['name']))
                continue
            elif processes.blocking_applications_running(item):
                skipped_removals.append(item)
                display.display_detail(
                    'Skipping unattended removal of %s because '
                    'blocking application(s) running.' % item['name'])
                continue

        dependent_skipped_items = skipped_items_that_require_this(
            item, skipped_removals)
        if dependent_skipped_items:
            # need to skip this too
            skipped_removals.append(item)
            display.display_detail(
                'Skipping removal of %s because these '
                'skipped items required it: %s'
                % (item['name'], ", ".join(dependent_skipped_items)))
            continue

        if processes.stop_requested():
            return restart_flag, skipped_removals
        if not item.get('installed'):
            # not installed, so skip it (this shouldn't happen...)
            continue

        index += 1
        display_name = item.get('display_name') or item.get('name')
        display.display_status_major(
            "Removing %s (%s of %s)...", display_name, index, len(removallist))

        retcode = 0
        # run preuninstall_script if it exists
        if 'preuninstall_script' in item:
            retcode = scriptutils.run_embedded_script(
                'preuninstall_script', item)

        if retcode == 0 and 'uninstall_method' in item:
            uninstallmethod = item['uninstall_method']
            if uninstallmethod == "removepackages":
                if 'packages' in item:
                    restart_flag = requires_restart(item)
                    retcode = rmpkgs.removepackages(item['packages'],
                                                    forcedeletebundles=True)
                    if retcode:
                        if retcode == -128:
                            message = (
                                "Uninstall of %s was cancelled." % display_name)
                        else:
                            message = "Uninstall of %s failed." % display_name
                        display.display_error(message)
                    else:
                        munkilog.log(
                            "Uninstall of %s was successful." % display_name)

            elif uninstallmethod.startswith("Adobe"):
                retcode = adobeutils.do_adobe_removal(item)

            elif uninstallmethod == "remove_copied_items":
                retcode = remove_copied_items(item.get('items_to_remove'))

            elif uninstallmethod == "remove_app":
                # deprecated with appdmg!
                remove_app_info = item.get('remove_app_info', None)
                if remove_app_info:
                    path_to_remove = remove_app_info['path']
                    display.display_status_minor(
                        'Removing %s' % path_to_remove)
                    retcode = subprocess.call(
                        ["/bin/rm", "-rf", path_to_remove])
                    if retcode:
                        display.display_error(
                            "Removal error for %s", path_to_remove)
                else:
                    display.display_error(
                        "Application removal info missing from %s",
                        display_name)

            elif uninstallmethod == 'remove_profile':
                identifier = item.get('PayloadIdentifier')
                if identifier:
                    retcode = 0
                    if not profiles.remove_profile(identifier):
                        retcode = -1
                        display.display_error(
                            "Profile removal error for %s", identifier)
                else:
                    display.display_error(
                        "Profile removal info missing from %s", display_name)

            elif uninstallmethod == 'uninstall_script':
                retcode = scriptutils.run_embedded_script(
                    'uninstall_script', item)
                if retcode == 0 and requires_restart(item):
                    restart_flag = True

            elif (os.path.exists(uninstallmethod) and
                  os.access(uninstallmethod, os.X_OK)):
                # it's a script or program to uninstall
                retcode = scriptutils.run_script(
                    display_name, uninstallmethod, 'uninstall script')
                if retcode == 0 and requires_restart(item):
                    restart_flag = True

            else:
                munkilog.log("Uninstall of %s failed because there was no "
                             "valid uninstall method." % display_name)
                retcode = -99

            if retcode == 0 and item.get('postuninstall_script'):
                retcode = scriptutils.run_embedded_script(
                    'postuninstall_script', item)
                if retcode:
                    # we won't consider postuninstall script failures as fatal
                    # since the item has been uninstalled
                    # but admin should be notified
                    display.display_warning(
                        'Postuninstall script for %s returned %s'
                        % (item['name'], retcode))
                    # reset retcode to 0 so we will mark this uninstall
                    # as successful
                    retcode = 0

        # record removal success/failure
        if not 'RemovalResults' in reports.report:
            reports.report['RemovalResults'] = []
        if retcode == 0:
            success_msg = "Removal of %s: SUCCESSFUL" % display_name
            munkilog.log(success_msg, "Install.log")
            remove_from_selfserve_uninstalls(item['name'])
        else:
            failure_msg = "Removal of %s: " % display_name + \
                          " FAILED with return code: %s" % retcode
            munkilog.log(failure_msg, "Install.log")
            # append failed removal to skipped_removals so dependencies
            # aren't removed yet.
            skipped_removals.append(item)
        removal_result = {
            'display_name': display_name,
            'name': item['name'],
            'status': retcode,
            'time': NSDate.new(),
            'unattended': only_unattended,
        }
        reports.report['RemovalResults'].append(removal_result)

    return (restart_flag, skipped_removals)
Exemplo n.º 16
0
def install_with_info(
        dirpath, installlist, only_unattended=False, applesus=False):
    """
    Uses the installlist to install items in the
    correct order.
    """
    restartflag = False
    itemindex = 0
    skipped_installs = []
    for item in installlist:
        # Keep track of when this particular install started.
        utc_now = datetime.datetime.utcnow()
        itemindex = itemindex + 1

        if item.get('installer_type') == 'startosinstall':
            skipped_installs.append(item)
            display.display_debug1(
                'Skipping install of %s because it\'s a startosinstall item. '
                'Will install later.' % item['name'])
            continue
        if only_unattended:
            if not item.get('unattended_install'):
                skipped_installs.append(item)
                display.display_detail(
                    'Skipping install of %s because it\'s not unattended.'
                    % item['name'])
                continue
            elif processes.blocking_applications_running(item):
                skipped_installs.append(item)
                display.display_detail(
                    'Skipping unattended install of %s because blocking '
                    'application(s) running.' % item['name'])
                continue

        skipped_prereqs = item_prereqs_in_skipped_items(item, skipped_installs)
        if skipped_prereqs:
            # one or more prerequisite for this item was skipped or failed;
            # need to skip this item too
            skipped_installs.append(item)
            if only_unattended:
                format_str = ('Skipping unattended install of %s because these '
                              'prerequisites were skipped: %s')
            else:
                format_str = ('Skipping install of %s because these '
                              'prerequisites were not installed: %s')
            display.display_detail(
                format_str % (item['name'], ", ".join(skipped_prereqs)))
            continue

        if processes.stop_requested():
            return restartflag, skipped_installs

        display_name = item.get('display_name') or item.get('name')
        version_to_install = item.get('version_to_install', '')

        retcode = 0
        if 'preinstall_script' in item:
            retcode = scriptutils.run_embedded_script('preinstall_script', item)

        if retcode == 0 and 'installer_item' in item:
            display.display_status_major(
                "Installing %s (%s of %s)"
                % (display_name, itemindex, len(installlist)))

            installer_type = item.get("installer_type", "")

            itempath = os.path.join(dirpath, item["installer_item"])
            if installer_type != "nopkg" and not os.path.exists(itempath):
                # can't install, so we should stop. Since later items might
                # depend on this one, we shouldn't continue
                display.display_error(
                    "Installer item %s was not found.", item["installer_item"])
                return restartflag, skipped_installs
            # Adobe installs
            if installer_type.startswith("Adobe"):
                retcode = adobeutils.do_adobe_install(item)
                if retcode == 0 and requires_restart(item):
                    restartflag = True
                if retcode == 8:
                    # Adobe Setup says restart needed.
                    restartflag = True
                    retcode = 0
            # copy_from_dmg install
            elif installer_type == "copy_from_dmg":
                retcode = dmg.copy_from_dmg(itempath, item.get('items_to_copy'))
                if retcode == 0 and requires_restart(item):
                    restartflag = True
            # appdmg install (depercated)
            elif installer_type == "appdmg":
                display.display_warning(
                    "install_type 'appdmg' is deprecated. Use 'copy_from_dmg'.")
                retcode = dmg.copy_app_from_dmg(itempath)
            # configuration profile install
            elif installer_type == 'profile':
                # profiles.install_profile returns True/False
                retcode = 0
                identifier = item.get('PayloadIdentifier')
                if not profiles.install_profile(itempath, identifier):
                    retcode = -1
            # nopkg (Packageless) install
            elif installer_type == "nopkg":
                restartflag = requires_restart(item)
            # unknown installer_type
            elif installer_type != "":
                # we've encountered an installer type
                # we don't know how to handle
                display.display_error(
                    "Unsupported install type: %s" % installer_type)
                retcode = -99
            # better be Apple installer package
            else:
                (retcode, need_to_restart) = handle_apple_package_install(
                    item, itempath)
                if need_to_restart:
                    restartflag = True

            if processes.stop_requested():
                return restartflag, skipped_installs

        # install succeeded. Do we have a postinstall_script?
        if retcode == 0 and 'postinstall_script' in item:
            # only run embedded postinstall script if the install did not
            # return a failure code
            retcode = scriptutils.run_embedded_script(
                'postinstall_script', item)
            if retcode:
                # we won't consider postinstall script failures as fatal
                # since the item has been installed via package/disk image
                # but admin should be notified
                display.display_warning(
                    'Postinstall script for %s returned %s'
                    % (item['name'], retcode))
                # reset retcode to 0 so we will mark this install
                # as successful
                retcode = 0

        # if install was successful and this is a SelfService OnDemand install
        # remove the item from the SelfServeManifest's managed_installs
        if retcode == 0 and item.get('OnDemand'):
            remove_from_selfserve_installs(item['name'])

        # record install success/failure
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        if applesus:
            message = "Apple SUS install of %s-%s: %s"
        else:
            message = "Install of %s-%s: %s"

        if retcode == 0:
            status = "SUCCESSFUL"
        else:
            status = "FAILED with return code: %s" % retcode
            # add this failed install to the skipped_installs list
            # so that any item later in the list that requires this
            # item is skipped as well.
            skipped_installs.append(item)

        log_msg = message % (display_name, version_to_install, status)
        munkilog.log(log_msg, "Install.log")

        # Calculate install duration; note, if a machine is put to sleep
        # during the install this time may be inaccurate.
        utc_now_complete = datetime.datetime.utcnow()
        duration_seconds = (utc_now_complete - utc_now).seconds

        download_speed = item.get('download_kbytes_per_sec', 0)
        install_result = {
            'display_name': display_name,
            'name': item['name'],
            'version': version_to_install,
            'applesus': applesus,
            'status': retcode,
            'time': NSDate.new(),
            'duration_seconds': duration_seconds,
            'download_kbytes_per_sec': download_speed,
            'unattended': only_unattended,
        }
        reports.report['InstallResults'].append(install_result)

        # check to see if this installer item is needed by any additional
        # items in installinfo
        # this might happen if there are multiple things being installed
        # with choicesXML files applied to a metapackage or
        # multiple packages being installed from a single DMG
        foundagain = False
        current_installer_item = item['installer_item']
        # are we at the end of the installlist?
        # (we already incremented itemindex for display
        # so with zero-based arrays itemindex now points to the item
        # after the current item)
        if itemindex < len(installlist):
            # nope, let's check the remaining items
            for lateritem in installlist[itemindex:]:
                if (lateritem.get('installer_item') ==
                        current_installer_item):
                    foundagain = True
                    break

        # need to check skipped_installs as well
        if not foundagain:
            for skipped_item in skipped_installs:
                if (skipped_item.get('installer_item') ==
                        current_installer_item):
                    foundagain = True
                    break

        # ensure package is not deleted from cache if installation
        # fails by checking retcode
        if not foundagain and retcode == 0:
            # now remove the item from the install cache
            # (if it's still there)
            itempath = os.path.join(dirpath, current_installer_item)
            if os.path.exists(itempath):
                if os.path.isdir(itempath):
                    retcode = subprocess.call(["/bin/rm", "-rf", itempath])
                else:
                    # flat pkg or dmg
                    retcode = subprocess.call(["/bin/rm", itempath])
                    if pkgutils.hasValidDiskImageExt(itempath):
                        shadowfile = os.path.join(itempath, ".shadow")
                        if os.path.exists(shadowfile):
                            retcode = subprocess.call(["/bin/rm", shadowfile])

    return (restartflag, skipped_installs)
Exemplo n.º 17
0
def process_removals(removallist, only_unattended=False):
    '''processes removals from the removal list'''
    restart_flag = False
    index = 0
    skipped_removals = []
    for item in removallist:
        if only_unattended:
            if not item.get('unattended_uninstall'):
                skipped_removals.append(item)
                display.display_detail(
                    ('Skipping removal of %s because it\'s not unattended.' %
                     item['name']))
                continue
            elif processes.blocking_applications_running(item):
                skipped_removals.append(item)
                display.display_detail(
                    'Skipping unattended removal of %s because '
                    'blocking application(s) running.' % item['name'])
                continue

        dependent_skipped_items = skipped_items_that_require_this(
            item, skipped_removals)
        if dependent_skipped_items:
            # need to skip this too
            skipped_removals.append(item)
            display.display_detail(
                'Skipping removal of %s because these '
                'skipped items required it: %s' %
                (item['name'], ", ".join(dependent_skipped_items)))
            continue

        if processes.stop_requested():
            return restart_flag, skipped_removals
        if not item.get('installed'):
            # not installed, so skip it (this shouldn't happen...)
            continue

        index += 1
        display_name = item.get('display_name') or item.get('name')
        display.display_status_major("Removing %s (%s of %s)...", display_name,
                                     index, len(removallist))

        retcode = 0
        # run preuninstall_script if it exists
        if 'preuninstall_script' in item:
            retcode = scriptutils.run_embedded_script('preuninstall_script',
                                                      item)

        if retcode == 0 and 'uninstall_method' in item:
            uninstallmethod = item['uninstall_method']
            if uninstallmethod == "removepackages":
                if 'packages' in item:
                    restart_flag = requires_restart(item)
                    retcode = rmpkgs.removepackages(item['packages'],
                                                    forcedeletebundles=True)
                    if retcode:
                        if retcode == -128:
                            message = ("Uninstall of %s was cancelled." %
                                       display_name)
                        else:
                            message = "Uninstall of %s failed." % display_name
                        display.display_error(message)
                    else:
                        munkilog.log("Uninstall of %s was successful." %
                                     display_name)

            elif uninstallmethod.startswith("Adobe"):
                retcode = adobeutils.do_adobe_removal(item)

            elif uninstallmethod == "remove_copied_items":
                retcode = remove_copied_items(item.get('items_to_remove'))

            elif uninstallmethod == "remove_app":
                # deprecated with appdmg!
                remove_app_info = item.get('remove_app_info', None)
                if remove_app_info:
                    path_to_remove = remove_app_info['path']
                    display.display_status_minor('Removing %s' %
                                                 path_to_remove)
                    retcode = subprocess.call(
                        ["/bin/rm", "-rf", path_to_remove])
                    if retcode:
                        display.display_error("Removal error for %s",
                                              path_to_remove)
                else:
                    display.display_error(
                        "Application removal info missing from %s",
                        display_name)

            elif uninstallmethod == 'remove_profile':
                identifier = item.get('PayloadIdentifier')
                if identifier:
                    retcode = 0
                    if not profiles.remove_profile(identifier):
                        retcode = -1
                        display.display_error("Profile removal error for %s",
                                              identifier)
                else:
                    display.display_error(
                        "Profile removal info missing from %s", display_name)

            elif uninstallmethod == 'uninstall_script':
                retcode = scriptutils.run_embedded_script(
                    'uninstall_script', item)
                if retcode == 0 and requires_restart(item):
                    restart_flag = True

            elif (os.path.exists(uninstallmethod)
                  and os.access(uninstallmethod, os.X_OK)):
                # it's a script or program to uninstall
                retcode = scriptutils.run_script(display_name, uninstallmethod,
                                                 'uninstall script')
                if retcode == 0 and requires_restart(item):
                    restart_flag = True

            else:
                munkilog.log("Uninstall of %s failed because there was no "
                             "valid uninstall method." % display_name)
                retcode = -99

            if retcode == 0 and item.get('postuninstall_script'):
                retcode = scriptutils.run_embedded_script(
                    'postuninstall_script', item)
                if retcode:
                    # we won't consider postuninstall script failures as fatal
                    # since the item has been uninstalled
                    # but admin should be notified
                    display.display_warning(
                        'Postuninstall script for %s returned %s' %
                        (item['name'], retcode))
                    # reset retcode to 0 so we will mark this uninstall
                    # as successful
                    retcode = 0

        # record removal success/failure
        if not 'RemovalResults' in reports.report:
            reports.report['RemovalResults'] = []
        if retcode == 0:
            success_msg = "Removal of %s: SUCCESSFUL" % display_name
            munkilog.log(success_msg, "Install.log")
            manifestutils.remove_from_selfserve_uninstalls(item['name'])
        else:
            failure_msg = "Removal of %s: " % display_name + \
                          " FAILED with return code: %s" % retcode
            munkilog.log(failure_msg, "Install.log")
            # append failed removal to skipped_removals so dependencies
            # aren't removed yet.
            skipped_removals.append(item)
        removal_result = {
            'display_name': display_name,
            'name': item['name'],
            'status': retcode,
            'time': NSDate.new(),
            'unattended': only_unattended,
        }
        reports.report['RemovalResults'].append(removal_result)

    return (restart_flag, skipped_removals)
Exemplo n.º 18
0
def install_with_info(dirpath,
                      installlist,
                      only_unattended=False,
                      applesus=False):
    """
    Uses the installlist to install items in the
    correct order.
    """
    restartflag = False
    itemindex = 0
    skipped_installs = []
    for item in installlist:
        # Keep track of when this particular install started.
        utc_now = datetime.datetime.utcnow()
        itemindex = itemindex + 1

        if item.get('installer_type') == 'startosinstall':
            skipped_installs.append(item)
            display.display_debug1(
                'Skipping install of %s because it\'s a startosinstall item. '
                'Will install later.' % item['name'])
            continue
        if only_unattended:
            if not item.get('unattended_install'):
                skipped_installs.append(item)
                display.display_detail(
                    'Skipping install of %s because it\'s not unattended.' %
                    item['name'])
                continue
            elif processes.blocking_applications_running(item):
                skipped_installs.append(item)
                display.display_detail(
                    'Skipping unattended install of %s because blocking '
                    'application(s) running.' % item['name'])
                continue

        skipped_prereqs = item_prereqs_in_skipped_items(item, skipped_installs)
        if skipped_prereqs:
            # one or more prerequisite for this item was skipped or failed;
            # need to skip this item too
            skipped_installs.append(item)
            if only_unattended:
                format_str = (
                    'Skipping unattended install of %s because these '
                    'prerequisites were skipped: %s')
            else:
                format_str = ('Skipping install of %s because these '
                              'prerequisites were not installed: %s')
            display.display_detail(format_str %
                                   (item['name'], ", ".join(skipped_prereqs)))
            continue

        if processes.stop_requested():
            return restartflag, skipped_installs

        display_name = item.get('display_name') or item.get('name')
        version_to_install = item.get('version_to_install', '')

        retcode = 0
        if 'preinstall_script' in item:
            retcode = scriptutils.run_embedded_script('preinstall_script',
                                                      item)

        if retcode == 0 and 'installer_item' in item:
            display.display_status_major(
                "Installing %s (%s of %s)" %
                (display_name, itemindex, len(installlist)))

            installer_type = item.get("installer_type", "")

            itempath = os.path.join(dirpath, item["installer_item"])
            if installer_type != "nopkg" and not os.path.exists(itempath):
                # can't install, so we should stop. Since later items might
                # depend on this one, we shouldn't continue
                display.display_error("Installer item %s was not found.",
                                      item["installer_item"])
                return restartflag, skipped_installs
            # Adobe installs
            if installer_type.startswith("Adobe"):
                retcode = adobeutils.do_adobe_install(item)
                if retcode == 0 and requires_restart(item):
                    restartflag = True
                if retcode == 8:
                    # Adobe Setup says restart needed.
                    restartflag = True
                    retcode = 0
            # copy_from_dmg install
            elif installer_type == "copy_from_dmg":
                retcode = dmg.copy_from_dmg(itempath,
                                            item.get('items_to_copy'))
                if retcode == 0 and requires_restart(item):
                    restartflag = True
            # appdmg install (depercated)
            elif installer_type == "appdmg":
                display.display_warning(
                    "install_type 'appdmg' is deprecated. Use 'copy_from_dmg'."
                )
                retcode = dmg.copy_app_from_dmg(itempath)
            # configuration profile install
            elif installer_type == 'profile':
                # profiles.install_profile returns True/False
                retcode = 0
                identifier = item.get('PayloadIdentifier')
                if not profiles.install_profile(itempath, identifier):
                    retcode = -1
                if retcode == 0 and requires_restart(item):
                    restartflag = True
            # nopkg (Packageless) install
            elif installer_type == "nopkg":
                restartflag = restartflag or requires_restart(item)
            # unknown installer_type
            elif installer_type != "":
                # we've encountered an installer type
                # we don't know how to handle
                display.display_error("Unsupported install type: %s" %
                                      installer_type)
                retcode = -99
            # better be Apple installer package
            else:
                (retcode, need_to_restart) = handle_apple_package_install(
                    item, itempath)
                if need_to_restart:
                    restartflag = True

            if processes.stop_requested():
                return restartflag, skipped_installs

        # install succeeded. Do we have a postinstall_script?
        if retcode == 0 and 'postinstall_script' in item:
            # only run embedded postinstall script if the install did not
            # return a failure code
            retcode = scriptutils.run_embedded_script('postinstall_script',
                                                      item)
            if retcode:
                # we won't consider postinstall script failures as fatal
                # since the item has been installed via package/disk image
                # but admin should be notified
                display.display_warning(
                    'Postinstall script for %s returned %s' %
                    (item['name'], retcode))
                # reset retcode to 0 so we will mark this install
                # as successful
                retcode = 0

        # if install was successful and this is a SelfService OnDemand install
        # remove the item from the SelfServeManifest's managed_installs
        if retcode == 0 and item.get('OnDemand'):
            manifestutils.remove_from_selfserve_installs(item['name'])

        # record install success/failure
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        if applesus:
            message = "Apple SUS install of %s-%s: %s"
        else:
            message = "Install of %s-%s: %s"

        if retcode == 0:
            status = "SUCCESSFUL"
        else:
            status = "FAILED with return code: %s" % retcode
            # add this failed install to the skipped_installs list
            # so that any item later in the list that requires this
            # item is skipped as well.
            skipped_installs.append(item)

        log_msg = message % (display_name, version_to_install, status)
        munkilog.log(log_msg, "Install.log")

        # Calculate install duration; note, if a machine is put to sleep
        # during the install this time may be inaccurate.
        utc_now_complete = datetime.datetime.utcnow()
        duration_seconds = (utc_now_complete - utc_now).seconds

        download_speed = item.get('download_kbytes_per_sec', 0)
        install_result = {
            'display_name': display_name,
            'name': item['name'],
            'version': version_to_install,
            'applesus': applesus,
            'status': retcode,
            'time': NSDate.new(),
            'duration_seconds': duration_seconds,
            'download_kbytes_per_sec': download_speed,
            'unattended': only_unattended,
        }
        reports.report['InstallResults'].append(install_result)

        # check to see if this installer item is needed by any additional
        # items in installinfo
        # this might happen if there are multiple things being installed
        # with choicesXML files applied to a metapackage or
        # multiple packages being installed from a single DMG
        stillneeded = False
        current_installer_item = item['installer_item']
        # are we at the end of the installlist?
        # (we already incremented itemindex for display
        # so with zero-based arrays itemindex now points to the item
        # after the current item)
        if itemindex < len(installlist):
            # nope, let's check the remaining items
            for lateritem in installlist[itemindex:]:
                if (lateritem.get('installer_item') == current_installer_item):
                    stillneeded = True
                    break

        # check to see if the item is both precache and OnDemand
        if not stillneeded and item.get('precache') and item.get('OnDemand'):
            stillneeded = True
            break

        # need to check skipped_installs as well
        if not stillneeded:
            for skipped_item in skipped_installs:
                if (skipped_item.get('installer_item') ==
                        current_installer_item):
                    stillneeded = True
                    break

        # ensure package is not deleted from cache if installation
        # fails by checking retcode
        if not stillneeded and retcode == 0:
            # now remove the item from the install cache
            # (if it's still there)
            itempath = os.path.join(dirpath, current_installer_item)
            if os.path.exists(itempath):
                if os.path.isdir(itempath):
                    retcode = subprocess.call(["/bin/rm", "-rf", itempath])
                else:
                    # flat pkg or dmg
                    retcode = subprocess.call(["/bin/rm", itempath])
                    if pkgutils.hasValidDiskImageExt(itempath):
                        shadowfile = os.path.join(itempath, ".shadow")
                        if os.path.exists(shadowfile):
                            retcode = subprocess.call(["/bin/rm", shadowfile])

    return (restartflag, skipped_installs)
Exemplo n.º 19
0
    def install_apple_updates(self, only_unattended=False):
        """Uses softwareupdate to install previously downloaded updates.

        Returns:
          Boolean. True if a restart is needed after install, False otherwise.
        """
        # disable Stop button if we are presenting GUI status
        if display.munkistatusoutput:
            munkistatus.hideStopButton()

        # Get list of unattended_installs
        if only_unattended:
            msg = 'Installing unattended Apple Software Updates...'
            # Creating an 'unattended_install' filtered catalog
            # against the existing filtered catalog is not an option as
            # cached downloads are purged if they do not exist in the
            # filtered catalog.  Instead, get a list of updates, and their
            # product_ids, that are eligible for unattended_install.
            unattended_install_items, unattended_install_product_ids = \
                self.get_unattended_installs()
            # ensure that we don't restart for unattended installations
            restartneeded = False
            if not unattended_install_items:
                return False  # didn't find any unattended installs
        else:
            msg = 'Installing available Apple Software Updates...'
            restartneeded = self.restart_needed()

        self._display_status_major(msg)

        installlist = self.software_update_info()
        installresults = {'installed': [], 'download': []}

        su_options = ['-i']

        if only_unattended:
            # Append list of unattended_install items
            su_options.extend(unattended_install_items)
            # Filter installist to only include items
            # which we're attempting to install
            installlist = [
                item for item in installlist
                if item.get('productKey') in unattended_install_product_ids
            ]
        else:
            # We're installing all available updates; add all their names
            for item in installlist:
                su_options.append(item['name'] + '-' +
                                  item['version_to_install'])

        # new in 10.11: '--no-scan' flag to tell softwareupdate to just install
        # and not rescan for available updates.
        os_version_tuple = osutils.getOsVersion(as_tuple=True)
        if os_version_tuple >= (10, 11):
            su_options.append('--no-scan')
            # 10.11 seems not to like file:// URLs, and we don't really need
            # to switch to a local file URL anyway since we now have the
            # --no-scan option
            catalog_url = None
        else:
            # use our filtered local catalog
            if not os.path.exists(self.applesync.local_catalog_path):
                display.display_error(
                    'Missing local Software Update catalog at %s',
                    self.applesync.local_catalog_path)
                return False  # didn't do anything, so no restart needed
            catalog_url = 'file://localhost' + urllib2.quote(
                self.applesync.local_catalog_path)

        retcode = self._run_softwareupdate(su_options,
                                           mode='install',
                                           catalog_url=catalog_url,
                                           results=installresults)
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        display.display_debug1('Raw Apple Update install results: %s',
                               installresults)
        for item in installlist:
            rep = {}
            rep['name'] = item.get('apple_product_name')
            rep['version'] = item.get('version_to_install', '')
            rep['applesus'] = True
            rep['time'] = NSDate.new()
            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 ('display_name' in item
                  and item['display_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.'
                display.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'
                display.display_warning(
                    'Apple update %s, %s may have failed to install. No record '
                    'of success or failure.', rep['name'], rep['productKey'])
                if installresults['installed']:
                    display.display_warning(
                        'softwareupdate recorded these installations: %s',
                        installresults['installed'])

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

        if retcode:  # there was an error
            display.display_error('softwareupdate error: %s' % retcode)

        # Refresh Applicable updates and catalogs
        # since we may have performed some unattended installs
        if only_unattended:
            product_ids = self.available_update_product_ids()
            self.applesync.write_filtered_catalog(product_ids)

        # clean up our now stale local cache
        if not only_unattended:
            self.applesync.clean_up_cache()
        # remove the now invalid AppleUpdates.plist and AvailableUpdates.plist
        self.clear_apple_update_info()
        # 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.
        prefs.set_pref('LastAppleSoftwareUpdateCheck', None)

        # show stop button again
        if display.munkistatusoutput:
            munkistatus.showStopButton()

        return restartneeded
Exemplo n.º 20
0
Arquivo: au.py Projeto: munki/munki
    def install_apple_updates(self, only_unattended=False):
        """Uses softwareupdate to install previously downloaded updates.

        Returns:
          Boolean. True if a restart is needed after install, False otherwise.
        """
        # disable Stop button if we are presenting GUI status
        if display.munkistatusoutput:
            munkistatus.hideStopButton()

        self.shutdown_instead_of_restart = False
        # Get list of unattended_installs
        if only_unattended:
            msg = 'Installing unattended Apple Software Updates...'
            unattended_install_items, unattended_install_product_ids = \
                self.get_unattended_installs()
            # ensure that we don't restart for unattended installations
            restart_action = POSTACTION_NONE
            if not unattended_install_items:
                return False  # didn't find any unattended installs
        else:
            msg = 'Installing available Apple Software Updates...'
            restart_action = self.restart_action_for_updates()

        display.display_status_major(msg)

        installlist = self.software_update_info()
        remaining_apple_updates = []
        installresults = {'installed': [], 'download': []}

        su_options = ['-i']

        if only_unattended:
            # Append list of unattended_install items
            su_options.extend(unattended_install_items)
            # Filter installlist to only include items
            # which we're attempting to install
            filtered_installlist = [item for item in installlist
                                    if item.get('productKey') in
                                    unattended_install_product_ids]
            # record items we aren't planning to attempt to install
            remaining_apple_updates = [item for item in installlist
                                       if item not in filtered_installlist]
            installlist = filtered_installlist

        else:
            # We're installing all available updates; add all their names
            for item in installlist:
                su_options.append(
                    item['name'] + '-' + item['version_to_install'])

        # new in 10.11: '--no-scan' flag to tell softwareupdate to just install
        # and not rescan for available updates.
        os_version_tuple = osutils.getOsVersion(as_tuple=True)
        if os_version_tuple >= (10, 11):
            try:
                # attempt to fetch the apple catalog to confirm connectivity
                # to the Apple Software Update server
                self.applesync.cache_apple_catalog()
            except (sync.Error, fetch.Error):
                # network or catalog server not available, suppress scan
                # (we used to do this all the time, but this led to issues
                #  with updates cached "too long" in 10.12+)
                munkilog.log(
                    "WARNING: Cannot reach Apple Software Update server while "
                    "installing Apple updates")
                su_options.append('--no-scan')
            # 10.11 seems not to like file:// URLs, and we don't really need
            # to switch to a local file URL anyway since we now have the
            # --no-scan option
            catalog_url = None
        else:
            # use our local catalog
            if not os.path.exists(self.applesync.local_catalog_path):
                display.display_error(
                    'Missing local Software Update catalog at %s',
                    self.applesync.local_catalog_path)
                return False  # didn't do anything, so no restart needed
            catalog_url = 'file://localhost' + urllib2.quote(
                self.applesync.local_catalog_path)

        su_start_date = NSDate.new()
        installresults = su_tool.run(su_options, catalog_url=catalog_url)
        retcode = installresults.get('exit_code', 0)
        self.shutdown_instead_of_restart = installresults.get(
            'post_action') == POSTACTION_SHUTDOWN
        su_end_date = NSDate.new()

        # get the items that were just installed from InstallHistory.plist
        installed_items = softwareupdated_installhistory(
            start_date=su_start_date, end_date=su_end_date)
        display.display_debug2(
            'InstallHistory.plist items:\n%s', installed_items)
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        display.display_debug1(
            'Raw Apple Update install results: %s', installresults)
        for item in installlist:
            rep = {}
            rep['name'] = item.get('apple_product_name')
            rep['version'] = item.get('version_to_install', '')
            rep['applesus'] = True
            rep['time'] = su_end_date
            rep['productKey'] = item.get('productKey', '')
            message = 'Apple Software Update install of %s-%s: %s'
            # first try to match against the items from InstallHistory.plist
            matched_installed_items = [
                ih_item for ih_item in installed_items
                if ih_item['displayName'] in [
                    item.get('apple_product_name'), item.get('display_name')]
                and ih_item['displayVersion'] == item.get('version_to_install')
            ]
            if matched_installed_items:
                display.display_debug2('Matched %s in InstallHistory.plist',
                                       item.get('apple_product_name'))
                rep['status'] = 0
                rep['time'] = matched_installed_items[0]['date']
                install_status = 'SUCCESSFUL'
            elif rep['name'] in installresults['installed']:
                rep['status'] = 0
                install_status = 'SUCCESSFUL'
            elif ('display_name' in item and
                  item['display_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.'
                display.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'
                display.display_warning(
                    'Apple update %s, %s may have failed to install. No record '
                    'of success or failure.', rep['name'], rep['productKey'])
                if installresults['installed']:
                    display.display_warning(
                        'softwareupdate recorded these installations: %s',
                        installresults['installed'])

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

        if retcode:  # there was an error
            display.display_error('softwareupdate error: %s' % retcode)

        if not remaining_apple_updates:
            # clean up our now stale local cache
            self.applesync.clean_up_cache()
            # remove the now invalid AppleUpdates.plist
            self.clear_apple_update_info()
        else:
            # we installed some of the updates, but some are still uninstalled.
            # re-write the apple_update_info to match
            plist = {'AppleUpdates': remaining_apple_updates}
            FoundationPlist.writePlist(plist, self.apple_updates_plist)

        # 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.
        prefs.set_pref('LastAppleSoftwareUpdateCheck', None)

        # show stop button again
        if display.munkistatusoutput:
            munkistatus.showStopButton()

        if self.shutdown_instead_of_restart:
            display.display_info(
                'One or more Apple updates requires a shutdown instead of '
                'restart.')
            restart_action = POSTACTION_SHUTDOWN

        return restart_action
Exemplo n.º 21
0
    def install_apple_updates(self, only_unattended=False):
        """Uses softwareupdate to install previously downloaded updates.

        Returns:
          Boolean. True if a restart is needed after install, False otherwise.
        """
        # disable Stop button if we are presenting GUI status
        if display.munkistatusoutput:
            munkistatus.hideStopButton()

        self.shutdown_instead_of_restart = False
        # Get list of unattended_installs
        if only_unattended:
            msg = 'Installing unattended Apple Software Updates...'
            unattended_install_items, unattended_install_product_ids = \
                self.get_unattended_installs()
            # ensure that we don't restart for unattended installations
            restart_action = POSTACTION_NONE
            if not unattended_install_items:
                return False  # didn't find any unattended installs
        else:
            msg = 'Installing available Apple Software Updates...'
            restart_action = self.restart_action_for_updates()

        display.display_status_major(msg)

        installlist = self.software_update_info()
        remaining_apple_updates = []
        installresults = {'installed': [], 'download': []}

        su_options = ['-i']

        if only_unattended:
            # Append list of unattended_install items
            su_options.extend(unattended_install_items)
            # Filter installlist to only include items
            # which we're attempting to install
            filtered_installlist = [
                item for item in installlist
                if item.get('productKey') in unattended_install_product_ids
            ]
            # record items we aren't planning to attempt to install
            remaining_apple_updates = [
                item for item in installlist
                if item not in filtered_installlist
            ]
            installlist = filtered_installlist

        else:
            # We're installing all available updates; add all their names
            for item in installlist:
                su_options.append(item['name'] + '-' +
                                  item['version_to_install'])

        # new in 10.11: '--no-scan' flag to tell softwareupdate to just install
        # and not rescan for available updates.
        os_version_tuple = osutils.getOsVersion(as_tuple=True)
        if os_version_tuple >= (10, 11):
            try:
                # attempt to fetch the apple catalog to confirm connectivity
                # to the Apple Software Update server
                self.applesync.cache_apple_catalog()
            except (sync.Error, fetch.Error):
                # network or catalog server not available, suppress scan
                # (we used to do this all the time, but this led to issues
                #  with updates cached "too long" in 10.12+)
                munkilog.log(
                    "WARNING: Cannot reach Apple Software Update server while "
                    "installing Apple updates")
                su_options.append('--no-scan')
            # 10.11 seems not to like file:// URLs, and we don't really need
            # to switch to a local file URL anyway since we now have the
            # --no-scan option
            catalog_url = None
        else:
            # use our local catalog
            if not os.path.exists(self.applesync.local_catalog_path):
                display.display_error(
                    'Missing local Software Update catalog at %s',
                    self.applesync.local_catalog_path)
                return False  # didn't do anything, so no restart needed
            catalog_url = 'file://localhost' + quote(
                self.applesync.local_catalog_path)

        su_start_date = NSDate.new()
        installresults = su_tool.run(su_options, catalog_url=catalog_url)
        retcode = installresults.get('exit_code', 0)
        self.shutdown_instead_of_restart = installresults.get(
            'post_action') == POSTACTION_SHUTDOWN
        su_end_date = NSDate.new()

        # get the items that were just installed from InstallHistory.plist
        installed_items = softwareupdated_installhistory(
            start_date=su_start_date, end_date=su_end_date)
        display.display_debug2('InstallHistory.plist items:\n%s',
                               installed_items)
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        display.display_debug1('Raw Apple Update install results: %s',
                               installresults)
        for item in installlist:
            rep = {}
            rep['name'] = item.get('apple_product_name')
            rep['version'] = item.get('version_to_install', '')
            rep['applesus'] = True
            rep['time'] = su_end_date
            rep['productKey'] = item.get('productKey', '')
            message = 'Apple Software Update install of %s-%s: %s'
            # first try to match against the items from InstallHistory.plist
            matched_installed_items = [
                ih_item for ih_item in installed_items
                if ih_item['displayName'] in
                [item.get('apple_product_name'),
                 item.get('display_name')]
                and ih_item['displayVersion'] == item.get('version_to_install')
            ]
            if matched_installed_items:
                display.display_debug2('Matched %s in InstallHistory.plist',
                                       item.get('apple_product_name'))
                rep['status'] = 0
                rep['time'] = matched_installed_items[0]['date']
                install_status = 'SUCCESSFUL'
            elif rep['name'] in installresults['installed']:
                rep['status'] = 0
                install_status = 'SUCCESSFUL'
            elif ('display_name' in item
                  and item['display_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.'
                display.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'
                display.display_warning(
                    'Apple update %s, %s may have failed to install. No record '
                    'of success or failure.', rep['name'], rep['productKey'])
                if installresults['installed']:
                    display.display_warning(
                        'softwareupdate recorded these installations: %s',
                        installresults['installed'])

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

        if retcode:  # there was an error
            display.display_error('softwareupdate error: %s' % retcode)

        if not remaining_apple_updates:
            # clean up our now stale local cache
            self.applesync.clean_up_cache()
            # remove the now invalid AppleUpdates.plist
            self.clear_apple_update_info()
        else:
            # we installed some of the updates, but some are still uninstalled.
            # re-write the apple_update_info to match
            plist = {'AppleUpdates': remaining_apple_updates}
            FoundationPlist.writePlist(plist, self.apple_updates_plist)

        # 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.
        prefs.set_pref('LastAppleSoftwareUpdateCheck', None)

        # show stop button again
        if display.munkistatusoutput:
            munkistatus.showStopButton()

        if self.shutdown_instead_of_restart:
            display.display_info(
                'One or more Apple updates requires a shutdown instead of '
                'restart.')
            restart_action = POSTACTION_SHUTDOWN

        return restart_action
Exemplo n.º 22
0
def install_with_info(dirpath,
                      installlist,
                      only_unattended=False,
                      applesus=False):
    """
    Uses the installlist to install items in the
    correct order.
    """
    restartflag = False
    itemindex = 0
    skipped_installs = []
    for item in installlist:
        # Keep track of when this particular install started.
        utc_now = datetime.datetime.utcnow()
        itemindex = itemindex + 1
        if only_unattended:
            if not item.get('unattended_install'):
                skipped_installs.append(item)
                display.display_detail(
                    ('Skipping install of %s because it\'s not unattended.' %
                     item['name']))
                continue
            elif processes.blocking_applications_running(item):
                skipped_installs.append(item)
                display.display_detail(
                    'Skipping unattended install of %s because '
                    'blocking application(s) running.' % item['name'])
                continue

        skipped_prereqs = item_prereqs_in_skipped_items(item, skipped_installs)
        if skipped_prereqs:
            # one or more prerequisite for this item was skipped or failed;
            # need to skip this item too
            skipped_installs.append(item)
            if only_unattended:
                format_str = (
                    'Skipping unattended install of %s because these '
                    'prerequisites were skipped: %s')
            else:
                format_str = ('Skipping install of %s because these '
                              'prerequisites were not installed: %s')
            display.display_detail(format_str %
                                   (item['name'], ", ".join(skipped_prereqs)))
            continue

        if processes.stop_requested():
            return restartflag, skipped_installs

        display_name = item.get('display_name') or item.get('name')
        version_to_install = item.get('version_to_install', '')

        retcode = 0
        if 'preinstall_script' in item:
            retcode = scriptutils.run_embedded_script('preinstall_script',
                                                      item)

        if retcode == 0 and 'installer_item' in item:
            display.display_status_major(
                "Installing %s (%s of %s)" %
                (display_name, itemindex, len(installlist)))

            installer_type = item.get("installer_type", "")

            itempath = os.path.join(dirpath, item["installer_item"])
            if installer_type != "nopkg" and not os.path.exists(itempath):
                # can't install, so we should stop. Since later items might
                # depend on this one, we shouldn't continue
                display.display_error("Installer item %s was not found.",
                                      item["installer_item"])
                return restartflag, skipped_installs

            if installer_type.startswith("Adobe"):
                retcode = adobeutils.do_adobe_install(item)
                if retcode == 0:
                    if (item.get("RestartAction") == "RequireRestart" or
                            item.get("RestartAction") == "RecommendRestart"):
                        restartflag = True
                if retcode == 8:
                    # Adobe Setup says restart needed.
                    restartflag = True
                    retcode = 0
            elif installer_type == "copy_from_dmg":
                retcode = dmg.copy_from_dmg(itempath,
                                            item.get('items_to_copy'))
                if retcode == 0:
                    if (item.get("RestartAction") == "RequireRestart" or
                            item.get("RestartAction") == "RecommendRestart"):
                        restartflag = True
            elif installer_type == "appdmg":
                display.display_warning(
                    "install_type 'appdmg' is deprecated. Use 'copy_from_dmg'."
                )
                retcode = dmg.copy_app_from_dmg(itempath)
            elif installer_type == 'profile':
                # profiles.install_profile returns True/False
                retcode = 0
                identifier = item.get('PayloadIdentifier')
                if not profiles.install_profile(itempath, identifier):
                    retcode = -1
            elif installer_type == "nopkg":  # Packageless install
                if (item.get("RestartAction") == "RequireRestart"
                        or item.get("RestartAction") == "RecommendRestart"):
                    restartflag = True
            elif installer_type != "":
                # we've encountered an installer type
                # we don't know how to handle
                display.display_error("Unsupported install type: %s" %
                                      installer_type)
                retcode = -99
            else:
                # better be Apple installer package
                suppress_bundle_relocation = item.get(
                    "suppress_bundle_relocation", False)
                display.display_debug1("suppress_bundle_relocation: %s",
                                       suppress_bundle_relocation)
                if 'installer_choices_xml' in item:
                    choices_xml_file = os.path.join(osutils.tmpdir(),
                                                    'choices.xml')
                    FoundationPlist.writePlist(item['installer_choices_xml'],
                                               choices_xml_file)
                else:
                    choices_xml_file = ''
                installer_environment = item.get('installer_environment')
                if pkgutils.hasValidDiskImageExt(itempath):
                    display.display_status_minor("Mounting disk image %s" %
                                                 item["installer_item"])
                    mount_with_shadow = suppress_bundle_relocation
                    # we need to mount the diskimage as read/write to
                    # be able to modify the package to suppress bundle
                    # relocation
                    mountpoints = dmgutils.mountdmg(
                        itempath, use_shadow=mount_with_shadow)
                    if mountpoints == []:
                        display.display_error("No filesystems mounted from %s",
                                              item["installer_item"])
                        return restartflag, skipped_installs
                    if processes.stop_requested():
                        dmgutils.unmountdmg(mountpoints[0])
                        return restartflag, skipped_installs

                    retcode = -99  # in case we find nothing to install
                    needtorestart = False
                    if pkgutils.hasValidInstallerItemExt(
                            item.get('package_path', '')):
                        # admin has specified the relative path of the pkg
                        # on the DMG
                        # this is useful if there is more than one pkg on
                        # the DMG, or the actual pkg is not at the root
                        # of the DMG
                        fullpkgpath = os.path.join(mountpoints[0],
                                                   item['package_path'])
                        if os.path.exists(fullpkgpath):
                            (retcode, needtorestart) = pkg.install(
                                fullpkgpath, display_name, choices_xml_file,
                                suppress_bundle_relocation,
                                installer_environment)
                    else:
                        # no relative path to pkg on dmg, so just install all
                        # pkgs found at the root of the first mountpoint
                        # (hopefully there's only one)
                        (retcode, needtorestart) = pkg.installall(
                            mountpoints[0], display_name, choices_xml_file,
                            suppress_bundle_relocation, installer_environment)
                    if (needtorestart
                            or item.get("RestartAction") == "RequireRestart" or
                            item.get("RestartAction") == "RecommendRestart"):
                        restartflag = True
                    dmgutils.unmountdmg(mountpoints[0])
                elif (pkgutils.hasValidPackageExt(itempath)
                      or itempath.endswith(".dist")):
                    (retcode, needtorestart) = pkg.install(
                        itempath, display_name, choices_xml_file,
                        suppress_bundle_relocation, installer_environment)
                    if (needtorestart
                            or item.get("RestartAction") == "RequireRestart" or
                            item.get("RestartAction") == "RecommendRestart"):
                        restartflag = True

                else:
                    # we didn't find anything we know how to install
                    munkilog.log("Found nothing we know how to install in %s" %
                                 itempath)
                    retcode = -99

        if retcode == 0 and 'postinstall_script' in item:
            # only run embedded postinstall script if the install did not
            # return a failure code
            retcode = scriptutils.run_embedded_script('postinstall_script',
                                                      item)
            if retcode:
                # we won't consider postinstall script failures as fatal
                # since the item has been installed via package/disk image
                # but admin should be notified
                display.display_warning(
                    'Postinstall script for %s returned %s' %
                    (item['name'], retcode))
                # reset retcode to 0 so we will mark this install
                # as successful
                retcode = 0

        # if install was successful and this is a SelfService OnDemand install
        # remove the item from the SelfServeManifest's managed_installs
        if retcode == 0 and item.get('OnDemand'):
            remove_from_selfserve_installs(item['name'])

        # record install success/failure
        if not 'InstallResults' in reports.report:
            reports.report['InstallResults'] = []

        if applesus:
            message = "Apple SUS install of %s-%s: %s"
        else:
            message = "Install of %s-%s: %s"

        if retcode == 0:
            status = "SUCCESSFUL"
        else:
            status = "FAILED with return code: %s" % retcode
            # add this failed install to the skipped_installs list
            # so that any item later in the list that requires this
            # item is skipped as well.
            skipped_installs.append(item)

        log_msg = message % (display_name, version_to_install, status)
        munkilog.log(log_msg, "Install.log")

        # Calculate install duration; note, if a machine is put to sleep
        # during the install this time may be inaccurate.
        utc_now_complete = datetime.datetime.utcnow()
        duration_seconds = (utc_now_complete - utc_now).seconds

        download_speed = item.get('download_kbytes_per_sec', 0)
        install_result = {
            'display_name': display_name,
            'name': item['name'],
            'version': version_to_install,
            'applesus': applesus,
            'status': retcode,
            'time': NSDate.new(),
            'duration_seconds': duration_seconds,
            'download_kbytes_per_sec': download_speed,
            'unattended': only_unattended,
        }
        reports.report['InstallResults'].append(install_result)

        # check to see if this installer item is needed by any additional
        # items in installinfo
        # this might happen if there are multiple things being installed
        # with choicesXML files applied to a metapackage or
        # multiple packages being installed from a single DMG
        foundagain = False
        current_installer_item = item['installer_item']
        # are we at the end of the installlist?
        # (we already incremented itemindex for display
        # so with zero-based arrays itemindex now points to the item
        # after the current item)
        if itemindex < len(installlist):
            # nope, let's check the remaining items
            for lateritem in installlist[itemindex:]:
                if (lateritem.get('installer_item') == current_installer_item):
                    foundagain = True
                    break

        # need to check skipped_installs as well
        if not foundagain:
            for skipped_item in skipped_installs:
                if (skipped_item.get('installer_item') ==
                        current_installer_item):
                    foundagain = True
                    break

        # ensure package is not deleted from cache if installation
        # fails by checking retcode
        if not foundagain and retcode == 0:
            # now remove the item from the install cache
            # (if it's still there)
            itempath = os.path.join(dirpath, current_installer_item)
            if os.path.exists(itempath):
                if os.path.isdir(itempath):
                    retcode = subprocess.call(["/bin/rm", "-rf", itempath])
                else:
                    # flat pkg or dmg
                    retcode = subprocess.call(["/bin/rm", itempath])
                    if pkgutils.hasValidDiskImageExt(itempath):
                        shadowfile = os.path.join(itempath, ".shadow")
                        if os.path.exists(shadowfile):
                            retcode = subprocess.call(["/bin/rm", shadowfile])

    return (restartflag, skipped_installs)