#
# 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
# 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
#
#
#
#
# pkg - package system client utility
#
# The client is going to maintain an on-disk cache of its state, so that
# startup assembly of the graph is reduced.
#
# Client graph is of the entire local catalog. As operations progress, package
# states will change.
#
# Deduction operation allows the compilation of the local component of the
# catalog, only if an authoritative repository can identify critical files.
#
# Environment variables
#
# PKG_IMAGE - root path of target image
# PKG_IMAGE_TYPE [entire, partial, user] - type of image
# XXX or is this in the Image configuration?
try:
import calendar
import collections
import datetime
import errno
import getopt
import gettext
import itertools
import locale
import logging
import os
import re
import six
import socket
import sys
import tempfile
import textwrap
import time
import traceback
import pkg
else:
except KeyboardInterrupt:
import sys
default_attrs = {}
if atype == "depend":
if atype == "set":
_api_inst = None
def format_update_error(e):
# This message is displayed to the user whenever an
# ImageFormatUpdateNeeded exception is encountered.
"privileged user and then try again. Please note that updating "
"the format of the image will render it unusable with older "
"versions of the pkg(7) system."))
"""Emit an error message prefixed by the command name """
# Assume it's an object that can be stringified.
# If the message starts with whitespace, assume that it should come
# *before* the command-name prefix.
if cmd:
pkg_cmd = "pkg "
else:
pkg_cmd = "pkg: "
# This has to be a constant value as we can't reliably get our actual
# program name on all platforms.
"""Emit a usage message and optionally prefix it with a more
specific error message. Causes program to exit. """
if usage_error:
basic_usage = {}
adv_usage = {}
priv_usage = {}
"version"]
basic_usage["install"] = _(
"[-nvq] [-C n] [-g path_or_uri ...] [--accept]\n"
" [--licenses] [--no-be-activate] [--no-index] [--no-refresh]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [-r [-z zonename ... | -Z zonename ...]]\n"
" [--sync-actuators | --sync-actuators-timeout timeout]\n"
" [--reject pkg_fmri_pattern ... ] pkg_fmri_pattern ...")
basic_usage["uninstall"] = _(
"[-nvq] [-C n] [--ignore-missing] [--no-be-activate] [--no-index]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [-r [-z zonename ... | -Z zonename ...]]\n"
" [--sync-actuators | --sync-actuators-timeout timeout]\n"
" pkg_fmri_pattern ...")
basic_usage["update"] = _(
"[-fnvq] [-C n] [-g path_or_uri ...] [--accept] [--ignore-missing]\n"
" [--licenses] [--no-be-activate] [--no-index] [--no-refresh]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [-r [-z zonename ... | -Z zonename ...]]\n"
" [--sync-actuators | --sync-actuators-timeout timeout]\n"
" [--reject pkg_fmri_pattern ...] [pkg_fmri_pattern ...]")
basic_usage["list"] = _(
"[-Hafnqsuv] [-g path_or_uri ...] [--no-refresh]\n"
" [pkg_fmri_pattern ...]")
advanced_cmds = [
"info",
"contents",
"search",
"",
"verify",
"fix",
"revert",
"",
"mediator",
"set-mediator",
"unset-mediator",
"",
"variant",
"change-variant",
"",
"facet",
"change-facet",
"",
"avoid",
"unavoid",
"",
"freeze",
"unfreeze",
"",
"property",
"set-property",
"add-property-value",
"remove-property-value",
"unset-property",
"",
"publisher",
"set-publisher",
"unset-publisher",
"",
"history",
"purge-history",
"",
"rebuild-index",
"update-format",
"image-create",
"exact-install",
"",
"dehydrate",
"rehydrate"
]
adv_usage["info"] = \
_("[-lqr] [-g path_or_uri ...] [--license] [pkg_fmri_pattern ...]")
adv_usage["contents"] = _(
"[-Hmr] [-a attribute=pattern ...] [-g path_or_uri ...]\n"
" [-o attribute ...] [-s sort_key] [-t action_type ...]\n"
" [pkg_fmri_pattern ...]")
adv_usage["search"] = _(
"[-HIaflpr] [-o attribute ...] [-s repo_uri] query")
" [--unpackaged] [--unpackaged-only] [pkg_fmri_pattern ...]")
adv_usage["fix"] = _(
"[-Hnvq] [--no-be-activate]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [--accept] [--licenses] [--parsable version] [--unpackaged]\n"
" [pkg_fmri_pattern ...]")
adv_usage["revert"] = _(
"[-nv] [--no-be-activate]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" (--tagged tag-name ... | path-to-file ...)")
adv_usage["image-create"] = _(
"[-FPUfz] [--force] [--full|--partial|--user] [--zone]\n"
" [-k ssl_key] [-c ssl_cert] [--no-refresh]\n"
" [--variant <variant_spec>=<instance> ...]\n"
" [-g uri|--origin=uri ...] [-m uri|--mirror=uri ...]\n"
" [--facet <facet_spec>=(True|False) ...]\n"
" [(-p|--publisher) [<name>=]<repo_uri>] dir")
adv_usage["change-variant"] = _(
"[-nvq] [-C n] [-g path_or_uri ...] [--accept]\n"
" [--licenses] [--no-be-activate] [--no-index] [--no-refresh]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [-r [-z zonename ... | -Z zonename ...]]\n"
" [--sync-actuators | --sync-actuators-timeout timeout]\n"
" [--reject pkg_fmri_pattern ... ]\n"
" <variant_spec>=<instance> ...")
adv_usage["change-facet"] = _(
"[-nvq] [-C n] [-g path_or_uri ...] [--accept]\n"
" [--licenses] [--no-be-activate] [--no-index] [--no-refresh]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [-r [-z zonename ... | -Z zonename ...]]\n"
" [--sync-actuators | --sync-actuators-timeout timeout]\n"
" [--reject pkg_fmri_pattern ... ]\n"
" <facet_spec>=[True|False|None] ...")
adv_usage["set-mediator"] = _(
"[-nv] [-I <implementation>]\n"
" [-V <version>] [--no-be-activate]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" <mediator> ...")
" [--no-backup-be | --require-backup-be] [--backup-be-name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" <mediator> ...")
" [-g origin_to_add|--add-origin=origin_to_add ...]\n"
" [-G origin_to_remove|--remove-origin=origin_to_remove ...]\n"
" [-m mirror_to_add|--add-mirror=mirror_to_add ...]\n"
" [-M mirror_to_remove|--remove-mirror=mirror_to_remove ...]\n"
" [-p repo_uri] [--enable] [--disable] [--no-refresh]\n"
" [--reset-uuid] [--non-sticky] [--sticky]\n"
" [--search-after=publisher]\n"
" [--search-before=publisher]\n"
" [--search-first]\n"
" [--approve-ca-cert=path_to_CA]\n"
" [--revoke-ca-cert=hash_of_CA_to_revoke]\n"
" [--unset-ca-cert=hash_of_CA_to_unset]\n"
" [--set-property name_of_property=value]\n"
" [--add-property-value name_of_property=value_to_add]\n"
" [--remove-property-value name_of_property=value_to_remove]\n"
" [--unset-property name_of_property_to_delete]\n"
" [--proxy proxy to use]\n"
" [publisher]")
" [--licenses] [--no-be-activate] [--no-index] [--no-refresh]\n"
" [--no-backup-be | --require-backup-be] [--backup-be-name name]\n"
" [--deny-new-be | --require-new-be] [--be-name name]\n"
" [--reject pkg_fmri_pattern ... ] pkg_fmri_pattern ...")
priv_usage["remote"] = _(
"--ctlfd=file_descriptor --progfd=file_descriptor")
priv_usage["attach-linked"] = _(
"[-fnvq] [-C n] [--accept] [--licenses] [--no-index]\n"
" [--no-refresh] [--no-pkg-updates] [--linked-md-only]\n"
" [--allow-relink]\n"
" [--prop-linked <propname>=<propvalue> ...]\n"
" (-c|-p) <li-name> <dir>")
priv_usage["detach-linked"] = _(
"[-fnvq] [-a|-l <li-name>] [--no-pkg-updates] [--linked-md-only]")
priv_usage["audit-linked"] = _(
"[-H] [-a|-l <li-name>] [--no-parent-sync]")
priv_usage["sync-linked"] = _(
"[-nvq] [-C n] [--accept] [--licenses] [--no-index]\n"
" [--no-refresh] [--no-parent-sync] [--no-pkg-updates]\n"
" [--linked-md-only] [-a|-l <name>]")
priv_usage["set-property-linked"] = _(
"[-nvq] [--accept] [--licenses] [--no-index] [--no-refresh]\n"
" [--no-parent-sync] [--no-pkg-updates]\n"
" [--linked-md-only] <propname>=<propvalue> ...")
if cmd is "":
else:
# this should never happen - callers
# should check for valid subcommands
# before calling usage(..)
raise ValueError(
"Unable to find usage str for "
if use_txt is not "":
" pkg {cmd} "
else:
if cmd not in priv_usage:
else:
"subject to change at any time:"))
combined = {}
elif not full:
# The full list of subcommands isn't desired.
known_words = ["help"]
if candidates:
# Suggest correct subcommands if we can.
if verbose:
# Display a verbose usage message of subcommands.
Usage:
pkg [options] command [cmd_options] [operands]
"""))
Options:
-R dir
--no-network-cache
--help or -?
Environment:
PKG_IMAGE"""))
else:
# Display the full list of subcommands.
Usage: pkg [options] command [cmd_options] [operands]"""))
Package Information : list search info contents
Package Transitions : update install uninstall
history exact-install
Package Maintenance : verify fix revert
Publishers : publisher set-publisher unset-publisher
Package Configuration: mediator set-mediator unset-mediator
facet change-facet
variant change-variant
Image Constraints : avoid unavoid freeze unfreeze
Image Configuration : refresh rebuild-index purge-history
property set-property add-property-value
unset-property remove-property-value
Miscellaneous : image-create dehydrate rehydrate
For more info, run: pkg help <command>"""))
""" Convenience routine to check that input args are valid fmris. """
res = []
errors = []
if not err:
continue
# For version errors, include the pattern so
# that the user understands why it failed.
err))
else:
# Including the pattern is redundant for other
# exceptions.
if errors:
verbose):
"""List packages."""
if verbose:
fmt_str = "{0:76} {1}"
elif summary:
fmt_str = "{0:55} {1}"
else:
fmt_str = "{0:49} {1:26} {2}"
state_map = [
[("installed", "i")],
[("frozen", "f")],
[
("obsolete", "o"),
("renamed", "r")
],
]
if ppub:
# getting json output.
errors = None
if "errors" in out_json:
if "data" in out_json:
if quiet:
continue
if not omit_headers:
if verbose:
"FMRI", "IFO"))
elif summary:
"NAME (PUBLISHER)",
"SUMMARY"))
else:
"NAME (PUBLISHER)",
"VERSION", "IFO"))
status = ""
for s, v in sentry:
if s in entry["states"]:
st = v
break
else:
st = "-"
entry["version"]
spub = ""
else:
# Display full FMRI (without build version) for
# verbose case.
# Use class method instead of creating an object for
# performance reasons.
if verbose:
continue
# Display short FMRI + summary.
if summary:
if summ is None:
summ = ""
continue
# Default case; display short FMRI and version info.
# Print errors left.
if errors:
return out_json["status"]
def get_tracker():
if global_settings.client_output_parsable_version is not None:
# This logic handles linked images: for linked children
# we elide the progress output.
"w")
else:
try:
except progress.ProgressTrackerException:
return progresstracker
"""Helper function that marks all licenses for the current plan as
accepted if they require acceptance."""
if not dest.must_accept:
continue
"actions", "boot-archive"]
"""Helper function to display plan to the desired degree.
Verbose can either be a numerical value, or a list of
items to display"""
disp = ["basic"]
if verbose > 0:
"release-notes", "editable", "actuators"])
if verbose > 1:
if verbose > 2:
else:
if plan.must_display_notes():
# Warn the user since this isn't likely what they wanted.
WARNING: The boot environment being modified is not the active one. Changes
made in the active BE will not be reflected on the next boot.
"""))
else:
WARNING: The boot environment being modified is not the active one. Changes
made will not be reflected on the next boot.
"""))
a, r, i, c = [], [], [], []
if dest is None:
elif src is None:
else:
# Changing or repairing package content (e.g. fix,
# change-facet, etc.)
if val:
return _("Yes")
return _("No")
status = []
if "basic" in disp:
if v:
else:
if verbose:
# Only show space information in verbose mode.
if abytes:
_("Estimated space to be consumed:"),
# only display BE information if we're operating on the
# liveroot environment (since otherwise we'll never be
# manipulating BEs).
if api_inst.is_liveroot:
# Only show activation status if verbose or
# if new BE will not be activated.
if "boot-archive" in disp:
# Right-justify all status strings based on length of longest string.
if status:
for s in status:
if need_blank:
for x in mediators:
# output has trailing blank
if need_blank:
for x in varcets:
if "solver-errors" in disp:
for l in plan.get_solver_errors():
if first:
if need_blank:
if "fmris" in disp:
else:
# Only display timestamp if version is same and
# timestamp is not between the two fmris.
if include_ts:
else:
if include_ts:
else:
elif dest:
else:
if changed:
if need_blank:
last_parent = None
):
if pparent != last_parent:
if "actuators" in disp:
# print pkg which have been altered due to pkg actuators
# e.g:
#
# Package-triggered Operations:
# TriggerPackage
# update
# PackageA
# PackageB
# uninstall
# PackageC
if first:
if need_blank:
_("Package-triggered Operations:"))
last_action = None
if last_action is None:
if need_blank:
if action != last_action:
# Displaying editable file list is redundant for pkg fix.
cfg_change_fmt = " {0}"
cfg_changes = []
if first:
chg_hdr))
_("Remove:"))
_("Install:"))
_("Update:"))
if cfg_changes:
if need_blank:
for l in cfg_changes:
if "actions" in disp:
if need_blank:
for a in plan.get_actions():
if plan.has_release_notes():
if need_blank:
if "release-notes" in disp:
for a in plan.get_release_notes():
else:
else:
if tmp_path:
"""try to write release notes out to a file in /tmp and return the name"""
try:
# make file world readable
for a in plan.get_release_notes():
return path
except Exception:
pass
"""Display the parsable version of the plan."""
# Set the default values.
added_fmris = []
removed_fmris = []
changed_fmris = []
affected_fmris = []
backup_be_name = None
be_name = None
space_available = None
space_required = None
facets_changed = []
variants_changed = []
services_affected = []
mediators_changed = []
editables_changed = []
pkg_actuators = {}
item_messages = {}
licenses = []
if child_images is None:
child_images = []
release_notes = []
if plan:
# Lists of lists are used here becuase json will
# convert lists of tuples into lists of lists
# anyway.
else:
elif rem is not None:
else:
# Lists of lists are used here to ensure a consistent ordering
# and because tuples will be convereted to lists anyway; a
# dictionary would be more logical for the top level entries,
# but would make testing more difficult and this is a small,
# known set anyway.
if emoved:
if eremoved:
if einstalled:
if eupdated:
for n in plan.get_release_notes():
plan.get_licenses():
src_tup = ()
if src_li:
dest_tup = ()
if dest_li:
ret = {
"activate-be": be_activated,
"backup-be-name": backup_be_name,
"be-name": be_name,
"boot-archive-rebuild": boot_archive_rebuilt,
"change-editables": editables_changed,
"child-images": child_images,
"create-backup-be": backup_be_created,
"create-new-be": new_be_created,
"image-name": None,
"item-messages": item_messages,
"release-notes": release_notes,
"space-available": space_available,
"space-required": space_required,
"version": parsable_version,
}
if pkg_actuators:
# The image name for the parent image is always None. If this image is
# a child image, then the image name will be set when the parent image
# processes this dictionary.
"""Helper function to display licenses for the current plan.
'show_all' is an optional boolean value indicating whether all licenses
should be displayed or only those that have must-display=true."""
continue
# License already displayed, so doesn't need to be
# displayed again.
continue
if show_req:
# Mark license as having been displayed.
"""Display plan function."""
if not plan:
return
# we should have displayed licenses earlier so mark all
# licenses as having been displayed.
return
if not quiet and parsable_version is None and \
# nothing todo
if op == PKG_OP_UPDATE:
s = _("No updates available for this image.")
else:
s = _("No updates necessary for this image.")
msg(s)
# Even nothingtodo, but need to continue to display INFO
# message if verbose is True.
return
if parsable_version is None and not quiet_plan:
if not quiet and not quiet_plan:
if parsable_version is not None:
elif not quiet:
if not quiet_plan:
# Ensure a blank line is inserted before the message
# output.
msg()
last_item_id = None
if ntd:
msg(_("Could not repair: {0:50}"
else:
msg(_("Repairing: {0:50}"
if op == PKG_OP_FIX:
# If verbose is False, don't display
# any INFO messages.
continue
if not omit_headers:
pkg_name=_("PACKAGE"),
result=_("STATUS")))
if print_packaged:
last_item_id = None
if msg_type == MSG_UNPACKAGED:
continue
if ntd:
msg(_("Could not repair: {0:50}"
else:
msg(_("Repairing: {0:50}"
# If verbose is False, don't display
# any INFO messages.
continue
if not omit_headers:
msg(_("{pkg_name:70} {result:>7}"
result=_("STATUS")))
# Top level message.
if not parent_id:
elif item_id == "overlay_errors":
elif last_item_id != item_id:
# A new action id; we need to print it out and
# then group its subsequent messages.
msg_text))
else:
else:
if not omit_headers:
msg(_("UNPACKAGED CONTENTS"))
# Print warning messages at the beginning.
if msg_type != MSG_UNPACKAGED:
continue
if msg_level == MSG_WARNING:
# Print the rest of messages.
if msg_type != MSG_UNPACKAGED:
continue
return did_print_something
"""Callback function for displaying plan."""
if plan_only:
return
if not plan:
return
# we should have displayed licenses earlier so mark all
# licenses as having been displayed.
return
if not quiet and parsable_version is None and \
# nothing todo
if op == PKG_OP_UPDATE:
s = _("No updates available for this image.")
else:
s = _("No updates necessary for this image.")
msg(s)
# Even nothingtodo, but need to continue to display INFO
# message if verbose is True.
return
if parsable_version is None and not quiet_plan:
if not quiet and not quiet_plan:
if parsable_version is not None:
elif not quiet:
if not quiet_plan:
# Ensure a blank line is inserted before the message
# output.
msg()
# Message print for package verification result.
if not unpackaged_only:
# Print an extra line to separate output between
# packaged and unpackaged content.
MSG_UNPACKAGED for entry in
if unpackaged or unpackaged_only:
"""Print out any messages generated during the specified
stages."""
if not plan:
return
elif msg_level == MSG_WARNING:
else:
"""Prepare plan."""
# Exceptions which happen here are printed in the above level, with
# or without some extra decoration done here.
# XXX would be nice to kick the progress tracker.
try:
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
return EXIT_OOPS
except api_errors.TransportError as e:
# move past the progress tracker line.
msg("\n")
raise e
except api_errors.PlanLicenseErrors as e:
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
error(_("The following packages require their "
"licenses to be accepted before they can be installed "
"or updated: "))
"terms of the licenses of the packages listed above, "
"use the --accept option. To display all of the related "
"licenses, use the --licenses option."))
return EXIT_LICENSE
except api_errors.InvalidPlanError as e:
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
return EXIT_OOPS
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.ImageInsufficentSpace as e:
return EXIT_OOPS
except KeyboardInterrupt:
raise
except:
error(_("\nAn unexpected error happened while preparing for "
raise
finally:
return EXIT_OK
"""Execute plan."""
rval = None
try:
if pd.actuator_timed_out:
else:
except RuntimeError as e:
except (api_errors.InvalidPlanError,
api_errors.InvalidPackageErrors) as e:
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
except (api_errors.LinkedImageException) as e:
error(_("{operation} failed (linked image exception(s)):\n"
rval = e.lix_exitrv
error(_("Requested \"{0}\" operation would affect files that "
"cannot be modified in live image.\n"
"Please retry this operation on an alternate boot "
except api_errors.CorruptedIndexException as e:
error("The search index appears corrupted. Please rebuild the "
"index with 'pkg rebuild-index'.")
except api_errors.ProblematicPermissionsIndexException as e:
error(_("\n(Failure to consistently execute pkg commands as a "
"privileged user is often a source of this problem.)"))
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
except api_errors.ImageFormatUpdateNeeded as e:
except api_errors.BEException as e:
error(e)
raise
except api_errors.ImageInsufficentSpace as e:
except Exception as e:
error(_("An unexpected error happened during "
"{operation}: {err}").format(
raise
finally:
if rval is None:
# Store original exception so that the real cause of
# failure can be raised if this fails.
try:
# Only show salvaged file list if populated
# and operation was successful, or if operation
# failed and a new BE was not created for
# the operation.
"editable files and directories were\n"
"salvaged while executing the requested "
"package operation; they\nhave been moved "
"to the displayed location in the image:\n"))
except Exception:
if rval is not None:
# Only raise exception encountered here if the
# exception previously raised was suppressed.
raise
else:
raise exc_value
return rval
"""Allocate API instance."""
try:
progresstracker, None, PKG_CLIENT_NAME,
except api_errors.ImageNotFoundException as e:
if e.user_specified:
if pkg_image_used:
error(_("No image rooted at '{0}' "
else:
e.user_dir))
else:
error(_("No image found."))
return
except api_errors.PermissionsException as e:
error(e)
return
except api_errors.ImageFormatUpdateNeeded as e:
return
"""Handle plan exception."""
return EXIT_OOPS
return EXIT_OOPS
error(_("{operation} failed (linked image exception(s)):\n"
return e.lix_exitrv
msg(_("""\
WARNING: pkg(7) appears to be out of date, and should be updated before
running {op}. Please update pkg(7) by executing 'pkg install
return EXIT_OOPS
return EXIT_OOPS
return EXIT_OOPS
if verbose:
return EXIT_OOPS
return EXIT_OOPS
return EXIT_OOPS
return EXIT_NOTLIVE
return EXIT_OOPS
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
if e.multiple_matches:
"FMRIs listed above to the install command.")
if verbose:
if e.invalid_mediations:
# Bad user input for mediation.
return EXIT_BADOPT
return EXIT_OOPS
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
return EXIT_OOPS
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
return EXIT_OOPS
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
return EXIT_OOPS
return EXIT_OOPS
# if we didn't deal with the exception above, pass it on.
raise
# NOTREACHED
"""API plan invocation entry."""
# All the api interface functions that we invoke have some
# common arguments. Set those up now.
if _origins:
if _stage != API_STAGE_DEFAULT:
# display plan debugging information
if _verbose > 2:
# plan the requested operation
stuff_to_do = None
if _op == PKG_OP_ATTACH:
elif _op == PKG_OP_DEHYDRATE:
elif _op == PKG_OP_DETACH:
elif _op == PKG_OP_EXACT_INSTALL:
elif _op == PKG_OP_FIX:
elif _op == PKG_OP_INSTALL:
elif _op == PKG_OP_REHYDRATE:
elif _op == PKG_OP_REVERT:
elif _op == PKG_OP_SYNC:
elif _op == PKG_OP_UNINSTALL:
elif _op == PKG_OP_UPDATE:
else:
child_plans = []
try:
if planned_self:
# we don't display anything for child images
# since they currently do their own display
# work (unless parsable output is requested).
continue
# the first plan description is always for ourself.
# if we're in parsable mode don't display anything
# until after we finish planning for all children
if _parsable_version is None:
# if requested accept licenses for child images. we
# have to do this before recursing into children.
if _accept:
except:
return rv
if not planned_self:
# if we got an exception we didn't do planning for children
# if we didn't get an exception and we're a parent image then
# we should have done planning for child images.
# if we didn't display our own plan (due to an exception), or if we're
# in parsable mode, then display our plan now.
if not planned_self or _parsable_version is not None:
try:
except api_errors.ApiException as e:
return EXIT_OOPS
# if we didn't accept licenses (due to an exception) then do that now.
if not planned_self and _accept:
return EXIT_OK
"""Return the path to the PlanDescription save file."""
"""Save an image plan to a file."""
# get a pointer to the plan
# save the PlanDescription to a file
try:
# cleanup any old style imageplan save files
except OSError as e:
raise api_errors._convert_error(e)
"""Loan an image plan from a file."""
# load an existing plan
try:
except OSError as e:
raise api_errors._convert_error(e)
if stage == API_STAGE_EXECUTE:
"""Delete an image plan file."""
try:
except OSError as e:
raise api_errors._convert_error(e)
"""Determine the exit code of pkg verify, which should be based on
whether we find errors."""
return EXIT_OOPS
return EXIT_OK
"""Do something that involves the api.
Arguments prefixed with '_' are primarily used within this
function. All other arguments must be specified via keyword
assignment and will be passed directly on to the api
interfaces being invoked."""
# create a new plan
return rv
# We always save the plan, even if it is a noop. We
# do this because we want to be able to verify that we
# can load and execute a noop plan. (This mimics
# normal api behavior which doesn't prevent an api
# consumer from creating a noop plan and then
# preparing and executing it.)
# for pkg verify
return _verify_exit_code(_api_inst)
if _api_inst.planned_nothingtodo():
return EXIT_NOP
return EXIT_OK
else:
# Exceptions which happen here are printed in the above level,
# with or without some extra decoration done here.
return ret_code
if _stage == API_STAGE_PREPARE:
return EXIT_OK
return ret_code
"""RPC Server Class which invoked by the PipedRPCServer when a RPC
request is recieved."""
# if we were called with no arguments then pwargs will be []
if pwargs == []:
pwargs = {}
op_supported = [
]
if op not in op_supported:
raise Exception(
# if a stage was specified, get it.
assert stage in api_stage_values
# if we're starting a new operation, reset the api. we do
# this just in case our parent updated our linked image
# metadata.
if "pargs" not in pwargs:
pwargs["pargs"] = []
if DebugValues["timings"]:
return rv
"""Primary RPC dispatch function.
This function must be kept super simple because if we take an
exception here then no output will be generated and this
package remote process will silently exit with a non-zero
return value (and the lack of an exception message makes this
failure very difficult to debug). Hence we wrap the real
remote dispatch routine with a call to handle_errors(), which
will catch and display any exceptions encountered."""
# flush output before and after every operation.
return rv
"""Execute commands from a remote pipe"""
#
# this is kinda a gross hack. SocketServer.py uses select.select()
# which doesn't support file descriptors larger than FD_SETSIZE.
# Since ctlfd may have been allocated in a parent process with many
# file descriptors, it may be larger than FD_SETSIZE. Here in the
# child, though, the majority of those have been closed, so os.dup()
# should return a lower-numbered descriptor which will work with
# select.select().
#
"""Attempt to change a variant associated with an image, updating
the image contents as necessary."""
if not xrval:
return EXIT_OOPS
if not pargs:
# '=' is not allowed in variant names or values
usage(_("{0}: variants must to be of the form "
# get the variant name and value
# forcibly lower-case for 'true' or 'false'
# make sure the user didn't specify duplicate variants
usage(_("{subcmd}: duplicate variant specified: "
"""Attempt to change the facets as specified, updating
image as necessary"""
if not xrval:
return EXIT_OOPS
if not pargs:
facets = {}
allowed_values = {
"TRUE" : True,
"FALSE": False,
"NONE" : None
}
# '=' is not allowed in facet names or values
usage(_("{0}: facets must to be of the form "
# get the facet name and value
usage(_("{0}: facets must to be of the form "
"""This is the main client_json_api output handling function used for
install, update and uninstall and so on."""
if "errors" in out_json:
return out_json["status"]
"""Callback for emitting general errors."""
if status == EXIT_BADOPT:
# Usage errors are not in any specific type, print it only
# there is no selected type.
if not selected_type:
else:
return False
elif "errtype" in err:
# if the selected_type is specified and err not in selected type,
# Don't print and return False.
return False
emsg("\n")
emsg(_("To continue, execute 'pkg update-format' as a "
"privileged user and then try again. Please note "
"that updating the format of the image will render "
"it unusable with older versions of the pkg(7) "
"system."))
return False
if "reason" in err:
elif "info" in err:
return False
if "reason" in err:
emsg(" ")
return False
return False
emsg(_("To indicate that you "
"agree to and accept the terms of the licenses of "
"the packages listed above, use the --accept "
"option. To display all of the related licenses, "
"use the --licenses option."))
return False
emsg(" ")
emsg("Use -af to allow all versions.")
return False
emsg(_("""
To add a publisher using this repository, execute the following command as a
privileged user:
pkg set-publisher -g {0} <publisher>
elif "info" in err:
elif "reason" in err:
else:
if selected_type:
return False
if "reason" in err:
elif "info" in err:
return True
"""Generate error messages."""
# Return errors not being printed.
return errs_left
"""Attempt to take package specified to INSTALLED state.
The operands are interpreted as glob patterns."""
"""Attempt to take package specified to INSTALLED state. The operands
are interpreted as glob patterns."""
"""Attempt to take all installed packages specified to latest
version."""
"""Attempt to take package specified to DELETED state."""
"""Determine if installed packages match manifests."""
# Print error messages.
if "errors" in out_json:
# Since the verify output has been handled by display_plan_cb, only
# status code needs to be returned.
return out_json["status"]
"""Attempt to revert files to their original state, either
via explicit path names or via tagged contents."""
if not pargs:
"""Minimize image size for later redeployment."""
"""Restore content removed from a dehydrated image."""
"""Fix packaging errors found in the image."""
# Print error messages.
if "errors" in out_json:
return out_json["status"]
"""Display configured or available mediator version(s) and
implementation(s)."""
subcommand = "mediator"
if output_format is None:
output_format = "default"
# mediator information is returned as a dictionary of dictionaries
# of version and implementation indexed by mediator name.
if list_available:
else:
# Configured mediator information
gen_mediators = (
)
# Set minimum widths for mediator and version columns by using the
# length of the column headers and values to be displayed.
else:
def gen_listing():
):
yield {
"mediator": mediator,
"implementation": med_impl,
"implementation-source"),
"implementation-version": med_impl_ver,
}
# MEDIATOR VER. SRC. VERSION IMPL. SRC. IMPLEMENTATION IMPL. VER.
# <med_1> <src_1> <ver_1> <src_1> <impl_1_value> <impl_1_ver>
# <med_2> <src_2> <ver_2> <src_2> <impl_2_value> <impl_2_ver>
# ...
field_data = {
"mediator" : [("default", "json", "tsv"), _("MEDIATOR"), ""],
"version" : [("default", "json", "tsv"), _("VERSION"), ""],
"version-source": [("default", "json", "tsv"), _("VER. SRC."), ""],
"implementation" : [("default", "json", "tsv"), _("IMPLEMENTATION"),
""],
"implementation-source": [("default", "json", "tsv"),
_("IMPL. SRC."), ""],
"implementation-version" : [("json", "tsv"), _("IMPL. VER."), ""],
}
_("IMPL. SRC."), _("IMPLEMENTATION"), _("IMPL. VER."))
# Default output formatting.
"} {4}"
msg(_("WARNING: pkg mediators may not be accurately shown "
"when one or more publishers have been dehydrated. The "
"correct mediation will be applied when the publishers "
"are rehydrated."))
return EXIT_PARTIAL
if requested_mediators and not found:
if output_format == "default":
# Don't pollute other output formats.
error(_("no matching mediators found"),
return EXIT_OOPS
return EXIT_OK
verbose):
mediator(s)."""
if not pargs:
usage(_("at least one mediator must be specified"),
if not (med_version or med_implementation):
if verbose > 2:
# The user may specify 'None' as a special value to explicitly
# request mediations that do not have the related component.
for m in pargs:
if med_version == "":
# Request reset of version.
mediators[m]["version"] = None
elif med_version == "None":
# Explicit selection of no version.
elif med_version:
if med_implementation == "":
# Request reset of implementation.
mediators[m]["implementation"] = None
elif med_implementation == "None":
# Explicit selection of no implementation.
elif med_implementation:
stuff_to_do = None
try:
continue
except:
return ret_code
if not stuff_to_do:
if verbose:
if parsable_version is not None:
try:
except api_errors.ApiException as e:
return EXIT_OOPS
else:
msg(_("No changes required."))
return EXIT_NOP
msg(_("WARNING: pkg mediators may not be accurately shown "
"when one or more publishers have been dehydrated. The "
"correct mediation will be applied when the publishers "
"are rehydrated."))
if not quiet:
if parsable_version is not None:
try:
except api_errors.ApiException as e:
return EXIT_OOPS
if noexecute:
return EXIT_OK
return ret_code
return ret_code
verbose):
mediator(s)."""
if not pargs:
usage(_("at least one mediator must be specified"),
if verbose > 2:
# Build dictionary of mediators to unset based on input.
if not (med_version or med_implementation):
# Unset both if nothing specific requested.
for m in pargs:
if med_version:
mediators[m]["version"] = None
mediators[m]["implementation"] = None
stuff_to_do = None
try:
continue
except:
return ret_code
if not stuff_to_do:
if verbose:
if parsable_version is not None:
try:
except api_errors.ApiException as e:
return EXIT_OOPS
else:
msg(_("No changes required."))
return EXIT_NOP
if not quiet:
if parsable_version is not None:
try:
except api_errors.ApiException as e:
return EXIT_OOPS
if noexecute:
return EXIT_OK
return ret_code
return ret_code
"""Place the specified packages on the avoid list"""
if not args:
return __display_avoids(api_inst)
try:
return EXIT_OK
except:
"""Remove the specified packages from the avoid list"""
if not args:
return __display_avoids(api_inst)
try:
return EXIT_OK
except:
"""Display the current avoid list, and the pkgs that are tracking
that pkg"""
for a in api_inst.get_avoid_list():
if tracking:
" {avoid_pkg} (group dependency of "
"'{tracking_pkg}')")
else:
return EXIT_OK
"""Place the specified packages on the frozen list"""
comment = None
if opt == "-H":
elif opt == "-c":
elif opt == "-n":
if not display_headers and pargs:
"currently frozen packages."))
if not pargs:
try:
if ts:
return EXIT_OK
except api_errors.FreezePkgsException as e:
return EXIT_OOPS
except:
"""Remove the specified packages from the frozen list"""
if opt == "-H":
elif opt == "-n":
if not pargs:
try:
if not pkgs:
return EXIT_NOP
for s in pkgs:
return EXIT_OK
except:
"""Display the current frozen list"""
try:
except api_errors.ApiException as e:
error(e)
return EXIT_OOPS
return EXIT_OK
fmt = "{name:18} {ver:27} {time:24} {comment}"
if display_headers:
name=_("NAME"),
ver=_("VERSION"),
time=_("DATE"),
comment=_("COMMENT")
))
if ts:
if not comment:
comment = "None"
))
return EXIT_OK
"""Converts a string to a three tuple with the information to fill
the INDEX, ACTION, and VALUE columns.
The "a_str" parameter is the string representation of an action.
The "match" parameter is a string whose precise interpretation is given
below.
For most action types, match defines which attribute the query matched
with. For example, it states whether the basename or path attribute of
a file action matched the query. Attribute (set) actions are treated
differently because they only have one attribute, and many values
associated with that attribute. For those actions, the match parameter
states which value matched the query."""
"""Given an action and a match value (see convert_output for more
details on this parameter), return the token which matched the query."""
return match
if match == "basename":
if r:
return r
"""Given an action and a match value (see convert_output for more
details on this parameter), return the kind of match this was. For
example, if the query matched a portion of a path of an action, this
will return 'path'. If the action is an attribute action, it returns
the name set in the action. """
return match
"""Given a result from search, massages the information into a form
useful for pkg.misc.list_actions_by_attrs.
The "return_type" parameter is an enumeration that describes the type
of the information that will be converted.
The type of the "tup" parameter depends on the value of "return_type".
If "return_type" is action information, "tup" is a three-tuple of the
fmri name, the match, and a string representation of the action. In
the case where "return_type" is package information, "tup" is a one-
tuple containing the fmri name.
The "pub" parameter contains information about the publisher from which
the result was obtained."""
action = None
match = None
match_type = None
try:
except ValueError:
error(_("The repository returned a malformed result.\n"
return False
try:
except actions.ActionError as e:
error(_("The repository returned an invalid or "
"unsupported action.\n{0}").format(e))
return False
else:
"""Search for the given query."""
# Constants which control the paging behavior for search output.
page_timeout = .5
max_timeout = 5
min_page_size = 5
"search.match", "pkg.shortfmri"]
servers = []
attrs = []
if opt == "-H":
elif opt == "-a":
elif opt == "-f":
elif opt == "-l":
elif opt == "-o":
elif opt == "-p":
elif opt == "-r":
elif opt == "-s":
elif opt == "-I":
if not pargs:
usage(_("at least one search term must be provided"),
cmd="search")
for a in attrs:
if not return_actions:
usage(_("action level options ('{0}') to -o "
"cannot be used with the -p "
break
searches = []
# Strip pkg:/ or pkg:/// from the fmri.
# If fmri has pkg:// then strip the prefix
# from 'pkg://' upto the first slash.
try:
except api_errors.BooleanQueryException as e:
error(e)
return EXIT_OOPS
except api_errors.ParseError as e:
error(e)
return EXIT_OOPS
try:
if local:
if remote:
# By default assume we don't find anything.
# get initial set of results
widths = []
st = None
err = None
last_line = None
while page_again:
unprocessed_res = []
# Indexless search raises a slow search exception. In
# that case, catch the exception, finish processing the
# results, then propogate the error.
try:
if not st:
try:
(v, return_type, tmp) = \
except ValueError as e:
error(_("The repository "
"returned a malformed "
"result:{0!r}").format(
continue
# This check is necessary since a
# a pacakge search can be specified
# using the <> operator.
if action_attr and \
return_type != \
usage(_("action level options "
"to -o cannot be used with "
"the queries that return "
if use_default_attrs and not justs:
if return_type == \
attrs = \
header_attrs = \
["index", "action",
"value", "package"]
else:
header_attrs = \
["package",
"publisher"]
if ret:
# Check whether the paging timeout
# should be increased.
if len(unprocessed_res) > \
break
else:
page_timeout = min(
page_timeout * 2,
except api_errors.ApiException as e:
err = e
if not lines:
continue
old_widths = widths[:]
# If headers are being displayed and the layout of the
# columns have changed, print the headers again using
# the new widths.
if display_headers and (not shown_headers or
if err:
raise err
except (api_errors.IncorrectIndexFileHash,
error(_("The search index appears corrupted. Please "
"rebuild the index with 'pkg rebuild-index'."))
return EXIT_OOPS
except api_errors.ProblematicSearchServers as e:
error(e)
except api_errors.SlowSearchUsed as e:
error(e)
except (api_errors.IncorrectIndexFileHash,
error(_("The search index appears corrupted. Please "
"rebuild the index with 'pkg rebuild-index'."))
return EXIT_OOPS
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.ApiException as e:
error(e)
return EXIT_OOPS
elif bad_res:
elif good_res:
return retcode
"""Display information about a package or packages.
"""
if "data" in ret_json:
# display_license is true.
data_type = "licenses"
data_type = "package_attrs"
if not quiet and i > 0:
msg("")
if display_license and not quiet:
continue
try:
)
except ValueError:
# Only display header if there are
# other attributes to show.
continue
if res:
if "errors" in ret_json:
cmd="info")
return ret_json["status"]
"""Given a set of lines and a set of attributes, calculate the minimum
width each column needs to hold its contents."""
if not widths:
for l in lines:
for i, a in enumerate(l):
return widths
"""Given a set of output attributes, find any attributes with known
justification directions and assign them."""
"pkg.name", "pkg.fmri", "pkg.shortfmri", "pkg.publisher"]:
return JUST_LEFT
return JUST_UNKNOWN
def default_left(v):
"""For a given justification "v", use the default of left justification
if "v" is JUST_UNKNOWN."""
if v == JUST_UNKNOWN:
return JUST_LEFT
return v
"""Print out the headers for the columns in the output.
The "attrs" parameter provides the headings that should be used.
The "widths" parameter provides the current estimates of the width
for each column. These may be changed due to the length of the headers.
This function does modify the values contained in "widths" outside this
function.
The "justs" parameter contains the justifications to use with each
header."""
headers = []
# Now that we know all the widths, multiply them by the
# justification values to get positive or negative numbers to
# pass to the format specifier.
fmt = ""
if widths[n] < 0:
else:
def guess_unknown(j, v):
"""If the justificaton to use for a value is unknown, assume that if
it is an integer, the output should be right justified, otherwise it
should be left justified."""
if j != JUST_UNKNOWN:
return j
try:
int(v)
return JUST_RIGHT
except (ValueError, TypeError):
# attribute is non-numeric or is something like
# a list.
return JUST_LEFT
"""Produce a format string that can be used to display results.
The "display_headers" parameter is whether headers have been displayed
or not. If they have not, then use a simple tab system. If they
have, use the information in the other parameters to control the
formatting of the line.
The "widths" parameter contains the width to use for each column.
The "justs" parameter contains the justifications to use for each
column.
The "line" parameter contains the information that will be displayed
using the resulting format. It's needed so that a choice can be made
about columns with unknown justifications.
"""
fmt = ""
if display_headers:
# Now that we know all the widths, multiply them by the
# justification values to get positive or negative numbers to
# pass to the format specifier.
line_widths = [
w * guess_unknown(j, a)
]
if line_widths[n] < 0:
-line_widths[n])
else:
line_widths[n])
return fmt
return fmt
"""Print results of a "list" operation. Returns False if no output
was produced."""
if sort_attrs:
sortidx = 0
sortidx = i
break
# Sort numeric columns numerically.
def key_extract(x):
try:
except (ValueError, TypeError):
return 0
else:
key_extract = lambda x: x[sortidx]
else:
if not text:
continue
if not printed_output and display_headers:
return printed_output
"""For a set of output attributes ("attrs") passed to a command ("cmd"),
if the attribute lives in a known name space, check whether it is valid.
"""
if reference is None:
if prefixes is None:
for a in attrs:
for p in prefixes:
if a.startswith(p) and not a in reference:
cmd)
"""List package contents.
If no arguments are given, display for all locally installed packages.
With -H omit headers and use a tab-delimited format; with -o select
attributes to display; with -s, specify attributes to sort on; with -t,
specify which action types to list."""
subcommand = "contents"
attrs = []
sort_attrs = []
action_types = []
attr_match = {}
if opt == "-H":
elif opt == "-a":
try:
except ValueError:
usage(_("-a takes an argument of the form "
elif opt == "-g":
elif opt == "-o":
elif opt == "-s":
elif opt == "-t":
elif opt == "-r":
elif opt == "-m":
if origins:
elif not remote:
usage(_("contents: must request remote contents for specific "
if display_raw:
attrs = ["action.raw"]
usage(_("-m and {0} may not be specified at the same "
if action_types:
for atype in action_types
if atype not in default_attrs]
if invalid_atype == action_types:
usage(_("no valid action types specified"),
elif invalid_atype:
emsg(_("""\
WARNING: invalid action types specified: {0}
if local:
elif remote:
#
# If the user specifies no specific attrs, and no specific
# sort order, then we fill in some defaults.
#
if not attrs:
if not action_types:
# XXX Possibly have multiple exclusive attributes per
# column? If listing dependencies and files, you could
# files and fmris for dependencies.
else:
# Choose default attrs based on specified action
# types. A list is used here instead of a set is
# because we want to maintain the order of the
# attributes in which the users specify.
for atype in action_types):
if not sort_attrs and not display_raw:
# XXX reverse sorting
# Most likely want to sort by path, so don't force people to
# make it explicit
if "path" in attrs:
else:
# if we want a raw display (contents -m), disable the automatic
# variant filtering that normally limits working set.
if display_raw:
else:
# Now get the matching list of packages and display it.
try:
manifests = []
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.InvalidPackageErrors as e:
return EXIT_OOPS
except api_errors.CatalogRefreshException as e:
return EXIT_OOPS
except api_errors.InventoryException as e:
if e.illegal:
for i in e.illegal:
error(i)
return EXIT_OOPS
else:
return EXIT_OOPS
# Build a generator expression based on whether specific action types
# were provided.
if action_types:
# If query is limited to specific action types, use the more
# efficient type-based generation mechanism.
gen_expr = (
(m.fmri, a, None, None, None)
for m in manifests
for a in m.gen_actions_by_types(action_types,
)
else:
gen_expr = (
(m.fmri, a, None, None, None)
for m in manifests
)
# Determine if the query returned any results by "peeking" at the first
# value returned from the generator expression.
try:
except StopIteration:
found = None
actionlist = []
if found:
# If any matching entries were found, create a new generator
# expression using itertools.chain that includes the first
# result.
pkg: contents: no matching actions found in the listed packages"""))
if not displayed_results:
if output_fields:
This package contains no actions with the fields specified using the -o
option. Please specify other fields, or use the -m option to show the raw
package manifests.""", """\
These packages contain no actions with the fields specified using the -o
option. Please specify other fields, or use the -m option to show the raw
else:
This package delivers no filesystem content, but may contain metadata. Use
the -o option to specify fields other than 'path', or use the -m option to show
the raw package manifests.""", """\
These packages deliver no filesystem content, but may contain metadata. Use
the -o option to specify fields other than 'path', or use the -m option to show
if notfound:
if manifests:
if local:
pkg: contents: no packages matching the following patterns you specified are
installed on the system. Try specifying -r to query remotely:"""))
elif remote:
pkg: contents: no packages matching the following patterns you specified were
examining the catalogs:"""))
for p in notfound:
else:
return rval
partial = 0
refresh_errstr = ""
partial += 1
refresh_errstr += _("\n{0}/{1} repositories for " \
"publisher '{2}' could not be reached for " \
"catalog refresh.\n").format(
refresh_errstr += "\n"
refresh_errstr += str(e)
refresh_errstr += "\n"
else:
partial_str = ":"
if partial:
txt = _("pkg: {succeeded}/{total} catalogs successfully "
# This ensures that the text gets printed before the errors.
else:
if ignore_perms_failure and \
# If any errors other than a permissions exception are
# found, then don't ignore them.
break
# Consider those that failed to have succeeded and add them
# to the actual successful total.
if cre.errmessage:
outstr = """
WARNING: Errors were encountered when attempting to retrieve package
catalog information. Packages added to the affected publisher repositories since
the last retrieval may not be available.
"""
# This pub did not have any repo problems, ignore.
continue
repo_str = _("repository")
else:
outstr += _("Errors were encountered when attempting to " \
outstr += "\n"
outstr += "\n"
"""Private helper method for refreshing publisher data."""
try:
# The user explicitly requested this refresh, so set the
# refresh to occur immediately.
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.PublisherError as e:
error(e)
error(_("'pkg publisher' will show a list of publishers."))
return EXIT_OOPS
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
return EXIT_OOPS
except api_errors.CatalogRefreshException as e:
if display_catalog_failures(e) == 0:
return EXIT_OOPS
return EXIT_PARTIAL
return EXIT_OK
"""Update metadata for the image's publishers."""
# XXX will need to show available content series for each package
if opt == "-q":
if opt == "--full":
# Reset the progress tracker here, because we may have
# to switch to a different tracker due to the options parse.
# suppress phase information since we're doing just one thing.
# In the case of zones, the ssl cert given is assumed to
# be relative to the root of the image, not truly absolute.
if is_zone:
if ssl_cert is not None:
if ssl_key is not None:
elif orig_cwd:
"""Helper function to wrap set-publisher private methods. Returns
a tuple of (return value, message). Callers should check the return
value for errors."""
try:
except api_errors.CatalogRefreshException as e:
for entry in raise_errors:
if isinstance(e, entry):
raise
pfx)
except api_errors.InvalidDepotResponseException as e:
for entry in raise_errors:
if isinstance(e, entry):
raise
if pfx:
return EXIT_OOPS, _("The origin URIs for '{pubname}' "
"do not appear to point to a valid pkg repository."
"\nPlease verify the repository's location and the "
"client's network configuration."
"\nAdditional details:\n\n{details}").format(
return EXIT_OOPS, _("The specified URI does not appear to "
"point to a valid pkg repository.\nPlease check the URI "
"and the client's network configuration."
except api_errors.ImageFormatUpdateNeeded as e:
for entry in raise_errors:
if isinstance(e, entry):
raise
return EXIT_OOPS, ""
except api_errors.ApiException as e:
for entry in raise_errors:
if isinstance(e, entry):
raise
# Prepend a newline because otherwise the exception will
# be printed on the same line as the spinner.
"""pkg set-publisher [-Ped] [-k ssl_key] [-c ssl_cert] [--reset-uuid]
[-g|--add-origin origin to add] [-G|--remove-origin origin to
remove] [-m|--add-mirror mirror to add] [-M|--remove-mirror mirror
to remove] [-p repo_uri] [--enable] [--disable] [--no-refresh]
[--sticky] [--non-sticky ] [--search-before=publisher]
[--search-after=publisher]
[--approve-ca-cert path to CA]
[--revoke-ca-cert hash of CA to remove]
[--unset-ca-cert hash of CA to unset]
[--set-property name of property=value]
[--add-property-value name of property=value to add]
[--remove-property-value name of property=value to remove]
[--unset-property name of property to delete]
[--proxy proxy to use]
[publisher] """
errors = None
if "errors" in out_json:
selected_type=["publisher_set"])
if "data" in out_json:
if errors:
return out_json["status"]
"""pkg unset-publisher publisher ..."""
if "errors" in out_json:
return out_json["status"]
"""pkg publishers."""
# Create a formatting string for the default output
# format.
if output_format == "default":
fmt = "{0:14} {1:12} {2:8} {3:2} {4} {5}"
# Create a formatting string for the tsv output
# format.
if output_format == "tsv":
fmt = "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}"
# Output an header if desired.
if not omit_headers:
else:
def display_signing_certs(p):
if "Approved CAs" in p:
msg(_(" Approved CAs:"),
p["Approved CAs"][0])
for h in p["Approved CAs"][1:]:
msg(_(" :"), h)
if "Revoked CAs" in p:
msg(_(" Revoked CAs:"),
p["Revoked CAs"][0])
for h in p["Revoked CAs"][1:]:
msg(_(" :"), h)
if "errors" in ret_json:
for e in ret_json["errors"]:
if "errtype" in e and \
e["errtype"] == "cert_info":
emsg(e["reason"])
if "Cert. Effective Date" in uri_data:
msg(_(" Cert. Effective Date:"),
uri_data["Cert. Effective Date"])
msg(_("Cert. Expiration Date:"),
uri_data["Cert. Expiration Date"])
ret_json["data"]:
return retcode
msg("")
if "origins" in pub:
msg(_(" Origin URI:"),
od["Origin URI"])
if "Proxy" in od:
msg(_(" Proxy:"),
if "mirrors" in pub:
msg(_(" Mirror URI:"),
md["Mirror URI"])
if "Proxy" in md:
msg(_(" Proxy:"),
msg(_(" Client UUID:"),
pub["Client UUID"])
msg(_(" Catalog Updated:"),
pub["Catalog Updated"])
msg(_(" Enabled:"),
_(pub["enabled"]))
if "Properties" not in pub:
continue
property_padding = " "
for k, v in pub_items:
if not v:
continue
if not properties_displayed:
msg(_(" Properties:"))
return retcode
"""pkg add-property-value propname propvalue"""
# ensure no options are passed in
subcommand = "add-property-value"
try:
except ValueError:
# XXX image property management should be in pkg.client.api
try:
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.ApiException as e:
return EXIT_OOPS
return EXIT_OK
"""pkg remove-property-value propname propvalue"""
# ensure no options are passed in
subcommand = "remove-property-value"
try:
except ValueError:
# XXX image property management should be in pkg.client.api
try:
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.ApiException as e:
return EXIT_OOPS
return EXIT_OK
"""pkg set-property propname propvalue [propvalue ...]"""
# ensure no options are passed in
subcommand = "set-property"
try:
except IndexError:
propvalues = []
usage(_("requires a property name and at least one value"),
elif propname not in ("publisher-search-order",
"signature-policy", "signature-required-names") and \
# All other properties are single value, so if only one (or no)
# value was specified, transform it. If multiple values were
# specified, allow the value to be passed on so that the
# configuration classes can re-raise the appropriate error.
if propname == "signature-policy":
usage(_("Signature-policy {0} doesn't allow additional "
elif policy == "require-names":
# XXX image property management should be in pkg.client.api
try:
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.ApiException as e:
return EXIT_OOPS
return EXIT_OK
"""pkg unset-property propname ..."""
# is this an existing property in our image?
# if so, delete it
# if not, error
# ensure no options are passed in
subcommand = "unset-property"
if not pargs:
usage(_("requires at least one property name"),
# XXX image property management should be in pkg.client.api
for p in pargs:
try:
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.ApiException as e:
return EXIT_OOPS
return EXIT_OK
"""pkg property [-H] [propname ...]"""
subcommand = "property"
if opt == "-H":
# XXX image property management should be in pkg.client.api
for p in pargs:
if not img.has_property(p):
return EXIT_OOPS
if not pargs:
# If specific properties were named, list them in the order
# requested; otherwise, list them sorted.
if not omit_headers:
for p in pargs:
return EXIT_OK
"""pkg variant [-Haiv] [-F format] [<variant_pattern> ...]"""
subcommand = "variant"
if output_format is None:
output_format = "default"
# To work around Python 2.x's scoping limits, a list is used.
# If user explicitly provides variants, display implicit value even if
# not explicitly set in the image or found in a package.
def gen_current():
if output_format == "default":
yield {
"variant": name,
"value": val
}
def gen_possible():
if output_format == "default":
yield {
"variant": name,
"value": pval
}
if verbose:
else:
if list_all_items:
if verbose:
else:
elif list_installed:
if verbose:
else:
else:
if verbose:
else:
# VARIANT VALUE
# <variant> <value>
# <variant_2> <value_2>
# ...
field_data = {
"variant" : [("default", "json", "tsv"), _("VARIANT"), ""],
"value" : [("default", "json", "tsv"), _("VALUE"), ""],
}
# Default output formatting.
def_fmt = "{0:70} {1}"
# print without trailing newline.
if output_format == "default":
# Don't pollute other output formats.
error(_("no matching variants found"),
return EXIT_OOPS
# Successful if no variants exist or if at least one matched.
return EXIT_OK
"""pkg facet [-Hai] [-F format] [<facet_pattern> ...]"""
subcommand = "facet"
if output_format is None:
output_format = "default"
# To work around Python 2.x's scoping limits, a list is used.
if list_all_items:
elif list_installed:
# If user explicitly provides facets, display implicit value even if
# not explicitly set in the image or found in a package.
def gen_listing():
if output_format == "default":
if not list_masked and masked:
continue
# "value" and "masked" are intentionally not _().
yield {
"facet": name,
"src": src,
}
# FACET VALUE
# <facet> <value> <src>
# <facet_2> <value_2> <src2>
# ...
field_data = {
"facet" : [("default", "json", "tsv"), _("FACET"), ""],
"value" : [("default", "json", "tsv"), _("VALUE"), ""],
"src" : [("default", "json", "tsv"), _("SRC"), ""],
}
def_fmt = "{0:64} {1:5} {2}"
if list_masked:
# if we're displaying masked facets, we should also mark which
# facets are masked in the output.
field_data["masked"] = \
[("default", "json", "tsv"), _("MASKED"), ""]
desired_field_order += (_("MASKED"),)
def_fmt = "{0:57} {1:5} {2:6} {3}"
# print without trailing newline.
if output_format == "default":
# Don't pollute other output formats.
error(_("no matching facets found"),
return EXIT_OOPS
# Successful if no facets exist or if at least one matched.
return EXIT_OK
"""pkg list-linked [-H]
List all the linked images known to the current image."""
return EXIT_OK
fmt = ""
if (fmt != ''):
fmt += "\t"
if not omit_headers:
return EXIT_OK
"""If we're a child image, verify that the parent image
publisher configuration is a subset of our publisher configuration.
If we have any children, recurse into them and perform a publisher
check."""
try:
except api_errors.ImageLockedError as e:
error(e)
return EXIT_LOCKED
return EXIT_OK
""""Parse linked image property options that were specified on the
command line into a dictionary. Make sure duplicate properties were
not specified."""
linked_props = dict()
try:
except ValueError:
usage(_("linked image property arguments must be of "
if p not in li.prop_values:
usage(_("invalid linked image property: "
if p in linked_props:
usage(_("linked image property specified multiple "
linked_props[p] = v
return linked_props
"""pkg property-linked [-H] [-l <li-name>] [propname ...]
List the linked image properties associated with a child or parent
image."""
lin = None
if li_name:
for p in pargs:
return EXIT_OOPS
return EXIT_OK
if not pargs:
if not omit_headers:
if not props[p]:
continue
return EXIT_OK
"""pkg set-property-linked
[-nvq] [--accept] [--licenses] [--no-index] [--no-refresh]
[--no-parent-sync] [--no-pkg-updates]
[--linked-md-only] <propname>=<propvalue> ...
Change the specified linked image properties. This may result in
updating the package contents of a child image."""
# make sure we're a child image
if li_name:
else:
if not xrval:
return EXIT_OOPS
return EXIT_OK
quiet):
"""pkg audit-linked [-a|-l <li-name>]
Audit one or more child images to see if they are in sync
with their parent image."""
# audit the requested child image(s)
if not li_target_all and not li_target_list:
# audit the current image
else:
# audit the requested child image(s)
if not rvdict:
# may not have had any children
return EXIT_OK
# display audit return values
if not omit_headers:
if not quiet:
elif rv == EXIT_DIVERGED:
if err:
return rv
"""pkg sync-linked [-a|-l <li-name>]
[-nvq] [--accept] [--licenses] [--no-index] [--no-refresh]
[--no-parent-sync] [--no-pkg-updates]
[--linked-md-only] [-a|-l <name>]
Sync one or more child images with their parent image."""
if not xrval:
return EXIT_OOPS
if not li_target_all and not li_target_list:
# sync the current image
# sync the requested child image(s)
if err:
try:
except api_errors.ApiException as e:
return EXIT_OOPS
return rv
"""pkg attach-linked
[-fnvq] [--accept] [--licenses] [--no-index] [--no-refresh]
[--no-pkg-updates] [--linked-md-only]
[--allow-relink]
[--parsable-version=<version>]
[--prop-linked <propname>=<propvalue> ...]
(-c|-p) <li-name> <dir>
Attach a child linked image. The child could be this image attaching
itself to a parent, or another image being attach as a child with
this image being the parent."""
for k, v in li_props:
usage(_("cannot specify linked image property: "
usage(_("a linked image name and path must be specified"),
# parse the specified name
if not xrval:
return EXIT_OOPS
if attach_parent:
# attach the current image to a parent
# attach the requested child image
if err:
assert p_dict is not None
try:
[p_dict])
except api_errors.ApiException as e:
return EXIT_OOPS
return rv
"""pkg detach-linked
[-fnvq] [-a|-l <li-name>] [--linked-md-only]
Detach one or more child linked images."""
if not li_target_all and not li_target_list:
# detach the current image
if err:
return rv
"""Create an image of the requested kind, at the given path. Load
catalog for initial publisher for convenience.
At present, it is legitimate for a user image to specify that it will be
deployed in a zone. An easy example would be a program with an optional
component that consumes global zone-only information, such as various
kernel statistics or device information."""
cmd_name = "image-create"
add_mirrors = set()
add_origins = set()
pub_name = None
pub_url = None
ssl_key = None
ssl_cert = None
variants = {}
set_props = {}
version = None
["force", "full", "partial", "user", "zone", "facet=", "mirror=",
"origin=", "publisher=", "no-refresh", "variant=",
"set-property="])
if pub_url:
usage(_("The -p option can be specified only "
try:
except ValueError:
pub_name = None
if pub_url:
elif opt == "-c":
elif opt == "-k":
elif opt == "--facet":
try:
except ValueError:
f_value = ""
usage(_("Facet arguments must be of the "
"form '<name>=(True|False)'"),
elif opt == "--no-refresh":
elif opt == "--set-property":
if len(t) < 2:
usage(_("properties to be set must be of the "
"form '<name>=<value>'. This is what was "
if t[0] in set_props:
usage(_("a property may only be set once in a "
elif opt == "--variant":
try:
except ValueError:
usage(_("variant arguments must be of the "
"form '<name>=<value>'."),
if not pargs:
usage(_("an image directory path must be specified"),
usage(_("only one image directory path may be specified"),
usage(_("--no-refresh cannot be used with -p unless a "
usage(_("A publisher must be specified if -g or -m are used."),
if not refresh_allowed and pub_url:
# Auto-config can't be done if refresh isn't allowed, so treat
# this as a manual configuration case.
repo_uri = None
else:
progtrack = get_tracker()
global _api_inst
global img
try:
except api_errors.InvalidDepotResponseException as e:
# Ensure messages are displayed after the spinner.
error(_("The URI '{pub_url}' does not appear to point to a "
"valid pkg repository.\nPlease check the repository's "
"location and the client's network configuration."
"\nAdditional details:\n\n{error}").format(
return EXIT_OOPS
# Ensure messages are displayed after the spinner.
return EXIT_OOPS
else:
return EXIT_PARTIAL
except api_errors.ApiException as e:
return EXIT_OOPS
finally:
# Normally this would get flushed by handle_errors
# but that won't happen if the above code throws, because
# _api_inst will be None.
return EXIT_OK
"""pkg rebuild-index
Forcibly rebuild the search indexes. Will remove existing indexes
and build new ones from scratch."""
if pargs:
try:
except api_errors.ImageFormatUpdateNeeded as e:
return EXIT_OOPS
except api_errors.CorruptedIndexException:
error("The search index appears corrupted. Please rebuild the "
return EXIT_OOPS
except api_errors.ProblematicPermissionsIndexException as e:
error(_("\n(Failure to consistently execute pkg commands as a "
"privileged user is often a source of this problem.)"))
return EXIT_OOPS
else:
return EXIT_OK
"""Display history about the current image.
"""
# define column name, header, field width and <History> attribute name
# we compute 'reason', 'time' and 'release_note' columns ourselves
history_cols = {
"be": (_("BE"), "20", "operation_be"),
"be_uuid": (_("BE UUID"), "41", "operation_be_uuid"),
"client": (_("CLIENT"), "19", "client_name"),
"client_ver": (_("VERSION"), "15", "client_version"),
"command": (_("COMMAND"), "", "client_args"),
"finish": (_("FINISH"), "25", "operation_end_time"),
"id": (_("ID"), "10", "operation_userid"),
"new_be": (_("NEW BE"), "20", "operation_new_be"),
"new_be_uuid": (_("NEW BE UUID"), "41", "operation_new_be_uuid"),
"operation": (_("OPERATION"), "25", "operation_name"),
"outcome": (_("OUTCOME"), "12", "operation_result"),
"reason": (_("REASON"), "10", None),
"release_notes": (_("RELEASE NOTES"), "12", None),
"snapshot": (_("SNAPSHOT"), "20", "operation_snapshot"),
"start": (_("START"), "25", "operation_start_time"),
"time": (_("TIME"), "10", None),
"user": (_("USER"), "10", "operation_username"),
# omitting start state, end state, errors for now
# as these don't nicely fit into columns
}
display_limit = None # Infinite
time_vals = [] # list of timestamps for which we want history events
if opt == "-H":
elif opt == "-N":
elif opt == "-l":
elif opt == "-n":
try:
except ValueError:
_("Argument to -n must be numeric"))
return EXIT_BADOPT
if display_limit <= 0:
_("Argument to -n must be positive"))
return EXIT_BADOPT
elif opt == "-o":
# 'command' and 'reason' are multi-field columns, we
# insist they be the last item in the -o output,
# otherwise scripts could be broken by different numbers
# of output fields
# Translators: 'command' and 'reason' are
# keywords and should not be translated
"cannot be used together."))
return EXIT_BADOPT
_("The '{0}' column must be the "
"last item in the -o list").format(
col))
return EXIT_BADOPT
if col not in history_cols:
_("Unknown output column "
return EXIT_BADOPT
if not __unique_columns(columns):
return EXIT_BADOPT
elif opt == "-t":
if omit_headers and long_format:
if column_format and long_format:
if time_vals and display_limit:
if column_format and show_notes:
if long_format and show_notes:
history_fmt = None
if not long_format and not show_notes:
headers = []
# build our format string
# no need for trailing space for our last column
fmt = ""
else:
if history_fmt:
else:
if not omit_headers:
def gen_entries():
"""Error handler for history generation; avoids need to indent
and clobber formatting of logic below."""
try:
yield he
except api_errors.HistoryException as e:
if show_notes:
for he in gen_entries():
else:
return EXIT_OK
for he in gen_entries():
# populate a dictionary containing our output
output = {}
for col in history_cols:
continue
# format some of the History object attributes ourselves
output["finish"] = \
_("{0} (clock drift detected)").format(
output["finish"])
# We can't use timedelta's str() method, since when
# output["time"].days > 0, it prints eg. "4 days, 3:12:54"
# breaking our field separation, so we need to do this by hand.
# Where we weren't able to lookup the current name, add a '*' to
# the entry, indicating the boot environment is no longer
# present.
elif he.operation_be_uuid:
else:
elif he.operation_new_be_uuid:
else:
else:
# be, snapshot and new_be use values in parenthesis
# since these cannot appear in valid BE or snapshot names
if not output["be"]:
if not output["be_uuid"]:
if not output["snapshot"]:
if not output["new_be"]:
if not output["new_be_uuid"]:
if not enc:
if long_format:
# Separate log entries with a blank line.
msg("")
else:
items = []
return EXIT_OK
"""Return true if each entry in the provided list of columns only
appears once."""
return not dup_cols
"""Return an array of tuples containing long_format history info"""
data = []
hist_info["id"])))
if hist_info["be"]:
if hist_info["be_uuid"]:
if hist_info["new_be"]:
if hist_info["new_be_uuid"]:
hist_info["new_be_uuid"]))
if hist_info["snapshot"]:
if state:
if state:
if errors:
return data
"""Purge image history"""
msg(_("History purged."))
def print_proxy_config():
"""If the user has configured http_proxy or https_proxy in the
environment, print out the values. Some transport errors are
not debuggable without this information handy."""
if not http_proxy and not https_proxy:
return
" environment:\n"))
if http_proxy:
if https_proxy:
"""Update image to newest format."""
try:
except api_errors.ApiException as e:
return EXIT_OOPS
if res:
return EXIT_OK
return EXIT_NOP
if pargs:
usage(_("version: command does not take operands "
return EXIT_OK
# To allow exception handler access to the image.
_api_inst = None
pargs = None
img = None
orig_cwd = None
#
# Mapping of the internal option name to short and long CLI options.
#
# {option_name: (short, long)}
#
#
opts_mapping = {
"backup_be_name" : ("", "backup-be-name"),
"be_name" : ("", "be-name"),
"deny_new_be" : ("", "deny-new-be"),
"no_backup_be" : ("", "no-backup-be"),
"be_activate" : ("", "no-be-activate"),
"require_backup_be" : ("", "require-backup-be"),
"require_new_be" : ("", "require-new-be"),
"concurrency" : ("C", "concurrency"),
"force" : ("f", ""),
"ignore_missing" : ("", "ignore-missing"),
"li_ignore_all" : ("I", ""),
"li_ignore_list" : ("i", ""),
"li_md_only" : ("", "linked-md-only"),
"li_pkg_updates" : ("", "no-pkg-updates"),
"li_parent_sync" : ("", "no-parent-sync"),
"li_props" : ("", "prop-linked"),
"li_target_all" : ("a", ""),
"li_target_list" : ("l", ""),
"li_name" : ("l", ""),
# These options are used for explicit recursion into linked children.
# li_erecurse_all enables explicit recursion into all children if neither
# li_erecurse_list nor li_erecurse_excl is set. If any children are
# specified in li_erecurse_list, only recurse into those. If any children
# are specified in li_erecurse_excl, recurse into all children except for
# those.
# Explicit recursion means we run the same operation in the child as we run
# in the parent. Children we do not explicitely recurse into are still
# getting synced.
"li_erecurse_all" : ("r", "recurse"),
"li_erecurse_list" : ("z", ""),
"li_erecurse_excl" : ("Z", ""),
"accept" : ("", "accept"),
"show_licenses" : ("", "licenses"),
"omit_headers" : ("H", ""),
"update_index" : ("", "no-index"),
"unpackaged" : ("", "unpackaged"),
"unpackaged_only" : ("", "unpackaged-only"),
"refresh_catalogs" : ("", "no-refresh"),
"reject_pats" : ("", "reject"),
"verbose" : ("v", ""),
"quiet" : ("q", ""),
"parsable_version" : ("", "parsable"),
"noexecute" : ("n", ""),
"origins" : ("g", ""),
"stage" : ("", "stage"),
"allow_relink" : ("", "allow-relink"),
"attach_child" : ("c", ""),
"attach_parent" : ("p", ""),
"list_available" : ("a", ""),
"list_masked" : ("m", ""),
"list_all_items" : ("a", ""),
"output_format" : ("F", "output-format"),
"tagged" : ("", "tagged"),
"publishers" : ("p", ""),
# These options are used in set-mediator and unset-mediator but
# the long options are only valid in set_mediator (as per the previous
# implementation). However, the long options are not documented in the
# manpage for set-mediator either, so I think we're good.
"med_implementation" : ("I", "implementation"),
"med_version" : ("V", "version"),
"list_installed_newest" : ("a", ""),
"list_all" : ("f", ""),
"list_newest" : ("n", ""),
"summary" : ("s", ""),
"list_upgradable" : ("u", ""),
"ctlfd" : ("", "ctlfd"),
"progfd" : ("", "progfd"),
"list_installed" : ("i", ""),
"sync_act" : ("", "sync-actuators"),
"act_timeout" : ("", "sync-actuators-timeout"),
"ssl_key": ("k", ""),
"ssl_cert": ("c", ""),
"approved_ca_certs": ("", "approve-ca-cert"),
"revoked_ca_certs": ("", "revoke-ca-cert"),
"unset_ca_certs": ("", "unset-ca-cert"),
"origin_uri": ("O", ""),
"reset_uuid": ("", "reset-uuid"),
"add_mirrors": ("m", "add-mirror"),
"remove_mirrors": ("M", "remove-mirror"),
"add_origins": ("g", "add-origin"),
"remove_origins": ("G", "remove-origin"),
"enable_origins": ("", "enable-origins"),
"disable_origins": ("", "disable-origins"),
"refresh_allowed": ("", "no-refresh"),
"enable": ("e", "enable"),
"disable": ("d", "disable"),
"sticky": ("", "sticky"),
"non_sticky": ("", "non-sticky"),
"repo_uri": ("p", ""),
"proxy_uri": ("", "proxy"),
"search_before": ("", "search-before"),
"search_after": ("", "search-after"),
"search_first": ("P", "search-first"),
"set_props": ("", "set-property"),
"add_prop_values": ("", "add-property-value"),
"remove_prop_values": ("", "remove-property-value"),
"unset_props": ("", "unset-property"),
"preferred_only": ("P", ""),
"inc_disabled": ("n", ""),
"info_local": ("l", ""),
"info_remote": ("r", ""),
"display_license": ("", "license"),
"publisher_a": ("a", ""),
"verify_paths": ("p", "")
}
#
# cmds dictionary is used to dispatch subcommands. The format of this
# dictionary is:
#
# "subcommand-name" : subcommand-cb
#
# subcommand-cb: the callback function invoked for this subcommand
#
# placeholders in this lookup table for image-create, help and version
# which don't have dedicated methods
#
cmds = {
"add-property-value" : [property_add_value],
"avoid" : [avoid],
"change-facet" : [change_facet],
"change-variant" : [change_variant],
"contents" : [list_contents],
"dehydrate" : [dehydrate],
"exact-install" : [exact_install],
"facet" : [list_facet],
"fix" : [fix],
"freeze" : [freeze],
"help" : [None],
"history" : [history_list],
"image-create" : [None],
"info" : [info],
"install" : [install],
"list" : [list_inventory],
"mediator" : [list_mediators],
"property" : [property_list],
"property-linked" : [list_property_linked],
"publisher" : [publisher_list],
"purge-history" : [history_purge],
"rebuild-index" : [rebuild_index],
"refresh" : [publisher_refresh],
"rehydrate" : [rehydrate],
"remove-property-value" : [property_remove_value],
"revert" : [revert],
"search" : [search],
"set-mediator" : [set_mediator],
"set-property" : [property_set],
"set-property-linked" : [set_property_linked],
"set-publisher" : [publisher_set],
"unavoid" : [unavoid],
"unfreeze" : [unfreeze],
"uninstall" : [uninstall],
"unset-property" : [property_unset],
"unset-mediator" : [unset_mediator],
"unset-publisher" : [publisher_unset],
"update" : [update],
"update-format" : [update_format],
"variant" : [list_variant],
"verify" : [verify],
"version" : [None],
}
# Option value dictionary which pre-defines the valid values for
# some options.
valid_opt_values = {
"output_format": ["default", "tsv", "json", "json-formatted"]
}
# These tables are an addendum to the the pkg_op_opts/opts_* lists in
# modules/client/options.py. They contain all the options for functions which
# are not represented in options.py but go through common option processing.
# functions out of client.py.
# move progfd from opts_new into a global
del opts_new["progfd"]
opts_remote = [
("ctlfd", None),
("progfd", None),
]
raise api_errors.InvalidOptionError(
["list_all_items", "list_installed"])
opts_list_varcet = \
[
("list_all_items", False),
("list_installed", False),
]
opts_list_facet = \
opts_list_varcet + \
[
("list_masked", False),
]
opts_list_varcet + \
[
("verbose", False)
]
[
("list_available", False),
]
[
("med_implementation", False),
("med_version", False)
]
cmd_opts = {
"facet" : opts_list_facet,
"mediator" : opts_list_mediator,
"unset-mediator" : opts_unset_mediator,
"remote" : opts_remote,
"variant" : opts_list_variant,
}
def main_func():
global _api_inst
global img
global orig_cwd
global pargs
try:
except OSError as e:
try:
orig_cwd = None
except KeyError:
orig_cwd = None
try:
["debug=", "help", "runid=", "no-network-cache"])
except getopt.GetoptError as e:
runid = None
value = "True"
else:
try:
except (AttributeError, ValueError):
usage(_("{opt} takes argument of form "
"name=value, not {arg}").format(
elif opt == "-R":
elif opt == "--runid":
elif opt == "--no-network-cache":
# The globals in pkg.digest can be influenced by debug flags
if DebugValues:
subcommand = None
if pargs:
if subcommand == "help":
if pargs:
elif sub == "-v":
# Only display the long usage message
# in the verbose mode.
usage(_("unknown subcommand "
else:
else:
# A gauntlet of tests to see if we need to print usage information
if show_usage:
if not subcommand:
if runid is not None:
try:
except:
usage(_("runid must be an integer"))
# This call only affects sockets created by Python. The transport
# framework uses the defaults in global_settings, which may be
# overridden in the environment. The default socket module should
# only be used in rare cases by ancillary code, making it safe to
# code the value here, at least for now.
cmds_no_image = {
"version" : print_version,
"image-create" : image_create,
}
if func:
if "mydir" in locals():
try:
except getopt.GetoptError as e:
return ret
if "mydir" not in locals():
# It's assumed that this has been checked by the above
# function call and hasn't been removed from the
# environment.
if not mydir:
error(_("Could not find image. Use the -R option or set "
"$PKG_IMAGE to the\nlocation of an image."))
return EXIT_OOPS
# Get ImageInterface and image object.
if api_inst is None:
return EXIT_OOPS
# Find subcommand and execute operation.
pargs_limit = None
# Get the available options for the requested operation to create the
# getopt parsing strings.
if not valid_opts:
# if there are no options for an op, it has its own processing
try:
except getopt.GetoptError as e:
try:
# Parse CLI arguments into dictionary containing corresponding
# options and values.
except api_errors.InvalidOptionError as e:
# We can't use the string representation of the exception since
# it references internal option names. We substitute the CLI
# options and create a new exception to make sure the messages
# are correct.
# Convert the internal options to CLI options. We make sure that
# when there is a short and a long version for the same option
# we print both to avoid confusion.
try:
s, l = opts_mapping[option]
if l and not s:
return "--{0}".format(l)
elif s and not l:
return "-{0}".format(s)
else:
return "-{0}/--{1}".format(s, l)
except KeyError:
# ignore if we can't find a match
# (happens for repeated arguments or invalid
# arguments)
return option
except TypeError:
# ignore if we can't find a match
# (happens for an invalid arguments list)
return option
cli_opts = []
opt_def = []
for o in e.options:
# collect the default value (see comment below)
# Prepare for headache:
# If we have an option 'b' which is set to True by default it
# will be toggled to False if the users specifies the according
# option on the CLI.
# If we now have an option 'a' which requires option 'b' to be
# set, we can't say "'a' requires 'b'" because the user can only
# specify 'not b'. So the correct message would be:
# "'a' is incompatible with 'not b'".
# We can get there by just changing the type of the exception
# for all cases where the default value of one of the options is
# True.
e.err_type = \
# This new exception will have the CLI options, so can be passed
# directly to usage().
# Reset the progress tracker here, because we may have
# to switch to a different tracker due to the options parse.
#
# 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.
#
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:
raise
if _api_inst:
except SystemExit as __e:
if _api_inst:
raise __e
except (PipeError, KeyboardInterrupt):
if _api_inst:
# We don't want to display any messages here to prevent
# possible further broken pipe (EPIPE) errors.
if _api_inst:
if _api_inst:
if _api_inst:
if _api_inst:
"to retrieve package or file data for\nthe requested "
"operation."))
if _api_inst:
An error was encountered while attempting to read image state information
if _api_inst:
"repository. This may be due to a problem with the "
"repository, network misconfiguration, or an incorrect "
"pkg client configuration. Please verify the client's "
"network configuration and repository's location."))
# Since a history related error occurred, discard all
# information about the current operation(s) in progress.
if _api_inst:
error(_("An error was encountered while attempting to load "
"history information\nabout past client operations."))
# Since a history related error occurred, discard all
# information about the current operation(s) in progress.
if _api_inst:
error(_("An error was encountered while attempting to store "
"information about the\ncurrent operation in client "
"history."))
# Since a history related error occurred, discard all
# information about the current operation(s) in progress.
if _api_inst:
error(_("An error was encountered while attempting to purge "
"client history."))
if _api_inst:
error(_("The pkg command appears out of sync with the libraries"
"{client} while the library\nAPI version is {api}.").format(
))
def _wrapper():
s = ""
if __ret == 99:
s += _("\n{err}{stacktrace}").format(
s += _("\n\nDespite the error while indexing, the operation "
"has completed successfuly.")
error(s)
except:
if _api_inst:
if non_wrap_print:
return __ret
"""Attempt to gracefully handle SIGHUP and SIGTERM by telling the api
to abort and record the cancellation before exiting."""
try:
if _api_inst:
except:
# If history operation fails for some reason, drive on.
pass
# Use os module to immediately exit (bypasses standard exit handling);
# this is preferred over raising a KeyboardInterupt as whatever module
# we interrupted may not expect that if they disabled SIGINT handling.
if __name__ == "__main__":
# Make all warnings be errors.
import warnings
# disable ResourceWarning: unclosed file
import signal
# SIGHUP not supported on windows; will cause exception.
if DebugValues["timings"]:
def __display_timings():
try:
except IOError:
# Ignore python's spurious pipe problems.
pass