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