xref: /illumos-gate/usr/src/cmd/svr4pkg/pkgcond/main.c (revision 5c51f124)
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 
28 /*
29  * Program:	pkgcond
30  *
31  * Function:	Implements the package command suite public utility pkgcond(1M)
32  *
33  * Usage:	pkgcond [-nv] [-O debug] condition [ argument ]
34  *
35  *		command options:
36  *			-n - negate results of condition test
37  *			-v - verbose output of condition testing
38  *
39  *		<condition> may be any one of:
40  *			can_add_driver [path]
41  *			can_remove_driver [path]
42  *			can_update_driver [path]
43  *			is_alternative_root [path]
44  *			is_boot_environment [path]
45  *			is_diskless_client [path]
46  *			is_global_zone [path]
47  *			is_mounted_miniroot [path]
48  *			is_netinstall_image [path]
49  *			is_nonglobal_zone [path]
50  *			is_path_writable path
51  *			is_running_system [path]
52  *			is_sparse_root_nonglobal_zone [path]
53  *			is_what [path]
54  *			is_whole_root_nonglobal_zone [path]
55  *
56  *		<option(s)> are specific to the condition used
57  *
58  * Input:	depends on command
59  *
60  * Output:	depends on command
61  *
62  * Exit status:	If the -n option is not specified:
63  *		== 0 - the specified condition is true (or exists).
64  *		== 1 - the specified condition is false (or does not exist).
65  *		== 2 - command line usage errors (including bad keywords)
66  *		== 3 - command failed to perform the test due to a fatal error
67  *
68  *		If the -n option is specified:
69  *		== 0 - the specified condition is false (or does not exist).
70  *		== 1 - the specified condition is true (or exists).
71  *		== 2 - command line usage errors (including bad keywords)
72  *		== 3 - command failed to perform the test due to a fatal error
73  */
74 
75 #include <stdio.h>
76 #include <sys/mnttab.h>
77 #include <sys/mntent.h>
78 #include <stdarg.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <strings.h>
82 #include <fcntl.h>
83 #include <ctype.h>
84 #include <sys/types.h>
85 #include <sys/stat.h>
86 #include <unistd.h>
87 #include <locale.h>
88 #include <errno.h>
89 #include <sys/param.h>
90 #include <assert.h>
91 
92 #include <instzones_api.h>
93 #include <pkglib.h>
94 #include <install.h>
95 #include <libinst.h>
96 #include <libadm.h>
97 #include <messages.h>
98 #include "pkgcond.h"
99 #include "pkgcond_msgs.h"
100 
101 /* Should be defined by cc -D */
102 
103 #if	!defined(TEXT_DOMAIN)
104 #define	TEXT_DOMAIN "SYS_TEST"
105 #endif
106 
107 /* commands to execute */
108 
109 #define	LS_CMD		"/usr/bin/ls"
110 
111 /*
112  * type definition and "types" for testPath()
113  */
114 
115 typedef enum {
116 	TEST_EXISTS = 0x01,
117 	TEST_NOT_EXISTS = 0x02,
118 	TEST_IS_DIRECTORY = 0x04,
119 	TEST_IS_FILE = 0x08,
120 	TEST_NOT_DIRECTORY = 0x10,
121 	TEST_NOT_FILE = 0x20,
122 	TEST_IS_SYMBOLIC_LINK = 0x40,
123 	TEST_NOT_SYMBOLIC_LINK = 0x80,
124 	TEST_GLOBAL_TOKEN_IN_FILE = 0x100
125 } TEST_TYPES;
126 
127 /* holds file system info */
128 
129 struct fsi_t {
130 	char	*fsi_mntOptions;
131 	char	*fsi_fsType;
132 	char	*fsi_mntPoint;
133 };
134 typedef struct fsi_t	FSI_T;
135 
136 /* holds parsed global data */
137 
138 struct globalData_t {
139 		/* sparse root (are any file systems mounted read-only)? */
140 	boolean_t gd_srFsMountedRO;
141 		/* initial install: PKG_INIT_INSTALL=true */
142 	boolean_t gd_initialInstall;
143 		/* global zone install: SUNW_PKG_INSTALL_ZONENAME=global */
144 	boolean_t gd_globalZoneInstall;
145 		/* non-global zone install: SUNW_PKG_INSTALL_ZONENAME!=global */
146 	boolean_t gd_nonglobalZoneInstall;
147 		/* non-global zone is in a mounted state */
148 	boolean_t inMountedState;
149 		/* sorted list of all mounted file systems */
150 	FSI_T	*gd_fileSystemConfig;
151 		/* number of mounted file systems in list */
152 	long	gd_fileSystemConfigLen;
153 		/* current zone name */
154 	char	*gd_zoneName;
155 		/* version of target: PATCH_CLIENT_VERSION */
156 	char	*gd_patchClientVersion;
157 		/* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneName */
158 	char	*gd_parentZoneName;
159 		/* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneType */
160 	char	*gd_parentZoneType;
161 		/* root path to target: PKG_INSTALL_ROOT */
162 	char	*gd_installRoot;
163 		/* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneName */
164 	char	*gd_currentZoneName;
165 		/* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneType */
166 	char	*gd_currentZoneType;
167 		/* list of inherited file systems */
168 	char	**gd_inheritedFileSystems;
169 		/* path provided on command line */
170 	char	*gd_cmdline_path;
171 };
172 typedef struct globalData_t	GLOBALDATA_T;
173 
174 /* holds subcommands and their definitions */
175 
176 struct cmd_t {
177 	char		*c_name;
178 	char		*c_args;
179 	int		(*c_func)(int argc, char **argv, GLOBALDATA_T *a_gdt);
180 };
181 typedef struct cmd_t	CMD_T;
182 
183 /* Command function prototypes */
184 
185 static int		cmd_can_add_driver(int argc, char **argv,
186 				GLOBALDATA_T *a_gdt);
187 static int		cmd_can_remove_driver(int argc, char **argv,
188 				GLOBALDATA_T *a_gdt);
189 static int		cmd_can_update_driver(int argc, char **argv,
190 				GLOBALDATA_T *a_gdt);
191 static int		cmd_is_alternative_root(int argc, char **argv,
192 				GLOBALDATA_T *a_gdt);
193 static int		cmd_is_boot_environment(int argc, char **argv,
194 				GLOBALDATA_T *a_gdt);
195 static int		cmd_is_diskless_client(int argc, char **argv,
196 				GLOBALDATA_T *a_gdt);
197 static int		cmd_is_global_zone(int argc, char **argv,
198 				GLOBALDATA_T *a_gdt);
199 static int		cmd_is_mounted_miniroot(int argc, char **argv,
200 				GLOBALDATA_T *a_gdt);
201 static int		cmd_is_netinstall_image(int argc, char **argv,
202 				GLOBALDATA_T *a_gdt);
203 static int		cmd_is_nonglobal_zone(int argc, char **argv,
204 				GLOBALDATA_T *a_gdt);
205 static int		cmd_is_path_writable(int argc, char **argv,
206 				GLOBALDATA_T *a_gdt);
207 static int		cmd_is_running_system(int argc, char **argv,
208 				GLOBALDATA_T *a_gdt);
209 static int		cmd_is_sparse_root_ng_zone(int argc, char **argv,
210 				GLOBALDATA_T *a_gdt);
211 static int		cmd_is_what(int argc, char **argv,
212 				GLOBALDATA_T *a_gdt);
213 static int		cmd_is_whole_root_ng_zone(int argc, char **argv,
214 				GLOBALDATA_T *a_gdt);
215 
216 /* Utility function Prototypes */
217 
218 static boolean_t	getNegateResults(void);
219 static boolean_t	recursionCheck(int *r_recursion, char *a_function);
220 static boolean_t	checkForReadOnlyMount(GLOBALDATA_T *a_gdt,
221     char *a_mntPoint, char *a_fsType, char *a_mntOptions);
222 static int		adjustResults(int a_result);
223 static int		calculateFileSystemConfig(GLOBALDATA_T *a_gdt);
224 static int		getRootPath(char **r_rootPath);
225 static int		getZoneName(char **r_zoneName);
226 static int		mountOptionPresent(char *a_mntOptions, char *a_opt);
227 static int		parseGlobalData(char *a_envVar, GLOBALDATA_T **a_gdt);
228 static int		resolvePath(char **r_path);
229 static int		setRootPath(char *a_path, char *a_envVar,
230     boolean_t a_mustExist);
231 static int		testPath(TEST_TYPES a_tt, char *format, ...);
232 static int		usage(char *a_format, ...);
233 static int		findToken(char *path, char *token);
234 static char		*getMountOption(char **p);
235 static void		dumpGlobalData(GLOBALDATA_T *a_gdt);
236 static void		removeLeadingWhitespace(char **a_str);
237 static void		setNegateResults(boolean_t setting);
238 static void		setVerbose(boolean_t);
239 static void		sortedInsert(FSI_T **r_list, long *a_listSize,
240     char *a_mntPoint, char *a_fsType, char *a_mntOptions);
241 static void		setCmdLinePath(char **a_path, char **args,
242     int num_args);
243 
244 /* local static data */
245 
246 static boolean_t	_negateResults = B_FALSE;
247 static char		*_rootPath = "/";
248 
249 /* define subcommand data structure */
250 
251 static CMD_T cmds[] = {
252 	{ "can_add_driver",		" [path]",
253 		cmd_can_add_driver },
254 	{ "can_remove_driver",		" [path]",
255 		cmd_can_remove_driver },
256 	{ "can_update_driver",		" [path]",
257 		cmd_can_update_driver },
258 	{ "is_alternative_root",	" [path]",
259 		cmd_is_alternative_root },
260 	{ "is_boot_environment",	" [path]",
261 		cmd_is_boot_environment },
262 	{ "is_diskless_client",		" [path]",
263 		cmd_is_diskless_client },
264 	{ "is_global_zone",		" [path]",
265 		cmd_is_global_zone },
266 	{ "is_mounted_miniroot",	" [path]",
267 		cmd_is_mounted_miniroot },
268 	{ "is_netinstall_image",	" [path]",
269 		cmd_is_netinstall_image },
270 	{ "is_nonglobal_zone",		" [path]",
271 		cmd_is_nonglobal_zone },
272 	{ "is_path_writable",		" path",
273 		cmd_is_path_writable },
274 	{ "is_running_system",		" [path]",
275 		cmd_is_running_system },
276 	{ "is_sparse_root_nonglobal_zone", " [path]",
277 		cmd_is_sparse_root_ng_zone },
278 	{ "is_what", " [path]",
279 		cmd_is_what },
280 	{ "is_whole_root_nonglobal_zone", " [path]",
281 		cmd_is_whole_root_ng_zone },
282 	/* last one must be all NULLs */
283 	{ NULL, NULL }
284 };
285 
286 /*
287  * *****************************************************************************
288  * main
289  * *****************************************************************************
290  */
291 
292 /*
293  * Name:	main
294  * Description:	main processing loop for pkgcond *
295  * Return:	0 - condition is satisfied (true)
296  *		1 - condition is not satisfied (false)
297  *		2 - command line usage errors
298  *		3 - failure to determine condition
299  */
300 
301 int
302 main(int argc, char **argv)
303 {
304 	GLOBALDATA_T	*gdt = NULL;
305 	char		**newargv;
306 	char		*p;
307 	int		cur_cmd;
308 	int		i;
309 	int		newargc;
310 
311 	/* make standard output non-buffered */
312 
313 	setbuf(stdout, NULL);
314 
315 	/* set the default text domain for messaging */
316 
317 	(void) setlocale(LC_ALL, "");
318 	(void) textdomain(TEXT_DOMAIN);
319 
320 	/* remember command name */
321 
322 	set_prog_name(argv[0]);
323 
324 	/* tell spmi zones interface how to access package output functions */
325 
326 	z_set_output_functions(echo, echoDebug, progerr);
327 
328 	/* set verbose mode if appropriate environment variable is set */
329 
330 	if (getenv(ENV_VAR_VERBOSE)) {
331 		/* same as -v */
332 		setVerbose(B_TRUE);
333 	}
334 
335 	/* set debug mode if appropriate environment variable is set */
336 
337 	if (getenv(ENV_VAR_DEBUG)) {
338 		/* same as -O debug */
339 
340 		/* set sml tracing (sml.c) */
341 		smlSetVerbose(B_TRUE);
342 
343 		/* set log and echo (interactive) message tracing */
344 		setVerbose(B_TRUE);
345 
346 		/* enable echoDebug debugging messages */
347 		echoDebugSetFlag(B_TRUE);
348 	}
349 
350 	/* generate usage if no options or arguments specified */
351 
352 	if (argc <= 1) {
353 		(void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
354 		return (R_USAGE);
355 	}
356 
357 	/*
358 	 * process any arguments that can appear before the subcommand
359 	 */
360 
361 	while ((i = getopt(argc, argv, ":O:vn?")) != EOF) {
362 		switch (i) {
363 		/*
364 		 * Not a public interface: the -O option allows the behavior
365 		 * of the package tools to be modified. Recognized options:
366 		 * -> debug
367 		 * ---> enable debugging output
368 		 */
369 
370 		case 'O':
371 			for (p = strtok(optarg, ","); p != NULL;
372 				p = strtok(NULL, ",")) {
373 
374 				/* debug - enable all tracing */
375 
376 				if (strcmp(p, "debug") == 0) {
377 					/* set sml tracing */
378 					smlSetVerbose(B_TRUE);
379 					/* set log/echo tracing */
380 					setVerbose(B_TRUE);
381 					/* enable debugging messages */
382 					echoDebugSetFlag(B_TRUE);
383 					continue;
384 				}
385 
386 				progerr(ERR_INVALID_O_OPTION, p);
387 				return (adjustResults(R_USAGE));
388 			}
389 			break;
390 
391 		/*
392 		 * Public interface: enable verbose (debug) output.
393 		 */
394 
395 		case 'v':	/* verbose mode enabled */
396 			/* set command tracing only */
397 			setVerbose(B_TRUE);
398 			break;
399 
400 		/*
401 		 * Public interface: negate output results.
402 		 */
403 
404 		case 'n':
405 			setNegateResults(B_TRUE);
406 			break;
407 
408 		/*
409 		 * unrecognized option
410 		 */
411 
412 		case '?':
413 		default:
414 			(void) usage(MSG_INVALID_OPTION_SPECIFIED, optopt);
415 			return (R_USAGE);
416 		}
417 	}
418 
419 	/*
420 	 * done processing options that can preceed subcommand
421 	 */
422 
423 	/* error if no subcommand specified */
424 
425 	if ((argc-optind) <= 0) {
426 		(void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
427 		return (R_USAGE);
428 	}
429 
430 	/* parse global data if environment variable set */
431 
432 	if (parseGlobalData(PKGCOND_GLOBAL_VARIABLE, &gdt) != R_SUCCESS) {
433 		log_msg(LOG_MSG_ERR, ERR_CANNOT_USE_GLOBAL_DATA,
434 			PKGCOND_GLOBAL_VARIABLE);
435 		return (R_ERROR);
436 	}
437 
438 	if (setRootPath(gdt->gd_installRoot,
439 	    (strcmp(gdt->gd_installRoot, "/") == 0) ? NULL :
440 	    ENV_VAR_SET, B_TRUE) != R_SUCCESS) {
441 		log_msg(LOG_MSG_ERR, ERR_CANNOT_SET_ROOT_PATH,
442 			ENV_VAR_PKGROOT);
443 		return (R_ERROR);
444 	}
445 
446 	/* set path provided on the command line */
447 
448 	setCmdLinePath(&(gdt->gd_cmdline_path), argv, argc);
449 	echoDebug(DBG_CMDLINE_PATH,
450 	    gdt->gd_cmdline_path == NULL ? "" : gdt->gd_cmdline_path);
451 
452 	/* determine how file systems are layered in this zone */
453 
454 	if (calculateFileSystemConfig(gdt) != R_SUCCESS) {
455 		log_msg(LOG_MSG_ERR, ERR_CANNOT_CALC_FS_CONFIG);
456 		return (R_ERROR);
457 	}
458 
459 	/* dump global data read in (only if debugging) */
460 
461 	dumpGlobalData(gdt);
462 
463 	/* search for specified subcommand and execute if found */
464 
465 	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
466 		if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
467 			int	result;
468 
469 			/* make subcommand the first option */
470 
471 			newargc = argc - optind;
472 			newargv = argv + optind;
473 			opterr = optind = 1; optopt = 0;
474 
475 
476 			/* call subcommand with its own argc/argv */
477 
478 			result = cmds[cur_cmd].c_func(newargc, newargv, gdt);
479 
480 			/* process result code and exit */
481 
482 			result = adjustResults(result);
483 			log_msg(LOG_MSG_DEBUG, DBG_RESULTS, result);
484 			return (result);
485 		}
486 	}
487 
488 	/* subcommand not found - output error message and exit with error */
489 
490 	log_msg(LOG_MSG_ERR, ERR_BAD_SUB, argv[optind]);
491 	(void) usage(MSG_UNRECOGNIZED_CONDITION_SPECIFIED);
492 	return (R_USAGE);
493 }
494 
495 /*
496  * *****************************************************************************
497  * command implementation functions
498  * *****************************************************************************
499  */
500 
501 /*
502  * Name:	cmd_is_diskless_client
503  * Description:	determine if target is a diskless client
504  * Scope:	public
505  * Arguments:	argc,argv:
506  *		  - optional path to target to test
507  * Returns:	int
508  *			== 0 - success
509  *			!= 0 - failure
510  * IMPLEMENTATION:
511  *  - must not be initial installation to the install root
512  *  - must not be installation of a zone
513  *  - must not be a whole root non-global zone
514  *  - must not be a non-global zone
515  *  - must not be a mounted mini-root
516  *  - must not be a netinstall image
517  *  - must not be a boot environment
518  *  - The package "SUNWdclnt" must be installed at "/"
519  *  - The root path must not be "/"
520  *  - The path "/export/exec/Solaris_\*\/usr" must exist at "/"
521  *  - The directory "$ROOTDIR/../templates" must exist
522  */
523 
524 static int
525 cmd_is_diskless_client(int argc, char **argv, GLOBALDATA_T *a_gdt)
526 {
527 	char	*rootPath = NULL;
528 	char	cmd[MAXPATHLEN+1];
529 	int	c;
530 	int	r;
531 	int	rc;
532 static	char	*cmdName = "is_diskless_client";
533 static	int	recursion = 0;
534 
535 	/* process any command line options */
536 
537 	while ((c = getopt(argc, argv, ":")) != EOF) {
538 		switch (c) {
539 		case '\0':	/* prevent end-of-loop not reached warning */
540 			break;
541 		case '?':
542 		default:
543 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
544 			return (R_USAGE);
545 		}
546 	}
547 
548 	/* prevent recursion */
549 
550 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
551 
552 		/*
553 		 * a diskless client cannot be any of the following
554 		 */
555 
556 		/* cannot be whole root non-global zone */
557 
558 		r = cmd_is_whole_root_ng_zone(argc, argv, a_gdt);
559 
560 		/* cannot be nonglobal zone */
561 
562 		if (r != R_SUCCESS) {
563 			r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
564 		}
565 
566 		/* cannot be mounted miniroot */
567 
568 		if (r != R_SUCCESS) {
569 			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
570 		}
571 
572 		/* cannot be a netinstall image */
573 
574 		if (r != R_SUCCESS) {
575 			r = cmd_is_netinstall_image(argc, argv, a_gdt);
576 		}
577 
578 		/* cannot be a boot environment */
579 
580 		if (r != R_SUCCESS) {
581 			r = cmd_is_boot_environment(argc, argv, a_gdt);
582 		}
583 
584 		/* no need to guard against recursion any more */
585 
586 		recursion--;
587 
588 		/* return failure if any of the preceeding are true */
589 
590 		switch (r) {
591 			case R_SUCCESS:
592 				return (R_FAILURE);
593 			case R_FAILURE:
594 				break;
595 			case R_USAGE:
596 			case R_ERROR:
597 			default:
598 				return (r);
599 		}
600 	}
601 
602 	/* normalize argc/argv */
603 
604 	argc -= optind;
605 	argv += optind;
606 
607 	/* error if more than one argument */
608 
609 	if (argc > 1) {
610 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
611 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
612 		return (R_USAGE);
613 	}
614 
615 	/* process root path if first argument present */
616 
617 	if (argc == 1) {
618 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
619 			return (R_ERROR);
620 		}
621 	}
622 
623 	/* get current root path */
624 
625 	r = getRootPath(&rootPath);
626 	if (r != R_SUCCESS) {
627 		return (r);
628 	}
629 
630 	/* start of command debugging information */
631 
632 	echoDebug(DBG_ROOTPATH_IS, rootPath);
633 
634 	/* SUNWdclnt must be installed */
635 
636 	if (pkgTestInstalled("SUNWdclnt", "/") != B_TRUE) {
637 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_PKG_NOT_INSTALLED,
638 			rootPath, "SUNWdclnt", "/");
639 		return (R_FAILURE);
640 	}
641 
642 	/*   - $ROOTDIR must not be "/" */
643 
644 	if (strcmp(rootPath, "/") == 0) {
645 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ROOTPATH_BAD, rootPath, "/");
646 		return (R_FAILURE);
647 	}
648 
649 	/*   - zone name must be global */
650 
651 	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
652 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_BAD, rootPath,
653 			GLOBAL_ZONENAME);
654 		return (R_FAILURE);
655 	}
656 
657 	/*
658 	 * /export/exec/Solaris_"*"/usr must exist;
659 	 * create ls command to test:
660 	 * /usr/bin/ls /export/exec/Solaris_"*"/usr
661 	 */
662 
663 	(void) snprintf(cmd, sizeof (cmd), "%s %s >/dev/null 2>&1",
664 		LS_CMD, "/export/exec/Solaris_*/usr");
665 
666 	/* execute command */
667 
668 	rc = system(cmd);
669 
670 	/* return error if ls returns something other than "0" */
671 
672 	if (rc != 0) {
673 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_MISSING,
674 			rootPath, "/export/exec/Solaris_*/usr");
675 		return (R_FAILURE);
676 	}
677 
678 	/*
679 	 * /usr must be empty on a diskless client:
680 	 * create ls command to test:
681 	 * /usr/bin/ls -d1 $ROOTDIR/usr/\*
682 	 */
683 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s/%s >/dev/null 2>&1",
684 		LS_CMD, "-1d", rootPath, "usr/*");
685 
686 	/* execute command */
687 
688 	rc = system(cmd);
689 
690 	/* return error if ls returns "0" */
691 
692 	if (rc == 0) {
693 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_USR_IS_NOT_EMPTY,
694 			rootPath);
695 		return (R_FAILURE);
696 	}
697 
698 	/* there must be a templates directory at ${ROOTPATH}/../templates */
699 
700 	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
701 		"%s/%s", rootPath, "../templates");
702 	if (r != R_SUCCESS) {
703 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_NO_TEMPLATES_PATH,
704 			rootPath, rootPath, "../templates");
705 		return (R_FAILURE);
706 	}
707 
708 	/* must not be initial installation to the install root */
709 
710 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
711 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
712 		/* initial install: install root cannot be diskless client */
713 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_INITIAL_INSTALL, rootPath);
714 		return (R_FAILURE);
715 	}
716 
717 	/* must not be installation of a zone */
718 
719 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
720 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
721 		/* initial zone install: no path can be diskless client */
722 		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_INSTALL, rootPath);
723 		return (R_FAILURE);
724 	}
725 
726 	/* the path is a diskless client */
727 
728 	log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_IS_DISKLESS_CLIENT, rootPath);
729 
730 	return (R_SUCCESS);
731 }
732 
733 /*
734  * Name:	cmd_is_global_zone
735  * Description:	determine if target is a global zone
736  * Scope:	public
737  * Arguments:	argc,argv:
738  *		  - optional path to target to test
739  * Returns:	int
740  *			== 0 - success
741  *			!= 0 - failure
742  * IMPLEMENTATION:
743  *  - must not be initial installation to the install root
744  *  - must not be installation of a non-global zone
745  *  - must not be a non-global zone
746  *  - must not be a mounted mini-root
747  *  - must not be a netinstall image
748  *  - must not be a diskless client
749  *  - if $ROOTDIR is "/":
750  *  -- if zone name is "GLOBAL", then is a global zone;
751  *  -- else not a global zone.
752  *  - $ROOTDIR/etc/zones must exist and be a directory
753  *  - $ROOTDIR/.tmp_proto must not exist
754  *  - $ROOTDIR/var must exist and must not be a symbolic link
755  */
756 
757 static int
758 cmd_is_global_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
759 {
760 	char	*rootPath = NULL;
761 	int	c;
762 	int	r;
763 static	char	*cmdName = "is_global_zone";
764 static	int	recursion = 0;
765 
766 	/* process any command line options */
767 
768 	while ((c = getopt(argc, argv, ":")) != EOF) {
769 		switch (c) {
770 		case '\0':	/* prevent end-of-loop not reached warning */
771 			break;
772 		case '?':
773 		default:
774 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
775 			return (R_USAGE);
776 		}
777 	}
778 
779 	/* prevent recursion */
780 
781 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
782 
783 		/*
784 		 * a global zone cannot be any of the following
785 		 */
786 
787 		/* cannot be a non-global zone */
788 
789 		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
790 
791 		/* cannot be a mounted miniroot */
792 
793 		if (r != R_SUCCESS) {
794 			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
795 		}
796 
797 		/* cannot be a netinstall image */
798 
799 		if (r != R_SUCCESS) {
800 			r = cmd_is_netinstall_image(argc, argv, a_gdt);
801 		}
802 
803 		/* cannot be a diskless client */
804 
805 		if (r != R_SUCCESS) {
806 			r = cmd_is_diskless_client(argc, argv, a_gdt);
807 		}
808 
809 		/* no need to guard against recursion any more */
810 
811 		recursion--;
812 
813 		/* return failure if any of the preceeding are true */
814 
815 		switch (r) {
816 			case R_SUCCESS:
817 				return (R_FAILURE);
818 			case R_FAILURE:
819 				break;
820 			case R_USAGE:
821 			case R_ERROR:
822 			default:
823 				return (r);
824 		}
825 	}
826 
827 	/* normalize argc/argv */
828 
829 	argc -= optind;
830 	argv += optind;
831 
832 	/* error if more than one argument */
833 
834 	if (argc > 1) {
835 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
836 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
837 		return (R_USAGE);
838 	}
839 
840 	/* process root path if first argument present */
841 
842 	if (argc == 1) {
843 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
844 			return (R_ERROR);
845 		}
846 	}
847 
848 	/* get current root path */
849 
850 	r = getRootPath(&rootPath);
851 	if (r != R_SUCCESS) {
852 		return (r);
853 	}
854 
855 	/* start of command debugging information */
856 
857 	echoDebug(DBG_ROOTPATH_IS, rootPath);
858 
859 	/* must not be initial installation to the install root */
860 
861 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
862 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
863 		/* initial install: install root cannot be global zone */
864 		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_INITIAL_INSTALL, rootPath);
865 		return (R_FAILURE);
866 	}
867 
868 	/* must not be installation of a non-global zone */
869 
870 	if (a_gdt->gd_nonglobalZoneInstall == B_TRUE) {
871 		/* initial nonglobal zone install: no path can be global zone */
872 		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_NGZ_ZONE_INSTALL, rootPath);
873 		return (R_FAILURE);
874 	}
875 
876 	/* handle if global zone installation to the install root */
877 
878 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) &&
879 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
880 			/* the path is a global zone */
881 
882 			log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
883 				rootPath);
884 
885 			return (R_SUCCESS);
886 	}
887 
888 	/* true if current root is "/" and zone name is GLOBAL_ZONENAME */
889 
890 	if (strcmp(rootPath, "/") == 0) {
891 		if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
892 			/* the path is a global zone */
893 
894 			log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
895 				rootPath);
896 
897 			return (R_SUCCESS);
898 		}
899 
900 		/* inside a non-global zone */
901 
902 		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_ZONENAME_ISNT_GLOBAL,
903 			rootPath, a_gdt->gd_zoneName);
904 
905 		return (R_FAILURE);
906 	}
907 
908 	/*
909 	 * current root is not "/" - see if target looks like a global zone
910 	 *
911 	 * - rootpath is not "/"
912 	 * - and $ROOTDIR/etc/zones exists
913 	 * - and $ROOTDIR/.tmp_proto does not exist
914 	 * - and $ROOTDIR/var is not a symbolic link
915 	 */
916 
917 	/* not global zone if /etc/zones does not exist */
918 
919 	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
920 		"%s/%s", rootPath, "/etc/zones");
921 	if (r != R_SUCCESS) {
922 		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_ISNT_DIRECTORY,
923 			rootPath, "/etc/zones");
924 		return (R_FAILURE);
925 	}
926 
927 	/* .tmp_proto must not exist */
928 
929 	r = testPath(TEST_NOT_EXISTS,
930 		"%s/%s", rootPath, ".tmp_proto");
931 	if (r != R_SUCCESS) {
932 		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_EXISTS,
933 			rootPath, "/.tmp_proto");
934 		return (R_FAILURE);
935 	}
936 
937 	/* /var must not be a symbolic link */
938 
939 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
940 		"%s/%s", rootPath, "/var");
941 	if (r != R_SUCCESS) {
942 		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_SYMLINK,
943 			rootPath, "/var");
944 		return (R_FAILURE);
945 	}
946 
947 	/* the path is a global zone */
948 
949 	log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE, rootPath);
950 
951 	return (R_SUCCESS);
952 }
953 
954 /*
955  * Name:	cmd_is_netinstall_image
956  * Description:	determine if target is a net install image
957  * Scope:	public
958  * Arguments:	argc,argv:
959  *		  - optional path to target to test
960  * Returns:	int
961  *			== 0 - success
962  *			!= 0 - failure
963  * IMPLEMENTATION:
964  *  - must not be initial installation to the install root
965  *  - must not be installation of a zone
966  *  - must not be a global zone
967  *  - must not be a mounted mini-root
968  *  - zone name must be "global"
969  *  - $ROOTDIR/.tmp_proto must exist and must be a directory
970  *  - $ROOTDIR/var must exist and must be a symbolic link
971  *  - $ROOTDIR/tmp/kernel must exist and must be a directory
972  *  - $ROOTDIR/.tmp_proto/kernel must exist and must be a symbolic link
973  */
974 
975 static int
976 cmd_is_netinstall_image(int argc, char **argv, GLOBALDATA_T *a_gdt)
977 {
978 	char	*rootPath = NULL;
979 	int	c;
980 	int	r;
981 static	char	*cmdName = "is_netinstall_image";
982 static	int	recursion = 0;
983 
984 	/* process any command line options */
985 
986 	while ((c = getopt(argc, argv, ":")) != EOF) {
987 		switch (c) {
988 		case '\0':	/* prevent end-of-loop not reached warning */
989 			break;
990 		case '?':
991 		default:
992 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
993 			return (R_USAGE);
994 		}
995 	}
996 
997 	/* prevent recursion */
998 
999 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1000 
1001 		/* a netinstall image cannot be a global zone */
1002 
1003 		r = cmd_is_global_zone(argc, argv, a_gdt);
1004 
1005 		/* no need to guard against recursion any more */
1006 
1007 		recursion--;
1008 
1009 		switch (r) {
1010 			case R_SUCCESS:
1011 				return (R_FAILURE);
1012 			case R_FAILURE:
1013 				break;
1014 			case R_USAGE:
1015 			case R_ERROR:
1016 			default:
1017 				return (r);
1018 		}
1019 	}
1020 
1021 	/* normalize argc/argv */
1022 
1023 	argc -= optind;
1024 	argv += optind;
1025 
1026 	/* error if more than one argument */
1027 
1028 	if (argc > 1) {
1029 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1030 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1031 		return (R_USAGE);
1032 	}
1033 
1034 	/* process root path if first argument present */
1035 
1036 	if (argc == 1) {
1037 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1038 			return (R_ERROR);
1039 		}
1040 	}
1041 
1042 	/* get current root path */
1043 
1044 	r = getRootPath(&rootPath);
1045 	if (r != R_SUCCESS) {
1046 		return (r);
1047 	}
1048 
1049 	/* start of command debugging information */
1050 
1051 	echoDebug(DBG_ROOTPATH_IS, rootPath);
1052 
1053 	/* current zone name must be "global" */
1054 
1055 	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
1056 		log_msg(LOG_MSG_DEBUG, DBG_INIM_BAD_CURRENT_ZONE,
1057 			rootPath, GLOBAL_ZONENAME);
1058 		return (R_FAILURE);
1059 	}
1060 
1061 	/* cannot be a mounted_miniroot */
1062 
1063 	if (cmd_is_mounted_miniroot(argc, argv, a_gdt) == R_SUCCESS) {
1064 		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT,
1065 			rootPath);
1066 		return (R_FAILURE);
1067 	}
1068 
1069 	/* $ROOTDIR/.tmp_proto exists */
1070 
1071 	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
1072 		"%s/%s", rootPath, ".tmp_proto");
1073 	if (r != R_SUCCESS) {
1074 		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
1075 			rootPath, "/.tmp_proto");
1076 		return (R_FAILURE);
1077 	}
1078 
1079 	/* $ROOTDIR/var is a symbolic link */
1080 
1081 	r = testPath(TEST_IS_SYMBOLIC_LINK,
1082 		"%s/%s", rootPath, "/var");
1083 	if (r != R_SUCCESS) {
1084 		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
1085 			rootPath, "/var");
1086 		return (R_FAILURE);
1087 	}
1088 
1089 	/* $ROOTDIR/tmp/kernel does exist */
1090 
1091 	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
1092 		"%s/%s", rootPath, "/tmp/kernel");
1093 	if (r != R_SUCCESS) {
1094 		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
1095 			rootPath, "/tmp/kernel");
1096 		return (R_FAILURE);
1097 	}
1098 
1099 	/* $ROOTDIR/.tmp_proto/kernel is a symbolic link */
1100 
1101 	r = testPath(TEST_IS_SYMBOLIC_LINK,
1102 		"%s/%s", rootPath, "/.tmp_proto/kernel");
1103 	if (r != R_SUCCESS) {
1104 		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
1105 			rootPath, "/.tmp_proto/kernel");
1106 		return (R_FAILURE);
1107 	}
1108 
1109 	/* must not be initial installation to the install root */
1110 
1111 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
1112 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1113 		/* initial install: install root cannot be netinstall image */
1114 		log_msg(LOG_MSG_DEBUG, DBG_INIM_INITIAL_INSTALL, rootPath);
1115 		return (R_FAILURE);
1116 	}
1117 
1118 	/* must not be installation of a zone */
1119 
1120 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1121 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1122 		/* initial zone install: no path can be netinstall image */
1123 		log_msg(LOG_MSG_DEBUG, DBG_INIM_ZONE_INSTALL, rootPath);
1124 		return (R_FAILURE);
1125 	}
1126 
1127 	/* target is a netinstall image */
1128 
1129 	log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_IS_NETINSTALL_IMAGE, rootPath);
1130 
1131 	return (R_SUCCESS);
1132 }
1133 
1134 /*
1135  * Name:	cmd_is_mounted_miniroot
1136  * Description:	determine if target is a mounted miniroot image
1137  * Scope:	public
1138  * Arguments:	argc,argv:
1139  *		  - optional path to target to test
1140  * Returns:	int
1141  *			== 0 - success
1142  *			!= 0 - failure
1143  * IMPLEMENTATION:
1144  *  - must not be initial installation to the install root
1145  *  - must not be installation of a zone
1146  *  - zone name must be "global"
1147  *  - $ROOTDIR/tmp/kernel must exist and must be a symbolic link
1148  *  - $ROOTDIR/tmp/root/kernel must exist and must be a directory
1149  */
1150 
1151 static int
1152 cmd_is_mounted_miniroot(int argc, char **argv, GLOBALDATA_T *a_gdt)
1153 {
1154 	char	*rootPath = NULL;
1155 	int	c;
1156 	int	r;
1157 static	char	*cmdName = "is_mounted_miniroot";
1158 static	int	recursion = 0;
1159 
1160 	/* process any command line options */
1161 
1162 	while ((c = getopt(argc, argv, ":")) != EOF) {
1163 		switch (c) {
1164 		case '\0':	/* prevent end-of-loop not reached warning */
1165 			break;
1166 		case '?':
1167 		default:
1168 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1169 			return (R_USAGE);
1170 		}
1171 	}
1172 
1173 	/* prevent recursion */
1174 
1175 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1176 		recursion--;
1177 	}
1178 
1179 	/* normalize argc/argv */
1180 
1181 	argc -= optind;
1182 	argv += optind;
1183 
1184 	/* error if more than one argument */
1185 
1186 	if (argc > 1) {
1187 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1188 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1189 		return (R_USAGE);
1190 	}
1191 
1192 	/* process root path if first argument present */
1193 
1194 	if (argc == 1) {
1195 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1196 			return (R_ERROR);
1197 		}
1198 	}
1199 
1200 	/* get current root path */
1201 
1202 	r = getRootPath(&rootPath);
1203 	if (r != R_SUCCESS) {
1204 		return (r);
1205 	}
1206 
1207 	/* start of command debugging information */
1208 
1209 	echoDebug(DBG_ROOTPATH_IS, rootPath);
1210 
1211 	/* current zone name must be "global" */
1212 
1213 	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
1214 		log_msg(LOG_MSG_DEBUG, DBG_IMRT_BAD_CURRENT_ZONE,
1215 			rootPath, GLOBAL_ZONENAME);
1216 		return (R_FAILURE);
1217 	}
1218 
1219 	/* $ROOTDIR/tmp/kernel is a symbolic link */
1220 
1221 	r = testPath(TEST_IS_SYMBOLIC_LINK,
1222 		"%s/%s", rootPath, "/tmp/kernel");
1223 	if (r != R_SUCCESS) {
1224 		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_SYMLINK,
1225 			rootPath, "/tmp/kernel");
1226 		return (R_FAILURE);
1227 	}
1228 
1229 	/* $ROOTDIR/tmp/root/kernel is a directory */
1230 
1231 	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
1232 		"%s/%s", rootPath, "/tmp/root/kernel");
1233 	if (r != R_SUCCESS) {
1234 		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_DIRECTORY,
1235 			rootPath, "/tmp/root/kernel");
1236 		return (R_FAILURE);
1237 	}
1238 
1239 	/* must not be initial installation to the install root */
1240 
1241 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
1242 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1243 		/* initial install: install root cannot be mounted miniroot */
1244 		log_msg(LOG_MSG_DEBUG, DBG_IMRT_INITIAL_INSTALL, rootPath);
1245 		return (R_FAILURE);
1246 	}
1247 
1248 	/* must not be installation of a zone */
1249 
1250 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1251 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1252 		/* initial zone install: no path can be mounted miniroot */
1253 		log_msg(LOG_MSG_DEBUG, DBG_IMRT_ZONE_INSTALL, rootPath);
1254 		return (R_FAILURE);
1255 	}
1256 
1257 	/* target is a mounted miniroot */
1258 
1259 	log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT, rootPath);
1260 
1261 	return (R_SUCCESS);
1262 }
1263 
1264 /*
1265  * Name:	cmd_is_nonglobal_zone
1266  * Description:	determine if target is a global zone
1267  * Scope:	public
1268  * Arguments:	argc,argv:
1269  *		  - optional path to target to test
1270  * Returns:	int
1271  *			== 0 - success
1272  *			!= 0 - failure
1273  *  - must not be initial installation to the install root
1274  *  - must not be installation of a global zone
1275  *  - success if installation of a non-global zone
1276  */
1277 
1278 static int
1279 cmd_is_nonglobal_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
1280 {
1281 	char	*rootPath = NULL;
1282 	int	c;
1283 	int	r;
1284 static	char	*cmdName = "is_nonglobal_zone";
1285 static	int	recursion = 0;
1286 
1287 	/* process any command line options */
1288 
1289 	while ((c = getopt(argc, argv, ":")) != EOF) {
1290 		switch (c) {
1291 		case '\0':	/* prevent end-of-loop not reached warning */
1292 			break;
1293 		case '?':
1294 		default:
1295 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1296 			return (R_USAGE);
1297 		}
1298 	}
1299 
1300 	/* prevent recursion */
1301 
1302 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1303 		recursion--;
1304 	}
1305 
1306 	/* normalize argc/argv */
1307 
1308 	argc -= optind;
1309 	argv += optind;
1310 
1311 	/* error if more than one argument */
1312 
1313 	if (argc > 1) {
1314 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1315 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1316 		return (R_USAGE);
1317 	}
1318 
1319 	/* process root path if first argument present */
1320 
1321 	if (argc == 1) {
1322 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1323 			return (R_ERROR);
1324 		}
1325 	}
1326 
1327 	/* get current root path */
1328 
1329 	r = getRootPath(&rootPath);
1330 	if (r != R_SUCCESS) {
1331 		return (r);
1332 	}
1333 
1334 	/* start of command debugging information */
1335 
1336 	echoDebug(DBG_ROOTPATH_IS, rootPath);
1337 
1338 	/* handle if non-global zone installation to the install root */
1339 
1340 	if ((a_gdt->gd_nonglobalZoneInstall == B_TRUE) &&
1341 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1342 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_INSTALL_ZONENAME_IS_NGZ,
1343 			rootPath, a_gdt->gd_zoneName);
1344 		return (R_SUCCESS);
1345 	}
1346 
1347 	/* must not be initial installation to the install root */
1348 
1349 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
1350 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1351 		/* initial install: install root cannot be non-global zone */
1352 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_INITIAL_INSTALL, rootPath);
1353 		return (R_FAILURE);
1354 	}
1355 
1356 	/* must not be installation of a global zone */
1357 
1358 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1359 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1360 		/* initial global zone install: no path can be nonglobal zone */
1361 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_GLOBAL_ZONE_INSTALL, rootPath);
1362 		return (R_FAILURE);
1363 	}
1364 
1365 	/*
1366 	 * *********************************************************************
1367 	 * if root directory is "/" then the only thing that needs to be done is
1368 	 * to test the zone name directly - if the zone name is "global" then
1369 	 * the target is not a non-global zone; otherwise if the zone name is
1370 	 * not "global" then the target IS a non-global zone.
1371 	 * *********************************************************************
1372 	 */
1373 
1374 	if (strcmp(rootPath, "/") == 0) {
1375 		/* target is current running root */
1376 		if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
1377 			/* in the global zone */
1378 			log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
1379 				rootPath, a_gdt->gd_zoneName);
1380 			return (R_FAILURE);
1381 		}
1382 		/* in a non-global zone */
1383 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_IS_NGZ,
1384 			rootPath, a_gdt->gd_zoneName);
1385 		return (R_SUCCESS);
1386 	}
1387 
1388 	/*
1389 	 * $ROOTDIR/etc/zones/index must exist in a global zone. It also
1390 	 * exists in a non-global zone after s10u4 but we can't check that
1391 	 * since it is undeterministic for all releases so we only check
1392 	 * for the global zone here.
1393 	 */
1394 
1395 	r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/zones/index");
1396 	if (r == R_SUCCESS) {
1397 
1398 		/* See if "global" exists in .../etc/zones/index */
1399 
1400 		if (testPath(TEST_GLOBAL_TOKEN_IN_FILE, "%s/%s", rootPath,
1401 		    "/etc/zones/index") != R_SUCCESS) {
1402 			log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
1403 			    rootPath, GLOBAL_ZONENAME);
1404 			return (R_FAILURE);
1405 		}
1406 	}
1407 
1408 	/*
1409 	 * *********************************************************************
1410 	 * If the root directory is "/" then you can use only the zone
1411 	 * name to determine if the zone is non-global or not since the
1412 	 * package is being installed or removed to the current "zone".
1413 	 *
1414 	 * Since the root directory being tested is not "/" then you have to
1415 	 * look into the target to try and infer zone type using means other
1416 	 * than the zone name only.
1417 	 * *********************************************************************
1418 	 */
1419 
1420 	/* reject if any items found that cannot be in a non-global zone */
1421 
1422 	/* .tmp_proto must not exist */
1423 
1424 	r = testPath(TEST_NOT_EXISTS, "%s/%s", rootPath, ".tmp_proto");
1425 	if (r != R_SUCCESS) {
1426 		/* $R/.tmp_proto cannot exist in a non-global zone */
1427 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
1428 			rootPath, "/.tmp_proto");
1429 		return (R_FAILURE);
1430 	}
1431 
1432 	/* /var must not be a symbolic link */
1433 
1434 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1435 		"%s/%s", rootPath, "/var");
1436 	if (r != R_SUCCESS) {
1437 		/* $R/var cannot be a symbolic link in a non-global zone */
1438 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_DOES_NOT_EXIST,
1439 			rootPath, "/var");
1440 		return (R_FAILURE);
1441 	}
1442 
1443 	/* $ROOTDIR/tmp/root/kernel must not exist */
1444 
1445 	r = testPath(TEST_NOT_EXISTS,
1446 		"%s/%s", rootPath, "/tmp/root/kernel");
1447 	if (r != R_SUCCESS) {
1448 		/* $R/tmp/root/kernel cannot exist in a non-global zone */
1449 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
1450 			rootPath, "/tmp/root/kernel");
1451 		return (R_FAILURE);
1452 	}
1453 
1454 	/*
1455 	 * *********************************************************************
1456 	 * no items exist in $ROOTDIR that identify something other than
1457 	 * a non-global zone.
1458 	 *
1459 	 * if in global zone no more tests possible: is a non-global zone
1460 	 * *********************************************************************
1461 	 */
1462 
1463 	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
1464 		/* in the global zone */
1465 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE,
1466 			rootPath);
1467 		return (R_SUCCESS);
1468 	}
1469 
1470 	/*
1471 	 * *********************************************************************
1472 	 * In non-global zone: interrogate zone name and type.
1473 	 *
1474 	 * The parent zone is the zone that the "pkgadd" or "pkgrm" command was
1475 	 * run in. The child zone is the zone that the "pkginstall" or
1476 	 * "pkgremove" command was run in.
1477 	 * *********************************************************************
1478 	 */
1479 
1480 	/*
1481 	 * If parent zone name and current zone name defined, and
1482 	 * both zone names are the same, since pkgcond is running
1483 	 * inside of a non-global zone, this is how the scratch
1484 	 * zone is implemented, so target is a non-global zone
1485 	 */
1486 
1487 	if ((a_gdt->gd_parentZoneName != NULL) &&
1488 		(a_gdt->gd_currentZoneName != NULL) &&
1489 		(strcmp(a_gdt->gd_parentZoneName,
1490 					a_gdt->gd_currentZoneName) == 0)) {
1491 			/* parent and current zone name identical: non-gz */
1492 			log_msg(LOG_MSG_DEBUG, DBG_NGZN_PARENT_CHILD_SAMEZONE,
1493 				rootPath, a_gdt->gd_parentZoneName);
1494 			return (R_SUCCESS);
1495 	}
1496 
1497 	/*
1498 	 * In non-global zone if inherited FS's exits
1499 	 * or zone specific read only FS's exist
1500 	 * or it is in a mounted state.
1501 	 */
1502 
1503 	if (a_gdt->gd_inheritedFileSystems != NULL ||
1504 		a_gdt->gd_srFsMountedRO || a_gdt->inMountedState) {
1505 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
1506 		return (R_SUCCESS);
1507 	}
1508 
1509 	/*
1510 	 * the parent and current zone name are not the same;
1511 	 * interrogate the zone types: the parent must be global
1512 	 * and the current must be non-global, which would be set
1513 	 * when a package command is run in the global zone that in
1514 	 * turn runs a package command within the non-global zone.
1515 	 */
1516 
1517 	/* if defined, parent zone type must be "global" */
1518 
1519 	if ((a_gdt->gd_parentZoneType != NULL) &&
1520 		(strcmp(a_gdt->gd_parentZoneType, "nonglobal") == 0)) {
1521 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_PARENT_ZONETYPE,
1522 			rootPath, "nonglobal");
1523 		return (R_FAILURE);
1524 	}
1525 
1526 	/* if defined, current zone type must be "nonglobal" */
1527 
1528 	if ((a_gdt->gd_currentZoneType != NULL) &&
1529 		(strcmp(a_gdt->gd_currentZoneType, GLOBAL_ZONENAME) == 0)) {
1530 		log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_CURRENT_ZONETYPE,
1531 			rootPath, GLOBAL_ZONENAME);
1532 		return (R_FAILURE);
1533 	}
1534 
1535 	/*
1536 	 * *********************************************************************
1537 	 * no other tests possible: target is a non-global zone
1538 	 * *********************************************************************
1539 	 */
1540 
1541 	log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
1542 
1543 	return (R_SUCCESS);
1544 }
1545 
1546 /*
1547  * Name:	cmd_is_running_system
1548  * Description:	determine if target is a global zone
1549  * Scope:	public
1550  * Arguments:	argc,argv:
1551  *		  - optional path to target to test
1552  * Returns:	int
1553  *			== 0 - success
1554  *			!= 0 - failure
1555  * IMPLEMENTATION:
1556  *  - must not be initial installation to the install root
1557  *  - must not be installation of a zone
1558  *  - must not be a diskless client
1559  *  - $ROOTDIR must be "/"
1560  *  - zone name must be "global"
1561  */
1562 
1563 static int
1564 cmd_is_running_system(int argc, char **argv, GLOBALDATA_T *a_gdt)
1565 {
1566 	char	*rootPath = NULL;
1567 	int	c;
1568 	int	r;
1569 static	char	*cmdName = "is_running_system";
1570 static	int	recursion = 0;
1571 
1572 	/* process any command line options */
1573 
1574 	while ((c = getopt(argc, argv, ":")) != EOF) {
1575 		switch (c) {
1576 		case '\0':	/* prevent end-of-loop not reached warning */
1577 			break;
1578 		case '?':
1579 		default:
1580 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1581 			return (R_USAGE);
1582 		}
1583 	}
1584 
1585 	/* prevent recursion */
1586 
1587 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1588 
1589 		/* a running system cannot be a diskless client */
1590 
1591 		r = cmd_is_diskless_client(argc, argv, a_gdt);
1592 
1593 		/* no need to guard against recursion any more */
1594 
1595 		recursion--;
1596 
1597 		switch (r) {
1598 			case R_SUCCESS:
1599 				return (R_FAILURE);
1600 			case R_FAILURE:
1601 				break;
1602 			case R_USAGE:
1603 			case R_ERROR:
1604 			default:
1605 				return (r);
1606 		}
1607 	}
1608 
1609 	/* normalize argc/argv */
1610 
1611 	argc -= optind;
1612 	argv += optind;
1613 
1614 	/* error if more than one argument */
1615 
1616 	if (argc > 1) {
1617 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1618 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1619 		return (R_USAGE);
1620 	}
1621 
1622 	/* process root path if first argument present */
1623 
1624 	if (argc == 1) {
1625 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1626 			return (R_ERROR);
1627 		}
1628 	}
1629 
1630 	/* get current root path */
1631 
1632 	r = getRootPath(&rootPath);
1633 	if (r != R_SUCCESS) {
1634 		return (r);
1635 	}
1636 
1637 	/* start of command debugging information */
1638 
1639 	echoDebug(DBG_ROOTPATH_IS, rootPath);
1640 
1641 	/* if root path is "/" then check zone name */
1642 
1643 	if (strcmp(rootPath, "/") != 0) {
1644 		log_msg(LOG_MSG_DEBUG, DBG_IRST_ROOTPATH_BAD, rootPath, "/");
1645 		return (R_FAILURE);
1646 	}
1647 
1648 	/* zone name must be global */
1649 
1650 	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
1651 		log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_BAD, rootPath,
1652 			GLOBAL_ZONENAME);
1653 		return (R_FAILURE);
1654 	}
1655 
1656 	/* must not be initial installation to the install root */
1657 
1658 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
1659 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1660 		/* initial install: install root cannot be the running system */
1661 		log_msg(LOG_MSG_DEBUG, DBG_IRST_INITIAL_INSTALL, rootPath);
1662 		return (R_FAILURE);
1663 	}
1664 
1665 	/* must not be installation of a zone */
1666 
1667 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1668 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1669 		/* initial zone install: no path can be running system */
1670 		log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_INSTALL, rootPath);
1671 		return (R_FAILURE);
1672 	}
1673 
1674 	/* target is a running system */
1675 
1676 	log_msg(LOG_MSG_DEBUG, DBG_IRST_PATH_IS_RUNNING_SYSTEM, rootPath);
1677 
1678 	return (R_SUCCESS);
1679 }
1680 
1681 /*
1682  * Name:	cmd_can_add_driver
1683  * Description:	determine if target is a global zone
1684  * Scope:	public
1685  * Arguments:	argc,argv:
1686  *		  - optional path to target to test
1687  * Returns:	int
1688  *			== 0 - success
1689  *			!= 0 - failure
1690  * Implementation:
1691  * A driver can be added to the system if the components of a Solaris
1692  * instance capable of loading drivers is present and it is not the
1693  * currently running system.
1694  */
1695 
1696 static int
1697 cmd_can_add_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
1698 {
1699 	char	*rootPath = NULL;
1700 	int	c;
1701 	int	r;
1702 static	char	*cmdName = "can_add_driver";
1703 static	int	recursion = 0;
1704 
1705 	/* process any command line options */
1706 
1707 	while ((c = getopt(argc, argv, ":")) != EOF) {
1708 		switch (c) {
1709 		case '\0':	/* prevent end-of-loop not reached warning */
1710 			break;
1711 		case '?':
1712 		default:
1713 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1714 			return (R_USAGE);
1715 		}
1716 	}
1717 
1718 	/* prevent recursion */
1719 
1720 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1721 
1722 		/* see if this is the current running system */
1723 
1724 		r = cmd_is_running_system(argc, argv, a_gdt);
1725 
1726 		/* cannot be a diskless client */
1727 
1728 		if (r != R_SUCCESS) {
1729 			r = cmd_is_diskless_client(argc, argv, a_gdt);
1730 		}
1731 
1732 		/* no need to guard against recursion any more */
1733 
1734 		recursion--;
1735 
1736 		switch (r) {
1737 			case R_SUCCESS:
1738 				/* is a running system */
1739 				return (R_FAILURE);
1740 			case R_FAILURE:
1741 				/* not a running syste */
1742 				break;
1743 			case R_USAGE:
1744 			case R_ERROR:
1745 			default:
1746 				/* cannot determine if is a running system */
1747 				return (r);
1748 		}
1749 	}
1750 
1751 	/* normalize argc/argv */
1752 
1753 	argc -= optind;
1754 	argv += optind;
1755 
1756 	/* error if more than one argument */
1757 
1758 	if (argc > 1) {
1759 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1760 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1761 		return (R_USAGE);
1762 	}
1763 
1764 	/* process root path if first argument present */
1765 
1766 	if (argc == 1) {
1767 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1768 			return (R_ERROR);
1769 		}
1770 	}
1771 
1772 	/* get current root path */
1773 
1774 	r = getRootPath(&rootPath);
1775 	if (r != R_SUCCESS) {
1776 		return (r);
1777 	}
1778 
1779 	/* start of command debugging information */
1780 
1781 	echoDebug(DBG_ROOTPATH_IS, rootPath);
1782 
1783 	/* /etc must exist and must not be a symbolic link */
1784 
1785 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1786 		"%s/%s", rootPath, "/etc");
1787 	if (r != R_SUCCESS) {
1788 		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
1789 			rootPath, "/etc");
1790 		return (R_FAILURE);
1791 	}
1792 
1793 	/* /platform must exist and must not be a symbolic link */
1794 
1795 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1796 		"%s/%s", rootPath, "/platform");
1797 	if (r != R_SUCCESS) {
1798 		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
1799 			rootPath, "/platform");
1800 		return (R_FAILURE);
1801 	}
1802 
1803 	/* /kernel must exist and must not be a symbolic link */
1804 
1805 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1806 		"%s/%s", rootPath, "/kernel");
1807 	if (r != R_SUCCESS) {
1808 		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
1809 			rootPath, "/kernel");
1810 		return (R_FAILURE);
1811 	}
1812 
1813 	/* can add a driver */
1814 
1815 	log_msg(LOG_MSG_DEBUG, DBG_ADDV_YES, rootPath);
1816 
1817 	return (R_SUCCESS);
1818 }
1819 
1820 /*
1821  * Name:	cmd_can_update_driver
1822  * Description:	determine if target is a global zone
1823  * Scope:	public
1824  * Arguments:	argc,argv:
1825  *		  - optional path to target to test
1826  * Returns:	int
1827  *			== 0 - success
1828  *			!= 0 - failure
1829  * Implementation:
1830  * A driver can be added to the system if the components of a Solaris
1831  * instance capable of loading drivers is present and it is not the
1832  * currently running system.
1833  */
1834 
1835 static int
1836 cmd_can_update_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
1837 {
1838 	char	*rootPath = NULL;
1839 	int	c;
1840 	int	r;
1841 static	char	*cmdName = "can_update_driver";
1842 static	int	recursion = 0;
1843 
1844 	/* process any command line options */
1845 
1846 	while ((c = getopt(argc, argv, ":")) != EOF) {
1847 		switch (c) {
1848 		case '\0':	/* prevent end-of-loop not reached warning */
1849 			break;
1850 		case '?':
1851 		default:
1852 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1853 			return (R_USAGE);
1854 		}
1855 	}
1856 
1857 	/* prevent recursion */
1858 
1859 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1860 
1861 		/* see if this is the current running system */
1862 
1863 		r = cmd_is_running_system(argc, argv, a_gdt);
1864 
1865 		/* cannot be a diskless client */
1866 
1867 		if (r != R_SUCCESS) {
1868 			r = cmd_is_diskless_client(argc, argv, a_gdt);
1869 		}
1870 
1871 		/* no need to guard against recursion any more */
1872 
1873 		recursion--;
1874 
1875 		switch (r) {
1876 			case R_SUCCESS:
1877 				/* is a running system */
1878 				return (R_FAILURE);
1879 			case R_FAILURE:
1880 				/* not a running syste */
1881 				break;
1882 			case R_USAGE:
1883 			case R_ERROR:
1884 			default:
1885 				/* cannot determine if is a running system */
1886 				return (r);
1887 		}
1888 	}
1889 
1890 	/* normalize argc/argv */
1891 
1892 	argc -= optind;
1893 	argv += optind;
1894 
1895 	/* error if more than one argument */
1896 
1897 	if (argc > 1) {
1898 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1899 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1900 		return (R_USAGE);
1901 	}
1902 
1903 	/* process root path if first argument present */
1904 
1905 	if (argc == 1) {
1906 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1907 			return (R_ERROR);
1908 		}
1909 	}
1910 
1911 	/* get current root path */
1912 
1913 	r = getRootPath(&rootPath);
1914 	if (r != R_SUCCESS) {
1915 		return (r);
1916 	}
1917 
1918 	/* start of command debugging information */
1919 
1920 	echoDebug(DBG_ROOTPATH_IS, rootPath);
1921 
1922 	/* /etc must exist and must not be a symbolic link */
1923 
1924 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1925 		"%s/%s", rootPath, "/etc");
1926 	if (r != R_SUCCESS) {
1927 		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
1928 			rootPath, "/etc");
1929 		return (R_FAILURE);
1930 	}
1931 
1932 	/* /platform must exist and must not be a symbolic link */
1933 
1934 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1935 		"%s/%s", rootPath, "/platform");
1936 	if (r != R_SUCCESS) {
1937 		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
1938 			rootPath, "/platform");
1939 		return (R_FAILURE);
1940 	}
1941 
1942 	/* /kernel must exist and must not be a symbolic link */
1943 
1944 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1945 		"%s/%s", rootPath, "/kernel");
1946 	if (r != R_SUCCESS) {
1947 		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
1948 			rootPath, "/kernel");
1949 		return (R_FAILURE);
1950 	}
1951 
1952 	/* can update driver */
1953 
1954 	log_msg(LOG_MSG_DEBUG, DBG_UPDV_YES, rootPath);
1955 
1956 	return (R_SUCCESS);
1957 }
1958 
1959 /*
1960  * Name:	cmd_can_remove_driver
1961  * Description:	determine if target is a global zone
1962  * Scope:	public
1963  * Arguments:	argc,argv:
1964  *		  - optional path to target to test
1965  * Returns:	int
1966  *			== 0 - success
1967  *			!= 0 - failure
1968  * Implementation:
1969  * A driver can be added to the system if the components of a Solaris
1970  * instance capable of loading drivers is present and it is not the
1971  * currently running system.
1972  */
1973 
1974 static int
1975 cmd_can_remove_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
1976 {
1977 	char	*rootPath = NULL;
1978 	int	c;
1979 	int	r;
1980 static	char	*cmdName = "can_remove_driver";
1981 static	int	recursion = 0;
1982 
1983 	/* process any command line options */
1984 
1985 	while ((c = getopt(argc, argv, ":")) != EOF) {
1986 		switch (c) {
1987 		case '\0':	/* prevent end-of-loop not reached warning */
1988 			break;
1989 		case '?':
1990 		default:
1991 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1992 			return (R_USAGE);
1993 		}
1994 	}
1995 
1996 	/* prevent recursion */
1997 
1998 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1999 
2000 		/* see if this is the current running system */
2001 
2002 		r = cmd_is_running_system(argc, argv, a_gdt);
2003 
2004 		/* cannot be a diskless client */
2005 
2006 		if (r != R_SUCCESS) {
2007 			r = cmd_is_diskless_client(argc, argv, a_gdt);
2008 		}
2009 
2010 		/* no need to guard against recursion any more */
2011 
2012 		recursion--;
2013 
2014 		switch (r) {
2015 			case R_SUCCESS:
2016 				/* is a running system */
2017 				return (R_FAILURE);
2018 			case R_FAILURE:
2019 				/* not a running syste */
2020 				break;
2021 			case R_USAGE:
2022 			case R_ERROR:
2023 			default:
2024 				/* cannot determine if is a running system */
2025 				return (r);
2026 		}
2027 	}
2028 
2029 	/* normalize argc/argv */
2030 
2031 	argc -= optind;
2032 	argv += optind;
2033 
2034 	/* error if more than one argument */
2035 
2036 	if (argc > 1) {
2037 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2038 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2039 		return (R_USAGE);
2040 	}
2041 
2042 	/* process root path if first argument present */
2043 
2044 	if (argc == 1) {
2045 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2046 			return (R_ERROR);
2047 		}
2048 	}
2049 
2050 	/* get current root path */
2051 
2052 	r = getRootPath(&rootPath);
2053 	if (r != R_SUCCESS) {
2054 		return (r);
2055 	}
2056 
2057 	/* start of command debugging information */
2058 
2059 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2060 
2061 	/* /etc must exist and must not be a symbolic link */
2062 
2063 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
2064 		"%s/%s", rootPath, "/etc");
2065 	if (r != R_SUCCESS) {
2066 		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
2067 			rootPath, "/etc");
2068 		return (R_FAILURE);
2069 	}
2070 
2071 	/* /platform must exist and must not be a symbolic link */
2072 
2073 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
2074 		"%s/%s", rootPath, "/platform");
2075 	if (r != R_SUCCESS) {
2076 		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
2077 			rootPath, "/platform");
2078 		return (R_FAILURE);
2079 	}
2080 
2081 	/* /kernel must exist and must not be a symbolic link */
2082 
2083 	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
2084 		"%s/%s", rootPath, "/kernel");
2085 	if (r != R_SUCCESS) {
2086 		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
2087 			rootPath, "/kernel");
2088 		return (R_FAILURE);
2089 	}
2090 
2091 	/* can remove driver */
2092 
2093 	log_msg(LOG_MSG_DEBUG, DBG_RMDV_YES, rootPath);
2094 
2095 	return (R_SUCCESS);
2096 }
2097 
2098 /*
2099  * Name:	cmd_is_path_writable
2100  * Description:	determine if target path is writable (not inherited)
2101  * Scope:	public
2102  * Arguments:	argc,argv:
2103  *		  - optional path to target to test
2104  * Returns:	int
2105  *			== 0 - success
2106  *			!= 0 - failure
2107  * IMPLEMENTATION:
2108  * - path must be found in the file systems configured
2109  * - file system type must not be "inherited"
2110  * - mount options must not include "read only"
2111  */
2112 
2113 static int
2114 cmd_is_path_writable(int argc, char **argv, GLOBALDATA_T *a_gdt)
2115 {
2116 	FSI_T	*list;
2117 	char	*rootPath = NULL;
2118 	int	c;
2119 	int	n;
2120 	int	nn;
2121 	int	r;
2122 	long	listSize;
2123 	long	rootPathLen;
2124 static	char	*cmdName = "is_path_writable";
2125 static	int	recursion = 0;
2126 
2127 	/* process any command line options */
2128 
2129 	while ((c = getopt(argc, argv, ":")) != EOF) {
2130 		switch (c) {
2131 		case '\0':	/* prevent end-of-loop not reached warning */
2132 			break;
2133 		case '?':
2134 		default:
2135 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2136 			return (R_USAGE);
2137 		}
2138 	}
2139 
2140 	/* prevent recursion */
2141 
2142 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2143 		recursion--;
2144 	}
2145 
2146 	/* normalize argc/argv */
2147 
2148 	argc -= optind;
2149 	argv += optind;
2150 
2151 	/* error if more than one argument */
2152 
2153 	if (argc > 1) {
2154 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2155 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2156 		return (R_USAGE);
2157 	}
2158 
2159 	/* process root path if first argument present */
2160 
2161 	if (argc != 1) {
2162 		(void) usage(ERR_REQUIRED_ROOTPATH_MISSING, cmdName);
2163 		return (R_USAGE);
2164 	}
2165 
2166 	if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2167 		return (R_ERROR);
2168 	}
2169 
2170 	/* get current root path */
2171 
2172 	r = getRootPath(&rootPath);
2173 	if (r != R_SUCCESS) {
2174 		return (r);
2175 	}
2176 
2177 	/* start of command debugging information */
2178 
2179 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2180 
2181 	/* search file system conf for this path */
2182 
2183 	rootPathLen = strlen(rootPath);
2184 	list = a_gdt->gd_fileSystemConfig;
2185 	listSize = a_gdt->gd_fileSystemConfigLen;
2186 	for (nn = 0, n = 0; n < listSize; n++) {
2187 		long	mplen = strlen(list[n].fsi_mntPoint);
2188 		if (rootPathLen < mplen) {
2189 			/* root path is longer than target, ignore */
2190 			continue;
2191 		}
2192 		if (strncmp(rootPath, list[n].fsi_mntPoint, mplen) == 0) {
2193 			/* remember last partial match */
2194 			nn = n;
2195 		}
2196 	}
2197 
2198 	log_msg(LOG_MSG_DEBUG, DBG_PWRT_INFO,
2199 		rootPath, list[nn].fsi_mntPoint, list[nn].fsi_fsType,
2200 		list[nn].fsi_mntOptions);
2201 
2202 	/*
2203 	 * need to determine if the mount point is writeable:
2204 	 * If parent mount point is "inherited" then it is not writeable
2205 	 */
2206 
2207 	if (strcmp(list[nn].fsi_fsType, FSTYPE_INHERITED) == 0) {
2208 		log_msg(LOG_MSG_DEBUG, DBG_PWRT_INHERITED, rootPath,
2209 			list[nn].fsi_mntOptions);
2210 		return (R_FAILURE);
2211 	}
2212 
2213 	/* see if the file system is mounted with the "read only" option */
2214 
2215 	r = mountOptionPresent(list[nn].fsi_mntOptions, MNTOPT_RO);
2216 	if (r == R_SUCCESS) {
2217 		log_msg(LOG_MSG_DEBUG, DBG_PWRT_READONLY,
2218 			rootPath, list[nn].fsi_mntOptions);
2219 		return (R_FAILURE);
2220 	}
2221 
2222 	/* target path is writable */
2223 
2224 	log_msg(LOG_MSG_DEBUG, DBG_PWRT_IS, rootPath);
2225 
2226 	return (R_SUCCESS);
2227 }
2228 
2229 /*
2230  * Name:	cmd_is_alternative_root
2231  * Description:	determine if target is an alternative root
2232  * Scope:	public
2233  * Arguments:	argc,argv:
2234  *		  - optional path to target to test
2235  * Returns:	int
2236  *			== 0 - success
2237  *			!= 0 - failure
2238  * Implementation:
2239  *  - success if an initial installation to the install root
2240  *	(an initial install to $PKG_INSTALL_ROOT means that $PKG_INSTALL_ROOT
2241  *	points to an alternative root that is under construction)
2242  *  - must not be installation of a zone
2243  *  - must not be a boot environment
2244  *  - must not be a diskless client
2245  *  - must not be a mounted miniroot
2246  *  - must not be a netinstall image
2247  *  - must not be a nonglobal zone
2248  *  - must not be a running system
2249  *  - $ROOTDIR must not be "/"
2250  *  - $ROOTDIR/var must exist
2251  */
2252 
2253 static int
2254 cmd_is_alternative_root(int argc, char **argv, GLOBALDATA_T *a_gdt)
2255 {
2256 	char	*rootPath = NULL;
2257 	int	c;
2258 	int	r;
2259 static	char	*cmdName = "is_alternative_root";
2260 static	int	recursion = 0;
2261 
2262 	/* process any command line options */
2263 
2264 	while ((c = getopt(argc, argv, ":")) != EOF) {
2265 		switch (c) {
2266 		case '\0':	/* prevent end-of-loop not reached warning */
2267 			break;
2268 		case '?':
2269 		default:
2270 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2271 			return (R_USAGE);
2272 		}
2273 	}
2274 
2275 	/* prevent recursion */
2276 
2277 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2278 
2279 		/*
2280 		 * an alternative root cannot be any of the following
2281 		 */
2282 
2283 		/* cannot be a boot_environment */
2284 
2285 		r = cmd_is_boot_environment(argc, argv, a_gdt);
2286 
2287 		/* cannot be a diskless_client */
2288 
2289 		if (r != R_SUCCESS) {
2290 			r = cmd_is_diskless_client(argc, argv, a_gdt);
2291 		}
2292 
2293 		/* cannot be a mounted_miniroot */
2294 
2295 		if (r != R_SUCCESS) {
2296 			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
2297 		}
2298 
2299 		/* cannot be a netinstall_image */
2300 
2301 		if (r != R_SUCCESS) {
2302 			r = cmd_is_netinstall_image(argc, argv, a_gdt);
2303 		}
2304 
2305 		/* cannot be a nonglobal_zone */
2306 
2307 		if (r != R_SUCCESS) {
2308 			r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
2309 		}
2310 
2311 		/* cannot be a running_system */
2312 
2313 		if (r != R_SUCCESS) {
2314 			r = cmd_is_running_system(argc, argv, a_gdt);
2315 		}
2316 
2317 		/* no need to guard against recursion any more */
2318 
2319 		recursion--;
2320 
2321 		/* return failure if any of the preceeding are true */
2322 
2323 		switch (r) {
2324 			case R_SUCCESS:
2325 				return (R_FAILURE);
2326 			case R_FAILURE:
2327 				break;
2328 			case R_USAGE:
2329 			case R_ERROR:
2330 			default:
2331 				return (r);
2332 		}
2333 	}
2334 
2335 	/* normalize argc/argv */
2336 
2337 	argc -= optind;
2338 	argv += optind;
2339 
2340 	/* error if more than one argument */
2341 
2342 	if (argc > 1) {
2343 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2344 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2345 		return (R_USAGE);
2346 	}
2347 
2348 	/* process root path if first argument present */
2349 
2350 	if (argc == 1) {
2351 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2352 			return (R_ERROR);
2353 		}
2354 	}
2355 
2356 	/* get current root path */
2357 
2358 	r = getRootPath(&rootPath);
2359 	if (r != R_SUCCESS) {
2360 		return (r);
2361 	}
2362 
2363 	/* start of command debugging information */
2364 
2365 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2366 
2367 	/* return success if initial installation */
2368 
2369 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
2370 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
2371 		log_msg(LOG_MSG_DEBUG, DBG_IALR_INITIAL_INSTALL, rootPath);
2372 		return (R_SUCCESS);
2373 	}
2374 
2375 	/* root path must not be "/" */
2376 
2377 	if (strcmp(rootPath, "/") == 0) {
2378 		log_msg(LOG_MSG_DEBUG, DBG_IALR_BAD_ROOTPATH, rootPath, "/");
2379 		return (R_FAILURE);
2380 	}
2381 
2382 	/* /var must exist */
2383 
2384 	r = testPath(TEST_EXISTS,
2385 		"%s/%s", rootPath, "/var");
2386 	if (r != R_SUCCESS) {
2387 		log_msg(LOG_MSG_DEBUG, DBG_IALR_PATH_DOES_NOT_EXIST,
2388 			rootPath, "/var");
2389 		return (R_FAILURE);
2390 	}
2391 
2392 	/* must not be installation of a zone */
2393 
2394 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
2395 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
2396 		/* initial zone install: no path can be alternative root */
2397 		log_msg(LOG_MSG_DEBUG, DBG_IALR_ZONE_INSTALL, rootPath);
2398 		return (R_FAILURE);
2399 	}
2400 
2401 	/* target is an alternative root */
2402 
2403 	log_msg(LOG_MSG_DEBUG, DBG_IALR_IS, rootPath);
2404 
2405 	return (R_SUCCESS);
2406 }
2407 
2408 /*
2409  * Name:	cmd_is_boot_environment
2410  * Description:	determine if target is an alternative, inactive boot environment
2411  * Scope:	public
2412  * Arguments:	argc,argv:
2413  *		  - optional path to target to test
2414  * Returns:	int
2415  *			== 0 - success
2416  *			!= 0 - failure
2417  * IMPLEMENTATION:
2418  *  - must not be initial installation to the install root
2419  *  - must not be installation of a zone
2420  *  - must not be a diskless client
2421  *  - must not be a netinstall image
2422  *  - must not be a mounted miniroot
2423  *  - $ROOTDIR must not be "/"
2424  *  - $ROOTDIR/etc/lutab must exist
2425  *  - $ROOTDIR/etc/lu must exist and must be a directory
2426  */
2427 
2428 static int
2429 cmd_is_boot_environment(int argc, char **argv, GLOBALDATA_T *a_gdt)
2430 {
2431 	char	*rootPath = NULL;
2432 	int	c;
2433 	int	r;
2434 static	char	*cmdName = "is_boot_environment";
2435 static	int	recursion = 0;
2436 
2437 	/* process any command line options */
2438 
2439 	while ((c = getopt(argc, argv, ":")) != EOF) {
2440 		switch (c) {
2441 		case '\0':	/* prevent end-of-loop not reached warning */
2442 			break;
2443 		case '?':
2444 		default:
2445 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2446 			return (R_USAGE);
2447 		}
2448 	}
2449 
2450 	/* prevent recursion */
2451 
2452 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2453 		/*
2454 		 * a boot environment cannot be any of the following
2455 		 */
2456 
2457 		/* cannot be a diskless client */
2458 
2459 		r = cmd_is_diskless_client(argc, argv, a_gdt);
2460 
2461 		/* cannot be a netinstall_image */
2462 
2463 		if (r != R_SUCCESS) {
2464 			r = cmd_is_netinstall_image(argc, argv, a_gdt);
2465 		}
2466 
2467 		/* cannot be a mounted_miniroot */
2468 
2469 		if (r != R_SUCCESS) {
2470 			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
2471 		}
2472 
2473 		/* no need to guard against recursion any more */
2474 
2475 		recursion--;
2476 
2477 		/* return failure if any of the preceeding are true */
2478 
2479 		switch (r) {
2480 			case R_SUCCESS:
2481 				return (R_FAILURE);
2482 			case R_FAILURE:
2483 				break;
2484 			case R_USAGE:
2485 			case R_ERROR:
2486 			default:
2487 				return (r);
2488 		}
2489 	}
2490 
2491 	/* normalize argc/argv */
2492 
2493 	argc -= optind;
2494 	argv += optind;
2495 
2496 	/* error if more than one argument */
2497 
2498 	if (argc > 1) {
2499 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2500 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2501 		return (R_USAGE);
2502 	}
2503 
2504 	/* process root path if first argument present */
2505 
2506 	if (argc == 1) {
2507 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2508 			return (R_ERROR);
2509 		}
2510 	}
2511 
2512 	/* get current root path */
2513 
2514 	r = getRootPath(&rootPath);
2515 	if (r != R_SUCCESS) {
2516 		return (r);
2517 	}
2518 
2519 	/* start of command debugging information */
2520 
2521 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2522 
2523 	/* root path must not be "/" */
2524 
2525 	if (strcmp(rootPath, "/") == 0) {
2526 		log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ROOTPATH, rootPath, "/");
2527 		return (R_FAILURE);
2528 	}
2529 
2530 	/* zone name must be global */
2531 
2532 	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
2533 		log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ZONE, rootPath,
2534 			GLOBAL_ZONENAME);
2535 		return (R_FAILURE);
2536 	}
2537 
2538 	/* $ROOTDIR/etc/lutab must exist */
2539 
2540 	r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/lutab");
2541 	if (r != R_SUCCESS) {
2542 		log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLUTAB, rootPath,
2543 			"/etc/lutab");
2544 		return (R_FAILURE);
2545 	}
2546 
2547 	/* $ROOTDIR/etc/lu must exist */
2548 
2549 	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
2550 		"%s/%s", rootPath, "/etc/lu");
2551 	if (r != R_SUCCESS) {
2552 		log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLU, rootPath, "/etc/lu");
2553 		return (R_FAILURE);
2554 	}
2555 
2556 	/* must not be initial installation */
2557 
2558 	if ((a_gdt->gd_initialInstall == B_TRUE) &&
2559 	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
2560 		log_msg(LOG_MSG_DEBUG, DBG_BENV_INITIAL_INSTALL, rootPath);
2561 		return (R_FAILURE);
2562 	}
2563 
2564 	/* must not be installation of a zone */
2565 
2566 	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
2567 	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
2568 		/* initial zone install: no path can be boot environment */
2569 		log_msg(LOG_MSG_DEBUG, DBG_BENV_ZONE_INSTALL, rootPath);
2570 		return (R_FAILURE);
2571 	}
2572 
2573 	/* target is a boot environment */
2574 
2575 	log_msg(LOG_MSG_DEBUG, DBG_BENV_IS, rootPath);
2576 
2577 	return (R_SUCCESS);
2578 }
2579 
2580 /*
2581  * Name:	cmd_is_sparse_root_ng_zone
2582  * Description:	determine if target is a sparse non-global zone
2583  * Scope:	public
2584  * Arguments:	argc,argv:
2585  *		  - optional path to target to test
2586  * Returns:	int
2587  *			== 0 - success
2588  *			!= 0 - failure
2589  * IMPLEMENATION:
2590  *  - must be a non-global zone
2591  *  - inherited file systems must be present, and/or
2592  *  - read-only lofs file systems must be present
2593  */
2594 
2595 static int
2596 cmd_is_sparse_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
2597 {
2598 	char	*rootPath = NULL;
2599 	int	c;
2600 	int	r;
2601 static	char	*cmdName = "is_sparse_root_nonglobal_zone";
2602 static	int	recursion = 0;
2603 
2604 	/* process any command line options */
2605 
2606 	while ((c = getopt(argc, argv, ":")) != EOF) {
2607 		switch (c) {
2608 		case '\0':	/* prevent end-of-loop not reached warning */
2609 			break;
2610 		case '?':
2611 		default:
2612 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2613 			return (R_USAGE);
2614 		}
2615 	}
2616 
2617 	/* prevent recursion */
2618 
2619 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2620 
2621 		/* see if this is a non-global zone */
2622 
2623 		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
2624 
2625 		/* no need to guard against recursion any more */
2626 
2627 		recursion--;
2628 
2629 		switch (r) {
2630 			case R_SUCCESS:
2631 				/* is a non-global zone */
2632 				break;
2633 			case R_FAILURE:
2634 				/* not a non-global zone */
2635 				return (R_FAILURE);
2636 			case R_USAGE:
2637 			case R_ERROR:
2638 			default:
2639 				/* cannot determine if non-global zone */
2640 				return (r);
2641 		}
2642 	}
2643 
2644 	/* normalize argc/argv */
2645 
2646 	argc -= optind;
2647 	argv += optind;
2648 
2649 	/* error if more than one argument */
2650 
2651 	if (argc > 1) {
2652 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2653 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2654 		return (R_USAGE);
2655 	}
2656 
2657 	/* process root path if first argument present */
2658 
2659 	if (argc == 1) {
2660 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2661 			return (R_ERROR);
2662 		}
2663 	}
2664 
2665 	/* get current root path */
2666 
2667 	r = getRootPath(&rootPath);
2668 	if (r != R_SUCCESS) {
2669 		return (r);
2670 	}
2671 
2672 	/* start of command debugging information */
2673 
2674 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2675 
2676 	/*
2677 	 * in a non-global zone:
2678 	 * if any file systems are inherited, or if /usr is read only,
2679 	 * then the target is a sparse root non-global zone.
2680 	 */
2681 
2682 	if ((a_gdt->gd_inheritedFileSystems != (char **)NULL) ||
2683 		(a_gdt->gd_srFsMountedRO == B_TRUE)) {
2684 		/* no inherited file systems */
2685 		log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS, rootPath);
2686 		return (R_SUCCESS);
2687 	}
2688 
2689 	/* target is not a sparse root non-global zone */
2690 
2691 	log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS_NOT, rootPath);
2692 
2693 	return (R_FAILURE);
2694 
2695 }
2696 
2697 /*
2698  * Name:	cmd_is_what
2699  * Description:	determine what the target is
2700  * Scope:	public
2701  * Arguments:	argc,argv:
2702  *		  - optional path to target to test
2703  * Returns:	int
2704  *			== 0 - success
2705  *			!= 0 - failure
2706  */
2707 
2708 static int
2709 cmd_is_what(int argc, char **argv, GLOBALDATA_T *a_gdt)
2710 {
2711 	char	*rootPath = NULL;
2712 	int	c;
2713 	int	cur_cmd;
2714 	int	r;
2715 static	char	*cmdName = "is_what";
2716 
2717 	/* process any command line options */
2718 
2719 	while ((c = getopt(argc, argv, ":")) != EOF) {
2720 		switch (c) {
2721 		case '\0':	/* prevent end-of-loop not reached warning */
2722 			break;
2723 		case '?':
2724 		default:
2725 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2726 			return (R_USAGE);
2727 		}
2728 	}
2729 
2730 	/* normalize argc/argv */
2731 
2732 	argc -= optind;
2733 	argv += optind;
2734 
2735 	/* error if more than one argument */
2736 
2737 	if (argc > 1) {
2738 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2739 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2740 		return (R_USAGE);
2741 	}
2742 
2743 	/* process root path if first argument present */
2744 
2745 	if (argc == 1) {
2746 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2747 			return (R_ERROR);
2748 		}
2749 	}
2750 
2751 	/* get current root path */
2752 
2753 	r = getRootPath(&rootPath);
2754 	if (r != R_SUCCESS) {
2755 		return (r);
2756 	}
2757 
2758 	/*
2759 	 * construct the command line for all of the packages
2760 	 */
2761 
2762 	argc = 0;
2763 	argv[argc++] = strdup(get_prog_name());
2764 	argv[argc++] = strdup(rootPath);
2765 
2766 	/* start of command debugging information */
2767 
2768 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2769 
2770 	/* search for specified subcommand and execute if found */
2771 
2772 	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
2773 		int	result;
2774 
2775 		/* do not recursively call this function */
2776 
2777 		if (cmds[cur_cmd].c_func == cmd_is_what) {
2778 			continue;
2779 		}
2780 
2781 		/* call subcommand with its own argc/argv */
2782 
2783 		result = cmds[cur_cmd].c_func(argc, argv, a_gdt);
2784 
2785 		/* process result code and exit */
2786 
2787 		result = adjustResults(result);
2788 		log_msg(LOG_MSG_INFO, MSG_IS_WHAT_RESULT,
2789 			cmds[cur_cmd].c_name, result);
2790 	}
2791 	return (R_SUCCESS);
2792 }
2793 
2794 /*
2795  * Name:	cmd_is_whole_root_ng_zone
2796  * Description:	determine if target is a global zone
2797  * Scope:	public
2798  * Arguments:	argc,argv:
2799  *		  - optional path to target to test
2800  * Returns:	int
2801  *			== 0 - success
2802  *			!= 0 - failure
2803  * IMPLEMENTATION:
2804  *  - must be a non-global zone
2805  *  - no inherited file systems may be present
2806  *  - no read-only lofs file systems may be present
2807  */
2808 
2809 static int
2810 cmd_is_whole_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
2811 {
2812 	char	*rootPath = NULL;
2813 	int	c;
2814 	int	r;
2815 static	char	*cmdName = "is_whole_root_nonglobal_zone";
2816 static	int	recursion = 0;
2817 
2818 	/* process any command line options */
2819 
2820 	while ((c = getopt(argc, argv, ":")) != EOF) {
2821 		switch (c) {
2822 		case '\0':	/* prevent end-of-loop not reached warning */
2823 			break;
2824 		case '?':
2825 		default:
2826 			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2827 			return (R_USAGE);
2828 		}
2829 	}
2830 
2831 	/* prevent recursion */
2832 
2833 	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2834 
2835 		/* see if this is a non-global zone */
2836 
2837 		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
2838 
2839 		/* no need to guard against recursion any more */
2840 
2841 		recursion--;
2842 
2843 		switch (r) {
2844 			case R_SUCCESS:
2845 				/* is a non-global zone */
2846 				break;
2847 			case R_FAILURE:
2848 				/* not a non-global zone */
2849 				return (R_FAILURE);
2850 			case R_USAGE:
2851 			case R_ERROR:
2852 			default:
2853 				/* cannot determine if non-global zone */
2854 				return (r);
2855 		}
2856 	}
2857 
2858 	/* normalize argc/argv */
2859 
2860 	argc -= optind;
2861 	argv += optind;
2862 
2863 	/* error if more than one argument */
2864 
2865 	if (argc > 1) {
2866 		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2867 		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2868 		return (R_USAGE);
2869 	}
2870 
2871 	/* process root path if first argument present */
2872 
2873 	if (argc == 1) {
2874 		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2875 			return (R_ERROR);
2876 		}
2877 	}
2878 
2879 	/* get current root path */
2880 
2881 	r = getRootPath(&rootPath);
2882 	if (r != R_SUCCESS) {
2883 		return (r);
2884 	}
2885 
2886 	/* start of command debugging information */
2887 
2888 	echoDebug(DBG_ROOTPATH_IS, rootPath);
2889 
2890 	/*
2891 	 * in a non-global zone:
2892 	 * if no file systems are inherited, and if /usr is not
2893 	 * read only, then the target is a whole root non-global zone.
2894 	 */
2895 
2896 	if ((a_gdt->gd_inheritedFileSystems == (char **)NULL) &&
2897 		(a_gdt->gd_srFsMountedRO == B_FALSE)) {
2898 		/* no inherited file systems */
2899 		log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS, rootPath);
2900 		return (R_SUCCESS);
2901 	}
2902 
2903 	/* target is not a whole-root non-global zone */
2904 
2905 	log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS_NOT, rootPath);
2906 
2907 	return (R_FAILURE);
2908 }
2909 
2910 /*
2911  * *****************************************************************************
2912  * utility support functions
2913  * *****************************************************************************
2914  */
2915 
2916 /*
2917  * Name:	getMountOption
2918  * Description:	return next mount option in a string
2919  * Arguments:	p - pointer to string containing mount options
2920  * Output:	none
2921  * Returns:	char * - pointer to next option in string "p"
2922  * Side Effects: advances input "p" and inserts \0 in place of the
2923  *		option separator found.
2924  */
2925 
2926 static char *
2927 getMountOption(char **p)
2928 {
2929 	char *cp = *p;
2930 	char *retstr;
2931 
2932 	/* advance past all white space */
2933 
2934 	while (*cp && isspace(*cp))
2935 		cp++;
2936 
2937 	/* remember start of next option */
2938 
2939 	retstr = cp;
2940 
2941 	/* advance to end of string or option separator */
2942 
2943 	while (*cp && *cp != ',')
2944 		cp++;
2945 
2946 	/* replace separator with '\0' if not at end of string */
2947 	if (*cp) {
2948 		*cp = '\0';
2949 		cp++;
2950 	}
2951 
2952 	/* reset caller's pointer and return pointer to option */
2953 
2954 	*p = cp;
2955 	return (retstr);
2956 }
2957 
2958 /*
2959  * Name:	mountOptionPresent
2960  * Description:	determine if specified mount option is present in list
2961  *		of mount point options
2962  * Arguments:	a_mntOptions - pointer to string containing list of mount
2963  *			point options to search
2964  *		a_opt - pointer to string containing option to search for
2965  * Output:	none
2966  * Returns:	R_SUCCESS - option is present in list of mount point options
2967  *		R_FAILURE - options is not present
2968  *		R_ERROR - unable to determine if option is present or not
2969  */
2970 
2971 static int
2972 mountOptionPresent(char *a_mntOptions, char *a_opt)
2973 {
2974 	char tmpopts[MNT_LINE_MAX];
2975 	char *f, *opts = tmpopts;
2976 
2977 	/* return false if no mount options present */
2978 
2979 	if ((a_opt == NULL) || (*a_opt == '\0')) {
2980 		return (R_FAILURE);
2981 	}
2982 
2983 	/* return not present if no list of options to search */
2984 
2985 	if (a_mntOptions == NULL) {
2986 		return (R_FAILURE);
2987 	}
2988 
2989 	/* return not present if list of options to search is empty */
2990 
2991 	if (*a_mntOptions == '\0') {
2992 		return (R_FAILURE);
2993 	}
2994 
2995 	/* make local copy of option list to search */
2996 
2997 	(void) strcpy(opts, a_mntOptions);
2998 
2999 	/* scan each option looking for the specified option */
3000 
3001 	f = getMountOption(&opts);
3002 	for (; *f; f = getMountOption(&opts)) {
3003 		/* return success if option matches target */
3004 		if (strncmp(a_opt, f, strlen(a_opt)) == 0) {
3005 			return (R_SUCCESS);
3006 		}
3007 	}
3008 
3009 	/* option not found */
3010 
3011 	return (R_FAILURE);
3012 }
3013 
3014 /*
3015  * Name:	sortedInsert
3016  * Description:	perform an alphabetical sorted insert into a list
3017  * Arguments:	r_list - pointer to list to insert next entry into
3018  *		a_listSize - pointer to current list size
3019  *		a_mntPoint - mount point to insert (is sort key)
3020  *		a_fsType - file system type for mount point
3021  *		a_mntOptions - file syste mount options for mount point
3022  * Output:	None
3023  * Returns:	None
3024  */
3025 
3026 static void
3027 sortedInsert(FSI_T **r_list, long *a_listSize, char *a_mntPoint,
3028 	char *a_fsType, char *a_mntOptions)
3029 {
3030 	int	listSize;
3031 	FSI_T	*list;
3032 	int	n;
3033 
3034 	/* entry assertions */
3035 
3036 	assert(a_listSize != (long *)NULL);
3037 	assert(a_mntPoint != NULL);
3038 	assert(a_fsType != NULL);
3039 	assert(a_mntOptions != NULL);
3040 
3041 	/* entry debugging info */
3042 
3043 	echoDebug(DBG_SINS_ENTRY, a_mntPoint, a_fsType, a_mntOptions);
3044 
3045 	/* localize references to the list and list size */
3046 
3047 	listSize = *a_listSize;
3048 	list = *r_list;
3049 
3050 	/*
3051 	 * if list empty insert this entry as the first one in the list
3052 	 */
3053 
3054 	if (listSize == 0) {
3055 		/* allocate new entry for list */
3056 		listSize++;
3057 		list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
3058 
3059 		/* first entry is data passed to this function */
3060 		list[0].fsi_mntPoint = strdup(a_mntPoint);
3061 		list[0].fsi_fsType = strdup(a_fsType);
3062 		list[0].fsi_mntOptions = strdup(a_mntOptions);
3063 
3064 		/* second entry is all NULL - end of entry marker */
3065 		list[1].fsi_mntPoint = NULL;
3066 		list[1].fsi_fsType = NULL;
3067 		list[1].fsi_mntOptions = NULL;
3068 
3069 		/* restore list and list size references to caller */
3070 		*a_listSize = listSize;
3071 		*r_list = list;
3072 
3073 		return;
3074 	}
3075 
3076 	/*
3077 	 * list not empty - scan looking for largest match
3078 	 */
3079 
3080 	for (n = 0; n < listSize; n++) {
3081 		int	c;
3082 
3083 		/* compare target with current list entry */
3084 
3085 		c = strcmp(list[n].fsi_mntPoint, a_mntPoint);
3086 
3087 		if (c == 0) {
3088 			char	*me;
3089 			long	len;
3090 
3091 			/* entry already in list -- merge entries */
3092 
3093 			len = strlen(list[n].fsi_mntOptions) +
3094 				strlen(a_mntOptions) + 2;
3095 			me = (char *)calloc(1, len);
3096 
3097 			/* merge two mount options lists into one */
3098 
3099 			(void) strlcat(me, list[n].fsi_mntOptions, len);
3100 			(void) strlcat(me, ",", len);
3101 			(void) strlcat(me, a_mntOptions, len);
3102 
3103 			/* free old list, replace with merged one */
3104 
3105 			free(list[n].fsi_mntOptions);
3106 			list[n].fsi_mntOptions = me;
3107 
3108 			echoDebug(DBG_SORTEDINS_SKIPPED,
3109 				n, list[n].fsi_mntPoint, a_fsType,
3110 				list[n].fsi_fsType, a_mntOptions,
3111 				list[n].fsi_mntOptions);
3112 
3113 			continue;
3114 		} else if (c < 0) {
3115 			/* entry before this one - skip */
3116 			continue;
3117 		}
3118 
3119 		/*
3120 		 * entry after this one - insert new entry
3121 		 */
3122 
3123 		/* allocate one more entry and make space for new entry */
3124 		listSize++;
3125 		list = (FSI_T *)realloc(list,
3126 			sizeof (FSI_T)*(listSize+1));
3127 		(void) memmove(&(list[n+1]), &(list[n]),
3128 			sizeof (FSI_T)*(listSize-n));
3129 
3130 		/* insert this entry into list */
3131 		list[n].fsi_mntPoint = strdup(a_mntPoint);
3132 		list[n].fsi_fsType = strdup(a_fsType);
3133 		list[n].fsi_mntOptions = strdup(a_mntOptions);
3134 
3135 		/* restore list and list size references to caller */
3136 		*a_listSize = listSize;
3137 		*r_list = list;
3138 
3139 		return;
3140 	}
3141 
3142 	/*
3143 	 * all entries are before this one - append to end of list
3144 	 */
3145 
3146 	/* allocate new entry at end of list */
3147 	listSize++;
3148 	list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
3149 
3150 	/* append this entry to the end of the list */
3151 	list[listSize-1].fsi_mntPoint = strdup(a_mntPoint);
3152 	list[listSize-1].fsi_fsType = strdup(a_fsType);
3153 	list[listSize-1].fsi_mntOptions = strdup(a_mntOptions);
3154 
3155 	/* restore list and list size references to caller */
3156 	*a_listSize = listSize;
3157 	*r_list = list;
3158 }
3159 
3160 /*
3161  * Name:	calculateFileSystemConfig
3162  * Description:	generate sorted list of all mounted file systems
3163  * Arguments:	a_gdt - global data structure to place sorted entries into
3164  * Output:	None
3165  * Returns:	R_SUCCESS - successfully generated mounted file systems list
3166  *		R_FAILURE - options is not present
3167  *		R_ERROR - unable to determine if option is present or not
3168  */
3169 
3170 static int
3171 calculateFileSystemConfig(GLOBALDATA_T *a_gdt)
3172 {
3173 	FILE		*fp;
3174 	struct mnttab	mntbuf;
3175 	FSI_T		*list;
3176 	long		listSize;
3177 	boolean_t	readOnlyMountFound = B_FALSE;
3178 
3179 	/* entry assetions */
3180 
3181 	assert(a_gdt != (GLOBALDATA_T *)NULL);
3182 
3183 	/* entry debugging info */
3184 
3185 	echoDebug(DBG_CALCSCFG_ENTRY);
3186 
3187 	/* allocate a list that has one termination entry */
3188 
3189 	list = (FSI_T *)calloc(1, sizeof (FSI_T));
3190 	list[0].fsi_mntPoint = NULL;
3191 	list[0].fsi_fsType = NULL;
3192 	list[0].fsi_mntOptions = NULL;
3193 	listSize = 0;
3194 
3195 	/* insert entries for all inherited file systems */
3196 
3197 	if (a_gdt->gd_inheritedFileSystems) {
3198 		int n;
3199 		char **ifs = a_gdt->gd_inheritedFileSystems;
3200 
3201 		/* debugging info */
3202 
3203 		echoDebug(DBG_CALCSCFG_INHERITED);
3204 
3205 		/* insert all inherited file systems */
3206 
3207 		for (n = 0; ifs[n]; n++) {
3208 			sortedInsert(&list, &listSize, ifs[n],
3209 				FSTYPE_INHERITED, MNTOPT_RO);
3210 		}
3211 	}
3212 
3213 	/* open the mount table for reading */
3214 
3215 	fp = fopen(MNTTAB, "r");
3216 	if (fp == (FILE *)NULL) {
3217 		return (R_ERROR);
3218 	}
3219 
3220 	/* debugging info */
3221 
3222 	echoDebug(DBG_CALCSCFG_MOUNTED);
3223 
3224 	/* go through all the specials looking for the device */
3225 
3226 	while (getmntent(fp, &mntbuf) == 0) {
3227 		if (mntbuf.mnt_mountp[0] == '/') {
3228 			sortedInsert(&list, &listSize,
3229 			strdup(mntbuf.mnt_mountp),
3230 			strdup(mntbuf.mnt_fstype),
3231 			strdup(mntbuf.mnt_mntopts ? mntbuf.mnt_mntopts : ""));
3232 		}
3233 
3234 		/*
3235 		 * Set flag if we are in a non-global zone and it is in
3236 		 * the mounted state.
3237 		 */
3238 
3239 		if (strcmp(mntbuf.mnt_mountp, "/a") == 0 &&
3240 			strcmp(mntbuf.mnt_special, "/a") == 0 &&
3241 			strcmp(mntbuf.mnt_fstype, "lofs") == 0) {
3242 			a_gdt->inMountedState = B_TRUE;
3243 		}
3244 
3245 		if (!readOnlyMountFound) {
3246 			readOnlyMountFound = checkForReadOnlyMount(a_gdt,
3247 			    mntbuf.mnt_mountp, mntbuf.mnt_fstype,
3248 			    mntbuf.mnt_mntopts);
3249 		}
3250 	}
3251 
3252 	/* close mount table file */
3253 
3254 	(void) fclose(fp);
3255 
3256 	/* store list pointers in global data structure */
3257 
3258 	a_gdt->gd_fileSystemConfig = list;
3259 	a_gdt->gd_fileSystemConfigLen = listSize;
3260 
3261 	return (R_SUCCESS);
3262 }
3263 
3264 /*
3265  * Name:	checkForReadOnlyMount
3266  * Description:	given a mount point, type and options, determine if the
3267  *              mounted file system is part of a "sparse root" configuration
3268  *              by checking if the known Zone directories a ro LOFS mounted.
3269  * Arguments:	a_gdt - global data structure to place sorted entries into
3270  *		a_mntPoint - pointer to string representing mount point
3271  *		a_fsType - pointer to string representing file system type
3272  *		a_mntOptions - pointer to string representing the options
3273  *			used to mount the file system
3274  * Returns:	B_TRUE - if sparse root mount is found
3275  * 		B_FLASE - if no sparse root mount's are found
3276  * Side Effects: set:
3277  *			a_gdt->gd_srFsMountedRO = B_TRUE
3278  *		if the mounted file system is part of a 'sparse root' config
3279  */
3280 
3281 static boolean_t
3282 checkForReadOnlyMount(GLOBALDATA_T *a_gdt, char *a_mntPoint,
3283 	char *a_fsType, char *a_mntOptions)
3284 {
3285 	/* entry assertions */
3286 	int	i;
3287 	char mntPoint[MAXPATHLEN];
3288 	char *zDirs[] = { "/usr", "/lib", "/platform", "/sbin", NULL };
3289 	char *aZDirs[] = { "/a/usr", "/a/lib", "/a/platform", "/a/sbin", NULL };
3290 
3291 	assert(a_gdt != (GLOBALDATA_T *)NULL);
3292 	assert(a_mntPoint != NULL);
3293 	assert(a_fsType != NULL);
3294 
3295 	/* return if no read-only mount option */
3296 
3297 	if (mountOptionPresent(a_mntOptions, MNTOPT_RO) != R_SUCCESS) {
3298 		return (B_FALSE);
3299 	}
3300 
3301 	/* return if file system is not read-only mounted */
3302 
3303 	if (strcmp(a_fsType, MNTTYPE_LOFS) != 0) {
3304 		return (B_FALSE);
3305 	}
3306 
3307 	/* file system is a read-only lofs mounted.  */
3308 
3309 	/* Check read-only lofs mount's appended to the command line path */
3310 
3311 	if (a_gdt->gd_cmdline_path != NULL) {
3312 		if (strncmp(a_mntPoint, a_gdt->gd_cmdline_path,
3313 			strlen(a_gdt->gd_cmdline_path)) == 0) {
3314 			for (i = 0; zDirs[i] != NULL; i++) {
3315 				(void) snprintf(mntPoint, sizeof (mntPoint),
3316 				    "%s%s", a_gdt->gd_cmdline_path,
3317 				    zDirs[i]);
3318 				if (strcmp(a_mntPoint, mntPoint) == 0) {
3319 					echoDebug(DBG_CKSR_FSREADONLY,
3320 					    a_mntPoint, a_fsType);
3321 					a_gdt->gd_srFsMountedRO = B_TRUE;
3322 					return (B_TRUE);
3323 				}
3324 			}
3325 		}
3326 
3327 	/* Check read-only lofs mount's in the mounted state */
3328 
3329 	} else if (a_gdt->inMountedState) {
3330 		for (i = 0; aZDirs[i] != NULL; i++) {
3331 			if (strncmp(a_mntPoint, aZDirs[i],
3332 			    sizeof (aZDirs[i])) == 0) {
3333 				echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
3334 				    a_fsType);
3335 				a_gdt->gd_srFsMountedRO = B_TRUE;
3336 				return (B_TRUE);
3337 			}
3338 		}
3339 
3340 	/* Check read-only lofs mount's for live system */
3341 
3342 	} else {
3343 		for (i = 0; zDirs[i] != NULL; i++) {
3344 			if (strncmp(a_mntPoint, zDirs[i],
3345 			    sizeof (zDirs[i])) == 0) {
3346 				echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
3347 				    a_fsType);
3348 				a_gdt->gd_srFsMountedRO = B_TRUE;
3349 				return (B_TRUE);
3350 			}
3351 		}
3352 	}
3353 
3354 	return (B_FALSE);
3355 }
3356 
3357 /*
3358  * Name: 	adjustResults
3359  * Description:	adjust output result code before existing
3360  * Arguments:	a_result - result code to adjust
3361  * Returns:	int - adjusted result code
3362  */
3363 
3364 static int
3365 adjustResults(int a_result)
3366 {
3367 	boolean_t	negate = getNegateResults();
3368 	int		realResult;
3369 
3370 	/* adjust code as appropriate */
3371 
3372 	switch (a_result) {
3373 	case R_SUCCESS:		/* condition satisfied */
3374 		realResult = ((negate == B_TRUE) ? 1 : 0);
3375 		break;
3376 	case R_FAILURE:		/* condition not satisfied */
3377 		realResult = ((negate == B_TRUE) ? 0 : 1);
3378 		break;
3379 	case R_USAGE:		/* usage errors */
3380 		realResult = 2;
3381 		break;
3382 	case R_ERROR:		/* condition could not be determined */
3383 	default:
3384 		realResult = 3;
3385 		break;
3386 	}
3387 
3388 	/* debugging output */
3389 
3390 	log_msg(LOG_MSG_DEBUG, DBG_ADJUST_RESULTS, a_result, negate,
3391 		realResult);
3392 
3393 	/* return results */
3394 
3395 	return (realResult);
3396 }
3397 
3398 /*
3399  * Name:        setCmdLinePath
3400  * Description:	set global command line path
3401  * Arguments:   path - path to set from the command line
3402  *              args - command line args
3403  *              num_args - number of command line args
3404  * Returns:     R_SUCCESS - root path successfully set
3405  *              R_FAILURE - root path could not be set
3406  *              R_ERROR - fatal error attempting to set root path
3407  */
3408 
3409 static void
3410 setCmdLinePath(char **path, char **args, int num_args)
3411 {
3412 	char   rp[PATH_MAX] = { '\0' };
3413 	struct stat statbuf;
3414 
3415 	if (*path != NULL) {
3416 		return;
3417 	}
3418 
3419 	/*
3420 	 * If a path "pkgcond is_global_zone [path]" is provided on the
3421 	 * command line it must be the last argument.
3422 	 */
3423 
3424 	if (realpath(args[num_args - 1], rp) != NULL) {
3425 		if (stat(rp, &statbuf) == 0) {
3426 			/* make sure the target is a directory */
3427 			if ((statbuf.st_mode & S_IFDIR)) {
3428 				*path = strdup(rp);
3429 			} else {
3430 				*path = NULL;
3431 			}
3432 		}
3433 	}
3434 }
3435 
3436 /*
3437  * Name:	setRootPath
3438  * Description:	set global root path returned by getRootPath
3439  * Arguments:	a_path - root path to set
3440  *		a_mustExist - B_TRUE if path must exist (else error)
3441  *			- B_FALSE if path may not exist
3442  * Returns:	R_SUCCESS - root path successfully set
3443  *		R_FAILURE - root path could not be set
3444  *		R_ERROR - fatal error attempting to set root path
3445  */
3446 
3447 static int
3448 setRootPath(char *a_path, char *a_envVar, boolean_t a_mustExist)
3449 {
3450 	char		rp[PATH_MAX] = { '\0' };
3451 	struct stat	statbuf;
3452 
3453 	/* if no data then issue warning and return success */
3454 
3455 	if ((a_path == NULL) || (*a_path == '\0')) {
3456 		echoDebug(DBG_NO_DEFAULT_ROOT_PATH_SET);
3457 		return (R_SUCCESS);
3458 	}
3459 
3460 	/* path present - resolve to absolute path */
3461 
3462 	if (realpath(a_path, rp) == NULL) {
3463 		if (a_mustExist == B_TRUE) {
3464 			/* must exist ... error */
3465 			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
3466 				a_path, strerror(errno));
3467 			return (R_ERROR);
3468 		} else {
3469 			/* may not exist - use path as specified */
3470 			(void) strcpy(rp, a_path);
3471 		}
3472 	}
3473 
3474 	/* debugging output */
3475 
3476 	echoDebug(DBG_DEFAULT_ROOT_PATH_SET, rp, a_envVar ? a_envVar : "");
3477 
3478 	/* validate path existence if it must exist */
3479 
3480 	if (a_mustExist == B_TRUE) {
3481 
3482 		/* get node status */
3483 
3484 		if (stat(rp, &statbuf) != 0) {
3485 			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
3486 				rp, strerror(errno));
3487 			return (R_ERROR);
3488 		}
3489 
3490 		/* make sure the target is a directory */
3491 
3492 		if (!(statbuf.st_mode & S_IFDIR)) {
3493 			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_NOT_DIR, rp);
3494 			return (R_ERROR);
3495 		}
3496 	}
3497 
3498 	/* target exists and is a directory - set */
3499 
3500 	echoDebug(DBG_SET_ROOT_PATH_TO, rp);
3501 
3502 	/* store copy of resolved root path */
3503 
3504 	_rootPath = strdup(rp);
3505 
3506 	/* success! */
3507 
3508 	return (R_SUCCESS);
3509 }
3510 
3511 /*
3512  * Name:	testPath
3513  * Description:	determine if a path meets the specified conditions
3514  * Arguments:	a_tt - conditions to test path against
3515  * 		a_format - format to use to generate path
3516  *		arguments following a_format - as needed for a_format
3517  * Returns:	R_SUCCESS - the path meets all of the specified conditions
3518  *		R_FAILURE - the path does not meet all of the conditions
3519  *		R_ERROR - error attempting to test path
3520  */
3521 
3522 /*PRINTFLIKE2*/
3523 static int
3524 testPath(TEST_TYPES a_tt, char *a_format, ...)
3525 {
3526 	char		*mbPath;	/* copy for the path to be returned */
3527 	char		bfr[1];
3528 	int		r;
3529 	size_t		vres = 0;
3530 	struct stat	statbuf;
3531 	va_list		ap;
3532 	int		fd;
3533 
3534 	/* entry assertions */
3535 
3536 	assert(a_format != NULL);
3537 	assert(*a_format != '\0');
3538 
3539 	/* determine size of the message in bytes */
3540 
3541 	va_start(ap, a_format);
3542 	vres = vsnprintf(bfr, 1, a_format, ap);
3543 	va_end(ap);
3544 
3545 	assert(vres > 0);
3546 
3547 	/* allocate storage to hold the message */
3548 
3549 	mbPath = (char *)calloc(1, vres+2);
3550 	assert(mbPath != NULL);
3551 
3552 	/* generate the results of the printf conversion */
3553 
3554 	va_start(ap, a_format);
3555 	vres = vsnprintf(mbPath, vres+1, a_format, ap);
3556 	va_end(ap);
3557 
3558 	assert(vres > 0);
3559 
3560 	echoDebug(DBG_TEST_PATH, mbPath, (unsigned long)a_tt);
3561 
3562 	/*
3563 	 * When a path given to open(2) contains symbolic links, the
3564 	 * open system call first resolves all symbolic links and then
3565 	 * opens that final "resolved" path. As a result, it is not
3566 	 * possible to check the result of an fstat(2) against the
3567 	 * file descriptor returned by open(2) for S_IFLNK (a symbolic
3568 	 * link) since all symbolic links are resolved before the
3569 	 * target is opened.
3570 	 *
3571 	 * When testing the target as being (or not being) a symbolic
3572 	 * link, first use lstat(2) against the target to determine
3573 	 * whether or not the specified target itself is (or is not) a
3574 	 * symbolic link.
3575 	 */
3576 
3577 	if (a_tt & (TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)) {
3578 		/*
3579 		 * testing target is/is not a symbolic link; use lstat
3580 		 * to determine the status of the target itself rather
3581 		 * than what the target might finally address.
3582 		 */
3583 
3584 		if (lstat(mbPath, &statbuf) != 0) {
3585 			echoDebug(DBG_CANNOT_LSTAT_PATH, mbPath,
3586 				strerror(errno));
3587 			free(mbPath);
3588 			return (R_FAILURE);
3589 		}
3590 
3591 		/* Is the target required to be a symbolic link? */
3592 
3593 		if (a_tt & TEST_IS_SYMBOLIC_LINK) {
3594 			/* target must be a symbolic link */
3595 			if (!(statbuf.st_mode & S_IFLNK)) {
3596 				/* failure: target is not a symbolic link */
3597 				echoDebug(DBG_IS_NOT_A_SYMLINK, mbPath);
3598 				free(mbPath);
3599 				return (R_FAILURE);
3600 			}
3601 			/* success: target is a symbolic link */
3602 			echoDebug(DBG_SYMLINK_IS, mbPath);
3603 		}
3604 
3605 		/* Is the target required to not be a symbolic link? */
3606 
3607 		if (a_tt & TEST_NOT_SYMBOLIC_LINK) {
3608 			/* target must not be a symbolic link */
3609 			if (statbuf.st_mode & S_IFLNK) {
3610 				/* failure: target is a symbolic link */
3611 				echoDebug(DBG_IS_A_SYMLINK, mbPath);
3612 				free(mbPath);
3613 				return (R_FAILURE);
3614 			}
3615 			/* success: target is not a symbolic link */
3616 			echoDebug(DBG_SYMLINK_NOT, mbPath);
3617 		}
3618 
3619 		/*
3620 		 * if only testing is/is not a symbolic link, then
3621 		 * no need to open the target: return success.
3622 		 */
3623 
3624 		if (!(a_tt &
3625 		    (~(TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)))) {
3626 			free(mbPath);
3627 			return (R_SUCCESS);
3628 		}
3629 	}
3630 
3631 	/* resolve path and remove any whitespace */
3632 
3633 	r = resolvePath(&mbPath);
3634 	if (r != R_SUCCESS) {
3635 		echoDebug(DBG_TEST_PATH_NO_RESOLVE, mbPath);
3636 		free(mbPath);
3637 		if (a_tt & TEST_NOT_EXISTS) {
3638 			return (R_SUCCESS);
3639 		}
3640 		return (r);
3641 	}
3642 
3643 	echoDebug(DBG_TEST_PATH_RESOLVE, mbPath);
3644 
3645 	/* open the file - this is the basic existence test */
3646 
3647 	fd = open(mbPath, O_RDONLY|O_LARGEFILE, 0);
3648 
3649 	/* existence test failed if file cannot be opened */
3650 
3651 	if (fd < 0) {
3652 		/*
3653 		 * target could not be opened - if testing for non-existence,
3654 		 * return success, otherwise return failure
3655 		 */
3656 		if (a_tt & TEST_NOT_EXISTS) {
3657 			echoDebug(DBG_CANNOT_ACCESS_PATH_OK, mbPath);
3658 			free(mbPath);
3659 			return (R_SUCCESS);
3660 		}
3661 
3662 		echoDebug(DBG_CANNOT_ACCESS_PATH_BUT_SHOULD,
3663 		    mbPath, strerror(errno));
3664 		free(mbPath);
3665 
3666 		return (R_FAILURE);
3667 	}
3668 
3669 	/*
3670 	 * target successfully opened - if testing for non-existence,
3671 	 * return failure, otherwise continue with specified tests
3672 	 */
3673 
3674 	if (a_tt & TEST_NOT_EXISTS) {
3675 		/* testing for non-existence: return failure */
3676 		echoDebug(DBG_TEST_EXISTS_SHOULD_NOT, mbPath);
3677 		free(mbPath);
3678 		(void) close(fd);
3679 		return (R_FAILURE);
3680 	}
3681 
3682 	/* get the file status */
3683 
3684 	r = fstat(fd, &statbuf);
3685 	if (r != 0) {
3686 		echoDebug(DBG_PATH_DOES_NOT_EXIST, mbPath, strerror(errno));
3687 		(void) close(fd);
3688 		free(mbPath);
3689 		return (R_FAILURE);
3690 	}
3691 
3692 	/* required to be a directory? */
3693 
3694 	if (a_tt & TEST_IS_DIRECTORY) {
3695 		if (!(statbuf.st_mode & S_IFDIR)) {
3696 			/* is not a directory */
3697 			echoDebug(DBG_IS_NOT_A_DIRECTORY, mbPath);
3698 			free(mbPath);
3699 			return (R_FAILURE);
3700 		}
3701 		/* a directory */
3702 		echoDebug(DBG_DIRECTORY_IS, mbPath);
3703 	}
3704 
3705 	/* required to not be a directory? */
3706 
3707 	if (a_tt & TEST_NOT_DIRECTORY) {
3708 		if (statbuf.st_mode & S_IFDIR) {
3709 			/* is a directory */
3710 			echoDebug(DBG_IS_A_DIRECTORY, mbPath);
3711 			free(mbPath);
3712 			return (R_FAILURE);
3713 		}
3714 		/* not a directory */
3715 		echoDebug(DBG_DIRECTORY_NOT, mbPath);
3716 	}
3717 
3718 	/* required to be a file? */
3719 
3720 	if (a_tt & TEST_IS_FILE) {
3721 		if (!(statbuf.st_mode & S_IFREG)) {
3722 			/* is not a regular file */
3723 			echoDebug(DBG_IS_NOT_A_FILE, mbPath);
3724 			free(mbPath);
3725 			return (R_FAILURE);
3726 		}
3727 		/* a regular file */
3728 		echoDebug(DBG_FILE_IS, mbPath);
3729 	}
3730 
3731 	/* required to not be a file? */
3732 
3733 	if (a_tt & TEST_NOT_FILE) {
3734 		if (statbuf.st_mode & S_IFREG) {
3735 			/* is a regular file */
3736 			echoDebug(DBG_IS_A_FILE, mbPath);
3737 			free(mbPath);
3738 			return (R_FAILURE);
3739 		}
3740 		/* not a regular file */
3741 		echoDebug(DBG_FILE_NOT, mbPath);
3742 	}
3743 
3744 	/*
3745 	 * Find token (global) in file pointed to by mbPath.
3746 	 * token is only compared to first word in mbPath.
3747 	 */
3748 
3749 	if (a_tt & TEST_GLOBAL_TOKEN_IN_FILE) {
3750 		if (!(statbuf.st_mode & S_IFREG)) {
3751 			/* is not a regular file */
3752 			echoDebug(DBG_IS_NOT_A_FILE, mbPath);
3753 			free(mbPath);
3754 			return (R_FAILURE);
3755 		}
3756 		/* If global exists then we're not in a non-global zone */
3757 		if (findToken(mbPath, GLOBAL_ZONENAME) == R_SUCCESS) {
3758 			echoDebug(DBG_TOKEN__EXISTS, GLOBAL_ZONENAME, mbPath);
3759 			free(mbPath);
3760 			return (R_FAILURE);
3761 		}
3762 	}
3763 
3764 	(void) close(fd);
3765 
3766 	/* success! */
3767 
3768 	echoDebug(DBG_TESTPATH_OK, mbPath);
3769 
3770 	/* free up temp storage used to hold path to test */
3771 
3772 	free(mbPath);
3773 
3774 	return (R_SUCCESS);
3775 }
3776 
3777 /*
3778  * Name:        findToken
3779  * Description:	Find first token in file.
3780  * Arguments:
3781  *              path - file to search for token
3782  *              token - string to search for
3783  * Returns:
3784  *              R_SUCCESS - the token exists
3785  *              R_FAILURE - the token does not exist
3786  *              R_ERROR - fatal error attempting to find token
3787  */
3788 
3789 static int
3790 findToken(char *path, char *token)
3791 {
3792 	FILE	*fp;
3793 	char	*cp;
3794 	char	line[MAXPATHLEN];
3795 
3796 	if (path == NULL || token == NULL) {
3797 		return (R_ERROR);
3798 	}
3799 	if ((fp = fopen(path, "r")) == NULL) {
3800 		return (R_ERROR);
3801 	}
3802 
3803 	while (fgets(line, sizeof (line), fp) != NULL) {
3804 		for (cp = line; *cp && isspace(*cp); cp++);
3805 		/* skip comments */
3806 		if (*cp == '#') {
3807 			continue;
3808 		}
3809 		if (pkgstrContainsToken(cp, token, ":")) {
3810 			(void) fclose(fp);
3811 			return (R_SUCCESS);
3812 		}
3813 	}
3814 	(void) fclose(fp);
3815 	return (R_FAILURE);
3816 }
3817 
3818 
3819 /*
3820  * Name:	resolvePath
3821  * Description:	fully resolve a path to an absolute real path
3822  * Arguments:	r_path - pointer to pointer to malloc()ed storage containing
3823  *			the path to resolve - this path may be reallocated
3824  *			as necessary to hold the fully resolved path
3825  * Output:	r_path - is realloc()ed as necessary
3826  * Returns:	R_SUCCESS - the path is fully resolved
3827  *		R_FAILURE - the path could not be resolved
3828  *		R_ERROR - fatal error attempting to resolve path
3829  */
3830 
3831 static int
3832 resolvePath(char **r_path)
3833 {
3834 	int		i;
3835 	char		resolvedPath[MAXPATHLEN+1] = {'\0'};
3836 	size_t		mbPathlen;	/* length of multi-byte path */
3837 	size_t		wcPathlen;	/* length of wide-character path */
3838 	wchar_t		*wcPath;	/* wide-character version of the path */
3839 	wchar_t		*wptr;		/* scratch pointer */
3840 
3841 	/* entry assertions */
3842 
3843 	assert(r_path != (char **)NULL);
3844 
3845 	/* return error if the path is completely empty */
3846 
3847 	if (*r_path == '\0') {
3848 		return (R_FAILURE);
3849 	}
3850 
3851 	/* remove all leading whitespace */
3852 
3853 	removeLeadingWhitespace(r_path);
3854 
3855 	/*
3856 	 * convert to real path: an absolute pathname that names the same file,
3857 	 * whose resolution does not involve ".", "..",  or  symbolic links.
3858 	 */
3859 
3860 	if (realpath(*r_path, resolvedPath) != NULL) {
3861 		free(*r_path);
3862 		*r_path = strdup(resolvedPath);
3863 	}
3864 
3865 	/*
3866 	 *  convert the multi-byte version of the path to a
3867 	 *  wide-character rendering, for doing our figuring.
3868 	 */
3869 
3870 	mbPathlen = strlen(*r_path);
3871 
3872 	if ((wcPath = (wchar_t *)
3873 		calloc(1, sizeof (wchar_t)*(mbPathlen+1))) == NULL) {
3874 		return (R_FAILURE);
3875 	}
3876 
3877 	/*LINTED*/
3878 	if ((wcPathlen = mbstowcs(wcPath, *r_path, mbPathlen)) == -1) {
3879 		free(wcPath);
3880 		return (R_FAILURE);
3881 	}
3882 
3883 	/*
3884 	 *  remove duplicate slashes first ("//../" -> "/")
3885 	 */
3886 
3887 	for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
3888 		*wptr++ = wcPath[i];
3889 
3890 		if (wcPath[i] == '/') {
3891 			i++;
3892 
3893 			while (wcPath[i] == '/') {
3894 				i++;
3895 			}
3896 
3897 			i--;
3898 		}
3899 	}
3900 
3901 	*wptr = '\0';
3902 
3903 	/*
3904 	 *  now convert back to the multi-byte format.
3905 	 */
3906 
3907 	/*LINTED*/
3908 	if (wcstombs(*r_path, wcPath, mbPathlen) == -1) {
3909 		free(wcPath);
3910 		return (R_FAILURE);
3911 	}
3912 
3913 	/* at this point have a path */
3914 
3915 	/* free up temporary storage */
3916 
3917 	free(wcPath);
3918 
3919 	return (R_SUCCESS);
3920 }
3921 
3922 /*
3923  * Name:	removeLeadingWhitespace
3924  * Synopsis:	Remove leading whitespace from string
3925  * Description:	Remove all leading whitespace characters from a string
3926  * Arguments:	a_str - [RO, *RW] - (char **)
3927  *			Pointer to handle to string (in allocated storage) to
3928  *			remove all leading whitespace from
3929  * Returns:	void
3930  *			The input string is modified as follows:
3931  *			== NULL:
3932  *				- input string was NULL
3933  *				- input string is all whitespace
3934  *			!= NULL:
3935  *				- copy of input string with leading
3936  *				  whitespace removed
3937  * CAUTION:	The input string must be allocated space (via malloc() or
3938  *		strdup()) - it must not be a static or inline character string
3939  * NOTE:	The input string a_str will be freed with 'free'
3940  *		if it is all whitespace, or if it contains any leading
3941  *		whitespace characters
3942  * NOTE:    	Any string returned is placed in new storage for the
3943  *		calling method. The caller must use 'free' to dispose
3944  *		of the storage once the string is no longer needed.
3945  * Errors:	If the string cannot be created, the process exits
3946  */
3947 
3948 static void
3949 removeLeadingWhitespace(char **a_str)
3950 {
3951 	char	*o_str;
3952 
3953 	/* entry assertions */
3954 
3955 	assert(a_str != (char **)NULL);
3956 
3957 	/* if string is null, just return */
3958 
3959 	if (*a_str == NULL) {
3960 		return;
3961 	}
3962 	o_str = *a_str;
3963 
3964 	/* if string is empty, deallocate and return NULL */
3965 
3966 	if (*o_str == '\0') {
3967 		/* free string */
3968 		free(*a_str);
3969 		*a_str = NULL;
3970 		return;
3971 	}
3972 
3973 	/* if first character is not a space, just return */
3974 
3975 	if (!isspace(*o_str)) {
3976 		return;
3977 	}
3978 
3979 	/* advance past all space characters */
3980 
3981 	while ((*o_str != '\0') && (isspace(*o_str))) {
3982 		o_str++;
3983 	}
3984 
3985 	/* if string was all space characters, deallocate and return NULL */
3986 
3987 	if (*o_str == '\0') {
3988 		/* free string */
3989 		free(*a_str);
3990 		*a_str = NULL;
3991 		return;
3992 	}
3993 
3994 	/* have non-space/null byte, return dup, deallocate original */
3995 
3996 	o_str = strdup(o_str);
3997 	free(*a_str);
3998 	*a_str = o_str;
3999 }
4000 
4001 /*
4002  * Name:	getZoneName
4003  * Description:	get the name of the zone this process is running in
4004  * Arguments:	r_zoneName - pointer to pointer to receive zone name
4005  * Output:	r_zoneName - a pointer to malloc()ed storage containing
4006  *			the zone name this process is running in is stored
4007  *			in the location pointed to by r_zoneName
4008  * Returns:	R_SUCCESS - the zone name is successfully returned
4009  *		R_FAILURE - the zone name is not successfully returned
4010  *		R_ERROR - error attempting to get the zone name
4011  */
4012 
4013 static int
4014 getZoneName(char **r_zoneName)
4015 {
4016 static char zoneName[ZONENAME_MAX] = { '\0' };
4017 
4018 	/* if zone name not already present, retrieve and cache name */
4019 
4020 	if (zoneName[0] == '\0') {
4021 		if (getzonenamebyid(getzoneid(), zoneName,
4022 			sizeof (zoneName)) < 0) {
4023 			log_msg(LOG_MSG_ERR, ERR_CANNOT_GET_ZONENAME);
4024 			return (R_ERROR);
4025 		}
4026 	}
4027 
4028 	/* return cached zone name */
4029 
4030 	*r_zoneName = zoneName;
4031 	return (R_SUCCESS);
4032 }
4033 
4034 /*
4035  * Name:	getRootPath
4036  * Description:	get the root path being tested by this process
4037  * Arguments:	r_rootPath - pointer to pointer to receive root path
4038  * Output:	r_rootPath - a pointer to malloc()ed storage containing
4039  *			the root path name this process is testing
4040  * Returns:	R_SUCCESS - the root path is successfully returned
4041  *		R_FAILURE - the root path is not successfully returned
4042  *		R_ERROR - error attempting to get the root path
4043  */
4044 
4045 static int
4046 getRootPath(char **r_rootPath)
4047 {
4048 	*r_rootPath = _rootPath;
4049 	return (R_SUCCESS);
4050 }
4051 
4052 /*
4053  * Name:	setVerbose
4054  * Description:	Turns on verbose output
4055  * Scope:	public
4056  * Arguments:	verbose = B_TRUE indicates verbose mode
4057  * Returns:	none
4058  */
4059 
4060 static void
4061 setVerbose(boolean_t setting)
4062 {
4063 	/* set log verbose messages */
4064 
4065 	log_set_verbose(setting);
4066 
4067 	/* set interactive messages */
4068 
4069 	echoSetFlag(setting);
4070 }
4071 
4072 /*
4073  * Name:	negate_results
4074  * Description:	control negation of results
4075  * Scope:	public
4076  * Arguments:	setting
4077  *		== B_TRUE indicates negated results mode
4078  *		== B_FALSE indicates non-negated results mode
4079  * Returns:	none
4080  */
4081 
4082 static void
4083 setNegateResults(boolean_t setting)
4084 {
4085 	log_msg(LOG_MSG_DEBUG, DBG_SET_NEGATE_RESULTS,
4086 		_negateResults, setting);
4087 
4088 	_negateResults = setting;
4089 }
4090 
4091 /*
4092  * Name:	getNegateResults
4093  * Description:	Returns whether or not to results are negated
4094  * Scope:	public
4095  * Arguments:	none
4096  * Returns:	B_TRUE - results are negated
4097  *		B_FALSE - results are not negated
4098  */
4099 
4100 static boolean_t
4101 getNegateResults(void)
4102 {
4103 	return (_negateResults);
4104 }
4105 
4106 /*
4107  * Name:	usage
4108  * Description:	output usage string
4109  * Arguments:	a_format - format to use to generate message
4110  *		arguments following a_format - as needed for a_format
4111  * Output:	Outputs the usage string to stderr.
4112  * Returns:	R_ERROR
4113  */
4114 
4115 static int
4116 usage(char *a_format, ...)
4117 {
4118 	int		cur_cmd;
4119 	char		cmdlst[LINE_MAX+1] = { '\0' };
4120 	char		*message;
4121 	char		bfr[1];
4122 	char		*p = get_prog_name();
4123 	size_t		vres = 0;
4124 	va_list		ap;
4125 
4126 	/* entry assertions */
4127 
4128 	assert(a_format != NULL);
4129 	assert(*a_format != '\0');
4130 
4131 	/* determine size of the message in bytes */
4132 
4133 	va_start(ap, a_format);
4134 	/* LINTED warning: variable format specifier to vsnprintf(); */
4135 	vres = vsnprintf(bfr, 1, a_format, ap);
4136 	va_end(ap);
4137 
4138 	assert(vres > 0);
4139 
4140 	/* allocate storage to hold the message */
4141 
4142 	message = (char *)calloc(1, vres+2);
4143 	assert(message != NULL);
4144 
4145 	/* generate the results of the printf conversion */
4146 
4147 	va_start(ap, a_format);
4148 	/* LINTED warning: variable format specifier to vsnprintf(); */
4149 	vres = vsnprintf(message, vres+1, a_format, ap);
4150 	va_end(ap);
4151 
4152 	assert(vres > 0);
4153 
4154 	/* generate list of all defined conditions */
4155 
4156 	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
4157 		(void) strlcat(cmdlst, "\t", sizeof (cmdlst));
4158 		(void) strlcat(cmdlst, cmds[cur_cmd].c_name, sizeof (cmdlst));
4159 		if (cmds[cur_cmd].c_args != NULL) {
4160 			(void) strlcat(cmdlst, cmds[cur_cmd].c_args,
4161 						sizeof (cmdlst));
4162 		}
4163 		(void) strlcat(cmdlst, "\n", sizeof (cmdlst));
4164 	}
4165 
4166 	/* output usage with conditions */
4167 
4168 	log_msg(LOG_MSG_INFO, MSG_USAGE, message, p ? p : "pkgcond", cmdlst);
4169 
4170 	return (R_ERROR);
4171 }
4172 
4173 /*
4174  * Name:	parseGlobalData
4175  * Description:	parse environment global data and store in global data structure
4176  * Arguments:	a_envVar - pointer to string representing the name of the
4177  *			environment variable to get and parse
4178  *		r_gdt - pointer to pointer to global data structure to fill in
4179  *			using the parsed data from a_envVar
4180  * Output:	none
4181  * Returns:	R_SUCCESS - the global data is successfully parsed
4182  *		R_FAILURE - problem parsing global data
4183  *		R_ERROR - fatal error attempting to parse global data
4184  */
4185 
4186 static int
4187 parseGlobalData(char *a_envVar, GLOBALDATA_T **r_gdt)
4188 {
4189 	int		r;
4190 	int		n;
4191 	char		*a;
4192 	SML_TAG		*tag;
4193 	SML_TAG		*ntag;
4194 
4195 	assert(r_gdt != (GLOBALDATA_T **)NULL);
4196 
4197 	/*
4198 	 * allocate space for global data structure if needed
4199 	 */
4200 
4201 	if (*r_gdt == (GLOBALDATA_T *)NULL) {
4202 		*r_gdt = (GLOBALDATA_T *)calloc(1, sizeof (GLOBALDATA_T));
4203 	}
4204 
4205 	/*
4206 	 * get initial installation indication:
4207 	 * If the initial install variable is set to "true", then an initial
4208 	 * installation of Solaris is underway. When this condition is true:
4209 	 * - if the path being checked is the package install root, then
4210 	 *   the path is considered to be an 'alternative root' which is
4211 	 *   currently being installed.
4212 	 * - if the path being checked is not the package install root, then
4213 	 *   the path needs to be further analyzed to determine what it may
4214 	 *   be referring to.
4215 	 */
4216 
4217 	a = getenv(ENV_VAR_INITIAL_INSTALL);
4218 	if ((a != NULL) && (strcasecmp(a, "true") == 0)) {
4219 		(*r_gdt)->gd_initialInstall = B_TRUE;
4220 	}
4221 
4222 	/* get current zone name */
4223 
4224 	r = getZoneName(&(*r_gdt)->gd_zoneName);
4225 	if (r != R_SUCCESS) {
4226 		(*r_gdt)->gd_zoneName = "";
4227 	}
4228 
4229 	/*
4230 	 * get zone installation status:
4231 	 * - If the package install zone name is not set, then an installation
4232 	 *   of a global zone, or of a non-global zone, is not underway.
4233 	 * - If the package install zone name is set to "global", then an
4234 	 *   installation of a global zone is underway. In this case, no path
4235 	 *   can be a netinstall image, diskless client, mounted miniroot,
4236 	 *   non-global zone, the current running system, alternative root,
4237 	 *   or alternative boot environment.
4238 	 * - If the package install zone name is set to a value other than
4239 	 *   "global", then an installation of a non-global zone with that name
4240 	 *   is underway.  In this case, no path can be a netinstall image,
4241 	 *   diskless client, mounted miniroot, global zone, the current
4242 	 *   running system, alternative root, or alternative boot environment.
4243 	 */
4244 
4245 	a = getenv(ENV_VAR_PKGZONENAME);
4246 	if ((a == NULL) || (*a == '\0')) {
4247 		/* not installing a zone */
4248 		(*r_gdt)->gd_globalZoneInstall = B_FALSE;
4249 		(*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
4250 	} else if (strcmp(a, GLOBAL_ZONENAME) == 0) {
4251 		/* installing a global zone */
4252 		(*r_gdt)->gd_globalZoneInstall = B_TRUE;
4253 		(*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
4254 		(*r_gdt)->gd_zoneName = a;
4255 	} else {
4256 		/* installing a non-global zone by that name */
4257 		(*r_gdt)->gd_globalZoneInstall = B_FALSE;
4258 		(*r_gdt)->gd_nonglobalZoneInstall = B_TRUE;
4259 		(*r_gdt)->gd_zoneName = a;
4260 	}
4261 
4262 	/*
4263 	 * get package install root. If it doesn't exist check for
4264 	 * patch install root (ROOTDIR)
4265 	 */
4266 
4267 	a = getenv(ENV_VAR_PKGROOT);
4268 	if ((a != NULL) && (*a != '\0')) {
4269 		(*r_gdt)->gd_installRoot = a;
4270 	} else {
4271 		a = getenv(ENV_VAR_PATCHROOT);
4272 		if ((a != NULL) && (*a != '\0')) {
4273 			(*r_gdt)->gd_installRoot = a;
4274 		} else {
4275 			(*r_gdt)->gd_installRoot = "/";
4276 		}
4277 	}
4278 
4279 	/*
4280 	 * get patch client version: always set if $ROOTDIR != / and
4281 	 * the $ROOTDIR/var/sadm/softinfo/INST_RELEASE file exists.
4282 	 */
4283 
4284 	a = getenv(ENV_VAR_PATCH_CLIENTVER);
4285 	(*r_gdt)->gd_patchClientVersion = (a ? a : "");
4286 
4287 	/* get the global data environment variable */
4288 
4289 	a = getenv(a_envVar);
4290 
4291 	/* if no data then issue warning and return success */
4292 
4293 	if ((a == NULL) || (*a_envVar == '\0')) {
4294 		log_msg(LOG_MSG_DEBUG, DBG_NO_GLOBAL_DATA_AVAILABLE, a_envVar);
4295 		return (R_SUCCESS);
4296 	}
4297 
4298 	/* data present - parse into SML structure */
4299 
4300 	log_msg(LOG_MSG_DEBUG, DBG_PARSE_GLOBAL, a);
4301 
4302 	r = smlConvertStringToTag(&tag, a);
4303 	if (r != R_SUCCESS) {
4304 		log_msg(LOG_MSG_ERR, ERR_CANNOT_PARSE_GLOBAL_DATA, a);
4305 		return (R_FAILURE);
4306 	}
4307 
4308 	smlDbgPrintTag(tag, DBG_PARSED_ENVIRONMENT, a_envVar);
4309 
4310 	/* fill in global data structure */
4311 
4312 	/* find the environment condition information structure */
4313 
4314 	ntag = smlGetTagByName(tag, 0, TAG_COND_TOPLEVEL);
4315 	if (ntag == SML_TAG__NULL) {
4316 		log_msg(LOG_MSG_WRN, WRN_PARSED_DATA_MISSING,
4317 			TAG_COND_TOPLEVEL);
4318 		return (R_FAILURE);
4319 	}
4320 
4321 	/*
4322 	 * data found - extract what we know about
4323 	 */
4324 
4325 	/* parent zone name */
4326 
4327 	a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_NAME);
4328 	(*r_gdt)->gd_parentZoneName = a;
4329 
4330 	/* parent zone type */
4331 
4332 	a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_TYPE);
4333 	(*r_gdt)->gd_parentZoneType = a;
4334 
4335 	/* current zone name */
4336 
4337 	a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
4338 		TAG_COND_ZONE_NAME);
4339 	(*r_gdt)->gd_currentZoneName = a;
4340 
4341 	/* current zone type */
4342 
4343 	a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
4344 		TAG_COND_ZONE_TYPE);
4345 	(*r_gdt)->gd_currentZoneType = a;
4346 
4347 	/* extract any inherited file systems */
4348 
4349 	for (n = 0; ; n++) {
4350 		char	**ifs;
4351 
4352 		a = smlGetParamByTag(ntag, n, TAG_COND_INHERITED_FS,
4353 			TAG_COND_FS_NAME);
4354 
4355 		if (a == NULL) {
4356 			if (n > 0) {
4357 				/* LINTED warning: variable may be used */
4358 				(*r_gdt)->gd_inheritedFileSystems = ifs;
4359 			}
4360 			break;
4361 		}
4362 
4363 		if (n == 0) {
4364 			ifs = (char **)calloc(1, sizeof (char **)*(n+2));
4365 			ifs[n] = a;
4366 			ifs[n+1] = NULL;
4367 		} else {
4368 			ifs = (char **)realloc(ifs, sizeof (char **)*(n+2));
4369 			ifs[n] = a;
4370 			ifs[n+1] = NULL;
4371 		}
4372 	}
4373 
4374 	return (R_SUCCESS);
4375 }
4376 
4377 /*
4378  * Name:	dumpGlobalData
4379  * Description:	dump global data structure using echoDebug
4380  * Arguments:	a_gdt - pointer to global data structure to dump
4381  * Outputs:	echoDebug is called to output global data strucutre information
4382  * Returns:	void
4383  */
4384 
4385 static void
4386 dumpGlobalData(GLOBALDATA_T *a_gdt)
4387 {
4388 	/* entry assertions */
4389 
4390 	assert(a_gdt != (GLOBALDATA_T *)NULL);
4391 
4392 	/* debugging enabled, dump the global data structure */
4393 
4394 	echoDebug(DBG_DUMP_GLOBAL_ENTRY);
4395 	echoDebug(DBG_DUMP_GLOBAL_PARENT_ZONE,
4396 		a_gdt->gd_parentZoneName ? a_gdt->gd_parentZoneName : "",
4397 		a_gdt->gd_parentZoneType ? a_gdt->gd_parentZoneType : "");
4398 	echoDebug(DBG_DUMP_GLOBAL_CURRENT_ZONE,
4399 		a_gdt->gd_currentZoneName ? a_gdt->gd_currentZoneName : "",
4400 		a_gdt->gd_currentZoneType ? a_gdt->gd_currentZoneType : "");
4401 
4402 	if (a_gdt->gd_inheritedFileSystems) {
4403 		int n;
4404 		char **ifs = a_gdt->gd_inheritedFileSystems;
4405 		for (n = 0; ifs[n]; n++) {
4406 			echoDebug(DBG_DUMP_GLOBAL_LINE, n, ifs[n]);
4407 		}
4408 	}
4409 }
4410 
4411 /*
4412  * Name:	recursionCheck
4413  * Description:	prevent recursive calling of functions
4414  * Arguments:	r_recursion - pointer to int recursion counter
4415  *		a_function - pointer to name of function
4416  * Returns:	B_TRUE - function is recursively called
4417  *		B_FALSE - function not recursively called
4418  */
4419 
4420 static boolean_t
4421 recursionCheck(int *r_recursion, char *a_function)
4422 {
4423 	/* prevent recursion */
4424 
4425 	(*r_recursion)++;
4426 	if (*r_recursion > 1) {
4427 		echoDebug(DBG_RECURSION, a_function, *r_recursion);
4428 		(*r_recursion)--;
4429 		return (B_TRUE);
4430 	}
4431 
4432 	echoDebug(DBG_NO_RECURSION, a_function);
4433 	return (B_FALSE);
4434 }
4435 
4436 /*
4437  * Name:	quit
4438  * Description:	cleanup and exit
4439  * Arguments:	a_retcode - the code to use to determine final exit status;
4440  *			if this is NOT "99" and if a "ckreturnFunc" is
4441  *			set, then that function is called with a_retcode
4442  *			to set the final exit status.
4443  *		Valid values are:
4444  *		0 - success
4445  *		1 - package operation failed (fatal error)
4446  *		2 - non-fatal error (warning)
4447  *		3 - user selected quit (operation interrupted)
4448  *		4 - admin settings prevented operation
4449  *		5 - interaction required and -n (non-interactive) specified
4450  *		"10" is added to indicate "immediate reboot required"
4451  *		"20" is be added to indicate "reboot after install required"
4452  *		99 - do not interpret the code - just exit "99"
4453  * Returns:	<<this function does not return - calls exit()>>
4454  * NOTE:	This is needed because libinst functions can call "quit(99)"
4455  *		to force an error exit.
4456  */
4457 
4458 void
4459 quit(int a_retcode)
4460 {
4461 	/* process return code if not quit(99) */
4462 
4463 	if (a_retcode == 99) {
4464 		exit(0x7f);	/* processing error (127) */
4465 	}
4466 
4467 	exit(R_FAILURE);
4468 }
4469