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