8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / svr4pkg / pkgcond / main.c
blobe3f5ac723d0250d9ba47438085949da8e0b05ce5
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2017 Peter Tribble.
27 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
32 * Program: pkgcond
34 * Function: Implements the package command suite public utility pkgcond(1M)
36 * Usage: pkgcond [-nv] [-O debug] condition [ argument ]
38 * command options:
39 * -n - negate results of condition test
40 * -v - verbose output of condition testing
42 * <condition> may be any one of:
43 * can_add_driver [path]
44 * can_remove_driver [path]
45 * can_update_driver [path]
46 * is_alternative_root [path]
47 * is_boot_environment [path]
48 * is_diskless_client [path]
49 * is_global_zone [path]
50 * is_mounted_miniroot [path]
51 * is_netinstall_image [path]
52 * is_nonglobal_zone [path]
53 * is_path_writable path
54 * is_running_system [path]
55 * is_what [path]
56 * is_whole_root_nonglobal_zone [path]
58 * <option(s)> are specific to the condition used
60 * Input: depends on command
62 * Output: depends on command
64 * Exit status: If the -n option is not specified:
65 * == 0 - the specified condition is true (or exists).
66 * == 1 - the specified condition is false (or does not exist).
67 * == 2 - command line usage errors (including bad keywords)
68 * == 3 - command failed to perform the test due to a fatal error
70 * If the -n option is specified:
71 * == 0 - the specified condition is false (or does not exist).
72 * == 1 - the specified condition is true (or exists).
73 * == 2 - command line usage errors (including bad keywords)
74 * == 3 - command failed to perform the test due to a fatal error
77 #include <stdio.h>
78 #include <sys/mnttab.h>
79 #include <sys/mntent.h>
80 #include <stdarg.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <strings.h>
84 #include <fcntl.h>
85 #include <ctype.h>
86 #include <sys/types.h>
87 #include <sys/stat.h>
88 #include <unistd.h>
89 #include <locale.h>
90 #include <errno.h>
91 #include <sys/param.h>
92 #include <assert.h>
94 #include <instzones_api.h>
95 #include <pkglib.h>
96 #include <install.h>
97 #include <libinst.h>
98 #include <libadm.h>
99 #include <messages.h>
100 #include "pkgcond.h"
101 #include "pkgcond_msgs.h"
103 /* Should be defined by cc -D */
105 #if !defined(TEXT_DOMAIN)
106 #define TEXT_DOMAIN "SYS_TEST"
107 #endif
109 /* commands to execute */
111 #define LS_CMD "/usr/bin/ls"
114 * type definition and "types" for testPath()
117 typedef enum {
118 TEST_EXISTS = 0x01,
119 TEST_NOT_EXISTS = 0x02,
120 TEST_IS_DIRECTORY = 0x04,
121 TEST_IS_FILE = 0x08,
122 TEST_NOT_DIRECTORY = 0x10,
123 TEST_NOT_FILE = 0x20,
124 TEST_IS_SYMBOLIC_LINK = 0x40,
125 TEST_NOT_SYMBOLIC_LINK = 0x80,
126 TEST_GLOBAL_TOKEN_IN_FILE = 0x100
127 } TEST_TYPES;
129 /* holds file system info */
131 struct fsi_t {
132 char *fsi_mntOptions;
133 char *fsi_fsType;
134 char *fsi_mntPoint;
136 typedef struct fsi_t FSI_T;
138 /* holds parsed global data */
140 struct globalData_t {
141 /* initial install: PKG_INIT_INSTALL=true */
142 boolean_t gd_initialInstall;
143 /* global zone install: SUNW_PKG_INSTALL_ZONENAME=global */
144 boolean_t gd_globalZoneInstall;
145 /* non-global zone install: SUNW_PKG_INSTALL_ZONENAME!=global */
146 boolean_t gd_nonglobalZoneInstall;
147 /* non-global zone is in a mounted state */
148 boolean_t inMountedState;
149 /* sorted list of all mounted file systems */
150 FSI_T *gd_fileSystemConfig;
151 /* number of mounted file systems in list */
152 long gd_fileSystemConfigLen;
153 /* current zone name */
154 char *gd_zoneName;
155 /* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneName */
156 char *gd_parentZoneName;
157 /* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneType */
158 char *gd_parentZoneType;
159 /* root path to target: PKG_INSTALL_ROOT */
160 char *gd_installRoot;
161 /* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneName */
162 char *gd_currentZoneName;
163 /* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneType */
164 char *gd_currentZoneType;
165 /* path provided on command line */
166 char *gd_cmdline_path;
168 typedef struct globalData_t GLOBALDATA_T;
170 /* holds subcommands and their definitions */
172 struct cmd_t {
173 char *c_name;
174 char *c_args;
175 int (*c_func)(int argc, char **argv, GLOBALDATA_T *a_gdt);
177 typedef struct cmd_t CMD_T;
179 /* Command function prototypes */
181 static int cmd_can_add_driver(int argc, char **argv,
182 GLOBALDATA_T *a_gdt);
183 static int cmd_can_remove_driver(int argc, char **argv,
184 GLOBALDATA_T *a_gdt);
185 static int cmd_can_update_driver(int argc, char **argv,
186 GLOBALDATA_T *a_gdt);
187 static int cmd_is_alternative_root(int argc, char **argv,
188 GLOBALDATA_T *a_gdt);
189 static int cmd_is_boot_environment(int argc, char **argv,
190 GLOBALDATA_T *a_gdt);
191 static int cmd_is_diskless_client(int argc, char **argv,
192 GLOBALDATA_T *a_gdt);
193 static int cmd_is_global_zone(int argc, char **argv,
194 GLOBALDATA_T *a_gdt);
195 static int cmd_is_mounted_miniroot(int argc, char **argv,
196 GLOBALDATA_T *a_gdt);
197 static int cmd_is_netinstall_image(int argc, char **argv,
198 GLOBALDATA_T *a_gdt);
199 static int cmd_is_nonglobal_zone(int argc, char **argv,
200 GLOBALDATA_T *a_gdt);
201 static int cmd_is_path_writable(int argc, char **argv,
202 GLOBALDATA_T *a_gdt);
203 static int cmd_is_running_system(int argc, char **argv,
204 GLOBALDATA_T *a_gdt);
205 static int cmd_is_what(int argc, char **argv,
206 GLOBALDATA_T *a_gdt);
208 /* Utility function Prototypes */
210 static boolean_t getNegateResults(void);
211 static boolean_t recursionCheck(int *r_recursion, char *a_function);
212 static int adjustResults(int a_result);
213 static int calculateFileSystemConfig(GLOBALDATA_T *a_gdt);
214 static int getRootPath(char **r_rootPath);
215 static int getZoneName(char **r_zoneName);
216 static int mountOptionPresent(char *a_mntOptions, char *a_opt);
217 static int parseGlobalData(char *a_envVar, GLOBALDATA_T **a_gdt);
218 static int resolvePath(char **r_path);
219 static int setRootPath(char *a_path, char *a_envVar,
220 boolean_t a_mustExist);
221 static int testPath(TEST_TYPES a_tt, char *format, ...);
222 static int usage(char *a_format, ...);
223 static int findToken(char *path, char *token);
224 static char *getMountOption(char **p);
225 static void dumpGlobalData(GLOBALDATA_T *a_gdt);
226 static void removeLeadingWhitespace(char **a_str);
227 static void setNegateResults(boolean_t setting);
228 static void setVerbose(boolean_t);
229 static void sortedInsert(FSI_T **r_list, long *a_listSize,
230 char *a_mntPoint, char *a_fsType, char *a_mntOptions);
231 static void setCmdLinePath(char **a_path, char **args,
232 int num_args);
234 /* local static data */
236 static boolean_t _negateResults = B_FALSE;
237 static char *_rootPath = "/";
239 /* define subcommand data structure */
241 static CMD_T cmds[] = {
242 { "can_add_driver", " [path]",
243 cmd_can_add_driver },
244 { "can_remove_driver", " [path]",
245 cmd_can_remove_driver },
246 { "can_update_driver", " [path]",
247 cmd_can_update_driver },
248 { "is_alternative_root", " [path]",
249 cmd_is_alternative_root },
250 { "is_boot_environment", " [path]",
251 cmd_is_boot_environment },
252 { "is_diskless_client", " [path]",
253 cmd_is_diskless_client },
254 { "is_global_zone", " [path]",
255 cmd_is_global_zone },
256 { "is_mounted_miniroot", " [path]",
257 cmd_is_mounted_miniroot },
258 { "is_netinstall_image", " [path]",
259 cmd_is_netinstall_image },
260 { "is_nonglobal_zone", " [path]",
261 cmd_is_nonglobal_zone },
262 { "is_path_writable", " path",
263 cmd_is_path_writable },
264 { "is_running_system", " [path]",
265 cmd_is_running_system },
266 { "is_what", " [path]",
267 cmd_is_what },
268 /* last one must be all NULLs */
269 { NULL, NULL, NULL }
273 * *****************************************************************************
274 * main
275 * *****************************************************************************
279 * Name: main
280 * Description: main processing loop for pkgcond *
281 * Return: 0 - condition is satisfied (true)
282 * 1 - condition is not satisfied (false)
283 * 2 - command line usage errors
284 * 3 - failure to determine condition
288 main(int argc, char **argv)
290 GLOBALDATA_T *gdt = NULL;
291 char **newargv;
292 char *p;
293 int cur_cmd;
294 int i;
295 int newargc;
297 /* make standard output non-buffered */
299 setbuf(stdout, NULL);
301 /* set the default text domain for messaging */
303 (void) setlocale(LC_ALL, "");
304 (void) textdomain(TEXT_DOMAIN);
306 /* remember command name */
308 set_prog_name(argv[0]);
310 /* tell spmi zones interface how to access package output functions */
312 z_set_output_functions(echo, echoDebug, progerr);
314 /* set verbose mode if appropriate environment variable is set */
316 if (getenv(ENV_VAR_VERBOSE)) {
317 /* same as -v */
318 setVerbose(B_TRUE);
321 /* set debug mode if appropriate environment variable is set */
323 if (getenv(ENV_VAR_DEBUG)) {
324 /* same as -O debug */
326 /* set sml tracing (sml.c) */
327 smlSetVerbose(B_TRUE);
329 /* set log and echo (interactive) message tracing */
330 setVerbose(B_TRUE);
332 /* enable echoDebug debugging messages */
333 echoDebugSetFlag(B_TRUE);
336 /* generate usage if no options or arguments specified */
338 if (argc <= 1) {
339 (void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
340 return (R_USAGE);
344 * process any arguments that can appear before the subcommand
347 while ((i = getopt(argc, argv, ":O:vn?")) != EOF) {
348 switch (i) {
350 * Not a public interface: the -O option allows the behavior
351 * of the package tools to be modified. Recognized options:
352 * -> debug
353 * ---> enable debugging output
356 case 'O':
357 for (p = strtok(optarg, ","); p != NULL;
358 p = strtok(NULL, ",")) {
360 /* debug - enable all tracing */
362 if (strcmp(p, "debug") == 0) {
363 /* set sml tracing */
364 smlSetVerbose(B_TRUE);
365 /* set log/echo tracing */
366 setVerbose(B_TRUE);
367 /* enable debugging messages */
368 echoDebugSetFlag(B_TRUE);
369 continue;
372 progerr(ERR_INVALID_O_OPTION, p);
373 return (adjustResults(R_USAGE));
375 break;
378 * Public interface: enable verbose (debug) output.
381 case 'v': /* verbose mode enabled */
382 /* set command tracing only */
383 setVerbose(B_TRUE);
384 break;
387 * Public interface: negate output results.
390 case 'n':
391 setNegateResults(B_TRUE);
392 break;
395 * unrecognized option
398 case '?':
399 default:
400 (void) usage(MSG_INVALID_OPTION_SPECIFIED, optopt);
401 return (R_USAGE);
406 * done processing options that can preceed subcommand
409 /* error if no subcommand specified */
411 if ((argc-optind) <= 0) {
412 (void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
413 return (R_USAGE);
416 /* parse global data if environment variable set */
418 if (parseGlobalData(PKGCOND_GLOBAL_VARIABLE, &gdt) != R_SUCCESS) {
419 log_msg(LOG_MSG_ERR, ERR_CANNOT_USE_GLOBAL_DATA,
420 PKGCOND_GLOBAL_VARIABLE);
421 return (R_ERROR);
424 if (setRootPath(gdt->gd_installRoot,
425 (strcmp(gdt->gd_installRoot, "/") == 0) ? NULL :
426 ENV_VAR_SET, B_TRUE) != R_SUCCESS) {
427 log_msg(LOG_MSG_ERR, ERR_CANNOT_SET_ROOT_PATH,
428 ENV_VAR_PKGROOT);
429 return (R_ERROR);
432 /* set path provided on the command line */
434 setCmdLinePath(&(gdt->gd_cmdline_path), argv, argc);
435 echoDebug(DBG_CMDLINE_PATH,
436 gdt->gd_cmdline_path == NULL ? "" : gdt->gd_cmdline_path);
438 /* determine how file systems are layered in this zone */
440 if (calculateFileSystemConfig(gdt) != R_SUCCESS) {
441 log_msg(LOG_MSG_ERR, ERR_CANNOT_CALC_FS_CONFIG);
442 return (R_ERROR);
445 /* dump global data read in (only if debugging) */
447 dumpGlobalData(gdt);
449 /* search for specified subcommand and execute if found */
451 for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
452 if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
453 int result;
455 /* make subcommand the first option */
457 newargc = argc - optind;
458 newargv = argv + optind;
459 opterr = optind = 1; optopt = 0;
462 /* call subcommand with its own argc/argv */
464 result = cmds[cur_cmd].c_func(newargc, newargv, gdt);
466 /* process result code and exit */
468 result = adjustResults(result);
469 log_msg(LOG_MSG_DEBUG, DBG_RESULTS, result);
470 return (result);
474 /* subcommand not found - output error message and exit with error */
476 log_msg(LOG_MSG_ERR, ERR_BAD_SUB, argv[optind]);
477 (void) usage(MSG_UNRECOGNIZED_CONDITION_SPECIFIED);
478 return (R_USAGE);
482 * *****************************************************************************
483 * command implementation functions
484 * *****************************************************************************
488 * Name: cmd_is_diskless_client
489 * Description: determine if target is a diskless client
490 * Scope: public
491 * Arguments: argc,argv:
492 * - optional path to target to test
493 * Returns: int
494 * == 0 - success
495 * != 0 - failure
496 * IMPLEMENTATION:
497 * - must not be initial installation to the install root
498 * - must not be installation of a zone
499 * - must not be a whole root non-global zone
500 * - must not be a non-global zone
501 * - must not be a mounted mini-root
502 * - must not be a netinstall image
503 * - must not be a boot environment
504 * - The package "SUNWdclnt" must be installed at "/"
505 * - The root path must not be "/"
506 * - The path "/export/exec/Solaris_\*\/usr" must exist at "/"
507 * - The directory "$ROOTDIR/../templates" must exist
510 static int
511 cmd_is_diskless_client(int argc, char **argv, GLOBALDATA_T *a_gdt)
513 char *rootPath = NULL;
514 char cmd[MAXPATHLEN+1];
515 int c;
516 int r;
517 int rc;
518 static char *cmdName = "is_diskless_client";
519 static int recursion = 0;
521 /* process any command line options */
523 while ((c = getopt(argc, argv, ":")) != EOF) {
524 switch (c) {
525 case '\0': /* prevent end-of-loop not reached warning */
526 break;
527 case '?':
528 default:
529 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
530 return (R_USAGE);
534 /* prevent recursion */
536 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
539 * a diskless client cannot be any of the following
542 /* cannot be non-global zone */
544 r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
546 /* cannot be mounted miniroot */
548 if (r != R_SUCCESS) {
549 r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
552 /* cannot be a netinstall image */
554 if (r != R_SUCCESS) {
555 r = cmd_is_netinstall_image(argc, argv, a_gdt);
558 /* cannot be a boot environment */
560 if (r != R_SUCCESS) {
561 r = cmd_is_boot_environment(argc, argv, a_gdt);
564 /* no need to guard against recursion any more */
566 recursion--;
568 /* return failure if any of the preceeding are true */
570 switch (r) {
571 case R_SUCCESS:
572 return (R_FAILURE);
573 case R_FAILURE:
574 break;
575 case R_USAGE:
576 case R_ERROR:
577 default:
578 return (r);
582 /* normalize argc/argv */
584 argc -= optind;
585 argv += optind;
587 /* error if more than one argument */
589 if (argc > 1) {
590 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
591 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
592 return (R_USAGE);
595 /* process root path if first argument present */
597 if (argc == 1) {
598 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
599 return (R_ERROR);
603 /* get current root path */
605 r = getRootPath(&rootPath);
606 if (r != R_SUCCESS) {
607 return (r);
610 /* start of command debugging information */
612 echoDebug(DBG_ROOTPATH_IS, rootPath);
614 /* SUNWdclnt must be installed */
616 if (pkgTestInstalled("SUNWdclnt", "/") != B_TRUE) {
617 log_msg(LOG_MSG_DEBUG, DBG_IDLC_PKG_NOT_INSTALLED,
618 rootPath, "SUNWdclnt", "/");
619 return (R_FAILURE);
622 /* - $ROOTDIR must not be "/" */
624 if (strcmp(rootPath, "/") == 0) {
625 log_msg(LOG_MSG_DEBUG, DBG_IDLC_ROOTPATH_BAD, rootPath, "/");
626 return (R_FAILURE);
629 /* - zone name must be global */
631 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
632 log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_BAD, rootPath,
633 GLOBAL_ZONENAME);
634 return (R_FAILURE);
638 * /export/exec/Solaris_"*"/usr must exist;
639 * create ls command to test:
640 * /usr/bin/ls /export/exec/Solaris_"*"/usr
643 (void) snprintf(cmd, sizeof (cmd), "%s %s >/dev/null 2>&1",
644 LS_CMD, "/export/exec/Solaris_*/usr");
646 /* execute command */
648 rc = system(cmd);
650 /* return error if ls returns something other than "0" */
652 if (rc != 0) {
653 log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_MISSING,
654 rootPath, "/export/exec/Solaris_*/usr");
655 return (R_FAILURE);
659 * /usr must be empty on a diskless client:
660 * create ls command to test:
661 * /usr/bin/ls -d1 $ROOTDIR/usr/\*
663 (void) snprintf(cmd, sizeof (cmd), "%s %s %s/%s >/dev/null 2>&1",
664 LS_CMD, "-1d", rootPath, "usr/*");
666 /* execute command */
668 rc = system(cmd);
670 /* return error if ls returns "0" */
672 if (rc == 0) {
673 log_msg(LOG_MSG_DEBUG, DBG_IDLC_USR_IS_NOT_EMPTY,
674 rootPath);
675 return (R_FAILURE);
678 /* there must be a templates directory at ${ROOTPATH}/../templates */
680 r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
681 "%s/%s", rootPath, "../templates");
682 if (r != R_SUCCESS) {
683 log_msg(LOG_MSG_DEBUG, DBG_IDLC_NO_TEMPLATES_PATH,
684 rootPath, rootPath, "../templates");
685 return (R_FAILURE);
688 /* must not be initial installation to the install root */
690 if ((a_gdt->gd_initialInstall == B_TRUE) &&
691 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
692 /* initial install: install root cannot be diskless client */
693 log_msg(LOG_MSG_DEBUG, DBG_IDLC_INITIAL_INSTALL, rootPath);
694 return (R_FAILURE);
697 /* must not be installation of a zone */
699 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
700 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
701 /* initial zone install: no path can be diskless client */
702 log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_INSTALL, rootPath);
703 return (R_FAILURE);
706 /* the path is a diskless client */
708 log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_IS_DISKLESS_CLIENT, rootPath);
710 return (R_SUCCESS);
714 * Name: cmd_is_global_zone
715 * Description: determine if target is a global zone
716 * Scope: public
717 * Arguments: argc,argv:
718 * - optional path to target to test
719 * Returns: int
720 * == 0 - success
721 * != 0 - failure
722 * IMPLEMENTATION:
723 * - must not be initial installation to the install root
724 * - must not be installation of a non-global zone
725 * - must not be a non-global zone
726 * - must not be a mounted mini-root
727 * - must not be a netinstall image
728 * - must not be a diskless client
729 * - if $ROOTDIR is "/":
730 * -- if zone name is "GLOBAL", then is a global zone;
731 * -- else not a global zone.
732 * - $ROOTDIR/etc/zones must exist and be a directory
733 * - $ROOTDIR/.tmp_proto must not exist
734 * - $ROOTDIR/var must exist and must not be a symbolic link
737 static int
738 cmd_is_global_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
740 char *rootPath = NULL;
741 int c;
742 int r;
743 static char *cmdName = "is_global_zone";
744 static int recursion = 0;
746 /* process any command line options */
748 while ((c = getopt(argc, argv, ":")) != EOF) {
749 switch (c) {
750 case '\0': /* prevent end-of-loop not reached warning */
751 break;
752 case '?':
753 default:
754 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
755 return (R_USAGE);
759 /* prevent recursion */
761 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
764 * a global zone cannot be any of the following
767 /* cannot be a non-global zone */
769 r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
771 /* cannot be a mounted miniroot */
773 if (r != R_SUCCESS) {
774 r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
777 /* cannot be a netinstall image */
779 if (r != R_SUCCESS) {
780 r = cmd_is_netinstall_image(argc, argv, a_gdt);
783 /* cannot be a diskless client */
785 if (r != R_SUCCESS) {
786 r = cmd_is_diskless_client(argc, argv, a_gdt);
789 /* no need to guard against recursion any more */
791 recursion--;
793 /* return failure if any of the preceeding are true */
795 switch (r) {
796 case R_SUCCESS:
797 return (R_FAILURE);
798 case R_FAILURE:
799 break;
800 case R_USAGE:
801 case R_ERROR:
802 default:
803 return (r);
807 /* normalize argc/argv */
809 argc -= optind;
810 argv += optind;
812 /* error if more than one argument */
814 if (argc > 1) {
815 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
816 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
817 return (R_USAGE);
820 /* process root path if first argument present */
822 if (argc == 1) {
823 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
824 return (R_ERROR);
828 /* get current root path */
830 r = getRootPath(&rootPath);
831 if (r != R_SUCCESS) {
832 return (r);
835 /* start of command debugging information */
837 echoDebug(DBG_ROOTPATH_IS, rootPath);
839 /* must not be initial installation to the install root */
841 if ((a_gdt->gd_initialInstall == B_TRUE) &&
842 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
843 /* initial install: install root cannot be global zone */
844 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_INITIAL_INSTALL, rootPath);
845 return (R_FAILURE);
848 /* must not be installation of a non-global zone */
850 if (a_gdt->gd_nonglobalZoneInstall == B_TRUE) {
851 /* initial nonglobal zone install: no path can be global zone */
852 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_NGZ_ZONE_INSTALL, rootPath);
853 return (R_FAILURE);
856 /* handle if global zone installation to the install root */
858 if ((a_gdt->gd_globalZoneInstall == B_TRUE) &&
859 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
860 /* the path is a global zone */
862 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
863 rootPath);
865 return (R_SUCCESS);
868 /* true if current root is "/" and zone name is GLOBAL_ZONENAME */
870 if (strcmp(rootPath, "/") == 0) {
871 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
872 /* the path is a global zone */
874 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
875 rootPath);
877 return (R_SUCCESS);
880 /* inside a non-global zone */
882 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_ZONENAME_ISNT_GLOBAL,
883 rootPath, a_gdt->gd_zoneName);
885 return (R_FAILURE);
889 * current root is not "/" - see if target looks like a global zone
891 * - rootpath is not "/"
892 * - and $ROOTDIR/etc/zones exists
893 * - and $ROOTDIR/.tmp_proto does not exist
894 * - and $ROOTDIR/var is not a symbolic link
897 /* not global zone if /etc/zones does not exist */
899 r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
900 "%s/%s", rootPath, "/etc/zones");
901 if (r != R_SUCCESS) {
902 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_ISNT_DIRECTORY,
903 rootPath, "/etc/zones");
904 return (R_FAILURE);
907 /* .tmp_proto must not exist */
909 r = testPath(TEST_NOT_EXISTS,
910 "%s/%s", rootPath, ".tmp_proto");
911 if (r != R_SUCCESS) {
912 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_EXISTS,
913 rootPath, "/.tmp_proto");
914 return (R_FAILURE);
917 /* /var must not be a symbolic link */
919 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
920 "%s/%s", rootPath, "/var");
921 if (r != R_SUCCESS) {
922 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_SYMLINK,
923 rootPath, "/var");
924 return (R_FAILURE);
927 /* the path is a global zone */
929 log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE, rootPath);
931 return (R_SUCCESS);
935 * Name: cmd_is_netinstall_image
936 * Description: determine if target is a net install image
937 * Scope: public
938 * Arguments: argc,argv:
939 * - optional path to target to test
940 * Returns: int
941 * == 0 - success
942 * != 0 - failure
943 * IMPLEMENTATION:
944 * - must not be initial installation to the install root
945 * - must not be installation of a zone
946 * - must not be a global zone
947 * - must not be a mounted mini-root
948 * - zone name must be "global"
949 * - $ROOTDIR/.tmp_proto must exist and must be a directory
950 * - $ROOTDIR/var must exist and must be a symbolic link
951 * - $ROOTDIR/tmp/kernel must exist and must be a directory
952 * - $ROOTDIR/.tmp_proto/kernel must exist and must be a symbolic link
955 static int
956 cmd_is_netinstall_image(int argc, char **argv, GLOBALDATA_T *a_gdt)
958 char *rootPath = NULL;
959 int c;
960 int r;
961 static char *cmdName = "is_netinstall_image";
962 static int recursion = 0;
964 /* process any command line options */
966 while ((c = getopt(argc, argv, ":")) != EOF) {
967 switch (c) {
968 case '\0': /* prevent end-of-loop not reached warning */
969 break;
970 case '?':
971 default:
972 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
973 return (R_USAGE);
977 /* prevent recursion */
979 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
981 /* a netinstall image cannot be a global zone */
983 r = cmd_is_global_zone(argc, argv, a_gdt);
985 /* no need to guard against recursion any more */
987 recursion--;
989 switch (r) {
990 case R_SUCCESS:
991 return (R_FAILURE);
992 case R_FAILURE:
993 break;
994 case R_USAGE:
995 case R_ERROR:
996 default:
997 return (r);
1001 /* normalize argc/argv */
1003 argc -= optind;
1004 argv += optind;
1006 /* error if more than one argument */
1008 if (argc > 1) {
1009 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1010 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1011 return (R_USAGE);
1014 /* process root path if first argument present */
1016 if (argc == 1) {
1017 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1018 return (R_ERROR);
1022 /* get current root path */
1024 r = getRootPath(&rootPath);
1025 if (r != R_SUCCESS) {
1026 return (r);
1029 /* start of command debugging information */
1031 echoDebug(DBG_ROOTPATH_IS, rootPath);
1033 /* current zone name must be "global" */
1035 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
1036 log_msg(LOG_MSG_DEBUG, DBG_INIM_BAD_CURRENT_ZONE,
1037 rootPath, GLOBAL_ZONENAME);
1038 return (R_FAILURE);
1041 /* cannot be a mounted_miniroot */
1043 if (cmd_is_mounted_miniroot(argc, argv, a_gdt) == R_SUCCESS) {
1044 log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT,
1045 rootPath);
1046 return (R_FAILURE);
1049 /* $ROOTDIR/.tmp_proto exists */
1051 r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
1052 "%s/%s", rootPath, ".tmp_proto");
1053 if (r != R_SUCCESS) {
1054 log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
1055 rootPath, "/.tmp_proto");
1056 return (R_FAILURE);
1059 /* $ROOTDIR/var is a symbolic link */
1061 r = testPath(TEST_IS_SYMBOLIC_LINK,
1062 "%s/%s", rootPath, "/var");
1063 if (r != R_SUCCESS) {
1064 log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
1065 rootPath, "/var");
1066 return (R_FAILURE);
1069 /* $ROOTDIR/tmp/kernel does exist */
1071 r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
1072 "%s/%s", rootPath, "/tmp/kernel");
1073 if (r != R_SUCCESS) {
1074 log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
1075 rootPath, "/tmp/kernel");
1076 return (R_FAILURE);
1079 /* $ROOTDIR/.tmp_proto/kernel is a symbolic link */
1081 r = testPath(TEST_IS_SYMBOLIC_LINK,
1082 "%s/%s", rootPath, "/.tmp_proto/kernel");
1083 if (r != R_SUCCESS) {
1084 log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
1085 rootPath, "/.tmp_proto/kernel");
1086 return (R_FAILURE);
1089 /* must not be initial installation to the install root */
1091 if ((a_gdt->gd_initialInstall == B_TRUE) &&
1092 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1093 /* initial install: install root cannot be netinstall image */
1094 log_msg(LOG_MSG_DEBUG, DBG_INIM_INITIAL_INSTALL, rootPath);
1095 return (R_FAILURE);
1098 /* must not be installation of a zone */
1100 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1101 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1102 /* initial zone install: no path can be netinstall image */
1103 log_msg(LOG_MSG_DEBUG, DBG_INIM_ZONE_INSTALL, rootPath);
1104 return (R_FAILURE);
1107 /* target is a netinstall image */
1109 log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_IS_NETINSTALL_IMAGE, rootPath);
1111 return (R_SUCCESS);
1115 * Name: cmd_is_mounted_miniroot
1116 * Description: determine if target is a mounted miniroot image
1117 * Scope: public
1118 * Arguments: argc,argv:
1119 * - optional path to target to test
1120 * Returns: int
1121 * == 0 - success
1122 * != 0 - failure
1123 * IMPLEMENTATION:
1124 * - must not be initial installation to the install root
1125 * - must not be installation of a zone
1126 * - zone name must be "global"
1127 * - $ROOTDIR/tmp/kernel must exist and must be a symbolic link
1128 * - $ROOTDIR/tmp/root/kernel must exist and must be a directory
1131 static int
1132 cmd_is_mounted_miniroot(int argc, char **argv, GLOBALDATA_T *a_gdt)
1134 char *rootPath = NULL;
1135 int c;
1136 int r;
1137 static char *cmdName = "is_mounted_miniroot";
1138 static int recursion = 0;
1140 /* process any command line options */
1142 while ((c = getopt(argc, argv, ":")) != EOF) {
1143 switch (c) {
1144 case '\0': /* prevent end-of-loop not reached warning */
1145 break;
1146 case '?':
1147 default:
1148 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1149 return (R_USAGE);
1153 /* prevent recursion */
1155 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1156 recursion--;
1159 /* normalize argc/argv */
1161 argc -= optind;
1162 argv += optind;
1164 /* error if more than one argument */
1166 if (argc > 1) {
1167 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1168 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1169 return (R_USAGE);
1172 /* process root path if first argument present */
1174 if (argc == 1) {
1175 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1176 return (R_ERROR);
1180 /* get current root path */
1182 r = getRootPath(&rootPath);
1183 if (r != R_SUCCESS) {
1184 return (r);
1187 /* start of command debugging information */
1189 echoDebug(DBG_ROOTPATH_IS, rootPath);
1191 /* current zone name must be "global" */
1193 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
1194 log_msg(LOG_MSG_DEBUG, DBG_IMRT_BAD_CURRENT_ZONE,
1195 rootPath, GLOBAL_ZONENAME);
1196 return (R_FAILURE);
1199 /* $ROOTDIR/tmp/kernel is a symbolic link */
1201 r = testPath(TEST_IS_SYMBOLIC_LINK,
1202 "%s/%s", rootPath, "/tmp/kernel");
1203 if (r != R_SUCCESS) {
1204 log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_SYMLINK,
1205 rootPath, "/tmp/kernel");
1206 return (R_FAILURE);
1209 /* $ROOTDIR/tmp/root/kernel is a directory */
1211 r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
1212 "%s/%s", rootPath, "/tmp/root/kernel");
1213 if (r != R_SUCCESS) {
1214 log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_DIRECTORY,
1215 rootPath, "/tmp/root/kernel");
1216 return (R_FAILURE);
1219 /* must not be initial installation to the install root */
1221 if ((a_gdt->gd_initialInstall == B_TRUE) &&
1222 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1223 /* initial install: install root cannot be mounted miniroot */
1224 log_msg(LOG_MSG_DEBUG, DBG_IMRT_INITIAL_INSTALL, rootPath);
1225 return (R_FAILURE);
1228 /* must not be installation of a zone */
1230 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1231 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1232 /* initial zone install: no path can be mounted miniroot */
1233 log_msg(LOG_MSG_DEBUG, DBG_IMRT_ZONE_INSTALL, rootPath);
1234 return (R_FAILURE);
1237 /* target is a mounted miniroot */
1239 log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT, rootPath);
1241 return (R_SUCCESS);
1245 * Name: cmd_is_nonglobal_zone
1246 * Description: determine if target is a global zone
1247 * Scope: public
1248 * Arguments: argc,argv:
1249 * - optional path to target to test
1250 * Returns: int
1251 * == 0 - success
1252 * != 0 - failure
1253 * - must not be initial installation to the install root
1254 * - must not be installation of a global zone
1255 * - success if installation of a non-global zone
1258 static int
1259 cmd_is_nonglobal_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
1261 char *rootPath = NULL;
1262 int c;
1263 int r;
1264 static char *cmdName = "is_nonglobal_zone";
1265 static int recursion = 0;
1267 /* process any command line options */
1269 while ((c = getopt(argc, argv, ":")) != EOF) {
1270 switch (c) {
1271 case '\0': /* prevent end-of-loop not reached warning */
1272 break;
1273 case '?':
1274 default:
1275 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1276 return (R_USAGE);
1280 /* prevent recursion */
1282 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1283 recursion--;
1286 /* normalize argc/argv */
1288 argc -= optind;
1289 argv += optind;
1291 /* error if more than one argument */
1293 if (argc > 1) {
1294 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1295 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1296 return (R_USAGE);
1299 /* process root path if first argument present */
1301 if (argc == 1) {
1302 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1303 return (R_ERROR);
1307 /* get current root path */
1309 r = getRootPath(&rootPath);
1310 if (r != R_SUCCESS) {
1311 return (r);
1314 /* start of command debugging information */
1316 echoDebug(DBG_ROOTPATH_IS, rootPath);
1318 /* handle if non-global zone installation to the install root */
1320 if ((a_gdt->gd_nonglobalZoneInstall == B_TRUE) &&
1321 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1322 log_msg(LOG_MSG_DEBUG, DBG_NGZN_INSTALL_ZONENAME_IS_NGZ,
1323 rootPath, a_gdt->gd_zoneName);
1324 return (R_SUCCESS);
1327 /* must not be initial installation to the install root */
1329 if ((a_gdt->gd_initialInstall == B_TRUE) &&
1330 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1331 /* initial install: install root cannot be non-global zone */
1332 log_msg(LOG_MSG_DEBUG, DBG_NGZN_INITIAL_INSTALL, rootPath);
1333 return (R_FAILURE);
1336 /* must not be installation of a global zone */
1338 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1339 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1340 /* initial global zone install: no path can be nonglobal zone */
1341 log_msg(LOG_MSG_DEBUG, DBG_NGZN_GLOBAL_ZONE_INSTALL, rootPath);
1342 return (R_FAILURE);
1346 * *********************************************************************
1347 * if root directory is "/" then the only thing that needs to be done is
1348 * to test the zone name directly - if the zone name is "global" then
1349 * the target is not a non-global zone; otherwise if the zone name is
1350 * not "global" then the target IS a non-global zone.
1351 * *********************************************************************
1354 if (strcmp(rootPath, "/") == 0) {
1355 /* target is current running root */
1356 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
1357 /* in the global zone */
1358 log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
1359 rootPath, a_gdt->gd_zoneName);
1360 return (R_FAILURE);
1362 /* in a non-global zone */
1363 log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_IS_NGZ,
1364 rootPath, a_gdt->gd_zoneName);
1365 return (R_SUCCESS);
1369 * $ROOTDIR/etc/zones/index must exist in a global zone. It also
1370 * exists in a non-global zone after s10u4 but we can't check that
1371 * since it is undeterministic for all releases so we only check
1372 * for the global zone here.
1375 r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/zones/index");
1376 if (r == R_SUCCESS) {
1378 /* See if "global" exists in .../etc/zones/index */
1380 if (testPath(TEST_GLOBAL_TOKEN_IN_FILE, "%s/%s", rootPath,
1381 "/etc/zones/index") != R_SUCCESS) {
1382 log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
1383 rootPath, GLOBAL_ZONENAME);
1384 return (R_FAILURE);
1389 * *********************************************************************
1390 * If the root directory is "/" then you can use only the zone
1391 * name to determine if the zone is non-global or not since the
1392 * package is being installed or removed to the current "zone".
1394 * Since the root directory being tested is not "/" then you have to
1395 * look into the target to try and infer zone type using means other
1396 * than the zone name only.
1397 * *********************************************************************
1400 /* reject if any items found that cannot be in a non-global zone */
1402 /* .tmp_proto must not exist */
1404 r = testPath(TEST_NOT_EXISTS, "%s/%s", rootPath, ".tmp_proto");
1405 if (r != R_SUCCESS) {
1406 /* $R/.tmp_proto cannot exist in a non-global zone */
1407 log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
1408 rootPath, "/.tmp_proto");
1409 return (R_FAILURE);
1412 /* /var must not be a symbolic link */
1414 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1415 "%s/%s", rootPath, "/var");
1416 if (r != R_SUCCESS) {
1417 /* $R/var cannot be a symbolic link in a non-global zone */
1418 log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_DOES_NOT_EXIST,
1419 rootPath, "/var");
1420 return (R_FAILURE);
1423 /* $ROOTDIR/tmp/root/kernel must not exist */
1425 r = testPath(TEST_NOT_EXISTS,
1426 "%s/%s", rootPath, "/tmp/root/kernel");
1427 if (r != R_SUCCESS) {
1428 /* $R/tmp/root/kernel cannot exist in a non-global zone */
1429 log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
1430 rootPath, "/tmp/root/kernel");
1431 return (R_FAILURE);
1435 * *********************************************************************
1436 * no items exist in $ROOTDIR that identify something other than
1437 * a non-global zone.
1439 * if in global zone no more tests possible: is a non-global zone
1440 * *********************************************************************
1443 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
1444 /* in the global zone */
1445 log_msg(LOG_MSG_DEBUG, DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE,
1446 rootPath);
1447 return (R_SUCCESS);
1451 * *********************************************************************
1452 * In non-global zone: interrogate zone name and type.
1454 * The parent zone is the zone that the "pkgadd" or "pkgrm" command was
1455 * run in. The child zone is the zone that the "pkginstall" or
1456 * "pkgremove" command was run in.
1457 * *********************************************************************
1461 * If parent zone name and current zone name defined, and
1462 * both zone names are the same, since pkgcond is running
1463 * inside of a non-global zone, this is how the scratch
1464 * zone is implemented, so target is a non-global zone
1467 if ((a_gdt->gd_parentZoneName != NULL) &&
1468 (a_gdt->gd_currentZoneName != NULL) &&
1469 (strcmp(a_gdt->gd_parentZoneName,
1470 a_gdt->gd_currentZoneName) == 0)) {
1471 /* parent and current zone name identical: non-gz */
1472 log_msg(LOG_MSG_DEBUG, DBG_NGZN_PARENT_CHILD_SAMEZONE,
1473 rootPath, a_gdt->gd_parentZoneName);
1474 return (R_SUCCESS);
1478 * In non-global zone if zone specific read only FS's exist
1479 * or it is in a mounted state.
1482 if (a_gdt->inMountedState) {
1483 log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
1484 return (R_SUCCESS);
1488 * the parent and current zone name are not the same;
1489 * interrogate the zone types: the parent must be global
1490 * and the current must be non-global, which would be set
1491 * when a package command is run in the global zone that in
1492 * turn runs a package command within the non-global zone.
1495 /* if defined, parent zone type must be "global" */
1497 if ((a_gdt->gd_parentZoneType != NULL) &&
1498 (strcmp(a_gdt->gd_parentZoneType, "nonglobal") == 0)) {
1499 log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_PARENT_ZONETYPE,
1500 rootPath, "nonglobal");
1501 return (R_FAILURE);
1504 /* if defined, current zone type must be "nonglobal" */
1506 if ((a_gdt->gd_currentZoneType != NULL) &&
1507 (strcmp(a_gdt->gd_currentZoneType, GLOBAL_ZONENAME) == 0)) {
1508 log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_CURRENT_ZONETYPE,
1509 rootPath, GLOBAL_ZONENAME);
1510 return (R_FAILURE);
1514 * *********************************************************************
1515 * no other tests possible: target is a non-global zone
1516 * *********************************************************************
1519 log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
1521 return (R_SUCCESS);
1525 * Name: cmd_is_running_system
1526 * Description: determine if target is a global zone
1527 * Scope: public
1528 * Arguments: argc,argv:
1529 * - optional path to target to test
1530 * Returns: int
1531 * == 0 - success
1532 * != 0 - failure
1533 * IMPLEMENTATION:
1534 * - must not be initial installation to the install root
1535 * - must not be installation of a zone
1536 * - must not be a diskless client
1537 * - $ROOTDIR must be "/"
1538 * - zone name must be "global"
1541 static int
1542 cmd_is_running_system(int argc, char **argv, GLOBALDATA_T *a_gdt)
1544 char *rootPath = NULL;
1545 int c;
1546 int r;
1547 static char *cmdName = "is_running_system";
1548 static int recursion = 0;
1550 /* process any command line options */
1552 while ((c = getopt(argc, argv, ":")) != EOF) {
1553 switch (c) {
1554 case '\0': /* prevent end-of-loop not reached warning */
1555 break;
1556 case '?':
1557 default:
1558 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1559 return (R_USAGE);
1563 /* prevent recursion */
1565 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1567 /* a running system cannot be a diskless client */
1569 r = cmd_is_diskless_client(argc, argv, a_gdt);
1571 /* no need to guard against recursion any more */
1573 recursion--;
1575 switch (r) {
1576 case R_SUCCESS:
1577 return (R_FAILURE);
1578 case R_FAILURE:
1579 break;
1580 case R_USAGE:
1581 case R_ERROR:
1582 default:
1583 return (r);
1587 /* normalize argc/argv */
1589 argc -= optind;
1590 argv += optind;
1592 /* error if more than one argument */
1594 if (argc > 1) {
1595 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1596 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1597 return (R_USAGE);
1600 /* process root path if first argument present */
1602 if (argc == 1) {
1603 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1604 return (R_ERROR);
1608 /* get current root path */
1610 r = getRootPath(&rootPath);
1611 if (r != R_SUCCESS) {
1612 return (r);
1615 /* start of command debugging information */
1617 echoDebug(DBG_ROOTPATH_IS, rootPath);
1619 /* if root path is "/" then check zone name */
1621 if (strcmp(rootPath, "/") != 0) {
1622 log_msg(LOG_MSG_DEBUG, DBG_IRST_ROOTPATH_BAD, rootPath, "/");
1623 return (R_FAILURE);
1626 /* zone name must be global */
1628 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
1629 log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_BAD, rootPath,
1630 GLOBAL_ZONENAME);
1631 return (R_FAILURE);
1634 /* must not be initial installation to the install root */
1636 if ((a_gdt->gd_initialInstall == B_TRUE) &&
1637 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
1638 /* initial install: install root cannot be the running system */
1639 log_msg(LOG_MSG_DEBUG, DBG_IRST_INITIAL_INSTALL, rootPath);
1640 return (R_FAILURE);
1643 /* must not be installation of a zone */
1645 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
1646 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
1647 /* initial zone install: no path can be running system */
1648 log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_INSTALL, rootPath);
1649 return (R_FAILURE);
1652 /* target is a running system */
1654 log_msg(LOG_MSG_DEBUG, DBG_IRST_PATH_IS_RUNNING_SYSTEM, rootPath);
1656 return (R_SUCCESS);
1660 * Name: cmd_can_add_driver
1661 * Description: determine if target is a global zone
1662 * Scope: public
1663 * Arguments: argc,argv:
1664 * - optional path to target to test
1665 * Returns: int
1666 * == 0 - success
1667 * != 0 - failure
1668 * Implementation:
1669 * A driver can be added to the system if the components of a Solaris
1670 * instance capable of loading drivers is present and it is not the
1671 * currently running system.
1674 static int
1675 cmd_can_add_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
1677 char *rootPath = NULL;
1678 int c;
1679 int r;
1680 static char *cmdName = "can_add_driver";
1681 static int recursion = 0;
1683 /* process any command line options */
1685 while ((c = getopt(argc, argv, ":")) != EOF) {
1686 switch (c) {
1687 case '\0': /* prevent end-of-loop not reached warning */
1688 break;
1689 case '?':
1690 default:
1691 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1692 return (R_USAGE);
1696 /* prevent recursion */
1698 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1700 /* see if this is the current running system */
1702 r = cmd_is_running_system(argc, argv, a_gdt);
1704 /* cannot be a diskless client */
1706 if (r != R_SUCCESS) {
1707 r = cmd_is_diskless_client(argc, argv, a_gdt);
1710 /* no need to guard against recursion any more */
1712 recursion--;
1714 switch (r) {
1715 case R_SUCCESS:
1716 /* is a running system */
1717 return (R_FAILURE);
1718 case R_FAILURE:
1719 /* not a running syste */
1720 break;
1721 case R_USAGE:
1722 case R_ERROR:
1723 default:
1724 /* cannot determine if is a running system */
1725 return (r);
1729 /* normalize argc/argv */
1731 argc -= optind;
1732 argv += optind;
1734 /* error if more than one argument */
1736 if (argc > 1) {
1737 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1738 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1739 return (R_USAGE);
1742 /* process root path if first argument present */
1744 if (argc == 1) {
1745 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1746 return (R_ERROR);
1750 /* get current root path */
1752 r = getRootPath(&rootPath);
1753 if (r != R_SUCCESS) {
1754 return (r);
1757 /* start of command debugging information */
1759 echoDebug(DBG_ROOTPATH_IS, rootPath);
1761 /* /etc must exist and must not be a symbolic link */
1763 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1764 "%s/%s", rootPath, "/etc");
1765 if (r != R_SUCCESS) {
1766 log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
1767 rootPath, "/etc");
1768 return (R_FAILURE);
1771 /* /platform must exist and must not be a symbolic link */
1773 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1774 "%s/%s", rootPath, "/platform");
1775 if (r != R_SUCCESS) {
1776 log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
1777 rootPath, "/platform");
1778 return (R_FAILURE);
1781 /* /kernel must exist and must not be a symbolic link */
1783 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1784 "%s/%s", rootPath, "/kernel");
1785 if (r != R_SUCCESS) {
1786 log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
1787 rootPath, "/kernel");
1788 return (R_FAILURE);
1791 /* can add a driver */
1793 log_msg(LOG_MSG_DEBUG, DBG_ADDV_YES, rootPath);
1795 return (R_SUCCESS);
1799 * Name: cmd_can_update_driver
1800 * Description: determine if target is a global zone
1801 * Scope: public
1802 * Arguments: argc,argv:
1803 * - optional path to target to test
1804 * Returns: int
1805 * == 0 - success
1806 * != 0 - failure
1807 * Implementation:
1808 * A driver can be added to the system if the components of a Solaris
1809 * instance capable of loading drivers is present and it is not the
1810 * currently running system.
1813 static int
1814 cmd_can_update_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
1816 char *rootPath = NULL;
1817 int c;
1818 int r;
1819 static char *cmdName = "can_update_driver";
1820 static int recursion = 0;
1822 /* process any command line options */
1824 while ((c = getopt(argc, argv, ":")) != EOF) {
1825 switch (c) {
1826 case '\0': /* prevent end-of-loop not reached warning */
1827 break;
1828 case '?':
1829 default:
1830 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1831 return (R_USAGE);
1835 /* prevent recursion */
1837 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1839 /* see if this is the current running system */
1841 r = cmd_is_running_system(argc, argv, a_gdt);
1843 /* cannot be a diskless client */
1845 if (r != R_SUCCESS) {
1846 r = cmd_is_diskless_client(argc, argv, a_gdt);
1849 /* no need to guard against recursion any more */
1851 recursion--;
1853 switch (r) {
1854 case R_SUCCESS:
1855 /* is a running system */
1856 return (R_FAILURE);
1857 case R_FAILURE:
1858 /* not a running syste */
1859 break;
1860 case R_USAGE:
1861 case R_ERROR:
1862 default:
1863 /* cannot determine if is a running system */
1864 return (r);
1868 /* normalize argc/argv */
1870 argc -= optind;
1871 argv += optind;
1873 /* error if more than one argument */
1875 if (argc > 1) {
1876 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
1877 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
1878 return (R_USAGE);
1881 /* process root path if first argument present */
1883 if (argc == 1) {
1884 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
1885 return (R_ERROR);
1889 /* get current root path */
1891 r = getRootPath(&rootPath);
1892 if (r != R_SUCCESS) {
1893 return (r);
1896 /* start of command debugging information */
1898 echoDebug(DBG_ROOTPATH_IS, rootPath);
1900 /* /etc must exist and must not be a symbolic link */
1902 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1903 "%s/%s", rootPath, "/etc");
1904 if (r != R_SUCCESS) {
1905 log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
1906 rootPath, "/etc");
1907 return (R_FAILURE);
1910 /* /platform must exist and must not be a symbolic link */
1912 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1913 "%s/%s", rootPath, "/platform");
1914 if (r != R_SUCCESS) {
1915 log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
1916 rootPath, "/platform");
1917 return (R_FAILURE);
1920 /* /kernel must exist and must not be a symbolic link */
1922 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
1923 "%s/%s", rootPath, "/kernel");
1924 if (r != R_SUCCESS) {
1925 log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
1926 rootPath, "/kernel");
1927 return (R_FAILURE);
1930 /* can update driver */
1932 log_msg(LOG_MSG_DEBUG, DBG_UPDV_YES, rootPath);
1934 return (R_SUCCESS);
1938 * Name: cmd_can_remove_driver
1939 * Description: determine if target is a global zone
1940 * Scope: public
1941 * Arguments: argc,argv:
1942 * - optional path to target to test
1943 * Returns: int
1944 * == 0 - success
1945 * != 0 - failure
1946 * Implementation:
1947 * A driver can be added to the system if the components of a Solaris
1948 * instance capable of loading drivers is present and it is not the
1949 * currently running system.
1952 static int
1953 cmd_can_remove_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
1955 char *rootPath = NULL;
1956 int c;
1957 int r;
1958 static char *cmdName = "can_remove_driver";
1959 static int recursion = 0;
1961 /* process any command line options */
1963 while ((c = getopt(argc, argv, ":")) != EOF) {
1964 switch (c) {
1965 case '\0': /* prevent end-of-loop not reached warning */
1966 break;
1967 case '?':
1968 default:
1969 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
1970 return (R_USAGE);
1974 /* prevent recursion */
1976 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
1978 /* see if this is the current running system */
1980 r = cmd_is_running_system(argc, argv, a_gdt);
1982 /* cannot be a diskless client */
1984 if (r != R_SUCCESS) {
1985 r = cmd_is_diskless_client(argc, argv, a_gdt);
1988 /* no need to guard against recursion any more */
1990 recursion--;
1992 switch (r) {
1993 case R_SUCCESS:
1994 /* is a running system */
1995 return (R_FAILURE);
1996 case R_FAILURE:
1997 /* not a running syste */
1998 break;
1999 case R_USAGE:
2000 case R_ERROR:
2001 default:
2002 /* cannot determine if is a running system */
2003 return (r);
2007 /* normalize argc/argv */
2009 argc -= optind;
2010 argv += optind;
2012 /* error if more than one argument */
2014 if (argc > 1) {
2015 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2016 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2017 return (R_USAGE);
2020 /* process root path if first argument present */
2022 if (argc == 1) {
2023 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2024 return (R_ERROR);
2028 /* get current root path */
2030 r = getRootPath(&rootPath);
2031 if (r != R_SUCCESS) {
2032 return (r);
2035 /* start of command debugging information */
2037 echoDebug(DBG_ROOTPATH_IS, rootPath);
2039 /* /etc must exist and must not be a symbolic link */
2041 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
2042 "%s/%s", rootPath, "/etc");
2043 if (r != R_SUCCESS) {
2044 log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
2045 rootPath, "/etc");
2046 return (R_FAILURE);
2049 /* /platform must exist and must not be a symbolic link */
2051 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
2052 "%s/%s", rootPath, "/platform");
2053 if (r != R_SUCCESS) {
2054 log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
2055 rootPath, "/platform");
2056 return (R_FAILURE);
2059 /* /kernel must exist and must not be a symbolic link */
2061 r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
2062 "%s/%s", rootPath, "/kernel");
2063 if (r != R_SUCCESS) {
2064 log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
2065 rootPath, "/kernel");
2066 return (R_FAILURE);
2069 /* can remove driver */
2071 log_msg(LOG_MSG_DEBUG, DBG_RMDV_YES, rootPath);
2073 return (R_SUCCESS);
2077 * Name: cmd_is_path_writable
2078 * Description: determine if target path is writable
2079 * Scope: public
2080 * Arguments: argc,argv:
2081 * - optional path to target to test
2082 * Returns: int
2083 * == 0 - success
2084 * != 0 - failure
2085 * IMPLEMENTATION:
2086 * - path must be found in the file systems configured
2087 * - mount options must not include "read only"
2090 static int
2091 cmd_is_path_writable(int argc, char **argv, GLOBALDATA_T *a_gdt)
2093 FSI_T *list;
2094 char *rootPath = NULL;
2095 int c;
2096 int n;
2097 int nn;
2098 int r;
2099 long listSize;
2100 long rootPathLen;
2101 static char *cmdName = "is_path_writable";
2102 static int recursion = 0;
2104 /* process any command line options */
2106 while ((c = getopt(argc, argv, ":")) != EOF) {
2107 switch (c) {
2108 case '\0': /* prevent end-of-loop not reached warning */
2109 break;
2110 case '?':
2111 default:
2112 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2113 return (R_USAGE);
2117 /* prevent recursion */
2119 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2120 recursion--;
2123 /* normalize argc/argv */
2125 argc -= optind;
2126 argv += optind;
2128 /* error if more than one argument */
2130 if (argc > 1) {
2131 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2132 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2133 return (R_USAGE);
2136 /* process root path if first argument present */
2138 if (argc != 1) {
2139 (void) usage(ERR_REQUIRED_ROOTPATH_MISSING, cmdName);
2140 return (R_USAGE);
2143 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2144 return (R_ERROR);
2147 /* get current root path */
2149 r = getRootPath(&rootPath);
2150 if (r != R_SUCCESS) {
2151 return (r);
2154 /* start of command debugging information */
2156 echoDebug(DBG_ROOTPATH_IS, rootPath);
2158 /* search file system conf for this path */
2160 rootPathLen = strlen(rootPath);
2161 list = a_gdt->gd_fileSystemConfig;
2162 listSize = a_gdt->gd_fileSystemConfigLen;
2163 for (nn = 0, n = 0; n < listSize; n++) {
2164 long mplen = strlen(list[n].fsi_mntPoint);
2165 if (rootPathLen < mplen) {
2166 /* root path is longer than target, ignore */
2167 continue;
2169 if (strncmp(rootPath, list[n].fsi_mntPoint, mplen) == 0) {
2170 /* remember last partial match */
2171 nn = n;
2175 log_msg(LOG_MSG_DEBUG, DBG_PWRT_INFO,
2176 rootPath, list[nn].fsi_mntPoint, list[nn].fsi_fsType,
2177 list[nn].fsi_mntOptions);
2180 * need to determine if the mount point is writeable:
2183 /* see if the file system is mounted with the "read only" option */
2185 r = mountOptionPresent(list[nn].fsi_mntOptions, MNTOPT_RO);
2186 if (r == R_SUCCESS) {
2187 log_msg(LOG_MSG_DEBUG, DBG_PWRT_READONLY,
2188 rootPath, list[nn].fsi_mntOptions);
2189 return (R_FAILURE);
2192 /* target path is writable */
2194 log_msg(LOG_MSG_DEBUG, DBG_PWRT_IS, rootPath);
2196 return (R_SUCCESS);
2200 * Name: cmd_is_alternative_root
2201 * Description: determine if target is an alternative root
2202 * Scope: public
2203 * Arguments: argc,argv:
2204 * - optional path to target to test
2205 * Returns: int
2206 * == 0 - success
2207 * != 0 - failure
2208 * Implementation:
2209 * - success if an initial installation to the install root
2210 * (an initial install to $PKG_INSTALL_ROOT means that $PKG_INSTALL_ROOT
2211 * points to an alternative root that is under construction)
2212 * - must not be installation of a zone
2213 * - must not be a boot environment
2214 * - must not be a diskless client
2215 * - must not be a mounted miniroot
2216 * - must not be a netinstall image
2217 * - must not be a nonglobal zone
2218 * - must not be a running system
2219 * - $ROOTDIR must not be "/"
2220 * - $ROOTDIR/var must exist
2223 static int
2224 cmd_is_alternative_root(int argc, char **argv, GLOBALDATA_T *a_gdt)
2226 char *rootPath = NULL;
2227 int c;
2228 int r;
2229 static char *cmdName = "is_alternative_root";
2230 static int recursion = 0;
2232 /* process any command line options */
2234 while ((c = getopt(argc, argv, ":")) != EOF) {
2235 switch (c) {
2236 case '\0': /* prevent end-of-loop not reached warning */
2237 break;
2238 case '?':
2239 default:
2240 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2241 return (R_USAGE);
2245 /* prevent recursion */
2247 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2250 * an alternative root cannot be any of the following
2253 /* cannot be a boot_environment */
2255 r = cmd_is_boot_environment(argc, argv, a_gdt);
2257 /* cannot be a diskless_client */
2259 if (r != R_SUCCESS) {
2260 r = cmd_is_diskless_client(argc, argv, a_gdt);
2263 /* cannot be a mounted_miniroot */
2265 if (r != R_SUCCESS) {
2266 r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
2269 /* cannot be a netinstall_image */
2271 if (r != R_SUCCESS) {
2272 r = cmd_is_netinstall_image(argc, argv, a_gdt);
2275 /* cannot be a nonglobal_zone */
2277 if (r != R_SUCCESS) {
2278 r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
2281 /* cannot be a running_system */
2283 if (r != R_SUCCESS) {
2284 r = cmd_is_running_system(argc, argv, a_gdt);
2287 /* no need to guard against recursion any more */
2289 recursion--;
2291 /* return failure if any of the preceeding are true */
2293 switch (r) {
2294 case R_SUCCESS:
2295 return (R_FAILURE);
2296 case R_FAILURE:
2297 break;
2298 case R_USAGE:
2299 case R_ERROR:
2300 default:
2301 return (r);
2305 /* normalize argc/argv */
2307 argc -= optind;
2308 argv += optind;
2310 /* error if more than one argument */
2312 if (argc > 1) {
2313 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2314 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2315 return (R_USAGE);
2318 /* process root path if first argument present */
2320 if (argc == 1) {
2321 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2322 return (R_ERROR);
2326 /* get current root path */
2328 r = getRootPath(&rootPath);
2329 if (r != R_SUCCESS) {
2330 return (r);
2333 /* start of command debugging information */
2335 echoDebug(DBG_ROOTPATH_IS, rootPath);
2337 /* return success if initial installation */
2339 if ((a_gdt->gd_initialInstall == B_TRUE) &&
2340 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
2341 log_msg(LOG_MSG_DEBUG, DBG_IALR_INITIAL_INSTALL, rootPath);
2342 return (R_SUCCESS);
2345 /* root path must not be "/" */
2347 if (strcmp(rootPath, "/") == 0) {
2348 log_msg(LOG_MSG_DEBUG, DBG_IALR_BAD_ROOTPATH, rootPath, "/");
2349 return (R_FAILURE);
2352 /* /var must exist */
2354 r = testPath(TEST_EXISTS,
2355 "%s/%s", rootPath, "/var");
2356 if (r != R_SUCCESS) {
2357 log_msg(LOG_MSG_DEBUG, DBG_IALR_PATH_DOES_NOT_EXIST,
2358 rootPath, "/var");
2359 return (R_FAILURE);
2362 /* must not be installation of a zone */
2364 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
2365 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
2366 /* initial zone install: no path can be alternative root */
2367 log_msg(LOG_MSG_DEBUG, DBG_IALR_ZONE_INSTALL, rootPath);
2368 return (R_FAILURE);
2371 /* target is an alternative root */
2373 log_msg(LOG_MSG_DEBUG, DBG_IALR_IS, rootPath);
2375 return (R_SUCCESS);
2379 * Name: cmd_is_boot_environment
2380 * Description: determine if target is an alternative, inactive boot environment
2381 * Scope: public
2382 * Arguments: argc,argv:
2383 * - optional path to target to test
2384 * Returns: int
2385 * == 0 - success
2386 * != 0 - failure
2387 * IMPLEMENTATION:
2388 * - must not be initial installation to the install root
2389 * - must not be installation of a zone
2390 * - must not be a diskless client
2391 * - must not be a netinstall image
2392 * - must not be a mounted miniroot
2393 * - $ROOTDIR must not be "/"
2394 * - $ROOTDIR/etc/lutab must exist
2395 * - $ROOTDIR/etc/lu must exist and must be a directory
2398 static int
2399 cmd_is_boot_environment(int argc, char **argv, GLOBALDATA_T *a_gdt)
2401 char *rootPath = NULL;
2402 int c;
2403 int r;
2404 static char *cmdName = "is_boot_environment";
2405 static int recursion = 0;
2407 /* process any command line options */
2409 while ((c = getopt(argc, argv, ":")) != EOF) {
2410 switch (c) {
2411 case '\0': /* prevent end-of-loop not reached warning */
2412 break;
2413 case '?':
2414 default:
2415 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2416 return (R_USAGE);
2420 /* prevent recursion */
2422 if (recursionCheck(&recursion, cmdName) == B_FALSE) {
2424 * a boot environment cannot be any of the following
2427 /* cannot be a diskless client */
2429 r = cmd_is_diskless_client(argc, argv, a_gdt);
2431 /* cannot be a netinstall_image */
2433 if (r != R_SUCCESS) {
2434 r = cmd_is_netinstall_image(argc, argv, a_gdt);
2437 /* cannot be a mounted_miniroot */
2439 if (r != R_SUCCESS) {
2440 r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
2443 /* no need to guard against recursion any more */
2445 recursion--;
2447 /* return failure if any of the preceeding are true */
2449 switch (r) {
2450 case R_SUCCESS:
2451 return (R_FAILURE);
2452 case R_FAILURE:
2453 break;
2454 case R_USAGE:
2455 case R_ERROR:
2456 default:
2457 return (r);
2461 /* normalize argc/argv */
2463 argc -= optind;
2464 argv += optind;
2466 /* error if more than one argument */
2468 if (argc > 1) {
2469 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2470 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2471 return (R_USAGE);
2474 /* process root path if first argument present */
2476 if (argc == 1) {
2477 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2478 return (R_ERROR);
2482 /* get current root path */
2484 r = getRootPath(&rootPath);
2485 if (r != R_SUCCESS) {
2486 return (r);
2489 /* start of command debugging information */
2491 echoDebug(DBG_ROOTPATH_IS, rootPath);
2493 /* root path must not be "/" */
2495 if (strcmp(rootPath, "/") == 0) {
2496 log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ROOTPATH, rootPath, "/");
2497 return (R_FAILURE);
2500 /* zone name must be global */
2502 if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
2503 log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ZONE, rootPath,
2504 GLOBAL_ZONENAME);
2505 return (R_FAILURE);
2508 /* $ROOTDIR/etc/lutab must exist */
2510 r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/lutab");
2511 if (r != R_SUCCESS) {
2512 log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLUTAB, rootPath,
2513 "/etc/lutab");
2514 return (R_FAILURE);
2517 /* $ROOTDIR/etc/lu must exist */
2519 r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
2520 "%s/%s", rootPath, "/etc/lu");
2521 if (r != R_SUCCESS) {
2522 log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLU, rootPath, "/etc/lu");
2523 return (R_FAILURE);
2526 /* must not be initial installation */
2528 if ((a_gdt->gd_initialInstall == B_TRUE) &&
2529 (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
2530 log_msg(LOG_MSG_DEBUG, DBG_BENV_INITIAL_INSTALL, rootPath);
2531 return (R_FAILURE);
2534 /* must not be installation of a zone */
2536 if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
2537 (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
2538 /* initial zone install: no path can be boot environment */
2539 log_msg(LOG_MSG_DEBUG, DBG_BENV_ZONE_INSTALL, rootPath);
2540 return (R_FAILURE);
2543 /* target is a boot environment */
2545 log_msg(LOG_MSG_DEBUG, DBG_BENV_IS, rootPath);
2547 return (R_SUCCESS);
2551 * Name: cmd_is_what
2552 * Description: determine what the target is
2553 * Scope: public
2554 * Arguments: argc,argv:
2555 * - optional path to target to test
2556 * Returns: int
2557 * == 0 - success
2558 * != 0 - failure
2561 static int
2562 cmd_is_what(int argc, char **argv, GLOBALDATA_T *a_gdt)
2564 char *rootPath = NULL;
2565 int c;
2566 int cur_cmd;
2567 int r;
2568 static char *cmdName = "is_what";
2570 /* process any command line options */
2572 while ((c = getopt(argc, argv, ":")) != EOF) {
2573 switch (c) {
2574 case '\0': /* prevent end-of-loop not reached warning */
2575 break;
2576 case '?':
2577 default:
2578 (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
2579 return (R_USAGE);
2583 /* normalize argc/argv */
2585 argc -= optind;
2586 argv += optind;
2588 /* error if more than one argument */
2590 if (argc > 1) {
2591 log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
2592 (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
2593 return (R_USAGE);
2596 /* process root path if first argument present */
2598 if (argc == 1) {
2599 if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
2600 return (R_ERROR);
2604 /* get current root path */
2606 r = getRootPath(&rootPath);
2607 if (r != R_SUCCESS) {
2608 return (r);
2612 * construct the command line for all of the packages
2615 argc = 0;
2616 argv[argc++] = strdup(get_prog_name());
2617 argv[argc++] = strdup(rootPath);
2619 /* start of command debugging information */
2621 echoDebug(DBG_ROOTPATH_IS, rootPath);
2623 /* search for specified subcommand and execute if found */
2625 for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
2626 int result;
2628 /* do not recursively call this function */
2630 if (cmds[cur_cmd].c_func == cmd_is_what) {
2631 continue;
2634 /* call subcommand with its own argc/argv */
2636 result = cmds[cur_cmd].c_func(argc, argv, a_gdt);
2638 /* process result code and exit */
2640 result = adjustResults(result);
2641 log_msg(LOG_MSG_INFO, MSG_IS_WHAT_RESULT,
2642 cmds[cur_cmd].c_name, result);
2644 return (R_SUCCESS);
2648 * *****************************************************************************
2649 * utility support functions
2650 * *****************************************************************************
2654 * Name: getMountOption
2655 * Description: return next mount option in a string
2656 * Arguments: p - pointer to string containing mount options
2657 * Output: none
2658 * Returns: char * - pointer to next option in string "p"
2659 * Side Effects: advances input "p" and inserts \0 in place of the
2660 * option separator found.
2663 static char *
2664 getMountOption(char **p)
2666 char *cp = *p;
2667 char *retstr;
2669 /* advance past all white space */
2671 while (*cp && isspace(*cp))
2672 cp++;
2674 /* remember start of next option */
2676 retstr = cp;
2678 /* advance to end of string or option separator */
2680 while (*cp && *cp != ',')
2681 cp++;
2683 /* replace separator with '\0' if not at end of string */
2684 if (*cp) {
2685 *cp = '\0';
2686 cp++;
2689 /* reset caller's pointer and return pointer to option */
2691 *p = cp;
2692 return (retstr);
2696 * Name: mountOptionPresent
2697 * Description: determine if specified mount option is present in list
2698 * of mount point options
2699 * Arguments: a_mntOptions - pointer to string containing list of mount
2700 * point options to search
2701 * a_opt - pointer to string containing option to search for
2702 * Output: none
2703 * Returns: R_SUCCESS - option is present in list of mount point options
2704 * R_FAILURE - options is not present
2705 * R_ERROR - unable to determine if option is present or not
2708 static int
2709 mountOptionPresent(char *a_mntOptions, char *a_opt)
2711 char tmpopts[MNT_LINE_MAX];
2712 char *f, *opts = tmpopts;
2714 /* return false if no mount options present */
2716 if ((a_opt == NULL) || (*a_opt == '\0')) {
2717 return (R_FAILURE);
2720 /* return not present if no list of options to search */
2722 if (a_mntOptions == NULL) {
2723 return (R_FAILURE);
2726 /* return not present if list of options to search is empty */
2728 if (*a_mntOptions == '\0') {
2729 return (R_FAILURE);
2732 /* make local copy of option list to search */
2734 (void) strcpy(opts, a_mntOptions);
2736 /* scan each option looking for the specified option */
2738 f = getMountOption(&opts);
2739 for (; *f; f = getMountOption(&opts)) {
2740 /* return success if option matches target */
2741 if (strncmp(a_opt, f, strlen(a_opt)) == 0) {
2742 return (R_SUCCESS);
2746 /* option not found */
2748 return (R_FAILURE);
2752 * Name: sortedInsert
2753 * Description: perform an alphabetical sorted insert into a list
2754 * Arguments: r_list - pointer to list to insert next entry into
2755 * a_listSize - pointer to current list size
2756 * a_mntPoint - mount point to insert (is sort key)
2757 * a_fsType - file system type for mount point
2758 * a_mntOptions - file syste mount options for mount point
2759 * Output: None
2760 * Returns: None
2763 static void
2764 sortedInsert(FSI_T **r_list, long *a_listSize, char *a_mntPoint,
2765 char *a_fsType, char *a_mntOptions)
2767 int listSize;
2768 FSI_T *list;
2769 int n;
2771 /* entry assertions */
2773 assert(a_listSize != (long *)NULL);
2774 assert(a_mntPoint != NULL);
2775 assert(a_fsType != NULL);
2776 assert(a_mntOptions != NULL);
2778 /* entry debugging info */
2780 echoDebug(DBG_SINS_ENTRY, a_mntPoint, a_fsType, a_mntOptions);
2782 /* localize references to the list and list size */
2784 listSize = *a_listSize;
2785 list = *r_list;
2788 * if list empty insert this entry as the first one in the list
2791 if (listSize == 0) {
2792 /* allocate new entry for list */
2793 listSize++;
2794 list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
2796 /* first entry is data passed to this function */
2797 list[0].fsi_mntPoint = strdup(a_mntPoint);
2798 list[0].fsi_fsType = strdup(a_fsType);
2799 list[0].fsi_mntOptions = strdup(a_mntOptions);
2801 /* second entry is all NULL - end of entry marker */
2802 list[1].fsi_mntPoint = NULL;
2803 list[1].fsi_fsType = NULL;
2804 list[1].fsi_mntOptions = NULL;
2806 /* restore list and list size references to caller */
2807 *a_listSize = listSize;
2808 *r_list = list;
2810 return;
2814 * list not empty - scan looking for largest match
2817 for (n = 0; n < listSize; n++) {
2818 int c;
2820 /* compare target with current list entry */
2822 c = strcmp(list[n].fsi_mntPoint, a_mntPoint);
2824 if (c == 0) {
2825 char *me;
2826 long len;
2828 /* entry already in list -- merge entries */
2830 len = strlen(list[n].fsi_mntOptions) +
2831 strlen(a_mntOptions) + 2;
2832 me = (char *)calloc(1, len);
2834 /* merge two mount options lists into one */
2836 (void) strlcat(me, list[n].fsi_mntOptions, len);
2837 (void) strlcat(me, ",", len);
2838 (void) strlcat(me, a_mntOptions, len);
2840 /* free old list, replace with merged one */
2842 free(list[n].fsi_mntOptions);
2843 list[n].fsi_mntOptions = me;
2845 echoDebug(DBG_SORTEDINS_SKIPPED,
2846 n, list[n].fsi_mntPoint, a_fsType,
2847 list[n].fsi_fsType, a_mntOptions,
2848 list[n].fsi_mntOptions);
2850 continue;
2851 } else if (c < 0) {
2852 /* entry before this one - skip */
2853 continue;
2857 * entry after this one - insert new entry
2860 /* allocate one more entry and make space for new entry */
2861 listSize++;
2862 list = (FSI_T *)realloc(list,
2863 sizeof (FSI_T)*(listSize+1));
2864 (void) memmove(&(list[n+1]), &(list[n]),
2865 sizeof (FSI_T)*(listSize-n));
2867 /* insert this entry into list */
2868 list[n].fsi_mntPoint = strdup(a_mntPoint);
2869 list[n].fsi_fsType = strdup(a_fsType);
2870 list[n].fsi_mntOptions = strdup(a_mntOptions);
2872 /* restore list and list size references to caller */
2873 *a_listSize = listSize;
2874 *r_list = list;
2876 return;
2880 * all entries are before this one - append to end of list
2883 /* allocate new entry at end of list */
2884 listSize++;
2885 list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
2887 /* append this entry to the end of the list */
2888 list[listSize-1].fsi_mntPoint = strdup(a_mntPoint);
2889 list[listSize-1].fsi_fsType = strdup(a_fsType);
2890 list[listSize-1].fsi_mntOptions = strdup(a_mntOptions);
2892 /* restore list and list size references to caller */
2893 *a_listSize = listSize;
2894 *r_list = list;
2898 * Name: calculateFileSystemConfig
2899 * Description: generate sorted list of all mounted file systems
2900 * Arguments: a_gdt - global data structure to place sorted entries into
2901 * Output: None
2902 * Returns: R_SUCCESS - successfully generated mounted file systems list
2903 * R_FAILURE - options is not present
2904 * R_ERROR - unable to determine if option is present or not
2907 static int
2908 calculateFileSystemConfig(GLOBALDATA_T *a_gdt)
2910 FILE *fp;
2911 struct mnttab mntbuf;
2912 FSI_T *list;
2913 long listSize;
2915 /* entry assetions */
2917 assert(a_gdt != (GLOBALDATA_T *)NULL);
2919 /* allocate a list that has one termination entry */
2921 list = (FSI_T *)calloc(1, sizeof (FSI_T));
2922 list[0].fsi_mntPoint = NULL;
2923 list[0].fsi_fsType = NULL;
2924 list[0].fsi_mntOptions = NULL;
2925 listSize = 0;
2927 /* open the mount table for reading */
2929 fp = fopen(MNTTAB, "r");
2930 if (fp == (FILE *)NULL) {
2931 return (R_ERROR);
2934 /* debugging info */
2936 echoDebug(DBG_CALCSCFG_MOUNTED);
2938 /* go through all the specials looking for the device */
2940 while (getmntent(fp, &mntbuf) == 0) {
2941 if (mntbuf.mnt_mountp[0] == '/') {
2942 sortedInsert(&list, &listSize,
2943 strdup(mntbuf.mnt_mountp),
2944 strdup(mntbuf.mnt_fstype),
2945 strdup(mntbuf.mnt_mntopts ?
2946 mntbuf.mnt_mntopts : ""));
2950 * Set flag if we are in a non-global zone and it is in
2951 * the mounted state.
2954 if (strcmp(mntbuf.mnt_mountp, "/a") == 0 &&
2955 strcmp(mntbuf.mnt_special, "/a") == 0 &&
2956 strcmp(mntbuf.mnt_fstype, "lofs") == 0) {
2957 a_gdt->inMountedState = B_TRUE;
2962 /* close mount table file */
2964 (void) fclose(fp);
2966 /* store list pointers in global data structure */
2968 a_gdt->gd_fileSystemConfig = list;
2969 a_gdt->gd_fileSystemConfigLen = listSize;
2971 return (R_SUCCESS);
2975 * Name: adjustResults
2976 * Description: adjust output result code before existing
2977 * Arguments: a_result - result code to adjust
2978 * Returns: int - adjusted result code
2981 static int
2982 adjustResults(int a_result)
2984 boolean_t negate = getNegateResults();
2985 int realResult;
2987 /* adjust code as appropriate */
2989 switch (a_result) {
2990 case R_SUCCESS: /* condition satisfied */
2991 realResult = ((negate == B_TRUE) ? 1 : 0);
2992 break;
2993 case R_FAILURE: /* condition not satisfied */
2994 realResult = ((negate == B_TRUE) ? 0 : 1);
2995 break;
2996 case R_USAGE: /* usage errors */
2997 realResult = 2;
2998 break;
2999 case R_ERROR: /* condition could not be determined */
3000 default:
3001 realResult = 3;
3002 break;
3005 /* debugging output */
3007 log_msg(LOG_MSG_DEBUG, DBG_ADJUST_RESULTS, a_result, negate,
3008 realResult);
3010 /* return results */
3012 return (realResult);
3016 * Name: setCmdLinePath
3017 * Description: set global command line path
3018 * Arguments: path - path to set from the command line
3019 * args - command line args
3020 * num_args - number of command line args
3021 * Returns: R_SUCCESS - root path successfully set
3022 * R_FAILURE - root path could not be set
3023 * R_ERROR - fatal error attempting to set root path
3026 static void
3027 setCmdLinePath(char **path, char **args, int num_args)
3029 char rp[PATH_MAX] = { '\0' };
3030 struct stat statbuf;
3032 if (*path != NULL) {
3033 return;
3037 * If a path "pkgcond is_global_zone [path]" is provided on the
3038 * command line it must be the last argument.
3041 if (realpath(args[num_args - 1], rp) != NULL) {
3042 if (stat(rp, &statbuf) == 0) {
3043 /* make sure the target is a directory */
3044 if ((statbuf.st_mode & S_IFDIR)) {
3045 *path = strdup(rp);
3046 } else {
3047 *path = NULL;
3054 * Name: setRootPath
3055 * Description: set global root path returned by getRootPath
3056 * Arguments: a_path - root path to set
3057 * a_mustExist - B_TRUE if path must exist (else error)
3058 * - B_FALSE if path may not exist
3059 * Returns: R_SUCCESS - root path successfully set
3060 * R_FAILURE - root path could not be set
3061 * R_ERROR - fatal error attempting to set root path
3064 static int
3065 setRootPath(char *a_path, char *a_envVar, boolean_t a_mustExist)
3067 char rp[PATH_MAX] = { '\0' };
3068 struct stat statbuf;
3070 /* if no data then issue warning and return success */
3072 if ((a_path == NULL) || (*a_path == '\0')) {
3073 echoDebug(DBG_NO_DEFAULT_ROOT_PATH_SET);
3074 return (R_SUCCESS);
3077 /* path present - resolve to absolute path */
3079 if (realpath(a_path, rp) == NULL) {
3080 if (a_mustExist == B_TRUE) {
3081 /* must exist ... error */
3082 log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
3083 a_path, strerror(errno));
3084 return (R_ERROR);
3085 } else {
3086 /* may not exist - use path as specified */
3087 (void) strcpy(rp, a_path);
3091 /* debugging output */
3093 echoDebug(DBG_DEFAULT_ROOT_PATH_SET, rp, a_envVar ? a_envVar : "");
3095 /* validate path existence if it must exist */
3097 if (a_mustExist == B_TRUE) {
3099 /* get node status */
3101 if (stat(rp, &statbuf) != 0) {
3102 log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
3103 rp, strerror(errno));
3104 return (R_ERROR);
3107 /* make sure the target is a directory */
3109 if (!(statbuf.st_mode & S_IFDIR)) {
3110 log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_NOT_DIR, rp);
3111 return (R_ERROR);
3115 /* target exists and is a directory - set */
3117 echoDebug(DBG_SET_ROOT_PATH_TO, rp);
3119 /* store copy of resolved root path */
3121 _rootPath = strdup(rp);
3123 /* success! */
3125 return (R_SUCCESS);
3129 * Name: testPath
3130 * Description: determine if a path meets the specified conditions
3131 * Arguments: a_tt - conditions to test path against
3132 * a_format - format to use to generate path
3133 * arguments following a_format - as needed for a_format
3134 * Returns: R_SUCCESS - the path meets all of the specified conditions
3135 * R_FAILURE - the path does not meet all of the conditions
3136 * R_ERROR - error attempting to test path
3139 /*PRINTFLIKE2*/
3140 static int
3141 testPath(TEST_TYPES a_tt, char *a_format, ...)
3143 char *mbPath; /* copy for the path to be returned */
3144 char bfr[1];
3145 int r;
3146 size_t vres = 0;
3147 struct stat statbuf;
3148 va_list ap;
3149 int fd;
3151 /* entry assertions */
3153 assert(a_format != NULL);
3154 assert(*a_format != '\0');
3156 /* determine size of the message in bytes */
3158 va_start(ap, a_format);
3159 vres = vsnprintf(bfr, 1, a_format, ap);
3160 va_end(ap);
3162 assert(vres > 0);
3164 /* allocate storage to hold the message */
3166 mbPath = (char *)calloc(1, vres+2);
3167 assert(mbPath != NULL);
3169 /* generate the results of the printf conversion */
3171 va_start(ap, a_format);
3172 vres = vsnprintf(mbPath, vres+1, a_format, ap);
3173 va_end(ap);
3175 assert(vres > 0);
3177 echoDebug(DBG_TEST_PATH, mbPath, (unsigned long)a_tt);
3180 * When a path given to open(2) contains symbolic links, the
3181 * open system call first resolves all symbolic links and then
3182 * opens that final "resolved" path. As a result, it is not
3183 * possible to check the result of an fstat(2) against the
3184 * file descriptor returned by open(2) for S_IFLNK (a symbolic
3185 * link) since all symbolic links are resolved before the
3186 * target is opened.
3188 * When testing the target as being (or not being) a symbolic
3189 * link, first use lstat(2) against the target to determine
3190 * whether or not the specified target itself is (or is not) a
3191 * symbolic link.
3194 if (a_tt & (TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)) {
3196 * testing target is/is not a symbolic link; use lstat
3197 * to determine the status of the target itself rather
3198 * than what the target might finally address.
3201 if (lstat(mbPath, &statbuf) != 0) {
3202 echoDebug(DBG_CANNOT_LSTAT_PATH, mbPath,
3203 strerror(errno));
3204 free(mbPath);
3205 return (R_FAILURE);
3208 /* Is the target required to be a symbolic link? */
3210 if (a_tt & TEST_IS_SYMBOLIC_LINK) {
3211 /* target must be a symbolic link */
3212 if (!(statbuf.st_mode & S_IFLNK)) {
3213 /* failure: target is not a symbolic link */
3214 echoDebug(DBG_IS_NOT_A_SYMLINK, mbPath);
3215 free(mbPath);
3216 return (R_FAILURE);
3218 /* success: target is a symbolic link */
3219 echoDebug(DBG_SYMLINK_IS, mbPath);
3222 /* Is the target required to not be a symbolic link? */
3224 if (a_tt & TEST_NOT_SYMBOLIC_LINK) {
3225 /* target must not be a symbolic link */
3226 if (statbuf.st_mode & S_IFLNK) {
3227 /* failure: target is a symbolic link */
3228 echoDebug(DBG_IS_A_SYMLINK, mbPath);
3229 free(mbPath);
3230 return (R_FAILURE);
3232 /* success: target is not a symbolic link */
3233 echoDebug(DBG_SYMLINK_NOT, mbPath);
3237 * if only testing is/is not a symbolic link, then
3238 * no need to open the target: return success.
3241 if (!(a_tt &
3242 (~(TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)))) {
3243 free(mbPath);
3244 return (R_SUCCESS);
3248 /* resolve path and remove any whitespace */
3250 r = resolvePath(&mbPath);
3251 if (r != R_SUCCESS) {
3252 echoDebug(DBG_TEST_PATH_NO_RESOLVE, mbPath);
3253 free(mbPath);
3254 if (a_tt & TEST_NOT_EXISTS) {
3255 return (R_SUCCESS);
3257 return (r);
3260 echoDebug(DBG_TEST_PATH_RESOLVE, mbPath);
3262 /* open the file - this is the basic existence test */
3264 fd = open(mbPath, O_RDONLY|O_LARGEFILE, 0);
3266 /* existence test failed if file cannot be opened */
3268 if (fd < 0) {
3270 * target could not be opened - if testing for non-existence,
3271 * return success, otherwise return failure
3273 if (a_tt & TEST_NOT_EXISTS) {
3274 echoDebug(DBG_CANNOT_ACCESS_PATH_OK, mbPath);
3275 free(mbPath);
3276 return (R_SUCCESS);
3279 echoDebug(DBG_CANNOT_ACCESS_PATH_BUT_SHOULD,
3280 mbPath, strerror(errno));
3281 free(mbPath);
3283 return (R_FAILURE);
3287 * target successfully opened - if testing for non-existence,
3288 * return failure, otherwise continue with specified tests
3291 if (a_tt & TEST_NOT_EXISTS) {
3292 /* testing for non-existence: return failure */
3293 echoDebug(DBG_TEST_EXISTS_SHOULD_NOT, mbPath);
3294 free(mbPath);
3295 (void) close(fd);
3296 return (R_FAILURE);
3299 /* get the file status */
3301 r = fstat(fd, &statbuf);
3302 if (r != 0) {
3303 echoDebug(DBG_PATH_DOES_NOT_EXIST, mbPath, strerror(errno));
3304 (void) close(fd);
3305 free(mbPath);
3306 return (R_FAILURE);
3309 /* required to be a directory? */
3311 if (a_tt & TEST_IS_DIRECTORY) {
3312 if (!(statbuf.st_mode & S_IFDIR)) {
3313 /* is not a directory */
3314 echoDebug(DBG_IS_NOT_A_DIRECTORY, mbPath);
3315 free(mbPath);
3316 return (R_FAILURE);
3318 /* a directory */
3319 echoDebug(DBG_DIRECTORY_IS, mbPath);
3322 /* required to not be a directory? */
3324 if (a_tt & TEST_NOT_DIRECTORY) {
3325 if (statbuf.st_mode & S_IFDIR) {
3326 /* is a directory */
3327 echoDebug(DBG_IS_A_DIRECTORY, mbPath);
3328 free(mbPath);
3329 return (R_FAILURE);
3331 /* not a directory */
3332 echoDebug(DBG_DIRECTORY_NOT, mbPath);
3335 /* required to be a file? */
3337 if (a_tt & TEST_IS_FILE) {
3338 if (!(statbuf.st_mode & S_IFREG)) {
3339 /* is not a regular file */
3340 echoDebug(DBG_IS_NOT_A_FILE, mbPath);
3341 free(mbPath);
3342 return (R_FAILURE);
3344 /* a regular file */
3345 echoDebug(DBG_FILE_IS, mbPath);
3348 /* required to not be a file? */
3350 if (a_tt & TEST_NOT_FILE) {
3351 if (statbuf.st_mode & S_IFREG) {
3352 /* is a regular file */
3353 echoDebug(DBG_IS_A_FILE, mbPath);
3354 free(mbPath);
3355 return (R_FAILURE);
3357 /* not a regular file */
3358 echoDebug(DBG_FILE_NOT, mbPath);
3362 * Find token (global) in file pointed to by mbPath.
3363 * token is only compared to first word in mbPath.
3366 if (a_tt & TEST_GLOBAL_TOKEN_IN_FILE) {
3367 if (!(statbuf.st_mode & S_IFREG)) {
3368 /* is not a regular file */
3369 echoDebug(DBG_IS_NOT_A_FILE, mbPath);
3370 free(mbPath);
3371 return (R_FAILURE);
3373 /* If global exists then we're not in a non-global zone */
3374 if (findToken(mbPath, GLOBAL_ZONENAME) == R_SUCCESS) {
3375 echoDebug(DBG_TOKEN__EXISTS, GLOBAL_ZONENAME, mbPath);
3376 free(mbPath);
3377 return (R_FAILURE);
3381 (void) close(fd);
3383 /* success! */
3385 echoDebug(DBG_TESTPATH_OK, mbPath);
3387 /* free up temp storage used to hold path to test */
3389 free(mbPath);
3391 return (R_SUCCESS);
3395 * Name: findToken
3396 * Description: Find first token in file.
3397 * Arguments:
3398 * path - file to search for token
3399 * token - string to search for
3400 * Returns:
3401 * R_SUCCESS - the token exists
3402 * R_FAILURE - the token does not exist
3403 * R_ERROR - fatal error attempting to find token
3406 static int
3407 findToken(char *path, char *token)
3409 FILE *fp;
3410 char *cp;
3411 char line[MAXPATHLEN];
3413 if (path == NULL || token == NULL) {
3414 return (R_ERROR);
3416 if ((fp = fopen(path, "r")) == NULL) {
3417 return (R_ERROR);
3420 while (fgets(line, sizeof (line), fp) != NULL) {
3421 for (cp = line; *cp && isspace(*cp); cp++)
3423 /* skip comments */
3424 if (*cp == '#') {
3425 continue;
3427 if (pkgstrContainsToken(cp, token, ":")) {
3428 (void) fclose(fp);
3429 return (R_SUCCESS);
3432 (void) fclose(fp);
3433 return (R_FAILURE);
3438 * Name: resolvePath
3439 * Description: fully resolve a path to an absolute real path
3440 * Arguments: r_path - pointer to pointer to malloc()ed storage containing
3441 * the path to resolve - this path may be reallocated
3442 * as necessary to hold the fully resolved path
3443 * Output: r_path - is realloc()ed as necessary
3444 * Returns: R_SUCCESS - the path is fully resolved
3445 * R_FAILURE - the path could not be resolved
3446 * R_ERROR - fatal error attempting to resolve path
3449 static int
3450 resolvePath(char **r_path)
3452 int i;
3453 char resolvedPath[MAXPATHLEN+1] = {'\0'};
3454 size_t mbPathlen; /* length of multi-byte path */
3455 size_t wcPathlen; /* length of wide-character path */
3456 wchar_t *wcPath; /* wide-character version of the path */
3457 wchar_t *wptr; /* scratch pointer */
3459 /* entry assertions */
3461 assert(r_path != (char **)NULL);
3463 /* return error if the path is completely empty */
3465 if (*r_path == '\0') {
3466 return (R_FAILURE);
3469 /* remove all leading whitespace */
3471 removeLeadingWhitespace(r_path);
3474 * convert to real path: an absolute pathname that names the same file,
3475 * whose resolution does not involve ".", "..", or symbolic links.
3478 if (realpath(*r_path, resolvedPath) != NULL) {
3479 free(*r_path);
3480 *r_path = strdup(resolvedPath);
3484 * convert the multi-byte version of the path to a
3485 * wide-character rendering, for doing our figuring.
3488 mbPathlen = strlen(*r_path);
3490 if ((wcPath = (wchar_t *)
3491 calloc(1, sizeof (wchar_t)*(mbPathlen+1))) == NULL) {
3492 return (R_FAILURE);
3495 /*LINTED*/
3496 if ((wcPathlen = mbstowcs(wcPath, *r_path, mbPathlen)) == -1) {
3497 free(wcPath);
3498 return (R_FAILURE);
3502 * remove duplicate slashes first ("//../" -> "/")
3505 for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
3506 *wptr++ = wcPath[i];
3508 if (wcPath[i] == '/') {
3509 i++;
3511 while (wcPath[i] == '/') {
3512 i++;
3515 i--;
3519 *wptr = '\0';
3522 * now convert back to the multi-byte format.
3525 /*LINTED*/
3526 if (wcstombs(*r_path, wcPath, mbPathlen) == -1) {
3527 free(wcPath);
3528 return (R_FAILURE);
3531 /* at this point have a path */
3533 /* free up temporary storage */
3535 free(wcPath);
3537 return (R_SUCCESS);
3541 * Name: removeLeadingWhitespace
3542 * Synopsis: Remove leading whitespace from string
3543 * Description: Remove all leading whitespace characters from a string
3544 * Arguments: a_str - [RO, *RW] - (char **)
3545 * Pointer to handle to string (in allocated storage) to
3546 * remove all leading whitespace from
3547 * Returns: void
3548 * The input string is modified as follows:
3549 * == NULL:
3550 * - input string was NULL
3551 * - input string is all whitespace
3552 * != NULL:
3553 * - copy of input string with leading
3554 * whitespace removed
3555 * CAUTION: The input string must be allocated space (via malloc() or
3556 * strdup()) - it must not be a static or inline character string
3557 * NOTE: The input string a_str will be freed with 'free'
3558 * if it is all whitespace, or if it contains any leading
3559 * whitespace characters
3560 * NOTE: Any string returned is placed in new storage for the
3561 * calling method. The caller must use 'free' to dispose
3562 * of the storage once the string is no longer needed.
3563 * Errors: If the string cannot be created, the process exits
3566 static void
3567 removeLeadingWhitespace(char **a_str)
3569 char *o_str;
3571 /* entry assertions */
3573 assert(a_str != (char **)NULL);
3575 /* if string is null, just return */
3577 if (*a_str == NULL) {
3578 return;
3580 o_str = *a_str;
3582 /* if string is empty, deallocate and return NULL */
3584 if (*o_str == '\0') {
3585 /* free string */
3586 free(*a_str);
3587 *a_str = NULL;
3588 return;
3591 /* if first character is not a space, just return */
3593 if (!isspace(*o_str)) {
3594 return;
3597 /* advance past all space characters */
3599 while ((*o_str != '\0') && (isspace(*o_str))) {
3600 o_str++;
3603 /* if string was all space characters, deallocate and return NULL */
3605 if (*o_str == '\0') {
3606 /* free string */
3607 free(*a_str);
3608 *a_str = NULL;
3609 return;
3612 /* have non-space/null byte, return dup, deallocate original */
3614 o_str = strdup(o_str);
3615 free(*a_str);
3616 *a_str = o_str;
3620 * Name: getZoneName
3621 * Description: get the name of the zone this process is running in
3622 * Arguments: r_zoneName - pointer to pointer to receive zone name
3623 * Output: r_zoneName - a pointer to malloc()ed storage containing
3624 * the zone name this process is running in is stored
3625 * in the location pointed to by r_zoneName
3626 * Returns: R_SUCCESS - the zone name is successfully returned
3627 * R_FAILURE - the zone name is not successfully returned
3628 * R_ERROR - error attempting to get the zone name
3631 static int
3632 getZoneName(char **r_zoneName)
3634 static char zoneName[ZONENAME_MAX] = { '\0' };
3636 /* if zone name not already present, retrieve and cache name */
3638 if (zoneName[0] == '\0') {
3639 if (getzonenamebyid(getzoneid(), zoneName,
3640 sizeof (zoneName)) < 0) {
3641 log_msg(LOG_MSG_ERR, ERR_CANNOT_GET_ZONENAME);
3642 return (R_ERROR);
3646 /* return cached zone name */
3648 *r_zoneName = zoneName;
3649 return (R_SUCCESS);
3653 * Name: getRootPath
3654 * Description: get the root path being tested by this process
3655 * Arguments: r_rootPath - pointer to pointer to receive root path
3656 * Output: r_rootPath - a pointer to malloc()ed storage containing
3657 * the root path name this process is testing
3658 * Returns: R_SUCCESS - the root path is successfully returned
3659 * R_FAILURE - the root path is not successfully returned
3660 * R_ERROR - error attempting to get the root path
3663 static int
3664 getRootPath(char **r_rootPath)
3666 *r_rootPath = _rootPath;
3667 return (R_SUCCESS);
3671 * Name: setVerbose
3672 * Description: Turns on verbose output
3673 * Scope: public
3674 * Arguments: verbose = B_TRUE indicates verbose mode
3675 * Returns: none
3678 static void
3679 setVerbose(boolean_t setting)
3681 /* set log verbose messages */
3683 log_set_verbose(setting);
3685 /* set interactive messages */
3687 echoSetFlag(setting);
3691 * Name: negate_results
3692 * Description: control negation of results
3693 * Scope: public
3694 * Arguments: setting
3695 * == B_TRUE indicates negated results mode
3696 * == B_FALSE indicates non-negated results mode
3697 * Returns: none
3700 static void
3701 setNegateResults(boolean_t setting)
3703 log_msg(LOG_MSG_DEBUG, DBG_SET_NEGATE_RESULTS,
3704 _negateResults, setting);
3706 _negateResults = setting;
3710 * Name: getNegateResults
3711 * Description: Returns whether or not to results are negated
3712 * Scope: public
3713 * Arguments: none
3714 * Returns: B_TRUE - results are negated
3715 * B_FALSE - results are not negated
3718 static boolean_t
3719 getNegateResults(void)
3721 return (_negateResults);
3725 * Name: usage
3726 * Description: output usage string
3727 * Arguments: a_format - format to use to generate message
3728 * arguments following a_format - as needed for a_format
3729 * Output: Outputs the usage string to stderr.
3730 * Returns: R_ERROR
3733 static int
3734 usage(char *a_format, ...)
3736 int cur_cmd;
3737 char cmdlst[LINE_MAX+1] = { '\0' };
3738 char *message;
3739 char bfr[1];
3740 char *p = get_prog_name();
3741 size_t vres = 0;
3742 va_list ap;
3744 /* entry assertions */
3746 assert(a_format != NULL);
3747 assert(*a_format != '\0');
3749 /* determine size of the message in bytes */
3751 va_start(ap, a_format);
3752 /* LINTED warning: variable format specifier to vsnprintf(); */
3753 vres = vsnprintf(bfr, 1, a_format, ap);
3754 va_end(ap);
3756 assert(vres > 0);
3758 /* allocate storage to hold the message */
3760 message = (char *)calloc(1, vres+2);
3761 assert(message != NULL);
3763 /* generate the results of the printf conversion */
3765 va_start(ap, a_format);
3766 /* LINTED warning: variable format specifier to vsnprintf(); */
3767 vres = vsnprintf(message, vres+1, a_format, ap);
3768 va_end(ap);
3770 assert(vres > 0);
3772 /* generate list of all defined conditions */
3774 for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
3775 (void) strlcat(cmdlst, "\t", sizeof (cmdlst));
3776 (void) strlcat(cmdlst, cmds[cur_cmd].c_name, sizeof (cmdlst));
3777 if (cmds[cur_cmd].c_args != NULL) {
3778 (void) strlcat(cmdlst, cmds[cur_cmd].c_args,
3779 sizeof (cmdlst));
3781 (void) strlcat(cmdlst, "\n", sizeof (cmdlst));
3784 /* output usage with conditions */
3786 log_msg(LOG_MSG_INFO, MSG_USAGE, message, p ? p : "pkgcond", cmdlst);
3788 return (R_ERROR);
3792 * Name: parseGlobalData
3793 * Description: parse environment global data and store in global data structure
3794 * Arguments: a_envVar - pointer to string representing the name of the
3795 * environment variable to get and parse
3796 * r_gdt - pointer to pointer to global data structure to fill in
3797 * using the parsed data from a_envVar
3798 * Output: none
3799 * Returns: R_SUCCESS - the global data is successfully parsed
3800 * R_FAILURE - problem parsing global data
3801 * R_ERROR - fatal error attempting to parse global data
3804 static int
3805 parseGlobalData(char *a_envVar, GLOBALDATA_T **r_gdt)
3807 int r;
3808 char *a;
3809 SML_TAG *tag;
3810 SML_TAG *ntag;
3812 assert(r_gdt != (GLOBALDATA_T **)NULL);
3815 * allocate space for global data structure if needed
3818 if (*r_gdt == (GLOBALDATA_T *)NULL) {
3819 *r_gdt = (GLOBALDATA_T *)calloc(1, sizeof (GLOBALDATA_T));
3823 * get initial installation indication:
3824 * If the initial install variable is set to "true", then an initial
3825 * installation of Solaris is underway. When this condition is true:
3826 * - if the path being checked is the package install root, then
3827 * the path is considered to be an 'alternative root' which is
3828 * currently being installed.
3829 * - if the path being checked is not the package install root, then
3830 * the path needs to be further analyzed to determine what it may
3831 * be referring to.
3834 a = getenv(ENV_VAR_INITIAL_INSTALL);
3835 if ((a != NULL) && (strcasecmp(a, "true") == 0)) {
3836 (*r_gdt)->gd_initialInstall = B_TRUE;
3839 /* get current zone name */
3841 r = getZoneName(&(*r_gdt)->gd_zoneName);
3842 if (r != R_SUCCESS) {
3843 (*r_gdt)->gd_zoneName = "";
3847 * get zone installation status:
3848 * - If the package install zone name is not set, then an installation
3849 * of a global zone, or of a non-global zone, is not underway.
3850 * - If the package install zone name is set to "global", then an
3851 * installation of a global zone is underway. In this case, no path
3852 * can be a netinstall image, diskless client, mounted miniroot,
3853 * non-global zone, the current running system, alternative root,
3854 * or alternative boot environment.
3855 * - If the package install zone name is set to a value other than
3856 * "global", then an installation of a non-global zone with that name
3857 * is underway. In this case, no path can be a netinstall image,
3858 * diskless client, mounted miniroot, global zone, the current
3859 * running system, alternative root, or alternative boot environment.
3862 a = getenv(ENV_VAR_PKGZONENAME);
3863 if ((a == NULL) || (*a == '\0')) {
3864 /* not installing a zone */
3865 (*r_gdt)->gd_globalZoneInstall = B_FALSE;
3866 (*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
3867 } else if (strcmp(a, GLOBAL_ZONENAME) == 0) {
3868 /* installing a global zone */
3869 (*r_gdt)->gd_globalZoneInstall = B_TRUE;
3870 (*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
3871 (*r_gdt)->gd_zoneName = a;
3872 } else {
3873 /* installing a non-global zone by that name */
3874 (*r_gdt)->gd_globalZoneInstall = B_FALSE;
3875 (*r_gdt)->gd_nonglobalZoneInstall = B_TRUE;
3876 (*r_gdt)->gd_zoneName = a;
3880 * get package install root.
3883 a = getenv(ENV_VAR_PKGROOT);
3884 if ((a != NULL) && (*a != '\0')) {
3885 (*r_gdt)->gd_installRoot = a;
3886 } else {
3887 (*r_gdt)->gd_installRoot = "/";
3890 /* get the global data environment variable */
3892 a = getenv(a_envVar);
3894 /* if no data then issue warning and return success */
3896 if ((a == NULL) || (*a_envVar == '\0')) {
3897 log_msg(LOG_MSG_DEBUG, DBG_NO_GLOBAL_DATA_AVAILABLE, a_envVar);
3898 return (R_SUCCESS);
3901 /* data present - parse into SML structure */
3903 log_msg(LOG_MSG_DEBUG, DBG_PARSE_GLOBAL, a);
3905 r = smlConvertStringToTag(&tag, a);
3906 if (r != R_SUCCESS) {
3907 log_msg(LOG_MSG_ERR, ERR_CANNOT_PARSE_GLOBAL_DATA, a);
3908 return (R_FAILURE);
3911 smlDbgPrintTag(tag, DBG_PARSED_ENVIRONMENT, a_envVar);
3913 /* fill in global data structure */
3915 /* find the environment condition information structure */
3917 ntag = smlGetTagByName(tag, 0, TAG_COND_TOPLEVEL);
3918 if (ntag == SML_TAG__NULL) {
3919 log_msg(LOG_MSG_WRN, WRN_PARSED_DATA_MISSING,
3920 TAG_COND_TOPLEVEL);
3921 return (R_FAILURE);
3925 * data found - extract what we know about
3928 /* parent zone name */
3930 a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_NAME);
3931 (*r_gdt)->gd_parentZoneName = a;
3933 /* parent zone type */
3935 a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_TYPE);
3936 (*r_gdt)->gd_parentZoneType = a;
3938 /* current zone name */
3940 a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
3941 TAG_COND_ZONE_NAME);
3942 (*r_gdt)->gd_currentZoneName = a;
3944 /* current zone type */
3946 a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
3947 TAG_COND_ZONE_TYPE);
3948 (*r_gdt)->gd_currentZoneType = a;
3950 return (R_SUCCESS);
3954 * Name: dumpGlobalData
3955 * Description: dump global data structure using echoDebug
3956 * Arguments: a_gdt - pointer to global data structure to dump
3957 * Outputs: echoDebug is called to output global data strucutre information
3958 * Returns: void
3961 static void
3962 dumpGlobalData(GLOBALDATA_T *a_gdt)
3964 /* entry assertions */
3966 assert(a_gdt != (GLOBALDATA_T *)NULL);
3968 /* debugging enabled, dump the global data structure */
3970 echoDebug(DBG_DUMP_GLOBAL_ENTRY);
3971 echoDebug(DBG_DUMP_GLOBAL_PARENT_ZONE,
3972 a_gdt->gd_parentZoneName ? a_gdt->gd_parentZoneName : "",
3973 a_gdt->gd_parentZoneType ? a_gdt->gd_parentZoneType : "");
3974 echoDebug(DBG_DUMP_GLOBAL_CURRENT_ZONE,
3975 a_gdt->gd_currentZoneName ? a_gdt->gd_currentZoneName : "",
3976 a_gdt->gd_currentZoneType ? a_gdt->gd_currentZoneType : "");
3981 * Name: recursionCheck
3982 * Description: prevent recursive calling of functions
3983 * Arguments: r_recursion - pointer to int recursion counter
3984 * a_function - pointer to name of function
3985 * Returns: B_TRUE - function is recursively called
3986 * B_FALSE - function not recursively called
3989 static boolean_t
3990 recursionCheck(int *r_recursion, char *a_function)
3992 /* prevent recursion */
3994 (*r_recursion)++;
3995 if (*r_recursion > 1) {
3996 echoDebug(DBG_RECURSION, a_function, *r_recursion);
3997 (*r_recursion)--;
3998 return (B_TRUE);
4001 echoDebug(DBG_NO_RECURSION, a_function);
4002 return (B_FALSE);
4006 * Name: quit
4007 * Description: cleanup and exit
4008 * Arguments: a_retcode - the code to use to determine final exit status;
4009 * if this is NOT "99" and if a "ckreturnFunc" is
4010 * set, then that function is called with a_retcode
4011 * to set the final exit status.
4012 * Valid values are:
4013 * 0 - success
4014 * 1 - package operation failed (fatal error)
4015 * 2 - non-fatal error (warning)
4016 * 3 - user selected quit (operation interrupted)
4017 * 4 - admin settings prevented operation
4018 * 5 - interaction required and -n (non-interactive) specified
4019 * "10" is added to indicate "immediate reboot required"
4020 * "20" is be added to indicate "reboot after install required"
4021 * 99 - do not interpret the code - just exit "99"
4022 * Returns: <<this function does not return - calls exit()>>
4023 * NOTE: This is needed because libinst functions can call "quit(99)"
4024 * to force an error exit.
4027 void
4028 quit(int a_retcode)
4030 /* process return code if not quit(99) */
4032 if (a_retcode == 99) {
4033 exit(0x7f); /* processing error (127) */
4036 exit(R_FAILURE);