/*
* 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) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Program: pkgadd / pkgask
*
* Function: public command and private utility functions that
* implement the package add and package ask operations.
*
*/
/*
* System includes
*/
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include <pkgdev.h>
#include <pkginfo.h>
#include <pkglocs.h>
#include <locale.h>
#include <libintl.h>
#include <pkgtrans.h>
#include <boot_http.h>
#include <assert.h>
/*
* consolidation pkg command library includes
*/
#include <pkglib.h>
#include <pkgerr.h>
#include <pkgweb.h>
#include <instzones_api.h>
/*
* local pkg command library includes
*/
#include <install.h>
#include <libinst.h>
#include <libadm.h>
#include <messages.h>
/*
* pkgadd local includes
*/
#include "quit.h"
/*
* imported global variables/functions
*/
/* check.c */
extern int preinstall_verify(char **a_pkgList, zoneList_t a_zlst,
char *a_zoneTempDir);
/*
* ckquit is a global that controls 'ckyorn' (defined in libadm)
* If ckquit is non-zero, then "quit" is allowed as an answer when
* ckyorn is called. If is it zero, then "quit" is not an allowed answer.
*/
extern int ckquit;
/*
* exported global variables
*/
/* these globals are set by ckreturn and used by quit.c */
int admnflag = 0; /* != 0 if any pkg op admin setting failure (4) */
int doreboot = 0; /* != 0 if reboot required after installation */
int failflag = 0; /* != 0 if fatal error has occurred (1) */
int intrflag = 0; /* != 0 if user selected quit (3) */
int ireboot = 0; /* != 0 if immediate reboot required */
int nullflag = 0; /* != 0 if admin interaction required (5) */
int warnflag = 0; /* != 0 if non-fatal error has occurred (2) */
/* imported by quit.c */
int npkgs = 0; /* the number of packages yet to be installed */
/* imported by various (many) */
char *respfile = NULL; /* response pathname (or NULL) */
char *tmpdir = NULL; /* location to place temporary files */
struct admin adm; /* holds info about installation admin */
struct pkgdev pkgdev; /* holds info about the installation device */
/*
* internal global variables
*/
static char *admnfile = NULL; /* file to use for installation admin */
static char *ids_name = NULL; /* name of data stream device */
static char *pkgcontsrc = NULL; /* continuation file (-c option) */
static char *pkgdrtarg = NULL; /* dry run file (-D option) */
static char *pkginst = NULL; /* current pkg/src instance 2 process */
static char *respdir = NULL; /* respfile is a directory spec */
static char *rw_block_size = NULL;
static char *vfstab_file = NULL;
static int askflag = 0; /* non-zero if invoked as "pkgask" */
static int disableAttributes = 0; /* Disabling attribute checking */
static int disableChecksum = 0; /* Disable checksumming */
static int disableSaveSpool = 0; /* Disable partial spool dir create */
static int init_install = 0; /* inform scripts initial install */
static int no_map_client = 0; /* do not map from vfstab file */
static int nointeract = 0; /* non-zero - no user interaction */
static int pkgverbose = 0; /* non-zero if verbose mode selected */
static int saveSpoolInstall = 0; /* installing from save spool dir */
static int suppressCopyright = 0; /* suppress copyright notices */
/* set by ckreturn() */
static int interrupted = 0; /* last pkg op was quit (1,2,3,4,5) */
static int needconsult = 0; /* essential ask admin now (1,2,3,5) */
/* Set by -O nozones: do not process any zones */
static boolean_t noZones = B_FALSE;
/* Set by -O zonelist=<names...>: process only named zones */
static boolean_t usedZoneList = B_FALSE;
/* Set by -O debug: debug output is enabled? */
static boolean_t debugFlag = B_FALSE;
/* Set by the -G option: install packages in global zone only */
static boolean_t globalZoneOnly = B_FALSE;
/*
* Assume the package is ABI and POSIX compliant as regards user
* interactiion during procedure scripts.
*/
static int old_pkg = 0;
/* Assume pkg should be installed according to the ABI */
static int old_symlinks = 0;
/*
* Default name length will be 32 chars - if this is set,
* disable the 32 char name limit extension
*/
static int ABI_namelength = 0;
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
/* printable string - if string is null results in ??? */
#define PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
#define MAX_FDS 20
/*
* forward declarations
*/
static int boot_and_pkginstall_check_in_zones(zoneList_t a_zlst,
char *a_idsName, char *a_altBinDir,
char *a_zoneAdminFile, char *a_zoneTempDir);
static int boot_and_install_in_zones(zoneList_t a_zlst,
char *a_idsName, char *a_altBinDir,
char *a_zoneAdminFile, char *a_zoneTempDir);
static void pkginstall_check_in_one_zone(char *a_zoneName,
char *a_idsName, char *a_zoneAdminFile,
char *a_zoneTempDir, char *a_altBinDir,
char *a_scratchName, zone_state_t a_zoneState,
boolean_t a_tmpzn);
static void ckreturn(int retcode);
static void create_zone_adminfile(char **r_zoneAdminFile,
char *a_zoneTempDir, char *a_admnfile);
static void create_zone_tempdir(char **r_zoneTempDir,
char *a_tmpdir);
static void install_in_one_zone(char *a_zoneName, char *a_idsName,
char *a_zoneAdminFile, char *a_zoneTempDir,
char *a_altBinDir, zone_state_t a_zoneState,
boolean_t a_tmpzn);
static int pkginstall_check_in_zones(zoneList_t a_zlst,
char *a_idsName, char *a_altBinDir,
char *a_zoneAdminFile, char *a_zoneTempDir);
static int install_in_zones(zoneList_t a_zlst, char *a_idsName,
char *a_altBinDir, char *a_zoneAdminFile,
char *a_zoneTempDir);
static int pkgInstall(char *ir, char *a_idsName, char *a_pkgDir,
char *a_altBinDir);
static int pkgZoneCheckInstall(char *a_zoneName,
zone_state_t a_zoneState,
char *a_idsName, char *a_altBinDir,
char *a_adminFile, char *a_stdoutPath,
boolean_t a_tmpzn);
static int pkgZoneInstall(char *a_zoneName,
zone_state_t a_zoneState,
char *a_idsName, char *a_altBinDir,
char *a_adminFile, boolean_t a_tmpzn);
static void resetreturn();
static void usage(void);
static boolean_t add_packages(char **a_pkgList, char *a_uri,
char *a_idsName, int a_repeat,
char *a_altBinDir, char *a_device,
boolean_t a_noZones);
static boolean_t add_packages_in_global_no_zones(char **a_pkgList,
char *a_uri, char *a_idsName, int a_repeat,
char *a_altBinDir, char *a_device);
static boolean_t add_packages_in_global_with_zones(char **a_pkgList,
char *a_uri, char *a_idsName, int a_repeat,
char *a_altBinDir, char *a_device,
zoneList_t a_zlst);
static boolean_t add_packages_in_nonglobal_zone(char **a_pkgList,
char *a_uri, char *a_idsName, int a_repeat,
char *a_altBinDir, char *a_device);
static boolean_t check_applicability(char *a_packageDir,
char *a_pkgInst, char *a_rootPath,
CAF_T a_flags);
static boolean_t get_package_list(char ***r_pkgList, char **a_argv,
char *a_categories, char **a_categoryList,
int a_ignoreSignatures, PKG_ERR *a_err,
ushort_t a_httpProxyPort, char *a_httpProxyName,
keystore_handle_t a_keystore,
char *a_keystoreFile, char *a_idsName,
int *r_repeat);
static boolean_t continue_installation(void);
static boolean_t unpack_and_check_packages(char **a_pkgList,
char *a_idsName, char *a_packageDir);
/*
* *****************************************************************************
* global external (public) functions
* *****************************************************************************
*/
/*
* Name: main
* Description: main entry point for pkgadd/pkgask
* Returns: int
* 0 Successful completion
* 1 Fatal error.
* 2 Warning.
* 3 Interruption.
* 4 Administration.
* 5 Administration. Interaction is required. Do not use pkgadd -n.
* In addition, one of the following values may be added to the previous value
* as appropriate:
* 10 Reboot after installation of all packages.
* 20 Reboot after installation of this package.
* For example, "14" would indicate both "administration" and "reboot after
* installation of all packages".
*/
int
main(int argc, char **argv)
{
PKG_ERR *err = NULL;
WebScheme scheme = none;
char **category = NULL;
char *abiPtr;
char *altBinDir = (char *)NULL;
char *catg_arg = NULL;
char *device = NULL; /* dev pkg stored on */
char *dwnld_dir = NULL;
char *keystore_file = NULL;
char *p;
char *q;
char *prog;
char *prog_full_name = NULL;
char *proxy = NULL;
char *spoolDir = NULL; /* specified with -s */
char *uri = NULL;
char Rpath[PATH_MAX+1] = {'\0'};
int c;
int ignore_sig = 0;
int n;
int repeat;
int retries = NET_RETRIES_DEFAULT;
int timeout = NET_TIMEOUT_DEFAULT;
keystore_handle_t keystore = NULL;
struct sigaction nact;
struct sigaction oact;
ushort_t proxy_port = 0;
/* initialize locale environment */
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
/* initialize program name */
prog_full_name = argv[0];
prog = set_prog_name(argv[0]);
/* tell spmi zones interface how to access package output functions */
z_set_output_functions(echo, echoDebug, progerr);
askflag = (strcmp(prog, "pkgask") == 0);
/* set sane umask */
(void) umask(0022);
/* tell quit which ckreturn function to call */
quitSetCkreturnFunc(&ckreturn);
/* initially no source "device" */
device = NULL;
/* reset npkgs (used as pkg remaining count in quit.c) */
npkgs = 0;
/* set default password prompt for encrypted packages */
set_passphrase_prompt(MSG_PASSPROMPT);
/* initialize security operations structures and libraries */
sec_init();
if (z_running_in_global_zone() && !enable_local_fs()) {
progerr(ERR_CANNOT_ENABLE_LOCAL_FS);
}
pkgserversetmode(DEFAULTMODE);
/*
* ********************************************************************
* parse command line options
* ********************************************************************
*/
while ((c = getopt(argc, argv,
"?Aa:b:B:Cc:D:d:GhIik:MnO:P:R:r:Ss:tV:vx:Y:zZ")) != EOF) {
switch (c) {
/*
* Not a public interface: This disables attribute checking.
* It speeds up installation a little bit.
*/
case 'A':
disableAttributes++;
break;
/*
* Public interface: Define an installation administration
* file, admin, to be used in place of the default
* administration file. The token none overrides the use
* of any admin file, and thus forces interaction with the
* user. Unless a full path name is given, pkgadd first
* looks in the current working directory for the
* administration file. If the specified administration
* file is not in the current working directory, pkgadd
* looks in the /var/sadm/install/admin directory for the
* administration file.
*/
case 'a':
admnfile = flex_device(optarg, 0);
break;
/*
* Not a public interface: control block size given to
* pkginstall - block size used in read()/write() loop;
* default is st_blksize from stat() of source file.
*/
case 'B':
if (optarg[0] == '-') {
usage();
quit(1);
}
rw_block_size = optarg;
break;
/*
* Not a public interface: location where package executables
* can be found - default is /usr/sadm/install/bin.
*/
case 'b':
if (optarg[0] == '-') {
usage();
quit(1);
}
if (!path_valid(optarg)) {
progerr(ERR_PATH, optarg);
quit(1);
}
if (isdir(optarg) != 0) {
p = strerror(errno);
progerr(ERR_CANNOT_USE_DIR, optarg, p);
quit(1);
}
altBinDir = optarg;
break;
/*
* Not a public interface: This disables checksum tests on
* the source files. It speeds up installation a little bit.
*/
case 'C':
disableChecksum++;
break;
/*
* Not a public interface: This allows designation of a
* continuation file. It is the same format as a dryrun file
* but it is used to take up where the dryrun left off.
*/
case 'c':
pkgcontsrc = flex_device(optarg, 0);
break;
/*
* Not a public interface: This allows designation of a
* dryrun file. This pkgadd will create dryrun files
* in the directory provided.
*/
case 'D':
if (optarg[0] == '-') {
usage();
quit(1);
}
pkgdrtarg = flex_device(optarg, 0);
break;
/*
* Public interface: Install or copy a package from
* device. device can be a full path name to a directory
* or the identifiers for tape, floppy disk, or removable
* disk - for example, /var/tmp or /floppy/floppy_name.
* It can also be a device alias - for example,
* /floppy/floppy0, or a datastream created by pkgtrans.
*/
case 'd':
if (optarg[0] == '-') {
usage();
quit(1);
}
if (!path_valid(optarg)) {
progerr(ERR_PATH, optarg);
quit(1);
/* NOTREACHED */
}
if (strncmp(optarg, HTTP, 7) == 0) {
scheme = web_http;
} else if (strncmp(optarg, HTTPS, 8) == 0) {
scheme = web_https;
}
if (scheme == web_https || scheme == web_http) {
uri = optarg;
if ((device = malloc(PATH_MAX)) == NULL) {
progerr(ERR_MEM);
exit(1);
}
(void) memset(device, '\0', PATH_MAX);
} else {
device = flex_device(optarg, 1);
}
break;
/*
* Public interface: install package in global zone only.
*/
case 'G':
globalZoneOnly = B_TRUE;
break;
/*
* Not a public interface: Enable hollow package support. When
* specified, for any package that has SUNW_PKG_HOLLOW=true:
* Do not calculate and verify package size against target.
* Do not run any package procedure or class action scripts.
* Do not create any target directories.
* Do not perform any script locking.
* Do not install any components of any package.
* Do not output any status or database update messages.
*/
case 'h':
set_depend_pkginfo_DB(B_TRUE);
break;
/*
* Not a public interface: Informs scripts that this is
* an initial install by setting the environment parameter
* PKG_INIT_INSTALL=TRUE for all scripts. They may use it as
* they see fit, safe in the knowledge that the target
* filesystem is tabula rasa.
*/
case 'I':
init_install++;
break;
/*
* Not a public interface: ignore signatures.
*/
case 'i':
ignore_sig++;
break;
/*
* Public interface: Use keystore as the location from which to
* get trusted certificate authority certificates when verifying
* digital signatures found in packages. If no keystore is
* specified, then the default keystore locations are searched
* for valid trusted certificates.
*/
case 'k':
if (!path_valid(optarg)) {
progerr(ERR_PATH, optarg);
quit(1);
/* NOTREACHED */
}
keystore_file = optarg;
break;
/*
* Public interface: Instruct pkgadd not to use the
* $root_path/etc/vfstab file for determining the client's
* mount points. This option assumes the mount points are
* correct on the server and it behaves consistently with
* Solaris 2.5 and earlier releases.
*/
case 'M':
no_map_client = 1;
break;
/*
* Not a public interface: the -O option allows the behavior
* of the package tools to be modified. Recognized options:
* -> debug
* ---> enable debugging output
* -> addzonename
* ---> add zone name to appropriate messages
* -> nozones
* ---> act as though in global zone with no non-global zones
* -> enable-hollow-package-support
* ---> Enable hollow package support. When specified, for any
* ---> package that has SUNW_PKG_HOLLOW=true:
* ---> Do not calculate and verify package size against target
* ---> Do not run any package procedure or class action scripts
* ---> Do not create any target directories
* ---> Do not perform any script locking
* ---> Do not install any components of any package
* ---> Do not output any status or database update messages
* -> zonelist="<names...>"
* ---> add package to space/colon separated list of zones only
*/
case 'O':
for (p = strtok(optarg, ","); p != (char *)NULL;
p = strtok(NULL, ",")) {
if (strcmp(p, "debug") == 0) {
/* set debug flag/enable debug output */
debugFlag = B_TRUE;
(void) echoDebugSetFlag(debugFlag);
/* debug info on arguments to pkgadd */
for (n = 0; n < argc && argv[n]; n++) {
echoDebug(DBG_ARG, n, argv[n]);
}
continue;
}
if (strcmp(p,
"enable-hollow-package-support") == 0) {
set_depend_pkginfo_DB(B_TRUE);
continue;
}
if (strcmp(p, "addzonename") == 0) {
quitSetZoneName(z_get_zonename());
continue;
}
if (strcmp(p, "nozones") == 0) {
noZones = B_TRUE;
continue;
}
if (strncmp(p, "zonelist=", 9) == 0) {
/*
* If colons used as separators,
* convert to spaces.
*/
q = p + 9;
while (*q != '\0') {
if (*q == ':') {
*q = ' ';
}
q++;
}
if (z_set_zone_spec(p + 9) == -1)
quit(1);
usedZoneList = B_TRUE;
continue;
}
progerr(ERR_INVALID_O_OPTION, p);
continue;
}
break;
/*
* Public interface: installation occurs in
* non-interactive mode. Suppress output of the list of
* installed files. The default mode is interactive.
*/
case 'n':
nointeract++;
(void) echoSetFlag(B_FALSE);
break;
/*
* Public interface: Password to use to decrypt keystore
* specified with -k, if required. See PASS PHRASE
* ARGUMENTS for more information about the format of this
* option's argument.
*/
case 'P':
if (optarg[0] == '-') {
usage();
quit(1);
}
set_passphrase_passarg(optarg);
if (ci_strneq(optarg, "pass:", 5)) {
/*
* passwords on the command line are highly
* insecure. complain.
*/
logerr(PASSWD_CMDLINE, "pass:<pass>");
}
break;
/*
* Public interface: Define the full path name of a
* directory to use as the root_path. All files,
* including package system information files, are
* relocated to a directory tree starting in the specified
* root_path. The root_path may be specified when
* installing to a client from a server (for example,
* /export/root/client1).
*/
case 'R':
if (optarg[0] == '-') {
usage();
quit(1);
}
/* determine the real path specified */
n = resolvepath(optarg, Rpath, sizeof (Rpath)-1);
/* use supplied path if not resolvable */
if (n == -1) {
(void) strlcpy(Rpath, optarg, sizeof (Rpath));
} else {
/* null terminate string */
Rpath[n] = '\0';
}
/* set the alternative root path */
if (!set_inst_root(Rpath)) {
progerr(ERR_ROOT_CMD);
exit(1);
}
break;
/*
* Public interface: Identify a file or directory which
* contains output from a previous pkgask(1M)
* session. This file supplies the interaction responses
* that would be requested by the package in interactive
* mode. response must be a full pathname.
*/
case 'r':
if (optarg[0] == '-') {
usage();
quit(1);
}
respfile = flex_device(optarg, 2);
if (isdir(respfile) == 0)
respdir = respfile;
break;
/*
* Not a public interface: suppress copyright notice being
* output during installation.
*/
case 'S':
suppressCopyright++;
break;
/*
* Public interface: Write the package into the directory
* spool instead of installing it. The default directory
* for spooled packages is /var/sadm/pkg.
*/
case 's':
spoolDir = flex_device(optarg, 1);
break;
/*
* Not a public interface: disable save spool area creation;
* suppress the creation and population of the package save
* spool area (var/sadm/pkg/PKG/save/pspool/PKG).
*/
case 't':
disableSaveSpool++;
break;
/*
* Public interface: Specify an alternative fs_file to map
* the client's file systems. For example, used in
* situations where the $root_path/etc/vfstab file is
* non-existent or unreliable. Informs the pkginstall
* portion to mount up a client filesystem based upon the
* supplied vfstab-like file of stable format.
*/
case 'V':
vfstab_file = flex_device(optarg, 2);
no_map_client = 0;
break;
/*
* Public interface: Trace all of the scripts that get
* executed by pkgadd, located in the pkginst/install
* directory. This option is used for debugging the
* procedural and non-procedural scripts
*/
case 'v':
pkgverbose++;
break;
/*
* Public interface: Specify a HTTP[S] proxy to use when
* downloading packages The format of proxy is host:port,
* where host is the hostname of the HTTP[S] proxy, and
* port is the port number associated with the proxy. This
* switch overrides all other methods of specifying a
* proxy. See ENVIRONMENT VARIABLES for more information
* on alternate methods of specifying a default proxy.
*/
case 'x':
if (!path_valid(optarg)) {
progerr(ERR_PATH, optarg);
quit(1);
/* NOTREACHED */
}
proxy = optarg;
break;
/*
* Public interface: Install packages based on the value
* of the CATEGORY parameter stored in the package's
* pkginfo(4) file. All packages on the source medium
* whose CATEGORY matches one of the specified categories
* will be selected for installation or spooling. Install
* packages that contain the same CATEGORY as the one
* provided on the command line.
*/
case 'Y':
if (optarg[0] == '-') {
usage();
quit(1);
}
catg_arg = strdup(optarg);
if ((category = get_categories(catg_arg)) == NULL) {
progerr(ERR_CAT_INV, catg_arg);
exit(1);
} else if (is_not_valid_length(category)) {
progerr(ERR_CAT_LNGTH);
exit(1);
}
break;
/*
* Not a public interface: perform fresh install from
* package save spool area. When set, the package contents
* are installed from the package spool save area instead
* of from the package root area, so that the original
* source packages are not required to install the
* package. If the -h option is also specified and the
* package is hollow, then this option is ignored. When -z
* is specified:
* - Editable files are installed from the package instance
* save area.
* - Volatile files are installed from the package instance
* save area.
* - Executable and data files are installed from the final
* installed location as specified in the pkgmap file.
* - Installation scripts are run from the package spool
* save area.
*/
case 'z':
saveSpoolInstall++;
break;
/*
* unrecognized option
*/
default:
usage();
return (1);
}
}
/*
* ********************************************************************
* validate command line options
* ********************************************************************
*/
/* set "debug echo" flag according to setting of "-O debug" option */
(void) echoDebugSetFlag(debugFlag);
/* output entry debugging information */
if (z_running_in_global_zone()) {
echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
} else {
echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
z_get_zonename());
}
/*
* Later, it may be decided to pursue this ability to continue to an
* actual installation based only on the dryrun data. At this time,
* it is too risky.
*/
if (pkgcontsrc && !pkgdrtarg) {
progerr(ERR_NO_LIVE_MODE);
usage();
return (1);
}
/* ignore -G option if not used in the global zone */
if (!z_running_in_global_zone()) {
globalZoneOnly = B_FALSE;
}
/* if zonelist used, must be in global zone */
if (usedZoneList && !z_running_in_global_zone()) {
progerr(ERR_Z_USED_IN_NONGLOBAL_ZONE);
return (1);
}
/* -G and zonelist cannot be used together */
if (globalZoneOnly && usedZoneList) {
progerr(ERR_GZ_USED_TOGETHER);
usage();
return (1);
}
/* -s cannot be used with either -G or zonelist */
if (spoolDir != NULL) {
if (globalZoneOnly) {
progerr(ERR_SPOOLDIR_USED_WITH_G);
usage();
return (1);
}
if (usedZoneList) {
progerr(ERR_SPOOLDIR_USED_WITH_Z);
usage();
return (1);
}
if (strcmp(spoolDir, "/var/sadm/pkg") == 0) {
progerr(ERR_SPOOLDIR_CANNOT_BE_SYS, "/var/sadm/pkg");
usage();
return (1);
}
}
/* pkgask does not support the same options as pkgadd */
if (askflag && proxy) {
progerr(ERR_PKGASK_AND_PROXY);
usage();
return (1);
}
if (askflag && uri) {
progerr(ERR_PKGASK_AND_URI);
usage();
return (1);
}
if (askflag && keystore_file) {
progerr(ERR_PKGASK_AND_KEYSTORE_FILE);
usage();
return (1);
}
if (askflag && ignore_sig) {
progerr(ERR_PKGASK_AND_IGNORE_SIG);
usage();
return (1);
}
if (askflag && spoolDir) {
progerr(ERR_PKGASK_AND_SPOOLDIR);
usage();
return (1);
}
if (askflag && nointeract) {
progerr(ERR_PKGASK_AND_NOINTERACT);
usage();
return (1);
}
/* cannot use response file and web address together */
if (respfile && uri) {
progerr(ERR_RESPFILE_AND_URI);
usage();
return (1);
}
/* cannot use response file/not-interactive and spool-to directory */
if (spoolDir && nointeract) {
progerr(ERR_SPOOLDIR_AND_NOINTERACT);
usage();
return (1);
}
if (spoolDir && respfile) {
progerr(ERR_SPOOLDIR_AND_RESPFILE);
usage();
return (1);
}
if (usedZoneList) {
/* Verify supplied zone list valid for the target */
if (z_verify_zone_spec() == -1)
return (1);
/* -z zonelist=global is logically the same as -G */
if (z_global_only() && z_running_in_global_zone())
globalZoneOnly = B_TRUE;
}
/*
* hook SIGINT and SIGHUP interrupts into quit.c's trap handler
*/
/* hold SIGINT/SIGHUP interrupts */
(void) sighold(SIGHUP);
(void) sighold(SIGINT);
/* connect quit.c:trap() to SIGINT */
nact.sa_handler = quitGetTrapHandler();
nact.sa_flags = SA_RESTART;
(void) sigemptyset(&nact.sa_mask);
(void) sigaction(SIGINT, &nact, &oact);
/* connect quit.c:trap() to SIGHUP */
nact.sa_handler = quitGetTrapHandler();
nact.sa_flags = SA_RESTART;
(void) sigemptyset(&nact.sa_mask);
(void) sigaction(SIGHUP, &nact, &oact);
/* release hold on signals */
(void) sigrelse(SIGHUP);
(void) sigrelse(SIGINT);
/*
* This function is in the libadm library; it sets:
* -> get_PKGLOC() = <install_root>/var/sadm/pkg
* -> get_PKGADM() = <install_root>/var/sadm/install
* -> pkgdir = <install_root>/var/sadm/pkg
* -> pkg_install_root = <install_root>
* This controls operations of libadm functions such as:
* -> pkginfofind, pkginfopen, fpkgparam, pkgparam, get_PKGLOC,
* -> get_PKGADM, get_install_root
*/
set_PKGpaths(get_inst_root());
echoDebug(DBG_PKGADD_PKGPATHS,
get_PKGLOC() ? get_PKGLOC() : "",
get_PKGADM() ? get_PKGADM() : "");
/*
* This function is in the libinst library; it reads the specified
* admin(4) file and, using fpkgparam(), sets the global "adm" structure
* values to match what is in the specified admin file.
*/
echoDebug(DBG_PKGADD_ADMINFILE, admnfile ? admnfile : "");
setadminFile(admnfile);
/*
* if running in the global zone, and non-global zones exist, then
* enable hollow package support so that any packages that are marked
* SUNW_PKG_HOLLOW=true will be correctly installed in non-global zones
* when added directly in the global zone by the global zone admin.
*/
if (is_depend_pkginfo_DB()) {
echoDebug(DBG_PKGADD_HOLLOW_ENABLED);
} else if ((z_running_in_global_zone() == B_TRUE) &&
(z_non_global_zones_exist() == B_TRUE)) {
echoDebug(DBG_PKGADD_ENABLING_HOLLOW);
set_depend_pkginfo_DB(B_TRUE);
}
/* if no device and no url, get and validate default device */
if ((device == NULL) && (uri == NULL)) {
device = devattr("spool", "pathname");
if (device == NULL) {
progerr(ERR_NODEVICE);
quit(1);
/* NOTREACHED */
}
}
/* must be root if not directing results to spool directory */
if ((getuid() != 0) && (spoolDir == NULL)) {
progerr(ERR_NOT_ROOT, prog);
exit(1);
}
/*
* process response file argument
*/
if (respfile) {
echoDebug(DBG_PKGADD_RESPFILE,
respfile, respdir ? respdir : "");
if (respfile[0] != '/') {
progerr(ERR_RSP_FILE_NOTFULLPATH, respfile);
quit(1);
/* NOTREACHED */
}
if (respdir == NULL) {
if (askflag) {
if (access(respfile, F_OK) == 0) {
progerr(ERR_NORESP, respfile);
quit(1);
/* NOTREACHED */
}
} else if (access(respfile, F_OK) != 0) {
progerr(ERR_ACCRESP, respfile);
quit(1);
/* NOTREACHED */
}
}
} else if (askflag) {
progerr(ERR_RSP_FILE_NOT_GIVEN);
usage();
quit(1);
/* NOTREACHED */
}
/* establish temporary directory to use */
if ((tmpdir = getenv("TMPDIR")) == NULL) {
/* use default - no override specified */
tmpdir = P_tmpdir;
}
echoDebug(DBG_PKGADD_TMPDIR, tmpdir);
/*
* setup and prepare secure package operations
*/
/* initialize error object used by security functions */
err = pkgerr_new();
/* validate keystore file */
if (!check_keystore_admin(&keystore_file)) {
progerr(ERR_ADM_KEYSTORE);
quit(1);
/* NOTREACHED */
}
/* if uri provided, establish session */
if (uri != NULL) {
boolean_t b;
int len;
char *bname = (char *)NULL;
set_web_install();
if (!get_proxy_port(err, &proxy, &proxy_port)) {
pkgerr(err);
quit(1);
/* NOTREACHED */
}
if (proxy == NULL) {
if (!get_proxy_port_admin(&proxy, &proxy_port)) {
progerr(ERR_ADM_PROXY);
quit(1);
/* NOTREACHED */
}
}
if ((retries = web_ck_retries()) == 0) {
pkgerr(err);
quit(1);
/* NOTREACHED */
}
if ((timeout = web_ck_timeout()) == 0) {
pkgerr(err);
quit(1);
/* NOTREACHED */
}
/* create temporary directory */
b = setup_temporary_directory(&dwnld_dir, tmpdir, "dwnld");
if (b != B_TRUE) {
progerr(ERR_DWNLDTEMPDIR, tmpdir, strerror(errno));
quit(1);
/* NOTREACHED */
}
canonize_slashes(dwnld_dir);
/* register with quit() so directory is removed on exit */
quitSetDwnldTmpdir(dwnld_dir); /* DO NOT FREE() */
/* open keystore if this is a secure download */
if (scheme == web_https) {
if (open_keystore(err, keystore_file,
get_prog_name(), pkg_passphrase_cb,
KEYSTORE_DFLT_FLAGS, &keystore) != 0) {
pkgerr(err);
web_cleanup();
quit(1);
/* NOTREACHED */
}
}
if (!web_session_control(err, uri, dwnld_dir, keystore, proxy,
proxy_port, retries, timeout, nointeract, &bname)) {
pkgerr(err);
web_cleanup();
quit(1);
/* NOTREACHED */
}
/*
* reset device to point to newly-downloaded file; note
* when (scheme == web_https || scheme == web_http) that
* device gets preloaded with a pointer to PATH_MAX bytes
* allocated via malloc().
*/
len = snprintf(device, PATH_MAX, "%s/%s", dwnld_dir, bname);
if ((len < 0) || (len >= PATH_MAX)) {
progerr(ERR_DIR_CONST, tmpdir);
quit(1);
/* NOTREACHED */
}
}
/*
* See if user wants this to be handled as an old style pkg.
* NOTE : the ``exception_pkg()'' stuff is to be used only
* through on495. This function comes out for on1095. See
* PSARC 1993-546. -- JST
*/
if (getenv("NONABI_SCRIPTS") != NULL) {
old_pkg = 1;
}
/*
* See if the user wants to process symlinks consistent with
* the old behavior.
*/
if (getenv("PKG_NONABI_SYMLINKS") != NULL) {
old_symlinks = 1;
}
/*
* See if the user wants the package name length restricted.
*/
abiPtr = getenv("PKG_ABI_NAMELENGTH");
if (abiPtr && strncasecmp(abiPtr, "TRUE", 4) == 0) {
ABI_namelength = 1;
}
/*
* validate the package source device - return pkgdev info that
* describes the package source device.
*/
if (devtype(device, &pkgdev)) {
progerr(ERR_BAD_DEVICE, device);
quit(1);
/* NOTREACHED */
}
/*
* If writing the packages into a spool directory instead of
* installing the packages, open the package datastream and
* invoke pkgtrans to perform the conversion and exit.
*/
if (spoolDir != (char *)NULL) {
boolean_t b;
int n;
echoDebug(DBG_INSTALLING_TO_SPOOL, spoolDir);
b = open_package_datastream(argc, argv, spoolDir, device,
&repeat, &ids_name, tmpdir,
&pkgdev, optind);
quitSetIdsName(ids_name);
if (b != B_TRUE) {
progerr(ERR_CANNOT_OPEN_PKG_STREAM, PSTR(device));
quit(1);
}
n = pkgtrans(device, spoolDir, &argv[optind],
0, NULL, NULL);
quit(n);
/* NOTREACHED */
}
/*
* error if there are packages on the command line and a category
* was specified
*/
if ((optind < argc) && (catg_arg != NULL)) {
progerr(ERR_PKGS_AND_CAT_PKGADD);
usage();
quit(1);
/* NOTREACHED */
}
/*
* ********************************************************************
* main package processing "loop"
* ********************************************************************
*/
ids_name = NULL;
quitSetIdsName(ids_name);
for (;;) {
boolean_t b;
char **pkglist; /* points to array of pkgs */
/*
* open next package data stream
*/
b = open_package_datastream(argc, argv, spoolDir, device,
&repeat, &ids_name, tmpdir,
&pkgdev, optind);
quitSetIdsName(ids_name);
if (b == B_FALSE) {
echoDebug(ERR_CANNOT_OPEN_PKG_STREAM, PSTR(device));
continue;
}
/*
* package source data stream open - get the package list
*/
b = get_package_list(&pkglist, argv, catg_arg, category,
ignore_sig, err, proxy_port, proxy, keystore,
keystore_file, ids_name, &repeat);
if (b == B_FALSE) {
char path[PATH_MAX];
echoDebug(DBG_CANNOT_GET_PKGLIST);
progerr(ERR_NOPKGS, pkgdev.dirname);
quit(1);
/* NOTREACHED */
}
/*
* count the number of packages to install
* NOTE: npkgs is a global variable that is referenced by quit.c
* when error messages are generated - it is referenced directly
* by the other functions called below...
*/
for (npkgs = 0; pkglist[npkgs] != (char *)NULL; /* void */) {
echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
npkgs++;
}
/* output number of packages to be added */
echoDebug(DBG_NUM_PKGS_TO_ADD, npkgs);
/*
* if pkgask and response container is a file (not a directory),
* and there is more than one package to install, then it is an
* error - too many packages to install when response container
* is a file.
*/
if ((askflag != 0) && (respdir == (char *)NULL) &&
(npkgs > 1)) {
progerr(ERR_TOO_MANY_PKGS);
quit(1);
/* NOTREACHED */
}
/*
* package list generated - add packages
*/
b = add_packages(pkglist, uri, ids_name, repeat,
altBinDir, device, noZones);
/*
* close open input data stream (source package) if left open.
*/
if (ids_name) {
echoDebug(DBG_CLOSING_STREAM, ids_name,
PSTR(pkgdev.dirname));
(void) ds_close(1);
rrmdir(pkgdev.dirname);
ids_name = NULL;
quitSetIdsName(ids_name);
}
/*
* continue with next sequence of packages if continue set
*/
if (b == B_TRUE) {
continue;
}
/*
* not continuing - quit with 0 exit code
*/
quit(0);
/* NOTREACHED */
}
/* NOTREACHED */
}
/*
* *****************************************************************************
* static internal (private) functions
* *****************************************************************************
*/
/*
* Name: pkgZoneCheckInstall
* Description: Invoke pkginstall in a specified zone to perform a preinstall
* check of the a single package in the specified zone
* Arguments: a_zoneName - pointer to string representing the name of the
* zone to check install the package in.
* a_zoneState - current state of the zone; must be mounted or
* running.
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be check installed.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_adminFile - pointer to string representing the admin
* file to pass to pkginstall when installing the package.
* If this is == NULL no admin file is given to pkginstall.
* a_stdoutPath - pointer to string representing the local path
* into which all output written by pkginstall to stdout
* is stored.
* If this is == NULL stdout is redirected to /dev/null
* a_tmpzn - B_TRUE when this zone is booted by the package
* command or B_FALSE if it was running before.
* Returns: int (see ckreturn() function for details)
* 0 - success
* 1 - package operation failed (fatal error)
* 2 - non-fatal error (warning)
* 3 - user selected quit (operation interrupted)
* 4 - admin settings prevented operation
* 5 - interaction required and -n (non-interactive) specified
* "10" will be added to indicate "immediate reboot required"
* "20" will be added to indicate "reboot after install required"
*/
static int
pkgZoneCheckInstall(char *a_zoneName, zone_state_t a_zoneState,
char *a_idsName, char *a_altBinDir, char *a_adminFile,
char *a_stdoutPath, boolean_t a_tmpzn)
{
char *arg[MAXARGS];
char *p;
char adminfd_path[PATH_MAX];
char path[PATH_MAX];
char pkgstreamfd_path[PATH_MAX];
int fds[MAX_FDS];
int maxfds;
int n;
int nargs;
/* entry assertions */
assert(a_zoneName != (char *)NULL);
assert(*a_zoneName != '\0');
/* entry debugging info */
echoDebug(DBG_PKGZONECHECKINSTALL_ENTRY);
echoDebug(DBG_PKGZONECHECKINSTALL_ARGS, a_zoneName, PSTR(pkginst),
PSTR(pkgdev.dirname), PSTR(pkgdev.mount), PSTR(pkgdev.bdevice),
a_zoneState == ZONE_STATE_MOUNTED ? "/a" : "/",
PSTR(a_idsName), PSTR(a_adminFile), PSTR(a_stdoutPath));
/* generate full path to 'phatinstall' to run in zone */
(void) snprintf(path, sizeof (path), "%s/pkginstall",
"/usr/sadm/install/bin");
/* start at first file descriptor */
maxfds = 0;
/*
* generate argument list for call to pkginstall
*/
/* start at argument 0 */
nargs = 0;
/* first argument is always: full path to executable */
arg[nargs++] = path;
/*
* second argument is always: pass -O debug to pkginstall: debug mode
*/
if (debugFlag == B_TRUE) {
arg[nargs++] = "-O";
arg[nargs++] = "debug";
}
/* pkgadd -G: pass -G to pkginstall */
if (globalZoneOnly == B_TRUE) {
arg[nargs++] = "-G";
}
/* pkgadd -b dir: pass -b to pkginstall */
if (a_altBinDir != (char *)NULL) {
arg[nargs++] = "-b";
arg[nargs++] = a_altBinDir;
}
/* pkgadd -C: pass -C to pkginstall: disable checksum */
if (disableChecksum) {
arg[nargs++] = "-C";
}
/* pkgadd -A: pass -A to pkginstall: disable attribute checking */
if (disableAttributes) {
arg[nargs++] = "-A";
}
/*
* NONABI_SCRIPTS defined: pass -o to pkginstall; refers to a
* pkg requiring operator interaction during a procedure script
* (common before on1093)
*/
if (old_pkg) {
arg[nargs++] = "-o";
}
/*
* PKG_NONABI_SYMLINKS defined: pass -y to pkginstall; process
* symlinks consistent with old behavior
*/
if (old_symlinks) {
arg[nargs++] = "-y";
}
/*
* PKG_ABI_NAMELENGTH defined: pass -e to pkginstall; causes
* package name length to be restricted
*/
if (ABI_namelength) {
arg[nargs++] = "-e";
}
/* pkgadd -S: pass -S to pkginstall: suppress copyright notices */
arg[nargs++] = "-S";
/* pkgadd -M: pass -M to pkginstall: dont mount client file systems */
arg[nargs++] = "-M";
/* pkgadd -v: pass -v to pkginstall: never trace scripts */
/* if running pkgask, pass -i to pkginstall: running pkgask */
if (askflag) {
return (0);
}
/* pass "-O enable-hollow-package-support" */
if (is_depend_pkginfo_DB()) {
arg[nargs++] = "-O";
arg[nargs++] = "enable-hollow-package-support";
}
/* check is always in non-interactive mode */
arg[nargs++] = "-n";
/* pkgadd -a admin: pass -a admin to pkginstall in zone: admin file */
if (a_adminFile) {
int fd;
fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
if (fd < 0) {
progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
errno, strerror(errno));
return (1);
}
(void) snprintf(adminfd_path, sizeof (adminfd_path),
"/proc/self/fd/%d", fd);
fds[maxfds++] = fd;
arg[nargs++] = "-a";
arg[nargs++] = adminfd_path;
}
/* pkgadd -R root: pass -R /a to pkginstall when zone is mounted */
if (a_zoneState == ZONE_STATE_MOUNTED) {
arg[nargs++] = "-R";
arg[nargs++] = "/a";
}
/* pass -N to pkginstall: program name to report */
arg[nargs++] = "-N";
arg[nargs++] = get_prog_name();
/* pass "-O preinstallcheck" */
arg[nargs++] = "-O";
arg[nargs++] = "preinstallcheck";
/* add "-O addzonename" */
arg[nargs++] = "-O";
arg[nargs++] = "addzonename";
/*
* add parent zone info/type
*/
p = z_get_zonename();
if ((p != NULL) && (*p != '\0')) {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-name=%s", p);
arg[nargs++] = "-O";
arg[nargs++] = strdup(zn);
}
/* current zone type */
arg[nargs++] = "-O";
if (z_running_in_global_zone() == B_TRUE) {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-type=%s",
TAG_VALUE_GLOBAL_ZONE);
arg[nargs++] = strdup(zn);
} else {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-type=%s",
TAG_VALUE_NONGLOBAL_ZONE);
arg[nargs++] = strdup(zn);
}
/* Add the pkgserv options */
arg[nargs++] = "-O";
arg[nargs++] = pkgmodeargument(a_tmpzn ? RUN_ONCE : pkgservergetmode());
/* add in the package stream file */
if (a_idsName != NULL) {
int fd;
fd = openLocal(a_idsName, O_RDONLY, tmpdir);
if (fd < 0) {
progerr(ERR_STREAM_UNAVAILABLE, a_idsName,
pkginst, strerror(errno));
quit(1);
}
(void) snprintf(pkgstreamfd_path, sizeof (pkgstreamfd_path),
"/proc/self/fd/%d", fd);
fds[maxfds++] = fd;
arg[nargs++] = pkgstreamfd_path;
} else {
progerr(ERR_PKGZONEINSTALL_NO_STREAM);
quit(1);
}
/* add package instance name */
arg[nargs++] = pkginst;
/* terminate the argument list */
arg[nargs++] = NULL;
/*
* run the appropriate pkginstall command in the specified zone
*/
if (debugFlag == B_TRUE) {
echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
for (n = 0; arg[n]; n++) {
echoDebug(DBG_ARG, n, arg[n]);
}
}
/* terminate file descriptor list */
fds[maxfds] = -1;
/* exec command in zone */
n = z_zone_exec(a_zoneName, path, arg, a_stdoutPath, (char *)NULL, fds);
echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n,
PSTR(a_stdoutPath));
/*
* close any files that were opened for use by the
* /proc/self/fd interface so they could be passed to programs
* via the z_zone_exec() interface
*/
for (; maxfds > 0; maxfds--) {
(void) close(fds[maxfds-1]);
}
/* return results of pkginstall in zone execution */
return (n);
}
/*
* Name: pkgZoneInstall
* Description: Invoke pkginstall in a specified zone to perform an install
* of a single package in the specified zone
* Arguments: a_zoneName - pointer to string representing the name of the
* zone to install the package in.
* a_zoneState - current state of the zone; must be mounted or
* running.
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be installed.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_adminFile - pointer to string representing the admin
* file to pass to pkginstall when installing the package.
* If this is == NULL no admin file is given to pkginstall.
* a_stdoutPath - pointer to string representing the local path
* into which all output written by pkginstall to stdout
* is stored.
* If this is == NULL stdout is redirected to /dev/null
* a_tmpzn - B_TRUE when this zone is booted by the package
* command or B_FALSE if it was running before.
* Returns: int (see ckreturn() function for details)
* 0 - success
* 1 - package operation failed (fatal error)
* 2 - non-fatal error (warning)
* 3 - user selected quit (operation interrupted)
* 4 - admin settings prevented operation
* 5 - interaction required and -n (non-interactive) specified
* "10" will be added to indicate "immediate reboot required"
* "20" will be added to indicate "reboot after install required"
*/
static int
pkgZoneInstall(char *a_zoneName, zone_state_t a_zoneState, char *a_idsName,
char *a_altBinDir, char *a_adminFile, boolean_t a_tmpzn)
{
char *arg[MAXARGS];
char *p;
char adminfd_path[PATH_MAX];
char path[PATH_MAX];
char pkgstreamfd_path[PATH_MAX];
char respfilefd_path[PATH_MAX];
int fds[MAX_FDS];
int maxfds;
int n;
int nargs;
/* entry assertions */
assert(a_zoneName != (char *)NULL);
assert(*a_zoneName != '\0');
/* entry debugging info */
echoDebug(DBG_PKGZONEINSTALL_ENTRY);
echoDebug(DBG_PKGZONEINSTALL_ARGS, a_zoneName, PSTR(pkginst),
PSTR(pkgdev.dirname), PSTR(pkgdev.mount), PSTR(pkgdev.bdevice),
a_zoneState == ZONE_STATE_MOUNTED ? "/a" : "", PSTR(a_idsName),
a_adminFile);
/* generate path to pkginstall */
(void) snprintf(path, sizeof (path), "%s/pkginstall", PKGBIN);
/* start at first file descriptor */
maxfds = 0;
/*
* generate argument list for call to pkginstall
*/
/* start at argument 0 */
nargs = 0;
/* first argument is path to executable */
arg[nargs++] = path;
/*
* second argument is always: pass -O debug to pkginstall: debug mode
*/
if (debugFlag == B_TRUE) {
arg[nargs++] = "-O";
arg[nargs++] = "debug";
}
/* pkgadd -G: pass -G to pkginstall */
if (globalZoneOnly == B_TRUE) {
arg[nargs++] = "-G";
}
/* pkgadd -b dir: pass -b to pkginstall in zone */
if (a_altBinDir != (char *)NULL) {
arg[nargs++] = "-b";
arg[nargs++] = a_altBinDir;
}
/* pkgadd -B blocksize: pass -B to pkginstall in zone */
if (rw_block_size != NULL) {
arg[nargs++] = "-B";
arg[nargs++] = rw_block_size;
}
/* pkgadd -C: pass -C to pkgadd in zone: disable checksum */
if (disableChecksum) {
arg[nargs++] = "-C";
}
/* pkgadd -A: pass -A to pkgadd in zone: disable attribute checking */
if (disableAttributes) {
arg[nargs++] = "-A";
}
/* pkgadd -S: pass -S to pkgadd in zone: suppress copyright notices */
arg[nargs++] = "-S";
/* pkgadd -I: pass -I to pkgadd in zone: initial install */
if (init_install) {
arg[nargs++] = "-I";
}
/* pkgadd -M: pass -M to pkgadd in zone: dont mount client file sys */
arg[nargs++] = "-M";
/* pkgadd -v: pass -v to pkgadd in zone: trace scripts */
if (pkgverbose) {
arg[nargs++] = "-v";
}
/* pkgadd -z: pass -z to pkgadd in zone fresh inst from pkg save area */
if (saveSpoolInstall) {
arg[nargs++] = "-z";
}
/* pass "-O enable-hollow-package-support" */
if (is_depend_pkginfo_DB()) {
arg[nargs++] = "-O";
arg[nargs++] = "enable-hollow-package-support";
}
/* pkgadd -t pass -t to pkgadd in zone disable save spool area create */
if (disableSaveSpool) {
arg[nargs++] = "-t";
}
/* if running pkgask, pass -i to pkgadd in zone: running pkgask */
if (askflag) {
echo(MSG_BYPASSING_ZONE, a_zoneName);
return (0);
}
/*
* pkgadd -n (not pkgask): pass -n to pkginstall: noninteractive mode
*/
if (nointeract && !askflag) {
arg[nargs++] = "-n";
}
/* pkgadd -a admin: pass -a admin to pkginstall in zone: admin file */
if (a_adminFile) {
int fd;
fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
if (fd < 0) {
progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
errno, strerror(errno));
return (1);
}
(void) snprintf(adminfd_path, sizeof (adminfd_path),
"/proc/self/fd/%d", fd);
fds[maxfds++] = fd;
arg[nargs++] = "-a";
arg[nargs++] = adminfd_path;
}
/* pkgadd -R root: pass -R /a to pkginstall when zone is mounted */
if (a_zoneState == ZONE_STATE_MOUNTED) {
arg[nargs++] = "-R";
arg[nargs++] = "/a";
}
/*
* pkgadd -D arg: pass -D dryrun to pkginstall in zone: dryrun
* mode/file
*/
if (pkgdrtarg) {
arg[nargs++] = "-D";
arg[nargs++] = pkgdrtarg;
}
/*
* pkgadd -c cont: pass -c cont to pkginstall in zone: continuation
* file
*/
if (pkgcontsrc) {
arg[nargs++] = "-c";
arg[nargs++] = pkgcontsrc;
}
/* pkgadd -r resp: pass -r resp to pkginstall in zone: response file */
if (respfile) {
int fd;
fd = openLocal(respfile, O_RDONLY, tmpdir);
if (fd < 0) {
progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
errno, strerror(errno));
return (1);
}
(void) snprintf(respfilefd_path,
sizeof (respfilefd_path),
"/proc/self/fd/%d", fd);
fds[maxfds++] = fd;
arg[nargs++] = "-r";
arg[nargs++] = respfilefd_path;
}
/* add "-O addzonename" */
arg[nargs++] = "-O";
arg[nargs++] = "addzonename";
/*
* add parent zone info/type
*/
p = z_get_zonename();
if ((p != NULL) && (*p != '\0')) {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-name=%s", p);
arg[nargs++] = "-O";
arg[nargs++] = strdup(zn);
}
/* current zone type */
arg[nargs++] = "-O";
if (z_running_in_global_zone() == B_TRUE) {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-type=%s",
TAG_VALUE_GLOBAL_ZONE);
arg[nargs++] = strdup(zn);
} else {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-type=%s",
TAG_VALUE_NONGLOBAL_ZONE);
arg[nargs++] = strdup(zn);
}
/* Add the pkgserv options */
arg[nargs++] = "-O";
arg[nargs++] = pkgmodeargument(a_tmpzn ? RUN_ONCE : pkgservergetmode());
/* add in the package stream file */
if (a_idsName != NULL) {
int fd;
fd = openLocal(a_idsName, O_RDONLY, tmpdir);
if (fd < 0) {
progerr(ERR_STREAM_UNAVAILABLE, a_idsName,
pkginst, strerror(errno));
quit(1);
}
(void) snprintf(pkgstreamfd_path, sizeof (pkgstreamfd_path),
"/proc/self/fd/%d", fd);
fds[maxfds++] = fd;
arg[nargs++] = pkgstreamfd_path;
} else {
progerr(ERR_PKGZONEINSTALL_NO_STREAM);
quit(1);
}
/* add package instance name */
arg[nargs++] = pkginst;
/* terminate the argument list */
arg[nargs++] = NULL;
/*
* run the appropriate pkginstall command in the specified zone
*/
if (debugFlag == B_TRUE) {
echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
for (n = 0; arg[n]; n++) {
echoDebug(DBG_ARG, n, arg[n]);
}
}
/* terminate file descriptor list */
fds[maxfds] = -1;
/* exec command in zone */
n = z_zone_exec(a_zoneName, path, arg, (char *)NULL, (char *)NULL, fds);
echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n, "");
/*
* close any files that were opened for use by the
* /proc/self/fd interface so they could be passed to programs
* via the z_zone_exec() interface
*/
for (; maxfds > 0; maxfds--) {
(void) close(fds[maxfds-1]);
}
/* return results of pkginstall in zone execution */
return (n);
}
/*
* Name: pkgInstall
* Description: Invoke pkginstall in the current zone to perform an install
* of a single package to the current zone or standalone system
* Arguments: a_altRoot - pointer to string representing the alternative
* root to use for the install
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be installed.
* a_pkgDir - pointer to string representing the path to the
* directory containing the package
* a_altBinDir - pointer to string representing location of the
* pkginstall executable to run. If not NULL, then pass
* the path specified to the -b option to pkginstall.
* Returns: int (see ckreturn() function for details)
* 0 - success
* 1 - package operation failed (fatal error)
* 2 - non-fatal error (warning)
* 3 - user selected quit (operation interrupted)
* 4 - admin settings prevented operation
* 5 - interaction required and -n (non-interactive) specified
* "10" will be added to indicate "immediate reboot required"
* "20" will be added to indicate "reboot after install required"
* NOTE: Both a_idsName and a_pkgDir are used to determine where the
* package to be installed is located. If a_idsName is != NULL
* then it must be the path to a device containing a package
* stream that contains the package to be installed. If a_idsName
* is == NULL then a_pkgDir must contain a full path to a directory
* that contains the package to be installed.
*/
static int
pkgInstall(char *a_altRoot, char *a_idsName, char *a_pkgDir, char *a_altBinDir)
{
char *arg[MAXARGS];
char *p;
char path[PATH_MAX];
char buffer[256];
int n, nargs;
/* entry debugging info */
echoDebug(DBG_PKGINSTALL_ENTRY);
echoDebug(DBG_PKGINSTALL_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
PSTR(pkgdev.mount), PSTR(pkgdev.bdevice), PSTR(a_altRoot),
PSTR(a_idsName), PSTR(a_pkgDir));
/* generate full path to 'pkginstall' to run in zone */
(void) snprintf(path, sizeof (path), "%s/pkginstall",
a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
/*
* generate argument list for call to pkginstall
*/
/* start at argument 0 */
nargs = 0;
/* first argument is path to executable */
arg[nargs++] = path;
/*
* second argument is always: pass -O debug to pkginstall: debug mode
*/
if (debugFlag == B_TRUE) {
arg[nargs++] = "-O";
arg[nargs++] = "debug";
}
arg[nargs++] = "-O";
arg[nargs++] = pkgmodeargument(pkgservergetmode());
/*
* pkgadd -G: pass -G to pkginstall if:
* - the -G option is specified on the pkgadd command line
* - this package is marked 'this zone only':
* -- package has SUNW_PKG_THISZONE=true, or
* -- package has a request script
* Setting -G for pkginstall causes pkginstall to install the package
* in the target zone. If running in the global zone, will install the
* package and mark the package as installed "in the global zone only".
* If running in a non-global zone, will just install the package.
*/
if (globalZoneOnly == B_TRUE) {
arg[nargs++] = "-G";
} else if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
arg[nargs++] = "-G";
}
/* pkgadd -b dir: pass -b to pkginstall */
if (a_altBinDir != (char *)NULL) {
arg[nargs++] = "-b";
arg[nargs++] = a_altBinDir;
}
/* pkgadd -B blocksize: pass -B to pkginstall */
if (rw_block_size != NULL) {
arg[nargs++] = "-B";
arg[nargs++] = rw_block_size;
}
/* pkgadd -C: pass -C to pkginstall: disable checksum */
if (disableChecksum) {
arg[nargs++] = "-C";
}
/* pkgadd -A: pass -A to pkginstall: disable attribute checking */
if (disableAttributes) {
arg[nargs++] = "-A";
}
/*
* NONABI_SCRIPTS defined: pass -o to pkginstall; refers to a
* pkg requiring operator interaction during a procedure script
* (common before on1093)
*/
if (old_pkg) {
arg[nargs++] = "-o";
}
/*
* PKG_NONABI_SYMLINKS defined: pass -y to pkginstall; process
* symlinks consistent with old behavior
*/
if (old_symlinks) {
arg[nargs++] = "-y";
}
/*
* PKG_ABI_NAMELENGTH defined: pass -e to pkginstall; causes
* package name length to be restricted
*/
if (ABI_namelength) {
arg[nargs++] = "-e";
}
/* pkgadd -S: pass -S to pkginstall: suppress copyright notices */
if (suppressCopyright) {
arg[nargs++] = "-S";
}
/* pkgadd -I: pass -I to pkginstall: initial install being performed */
if (init_install) {
arg[nargs++] = "-I";
}
/* pkgadd -M: pass -M to pkginstall: dont mount client file systems */
if (no_map_client) {
arg[nargs++] = "-M";
}
/* pkgadd -v: pass -v to pkginstall: trace scripts */
if (pkgverbose) {
arg[nargs++] = "-v";
}
/* pkgadd -z: pass -z to pkginstall: fresh install from pkg save area */
if (saveSpoolInstall) {
arg[nargs++] = "-z";
}
/*
* if running in a non-global zone and the 'hollow' attribute is
* passed in, then pass -h to pkginstall so that it knows how to
* handle hollow packages for this local zone.
*/
if (!z_running_in_global_zone() && is_depend_pkginfo_DB()) {
arg[nargs++] = "-h";
}
/* pkgadd -t: pass -t to pkginstall: disable save spool area creation */
if (disableSaveSpool) {
arg[nargs++] = "-t";
}
/* if running pkgask, pass -i to pkginstall: running pkgask */
if (askflag) {
arg[nargs++] = "-i";
}
/* pkgadd -n (not pkgask): pass -n to pkginstall: noninteractive mode */
if (nointeract && !askflag) {
arg[nargs++] = "-n";
}
/* pkgadd -a admin: pass -a admin to pkginstall: admin file */
if (admnfile) {
arg[nargs++] = "-a";
arg[nargs++] = admnfile;
}
/* pkgadd -D dryrun: pass -D dryrun to pkginstall: dryrun mode/file */
if (pkgdrtarg) {
arg[nargs++] = "-D";
arg[nargs++] = pkgdrtarg;
}
/* pkgadd -c cont: pass -c cont to pkginstall: continuation file */
if (pkgcontsrc) {
arg[nargs++] = "-c";
arg[nargs++] = pkgcontsrc;
}
/* pkgadd -V vfstab: pass -V vfstab to pkginstall: alternate vfstab */
if (vfstab_file) {
arg[nargs++] = "-V";
arg[nargs++] = vfstab_file;
}
/* pkgadd -r resp: pass -r resp to pkginstall: response file */
if (respfile) {
arg[nargs++] = "-r";
arg[nargs++] = respfile;
}
/* pkgadd -R root: pass -R root to pkginstall: alternative root */
if (a_altRoot && *a_altRoot) {
arg[nargs++] = "-R";
arg[nargs++] = a_altRoot;
}
/*
* If input data stream is available,
* - add: -d ids_name -p number_of_parts
* else,
* - add: -d device -m mount [-f type]
*/
if (a_idsName != NULL) {
arg[nargs++] = "-d";
arg[nargs++] = a_idsName;
arg[nargs++] = "-p";
ds_close(1);
ds_putinfo(buffer, sizeof (buffer));
arg[nargs++] = buffer;
} else if (pkgdev.mount != NULL) {
arg[nargs++] = "-d";
arg[nargs++] = pkgdev.bdevice;
arg[nargs++] = "-m";
arg[nargs++] = pkgdev.mount;
if (pkgdev.fstyp != NULL) {
arg[nargs++] = "-f";
arg[nargs++] = pkgdev.fstyp;
}
}
/*
* add parent zone info/type
*/
p = z_get_zonename();
if ((p != NULL) && (*p != '\0')) {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-name=%s", p);
arg[nargs++] = "-O";
arg[nargs++] = strdup(zn);
}
/* current zone type */
arg[nargs++] = "-O";
if (z_running_in_global_zone() == B_TRUE) {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-type=%s",
TAG_VALUE_GLOBAL_ZONE);
arg[nargs++] = strdup(zn);
} else {
char zn[MAXPATHLEN];
(void) snprintf(zn, sizeof (zn),
"parent-zone-type=%s",
TAG_VALUE_NONGLOBAL_ZONE);
arg[nargs++] = strdup(zn);
}
/* pass -N to pkginstall: program name to report */
arg[nargs++] = "-N";
arg[nargs++] = get_prog_name();
/* add package directory name */
arg[nargs++] = a_pkgDir;
/* add package instance name */
arg[nargs++] = pkginst;
/* terminate the argument list */
arg[nargs++] = NULL;
/*
* run the appropriate pkginstall command in the specified zone
*/
if (debugFlag == B_TRUE) {
echoDebug(DBG_ZONE_EXEC_ENTER, "global", arg[0]);
for (n = 0; arg[n]; n++) {
echoDebug(DBG_ARG, n, arg[n]);
}
}
/* execute pkginstall command */
n = pkgexecv(NULL, NULL, NULL, NULL, arg);
/* return results of pkginstall execution */
return (n);
}
/*
* function to clear out any exisiting error return conditions that may have
* been set by previous calls to ckreturn()
*/
static void
resetreturn()
{
admnflag = 0; /* != 0 if any pkg op admin setting failure (4) */
doreboot = 0; /* != 0 if reboot required after installation (>= 10) */
failflag = 0; /* != 0 if fatal error has occurred (1) */
intrflag = 0; /* != 0 if user selected quit (3) */
ireboot = 0; /* != 0 if immediate reboot required (>= 20) */
nullflag = 0; /* != 0 if admin interaction required (5) */
warnflag = 0; /* != 0 if non-fatal error has occurred (2) */
interrupted = 0; /* last pkg op was quit (1,2,3,4,5) */
needconsult = 0; /* essential ask admin now (1,2,3,5) */
}
/*
* function which checks the indicated return value
* and indicates disposition of installation
*/
static void
ckreturn(int retcode)
{
/*
* entry debugging info
*/
echoDebug(DBG_PKGADD_CKRETURN, retcode, PSTR(pkginst));
/* reset needconsult so it only reflects this call to ckreturn */
needconsult = 0;
switch (retcode) {
case 0: /* successful */
case 10:
case 20:
break; /* empty case */
case 1: /* package operation failed (fatal error) */
case 11:
case 21:
failflag++;
interrupted++;
needconsult++;
break;
case 2: /* non-fatal error (warning) */
case 12:
case 22:
warnflag++;
interrupted++;
needconsult++;
break;
case 3: /* user selected quit; operation interrupted */
case 13:
case 23:
intrflag++;
interrupted++;
needconsult++;
break;
case 4: /* admin settings prevented operation */
case 14:
case 24:
admnflag++;
interrupted++;
break;
case 5: /* administration: interaction req (no -n) */
case 15:
case 25:
nullflag++;
interrupted++;
needconsult++;
break;
default:
failflag++;
interrupted++;
needconsult++;
return;
}
if (retcode >= 20) {
ireboot++;
} else if (retcode >= 10) {
doreboot++;
}
}
static void
usage(void)
{
char *prog = get_prog_name();
if (askflag) {
(void) fprintf(stderr, ERR_USAGE_PKGASK, prog);
} else if (z_running_in_global_zone() == B_FALSE) {
(void) fprintf(stderr, ERR_USAGE_PKGADD_NONGLOBALZONE,
prog, prog);
} else {
(void) fprintf(stderr, ERR_USAGE_PKGADD_GLOBALZONE,
prog, prog);
}
}
/*
* Name: check_applicability
* Description: determine if a package is installable in this zone; that is,
* does the scope of install conflict with existing installation
* or can the package be installed
* Arguments: a_packageDir - [RO, *RO] - (char *)
* Pointer to string representing the directory where the
* package is located
* a_pkgInst - [RO, *RO] - (char *)
* Pointer to string representing the name of the package
* to check
* a_rootPath - [RO, *RO] - (char *)
* Pointer to string representing path to the root of the
* file system where the package is to be installed - this
* is usually the same as the "-R" argument to pkgadd
* a_flags - [RO, *RO] - (CAF_T)
* Flags set by the caller to indicate the conditions
* under which the package is to be installed:
* CAF_IN_GLOBAL_ZONE - in global zone
* CAF_SCOPE_GLOBAL - -G specified
* CAF_SCOPE_NONGLOBAL - -Z specified
* Returns: boolean_t
* B_TRUE - the package can be installed
* B_FALSE - the package can not be installed
*/
static boolean_t
check_applicability(char *a_packageDir, char *a_pkgInst, char *a_rootPath,
CAF_T a_flags)
{
FILE *pkginfoFP;
FILE *pkgmapFP;
boolean_t all_zones; /* pkg is "all zones" only */
boolean_t in_gz_only; /* pkg installed in global zone only */
boolean_t is_hollow; /* pkg is "hollow" */
boolean_t pkg_installed; /* pkg is installed */
boolean_t this_zone; /* pkg is "this zone" only */
boolean_t reqfile_found = B_FALSE;
char instPkg[PKGSIZ+1]; /* installed pkg instance nam */
char instPkgPath[PATH_MAX]; /* installed pkg toplevel dir */
char pkginfoPath[PATH_MAX]; /* pkg 2 install pkginfo file */
char pkgmapPath[PATH_MAX]; /* pkg 2 install pkgmap file */
char pkgpath[PATH_MAX]; /* pkg 2 install toplevel dir */
int len;
char line[LINE_MAX];
/* entry assertions */
assert(a_packageDir != (char *)NULL);
assert(*a_packageDir != '\0');
assert(a_pkgInst != (char *)NULL);
assert(*a_pkgInst != '\0');
/* normalize root path */
if (a_rootPath == (char *)NULL) {
a_rootPath = "";
}
/* entry debugging info */
echoDebug(DBG_CHECKAPP_ENTRY);
echoDebug(DBG_CHECKAPP_ARGS, a_pkgInst, a_packageDir, a_rootPath);
/*
* calculate paths to various objects
*/
/* path to package to be installed top level (main) directory */
len = snprintf(pkgpath, sizeof (pkgpath), "%s/%s", a_packageDir,
a_pkgInst);
if (len > sizeof (pkgpath)) {
progerr(ERR_CREATE_PATH_2, a_packageDir, a_pkgInst);
return (B_FALSE);
}
/* error if package top level directory does not exist */
if (isdir(pkgpath) != 0) {
progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
return (B_FALSE);
}
/* path to pkginfo file within the package to be installed */
len = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/pkginfo",
pkgpath);
if (len > sizeof (pkginfoPath)) {
progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
return (B_FALSE);
}
/* path to highest instance of package currently installed */
pkgLocateHighestInst(instPkgPath, sizeof (instPkgPath),
instPkg, sizeof (instPkg), a_rootPath, a_pkgInst);
/*
* gather information from this package's pkginfo file
*/
pkginfoFP = fopen(pkginfoPath, "r");
if (pkginfoFP == (FILE *)NULL) {
progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
strerror(errno));
return (B_FALSE);
}
/* determine "HOLLOW" setting for this package */
is_hollow = pkginfoParamTruth(pkginfoFP, PKG_HOLLOW_VARIABLE,
"true", B_FALSE);
/* determine "ALLZONES" setting for this package */
all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
"true", B_FALSE);
/* determine "THISZONE" setting for this package */
this_zone = pkginfoParamTruth(pkginfoFP, PKG_THISZONE_VARIABLE,
"true", B_FALSE);
/* close pkginfo file */
(void) fclose(pkginfoFP);
/*
* If request file is not found, it may be in the datastream which
* is not yet unpacked. Check in the pkgmap file.
*/
if (isfile(pkgpath, REQUEST_FILE) != 0) {
/* path to pkgmap file within the package to be installed */
(void) snprintf(pkgmapPath, sizeof (pkgmapPath), "%s/pkgmap",
pkgpath);
pkgmapFP = fopen(pkgmapPath, "r");
if (pkgmapFP == NULL) {
progerr(ERR_NO_PKG_MAPFILE, a_pkgInst,
pkgmapPath, strerror(errno));
return (B_FALSE);
}
while (fgets(line, LINE_MAX, pkgmapFP) != NULL) {
if (strstr(line, " i request") != NULL) {
reqfile_found = B_TRUE;
break;
}
}
(void) fclose(pkgmapFP);
} else {
reqfile_found = B_TRUE;
}
/*
* If this package is not marked for installation in this zone only,
* check to see if this package has a request script. If this package
* does have a request script, then mark the package for installation
* in this zone only. Any package with a request script cannot be
* installed outside of the zone the pkgadd command is being run in,
* nor can such a package be installed as part of a new zone install.
* A new zone install must be non-interactive, which is required
* by all packages integrated into the Solaris WOS.
*/
if ((!this_zone) && (reqfile_found)) {
if (a_flags & CAF_IN_GLOBAL_ZONE) {
echoDebug(DBG_CHECKAPP_THISZONE_REQUEST, a_pkgInst);
}
this_zone = B_TRUE;
}
/*
* If this package is already installed, see if the current installation
* of the package has a request file - if it does, then act as though
* the current package to be added has a request file - install the
* package in the current zone only.
*/
if ((!this_zone) && (instPkgPath[0] != '\0') &&
(isfile(instPkgPath, REQUEST_FILE) == 0)) {
if (a_flags & CAF_IN_GLOBAL_ZONE) {
echoDebug(DBG_CHECKAPP_THISZONE_INSTREQ,
a_pkgInst, instPkg);
}
this_zone = B_TRUE;
}
/* gather information from the global zone only file */
in_gz_only = B_FALSE;
if (a_flags & CAF_IN_GLOBAL_ZONE) {
in_gz_only = pkgIsPkgInGzOnly(a_rootPath, a_pkgInst);
}
/* determine if this package is currently installed */
pkg_installed = pkginfoIsPkgInstalled((struct pkginfo **)NULL,
a_pkgInst);
/*
* verify package applicability based on information gathered,
* and validate the three SUNW_PKG_ options:
*
* -----------|--------------|-------------|-------------|-----------
* - - - - - -| GLOBAL ZONE -| GLOBAL ZONE | LOCAL ZONE | LOCAL ZONE
* - - - - - -| - - pkgadd - | pkgadd -G | pkgadd | pkgadd -G
* ----1------|--------------|-------------|-------------|------------
* ALLZONES f | add to gz | add to gz | add to ls | add to ls
* HOLLOW f | current lz | not to curr | only - - - -| only - - -
* THISZONE f | futr lz - - -| or futr lz | - - - - - - | - - - - - -
* ----2------|--------------|-------------|-------------|------------
* ALLZONES T | add to gz | operation | operation | operation
* HOLLOW f | current lz | not allowed | not allowed | not allowed
* THISZONE f | future lz | - - - - - - | - - - - - - | - - - - - -
* ----3------|--------------|-------------|-------------|------------
* ALLZONES T | add to gz | operation | operation | operation
* HOLLOW T | pkg db only | not allowed | not allowed | not allowed
* THISZONE f | curr/futr lz | - - - - - - | - - - - - - | - - - - - -
* ----4------|--------------|-------------|-------------|------------
* ALLZONES T | bad option | bad option | bad option | bad option
* HOLLOW * | combo - - - -| combo - - - | combo - - - | combo - -
* THISZONE T | - - - - - - -|- - - - - - -|- - - - - - -|- - - - - -
* ----5------|--------------|-------------|-------------|------------
* ALLZONES f | bad option | bad option | bad option | bad option
* HOLLOW T | combo - - - -| combo - - - | combo - - - | combo - - -
* THISZONE * | - - - - - - -| - - - - - - | - - - - - - | - - - - - -
* ----6------|--------------|-------------|-------------|------------
* ALLZONES f | add to gz | add to gz | add to lz | add to lz
* HOLLOW f | not current | not current | only - - - | only - - -
* THISZONE T | or future lz | or futr lz | - - - - - - | - - - - - -
* -----------|--------------|-------------|-------------|-----------
*/
/* pkg "all zones" && "this zone" (#4) */
if (all_zones && this_zone) {
progerr(ERR_ALLZONES_AND_THISZONE, a_pkgInst,
PKG_ALLZONES_VARIABLE, PKG_THISZONE_VARIABLE);
return (B_FALSE);
}
/* pkg "!all zones" && "hollow" (#5) */
if ((!all_zones) && is_hollow) {
progerr(ERR_NOW_ALLZONES_AND_HOLLOW, a_pkgInst,
PKG_ALLZONES_VARIABLE, PKG_HOLLOW_VARIABLE);
return (B_FALSE);
}
/* pkg ALLZONES=true && -Z specified */
if (all_zones && (a_flags & CAF_SCOPE_NONGLOBAL)) {
progerr(ERR_ALLZONES_AND_Z_USED, a_pkgInst);
return (B_FALSE);
}
/* pkg ALLZONES=true & not running in global zone (#2/#3) */
if (all_zones && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
progerr(ERR_ALLZONES_AND_IN_LZ, a_pkgInst);
return (B_FALSE);
}
/* pkg "in gz only" & pkg "NOT installed" */
if (in_gz_only && (!pkg_installed)) {
/* MAKE A WARNING */
echo(ERR_IN_GZ_AND_NOT_INSTALLED, a_pkgInst,
pkgGetGzOnlyPath());
}
/* pkg ALLZONES=true & pkg "in gz only" & pkg "is installed" */
if (all_zones && in_gz_only && pkg_installed) {
progerr(ERR_IN_GZ_AND_ALLZONES_AND_INSTALLED, a_pkgInst);
return (B_FALSE);
}
/* pkg ALLZONES=true && -G specified (#2/#3) */
if (all_zones && (a_flags & CAF_SCOPE_GLOBAL)) {
progerr(ERR_ALLZONES_AND_G_USED, a_pkgInst);
return (B_FALSE);
}
/* pkg "!this zone" && "in gz only" & -G not specified */
if ((!this_zone) && in_gz_only && (!(a_flags & CAF_SCOPE_GLOBAL))) {
progerr(ERR_IN_GZ_AND_NO_G_USED, a_pkgInst);
return (B_FALSE);
}
/* pkg "NOT in gz only" & -Z specified */
if ((!in_gz_only) && (a_flags & CAF_SCOPE_NONGLOBAL)) {
progerr(ERR_NOT_IN_GZ_AND_Z_USED, a_pkgInst);
return (B_FALSE);
}
/* pkg "this zone" && -Z specified */
if (this_zone && (a_flags & CAF_SCOPE_NONGLOBAL)) {
progerr(ERR_THISZONE_AND_Z_USED, PKG_THISZONE_VARIABLE,
a_pkgInst);
return (B_FALSE);
}
/*
* If this package is marked 'this zone only', then mark the package
* as "add to this zone only". This is referenced by the various
* add_package_... functions to determine if the package should be
* added to the current zone, or to all zones, depending on the
* zone in which the command is being run.
*/
if (this_zone) {
pkgAddThisZonePackage(a_pkgInst);
}
return (B_TRUE);
}
/*
* Name: create_zone_adminfile
* Description: Given a zone temporary directory and optionally an existing
* administration file, generate an administration file that
* can be used to perform "non-interactive" operations in a
* non-global zone.
* Arguments: r_zoneAdminFile - pointer to handle that will contain a
* string representing the path to the temporary
* administration file created - this must be NULL
* before the first call to this function - on
* subsequent calls if the pointer is NOT null then
* the existing string will NOT be overwritten.
* a_zoneTempDir - pointer to string representing the path
* to the zone temporary directory to create the
* temporary administration file in
* a_admnfile - pointer to string representing the path to
* an existing "user" administration file - the
* administration file created will contain the
* settings contained in this file, modified as
* appropriate to supress any interaction;
* If this is == NULL then the administration file
* created will not contain any extra settings
* Returns: void
* NOTE: Any string returned is placed in new storage for the
* calling method. The caller must use 'free' to dispose
* of the storage once the string is no longer needed.
* NOTE: On any error this function will call 'quit(1)'
*/
static void
create_zone_adminfile(char **r_zoneAdminFile, char *a_zoneTempDir,
char *a_admnfile)
{
boolean_t b;
/* entry assertions */
assert(r_zoneAdminFile != (char **)NULL);
assert(a_zoneTempDir != (char *)NULL);
assert(*a_zoneTempDir != '\0');
/* entry debugging info */
echoDebug(DBG_CREATE_ZONE_ADMINFILE, a_zoneTempDir, PSTR(a_admnfile));
/* if temporary name already exists, do not overwrite */
if (*r_zoneAdminFile != (char *)NULL) {
return;
}
/* create temporary name */
*r_zoneAdminFile = tempnam(a_zoneTempDir, "zadmn");
b = z_create_zone_admin_file(*r_zoneAdminFile, a_admnfile);
if (b == B_FALSE) {
progerr(ERR_CREATE_TMPADMIN, *r_zoneAdminFile,
strerror(errno));
quit(1);
/* NOTREACHED */
}
echoDebug(DBG_CREATED_ZONE_ADMINFILE, *r_zoneAdminFile);
}
/*
* Name: create_zone_tempdir
* Description: Given a system temporary directory, create a "zone" specific
* temporary directory and return the path to the directory
* created.
* Arguments: r_zoneTempDir - pointer to handle that will contain a
* string representing the path to the temporary
* directory created - this must be NULL before the
* first call to this function - on subsequent calls
* if the pointer is NOT null then the existing string
* will NOT be overwritten.
* a_zoneTempDir - pointer to string representing the path
* to the system temporary directory to create the
* temporary zone directory in
* Returns: void
* NOTE: Any string returned is placed in new storage for the
* calling method. The caller must use 'free' to dispose
* of the storage once the string is no longer needed.
* NOTE: On any error this function will call 'quit(1)'
* NOTE: This function calls "quitSetZoneTmpdir" on success to
* register the directory created with quit() so that the
* directory will be automatically deleted on exit.
*/
static void
create_zone_tempdir(char **r_zoneTempDir, char *a_tmpdir)
{
boolean_t b;
/* entry assertions */
assert(r_zoneTempDir != (char **)NULL);
assert(a_tmpdir != (char *)NULL);
assert(*a_tmpdir != '\0');
/* entry debugging info */
echoDebug(DBG_CREATE_ZONE_TEMPDIR, a_tmpdir);
/* if temporary directory already exists, do not overwrite */
if (*r_zoneTempDir != (char *)NULL) {
return;
}
/* create temporary directory */
b = setup_temporary_directory(r_zoneTempDir, a_tmpdir, "ztemp");
if (b == B_FALSE) {
progerr(ERR_ZONETEMPDIR, a_tmpdir, strerror(errno));
quit(1);
/* NOTREACHED */
}
/* register with quit() so directory is removed on exit */
quitSetZoneTmpdir(*r_zoneTempDir);
/* exit debugging info */
echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
}
/*
* Name: continue_installation
* Description: Called from within a loop that is installing packages,
* this function examines various global variables and decides
* whether or not to ask an appropriate question, and wait for
* and appropriate reply.
* Arguments: <<global variables>>
* Returns: B_TRUE - continue processing with next package
* B_FALSE - do not continue processing with next package
*/
static boolean_t
continue_installation(void)
{
char ans[MAX_INPUT];
int n;
/* return TRUE if not interrupted */
if (!interrupted) {
return (B_TRUE);
}
/*
* process interrupted - determine whether or not to continue
*/
/* output appropriate interrupted message */
if (askflag) {
echo(npkgs == 1 ? MSG_1MORE_PROC : MSG_MORE_PROC, npkgs);
} else {
echo(npkgs == 1 ? MSG_1MORE_INST : MSG_MORE_INST, npkgs);
}
/* if running with no interaction (-n) do not ask question */
if (nointeract) {
/* if admin required return 'dont continue' */
if (needconsult) {
return (B_FALSE);
}
ckquit = 1;
return (B_TRUE);
}
/* interaction possible: ask question */
ckquit = 0;
n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_ADD);
if (n != 0) {
quit(n);
/* NOTREACHED */
}
ckquit = 1;
if (strchr("yY", *ans) == NULL) {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* package can be in a number of formats:
* - file containing package stream (pkgadd -d file [pkgs])
* - directory containing packages (pkgadd -d /dir [pkgs])
* - device containing packages (pkgadd -d diskette1 [pkgs])
* non-global zones can be passed open files and strings as arguments
* - for file containing package stream
* -- the stream can be passed directly to the non-global zone
* - for directory
* -- convert packages to datastream to pass to the non-global zone
* - for device
* -- ?
*/
static boolean_t
unpack_and_check_packages(char **a_pkgList, char *a_idsName, char *a_packageDir)
{
int savenpkgs = npkgs;
int i;
CAF_T flags = 0;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
/* entry debugging info */
echoDebug(DBG_UNPACKCHECK_ENTRY);
echoDebug(DBG_UNPACKCHECK_ARGS, PSTR(a_idsName), PSTR(a_packageDir));
/*
* set flags for applicability check
*/
/* determine if running in the global zone */
if (z_running_in_global_zone() == B_TRUE) {
flags |= CAF_IN_GLOBAL_ZONE;
}
/* set -G flag */
if (globalZoneOnly == B_TRUE) {
flags |= CAF_SCOPE_GLOBAL;
}
/*
* for each package to install:
* - if packages from datastream, unpack package into package dir
* - check applicability of installing package on this system/zone
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
if (a_idsName != (char *)NULL) {
/* create stream out of package if not already one */
if (unpack_package_from_stream(a_idsName, pkginst,
a_packageDir) == B_FALSE) {
progerr(ERR_CANNOT_UNPACK_PKGSTRM,
PSTR(pkginst), PSTR(a_idsName),
PSTR(a_packageDir));
npkgs = savenpkgs;
return (B_FALSE);
}
} else {
echoDebug(DBG_PKG_IN_DIR, pkginst, a_packageDir);
}
/* check package applicability */
if (check_applicability(a_packageDir,
pkginst, get_inst_root(), flags) == B_FALSE) {
progerr(ERR_PKG_NOT_INSTALLABLE, pkginst);
npkgs = savenpkgs;
return (B_FALSE);
}
npkgs--;
}
npkgs = savenpkgs;
return (B_TRUE);
}
/*
* returns:
* B_TRUE - package list generated
* B_FALSE - failed to generate package list
* Will call quit(n) on fatal error.
*/
static boolean_t
get_package_list(char ***r_pkgList, char **a_argv, char *a_categories,
char **a_categoryList, int a_ignoreSignatures, PKG_ERR *a_err,
ushort_t a_httpProxyPort, char *a_httpProxyName,
keystore_handle_t a_keystore, char *a_keystoreFile,
char *a_idsName, int *r_repeat)
{
int n;
url_hport_t *proxytmp = NULL;
/* entry assertions */
assert(r_repeat != (int *)NULL);
/* entry debugging info */
echoDebug(DBG_GETPKGLIST_ENTRY);
echoDebug(DBG_GETPKGLIST_ARGS, PSTR(a_idsName), PSTR(pkgdev.dirname),
*r_repeat);
/*
* get the list of the packages to add
*/
n = pkgGetPackageList(r_pkgList, a_argv, optind, a_categories,
a_categoryList, &pkgdev);
switch (n) {
case -1: /* no packages found */
echoDebug(DBG_PKGLIST_NONFOUND, PSTR(a_idsName),
pkgdev.dirname);
return (B_FALSE);
case 0: /* packages found */
break;
default: /* "quit" error */
echoDebug(DBG_PKGLIST_ERROR, PSTR(a_idsName),
pkgdev.dirname, n);
quit(n);
/* NOTREACHED */
}
/*
* If we are not ignoring signatures, check the package's
* signature if one exists. pkgask doesn't care about
* signatures though.
*/
if (!askflag && !a_ignoreSignatures && a_idsName &&
(web_ck_authentication() == AUTH_QUIT)) {
PKCS7 *sig = NULL;
STACK_OF(X509) *cas = NULL;
/* Retrieve signature */
if (!get_signature(a_err, a_idsName, &pkgdev, &sig)) {
pkgerr(a_err);
web_cleanup();
quit(1);
/* NOTREACHED */
}
if (sig != NULL) {
/* Found signature. Verify. */
if (a_httpProxyName != NULL) {
/* Proxy will be needed for OCSP */
proxytmp = malloc(sizeof (url_hport_t));
if (url_parse_hostport(a_httpProxyName,
proxytmp, a_httpProxyPort)
!= URL_PARSE_SUCCESS) {
progerr(ERR_PROXY,
a_httpProxyName);
PKCS7_free(sig);
quit(99);
/* NOTREACHED */
}
}
/* Start with fresh error stack */
pkgerr_clear(a_err);
if (a_keystore == NULL) {
/* keystore not opened - open it */
if (open_keystore(a_err, a_keystoreFile,
get_prog_name(), pkg_passphrase_cb,
KEYSTORE_DFLT_FLAGS,
&a_keystore) != 0) {
pkgerr(a_err);
web_cleanup();
PKCS7_free(sig);
quit(1);
/* NOTREACHED */
}
}
/* get trusted CA certs */
if (find_ca_certs(a_err, a_keystore, &cas) != 0) {
pkgerr(a_err);
PKCS7_free(sig);
web_cleanup();
quit(1);
/* NOTREACHED */
}
/* Verify signature */
if (!ds_validate_signature(a_err, &pkgdev,
&a_argv[optind], a_idsName, sig,
cas, proxytmp, nointeract)) {
pkgerr(a_err);
quit(99);
/* NOTREACHED */
}
/* cleanup */
PKCS7_free(sig);
web_cleanup();
pkgerr_free(a_err);
}
}
/* order package list if input data stream specified */
if (a_idsName) {
ds_order(*r_pkgList);
}
return (B_TRUE);
}
/*
* Name: install_in_one_zone
* Description: Install a single package in a single zone
* Arguments: a_zoneName - pointer to string representing the name of the
* zone to install the package into.
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be installed.
* If this is == NULL the package is assumed to be
* spooled in the zone temporary directory.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkginstall when installing the package.
* If this is == NULL no admin file is given to pkginstall.
* a_zoneTempDir - pointer to string representing the temporary
* directory in which spooled packages can be found if
* a_idsName is == NULL.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_scratchName - pointer to string representing the name of the
* scratch zone to use for installation.
* a_zoneState - state of the zone; must be mounted or running.
* a_tmpzn - B_TRUE when this zone is booted by the package
* command or B_FALSE if it was running before.
* Returns: void
* NOTE: As a side effect, "ckreturn" is called on the result returned
* from running 'pkginstall' in the zone; this sets several global
* variables which allows the caller to determine the result of
* the installation operation.
*/
static void
install_in_one_zone(char *a_zoneName, char *a_idsName,
char *a_zoneAdminFile, char *a_zoneTempDir,
char *a_altBinDir, zone_state_t a_zoneState, boolean_t a_tmpzn)
{
char zoneStreamName[PATH_MAX] = {'\0'};
int n;
/* entry assertions */
assert(a_zoneName != (char *)NULL);
assert(*a_zoneName != '\0');
/* entry debugging info */
echoDebug(DBG_INSTINONEZONE_ENTRY);
echoDebug(DBG_INSTINONEZONE_ARGS, a_zoneName, PSTR(a_idsName),
PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir),
PSTR(a_altBinDir));
/* echo operation to perform to stdout */
echo(MSG_INSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
/* determine path to the package stream */
if (a_idsName == (char *)NULL) {
/* locate temp stream created earlier */
(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
"%s/%s.dstream", a_zoneTempDir, pkginst);
} else {
/* use stream passed in on command line */
(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
"%s", a_idsName);
}
echoDebug(DBG_INSTALL_IN_ZONE, pkginst, a_zoneName, zoneStreamName);
n = pkgZoneInstall(a_zoneName, a_zoneState, zoneStreamName,
a_altBinDir, a_zoneAdminFile, a_tmpzn);
/* set success/fail condition variables */
ckreturn(n);
/* exit debugging info */
echoDebug(DBG_INSTALL_FLAG_VALUES, "after install", admnflag, doreboot,
failflag, interrupted, intrflag, ireboot, needconsult,
nullflag, warnflag);
}
/*
* Name: install_in_zones
* Description: Install a single package in the zones that are running from
* a list of zones
* Arguments: a_zlst - list of zones to install the package into
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be installed.
* If this is == NULL the package is assumed to be
* spooled in the zone temporary directory.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkginstall when installing the package.
* If this is == NULL no admin file is given to pkginstall.
* a_zoneTempDir - pointer to string representing the temporary
* directory in which spooled packages can be found if
* a_idsName is == NULL.
*/
static int
install_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
char *a_zoneAdminFile, char *a_zoneTempDir)
{
char *zoneName;
int zoneIndex;
int zonesSkipped = 0;
zone_state_t zst;
/* entry assertions */
assert(a_zlst != (zoneList_t)NULL);
/* entry debugging info */
echoDebug(DBG_INSTALLINZONES_ENTRY);
echoDebug(DBG_INSTALLINZONES_ARGS, PSTR(a_idsName),
PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
/* process each zone in the list */
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
zoneIndex++) {
/* skip the zone if it is NOT running */
zst = z_zlist_get_current_state(a_zlst, zoneIndex);
if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
zonesSkipped++;
echoDebug(DBG_SKIPPING_ZONE, zoneName);
continue;
}
/* install the package in this zone */
install_in_one_zone(z_zlist_get_scratch(a_zlst, zoneIndex),
a_idsName, a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
zst, B_FALSE);
}
return (zonesSkipped);
}
/*
* Name: boot_and_install_in_zones
* Description: Install a single package in the zones that are NOT running from
* a list of zones - each zone is booted, the package installed,
* and the zone is halted
* Arguments: a_zlst - list of zones to install the package into
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be installed.
* If this is == NULL the package is assumed to be
* spooled in the zone temporary directory.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkginstall when installing the package.
* If this is == NULL no admin file is given to pkginstall.
* a_zoneTempDir - pointer to string representing the temporary
* directory in which spooled packages can be found if
* a_idsName is == NULL.
*/
static int
boot_and_install_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
char *a_zoneAdminFile, char *a_zoneTempDir)
{
boolean_t b;
char *zoneName;
int zoneIndex;
int zonesSkipped = 0;
zone_state_t zst;
/* entry assertions */
assert(a_zlst != (zoneList_t)NULL);
/* entry debugging info */
echoDebug(DBG_BOOTINSTALLINZONES_ENTRY);
echoDebug(DBG_BOOTINSTALLINZONES_ARGS, PSTR(a_idsName),
PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
/* process each zone in the list */
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
zoneIndex++) {
/* skip the zone if it IS running */
zst = z_zlist_get_current_state(a_zlst, zoneIndex);
if (zst == ZONE_STATE_RUNNING || zst == ZONE_STATE_MOUNTED) {
echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
continue;
}
/* skip the zone if it is NOT bootable */
if (z_zlist_is_zone_runnable(a_zlst, zoneIndex) == B_FALSE) {
echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
continue;
}
/* mount up the zone */
echo(MSG_BOOTING_ZONE, zoneName);
echoDebug(DBG_BOOTING_ZONE, zoneName);
b = z_zlist_change_zone_state(a_zlst, zoneIndex,
ZONE_STATE_MOUNTED);
if (b == B_FALSE) {
progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
/* set fatal error return condition */
ckreturn(1);
zonesSkipped++;
continue;
}
/* install the package in this zone */
install_in_one_zone(z_zlist_get_scratch(a_zlst, zoneIndex),
a_idsName, a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
ZONE_STATE_MOUNTED, B_TRUE);
/* restore original state of zone */
echo(MSG_RESTORE_ZONE_STATE, zoneName);
echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
}
return (zonesSkipped);
}
/*
* Name: pkginstall_check_in_one_zone
* Description: Do a pre install check of a single package in a single zone
* Arguments: a_zoneName - pointer to string representing the name of the
* zone to check install the package in.
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be check installed.
* If this is == NULL the package is assumed to be
* spooled in the zone temporary directory.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkginstall when installing the package.
* If this is == NULL no admin file is given to pkginstall.
* a_zoneTempDir - pointer to string representing the temporary
* directory in which spooled packages can be found if
* a_idsName is == NULL.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_scratchName - pointer to string representing the name of the
* scratch zone to use for installation.
* a_zoneState - state of the zone; must be mounted or running.
* a_tmpzn - B_TRUE when this zone is booted by the package
* command or B_FALSE if it was running before.
* Returns: void
* NOTE: As a side effect, "ckreturn" is called on the result returned
* from running 'pkginstall' in the zone; this sets several global
* variables which allows the caller to determine the result of
* the pre installation check operation.
*/
static void
pkginstall_check_in_one_zone(char *a_zoneName,
char *a_idsName, char *a_zoneAdminFile, char *a_zoneTempDir,
char *a_altBinDir, char *a_scratchName, zone_state_t a_zoneState,
boolean_t a_tmpzn)
{
char preinstallcheckPath[PATH_MAX+1];
char zoneStreamName[PATH_MAX] = {'\0'};
int n;
echo(MSG_CHECKINSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
echoDebug(MSG_CHECKINSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
(void) snprintf(preinstallcheckPath, sizeof (preinstallcheckPath),
"%s/%s.%s.preinstallcheck.txt", a_zoneTempDir, pkginst,
a_zoneName);
if (a_idsName == (char *)NULL) {
/* locate temporary stream created earlier */
(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
"%s/%s.dstream", a_zoneTempDir, pkginst);
} else {
(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
"%s", a_idsName);
}
echoDebug(DBG_CHECKINSTALL_IN_ZONE, pkginst, a_zoneName,
zoneStreamName);
n = pkgZoneCheckInstall(a_scratchName, a_zoneState, zoneStreamName,
a_altBinDir, a_zoneAdminFile, preinstallcheckPath, a_tmpzn);
/* set success/fail condition variables */
ckreturn(n);
echoDebug(DBG_INSTALL_FLAG_VALUES, "after preinstall check",
admnflag, doreboot, failflag, interrupted, intrflag,
ireboot, needconsult, nullflag, warnflag);
}
/*
* Name: pkginstall_check_in_zones
* Description: Check installation of a single package in the zones that
* are running from a list of zones
* Arguments: a_zlst - list of zones to check install the package
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be check installed.
* If this is == NULL the package is assumed to be
* spooled in the zone temporary directory.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkginstall when checking the installing
* of the package.
* If this is == NULL no admin file is given to pkginstall.
* a_zoneTempDir - pointer to string representing the temporary
* directory in which spooled packages can be found if
* a_idsName is == NULL.
*/
static int
pkginstall_check_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
char *a_zoneAdminFile, char *a_zoneTempDir)
{
char *zoneName;
int zoneIndex;
int zonesSkipped = 0;
zone_state_t zst;
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
zoneIndex++) {
zst = z_zlist_get_current_state(a_zlst, zoneIndex);
if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
zonesSkipped++;
echoDebug(DBG_SKIPPING_ZONE, zoneName);
continue;
}
pkginstall_check_in_one_zone(zoneName, a_idsName,
a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
z_zlist_get_scratch(a_zlst, zoneIndex), zst, B_FALSE);
}
return (zonesSkipped);
}
/*
* Name: boot_and_pkginstall_check_in_zones
* Description: Check installation of a single package in the zones that
* are NOT running from a list of zones - each zone is booted,
* the package installation is checked, and the zone is halted.
* Arguments: a_zlst - list of zones to install the package into
* a_idsName - pointer to string representing the data stream
* device (input data stream) containing the package to
* be check installed.
* If this is == NULL the package is assumed to be
* spooled in the zone temporary directory.
* a_altBinDir - pointer to string representing an alternative
* binary location directory to pass to pkginstall.
* If this is == NULL no alternative binary location is
* passed to pkginstall.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkginstall when check installing the
* package.
* If this is == NULL no admin file is given to pkginstall.
* a_zoneTempDir - pointer to string representing the temporary
* directory in which spooled packages can be found if
* a_idsName is == NULL.
*/
static int
boot_and_pkginstall_check_in_zones(zoneList_t a_zlst, char *a_idsName,
char *a_altBinDir, char *a_zoneAdminFile, char *a_zoneTempDir)
{
int zoneIndex;
int zonesSkipped = 0;
char *zoneName;
boolean_t b;
zone_state_t zst;
/* entry assertions */
assert(a_zlst != (zoneList_t)NULL);
/* entry debugging info */
echoDebug(DBG_BOOTCHECKINSTALLINZONES_ENTRY);
echoDebug(DBG_BOOTCHECKINSTALLINZONES_ARGS, PSTR(a_idsName),
PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
/* process each zone in the list */
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
zoneIndex++) {
/* skip the zone if it IS running */
zst = z_zlist_get_current_state(a_zlst, zoneIndex);
if (zst == ZONE_STATE_RUNNING || zst == ZONE_STATE_MOUNTED) {
echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
continue;
}
/* skip the zone if it is NOT bootable */
if (z_zlist_is_zone_runnable(a_zlst, zoneIndex) == B_FALSE) {
echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
continue;
}
/* mount up the zone */
echo(MSG_BOOTING_ZONE, zoneName);
echoDebug(DBG_BOOTING_ZONE, zoneName);
b = z_zlist_change_zone_state(a_zlst, zoneIndex,
ZONE_STATE_MOUNTED);
if (b == B_FALSE) {
progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
/* set fatal error return condition */
ckreturn(1);
zonesSkipped++;
continue;
}
/* pre-installation check of the package in this zone */
pkginstall_check_in_one_zone(zoneName, a_idsName,
a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
z_zlist_get_scratch(a_zlst, zoneIndex),
ZONE_STATE_MOUNTED, B_TRUE);
/* restore original state of zone */
echo(MSG_RESTORE_ZONE_STATE, zoneName);
echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
}
return (zonesSkipped);
}
/*
* Function: add_packages_in_global_with_zones
* Description: call this function to add a list of packages in the global zone
* when one or more non-global zones exist
* returns:
* B_TRUE to process next data stream
* B_FALSE to exit
*/
static boolean_t
add_packages_in_global_with_zones(char **a_pkgList, char *a_uri,
char *a_idsName, int a_repeat, char *a_altBinDir,
char *a_device, zoneList_t a_zlst)
{
static char *zoneTempDir = (char *)NULL;
static char *zoneAdminFile = (char *)NULL;
boolean_t b;
char *packageDir;
char instdir[PATH_MAX];
char respfile_path[PATH_MAX];
char zoneStreamName[PATH_MAX] = {'\0'};
int i;
int n;
int savenpkgs = npkgs;
int zonesSkipped;
boolean_t globalPresent;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
assert(a_zlst != (zoneList_t)NULL);
echoDebug(DBG_ADDPACKAGES_GZ_W_LZ_ENTRY);
echoDebug(DBG_ADDPACKAGES_GZ_W_LZ_ARGS, npkgs, PSTR(a_uri),
PSTR(a_idsName), a_repeat, PSTR(a_device));
/* create temporary directory for use by zone operations */
create_zone_tempdir(&zoneTempDir, tmpdir);
/* create hands off settings admin file for use in a non-global zone */
create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
/* determine directory where packages can be found */
if (a_idsName == (char *)NULL) {
/* no stream - directory containing packages provided */
packageDir = pkgdev.dirname;
} else {
packageDir = zoneTempDir;
}
/* unpack and check all packages */
b = unpack_and_check_packages(a_pkgList, a_idsName, packageDir);
if (b != B_TRUE) {
quit(1);
}
/*
* if the packages are contained in a directory, convert the
* packages into individual streams because pkgZoneInstall is only able
* to pass a stream to the non-global zone's pkginstall command.
* After this code is executed:
* if the original input was a datastream:
* -> that datastream has been unpacked into "instdir"
* if the original input was a directory with packages in it:
* -> those packages have been placed into a single datastream
*/
if (a_idsName == (char *)NULL) {
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
char *pkgs[2];
/* package is not a stream - create one */
(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
"%s/%s.dstream", zoneTempDir, pkginst);
echoDebug(DBG_CONVERTING_PKG, packageDir, pkginst,
zoneStreamName);
/* set up list of packages to be this package only */
pkgs[0] = pkginst;
pkgs[1] = (char *)NULL;
n = pkgtrans(packageDir, zoneStreamName, pkgs,
PT_SILENT|PT_ODTSTREAM, NULL, NULL);
if (n != 0) {
progerr(ERR_CANNOT_CONVERT_PKGSTRM,
pkginst, packageDir, zoneStreamName);
quit(1);
}
npkgs--;
}
npkgs = savenpkgs;
}
/*
* Phase I - run collect dependency information for all packages for all
* zones - this involves running pkginstall with the "preinstallcheck"
* option which causes all dependency checks to be performed without
* actually doing the installation of the packages. This information is
* gathered in the zone temporary directory and is used later to present
* the dependency check results to the system administrator depending
* on the administration settings.
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
/* reset interrupted flag before calling pkginstall */
interrupted = 0; /* last action was NOT quit */
/*
* if this package is marked "install in this zone only", then
* do not check dependencies in any other zone
*/
if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
echoDebug(DBG_VERIFY_SKIP_THISZONE, pkginst);
npkgs--;
continue;
}
/*
* if operation failed in global zone do not propagate
* to any non-global zones
*/
if (interrupted != 0) {
echo(MSG_CHECKINSTALL_INTERRUPT_B4_Z, pkginst);
echoDebug(MSG_CHECKINSTALL_INTERRUPT_B4_Z, pkginst);
break;
}
echoDebug(DBG_INSTALL_FLAG_VALUES, "after pkginstall",
admnflag, doreboot, failflag, interrupted, intrflag,
ireboot, needconsult, nullflag, warnflag);
/*
* call pkginstall to verify this package for all non-global
* zones that are currently booted
*/
zonesSkipped = pkginstall_check_in_zones(a_zlst, a_idsName,
a_altBinDir, admnfile, zoneTempDir);
/*
* if any zones were skipped (becuase they are not currently
* booted), boot each zone one at a time and call pkginstall
* to verify this package for each such non-global zone
*/
if (zonesSkipped > 0) {
echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
zonesSkipped =
boot_and_pkginstall_check_in_zones(a_zlst,
a_idsName, a_altBinDir, admnfile,
zoneTempDir);
if (zonesSkipped > 0) {
progerr(ERR_INSTALL_ZONES_SKIPPED,
zonesSkipped);
}
}
npkgs--;
}
/*
* At this point, all of the dependency information has been gathered
* and is ready to be analyzed. This function processes all of that
* dependency information and presents the results to the system
* administrator, depending on the current administration settings.
*/
i = preinstall_verify(a_pkgList, a_zlst, zoneTempDir);
if (i != 0) {
/* dependency checks failed - exit */
quit(i);
}
npkgs = savenpkgs;
/*
* reset all error return condition variables that may have been
* set during package installation dependency checking so that they
* do not reflect on the success/failure of the actual package
* installation operations
*/
resetreturn();
/*
* At this point, all of the dependency checking is completed, and
* the installation of the packages can proceed. Install each package
* one at a time, starting with the global zone, and the for each
* non-global zone that is booted, and then for each non-global zone
* that is not currently booted.
*/
globalPresent = z_on_zone_spec(GLOBAL_ZONENAME);
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
/*
* if immediate reboot required from last package and this is
* not 'pkgask' then suspend installation of remaining packages
*/
if ((ireboot != 0) && (askflag == 0)) {
ptext(stderr, MSG_SUSPEND_ADD, pkginst);
continue;
}
/*
* handle interrupt if the previous pkginstall was interrupted
*/
if (continue_installation() == B_FALSE) {
return (B_FALSE);
}
/*
* if pkgask, handle response file creation:
* - if the response file is a directory, then create a path to
* -- a package instance within the response file directory.
* - If the response file is NOT a directory, if more than one
* -- package is to be installed.
*/
if ((askflag != 0) && (respdir != (char *)NULL)) {
(void) snprintf(respfile_path, sizeof (respfile_path),
"%s/%s", respdir, pkginst);
respfile = respfile_path;
}
echo(MSG_PROC_INST, pkginst,
(a_uri && a_idsName) ? a_uri : a_device);
/*
* If we're installing another package in the same
* session, the second through nth pkginstall, must
* continue from where the prior one left off. For this
* reason, the continuation feature (implied by the
* nature of the command) is used for the remaining
* packages.
*/
if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
pkgcontsrc = pkgdrtarg;
}
if (globalPresent) {
/*
* call pkginstall for this package for the global zone
*/
echo(MSG_INSTALLING_PKG_IN_GZ, pkginst);
/* reset interrupted flag before calling pkginstall */
interrupted = 0; /* last action was NOT quit */
n = pkgInstall(get_inst_root(), NULL, packageDir,
a_altBinDir);
/* set success/fail condition variables */
ckreturn(n);
/*
* if operation failed in global zone do not propagate
* to any non-global zones
*/
if (interrupted != 0) {
echo(MSG_INSTALL_INTERRUPT_B4_ZONES, pkginst);
echoDebug(MSG_INSTALL_INTERRUPT_B4_ZONES,
pkginst);
break;
}
}
/*
* if this package is marked "install in this zone only",
* then only need to install the package in the global zone;
* skip installation in any non-global zones.
*/
if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
echoDebug(DBG_INSTALL_SKIP_THISZONE, pkginst);
npkgs--;
continue;
}
echoDebug(DBG_INSTALL_FLAG_VALUES, "install in running zones",
admnflag, doreboot, failflag, interrupted, intrflag,
ireboot, needconsult, nullflag, warnflag);
/* install package in currently booted zones */
zonesSkipped = install_in_zones(a_zlst, a_idsName, a_altBinDir,
zoneAdminFile, zoneTempDir);
/* install package in zones that are not currently booted */
if (zonesSkipped > 0) {
echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
zonesSkipped = boot_and_install_in_zones(a_zlst,
a_idsName, a_altBinDir, zoneAdminFile,
zoneTempDir);
if (zonesSkipped > 0) {
progerr(ERR_INSTALL_ZONES_SKIPPED,
zonesSkipped);
}
}
/*
* package completely installed - remove any temporary stream
* of the package that might have been created
*/
if (a_idsName == (char *)NULL) {
/* locate temporary stream created earlier */
(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
"%s/%s.dstream", zoneTempDir, pkginst);
/* remove stream - no longer needed */
echoDebug(DBG_REMOVING_DSTREAM_PKGDIR, zoneStreamName,
pkginst);
(void) remove(zoneStreamName);
} else {
/* remove package - no longer needed */
if (snprintf(instdir, sizeof (instdir), "%s/%s",
zoneTempDir, pkginst) >= PATH_MAX) {
progerr(ERR_CANNOT_CREATE_PKGPATH, tmpdir);
quit(1);
}
echoDebug(DBG_REMOVING_PKG_TMPDIR, instdir, pkginst);
(void) remove(instdir);
}
/* decrement number of packages left to install */
npkgs--;
/*
* if no packages left to install, unmount package source
* device if appropriate
*/
if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
(void) chdir("/");
if (!a_idsName) {
echoDebug(DBG_UNMOUNTING_DEV,
PSTR(pkgdev.mount));
(void) pkgumount(&pkgdev);
}
}
}
/*
* all packages in the package list have been installed.
* Continue with installation if:
* -- immediate reboot is NOT required
* -- there are more packages to install
* -- the package source is a path to a file
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0) &&
(pkgdev.pathname == (char *)NULL)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* Function: add_packages_in_nonglobal_zone
* Description: call this function to add a list of packages in a non-global
* zone
* returns:
* B_TRUE to process next data stream
* B_FALSE to exit
*/
static boolean_t
add_packages_in_nonglobal_zone(char **a_pkgList, char *a_uri,
char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device)
{
static char *zoneTempDir = (char *)NULL;
char *packageDir;
char respfile_path[PATH_MAX];
int i;
int n;
boolean_t b;
int savenpkgs = npkgs;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
/* entry debugging info */
echoDebug(DBG_ADDPACKAGES_LZ_ENTRY);
echoDebug(DBG_ADDPACKAGES_LZ_ARGS, npkgs, PSTR(a_uri), PSTR(a_idsName),
a_repeat, PSTR(a_device));
/* create temporary directory for use by zone operations */
create_zone_tempdir(&zoneTempDir, tmpdir);
/*
* package can be in a number of formats:
* - file containing package stream (pkgadd -d file [pkgs])
* - directory containing packages (pkgadd -d /dir [pkgs])
* - device containing packages (pkgadd -d diskette1 [pkgs])
* non-global zones can be passed open file drescriptors and
* strings as arguments
* - for file containing package stream
* -- the stream can be passed directly to the non-global zone
* - for directory
* -- convert packages to datastream to pass to the non-global zone
* - for device
*/
/* determine directory where packages can be found */
if (a_idsName == (char *)NULL) {
/* no stream - directory containing packages provided */
packageDir = pkgdev.dirname;
} else {
packageDir = zoneTempDir;
}
b = unpack_and_check_packages(a_pkgList, a_idsName, packageDir);
if (b != B_TRUE) {
quit(1);
}
/*
* this is the main loop where all of the packages (as listed in the
* package list) are added one at a time.
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
npkgs--;
}
npkgs = savenpkgs;
/*
* this is the main loop where all of the packages (as listed in the
* package list) are added one at a time.
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
/*
* if immediate reboot required from last package and this is
* not 'pkgask' then suspend installation of remaining packages
*/
if ((ireboot != 0) && (askflag == 0)) {
ptext(stderr, MSG_SUSPEND_ADD, pkginst);
continue;
}
/*
* handle interrupt if the previous pkginstall was interrupted
*/
if (continue_installation() == B_FALSE) {
return (B_FALSE);
}
/*
* if pkgask, handle response file creation:
* - if the response file is a directory, then create a path to
* -- a package instance within the response file directory.
* - If the response file is NOT a directory, if more than one
* -- package is to be installed.
*/
if ((askflag != 0) && (respdir != (char *)NULL)) {
(void) snprintf(respfile_path, sizeof (respfile_path),
"%s/%s", respdir, pkginst);
respfile = respfile_path;
}
echo(MSG_PROC_INST, pkginst,
(a_uri && a_idsName) ? a_uri : a_device);
/*
* If we're installing another package in the same
* session, the second through nth pkginstall, must
* continue from where the prior one left off. For this
* reason, the continuation feature (implied by the
* nature of the command) is used for the remaining
* packages.
*/
if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
pkgcontsrc = pkgdrtarg;
}
/* reset interrupted flag before calling pkginstall */
interrupted = 0; /* last action was NOT quit */
/* call pkginstall for this package */
n = pkgInstall(get_inst_root(), NULL,
packageDir, a_altBinDir);
/* set success/fail condition variables */
ckreturn(n);
/* decrement number of packages left to install */
npkgs--;
/*
* if no packages left to install, unmount package source
* device if appropriate
*/
if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
(void) chdir("/");
if (!a_idsName) {
(void) pkgumount(&pkgdev);
}
}
}
/*
* all packages in the package list have been installed.
* Continue with installation if:
* -- immediate reboot is NOT required
* -- there are more packages to install
* -- the package source is a path to a file
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0) &&
(pkgdev.pathname == (char *)NULL)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* Function: add_packages_in_global_no_zones
* Description: call this function to add a list of packages in the global zone
* when no non-global zones exist
* returns:
* B_TRUE to process next data stream
* B_FALSE to exit
*/
static boolean_t
add_packages_in_global_no_zones(char **a_pkgList, char *a_uri,
char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device)
{
int n;
int i;
char respfile_path[PATH_MAX];
CAF_T flags = 0;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
echoDebug(DBG_ADDPACKAGES_GZ_NO_LZ_ENTRY);
echoDebug(DBG_ADDPACKAGES_GZ_NO_LZ_ARGS, npkgs, PSTR(a_uri),
PSTR(a_idsName), a_repeat, PSTR(a_device));
/*
* set flags for applicability check
*/
/* in the global zone */
flags |= CAF_IN_GLOBAL_ZONE;
/* set -G flag */
if (globalZoneOnly == B_TRUE) {
flags |= CAF_SCOPE_GLOBAL;
}
/*
* this is the main loop where all of the packages (as listed in the
* package list) are added one at a time.
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
/*
* if immediate reboot required from last package and this is
* not 'pkgask' then suspend installation of remaining packages
*/
if ((ireboot != 0) && (askflag == 0)) {
ptext(stderr, MSG_SUSPEND_ADD, pkginst);
continue;
}
/*
* handle interrupt if the previous pkginstall was interrupted
*/
if (continue_installation() == B_FALSE) {
return (B_FALSE);
}
/*
* check package applicability to install in this context
*/
if (check_applicability(pkgdev.dirname,
pkginst, get_inst_root(), flags) == B_FALSE) {
progerr(ERR_PKG_NOT_APPLICABLE, pkginst);
quit(1);
}
/*
* if pkgask, handle response file creation:
* - if the response file is a directory, then create a path to
* -- a package instance within the response file directory.
* - If the response file is NOT a directory, if more than one
* -- package is to be installed.
*/
if ((askflag != 0) && (respdir != (char *)NULL)) {
(void) snprintf(respfile_path, sizeof (respfile_path),
"%s/%s", respdir, pkginst);
respfile = respfile_path;
}
echo(MSG_PROC_INST, pkginst,
(a_uri && a_idsName) ? a_uri : a_device);
/*
* If we're installing another package in the same
* session, the second through nth pkginstall, must
* continue from where the prior one left off. For this
* reason, the continuation feature (implied by the
* nature of the command) is used for the remaining
* packages.
*/
if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
pkgcontsrc = pkgdrtarg;
}
/* reset interrupted flag before calling pkginstall */
interrupted = 0; /* last action was NOT quit */
/* call pkginstall for this package */
n = pkgInstall(get_inst_root(), a_idsName,
pkgdev.dirname, a_altBinDir);
/* set success/fail condition variables */
ckreturn(n);
/* decrement number of packages left to install */
npkgs--;
/*
* if no packages left to install, unmount package source
* device if appropriate
*/
if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
(void) chdir("/");
if (!a_idsName) {
(void) pkgumount(&pkgdev);
}
}
}
/*
* all packages in the package list have been installed.
* Continue with installation if:
* -- immediate reboot is NOT required
* -- there are more packages to install
* -- the package source is a path to a file
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0) &&
(pkgdev.pathname == (char *)NULL)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* returns:
* B_TRUE to process next data stream
* B_FALSE to exit
*/
static boolean_t
add_packages(char **a_pkgList, char *a_uri,
char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device,
boolean_t a_noZones)
{
zoneList_t zlst;
boolean_t b;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
echoDebug(DBG_ADDPACKAGES_ENTRY);
echoDebug(DBG_ADDPACKAGES_ARGS, npkgs, PSTR(a_uri), PSTR(a_idsName),
a_repeat, PSTR(a_altBinDir), PSTR(a_device));
/*
* if running in the global zone AND one or more non-global
* zones exist, add packages in a 'zones aware' manner, else
* add packages in the standard 'non-zones aware' manner.
*/
if ((a_noZones == B_FALSE) && (z_running_in_global_zone() == B_FALSE)) {
/* in non-global zone */
echoDebug(DBG_IN_LZ);
b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
if (b != B_TRUE) {
progerr(ERR_CANNOT_LOCK_THIS_ZONE);
/* set fatal error return condition */
ckreturn(1);
return (B_FALSE);
}
b = add_packages_in_nonglobal_zone(a_pkgList, a_uri, a_idsName,
a_repeat, a_altBinDir, a_device);
(void) z_unlock_this_zone(ZLOCKS_ALL);
return (B_FALSE);
}
/* running in the global zone */
b = z_non_global_zones_exist();
if ((a_noZones == B_FALSE) && (b == B_TRUE) &&
(globalZoneOnly == B_FALSE)) {
echoDebug(DBG_IN_GZ_WITH_LZ);
/* error if -V specified - what to use in non-global zone? */
if (vfstab_file) {
progerr(ERR_V_USED_WITH_GZS);
quit(1);
}
/* get a list of all non-global zones */
zlst = z_get_nonglobal_zone_list();
if (zlst == (zoneList_t)NULL) {
progerr(ERR_CANNOT_GET_ZONE_LIST);
quit(1);
}
/* need to lock all of the zones */
quitSetZonelist(zlst);
b = z_lock_zones(zlst, ZLOCKS_PKG_ADMIN);
if (b == B_FALSE) {
z_free_zone_list(zlst);
progerr(ERR_CANNOT_LOCK_ZONES);
/* set fatal error return condition */
ckreturn(1);
return (B_FALSE);
}
/* add packages to all zones */
b = add_packages_in_global_with_zones(a_pkgList, a_uri,
a_idsName, a_repeat, a_altBinDir, a_device, zlst);
/* unlock all zones */
(void) z_unlock_zones(zlst, ZLOCKS_ALL);
quitSetZonelist((zoneList_t)NULL);
/* free list of all non-global zones */
z_free_zone_list(zlst);
return (B_FALSE);
}
/* in global zone no non-global zones */
echoDebug(DBG_IN_GZ_NO_LZ);
b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
if (b != B_TRUE) {
progerr(ERR_CANNOT_LOCK_THIS_ZONE);
/* set fatal error return condition */
ckreturn(1);
return (B_FALSE);
}
b = add_packages_in_global_no_zones(a_pkgList, a_uri, a_idsName,
a_repeat, a_altBinDir, a_device);
(void) z_unlock_this_zone(ZLOCKS_ALL);
return (B_FALSE);
}