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