OpenSolaris_b135/cmd/svr4pkg/pkgcond/main.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * Program:	pkgcond
 *
 * Function:	Implements the package command suite public utility pkgcond(1M)
 *
 * Usage:	pkgcond [-nv] [-O debug] condition [ argument ]
 *
 *		command options:
 *			-n - negate results of condition test
 *			-v - verbose output of condition testing
 *
 *		<condition> may be any one of:
 *			can_add_driver [path]
 *			can_remove_driver [path]
 *			can_update_driver [path]
 *			is_alternative_root [path]
 *			is_boot_environment [path]
 *			is_diskless_client [path]
 *			is_global_zone [path]
 *			is_mounted_miniroot [path]
 *			is_netinstall_image [path]
 *			is_nonglobal_zone [path]
 *			is_path_writable path
 *			is_running_system [path]
 *			is_sparse_root_nonglobal_zone [path]
 *			is_what [path]
 *			is_whole_root_nonglobal_zone [path]
 *
 *		<option(s)> are specific to the condition used
 *
 * Input:	depends on command
 *
 * Output:	depends on command
 *
 * Exit status:	If the -n option is not specified:
 *		== 0 - the specified condition is true (or exists).
 *		== 1 - the specified condition is false (or does not exist).
 *		== 2 - command line usage errors (including bad keywords)
 *		== 3 - command failed to perform the test due to a fatal error
 *
 *		If the -n option is specified:
 *		== 0 - the specified condition is false (or does not exist).
 *		== 1 - the specified condition is true (or exists).
 *		== 2 - command line usage errors (including bad keywords)
 *		== 3 - command failed to perform the test due to a fatal error
 */

#include <stdio.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <locale.h>
#include <errno.h>
#include <sys/param.h>
#include <assert.h>

#include <instzones_api.h>
#include <pkglib.h>
#include <install.h>
#include <libinst.h>
#include <libadm.h>
#include <messages.h>
#include "pkgcond.h"
#include "pkgcond_msgs.h"

/* Should be defined by cc -D */

#if	!defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif

/* commands to execute */

#define	LS_CMD		"/usr/bin/ls"

/*
 * type definition and "types" for testPath()
 */

typedef enum {
	TEST_EXISTS = 0x01,
	TEST_NOT_EXISTS = 0x02,
	TEST_IS_DIRECTORY = 0x04,
	TEST_IS_FILE = 0x08,
	TEST_NOT_DIRECTORY = 0x10,
	TEST_NOT_FILE = 0x20,
	TEST_IS_SYMBOLIC_LINK = 0x40,
	TEST_NOT_SYMBOLIC_LINK = 0x80,
	TEST_GLOBAL_TOKEN_IN_FILE = 0x100
} TEST_TYPES;

/* holds file system info */

struct fsi_t {
	char	*fsi_mntOptions;
	char	*fsi_fsType;
	char	*fsi_mntPoint;
};
typedef struct fsi_t	FSI_T;

/* holds parsed global data */

struct globalData_t {
		/* sparse root (are any file systems mounted read-only)? */
	boolean_t gd_srFsMountedRO;
		/* initial install: PKG_INIT_INSTALL=true */
	boolean_t gd_initialInstall;
		/* global zone install: SUNW_PKG_INSTALL_ZONENAME=global */
	boolean_t gd_globalZoneInstall;
		/* non-global zone install: SUNW_PKG_INSTALL_ZONENAME!=global */
	boolean_t gd_nonglobalZoneInstall;
		/* non-global zone is in a mounted state */
	boolean_t inMountedState;
		/* sorted list of all mounted file systems */
	FSI_T	*gd_fileSystemConfig;
		/* number of mounted file systems in list */
	long	gd_fileSystemConfigLen;
		/* current zone name */
	char	*gd_zoneName;
		/* version of target: PATCH_CLIENT_VERSION */
	char	*gd_patchClientVersion;
		/* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneName */
	char	*gd_parentZoneName;
		/* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneType */
	char	*gd_parentZoneType;
		/* root path to target: PKG_INSTALL_ROOT */
	char	*gd_installRoot;
		/* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneName */
	char	*gd_currentZoneName;
		/* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneType */
	char	*gd_currentZoneType;
		/* list of inherited file systems */
	char	**gd_inheritedFileSystems;
		/* path provided on command line */
	char	*gd_cmdline_path;
};
typedef struct globalData_t	GLOBALDATA_T;

/* holds subcommands and their definitions */

struct cmd_t {
	char		*c_name;
	char		*c_args;
	int		(*c_func)(int argc, char **argv, GLOBALDATA_T *a_gdt);
};
typedef struct cmd_t	CMD_T;

/* Command function prototypes */

static int		cmd_can_add_driver(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_can_remove_driver(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_can_update_driver(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_alternative_root(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_boot_environment(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_diskless_client(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_global_zone(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_mounted_miniroot(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_netinstall_image(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_nonglobal_zone(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_path_writable(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_running_system(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_sparse_root_ng_zone(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_what(int argc, char **argv,
				GLOBALDATA_T *a_gdt);
static int		cmd_is_whole_root_ng_zone(int argc, char **argv,
				GLOBALDATA_T *a_gdt);

/* Utility function Prototypes */

static boolean_t	getNegateResults(void);
static boolean_t	recursionCheck(int *r_recursion, char *a_function);
static boolean_t	checkForReadOnlyMount(GLOBALDATA_T *a_gdt,
    char *a_mntPoint, char *a_fsType, char *a_mntOptions);
static int		adjustResults(int a_result);
static int		calculateFileSystemConfig(GLOBALDATA_T *a_gdt);
static int		getRootPath(char **r_rootPath);
static int		getZoneName(char **r_zoneName);
static int		mountOptionPresent(char *a_mntOptions, char *a_opt);
static int		parseGlobalData(char *a_envVar, GLOBALDATA_T **a_gdt);
static int		resolvePath(char **r_path);
static int		setRootPath(char *a_path, char *a_envVar,
    boolean_t a_mustExist);
static int		testPath(TEST_TYPES a_tt, char *format, ...);
static int		usage(char *a_format, ...);
static int		findToken(char *path, char *token);
static char		*getMountOption(char **p);
static void		dumpGlobalData(GLOBALDATA_T *a_gdt);
static void		removeLeadingWhitespace(char **a_str);
static void		setNegateResults(boolean_t setting);
static void		setVerbose(boolean_t);
static void		sortedInsert(FSI_T **r_list, long *a_listSize,
    char *a_mntPoint, char *a_fsType, char *a_mntOptions);
static void		setCmdLinePath(char **a_path, char **args,
    int num_args);

/* local static data */

static boolean_t	_negateResults = B_FALSE;
static char		*_rootPath = "/";

/* define subcommand data structure */

static CMD_T cmds[] = {
	{ "can_add_driver",		" [path]",
		cmd_can_add_driver },
	{ "can_remove_driver",		" [path]",
		cmd_can_remove_driver },
	{ "can_update_driver",		" [path]",
		cmd_can_update_driver },
	{ "is_alternative_root",	" [path]",
		cmd_is_alternative_root },
	{ "is_boot_environment",	" [path]",
		cmd_is_boot_environment },
	{ "is_diskless_client",		" [path]",
		cmd_is_diskless_client },
	{ "is_global_zone",		" [path]",
		cmd_is_global_zone },
	{ "is_mounted_miniroot",	" [path]",
		cmd_is_mounted_miniroot },
	{ "is_netinstall_image",	" [path]",
		cmd_is_netinstall_image },
	{ "is_nonglobal_zone",		" [path]",
		cmd_is_nonglobal_zone },
	{ "is_path_writable",		" path",
		cmd_is_path_writable },
	{ "is_running_system",		" [path]",
		cmd_is_running_system },
	{ "is_sparse_root_nonglobal_zone", " [path]",
		cmd_is_sparse_root_ng_zone },
	{ "is_what", " [path]",
		cmd_is_what },
	{ "is_whole_root_nonglobal_zone", " [path]",
		cmd_is_whole_root_ng_zone },
	/* last one must be all NULLs */
	{ NULL, NULL }
};

/*
 * *****************************************************************************
 * main
 * *****************************************************************************
 */

/*
 * Name:	main
 * Description:	main processing loop for pkgcond *
 * Return:	0 - condition is satisfied (true)
 *		1 - condition is not satisfied (false)
 *		2 - command line usage errors
 *		3 - failure to determine condition
 */

int
main(int argc, char **argv)
{
	GLOBALDATA_T	*gdt = NULL;
	char		**newargv;
	char		*p;
	int		cur_cmd;
	int		i;
	int		newargc;

	/* make standard output non-buffered */

	setbuf(stdout, NULL);

	/* set the default text domain for messaging */

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	/* remember command name */

	set_prog_name(argv[0]);

	/* tell spmi zones interface how to access package output functions */

	z_set_output_functions(echo, echoDebug, progerr);

	/* set verbose mode if appropriate environment variable is set */

	if (getenv(ENV_VAR_VERBOSE)) {
		/* same as -v */
		setVerbose(B_TRUE);
	}

	/* set debug mode if appropriate environment variable is set */

	if (getenv(ENV_VAR_DEBUG)) {
		/* same as -O debug */

		/* set sml tracing (sml.c) */
		smlSetVerbose(B_TRUE);

		/* set log and echo (interactive) message tracing */
		setVerbose(B_TRUE);

		/* enable echoDebug debugging messages */
		echoDebugSetFlag(B_TRUE);
	}

	/* generate usage if no options or arguments specified */

	if (argc <= 1) {
		(void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
		return (R_USAGE);
	}

	/*
	 * process any arguments that can appear before the subcommand
	 */

	while ((i = getopt(argc, argv, ":O:vn?")) != EOF) {
		switch (i) {
		/*
		 * Not a public interface: the -O option allows the behavior
		 * of the package tools to be modified. Recognized options:
		 * -> debug
		 * ---> enable debugging output
		 */

		case 'O':
			for (p = strtok(optarg, ","); p != NULL;
				p = strtok(NULL, ",")) {

				/* debug - enable all tracing */

				if (strcmp(p, "debug") == 0) {
					/* set sml tracing */
					smlSetVerbose(B_TRUE);
					/* set log/echo tracing */
					setVerbose(B_TRUE);
					/* enable debugging messages */
					echoDebugSetFlag(B_TRUE);
					continue;
				}

				progerr(ERR_INVALID_O_OPTION, p);
				return (adjustResults(R_USAGE));
			}
			break;

		/*
		 * Public interface: enable verbose (debug) output.
		 */

		case 'v':	/* verbose mode enabled */
			/* set command tracing only */
			setVerbose(B_TRUE);
			break;

		/*
		 * Public interface: negate output results.
		 */

		case 'n':
			setNegateResults(B_TRUE);
			break;

		/*
		 * unrecognized option
		 */

		case '?':
		default:
			(void) usage(MSG_INVALID_OPTION_SPECIFIED, optopt);
			return (R_USAGE);
		}
	}

	/*
	 * done processing options that can preceed subcommand
	 */

	/* error if no subcommand specified */

	if ((argc-optind) <= 0) {
		(void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
		return (R_USAGE);
	}

	/* parse global data if environment variable set */

	if (parseGlobalData(PKGCOND_GLOBAL_VARIABLE, &gdt) != R_SUCCESS) {
		log_msg(LOG_MSG_ERR, ERR_CANNOT_USE_GLOBAL_DATA,
			PKGCOND_GLOBAL_VARIABLE);
		return (R_ERROR);
	}

	if (setRootPath(gdt->gd_installRoot,
	    (strcmp(gdt->gd_installRoot, "/") == 0) ? NULL :
	    ENV_VAR_SET, B_TRUE) != R_SUCCESS) {
		log_msg(LOG_MSG_ERR, ERR_CANNOT_SET_ROOT_PATH,
			ENV_VAR_PKGROOT);
		return (R_ERROR);
	}

	/* set path provided on the command line */

	setCmdLinePath(&(gdt->gd_cmdline_path), argv, argc);
	echoDebug(DBG_CMDLINE_PATH,
	    gdt->gd_cmdline_path == NULL ? "" : gdt->gd_cmdline_path);

	/* determine how file systems are layered in this zone */

	if (calculateFileSystemConfig(gdt) != R_SUCCESS) {
		log_msg(LOG_MSG_ERR, ERR_CANNOT_CALC_FS_CONFIG);
		return (R_ERROR);
	}

	/* dump global data read in (only if debugging) */

	dumpGlobalData(gdt);

	/* search for specified subcommand and execute if found */

	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
		if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
			int	result;

			/* make subcommand the first option */

			newargc = argc - optind;
			newargv = argv + optind;
			opterr = optind = 1; optopt = 0;


			/* call subcommand with its own argc/argv */

			result = cmds[cur_cmd].c_func(newargc, newargv, gdt);

			/* process result code and exit */

			result = adjustResults(result);
			log_msg(LOG_MSG_DEBUG, DBG_RESULTS, result);
			return (result);
		}
	}

	/* subcommand not found - output error message and exit with error */

	log_msg(LOG_MSG_ERR, ERR_BAD_SUB, argv[optind]);
	(void) usage(MSG_UNRECOGNIZED_CONDITION_SPECIFIED);
	return (R_USAGE);
}

/*
 * *****************************************************************************
 * command implementation functions
 * *****************************************************************************
 */

/*
 * Name:	cmd_is_diskless_client
 * Description:	determine if target is a diskless client
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must not be initial installation to the install root
 *  - must not be installation of a zone
 *  - must not be a whole root non-global zone
 *  - must not be a non-global zone
 *  - must not be a mounted mini-root
 *  - must not be a netinstall image
 *  - must not be a boot environment
 *  - The package "SUNWdclnt" must be installed at "/"
 *  - The root path must not be "/"
 *  - The path "/export/exec/Solaris_\*\/usr" must exist at "/"
 *  - The directory "$ROOTDIR/../templates" must exist
 */

static int
cmd_is_diskless_client(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	char	cmd[MAXPATHLEN+1];
	int	c;
	int	r;
	int	rc;
static	char	*cmdName = "is_diskless_client";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/*
		 * a diskless client cannot be any of the following
		 */

		/* cannot be whole root non-global zone */

		r = cmd_is_whole_root_ng_zone(argc, argv, a_gdt);

		/* cannot be nonglobal zone */

		if (r != R_SUCCESS) {
			r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
		}

		/* cannot be mounted miniroot */

		if (r != R_SUCCESS) {
			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
		}

		/* cannot be a netinstall image */

		if (r != R_SUCCESS) {
			r = cmd_is_netinstall_image(argc, argv, a_gdt);
		}

		/* cannot be a boot environment */

		if (r != R_SUCCESS) {
			r = cmd_is_boot_environment(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		/* return failure if any of the preceeding are true */

		switch (r) {
			case R_SUCCESS:
				return (R_FAILURE);
			case R_FAILURE:
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* SUNWdclnt must be installed */

	if (pkgTestInstalled("SUNWdclnt", "/") != B_TRUE) {
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_PKG_NOT_INSTALLED,
			rootPath, "SUNWdclnt", "/");
		return (R_FAILURE);
	}

	/*   - $ROOTDIR must not be "/" */

	if (strcmp(rootPath, "/") == 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ROOTPATH_BAD, rootPath, "/");
		return (R_FAILURE);
	}

	/*   - zone name must be global */

	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_BAD, rootPath,
			GLOBAL_ZONENAME);
		return (R_FAILURE);
	}

	/*
	 * /export/exec/Solaris_"*"/usr must exist;
	 * create ls command to test:
	 * /usr/bin/ls /export/exec/Solaris_"*"/usr
	 */

	(void) snprintf(cmd, sizeof (cmd), "%s %s >/dev/null 2>&1",
		LS_CMD, "/export/exec/Solaris_*/usr");

	/* execute command */

	rc = system(cmd);

	/* return error if ls returns something other than "0" */

	if (rc != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_MISSING,
			rootPath, "/export/exec/Solaris_*/usr");
		return (R_FAILURE);
	}

	/*
	 * /usr must be empty on a diskless client:
	 * create ls command to test:
	 * /usr/bin/ls -d1 $ROOTDIR/usr/\*
	 */
	(void) snprintf(cmd, sizeof (cmd), "%s %s %s/%s >/dev/null 2>&1",
		LS_CMD, "-1d", rootPath, "usr/*");

	/* execute command */

	rc = system(cmd);

	/* return error if ls returns "0" */

	if (rc == 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_USR_IS_NOT_EMPTY,
			rootPath);
		return (R_FAILURE);
	}

	/* there must be a templates directory at ${ROOTPATH}/../templates */

	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
		"%s/%s", rootPath, "../templates");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_NO_TEMPLATES_PATH,
			rootPath, rootPath, "../templates");
		return (R_FAILURE);
	}

	/* must not be initial installation to the install root */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		/* initial install: install root cannot be diskless client */
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial zone install: no path can be diskless client */
		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* the path is a diskless client */

	log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_IS_DISKLESS_CLIENT, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_global_zone
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must not be initial installation to the install root
 *  - must not be installation of a non-global zone
 *  - must not be a non-global zone
 *  - must not be a mounted mini-root
 *  - must not be a netinstall image
 *  - must not be a diskless client
 *  - if $ROOTDIR is "/":
 *  -- if zone name is "GLOBAL", then is a global zone;
 *  -- else not a global zone.
 *  - $ROOTDIR/etc/zones must exist and be a directory
 *  - $ROOTDIR/.tmp_proto must not exist
 *  - $ROOTDIR/var must exist and must not be a symbolic link
 */

static int
cmd_is_global_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_global_zone";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/*
		 * a global zone cannot be any of the following
		 */

		/* cannot be a non-global zone */

		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);

		/* cannot be a mounted miniroot */

		if (r != R_SUCCESS) {
			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
		}

		/* cannot be a netinstall image */

		if (r != R_SUCCESS) {
			r = cmd_is_netinstall_image(argc, argv, a_gdt);
		}

		/* cannot be a diskless client */

		if (r != R_SUCCESS) {
			r = cmd_is_diskless_client(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		/* return failure if any of the preceeding are true */

		switch (r) {
			case R_SUCCESS:
				return (R_FAILURE);
			case R_FAILURE:
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* must not be initial installation to the install root */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		/* initial install: install root cannot be global zone */
		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a non-global zone */

	if (a_gdt->gd_nonglobalZoneInstall == B_TRUE) {
		/* initial nonglobal zone install: no path can be global zone */
		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_NGZ_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* handle if global zone installation to the install root */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
			/* the path is a global zone */

			log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
				rootPath);

			return (R_SUCCESS);
	}

	/* true if current root is "/" and zone name is GLOBAL_ZONENAME */

	if (strcmp(rootPath, "/") == 0) {
		if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
			/* the path is a global zone */

			log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
				rootPath);

			return (R_SUCCESS);
		}

		/* inside a non-global zone */

		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_ZONENAME_ISNT_GLOBAL,
			rootPath, a_gdt->gd_zoneName);

		return (R_FAILURE);
	}

	/*
	 * current root is not "/" - see if target looks like a global zone
	 *
	 * - rootpath is not "/"
	 * - and $ROOTDIR/etc/zones exists
	 * - and $ROOTDIR/.tmp_proto does not exist
	 * - and $ROOTDIR/var is not a symbolic link
	 */

	/* not global zone if /etc/zones does not exist */

	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
		"%s/%s", rootPath, "/etc/zones");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_ISNT_DIRECTORY,
			rootPath, "/etc/zones");
		return (R_FAILURE);
	}

	/* .tmp_proto must not exist */

	r = testPath(TEST_NOT_EXISTS,
		"%s/%s", rootPath, ".tmp_proto");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_EXISTS,
			rootPath, "/.tmp_proto");
		return (R_FAILURE);
	}

	/* /var must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/var");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_SYMLINK,
			rootPath, "/var");
		return (R_FAILURE);
	}

	/* the path is a global zone */

	log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_netinstall_image
 * Description:	determine if target is a net install image
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must not be initial installation to the install root
 *  - must not be installation of a zone
 *  - must not be a global zone
 *  - must not be a mounted mini-root
 *  - zone name must be "global"
 *  - $ROOTDIR/.tmp_proto must exist and must be a directory
 *  - $ROOTDIR/var must exist and must be a symbolic link
 *  - $ROOTDIR/tmp/kernel must exist and must be a directory
 *  - $ROOTDIR/.tmp_proto/kernel must exist and must be a symbolic link
 */

static int
cmd_is_netinstall_image(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_netinstall_image";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* a netinstall image cannot be a global zone */

		r = cmd_is_global_zone(argc, argv, a_gdt);

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				return (R_FAILURE);
			case R_FAILURE:
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* current zone name must be "global" */

	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_INIM_BAD_CURRENT_ZONE,
			rootPath, GLOBAL_ZONENAME);
		return (R_FAILURE);
	}

	/* cannot be a mounted_miniroot */

	if (cmd_is_mounted_miniroot(argc, argv, a_gdt) == R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT,
			rootPath);
		return (R_FAILURE);
	}

	/* $ROOTDIR/.tmp_proto exists */

	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
		"%s/%s", rootPath, ".tmp_proto");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
			rootPath, "/.tmp_proto");
		return (R_FAILURE);
	}

	/* $ROOTDIR/var is a symbolic link */

	r = testPath(TEST_IS_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/var");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
			rootPath, "/var");
		return (R_FAILURE);
	}

	/* $ROOTDIR/tmp/kernel does exist */

	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
		"%s/%s", rootPath, "/tmp/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
			rootPath, "/tmp/kernel");
		return (R_FAILURE);
	}

	/* $ROOTDIR/.tmp_proto/kernel is a symbolic link */

	r = testPath(TEST_IS_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/.tmp_proto/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
			rootPath, "/.tmp_proto/kernel");
		return (R_FAILURE);
	}

	/* must not be initial installation to the install root */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		/* initial install: install root cannot be netinstall image */
		log_msg(LOG_MSG_DEBUG, DBG_INIM_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial zone install: no path can be netinstall image */
		log_msg(LOG_MSG_DEBUG, DBG_INIM_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* target is a netinstall image */

	log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_IS_NETINSTALL_IMAGE, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_mounted_miniroot
 * Description:	determine if target is a mounted miniroot image
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must not be initial installation to the install root
 *  - must not be installation of a zone
 *  - zone name must be "global"
 *  - $ROOTDIR/tmp/kernel must exist and must be a symbolic link
 *  - $ROOTDIR/tmp/root/kernel must exist and must be a directory
 */

static int
cmd_is_mounted_miniroot(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_mounted_miniroot";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
		recursion--;
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* current zone name must be "global" */

	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IMRT_BAD_CURRENT_ZONE,
			rootPath, GLOBAL_ZONENAME);
		return (R_FAILURE);
	}

	/* $ROOTDIR/tmp/kernel is a symbolic link */

	r = testPath(TEST_IS_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/tmp/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_SYMLINK,
			rootPath, "/tmp/kernel");
		return (R_FAILURE);
	}

	/* $ROOTDIR/tmp/root/kernel is a directory */

	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
		"%s/%s", rootPath, "/tmp/root/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_DIRECTORY,
			rootPath, "/tmp/root/kernel");
		return (R_FAILURE);
	}

	/* must not be initial installation to the install root */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		/* initial install: install root cannot be mounted miniroot */
		log_msg(LOG_MSG_DEBUG, DBG_IMRT_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial zone install: no path can be mounted miniroot */
		log_msg(LOG_MSG_DEBUG, DBG_IMRT_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* target is a mounted miniroot */

	log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_nonglobal_zone
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 *  - must not be initial installation to the install root
 *  - must not be installation of a global zone
 *  - success if installation of a non-global zone
 */

static int
cmd_is_nonglobal_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_nonglobal_zone";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
		recursion--;
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* handle if non-global zone installation to the install root */

	if ((a_gdt->gd_nonglobalZoneInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_INSTALL_ZONENAME_IS_NGZ,
			rootPath, a_gdt->gd_zoneName);
		return (R_SUCCESS);
	}

	/* must not be initial installation to the install root */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		/* initial install: install root cannot be non-global zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a global zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial global zone install: no path can be nonglobal zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_GLOBAL_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/*
	 * *********************************************************************
	 * if root directory is "/" then the only thing that needs to be done is
	 * to test the zone name directly - if the zone name is "global" then
	 * the target is not a non-global zone; otherwise if the zone name is
	 * not "global" then the target IS a non-global zone.
	 * *********************************************************************
	 */

	if (strcmp(rootPath, "/") == 0) {
		/* target is current running root */
		if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
			/* in the global zone */
			log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
				rootPath, a_gdt->gd_zoneName);
			return (R_FAILURE);
		}
		/* in a non-global zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_IS_NGZ,
			rootPath, a_gdt->gd_zoneName);
		return (R_SUCCESS);
	}

	/*
	 * $ROOTDIR/etc/zones/index must exist in a global zone. It also
	 * exists in a non-global zone after s10u4 but we can't check that
	 * since it is undeterministic for all releases so we only check
	 * for the global zone here.
	 */

	r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/zones/index");
	if (r == R_SUCCESS) {

		/* See if "global" exists in .../etc/zones/index */

		if (testPath(TEST_GLOBAL_TOKEN_IN_FILE, "%s/%s", rootPath,
		    "/etc/zones/index") != R_SUCCESS) {
			log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
			    rootPath, GLOBAL_ZONENAME);
			return (R_FAILURE);
		}
	}

	/*
	 * *********************************************************************
	 * If the root directory is "/" then you can use only the zone
	 * name to determine if the zone is non-global or not since the
	 * package is being installed or removed to the current "zone".
	 *
	 * Since the root directory being tested is not "/" then you have to
	 * look into the target to try and infer zone type using means other
	 * than the zone name only.
	 * *********************************************************************
	 */

	/* reject if any items found that cannot be in a non-global zone */

	/* .tmp_proto must not exist */

	r = testPath(TEST_NOT_EXISTS, "%s/%s", rootPath, ".tmp_proto");
	if (r != R_SUCCESS) {
		/* $R/.tmp_proto cannot exist in a non-global zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
			rootPath, "/.tmp_proto");
		return (R_FAILURE);
	}

	/* /var must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/var");
	if (r != R_SUCCESS) {
		/* $R/var cannot be a symbolic link in a non-global zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_DOES_NOT_EXIST,
			rootPath, "/var");
		return (R_FAILURE);
	}

	/* $ROOTDIR/tmp/root/kernel must not exist */

	r = testPath(TEST_NOT_EXISTS,
		"%s/%s", rootPath, "/tmp/root/kernel");
	if (r != R_SUCCESS) {
		/* $R/tmp/root/kernel cannot exist in a non-global zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
			rootPath, "/tmp/root/kernel");
		return (R_FAILURE);
	}

	/*
	 * *********************************************************************
	 * no items exist in $ROOTDIR that identify something other than
	 * a non-global zone.
	 *
	 * if in global zone no more tests possible: is a non-global zone
	 * *********************************************************************
	 */

	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
		/* in the global zone */
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE,
			rootPath);
		return (R_SUCCESS);
	}

	/*
	 * *********************************************************************
	 * In non-global zone: interrogate zone name and type.
	 *
	 * The parent zone is the zone that the "pkgadd" or "pkgrm" command was
	 * run in. The child zone is the zone that the "pkginstall" or
	 * "pkgremove" command was run in.
	 * *********************************************************************
	 */

	/*
	 * If parent zone name and current zone name defined, and
	 * both zone names are the same, since pkgcond is running
	 * inside of a non-global zone, this is how the scratch
	 * zone is implemented, so target is a non-global zone
	 */

	if ((a_gdt->gd_parentZoneName != NULL) &&
		(a_gdt->gd_currentZoneName != NULL) &&
		(strcmp(a_gdt->gd_parentZoneName,
					a_gdt->gd_currentZoneName) == 0)) {
			/* parent and current zone name identical: non-gz */
			log_msg(LOG_MSG_DEBUG, DBG_NGZN_PARENT_CHILD_SAMEZONE,
				rootPath, a_gdt->gd_parentZoneName);
			return (R_SUCCESS);
	}

	/*
	 * In non-global zone if inherited FS's exits
	 * or zone specific read only FS's exist
	 * or it is in a mounted state.
	 */

	if (a_gdt->gd_inheritedFileSystems != NULL ||
		a_gdt->gd_srFsMountedRO || a_gdt->inMountedState) {
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
		return (R_SUCCESS);
	}

	/*
	 * the parent and current zone name are not the same;
	 * interrogate the zone types: the parent must be global
	 * and the current must be non-global, which would be set
	 * when a package command is run in the global zone that in
	 * turn runs a package command within the non-global zone.
	 */

	/* if defined, parent zone type must be "global" */

	if ((a_gdt->gd_parentZoneType != NULL) &&
		(strcmp(a_gdt->gd_parentZoneType, "nonglobal") == 0)) {
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_PARENT_ZONETYPE,
			rootPath, "nonglobal");
		return (R_FAILURE);
	}

	/* if defined, current zone type must be "nonglobal" */

	if ((a_gdt->gd_currentZoneType != NULL) &&
		(strcmp(a_gdt->gd_currentZoneType, GLOBAL_ZONENAME) == 0)) {
		log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_CURRENT_ZONETYPE,
			rootPath, GLOBAL_ZONENAME);
		return (R_FAILURE);
	}

	/*
	 * *********************************************************************
	 * no other tests possible: target is a non-global zone
	 * *********************************************************************
	 */

	log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_running_system
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must not be initial installation to the install root
 *  - must not be installation of a zone
 *  - must not be a diskless client
 *  - $ROOTDIR must be "/"
 *  - zone name must be "global"
 */

static int
cmd_is_running_system(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_running_system";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* a running system cannot be a diskless client */

		r = cmd_is_diskless_client(argc, argv, a_gdt);

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				return (R_FAILURE);
			case R_FAILURE:
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* if root path is "/" then check zone name */

	if (strcmp(rootPath, "/") != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IRST_ROOTPATH_BAD, rootPath, "/");
		return (R_FAILURE);
	}

	/* zone name must be global */

	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_BAD, rootPath,
			GLOBAL_ZONENAME);
		return (R_FAILURE);
	}

	/* must not be initial installation to the install root */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		/* initial install: install root cannot be the running system */
		log_msg(LOG_MSG_DEBUG, DBG_IRST_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial zone install: no path can be running system */
		log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* target is a running system */

	log_msg(LOG_MSG_DEBUG, DBG_IRST_PATH_IS_RUNNING_SYSTEM, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_can_add_driver
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * Implementation:
 * A driver can be added to the system if the components of a Solaris
 * instance capable of loading drivers is present and it is not the
 * currently running system.
 */

static int
cmd_can_add_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "can_add_driver";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* see if this is the current running system */

		r = cmd_is_running_system(argc, argv, a_gdt);

		/* cannot be a diskless client */

		if (r != R_SUCCESS) {
			r = cmd_is_diskless_client(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				/* is a running system */
				return (R_FAILURE);
			case R_FAILURE:
				/* not a running syste */
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				/* cannot determine if is a running system */
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* /etc must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/etc");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
			rootPath, "/etc");
		return (R_FAILURE);
	}

	/* /platform must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/platform");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
			rootPath, "/platform");
		return (R_FAILURE);
	}

	/* /kernel must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
			rootPath, "/kernel");
		return (R_FAILURE);
	}

	/* can add a driver */

	log_msg(LOG_MSG_DEBUG, DBG_ADDV_YES, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_can_update_driver
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * Implementation:
 * A driver can be added to the system if the components of a Solaris
 * instance capable of loading drivers is present and it is not the
 * currently running system.
 */

static int
cmd_can_update_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "can_update_driver";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* see if this is the current running system */

		r = cmd_is_running_system(argc, argv, a_gdt);

		/* cannot be a diskless client */

		if (r != R_SUCCESS) {
			r = cmd_is_diskless_client(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				/* is a running system */
				return (R_FAILURE);
			case R_FAILURE:
				/* not a running syste */
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				/* cannot determine if is a running system */
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* /etc must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/etc");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
			rootPath, "/etc");
		return (R_FAILURE);
	}

	/* /platform must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/platform");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
			rootPath, "/platform");
		return (R_FAILURE);
	}

	/* /kernel must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
			rootPath, "/kernel");
		return (R_FAILURE);
	}

	/* can update driver */

	log_msg(LOG_MSG_DEBUG, DBG_UPDV_YES, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_can_remove_driver
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * Implementation:
 * A driver can be added to the system if the components of a Solaris
 * instance capable of loading drivers is present and it is not the
 * currently running system.
 */

static int
cmd_can_remove_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "can_remove_driver";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* see if this is the current running system */

		r = cmd_is_running_system(argc, argv, a_gdt);

		/* cannot be a diskless client */

		if (r != R_SUCCESS) {
			r = cmd_is_diskless_client(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				/* is a running system */
				return (R_FAILURE);
			case R_FAILURE:
				/* not a running syste */
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				/* cannot determine if is a running system */
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* /etc must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/etc");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
			rootPath, "/etc");
		return (R_FAILURE);
	}

	/* /platform must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/platform");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
			rootPath, "/platform");
		return (R_FAILURE);
	}

	/* /kernel must exist and must not be a symbolic link */

	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
		"%s/%s", rootPath, "/kernel");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
			rootPath, "/kernel");
		return (R_FAILURE);
	}

	/* can remove driver */

	log_msg(LOG_MSG_DEBUG, DBG_RMDV_YES, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_path_writable
 * Description:	determine if target path is writable (not inherited)
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 * - path must be found in the file systems configured
 * - file system type must not be "inherited"
 * - mount options must not include "read only"
 */

static int
cmd_is_path_writable(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	FSI_T	*list;
	char	*rootPath = NULL;
	int	c;
	int	n;
	int	nn;
	int	r;
	long	listSize;
	long	rootPathLen;
static	char	*cmdName = "is_path_writable";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
		recursion--;
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc != 1) {
		(void) usage(ERR_REQUIRED_ROOTPATH_MISSING, cmdName);
		return (R_USAGE);
	}

	if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
		return (R_ERROR);
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* search file system conf for this path */

	rootPathLen = strlen(rootPath);
	list = a_gdt->gd_fileSystemConfig;
	listSize = a_gdt->gd_fileSystemConfigLen;
	for (nn = 0, n = 0; n < listSize; n++) {
		long	mplen = strlen(list[n].fsi_mntPoint);
		if (rootPathLen < mplen) {
			/* root path is longer than target, ignore */
			continue;
		}
		if (strncmp(rootPath, list[n].fsi_mntPoint, mplen) == 0) {
			/* remember last partial match */
			nn = n;
		}
	}

	log_msg(LOG_MSG_DEBUG, DBG_PWRT_INFO,
		rootPath, list[nn].fsi_mntPoint, list[nn].fsi_fsType,
		list[nn].fsi_mntOptions);

	/*
	 * need to determine if the mount point is writeable:
	 * If parent mount point is "inherited" then it is not writeable
	 */

	if (strcmp(list[nn].fsi_fsType, FSTYPE_INHERITED) == 0) {
		log_msg(LOG_MSG_DEBUG, DBG_PWRT_INHERITED, rootPath,
			list[nn].fsi_mntOptions);
		return (R_FAILURE);
	}

	/* see if the file system is mounted with the "read only" option */

	r = mountOptionPresent(list[nn].fsi_mntOptions, MNTOPT_RO);
	if (r == R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_PWRT_READONLY,
			rootPath, list[nn].fsi_mntOptions);
		return (R_FAILURE);
	}

	/* target path is writable */

	log_msg(LOG_MSG_DEBUG, DBG_PWRT_IS, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_alternative_root
 * Description:	determine if target is an alternative root
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * Implementation:
 *  - success if an initial installation to the install root
 *	(an initial install to $PKG_INSTALL_ROOT means that $PKG_INSTALL_ROOT
 *	points to an alternative root that is under construction)
 *  - must not be installation of a zone
 *  - must not be a boot environment
 *  - must not be a diskless client
 *  - must not be a mounted miniroot
 *  - must not be a netinstall image
 *  - must not be a nonglobal zone
 *  - must not be a running system
 *  - $ROOTDIR must not be "/"
 *  - $ROOTDIR/var must exist
 */

static int
cmd_is_alternative_root(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_alternative_root";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/*
		 * an alternative root cannot be any of the following
		 */

		/* cannot be a boot_environment */

		r = cmd_is_boot_environment(argc, argv, a_gdt);

		/* cannot be a diskless_client */

		if (r != R_SUCCESS) {
			r = cmd_is_diskless_client(argc, argv, a_gdt);
		}

		/* cannot be a mounted_miniroot */

		if (r != R_SUCCESS) {
			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
		}

		/* cannot be a netinstall_image */

		if (r != R_SUCCESS) {
			r = cmd_is_netinstall_image(argc, argv, a_gdt);
		}

		/* cannot be a nonglobal_zone */

		if (r != R_SUCCESS) {
			r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
		}

		/* cannot be a running_system */

		if (r != R_SUCCESS) {
			r = cmd_is_running_system(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		/* return failure if any of the preceeding are true */

		switch (r) {
			case R_SUCCESS:
				return (R_FAILURE);
			case R_FAILURE:
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* return success if initial installation */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		log_msg(LOG_MSG_DEBUG, DBG_IALR_INITIAL_INSTALL, rootPath);
		return (R_SUCCESS);
	}

	/* root path must not be "/" */

	if (strcmp(rootPath, "/") == 0) {
		log_msg(LOG_MSG_DEBUG, DBG_IALR_BAD_ROOTPATH, rootPath, "/");
		return (R_FAILURE);
	}

	/* /var must exist */

	r = testPath(TEST_EXISTS,
		"%s/%s", rootPath, "/var");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_IALR_PATH_DOES_NOT_EXIST,
			rootPath, "/var");
		return (R_FAILURE);
	}

	/* must not be installation of a zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial zone install: no path can be alternative root */
		log_msg(LOG_MSG_DEBUG, DBG_IALR_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* target is an alternative root */

	log_msg(LOG_MSG_DEBUG, DBG_IALR_IS, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_boot_environment
 * Description:	determine if target is an alternative, inactive boot environment
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must not be initial installation to the install root
 *  - must not be installation of a zone
 *  - must not be a diskless client
 *  - must not be a netinstall image
 *  - must not be a mounted miniroot
 *  - $ROOTDIR must not be "/"
 *  - $ROOTDIR/etc/lutab must exist
 *  - $ROOTDIR/etc/lu must exist and must be a directory
 */

static int
cmd_is_boot_environment(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_boot_environment";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
		/*
		 * a boot environment cannot be any of the following
		 */

		/* cannot be a diskless client */

		r = cmd_is_diskless_client(argc, argv, a_gdt);

		/* cannot be a netinstall_image */

		if (r != R_SUCCESS) {
			r = cmd_is_netinstall_image(argc, argv, a_gdt);
		}

		/* cannot be a mounted_miniroot */

		if (r != R_SUCCESS) {
			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
		}

		/* no need to guard against recursion any more */

		recursion--;

		/* return failure if any of the preceeding are true */

		switch (r) {
			case R_SUCCESS:
				return (R_FAILURE);
			case R_FAILURE:
				break;
			case R_USAGE:
			case R_ERROR:
			default:
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* root path must not be "/" */

	if (strcmp(rootPath, "/") == 0) {
		log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ROOTPATH, rootPath, "/");
		return (R_FAILURE);
	}

	/* zone name must be global */

	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
		log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ZONE, rootPath,
			GLOBAL_ZONENAME);
		return (R_FAILURE);
	}

	/* $ROOTDIR/etc/lutab must exist */

	r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/lutab");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLUTAB, rootPath,
			"/etc/lutab");
		return (R_FAILURE);
	}

	/* $ROOTDIR/etc/lu must exist */

	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
		"%s/%s", rootPath, "/etc/lu");
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLU, rootPath, "/etc/lu");
		return (R_FAILURE);
	}

	/* must not be initial installation */

	if ((a_gdt->gd_initialInstall == B_TRUE) &&
	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
		log_msg(LOG_MSG_DEBUG, DBG_BENV_INITIAL_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* must not be installation of a zone */

	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
		/* initial zone install: no path can be boot environment */
		log_msg(LOG_MSG_DEBUG, DBG_BENV_ZONE_INSTALL, rootPath);
		return (R_FAILURE);
	}

	/* target is a boot environment */

	log_msg(LOG_MSG_DEBUG, DBG_BENV_IS, rootPath);

	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_sparse_root_ng_zone
 * Description:	determine if target is a sparse non-global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENATION:
 *  - must be a non-global zone
 *  - inherited file systems must be present, and/or
 *  - read-only lofs file systems must be present
 */

static int
cmd_is_sparse_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_sparse_root_nonglobal_zone";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* see if this is a non-global zone */

		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				/* is a non-global zone */
				break;
			case R_FAILURE:
				/* not a non-global zone */
				return (R_FAILURE);
			case R_USAGE:
			case R_ERROR:
			default:
				/* cannot determine if non-global zone */
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/*
	 * in a non-global zone:
	 * if any file systems are inherited, or if /usr is read only,
	 * then the target is a sparse root non-global zone.
	 */

	if ((a_gdt->gd_inheritedFileSystems != (char **)NULL) ||
		(a_gdt->gd_srFsMountedRO == B_TRUE)) {
		/* no inherited file systems */
		log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS, rootPath);
		return (R_SUCCESS);
	}

	/* target is not a sparse root non-global zone */

	log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS_NOT, rootPath);

	return (R_FAILURE);

}

/*
 * Name:	cmd_is_what
 * Description:	determine what the target is
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 */

static int
cmd_is_what(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	cur_cmd;
	int	r;
static	char	*cmdName = "is_what";

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/*
	 * construct the command line for all of the packages
	 */

	argc = 0;
	argv[argc++] = strdup(get_prog_name());
	argv[argc++] = strdup(rootPath);

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/* search for specified subcommand and execute if found */

	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
		int	result;

		/* do not recursively call this function */

		if (cmds[cur_cmd].c_func == cmd_is_what) {
			continue;
		}

		/* call subcommand with its own argc/argv */

		result = cmds[cur_cmd].c_func(argc, argv, a_gdt);

		/* process result code and exit */

		result = adjustResults(result);
		log_msg(LOG_MSG_INFO, MSG_IS_WHAT_RESULT,
			cmds[cur_cmd].c_name, result);
	}
	return (R_SUCCESS);
}

/*
 * Name:	cmd_is_whole_root_ng_zone
 * Description:	determine if target is a global zone
 * Scope:	public
 * Arguments:	argc,argv:
 *		  - optional path to target to test
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 * IMPLEMENTATION:
 *  - must be a non-global zone
 *  - no inherited file systems may be present
 *  - no read-only lofs file systems may be present
 */

static int
cmd_is_whole_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
{
	char	*rootPath = NULL;
	int	c;
	int	r;
static	char	*cmdName = "is_whole_root_nonglobal_zone";
static	int	recursion = 0;

	/* process any command line options */

	while ((c = getopt(argc, argv, ":")) != EOF) {
		switch (c) {
		case '\0':	/* prevent end-of-loop not reached warning */
			break;
		case '?':
		default:
			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
			return (R_USAGE);
		}
	}

	/* prevent recursion */

	if (recursionCheck(&recursion, cmdName) == B_FALSE) {

		/* see if this is a non-global zone */

		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);

		/* no need to guard against recursion any more */

		recursion--;

		switch (r) {
			case R_SUCCESS:
				/* is a non-global zone */
				break;
			case R_FAILURE:
				/* not a non-global zone */
				return (R_FAILURE);
			case R_USAGE:
			case R_ERROR:
			default:
				/* cannot determine if non-global zone */
				return (r);
		}
	}

	/* normalize argc/argv */

	argc -= optind;
	argv += optind;

	/* error if more than one argument */

	if (argc > 1) {
		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
		return (R_USAGE);
	}

	/* process root path if first argument present */

	if (argc == 1) {
		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
			return (R_ERROR);
		}
	}

	/* get current root path */

	r = getRootPath(&rootPath);
	if (r != R_SUCCESS) {
		return (r);
	}

	/* start of command debugging information */

	echoDebug(DBG_ROOTPATH_IS, rootPath);

	/*
	 * in a non-global zone:
	 * if no file systems are inherited, and if /usr is not
	 * read only, then the target is a whole root non-global zone.
	 */

	if ((a_gdt->gd_inheritedFileSystems == (char **)NULL) &&
		(a_gdt->gd_srFsMountedRO == B_FALSE)) {
		/* no inherited file systems */
		log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS, rootPath);
		return (R_SUCCESS);
	}

	/* target is not a whole-root non-global zone */

	log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS_NOT, rootPath);

	return (R_FAILURE);
}

/*
 * *****************************************************************************
 * utility support functions
 * *****************************************************************************
 */

/*
 * Name:	getMountOption
 * Description:	return next mount option in a string
 * Arguments:	p - pointer to string containing mount options
 * Output:	none
 * Returns:	char * - pointer to next option in string "p"
 * Side Effects: advances input "p" and inserts \0 in place of the
 *		option separator found.
 */

static char *
getMountOption(char **p)
{
	char *cp = *p;
	char *retstr;

	/* advance past all white space */

	while (*cp && isspace(*cp))
		cp++;

	/* remember start of next option */

	retstr = cp;

	/* advance to end of string or option separator */

	while (*cp && *cp != ',')
		cp++;

	/* replace separator with '\0' if not at end of string */
	if (*cp) {
		*cp = '\0';
		cp++;
	}

	/* reset caller's pointer and return pointer to option */

	*p = cp;
	return (retstr);
}

/*
 * Name:	mountOptionPresent
 * Description:	determine if specified mount option is present in list
 *		of mount point options
 * Arguments:	a_mntOptions - pointer to string containing list of mount
 *			point options to search
 *		a_opt - pointer to string containing option to search for
 * Output:	none
 * Returns:	R_SUCCESS - option is present in list of mount point options
 *		R_FAILURE - options is not present
 *		R_ERROR - unable to determine if option is present or not
 */

static int
mountOptionPresent(char *a_mntOptions, char *a_opt)
{
	char tmpopts[MNT_LINE_MAX];
	char *f, *opts = tmpopts;

	/* return false if no mount options present */

	if ((a_opt == NULL) || (*a_opt == '\0')) {
		return (R_FAILURE);
	}

	/* return not present if no list of options to search */

	if (a_mntOptions == NULL) {
		return (R_FAILURE);
	}

	/* return not present if list of options to search is empty */

	if (*a_mntOptions == '\0') {
		return (R_FAILURE);
	}

	/* make local copy of option list to search */

	(void) strcpy(opts, a_mntOptions);

	/* scan each option looking for the specified option */

	f = getMountOption(&opts);
	for (; *f; f = getMountOption(&opts)) {
		/* return success if option matches target */
		if (strncmp(a_opt, f, strlen(a_opt)) == 0) {
			return (R_SUCCESS);
		}
	}

	/* option not found */

	return (R_FAILURE);
}

/*
 * Name:	sortedInsert
 * Description:	perform an alphabetical sorted insert into a list
 * Arguments:	r_list - pointer to list to insert next entry into
 *		a_listSize - pointer to current list size
 *		a_mntPoint - mount point to insert (is sort key)
 *		a_fsType - file system type for mount point
 *		a_mntOptions - file syste mount options for mount point
 * Output:	None
 * Returns:	None
 */

static void
sortedInsert(FSI_T **r_list, long *a_listSize, char *a_mntPoint,
	char *a_fsType, char *a_mntOptions)
{
	int	listSize;
	FSI_T	*list;
	int	n;

	/* entry assertions */

	assert(a_listSize != (long *)NULL);
	assert(a_mntPoint != NULL);
	assert(a_fsType != NULL);
	assert(a_mntOptions != NULL);

	/* entry debugging info */

	echoDebug(DBG_SINS_ENTRY, a_mntPoint, a_fsType, a_mntOptions);

	/* localize references to the list and list size */

	listSize = *a_listSize;
	list = *r_list;

	/*
	 * if list empty insert this entry as the first one in the list
	 */

	if (listSize == 0) {
		/* allocate new entry for list */
		listSize++;
		list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));

		/* first entry is data passed to this function */
		list[0].fsi_mntPoint = strdup(a_mntPoint);
		list[0].fsi_fsType = strdup(a_fsType);
		list[0].fsi_mntOptions = strdup(a_mntOptions);

		/* second entry is all NULL - end of entry marker */
		list[1].fsi_mntPoint = NULL;
		list[1].fsi_fsType = NULL;
		list[1].fsi_mntOptions = NULL;

		/* restore list and list size references to caller */
		*a_listSize = listSize;
		*r_list = list;

		return;
	}

	/*
	 * list not empty - scan looking for largest match
	 */

	for (n = 0; n < listSize; n++) {
		int	c;

		/* compare target with current list entry */

		c = strcmp(list[n].fsi_mntPoint, a_mntPoint);

		if (c == 0) {
			char	*me;
			long	len;

			/* entry already in list -- merge entries */

			len = strlen(list[n].fsi_mntOptions) +
				strlen(a_mntOptions) + 2;
			me = (char *)calloc(1, len);

			/* merge two mount options lists into one */

			(void) strlcat(me, list[n].fsi_mntOptions, len);
			(void) strlcat(me, ",", len);
			(void) strlcat(me, a_mntOptions, len);

			/* free old list, replace with merged one */

			free(list[n].fsi_mntOptions);
			list[n].fsi_mntOptions = me;

			echoDebug(DBG_SORTEDINS_SKIPPED,
				n, list[n].fsi_mntPoint, a_fsType,
				list[n].fsi_fsType, a_mntOptions,
				list[n].fsi_mntOptions);

			continue;
		} else if (c < 0) {
			/* entry before this one - skip */
			continue;
		}

		/*
		 * entry after this one - insert new entry
		 */

		/* allocate one more entry and make space for new entry */
		listSize++;
		list = (FSI_T *)realloc(list,
			sizeof (FSI_T)*(listSize+1));
		(void) memmove(&(list[n+1]), &(list[n]),
			sizeof (FSI_T)*(listSize-n));

		/* insert this entry into list */
		list[n].fsi_mntPoint = strdup(a_mntPoint);
		list[n].fsi_fsType = strdup(a_fsType);
		list[n].fsi_mntOptions = strdup(a_mntOptions);

		/* restore list and list size references to caller */
		*a_listSize = listSize;
		*r_list = list;

		return;
	}

	/*
	 * all entries are before this one - append to end of list
	 */

	/* allocate new entry at end of list */
	listSize++;
	list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));

	/* append this entry to the end of the list */
	list[listSize-1].fsi_mntPoint = strdup(a_mntPoint);
	list[listSize-1].fsi_fsType = strdup(a_fsType);
	list[listSize-1].fsi_mntOptions = strdup(a_mntOptions);

	/* restore list and list size references to caller */
	*a_listSize = listSize;
	*r_list = list;
}

/*
 * Name:	calculateFileSystemConfig
 * Description:	generate sorted list of all mounted file systems
 * Arguments:	a_gdt - global data structure to place sorted entries into
 * Output:	None
 * Returns:	R_SUCCESS - successfully generated mounted file systems list
 *		R_FAILURE - options is not present
 *		R_ERROR - unable to determine if option is present or not
 */

static int
calculateFileSystemConfig(GLOBALDATA_T *a_gdt)
{
	FILE		*fp;
	struct mnttab	mntbuf;
	FSI_T		*list;
	long		listSize;
	boolean_t	readOnlyMountFound = B_FALSE;

	/* entry assetions */

	assert(a_gdt != (GLOBALDATA_T *)NULL);

	/* entry debugging info */

	echoDebug(DBG_CALCSCFG_ENTRY);

	/* allocate a list that has one termination entry */

	list = (FSI_T *)calloc(1, sizeof (FSI_T));
	list[0].fsi_mntPoint = NULL;
	list[0].fsi_fsType = NULL;
	list[0].fsi_mntOptions = NULL;
	listSize = 0;

	/* insert entries for all inherited file systems */

	if (a_gdt->gd_inheritedFileSystems) {
		int n;
		char **ifs = a_gdt->gd_inheritedFileSystems;

		/* debugging info */

		echoDebug(DBG_CALCSCFG_INHERITED);

		/* insert all inherited file systems */

		for (n = 0; ifs[n]; n++) {
			sortedInsert(&list, &listSize, ifs[n],
				FSTYPE_INHERITED, MNTOPT_RO);
		}
	}

	/* open the mount table for reading */

	fp = fopen(MNTTAB, "r");
	if (fp == (FILE *)NULL) {
		return (R_ERROR);
	}

	/* debugging info */

	echoDebug(DBG_CALCSCFG_MOUNTED);

	/* go through all the specials looking for the device */

	while (getmntent(fp, &mntbuf) == 0) {
		if (mntbuf.mnt_mountp[0] == '/') {
			sortedInsert(&list, &listSize,
			strdup(mntbuf.mnt_mountp),
			strdup(mntbuf.mnt_fstype),
			strdup(mntbuf.mnt_mntopts ? mntbuf.mnt_mntopts : ""));
		}

		/*
		 * Set flag if we are in a non-global zone and it is in
		 * the mounted state.
		 */

		if (strcmp(mntbuf.mnt_mountp, "/a") == 0 &&
			strcmp(mntbuf.mnt_special, "/a") == 0 &&
			strcmp(mntbuf.mnt_fstype, "lofs") == 0) {
			a_gdt->inMountedState = B_TRUE;
		}

		if (!readOnlyMountFound) {
			readOnlyMountFound = checkForReadOnlyMount(a_gdt,
			    mntbuf.mnt_mountp, mntbuf.mnt_fstype,
			    mntbuf.mnt_mntopts);
		}
	}

	/* close mount table file */

	(void) fclose(fp);

	/* store list pointers in global data structure */

	a_gdt->gd_fileSystemConfig = list;
	a_gdt->gd_fileSystemConfigLen = listSize;

	return (R_SUCCESS);
}

/*
 * Name:	checkForReadOnlyMount
 * Description:	given a mount point, type and options, determine if the
 *              mounted file system is part of a "sparse root" configuration
 *              by checking if the known Zone directories a ro LOFS mounted.
 * Arguments:	a_gdt - global data structure to place sorted entries into
 *		a_mntPoint - pointer to string representing mount point
 *		a_fsType - pointer to string representing file system type
 *		a_mntOptions - pointer to string representing the options
 *			used to mount the file system
 * Returns:	B_TRUE - if sparse root mount is found
 * 		B_FLASE - if no sparse root mount's are found
 * Side Effects: set:
 *			a_gdt->gd_srFsMountedRO = B_TRUE
 *		if the mounted file system is part of a 'sparse root' config
 */

static boolean_t
checkForReadOnlyMount(GLOBALDATA_T *a_gdt, char *a_mntPoint,
	char *a_fsType, char *a_mntOptions)
{
	/* entry assertions */
	int	i;
	char mntPoint[MAXPATHLEN];
	char *zDirs[] = { "/usr", "/lib", "/platform", "/sbin", NULL };
	char *aZDirs[] = { "/a/usr", "/a/lib", "/a/platform", "/a/sbin", NULL };

	assert(a_gdt != (GLOBALDATA_T *)NULL);
	assert(a_mntPoint != NULL);
	assert(a_fsType != NULL);

	/* return if no read-only mount option */

	if (mountOptionPresent(a_mntOptions, MNTOPT_RO) != R_SUCCESS) {
		return (B_FALSE);
	}

	/* return if file system is not read-only mounted */

	if (strcmp(a_fsType, MNTTYPE_LOFS) != 0) {
		return (B_FALSE);
	}

	/* file system is a read-only lofs mounted.  */

	/* Check read-only lofs mount's appended to the command line path */

	if (a_gdt->gd_cmdline_path != NULL) {
		if (strncmp(a_mntPoint, a_gdt->gd_cmdline_path,
			strlen(a_gdt->gd_cmdline_path)) == 0) {
			for (i = 0; zDirs[i] != NULL; i++) {
				(void) snprintf(mntPoint, sizeof (mntPoint),
				    "%s%s", a_gdt->gd_cmdline_path,
				    zDirs[i]);
				if (strcmp(a_mntPoint, mntPoint) == 0) {
					echoDebug(DBG_CKSR_FSREADONLY,
					    a_mntPoint, a_fsType);
					a_gdt->gd_srFsMountedRO = B_TRUE;
					return (B_TRUE);
				}
			}
		}

	/* Check read-only lofs mount's in the mounted state */

	} else if (a_gdt->inMountedState) {
		for (i = 0; aZDirs[i] != NULL; i++) {
			if (strncmp(a_mntPoint, aZDirs[i],
			    sizeof (aZDirs[i])) == 0) {
				echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
				    a_fsType);
				a_gdt->gd_srFsMountedRO = B_TRUE;
				return (B_TRUE);
			}
		}

	/* Check read-only lofs mount's for live system */

	} else {
		for (i = 0; zDirs[i] != NULL; i++) {
			if (strncmp(a_mntPoint, zDirs[i],
			    sizeof (zDirs[i])) == 0) {
				echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
				    a_fsType);
				a_gdt->gd_srFsMountedRO = B_TRUE;
				return (B_TRUE);
			}
		}
	}

	return (B_FALSE);
}

/*
 * Name: 	adjustResults
 * Description:	adjust output result code before existing
 * Arguments:	a_result - result code to adjust
 * Returns:	int - adjusted result code
 */

static int
adjustResults(int a_result)
{
	boolean_t	negate = getNegateResults();
	int		realResult;

	/* adjust code as appropriate */

	switch (a_result) {
	case R_SUCCESS:		/* condition satisfied */
		realResult = ((negate == B_TRUE) ? 1 : 0);
		break;
	case R_FAILURE:		/* condition not satisfied */
		realResult = ((negate == B_TRUE) ? 0 : 1);
		break;
	case R_USAGE:		/* usage errors */
		realResult = 2;
		break;
	case R_ERROR:		/* condition could not be determined */
	default:
		realResult = 3;
		break;
	}

	/* debugging output */

	log_msg(LOG_MSG_DEBUG, DBG_ADJUST_RESULTS, a_result, negate,
		realResult);

	/* return results */

	return (realResult);
}

/*
 * Name:        setCmdLinePath
 * Description:	set global command line path
 * Arguments:   path - path to set from the command line
 *              args - command line args
 *              num_args - number of command line args
 * Returns:     R_SUCCESS - root path successfully set
 *              R_FAILURE - root path could not be set
 *              R_ERROR - fatal error attempting to set root path
 */

static void
setCmdLinePath(char **path, char **args, int num_args)
{
	char   rp[PATH_MAX] = { '\0' };
	struct stat statbuf;

	if (*path != NULL) {
		return;
	}

	/*
	 * If a path "pkgcond is_global_zone [path]" is provided on the
	 * command line it must be the last argument.
	 */

	if (realpath(args[num_args - 1], rp) != NULL) {
		if (stat(rp, &statbuf) == 0) {
			/* make sure the target is a directory */
			if ((statbuf.st_mode & S_IFDIR)) {
				*path = strdup(rp);
			} else {
				*path = NULL;
			}
		}
	}
}

/*
 * Name:	setRootPath
 * Description:	set global root path returned by getRootPath
 * Arguments:	a_path - root path to set
 *		a_mustExist - B_TRUE if path must exist (else error)
 *			- B_FALSE if path may not exist
 * Returns:	R_SUCCESS - root path successfully set
 *		R_FAILURE - root path could not be set
 *		R_ERROR - fatal error attempting to set root path
 */

static int
setRootPath(char *a_path, char *a_envVar, boolean_t a_mustExist)
{
	char		rp[PATH_MAX] = { '\0' };
	struct stat	statbuf;

	/* if no data then issue warning and return success */

	if ((a_path == NULL) || (*a_path == '\0')) {
		echoDebug(DBG_NO_DEFAULT_ROOT_PATH_SET);
		return (R_SUCCESS);
	}

	/* path present - resolve to absolute path */

	if (realpath(a_path, rp) == NULL) {
		if (a_mustExist == B_TRUE) {
			/* must exist ... error */
			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
				a_path, strerror(errno));
			return (R_ERROR);
		} else {
			/* may not exist - use path as specified */
			(void) strcpy(rp, a_path);
		}
	}

	/* debugging output */

	echoDebug(DBG_DEFAULT_ROOT_PATH_SET, rp, a_envVar ? a_envVar : "");

	/* validate path existence if it must exist */

	if (a_mustExist == B_TRUE) {

		/* get node status */

		if (stat(rp, &statbuf) != 0) {
			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
				rp, strerror(errno));
			return (R_ERROR);
		}

		/* make sure the target is a directory */

		if (!(statbuf.st_mode & S_IFDIR)) {
			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_NOT_DIR, rp);
			return (R_ERROR);
		}
	}

	/* target exists and is a directory - set */

	echoDebug(DBG_SET_ROOT_PATH_TO, rp);

	/* store copy of resolved root path */

	_rootPath = strdup(rp);

	/* success! */

	return (R_SUCCESS);
}

/*
 * Name:	testPath
 * Description:	determine if a path meets the specified conditions
 * Arguments:	a_tt - conditions to test path against
 * 		a_format - format to use to generate path
 *		arguments following a_format - as needed for a_format
 * Returns:	R_SUCCESS - the path meets all of the specified conditions
 *		R_FAILURE - the path does not meet all of the conditions
 *		R_ERROR - error attempting to test path
 */

/*PRINTFLIKE2*/
static int
testPath(TEST_TYPES a_tt, char *a_format, ...)
{
	char		*mbPath;	/* copy for the path to be returned */
	char		bfr[1];
	int		r;
	size_t		vres = 0;
	struct stat	statbuf;
	va_list		ap;
	int		fd;

	/* entry assertions */

	assert(a_format != NULL);
	assert(*a_format != '\0');

	/* determine size of the message in bytes */

	va_start(ap, a_format);
	vres = vsnprintf(bfr, 1, a_format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	mbPath = (char *)calloc(1, vres+2);
	assert(mbPath != NULL);

	/* generate the results of the printf conversion */

	va_start(ap, a_format);
	vres = vsnprintf(mbPath, vres+1, a_format, ap);
	va_end(ap);

	assert(vres > 0);

	echoDebug(DBG_TEST_PATH, mbPath, (unsigned long)a_tt);

	/*
	 * When a path given to open(2) contains symbolic links, the
	 * open system call first resolves all symbolic links and then
	 * opens that final "resolved" path. As a result, it is not
	 * possible to check the result of an fstat(2) against the
	 * file descriptor returned by open(2) for S_IFLNK (a symbolic
	 * link) since all symbolic links are resolved before the
	 * target is opened.
	 *
	 * When testing the target as being (or not being) a symbolic
	 * link, first use lstat(2) against the target to determine
	 * whether or not the specified target itself is (or is not) a
	 * symbolic link.
	 */

	if (a_tt & (TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)) {
		/*
		 * testing target is/is not a symbolic link; use lstat
		 * to determine the status of the target itself rather
		 * than what the target might finally address.
		 */

		if (lstat(mbPath, &statbuf) != 0) {
			echoDebug(DBG_CANNOT_LSTAT_PATH, mbPath,
				strerror(errno));
			free(mbPath);
			return (R_FAILURE);
		}

		/* Is the target required to be a symbolic link? */

		if (a_tt & TEST_IS_SYMBOLIC_LINK) {
			/* target must be a symbolic link */
			if (!(statbuf.st_mode & S_IFLNK)) {
				/* failure: target is not a symbolic link */
				echoDebug(DBG_IS_NOT_A_SYMLINK, mbPath);
				free(mbPath);
				return (R_FAILURE);
			}
			/* success: target is a symbolic link */
			echoDebug(DBG_SYMLINK_IS, mbPath);
		}

		/* Is the target required to not be a symbolic link? */

		if (a_tt & TEST_NOT_SYMBOLIC_LINK) {
			/* target must not be a symbolic link */
			if (statbuf.st_mode & S_IFLNK) {
				/* failure: target is a symbolic link */
				echoDebug(DBG_IS_A_SYMLINK, mbPath);
				free(mbPath);
				return (R_FAILURE);
			}
			/* success: target is not a symbolic link */
			echoDebug(DBG_SYMLINK_NOT, mbPath);
		}

		/*
		 * if only testing is/is not a symbolic link, then
		 * no need to open the target: return success.
		 */

		if (!(a_tt &
		    (~(TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)))) {
			free(mbPath);
			return (R_SUCCESS);
		}
	}

	/* resolve path and remove any whitespace */

	r = resolvePath(&mbPath);
	if (r != R_SUCCESS) {
		echoDebug(DBG_TEST_PATH_NO_RESOLVE, mbPath);
		free(mbPath);
		if (a_tt & TEST_NOT_EXISTS) {
			return (R_SUCCESS);
		}
		return (r);
	}

	echoDebug(DBG_TEST_PATH_RESOLVE, mbPath);

	/* open the file - this is the basic existence test */

	fd = open(mbPath, O_RDONLY|O_LARGEFILE, 0);

	/* existence test failed if file cannot be opened */

	if (fd < 0) {
		/*
		 * target could not be opened - if testing for non-existence,
		 * return success, otherwise return failure
		 */
		if (a_tt & TEST_NOT_EXISTS) {
			echoDebug(DBG_CANNOT_ACCESS_PATH_OK, mbPath);
			free(mbPath);
			return (R_SUCCESS);
		}

		echoDebug(DBG_CANNOT_ACCESS_PATH_BUT_SHOULD,
		    mbPath, strerror(errno));
		free(mbPath);

		return (R_FAILURE);
	}

	/*
	 * target successfully opened - if testing for non-existence,
	 * return failure, otherwise continue with specified tests
	 */

	if (a_tt & TEST_NOT_EXISTS) {
		/* testing for non-existence: return failure */
		echoDebug(DBG_TEST_EXISTS_SHOULD_NOT, mbPath);
		free(mbPath);
		(void) close(fd);
		return (R_FAILURE);
	}

	/* get the file status */

	r = fstat(fd, &statbuf);
	if (r != 0) {
		echoDebug(DBG_PATH_DOES_NOT_EXIST, mbPath, strerror(errno));
		(void) close(fd);
		free(mbPath);
		return (R_FAILURE);
	}

	/* required to be a directory? */

	if (a_tt & TEST_IS_DIRECTORY) {
		if (!(statbuf.st_mode & S_IFDIR)) {
			/* is not a directory */
			echoDebug(DBG_IS_NOT_A_DIRECTORY, mbPath);
			free(mbPath);
			return (R_FAILURE);
		}
		/* a directory */
		echoDebug(DBG_DIRECTORY_IS, mbPath);
	}

	/* required to not be a directory? */

	if (a_tt & TEST_NOT_DIRECTORY) {
		if (statbuf.st_mode & S_IFDIR) {
			/* is a directory */
			echoDebug(DBG_IS_A_DIRECTORY, mbPath);
			free(mbPath);
			return (R_FAILURE);
		}
		/* not a directory */
		echoDebug(DBG_DIRECTORY_NOT, mbPath);
	}

	/* required to be a file? */

	if (a_tt & TEST_IS_FILE) {
		if (!(statbuf.st_mode & S_IFREG)) {
			/* is not a regular file */
			echoDebug(DBG_IS_NOT_A_FILE, mbPath);
			free(mbPath);
			return (R_FAILURE);
		}
		/* a regular file */
		echoDebug(DBG_FILE_IS, mbPath);
	}

	/* required to not be a file? */

	if (a_tt & TEST_NOT_FILE) {
		if (statbuf.st_mode & S_IFREG) {
			/* is a regular file */
			echoDebug(DBG_IS_A_FILE, mbPath);
			free(mbPath);
			return (R_FAILURE);
		}
		/* not a regular file */
		echoDebug(DBG_FILE_NOT, mbPath);
	}

	/*
	 * Find token (global) in file pointed to by mbPath.
	 * token is only compared to first word in mbPath.
	 */

	if (a_tt & TEST_GLOBAL_TOKEN_IN_FILE) {
		if (!(statbuf.st_mode & S_IFREG)) {
			/* is not a regular file */
			echoDebug(DBG_IS_NOT_A_FILE, mbPath);
			free(mbPath);
			return (R_FAILURE);
		}
		/* If global exists then we're not in a non-global zone */
		if (findToken(mbPath, GLOBAL_ZONENAME) == R_SUCCESS) {
			echoDebug(DBG_TOKEN__EXISTS, GLOBAL_ZONENAME, mbPath);
			free(mbPath);
			return (R_FAILURE);
		}
	}

	(void) close(fd);

	/* success! */

	echoDebug(DBG_TESTPATH_OK, mbPath);

	/* free up temp storage used to hold path to test */

	free(mbPath);

	return (R_SUCCESS);
}

/*
 * Name:        findToken
 * Description:	Find first token in file.
 * Arguments:
 *              path - file to search for token
 *              token - string to search for
 * Returns:
 *              R_SUCCESS - the token exists
 *              R_FAILURE - the token does not exist
 *              R_ERROR - fatal error attempting to find token
 */

static int
findToken(char *path, char *token)
{
	FILE	*fp;
	char	*cp;
	char	line[MAXPATHLEN];

	if (path == NULL || token == NULL) {
		return (R_ERROR);
	}
	if ((fp = fopen(path, "r")) == NULL) {
		return (R_ERROR);
	}

	while (fgets(line, sizeof (line), fp) != NULL) {
		for (cp = line; *cp && isspace(*cp); cp++);
		/* skip comments */
		if (*cp == '#') {
			continue;
		}
		if (pkgstrContainsToken(cp, token, ":")) {
			(void) fclose(fp);
			return (R_SUCCESS);
		}
	}
	(void) fclose(fp);
	return (R_FAILURE);
}


/*
 * Name:	resolvePath
 * Description:	fully resolve a path to an absolute real path
 * Arguments:	r_path - pointer to pointer to malloc()ed storage containing
 *			the path to resolve - this path may be reallocated
 *			as necessary to hold the fully resolved path
 * Output:	r_path - is realloc()ed as necessary
 * Returns:	R_SUCCESS - the path is fully resolved
 *		R_FAILURE - the path could not be resolved
 *		R_ERROR - fatal error attempting to resolve path
 */

static int
resolvePath(char **r_path)
{
	int		i;
	char		resolvedPath[MAXPATHLEN+1] = {'\0'};
	size_t		mbPathlen;	/* length of multi-byte path */
	size_t		wcPathlen;	/* length of wide-character path */
	wchar_t		*wcPath;	/* wide-character version of the path */
	wchar_t		*wptr;		/* scratch pointer */

	/* entry assertions */

	assert(r_path != (char **)NULL);

	/* return error if the path is completely empty */

	if (*r_path == '\0') {
		return (R_FAILURE);
	}

	/* remove all leading whitespace */

	removeLeadingWhitespace(r_path);

	/*
	 * convert to real path: an absolute pathname that names the same file,
	 * whose resolution does not involve ".", "..",  or  symbolic links.
	 */

	if (realpath(*r_path, resolvedPath) != NULL) {
		free(*r_path);
		*r_path = strdup(resolvedPath);
	}

	/*
	 *  convert the multi-byte version of the path to a
	 *  wide-character rendering, for doing our figuring.
	 */

	mbPathlen = strlen(*r_path);

	if ((wcPath = (wchar_t *)
		calloc(1, sizeof (wchar_t)*(mbPathlen+1))) == NULL) {
		return (R_FAILURE);
	}

	/*LINTED*/
	if ((wcPathlen = mbstowcs(wcPath, *r_path, mbPathlen)) == -1) {
		free(wcPath);
		return (R_FAILURE);
	}

	/*
	 *  remove duplicate slashes first ("//../" -> "/")
	 */

	for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
		*wptr++ = wcPath[i];

		if (wcPath[i] == '/') {
			i++;

			while (wcPath[i] == '/') {
				i++;
			}

			i--;
		}
	}

	*wptr = '\0';

	/*
	 *  now convert back to the multi-byte format.
	 */

	/*LINTED*/
	if (wcstombs(*r_path, wcPath, mbPathlen) == -1) {
		free(wcPath);
		return (R_FAILURE);
	}

	/* at this point have a path */

	/* free up temporary storage */

	free(wcPath);

	return (R_SUCCESS);
}

/*
 * Name:	removeLeadingWhitespace
 * Synopsis:	Remove leading whitespace from string
 * Description:	Remove all leading whitespace characters from a string
 * Arguments:	a_str - [RO, *RW] - (char **)
 *			Pointer to handle to string (in allocated storage) to
 *			remove all leading whitespace from
 * Returns:	void
 *			The input string is modified as follows:
 *			== NULL:
 *				- input string was NULL
 *				- input string is all whitespace
 *			!= NULL:
 *				- copy of input string with leading
 *				  whitespace removed
 * CAUTION:	The input string must be allocated space (via malloc() or
 *		strdup()) - it must not be a static or inline character string
 * NOTE:	The input string a_str will be freed with 'free'
 *		if it is all whitespace, or if it contains any leading
 *		whitespace characters
 * NOTE:    	Any string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the string is no longer needed.
 * Errors:	If the string cannot be created, the process exits
 */

static void
removeLeadingWhitespace(char **a_str)
{
	char	*o_str;

	/* entry assertions */

	assert(a_str != (char **)NULL);

	/* if string is null, just return */

	if (*a_str == NULL) {
		return;
	}
	o_str = *a_str;

	/* if string is empty, deallocate and return NULL */

	if (*o_str == '\0') {
		/* free string */
		free(*a_str);
		*a_str = NULL;
		return;
	}

	/* if first character is not a space, just return */

	if (!isspace(*o_str)) {
		return;
	}

	/* advance past all space characters */

	while ((*o_str != '\0') && (isspace(*o_str))) {
		o_str++;
	}

	/* if string was all space characters, deallocate and return NULL */

	if (*o_str == '\0') {
		/* free string */
		free(*a_str);
		*a_str = NULL;
		return;
	}

	/* have non-space/null byte, return dup, deallocate original */

	o_str = strdup(o_str);
	free(*a_str);
	*a_str = o_str;
}

/*
 * Name:	getZoneName
 * Description:	get the name of the zone this process is running in
 * Arguments:	r_zoneName - pointer to pointer to receive zone name
 * Output:	r_zoneName - a pointer to malloc()ed storage containing
 *			the zone name this process is running in is stored
 *			in the location pointed to by r_zoneName
 * Returns:	R_SUCCESS - the zone name is successfully returned
 *		R_FAILURE - the zone name is not successfully returned
 *		R_ERROR - error attempting to get the zone name
 */

static int
getZoneName(char **r_zoneName)
{
static char zoneName[ZONENAME_MAX] = { '\0' };

	/* if zone name not already present, retrieve and cache name */

	if (zoneName[0] == '\0') {
		if (getzonenamebyid(getzoneid(), zoneName,
			sizeof (zoneName)) < 0) {
			log_msg(LOG_MSG_ERR, ERR_CANNOT_GET_ZONENAME);
			return (R_ERROR);
		}
	}

	/* return cached zone name */

	*r_zoneName = zoneName;
	return (R_SUCCESS);
}

/*
 * Name:	getRootPath
 * Description:	get the root path being tested by this process
 * Arguments:	r_rootPath - pointer to pointer to receive root path
 * Output:	r_rootPath - a pointer to malloc()ed storage containing
 *			the root path name this process is testing
 * Returns:	R_SUCCESS - the root path is successfully returned
 *		R_FAILURE - the root path is not successfully returned
 *		R_ERROR - error attempting to get the root path
 */

static int
getRootPath(char **r_rootPath)
{
	*r_rootPath = _rootPath;
	return (R_SUCCESS);
}

/*
 * Name:	setVerbose
 * Description:	Turns on verbose output
 * Scope:	public
 * Arguments:	verbose = B_TRUE indicates verbose mode
 * Returns:	none
 */

static void
setVerbose(boolean_t setting)
{
	/* set log verbose messages */

	log_set_verbose(setting);

	/* set interactive messages */

	echoSetFlag(setting);
}

/*
 * Name:	negate_results
 * Description:	control negation of results
 * Scope:	public
 * Arguments:	setting
 *		== B_TRUE indicates negated results mode
 *		== B_FALSE indicates non-negated results mode
 * Returns:	none
 */

static void
setNegateResults(boolean_t setting)
{
	log_msg(LOG_MSG_DEBUG, DBG_SET_NEGATE_RESULTS,
		_negateResults, setting);

	_negateResults = setting;
}

/*
 * Name:	getNegateResults
 * Description:	Returns whether or not to results are negated
 * Scope:	public
 * Arguments:	none
 * Returns:	B_TRUE - results are negated
 *		B_FALSE - results are not negated
 */

static boolean_t
getNegateResults(void)
{
	return (_negateResults);
}

/*
 * Name:	usage
 * Description:	output usage string
 * Arguments:	a_format - format to use to generate message
 *		arguments following a_format - as needed for a_format
 * Output:	Outputs the usage string to stderr.
 * Returns:	R_ERROR
 */

static int
usage(char *a_format, ...)
{
	int		cur_cmd;
	char		cmdlst[LINE_MAX+1] = { '\0' };
	char		*message;
	char		bfr[1];
	char		*p = get_prog_name();
	size_t		vres = 0;
	va_list		ap;

	/* entry assertions */

	assert(a_format != NULL);
	assert(*a_format != '\0');

	/* determine size of the message in bytes */

	va_start(ap, a_format);
	/* LINTED warning: variable format specifier to vsnprintf(); */
	vres = vsnprintf(bfr, 1, a_format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	message = (char *)calloc(1, vres+2);
	assert(message != NULL);

	/* generate the results of the printf conversion */

	va_start(ap, a_format);
	/* LINTED warning: variable format specifier to vsnprintf(); */
	vres = vsnprintf(message, vres+1, a_format, ap);
	va_end(ap);

	assert(vres > 0);

	/* generate list of all defined conditions */

	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
		(void) strlcat(cmdlst, "\t", sizeof (cmdlst));
		(void) strlcat(cmdlst, cmds[cur_cmd].c_name, sizeof (cmdlst));
		if (cmds[cur_cmd].c_args != NULL) {
			(void) strlcat(cmdlst, cmds[cur_cmd].c_args,
						sizeof (cmdlst));
		}
		(void) strlcat(cmdlst, "\n", sizeof (cmdlst));
	}

	/* output usage with conditions */

	log_msg(LOG_MSG_INFO, MSG_USAGE, message, p ? p : "pkgcond", cmdlst);

	return (R_ERROR);
}

/*
 * Name:	parseGlobalData
 * Description:	parse environment global data and store in global data structure
 * Arguments:	a_envVar - pointer to string representing the name of the
 *			environment variable to get and parse
 *		r_gdt - pointer to pointer to global data structure to fill in
 *			using the parsed data from a_envVar
 * Output:	none
 * Returns:	R_SUCCESS - the global data is successfully parsed
 *		R_FAILURE - problem parsing global data
 *		R_ERROR - fatal error attempting to parse global data
 */

static int
parseGlobalData(char *a_envVar, GLOBALDATA_T **r_gdt)
{
	int		r;
	int		n;
	char		*a;
	SML_TAG		*tag;
	SML_TAG		*ntag;

	assert(r_gdt != (GLOBALDATA_T **)NULL);

	/*
	 * allocate space for global data structure if needed
	 */

	if (*r_gdt == (GLOBALDATA_T *)NULL) {
		*r_gdt = (GLOBALDATA_T *)calloc(1, sizeof (GLOBALDATA_T));
	}

	/*
	 * get initial installation indication:
	 * If the initial install variable is set to "true", then an initial
	 * installation of Solaris is underway. When this condition is true:
	 * - if the path being checked is the package install root, then
	 *   the path is considered to be an 'alternative root' which is
	 *   currently being installed.
	 * - if the path being checked is not the package install root, then
	 *   the path needs to be further analyzed to determine what it may
	 *   be referring to.
	 */

	a = getenv(ENV_VAR_INITIAL_INSTALL);
	if ((a != NULL) && (strcasecmp(a, "true") == 0)) {
		(*r_gdt)->gd_initialInstall = B_TRUE;
	}

	/* get current zone name */

	r = getZoneName(&(*r_gdt)->gd_zoneName);
	if (r != R_SUCCESS) {
		(*r_gdt)->gd_zoneName = "";
	}

	/*
	 * get zone installation status:
	 * - If the package install zone name is not set, then an installation
	 *   of a global zone, or of a non-global zone, is not underway.
	 * - If the package install zone name is set to "global", then an
	 *   installation of a global zone is underway. In this case, no path
	 *   can be a netinstall image, diskless client, mounted miniroot,
	 *   non-global zone, the current running system, alternative root,
	 *   or alternative boot environment.
	 * - If the package install zone name is set to a value other than
	 *   "global", then an installation of a non-global zone with that name
	 *   is underway.  In this case, no path can be a netinstall image,
	 *   diskless client, mounted miniroot, global zone, the current
	 *   running system, alternative root, or alternative boot environment.
	 */

	a = getenv(ENV_VAR_PKGZONENAME);
	if ((a == NULL) || (*a == '\0')) {
		/* not installing a zone */
		(*r_gdt)->gd_globalZoneInstall = B_FALSE;
		(*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
	} else if (strcmp(a, GLOBAL_ZONENAME) == 0) {
		/* installing a global zone */
		(*r_gdt)->gd_globalZoneInstall = B_TRUE;
		(*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
		(*r_gdt)->gd_zoneName = a;
	} else {
		/* installing a non-global zone by that name */
		(*r_gdt)->gd_globalZoneInstall = B_FALSE;
		(*r_gdt)->gd_nonglobalZoneInstall = B_TRUE;
		(*r_gdt)->gd_zoneName = a;
	}

	/*
	 * get package install root. If it doesn't exist check for
	 * patch install root (ROOTDIR)
	 */

	a = getenv(ENV_VAR_PKGROOT);
	if ((a != NULL) && (*a != '\0')) {
		(*r_gdt)->gd_installRoot = a;
	} else {
		a = getenv(ENV_VAR_PATCHROOT);
		if ((a != NULL) && (*a != '\0')) {
			(*r_gdt)->gd_installRoot = a;
		} else {
			(*r_gdt)->gd_installRoot = "/";
		}
	}

	/*
	 * get patch client version: always set if $ROOTDIR != / and
	 * the $ROOTDIR/var/sadm/softinfo/INST_RELEASE file exists.
	 */

	a = getenv(ENV_VAR_PATCH_CLIENTVER);
	(*r_gdt)->gd_patchClientVersion = (a ? a : "");

	/* get the global data environment variable */

	a = getenv(a_envVar);

	/* if no data then issue warning and return success */

	if ((a == NULL) || (*a_envVar == '\0')) {
		log_msg(LOG_MSG_DEBUG, DBG_NO_GLOBAL_DATA_AVAILABLE, a_envVar);
		return (R_SUCCESS);
	}

	/* data present - parse into SML structure */

	log_msg(LOG_MSG_DEBUG, DBG_PARSE_GLOBAL, a);

	r = smlConvertStringToTag(&tag, a);
	if (r != R_SUCCESS) {
		log_msg(LOG_MSG_ERR, ERR_CANNOT_PARSE_GLOBAL_DATA, a);
		return (R_FAILURE);
	}

	smlDbgPrintTag(tag, DBG_PARSED_ENVIRONMENT, a_envVar);

	/* fill in global data structure */

	/* find the environment condition information structure */

	ntag = smlGetTagByName(tag, 0, TAG_COND_TOPLEVEL);
	if (ntag == SML_TAG__NULL) {
		log_msg(LOG_MSG_WRN, WRN_PARSED_DATA_MISSING,
			TAG_COND_TOPLEVEL);
		return (R_FAILURE);
	}

	/*
	 * data found - extract what we know about
	 */

	/* parent zone name */

	a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_NAME);
	(*r_gdt)->gd_parentZoneName = a;

	/* parent zone type */

	a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_TYPE);
	(*r_gdt)->gd_parentZoneType = a;

	/* current zone name */

	a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
		TAG_COND_ZONE_NAME);
	(*r_gdt)->gd_currentZoneName = a;

	/* current zone type */

	a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
		TAG_COND_ZONE_TYPE);
	(*r_gdt)->gd_currentZoneType = a;

	/* extract any inherited file systems */

	for (n = 0; ; n++) {
		char	**ifs;

		a = smlGetParamByTag(ntag, n, TAG_COND_INHERITED_FS,
			TAG_COND_FS_NAME);

		if (a == NULL) {
			if (n > 0) {
				/* LINTED warning: variable may be used */
				(*r_gdt)->gd_inheritedFileSystems = ifs;
			}
			break;
		}

		if (n == 0) {
			ifs = (char **)calloc(1, sizeof (char **)*(n+2));
			ifs[n] = a;
			ifs[n+1] = NULL;
		} else {
			ifs = (char **)realloc(ifs, sizeof (char **)*(n+2));
			ifs[n] = a;
			ifs[n+1] = NULL;
		}
	}

	return (R_SUCCESS);
}

/*
 * Name:	dumpGlobalData
 * Description:	dump global data structure using echoDebug
 * Arguments:	a_gdt - pointer to global data structure to dump
 * Outputs:	echoDebug is called to output global data strucutre information
 * Returns:	void
 */

static void
dumpGlobalData(GLOBALDATA_T *a_gdt)
{
	/* entry assertions */

	assert(a_gdt != (GLOBALDATA_T *)NULL);

	/* debugging enabled, dump the global data structure */

	echoDebug(DBG_DUMP_GLOBAL_ENTRY);
	echoDebug(DBG_DUMP_GLOBAL_PARENT_ZONE,
		a_gdt->gd_parentZoneName ? a_gdt->gd_parentZoneName : "",
		a_gdt->gd_parentZoneType ? a_gdt->gd_parentZoneType : "");
	echoDebug(DBG_DUMP_GLOBAL_CURRENT_ZONE,
		a_gdt->gd_currentZoneName ? a_gdt->gd_currentZoneName : "",
		a_gdt->gd_currentZoneType ? a_gdt->gd_currentZoneType : "");

	if (a_gdt->gd_inheritedFileSystems) {
		int n;
		char **ifs = a_gdt->gd_inheritedFileSystems;
		for (n = 0; ifs[n]; n++) {
			echoDebug(DBG_DUMP_GLOBAL_LINE, n, ifs[n]);
		}
	}
}

/*
 * Name:	recursionCheck
 * Description:	prevent recursive calling of functions
 * Arguments:	r_recursion - pointer to int recursion counter
 *		a_function - pointer to name of function
 * Returns:	B_TRUE - function is recursively called
 *		B_FALSE - function not recursively called
 */

static boolean_t
recursionCheck(int *r_recursion, char *a_function)
{
	/* prevent recursion */

	(*r_recursion)++;
	if (*r_recursion > 1) {
		echoDebug(DBG_RECURSION, a_function, *r_recursion);
		(*r_recursion)--;
		return (B_TRUE);
	}

	echoDebug(DBG_NO_RECURSION, a_function);
	return (B_FALSE);
}

/*
 * Name:	quit
 * Description:	cleanup and exit
 * Arguments:	a_retcode - the code to use to determine final exit status;
 *			if this is NOT "99" and if a "ckreturnFunc" is
 *			set, then that function is called with a_retcode
 *			to set the final exit status.
 *		Valid values are:
 *		0 - success
 *		1 - package operation failed (fatal error)
 *		2 - non-fatal error (warning)
 *		3 - user selected quit (operation interrupted)
 *		4 - admin settings prevented operation
 *		5 - interaction required and -n (non-interactive) specified
 *		"10" is added to indicate "immediate reboot required"
 *		"20" is be added to indicate "reboot after install required"
 *		99 - do not interpret the code - just exit "99"
 * Returns:	<<this function does not return - calls exit()>>
 * NOTE:	This is needed because libinst functions can call "quit(99)"
 *		to force an error exit.
 */

void
quit(int a_retcode)
{
	/* process return code if not quit(99) */

	if (a_retcode == 99) {
		exit(0x7f);	/* processing error (127) */
	}

	exit(R_FAILURE);
}