main.c revision 6e1ae2a33c618c4c2b14aec7d2f21743ddea5837
/*
* 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 */
/*
* System includes
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <locale.h>
#include <libintl.h>
#include <pkgstrct.h>
#include <pkgdev.h>
#include <pkginfo.h>
#include <pkglocs.h>
#include <pkglib.h>
#include <assert.h>
/*
* libinstzones includes
*/
#include <instzones_api.h>
/*
* consolidation pkg command library includes
*/
#include <pkglib.h>
/*
* local pkg command library includes
*/
#include "install.h"
#include "libinst.h"
#include "libadm.h"
#include "messages.h"
/*
* pkgrm local includes
*/
#include "quit.h"
/*
* 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 presvr4.c */
int started = 0;
char *tmpdir = NULL; /* location to place temporary files */
/* imported by various (many) */
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 *pkginst = NULL; /* current pkg/src instance 2 process */
static char *vfstab_file = NULL;
static char *zoneTempDir = (char *)NULL;
/* set by ckreturn() */
static int interrupted = 0; /* last pkg op was quit (1,2,3,4,5) */
static int nointeract = 0; /* non-zero - no user interaction */
static int pkgrmremote = 0; /* remove pkg objs stored remotely */
static int pkgverbose = 0; /* non-zero if verbose mode selected */
/*
* Assume the package complies with the standards as regards user
* interaction during procedure scripts.
*/
static int old_pkg = 0;
static int old_symlinks = 0;
static int no_map_client = 0;
/* 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;
/*
* imported (external) functions
*/
/* presvr4.c */
extern int presvr4(char *pkg, int a_nointeract);
/* check.c */
extern int preremove_verify(char **a_pkgList, zoneList_t a_zlst,
char *a_zoneTempDir);
/* quit.c */
extern void quitSetZonelist(zoneList_t a_zlst);
/*
* imported (external) variables
*/
extern char *pkgdir;
/* printable string - if string is null results in ??? */
#define PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
#define MAX_FDS 20
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
/*
* forward declarations
*/
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 int doRemove(int a_nodelete, char *a_altBinDir,
int a_longestPkg, char *a_adminFile,
char *a_zoneAdminFile, zoneList_t zlst);
static int pkgRemove(int a_nodelete, char *a_altBinDir,
char *a_adminFile);
static int pkgZoneCheckRemove(char *a_zoneName, char *a_altBinDir,
char *a_adminFile, char *a_stdoutPath,
zone_state_t a_zoneState, boolean_t tmpzone);
static int pkgZoneRemove(char *a_zoneName, int a_nodelete,
char *a_altBinDir, char *a_adminFile,
zone_state_t a_zoneState, boolean_t tmpzone);
static void resetreturn();
static void usage(void);
static boolean_t check_applicability(char *a_packageDir,
char *a_pkgInst, char *a_rootPath,
CAF_T a_flags);
static boolean_t check_packages(char **a_pkgList, char *a_packageDir);
static boolean_t path_valid(char *path);
static boolean_t remove_packages(char **a_pkgList, int a_nodelete,
int a_longestPkg, int a_repeat,
char *a_altBinDir, char *a_pkgdir,
char *a_spoolDir, boolean_t a_noZones);
static boolean_t remove_packages_from_spool_directory(char **a_pkgList,
int a_nodelete, int a_longestPkg, int a_repeat,
char *a_altBinDir);
static boolean_t remove_packages_in_global_no_zones(char **a_pkgList,
int a_nodelete, int a_longestPkg, int a_repeat,
char *a_altBinDir);
static boolean_t remove_packages_in_global_with_zones(char **a_pkgList,
int a_nodelete, int a_longestPkg, int a_repeat,
char *a_altBinDir, char *a_pkgdir,
zoneList_t a_zlst);
static boolean_t remove_packages_in_nonglobal_zone(char **a_pkgList,
int a_nodelete, int a_longestPkg, int a_repeat,
char *a_altBinDir, char *a_pkgdir);
static boolean_t shall_we_continue(char *a_pkgInst, int a_npkgs);
/*
* *****************************************************************************
* global external (public) functions
* *****************************************************************************
*/
/*
* Name: main
* Description: main entry point for pkgrm
* Returns: int
* 0 Successful completion
* 1 Fatal error.
* 2 Warning.
* 3 Interruption.
* 4 Administration.
* 5 Administration. Interaction is required. Do not use pkgrm -n.
* 10 Reboot after removal of all packages.
* 20 Reboot after removal of this package.
*/
int
main(int argc, char **argv)
{
char **category = NULL;
char *altBinDir = (char *)NULL;
char *catg_arg = NULL;
char *p;
char *prog_full_name = NULL;
char *spoolDir = 0;
int c;
int longestPkg = 0;
int n;
int nodelete = 0; /* dont rm files/run scripts */
int pkgLgth = 0;
int repeat;
struct sigaction nact;
struct sigaction oact;
/* initialize locale environment */
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
/* initialize program name */
prog_full_name = argv[0];
(void) set_prog_name(argv[0]);
/* tell spmi zones interface how to access package output functions */
z_set_output_functions(echo, echoDebug, progerr);
/* tell quit which ckreturn function to call */
quitSetCkreturnFunc(&ckreturn);
/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
progerr(ERR_ROOT_SET);
exit(1);
}
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:FMnO:R:s:V:vY:Z")) != EOF) {
switch (c) {
/*
* Public interface: Allow admin to remove objects
* from a service area via a reference client.
* Remove the package files from the client's file
* system, absolutely. If a file is shared with other
* packages, the default behavior is to not remove
* the file from the client's file system.
*/
case 'A':
pkgrmremote++;
break;
/*
* Public interface: Use the installation
* administration file, admin, in place of the
* default admin file. pkgrm first looks in the
* current working directory for the administration
* file. If the specified administration file is not
* in the current working directory, pkgrm looks in
* the /var/sadm/install/admin directory for the
* administra- tion file.
*/
case 'a':
admnfile = flex_device(optarg, 0);
break;
/*
* Not a public interface: location where package executables
* can be found - default is /usr/sadm/install/bin.
*/
case 'b':
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: pass -F option to
* pkgremove which suppresses the removal of any
* files and any class action scripts, and suppresses
* the running of any class action scripts. The
* package files remain but the package looks like it
* is not installed. This is mainly for use by the
* upgrade process.
*/
case 'F':
nodelete++;
break;
/*
* Public interface: Instruct pkgrm 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;
/*
* Public interface: package removal occurs in
* non-interactive mode. Suppress output of the list of
* removed files. The default mode is interactive.
*/
case 'n':
nointeract++;
(void) echoSetFlag(B_FALSE);
break;
/*
* Not a public interface: the -O option allows the behavior
* of the package tools to be modified. Recognized options:
* -> debug
* ---> enable debugging output
* -> 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 or remove any target directories
* --> Do not perform any script locking
* --> Do not install or uninstall any components of any package
* --> Do not output any status or database update messages
* -> zonelist="<names...>"
* ---> add package to space-separated list of zones only
*/
case 'O':
for (p = strtok(optarg, ","); p != (char *)NULL;
p = strtok(NULL, ",")) {
if (strcmp(p, "nozones") == 0) {
noZones = B_TRUE;
continue;
}
if (strcmp(p,
"enable-hollow-package-support") == 0) {
set_depend_pkginfo_DB(B_TRUE);
continue;
}
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 (strncmp(p, "zonelist=", 9) == 0) {
if (z_set_zone_spec(p + 9) == -1)
quit(1);
usedZoneList = B_TRUE;
continue;
}
/* -O option not recognized - issue warning */
progerr(ERR_INVALID_O_OPTION, p);
continue;
}
break;
/*
* Public interface: defines 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.
*/
case 'R':
if (!set_inst_root(optarg)) {
progerr(ERR_ROOT_CMD);
exit(1);
}
break;
/*
* Public interface: remove the specified package(s)
* from the directory spool. The default directory
* for spooled packages is /var/sadm/pkg.
*/
case 's':
spoolDir = flex_device(optarg, 1);
break;
/*
* Public interface: Allow admin to establish the client
* filesystem using a 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 pkgrm, located in the
* pkginst/install directory. This option is used for
* debugging the procedural and non- procedural
* scripts.
*/
case 'v':
pkgverbose++;
break;
/*
* Public interface: remove packages based on the
* CATEGORY variable from the installed/spooled
* pkginfo file
*/
case 'Y':
catg_arg = strdup(optarg);
if ((category = get_categories(catg_arg)) == NULL) {
progerr(ERR_CAT_INV, catg_arg);
exit(1);
} else if (is_not_valid_category(category,
get_prog_name())) {
progerr(ERR_CAT_SYS);
exit(1);
} else if (is_not_valid_length(category)) {
progerr(ERR_CAT_LNGTH);
exit(1);
}
break;
/*
* unrecognized option
*/
default:
usage();
/* NOTREACHED */
}
}
/*
* ********************************************************************
* 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());
}
/* -s cannot be used with several */
if (spoolDir != (char *)NULL) {
if (admnfile != (char *)NULL) {
progerr(ERR_SPOOLDIR_AND_ADMNFILE);
usage();
/* NOTREACHED */
}
if (pkgrmremote != 0) {
progerr(ERR_SPOOLDIR_AND_PKGRMREMOTE);
usage();
/* NOTREACHED */
}
if (pkgverbose != 0) {
progerr(ERR_SPOOLDIR_AND_PKGVERBOSE);
usage();
/* NOTREACHED */
}
if (is_an_inst_root() != 0) {
progerr(ERR_SPOOLDIR_AND_INST_ROOT);
usage();
/* NOTREACHED */
}
}
/* -V cannot be used with -A */
if (no_map_client && pkgrmremote) {
progerr(ERR_V_USED_AND_PKGRMREMOTE);
usage();
/* NOTREACHED */
}
/* -n used without pkg names or category */
if (nointeract && (optind == argc) && (catg_arg == NULL)) {
progerr(ERR_BAD_N_PKGRM);
usage();
/* NOTREACHED */
}
/* Error if specified zone list isn't valid on target */
if (usedZoneList && z_verify_zone_spec() == -1)
usage();
/*
* 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);
/* establish temporary directory to use */
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL) {
tmpdir = P_tmpdir;
}
echoDebug(DBG_PKGRM_TMPDIR, tmpdir);
/* initialize path parameters */
set_PKGpaths(get_inst_root());
/*
* initialize installation admin parameters - if removing from a spool
* directory then the admin file is ignore.
*/
if (spoolDir == NULL) {
echoDebug(DBG_PKGRM_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 removed in non-global zones
* when removed directly in the global zone by the global zone admin.
*/
if (is_depend_pkginfo_DB()) {
echoDebug(DBG_PKGRM_HOLLOW_ENABLED);
} else if ((z_running_in_global_zone() == B_TRUE) &&
(z_non_global_zones_exist() == B_TRUE)) {
echoDebug(DBG_PKGRM_ENABLING_HOLLOW);
set_depend_pkginfo_DB(B_TRUE);
}
/*
* 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;
}
if (devtype((spoolDir ? spoolDir : get_PKGLOC()), &pkgdev) ||
pkgdev.dirname == NULL) {
progerr(ERR_BAD_DEVICE, spoolDir ? spoolDir : get_PKGLOC());
quit(1);
/* NOTREACHED */
}
pkgdir = pkgdev.dirname;
repeat = ((optind >= argc) && pkgdev.mount);
/*
* 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_PKGRM);
usage();
/* NOTREACHED */
}
/*
* ********************************************************************
* main package processing "loop"
* ********************************************************************
*/
for (;;) {
boolean_t b;
char **pkglist; /* points to array of pkgs */
/*
* mount the spool device if required
*/
if (pkgdev.mount) {
if (n = pkgmount(&pkgdev, NULL, 0, 0, 1)) {
quit(n);
/* NOTREACHED */
}
}
if (chdir(pkgdev.dirname)) {
progerr(ERR_CHDIR, pkgdev.dirname);
quit(1);
/* NOTREACHED */
}
/*
* spool device mounted/available - get the list of the
* packages to remove
*/
n = pkgGetPackageList(&pkglist, argv, optind,
catg_arg, category, &pkgdev);
switch (n) {
case -1: /* no packages found */
echoDebug(DBG_PKGLIST_RM_NONFOUND,
PSTR(pkgdev.dirname));
progerr(ERR_NOPKGS, pkgdev.dirname);
quit(1);
/* NOTREACHED */
case 0: /* packages found */
break;
default: /* "quit" error */
echoDebug(DBG_PKGLIST_RM_ERROR,
pkgdev.dirname, n);
quit(n);
/* NOTREACHED */
}
/*
* count the number of packages to remove
* 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 */) {
pkgLgth = strlen(pkglist[npkgs]);
if (pkgLgth > longestPkg) {
longestPkg = pkgLgth;
}
echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
npkgs++;
}
/* output number of packages to be removed */
echoDebug(DBG_NUM_PKGS_TO_REMOVE, npkgs, longestPkg);
/*
* package list generated - remove packages
*/
b = remove_packages(pkglist, nodelete, longestPkg, repeat,
altBinDir, pkgdev.dirname, spoolDir, noZones);
/*
* unmount the spool directory if necessary
*/
if (pkgdev.mount) {
(void) chdir("/");
if (pkgumount(&pkgdev)) {
progerr(ERR_PKGUNMOUNT, pkgdev.bdevice);
quit(99);
/* NOTREACHED */
}
}
/*
* continue with next sequence of packages if continue set
*/
if (b == B_TRUE) {
continue;
}
/*
* not continuing - quit with 0 exit code
*/
quit(0);
/* NOTREACHED */
#ifdef lint
return (0);
#endif /* lint */
}
}
/*
* *****************************************************************************
* static internal (private) functions
* *****************************************************************************
*/
/*
* Name: doRemove
* Description: Remove a package from the global zone, and optionally from one
* or more non-global zones.
* Arguments: a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* a_longestPkg - length of the longest package "name" (for
* output format alignment)
* a_adminFile - pointer to string representing the admin
* file to pass to pkgremove when removing a package from
* the global zone only. Typically the admin file used for
* the global zone is the admin file passed in by the user.
* If this is == NULL no admin file is given to pkgremove.
* a_zoneAdminFile - pointer to string representing the admin
* file to pass to pkgremove when removing the package
* from a non-global zone only. Typically the admin file
* used for non-global zones supresses all checks since
* the dependency checking is done for all zones first
* before proceeding.
* A zoneAdminFile MUST be specified if a_zlst != NULL.
* A zoneAdminFile must NOT be specified if a_zlst == NULL.
* a_zlst - list of zones to process; NULL if no zones to process.
* 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
doRemove(int a_nodelete, char *a_altBinDir, int a_longestPkg, char *a_adminFile,
char *a_zoneAdminFile, zoneList_t a_zlst)
{
boolean_t b;
char *zoneName;
char ans[MAX_INPUT];
int n;
int zoneIndex;
int zonesSkipped;
struct pkginfo *pinfo = (struct pkginfo *)NULL;
zone_state_t zst;
/* entry assertions */
if (a_zlst != (zoneList_t)NULL) {
/* zone list specified - zone admin file required */
assert(a_zoneAdminFile != (char *)NULL);
assert(*a_zoneAdminFile != '\0');
} else {
/* no zone list specified - no zone admin file needed */
assert(a_zoneAdminFile == (char *)NULL);
}
/* NOTE: required 'pkgdir' set to spool directory or NULL */
b = pkginfoIsPkgInstalled(&pinfo, pkginst);
if (b == B_FALSE) {
progerr(ERR_NO_SUCH_INSTANCE, pkginst);
pkginfoFree(&pinfo);
return (2);
}
/* entry debugging info */
echoDebug(DBG_DOREMOVE_ENTRY);
echoDebug(DBG_DOREMOVE_ARGS, PSTR(pinfo->pkginst), PSTR(pinfo->name),
PSTR(pinfo->arch), PSTR(pinfo->version), PSTR(pinfo->basedir),
PSTR(pinfo->catg), pinfo->status);
if (!nointeract) {
char fmt1[100];
/* create format based on max pkg name length */
(void) snprintf(fmt1, sizeof (fmt1), " %%-%d.%ds %%s",
a_longestPkg, a_longestPkg);
if (pinfo->status == PI_SPOOLED) {
echo(INFO_SPOOLED);
} else {
if (getuid()) {
progerr(ERR_NOT_ROOT, get_prog_name());
exit(1);
}
echo(INFO_INSTALL);
}
echo(fmt1, pinfo->pkginst, pinfo->name);
if (pinfo->arch || pinfo->version) {
char fmt2[100];
/* create format based on max pkg name length */
(void) snprintf(fmt2, sizeof (fmt2), " %%%d.%ds ",
a_longestPkg, a_longestPkg);
/* LINTED variable format specifier to fprintf() */
(void) fprintf(stderr, fmt2, "");
if (pinfo->arch) {
(void) fprintf(stderr, "(%s) ", pinfo->arch);
}
if (pinfo->version) {
(void) fprintf(stderr, "%s", pinfo->version);
}
(void) fprintf(stderr, "\n");
}
n = ckyorn(ans, NULL, NULL, NULL, ASK_CONFIRM);
if (n != 0) {
quit(n);
/* NOTREACHED */
}
if (strchr("yY", *ans) == NULL) {
pkginfoFree(&pinfo);
return (0);
}
}
if (pinfo->status == PI_PRESVR4) {
pkginfoFree(&pinfo);
return (presvr4(pkginst, nointeract));
}
if (pinfo->status == PI_SPOOLED) {
/* removal from a directory */
echo(INFO_RMSPOOL, pkginst);
pkginfoFree(&pinfo);
return (rrmdir(pkginst));
}
/* exit if not root */
if (getuid()) {
progerr(ERR_NOT_ROOT, get_prog_name());
exit(1);
}
pkginfoFree(&pinfo);
zonesSkipped = 0;
if (interrupted != 0) {
echo(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
echoDebug(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
return (n);
}
echoDebug(DBG_REMOVE_FLAG_VALUES, "before pkgZoneRemove",
admnflag, doreboot, failflag, interrupted,
intrflag, ireboot, nullflag, warnflag);
for (zoneIndex = 0;
a_zlst != NULL &&
(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;
}
echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
echoDebug(DBG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
/*
* remove package from zone; use the zone admin file which
* suppresses all checks.
*/
n = pkgZoneRemove(z_zlist_get_scratch(a_zlst, zoneIndex),
a_nodelete, a_altBinDir, a_zoneAdminFile,
zst, B_FALSE);
/* set success/fail condition variables */
ckreturn(n);
echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
admnflag, doreboot, failflag, interrupted, intrflag,
ireboot, nullflag, warnflag);
}
if (zonesSkipped > 0) {
echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
(char *)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) {
zonesSkipped++;
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);
continue;
}
echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
/*
* remove package from zone; use the zone admin file
* which suppresses all checks.
*/
n = pkgZoneRemove(z_zlist_get_scratch(a_zlst,
zoneIndex), a_nodelete, a_altBinDir,
a_zoneAdminFile, ZONE_STATE_MOUNTED, B_TRUE);
/* set success/fail condition variables */
ckreturn(n);
echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
admnflag, doreboot, failflag, interrupted,
intrflag, ireboot, nullflag, warnflag);
/* 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);
}
}
/*
* Process global zone if it was either the only possible
* target (no list of zones specified) or it appears in the list
*/
if (a_zlst == NULL || z_on_zone_spec(GLOBAL_ZONENAME)) {
/* reset interrupted flag before calling pkgremove */
interrupted = 0; /* last action was NOT quit */
/*
* call pkgremove for this package for the global zone;
* use the admin file passed in by the user via -a.
*/
n = pkgRemove(a_nodelete, a_altBinDir, a_adminFile);
/* set success/fail condition variables */
ckreturn(n);
}
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) */
}
/*
* function which checks the indicated return value
* and indicates disposition of installation
*/
static void
ckreturn(int retcode)
{
/*
* entry debugging info
*/
echoDebug(DBG_PKGRM_CKRETURN, retcode, PSTR(pkginst));
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++;
break;
case 2: /* non-fatal error (warning) */
case 12:
case 22:
warnflag++;
interrupted++;
break;
case 3: /* user selected quit; operation interrupted */
case 13:
case 23:
intrflag++;
interrupted++;
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++;
break;
default:
failflag++;
interrupted++;
return;
}
if (retcode >= 20) {
ireboot++;
} else if (retcode >= 10) {
doreboot++;
}
}
static int
pkgZoneCheckRemove(char *a_zoneName, char *a_altBinDir, char *a_adminFile,
char *a_stdoutPath, zone_state_t a_zoneState, boolean_t tmpzone)
{
char *arg[MAXARGS];
char *p;
char adminfd_path[PATH_MAX];
char 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_PKGZONECHECKREMOVE_ENTRY);
echoDebug(DBG_PKGZONECHECKREMOVE_ARGS, a_zoneName, PSTR(pkginst),
PSTR(pkgdev.dirname), PSTR(a_adminFile), PSTR(a_stdoutPath));
/* generate path to pkgremove */
(void) snprintf(path, sizeof (path), "%s/pkgremove",
a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
/* start at first file descriptor */
maxfds = 0;
/*
* generate argument list for call to pkgremove
*/
/* start at argument 0 */
nargs = 0;
/* first argument is path to executable */
arg[nargs++] = strdup(path);
/* second argument is always: pass -O debug to pkgremove: debug mode */
if (debugFlag == B_TRUE) {
arg[nargs++] = "-O";
arg[nargs++] = "debug";
}
/* pkgrm -b dir: pass -b to pkgremove */
if (a_altBinDir != (char *)NULL) {
arg[nargs++] = "-b";
arg[nargs++] = a_altBinDir;
}
/*
* NONABI_SCRIPTS defined: pass -o to pkgremove; 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 pkgremove; process
* symlinks consistent with old behavior
*/
if (old_symlinks) {
arg[nargs++] = "-y";
}
/* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
arg[nargs++] = "-M";
/* pkgrm -A: pass -A to pkgremove */
if (pkgrmremote) {
arg[nargs++] = "-A";
}
/* pkgrm -v: pass -v to pkgremove: never trace scripts */
/* pass "-O enable-hollow-package-support" */
if (is_depend_pkginfo_DB()) {
arg[nargs++] = "-O";
arg[nargs++] = "enable-hollow-package-support";
}
/* pass -n to pkgremove: always in noninteractive mode */
arg[nargs++] = "-n";
/* pkgrm -a admin: pass -a admin to pkgremove: 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++] = strdup(adminfd_path);
}
/*
* pkgadd -R root: pass -R /a to pkgremove in mounted zone
*/
if (a_zoneState == ZONE_STATE_MOUNTED) {
arg[nargs++] = "-R";
arg[nargs++] = "/a";
}
/* pkgrm -F: pass -F to pkgremove: always update DB only */
arg[nargs++] = "-F";
/* pass "-O preremovecheck" */
arg[nargs++] = "-O";
arg[nargs++] = "preremovecheck";
/* 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 arguments how to start the pkgserv */
arg[nargs++] = "-O";
arg[nargs++] = pkgmodeargument(tmpzone ? RUN_ONCE : pkgservergetmode());
/* pass -N to pkgremove: program name to report */
arg[nargs++] = "-N";
arg[nargs++] = get_prog_name();
/* add package instance name */
arg[nargs++] = pkginst;
/* terminate argument list */
arg[nargs++] = NULL;
/* execute pkgremove command */
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 pkgremove in zone execution */
return (n);
}
static int
pkgZoneRemove(char *a_zoneName, int a_nodelete, char *a_altBinDir,
char *a_adminFile, zone_state_t a_zoneState, boolean_t tmpzone)
{
char *arg[MAXARGS];
char *p;
char adminfd_path[PATH_MAX];
char 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_PKGZONEREMOVE_ENTRY);
echoDebug(DBG_PKGZONEREMOVE_ARGS, a_zoneName, PSTR(pkginst),
PSTR(pkgdev.dirname), a_nodelete, PSTR(a_adminFile));
/* generate path to pkgremove */
(void) snprintf(path, sizeof (path), "%s/pkgremove",
a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
/* start at first file descriptor */
maxfds = 0;
/*
* generate argument list for call to pkgremove
*/
/* start at argument 0 */
nargs = 0;
/* first argument is path to executable */
arg[nargs++] = strdup(path);
/* second argument is always: pass -O debug to pkgremove: debug mode */
if (debugFlag == B_TRUE) {
arg[nargs++] = "-O";
arg[nargs++] = "debug";
}
/* pkgrm -b dir: pass -b to pkgremove */
if (a_altBinDir != (char *)NULL) {
arg[nargs++] = "-b";
arg[nargs++] = a_altBinDir;
}
/*
* NONABI_SCRIPTS defined: pass -o to pkgremove; 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 pkgremove; process
* symlinks consistent with old behavior
*/
if (old_symlinks) {
arg[nargs++] = "-y";
}
/* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
arg[nargs++] = "-M";
/* pkgrm -A: pass -A to pkgremove */
if (pkgrmremote) {
arg[nargs++] = "-A";
}
/* pkgrm -v: pass -v to pkgremove: trace scripts */
if (pkgverbose) {
arg[nargs++] = "-v";
}
/* pass "-O enable-hollow-package-support" */
if (is_depend_pkginfo_DB()) {
arg[nargs++] = "-O";
arg[nargs++] = "enable-hollow-package-support";
}
/* pkgrm -n: pass -n to pkgremove: noninteractive mode */
if (nointeract) {
arg[nargs++] = "-n";
}
/* pkgrm -a admin: pass -a admin to pkgremove: 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 pkgremove in mounted zone
*/
if (a_zoneState == ZONE_STATE_MOUNTED) {
arg[nargs++] = "-R";
arg[nargs++] = "/a";
}
/* pkgrm -F: pass -F to pkgremove: update DB only */
if (a_nodelete) {
arg[nargs++] = "-F";
}
/* 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 arguments how to start the pkgserv */
arg[nargs++] = "-O";
arg[nargs++] = pkgmodeargument(tmpzone ? RUN_ONCE : pkgservergetmode());
/* pass -N to pkgremove: program name to report */
arg[nargs++] = "-N";
arg[nargs++] = get_prog_name();
/* add package instance name */
arg[nargs++] = pkginst;
/* terminate argument list */
arg[nargs++] = NULL;
/* execute pkgremove command */
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);
/*
* 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 (n);
}
/*
* Name: pkgRemove
* Description: Invoke pkgremove in the current zone to perform a remove
* of a single package from the current zone or standalone system
* Arguments: a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* a_adminFile - pointer to string representing the admin
* file to pass to pkgremove when removing the package.
* If this is == NULL no admin file is given to pkgremove.
* 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
pkgRemove(int a_nodelete, char *a_altBinDir, char *a_adminFile)
{
char *arg[MAXARGS];
char *p;
char path[PATH_MAX];
int n;
int nargs;
/* entry debugging info */
echoDebug(DBG_PKGREMOVE_ENTRY);
echoDebug(DBG_PKGREMOVE_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
a_nodelete, PSTR(a_adminFile));
(void) snprintf(path, sizeof (path), "%s/pkgremove",
a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
nargs = 0;
/* first argument is path to executable */
arg[nargs++] = strdup(path);
/* second argument is always: pass -O debug to pkgremove: debug mode */
if (debugFlag == B_TRUE) {
arg[nargs++] = "-O";
arg[nargs++] = "debug";
}
/* Add arguments how to start the pkgserv */
arg[nargs++] = "-O";
arg[nargs++] = pkgmodeargument(pkgservergetmode());
/* pkgrm -b dir: pass -b to pkgremove */
if (a_altBinDir != (char *)NULL) {
arg[nargs++] = "-b";
arg[nargs++] = a_altBinDir;
}
/*
* NONABI_SCRIPTS defined: pass -o to pkgremove; 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 pkgremove; process
* symlinks consistent with old behavior
*/
if (old_symlinks) {
arg[nargs++] = "-y";
}
/* pkgrm -M: pass -M to pkgrm: dont mount client file systems */
if (no_map_client) {
arg[nargs++] = "-M";
}
/* pkgrm -A: pass -A to pkgrm */
if (pkgrmremote) {
arg[nargs++] = "-A";
}
/* pkgrm -v: pass -v to pkgremove: trace scripts */
if (pkgverbose) {
arg[nargs++] = "-v";
}
/* pkgrm -n: pass -n to pkgremove: noninteractive mode */
if (nointeract) {
arg[nargs++] = "-n";
}
/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
if (a_adminFile) {
arg[nargs++] = "-a";
arg[nargs++] = strdup(a_adminFile);
}
/* pkgrm -V vfstab: pass -V vfstab to pkgremove: alternate vfstab */
if (vfstab_file) {
arg[nargs++] = "-V";
arg[nargs++] = vfstab_file;
}
/* pkgrm -R root: pass -R root to pkgremove: alternative root */
if (is_an_inst_root()) {
arg[nargs++] = "-R";
arg[nargs++] = get_inst_root();
}
/* pkgrm -F: pass -F to pkgremove: update DB only */
if (a_nodelete) {
arg[nargs++] = "-F";
}
/*
* 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 pkgremove: program name to report */
arg[nargs++] = "-N";
arg[nargs++] = get_prog_name();
/* add package instance name */
arg[nargs++] = pkginst;
/* terminate argument list */
arg[nargs++] = NULL;
/*
* run the appropriate pkgremove 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 pkgremove command */
n = pkgexecv(NULL, NULL, NULL, NULL, arg);
/* return results of pkgrm in this zone */
return (n);
}
static void
usage(void)
{
char *prog = get_prog_name();
(void) fprintf(stderr, ERR_USAGE_PKGRM, prog, prog);
exit(1);
}
/*
* Name: remove_packages_in_global_with_zones
* Description: Remove packages from the global zone and from non-global zones
* when run from the global zone and when non-global zones are
* present.
* Arguments: a_pkgList - pointer to array of strings, each string specifying
* the name of one package to be removed.
* a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_longestPkg - length of the longest package "name" (for
* output format alignment)
* a_repeat - are there more packages avialable in "optind"
* - B_TRUE - process packages from optind
* - B_FALSE - do not process packages from optind
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* a_pkgdir - pointer to string representing the directory
* where the packages to be removed are located.
* a_zlst - list of zones to process; NULL if no zones to process.
* 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 boolean_t
remove_packages_in_global_with_zones(char **a_pkgList, int a_nodelete,
int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir,
zoneList_t a_zlst)
{
static char *zoneAdminFile = (char *)NULL;
boolean_t b;
char *zoneName;
char *scratchName;
char preremovecheckPath[PATH_MAX+1];
int i;
int n;
int savenpkgs = npkgs;
int zoneIndex;
int zonesSkipped;
zone_state_t zst;
/* entry assertions */
assert(a_zlst != (zoneList_t)NULL);
assert(a_pkgList != (char **)NULL);
assert(a_longestPkg > 0);
assert(a_pkgdir != (char *)NULL);
assert(*a_pkgdir != '\0');
/* entry debugging info */
echoDebug(DBG_PKGREMPKGSGZWNGZ_ENTRY);
echoDebug(DBG_PKGREMPKGSGZWNGZ_ARGS, a_nodelete, a_longestPkg,
a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
/* check all packages */
if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
quit(1);
}
/* 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);
/*
* all of the packages (as listed in the package list) are
* removed one at a time from all non-global zones and then
* from the global zone.
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
/* reset interrupted flag before calling pkgremove */
interrupted = 0; /* last action was NOT quit */
/* skip package if it is "in the global zone only" */
if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
continue;
}
/*
* if operation failed in global zone do not propagate to
* non-global zones
*/
zonesSkipped = 0;
if (interrupted != 0) {
echo(MSG_DOREMOVE_INTERRUPTED, pkginst);
echoDebug(DBG_DOREMOVE_INTERRUPTED, pkginst);
break;
}
echoDebug(DBG_REMOVE_FLAG_VALUES, "before loop",
admnflag, doreboot, failflag, interrupted,
intrflag, ireboot, nullflag, warnflag);
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
(char *)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;
}
echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
zoneName);
scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
(void) snprintf(preremovecheckPath,
sizeof (preremovecheckPath),
"%s/%s.%s.preremovecheck.txt",
zoneTempDir, pkginst, scratchName);
/*
* dependency check this package this zone; use the
* user supplied admin file so that the appropriate
* level of dependency checking is (or is not) done.
*/
n = pkgZoneCheckRemove(scratchName, a_altBinDir,
admnfile, preremovecheckPath,
zst, B_FALSE);
/* set success/fail condition variables */
ckreturn(n);
echoDebug(DBG_REMOVE_FLAG_VALUES,
"after pkgzonecheckremove",
admnflag, doreboot, failflag, interrupted,
intrflag, ireboot, nullflag, warnflag);
}
if (zonesSkipped == 0) {
continue;
}
echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
for (zoneIndex = 0;
(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
(char *)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) {
zonesSkipped++;
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);
continue;
}
echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
zoneName);
scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
(void) snprintf(preremovecheckPath,
sizeof (preremovecheckPath),
"%s/%s.%s.preremovecheck.txt",
zoneTempDir, pkginst, scratchName);
/*
* dependency check this package this zone; use the
* user supplied admin file so that the appropriate
* level of dependency checking is (or is not) done.
*/
n = pkgZoneCheckRemove(scratchName, a_altBinDir,
admnfile, preremovecheckPath,
ZONE_STATE_MOUNTED, B_TRUE);
/* set success/fail condition variables */
ckreturn(n);
echoDebug(DBG_REMOVE_FLAG_VALUES,
"after pkgzonecheckremove",
admnflag, doreboot, failflag, interrupted,
intrflag, ireboot, nullflag, warnflag);
/* 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);
}
npkgs--;
}
/*
* look at all pre-remove check files
*/
i = preremove_verify(a_pkgList, a_zlst, zoneTempDir);
if (i != 0) {
quit(i);
}
npkgs = savenpkgs;
/*
* reset all error return condition variables that may have been
* set during package removal dependency checking so that they
* do not reflect on the success/failure of the actual package
* removal operations
*/
resetreturn();
/*
* all of the packages (as listed in the package list) are
* removed one at a time.
*/
interrupted = 0;
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
boolean_t in_gz_only;
started = 0;
if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
continue;
}
in_gz_only = pkgIsPkgInGzOnly(get_inst_root(), pkginst);
/* reset interrupted flag before calling pkgremove */
interrupted = 0;
/*
* pkgrm invoked from within the global zone and there are
* non-global zones configured:
* Remove the package from the global zone.
* If not removing the package from the global zone only,
* then remove the package from the list of zones specified.
*/
if (in_gz_only) {
/* global zone only */
n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
admnfile, (char *)NULL, (zoneList_t)NULL);
} else {
/* global zone and non-global zones */
n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
zoneAdminFile, zoneAdminFile, a_zlst);
}
/* set success/fail condition variables */
ckreturn(n);
npkgs--;
}
/*
* all packages in the package list have been removed.
* Continue with removal if:
* -- immediate reboot is NOT required
* -- there are more packages to remove
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* Name: remove_packages_in_nonglobal_zone
* Description: Remove packages in a non-global zone when run from a
* non-global zone.
* Arguments: a_pkgList - pointer to array of strings, each string specifying
* the name of one package to be removed.
* a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_longestPkg - length of the longest package "name" (for
* output format alignment)
* a_repeat - are there more packages avialable in "optind"
* - B_TRUE - process packages from optind
* - B_FALSE - do not process packages from optind
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* a_pkgdir - pointer to string representing the directory
* where the packages to be removed are located.
* 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 boolean_t
remove_packages_in_nonglobal_zone(char **a_pkgList, int a_nodelete,
int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir)
{
static char *zoneAdminFile = (char *)NULL;
int n;
int i;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
assert(a_longestPkg > 0);
assert(a_pkgdir != (char *)NULL);
assert(*a_pkgdir != '\0');
/* entry debugging info */
echoDebug(DBG_PKGREMPKGSNGZ_ENTRY);
echoDebug(DBG_PKGREMPKGSNGZ_ARGS, a_nodelete, a_longestPkg,
a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
/* check all package */
if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
quit(1);
}
/* 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);
/*
* all of the packages (as listed in the package list) are
* removed one at a time.
*/
interrupted = 0;
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
started = 0;
if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
continue;
}
interrupted = 0;
/*
* pkgrm invoked from within a non-global zone: remove
* the package from the current zone only - no non-global
* zones are possible.
*/
n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
admnfile, (char *)NULL, (zoneList_t)NULL);
/* set success/fail condition variables */
ckreturn(n);
npkgs--;
}
/*
* all packages in the package list have been removed.
* Continue with removal if:
* -- immediate reboot is NOT required
* -- there are more packages to remove
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* Name: remove_packages_in_global_no_zones
* Description: Remove packages from the global zone only when run in the
* global zone and no non-global zones are installed.
* Arguments: a_pkgList - pointer to array of strings, each string specifying
* the name of one package to be removed.
* a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_longestPkg - length of the longest package "name" (for
* output format alignment)
* a_repeat - are there more packages avialable in "optind"
* - B_TRUE - process packages from optind
* - B_FALSE - do not process packages from optind
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* 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 boolean_t
remove_packages_in_global_no_zones(char **a_pkgList, int a_nodelete,
int a_longestPkg, int a_repeat, char *a_altBinDir)
{
int n;
int i;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
assert(a_longestPkg > 0);
/* entry debugging info */
echoDebug(DBG_PKGREMPKGSGZNNGZ_ENTRY);
echoDebug(DBG_PKGREMPKGSGZNNGZ_ARGS, a_nodelete, a_longestPkg,
a_repeat, PSTR(a_altBinDir));
/*
* all of the packages (as listed in the package list) are
* removed one at a time.
*/
interrupted = 0;
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
started = 0;
if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
continue;
}
interrupted = 0;
/*
* pkgrm invoked from within the global zone and there are
* NO non-global zones configured:
* Remove the package from the global zone only.
*/
n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
admnfile, (char *)NULL, (zoneList_t)NULL);
/* set success/fail condition variables */
ckreturn(n);
npkgs--;
}
/*
* all packages in the package list have been removed.
* Continue with removal if:
* -- immediate reboot is NOT required
* -- there are more packages to remove
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* Name: remove_packages_from_spool_directory
* Description: Remove packages from a spool directory only.
* Arguments: a_pkgList - pointer to array of strings, each string specifying
* the name of one package to be removed.
* a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_longestPkg - length of the longest package "name" (for
* output format alignment)
* a_repeat - are there more packages avialable in "optind"
* - B_TRUE - process packages from optind
* - B_FALSE - do not process packages from optind
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* 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 boolean_t
remove_packages_from_spool_directory(char **a_pkgList, int a_nodelete,
int a_longestPkg, int a_repeat, char *a_altBinDir)
{
int n;
int i;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
assert(a_longestPkg > 0);
/*
* all of the packages (as listed in the package list) are
* removed one at a time.
*/
interrupted = 0;
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
started = 0;
if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
continue;
}
interrupted = 0;
/*
* pkgrm invoked from any type of zone BUT the target
* to be removed is a local spool directory: remove the
* packages from the spool directory only.
*/
n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
admnfile, (char *)NULL, (zoneList_t)NULL);
/* set success/fail condition variables */
ckreturn(n);
npkgs--;
}
/*
* all packages in the package list have been removed.
* Continue with removal if:
* -- immediate reboot is NOT required
* -- there are more packages to remove
* else return do NOT continue.
*/
if ((ireboot == 0) && (a_repeat != 0)) {
return (B_TRUE);
}
/* return 'dont continue' */
return (B_FALSE);
}
/*
* Name: remove_packages
* Description: Remove packages from the global zone, and optionally from one
* or more non-global zones, or from a specified spool directory.
* Arguments: a_pkgList - pointer to array of strings, each string specifying
* the name of one package to be removed.
* a_nodelete: should the files and scripts remain installed?
* - if != 0 pass -F flag to pkgremove - suppress
* the removal of any files and any class action scripts
* and suppress the running of any class action scripts.
* The package files remain but the package looks like it
* is not installed. This is mainly for use by upgrade.
* - if == 0 do not pass -F flag to pkgremove - all
* files and class action scripts are removed, and any
* appropriate class action scripts are run.
* a_longestPkg - length of the longest package "name" (for
* output format alignment)
* a_repeat - are there more packages avialable in "optind"
* - B_TRUE - process packages from optind
* - B_FALSE - do not process packages from optind
* a_altBinDir - pointer to string representing location of the
* pkgremove executable to run. If not NULL, then pass
* the path specified to the -b option to pkgremove.
* a_pkgdir - pointer to string representing the directory
* where the packages to be removed are located.
* a_spoolDir - pointer to string specifying spool directory
* to remove packages from. If != NULL then all zones
* processing is bypassed and the packages are removed
* from the specified spool directory only.
* a_noZones - if non-global zones are configured, should the
* packages be removed from the non-global zones?
* - B_TRUE - do NOT remove packages from non-global zones
* - B_FALSE - remove packages from non-global zones
* 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 boolean_t
remove_packages(char **a_pkgList, int a_nodelete, int a_longestPkg,
int a_repeat, char *a_altBinDir, char *a_pkgdir, char *a_spoolDir,
boolean_t a_noZones)
{
zoneList_t zlst;
boolean_t b;
/* entry assertions */
assert(a_pkgList != (char **)NULL);
echoDebug(DBG_REMOVEPKGS_ENTRY);
echoDebug(DBG_REMOVEPKGS_ARGS, npkgs, a_nodelete, a_longestPkg,
a_repeat, PSTR(a_pkgdir), PSTR(a_spoolDir));
/*
* if removing from spool directory, bypass all zones checks
*/
if (a_spoolDir != (char *)NULL) {
/* in non-global zone */
echoDebug(DBG_REMOVE_PKGS_FROM_SPOOL, a_spoolDir);
b = remove_packages_from_spool_directory(a_pkgList, a_nodelete,
a_longestPkg, a_repeat, a_altBinDir);
return (B_FALSE);
}
/* exit if not root */
if (getuid()) {
progerr(ERR_NOT_ROOT, get_prog_name());
exit(1);
}
/*
* 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 = remove_packages_in_nonglobal_zone(a_pkgList, a_nodelete,
a_longestPkg, a_repeat, a_altBinDir, a_pkgdir);
(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)) {
echoDebug(DBG_IN_GZ_WITH_LZ);
/* 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 = remove_packages_in_global_with_zones(a_pkgList, a_nodelete,
a_longestPkg, a_repeat, a_altBinDir, a_pkgdir, 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 = remove_packages_in_global_no_zones(a_pkgList, a_nodelete,
a_longestPkg, a_repeat, a_altBinDir);
(void) z_unlock_this_zone(ZLOCKS_ALL);
return (B_FALSE);
}
/*
* Name: path_valid
* Description: Checks a string for being a valid path
*
* Arguments: path - path to validate
*
* Returns : B_TRUE - success, B_FALSE otherwise.
* B_FALSE means path was null, too long (>PATH_MAX),
* or too short (<1)
*/
static boolean_t
path_valid(char *path)
{
if (path == NULL) {
return (B_FALSE);
} else if (strlen(path) > PATH_MAX) {
return (B_FALSE);
} else if (strlen(path) >= 1) {
return (B_TRUE);
} else {
/* path < 1 */
return (B_FALSE);
}
}
/*
*/
static boolean_t
check_packages(char **a_pkgList, char *a_packageDir)
{
int savenpkgs = npkgs;
int i;
CAF_T flags = 0;
/* set flags for applicability check */
if (z_running_in_global_zone() == B_TRUE) {
flags |= CAF_IN_GLOBAL_ZONE;
}
/*
* for each package to remove, verify that the package is installed
* and is removable.
*/
for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
/* check package applicability */
if (check_applicability(a_packageDir, pkginst, get_inst_root(),
flags) == B_FALSE) {
progerr(ERR_PKG_NOT_REMOVABLE, pkginst);
npkgs = savenpkgs;
return (B_FALSE);
}
npkgs--;
}
npkgs = savenpkgs;
return (B_TRUE);
}
/*
* - is this package removable from this zone?
* - does the scope of remove conflict with existing installation
*/
static boolean_t
check_applicability(char *a_packageDir, char *a_pkgInst,
char *a_rootPath, CAF_T a_flags)
{
FILE *pkginfoFP;
boolean_t all_zones; /* pkg is "all zones" only */
char pkginfoPath[PATH_MAX];
char pkgpath[PATH_MAX];
int len;
/* 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 = "";
}
/*
* determine if this package is currently installed
* if not installed return success - operation will fail
* when the removal is attempted
*/
if (pkginfoIsPkgInstalled((struct pkginfo **)NULL, a_pkgInst) !=
B_TRUE) {
return (B_TRUE);
}
/*
* calculate paths to various objects
*/
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);
}
/* if not installed then just return */
if (isdir(pkgpath) != 0) {
progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
return (B_TRUE);
}
len = snprintf(pkginfoPath, sizeof (pkginfoPath),
"%s/pkginfo", pkgpath);
if (len > sizeof (pkgpath)) {
progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
return (B_FALSE);
}
/*
* gather information from this packages pkginfo file
*/
pkginfoFP = fopen(pkginfoPath, "r");
if (pkginfoFP == (FILE *)NULL) {
progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
strerror(errno));
return (B_FALSE);
}
/* determine "ALLZONES" setting for this package */
all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
"true", B_FALSE);
/* close pkginfo file */
(void) fclose(pkginfoFP);
/* gather information from the global zone only file */
/*
* verify package applicability based on information gathered;
* the package IS currently installed....
*/
/* pkg ALLZONES=true & not running in global zone */
if ((all_zones == B_TRUE) && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
progerr(ERR_ALLZONES_AND_IN_LZ_PKGRM, a_pkgInst);
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Name: shall_we_continue
* 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
shall_we_continue(char *a_pkgInst, int a_npkgs)
{
char ans[MAX_INPUT];
int n;
/* return FALSE if immediate reboot required */
if (ireboot) {
ptext(stderr, MSG_SUSPEND_RM, a_pkgInst);
return (B_FALSE);
}
/* return TRUE if not interrupted */
if (!interrupted) {
return (B_TRUE);
}
/* output appropriate interrupt message */
echo(a_npkgs == 1 ? MSG_1MORETODO : MSG_MORETODO, a_npkgs);
/* if running with no interaction (-n) do not ask question */
if (nointeract) {
quit(0);
/* NOTREACHED */
}
/* interaction possible: ask question */
n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_RM);
if (n != 0) {
quit(n);
/* NOTREACHED */
}
if (strchr("yY", *ans) == NULL) {
quit(0);
/* NOTREACHED */
}
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() to directory is removed on exit */
quitSetZoneTmpdir(*r_zoneTempDir);
/* exit debugging info */
echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
}