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