--- a/src/checkforupdates.py Fri Sep 27 11:21:00 2013 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,377 +0,0 @@
-#!/usr/bin/python2.6
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
-#
-
-"""This utility checks to see if there are any available updates for
-the relevant image. If so, it stashes information about the updates in
-the gui cache file, for retrieval by other desktop utilities. See also
-the update-refresh cron job."""
-
-import errno
-import getopt
-import gettext
-import locale
-import logging
-import os
-import sys
-import traceback
-import warnings
-
-import pkg.client.api as api
-import pkg.client.api_errors as apx
-import pkg.client.progress as progress
-import pkg.client.printengine as printengine
-import pkg.gui.enumerations as enumerations
-import pkg.gui.misc_non_gui as nongui_misc
-import pkg.misc as misc
-import pkg.nrlock as nrlock
-from cPickle import UnpicklingError
-from pkg.client import global_settings
-from pkg.client.pkgdefs import EXIT_OOPS
-
-logger = global_settings.logger
-
-PKG_CLIENT_NAME = "updatemanager"
-CACHE_VERSION = 3
-CACHE_NAME = ".last_refresh_cache"
-
-
-class CheckForUpdates:
- """Implements the main logic for this utility"""
-
- def __init__(self, image_directory, application_path, check_all,
- check_cache):
- global_settings.client_name = nongui_misc.get_um_name()
- self.api_lock = nrlock.NRLock()
- self.image_dir_arg = image_directory
- self.exact_match = True
- if self.image_dir_arg == None:
- self.image_dir_arg, self.exact_match = \
- api.get_default_image_root()
- if not self.exact_match:
- logger.debug("Unable to get image directory")
- sys.exit(enumerations.UPDATES_UNDETERMINED)
-
- self.application_path = application_path
- self.check_all = check_all
- self.check_cache_only = check_cache
- self.application_dir = \
- os.environ.get("PACKAGE_MANAGER_ROOT", "/")
- misc.setlocale(locale.LC_ALL, "")
-
- if global_settings.verbose:
- pe = printengine.LoggingPrintEngine(
- logger, logging.DEBUG)
- self.progress_tracker = \
- progress.CommandLineProgressTracker(print_engine=pe)
- else:
- self.progress_tracker = progress.NullProgressTracker()
- self.api_obj = None
- self.return_status = enumerations.UPDATES_UNDETERMINED
- self.pylintstub = None
-
- # Check Updates - by default check all
- self.api_obj = self.__get_api_obj()
- if self.api_obj == None:
- self.return_status = enumerations.UPDATES_UNDETERMINED
- return
-
- if self.check_all:
- self.__check_for_updates()
- elif self.check_cache_only:
- self.__check_for_updates_cache_only()
-
- def __get_api_obj(self):
- """Returns a singleton api instance."""
- if self.api_obj == None:
- api_obj = nongui_misc.get_api_object(self.image_dir_arg,
- self.progress_tracker)
- return api_obj
-
- def __check_for_updates_cache_only(self):
- """Reports on the cached status of available updates"""
- assert self.api_obj
- self.return_status = ret = self.__check_last_refresh()
- if ret == enumerations.UPDATES_AVAILABLE:
- logger.debug("From cache: Updates Available")
- elif ret == enumerations.NO_UPDATES_AVAILABLE:
- logger.debug("From cache: No Updates Available")
- else:
- logger.debug("From cache: Updates Undetermined")
- return ret
-
- def __check_for_updates(self):
- """Plans an update for the image."""
- assert self.api_obj
- ret = self.__check_for_updates_cache_only()
- if ret != enumerations.UPDATES_UNDETERMINED:
- # Definitive answer from cache.
- return
- logger.debug("Checking image for updates...")
- self.return_status = enumerations.UPDATES_UNDETERMINED
- try:
- #
- # Since this program is intended to primarily be a
- # helper for the gui components, and since the gui
- # components are currently unaware of child images,
- # we'll limit the available update check we're about
- # to do to just the parent image. If we didn't do
- # this we could end up in a situation where the parent
- # has no available updates, but a child image does,
- # and then the gui (which is unaware of children)
- # would show that no updates are available to the
- # parent.
- #
-
- # Unused variable; pylint: disable=W0612
- for pd in self.api_obj.gen_plan_update(
- refresh_catalogs=True, noexecute=True,
- force=True, li_ignore=[]):
- continue
- stuff_to_do = not self.api_obj.planned_nothingtodo()
- except apx.CatalogRefreshException, cre:
- res = nongui_misc.get_catalogrefresh_exception_msg(cre)
- logger.error(res[0])
- return
- except apx.ApiException, e:
- logger.error(str(e))
- return
-
- self.__dump_updates_available(stuff_to_do)
- if stuff_to_do:
- logger.debug("From image: Updates Available")
- self.return_status = enumerations.UPDATES_AVAILABLE
- else:
- logger.debug("From image: No Updates Available")
- self.return_status = enumerations.NO_UPDATES_AVAILABLE
-
- def __check_last_refresh(self):
- """Reads the cache if possible; if it isn't stale or corrupt
- or out of date, return whether updates are available.
- Otherwise return 'undetermined'."""
-
- cache_dir = nongui_misc.get_cache_dir(self.api_obj)
- if not cache_dir:
- return enumerations.UPDATES_UNDETERMINED
- try:
- info = nongui_misc.read_cache_file(os.path.join(
- cache_dir, CACHE_NAME + '.cpl'))
- if len(info) == 0:
- logger.debug("No cache")
- return enumerations.UPDATES_UNDETERMINED
- # Non-portable API used; pylint: disable=E0901
- utsname = os.uname()
- # pylint: disable=E1103
- if info.get("version") != CACHE_VERSION:
- logger.debug("Cache version mismatch: %s" %
- (info.get("version") + " " + CACHE_VERSION))
- return enumerations.UPDATES_UNDETERMINED
- if info.get("os_release") != utsname[2]:
- logger.debug("OS release mismatch: %s" %
- (info.get("os_release") + " " + utsname[2]))
- return enumerations.UPDATES_UNDETERMINED
- if info.get("os_version") != utsname[3]:
- logger.debug("OS version mismatch: %s" %
- (info.get("os_version") + " " + utsname[3]))
- return enumerations.UPDATES_UNDETERMINED
- old_publishers = info.get("publishers")
- count = 0
- for p in self.api_obj.get_publishers():
- if p.disabled:
- continue
- if old_publishers.get(p.prefix, -1) != \
- p.last_refreshed:
- return enumerations.UPDATES_UNDETERMINED
- count += 1
-
- if count != len(old_publishers):
- return enumerations.UPDATES_UNDETERMINED
-
- n_updates = n_installs = n_removes = 0
- if info.get("updates_available"):
- n_updates = info.get("updates")
- n_installs = info.get("installs")
- n_removes = info.get("removes")
- # pylint: enable=E1103
- if self.check_cache_only:
- print "n_updates: %d" % n_updates
- print "n_installs: %d" % n_installs
- print "n_removes: %d" % n_removes
- if (n_updates + n_installs + n_removes) > 0:
- return enumerations.UPDATES_AVAILABLE
- else:
- return enumerations.NO_UPDATES_AVAILABLE
-
- except (UnpicklingError, IOError):
- return enumerations.UPDATES_UNDETERMINED
-
- def __dump_updates_available(self, stuff_to_do):
- """Record update information to the cache file."""
- cache_dir = nongui_misc.get_cache_dir(self.api_obj)
- if not cache_dir:
- return
- publisher_list = {}
- for p in self.api_obj.get_publishers():
- if p.disabled:
- continue
- publisher_list[p.prefix] = p.last_refreshed
- n_installs = 0
- n_removes = 0
- n_updates = 0
- plan_desc = self.api_obj.describe()
- if plan_desc:
- plan = plan_desc.get_changes()
- for (orig, dest) in plan:
- if orig and dest:
- n_updates += 1
- elif not orig and dest:
- n_installs += 1
- elif orig and not dest:
- n_removes += 1
- dump_info = {}
- dump_info["version"] = CACHE_VERSION
- # Non-portable API used; pylint: disable=E0901
- dump_info["os_release"] = os.uname()[2]
- dump_info["os_version"] = os.uname()[3]
- dump_info["updates_available"] = stuff_to_do
- dump_info["publishers"] = publisher_list
- dump_info["updates"] = n_updates
- dump_info["installs"] = n_installs
- dump_info["removes"] = n_removes
-
- try:
- nongui_misc.dump_cache_file(os.path.join(
- cache_dir, CACHE_NAME + '.cpl'), dump_info)
- except IOError, e:
- logger.error("Failed to dump cache: %s" % e)
- return
-
-
-def main_func():
- """Main routine for this utility"""
- set_check_all = True
- set_check_cache = False
- image_dir = None
- try:
- # Unused variable pargs; pylint: disable=W0612
- opts, pargs = getopt.getopt(sys.argv[1:], "hdnacR:",
- ["help", "debug", "nice", "checkupdates-cache",
- "image-dir="])
- except getopt.GetoptError, oex:
- print >> sys.stderr, \
- ("Usage: illegal option -- %s, for help use -h or --help" %
- oex.opt )
- sys.exit(enumerations.UPDATES_UNDETERMINED)
- for opt, arg in opts:
- if opt in ("-h", "--help"):
- print >> sys.stderr, """\n\
-Use -h (--help) to print out help.
-Use -d (--debug) to run in debug mode.
-Use -n (--nice) to run at nice level 20.
-Use -c (--checkupdates-cache) to check for updates from cache only (output results to stdout).
-Use -R (--image-dir) to specify image directory (defaults to '/')"""
- sys.exit(0)
- elif opt in ( "-n", "--nice"):
- # Non-portable API used; pylint: disable=E0901
- os.nice(20)
- elif opt in ("-d", "--debug"):
- global_settings.verbose = True
- elif opt in ( "-c", "--checkupdates-cache"):
- set_check_cache = True
- set_check_all = False
- elif opt in ("-R", "--image-dir"):
- image_dir = arg
-
- if os.path.isabs(sys.argv[0]):
- app_path = sys.argv[0]
- else:
- cmd = os.path.join(os.getcwd(), sys.argv[0])
- app_path = os.path.realpath(cmd)
-
- checkforupdates = CheckForUpdates(image_dir, app_path,
- set_check_all, set_check_cache)
-
- return checkforupdates.return_status
-
-#
-# Establish a specific exit status which means: "python barfed an exception"
-# so that we can more easily detect these in testing of the CLI commands.
-#
-def handle_errors(func, *args, **kwargs):
- """Catch exceptions raised by the main program function and then print
- a message and/or exit with an appropriate return code.
- """
-
- traceback_str = misc.get_traceback_message()
-
- try:
- # Out of memory errors can be raised as EnvironmentErrors with
- # an errno of ENOMEM, so in order to handle those exceptions
- # with other errnos, we nest this try block and have the outer
- # one handle the other instances.
- try:
- __ret = func(*args, **kwargs)
- except (MemoryError, EnvironmentError), __e:
- if isinstance(__e, EnvironmentError) and \
- __e.errno != errno.ENOMEM:
- raise
- logger.error("\n" + misc.out_of_memory())
- __ret = EXIT_OOPS
- except SystemExit, __e:
- raise __e
- except (IOError, misc.PipeError, KeyboardInterrupt), __e:
- # Don't display any messages here to prevent possible further
- # broken pipe (EPIPE) errors.
- if isinstance(__e, IOError) and __e.errno != errno.EPIPE:
- logger.error(str(__e))
- __ret = EXIT_OOPS
- except apx.VersionException, __e:
- logger.error("The pmcheckforupdates command appears out of "
- "sync with the libraries provided\nby pkg:/package/pkg. "
- "The client version is %(client)s while the library\n"
- "API version is %(api)s." % \
- {'client': __e.received_version,
- 'api': __e.expected_version})
- __ret = EXIT_OOPS
- except:
- traceback.print_exc()
- logger.error(traceback_str)
- __ret = 99
- return __ret
-
-
-if __name__ == "__main__":
- misc.setlocale(locale.LC_ALL, "")
- gettext.install("pkg", "/usr/share/locale",
- codeset=locale.getpreferredencoding())
-
- # Make all warnings be errors.
- warnings.simplefilter('error')
-
- __retval = handle_errors(main_func)
- try:
- logging.shutdown()
- except IOError:
- # Ignore python's spurious pipe problems.
- pass
- sys.exit(__retval)