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