xref: /titanic_52/usr/src/cmd/svr4pkg/pkgrm/main.c (revision aad74983c015b9377370a62fd6867b6074434d3e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 /*
32  * System includes
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <locale.h>
42 #include <libintl.h>
43 #include <pkgstrct.h>
44 #include <pkgdev.h>
45 #include <pkginfo.h>
46 #include <pkglocs.h>
47 #include <pkglib.h>
48 #include <assert.h>
49 
50 /*
51  * libinstzones includes
52  */
53 
54 #include <instzones_api.h>
55 
56 /*
57  * consolidation pkg command library includes
58  */
59 
60 #include <pkglib.h>
61 
62 /*
63  * local pkg command library includes
64  */
65 
66 #include "install.h"
67 #include "libinst.h"
68 #include "libadm.h"
69 #include "messages.h"
70 
71 /*
72  * pkgrm local includes
73  */
74 
75 #include "quit.h"
76 
77 /*
78  * exported global variables
79  */
80 
81 /* these globals are set by ckreturn and used by quit.c */
82 
83 int	admnflag = 0;	/* != 0 if any pkg op admin setting failure (4) */
84 int	doreboot = 0;	/* != 0 if reboot required after installation */
85 int	failflag = 0;	/* != 0 if fatal error has occurred (1) */
86 int	intrflag = 0;	/* != 0 if user selected quit (3) */
87 int	ireboot = 0;	/* != 0 if immediate reboot required */
88 int	nullflag = 0;	/* != 0 if admin interaction required (5) */
89 int	warnflag = 0;	/* != 0 if non-fatal error has occurred (2) */
90 
91 /* imported by quit.c */
92 int	npkgs = 0;	/* the number of packages yet to be installed */
93 
94 /* imported by presvr4.c */
95 int	started = 0;
96 char	*tmpdir = NULL;	/* location to place temporary files */
97 
98 /* imported by various (many) */
99 struct admin	adm;	/* holds info about installation admin */
100 struct pkgdev	pkgdev;	/* holds info about the installation device */
101 
102 /*
103  * internal global variables
104  */
105 
106 static char	*admnfile = NULL;	/* file to use for installation admin */
107 static char	*pkginst = NULL;	/* current pkg/src instance 2 process */
108 static char	*vfstab_file = NULL;
109 static char	*zoneTempDir = (char *)NULL;
110 
111 /* set by ckreturn() */
112 
113 static int	interrupted = 0;	/* last pkg op was quit (1,2,3,4,5) */
114 
115 static int	nointeract = 0;		/* non-zero - no user interaction */
116 static int	pkgrmremote = 0;	/* remove pkg objs stored remotely  */
117 static int	pkgverbose = 0;		/* non-zero if verbose mode selected */
118 
119 /*
120  * Assume the package complies with the standards as regards user
121  * interaction during procedure scripts.
122  */
123 
124 static int	old_pkg = 0;
125 static int	old_symlinks = 0;
126 static int	no_map_client = 0;
127 
128 /* Set by -O nozones: do not process any zones */
129 
130 static boolean_t	noZones = B_FALSE;
131 
132 /* Set by -O zonelist=<names...>: process only named zones */
133 
134 static boolean_t	usedZoneList = B_FALSE;
135 
136 /* Set by -O debug: debug output is enabled? */
137 
138 static boolean_t	debugFlag = B_FALSE;
139 
140 /*
141  * imported (external) functions
142  */
143 
144 /* presvr4.c */
145 
146 extern int	presvr4(char *pkg, int a_nointeract);
147 
148 /* check.c */
149 
150 extern int	preremove_verify(char **a_pkgList, zoneList_t a_zlst,
151 			char *a_zoneTempDir);
152 /* quit.c */
153 
154 extern void	quitSetZonelist(zoneList_t a_zlst);
155 
156 /*
157  * imported (external) variables
158  */
159 
160 extern char	*pkgdir;
161 
162 /* printable string - if string is null results in ??? */
163 
164 #define	PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
165 
166 #define	MAX_FDS	20
167 
168 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
169 #define	TEXT_DOMAIN "SYS_TEST"
170 #endif
171 
172 #define	INHERITFS	"inherited-filesystem="
173 #define	INHERITFS_LEN	((sizeof (INHERITFS))-1)
174 
175 /*
176  * forward declarations
177  */
178 
179 static void		ckreturn(int retcode);
180 static void		create_zone_adminfile(char **r_zoneAdminFile,
181 				char *a_zoneTempDir, char *a_admnfile);
182 static void		create_zone_tempdir(char **r_zoneTempDir,
183 				char *a_tmpdir);
184 static int		doRemove(int a_nodelete, char *a_altBinDir,
185 				int a_longestPkg, char *a_adminFile,
186 				char *a_zoneAdminFile, zoneList_t zlst);
187 static int		pkgRemove(int a_nodelete, char *a_altBinDir,
188 				char *a_adminFile, char **a_inheritedPkgDirs);
189 static int		pkgZoneCheckRemove(char *a_zoneName,
190 				char **a_inheritedPkgDirs, char *a_altBinDir,
191 				char *a_adminFile, char *a_stdoutPath,
192 				zone_state_t a_zoneState);
193 static int		pkgZoneRemove(char *a_zoneName,
194 				char **a_inheritedPkgDirs, int a_nodelete,
195 				char *a_altBinDir, char *a_adminFile,
196 				zone_state_t a_zoneState);
197 static void		resetreturn();
198 static void		usage(void);
199 static boolean_t	check_applicability(char *a_packageDir,
200 				char *a_pkgInst, char *a_rootPath,
201 				CAF_T a_flags);
202 static boolean_t	check_packages(char **a_pkgList, char *a_packageDir);
203 static boolean_t	path_valid(char *path);
204 static boolean_t	remove_packages(char **a_pkgList, int a_nodelete,
205 				int a_longestPkg, int a_repeat,
206 				char *a_altBinDir, char *a_pkgdir,
207 				char *a_spoolDir, boolean_t a_noZones);
208 static boolean_t	remove_packages_from_spool_directory(char **a_pkgList,
209 				int a_nodelete, int a_longestPkg, int a_repeat,
210 				char *a_altBinDir);
211 static boolean_t	remove_packages_in_global_no_zones(char **a_pkgList,
212 				int a_nodelete, int a_longestPkg, int a_repeat,
213 				char *a_altBinDir);
214 static boolean_t	remove_packages_in_global_with_zones(char **a_pkgList,
215 				int a_nodelete, int a_longestPkg, int a_repeat,
216 				char *a_altBinDir, char *a_pkgdir,
217 				zoneList_t a_zlst);
218 static boolean_t	remove_packages_in_nonglobal_zone(char **a_pkgList,
219 				int a_nodelete, int a_longestPkg, int a_repeat,
220 				char *a_altBinDir, char *a_pkgdir);
221 static boolean_t	shall_we_continue(char *a_pkgInst, int a_npkgs);
222 
223 /*
224  * *****************************************************************************
225  * global external (public) functions
226  * *****************************************************************************
227  */
228 
229 /*
230  * Name:	main
231  * Description:	main entry point for pkgrm
232  * Returns:	int
233  *   0        Successful completion
234  *   1        Fatal error.
235  *   2        Warning.
236  *   3        Interruption.
237  *   4        Administration.
238  *   5        Administration. Interaction is required. Do not use pkgrm -n.
239  *  10       Reboot after removal of all packages.
240  *  20       Reboot after removal of this package.
241  */
242 
243 int
244 main(int argc, char **argv)
245 {
246 	char			**category = NULL;
247 	char			*altBinDir = (char *)NULL;
248 	char			*catg_arg = NULL;
249 	char			*p;
250 	char			*prog_full_name = NULL;
251 	char			*spoolDir = 0;
252 	int			c;
253 	int			longestPkg = 0;
254 	int			n;
255 	int			nodelete = 0;	/* dont rm files/run scripts */
256 	int			pkgLgth = 0;
257 	int			repeat;
258 	struct sigaction	nact;
259 	struct sigaction	oact;
260 
261 	/* initialize locale environment */
262 
263 	(void) setlocale(LC_ALL, "");
264 	(void) textdomain(TEXT_DOMAIN);
265 
266 	/* initialize program name */
267 
268 	prog_full_name = argv[0];
269 	(void) set_prog_name(argv[0]);
270 
271 	/* tell spmi zones interface how to access package output functions */
272 
273 	z_set_output_functions(echo, echoDebug, progerr);
274 
275 	/* tell quit which ckreturn function to call */
276 
277 	quitSetCkreturnFunc(&ckreturn);
278 
279 	/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
280 
281 	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
282 		progerr(ERR_ROOT_SET);
283 		exit(1);
284 	}
285 
286 	if (z_running_in_global_zone() && !enable_local_fs()) {
287 		progerr(ERR_CANNOT_ENABLE_LOCAL_FS);
288 	}
289 
290 	/*
291 	 * ********************************************************************
292 	 * parse command line options
293 	 * ********************************************************************
294 	 */
295 
296 	while ((c = getopt(argc, argv, "?Aa:b:FMnO:R:s:V:vY:Z")) != EOF) {
297 		switch (c) {
298 		/*
299 		 * Public interface: Allow admin to remove objects
300 		 * from a service area via a reference client.
301 		 * Remove the package files from the client's file
302 		 * system, absolutely. If a file is shared with other
303 		 * packages, the default behavior is to not remove
304 		 * the file from the client's file system.
305 		 */
306 		case 'A':
307 		    pkgrmremote++;
308 		    break;
309 
310 		/*
311 		 * Public interface: Use the installation
312 		 * administration file, admin, in place of the
313 		 * default admin file. pkgrm first looks in the
314 		 * current working directory for the administration
315 		 * file.  If the specified administration file is not
316 		 * in the current working directory, pkgrm looks in
317 		 * the /var/sadm/install/admin directory for the
318 		 * administra- tion file.
319 		 */
320 		case 'a':
321 		    admnfile = flex_device(optarg, 0);
322 		    break;
323 
324 		/*
325 		 * Not a public interface:  location where package executables
326 		 * can be found - default is /usr/sadm/install/bin.
327 		 */
328 		case 'b':
329 			if (!path_valid(optarg)) {
330 				progerr(ERR_PATH, optarg);
331 				quit(1);
332 			}
333 			if (isdir(optarg) != 0) {
334 				p = strerror(errno);
335 				progerr(ERR_CANNOT_USE_DIR, optarg, p);
336 				quit(1);
337 			}
338 			altBinDir = optarg;
339 			break;
340 
341 		/*
342 		 * Not a public interface: pass -F option to
343 		 * pkgremove which suppresses the removal of any
344 		 * files and any class action scripts, and suppresses
345 		 * the running of any class action scripts.  The
346 		 * package files remain but the package looks like it
347 		 * is not installed. This is mainly for use by the
348 		 * upgrade process.
349 		 */
350 		case 'F':
351 		    nodelete++;
352 		    break;
353 
354 		/*
355 		 * Public interface: Instruct pkgrm not to use the
356 		 * $root_path/etc/vfstab file for determining the
357 		 * client's mount points. This option assumes the
358 		 * mount points are correct on the server and it
359 		 * behaves consistently with Solaris 2.5 and earlier
360 		 * releases.
361 		 */
362 		case 'M':
363 		    no_map_client = 1;
364 		    break;
365 
366 		/*
367 		 * Public interface: package removal occurs in
368 		 * non-interactive mode.  Suppress output of the list of
369 		 * removed files. The default mode is interactive.
370 		 */
371 		case 'n':
372 		    nointeract++;
373 		    (void) echoSetFlag(B_FALSE);
374 		    break;
375 
376 		/*
377 		 * Not a public interface: the -O option allows the behavior
378 		 * of the package tools to be modified. Recognized options:
379 		 * -> debug
380 		 * ---> enable debugging output
381 		 * -> nozones
382 		 * ---> act as though in global zone with no non-global zones
383 		 * -> inherited-filesystems
384 		 * ---> add specified file system to list of file systems
385 		 * ---> that are inherited from the global zone
386 		 * -> enable-hollow-package-support
387 		 * --> Enable hollow package support. When specified, for any
388 		 * --> package that has SUNW_PKG_HOLLOW=true:
389 		 * --> Do not calculate and verify package size against target
390 		 * --> Do not run any package procedure or class action scripts
391 		 * --> Do not create or remove any target directories
392 		 * --> Do not perform any script locking
393 		 * --> Do not install or uninstall any components of any package
394 		 * --> Do not output any status or database update messages
395 		 * -> zonelist="<names...>"
396 		 * ---> add package to space-separated list of zones only
397 		 */
398 
399 		case 'O':
400 			for (p = strtok(optarg, ","); p != (char *)NULL;
401 				p = strtok(NULL, ",")) {
402 
403 				if (strcmp(p, "nozones") == 0) {
404 					noZones = B_TRUE;
405 					continue;
406 				}
407 
408 				if (strncmp(p, INHERITFS, INHERITFS_LEN) == 0) {
409 					if (z_add_inherited_file_system(
410 						p+INHERITFS_LEN) == B_FALSE) {
411 						progerr(ERR_NOSUCH_INHERITED,
412 							p+INHERITFS_LEN);
413 						quit(1);
414 						/* NOTREACHED */
415 					}
416 					continue;
417 				}
418 
419 				if (strcmp(p,
420 					"enable-hollow-package-support") == 0) {
421 					set_depend_pkginfo_DB(B_TRUE);
422 					continue;
423 				}
424 
425 				if (strcmp(p, "debug") == 0) {
426 					/* set debug flag/enable debug output */
427 					debugFlag = B_TRUE;
428 					(void) echoDebugSetFlag(debugFlag);
429 
430 					/* debug info on arguments to pkgadd */
431 					for (n = 0; n < argc && argv[n]; n++) {
432 						echoDebug(DBG_ARG, n, argv[n]);
433 					}
434 
435 					continue;
436 				}
437 
438 				if (strncmp(p, "zonelist=", 9) == 0) {
439 					if (z_set_zone_spec(p + 9) == -1)
440 						quit(1);
441 					usedZoneList = B_TRUE;
442 					continue;
443 				}
444 
445 				/* -O option not recognized - issue warning */
446 
447 				progerr(ERR_INVALID_O_OPTION, p);
448 				continue;
449 			}
450 			break;
451 
452 		/*
453 		 * Public interface: defines the full path name of a
454 		 * directory to use as the root_path.  All files,
455 		 * including package system information files, are
456 		 * relocated to a directory tree starting in the
457 		 * specified root_path.
458 		 */
459 		case 'R':
460 		    if (!set_inst_root(optarg)) {
461 			    progerr(ERR_ROOT_CMD);
462 			    exit(1);
463 		    }
464 		    break;
465 
466 		/*
467 		 * Public interface: remove the specified package(s)
468 		 * from the directory spool.  The default directory
469 		 * for spooled packages is /var/sadm/pkg.
470 		 */
471 		case 's':
472 		    spoolDir = flex_device(optarg, 1);
473 		    break;
474 
475 		/*
476 		 * Public interface: Allow admin to establish the client
477 		 * filesystem using a vfstab-like file of stable format.
478 		 */
479 		case 'V':
480 		    vfstab_file = flex_device(optarg, 2);
481 		    no_map_client = 0;
482 		    break;
483 
484 		/*
485 		 * Public interface: trace all of the scripts that
486 		 * get executed by pkgrm, located in the
487 		 * pkginst/install directory. This option is used for
488 		 * debugging the procedural and non- procedural
489 		 * scripts.
490 		 */
491 		case 'v':
492 		    pkgverbose++;
493 		    break;
494 
495 		/*
496 		 * Public interface: remove packages based on the
497 		 * CATEGORY variable from the installed/spooled
498 		 * pkginfo file
499 		 */
500 		case 'Y':
501 		    catg_arg = strdup(optarg);
502 
503 		    if ((category = get_categories(catg_arg)) == NULL) {
504 			    progerr(ERR_CAT_INV, catg_arg);
505 			    exit(1);
506 		    } else if (is_not_valid_category(category,
507 				    get_prog_name())) {
508 			    progerr(ERR_CAT_SYS);
509 			    exit(1);
510 		    } else if (is_not_valid_length(category)) {
511 			    progerr(ERR_CAT_LNGTH);
512 			    exit(1);
513 		    }
514 
515 		    break;
516 
517 		/*
518 		 * unrecognized option
519 		 */
520 		default:
521 		    usage();
522 		    /* NOTREACHED */
523 		}
524 	}
525 
526 	/*
527 	 * ********************************************************************
528 	 * validate command line options
529 	 * ********************************************************************
530 	 */
531 
532 	/* set "debug echo" flag according to setting of "-O debug" option */
533 
534 	(void) echoDebugSetFlag(debugFlag);
535 
536 	/* output entry debugging information */
537 
538 	if (z_running_in_global_zone()) {
539 		echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
540 	} else {
541 		echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
542 			z_get_zonename());
543 	}
544 
545 	/* -s cannot be used with several */
546 
547 	if (spoolDir != (char *)NULL) {
548 		if (admnfile != (char *)NULL) {
549 			progerr(ERR_SPOOLDIR_AND_ADMNFILE);
550 			usage();
551 			/* NOTREACHED */
552 		}
553 
554 		if (pkgrmremote != 0) {
555 			progerr(ERR_SPOOLDIR_AND_PKGRMREMOTE);
556 			usage();
557 			/* NOTREACHED */
558 		}
559 
560 		if (pkgverbose != 0) {
561 			progerr(ERR_SPOOLDIR_AND_PKGVERBOSE);
562 			usage();
563 			/* NOTREACHED */
564 		}
565 
566 		if (is_an_inst_root() != 0) {
567 			progerr(ERR_SPOOLDIR_AND_INST_ROOT);
568 			usage();
569 			/* NOTREACHED */
570 		}
571 	}
572 
573 	/* -V cannot be used with -A */
574 
575 	if (no_map_client && pkgrmremote) {
576 		progerr(ERR_V_USED_AND_PKGRMREMOTE);
577 		usage();
578 		/* NOTREACHED */
579 	}
580 
581 	/* -n used without pkg names or category */
582 
583 	if (nointeract && (optind == argc) && (catg_arg == NULL)) {
584 		progerr(ERR_BAD_N_PKGRM);
585 		usage();
586 		/* NOTREACHED */
587 	}
588 
589 	/* Error if specified zone list isn't valid on target */
590 	if (usedZoneList && z_verify_zone_spec() == -1)
591 		usage();
592 
593 	/*
594 	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
595 	 */
596 
597 	/* hold SIGINT/SIGHUP interrupts */
598 
599 	(void) sighold(SIGHUP);
600 	(void) sighold(SIGINT);
601 
602 	/* connect quit.c:trap() to SIGINT */
603 
604 	nact.sa_handler = quitGetTrapHandler();
605 	nact.sa_flags = SA_RESTART;
606 	(void) sigemptyset(&nact.sa_mask);
607 
608 	(void) sigaction(SIGINT, &nact, &oact);
609 
610 	/* connect quit.c:trap() to SIGHUP */
611 
612 	nact.sa_handler = quitGetTrapHandler();
613 	nact.sa_flags = SA_RESTART;
614 	(void) sigemptyset(&nact.sa_mask);
615 
616 	(void) sigaction(SIGHUP, &nact, &oact);
617 
618 	/* release hold on signals */
619 
620 	(void) sigrelse(SIGHUP);
621 	(void) sigrelse(SIGINT);
622 
623 	/* establish temporary directory to use */
624 
625 	tmpdir = getenv("TMPDIR");
626 	if (tmpdir == NULL) {
627 		tmpdir = P_tmpdir;
628 	}
629 
630 	echoDebug(DBG_PKGRM_TMPDIR, tmpdir);
631 
632 	/* initialize path parameters */
633 
634 	set_PKGpaths(get_inst_root());
635 
636 	/*
637 	 * initialize installation admin parameters - if removing from a spool
638 	 * directory then the admin file is ignore.
639 	 */
640 
641 	if (spoolDir == NULL) {
642 		echoDebug(DBG_PKGRM_ADMINFILE, admnfile ? admnfile : "");
643 		setadminFile(admnfile);
644 	}
645 
646 	/*
647 	 * if running in the global zone, and non-global zones exist, then
648 	 * enable hollow package support so that any packages that are marked
649 	 * SUNW_PKG_HOLLOW=true will be correctly removed in non-global zones
650 	 * when removed directly in the global zone by the global zone admin.
651 	 */
652 
653 	if (is_depend_pkginfo_DB()) {
654 		echoDebug(DBG_PKGRM_HOLLOW_ENABLED);
655 	} else if ((z_running_in_global_zone() == B_TRUE) &&
656 		(z_non_global_zones_exist() == B_TRUE)) {
657 		echoDebug(DBG_PKGRM_ENABLING_HOLLOW);
658 		set_depend_pkginfo_DB(B_TRUE);
659 	}
660 
661 	/*
662 	 * See if user wants this to be handled as an old style pkg.
663 	 * NOTE : the ``exception_pkg()'' stuff is to be used only
664 	 * through on495. This function comes out for on1095. See
665 	 * PSARC 1993-546. -- JST
666 	 */
667 	if (getenv("NONABI_SCRIPTS") != NULL) {
668 		old_pkg = 1;
669 	}
670 
671 	/*
672 	 * See if the user wants to process symlinks consistent with
673 	 * the old behavior.
674 	 */
675 
676 	if (getenv("PKG_NONABI_SYMLINKS") != NULL) {
677 		old_symlinks = 1;
678 	}
679 
680 	if (devtype((spoolDir ? spoolDir : get_PKGLOC()), &pkgdev) ||
681 	    pkgdev.dirname == NULL) {
682 		progerr(ERR_BAD_DEVICE, spoolDir ? spoolDir : get_PKGLOC());
683 		quit(1);
684 		/* NOTREACHED */
685 	}
686 
687 	pkgdir = pkgdev.dirname;
688 	repeat = ((optind >= argc) && pkgdev.mount);
689 
690 	/*
691 	 * error if there are packages on the command line and a category
692 	 * was specified
693 	 */
694 
695 	if (optind < argc && catg_arg != NULL) {
696 		progerr(ERR_PKGS_AND_CAT_PKGRM);
697 		usage();
698 		/* NOTREACHED */
699 	}
700 
701 	/*
702 	 * ********************************************************************
703 	 * main package processing "loop"
704 	 * ********************************************************************
705 	 */
706 
707 	for (;;) {
708 		boolean_t	b;
709 		char		**pkglist;	/* points to array of pkgs */
710 
711 		/*
712 		 * mount the spool device if required
713 		 */
714 
715 		if (pkgdev.mount) {
716 			if (n = pkgmount(&pkgdev, NULL, 0, 0, 1)) {
717 				quit(n);
718 				/* NOTREACHED */
719 			}
720 		}
721 
722 		if (chdir(pkgdev.dirname)) {
723 			progerr(ERR_CHDIR, pkgdev.dirname);
724 			quit(1);
725 			/* NOTREACHED */
726 		}
727 
728 		/*
729 		 * spool device mounted/available - get the list of the
730 		 * packages to remove
731 		 */
732 
733 		n = pkgGetPackageList(&pkglist, argv, optind,
734 			catg_arg, category, &pkgdev);
735 
736 		switch (n) {
737 			case -1:	/* no packages found */
738 				echoDebug(DBG_PKGLIST_RM_NONFOUND,
739 					PSTR(pkgdev.dirname));
740 				progerr(ERR_NOPKGS, pkgdev.dirname);
741 				quit(1);
742 				/* NOTREACHED */
743 
744 			case 0:		/* packages found */
745 				break;
746 
747 			default:	/* "quit" error */
748 				echoDebug(DBG_PKGLIST_RM_ERROR,
749 					pkgdev.dirname, n);
750 				quit(n);
751 				/* NOTREACHED */
752 		}
753 
754 		/*
755 		 * count the number of packages to remove
756 		 * NOTE: npkgs is a global variable that is referenced by quit.c
757 		 * when error messages are generated - it is referenced directly
758 		 * by the other functions called below...
759 		 */
760 
761 		for (npkgs = 0; pkglist[npkgs] != (char *)NULL; /* void */) {
762 			pkgLgth = strlen(pkglist[npkgs]);
763 			if (pkgLgth > longestPkg) {
764 				longestPkg = pkgLgth;
765 			}
766 			echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
767 			npkgs++;
768 		}
769 
770 		/* output number of packages to be removed */
771 
772 		echoDebug(DBG_NUM_PKGS_TO_REMOVE, npkgs, longestPkg);
773 
774 		/*
775 		 * package list generated - remove packages
776 		 */
777 
778 		b = remove_packages(pkglist, nodelete, longestPkg, repeat,
779 			altBinDir, pkgdev.dirname, spoolDir, noZones);
780 
781 		/*
782 		 * unmount the spool directory if necessary
783 		 */
784 
785 		if (pkgdev.mount) {
786 			(void) chdir("/");
787 			if (pkgumount(&pkgdev)) {
788 				progerr(ERR_PKGUNMOUNT, pkgdev.bdevice);
789 				quit(99);
790 				/* NOTREACHED */
791 
792 			}
793 		}
794 
795 		/*
796 		 * continue with next sequence of packages if continue set
797 		 */
798 
799 		if (b == B_TRUE) {
800 			continue;
801 		}
802 
803 		/*
804 		 * not continuing - quit with 0 exit code
805 		 */
806 
807 		quit(0);
808 		/* NOTREACHED */
809 #ifdef lint
810 		return (0);
811 #endif	/* lint */
812 	}
813 }
814 
815 /*
816  * *****************************************************************************
817  * static internal (private) functions
818  * *****************************************************************************
819  */
820 
821 /*
822  * Name:	doRemove
823  * Description:	Remove a package from the global zone, and optionally from one
824  *		or more non-global zones.
825  * Arguments:	a_nodelete: should the files and scripts remain installed?
826  *			- if != 0 pass -F flag to pkgremove - suppress
827  *			the removal of any files and any class action scripts
828  *			and suppress the running of any class action scripts.
829  *			The package files remain but the package looks like it
830  *			is not installed. This is mainly for use by upgrade.
831  *			- if == 0 do not pass -F flag to pkgremove - all
832  *			files and class action scripts are removed, and any
833  *			appropriate class action scripts are run.
834  *		a_altBinDir - pointer to string representing location of the
835  *			pkgremove executable to run. If not NULL, then pass
836  *			the path specified to the -b option to pkgremove.
837  *		a_longestPkg - length of the longest package "name" (for
838  *			output format alignment)
839  *		a_adminFile - pointer to string representing the admin
840  *			file to pass to pkgremove when removing a package from
841  *			the global zone only. Typically the admin file used for
842  *			the global zone is the admin file passed in by the user.
843  *			If this is == NULL no admin file is given to pkgremove.
844  *		a_zoneAdminFile - pointer to string representing the admin
845  *			file to pass to pkgremove when removing the package
846  *			from a non-global zone only. Typically the admin file
847  *			used for non-global zones supresses all checks since
848  *			the dependency checking is done for all zones first
849  *			before proceeding.
850  *			A zoneAdminFile MUST be specified if a_zlst != NULL.
851  *			A zoneAdminFile must NOT be specified if a_zlst == NULL.
852  *		a_zlst - list of zones to process; NULL if no zones to process.
853  * Returns:	int	(see ckreturn() function for details)
854  *		0 - success
855  *		1 - package operation failed (fatal error)
856  *		2 - non-fatal error (warning)
857  *		3 - user selected quit (operation interrupted)
858  *		4 - admin settings prevented operation
859  *		5 - interaction required and -n (non-interactive) specified
860  *		"10" will be added to indicate "immediate reboot required"
861  *		"20" will be added to indicate "reboot after install required"
862  */
863 
864 static int
865 doRemove(int a_nodelete, char *a_altBinDir, int a_longestPkg, char *a_adminFile,
866 	char *a_zoneAdminFile, zoneList_t a_zlst)
867 {
868 	boolean_t	b;
869 	char		**inheritedPkgDirs;
870 	char		*zoneName;
871 	char		ans[MAX_INPUT];
872 	int		n;
873 	int		zoneIndex;
874 	int		zonesSkipped;
875 	struct pkginfo	*pinfo = (struct pkginfo *)NULL;
876 	zone_state_t	zst;
877 
878 	/* entry assertions */
879 
880 	if (a_zlst != (zoneList_t)NULL) {
881 		/* zone list specified - zone admin file required */
882 		assert(a_zoneAdminFile != (char *)NULL);
883 		assert(*a_zoneAdminFile != '\0');
884 	} else {
885 		/* no zone list specified - no zone admin file needed */
886 		assert(a_zoneAdminFile == (char *)NULL);
887 	}
888 
889 	/* NOTE: required 'pkgdir' set to spool directory or NULL */
890 	b = pkginfoIsPkgInstalled(&pinfo, pkginst);
891 	if (b == B_FALSE) {
892 		progerr(ERR_NO_SUCH_INSTANCE, pkginst);
893 		pkginfoFree(&pinfo);
894 		return (2);
895 	}
896 
897 	/* entry debugging info */
898 
899 	echoDebug(DBG_DOREMOVE_ENTRY);
900 	echoDebug(DBG_DOREMOVE_ARGS, PSTR(pinfo->pkginst), PSTR(pinfo->name),
901 		PSTR(pinfo->arch), PSTR(pinfo->version), PSTR(pinfo->basedir),
902 		PSTR(pinfo->catg), pinfo->status);
903 
904 	if (!nointeract) {
905 		char	fmt1[100];
906 
907 		/* create format based on max pkg name length */
908 
909 		(void) snprintf(fmt1, sizeof (fmt1), "   %%-%d.%ds  %%s",
910 				a_longestPkg, a_longestPkg);
911 
912 		if (pinfo->status == PI_SPOOLED) {
913 			echo(INFO_SPOOLED);
914 		} else {
915 			if (getuid()) {
916 				progerr(ERR_NOT_ROOT, get_prog_name());
917 				exit(1);
918 			}
919 			echo(INFO_INSTALL);
920 		}
921 
922 		echo(fmt1, pinfo->pkginst, pinfo->name);
923 
924 		if (pinfo->arch || pinfo->version) {
925 			char	fmt2[100];
926 
927 			/* create format based on max pkg name length */
928 
929 			(void) snprintf(fmt2, sizeof (fmt2), "   %%%d.%ds  ",
930 					a_longestPkg, a_longestPkg);
931 
932 			/* LINTED variable format specifier to fprintf() */
933 			(void) fprintf(stderr, fmt2, "");
934 
935 			if (pinfo->arch) {
936 				(void) fprintf(stderr, "(%s) ", pinfo->arch);
937 			}
938 
939 			if (pinfo->version) {
940 				(void) fprintf(stderr, "%s", pinfo->version);
941 			}
942 
943 			(void) fprintf(stderr, "\n");
944 		}
945 
946 		n = ckyorn(ans, NULL, NULL, NULL, ASK_CONFIRM);
947 		if (n != 0) {
948 			quit(n);
949 			/* NOTREACHED */
950 		}
951 
952 		if (strchr("yY", *ans) == NULL) {
953 			pkginfoFree(&pinfo);
954 			return (0);
955 		}
956 	}
957 
958 	if (pinfo->status == PI_PRESVR4) {
959 		pkginfoFree(&pinfo);
960 		return (presvr4(pkginst, nointeract));
961 	}
962 
963 	if (pinfo->status == PI_SPOOLED) {
964 		/* removal from a directory */
965 		echo(INFO_RMSPOOL, pkginst);
966 		pkginfoFree(&pinfo);
967 		return (rrmdir(pkginst));
968 	}
969 
970 	/* exit if not root */
971 
972 	if (getuid()) {
973 		progerr(ERR_NOT_ROOT, get_prog_name());
974 		exit(1);
975 	}
976 
977 	pkginfoFree(&pinfo);
978 
979 	zonesSkipped = 0;
980 
981 	if (interrupted != 0) {
982 		echo(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
983 		echoDebug(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
984 		return (n);
985 	}
986 
987 	echoDebug(DBG_REMOVE_FLAG_VALUES, "before pkgZoneRemove",
988 		admnflag, doreboot, failflag, interrupted,
989 		intrflag, ireboot, nullflag, warnflag);
990 
991 	for (zoneIndex = 0;
992 	    a_zlst != NULL &&
993 	    (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
994 	    zoneIndex++) {
995 
996 		/* skip the zone if it is NOT running */
997 
998 		zst = z_zlist_get_current_state(a_zlst, zoneIndex);
999 		if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
1000 			zonesSkipped++;
1001 			echoDebug(DBG_SKIPPING_ZONE, zoneName);
1002 			continue;
1003 		}
1004 
1005 		echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
1006 		echoDebug(DBG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
1007 
1008 		/* determine list of directories inherited from global zone */
1009 
1010 		inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
1011 					zoneIndex);
1012 
1013 		/*
1014 		 * remove package from zone; use the zone admin file which
1015 		 * suppresses all checks.
1016 		 */
1017 
1018 		n = pkgZoneRemove(z_zlist_get_scratch(a_zlst, zoneIndex),
1019 			inheritedPkgDirs, a_nodelete, a_altBinDir,
1020 			a_zoneAdminFile, zst);
1021 
1022 		/* set success/fail condition variables */
1023 
1024 		ckreturn(n);
1025 
1026 		echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
1027 			admnflag, doreboot, failflag, interrupted, intrflag,
1028 			ireboot, nullflag, warnflag);
1029 	}
1030 
1031 	if (zonesSkipped > 0) {
1032 		echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
1033 
1034 		for (zoneIndex = 0;
1035 			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
1036 				(char *)NULL; zoneIndex++) {
1037 
1038 			/* skip the zone if it IS running */
1039 
1040 			zst = z_zlist_get_current_state(a_zlst, zoneIndex);
1041 			if (zst == ZONE_STATE_RUNNING ||
1042 			    zst == ZONE_STATE_MOUNTED) {
1043 				zonesSkipped++;
1044 				echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
1045 				continue;
1046 			}
1047 
1048 			/* skip the zone if it is NOT bootable */
1049 
1050 			if (z_zlist_is_zone_runnable(a_zlst,
1051 						zoneIndex) == B_FALSE) {
1052 				echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
1053 				echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE,
1054 					zoneName);
1055 				continue;
1056 			}
1057 
1058 			/* mount up the zone */
1059 
1060 			echo(MSG_BOOTING_ZONE, zoneName);
1061 			echoDebug(DBG_BOOTING_ZONE, zoneName);
1062 
1063 			b = z_zlist_change_zone_state(a_zlst, zoneIndex,
1064 				ZONE_STATE_MOUNTED);
1065 			if (b == B_FALSE) {
1066 				progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
1067 				/* set fatal error return condition */
1068 				ckreturn(1);
1069 				continue;
1070 			}
1071 
1072 			echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
1073 
1074 			/* determine list of dirs inherited from global zone */
1075 
1076 			inheritedPkgDirs =
1077 				z_zlist_get_inherited_pkg_dirs(a_zlst,
1078 						zoneIndex);
1079 
1080 			/*
1081 			 * remove package from zone; use the zone admin file
1082 			 * which suppresses all checks.
1083 			 */
1084 
1085 			n = pkgZoneRemove(z_zlist_get_scratch(a_zlst,
1086 				zoneIndex), inheritedPkgDirs,
1087 				a_nodelete, a_altBinDir, a_zoneAdminFile,
1088 				ZONE_STATE_MOUNTED);
1089 
1090 			/* set success/fail condition variables */
1091 
1092 			ckreturn(n);
1093 
1094 			echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
1095 				admnflag, doreboot, failflag, interrupted,
1096 				intrflag, ireboot, nullflag, warnflag);
1097 
1098 			/* restore original state of zone */
1099 
1100 			echo(MSG_RESTORE_ZONE_STATE, zoneName);
1101 			echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
1102 
1103 			b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
1104 		}
1105 	}
1106 
1107 	/*
1108 	 * Process global zone if it was either the only possible
1109 	 * target (no list of zones specified) or it appears in the list
1110 	 */
1111 	if (a_zlst == NULL || z_on_zone_spec(GLOBAL_ZONENAME)) {
1112 		/* reset interrupted flag before calling pkgremove */
1113 		interrupted = 0;	/* last action was NOT quit */
1114 
1115 		/*
1116 		 * call pkgremove for this package for the global zone;
1117 		 * use the admin file passed in by the user via -a.
1118 		 */
1119 		n = pkgRemove(a_nodelete, a_altBinDir, a_adminFile,
1120 		    z_get_inherited_file_systems());
1121 
1122 		/* set success/fail condition variables */
1123 		ckreturn(n);
1124 	}
1125 
1126 	return (n);
1127 }
1128 
1129 /*
1130  *  function to clear out any exisiting error return conditions that may have
1131  *  been set by previous calls to ckreturn()
1132  */
1133 static void
1134 resetreturn()
1135 {
1136 	admnflag = 0;	/* != 0 if any pkg op admin setting failure (4) */
1137 	doreboot = 0;	/* != 0 if reboot required after installation (>= 10) */
1138 	failflag = 0;	/* != 0 if fatal error has occurred (1) */
1139 	intrflag = 0;	/* != 0 if user selected quit (3) */
1140 	ireboot = 0;	/* != 0 if immediate reboot required (>= 20) */
1141 	nullflag = 0;	/* != 0 if admin interaction required (5) */
1142 	warnflag = 0;	/* != 0 if non-fatal error has occurred (2) */
1143 	interrupted = 0;	/* last pkg op was quit (1,2,3,4,5) */
1144 }
1145 
1146 /*
1147  *  function which checks the indicated return value
1148  *  and indicates disposition of installation
1149  */
1150 static void
1151 ckreturn(int retcode)
1152 {
1153 	/*
1154 	 * entry debugging info
1155 	 */
1156 
1157 	echoDebug(DBG_PKGRM_CKRETURN, retcode, PSTR(pkginst));
1158 
1159 	switch (retcode) {
1160 	    case  0:		/* successful */
1161 	    case 10:
1162 	    case 20:
1163 		break; /* empty case */
1164 
1165 	    case  1:		/* package operation failed (fatal error) */
1166 	    case 11:
1167 	    case 21:
1168 		failflag++;
1169 		interrupted++;
1170 		break;
1171 
1172 	    case  2:		/* non-fatal error (warning) */
1173 	    case 12:
1174 	    case 22:
1175 		warnflag++;
1176 		interrupted++;
1177 		break;
1178 
1179 	    case  3:		/* user selected quit; operation interrupted */
1180 	    case 13:
1181 	    case 23:
1182 		intrflag++;
1183 		interrupted++;
1184 		break;
1185 
1186 	    case  4:		/* admin settings prevented operation */
1187 	    case 14:
1188 	    case 24:
1189 		admnflag++;
1190 		interrupted++;
1191 		break;
1192 
1193 	    case  5:		/* administration: interaction req (no -n) */
1194 	    case 15:
1195 	    case 25:
1196 		nullflag++;
1197 		interrupted++;
1198 		break;
1199 
1200 	    default:
1201 		failflag++;
1202 		interrupted++;
1203 		return;
1204 	}
1205 
1206 	if (retcode >= 20) {
1207 		ireboot++;
1208 	} else if (retcode >= 10) {
1209 		doreboot++;
1210 	}
1211 }
1212 
1213 static int
1214 pkgZoneCheckRemove(char *a_zoneName, char **a_inheritedPkgDirs,
1215 	char *a_altBinDir, char *a_adminFile, char *a_stdoutPath,
1216 	zone_state_t a_zoneState)
1217 {
1218 	char	*arg[MAXARGS];
1219 	char	*p;
1220 	char	adminfd_path[PATH_MAX];
1221 	char	path[PATH_MAX];
1222 	int	fds[MAX_FDS];
1223 	int	maxfds;
1224 	int	n;
1225 	int	nargs;
1226 
1227 	/* entry assertions */
1228 
1229 	assert(a_zoneName != (char *)NULL);
1230 	assert(*a_zoneName != '\0');
1231 
1232 	/* entry debugging info */
1233 
1234 	echoDebug(DBG_PKGZONECHECKREMOVE_ENTRY);
1235 	echoDebug(DBG_PKGZONECHECKREMOVE_ARGS, a_zoneName, PSTR(pkginst),
1236 		PSTR(pkgdev.dirname), PSTR(a_adminFile), PSTR(a_stdoutPath));
1237 
1238 	/* generate path to pkgremove */
1239 
1240 	(void) snprintf(path, sizeof (path), "%s/pkgremove",
1241 		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
1242 
1243 	/* start at first file descriptor */
1244 
1245 	maxfds = 0;
1246 
1247 	/*
1248 	 * generate argument list for call to pkgremove
1249 	 */
1250 
1251 	/* start at argument 0 */
1252 
1253 	nargs = 0;
1254 
1255 	/* first argument is path to executable */
1256 
1257 	arg[nargs++] = strdup(path);
1258 
1259 	/* second argument is always: pass -O debug to pkgremove: debug mode */
1260 
1261 	if (debugFlag == B_TRUE) {
1262 		arg[nargs++] = "-O";
1263 		arg[nargs++] = "debug";
1264 	}
1265 
1266 	/* pkgrm -b dir: pass -b to pkgremove */
1267 
1268 	if (a_altBinDir != (char *)NULL) {
1269 		arg[nargs++] = "-b";
1270 		arg[nargs++] = a_altBinDir;
1271 	}
1272 
1273 	/*
1274 	 * NONABI_SCRIPTS defined: pass -o to pkgremove; refers to a
1275 	 * pkg requiring operator interaction during a procedure script
1276 	 * (common before on1093)
1277 	 */
1278 
1279 	if (old_pkg) {
1280 		arg[nargs++] = "-o";
1281 	}
1282 
1283 	/*
1284 	 * PKG_NONABI_SYMLINKS defined: pass -y to pkgremove; process
1285 	 * symlinks consistent with old behavior
1286 	 */
1287 
1288 	if (old_symlinks) {
1289 		arg[nargs++] = "-y";
1290 	}
1291 
1292 	/* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
1293 
1294 	arg[nargs++] = "-M";
1295 
1296 	/* pkgrm -A: pass -A to pkgremove */
1297 
1298 	if (pkgrmremote) {
1299 		arg[nargs++] = "-A";
1300 	}
1301 
1302 	/* pkgrm -v: pass -v to pkgremove: never trace scripts */
1303 
1304 	/* pass "-O enable-hollow-package-support" */
1305 
1306 	if (is_depend_pkginfo_DB()) {
1307 		arg[nargs++] = "-O";
1308 		arg[nargs++] = "enable-hollow-package-support";
1309 	}
1310 
1311 	/* pass -n to pkgremove: always in noninteractive mode */
1312 
1313 	arg[nargs++] = "-n";
1314 
1315 	/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
1316 
1317 	if (a_adminFile) {
1318 		int fd;
1319 		fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
1320 		if (fd < 0) {
1321 			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
1322 				errno, strerror(errno));
1323 			return (1);
1324 		}
1325 		(void) snprintf(adminfd_path, sizeof (adminfd_path),
1326 			"/proc/self/fd/%d", fd);
1327 		fds[maxfds++] = fd;
1328 		arg[nargs++] = "-a";
1329 		arg[nargs++] = strdup(adminfd_path);
1330 	}
1331 
1332 	/*
1333 	 * pkgadd -R root: pass -R /a to pkgremove in mounted zone
1334 	 */
1335 	if (a_zoneState == ZONE_STATE_MOUNTED) {
1336 		arg[nargs++] = "-R";
1337 		arg[nargs++] = "/a";
1338 	}
1339 
1340 	/* pkgrm -F: pass -F to pkgremove: always update DB only */
1341 
1342 	arg[nargs++] = "-F";
1343 
1344 	/* pass "-O preremovecheck" */
1345 
1346 	arg[nargs++] = "-O";
1347 	arg[nargs++] = "preremovecheck";
1348 
1349 	/* add "-O addzonename" */
1350 
1351 	arg[nargs++] = "-O";
1352 	arg[nargs++] = "addzonename";
1353 
1354 	/* add all inherited file systems */
1355 
1356 	if (a_inheritedPkgDirs != (char **)NULL) {
1357 		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
1358 			char	ifs[MAXPATHLEN+22];
1359 			(void) snprintf(ifs, sizeof (ifs),
1360 				"inherited-filesystem=%s",
1361 				a_inheritedPkgDirs[n]);
1362 			arg[nargs++] = "-O";
1363 			arg[nargs++] = strdup(ifs);
1364 		}
1365 	}
1366 
1367 	/*
1368 	 * add parent zone info/type
1369 	 */
1370 
1371 	p = z_get_zonename();
1372 	if ((p != NULL) && (*p != '\0')) {
1373 			char	zn[MAXPATHLEN];
1374 			(void) snprintf(zn, sizeof (zn),
1375 				"parent-zone-name=%s", p);
1376 			arg[nargs++] = "-O";
1377 			arg[nargs++] = strdup(zn);
1378 	}
1379 
1380 	/* current zone type */
1381 
1382 	arg[nargs++] = "-O";
1383 	if (z_running_in_global_zone() == B_TRUE) {
1384 			char	zn[MAXPATHLEN];
1385 			(void) snprintf(zn, sizeof (zn),
1386 				"parent-zone-type=%s",
1387 				TAG_VALUE_GLOBAL_ZONE);
1388 			arg[nargs++] = strdup(zn);
1389 	} else {
1390 			char	zn[MAXPATHLEN];
1391 			(void) snprintf(zn, sizeof (zn),
1392 				"parent-zone-type=%s",
1393 				TAG_VALUE_NONGLOBAL_ZONE);
1394 			arg[nargs++] = strdup(zn);
1395 	}
1396 
1397 	/* pass -N to pkgremove: program name to report */
1398 
1399 	arg[nargs++] = "-N";
1400 	arg[nargs++] = get_prog_name();
1401 
1402 	/* add package instance name */
1403 
1404 	arg[nargs++] = pkginst;
1405 
1406 	/* terminate argument list */
1407 
1408 	arg[nargs++] = NULL;
1409 
1410 	/* execute pkgremove command */
1411 
1412 	if (debugFlag == B_TRUE) {
1413 		echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
1414 		for (n = 0; arg[n]; n++) {
1415 			echoDebug(DBG_ARG, n, arg[n]);
1416 		}
1417 	}
1418 
1419 	/* terminate file descriptor list */
1420 
1421 	fds[maxfds] = -1;
1422 
1423 	/* exec command in zone */
1424 
1425 	n = z_zone_exec(a_zoneName, path, arg, a_stdoutPath, (char *)NULL, fds);
1426 
1427 	echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n,
1428 			PSTR(a_stdoutPath));
1429 
1430 	/*
1431 	 * close any files that were opened for use by the
1432 	 * /proc/self/fd interface so they could be passed to programs
1433 	 * via the z_zone_exec() interface
1434 	 */
1435 
1436 	for (; maxfds > 0; maxfds--) {
1437 		(void) close(fds[maxfds-1]);
1438 	}
1439 
1440 	/* return results of pkgremove in zone execution */
1441 
1442 	return (n);
1443 }
1444 
1445 static int
1446 pkgZoneRemove(char *a_zoneName, char **a_inheritedPkgDirs,
1447 	int a_nodelete, char *a_altBinDir, char *a_adminFile,
1448 	zone_state_t a_zoneState)
1449 {
1450 	char	*arg[MAXARGS];
1451 	char	*p;
1452 	char	adminfd_path[PATH_MAX];
1453 	char	path[PATH_MAX];
1454 	int	fds[MAX_FDS];
1455 	int	maxfds;
1456 	int	n;
1457 	int	nargs;
1458 
1459 	/* entry assertions */
1460 
1461 	assert(a_zoneName != (char *)NULL);
1462 	assert(*a_zoneName != '\0');
1463 
1464 	/* entry debugging info */
1465 
1466 	echoDebug(DBG_PKGZONEREMOVE_ENTRY);
1467 	echoDebug(DBG_PKGZONEREMOVE_ARGS, a_zoneName, PSTR(pkginst),
1468 		PSTR(pkgdev.dirname), a_nodelete, PSTR(a_adminFile));
1469 
1470 	/* generate path to pkgremove */
1471 
1472 	(void) snprintf(path, sizeof (path), "%s/pkgremove",
1473 		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
1474 
1475 	/* start at first file descriptor */
1476 
1477 	maxfds = 0;
1478 
1479 	/*
1480 	 * generate argument list for call to pkgremove
1481 	 */
1482 
1483 	/* start at argument 0 */
1484 
1485 	nargs = 0;
1486 
1487 	/* first argument is path to executable */
1488 
1489 	arg[nargs++] = strdup(path);
1490 
1491 	/* second argument is always: pass -O debug to pkgremove: debug mode */
1492 
1493 	if (debugFlag == B_TRUE) {
1494 		arg[nargs++] = "-O";
1495 		arg[nargs++] = "debug";
1496 	}
1497 
1498 	/* pkgrm -b dir: pass -b to pkgremove */
1499 
1500 	if (a_altBinDir != (char *)NULL) {
1501 		arg[nargs++] = "-b";
1502 		arg[nargs++] = a_altBinDir;
1503 	}
1504 
1505 	/*
1506 	 * NONABI_SCRIPTS defined: pass -o to pkgremove; refers to a
1507 	 * pkg requiring operator interaction during a procedure script
1508 	 * (common before on1093)
1509 	 */
1510 
1511 	if (old_pkg) {
1512 		arg[nargs++] = "-o";
1513 	}
1514 
1515 	/*
1516 	 * PKG_NONABI_SYMLINKS defined: pass -y to pkgremove; process
1517 	 * symlinks consistent with old behavior
1518 	 */
1519 
1520 	if (old_symlinks) {
1521 		arg[nargs++] = "-y";
1522 	}
1523 
1524 	/* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
1525 
1526 	arg[nargs++] = "-M";
1527 
1528 	/* pkgrm -A: pass -A to pkgremove */
1529 
1530 	if (pkgrmremote) {
1531 		arg[nargs++] = "-A";
1532 	}
1533 
1534 	/* pkgrm -v: pass -v to pkgremove: trace scripts */
1535 
1536 	if (pkgverbose) {
1537 		arg[nargs++] = "-v";
1538 	}
1539 
1540 	/* pass "-O enable-hollow-package-support" */
1541 
1542 	if (is_depend_pkginfo_DB()) {
1543 		arg[nargs++] = "-O";
1544 		arg[nargs++] = "enable-hollow-package-support";
1545 	}
1546 
1547 	/* pkgrm -n: pass -n to pkgremove: noninteractive mode */
1548 
1549 	if (nointeract) {
1550 		arg[nargs++] = "-n";
1551 	}
1552 
1553 	/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
1554 
1555 	if (a_adminFile) {
1556 		int fd;
1557 		fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
1558 		if (fd < 0) {
1559 			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
1560 				errno, strerror(errno));
1561 			return (1);
1562 		}
1563 		(void) snprintf(adminfd_path, sizeof (adminfd_path),
1564 			"/proc/self/fd/%d", fd);
1565 		fds[maxfds++] = fd;
1566 		arg[nargs++] = "-a";
1567 		arg[nargs++] = adminfd_path;
1568 	}
1569 
1570 	/*
1571 	 * pkgadd -R root: pass -R /a to pkgremove in mounted zone
1572 	 */
1573 	if (a_zoneState == ZONE_STATE_MOUNTED) {
1574 		arg[nargs++] = "-R";
1575 		arg[nargs++] = "/a";
1576 	}
1577 
1578 	/* pkgrm -F: pass -F to pkgremove: update DB only */
1579 
1580 	if (a_nodelete) {
1581 		arg[nargs++] = "-F";
1582 	}
1583 
1584 	/* add "-O addzonename" */
1585 
1586 	arg[nargs++] = "-O";
1587 	arg[nargs++] = "addzonename";
1588 
1589 	/* add all inherited file systems */
1590 
1591 	if (a_inheritedPkgDirs != (char **)NULL) {
1592 		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
1593 			char	ifs[MAXPATHLEN+22];
1594 
1595 			(void) snprintf(ifs, sizeof (ifs),
1596 				"inherited-filesystem=%s",
1597 				a_inheritedPkgDirs[n]);
1598 			arg[nargs++] = "-O";
1599 			arg[nargs++] = strdup(ifs);
1600 		}
1601 	}
1602 
1603 	/*
1604 	 * add parent zone info/type
1605 	 */
1606 
1607 	p = z_get_zonename();
1608 	if ((p != NULL) && (*p != '\0')) {
1609 			char	zn[MAXPATHLEN];
1610 			(void) snprintf(zn, sizeof (zn),
1611 				"parent-zone-name=%s", p);
1612 			arg[nargs++] = "-O";
1613 			arg[nargs++] = strdup(zn);
1614 	}
1615 
1616 	/* current zone type */
1617 
1618 	arg[nargs++] = "-O";
1619 	if (z_running_in_global_zone() == B_TRUE) {
1620 			char	zn[MAXPATHLEN];
1621 			(void) snprintf(zn, sizeof (zn),
1622 				"parent-zone-type=%s",
1623 				TAG_VALUE_GLOBAL_ZONE);
1624 			arg[nargs++] = strdup(zn);
1625 	} else {
1626 			char	zn[MAXPATHLEN];
1627 			(void) snprintf(zn, sizeof (zn),
1628 				"parent-zone-type=%s",
1629 				TAG_VALUE_NONGLOBAL_ZONE);
1630 			arg[nargs++] = strdup(zn);
1631 	}
1632 
1633 	/* pass -N to pkgremove: program name to report */
1634 
1635 	arg[nargs++] = "-N";
1636 	arg[nargs++] = get_prog_name();
1637 
1638 	/* add package instance name */
1639 
1640 	arg[nargs++] = pkginst;
1641 
1642 	/* terminate argument list */
1643 
1644 	arg[nargs++] = NULL;
1645 
1646 	/* execute pkgremove command */
1647 
1648 	if (debugFlag == B_TRUE) {
1649 		echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
1650 		for (n = 0; arg[n]; n++) {
1651 			echoDebug(DBG_ARG, n, arg[n]);
1652 		}
1653 	}
1654 
1655 	/* terminate file descriptor list */
1656 
1657 	fds[maxfds] = -1;
1658 
1659 	/* exec command in zone */
1660 
1661 	n = z_zone_exec(a_zoneName, path, arg, (char *)NULL, (char *)NULL, fds);
1662 
1663 	/*
1664 	 * close any files that were opened for use by the
1665 	 * /proc/self/fd interface so they could be passed to programs
1666 	 * via the z_zone_exec() interface
1667 	 */
1668 
1669 	for (; maxfds > 0; maxfds--) {
1670 		(void) close(fds[maxfds-1]);
1671 	}
1672 
1673 	return (n);
1674 }
1675 
1676 /*
1677  * Name:	pkgRemove
1678  * Description:	Invoke pkgremove in the current zone to perform a remove
1679  *		of a single package from the current zone or standalone system
1680  * Arguments:	a_nodelete: should the files and scripts remain installed?
1681  *			- if != 0 pass -F flag to pkgremove - suppress
1682  *			the removal of any files and any class action scripts
1683  *			and suppress the running of any class action scripts.
1684  *			The package files remain but the package looks like it
1685  *			is not installed. This is mainly for use by upgrade.
1686  *			- if == 0 do not pass -F flag to pkgremove - all
1687  *			files and class action scripts are removed, and any
1688  *			appropriate class action scripts are run.
1689  *		a_altBinDir - pointer to string representing location of the
1690  *			pkgremove executable to run. If not NULL, then pass
1691  *			the path specified to the -b option to pkgremove.
1692  *		a_adminFile - pointer to string representing the admin
1693  *			file to pass to pkgremove when removing the package.
1694  *			If this is == NULL no admin file is given to pkgremove.
1695  *		a_inheritedPkgDirs - pointer to array of strings, each one
1696  *			representing the non-global zones full path of a
1697  *			directory that is inherited from the global zone.
1698  * Returns:	int	(see ckreturn() function for details)
1699  *		0 - success
1700  *		1 - package operation failed (fatal error)
1701  *		2 - non-fatal error (warning)
1702  *		3 - user selected quit (operation interrupted)
1703  *		4 - admin settings prevented operation
1704  *		5 - interaction required and -n (non-interactive) specified
1705  *		"10" will be added to indicate "immediate reboot required"
1706  *		"20" will be added to indicate "reboot after install required"
1707  */
1708 
1709 static int
1710 pkgRemove(int a_nodelete, char *a_altBinDir, char *a_adminFile,
1711 	char **a_inheritedPkgDirs)
1712 {
1713 	char	*arg[MAXARGS];
1714 	char	*p;
1715 	char	path[PATH_MAX];
1716 	int	n;
1717 	int	nargs;
1718 
1719 	/* entry debugging info */
1720 
1721 	echoDebug(DBG_PKGREMOVE_ENTRY);
1722 	echoDebug(DBG_PKGREMOVE_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
1723 		a_nodelete, PSTR(a_adminFile));
1724 
1725 	(void) snprintf(path, sizeof (path), "%s/pkgremove",
1726 		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
1727 
1728 	nargs = 0;
1729 
1730 	/* first argument is path to executable */
1731 
1732 	arg[nargs++] = strdup(path);
1733 
1734 	/* second argument is always: pass -O debug to pkgremove: debug mode */
1735 
1736 	if (debugFlag == B_TRUE) {
1737 		arg[nargs++] = "-O";
1738 		arg[nargs++] = "debug";
1739 	}
1740 
1741 	/* pkgrm -b dir: pass -b to pkgremove */
1742 
1743 	if (a_altBinDir != (char *)NULL) {
1744 		arg[nargs++] = "-b";
1745 		arg[nargs++] = a_altBinDir;
1746 	}
1747 
1748 	/*
1749 	 * NONABI_SCRIPTS defined: pass -o to pkgremove; refers to a
1750 	 * pkg requiring operator interaction during a procedure script
1751 	 * (common before on1093)
1752 	 */
1753 
1754 	if (old_pkg) {
1755 		arg[nargs++] = "-o";
1756 	}
1757 
1758 	/*
1759 	 * PKG_NONABI_SYMLINKS defined: pass -y to pkgremove; process
1760 	 * symlinks consistent with old behavior
1761 	 */
1762 
1763 	if (old_symlinks) {
1764 		arg[nargs++] = "-y";
1765 	}
1766 
1767 	/* pkgrm -M: pass -M to pkgrm: dont mount client file systems */
1768 
1769 	if (no_map_client) {
1770 		arg[nargs++] = "-M";
1771 	}
1772 
1773 	/* pkgrm -A: pass -A to pkgrm */
1774 
1775 	if (pkgrmremote) {
1776 		arg[nargs++] = "-A";
1777 	}
1778 
1779 	/* pkgrm -v: pass -v to pkgremove: trace scripts */
1780 
1781 	if (pkgverbose) {
1782 		arg[nargs++] = "-v";
1783 	}
1784 
1785 	/* pkgrm -n: pass -n to pkgremove: noninteractive mode */
1786 
1787 	if (nointeract) {
1788 		arg[nargs++] = "-n";
1789 	}
1790 
1791 	/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
1792 
1793 	if (a_adminFile) {
1794 		arg[nargs++] = "-a";
1795 		arg[nargs++] = strdup(a_adminFile);
1796 	}
1797 
1798 	/* pkgrm -V vfstab: pass -V vfstab to pkgremove: alternate vfstab */
1799 
1800 	if (vfstab_file) {
1801 		arg[nargs++] = "-V";
1802 		arg[nargs++] = vfstab_file;
1803 	}
1804 
1805 	/* pkgrm -R root: pass -R root to pkgremove: alternative root */
1806 
1807 	if (is_an_inst_root()) {
1808 		arg[nargs++] = "-R";
1809 		arg[nargs++] = get_inst_root();
1810 	}
1811 
1812 	/* pkgrm -F: pass -F to pkgremove: update DB only */
1813 
1814 	if (a_nodelete) {
1815 		arg[nargs++] = "-F";
1816 	}
1817 
1818 	/* add all inherited file systems */
1819 
1820 	if (a_inheritedPkgDirs != (char **)NULL) {
1821 		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
1822 			char	ifs[MAXPATHLEN+22];
1823 			(void) snprintf(ifs, sizeof (ifs),
1824 				"inherited-filesystem=%s",
1825 				a_inheritedPkgDirs[n]);
1826 			arg[nargs++] = "-O";
1827 			arg[nargs++] = strdup(ifs);
1828 		}
1829 	}
1830 
1831 	/*
1832 	 * add parent zone info/type
1833 	 */
1834 
1835 	p = z_get_zonename();
1836 	if ((p != NULL) && (*p != '\0')) {
1837 			char	zn[MAXPATHLEN];
1838 			(void) snprintf(zn, sizeof (zn),
1839 				"parent-zone-name=%s", p);
1840 			arg[nargs++] = "-O";
1841 			arg[nargs++] = strdup(zn);
1842 	}
1843 
1844 	/* current zone type */
1845 
1846 	arg[nargs++] = "-O";
1847 	if (z_running_in_global_zone() == B_TRUE) {
1848 			char	zn[MAXPATHLEN];
1849 			(void) snprintf(zn, sizeof (zn),
1850 				"parent-zone-type=%s",
1851 				TAG_VALUE_GLOBAL_ZONE);
1852 			arg[nargs++] = strdup(zn);
1853 	} else {
1854 			char	zn[MAXPATHLEN];
1855 			(void) snprintf(zn, sizeof (zn),
1856 				"parent-zone-type=%s",
1857 				TAG_VALUE_NONGLOBAL_ZONE);
1858 			arg[nargs++] = strdup(zn);
1859 	}
1860 
1861 	/* pass -N to pkgremove: program name to report */
1862 
1863 	arg[nargs++] = "-N";
1864 	arg[nargs++] = get_prog_name();
1865 
1866 	/* add package instance name */
1867 
1868 	arg[nargs++] = pkginst;
1869 
1870 	/* terminate argument list */
1871 
1872 	arg[nargs++] = NULL;
1873 
1874 	/*
1875 	 * run the appropriate pkgremove command in the specified zone
1876 	 */
1877 
1878 	if (debugFlag == B_TRUE) {
1879 		echoDebug(DBG_ZONE_EXEC_ENTER, "global", arg[0]);
1880 		for (n = 0; arg[n]; n++) {
1881 			echoDebug(DBG_ARG, n, arg[n]);
1882 		}
1883 	}
1884 
1885 	/* execute pkgremove command */
1886 
1887 	n = pkgexecv(NULL, NULL, NULL, NULL, arg);
1888 
1889 	/* return results of pkgrm in this zone */
1890 
1891 	return (n);
1892 }
1893 
1894 static void
1895 usage(void)
1896 {
1897 	char	*prog = get_prog_name();
1898 
1899 	(void) fprintf(stderr, ERR_USAGE_PKGRM, prog, prog);
1900 	exit(1);
1901 }
1902 
1903 /*
1904  * Name:	remove_packages_in_global_with_zones
1905  * Description:	Remove packages from the global zone and from non-global zones
1906  *		when run from the global zone and when non-global zones are
1907  *		present.
1908  * Arguments:	a_pkgList - pointer to array of strings, each string specifying
1909  *			the name of one package to be removed.
1910  *		a_nodelete: should the files and scripts remain installed?
1911  *			- if != 0 pass -F flag to pkgremove - suppress
1912  *			the removal of any files and any class action scripts
1913  *			and suppress the running of any class action scripts.
1914  *			The package files remain but the package looks like it
1915  *			is not installed. This is mainly for use by upgrade.
1916  *			- if == 0 do not pass -F flag to pkgremove - all
1917  *			files and class action scripts are removed, and any
1918  *			appropriate class action scripts are run.
1919  *		a_longestPkg - length of the longest package "name" (for
1920  *			output format alignment)
1921  *		a_repeat - are there more packages avialable in "optind"
1922  *			- B_TRUE - process packages from optind
1923  *			- B_FALSE - do not process packages from optind
1924  *		a_altBinDir - pointer to string representing location of the
1925  *			pkgremove executable to run. If not NULL, then pass
1926  *			the path specified to the -b option to pkgremove.
1927  *		a_pkgdir - pointer to string representing the directory
1928  *			where the packages to be removed are located.
1929  *		a_zlst - list of zones to process; NULL if no zones to process.
1930  * Returns:	int	(see ckreturn() function for details)
1931  *		0 - success
1932  *		1 - package operation failed (fatal error)
1933  *		2 - non-fatal error (warning)
1934  *		3 - user selected quit (operation interrupted)
1935  *		4 - admin settings prevented operation
1936  *		5 - interaction required and -n (non-interactive) specified
1937  *		"10" will be added to indicate "immediate reboot required"
1938  *		"20" will be added to indicate "reboot after install required"
1939  */
1940 
1941 static boolean_t
1942 remove_packages_in_global_with_zones(char **a_pkgList, int a_nodelete,
1943 	int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir,
1944 	zoneList_t a_zlst)
1945 {
1946 static	char		*zoneAdminFile = (char *)NULL;
1947 
1948 	boolean_t	b;
1949 	char		**inheritedPkgDirs;
1950 	char		*zoneName;
1951 	char		*scratchName;
1952 	char		preremovecheckPath[PATH_MAX+1];
1953 	int		i;
1954 	int		n;
1955 	int		savenpkgs = npkgs;
1956 	int		zoneIndex;
1957 	int		zonesSkipped;
1958 	zone_state_t	zst;
1959 
1960 	/* entry assertions */
1961 
1962 	assert(a_zlst != (zoneList_t)NULL);
1963 	assert(a_pkgList != (char **)NULL);
1964 	assert(a_longestPkg > 0);
1965 	assert(a_pkgdir != (char *)NULL);
1966 	assert(*a_pkgdir != '\0');
1967 
1968 	/* entry debugging info */
1969 
1970 	echoDebug(DBG_PKGREMPKGSGZWNGZ_ENTRY);
1971 	echoDebug(DBG_PKGREMPKGSGZWNGZ_ARGS, a_nodelete, a_longestPkg,
1972 		a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
1973 
1974 	/* check all packages */
1975 
1976 	if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
1977 		quit(1);
1978 	}
1979 
1980 	/* create temporary directory for use by zone operations */
1981 
1982 	create_zone_tempdir(&zoneTempDir, tmpdir);
1983 
1984 	/* create hands off settings admin file for use in a non-global zone */
1985 
1986 	create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
1987 
1988 	/*
1989 	 * all of the packages (as listed in the package list) are
1990 	 * removed one at a time from all non-global zones and then
1991 	 * from the global zone.
1992 	 */
1993 
1994 	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
1995 		/* reset interrupted flag before calling pkgremove */
1996 
1997 		interrupted = 0;	/* last action was NOT quit */
1998 
1999 		/* skip package if it is "in the global zone only" */
2000 
2001 		if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
2002 			continue;
2003 		}
2004 
2005 		/*
2006 		 * if operation failed in global zone do not propagate to
2007 		 * non-global zones
2008 		 */
2009 
2010 		zonesSkipped = 0;
2011 
2012 		if (interrupted != 0) {
2013 			echo(MSG_DOREMOVE_INTERRUPTED, pkginst);
2014 			echoDebug(DBG_DOREMOVE_INTERRUPTED, pkginst);
2015 			break;
2016 		}
2017 
2018 		echoDebug(DBG_REMOVE_FLAG_VALUES, "before loop",
2019 			admnflag, doreboot, failflag, interrupted,
2020 			intrflag, ireboot, nullflag, warnflag);
2021 
2022 		for (zoneIndex = 0;
2023 			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
2024 				(char *)NULL; zoneIndex++) {
2025 
2026 			/* skip the zone if it is NOT running */
2027 
2028 			zst = z_zlist_get_current_state(a_zlst, zoneIndex);
2029 			if (zst != ZONE_STATE_RUNNING &&
2030 			    zst != ZONE_STATE_MOUNTED) {
2031 				zonesSkipped++;
2032 				echoDebug(DBG_SKIPPING_ZONE, zoneName);
2033 				continue;
2034 			}
2035 
2036 			echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
2037 			echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
2038 				zoneName);
2039 
2040 			scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
2041 
2042 			(void) snprintf(preremovecheckPath,
2043 				sizeof (preremovecheckPath),
2044 				"%s/%s.%s.preremovecheck.txt",
2045 				zoneTempDir, pkginst, scratchName);
2046 
2047 			/* determine list of dirs inherited from global zone */
2048 
2049 			inheritedPkgDirs =
2050 				z_zlist_get_inherited_pkg_dirs(a_zlst,
2051 					zoneIndex);
2052 
2053 			/*
2054 			 * dependency check this package this zone; use the
2055 			 * user supplied admin file so that the appropriate
2056 			 * level of dependency checking is (or is not) done.
2057 			 */
2058 
2059 			n = pkgZoneCheckRemove(scratchName, inheritedPkgDirs,
2060 				a_altBinDir, admnfile, preremovecheckPath,
2061 				zst);
2062 
2063 			/* set success/fail condition variables */
2064 
2065 			ckreturn(n);
2066 
2067 			echoDebug(DBG_REMOVE_FLAG_VALUES,
2068 				"after pkgzonecheckremove",
2069 				admnflag, doreboot, failflag, interrupted,
2070 				intrflag, ireboot, nullflag, warnflag);
2071 		}
2072 
2073 		if (zonesSkipped == 0) {
2074 			continue;
2075 		}
2076 
2077 		echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
2078 
2079 		for (zoneIndex = 0;
2080 			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
2081 				(char *)NULL; zoneIndex++) {
2082 
2083 			/* skip the zone if it IS running */
2084 
2085 			zst = z_zlist_get_current_state(a_zlst, zoneIndex);
2086 			if (zst == ZONE_STATE_RUNNING ||
2087 			    zst == ZONE_STATE_MOUNTED) {
2088 				zonesSkipped++;
2089 				echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
2090 				continue;
2091 			}
2092 
2093 			/* skip the zone if it is NOT bootable */
2094 
2095 			if (z_zlist_is_zone_runnable(a_zlst,
2096 						zoneIndex) == B_FALSE) {
2097 				echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
2098 				echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE,
2099 					zoneName);
2100 				continue;
2101 			}
2102 
2103 			/* mount up the zone */
2104 
2105 			echo(MSG_BOOTING_ZONE, zoneName);
2106 			echoDebug(DBG_BOOTING_ZONE, zoneName);
2107 
2108 			b = z_zlist_change_zone_state(a_zlst, zoneIndex,
2109 				ZONE_STATE_MOUNTED);
2110 			if (b == B_FALSE) {
2111 				progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
2112 				/* set fatal error return condition */
2113 				ckreturn(1);
2114 				continue;
2115 			}
2116 
2117 			echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
2118 			echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
2119 					zoneName);
2120 
2121 			scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
2122 
2123 			(void) snprintf(preremovecheckPath,
2124 				sizeof (preremovecheckPath),
2125 				"%s/%s.%s.preremovecheck.txt",
2126 				zoneTempDir, pkginst, scratchName);
2127 
2128 			/* determine list of dirs inherited from global zone */
2129 
2130 			inheritedPkgDirs =
2131 				z_zlist_get_inherited_pkg_dirs(a_zlst,
2132 					zoneIndex);
2133 
2134 			/*
2135 			 * dependency check this package this zone; use the
2136 			 * user supplied admin file so that the appropriate
2137 			 * level of dependency checking is (or is not) done.
2138 			 */
2139 
2140 			n = pkgZoneCheckRemove(scratchName, inheritedPkgDirs,
2141 				a_altBinDir, admnfile, preremovecheckPath,
2142 				ZONE_STATE_MOUNTED);
2143 
2144 			/* set success/fail condition variables */
2145 
2146 			ckreturn(n);
2147 
2148 			echoDebug(DBG_REMOVE_FLAG_VALUES,
2149 				"after pkgzonecheckremove",
2150 				admnflag, doreboot, failflag, interrupted,
2151 				intrflag, ireboot, nullflag, warnflag);
2152 
2153 			/* restore original state of zone */
2154 
2155 			echo(MSG_RESTORE_ZONE_STATE, zoneName);
2156 			echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
2157 
2158 			b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
2159 		}
2160 		npkgs--;
2161 	}
2162 
2163 	/*
2164 	 * look at all pre-remove check files
2165 	 */
2166 
2167 	i = preremove_verify(a_pkgList, a_zlst, zoneTempDir);
2168 	if (i != 0) {
2169 		quit(i);
2170 	}
2171 
2172 	npkgs = savenpkgs;
2173 
2174 	/*
2175 	 * reset all error return condition variables that may have been
2176 	 * set during package removal dependency checking so that they
2177 	 * do not reflect on the success/failure of the actual package
2178 	 * removal operations
2179 	 */
2180 
2181 	resetreturn();
2182 
2183 	/*
2184 	 * all of the packages (as listed in the package list) are
2185 	 * removed one at a time.
2186 	 */
2187 
2188 	interrupted = 0;
2189 	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
2190 		boolean_t	in_gz_only;
2191 		started = 0;
2192 
2193 		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
2194 			continue;
2195 		}
2196 
2197 		in_gz_only = pkgIsPkgInGzOnly(get_inst_root(), pkginst);
2198 
2199 		/* reset interrupted flag before calling pkgremove */
2200 
2201 		interrupted = 0;
2202 
2203 		/*
2204 		 * pkgrm invoked from within the global zone and there are
2205 		 * non-global zones configured:
2206 		 * Remove the package from the global zone.
2207 		 * If not removing the package from the global zone only,
2208 		 * then remove the package from the list of zones specified.
2209 		 */
2210 
2211 		if (in_gz_only) {
2212 			/* global zone only */
2213 			n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
2214 				admnfile, (char *)NULL, (zoneList_t)NULL);
2215 		} else {
2216 			/* global zone and non-global zones */
2217 			n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
2218 				zoneAdminFile, zoneAdminFile, a_zlst);
2219 		}
2220 
2221 		/* set success/fail condition variables */
2222 
2223 		ckreturn(n);
2224 
2225 		npkgs--;
2226 	}
2227 
2228 	/*
2229 	 * all packages in the package list have been removed.
2230 	 * Continue with removal if:
2231 	 * -- immediate reboot is NOT required
2232 	 * -- there are more packages to remove
2233 	 * else return do NOT continue.
2234 	 */
2235 
2236 	if ((ireboot == 0) && (a_repeat != 0)) {
2237 		return (B_TRUE);
2238 	}
2239 
2240 	/* return 'dont continue' */
2241 
2242 	return (B_FALSE);
2243 }
2244 
2245 /*
2246  * Name:	remove_packages_in_nonglobal_zone
2247  * Description:	Remove packages in a non-global zone when run from a
2248  *		non-global zone.
2249  * Arguments:	a_pkgList - pointer to array of strings, each string specifying
2250  *			the name of one package to be removed.
2251  *		a_nodelete: should the files and scripts remain installed?
2252  *			- if != 0 pass -F flag to pkgremove - suppress
2253  *			the removal of any files and any class action scripts
2254  *			and suppress the running of any class action scripts.
2255  *			The package files remain but the package looks like it
2256  *			is not installed. This is mainly for use by upgrade.
2257  *			- if == 0 do not pass -F flag to pkgremove - all
2258  *			files and class action scripts are removed, and any
2259  *			appropriate class action scripts are run.
2260  *		a_longestPkg - length of the longest package "name" (for
2261  *			output format alignment)
2262  *		a_repeat - are there more packages avialable in "optind"
2263  *			- B_TRUE - process packages from optind
2264  *			- B_FALSE - do not process packages from optind
2265  *		a_altBinDir - pointer to string representing location of the
2266  *			pkgremove executable to run. If not NULL, then pass
2267  *			the path specified to the -b option to pkgremove.
2268  *		a_pkgdir - pointer to string representing the directory
2269  *			where the packages to be removed are located.
2270  * Returns:	int	(see ckreturn() function for details)
2271  *		0 - success
2272  *		1 - package operation failed (fatal error)
2273  *		2 - non-fatal error (warning)
2274  *		3 - user selected quit (operation interrupted)
2275  *		4 - admin settings prevented operation
2276  *		5 - interaction required and -n (non-interactive) specified
2277  *		"10" will be added to indicate "immediate reboot required"
2278  *		"20" will be added to indicate "reboot after install required"
2279  */
2280 
2281 static boolean_t
2282 remove_packages_in_nonglobal_zone(char **a_pkgList, int a_nodelete,
2283 	int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir)
2284 {
2285 static	char		*zoneAdminFile = (char *)NULL;
2286 
2287 	int		n;
2288 	int		i;
2289 
2290 	/* entry assertions */
2291 
2292 	assert(a_pkgList != (char **)NULL);
2293 	assert(a_longestPkg > 0);
2294 	assert(a_pkgdir != (char *)NULL);
2295 	assert(*a_pkgdir != '\0');
2296 
2297 	/* entry debugging info */
2298 
2299 	echoDebug(DBG_PKGREMPKGSNGZ_ENTRY);
2300 	echoDebug(DBG_PKGREMPKGSNGZ_ARGS, a_nodelete, a_longestPkg,
2301 		a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
2302 
2303 	/* check all package */
2304 
2305 	if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
2306 		quit(1);
2307 	}
2308 
2309 	/* create temporary directory for use by zone operations */
2310 
2311 	create_zone_tempdir(&zoneTempDir, tmpdir);
2312 
2313 	/* create hands off settings admin file for use in a non-global zone */
2314 
2315 	create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
2316 
2317 	/*
2318 	 * all of the packages (as listed in the package list) are
2319 	 * removed one at a time.
2320 	 */
2321 
2322 	interrupted = 0;
2323 	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
2324 		started = 0;
2325 
2326 		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
2327 			continue;
2328 		}
2329 
2330 		interrupted = 0;
2331 
2332 		/*
2333 		 * pkgrm invoked from within a non-global zone: remove
2334 		 * the package from the current zone only - no non-global
2335 		 * zones are possible.
2336 		 */
2337 
2338 		n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
2339 			admnfile, (char *)NULL, (zoneList_t)NULL);
2340 
2341 		/* set success/fail condition variables */
2342 
2343 		ckreturn(n);
2344 
2345 		npkgs--;
2346 	}
2347 
2348 	/*
2349 	 * all packages in the package list have been removed.
2350 	 * Continue with removal if:
2351 	 * -- immediate reboot is NOT required
2352 	 * -- there are more packages to remove
2353 	 * else return do NOT continue.
2354 	 */
2355 
2356 	if ((ireboot == 0) && (a_repeat != 0)) {
2357 		return (B_TRUE);
2358 	}
2359 
2360 	/* return 'dont continue' */
2361 
2362 	return (B_FALSE);
2363 }
2364 
2365 /*
2366  * Name:	remove_packages_in_global_no_zones
2367  * Description:	Remove packages from the global zone only when run in the
2368  *		global zone and no non-global zones are installed.
2369  * Arguments:	a_pkgList - pointer to array of strings, each string specifying
2370  *			the name of one package to be removed.
2371  *		a_nodelete: should the files and scripts remain installed?
2372  *			- if != 0 pass -F flag to pkgremove - suppress
2373  *			the removal of any files and any class action scripts
2374  *			and suppress the running of any class action scripts.
2375  *			The package files remain but the package looks like it
2376  *			is not installed. This is mainly for use by upgrade.
2377  *			- if == 0 do not pass -F flag to pkgremove - all
2378  *			files and class action scripts are removed, and any
2379  *			appropriate class action scripts are run.
2380  *		a_longestPkg - length of the longest package "name" (for
2381  *			output format alignment)
2382  *		a_repeat - are there more packages avialable in "optind"
2383  *			- B_TRUE - process packages from optind
2384  *			- B_FALSE - do not process packages from optind
2385  *		a_altBinDir - pointer to string representing location of the
2386  *			pkgremove executable to run. If not NULL, then pass
2387  *			the path specified to the -b option to pkgremove.
2388  * Returns:	int	(see ckreturn() function for details)
2389  *		0 - success
2390  *		1 - package operation failed (fatal error)
2391  *		2 - non-fatal error (warning)
2392  *		3 - user selected quit (operation interrupted)
2393  *		4 - admin settings prevented operation
2394  *		5 - interaction required and -n (non-interactive) specified
2395  *		"10" will be added to indicate "immediate reboot required"
2396  *		"20" will be added to indicate "reboot after install required"
2397  */
2398 
2399 static boolean_t
2400 remove_packages_in_global_no_zones(char **a_pkgList, int a_nodelete,
2401 	int a_longestPkg, int a_repeat, char *a_altBinDir)
2402 {
2403 	int	n;
2404 	int	i;
2405 
2406 	/* entry assertions */
2407 
2408 	assert(a_pkgList != (char **)NULL);
2409 	assert(a_longestPkg > 0);
2410 
2411 	/* entry debugging info */
2412 
2413 	echoDebug(DBG_PKGREMPKGSGZNNGZ_ENTRY);
2414 	echoDebug(DBG_PKGREMPKGSGZNNGZ_ARGS, a_nodelete, a_longestPkg,
2415 		a_repeat, PSTR(a_altBinDir));
2416 
2417 	/*
2418 	 * all of the packages (as listed in the package list) are
2419 	 * removed one at a time.
2420 	 */
2421 
2422 	interrupted = 0;
2423 	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
2424 		started = 0;
2425 
2426 		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
2427 			continue;
2428 		}
2429 
2430 		interrupted = 0;
2431 
2432 		/*
2433 		 * pkgrm invoked from within the global zone and there are
2434 		 * NO non-global zones configured:
2435 		 * Remove the package from the global zone only.
2436 		 */
2437 
2438 		n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
2439 				admnfile, (char *)NULL, (zoneList_t)NULL);
2440 
2441 		/* set success/fail condition variables */
2442 
2443 		ckreturn(n);
2444 
2445 		npkgs--;
2446 	}
2447 
2448 	/*
2449 	 * all packages in the package list have been removed.
2450 	 * Continue with removal if:
2451 	 * -- immediate reboot is NOT required
2452 	 * -- there are more packages to remove
2453 	 * else return do NOT continue.
2454 	 */
2455 
2456 	if ((ireboot == 0) && (a_repeat != 0)) {
2457 		return (B_TRUE);
2458 	}
2459 
2460 	/* return 'dont continue' */
2461 
2462 	return (B_FALSE);
2463 }
2464 
2465 /*
2466  * Name:	remove_packages_from_spool_directory
2467  * Description:	Remove packages from a spool directory only.
2468  * Arguments:	a_pkgList - pointer to array of strings, each string specifying
2469  *			the name of one package to be removed.
2470  *		a_nodelete: should the files and scripts remain installed?
2471  *			- if != 0 pass -F flag to pkgremove - suppress
2472  *			the removal of any files and any class action scripts
2473  *			and suppress the running of any class action scripts.
2474  *			The package files remain but the package looks like it
2475  *			is not installed. This is mainly for use by upgrade.
2476  *			- if == 0 do not pass -F flag to pkgremove - all
2477  *			files and class action scripts are removed, and any
2478  *			appropriate class action scripts are run.
2479  *		a_longestPkg - length of the longest package "name" (for
2480  *			output format alignment)
2481  *		a_repeat - are there more packages avialable in "optind"
2482  *			- B_TRUE - process packages from optind
2483  *			- B_FALSE - do not process packages from optind
2484  *		a_altBinDir - pointer to string representing location of the
2485  *			pkgremove executable to run. If not NULL, then pass
2486  *			the path specified to the -b option to pkgremove.
2487  * Returns:	int	(see ckreturn() function for details)
2488  *		0 - success
2489  *		1 - package operation failed (fatal error)
2490  *		2 - non-fatal error (warning)
2491  *		3 - user selected quit (operation interrupted)
2492  *		4 - admin settings prevented operation
2493  *		5 - interaction required and -n (non-interactive) specified
2494  *		"10" will be added to indicate "immediate reboot required"
2495  *		"20" will be added to indicate "reboot after install required"
2496  */
2497 
2498 static boolean_t
2499 remove_packages_from_spool_directory(char **a_pkgList, int a_nodelete,
2500 	int a_longestPkg, int a_repeat, char *a_altBinDir)
2501 {
2502 	int	n;
2503 	int	i;
2504 
2505 	/* entry assertions */
2506 
2507 	assert(a_pkgList != (char **)NULL);
2508 	assert(a_longestPkg > 0);
2509 
2510 	/*
2511 	 * all of the packages (as listed in the package list) are
2512 	 * removed one at a time.
2513 	 */
2514 
2515 	interrupted = 0;
2516 	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
2517 		started = 0;
2518 
2519 		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
2520 			continue;
2521 		}
2522 
2523 		interrupted = 0;
2524 
2525 		/*
2526 		 * pkgrm invoked from any type of zone BUT the target
2527 		 * to be removed is a local spool directory: remove the
2528 		 * packages from the spool directory only.
2529 		 */
2530 
2531 		n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
2532 			admnfile, (char *)NULL, (zoneList_t)NULL);
2533 
2534 		/* set success/fail condition variables */
2535 
2536 		ckreturn(n);
2537 
2538 		npkgs--;
2539 	}
2540 
2541 	/*
2542 	 * all packages in the package list have been removed.
2543 	 * Continue with removal if:
2544 	 * -- immediate reboot is NOT required
2545 	 * -- there are more packages to remove
2546 	 * else return do NOT continue.
2547 	 */
2548 
2549 	if ((ireboot == 0) && (a_repeat != 0)) {
2550 		return (B_TRUE);
2551 	}
2552 
2553 	/* return 'dont continue' */
2554 
2555 	return (B_FALSE);
2556 }
2557 
2558 /*
2559  * Name:	remove_packages
2560  * Description:	Remove packages from the global zone, and optionally from one
2561  *		or more non-global zones, or from a specified spool directory.
2562  * Arguments:	a_pkgList - pointer to array of strings, each string specifying
2563  *			the name of one package to be removed.
2564  *		a_nodelete: should the files and scripts remain installed?
2565  *			- if != 0 pass -F flag to pkgremove - suppress
2566  *			the removal of any files and any class action scripts
2567  *			and suppress the running of any class action scripts.
2568  *			The package files remain but the package looks like it
2569  *			is not installed. This is mainly for use by upgrade.
2570  *			- if == 0 do not pass -F flag to pkgremove - all
2571  *			files and class action scripts are removed, and any
2572  *			appropriate class action scripts are run.
2573  *		a_longestPkg - length of the longest package "name" (for
2574  *			output format alignment)
2575  *		a_repeat - are there more packages avialable in "optind"
2576  *			- B_TRUE - process packages from optind
2577  *			- B_FALSE - do not process packages from optind
2578  *		a_altBinDir - pointer to string representing location of the
2579  *			pkgremove executable to run. If not NULL, then pass
2580  *			the path specified to the -b option to pkgremove.
2581  *		a_pkgdir - pointer to string representing the directory
2582  *			where the packages to be removed are located.
2583  *		a_spoolDir - pointer to string specifying spool directory
2584  *			to remove packages from. If != NULL then all zones
2585  *			processing is bypassed and the packages are removed
2586  *			from the specified spool directory only.
2587  *		a_noZones - if non-global zones are configured, should the
2588  *			packages be removed from the non-global zones?
2589  *			- B_TRUE - do NOT remove packages from non-global zones
2590  *			- B_FALSE - remove packages from non-global zones
2591  * Returns:	int	(see ckreturn() function for details)
2592  *		0 - success
2593  *		1 - package operation failed (fatal error)
2594  *		2 - non-fatal error (warning)
2595  *		3 - user selected quit (operation interrupted)
2596  *		4 - admin settings prevented operation
2597  *		5 - interaction required and -n (non-interactive) specified
2598  *		"10" will be added to indicate "immediate reboot required"
2599  *		"20" will be added to indicate "reboot after install required"
2600  */
2601 
2602 static boolean_t
2603 remove_packages(char **a_pkgList, int a_nodelete, int a_longestPkg,
2604 	int a_repeat, char *a_altBinDir, char *a_pkgdir, char *a_spoolDir,
2605 	boolean_t a_noZones)
2606 {
2607 	zoneList_t	zlst;
2608 	boolean_t	b;
2609 
2610 	/* entry assertions */
2611 
2612 	assert(a_pkgList != (char **)NULL);
2613 
2614 	echoDebug(DBG_REMOVEPKGS_ENTRY);
2615 	echoDebug(DBG_REMOVEPKGS_ARGS, npkgs, a_nodelete, a_longestPkg,
2616 		a_repeat, PSTR(a_pkgdir), PSTR(a_spoolDir));
2617 
2618 	/*
2619 	 * if removing from spool directory, bypass all zones checks
2620 	 */
2621 
2622 	if (a_spoolDir != (char *)NULL) {
2623 		/* in non-global zone */
2624 
2625 		echoDebug(DBG_REMOVE_PKGS_FROM_SPOOL, a_spoolDir);
2626 
2627 		b = remove_packages_from_spool_directory(a_pkgList, a_nodelete,
2628 			a_longestPkg, a_repeat, a_altBinDir);
2629 
2630 		return (B_FALSE);
2631 	}
2632 
2633 	/* exit if not root */
2634 
2635 	if (getuid()) {
2636 		progerr(ERR_NOT_ROOT, get_prog_name());
2637 		exit(1);
2638 	}
2639 
2640 	/*
2641 	 * if running in the global zone AND one or more non-global
2642 	 * zones exist, add packages in a 'zones aware' manner, else
2643 	 * add packages in the standard 'non-zones aware' manner.
2644 	 */
2645 
2646 	if ((a_noZones == B_FALSE) && (z_running_in_global_zone() == B_FALSE)) {
2647 		/* in non-global zone */
2648 
2649 		echoDebug(DBG_IN_LZ);
2650 
2651 		b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
2652 		if (b != B_TRUE) {
2653 			progerr(ERR_CANNOT_LOCK_THIS_ZONE);
2654 			/* set fatal error return condition */
2655 			ckreturn(1);
2656 			return (B_FALSE);
2657 		}
2658 
2659 		b = remove_packages_in_nonglobal_zone(a_pkgList, a_nodelete,
2660 			a_longestPkg, a_repeat, a_altBinDir, a_pkgdir);
2661 
2662 		(void) z_unlock_this_zone(ZLOCKS_ALL);
2663 
2664 		return (B_FALSE);
2665 	}
2666 
2667 	/* running in the global zone */
2668 
2669 	b = z_non_global_zones_exist();
2670 	if ((a_noZones == B_FALSE) && (b == B_TRUE)) {
2671 
2672 		echoDebug(DBG_IN_GZ_WITH_LZ);
2673 
2674 		/* get a list of all non-global zones */
2675 		zlst = z_get_nonglobal_zone_list();
2676 		if (zlst == (zoneList_t)NULL) {
2677 			progerr(ERR_CANNOT_GET_ZONE_LIST);
2678 			quit(1);
2679 		}
2680 
2681 		/* need to lock all of the zones */
2682 
2683 		quitSetZonelist(zlst);
2684 		b = z_lock_zones(zlst, ZLOCKS_PKG_ADMIN);
2685 		if (b == B_FALSE) {
2686 			z_free_zone_list(zlst);
2687 			progerr(ERR_CANNOT_LOCK_ZONES);
2688 			/* set fatal error return condition */
2689 			ckreturn(1);
2690 			return (B_FALSE);
2691 		}
2692 
2693 		/* add packages to all zones */
2694 
2695 		b = remove_packages_in_global_with_zones(a_pkgList, a_nodelete,
2696 			a_longestPkg, a_repeat, a_altBinDir, a_pkgdir, zlst);
2697 
2698 		/* unlock all zones */
2699 
2700 		(void) z_unlock_zones(zlst, ZLOCKS_ALL);
2701 		quitSetZonelist((zoneList_t)NULL);
2702 
2703 		/* free list of all non-global zones */
2704 
2705 		z_free_zone_list(zlst);
2706 
2707 		return (B_FALSE);
2708 	}
2709 
2710 	/* in global zone no non-global zones */
2711 
2712 	echoDebug(DBG_IN_GZ_NO_LZ);
2713 
2714 	b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
2715 	if (b != B_TRUE) {
2716 		progerr(ERR_CANNOT_LOCK_THIS_ZONE);
2717 		/* set fatal error return condition */
2718 		ckreturn(1);
2719 		return (B_FALSE);
2720 	}
2721 
2722 	b = remove_packages_in_global_no_zones(a_pkgList, a_nodelete,
2723 			a_longestPkg, a_repeat, a_altBinDir);
2724 
2725 	(void) z_unlock_this_zone(ZLOCKS_ALL);
2726 
2727 	return (B_FALSE);
2728 }
2729 
2730 /*
2731  * Name:		path_valid
2732  * Description:	Checks a string for being a valid path
2733  *
2734  * Arguments:	path - path to validate
2735  *
2736  * Returns :	B_TRUE - success, B_FALSE otherwise.
2737  *		B_FALSE means path was null, too long (>PATH_MAX),
2738  *		or too short (<1)
2739  */
2740 static boolean_t
2741 path_valid(char *path)
2742 {
2743 	if (path == NULL) {
2744 		return (B_FALSE);
2745 	} else if (strlen(path) > PATH_MAX) {
2746 		return (B_FALSE);
2747 	} else if (strlen(path) >= 1) {
2748 		return (B_TRUE);
2749 	} else {
2750 		/* path < 1 */
2751 		return (B_FALSE);
2752 	}
2753 }
2754 
2755 /*
2756  */
2757 
2758 static boolean_t
2759 check_packages(char **a_pkgList, char *a_packageDir)
2760 {
2761 	int	savenpkgs = npkgs;
2762 	int	i;
2763 	CAF_T	flags = 0;
2764 
2765 	/* set flags for applicability check */
2766 
2767 	if (z_running_in_global_zone() == B_TRUE) {
2768 		flags |= CAF_IN_GLOBAL_ZONE;
2769 	}
2770 
2771 	/*
2772 	 * for each package to remove, verify that the package is installed
2773 	 * and is removable.
2774 	 */
2775 
2776 	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
2777 		/* check package applicability */
2778 		if (check_applicability(a_packageDir, pkginst, get_inst_root(),
2779 			flags) == B_FALSE) {
2780 			progerr(ERR_PKG_NOT_REMOVABLE, pkginst);
2781 			npkgs = savenpkgs;
2782 			return (B_FALSE);
2783 		}
2784 		npkgs--;
2785 	}
2786 
2787 	npkgs = savenpkgs;
2788 	return (B_TRUE);
2789 }
2790 
2791 /*
2792  * - is this package removable from this zone?
2793  * - does the scope of remove conflict with existing installation
2794  */
2795 
2796 static boolean_t
2797 check_applicability(char *a_packageDir, char *a_pkgInst,
2798 	char *a_rootPath, CAF_T a_flags)
2799 {
2800 	FILE		*pkginfoFP;
2801 	boolean_t	all_zones;	/* pkg is "all zones" only */
2802 	char		pkginfoPath[PATH_MAX];
2803 	char		pkgpath[PATH_MAX];
2804 	int		len;
2805 
2806 	/* entry assertions */
2807 
2808 	assert(a_packageDir != (char *)NULL);
2809 	assert(*a_packageDir != '\0');
2810 	assert(a_pkgInst != (char *)NULL);
2811 	assert(*a_pkgInst != '\0');
2812 
2813 	/* normalize root path */
2814 
2815 	if (a_rootPath == (char *)NULL) {
2816 		a_rootPath = "";
2817 	}
2818 
2819 	/*
2820 	 * determine if this package is currently installed
2821 	 * if not installed return success - operation will fail
2822 	 * when the removal is attempted
2823 	 */
2824 
2825 	if (pkginfoIsPkgInstalled((struct pkginfo **)NULL, a_pkgInst) !=
2826 		B_TRUE) {
2827 		return (B_TRUE);
2828 	}
2829 
2830 	/*
2831 	 * calculate paths to various objects
2832 	 */
2833 
2834 	len = snprintf(pkgpath, sizeof (pkgpath), "%s/%s", a_packageDir,
2835 			a_pkgInst);
2836 	if (len > sizeof (pkgpath)) {
2837 		progerr(ERR_CREATE_PATH_2, a_packageDir, a_pkgInst);
2838 		return (B_FALSE);
2839 	}
2840 
2841 	/* if not installed then just return */
2842 
2843 	if (isdir(pkgpath) != 0) {
2844 		progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
2845 		return (B_TRUE);
2846 	}
2847 
2848 	len = snprintf(pkginfoPath, sizeof (pkginfoPath),
2849 			"%s/pkginfo", pkgpath);
2850 	if (len > sizeof (pkgpath)) {
2851 		progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
2852 		return (B_FALSE);
2853 	}
2854 
2855 	/*
2856 	 * gather information from this packages pkginfo file
2857 	 */
2858 
2859 	pkginfoFP = fopen(pkginfoPath, "r");
2860 
2861 	if (pkginfoFP == (FILE *)NULL) {
2862 		progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
2863 							strerror(errno));
2864 		return (B_FALSE);
2865 	}
2866 
2867 	/* determine "ALLZONES" setting for this package */
2868 
2869 	all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
2870 			"true", B_FALSE);
2871 
2872 	/* close pkginfo file */
2873 
2874 	(void) fclose(pkginfoFP);
2875 
2876 	/* gather information from the global zone only file */
2877 
2878 	/*
2879 	 * verify package applicability based on information gathered;
2880 	 * the package IS currently installed....
2881 	 */
2882 
2883 	/* pkg ALLZONES=true & not running in global zone */
2884 
2885 	if ((all_zones == B_TRUE) && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
2886 		progerr(ERR_ALLZONES_AND_IN_LZ_PKGRM, a_pkgInst);
2887 		return (B_FALSE);
2888 	}
2889 
2890 	return (B_TRUE);
2891 }
2892 
2893 /*
2894  * Name:	shall_we_continue
2895  * Description: Called from within a loop that is installing packages,
2896  *		this function examines various global variables and decides
2897  *		whether or not to ask an appropriate question, and wait for
2898  *		and appropriate reply.
2899  * Arguments:	<<global variables>>
2900  * Returns:	B_TRUE - continue processing with next package
2901  *		B_FALSE - do not continue processing with next package
2902  */
2903 
2904 static boolean_t
2905 shall_we_continue(char *a_pkgInst, int a_npkgs)
2906 {
2907 	char	ans[MAX_INPUT];
2908 	int	n;
2909 
2910 	/* return FALSE if immediate reboot required */
2911 
2912 	if (ireboot) {
2913 		ptext(stderr, MSG_SUSPEND_RM, a_pkgInst);
2914 		return (B_FALSE);
2915 	}
2916 
2917 	/* return TRUE if not interrupted */
2918 
2919 	if (!interrupted) {
2920 		return (B_TRUE);
2921 	}
2922 
2923 	/* output appropriate interrupt message */
2924 
2925 	echo(a_npkgs == 1 ? MSG_1MORETODO : MSG_MORETODO, a_npkgs);
2926 
2927 	/* if running with no interaction (-n) do not ask question */
2928 
2929 	if (nointeract) {
2930 		quit(0);
2931 		/* NOTREACHED */
2932 	}
2933 
2934 	/* interaction possible: ask question */
2935 
2936 	n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_RM);
2937 	if (n != 0) {
2938 		quit(n);
2939 		/* NOTREACHED */
2940 	}
2941 
2942 	if (strchr("yY", *ans) == NULL) {
2943 		quit(0);
2944 		/* NOTREACHED */
2945 	}
2946 	return (B_TRUE);
2947 }
2948 
2949 /*
2950  * Name:	create_zone_adminfile
2951  * Description: Given a zone temporary directory and optionally an existing
2952  *		administration file, generate an administration file that
2953  *		can be used to perform "non-interactive" operations in a
2954  *		non-global zone.
2955  * Arguments:	r_zoneAdminFile - pointer to handle that will contain a
2956  *			string representing the path to the temporary
2957  *			administration file created - this must be NULL
2958  *			before the first call to this function - on
2959  *			subsequent calls if the pointer is NOT null then
2960  *			the existing string will NOT be overwritten.
2961  *		a_zoneTempDir - pointer to string representing the path
2962  *			to the zone temporary directory to create the
2963  *			temporary administration file in
2964  *		a_admnfile - pointer to string representing the path to
2965  *			an existing "user" administration file - the
2966  *			administration file created will contain the
2967  *			settings contained in this file, modified as
2968  *			appropriate to supress any interaction;
2969  *			If this is == NULL then the administration file
2970  *			created will not contain any extra settings
2971  * Returns:	void
2972  * NOTE:	Any string returned is placed in new storage for the
2973  *		calling method. The caller must use 'free' to dispose
2974  *		of the storage once the string is no longer needed.
2975  * NOTE:	On any error this function will call 'quit(1)'
2976  */
2977 
2978 static void
2979 create_zone_adminfile(char **r_zoneAdminFile, char *a_zoneTempDir,
2980 	char *a_admnfile)
2981 {
2982 	boolean_t	b;
2983 
2984 	/* entry assertions */
2985 
2986 	assert(r_zoneAdminFile != (char **)NULL);
2987 	assert(a_zoneTempDir != (char *)NULL);
2988 	assert(*a_zoneTempDir != '\0');
2989 
2990 	/* entry debugging info */
2991 
2992 	echoDebug(DBG_CREATE_ZONE_ADMINFILE, a_zoneTempDir, PSTR(a_admnfile));
2993 
2994 	/* if temporary name already exists, do not overwrite */
2995 
2996 	if (*r_zoneAdminFile != (char *)NULL) {
2997 		return;
2998 	}
2999 
3000 	/* create temporary name */
3001 
3002 	*r_zoneAdminFile = tempnam(a_zoneTempDir, "zadmn");
3003 	b = z_create_zone_admin_file(*r_zoneAdminFile, a_admnfile);
3004 	if (b == B_FALSE) {
3005 		progerr(ERR_CREATE_TMPADMIN, *r_zoneAdminFile,
3006 			strerror(errno));
3007 		quit(1);
3008 		/* NOTREACHED */
3009 	}
3010 
3011 	echoDebug(DBG_CREATED_ZONE_ADMINFILE, *r_zoneAdminFile);
3012 }
3013 
3014 /*
3015  * Name:	create_zone_tempdir
3016  * Description: Given a system temporary directory, create a "zone" specific
3017  *		temporary directory and return the path to the directory
3018  *		created.
3019  * Arguments:	r_zoneTempDir - pointer to handle that will contain a
3020  *			string representing the path to the temporary
3021  *			directory created - this must be NULL before the
3022  *			first call to this function - on subsequent calls
3023  *			if the pointer is NOT null then the existing string
3024  *			will NOT be overwritten.
3025  *		a_zoneTempDir - pointer to string representing the path
3026  *			to the system temporary directory to create the
3027  *			temporary zone directory in
3028  * Returns:	void
3029  * NOTE:	Any string returned is placed in new storage for the
3030  *		calling method. The caller must use 'free' to dispose
3031  *		of the storage once the string is no longer needed.
3032  * NOTE:	On any error this function will call 'quit(1)'
3033  * NOTE:	This function calls "quitSetZoneTmpdir" on success to
3034  *		register the directory created with quit() so that the
3035  *		directory will be automatically deleted on exit.
3036  */
3037 
3038 static void
3039 create_zone_tempdir(char **r_zoneTempDir, char *a_tmpdir)
3040 {
3041 	boolean_t	b;
3042 
3043 	/* entry assertions */
3044 
3045 	assert(r_zoneTempDir != (char **)NULL);
3046 	assert(a_tmpdir != (char *)NULL);
3047 	assert(*a_tmpdir != '\0');
3048 
3049 	/* entry debugging info */
3050 
3051 	echoDebug(DBG_CREATE_ZONE_TEMPDIR, a_tmpdir);
3052 
3053 	/* if temporary directory already exists, do not overwrite */
3054 
3055 	if (*r_zoneTempDir != (char *)NULL) {
3056 		return;
3057 	}
3058 
3059 	/* create temporary directory */
3060 
3061 	b = setup_temporary_directory(r_zoneTempDir, a_tmpdir, "ztemp");
3062 	if (b == B_FALSE) {
3063 		progerr(ERR_ZONETEMPDIR, a_tmpdir, strerror(errno));
3064 		quit(1);
3065 		/* NOTREACHED */
3066 	}
3067 
3068 	/* register with quit() to directory is removed on exit */
3069 
3070 	quitSetZoneTmpdir(*r_zoneTempDir);
3071 
3072 	/* exit debugging info */
3073 
3074 	echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
3075 }
3076