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