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