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