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