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