10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51507Sgjelinek * Common Development and Distribution License (the "License"). 61507Sgjelinek * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 21222Scomay 220Sstevel@tonic-gate /* 23*11771Sgerald.jelinek@sun.com * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate /* 280Sstevel@tonic-gate * zoneadm is a command interpreter for zone administration. It is all in 290Sstevel@tonic-gate * C (i.e., no lex/yacc), and all the argument passing is argc/argv based. 300Sstevel@tonic-gate * main() calls parse_and_run() which calls cmd_match(), then invokes the 310Sstevel@tonic-gate * appropriate command's handler function. The rest of the program is the 320Sstevel@tonic-gate * handler functions and their helper functions. 330Sstevel@tonic-gate * 340Sstevel@tonic-gate * Some of the helper functions are used largely to simplify I18N: reducing 350Sstevel@tonic-gate * the need for translation notes. This is particularly true of many of 360Sstevel@tonic-gate * the zerror() calls: doing e.g. zerror(gettext("%s failed"), "foo") rather 370Sstevel@tonic-gate * than zerror(gettext("foo failed")) with a translation note indicating 380Sstevel@tonic-gate * that "foo" need not be translated. 390Sstevel@tonic-gate */ 400Sstevel@tonic-gate 410Sstevel@tonic-gate #include <stdio.h> 420Sstevel@tonic-gate #include <errno.h> 430Sstevel@tonic-gate #include <unistd.h> 440Sstevel@tonic-gate #include <signal.h> 450Sstevel@tonic-gate #include <stdarg.h> 460Sstevel@tonic-gate #include <ctype.h> 470Sstevel@tonic-gate #include <stdlib.h> 480Sstevel@tonic-gate #include <string.h> 490Sstevel@tonic-gate #include <wait.h> 500Sstevel@tonic-gate #include <zone.h> 510Sstevel@tonic-gate #include <priv.h> 520Sstevel@tonic-gate #include <locale.h> 530Sstevel@tonic-gate #include <libintl.h> 540Sstevel@tonic-gate #include <libzonecfg.h> 550Sstevel@tonic-gate #include <bsm/adt.h> 562712Snn35248 #include <sys/brand.h> 570Sstevel@tonic-gate #include <sys/param.h> 580Sstevel@tonic-gate #include <sys/types.h> 590Sstevel@tonic-gate #include <sys/stat.h> 600Sstevel@tonic-gate #include <sys/statvfs.h> 610Sstevel@tonic-gate #include <assert.h> 620Sstevel@tonic-gate #include <sys/sockio.h> 630Sstevel@tonic-gate #include <sys/mntent.h> 640Sstevel@tonic-gate #include <limits.h> 651867Sgjelinek #include <dirent.h> 662303Scarlsonj #include <uuid/uuid.h> 670Sstevel@tonic-gate #include <fcntl.h> 680Sstevel@tonic-gate #include <door.h> 690Sstevel@tonic-gate #include <macros.h> 700Sstevel@tonic-gate #include <libgen.h> 711300Sgjelinek #include <fnmatch.h> 721931Sgjelinek #include <sys/modctl.h> 732712Snn35248 #include <libbrand.h> 743247Sgjelinek #include <libscf.h> 753352Sgjelinek #include <procfs.h> 763686Sgjelinek #include <strings.h> 770Sstevel@tonic-gate #include <pool.h> 780Sstevel@tonic-gate #include <sys/pool.h> 793247Sgjelinek #include <sys/priocntl.h> 803247Sgjelinek #include <sys/fsspriocntl.h> 8110616SSebastien.Roy@Sun.COM #include <libdladm.h> 8210616SSebastien.Roy@Sun.COM #include <libdllink.h> 830Sstevel@tonic-gate 841867Sgjelinek #include "zoneadm.h" 851867Sgjelinek 860Sstevel@tonic-gate #define MAXARGS 8 870Sstevel@tonic-gate 880Sstevel@tonic-gate /* Reflects kernel zone entries */ 890Sstevel@tonic-gate typedef struct zone_entry { 900Sstevel@tonic-gate zoneid_t zid; 910Sstevel@tonic-gate char zname[ZONENAME_MAX]; 920Sstevel@tonic-gate char *zstate_str; 930Sstevel@tonic-gate zone_state_t zstate_num; 942712Snn35248 char zbrand[MAXNAMELEN]; 950Sstevel@tonic-gate char zroot[MAXPATHLEN]; 962303Scarlsonj char zuuid[UUID_PRINTABLE_STRING_LENGTH]; 973448Sdh155122 zone_iptype_t ziptype; 980Sstevel@tonic-gate } zone_entry_t; 990Sstevel@tonic-gate 1004350Std153743 #define CLUSTER_BRAND_NAME "cluster" 1014350Std153743 1020Sstevel@tonic-gate static zone_entry_t *zents; 1030Sstevel@tonic-gate static size_t nzents; 1040Sstevel@tonic-gate 1051915Sgjelinek #define LOOPBACK_IF "lo0" 1061915Sgjelinek #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af)) 1071915Sgjelinek 1081915Sgjelinek struct net_if { 1091915Sgjelinek char *name; 1101915Sgjelinek int af; 1111915Sgjelinek }; 1121915Sgjelinek 1130Sstevel@tonic-gate /* 0755 is the default directory mode. */ 1140Sstevel@tonic-gate #define DEFAULT_DIR_MODE \ 1150Sstevel@tonic-gate (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate struct cmd { 1180Sstevel@tonic-gate uint_t cmd_num; /* command number */ 1190Sstevel@tonic-gate char *cmd_name; /* command name */ 1200Sstevel@tonic-gate char *short_usage; /* short form help */ 1210Sstevel@tonic-gate int (*handler)(int argc, char *argv[]); /* function to call */ 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate }; 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate #define SHELP_HELP "help" 1262267Sdp #define SHELP_BOOT "boot [-- boot_arguments]" 1270Sstevel@tonic-gate #define SHELP_HALT "halt" 1280Sstevel@tonic-gate #define SHELP_READY "ready" 1292267Sdp #define SHELP_REBOOT "reboot [-- boot_arguments]" 1300Sstevel@tonic-gate #define SHELP_LIST "list [-cipv]" 1310Sstevel@tonic-gate #define SHELP_VERIFY "verify" 1322712Snn35248 #define SHELP_INSTALL "install [-x nodataset] [brand-specific args]" 1337089Sgjelinek #define SHELP_UNINSTALL "uninstall [-F] [brand-specific args]" 1347089Sgjelinek #define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] "\ 1357089Sgjelinek "[brand-specific args] zonename" 1361300Sgjelinek #define SHELP_MOVE "move zonepath" 1377089Sgjelinek #define SHELP_DETACH "detach [-n] [brand-specific args]" 1387089Sgjelinek #define SHELP_ATTACH "attach [-F] [-n <path>] [brand-specific args]" 1392303Scarlsonj #define SHELP_MARK "mark incomplete" 1400Sstevel@tonic-gate 1412712Snn35248 #define EXEC_PREFIX "exec " 1422712Snn35248 #define EXEC_LEN (strlen(EXEC_PREFIX)) 1432712Snn35248 #define RMCOMMAND "/usr/bin/rm -rf" 1442712Snn35248 1452712Snn35248 static int cleanup_zonepath(char *, boolean_t); 1462712Snn35248 1473448Sdh155122 1480Sstevel@tonic-gate static int help_func(int argc, char *argv[]); 1490Sstevel@tonic-gate static int ready_func(int argc, char *argv[]); 1500Sstevel@tonic-gate static int boot_func(int argc, char *argv[]); 1510Sstevel@tonic-gate static int halt_func(int argc, char *argv[]); 1520Sstevel@tonic-gate static int reboot_func(int argc, char *argv[]); 1530Sstevel@tonic-gate static int list_func(int argc, char *argv[]); 1540Sstevel@tonic-gate static int verify_func(int argc, char *argv[]); 1550Sstevel@tonic-gate static int install_func(int argc, char *argv[]); 1560Sstevel@tonic-gate static int uninstall_func(int argc, char *argv[]); 157766Scarlsonj static int mount_func(int argc, char *argv[]); 158766Scarlsonj static int unmount_func(int argc, char *argv[]); 1591300Sgjelinek static int clone_func(int argc, char *argv[]); 1601300Sgjelinek static int move_func(int argc, char *argv[]); 1611507Sgjelinek static int detach_func(int argc, char *argv[]); 1621507Sgjelinek static int attach_func(int argc, char *argv[]); 1632303Scarlsonj static int mark_func(int argc, char *argv[]); 1643247Sgjelinek static int apply_func(int argc, char *argv[]); 16510718SJordan.Vaughan@Sun.com static int sysboot_func(int argc, char *argv[]); 1660Sstevel@tonic-gate static int sanity_check(char *zone, int cmd_num, boolean_t running, 1672712Snn35248 boolean_t unsafe_when_running, boolean_t force); 1680Sstevel@tonic-gate static int cmd_match(char *cmd); 1693339Szt129084 static int verify_details(int, char *argv[]); 1703339Szt129084 static int verify_brand(zone_dochandle_t, int, char *argv[]); 1713339Szt129084 static int invoke_brand_handler(int, char *argv[]); 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate static struct cmd cmdtab[] = { 1740Sstevel@tonic-gate { CMD_HELP, "help", SHELP_HELP, help_func }, 1750Sstevel@tonic-gate { CMD_BOOT, "boot", SHELP_BOOT, boot_func }, 1760Sstevel@tonic-gate { CMD_HALT, "halt", SHELP_HALT, halt_func }, 1770Sstevel@tonic-gate { CMD_READY, "ready", SHELP_READY, ready_func }, 1780Sstevel@tonic-gate { CMD_REBOOT, "reboot", SHELP_REBOOT, reboot_func }, 1790Sstevel@tonic-gate { CMD_LIST, "list", SHELP_LIST, list_func }, 1800Sstevel@tonic-gate { CMD_VERIFY, "verify", SHELP_VERIFY, verify_func }, 1810Sstevel@tonic-gate { CMD_INSTALL, "install", SHELP_INSTALL, install_func }, 1820Sstevel@tonic-gate { CMD_UNINSTALL, "uninstall", SHELP_UNINSTALL, 183766Scarlsonj uninstall_func }, 1841300Sgjelinek /* mount and unmount are private commands for admin/install */ 185766Scarlsonj { CMD_MOUNT, "mount", NULL, mount_func }, 1861300Sgjelinek { CMD_UNMOUNT, "unmount", NULL, unmount_func }, 1871300Sgjelinek { CMD_CLONE, "clone", SHELP_CLONE, clone_func }, 1881507Sgjelinek { CMD_MOVE, "move", SHELP_MOVE, move_func }, 1891507Sgjelinek { CMD_DETACH, "detach", SHELP_DETACH, detach_func }, 1902303Scarlsonj { CMD_ATTACH, "attach", SHELP_ATTACH, attach_func }, 1913247Sgjelinek { CMD_MARK, "mark", SHELP_MARK, mark_func }, 19210718SJordan.Vaughan@Sun.com { CMD_APPLY, "apply", NULL, apply_func }, 19310718SJordan.Vaughan@Sun.com { CMD_SYSBOOT, "sysboot", NULL, sysboot_func } 1940Sstevel@tonic-gate }; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate /* global variables */ 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /* set early in main(), never modified thereafter, used all over the place */ 1990Sstevel@tonic-gate static char *execname; 2002712Snn35248 static char target_brand[MAXNAMELEN]; 20110943SEdward.Pilatowicz@Sun.COM static char default_brand[MAXPATHLEN]; 2020Sstevel@tonic-gate static char *locale; 2031867Sgjelinek char *target_zone; 2042303Scarlsonj static char *target_uuid; 2050Sstevel@tonic-gate 2061867Sgjelinek char * 2070Sstevel@tonic-gate cmd_to_str(int cmd_num) 2080Sstevel@tonic-gate { 2090Sstevel@tonic-gate assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 2100Sstevel@tonic-gate return (cmdtab[cmd_num].cmd_name); 2110Sstevel@tonic-gate } 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate /* This is a separate function because of gettext() wrapping. */ 2140Sstevel@tonic-gate static char * 2150Sstevel@tonic-gate long_help(int cmd_num) 2160Sstevel@tonic-gate { 217222Scomay assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 2180Sstevel@tonic-gate switch (cmd_num) { 2191634Sgjelinek case CMD_HELP: 2201634Sgjelinek return (gettext("Print usage message.")); 2211634Sgjelinek case CMD_BOOT: 2222267Sdp return (gettext("Activates (boots) specified zone. See " 2232267Sdp "zoneadm(1m) for valid boot\n\targuments.")); 2241634Sgjelinek case CMD_HALT: 2251634Sgjelinek return (gettext("Halts specified zone, bypassing shutdown " 2261634Sgjelinek "scripts and removing runtime\n\tresources of the zone.")); 2271634Sgjelinek case CMD_READY: 2281634Sgjelinek return (gettext("Prepares a zone for running applications but " 2291634Sgjelinek "does not start any user\n\tprocesses in the zone.")); 2301634Sgjelinek case CMD_REBOOT: 2311634Sgjelinek return (gettext("Restarts the zone (equivalent to a halt / " 2322267Sdp "boot sequence).\n\tFails if the zone is not active. " 2332267Sdp "See zoneadm(1m) for valid boot\n\targuments.")); 2341634Sgjelinek case CMD_LIST: 2351634Sgjelinek return (gettext("Lists the current zones, or a " 2361634Sgjelinek "specific zone if indicated. By default,\n\tall " 2371634Sgjelinek "running zones are listed, though this can be " 2381634Sgjelinek "expanded to all\n\tinstalled zones with the -i " 2391634Sgjelinek "option or all configured zones with the\n\t-c " 2402303Scarlsonj "option. When used with the general -z <zone> and/or -u " 2412303Scarlsonj "<uuid-match>\n\toptions, lists only the specified " 2422303Scarlsonj "matching zone, but lists it\n\tregardless of its state, " 2432303Scarlsonj "and the -i and -c options are disallowed. The\n\t-v " 2442303Scarlsonj "option can be used to display verbose information: zone " 2452303Scarlsonj "name, id,\n\tcurrent state, root directory and options. " 2462303Scarlsonj "The -p option can be used\n\tto request machine-parsable " 2472303Scarlsonj "output. The -v and -p options are mutually\n\texclusive." 2482303Scarlsonj " If neither -v nor -p is used, just the zone name is " 2492303Scarlsonj "listed.")); 2501634Sgjelinek case CMD_VERIFY: 2511634Sgjelinek return (gettext("Check to make sure the configuration " 2521634Sgjelinek "can safely be instantiated\n\ton the machine: " 2531634Sgjelinek "physical network interfaces exist, etc.")); 2541634Sgjelinek case CMD_INSTALL: 2551867Sgjelinek return (gettext("Install the configuration on to the system. " 2561867Sgjelinek "The -x nodataset option\n\tcan be used to prevent the " 2571867Sgjelinek "creation of a new ZFS file system for the\n\tzone " 2582712Snn35248 "(assuming the zonepath is within a ZFS file system).\n\t" 2592712Snn35248 "All other arguments are passed to the brand installation " 2607089Sgjelinek "function;\n\tsee brands(5) for more information.")); 2611634Sgjelinek case CMD_UNINSTALL: 2621634Sgjelinek return (gettext("Uninstall the configuration from the system. " 2637089Sgjelinek "The -F flag can be used\n\tto force the action. All " 2647089Sgjelinek "other arguments are passed to the brand\n\tuninstall " 2657089Sgjelinek "function; see brands(5) for more information.")); 2661634Sgjelinek case CMD_CLONE: 2671867Sgjelinek return (gettext("Clone the installation of another zone. " 2681867Sgjelinek "The -m option can be used to\n\tspecify 'copy' which " 2691867Sgjelinek "forces a copy of the source zone. The -s option\n\t" 2701867Sgjelinek "can be used to specify the name of a ZFS snapshot " 2711867Sgjelinek "that was taken from\n\ta previous clone command. The " 2721867Sgjelinek "snapshot will be used as the source\n\tinstead of " 2737089Sgjelinek "creating a new ZFS snapshot. All other arguments are " 2747089Sgjelinek "passed\n\tto the brand clone function; see " 2757089Sgjelinek "brands(5) for more information.")); 2761634Sgjelinek case CMD_MOVE: 2771634Sgjelinek return (gettext("Move the zone to a new zonepath.")); 2781634Sgjelinek case CMD_DETACH: 2791634Sgjelinek return (gettext("Detach the zone from the system. The zone " 2801634Sgjelinek "state is changed to\n\t'configured' (but the files under " 2811634Sgjelinek "the zonepath are untouched).\n\tThe zone can subsequently " 2821634Sgjelinek "be attached, or can be moved to another\n\tsystem and " 2832078Sgjelinek "attached there. The -n option can be used to specify\n\t" 2842078Sgjelinek "'no-execute' mode. When -n is used, the information " 2852078Sgjelinek "needed to attach\n\tthe zone is sent to standard output " 2867089Sgjelinek "but the zone is not actually\n\tdetached. All other " 2877089Sgjelinek "arguments are passed to the brand detach function;\n\tsee " 2887089Sgjelinek "brands(5) for more information.")); 2891634Sgjelinek case CMD_ATTACH: 2901634Sgjelinek return (gettext("Attach the zone to the system. The zone " 2911634Sgjelinek "state must be 'configured'\n\tprior to attach; upon " 2921634Sgjelinek "successful completion, the zone state will be\n\t" 2931634Sgjelinek "'installed'. The system software on the current " 2941634Sgjelinek "system must be\n\tcompatible with the software on the " 2957089Sgjelinek "zone's original system.\n\tSpecify -F " 2965829Sgjelinek "to force the attach and skip software compatibility " 2975829Sgjelinek "tests.\n\tThe -n option can be used to specify " 2985829Sgjelinek "'no-execute' mode. When -n is\n\tused, the information " 2995829Sgjelinek "needed to attach the zone is read from the\n\tspecified " 3005829Sgjelinek "path and the configuration is only validated. The path " 3017089Sgjelinek "can\n\tbe '-' to specify standard input. The -F and -n " 3027089Sgjelinek "options are mutually\n\texclusive. All other arguments " 3037089Sgjelinek "are passed to the brand attach\n\tfunction; see " 3047089Sgjelinek "brands(5) for more information.")); 3052303Scarlsonj case CMD_MARK: 3062303Scarlsonj return (gettext("Set the state of the zone. This can be used " 3072303Scarlsonj "to force the zone\n\tstate to 'incomplete' " 3082303Scarlsonj "administratively if some activity has rendered\n\tthe " 3092303Scarlsonj "zone permanently unusable. The only valid state that " 3102303Scarlsonj "may be\n\tspecified is 'incomplete'.")); 3111634Sgjelinek default: 3121634Sgjelinek return (""); 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate /* NOTREACHED */ 315222Scomay return (NULL); 3160Sstevel@tonic-gate } 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate /* 3190Sstevel@tonic-gate * Called with explicit B_TRUE when help is explicitly requested, B_FALSE for 3200Sstevel@tonic-gate * unexpected errors. 3210Sstevel@tonic-gate */ 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate static int 3240Sstevel@tonic-gate usage(boolean_t explicit) 3250Sstevel@tonic-gate { 3260Sstevel@tonic-gate int i; 3270Sstevel@tonic-gate FILE *fd = explicit ? stdout : stderr; 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate (void) fprintf(fd, "%s:\t%s help\n", gettext("usage"), execname); 3302303Scarlsonj (void) fprintf(fd, "\t%s [-z <zone>] [-u <uuid-match>] list\n", 3312303Scarlsonj execname); 3322303Scarlsonj (void) fprintf(fd, "\t%s {-z <zone>|-u <uuid-match>} <%s>\n", execname, 3330Sstevel@tonic-gate gettext("subcommand")); 3340Sstevel@tonic-gate (void) fprintf(fd, "\n%s:\n\n", gettext("Subcommands")); 3350Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 336766Scarlsonj if (cmdtab[i].short_usage == NULL) 337766Scarlsonj continue; 3380Sstevel@tonic-gate (void) fprintf(fd, "%s\n", cmdtab[i].short_usage); 3390Sstevel@tonic-gate if (explicit) 3400Sstevel@tonic-gate (void) fprintf(fd, "\t%s\n\n", long_help(i)); 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate if (!explicit) 3430Sstevel@tonic-gate (void) fputs("\n", fd); 3440Sstevel@tonic-gate return (Z_USAGE); 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate static void 3480Sstevel@tonic-gate sub_usage(char *short_usage, int cmd_num) 3490Sstevel@tonic-gate { 3500Sstevel@tonic-gate (void) fprintf(stderr, "%s:\t%s\n", gettext("usage"), short_usage); 3510Sstevel@tonic-gate (void) fprintf(stderr, "\t%s\n", long_help(cmd_num)); 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate /* 3550Sstevel@tonic-gate * zperror() is like perror(3c) except that this also prints the executable 3560Sstevel@tonic-gate * name at the start of the message, and takes a boolean indicating whether 3570Sstevel@tonic-gate * to call libc'c strerror() or that from libzonecfg. 3580Sstevel@tonic-gate */ 3590Sstevel@tonic-gate 3601867Sgjelinek void 3610Sstevel@tonic-gate zperror(const char *str, boolean_t zonecfg_error) 3620Sstevel@tonic-gate { 3630Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", execname, str, 3640Sstevel@tonic-gate zonecfg_error ? zonecfg_strerror(errno) : strerror(errno)); 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate /* 3680Sstevel@tonic-gate * zperror2() is very similar to zperror() above, except it also prints a 3690Sstevel@tonic-gate * supplied zone name after the executable. 3700Sstevel@tonic-gate * 3710Sstevel@tonic-gate * All current consumers of this function want libzonecfg's strerror() rather 3720Sstevel@tonic-gate * than libc's; if this ever changes, this function can be made more generic 3730Sstevel@tonic-gate * like zperror() above. 3740Sstevel@tonic-gate */ 3750Sstevel@tonic-gate 3761867Sgjelinek void 3770Sstevel@tonic-gate zperror2(const char *zone, const char *str) 3780Sstevel@tonic-gate { 3790Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s: %s\n", execname, zone, str, 3800Sstevel@tonic-gate zonecfg_strerror(errno)); 3810Sstevel@tonic-gate } 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate /* PRINTFLIKE1 */ 3841867Sgjelinek void 3850Sstevel@tonic-gate zerror(const char *fmt, ...) 3860Sstevel@tonic-gate { 3870Sstevel@tonic-gate va_list alist; 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate va_start(alist, fmt); 3900Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", execname); 3910Sstevel@tonic-gate if (target_zone != NULL) 3920Sstevel@tonic-gate (void) fprintf(stderr, "zone '%s': ", target_zone); 3930Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 3940Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3950Sstevel@tonic-gate va_end(alist); 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate static void * 3990Sstevel@tonic-gate safe_calloc(size_t nelem, size_t elsize) 4000Sstevel@tonic-gate { 4010Sstevel@tonic-gate void *r = calloc(nelem, elsize); 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate if (r == NULL) { 4040Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), 4050Sstevel@tonic-gate (ulong_t)nelem * elsize, strerror(errno)); 4060Sstevel@tonic-gate exit(Z_ERR); 4070Sstevel@tonic-gate } 4080Sstevel@tonic-gate return (r); 4090Sstevel@tonic-gate } 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate static void 4120Sstevel@tonic-gate zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) 4130Sstevel@tonic-gate { 4140Sstevel@tonic-gate static boolean_t firsttime = B_TRUE; 4153448Sdh155122 char *ip_type_str; 4163448Sdh155122 4178942Sgerald.jelinek@sun.com /* Skip a zone that shutdown while we were collecting data. */ 4188942Sgerald.jelinek@sun.com if (zent->zname[0] == '\0') 4198942Sgerald.jelinek@sun.com return; 4208942Sgerald.jelinek@sun.com 4213448Sdh155122 if (zent->ziptype == ZS_EXCLUSIVE) 4223448Sdh155122 ip_type_str = "excl"; 4233448Sdh155122 else 4243448Sdh155122 ip_type_str = "shared"; 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate assert(!(verbose && parsable)); 4270Sstevel@tonic-gate if (firsttime && verbose) { 4280Sstevel@tonic-gate firsttime = B_FALSE; 4293448Sdh155122 (void) printf("%*s %-16s %-10s %-30s %-8s %-6s\n", 4303448Sdh155122 ZONEID_WIDTH, "ID", "NAME", "STATUS", "PATH", "BRAND", 4313448Sdh155122 "IP"); 4320Sstevel@tonic-gate } 4330Sstevel@tonic-gate if (!verbose) { 4342303Scarlsonj char *cp, *clim; 4352303Scarlsonj 4360Sstevel@tonic-gate if (!parsable) { 4370Sstevel@tonic-gate (void) printf("%s\n", zent->zname); 4380Sstevel@tonic-gate return; 4390Sstevel@tonic-gate } 4400Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 4410Sstevel@tonic-gate (void) printf("-"); 4420Sstevel@tonic-gate else 4430Sstevel@tonic-gate (void) printf("%lu", zent->zid); 4442303Scarlsonj (void) printf(":%s:%s:", zent->zname, zent->zstate_str); 4452303Scarlsonj cp = zent->zroot; 4462303Scarlsonj while ((clim = strchr(cp, ':')) != NULL) { 4472303Scarlsonj (void) printf("%.*s\\:", clim - cp, cp); 4482303Scarlsonj cp = clim + 1; 4492303Scarlsonj } 4503448Sdh155122 (void) printf("%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand, 4513448Sdh155122 ip_type_str); 4520Sstevel@tonic-gate return; 4530Sstevel@tonic-gate } 4540Sstevel@tonic-gate if (zent->zstate_str != NULL) { 4550Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 4560Sstevel@tonic-gate (void) printf("%*s", ZONEID_WIDTH, "-"); 4570Sstevel@tonic-gate else 4580Sstevel@tonic-gate (void) printf("%*lu", ZONEID_WIDTH, zent->zid); 4593448Sdh155122 (void) printf(" %-16s %-10s %-30s %-8s %-6s\n", zent->zname, 4603448Sdh155122 zent->zstate_str, zent->zroot, zent->zbrand, ip_type_str); 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate static int 465766Scarlsonj lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) 4660Sstevel@tonic-gate { 4671676Sjpk char root[MAXPATHLEN], *cp; 4680Sstevel@tonic-gate int err; 4692303Scarlsonj uuid_t uuid; 4708942Sgerald.jelinek@sun.com zone_dochandle_t handle; 4710Sstevel@tonic-gate 4720Sstevel@tonic-gate (void) strlcpy(zent->zname, zone_name, sizeof (zent->zname)); 4730Sstevel@tonic-gate (void) strlcpy(zent->zroot, "???", sizeof (zent->zroot)); 4742712Snn35248 (void) strlcpy(zent->zbrand, "???", sizeof (zent->zbrand)); 4750Sstevel@tonic-gate zent->zstate_str = "???"; 4760Sstevel@tonic-gate 477766Scarlsonj zent->zid = zid; 4780Sstevel@tonic-gate 4792303Scarlsonj if (zonecfg_get_uuid(zone_name, uuid) == Z_OK && 4802303Scarlsonj !uuid_is_null(uuid)) 4812303Scarlsonj uuid_unparse(uuid, zent->zuuid); 4822303Scarlsonj else 4832303Scarlsonj zent->zuuid[0] = '\0'; 4842303Scarlsonj 4851676Sjpk /* 4861676Sjpk * For labeled zones which query the zone path of lower-level 4871676Sjpk * zones, the path needs to be adjusted to drop the final 4881676Sjpk * "/root" component. This adjusted path is then useful 4891676Sjpk * for reading down any exported directories from the 4901676Sjpk * lower-level zone. 4911676Sjpk */ 4921676Sjpk if (is_system_labeled() && zent->zid != ZONE_ID_UNDEFINED) { 4931676Sjpk if (zone_getattr(zent->zid, ZONE_ATTR_ROOT, zent->zroot, 4941676Sjpk sizeof (zent->zroot)) == -1) { 4951676Sjpk zperror2(zent->zname, 4961676Sjpk gettext("could not get zone path.")); 4971676Sjpk return (Z_ERR); 4981676Sjpk } 4991676Sjpk cp = zent->zroot + strlen(zent->zroot) - 5; 5001676Sjpk if (cp > zent->zroot && strcmp(cp, "/root") == 0) 5011676Sjpk *cp = 0; 5021676Sjpk } else { 5031676Sjpk if ((err = zone_get_zonepath(zent->zname, root, 5041676Sjpk sizeof (root))) != Z_OK) { 5051676Sjpk errno = err; 5061676Sjpk zperror2(zent->zname, 5071676Sjpk gettext("could not get zone path.")); 5081676Sjpk return (Z_ERR); 5091676Sjpk } 5101676Sjpk (void) strlcpy(zent->zroot, root, sizeof (zent->zroot)); 5111676Sjpk } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, &zent->zstate_num)) != Z_OK) { 5140Sstevel@tonic-gate errno = err; 5150Sstevel@tonic-gate zperror2(zent->zname, gettext("could not get state")); 5160Sstevel@tonic-gate return (Z_ERR); 5170Sstevel@tonic-gate } 5180Sstevel@tonic-gate zent->zstate_str = zone_state_str(zent->zstate_num); 5193009Snn35248 5203009Snn35248 /* 5213009Snn35248 * A zone's brand is only available in the .xml file describing it, 5223009Snn35248 * which is only visible to the global zone. This causes 5233009Snn35248 * zone_get_brand() to fail when called from within a non-global 5243009Snn35248 * zone. Fortunately we only do this on labeled systems, where we 5253009Snn35248 * know all zones are native. 5263009Snn35248 */ 5273009Snn35248 if (getzoneid() != GLOBAL_ZONEID) { 5283009Snn35248 assert(is_system_labeled() != 0); 52910943SEdward.Pilatowicz@Sun.COM (void) strlcpy(zent->zbrand, default_brand, 5303009Snn35248 sizeof (zent->zbrand)); 5313009Snn35248 } else if (zone_get_brand(zent->zname, zent->zbrand, 5322712Snn35248 sizeof (zent->zbrand)) != Z_OK) { 5332712Snn35248 zperror2(zent->zname, gettext("could not get brand name")); 5342712Snn35248 return (Z_ERR); 5352712Snn35248 } 5360Sstevel@tonic-gate 5373448Sdh155122 /* 5383448Sdh155122 * Get ip type of the zone. 5393448Sdh155122 * Note for global zone, ZS_SHARED is set always. 5403448Sdh155122 */ 5413448Sdh155122 if (zid == GLOBAL_ZONEID) { 5423448Sdh155122 zent->ziptype = ZS_SHARED; 5438942Sgerald.jelinek@sun.com return (Z_OK); 5448942Sgerald.jelinek@sun.com } 5458942Sgerald.jelinek@sun.com 5468942Sgerald.jelinek@sun.com /* 5478942Sgerald.jelinek@sun.com * There is a race condition where the zone could boot while 5488942Sgerald.jelinek@sun.com * we're walking the index file. In this case the zone state 5498942Sgerald.jelinek@sun.com * could be seen as running from the call above, but the zoneid 5508942Sgerald.jelinek@sun.com * would be undefined. 5518942Sgerald.jelinek@sun.com * 5528942Sgerald.jelinek@sun.com * There is also a race condition where the zone could shutdown after 5538942Sgerald.jelinek@sun.com * we got its running state above. This is also not an error and 5548942Sgerald.jelinek@sun.com * we fall back to getting the ziptype from the zone configuration. 5558942Sgerald.jelinek@sun.com */ 5568942Sgerald.jelinek@sun.com if (zent->zstate_num == ZONE_STATE_RUNNING && 5578942Sgerald.jelinek@sun.com zid != ZONE_ID_UNDEFINED) { 5588942Sgerald.jelinek@sun.com ushort_t flags; 5598942Sgerald.jelinek@sun.com 5608942Sgerald.jelinek@sun.com if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags, 5618942Sgerald.jelinek@sun.com sizeof (flags)) >= 0) { 5623448Sdh155122 if (flags & ZF_NET_EXCL) 5633448Sdh155122 zent->ziptype = ZS_EXCLUSIVE; 5643448Sdh155122 else 5653448Sdh155122 zent->ziptype = ZS_SHARED; 5668942Sgerald.jelinek@sun.com return (Z_OK); 5673448Sdh155122 } 5683448Sdh155122 } 5693448Sdh155122 5708942Sgerald.jelinek@sun.com if ((handle = zonecfg_init_handle()) == NULL) { 5718942Sgerald.jelinek@sun.com zperror2(zent->zname, gettext("could not init handle")); 5728942Sgerald.jelinek@sun.com return (Z_ERR); 5738942Sgerald.jelinek@sun.com } 5748942Sgerald.jelinek@sun.com if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) { 5758942Sgerald.jelinek@sun.com zperror2(zent->zname, gettext("could not get handle")); 5768942Sgerald.jelinek@sun.com zonecfg_fini_handle(handle); 5778942Sgerald.jelinek@sun.com return (Z_ERR); 5788942Sgerald.jelinek@sun.com } 5798942Sgerald.jelinek@sun.com 5808942Sgerald.jelinek@sun.com if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) { 5818942Sgerald.jelinek@sun.com zperror2(zent->zname, gettext("could not get ip-type")); 5828942Sgerald.jelinek@sun.com zonecfg_fini_handle(handle); 5838942Sgerald.jelinek@sun.com return (Z_ERR); 5848942Sgerald.jelinek@sun.com } 5858942Sgerald.jelinek@sun.com zonecfg_fini_handle(handle); 5868942Sgerald.jelinek@sun.com 5870Sstevel@tonic-gate return (Z_OK); 5880Sstevel@tonic-gate } 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate /* 5910Sstevel@tonic-gate * fetch_zents() calls zone_list(2) to find out how many zones are running 5920Sstevel@tonic-gate * (which is stored in the global nzents), then calls zone_list(2) again 5930Sstevel@tonic-gate * to fetch the list of running zones (stored in the global zents). This 5940Sstevel@tonic-gate * function may be called multiple times, so if zents is already set, we 5950Sstevel@tonic-gate * return immediately to save work. 5968942Sgerald.jelinek@sun.com * 5978942Sgerald.jelinek@sun.com * Note that the data about running zones can change while this function 5988942Sgerald.jelinek@sun.com * is running, so its possible that the list of zones will have empty slots 5998942Sgerald.jelinek@sun.com * at the end. 6000Sstevel@tonic-gate */ 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate static int 603766Scarlsonj fetch_zents(void) 6040Sstevel@tonic-gate { 6050Sstevel@tonic-gate zoneid_t *zids = NULL; 6060Sstevel@tonic-gate uint_t nzents_saved; 607766Scarlsonj int i, retv; 608766Scarlsonj FILE *fp; 609766Scarlsonj boolean_t inaltroot; 610766Scarlsonj zone_entry_t *zentp; 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate if (nzents > 0) 6130Sstevel@tonic-gate return (Z_OK); 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate if (zone_list(NULL, &nzents) != 0) { 6160Sstevel@tonic-gate zperror(gettext("failed to get zoneid list"), B_FALSE); 6170Sstevel@tonic-gate return (Z_ERR); 6180Sstevel@tonic-gate } 6190Sstevel@tonic-gate 6200Sstevel@tonic-gate again: 6210Sstevel@tonic-gate if (nzents == 0) 6220Sstevel@tonic-gate return (Z_OK); 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate zids = safe_calloc(nzents, sizeof (zoneid_t)); 6250Sstevel@tonic-gate nzents_saved = nzents; 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate if (zone_list(zids, &nzents) != 0) { 6280Sstevel@tonic-gate zperror(gettext("failed to get zone list"), B_FALSE); 6290Sstevel@tonic-gate free(zids); 6300Sstevel@tonic-gate return (Z_ERR); 6310Sstevel@tonic-gate } 6320Sstevel@tonic-gate if (nzents != nzents_saved) { 6330Sstevel@tonic-gate /* list changed, try again */ 6340Sstevel@tonic-gate free(zids); 6350Sstevel@tonic-gate goto again; 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate zents = safe_calloc(nzents, sizeof (zone_entry_t)); 6390Sstevel@tonic-gate 640766Scarlsonj inaltroot = zonecfg_in_alt_root(); 641766Scarlsonj if (inaltroot) 642766Scarlsonj fp = zonecfg_open_scratch("", B_FALSE); 643766Scarlsonj else 644766Scarlsonj fp = NULL; 645766Scarlsonj zentp = zents; 646766Scarlsonj retv = Z_OK; 6470Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6480Sstevel@tonic-gate char name[ZONENAME_MAX]; 649766Scarlsonj char altname[ZONENAME_MAX]; 6500Sstevel@tonic-gate 651766Scarlsonj if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { 6528942Sgerald.jelinek@sun.com /* 6538942Sgerald.jelinek@sun.com * There is a race condition where the zone may have 6548942Sgerald.jelinek@sun.com * shutdown since we retrieved the number of running 6558942Sgerald.jelinek@sun.com * zones above. This is not an error, there will be 6568942Sgerald.jelinek@sun.com * an empty slot at the end of the list. 6578942Sgerald.jelinek@sun.com */ 658766Scarlsonj continue; 659766Scarlsonj } 660766Scarlsonj if (zonecfg_is_scratch(name)) { 661766Scarlsonj /* Ignore scratch zones by default */ 662766Scarlsonj if (!inaltroot) 663766Scarlsonj continue; 664766Scarlsonj if (fp == NULL || 665766Scarlsonj zonecfg_reverse_scratch(fp, name, altname, 666766Scarlsonj sizeof (altname), NULL, 0) == -1) { 667924Sgjelinek zerror(gettext("could not resolve scratch " 668766Scarlsonj "zone %s"), name); 669766Scarlsonj retv = Z_ERR; 670766Scarlsonj continue; 671766Scarlsonj } 672766Scarlsonj (void) strcpy(name, altname); 673766Scarlsonj } else { 674766Scarlsonj /* Ignore non-scratch when in an alternate root */ 675766Scarlsonj if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0) 676766Scarlsonj continue; 677766Scarlsonj } 678766Scarlsonj if (lookup_zone_info(name, zids[i], zentp) != Z_OK) { 6798942Sgerald.jelinek@sun.com /* 6808942Sgerald.jelinek@sun.com * There is a race condition where the zone may have 6818942Sgerald.jelinek@sun.com * shutdown since we retrieved the number of running 6828942Sgerald.jelinek@sun.com * zones above. This is not an error, there will be 6838942Sgerald.jelinek@sun.com * an empty slot at the end of the list. 6848942Sgerald.jelinek@sun.com */ 685766Scarlsonj continue; 686766Scarlsonj } 687766Scarlsonj zentp++; 6880Sstevel@tonic-gate } 689766Scarlsonj nzents = zentp - zents; 690766Scarlsonj if (fp != NULL) 691766Scarlsonj zonecfg_close_scratch(fp); 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate free(zids); 694766Scarlsonj return (retv); 6950Sstevel@tonic-gate } 6960Sstevel@tonic-gate 697766Scarlsonj static int 6980Sstevel@tonic-gate zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable) 6990Sstevel@tonic-gate { 7000Sstevel@tonic-gate int i; 7010Sstevel@tonic-gate zone_entry_t zent; 7020Sstevel@tonic-gate FILE *cookie; 7030Sstevel@tonic-gate char *name; 7040Sstevel@tonic-gate 7050Sstevel@tonic-gate /* 7060Sstevel@tonic-gate * First get the list of running zones from the kernel and print them. 7070Sstevel@tonic-gate * If that is all we need, then return. 7080Sstevel@tonic-gate */ 709766Scarlsonj if ((i = fetch_zents()) != Z_OK) { 7100Sstevel@tonic-gate /* 7110Sstevel@tonic-gate * No need for error messages; fetch_zents() has already taken 7120Sstevel@tonic-gate * care of this. 7130Sstevel@tonic-gate */ 714766Scarlsonj return (i); 7150Sstevel@tonic-gate } 716766Scarlsonj for (i = 0; i < nzents; i++) 7170Sstevel@tonic-gate zone_print(&zents[i], verbose, parsable); 7180Sstevel@tonic-gate if (min_state >= ZONE_STATE_RUNNING) 719766Scarlsonj return (Z_OK); 7200Sstevel@tonic-gate /* 7210Sstevel@tonic-gate * Next, get the full list of zones from the configuration, skipping 7220Sstevel@tonic-gate * any we have already printed. 7230Sstevel@tonic-gate */ 7240Sstevel@tonic-gate cookie = setzoneent(); 7250Sstevel@tonic-gate while ((name = getzoneent(cookie)) != NULL) { 7260Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 7270Sstevel@tonic-gate if (strcmp(zents[i].zname, name) == 0) 7280Sstevel@tonic-gate break; 7290Sstevel@tonic-gate } 7300Sstevel@tonic-gate if (i < nzents) { 7310Sstevel@tonic-gate free(name); 7320Sstevel@tonic-gate continue; 7330Sstevel@tonic-gate } 734766Scarlsonj if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { 7350Sstevel@tonic-gate free(name); 7360Sstevel@tonic-gate continue; 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate free(name); 7390Sstevel@tonic-gate if (zent.zstate_num >= min_state) 7400Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate endzoneent(cookie); 743766Scarlsonj return (Z_OK); 7440Sstevel@tonic-gate } 7450Sstevel@tonic-gate 7468083SJordan.Vaughan@Sun.com /* 7478083SJordan.Vaughan@Sun.com * Retrieve a zone entry by name. Returns NULL if no such zone exists. 7488083SJordan.Vaughan@Sun.com */ 7490Sstevel@tonic-gate static zone_entry_t * 7508083SJordan.Vaughan@Sun.com lookup_running_zone(const char *str) 7510Sstevel@tonic-gate { 7520Sstevel@tonic-gate int i; 7530Sstevel@tonic-gate 7540Sstevel@tonic-gate if (fetch_zents() != Z_OK) 7550Sstevel@tonic-gate return (NULL); 7560Sstevel@tonic-gate 7570Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 7580Sstevel@tonic-gate if (strcmp(str, zents[i].zname) == 0) 7590Sstevel@tonic-gate return (&zents[i]); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate return (NULL); 7620Sstevel@tonic-gate } 7630Sstevel@tonic-gate 7640Sstevel@tonic-gate /* 7650Sstevel@tonic-gate * Check a bit in a mode_t: if on is B_TRUE, that bit should be on; if 7660Sstevel@tonic-gate * B_FALSE, it should be off. Return B_TRUE if the mode is bad (incorrect). 7670Sstevel@tonic-gate */ 7680Sstevel@tonic-gate static boolean_t 7690Sstevel@tonic-gate bad_mode_bit(mode_t mode, mode_t bit, boolean_t on, char *file) 7700Sstevel@tonic-gate { 7710Sstevel@tonic-gate char *str; 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate assert(bit == S_IRUSR || bit == S_IWUSR || bit == S_IXUSR || 7740Sstevel@tonic-gate bit == S_IRGRP || bit == S_IWGRP || bit == S_IXGRP || 7750Sstevel@tonic-gate bit == S_IROTH || bit == S_IWOTH || bit == S_IXOTH); 7760Sstevel@tonic-gate /* 7770Sstevel@tonic-gate * TRANSLATION_NOTE 7780Sstevel@tonic-gate * The strings below will be used as part of a larger message, 7790Sstevel@tonic-gate * either: 7800Sstevel@tonic-gate * (file name) must be (owner|group|world) (read|writ|execut)able 7810Sstevel@tonic-gate * or 7820Sstevel@tonic-gate * (file name) must not be (owner|group|world) (read|writ|execut)able 7830Sstevel@tonic-gate */ 7840Sstevel@tonic-gate switch (bit) { 7850Sstevel@tonic-gate case S_IRUSR: 7860Sstevel@tonic-gate str = gettext("owner readable"); 7870Sstevel@tonic-gate break; 7880Sstevel@tonic-gate case S_IWUSR: 7890Sstevel@tonic-gate str = gettext("owner writable"); 7900Sstevel@tonic-gate break; 7910Sstevel@tonic-gate case S_IXUSR: 7920Sstevel@tonic-gate str = gettext("owner executable"); 7930Sstevel@tonic-gate break; 7940Sstevel@tonic-gate case S_IRGRP: 7950Sstevel@tonic-gate str = gettext("group readable"); 7960Sstevel@tonic-gate break; 7970Sstevel@tonic-gate case S_IWGRP: 7980Sstevel@tonic-gate str = gettext("group writable"); 7990Sstevel@tonic-gate break; 8000Sstevel@tonic-gate case S_IXGRP: 8010Sstevel@tonic-gate str = gettext("group executable"); 8020Sstevel@tonic-gate break; 8030Sstevel@tonic-gate case S_IROTH: 8040Sstevel@tonic-gate str = gettext("world readable"); 8050Sstevel@tonic-gate break; 8060Sstevel@tonic-gate case S_IWOTH: 8070Sstevel@tonic-gate str = gettext("world writable"); 8080Sstevel@tonic-gate break; 8090Sstevel@tonic-gate case S_IXOTH: 8100Sstevel@tonic-gate str = gettext("world executable"); 8110Sstevel@tonic-gate break; 8120Sstevel@tonic-gate } 8130Sstevel@tonic-gate if ((mode & bit) == (on ? 0 : bit)) { 8140Sstevel@tonic-gate /* 8150Sstevel@tonic-gate * TRANSLATION_NOTE 8160Sstevel@tonic-gate * The first parameter below is a file name; the second 8170Sstevel@tonic-gate * is one of the "(owner|group|world) (read|writ|execut)able" 8180Sstevel@tonic-gate * strings from above. 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate /* 8210Sstevel@tonic-gate * The code below could be simplified but not in a way 8220Sstevel@tonic-gate * that would easily translate to non-English locales. 8230Sstevel@tonic-gate */ 8240Sstevel@tonic-gate if (on) { 8250Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must be %s.\n"), 8260Sstevel@tonic-gate file, str); 8270Sstevel@tonic-gate } else { 8280Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must not be %s.\n"), 8290Sstevel@tonic-gate file, str); 8300Sstevel@tonic-gate } 8310Sstevel@tonic-gate return (B_TRUE); 8320Sstevel@tonic-gate } 8330Sstevel@tonic-gate return (B_FALSE); 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate /* 8370Sstevel@tonic-gate * We want to make sure that no zone has its zone path as a child node 8380Sstevel@tonic-gate * (in the directory sense) of any other. We do that by comparing this 8390Sstevel@tonic-gate * zone's path to the path of all other (non-global) zones. The comparison 8400Sstevel@tonic-gate * in each case is simple: add '/' to the end of the path, then do a 8410Sstevel@tonic-gate * strncmp() of the two paths, using the length of the shorter one. 8420Sstevel@tonic-gate */ 8430Sstevel@tonic-gate 8440Sstevel@tonic-gate static int 8450Sstevel@tonic-gate crosscheck_zonepaths(char *path) 8460Sstevel@tonic-gate { 8470Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 8480Sstevel@tonic-gate char path_copy[MAXPATHLEN]; /* copy of original path */ 8490Sstevel@tonic-gate char rpath_copy[MAXPATHLEN]; /* copy of original rpath */ 8500Sstevel@tonic-gate struct zoneent *ze; 8510Sstevel@tonic-gate int res, err; 8520Sstevel@tonic-gate FILE *cookie; 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate cookie = setzoneent(); 8550Sstevel@tonic-gate while ((ze = getzoneent_private(cookie)) != NULL) { 8560Sstevel@tonic-gate /* Skip zones which are not installed. */ 8570Sstevel@tonic-gate if (ze->zone_state < ZONE_STATE_INSTALLED) { 8580Sstevel@tonic-gate free(ze); 8590Sstevel@tonic-gate continue; 8600Sstevel@tonic-gate } 8610Sstevel@tonic-gate /* Skip the global zone and the current target zone. */ 8620Sstevel@tonic-gate if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0 || 8630Sstevel@tonic-gate strcmp(ze->zone_name, target_zone) == 0) { 8640Sstevel@tonic-gate free(ze); 8650Sstevel@tonic-gate continue; 8660Sstevel@tonic-gate } 8670Sstevel@tonic-gate if (strlen(ze->zone_path) == 0) { 8680Sstevel@tonic-gate /* old index file without path, fall back */ 8690Sstevel@tonic-gate if ((err = zone_get_zonepath(ze->zone_name, 8700Sstevel@tonic-gate ze->zone_path, sizeof (ze->zone_path))) != Z_OK) { 8710Sstevel@tonic-gate errno = err; 8720Sstevel@tonic-gate zperror2(ze->zone_name, 8730Sstevel@tonic-gate gettext("could not get zone path")); 8740Sstevel@tonic-gate free(ze); 8750Sstevel@tonic-gate continue; 8760Sstevel@tonic-gate } 8770Sstevel@tonic-gate } 878766Scarlsonj (void) snprintf(path_copy, sizeof (path_copy), "%s%s", 879766Scarlsonj zonecfg_get_root(), ze->zone_path); 880766Scarlsonj res = resolvepath(path_copy, rpath, sizeof (rpath)); 8810Sstevel@tonic-gate if (res == -1) { 8820Sstevel@tonic-gate if (errno != ENOENT) { 883766Scarlsonj zperror(path_copy, B_FALSE); 8840Sstevel@tonic-gate free(ze); 8850Sstevel@tonic-gate return (Z_ERR); 8860Sstevel@tonic-gate } 8870Sstevel@tonic-gate (void) printf(gettext("WARNING: zone %s is installed, " 8880Sstevel@tonic-gate "but its %s %s does not exist.\n"), ze->zone_name, 889766Scarlsonj "zonepath", path_copy); 8900Sstevel@tonic-gate free(ze); 8910Sstevel@tonic-gate continue; 8920Sstevel@tonic-gate } 8930Sstevel@tonic-gate rpath[res] = '\0'; 8940Sstevel@tonic-gate (void) snprintf(path_copy, sizeof (path_copy), "%s/", path); 8950Sstevel@tonic-gate (void) snprintf(rpath_copy, sizeof (rpath_copy), "%s/", rpath); 8960Sstevel@tonic-gate if (strncmp(path_copy, rpath_copy, 8970Sstevel@tonic-gate min(strlen(path_copy), strlen(rpath_copy))) == 0) { 898924Sgjelinek /* 899924Sgjelinek * TRANSLATION_NOTE 900924Sgjelinek * zonepath is a literal that should not be translated. 901924Sgjelinek */ 9020Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s zonepath (%s) and " 9030Sstevel@tonic-gate "%s zonepath (%s) overlap.\n"), 9040Sstevel@tonic-gate target_zone, path, ze->zone_name, rpath); 9050Sstevel@tonic-gate free(ze); 9060Sstevel@tonic-gate return (Z_ERR); 9070Sstevel@tonic-gate } 9080Sstevel@tonic-gate free(ze); 9090Sstevel@tonic-gate } 9100Sstevel@tonic-gate endzoneent(cookie); 9110Sstevel@tonic-gate return (Z_OK); 9120Sstevel@tonic-gate } 9130Sstevel@tonic-gate 9140Sstevel@tonic-gate static int 9150Sstevel@tonic-gate validate_zonepath(char *path, int cmd_num) 9160Sstevel@tonic-gate { 9170Sstevel@tonic-gate int res; /* result of last library/system call */ 9180Sstevel@tonic-gate boolean_t err = B_FALSE; /* have we run into an error? */ 9190Sstevel@tonic-gate struct stat stbuf; 9202267Sdp struct statvfs64 vfsbuf; 9210Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 9220Sstevel@tonic-gate char ppath[MAXPATHLEN]; /* parent path */ 9230Sstevel@tonic-gate char rppath[MAXPATHLEN]; /* resolved parent path */ 9240Sstevel@tonic-gate char rootpath[MAXPATHLEN]; /* root path */ 9250Sstevel@tonic-gate zone_state_t state; 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate if (path[0] != '/') { 9280Sstevel@tonic-gate (void) fprintf(stderr, 9290Sstevel@tonic-gate gettext("%s is not an absolute path.\n"), path); 9300Sstevel@tonic-gate return (Z_ERR); 9310Sstevel@tonic-gate } 9320Sstevel@tonic-gate if ((res = resolvepath(path, rpath, sizeof (rpath))) == -1) { 9330Sstevel@tonic-gate if ((errno != ENOENT) || 9341300Sgjelinek (cmd_num != CMD_VERIFY && cmd_num != CMD_INSTALL && 9351300Sgjelinek cmd_num != CMD_CLONE && cmd_num != CMD_MOVE)) { 9360Sstevel@tonic-gate zperror(path, B_FALSE); 9370Sstevel@tonic-gate return (Z_ERR); 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate if (cmd_num == CMD_VERIFY) { 940924Sgjelinek /* 941924Sgjelinek * TRANSLATION_NOTE 942924Sgjelinek * zoneadm is a literal that should not be translated. 943924Sgjelinek */ 9440Sstevel@tonic-gate (void) fprintf(stderr, gettext("WARNING: %s does not " 945924Sgjelinek "exist, so it could not be verified.\nWhen " 946924Sgjelinek "'zoneadm %s' is run, '%s' will try to create\n%s, " 947924Sgjelinek "and '%s' will be tried again,\nbut the '%s' may " 948924Sgjelinek "fail if:\nthe parent directory of %s is group- or " 949924Sgjelinek "other-writable\nor\n%s overlaps with any other " 9500Sstevel@tonic-gate "installed zones.\n"), path, 9510Sstevel@tonic-gate cmd_to_str(CMD_INSTALL), cmd_to_str(CMD_INSTALL), 9520Sstevel@tonic-gate path, cmd_to_str(CMD_VERIFY), 9530Sstevel@tonic-gate cmd_to_str(CMD_VERIFY), path, path); 9540Sstevel@tonic-gate return (Z_OK); 9550Sstevel@tonic-gate } 9560Sstevel@tonic-gate /* 9570Sstevel@tonic-gate * The zonepath is supposed to be mode 700 but its 9580Sstevel@tonic-gate * parent(s) 755. So use 755 on the mkdirp() then 9590Sstevel@tonic-gate * chmod() the zonepath itself to 700. 9600Sstevel@tonic-gate */ 9610Sstevel@tonic-gate if (mkdirp(path, DEFAULT_DIR_MODE) < 0) { 9620Sstevel@tonic-gate zperror(path, B_FALSE); 9630Sstevel@tonic-gate return (Z_ERR); 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate /* 9660Sstevel@tonic-gate * If the chmod() fails, report the error, but might 9670Sstevel@tonic-gate * as well continue the verify procedure. 9680Sstevel@tonic-gate */ 9690Sstevel@tonic-gate if (chmod(path, S_IRWXU) != 0) 9700Sstevel@tonic-gate zperror(path, B_FALSE); 9710Sstevel@tonic-gate /* 9720Sstevel@tonic-gate * Since the mkdir() succeeded, we should not have to 9730Sstevel@tonic-gate * worry about a subsequent ENOENT, thus this should 9740Sstevel@tonic-gate * only recurse once. 9750Sstevel@tonic-gate */ 9761300Sgjelinek return (validate_zonepath(path, cmd_num)); 9770Sstevel@tonic-gate } 9780Sstevel@tonic-gate rpath[res] = '\0'; 9790Sstevel@tonic-gate if (strcmp(path, rpath) != 0) { 9800Sstevel@tonic-gate errno = Z_RESOLVED_PATH; 9810Sstevel@tonic-gate zperror(path, B_TRUE); 9820Sstevel@tonic-gate return (Z_ERR); 9830Sstevel@tonic-gate } 9840Sstevel@tonic-gate if ((res = stat(rpath, &stbuf)) != 0) { 9850Sstevel@tonic-gate zperror(rpath, B_FALSE); 9860Sstevel@tonic-gate return (Z_ERR); 9870Sstevel@tonic-gate } 9880Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 9890Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 9900Sstevel@tonic-gate rpath); 9910Sstevel@tonic-gate return (Z_ERR); 9920Sstevel@tonic-gate } 9933445Sblakej if (strcmp(stbuf.st_fstype, MNTTYPE_TMPFS) == 0) { 9940Sstevel@tonic-gate (void) printf(gettext("WARNING: %s is on a temporary " 9951867Sgjelinek "file system.\n"), rpath); 9960Sstevel@tonic-gate } 9970Sstevel@tonic-gate if (crosscheck_zonepaths(rpath) != Z_OK) 9980Sstevel@tonic-gate return (Z_ERR); 9990Sstevel@tonic-gate /* 10000Sstevel@tonic-gate * Try to collect and report as many minor errors as possible 10010Sstevel@tonic-gate * before returning, so the user can learn everything that needs 10020Sstevel@tonic-gate * to be fixed up front. 10030Sstevel@tonic-gate */ 10040Sstevel@tonic-gate if (stbuf.st_uid != 0) { 10050Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 10060Sstevel@tonic-gate rpath); 10070Sstevel@tonic-gate err = B_TRUE; 10080Sstevel@tonic-gate } 10090Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rpath); 10100Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rpath); 10110Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rpath); 10120Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRGRP, B_FALSE, rpath); 10130Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rpath); 10140Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXGRP, B_FALSE, rpath); 10150Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IROTH, B_FALSE, rpath); 10160Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rpath); 10170Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXOTH, B_FALSE, rpath); 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate (void) snprintf(ppath, sizeof (ppath), "%s/..", path); 10200Sstevel@tonic-gate if ((res = resolvepath(ppath, rppath, sizeof (rppath))) == -1) { 10210Sstevel@tonic-gate zperror(ppath, B_FALSE); 10220Sstevel@tonic-gate return (Z_ERR); 10230Sstevel@tonic-gate } 10240Sstevel@tonic-gate rppath[res] = '\0'; 10250Sstevel@tonic-gate if ((res = stat(rppath, &stbuf)) != 0) { 10260Sstevel@tonic-gate zperror(rppath, B_FALSE); 10270Sstevel@tonic-gate return (Z_ERR); 10280Sstevel@tonic-gate } 10290Sstevel@tonic-gate /* theoretically impossible */ 10300Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 10310Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 10320Sstevel@tonic-gate rppath); 10330Sstevel@tonic-gate return (Z_ERR); 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate if (stbuf.st_uid != 0) { 10360Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 10370Sstevel@tonic-gate rppath); 10380Sstevel@tonic-gate err = B_TRUE; 10390Sstevel@tonic-gate } 10400Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rppath); 10410Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rppath); 10420Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rppath); 10430Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rppath); 10440Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rppath); 10450Sstevel@tonic-gate if (strcmp(rpath, rppath) == 0) { 10460Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is its own parent.\n"), 10470Sstevel@tonic-gate rppath); 10480Sstevel@tonic-gate err = B_TRUE; 10490Sstevel@tonic-gate } 10500Sstevel@tonic-gate 10512267Sdp if (statvfs64(rpath, &vfsbuf) != 0) { 10520Sstevel@tonic-gate zperror(rpath, B_FALSE); 10530Sstevel@tonic-gate return (Z_ERR); 10540Sstevel@tonic-gate } 1055823Sgjelinek if (strcmp(vfsbuf.f_basetype, MNTTYPE_NFS) == 0) { 1056924Sgjelinek /* 1057924Sgjelinek * TRANSLATION_NOTE 1058924Sgjelinek * Zonepath and NFS are literals that should not be translated. 1059924Sgjelinek */ 1060924Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on an NFS " 10611867Sgjelinek "mounted file system.\n" 10621867Sgjelinek "\tA local file system must be used.\n"), rpath); 1063823Sgjelinek return (Z_ERR); 1064823Sgjelinek } 1065823Sgjelinek if (vfsbuf.f_flag & ST_NOSUID) { 1066924Sgjelinek /* 1067924Sgjelinek * TRANSLATION_NOTE 1068924Sgjelinek * Zonepath and nosuid are literals that should not be 1069924Sgjelinek * translated. 1070924Sgjelinek */ 1071823Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on a nosuid " 10721867Sgjelinek "file system.\n"), rpath); 10730Sstevel@tonic-gate return (Z_ERR); 10740Sstevel@tonic-gate } 10750Sstevel@tonic-gate 10760Sstevel@tonic-gate if ((res = zone_get_state(target_zone, &state)) != Z_OK) { 10770Sstevel@tonic-gate errno = res; 10780Sstevel@tonic-gate zperror2(target_zone, gettext("could not get state")); 10790Sstevel@tonic-gate return (Z_ERR); 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate /* 10820Sstevel@tonic-gate * The existence of the root path is only bad in the configured state, 10830Sstevel@tonic-gate * as it is *supposed* to be there at the installed and later states. 10841507Sgjelinek * However, the root path is expected to be there if the zone is 10851507Sgjelinek * detached. 10860Sstevel@tonic-gate * State/command mismatches are caught earlier in verify_details(). 10870Sstevel@tonic-gate */ 10881507Sgjelinek if (state == ZONE_STATE_CONFIGURED && cmd_num != CMD_ATTACH) { 10890Sstevel@tonic-gate if (snprintf(rootpath, sizeof (rootpath), "%s/root", rpath) >= 10900Sstevel@tonic-gate sizeof (rootpath)) { 1091924Sgjelinek /* 1092924Sgjelinek * TRANSLATION_NOTE 1093924Sgjelinek * Zonepath is a literal that should not be translated. 1094924Sgjelinek */ 10950Sstevel@tonic-gate (void) fprintf(stderr, 10960Sstevel@tonic-gate gettext("Zonepath %s is too long.\n"), rpath); 10970Sstevel@tonic-gate return (Z_ERR); 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate if ((res = stat(rootpath, &stbuf)) == 0) { 11007782Sgerald.jelinek@sun.com if (zonecfg_detached(rpath)) { 11011507Sgjelinek (void) fprintf(stderr, 11021507Sgjelinek gettext("Cannot %s detached " 11031507Sgjelinek "zone.\nUse attach or remove %s " 11041507Sgjelinek "directory.\n"), cmd_to_str(cmd_num), 11051507Sgjelinek rpath); 11067782Sgerald.jelinek@sun.com return (Z_ERR); 11077782Sgerald.jelinek@sun.com } 11087782Sgerald.jelinek@sun.com 11097782Sgerald.jelinek@sun.com /* Not detached, check if it really looks ok. */ 11107782Sgerald.jelinek@sun.com 11117782Sgerald.jelinek@sun.com if (!S_ISDIR(stbuf.st_mode)) { 11127782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("%s is not a " 11137782Sgerald.jelinek@sun.com "directory.\n"), rootpath); 11147782Sgerald.jelinek@sun.com return (Z_ERR); 11157782Sgerald.jelinek@sun.com } 11167782Sgerald.jelinek@sun.com 11177782Sgerald.jelinek@sun.com if (stbuf.st_uid != 0) { 11187782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("%s is not " 11197782Sgerald.jelinek@sun.com "owned by root.\n"), rootpath); 11207782Sgerald.jelinek@sun.com return (Z_ERR); 11217782Sgerald.jelinek@sun.com } 11227782Sgerald.jelinek@sun.com 11237782Sgerald.jelinek@sun.com if ((stbuf.st_mode & 0777) != 0755) { 11247782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("%s mode is not " 11257782Sgerald.jelinek@sun.com "0755.\n"), rootpath); 11267782Sgerald.jelinek@sun.com return (Z_ERR); 11277782Sgerald.jelinek@sun.com } 11280Sstevel@tonic-gate } 11290Sstevel@tonic-gate } 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate return (err ? Z_ERR : Z_OK); 11320Sstevel@tonic-gate } 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate static int 11353339Szt129084 invoke_brand_handler(int cmd_num, char *argv[]) 11363339Szt129084 { 11373339Szt129084 zone_dochandle_t handle; 11383339Szt129084 int err; 11393339Szt129084 11403339Szt129084 if ((handle = zonecfg_init_handle()) == NULL) { 11413339Szt129084 zperror(cmd_to_str(cmd_num), B_TRUE); 11423339Szt129084 return (Z_ERR); 11433339Szt129084 } 11443339Szt129084 if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 11453339Szt129084 errno = err; 11463339Szt129084 zperror(cmd_to_str(cmd_num), B_TRUE); 11473339Szt129084 zonecfg_fini_handle(handle); 11483339Szt129084 return (Z_ERR); 11493339Szt129084 } 11503339Szt129084 if (verify_brand(handle, cmd_num, argv) != Z_OK) { 11513339Szt129084 zonecfg_fini_handle(handle); 11523339Szt129084 return (Z_ERR); 11533339Szt129084 } 11543339Szt129084 zonecfg_fini_handle(handle); 11553339Szt129084 return (Z_OK); 11563339Szt129084 } 11573339Szt129084 11583339Szt129084 static int 11590Sstevel@tonic-gate ready_func(int argc, char *argv[]) 11600Sstevel@tonic-gate { 11610Sstevel@tonic-gate zone_cmd_arg_t zarg; 11620Sstevel@tonic-gate int arg; 11630Sstevel@tonic-gate 1164766Scarlsonj if (zonecfg_in_alt_root()) { 1165766Scarlsonj zerror(gettext("cannot ready zone in alternate root")); 1166766Scarlsonj return (Z_ERR); 1167766Scarlsonj } 1168766Scarlsonj 11690Sstevel@tonic-gate optind = 0; 11700Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 11710Sstevel@tonic-gate switch (arg) { 11720Sstevel@tonic-gate case '?': 11730Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 11740Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 11750Sstevel@tonic-gate default: 11760Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 11770Sstevel@tonic-gate return (Z_USAGE); 11780Sstevel@tonic-gate } 11790Sstevel@tonic-gate } 11800Sstevel@tonic-gate if (argc > optind) { 11810Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 11820Sstevel@tonic-gate return (Z_USAGE); 11830Sstevel@tonic-gate } 11842712Snn35248 if (sanity_check(target_zone, CMD_READY, B_FALSE, B_FALSE, B_FALSE) 11852712Snn35248 != Z_OK) 11860Sstevel@tonic-gate return (Z_ERR); 11873339Szt129084 if (verify_details(CMD_READY, argv) != Z_OK) 11880Sstevel@tonic-gate return (Z_ERR); 11890Sstevel@tonic-gate 11900Sstevel@tonic-gate zarg.cmd = Z_READY; 11917089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 11920Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 11930Sstevel@tonic-gate return (Z_ERR); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate return (Z_OK); 11960Sstevel@tonic-gate } 11970Sstevel@tonic-gate 11980Sstevel@tonic-gate static int 11990Sstevel@tonic-gate boot_func(int argc, char *argv[]) 12000Sstevel@tonic-gate { 12010Sstevel@tonic-gate zone_cmd_arg_t zarg; 12022712Snn35248 boolean_t force = B_FALSE; 12030Sstevel@tonic-gate int arg; 12040Sstevel@tonic-gate 1205766Scarlsonj if (zonecfg_in_alt_root()) { 1206766Scarlsonj zerror(gettext("cannot boot zone in alternate root")); 1207766Scarlsonj return (Z_ERR); 1208766Scarlsonj } 1209766Scarlsonj 12100Sstevel@tonic-gate zarg.bootbuf[0] = '\0'; 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate /* 12132267Sdp * The following getopt processes arguments to zone boot; that 12142267Sdp * is to say, the [here] portion of the argument string: 12152267Sdp * 12162267Sdp * zoneadm -z myzone boot [here] -- -v -m verbose 12172267Sdp * 12182267Sdp * Where [here] can either be nothing, -? (in which case we bail 12192712Snn35248 * and print usage), -f (a private option to indicate that the 12202712Snn35248 * boot operation should be 'forced'), or -s. Support for -s is 12212712Snn35248 * vestigal and obsolete, but is retained because it was a 12222712Snn35248 * documented interface and there are known consumers including 12232712Snn35248 * admin/install; the proper way to specify boot arguments like -s 12242712Snn35248 * is: 12252267Sdp * 12262267Sdp * zoneadm -z myzone boot -- -s -v -m verbose. 12270Sstevel@tonic-gate */ 12280Sstevel@tonic-gate optind = 0; 12292712Snn35248 while ((arg = getopt(argc, argv, "?fs")) != EOF) { 12300Sstevel@tonic-gate switch (arg) { 12310Sstevel@tonic-gate case '?': 12320Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 12330Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 12340Sstevel@tonic-gate case 's': 12350Sstevel@tonic-gate (void) strlcpy(zarg.bootbuf, "-s", 12360Sstevel@tonic-gate sizeof (zarg.bootbuf)); 12370Sstevel@tonic-gate break; 12382712Snn35248 case 'f': 12392712Snn35248 force = B_TRUE; 12402712Snn35248 break; 12410Sstevel@tonic-gate default: 12420Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 12430Sstevel@tonic-gate return (Z_USAGE); 12440Sstevel@tonic-gate } 12450Sstevel@tonic-gate } 12462267Sdp 12472267Sdp for (; optind < argc; optind++) { 12482267Sdp if (strlcat(zarg.bootbuf, argv[optind], 12492267Sdp sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) { 12502267Sdp zerror(gettext("Boot argument list too long")); 12512267Sdp return (Z_ERR); 12522267Sdp } 12532267Sdp if (optind < argc - 1) 12542267Sdp if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >= 12552267Sdp sizeof (zarg.bootbuf)) { 12562267Sdp zerror(gettext("Boot argument list too long")); 12572267Sdp return (Z_ERR); 12582267Sdp } 12592267Sdp } 12602712Snn35248 if (sanity_check(target_zone, CMD_BOOT, B_FALSE, B_FALSE, force) 12612712Snn35248 != Z_OK) 12620Sstevel@tonic-gate return (Z_ERR); 12633339Szt129084 if (verify_details(CMD_BOOT, argv) != Z_OK) 12640Sstevel@tonic-gate return (Z_ERR); 12652712Snn35248 zarg.cmd = force ? Z_FORCEBOOT : Z_BOOT; 12667089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 12670Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 12680Sstevel@tonic-gate return (Z_ERR); 12690Sstevel@tonic-gate } 12703247Sgjelinek 12710Sstevel@tonic-gate return (Z_OK); 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate static void 12750Sstevel@tonic-gate fake_up_local_zone(zoneid_t zid, zone_entry_t *zeptr) 12760Sstevel@tonic-gate { 12770Sstevel@tonic-gate ssize_t result; 12782303Scarlsonj uuid_t uuid; 12792303Scarlsonj FILE *fp; 12803448Sdh155122 ushort_t flags; 12812303Scarlsonj 12822303Scarlsonj (void) memset(zeptr, 0, sizeof (*zeptr)); 12830Sstevel@tonic-gate 12840Sstevel@tonic-gate zeptr->zid = zid; 12852303Scarlsonj 12860Sstevel@tonic-gate /* 12870Sstevel@tonic-gate * Since we're looking up our own (non-global) zone name, 12880Sstevel@tonic-gate * we can be assured that it will succeed. 12890Sstevel@tonic-gate */ 12900Sstevel@tonic-gate result = getzonenamebyid(zid, zeptr->zname, sizeof (zeptr->zname)); 12910Sstevel@tonic-gate assert(result >= 0); 12922303Scarlsonj if (zonecfg_is_scratch(zeptr->zname) && 12932303Scarlsonj (fp = zonecfg_open_scratch("", B_FALSE)) != NULL) { 12942303Scarlsonj (void) zonecfg_reverse_scratch(fp, zeptr->zname, zeptr->zname, 12952303Scarlsonj sizeof (zeptr->zname), NULL, 0); 12962303Scarlsonj zonecfg_close_scratch(fp); 12972303Scarlsonj } 12982303Scarlsonj 12992303Scarlsonj if (is_system_labeled()) { 13001676Sjpk (void) zone_getattr(zid, ZONE_ATTR_ROOT, zeptr->zroot, 13011676Sjpk sizeof (zeptr->zroot)); 13022712Snn35248 (void) strlcpy(zeptr->zbrand, NATIVE_BRAND_NAME, 13034350Std153743 sizeof (zeptr->zbrand)); 13042303Scarlsonj } else { 13052303Scarlsonj (void) strlcpy(zeptr->zroot, "/", sizeof (zeptr->zroot)); 13062712Snn35248 (void) zone_getattr(zid, ZONE_ATTR_BRAND, zeptr->zbrand, 13072712Snn35248 sizeof (zeptr->zbrand)); 13082303Scarlsonj } 13092303Scarlsonj 13100Sstevel@tonic-gate zeptr->zstate_str = "running"; 13112303Scarlsonj if (zonecfg_get_uuid(zeptr->zname, uuid) == Z_OK && 13122303Scarlsonj !uuid_is_null(uuid)) 13132303Scarlsonj uuid_unparse(uuid, zeptr->zuuid); 13143448Sdh155122 13153448Sdh155122 if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags, sizeof (flags)) < 0) { 13163448Sdh155122 zperror2(zeptr->zname, gettext("could not get zone flags")); 13173448Sdh155122 exit(Z_ERR); 13183448Sdh155122 } 13193448Sdh155122 if (flags & ZF_NET_EXCL) 13203448Sdh155122 zeptr->ziptype = ZS_EXCLUSIVE; 13213448Sdh155122 else 13223448Sdh155122 zeptr->ziptype = ZS_SHARED; 13230Sstevel@tonic-gate } 13240Sstevel@tonic-gate 13250Sstevel@tonic-gate static int 13260Sstevel@tonic-gate list_func(int argc, char *argv[]) 13270Sstevel@tonic-gate { 13280Sstevel@tonic-gate zone_entry_t *zentp, zent; 1329766Scarlsonj int arg, retv; 13300Sstevel@tonic-gate boolean_t output = B_FALSE, verbose = B_FALSE, parsable = B_FALSE; 13310Sstevel@tonic-gate zone_state_t min_state = ZONE_STATE_RUNNING; 13320Sstevel@tonic-gate zoneid_t zone_id = getzoneid(); 13330Sstevel@tonic-gate 13340Sstevel@tonic-gate if (target_zone == NULL) { 13350Sstevel@tonic-gate /* all zones: default view to running but allow override */ 13360Sstevel@tonic-gate optind = 0; 13370Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?cipv")) != EOF) { 13380Sstevel@tonic-gate switch (arg) { 13390Sstevel@tonic-gate case '?': 13400Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13410Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13421339Sjonb /* 13431339Sjonb * The 'i' and 'c' options are not mutually 13441339Sjonb * exclusive so if 'c' is given, then min_state 13451339Sjonb * is set to 0 (ZONE_STATE_CONFIGURED) which is 13461339Sjonb * the lowest possible state. If 'i' is given, 13471339Sjonb * then min_state is set to be the lowest state 13481339Sjonb * so far. 13491339Sjonb */ 13500Sstevel@tonic-gate case 'c': 13510Sstevel@tonic-gate min_state = ZONE_STATE_CONFIGURED; 13520Sstevel@tonic-gate break; 13530Sstevel@tonic-gate case 'i': 13541339Sjonb min_state = min(ZONE_STATE_INSTALLED, 13551339Sjonb min_state); 13561339Sjonb 13570Sstevel@tonic-gate break; 13580Sstevel@tonic-gate case 'p': 13590Sstevel@tonic-gate parsable = B_TRUE; 13600Sstevel@tonic-gate break; 13610Sstevel@tonic-gate case 'v': 13620Sstevel@tonic-gate verbose = B_TRUE; 13630Sstevel@tonic-gate break; 13640Sstevel@tonic-gate default: 13650Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13660Sstevel@tonic-gate return (Z_USAGE); 13670Sstevel@tonic-gate } 13680Sstevel@tonic-gate } 13690Sstevel@tonic-gate if (parsable && verbose) { 13700Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 13710Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 13720Sstevel@tonic-gate return (Z_ERR); 13730Sstevel@tonic-gate } 13741676Sjpk if (zone_id == GLOBAL_ZONEID || is_system_labeled()) { 1375766Scarlsonj retv = zone_print_list(min_state, verbose, parsable); 13760Sstevel@tonic-gate } else { 13772712Snn35248 fake_up_local_zone(zone_id, &zent); 1378766Scarlsonj retv = Z_OK; 13790Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 13800Sstevel@tonic-gate } 1381766Scarlsonj return (retv); 13820Sstevel@tonic-gate } 13830Sstevel@tonic-gate 13840Sstevel@tonic-gate /* 13850Sstevel@tonic-gate * Specific target zone: disallow -i/-c suboptions. 13860Sstevel@tonic-gate */ 13870Sstevel@tonic-gate optind = 0; 13880Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?pv")) != EOF) { 13890Sstevel@tonic-gate switch (arg) { 13900Sstevel@tonic-gate case '?': 13910Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13920Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13930Sstevel@tonic-gate case 'p': 13940Sstevel@tonic-gate parsable = B_TRUE; 13950Sstevel@tonic-gate break; 13960Sstevel@tonic-gate case 'v': 13970Sstevel@tonic-gate verbose = B_TRUE; 13980Sstevel@tonic-gate break; 13990Sstevel@tonic-gate default: 14000Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14010Sstevel@tonic-gate return (Z_USAGE); 14020Sstevel@tonic-gate } 14030Sstevel@tonic-gate } 14040Sstevel@tonic-gate if (parsable && verbose) { 14050Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14060Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14070Sstevel@tonic-gate return (Z_ERR); 14080Sstevel@tonic-gate } 14090Sstevel@tonic-gate if (argc > optind) { 14100Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14110Sstevel@tonic-gate return (Z_USAGE); 14120Sstevel@tonic-gate } 14133979Sgjelinek if (zone_id != GLOBAL_ZONEID && !is_system_labeled()) { 14140Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14150Sstevel@tonic-gate /* 14160Sstevel@tonic-gate * main() will issue a Z_NO_ZONE error if it cannot get an 14170Sstevel@tonic-gate * id for target_zone, which in a non-global zone should 14180Sstevel@tonic-gate * happen for any zone name except `zonename`. Thus we 14190Sstevel@tonic-gate * assert() that here but don't otherwise check. 14200Sstevel@tonic-gate */ 14210Sstevel@tonic-gate assert(strcmp(zent.zname, target_zone) == 0); 14220Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14230Sstevel@tonic-gate output = B_TRUE; 14240Sstevel@tonic-gate } else if ((zentp = lookup_running_zone(target_zone)) != NULL) { 14250Sstevel@tonic-gate zone_print(zentp, verbose, parsable); 14260Sstevel@tonic-gate output = B_TRUE; 1427766Scarlsonj } else if (lookup_zone_info(target_zone, ZONE_ID_UNDEFINED, 1428766Scarlsonj &zent) == Z_OK) { 14290Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14300Sstevel@tonic-gate output = B_TRUE; 14310Sstevel@tonic-gate } 14323339Szt129084 14333339Szt129084 /* 14343339Szt129084 * Invoke brand-specific handler. Note that we do this 14353542Sgjelinek * only if we're in the global zone, and target_zone is specified 14363542Sgjelinek * and it is not the global zone. 14373339Szt129084 */ 14383542Sgjelinek if (zone_id == GLOBAL_ZONEID && target_zone != NULL && 14393542Sgjelinek strcmp(target_zone, GLOBAL_ZONENAME) != 0) 14403339Szt129084 if (invoke_brand_handler(CMD_LIST, argv) != Z_OK) 14413339Szt129084 return (Z_ERR); 14423339Szt129084 14430Sstevel@tonic-gate return (output ? Z_OK : Z_ERR); 14440Sstevel@tonic-gate } 14450Sstevel@tonic-gate 14469310Sgerald.jelinek@sun.com int 14470Sstevel@tonic-gate do_subproc(char *cmdbuf) 14480Sstevel@tonic-gate { 14492712Snn35248 void (*saveint)(int); 14502712Snn35248 void (*saveterm)(int); 14512712Snn35248 void (*savequit)(int); 14522712Snn35248 void (*savehup)(int); 14532712Snn35248 int pid, child, status; 14542712Snn35248 14552712Snn35248 if ((child = vfork()) == 0) { 14562712Snn35248 (void) execl("/bin/sh", "sh", "-c", cmdbuf, (char *)NULL); 14572712Snn35248 } 14582712Snn35248 14592712Snn35248 if (child == -1) 14602712Snn35248 return (-1); 14612712Snn35248 14622712Snn35248 saveint = sigset(SIGINT, SIG_IGN); 14632712Snn35248 saveterm = sigset(SIGTERM, SIG_IGN); 14642712Snn35248 savequit = sigset(SIGQUIT, SIG_IGN); 14652712Snn35248 savehup = sigset(SIGHUP, SIG_IGN); 14662712Snn35248 14672712Snn35248 while ((pid = waitpid(child, &status, 0)) != child && pid != -1) 14682712Snn35248 ; 14692712Snn35248 14702712Snn35248 (void) sigset(SIGINT, saveint); 14712712Snn35248 (void) sigset(SIGTERM, saveterm); 14722712Snn35248 (void) sigset(SIGQUIT, savequit); 14732712Snn35248 (void) sigset(SIGHUP, savehup); 14742712Snn35248 14752712Snn35248 return (pid == -1 ? -1 : status); 14762712Snn35248 } 14772712Snn35248 14787089Sgjelinek int 14792712Snn35248 subproc_status(const char *cmd, int status, boolean_t verbose_failure) 14800Sstevel@tonic-gate { 14810Sstevel@tonic-gate if (WIFEXITED(status)) { 14820Sstevel@tonic-gate int exit_code = WEXITSTATUS(status); 14830Sstevel@tonic-gate 14842712Snn35248 if ((verbose_failure) && (exit_code != ZONE_SUBPROC_OK)) 14852712Snn35248 zerror(gettext("'%s' failed with exit code %d."), cmd, 14862712Snn35248 exit_code); 14872712Snn35248 14882712Snn35248 return (exit_code); 14890Sstevel@tonic-gate } else if (WIFSIGNALED(status)) { 14900Sstevel@tonic-gate int signal = WTERMSIG(status); 14910Sstevel@tonic-gate char sigstr[SIG2STR_MAX]; 14920Sstevel@tonic-gate 14930Sstevel@tonic-gate if (sig2str(signal, sigstr) == 0) { 14940Sstevel@tonic-gate zerror(gettext("'%s' terminated by signal SIG%s."), cmd, 14950Sstevel@tonic-gate sigstr); 14960Sstevel@tonic-gate } else { 14970Sstevel@tonic-gate zerror(gettext("'%s' terminated by an unknown signal."), 14980Sstevel@tonic-gate cmd); 14990Sstevel@tonic-gate } 15000Sstevel@tonic-gate } else { 15010Sstevel@tonic-gate zerror(gettext("'%s' failed for unknown reasons."), cmd); 15020Sstevel@tonic-gate } 15032712Snn35248 15042712Snn35248 /* 15052712Snn35248 * Assume a subprocess that died due to a signal or an unknown error 15062712Snn35248 * should be considered an exit code of ZONE_SUBPROC_FATAL, as the 15072712Snn35248 * user will likely need to do some manual cleanup. 15082712Snn35248 */ 15092712Snn35248 return (ZONE_SUBPROC_FATAL); 15100Sstevel@tonic-gate } 15110Sstevel@tonic-gate 15120Sstevel@tonic-gate /* 15130Sstevel@tonic-gate * Various sanity checks; make sure: 15140Sstevel@tonic-gate * 1. We're in the global zone. 15150Sstevel@tonic-gate * 2. The calling user has sufficient privilege. 15160Sstevel@tonic-gate * 3. The target zone is neither the global zone nor anything starting with 15170Sstevel@tonic-gate * "SUNW". 15180Sstevel@tonic-gate * 4a. If we're looking for a 'not running' (i.e., configured or installed) 15190Sstevel@tonic-gate * zone, the name service knows about it. 15200Sstevel@tonic-gate * 4b. For some operations which expect a zone not to be running, that it is 15210Sstevel@tonic-gate * not already running (or ready). 15220Sstevel@tonic-gate */ 15230Sstevel@tonic-gate static int 15240Sstevel@tonic-gate sanity_check(char *zone, int cmd_num, boolean_t running, 15252712Snn35248 boolean_t unsafe_when_running, boolean_t force) 15260Sstevel@tonic-gate { 15270Sstevel@tonic-gate zone_entry_t *zent; 15280Sstevel@tonic-gate priv_set_t *privset; 15292712Snn35248 zone_state_t state, min_state; 1530766Scarlsonj char kernzone[ZONENAME_MAX]; 1531766Scarlsonj FILE *fp; 15320Sstevel@tonic-gate 15330Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 15341645Scomay switch (cmd_num) { 15351645Scomay case CMD_HALT: 15361645Scomay zerror(gettext("use %s to %s this zone."), "halt(1M)", 15371645Scomay cmd_to_str(cmd_num)); 15381645Scomay break; 15391645Scomay case CMD_REBOOT: 15401645Scomay zerror(gettext("use %s to %s this zone."), 15411645Scomay "reboot(1M)", cmd_to_str(cmd_num)); 15421645Scomay break; 15431645Scomay default: 15441645Scomay zerror(gettext("must be in the global zone to %s a " 15451645Scomay "zone."), cmd_to_str(cmd_num)); 15461645Scomay break; 15471645Scomay } 15480Sstevel@tonic-gate return (Z_ERR); 15490Sstevel@tonic-gate } 15500Sstevel@tonic-gate 15510Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 15520Sstevel@tonic-gate zerror(gettext("%s failed"), "priv_allocset"); 15530Sstevel@tonic-gate return (Z_ERR); 15540Sstevel@tonic-gate } 15550Sstevel@tonic-gate 15560Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 15570Sstevel@tonic-gate zerror(gettext("%s failed"), "getppriv"); 15580Sstevel@tonic-gate priv_freeset(privset); 15590Sstevel@tonic-gate return (Z_ERR); 15600Sstevel@tonic-gate } 15610Sstevel@tonic-gate 15620Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 15630Sstevel@tonic-gate zerror(gettext("only a privileged user may %s a zone."), 15640Sstevel@tonic-gate cmd_to_str(cmd_num)); 15650Sstevel@tonic-gate priv_freeset(privset); 15660Sstevel@tonic-gate return (Z_ERR); 15670Sstevel@tonic-gate } 15680Sstevel@tonic-gate priv_freeset(privset); 15690Sstevel@tonic-gate 15700Sstevel@tonic-gate if (zone == NULL) { 15710Sstevel@tonic-gate zerror(gettext("no zone specified")); 15720Sstevel@tonic-gate return (Z_ERR); 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate if (strcmp(zone, GLOBAL_ZONENAME) == 0) { 15760Sstevel@tonic-gate zerror(gettext("%s operation is invalid for the global zone."), 15770Sstevel@tonic-gate cmd_to_str(cmd_num)); 15780Sstevel@tonic-gate return (Z_ERR); 15790Sstevel@tonic-gate } 15800Sstevel@tonic-gate 15810Sstevel@tonic-gate if (strncmp(zone, "SUNW", 4) == 0) { 15820Sstevel@tonic-gate zerror(gettext("%s operation is invalid for zones starting " 15830Sstevel@tonic-gate "with SUNW."), cmd_to_str(cmd_num)); 15840Sstevel@tonic-gate return (Z_ERR); 15850Sstevel@tonic-gate } 15860Sstevel@tonic-gate 1587766Scarlsonj if (!zonecfg_in_alt_root()) { 1588766Scarlsonj zent = lookup_running_zone(zone); 1589766Scarlsonj } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) { 1590766Scarlsonj zent = NULL; 1591766Scarlsonj } else { 1592766Scarlsonj if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), 1593766Scarlsonj kernzone, sizeof (kernzone)) == 0) 1594766Scarlsonj zent = lookup_running_zone(kernzone); 1595766Scarlsonj else 1596766Scarlsonj zent = NULL; 1597766Scarlsonj zonecfg_close_scratch(fp); 1598766Scarlsonj } 1599766Scarlsonj 16000Sstevel@tonic-gate /* 16010Sstevel@tonic-gate * Look up from the kernel for 'running' zones. 16020Sstevel@tonic-gate */ 16032712Snn35248 if (running && !force) { 16040Sstevel@tonic-gate if (zent == NULL) { 16050Sstevel@tonic-gate zerror(gettext("not running")); 16060Sstevel@tonic-gate return (Z_ERR); 16070Sstevel@tonic-gate } 16080Sstevel@tonic-gate } else { 16090Sstevel@tonic-gate int err; 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate if (unsafe_when_running && zent != NULL) { 16120Sstevel@tonic-gate /* check whether the zone is ready or running */ 16130Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, 16140Sstevel@tonic-gate &zent->zstate_num)) != Z_OK) { 16150Sstevel@tonic-gate errno = err; 16160Sstevel@tonic-gate zperror2(zent->zname, 16170Sstevel@tonic-gate gettext("could not get state")); 16180Sstevel@tonic-gate /* can't tell, so hedge */ 16190Sstevel@tonic-gate zent->zstate_str = "ready/running"; 16200Sstevel@tonic-gate } else { 16210Sstevel@tonic-gate zent->zstate_str = 16220Sstevel@tonic-gate zone_state_str(zent->zstate_num); 16230Sstevel@tonic-gate } 16240Sstevel@tonic-gate zerror(gettext("%s operation is invalid for %s zones."), 16250Sstevel@tonic-gate cmd_to_str(cmd_num), zent->zstate_str); 16260Sstevel@tonic-gate return (Z_ERR); 16270Sstevel@tonic-gate } 16280Sstevel@tonic-gate if ((err = zone_get_state(zone, &state)) != Z_OK) { 16290Sstevel@tonic-gate errno = err; 16300Sstevel@tonic-gate zperror2(zone, gettext("could not get state")); 16310Sstevel@tonic-gate return (Z_ERR); 16320Sstevel@tonic-gate } 16330Sstevel@tonic-gate switch (cmd_num) { 16340Sstevel@tonic-gate case CMD_UNINSTALL: 16350Sstevel@tonic-gate if (state == ZONE_STATE_CONFIGURED) { 16360Sstevel@tonic-gate zerror(gettext("is already in state '%s'."), 16370Sstevel@tonic-gate zone_state_str(ZONE_STATE_CONFIGURED)); 16380Sstevel@tonic-gate return (Z_ERR); 16390Sstevel@tonic-gate } 16400Sstevel@tonic-gate break; 16411507Sgjelinek case CMD_ATTACH: 16428759Sgerald.jelinek@sun.com if (state == ZONE_STATE_INSTALLED) { 16438759Sgerald.jelinek@sun.com zerror(gettext("is already %s."), 16448759Sgerald.jelinek@sun.com zone_state_str(ZONE_STATE_INSTALLED)); 16458759Sgerald.jelinek@sun.com return (Z_ERR); 16468759Sgerald.jelinek@sun.com } else if (state == ZONE_STATE_INCOMPLETE && !force) { 16478759Sgerald.jelinek@sun.com zerror(gettext("zone is %s; %s required."), 16488759Sgerald.jelinek@sun.com zone_state_str(ZONE_STATE_INCOMPLETE), 16498759Sgerald.jelinek@sun.com cmd_to_str(CMD_UNINSTALL)); 16508759Sgerald.jelinek@sun.com return (Z_ERR); 16518759Sgerald.jelinek@sun.com } 16528759Sgerald.jelinek@sun.com break; 16531300Sgjelinek case CMD_CLONE: 16540Sstevel@tonic-gate case CMD_INSTALL: 16550Sstevel@tonic-gate if (state == ZONE_STATE_INSTALLED) { 16560Sstevel@tonic-gate zerror(gettext("is already %s."), 16570Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED)); 16580Sstevel@tonic-gate return (Z_ERR); 16590Sstevel@tonic-gate } else if (state == ZONE_STATE_INCOMPLETE) { 16600Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 16610Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 16620Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 16630Sstevel@tonic-gate return (Z_ERR); 16640Sstevel@tonic-gate } 16650Sstevel@tonic-gate break; 16661507Sgjelinek case CMD_DETACH: 16671300Sgjelinek case CMD_MOVE: 16680Sstevel@tonic-gate case CMD_READY: 16690Sstevel@tonic-gate case CMD_BOOT: 1670766Scarlsonj case CMD_MOUNT: 16712303Scarlsonj case CMD_MARK: 16722712Snn35248 if ((cmd_num == CMD_BOOT || cmd_num == CMD_MOUNT) && 16732712Snn35248 force) 16742712Snn35248 min_state = ZONE_STATE_INCOMPLETE; 16758759Sgerald.jelinek@sun.com else if (cmd_num == CMD_MARK) 16768759Sgerald.jelinek@sun.com min_state = ZONE_STATE_CONFIGURED; 16772712Snn35248 else 16782712Snn35248 min_state = ZONE_STATE_INSTALLED; 16792712Snn35248 16802712Snn35248 if (state < min_state) { 16810Sstevel@tonic-gate zerror(gettext("must be %s before %s."), 16822712Snn35248 zone_state_str(min_state), 16830Sstevel@tonic-gate cmd_to_str(cmd_num)); 16840Sstevel@tonic-gate return (Z_ERR); 16850Sstevel@tonic-gate } 16860Sstevel@tonic-gate break; 16870Sstevel@tonic-gate case CMD_VERIFY: 16880Sstevel@tonic-gate if (state == ZONE_STATE_INCOMPLETE) { 16890Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 16900Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 16910Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 16920Sstevel@tonic-gate return (Z_ERR); 16930Sstevel@tonic-gate } 16940Sstevel@tonic-gate break; 1695766Scarlsonj case CMD_UNMOUNT: 1696766Scarlsonj if (state != ZONE_STATE_MOUNTED) { 1697766Scarlsonj zerror(gettext("must be %s before %s."), 1698766Scarlsonj zone_state_str(ZONE_STATE_MOUNTED), 1699766Scarlsonj cmd_to_str(cmd_num)); 1700766Scarlsonj return (Z_ERR); 1701766Scarlsonj } 1702766Scarlsonj break; 170310718SJordan.Vaughan@Sun.com case CMD_SYSBOOT: 170410718SJordan.Vaughan@Sun.com if (state != ZONE_STATE_INSTALLED) { 170510718SJordan.Vaughan@Sun.com zerror(gettext("%s operation is invalid for %s " 170610718SJordan.Vaughan@Sun.com "zones."), cmd_to_str(cmd_num), 170710718SJordan.Vaughan@Sun.com zone_state_str(state)); 170810718SJordan.Vaughan@Sun.com return (Z_ERR); 170910718SJordan.Vaughan@Sun.com } 171010718SJordan.Vaughan@Sun.com break; 17110Sstevel@tonic-gate } 17120Sstevel@tonic-gate } 17130Sstevel@tonic-gate return (Z_OK); 17140Sstevel@tonic-gate } 17150Sstevel@tonic-gate 17160Sstevel@tonic-gate static int 17170Sstevel@tonic-gate halt_func(int argc, char *argv[]) 17180Sstevel@tonic-gate { 17190Sstevel@tonic-gate zone_cmd_arg_t zarg; 17200Sstevel@tonic-gate int arg; 17210Sstevel@tonic-gate 1722766Scarlsonj if (zonecfg_in_alt_root()) { 1723766Scarlsonj zerror(gettext("cannot halt zone in alternate root")); 1724766Scarlsonj return (Z_ERR); 1725766Scarlsonj } 1726766Scarlsonj 17270Sstevel@tonic-gate optind = 0; 17280Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17290Sstevel@tonic-gate switch (arg) { 17300Sstevel@tonic-gate case '?': 17310Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17320Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 17330Sstevel@tonic-gate default: 17340Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17350Sstevel@tonic-gate return (Z_USAGE); 17360Sstevel@tonic-gate } 17370Sstevel@tonic-gate } 17380Sstevel@tonic-gate if (argc > optind) { 17390Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17400Sstevel@tonic-gate return (Z_USAGE); 17410Sstevel@tonic-gate } 17420Sstevel@tonic-gate /* 17430Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 17440Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 17450Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 17460Sstevel@tonic-gate */ 17472712Snn35248 if (sanity_check(target_zone, CMD_HALT, B_FALSE, B_FALSE, B_FALSE) 17482712Snn35248 != Z_OK) 17490Sstevel@tonic-gate return (Z_ERR); 17500Sstevel@tonic-gate 17513339Szt129084 /* 17523339Szt129084 * Invoke brand-specific handler. 17533339Szt129084 */ 17543339Szt129084 if (invoke_brand_handler(CMD_HALT, argv) != Z_OK) 17553339Szt129084 return (Z_ERR); 17563339Szt129084 17570Sstevel@tonic-gate zarg.cmd = Z_HALT; 17587089Sgjelinek return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, 17597089Sgjelinek B_TRUE) == 0) ? Z_OK : Z_ERR); 17600Sstevel@tonic-gate } 17610Sstevel@tonic-gate 17620Sstevel@tonic-gate static int 17630Sstevel@tonic-gate reboot_func(int argc, char *argv[]) 17640Sstevel@tonic-gate { 17650Sstevel@tonic-gate zone_cmd_arg_t zarg; 17660Sstevel@tonic-gate int arg; 17670Sstevel@tonic-gate 1768766Scarlsonj if (zonecfg_in_alt_root()) { 1769766Scarlsonj zerror(gettext("cannot reboot zone in alternate root")); 1770766Scarlsonj return (Z_ERR); 1771766Scarlsonj } 1772766Scarlsonj 17730Sstevel@tonic-gate optind = 0; 17740Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17750Sstevel@tonic-gate switch (arg) { 17760Sstevel@tonic-gate case '?': 17770Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17780Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 17790Sstevel@tonic-gate default: 17800Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17810Sstevel@tonic-gate return (Z_USAGE); 17820Sstevel@tonic-gate } 17830Sstevel@tonic-gate } 17842267Sdp 17852267Sdp zarg.bootbuf[0] = '\0'; 17862267Sdp for (; optind < argc; optind++) { 17872267Sdp if (strlcat(zarg.bootbuf, argv[optind], 17882267Sdp sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) { 17892267Sdp zerror(gettext("Boot argument list too long")); 17902267Sdp return (Z_ERR); 17912267Sdp } 17922267Sdp if (optind < argc - 1) 17932267Sdp if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >= 17942267Sdp sizeof (zarg.bootbuf)) { 17952267Sdp zerror(gettext("Boot argument list too long")); 17962267Sdp return (Z_ERR); 17972267Sdp } 17982267Sdp } 17992267Sdp 18002267Sdp 18010Sstevel@tonic-gate /* 18020Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 18030Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 18040Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 18050Sstevel@tonic-gate */ 18062712Snn35248 if (sanity_check(target_zone, CMD_REBOOT, B_TRUE, B_FALSE, B_FALSE) 18072712Snn35248 != Z_OK) 18080Sstevel@tonic-gate return (Z_ERR); 18093339Szt129084 if (verify_details(CMD_REBOOT, argv) != Z_OK) 1810823Sgjelinek return (Z_ERR); 18110Sstevel@tonic-gate 18120Sstevel@tonic-gate zarg.cmd = Z_REBOOT; 18137089Sgjelinek return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0) 18147089Sgjelinek ? Z_OK : Z_ERR); 18157089Sgjelinek } 18167089Sgjelinek 18177089Sgjelinek static int 18187089Sgjelinek get_hook(brand_handle_t bh, char *cmd, size_t len, int (*bp)(brand_handle_t, 18197089Sgjelinek const char *, const char *, char *, size_t), char *zonename, char *zonepath) 18207089Sgjelinek { 18217089Sgjelinek if (strlcpy(cmd, EXEC_PREFIX, len) >= len) 18227089Sgjelinek return (Z_ERR); 18237089Sgjelinek 18247089Sgjelinek if (bp(bh, zonename, zonepath, cmd + EXEC_LEN, len - EXEC_LEN) != 0) 18257089Sgjelinek return (Z_ERR); 18267089Sgjelinek 18277089Sgjelinek if (strlen(cmd) <= EXEC_LEN) 18287089Sgjelinek cmd[0] = '\0'; 18297089Sgjelinek 18307089Sgjelinek return (Z_OK); 18310Sstevel@tonic-gate } 18320Sstevel@tonic-gate 18330Sstevel@tonic-gate static int 18343339Szt129084 verify_brand(zone_dochandle_t handle, int cmd_num, char *argv[]) 18352712Snn35248 { 18362712Snn35248 char cmdbuf[MAXPATHLEN]; 18372712Snn35248 int err; 18382712Snn35248 char zonepath[MAXPATHLEN]; 18392727Sedp brand_handle_t bh = NULL; 18403339Szt129084 int status, i; 18412712Snn35248 18422712Snn35248 /* 18432712Snn35248 * Fetch the verify command from the brand configuration. 18442712Snn35248 * "exec" the command so that the returned status is that of 18452712Snn35248 * the command and not the shell. 18462712Snn35248 */ 18477089Sgjelinek if (handle == NULL) { 18487089Sgjelinek (void) strlcpy(zonepath, "-", sizeof (zonepath)); 18497089Sgjelinek } else if ((err = zonecfg_get_zonepath(handle, zonepath, 18507089Sgjelinek sizeof (zonepath))) != Z_OK) { 18512712Snn35248 errno = err; 18523339Szt129084 zperror(cmd_to_str(cmd_num), B_TRUE); 18532712Snn35248 return (Z_ERR); 18542712Snn35248 } 18552727Sedp if ((bh = brand_open(target_brand)) == NULL) { 18562712Snn35248 zerror(gettext("missing or invalid brand")); 18572712Snn35248 return (Z_ERR); 18582712Snn35248 } 18592712Snn35248 18602712Snn35248 /* 18612712Snn35248 * If the brand has its own verification routine, execute it now. 18623339Szt129084 * The verification routine validates the intended zoneadm 18633339Szt129084 * operation for the specific brand. The zoneadm subcommand and 18643339Szt129084 * all its arguments are passed to the routine. 18652712Snn35248 */ 18667089Sgjelinek err = get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_verify_adm, 18677089Sgjelinek target_zone, zonepath); 18682727Sedp brand_close(bh); 18697089Sgjelinek if (err != Z_OK) 18703339Szt129084 return (Z_BRAND_ERROR); 18717089Sgjelinek if (cmdbuf[0] == '\0') 18723339Szt129084 return (Z_OK); 18733339Szt129084 18743339Szt129084 if (strlcat(cmdbuf, cmd_to_str(cmd_num), 18753339Szt129084 sizeof (cmdbuf)) >= sizeof (cmdbuf)) 18763339Szt129084 return (Z_ERR); 18773339Szt129084 18783339Szt129084 /* Build the argv string */ 18793339Szt129084 i = 0; 18803339Szt129084 while (argv[i] != NULL) { 18813339Szt129084 if ((strlcat(cmdbuf, " ", 18823339Szt129084 sizeof (cmdbuf)) >= sizeof (cmdbuf)) || 18833339Szt129084 (strlcat(cmdbuf, argv[i++], 18843339Szt129084 sizeof (cmdbuf)) >= sizeof (cmdbuf))) 18853339Szt129084 return (Z_ERR); 18863339Szt129084 } 18873339Szt129084 18889310Sgerald.jelinek@sun.com status = do_subproc(cmdbuf); 18893339Szt129084 err = subproc_status(gettext("brand-specific verification"), 18903339Szt129084 status, B_FALSE); 18913339Szt129084 18923339Szt129084 return ((err == ZONE_SUBPROC_OK) ? Z_OK : Z_BRAND_ERROR); 18932712Snn35248 } 18942712Snn35248 18952712Snn35248 static int 18960Sstevel@tonic-gate verify_rctls(zone_dochandle_t handle) 18970Sstevel@tonic-gate { 18980Sstevel@tonic-gate struct zone_rctltab rctltab; 18990Sstevel@tonic-gate size_t rbs = rctlblk_size(); 19000Sstevel@tonic-gate rctlblk_t *rctlblk; 19010Sstevel@tonic-gate int error = Z_INVAL; 19020Sstevel@tonic-gate 19030Sstevel@tonic-gate if ((rctlblk = malloc(rbs)) == NULL) { 19040Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rbs, 19050Sstevel@tonic-gate strerror(errno)); 19060Sstevel@tonic-gate return (Z_NOMEM); 19070Sstevel@tonic-gate } 19080Sstevel@tonic-gate 19090Sstevel@tonic-gate if (zonecfg_setrctlent(handle) != Z_OK) { 19100Sstevel@tonic-gate zerror(gettext("zonecfg_setrctlent failed")); 19110Sstevel@tonic-gate free(rctlblk); 19120Sstevel@tonic-gate return (error); 19130Sstevel@tonic-gate } 19140Sstevel@tonic-gate 19150Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19160Sstevel@tonic-gate while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { 19170Sstevel@tonic-gate struct zone_rctlvaltab *rctlval; 19180Sstevel@tonic-gate const char *name = rctltab.zone_rctl_name; 19190Sstevel@tonic-gate 19200Sstevel@tonic-gate if (!zonecfg_is_rctl(name)) { 19210Sstevel@tonic-gate zerror(gettext("WARNING: Ignoring unrecognized rctl " 19220Sstevel@tonic-gate "'%s'."), name); 19230Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19240Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19250Sstevel@tonic-gate continue; 19260Sstevel@tonic-gate } 19270Sstevel@tonic-gate 19280Sstevel@tonic-gate for (rctlval = rctltab.zone_rctl_valptr; rctlval != NULL; 19290Sstevel@tonic-gate rctlval = rctlval->zone_rctlval_next) { 19300Sstevel@tonic-gate if (zonecfg_construct_rctlblk(rctlval, rctlblk) 19310Sstevel@tonic-gate != Z_OK) { 19320Sstevel@tonic-gate zerror(gettext("invalid rctl value: " 19330Sstevel@tonic-gate "(priv=%s,limit=%s,action%s)"), 19340Sstevel@tonic-gate rctlval->zone_rctlval_priv, 19350Sstevel@tonic-gate rctlval->zone_rctlval_limit, 19360Sstevel@tonic-gate rctlval->zone_rctlval_action); 19370Sstevel@tonic-gate goto out; 19380Sstevel@tonic-gate } 19390Sstevel@tonic-gate if (!zonecfg_valid_rctl(name, rctlblk)) { 19400Sstevel@tonic-gate zerror(gettext("(priv=%s,limit=%s,action=%s) " 19410Sstevel@tonic-gate "is not a valid value for rctl '%s'"), 19420Sstevel@tonic-gate rctlval->zone_rctlval_priv, 19430Sstevel@tonic-gate rctlval->zone_rctlval_limit, 19440Sstevel@tonic-gate rctlval->zone_rctlval_action, 19450Sstevel@tonic-gate name); 19460Sstevel@tonic-gate goto out; 19470Sstevel@tonic-gate } 19480Sstevel@tonic-gate } 19490Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19500Sstevel@tonic-gate } 19510Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19520Sstevel@tonic-gate error = Z_OK; 19530Sstevel@tonic-gate out: 19540Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19550Sstevel@tonic-gate (void) zonecfg_endrctlent(handle); 19560Sstevel@tonic-gate free(rctlblk); 19570Sstevel@tonic-gate return (error); 19580Sstevel@tonic-gate } 19590Sstevel@tonic-gate 19600Sstevel@tonic-gate static int 19610Sstevel@tonic-gate verify_pool(zone_dochandle_t handle) 19620Sstevel@tonic-gate { 19630Sstevel@tonic-gate char poolname[MAXPATHLEN]; 19640Sstevel@tonic-gate pool_conf_t *poolconf; 19650Sstevel@tonic-gate pool_t *pool; 19660Sstevel@tonic-gate int status; 19670Sstevel@tonic-gate int error; 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate /* 19700Sstevel@tonic-gate * This ends up being very similar to the check done in zoneadmd. 19710Sstevel@tonic-gate */ 19720Sstevel@tonic-gate error = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 19730Sstevel@tonic-gate if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) { 19740Sstevel@tonic-gate /* 19750Sstevel@tonic-gate * No pool specified. 19760Sstevel@tonic-gate */ 19770Sstevel@tonic-gate return (0); 19780Sstevel@tonic-gate } 19790Sstevel@tonic-gate if (error != Z_OK) { 19800Sstevel@tonic-gate zperror(gettext("Unable to retrieve pool name from " 19810Sstevel@tonic-gate "configuration"), B_TRUE); 19820Sstevel@tonic-gate return (error); 19830Sstevel@tonic-gate } 19840Sstevel@tonic-gate /* 19850Sstevel@tonic-gate * Don't do anything if pools aren't enabled. 19860Sstevel@tonic-gate */ 19870Sstevel@tonic-gate if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) { 19880Sstevel@tonic-gate zerror(gettext("WARNING: pools facility not active; " 19890Sstevel@tonic-gate "zone will not be bound to pool '%s'."), poolname); 19900Sstevel@tonic-gate return (Z_OK); 19910Sstevel@tonic-gate } 19920Sstevel@tonic-gate /* 19930Sstevel@tonic-gate * Try to provide a sane error message if the requested pool doesn't 19940Sstevel@tonic-gate * exist. It isn't clear that pools-related failures should 19950Sstevel@tonic-gate * necessarily translate to a failure to verify the zone configuration, 19960Sstevel@tonic-gate * hence they are not considered errors. 19970Sstevel@tonic-gate */ 19980Sstevel@tonic-gate if ((poolconf = pool_conf_alloc()) == NULL) { 19990Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_alloc failed; " 20000Sstevel@tonic-gate "using default pool")); 20010Sstevel@tonic-gate return (Z_OK); 20020Sstevel@tonic-gate } 20030Sstevel@tonic-gate if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) != 20040Sstevel@tonic-gate PO_SUCCESS) { 20050Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_open failed; " 20060Sstevel@tonic-gate "using default pool")); 20070Sstevel@tonic-gate pool_conf_free(poolconf); 20080Sstevel@tonic-gate return (Z_OK); 20090Sstevel@tonic-gate } 20100Sstevel@tonic-gate pool = pool_get_pool(poolconf, poolname); 20110Sstevel@tonic-gate (void) pool_conf_close(poolconf); 20120Sstevel@tonic-gate pool_conf_free(poolconf); 20130Sstevel@tonic-gate if (pool == NULL) { 20140Sstevel@tonic-gate zerror(gettext("WARNING: pool '%s' not found. " 20150Sstevel@tonic-gate "using default pool"), poolname); 20160Sstevel@tonic-gate } 20170Sstevel@tonic-gate 20180Sstevel@tonic-gate return (Z_OK); 20190Sstevel@tonic-gate } 20200Sstevel@tonic-gate 20210Sstevel@tonic-gate static int 2022823Sgjelinek verify_ipd(zone_dochandle_t handle) 2023823Sgjelinek { 2024823Sgjelinek int return_code = Z_OK; 2025823Sgjelinek struct zone_fstab fstab; 2026823Sgjelinek struct stat st; 2027823Sgjelinek char specdir[MAXPATHLEN]; 2028823Sgjelinek 2029823Sgjelinek if (zonecfg_setipdent(handle) != Z_OK) { 2030924Sgjelinek /* 2031924Sgjelinek * TRANSLATION_NOTE 2032924Sgjelinek * inherit-pkg-dirs is a literal that should not be translated. 2033924Sgjelinek */ 2034924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 2035823Sgjelinek "inherit-pkg-dirs: unable to enumerate mounts\n")); 2036823Sgjelinek return (Z_ERR); 2037823Sgjelinek } 2038823Sgjelinek while (zonecfg_getipdent(handle, &fstab) == Z_OK) { 2039823Sgjelinek /* 2040823Sgjelinek * Verify fs_dir exists. 2041823Sgjelinek */ 2042823Sgjelinek (void) snprintf(specdir, sizeof (specdir), "%s%s", 2043823Sgjelinek zonecfg_get_root(), fstab.zone_fs_dir); 2044823Sgjelinek if (stat(specdir, &st) != 0) { 2045924Sgjelinek /* 2046924Sgjelinek * TRANSLATION_NOTE 2047924Sgjelinek * inherit-pkg-dir is a literal that should not be 2048924Sgjelinek * translated. 2049924Sgjelinek */ 2050924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 2051823Sgjelinek "inherit-pkg-dir %s: %s\n"), 2052823Sgjelinek fstab.zone_fs_dir, strerror(errno)); 2053823Sgjelinek return_code = Z_ERR; 2054823Sgjelinek } 2055823Sgjelinek if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 2056924Sgjelinek /* 2057924Sgjelinek * TRANSLATION_NOTE 2058924Sgjelinek * inherit-pkg-dir and NFS are literals that should 2059924Sgjelinek * not be translated. 2060924Sgjelinek */ 2061823Sgjelinek (void) fprintf(stderr, gettext("cannot verify " 20621867Sgjelinek "inherit-pkg-dir %s: NFS mounted file system.\n" 20631867Sgjelinek "\tA local file system must be used.\n"), 2064823Sgjelinek fstab.zone_fs_dir); 2065823Sgjelinek return_code = Z_ERR; 2066823Sgjelinek } 2067823Sgjelinek } 2068823Sgjelinek (void) zonecfg_endipdent(handle); 2069823Sgjelinek 2070823Sgjelinek return (return_code); 2071823Sgjelinek } 2072823Sgjelinek 20731393Slling /* 20741867Sgjelinek * Verify that the special device/file system exists and is valid. 20751393Slling */ 20761393Slling static int 20771393Slling verify_fs_special(struct zone_fstab *fstab) 20781393Slling { 20796734Sjohnlev struct stat64 st; 20801393Slling 20812971Sgjelinek /* 20822971Sgjelinek * This validation is really intended for standard zone administration. 20832971Sgjelinek * If we are in a mini-root or some other upgrade situation where 20842971Sgjelinek * we are using the scratch zone, just by-pass this. 20852971Sgjelinek */ 20862971Sgjelinek if (zonecfg_in_alt_root()) 20872971Sgjelinek return (Z_OK); 20882971Sgjelinek 20891393Slling if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) 20901393Slling return (verify_fs_zfs(fstab)); 20911393Slling 20926734Sjohnlev if (stat64(fstab->zone_fs_special, &st) != 0) { 20931397Slling (void) fprintf(stderr, gettext("could not verify fs " 20941393Slling "%s: could not access %s: %s\n"), fstab->zone_fs_dir, 20951393Slling fstab->zone_fs_special, strerror(errno)); 20961393Slling return (Z_ERR); 20971393Slling } 20981393Slling 20991393Slling if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 21001393Slling /* 21011393Slling * TRANSLATION_NOTE 21021393Slling * fs and NFS are literals that should 21031393Slling * not be translated. 21041393Slling */ 21051393Slling (void) fprintf(stderr, gettext("cannot verify " 21061867Sgjelinek "fs %s: NFS mounted file system.\n" 21071867Sgjelinek "\tA local file system must be used.\n"), 21081393Slling fstab->zone_fs_special); 21091393Slling return (Z_ERR); 21101393Slling } 21111393Slling 21121393Slling return (Z_OK); 21131393Slling } 21141393Slling 2115823Sgjelinek static int 21166734Sjohnlev isregfile(const char *path) 21176734Sjohnlev { 21186734Sjohnlev struct stat64 st; 21196734Sjohnlev 21206734Sjohnlev if (stat64(path, &st) == -1) 21216734Sjohnlev return (-1); 21226734Sjohnlev 21236734Sjohnlev return (S_ISREG(st.st_mode)); 21246734Sjohnlev } 21256734Sjohnlev 21266734Sjohnlev static int 21270Sstevel@tonic-gate verify_filesystems(zone_dochandle_t handle) 21280Sstevel@tonic-gate { 21290Sstevel@tonic-gate int return_code = Z_OK; 21300Sstevel@tonic-gate struct zone_fstab fstab; 21310Sstevel@tonic-gate char cmdbuf[MAXPATHLEN]; 21320Sstevel@tonic-gate struct stat st; 21330Sstevel@tonic-gate 21340Sstevel@tonic-gate /* 21350Sstevel@tonic-gate * No need to verify inherit-pkg-dir fs types, as their type is 21360Sstevel@tonic-gate * implicitly lofs, which is known. Therefore, the types are only 21371867Sgjelinek * verified for regular file systems below. 21380Sstevel@tonic-gate * 21390Sstevel@tonic-gate * Since the actual mount point is not known until the dependent mounts 21400Sstevel@tonic-gate * are performed, we don't attempt any path validation here: that will 21410Sstevel@tonic-gate * happen later when zoneadmd actually does the mounts. 21420Sstevel@tonic-gate */ 21430Sstevel@tonic-gate if (zonecfg_setfsent(handle) != Z_OK) { 21441867Sgjelinek (void) fprintf(stderr, gettext("could not verify file systems: " 21450Sstevel@tonic-gate "unable to enumerate mounts\n")); 21460Sstevel@tonic-gate return (Z_ERR); 21470Sstevel@tonic-gate } 21480Sstevel@tonic-gate while (zonecfg_getfsent(handle, &fstab) == Z_OK) { 21490Sstevel@tonic-gate if (!zonecfg_valid_fs_type(fstab.zone_fs_type)) { 21500Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21510Sstevel@tonic-gate "type %s is not allowed.\n"), fstab.zone_fs_dir, 21520Sstevel@tonic-gate fstab.zone_fs_type); 21530Sstevel@tonic-gate return_code = Z_ERR; 21540Sstevel@tonic-gate goto next_fs; 21550Sstevel@tonic-gate } 21560Sstevel@tonic-gate /* 21570Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/mount exists. 21580Sstevel@tonic-gate */ 21590Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/mount", 21600Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 21610Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21620Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 21630Sstevel@tonic-gate fstab.zone_fs_type); 21640Sstevel@tonic-gate return_code = Z_ERR; 21650Sstevel@tonic-gate goto next_fs; 21660Sstevel@tonic-gate } 21670Sstevel@tonic-gate if (stat(cmdbuf, &st) != 0) { 2168924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2169924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 21700Sstevel@tonic-gate cmdbuf, strerror(errno)); 21710Sstevel@tonic-gate return_code = Z_ERR; 21720Sstevel@tonic-gate goto next_fs; 21730Sstevel@tonic-gate } 21740Sstevel@tonic-gate if (!S_ISREG(st.st_mode)) { 2175924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2176924Sgjelinek "%s: %s is not a regular file\n"), 2177924Sgjelinek fstab.zone_fs_dir, cmdbuf); 21780Sstevel@tonic-gate return_code = Z_ERR; 21790Sstevel@tonic-gate goto next_fs; 21800Sstevel@tonic-gate } 21810Sstevel@tonic-gate /* 21826734Sjohnlev * If zone_fs_raw is set, verify that there's an fsck 21836734Sjohnlev * binary for it. If zone_fs_raw is not set, and it's 21846734Sjohnlev * not a regular file (lofi mount), and there's an fsck 21856734Sjohnlev * binary for it, complain. 21860Sstevel@tonic-gate */ 21870Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/fsck", 21880Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 21890Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21900Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 21910Sstevel@tonic-gate fstab.zone_fs_type); 21920Sstevel@tonic-gate return_code = Z_ERR; 21930Sstevel@tonic-gate goto next_fs; 21940Sstevel@tonic-gate } 21950Sstevel@tonic-gate if (fstab.zone_fs_raw[0] != '\0' && 21960Sstevel@tonic-gate (stat(cmdbuf, &st) != 0 || !S_ISREG(st.st_mode))) { 21970Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21980Sstevel@tonic-gate "'raw' device specified but " 21990Sstevel@tonic-gate "no fsck executable exists for %s\n"), 22000Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 22010Sstevel@tonic-gate return_code = Z_ERR; 22020Sstevel@tonic-gate goto next_fs; 22036734Sjohnlev } else if (fstab.zone_fs_raw[0] == '\0' && 22046734Sjohnlev stat(cmdbuf, &st) == 0 && 22056734Sjohnlev isregfile(fstab.zone_fs_special) != 1) { 22066734Sjohnlev (void) fprintf(stderr, gettext("could not verify fs " 22076734Sjohnlev "%s: must specify 'raw' device for %s " 22086734Sjohnlev "file systems\n"), 22096734Sjohnlev fstab.zone_fs_dir, fstab.zone_fs_type); 22106734Sjohnlev return_code = Z_ERR; 22116734Sjohnlev goto next_fs; 22120Sstevel@tonic-gate } 22131393Slling 22141393Slling /* Verify fs_special. */ 22151393Slling if ((return_code = verify_fs_special(&fstab)) != Z_OK) 2216823Sgjelinek goto next_fs; 22171393Slling 22181393Slling /* Verify fs_raw. */ 2219823Sgjelinek if (fstab.zone_fs_raw[0] != '\0' && 2220823Sgjelinek stat(fstab.zone_fs_raw, &st) != 0) { 2221924Sgjelinek /* 2222924Sgjelinek * TRANSLATION_NOTE 2223924Sgjelinek * fs is a literal that should not be translated. 2224924Sgjelinek */ 2225924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2226924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 2227823Sgjelinek fstab.zone_fs_raw, strerror(errno)); 2228823Sgjelinek return_code = Z_ERR; 2229823Sgjelinek goto next_fs; 2230823Sgjelinek } 22310Sstevel@tonic-gate next_fs: 22320Sstevel@tonic-gate zonecfg_free_fs_option_list(fstab.zone_fs_options); 22330Sstevel@tonic-gate } 22340Sstevel@tonic-gate (void) zonecfg_endfsent(handle); 22350Sstevel@tonic-gate 22360Sstevel@tonic-gate return (return_code); 22370Sstevel@tonic-gate } 22380Sstevel@tonic-gate 22390Sstevel@tonic-gate static int 22401645Scomay verify_limitpriv(zone_dochandle_t handle) 22411645Scomay { 22421645Scomay char *privname = NULL; 22431645Scomay int err; 22441645Scomay priv_set_t *privs; 22451645Scomay 22461645Scomay if ((privs = priv_allocset()) == NULL) { 22471645Scomay zperror(gettext("failed to allocate privilege set"), B_FALSE); 22481645Scomay return (Z_NOMEM); 22491645Scomay } 22501645Scomay err = zonecfg_get_privset(handle, privs, &privname); 22511645Scomay switch (err) { 22521645Scomay case Z_OK: 22531645Scomay break; 22541645Scomay case Z_PRIV_PROHIBITED: 22551645Scomay (void) fprintf(stderr, gettext("privilege \"%s\" is not " 22561645Scomay "permitted within the zone's privilege set\n"), privname); 22571645Scomay break; 22581645Scomay case Z_PRIV_REQUIRED: 22591645Scomay (void) fprintf(stderr, gettext("required privilege \"%s\" is " 22601645Scomay "missing from the zone's privilege set\n"), privname); 22611645Scomay break; 22621645Scomay case Z_PRIV_UNKNOWN: 22631645Scomay (void) fprintf(stderr, gettext("unknown privilege \"%s\" " 22641645Scomay "specified in the zone's privilege set\n"), privname); 22651645Scomay break; 22661645Scomay default: 22671645Scomay zperror( 22681645Scomay gettext("failed to determine the zone's privilege set"), 22691645Scomay B_TRUE); 22701645Scomay break; 22711645Scomay } 22721645Scomay free(privname); 22731645Scomay priv_freeset(privs); 22741645Scomay return (err); 22751645Scomay } 22761645Scomay 22771915Sgjelinek static void 22781915Sgjelinek free_local_netifs(int if_cnt, struct net_if **if_list) 22791915Sgjelinek { 22801915Sgjelinek int i; 22811915Sgjelinek 22821915Sgjelinek for (i = 0; i < if_cnt; i++) { 22831915Sgjelinek free(if_list[i]->name); 22841915Sgjelinek free(if_list[i]); 22851915Sgjelinek } 22861915Sgjelinek free(if_list); 22871915Sgjelinek } 22881915Sgjelinek 22891915Sgjelinek /* 22901915Sgjelinek * Get a list of the network interfaces, along with their address families, 22911915Sgjelinek * that are plumbed in the global zone. See if_tcp(7p) for a description 22921915Sgjelinek * of the ioctls used here. 22931915Sgjelinek */ 22941915Sgjelinek static int 22951915Sgjelinek get_local_netifs(int *if_cnt, struct net_if ***if_list) 22961915Sgjelinek { 22971915Sgjelinek int s; 22981915Sgjelinek int i; 22991915Sgjelinek int res = Z_OK; 23001915Sgjelinek int space_needed; 23011915Sgjelinek int cnt = 0; 23021915Sgjelinek struct lifnum if_num; 23031915Sgjelinek struct lifconf if_conf; 23041915Sgjelinek struct lifreq *if_reqp; 23051915Sgjelinek char *if_buf; 23061915Sgjelinek struct net_if **local_ifs = NULL; 23071915Sgjelinek 23081915Sgjelinek *if_cnt = 0; 23091915Sgjelinek *if_list = NULL; 23101915Sgjelinek 23111915Sgjelinek if ((s = socket(SOCKET_AF(AF_INET), SOCK_DGRAM, 0)) < 0) 23121915Sgjelinek return (Z_ERR); 23131915Sgjelinek 23141915Sgjelinek /* 23151915Sgjelinek * Come back here in the unlikely event that the number of interfaces 23161915Sgjelinek * increases between the time we get the count and the time we do the 23171915Sgjelinek * SIOCGLIFCONF ioctl. 23181915Sgjelinek */ 23191915Sgjelinek retry: 23201915Sgjelinek /* Get the number of interfaces. */ 23211915Sgjelinek if_num.lifn_family = AF_UNSPEC; 23221915Sgjelinek if_num.lifn_flags = LIFC_NOXMIT; 23231915Sgjelinek if (ioctl(s, SIOCGLIFNUM, &if_num) < 0) { 23241915Sgjelinek (void) close(s); 23251915Sgjelinek return (Z_ERR); 23261915Sgjelinek } 23271915Sgjelinek 23281915Sgjelinek /* Get the interface configuration list. */ 23291915Sgjelinek space_needed = if_num.lifn_count * sizeof (struct lifreq); 23301915Sgjelinek if ((if_buf = malloc(space_needed)) == NULL) { 23311915Sgjelinek (void) close(s); 23321915Sgjelinek return (Z_ERR); 23331915Sgjelinek } 23341915Sgjelinek if_conf.lifc_family = AF_UNSPEC; 23351915Sgjelinek if_conf.lifc_flags = LIFC_NOXMIT; 23361915Sgjelinek if_conf.lifc_len = space_needed; 23371915Sgjelinek if_conf.lifc_buf = if_buf; 23381915Sgjelinek if (ioctl(s, SIOCGLIFCONF, &if_conf) < 0) { 23391915Sgjelinek free(if_buf); 23401915Sgjelinek /* 23411915Sgjelinek * SIOCGLIFCONF returns EINVAL if the buffer we passed in is 23421915Sgjelinek * too small. In this case go back and get the new if cnt. 23431915Sgjelinek */ 23441915Sgjelinek if (errno == EINVAL) 23451915Sgjelinek goto retry; 23461915Sgjelinek 23471915Sgjelinek (void) close(s); 23481915Sgjelinek return (Z_ERR); 23491915Sgjelinek } 23501915Sgjelinek (void) close(s); 23511915Sgjelinek 23521915Sgjelinek /* Get the name and address family for each interface. */ 23531915Sgjelinek if_reqp = if_conf.lifc_req; 23541915Sgjelinek for (i = 0; i < (if_conf.lifc_len / sizeof (struct lifreq)); i++) { 23551915Sgjelinek struct net_if **p; 23561915Sgjelinek struct lifreq req; 23571915Sgjelinek 23581915Sgjelinek if (strcmp(LOOPBACK_IF, if_reqp->lifr_name) == 0) { 23591915Sgjelinek if_reqp++; 23601915Sgjelinek continue; 23611915Sgjelinek } 23621915Sgjelinek 23631915Sgjelinek if ((s = socket(SOCKET_AF(if_reqp->lifr_addr.ss_family), 23641915Sgjelinek SOCK_DGRAM, 0)) == -1) { 23651915Sgjelinek res = Z_ERR; 23661915Sgjelinek break; 23671915Sgjelinek } 23681915Sgjelinek 23691915Sgjelinek (void) strncpy(req.lifr_name, if_reqp->lifr_name, 23701915Sgjelinek sizeof (req.lifr_name)); 23711915Sgjelinek if (ioctl(s, SIOCGLIFADDR, &req) < 0) { 23721915Sgjelinek (void) close(s); 23731915Sgjelinek if_reqp++; 23741915Sgjelinek continue; 23751915Sgjelinek } 23761915Sgjelinek 23771915Sgjelinek if ((p = (struct net_if **)realloc(local_ifs, 23781915Sgjelinek sizeof (struct net_if *) * (cnt + 1))) == NULL) { 23791915Sgjelinek res = Z_ERR; 23801915Sgjelinek break; 23811915Sgjelinek } 23821915Sgjelinek local_ifs = p; 23831915Sgjelinek 23841915Sgjelinek if ((local_ifs[cnt] = malloc(sizeof (struct net_if))) == NULL) { 23851915Sgjelinek res = Z_ERR; 23861915Sgjelinek break; 23871915Sgjelinek } 23881915Sgjelinek 23891915Sgjelinek if ((local_ifs[cnt]->name = strdup(if_reqp->lifr_name)) 23901915Sgjelinek == NULL) { 23911915Sgjelinek free(local_ifs[cnt]); 23921915Sgjelinek res = Z_ERR; 23931915Sgjelinek break; 23941915Sgjelinek } 23951915Sgjelinek local_ifs[cnt]->af = req.lifr_addr.ss_family; 23961915Sgjelinek cnt++; 23971915Sgjelinek 23981915Sgjelinek (void) close(s); 23991915Sgjelinek if_reqp++; 24001915Sgjelinek } 24011915Sgjelinek 24021915Sgjelinek free(if_buf); 24031915Sgjelinek 24041915Sgjelinek if (res != Z_OK) { 24051915Sgjelinek free_local_netifs(cnt, local_ifs); 24061915Sgjelinek } else { 24071915Sgjelinek *if_cnt = cnt; 24081915Sgjelinek *if_list = local_ifs; 24091915Sgjelinek } 24101915Sgjelinek 24111915Sgjelinek return (res); 24121915Sgjelinek } 24131915Sgjelinek 24141915Sgjelinek static char * 24151915Sgjelinek af2str(int af) 24161915Sgjelinek { 24171915Sgjelinek switch (af) { 24181915Sgjelinek case AF_INET: 24191915Sgjelinek return ("IPv4"); 24201915Sgjelinek case AF_INET6: 24211915Sgjelinek return ("IPv6"); 24221915Sgjelinek default: 24231915Sgjelinek return ("Unknown"); 24241915Sgjelinek } 24251915Sgjelinek } 24261915Sgjelinek 24271915Sgjelinek /* 24281915Sgjelinek * Cross check the network interface name and address family with the 24291915Sgjelinek * interfaces that are set up in the global zone so that we can print the 24301915Sgjelinek * appropriate error message. 24311915Sgjelinek */ 24321915Sgjelinek static void 24331915Sgjelinek print_net_err(char *phys, char *addr, int af, char *msg) 24341915Sgjelinek { 24351915Sgjelinek int i; 24361915Sgjelinek int local_if_cnt = 0; 24371915Sgjelinek struct net_if **local_ifs = NULL; 24381915Sgjelinek boolean_t found_if = B_FALSE; 24391915Sgjelinek boolean_t found_af = B_FALSE; 24401915Sgjelinek 24411915Sgjelinek if (get_local_netifs(&local_if_cnt, &local_ifs) != Z_OK) { 24421915Sgjelinek (void) fprintf(stderr, 24431915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 24441915Sgjelinek "net", "address", addr, "physical", phys, msg); 24451915Sgjelinek return; 24461915Sgjelinek } 24471915Sgjelinek 24481915Sgjelinek for (i = 0; i < local_if_cnt; i++) { 24491915Sgjelinek if (strcmp(phys, local_ifs[i]->name) == 0) { 24501915Sgjelinek found_if = B_TRUE; 24511915Sgjelinek if (af == local_ifs[i]->af) { 24521915Sgjelinek found_af = B_TRUE; 24531915Sgjelinek break; 24541915Sgjelinek } 24551915Sgjelinek } 24561915Sgjelinek } 24571915Sgjelinek 24581915Sgjelinek free_local_netifs(local_if_cnt, local_ifs); 24591915Sgjelinek 24601915Sgjelinek if (!found_if) { 24611915Sgjelinek (void) fprintf(stderr, 24621915Sgjelinek gettext("could not verify %s %s=%s\n\t" 24631915Sgjelinek "network interface %s is not plumbed in the global zone\n"), 24641915Sgjelinek "net", "physical", phys, phys); 24651915Sgjelinek return; 24661915Sgjelinek } 24671915Sgjelinek 24681915Sgjelinek /* 24691915Sgjelinek * Print this error if we were unable to find the address family 24701915Sgjelinek * for this interface. If the af variable is not initialized to 24711915Sgjelinek * to something meaningful by the caller (not AF_UNSPEC) then we 24721915Sgjelinek * also skip this message since it wouldn't be informative. 24731915Sgjelinek */ 24741915Sgjelinek if (!found_af && af != AF_UNSPEC) { 24751915Sgjelinek (void) fprintf(stderr, 24761915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\tthe %s address " 24773448Sdh155122 "family is not configured on this network interface in " 24783448Sdh155122 "the\n\tglobal zone\n"), 24791915Sgjelinek "net", "address", addr, "physical", phys, af2str(af)); 24801915Sgjelinek return; 24811915Sgjelinek } 24821915Sgjelinek 24831915Sgjelinek (void) fprintf(stderr, 24841915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 24851915Sgjelinek "net", "address", addr, "physical", phys, msg); 24861915Sgjelinek } 24871915Sgjelinek 24881645Scomay static int 24893339Szt129084 verify_handle(int cmd_num, zone_dochandle_t handle, char *argv[]) 24900Sstevel@tonic-gate { 24910Sstevel@tonic-gate struct zone_nwiftab nwiftab; 24920Sstevel@tonic-gate int return_code = Z_OK; 24930Sstevel@tonic-gate int err; 2494766Scarlsonj boolean_t in_alt_root; 24953448Sdh155122 zone_iptype_t iptype; 249610616SSebastien.Roy@Sun.COM dladm_handle_t dh; 249710616SSebastien.Roy@Sun.COM dladm_status_t status; 249810616SSebastien.Roy@Sun.COM datalink_id_t linkid; 249910616SSebastien.Roy@Sun.COM char errmsg[DLADM_STRSIZE]; 25000Sstevel@tonic-gate 2501766Scarlsonj in_alt_root = zonecfg_in_alt_root(); 2502766Scarlsonj if (in_alt_root) 2503766Scarlsonj goto no_net; 2504766Scarlsonj 25053448Sdh155122 if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK) { 25063448Sdh155122 errno = err; 25073448Sdh155122 zperror(cmd_to_str(cmd_num), B_TRUE); 25083448Sdh155122 zonecfg_fini_handle(handle); 25093448Sdh155122 return (Z_ERR); 25103448Sdh155122 } 25110Sstevel@tonic-gate if ((err = zonecfg_setnwifent(handle)) != Z_OK) { 25120Sstevel@tonic-gate errno = err; 25130Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 25140Sstevel@tonic-gate zonecfg_fini_handle(handle); 25150Sstevel@tonic-gate return (Z_ERR); 25160Sstevel@tonic-gate } 25170Sstevel@tonic-gate while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { 25180Sstevel@tonic-gate struct lifreq lifr; 25191915Sgjelinek sa_family_t af = AF_UNSPEC; 25203448Sdh155122 char dl_owner_zname[ZONENAME_MAX]; 25213448Sdh155122 zoneid_t dl_owner_zid; 25223448Sdh155122 zoneid_t target_zid; 25233448Sdh155122 int res; 25240Sstevel@tonic-gate 25250Sstevel@tonic-gate /* skip any loopback interfaces */ 25260Sstevel@tonic-gate if (strcmp(nwiftab.zone_nwif_physical, "lo0") == 0) 25270Sstevel@tonic-gate continue; 25283448Sdh155122 switch (iptype) { 25293448Sdh155122 case ZS_SHARED: 25303448Sdh155122 if ((res = zonecfg_valid_net_address( 25313448Sdh155122 nwiftab.zone_nwif_address, &lifr)) != Z_OK) { 25323448Sdh155122 print_net_err(nwiftab.zone_nwif_physical, 25333448Sdh155122 nwiftab.zone_nwif_address, af, 25343448Sdh155122 zonecfg_strerror(res)); 25354350Std153743 return_code = Z_ERR; 25364350Std153743 continue; 25373448Sdh155122 } 25383448Sdh155122 af = lifr.lifr_addr.ss_family; 25393448Sdh155122 if (!zonecfg_ifname_exists(af, 25403448Sdh155122 nwiftab.zone_nwif_physical)) { 25413448Sdh155122 /* 25423448Sdh155122 * The interface failed to come up. We continue 25433448Sdh155122 * on anyway for the sake of consistency: a 25443448Sdh155122 * zone is not shut down if the interface fails 25453448Sdh155122 * any time after boot, nor does the global zone 25463448Sdh155122 * fail to boot if an interface fails. 25473448Sdh155122 */ 25483448Sdh155122 (void) fprintf(stderr, 25493448Sdh155122 gettext("WARNING: skipping network " 25504350Std153743 "interface '%s' which may not be " 25514350Std153743 "present/plumbed in the global " 25524350Std153743 "zone.\n"), 25533448Sdh155122 nwiftab.zone_nwif_physical); 25543448Sdh155122 } 25553448Sdh155122 break; 25563448Sdh155122 case ZS_EXCLUSIVE: 25573448Sdh155122 /* Warning if it exists for either IPv4 or IPv6 */ 25583448Sdh155122 25593448Sdh155122 if (zonecfg_ifname_exists(AF_INET, 25603448Sdh155122 nwiftab.zone_nwif_physical) || 25613448Sdh155122 zonecfg_ifname_exists(AF_INET6, 25623448Sdh155122 nwiftab.zone_nwif_physical)) { 25633448Sdh155122 (void) fprintf(stderr, 25643448Sdh155122 gettext("WARNING: skipping network " 25653448Sdh155122 "interface '%s' which is used in the " 25663448Sdh155122 "global zone.\n"), 25673448Sdh155122 nwiftab.zone_nwif_physical); 25683448Sdh155122 break; 25693448Sdh155122 } 25704456Sss150715 25712611Svp157776 /* 257210616SSebastien.Roy@Sun.COM * Verify that the datalink exists and that it isn't 257310616SSebastien.Roy@Sun.COM * already assigned to a zone. 25743448Sdh155122 */ 257510616SSebastien.Roy@Sun.COM if ((status = dladm_open(&dh)) == DLADM_STATUS_OK) { 257610616SSebastien.Roy@Sun.COM status = dladm_name2info(dh, 257710616SSebastien.Roy@Sun.COM nwiftab.zone_nwif_physical, &linkid, NULL, 257810616SSebastien.Roy@Sun.COM NULL, NULL); 257910616SSebastien.Roy@Sun.COM dladm_close(dh); 258010616SSebastien.Roy@Sun.COM } 258110616SSebastien.Roy@Sun.COM if (status != DLADM_STATUS_OK) { 25823448Sdh155122 (void) fprintf(stderr, 25833448Sdh155122 gettext("WARNING: skipping network " 258410616SSebastien.Roy@Sun.COM "interface '%s': %s\n"), 25854456Sss150715 nwiftab.zone_nwif_physical, 258610616SSebastien.Roy@Sun.COM dladm_status2str(status, errmsg)); 25873448Sdh155122 break; 25883448Sdh155122 } 25893448Sdh155122 dl_owner_zid = ALL_ZONES; 259010616SSebastien.Roy@Sun.COM if (zone_check_datalink(&dl_owner_zid, linkid) != 0) 25913448Sdh155122 break; 25923448Sdh155122 25933448Sdh155122 /* 25943448Sdh155122 * If the zone being verified is 25953448Sdh155122 * running and owns the interface 25963448Sdh155122 */ 25973448Sdh155122 target_zid = getzoneidbyname(target_zone); 25983448Sdh155122 if (target_zid == dl_owner_zid) 25993448Sdh155122 break; 26003448Sdh155122 26013448Sdh155122 /* Zone id match failed, use name to check */ 26023448Sdh155122 if (getzonenamebyid(dl_owner_zid, dl_owner_zname, 26033448Sdh155122 ZONENAME_MAX) < 0) { 26043448Sdh155122 /* No name, show ID instead */ 26053448Sdh155122 (void) snprintf(dl_owner_zname, ZONENAME_MAX, 26063448Sdh155122 "<%d>", dl_owner_zid); 26073448Sdh155122 } else if (strcmp(dl_owner_zname, target_zone) == 0) 26083448Sdh155122 break; 26093448Sdh155122 26103448Sdh155122 /* 26113448Sdh155122 * Note here we only report a warning that 26123448Sdh155122 * the interface is already in use by another 26133448Sdh155122 * running zone, and the verify process just 26143448Sdh155122 * goes on, if the interface is still in use 26153448Sdh155122 * when this zone really boots up, zoneadmd 26163448Sdh155122 * will find it. If the name of the zone which 26173448Sdh155122 * owns this interface cannot be determined, 26183448Sdh155122 * then it is not possible to determine if there 26193448Sdh155122 * is a conflict so just report it as a warning. 26202611Svp157776 */ 26212611Svp157776 (void) fprintf(stderr, 26223448Sdh155122 gettext("WARNING: skipping network interface " 26233448Sdh155122 "'%s' which is used by the non-global zone " 26243448Sdh155122 "'%s'.\n"), nwiftab.zone_nwif_physical, 26253448Sdh155122 dl_owner_zname); 26263448Sdh155122 break; 26270Sstevel@tonic-gate } 26280Sstevel@tonic-gate } 26290Sstevel@tonic-gate (void) zonecfg_endnwifent(handle); 2630766Scarlsonj no_net: 26310Sstevel@tonic-gate 26321931Sgjelinek /* verify that lofs has not been excluded from the kernel */ 26332078Sgjelinek if (!(cmd_num == CMD_DETACH || cmd_num == CMD_ATTACH || 26342078Sgjelinek cmd_num == CMD_MOVE || cmd_num == CMD_CLONE) && 26352078Sgjelinek modctl(MODLOAD, 1, "fs/lofs", NULL) != 0) { 26361931Sgjelinek if (errno == ENXIO) 26371931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 26381931Sgjelinek "lofs(7FS): possibly excluded in /etc/system\n")); 26391931Sgjelinek else 26401931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 26411931Sgjelinek "lofs(7FS): %s\n"), strerror(errno)); 26421931Sgjelinek return_code = Z_ERR; 26431931Sgjelinek } 26441931Sgjelinek 26450Sstevel@tonic-gate if (verify_filesystems(handle) != Z_OK) 26460Sstevel@tonic-gate return_code = Z_ERR; 2647823Sgjelinek if (verify_ipd(handle) != Z_OK) 2648823Sgjelinek return_code = Z_ERR; 2649766Scarlsonj if (!in_alt_root && verify_rctls(handle) != Z_OK) 26500Sstevel@tonic-gate return_code = Z_ERR; 2651766Scarlsonj if (!in_alt_root && verify_pool(handle) != Z_OK) 26520Sstevel@tonic-gate return_code = Z_ERR; 26533339Szt129084 if (!in_alt_root && verify_brand(handle, cmd_num, argv) != Z_OK) 26542712Snn35248 return_code = Z_ERR; 2655789Sahrens if (!in_alt_root && verify_datasets(handle) != Z_OK) 2656789Sahrens return_code = Z_ERR; 26571645Scomay 26581645Scomay /* 26591645Scomay * As the "mount" command is used for patching/upgrading of zones 26601645Scomay * or other maintenance processes, the zone's privilege set is not 26611645Scomay * checked in this case. Instead, the default, safe set of 26621645Scomay * privileges will be used when this zone is created in the 26631645Scomay * kernel. 26641645Scomay */ 26651645Scomay if (!in_alt_root && cmd_num != CMD_MOUNT && 26661645Scomay verify_limitpriv(handle) != Z_OK) 26671645Scomay return_code = Z_ERR; 26682078Sgjelinek 26692078Sgjelinek return (return_code); 26702078Sgjelinek } 26712078Sgjelinek 26722078Sgjelinek static int 26733339Szt129084 verify_details(int cmd_num, char *argv[]) 26742078Sgjelinek { 26752078Sgjelinek zone_dochandle_t handle; 26762078Sgjelinek char zonepath[MAXPATHLEN], checkpath[MAXPATHLEN]; 26772078Sgjelinek int return_code = Z_OK; 26782078Sgjelinek int err; 26792078Sgjelinek 26802078Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 26812078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 26822078Sgjelinek return (Z_ERR); 26832078Sgjelinek } 26842078Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 26852078Sgjelinek errno = err; 26862078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 26872078Sgjelinek zonecfg_fini_handle(handle); 26882078Sgjelinek return (Z_ERR); 26892078Sgjelinek } 26902078Sgjelinek if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) != 26912078Sgjelinek Z_OK) { 26922078Sgjelinek errno = err; 26932078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 26942078Sgjelinek zonecfg_fini_handle(handle); 26952078Sgjelinek return (Z_ERR); 26962078Sgjelinek } 26972078Sgjelinek /* 26982078Sgjelinek * zonecfg_get_zonepath() gets its data from the XML repository. 26992078Sgjelinek * Verify this against the index file, which is checked first by 27002078Sgjelinek * zone_get_zonepath(). If they don't match, bail out. 27012078Sgjelinek */ 27022078Sgjelinek if ((err = zone_get_zonepath(target_zone, checkpath, 27032078Sgjelinek sizeof (checkpath))) != Z_OK) { 27042078Sgjelinek errno = err; 27052078Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 27063716Sgjelinek zonecfg_fini_handle(handle); 27072078Sgjelinek return (Z_ERR); 27082078Sgjelinek } 27092078Sgjelinek if (strcmp(zonepath, checkpath) != 0) { 27102078Sgjelinek /* 27112078Sgjelinek * TRANSLATION_NOTE 27122078Sgjelinek * XML and zonepath are literals that should not be translated. 27132078Sgjelinek */ 27142078Sgjelinek (void) fprintf(stderr, gettext("The XML repository has " 27152078Sgjelinek "zonepath '%s',\nbut the index file has zonepath '%s'.\n" 27162078Sgjelinek "These must match, so fix the incorrect entry.\n"), 27172078Sgjelinek zonepath, checkpath); 27183716Sgjelinek zonecfg_fini_handle(handle); 27192078Sgjelinek return (Z_ERR); 27202078Sgjelinek } 27219128Sgerald.jelinek@sun.com if (cmd_num != CMD_ATTACH && 27229128Sgerald.jelinek@sun.com validate_zonepath(zonepath, cmd_num) != Z_OK) { 27232078Sgjelinek (void) fprintf(stderr, gettext("could not verify zonepath %s " 27242078Sgjelinek "because of the above errors.\n"), zonepath); 27252078Sgjelinek return_code = Z_ERR; 27262078Sgjelinek } 27272078Sgjelinek 27283339Szt129084 if (verify_handle(cmd_num, handle, argv) != Z_OK) 27292078Sgjelinek return_code = Z_ERR; 27302078Sgjelinek 27310Sstevel@tonic-gate zonecfg_fini_handle(handle); 27320Sstevel@tonic-gate if (return_code == Z_ERR) 27330Sstevel@tonic-gate (void) fprintf(stderr, 27340Sstevel@tonic-gate gettext("%s: zone %s failed to verify\n"), 27350Sstevel@tonic-gate execname, target_zone); 27360Sstevel@tonic-gate return (return_code); 27370Sstevel@tonic-gate } 27380Sstevel@tonic-gate 27390Sstevel@tonic-gate static int 27400Sstevel@tonic-gate verify_func(int argc, char *argv[]) 27410Sstevel@tonic-gate { 27420Sstevel@tonic-gate int arg; 27430Sstevel@tonic-gate 27440Sstevel@tonic-gate optind = 0; 27450Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 27460Sstevel@tonic-gate switch (arg) { 27470Sstevel@tonic-gate case '?': 27480Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 27490Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 27500Sstevel@tonic-gate default: 27510Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 27520Sstevel@tonic-gate return (Z_USAGE); 27530Sstevel@tonic-gate } 27540Sstevel@tonic-gate } 27550Sstevel@tonic-gate if (argc > optind) { 27560Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 27570Sstevel@tonic-gate return (Z_USAGE); 27580Sstevel@tonic-gate } 27592712Snn35248 if (sanity_check(target_zone, CMD_VERIFY, B_FALSE, B_FALSE, B_FALSE) 27602712Snn35248 != Z_OK) 27610Sstevel@tonic-gate return (Z_ERR); 27623339Szt129084 return (verify_details(CMD_VERIFY, argv)); 27630Sstevel@tonic-gate } 27640Sstevel@tonic-gate 27652712Snn35248 static int 27667089Sgjelinek addoptions(char *buf, char *argv[], size_t len) 27677089Sgjelinek { 27687089Sgjelinek int i = 0; 27697089Sgjelinek 27707089Sgjelinek if (buf[0] == '\0') 27717089Sgjelinek return (Z_OK); 27727089Sgjelinek 27737089Sgjelinek while (argv[i] != NULL) { 27747089Sgjelinek if (strlcat(buf, " ", len) >= len || 27757089Sgjelinek strlcat(buf, argv[i++], len) >= len) { 27767089Sgjelinek zerror("Command line too long"); 27777089Sgjelinek return (Z_ERR); 27787089Sgjelinek } 27797089Sgjelinek } 27807089Sgjelinek 27817089Sgjelinek return (Z_OK); 27827089Sgjelinek } 27837089Sgjelinek 27847089Sgjelinek static int 27852712Snn35248 addopt(char *buf, int opt, char *optarg, size_t bufsize) 27862712Snn35248 { 27872712Snn35248 char optstring[4]; 27882712Snn35248 27892712Snn35248 if (opt > 0) 27902712Snn35248 (void) sprintf(optstring, " -%c", opt); 27912712Snn35248 else 27922712Snn35248 (void) strcpy(optstring, " "); 27932712Snn35248 27942712Snn35248 if ((strlcat(buf, optstring, bufsize) > bufsize)) 27952712Snn35248 return (Z_ERR); 27967089Sgjelinek 27972712Snn35248 if ((optarg != NULL) && (strlcat(buf, optarg, bufsize) > bufsize)) 27982712Snn35248 return (Z_ERR); 27997089Sgjelinek 28002712Snn35248 return (Z_OK); 28012712Snn35248 } 28020Sstevel@tonic-gate 28037089Sgjelinek /* ARGSUSED */ 28040Sstevel@tonic-gate static int 28050Sstevel@tonic-gate install_func(int argc, char *argv[]) 28060Sstevel@tonic-gate { 28072712Snn35248 char cmdbuf[MAXPATHLEN]; 28084586Sgjelinek char postcmdbuf[MAXPATHLEN]; 28090Sstevel@tonic-gate int lockfd; 28102712Snn35248 int arg, err, subproc_err; 28110Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 28122727Sedp brand_handle_t bh = NULL; 28130Sstevel@tonic-gate int status; 28141867Sgjelinek boolean_t nodataset = B_FALSE; 28154586Sgjelinek boolean_t do_postinstall = B_FALSE; 28167089Sgjelinek boolean_t brand_help = B_FALSE; 28172712Snn35248 char opts[128]; 28182712Snn35248 28192712Snn35248 if (target_zone == NULL) { 28202712Snn35248 sub_usage(SHELP_INSTALL, CMD_INSTALL); 28212712Snn35248 return (Z_USAGE); 28222712Snn35248 } 28230Sstevel@tonic-gate 2824766Scarlsonj if (zonecfg_in_alt_root()) { 2825766Scarlsonj zerror(gettext("cannot install zone in alternate root")); 2826766Scarlsonj return (Z_ERR); 2827766Scarlsonj } 2828766Scarlsonj 28292712Snn35248 if ((err = zone_get_zonepath(target_zone, zonepath, 28302712Snn35248 sizeof (zonepath))) != Z_OK) { 28312712Snn35248 errno = err; 28322712Snn35248 zperror2(target_zone, gettext("could not get zone path")); 28332712Snn35248 return (Z_ERR); 28342712Snn35248 } 28352712Snn35248 28362712Snn35248 /* Fetch the install command from the brand configuration. */ 28372727Sedp if ((bh = brand_open(target_brand)) == NULL) { 28382712Snn35248 zerror(gettext("missing or invalid brand")); 28392712Snn35248 return (Z_ERR); 28402712Snn35248 } 28412712Snn35248 28427089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_install, 28437089Sgjelinek target_zone, zonepath) != Z_OK) { 28442712Snn35248 zerror("invalid brand configuration: missing install resource"); 28452727Sedp brand_close(bh); 28462712Snn35248 return (Z_ERR); 28472712Snn35248 } 28482712Snn35248 28497089Sgjelinek if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postinstall, 28507089Sgjelinek target_zone, zonepath) != Z_OK) { 28514586Sgjelinek zerror("invalid brand configuration: missing postinstall " 28524586Sgjelinek "resource"); 28534586Sgjelinek brand_close(bh); 28544586Sgjelinek return (Z_ERR); 28557089Sgjelinek } 28567089Sgjelinek 28577089Sgjelinek if (postcmdbuf[0] != '\0') 28584586Sgjelinek do_postinstall = B_TRUE; 28594586Sgjelinek 28602712Snn35248 (void) strcpy(opts, "?x:"); 28617089Sgjelinek /* 28627089Sgjelinek * Fetch the list of recognized command-line options from 28637089Sgjelinek * the brand configuration file. 28647089Sgjelinek */ 28657089Sgjelinek if (brand_get_installopts(bh, opts + strlen(opts), 28667089Sgjelinek sizeof (opts) - strlen(opts)) != 0) { 28677089Sgjelinek zerror("invalid brand configuration: missing " 28687089Sgjelinek "install options resource"); 28697089Sgjelinek brand_close(bh); 28707089Sgjelinek return (Z_ERR); 28717089Sgjelinek } 28727089Sgjelinek 28732727Sedp brand_close(bh); 28742712Snn35248 28757089Sgjelinek if (cmdbuf[0] == '\0') { 28767089Sgjelinek zerror("Missing brand install command"); 28777089Sgjelinek return (Z_ERR); 28787089Sgjelinek } 28797089Sgjelinek 28807089Sgjelinek /* Check the argv string for args we handle internally */ 28810Sstevel@tonic-gate optind = 0; 28827089Sgjelinek opterr = 0; 28832712Snn35248 while ((arg = getopt(argc, argv, opts)) != EOF) { 28840Sstevel@tonic-gate switch (arg) { 28850Sstevel@tonic-gate case '?': 28867089Sgjelinek if (optopt == '?') { 28871867Sgjelinek sub_usage(SHELP_INSTALL, CMD_INSTALL); 28887089Sgjelinek brand_help = B_TRUE; 28892712Snn35248 } 28907089Sgjelinek /* Ignore unknown options - may be brand specific. */ 28917089Sgjelinek break; 28927089Sgjelinek case 'x': 28937089Sgjelinek /* Handle this option internally, don't pass to brand */ 28947089Sgjelinek if (strcmp(optarg, "nodataset") == 0) { 28957089Sgjelinek /* Handle this option internally */ 28967089Sgjelinek nodataset = B_TRUE; 28972712Snn35248 } 28987089Sgjelinek continue; 28997089Sgjelinek default: 29007089Sgjelinek /* Ignore unknown options - may be brand specific. */ 29012712Snn35248 break; 29020Sstevel@tonic-gate } 29037089Sgjelinek 29047089Sgjelinek /* 29057089Sgjelinek * Append the option to the command line passed to the 29067089Sgjelinek * brand-specific install and postinstall routines. 29077089Sgjelinek */ 29087089Sgjelinek if (addopt(cmdbuf, optopt, optarg, sizeof (cmdbuf)) != Z_OK) { 29097089Sgjelinek zerror("Install command line too long"); 29107089Sgjelinek return (Z_ERR); 29117089Sgjelinek } 29127089Sgjelinek if (addopt(postcmdbuf, optopt, optarg, sizeof (postcmdbuf)) 29137089Sgjelinek != Z_OK) { 29147089Sgjelinek zerror("Post-Install command line too long"); 29157089Sgjelinek return (Z_ERR); 29167089Sgjelinek } 29177089Sgjelinek } 29187089Sgjelinek 29197089Sgjelinek for (; optind < argc; optind++) { 29207089Sgjelinek if (addopt(cmdbuf, 0, argv[optind], sizeof (cmdbuf)) != Z_OK) { 29217089Sgjelinek zerror("Install command line too long"); 29227089Sgjelinek return (Z_ERR); 29237089Sgjelinek } 29247089Sgjelinek 29257089Sgjelinek if (addopt(postcmdbuf, 0, argv[optind], sizeof (postcmdbuf)) 29267089Sgjelinek != Z_OK) { 29277089Sgjelinek zerror("Post-Install command line too long"); 29287089Sgjelinek return (Z_ERR); 29292712Snn35248 } 29302712Snn35248 } 29312712Snn35248 29327089Sgjelinek if (!brand_help) { 29337089Sgjelinek if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE, 29347089Sgjelinek B_FALSE) != Z_OK) 29357089Sgjelinek return (Z_ERR); 29367089Sgjelinek if (verify_details(CMD_INSTALL, argv) != Z_OK) 29377089Sgjelinek return (Z_ERR); 29387089Sgjelinek 29397089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 29407089Sgjelinek zerror(gettext("another %s may have an operation in " 29417089Sgjelinek "progress."), "zoneadm"); 29427089Sgjelinek return (Z_ERR); 29437089Sgjelinek } 29447089Sgjelinek err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 29457089Sgjelinek if (err != Z_OK) { 29467089Sgjelinek errno = err; 29477089Sgjelinek zperror2(target_zone, gettext("could not set state")); 29487089Sgjelinek goto done; 29497089Sgjelinek } 29507089Sgjelinek 29517089Sgjelinek if (!nodataset) 29527089Sgjelinek create_zfs_zonepath(zonepath); 29537089Sgjelinek } 29547089Sgjelinek 29559310Sgerald.jelinek@sun.com status = do_subproc(cmdbuf); 29562712Snn35248 if ((subproc_err = 29572712Snn35248 subproc_status(gettext("brand-specific installation"), status, 29582712Snn35248 B_FALSE)) != ZONE_SUBPROC_OK) { 29597089Sgjelinek if (subproc_err == ZONE_SUBPROC_USAGE && !brand_help) { 29607089Sgjelinek sub_usage(SHELP_INSTALL, CMD_INSTALL); 29617089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 29627089Sgjelinek return (Z_ERR); 29637089Sgjelinek } 29642712Snn35248 err = Z_ERR; 29650Sstevel@tonic-gate goto done; 29662712Snn35248 } 29670Sstevel@tonic-gate 29687089Sgjelinek if (brand_help) 29697089Sgjelinek return (Z_OK); 29707089Sgjelinek 29710Sstevel@tonic-gate if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 29720Sstevel@tonic-gate errno = err; 29730Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 29740Sstevel@tonic-gate goto done; 29750Sstevel@tonic-gate } 29760Sstevel@tonic-gate 29774586Sgjelinek if (do_postinstall) { 29784586Sgjelinek status = do_subproc(postcmdbuf); 29794586Sgjelinek 29804586Sgjelinek if ((subproc_err = 29814586Sgjelinek subproc_status(gettext("brand-specific post-install"), 29824586Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 29834586Sgjelinek err = Z_ERR; 29844586Sgjelinek (void) zone_set_state(target_zone, 29854586Sgjelinek ZONE_STATE_INCOMPLETE); 29864586Sgjelinek } 29874586Sgjelinek } 29884586Sgjelinek 29890Sstevel@tonic-gate done: 29902712Snn35248 /* 29917089Sgjelinek * If the install script exited with ZONE_SUBPROC_NOTCOMPLETE, try to 29927089Sgjelinek * clean up the zone and leave the zone in the CONFIGURED state so that 29937089Sgjelinek * another install can be attempted without requiring an uninstall 29947089Sgjelinek * first. 29952712Snn35248 */ 29967089Sgjelinek if (subproc_err == ZONE_SUBPROC_NOTCOMPLETE) { 29972712Snn35248 if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) { 29982712Snn35248 errno = err; 29992712Snn35248 zperror2(target_zone, 30002712Snn35248 gettext("cleaning up zonepath failed")); 30012712Snn35248 } else if ((err = zone_set_state(target_zone, 30022712Snn35248 ZONE_STATE_CONFIGURED)) != Z_OK) { 30032712Snn35248 errno = err; 30042712Snn35248 zperror2(target_zone, gettext("could not set state")); 30052712Snn35248 } 30062712Snn35248 } 30072712Snn35248 30087089Sgjelinek if (!brand_help) 30097089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 30100Sstevel@tonic-gate return ((err == Z_OK) ? Z_OK : Z_ERR); 30110Sstevel@tonic-gate } 30120Sstevel@tonic-gate 30130Sstevel@tonic-gate /* 30141300Sgjelinek * Check that the inherited pkg dirs are the same for the clone and its source. 30151300Sgjelinek * The easiest way to do that is check that the list of ipds is the same 30161300Sgjelinek * by matching each one against the other. This algorithm should be fine since 30171300Sgjelinek * the list of ipds should not be that long. 30181300Sgjelinek */ 30191300Sgjelinek static int 30201300Sgjelinek valid_ipd_clone(zone_dochandle_t s_handle, char *source_zone, 30211300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 30221300Sgjelinek { 30231300Sgjelinek int err; 30241300Sgjelinek int res = Z_OK; 30251300Sgjelinek int s_cnt = 0; 30261300Sgjelinek int t_cnt = 0; 30271300Sgjelinek struct zone_fstab s_fstab; 30281300Sgjelinek struct zone_fstab t_fstab; 30291300Sgjelinek 30301300Sgjelinek /* 30311300Sgjelinek * First check the source of the clone against the target. 30321300Sgjelinek */ 30331300Sgjelinek if ((err = zonecfg_setipdent(s_handle)) != Z_OK) { 30341300Sgjelinek errno = err; 30351300Sgjelinek zperror2(source_zone, gettext("could not enumerate " 30361300Sgjelinek "inherit-pkg-dirs")); 30371300Sgjelinek return (Z_ERR); 30381300Sgjelinek } 30391300Sgjelinek 30401300Sgjelinek while (zonecfg_getipdent(s_handle, &s_fstab) == Z_OK) { 30411300Sgjelinek boolean_t match = B_FALSE; 30421300Sgjelinek 30431300Sgjelinek s_cnt++; 30441300Sgjelinek 30451300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 30461300Sgjelinek errno = err; 30471300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 30481300Sgjelinek "inherit-pkg-dirs")); 30491300Sgjelinek (void) zonecfg_endipdent(s_handle); 30501300Sgjelinek return (Z_ERR); 30511300Sgjelinek } 30521300Sgjelinek 30531300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) { 30541300Sgjelinek if (strcmp(s_fstab.zone_fs_dir, t_fstab.zone_fs_dir) 30551300Sgjelinek == 0) { 30561300Sgjelinek match = B_TRUE; 30571300Sgjelinek break; 30581300Sgjelinek } 30591300Sgjelinek } 30601300Sgjelinek (void) zonecfg_endipdent(t_handle); 30611300Sgjelinek 30621300Sgjelinek if (!match) { 30631300Sgjelinek (void) fprintf(stderr, gettext("inherit-pkg-dir " 30641300Sgjelinek "'%s' is not configured in zone %s.\n"), 30651300Sgjelinek s_fstab.zone_fs_dir, target_zone); 30661300Sgjelinek res = Z_ERR; 30671300Sgjelinek } 30681300Sgjelinek } 30691300Sgjelinek 30701300Sgjelinek (void) zonecfg_endipdent(s_handle); 30711300Sgjelinek 30721300Sgjelinek /* skip the next check if we already have errors */ 30731300Sgjelinek if (res == Z_ERR) 30741300Sgjelinek return (res); 30751300Sgjelinek 30761300Sgjelinek /* 30771300Sgjelinek * Now check the number of ipds in the target so we can verify 30781300Sgjelinek * that the source is not a subset of the target. 30791300Sgjelinek */ 30801300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 30811300Sgjelinek errno = err; 30821300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 30831300Sgjelinek "inherit-pkg-dirs")); 30841300Sgjelinek return (Z_ERR); 30851300Sgjelinek } 30861300Sgjelinek 30871300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) 30881300Sgjelinek t_cnt++; 30891300Sgjelinek 30901300Sgjelinek (void) zonecfg_endipdent(t_handle); 30911300Sgjelinek 30921300Sgjelinek if (t_cnt != s_cnt) { 30931300Sgjelinek (void) fprintf(stderr, gettext("Zone %s is configured " 30941300Sgjelinek "with inherit-pkg-dirs that are not configured in zone " 30951300Sgjelinek "%s.\n"), target_zone, source_zone); 30961300Sgjelinek res = Z_ERR; 30971300Sgjelinek } 30981300Sgjelinek 30991300Sgjelinek return (res); 31001300Sgjelinek } 31011300Sgjelinek 31021300Sgjelinek static void 31031300Sgjelinek warn_dev_match(zone_dochandle_t s_handle, char *source_zone, 31041300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 31051300Sgjelinek { 31061300Sgjelinek int err; 31071300Sgjelinek struct zone_devtab s_devtab; 31081300Sgjelinek struct zone_devtab t_devtab; 31091300Sgjelinek 31101300Sgjelinek if ((err = zonecfg_setdevent(t_handle)) != Z_OK) { 31111300Sgjelinek errno = err; 31121300Sgjelinek zperror2(target_zone, gettext("could not enumerate devices")); 31131300Sgjelinek return; 31141300Sgjelinek } 31151300Sgjelinek 31161300Sgjelinek while (zonecfg_getdevent(t_handle, &t_devtab) == Z_OK) { 31171300Sgjelinek if ((err = zonecfg_setdevent(s_handle)) != Z_OK) { 31181300Sgjelinek errno = err; 31191300Sgjelinek zperror2(source_zone, 31201300Sgjelinek gettext("could not enumerate devices")); 31211300Sgjelinek (void) zonecfg_enddevent(t_handle); 31221300Sgjelinek return; 31231300Sgjelinek } 31241300Sgjelinek 31251300Sgjelinek while (zonecfg_getdevent(s_handle, &s_devtab) == Z_OK) { 31261300Sgjelinek /* 31271300Sgjelinek * Use fnmatch to catch the case where wildcards 31281300Sgjelinek * were used in one zone and the other has an 31291300Sgjelinek * explicit entry (e.g. /dev/dsk/c0t0d0s6 vs. 31301300Sgjelinek * /dev/\*dsk/c0t0d0s6). 31311300Sgjelinek */ 31321300Sgjelinek if (fnmatch(t_devtab.zone_dev_match, 31331300Sgjelinek s_devtab.zone_dev_match, FNM_PATHNAME) == 0 || 31341300Sgjelinek fnmatch(s_devtab.zone_dev_match, 31351300Sgjelinek t_devtab.zone_dev_match, FNM_PATHNAME) == 0) { 31361300Sgjelinek (void) fprintf(stderr, 31371300Sgjelinek gettext("WARNING: device '%s' " 31381300Sgjelinek "is configured in both zones.\n"), 31391300Sgjelinek t_devtab.zone_dev_match); 31401300Sgjelinek break; 31411300Sgjelinek } 31421300Sgjelinek } 31431300Sgjelinek (void) zonecfg_enddevent(s_handle); 31441300Sgjelinek } 31451300Sgjelinek 31461300Sgjelinek (void) zonecfg_enddevent(t_handle); 31471300Sgjelinek } 31481300Sgjelinek 31491300Sgjelinek /* 31501300Sgjelinek * Check if the specified mount option (opt) is contained within the 31511300Sgjelinek * options string. 31521300Sgjelinek */ 31531300Sgjelinek static boolean_t 31541300Sgjelinek opt_match(char *opt, char *options) 31551300Sgjelinek { 31561300Sgjelinek char *p; 31571300Sgjelinek char *lastp; 31581300Sgjelinek 31591300Sgjelinek if ((p = strtok_r(options, ",", &lastp)) != NULL) { 31601300Sgjelinek if (strcmp(p, opt) == 0) 31611300Sgjelinek return (B_TRUE); 31621300Sgjelinek while ((p = strtok_r(NULL, ",", &lastp)) != NULL) { 31631300Sgjelinek if (strcmp(p, opt) == 0) 31641300Sgjelinek return (B_TRUE); 31651300Sgjelinek } 31661300Sgjelinek } 31671300Sgjelinek 31681300Sgjelinek return (B_FALSE); 31691300Sgjelinek } 31701300Sgjelinek 31711867Sgjelinek #define RW_LOFS "WARNING: read-write lofs file system on '%s' is configured " \ 31721300Sgjelinek "in both zones.\n" 31731300Sgjelinek 31741300Sgjelinek static void 31751300Sgjelinek print_fs_warnings(struct zone_fstab *s_fstab, struct zone_fstab *t_fstab) 31761300Sgjelinek { 31771300Sgjelinek /* 31781300Sgjelinek * It is ok to have shared lofs mounted fs but we want to warn if 31791300Sgjelinek * either is rw since this will effect the other zone. 31801300Sgjelinek */ 31811300Sgjelinek if (strcmp(t_fstab->zone_fs_type, "lofs") == 0) { 31821300Sgjelinek zone_fsopt_t *optp; 31831300Sgjelinek 31841300Sgjelinek /* The default is rw so no options means rw */ 31851300Sgjelinek if (t_fstab->zone_fs_options == NULL || 31861300Sgjelinek s_fstab->zone_fs_options == NULL) { 31871300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 31881300Sgjelinek t_fstab->zone_fs_special); 31891300Sgjelinek return; 31901300Sgjelinek } 31911300Sgjelinek 31921300Sgjelinek for (optp = s_fstab->zone_fs_options; optp != NULL; 31931300Sgjelinek optp = optp->zone_fsopt_next) { 31941300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 31951300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 31961300Sgjelinek s_fstab->zone_fs_special); 31971300Sgjelinek return; 31981300Sgjelinek } 31991300Sgjelinek } 32001300Sgjelinek 32011300Sgjelinek for (optp = t_fstab->zone_fs_options; optp != NULL; 32021300Sgjelinek optp = optp->zone_fsopt_next) { 32031300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 32041300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 32051300Sgjelinek t_fstab->zone_fs_special); 32061300Sgjelinek return; 32071300Sgjelinek } 32081300Sgjelinek } 32091300Sgjelinek 32101300Sgjelinek return; 32111300Sgjelinek } 32121300Sgjelinek 32131300Sgjelinek /* 32141300Sgjelinek * TRANSLATION_NOTE 32151867Sgjelinek * The first variable is the file system type and the second is 32161867Sgjelinek * the file system special device. For example, 32171867Sgjelinek * WARNING: ufs file system on '/dev/dsk/c0t0d0s0' ... 32181300Sgjelinek */ 32191867Sgjelinek (void) fprintf(stderr, gettext("WARNING: %s file system on '%s' " 32201300Sgjelinek "is configured in both zones.\n"), t_fstab->zone_fs_type, 32211300Sgjelinek t_fstab->zone_fs_special); 32221300Sgjelinek } 32231300Sgjelinek 32241300Sgjelinek static void 32251300Sgjelinek warn_fs_match(zone_dochandle_t s_handle, char *source_zone, 32261300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 32271300Sgjelinek { 32281300Sgjelinek int err; 32291300Sgjelinek struct zone_fstab s_fstab; 32301300Sgjelinek struct zone_fstab t_fstab; 32311300Sgjelinek 32321300Sgjelinek if ((err = zonecfg_setfsent(t_handle)) != Z_OK) { 32331300Sgjelinek errno = err; 32341300Sgjelinek zperror2(target_zone, 32351867Sgjelinek gettext("could not enumerate file systems")); 32361300Sgjelinek return; 32371300Sgjelinek } 32381300Sgjelinek 32391300Sgjelinek while (zonecfg_getfsent(t_handle, &t_fstab) == Z_OK) { 32401300Sgjelinek if ((err = zonecfg_setfsent(s_handle)) != Z_OK) { 32411300Sgjelinek errno = err; 32421300Sgjelinek zperror2(source_zone, 32431867Sgjelinek gettext("could not enumerate file systems")); 32441300Sgjelinek (void) zonecfg_endfsent(t_handle); 32451300Sgjelinek return; 32461300Sgjelinek } 32471300Sgjelinek 32481300Sgjelinek while (zonecfg_getfsent(s_handle, &s_fstab) == Z_OK) { 32491300Sgjelinek if (strcmp(t_fstab.zone_fs_special, 32501300Sgjelinek s_fstab.zone_fs_special) == 0) { 32511300Sgjelinek print_fs_warnings(&s_fstab, &t_fstab); 32521300Sgjelinek break; 32531300Sgjelinek } 32541300Sgjelinek } 32551300Sgjelinek (void) zonecfg_endfsent(s_handle); 32561300Sgjelinek } 32571300Sgjelinek 32581300Sgjelinek (void) zonecfg_endfsent(t_handle); 32591300Sgjelinek } 32601300Sgjelinek 32611300Sgjelinek /* 32621300Sgjelinek * We don't catch the case where you used the same IP address but 32631300Sgjelinek * it is not an exact string match. For example, 192.9.0.128 vs. 192.09.0.128. 32641300Sgjelinek * However, we're not going to worry about that but we will check for 32651300Sgjelinek * a possible netmask on one of the addresses (e.g. 10.0.0.1 and 10.0.0.1/24) 32661300Sgjelinek * and handle that case as a match. 32671300Sgjelinek */ 32681300Sgjelinek static void 32691300Sgjelinek warn_ip_match(zone_dochandle_t s_handle, char *source_zone, 32701300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 32711300Sgjelinek { 32721300Sgjelinek int err; 32731300Sgjelinek struct zone_nwiftab s_nwiftab; 32741300Sgjelinek struct zone_nwiftab t_nwiftab; 32751300Sgjelinek 32761300Sgjelinek if ((err = zonecfg_setnwifent(t_handle)) != Z_OK) { 32771300Sgjelinek errno = err; 32781300Sgjelinek zperror2(target_zone, 32791300Sgjelinek gettext("could not enumerate network interfaces")); 32801300Sgjelinek return; 32811300Sgjelinek } 32821300Sgjelinek 32831300Sgjelinek while (zonecfg_getnwifent(t_handle, &t_nwiftab) == Z_OK) { 32841300Sgjelinek char *p; 32851300Sgjelinek 32861300Sgjelinek /* remove an (optional) netmask from the address */ 32871300Sgjelinek if ((p = strchr(t_nwiftab.zone_nwif_address, '/')) != NULL) 32881300Sgjelinek *p = '\0'; 32891300Sgjelinek 32901300Sgjelinek if ((err = zonecfg_setnwifent(s_handle)) != Z_OK) { 32911300Sgjelinek errno = err; 32921300Sgjelinek zperror2(source_zone, 32931300Sgjelinek gettext("could not enumerate network interfaces")); 32941300Sgjelinek (void) zonecfg_endnwifent(t_handle); 32951300Sgjelinek return; 32961300Sgjelinek } 32971300Sgjelinek 32981300Sgjelinek while (zonecfg_getnwifent(s_handle, &s_nwiftab) == Z_OK) { 32991300Sgjelinek /* remove an (optional) netmask from the address */ 33001300Sgjelinek if ((p = strchr(s_nwiftab.zone_nwif_address, '/')) 33011300Sgjelinek != NULL) 33021300Sgjelinek *p = '\0'; 33031300Sgjelinek 33043448Sdh155122 /* For exclusive-IP zones, address is not specified. */ 33053448Sdh155122 if (strlen(s_nwiftab.zone_nwif_address) == 0) 33063448Sdh155122 continue; 33073448Sdh155122 33081300Sgjelinek if (strcmp(t_nwiftab.zone_nwif_address, 33091300Sgjelinek s_nwiftab.zone_nwif_address) == 0) { 33101300Sgjelinek (void) fprintf(stderr, 33111300Sgjelinek gettext("WARNING: network address '%s' " 33121300Sgjelinek "is configured in both zones.\n"), 33131300Sgjelinek t_nwiftab.zone_nwif_address); 33141300Sgjelinek break; 33151300Sgjelinek } 33161300Sgjelinek } 33171300Sgjelinek (void) zonecfg_endnwifent(s_handle); 33181300Sgjelinek } 33191300Sgjelinek 33201300Sgjelinek (void) zonecfg_endnwifent(t_handle); 33211300Sgjelinek } 33221300Sgjelinek 33231300Sgjelinek static void 33242712Snn35248 warn_dataset_match(zone_dochandle_t s_handle, char *source, 33252712Snn35248 zone_dochandle_t t_handle, char *target) 33261300Sgjelinek { 33271300Sgjelinek int err; 33281300Sgjelinek struct zone_dstab s_dstab; 33291300Sgjelinek struct zone_dstab t_dstab; 33301300Sgjelinek 33311300Sgjelinek if ((err = zonecfg_setdsent(t_handle)) != Z_OK) { 33321300Sgjelinek errno = err; 33332712Snn35248 zperror2(target, gettext("could not enumerate datasets")); 33341300Sgjelinek return; 33351300Sgjelinek } 33361300Sgjelinek 33371300Sgjelinek while (zonecfg_getdsent(t_handle, &t_dstab) == Z_OK) { 33381300Sgjelinek if ((err = zonecfg_setdsent(s_handle)) != Z_OK) { 33391300Sgjelinek errno = err; 33402712Snn35248 zperror2(source, 33411300Sgjelinek gettext("could not enumerate datasets")); 33421300Sgjelinek (void) zonecfg_enddsent(t_handle); 33431300Sgjelinek return; 33441300Sgjelinek } 33451300Sgjelinek 33461300Sgjelinek while (zonecfg_getdsent(s_handle, &s_dstab) == Z_OK) { 33471300Sgjelinek if (strcmp(t_dstab.zone_dataset_name, 33481300Sgjelinek s_dstab.zone_dataset_name) == 0) { 33492712Snn35248 target_zone = source; 33502712Snn35248 zerror(gettext("WARNING: dataset '%s' " 33511300Sgjelinek "is configured in both zones.\n"), 33521300Sgjelinek t_dstab.zone_dataset_name); 33531300Sgjelinek break; 33541300Sgjelinek } 33551300Sgjelinek } 33561300Sgjelinek (void) zonecfg_enddsent(s_handle); 33571300Sgjelinek } 33581300Sgjelinek 33591300Sgjelinek (void) zonecfg_enddsent(t_handle); 33601300Sgjelinek } 33611300Sgjelinek 33622712Snn35248 /* 33632712Snn35248 * Check that the clone and its source have the same brand type. 33642712Snn35248 */ 33652712Snn35248 static int 33662712Snn35248 valid_brand_clone(char *source_zone, char *target_zone) 33672712Snn35248 { 33682727Sedp brand_handle_t bh; 33692712Snn35248 char source_brand[MAXNAMELEN]; 33702712Snn35248 33712712Snn35248 if ((zone_get_brand(source_zone, source_brand, 33722712Snn35248 sizeof (source_brand))) != Z_OK) { 33732712Snn35248 (void) fprintf(stderr, "%s: zone '%s': %s\n", 33742712Snn35248 execname, source_zone, gettext("missing or invalid brand")); 33752712Snn35248 return (Z_ERR); 33762712Snn35248 } 33772712Snn35248 33782712Snn35248 if (strcmp(source_brand, target_brand) != NULL) { 33792712Snn35248 (void) fprintf(stderr, 33802712Snn35248 gettext("%s: Zones '%s' and '%s' have different brand " 33812712Snn35248 "types.\n"), execname, source_zone, target_zone); 33822712Snn35248 return (Z_ERR); 33832712Snn35248 } 33842712Snn35248 33852727Sedp if ((bh = brand_open(target_brand)) == NULL) { 33862712Snn35248 zerror(gettext("missing or invalid brand")); 33872712Snn35248 return (Z_ERR); 33882712Snn35248 } 33892727Sedp brand_close(bh); 33902712Snn35248 return (Z_OK); 33912712Snn35248 } 33922712Snn35248 33931300Sgjelinek static int 33941300Sgjelinek validate_clone(char *source_zone, char *target_zone) 33951300Sgjelinek { 33961300Sgjelinek int err = Z_OK; 33971300Sgjelinek zone_dochandle_t s_handle; 33981300Sgjelinek zone_dochandle_t t_handle; 33991300Sgjelinek 34001300Sgjelinek if ((t_handle = zonecfg_init_handle()) == NULL) { 34011300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34021300Sgjelinek return (Z_ERR); 34031300Sgjelinek } 34041300Sgjelinek if ((err = zonecfg_get_handle(target_zone, t_handle)) != Z_OK) { 34051300Sgjelinek errno = err; 34061300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34071300Sgjelinek zonecfg_fini_handle(t_handle); 34081300Sgjelinek return (Z_ERR); 34091300Sgjelinek } 34101300Sgjelinek 34111300Sgjelinek if ((s_handle = zonecfg_init_handle()) == NULL) { 34121300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34131300Sgjelinek zonecfg_fini_handle(t_handle); 34141300Sgjelinek return (Z_ERR); 34151300Sgjelinek } 34161300Sgjelinek if ((err = zonecfg_get_handle(source_zone, s_handle)) != Z_OK) { 34171300Sgjelinek errno = err; 34181300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34191300Sgjelinek goto done; 34201300Sgjelinek } 34211300Sgjelinek 34222712Snn35248 /* verify new zone has same brand type */ 34232712Snn35248 err = valid_brand_clone(source_zone, target_zone); 34242712Snn35248 if (err != Z_OK) 34252712Snn35248 goto done; 34262712Snn35248 34271300Sgjelinek /* verify new zone has same inherit-pkg-dirs */ 34281300Sgjelinek err = valid_ipd_clone(s_handle, source_zone, t_handle, target_zone); 34291300Sgjelinek 34301300Sgjelinek /* warn about imported fs's which are the same */ 34311300Sgjelinek warn_fs_match(s_handle, source_zone, t_handle, target_zone); 34321300Sgjelinek 34331300Sgjelinek /* warn about imported IP addresses which are the same */ 34341300Sgjelinek warn_ip_match(s_handle, source_zone, t_handle, target_zone); 34351300Sgjelinek 34361300Sgjelinek /* warn about imported devices which are the same */ 34371300Sgjelinek warn_dev_match(s_handle, source_zone, t_handle, target_zone); 34381300Sgjelinek 34391300Sgjelinek /* warn about imported datasets which are the same */ 34401300Sgjelinek warn_dataset_match(s_handle, source_zone, t_handle, target_zone); 34411300Sgjelinek 34421300Sgjelinek done: 34431300Sgjelinek zonecfg_fini_handle(t_handle); 34441300Sgjelinek zonecfg_fini_handle(s_handle); 34451300Sgjelinek 34461300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 34471300Sgjelinek } 34481300Sgjelinek 34491300Sgjelinek static int 34501300Sgjelinek copy_zone(char *src, char *dst) 34511300Sgjelinek { 34521300Sgjelinek boolean_t out_null = B_FALSE; 34531300Sgjelinek int status; 34541300Sgjelinek char *outfile; 34551300Sgjelinek char cmdbuf[MAXPATHLEN * 2 + 128]; 34561300Sgjelinek 34571300Sgjelinek if ((outfile = tempnam("/var/log", "zone")) == NULL) { 34581300Sgjelinek outfile = "/dev/null"; 34591300Sgjelinek out_null = B_TRUE; 34601300Sgjelinek } 34611300Sgjelinek 34621867Sgjelinek /* 34631867Sgjelinek * Use find to get the list of files to copy. We need to skip 34641867Sgjelinek * files of type "socket" since cpio can't handle those but that 34651867Sgjelinek * should be ok since the app will recreate the socket when it runs. 34661867Sgjelinek * We also need to filter out anything under the .zfs subdir. Since 34671867Sgjelinek * find is running depth-first, we need the extra egrep to filter .zfs. 34681867Sgjelinek */ 34691300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 34701867Sgjelinek "cd %s && /usr/bin/find . -type s -prune -o -depth -print | " 34711607Sgjelinek "/usr/bin/egrep -v '^\\./\\.zfs$|^\\./\\.zfs/' | " 34721300Sgjelinek "/usr/bin/cpio -pdmuP@ %s > %s 2>&1", 34731300Sgjelinek src, dst, outfile); 34741300Sgjelinek 34751300Sgjelinek status = do_subproc(cmdbuf); 34761300Sgjelinek 34772712Snn35248 if (subproc_status("copy", status, B_TRUE) != ZONE_SUBPROC_OK) { 34781300Sgjelinek if (!out_null) 34791300Sgjelinek (void) fprintf(stderr, gettext("\nThe copy failed.\n" 34801300Sgjelinek "More information can be found in %s\n"), outfile); 34812712Snn35248 return (Z_ERR); 34821300Sgjelinek } 34831300Sgjelinek 34841300Sgjelinek if (!out_null) 34851300Sgjelinek (void) unlink(outfile); 34861300Sgjelinek 34871300Sgjelinek return (Z_OK); 34881300Sgjelinek } 34891300Sgjelinek 34901300Sgjelinek /* ARGSUSED */ 349111276SJordan.Vaughan@Sun.com int 349211276SJordan.Vaughan@Sun.com zfm_print(const struct mnttab *p, void *r) { 349311276SJordan.Vaughan@Sun.com zerror(" %s\n", p->mnt_mountp); 34941300Sgjelinek return (0); 34951300Sgjelinek } 34961300Sgjelinek 34971867Sgjelinek int 34981867Sgjelinek clone_copy(char *source_zonepath, char *zonepath) 34991867Sgjelinek { 35001867Sgjelinek int err; 35011867Sgjelinek 35021867Sgjelinek /* Don't clone the zone if anything is still mounted there */ 35031867Sgjelinek if (zonecfg_find_mounts(source_zonepath, NULL, NULL)) { 35041867Sgjelinek zerror(gettext("These file systems are mounted on " 35051867Sgjelinek "subdirectories of %s.\n"), source_zonepath); 35061867Sgjelinek (void) zonecfg_find_mounts(source_zonepath, zfm_print, NULL); 35071867Sgjelinek return (Z_ERR); 35081867Sgjelinek } 35091867Sgjelinek 35101867Sgjelinek /* 35111867Sgjelinek * Attempt to create a ZFS fs for the zonepath. As usual, we don't 35121867Sgjelinek * care if this works or not since we always have the default behavior 35131867Sgjelinek * of a simple directory for the zonepath. 35141867Sgjelinek */ 35151867Sgjelinek create_zfs_zonepath(zonepath); 35161867Sgjelinek 35171867Sgjelinek (void) printf(gettext("Copying %s..."), source_zonepath); 35181867Sgjelinek (void) fflush(stdout); 35191867Sgjelinek 35201867Sgjelinek err = copy_zone(source_zonepath, zonepath); 35211867Sgjelinek 35221867Sgjelinek (void) printf("\n"); 35231867Sgjelinek 35241867Sgjelinek return (err); 35251867Sgjelinek } 35261867Sgjelinek 35271300Sgjelinek static int 35281300Sgjelinek clone_func(int argc, char *argv[]) 35291300Sgjelinek { 35301300Sgjelinek char *source_zone = NULL; 35311300Sgjelinek int lockfd; 35321300Sgjelinek int err, arg; 35331300Sgjelinek char zonepath[MAXPATHLEN]; 35341300Sgjelinek char source_zonepath[MAXPATHLEN]; 35351300Sgjelinek zone_state_t state; 35361300Sgjelinek zone_entry_t *zent; 35371867Sgjelinek char *method = NULL; 35381867Sgjelinek char *snapshot = NULL; 35397089Sgjelinek char cmdbuf[MAXPATHLEN]; 35407089Sgjelinek char postcmdbuf[MAXPATHLEN]; 35417089Sgjelinek char presnapbuf[MAXPATHLEN]; 35427089Sgjelinek char postsnapbuf[MAXPATHLEN]; 35437089Sgjelinek char validsnapbuf[MAXPATHLEN]; 35447089Sgjelinek brand_handle_t bh = NULL; 35457089Sgjelinek int status; 35467089Sgjelinek boolean_t brand_help = B_FALSE; 35471300Sgjelinek 35481300Sgjelinek if (zonecfg_in_alt_root()) { 35491300Sgjelinek zerror(gettext("cannot clone zone in alternate root")); 35501300Sgjelinek return (Z_ERR); 35511300Sgjelinek } 35521300Sgjelinek 35537089Sgjelinek /* Check the argv string for args we handle internally */ 35541300Sgjelinek optind = 0; 35557089Sgjelinek opterr = 0; 35567089Sgjelinek while ((arg = getopt(argc, argv, "?m:s:")) != EOF) { 35571300Sgjelinek switch (arg) { 35581300Sgjelinek case '?': 35597089Sgjelinek if (optopt == '?') { 35607089Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 35617089Sgjelinek brand_help = B_TRUE; 35627089Sgjelinek } 35637089Sgjelinek /* Ignore unknown options - may be brand specific. */ 35647089Sgjelinek break; 35651300Sgjelinek case 'm': 35661300Sgjelinek method = optarg; 35671300Sgjelinek break; 35681867Sgjelinek case 's': 35691867Sgjelinek snapshot = optarg; 35701867Sgjelinek break; 35711300Sgjelinek default: 35727089Sgjelinek /* Ignore unknown options - may be brand specific. */ 35737089Sgjelinek break; 35741300Sgjelinek } 35751300Sgjelinek } 35767089Sgjelinek 35777089Sgjelinek if (argc != (optind + 1)) { 35781300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 35791300Sgjelinek return (Z_USAGE); 35801300Sgjelinek } 35817089Sgjelinek 35821300Sgjelinek source_zone = argv[optind]; 35837089Sgjelinek 35847089Sgjelinek if (!brand_help) { 35857089Sgjelinek if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE, 35867089Sgjelinek B_FALSE) != Z_OK) 35877089Sgjelinek return (Z_ERR); 35887089Sgjelinek if (verify_details(CMD_CLONE, argv) != Z_OK) 35897089Sgjelinek return (Z_ERR); 35907089Sgjelinek 35917089Sgjelinek /* 35927089Sgjelinek * We also need to do some extra validation on the source zone. 35937089Sgjelinek */ 35947089Sgjelinek if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) { 35957089Sgjelinek zerror(gettext("%s operation is invalid for the " 35967089Sgjelinek "global zone."), cmd_to_str(CMD_CLONE)); 35977089Sgjelinek return (Z_ERR); 35987089Sgjelinek } 35997089Sgjelinek 36007089Sgjelinek if (strncmp(source_zone, "SUNW", 4) == 0) { 36017089Sgjelinek zerror(gettext("%s operation is invalid for zones " 36027089Sgjelinek "starting with SUNW."), cmd_to_str(CMD_CLONE)); 36037089Sgjelinek return (Z_ERR); 36047089Sgjelinek } 36057089Sgjelinek 36067089Sgjelinek zent = lookup_running_zone(source_zone); 36077089Sgjelinek if (zent != NULL) { 36087089Sgjelinek /* check whether the zone is ready or running */ 36097089Sgjelinek if ((err = zone_get_state(zent->zname, 36107089Sgjelinek &zent->zstate_num)) != Z_OK) { 36117089Sgjelinek errno = err; 36127089Sgjelinek zperror2(zent->zname, gettext("could not get " 36137089Sgjelinek "state")); 36147089Sgjelinek /* can't tell, so hedge */ 36157089Sgjelinek zent->zstate_str = "ready/running"; 36167089Sgjelinek } else { 36177089Sgjelinek zent->zstate_str = 36187089Sgjelinek zone_state_str(zent->zstate_num); 36197089Sgjelinek } 36207089Sgjelinek zerror(gettext("%s operation is invalid for %s zones."), 36217089Sgjelinek cmd_to_str(CMD_CLONE), zent->zstate_str); 36227089Sgjelinek return (Z_ERR); 36237089Sgjelinek } 36247089Sgjelinek 36257089Sgjelinek if ((err = zone_get_state(source_zone, &state)) != Z_OK) { 36261300Sgjelinek errno = err; 36277089Sgjelinek zperror2(source_zone, gettext("could not get state")); 36287089Sgjelinek return (Z_ERR); 36297089Sgjelinek } 36307089Sgjelinek if (state != ZONE_STATE_INSTALLED) { 36317089Sgjelinek (void) fprintf(stderr, 36327089Sgjelinek gettext("%s: zone %s is %s; %s is required.\n"), 36337089Sgjelinek execname, source_zone, zone_state_str(state), 36347089Sgjelinek zone_state_str(ZONE_STATE_INSTALLED)); 36357089Sgjelinek return (Z_ERR); 36361300Sgjelinek } 36377089Sgjelinek 36387089Sgjelinek /* 36397089Sgjelinek * The source zone checks out ok, continue with the clone. 36407089Sgjelinek */ 36417089Sgjelinek 36427089Sgjelinek if (validate_clone(source_zone, target_zone) != Z_OK) 36437089Sgjelinek return (Z_ERR); 36447089Sgjelinek 36457089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 36467089Sgjelinek zerror(gettext("another %s may have an operation in " 36477089Sgjelinek "progress."), "zoneadm"); 36487089Sgjelinek return (Z_ERR); 36497089Sgjelinek } 36501300Sgjelinek } 36511300Sgjelinek 36521300Sgjelinek if ((err = zone_get_zonepath(source_zone, source_zonepath, 36531300Sgjelinek sizeof (source_zonepath))) != Z_OK) { 36541300Sgjelinek errno = err; 36551300Sgjelinek zperror2(source_zone, gettext("could not get zone path")); 36561300Sgjelinek goto done; 36571300Sgjelinek } 36581300Sgjelinek 36591300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 36601300Sgjelinek != Z_OK) { 36611300Sgjelinek errno = err; 36621300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 36631300Sgjelinek goto done; 36641300Sgjelinek } 36651300Sgjelinek 36667089Sgjelinek /* 36677089Sgjelinek * Fetch the clone and postclone hooks from the brand configuration. 36687089Sgjelinek */ 36697089Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 36707089Sgjelinek zerror(gettext("missing or invalid brand")); 36717089Sgjelinek err = Z_ERR; 36727089Sgjelinek goto done; 36737089Sgjelinek } 36747089Sgjelinek 36757089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_clone, target_zone, 36767089Sgjelinek zonepath) != Z_OK) { 36777089Sgjelinek zerror("invalid brand configuration: missing clone resource"); 36787089Sgjelinek brand_close(bh); 36797089Sgjelinek err = Z_ERR; 36807089Sgjelinek goto done; 36817089Sgjelinek } 36827089Sgjelinek 36837089Sgjelinek if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postclone, 36847089Sgjelinek target_zone, zonepath) != Z_OK) { 36857089Sgjelinek zerror("invalid brand configuration: missing postclone " 36867089Sgjelinek "resource"); 36877089Sgjelinek brand_close(bh); 36887089Sgjelinek err = Z_ERR; 36897089Sgjelinek goto done; 36907089Sgjelinek } 36917089Sgjelinek 36927089Sgjelinek if (get_hook(bh, presnapbuf, sizeof (presnapbuf), brand_get_presnap, 36937089Sgjelinek source_zone, source_zonepath) != Z_OK) { 36947089Sgjelinek zerror("invalid brand configuration: missing presnap " 36957089Sgjelinek "resource"); 36967089Sgjelinek brand_close(bh); 36977089Sgjelinek err = Z_ERR; 36981300Sgjelinek goto done; 36991300Sgjelinek } 37001300Sgjelinek 37017089Sgjelinek if (get_hook(bh, postsnapbuf, sizeof (postsnapbuf), brand_get_postsnap, 37027089Sgjelinek source_zone, source_zonepath) != Z_OK) { 37037089Sgjelinek zerror("invalid brand configuration: missing postsnap " 37047089Sgjelinek "resource"); 37057089Sgjelinek brand_close(bh); 37067089Sgjelinek err = Z_ERR; 37077089Sgjelinek goto done; 37087089Sgjelinek } 37097089Sgjelinek 37107089Sgjelinek if (get_hook(bh, validsnapbuf, sizeof (validsnapbuf), 37117089Sgjelinek brand_get_validatesnap, target_zone, zonepath) != Z_OK) { 37127089Sgjelinek zerror("invalid brand configuration: missing validatesnap " 37137089Sgjelinek "resource"); 37147089Sgjelinek brand_close(bh); 37151867Sgjelinek err = Z_ERR; 37167089Sgjelinek goto done; 37177089Sgjelinek } 37187089Sgjelinek brand_close(bh); 37197089Sgjelinek 37207089Sgjelinek /* Append all options to clone hook. */ 37217089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) { 37227089Sgjelinek err = Z_ERR; 37237089Sgjelinek goto done; 37247089Sgjelinek } 37257089Sgjelinek 37267089Sgjelinek /* Append all options to postclone hook. */ 37277089Sgjelinek if (addoptions(postcmdbuf, argv, sizeof (postcmdbuf)) != Z_OK) { 37287089Sgjelinek err = Z_ERR; 37297089Sgjelinek goto done; 37307089Sgjelinek } 37317089Sgjelinek 37327089Sgjelinek if (!brand_help) { 37337089Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE)) 37347089Sgjelinek != Z_OK) { 37357089Sgjelinek errno = err; 37367089Sgjelinek zperror2(target_zone, gettext("could not set state")); 37377089Sgjelinek goto done; 37387089Sgjelinek } 37391867Sgjelinek } 37401867Sgjelinek 37412712Snn35248 /* 37427089Sgjelinek * The clone hook is optional. If it exists, use the hook for 37437089Sgjelinek * cloning, otherwise use the built-in clone support 37442712Snn35248 */ 37457089Sgjelinek if (cmdbuf[0] != '\0') { 37467089Sgjelinek /* Run the clone hook */ 37479310Sgerald.jelinek@sun.com status = do_subproc(cmdbuf); 37487089Sgjelinek if ((status = subproc_status(gettext("brand-specific clone"), 37497089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 37507089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 37517089Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 37527089Sgjelinek err = Z_ERR; 37537089Sgjelinek goto done; 37547089Sgjelinek } 37557089Sgjelinek 37567089Sgjelinek if (brand_help) 37577089Sgjelinek return (Z_OK); 37587089Sgjelinek 37597089Sgjelinek } else { 37607089Sgjelinek /* If just help, we're done since there is no brand help. */ 37617089Sgjelinek if (brand_help) 37627089Sgjelinek return (Z_OK); 37637089Sgjelinek 37647089Sgjelinek /* Run the built-in clone support. */ 37657089Sgjelinek 37667089Sgjelinek /* The only explicit built-in method is "copy". */ 37677089Sgjelinek if (method != NULL && strcmp(method, "copy") != 0) { 37687089Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 37697089Sgjelinek err = Z_USAGE; 37707089Sgjelinek goto done; 37717089Sgjelinek } 37727089Sgjelinek 37737089Sgjelinek if (snapshot != NULL) { 37747089Sgjelinek err = clone_snapshot_zfs(snapshot, zonepath, 37757089Sgjelinek validsnapbuf); 37767089Sgjelinek } else { 37777089Sgjelinek /* 37787089Sgjelinek * We always copy the clone unless the source is ZFS 37797089Sgjelinek * and a ZFS clone worked. We fallback to copying if 37807089Sgjelinek * the ZFS clone fails for some reason. 37817089Sgjelinek */ 37827089Sgjelinek err = Z_ERR; 37837089Sgjelinek if (method == NULL && is_zonepath_zfs(source_zonepath)) 37847089Sgjelinek err = clone_zfs(source_zonepath, zonepath, 37857089Sgjelinek presnapbuf, postsnapbuf); 37867089Sgjelinek 37877089Sgjelinek if (err != Z_OK) 37887089Sgjelinek err = clone_copy(source_zonepath, zonepath); 37897089Sgjelinek } 37907089Sgjelinek } 37917089Sgjelinek 37927089Sgjelinek if (err == Z_OK && postcmdbuf[0] != '\0') { 37937089Sgjelinek status = do_subproc(postcmdbuf); 37947089Sgjelinek if ((err = subproc_status("postclone", status, B_FALSE)) 37957089Sgjelinek != ZONE_SUBPROC_OK) { 37967089Sgjelinek zerror(gettext("post-clone configuration failed.")); 37977089Sgjelinek err = Z_ERR; 37987089Sgjelinek } 37997089Sgjelinek } 38001300Sgjelinek 38011300Sgjelinek done: 38022712Snn35248 /* 38032712Snn35248 * If everything went well, we mark the zone as installed. 38042712Snn35248 */ 38052712Snn35248 if (err == Z_OK) { 38062712Snn35248 err = zone_set_state(target_zone, ZONE_STATE_INSTALLED); 38072712Snn35248 if (err != Z_OK) { 38082712Snn35248 errno = err; 38092712Snn35248 zperror2(target_zone, gettext("could not set state")); 38102712Snn35248 } 38112712Snn35248 } 38127089Sgjelinek if (!brand_help) 38137089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 38141300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 38151300Sgjelinek } 38161300Sgjelinek 38171607Sgjelinek /* 38181867Sgjelinek * Used when removing a zonepath after uninstalling or cleaning up after 38191867Sgjelinek * the move subcommand. This handles a zonepath that has non-standard 38201867Sgjelinek * contents so that we will only cleanup the stuff we know about and leave 38211867Sgjelinek * any user data alone. 38221607Sgjelinek * 38231867Sgjelinek * If the "all" parameter is true then we should remove the whole zonepath 38241867Sgjelinek * even if it has non-standard files/directories in it. This can be used when 38251867Sgjelinek * we need to cleanup after moving the zonepath across file systems. 38261867Sgjelinek * 38271867Sgjelinek * We "exec" the RMCOMMAND so that the returned status is that of RMCOMMAND 38281867Sgjelinek * and not the shell. 38291607Sgjelinek */ 38301607Sgjelinek static int 38311867Sgjelinek cleanup_zonepath(char *zonepath, boolean_t all) 38321607Sgjelinek { 38331867Sgjelinek int status; 38341867Sgjelinek int i; 38351867Sgjelinek boolean_t non_std = B_FALSE; 38361867Sgjelinek struct dirent *dp; 38371867Sgjelinek DIR *dirp; 38383686Sgjelinek /* 38393686Sgjelinek * The SUNWattached.xml file is expected since it might 38403686Sgjelinek * exist if the zone was force-attached after a 38413686Sgjelinek * migration. 38423686Sgjelinek */ 38433686Sgjelinek char *std_entries[] = {"dev", "lu", "root", 38443686Sgjelinek "SUNWattached.xml", NULL}; 38451867Sgjelinek /* (MAXPATHLEN * 3) is for the 3 std_entries dirs */ 38461867Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64]; 38471867Sgjelinek 38481867Sgjelinek /* 38491867Sgjelinek * We shouldn't need these checks but lets be paranoid since we 38501867Sgjelinek * could blow away the whole system here if we got the wrong zonepath. 38511867Sgjelinek */ 38521867Sgjelinek if (*zonepath == NULL || strcmp(zonepath, "/") == 0) { 38531867Sgjelinek (void) fprintf(stderr, "invalid zonepath '%s'\n", zonepath); 38541867Sgjelinek return (Z_INVAL); 38551867Sgjelinek } 38561867Sgjelinek 38571867Sgjelinek /* 38581867Sgjelinek * If the dirpath is already gone (maybe it was manually removed) then 38591867Sgjelinek * we just return Z_OK so that the cleanup is successful. 38601867Sgjelinek */ 38611867Sgjelinek if ((dirp = opendir(zonepath)) == NULL) 38621867Sgjelinek return (Z_OK); 38631867Sgjelinek 38641867Sgjelinek /* 38651867Sgjelinek * Look through the zonepath directory to see if there are any 38661867Sgjelinek * non-standard files/dirs. Also skip .zfs since that might be 38671867Sgjelinek * there but we'll handle ZFS file systems as a special case. 38681867Sgjelinek */ 38691867Sgjelinek while ((dp = readdir(dirp)) != NULL) { 38701867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 38711867Sgjelinek strcmp(dp->d_name, "..") == 0 || 38721867Sgjelinek strcmp(dp->d_name, ".zfs") == 0) 38731867Sgjelinek continue; 38741867Sgjelinek 38751867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) 38761867Sgjelinek if (strcmp(dp->d_name, std_entries[i]) == 0) 38771867Sgjelinek break; 38781867Sgjelinek 38791867Sgjelinek if (std_entries[i] == NULL) 38801867Sgjelinek non_std = B_TRUE; 38811867Sgjelinek } 38821867Sgjelinek (void) closedir(dirp); 38831867Sgjelinek 38841867Sgjelinek if (!all && non_std) { 38851607Sgjelinek /* 38861867Sgjelinek * There are extra, non-standard directories/files in the 38871867Sgjelinek * zonepath so we don't want to remove the zonepath. We 38881867Sgjelinek * just want to remove the standard directories and leave 38891867Sgjelinek * the user data alone. 38901607Sgjelinek */ 38911867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND); 38921867Sgjelinek 38931867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) { 38941867Sgjelinek char tmpbuf[MAXPATHLEN]; 38951867Sgjelinek 38961867Sgjelinek if (snprintf(tmpbuf, sizeof (tmpbuf), " %s/%s", 38971867Sgjelinek zonepath, std_entries[i]) >= sizeof (tmpbuf) || 38981867Sgjelinek strlcat(cmdbuf, tmpbuf, sizeof (cmdbuf)) >= 38991867Sgjelinek sizeof (cmdbuf)) { 39001867Sgjelinek (void) fprintf(stderr, 39011867Sgjelinek gettext("path is too long\n")); 39021867Sgjelinek return (Z_INVAL); 39031867Sgjelinek } 39041867Sgjelinek } 39051867Sgjelinek 39061867Sgjelinek status = do_subproc(cmdbuf); 39071867Sgjelinek 39081867Sgjelinek (void) fprintf(stderr, gettext("WARNING: Unable to completely " 39091867Sgjelinek "remove %s\nbecause it contains additional user data. " 39101867Sgjelinek "Only the standard directory\nentries have been " 39111867Sgjelinek "removed.\n"), 39121867Sgjelinek zonepath); 39131867Sgjelinek 39142712Snn35248 return ((subproc_status(RMCOMMAND, status, B_TRUE) == 39152712Snn35248 ZONE_SUBPROC_OK) ? Z_OK : Z_ERR); 39161607Sgjelinek } 39171607Sgjelinek 39181867Sgjelinek /* 39191867Sgjelinek * There is nothing unexpected in the zonepath, try to get rid of the 39201867Sgjelinek * whole zonepath directory. 39211867Sgjelinek * 39221867Sgjelinek * If the zonepath is its own zfs file system, try to destroy the 39231867Sgjelinek * file system. If that fails for some reason (e.g. it has clones) 39241867Sgjelinek * then we'll just remove the contents of the zonepath. 39251867Sgjelinek */ 39261867Sgjelinek if (is_zonepath_zfs(zonepath)) { 39271867Sgjelinek if (destroy_zfs(zonepath) == Z_OK) 39281867Sgjelinek return (Z_OK); 39291867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND 39301867Sgjelinek " %s/*", zonepath); 39311867Sgjelinek status = do_subproc(cmdbuf); 39322712Snn35248 return ((subproc_status(RMCOMMAND, status, B_TRUE) == 39332712Snn35248 ZONE_SUBPROC_OK) ? Z_OK : Z_ERR); 39341867Sgjelinek } 39351867Sgjelinek 39361867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 39371867Sgjelinek zonepath); 39381607Sgjelinek status = do_subproc(cmdbuf); 39392712Snn35248 39402712Snn35248 return ((subproc_status(RMCOMMAND, status, B_TRUE) == ZONE_SUBPROC_OK) 39412712Snn35248 ? Z_OK : Z_ERR); 39421607Sgjelinek } 39431607Sgjelinek 39441300Sgjelinek static int 39451300Sgjelinek move_func(int argc, char *argv[]) 39461300Sgjelinek { 39471300Sgjelinek char *new_zonepath = NULL; 39481300Sgjelinek int lockfd; 39491300Sgjelinek int err, arg; 39501300Sgjelinek char zonepath[MAXPATHLEN]; 39511300Sgjelinek zone_dochandle_t handle; 39521300Sgjelinek boolean_t fast; 39531867Sgjelinek boolean_t is_zfs = B_FALSE; 395411276SJordan.Vaughan@Sun.com boolean_t root_fs_mounted = B_FALSE; 39551867Sgjelinek struct dirent *dp; 39561867Sgjelinek DIR *dirp; 39571867Sgjelinek boolean_t empty = B_TRUE; 39581300Sgjelinek boolean_t revert; 39591300Sgjelinek struct stat zonepath_buf; 39601300Sgjelinek struct stat new_zonepath_buf; 396111276SJordan.Vaughan@Sun.com zone_mounts_t mounts; 39621300Sgjelinek 39631300Sgjelinek if (zonecfg_in_alt_root()) { 39641300Sgjelinek zerror(gettext("cannot move zone in alternate root")); 39651300Sgjelinek return (Z_ERR); 39661300Sgjelinek } 39671300Sgjelinek 39681300Sgjelinek optind = 0; 39691300Sgjelinek if ((arg = getopt(argc, argv, "?")) != EOF) { 39701300Sgjelinek switch (arg) { 39711300Sgjelinek case '?': 39721300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 39731300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 39741300Sgjelinek default: 39751300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 39761300Sgjelinek return (Z_USAGE); 39771300Sgjelinek } 39781300Sgjelinek } 39791300Sgjelinek if (argc != (optind + 1)) { 39801300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 39811300Sgjelinek return (Z_USAGE); 39821300Sgjelinek } 39831300Sgjelinek new_zonepath = argv[optind]; 39842712Snn35248 if (sanity_check(target_zone, CMD_MOVE, B_FALSE, B_TRUE, B_FALSE) 39852712Snn35248 != Z_OK) 39861300Sgjelinek return (Z_ERR); 39873339Szt129084 if (verify_details(CMD_MOVE, argv) != Z_OK) 39881300Sgjelinek return (Z_ERR); 39891300Sgjelinek 39901300Sgjelinek /* 39911300Sgjelinek * Check out the new zonepath. This has the side effect of creating 39921300Sgjelinek * a directory for the new zonepath. We depend on this later when we 39931867Sgjelinek * stat to see if we are doing a cross file system move or not. 39941300Sgjelinek */ 39951300Sgjelinek if (validate_zonepath(new_zonepath, CMD_MOVE) != Z_OK) 39961300Sgjelinek return (Z_ERR); 39971300Sgjelinek 39981300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 39991300Sgjelinek != Z_OK) { 40001300Sgjelinek errno = err; 40011300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 40021300Sgjelinek return (Z_ERR); 40031300Sgjelinek } 40041300Sgjelinek 40051300Sgjelinek if (stat(zonepath, &zonepath_buf) == -1) { 40061300Sgjelinek zperror(gettext("could not stat zone path"), B_FALSE); 40071300Sgjelinek return (Z_ERR); 40081300Sgjelinek } 40091300Sgjelinek 40101300Sgjelinek if (stat(new_zonepath, &new_zonepath_buf) == -1) { 40111300Sgjelinek zperror(gettext("could not stat new zone path"), B_FALSE); 40121300Sgjelinek return (Z_ERR); 40131300Sgjelinek } 40141300Sgjelinek 40151867Sgjelinek /* 40161867Sgjelinek * Check if the destination directory is empty. 40171867Sgjelinek */ 40181867Sgjelinek if ((dirp = opendir(new_zonepath)) == NULL) { 40191867Sgjelinek zperror(gettext("could not open new zone path"), B_FALSE); 40201867Sgjelinek return (Z_ERR); 40211867Sgjelinek } 40221867Sgjelinek while ((dp = readdir(dirp)) != (struct dirent *)0) { 40231867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 40241867Sgjelinek strcmp(dp->d_name, "..") == 0) 40251867Sgjelinek continue; 40261867Sgjelinek empty = B_FALSE; 40271867Sgjelinek break; 40281867Sgjelinek } 40291867Sgjelinek (void) closedir(dirp); 40301867Sgjelinek 40311867Sgjelinek /* Error if there is anything in the destination directory. */ 40321867Sgjelinek if (!empty) { 40331867Sgjelinek (void) fprintf(stderr, gettext("could not move zone to %s: " 40341867Sgjelinek "directory not empty\n"), new_zonepath); 40351867Sgjelinek return (Z_ERR); 40361867Sgjelinek } 40371867Sgjelinek 403811276SJordan.Vaughan@Sun.com /* 403911276SJordan.Vaughan@Sun.com * Collect information about mounts within the zone's zonepath. 404011276SJordan.Vaughan@Sun.com * Overlay mounts on the zone's root directory are erroneous. 404111276SJordan.Vaughan@Sun.com * Bail if we encounter any unexpected mounts. 404211276SJordan.Vaughan@Sun.com */ 404311276SJordan.Vaughan@Sun.com if (zone_mounts_init(&mounts, zonepath) != 0) 40441300Sgjelinek return (Z_ERR); 404511276SJordan.Vaughan@Sun.com if (mounts.num_root_overlay_mounts != 0) { 404611276SJordan.Vaughan@Sun.com zerror(gettext("%d overlay mount(s) detected on %s/root."), 404711276SJordan.Vaughan@Sun.com mounts.num_root_overlay_mounts, zonepath); 404811276SJordan.Vaughan@Sun.com goto err_and_mounts_destroy; 404911276SJordan.Vaughan@Sun.com } 405011276SJordan.Vaughan@Sun.com if (mounts.num_unexpected_mounts != 0) 405111276SJordan.Vaughan@Sun.com goto err_and_mounts_destroy; 40521300Sgjelinek 40531300Sgjelinek /* 40541867Sgjelinek * Check if we are moving in the same file system and can do a fast 40551867Sgjelinek * move or if we are crossing file systems and have to copy the data. 40561300Sgjelinek */ 40571300Sgjelinek fast = (zonepath_buf.st_dev == new_zonepath_buf.st_dev); 40581300Sgjelinek 40591300Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 40601300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 406111276SJordan.Vaughan@Sun.com goto err_and_mounts_destroy; 40621300Sgjelinek } 40631300Sgjelinek 40641300Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 40651300Sgjelinek errno = err; 40661300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 406711276SJordan.Vaughan@Sun.com goto err_and_fini_handle; 40681300Sgjelinek } 40691300Sgjelinek 40707089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 40711300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 40721300Sgjelinek "zoneadm"); 407311276SJordan.Vaughan@Sun.com goto err_and_fini_handle; 407411276SJordan.Vaughan@Sun.com } 407511276SJordan.Vaughan@Sun.com 407611276SJordan.Vaughan@Sun.com /* 407711276SJordan.Vaughan@Sun.com * Unmount the zone's root filesystem before we move the zone's 407811276SJordan.Vaughan@Sun.com * zonepath. 407911276SJordan.Vaughan@Sun.com */ 408011276SJordan.Vaughan@Sun.com if (zone_unmount_rootfs(&mounts, zonepath, B_FALSE) != 0) 408111276SJordan.Vaughan@Sun.com goto err_and_rele_lockfile; 40821300Sgjelinek 40831300Sgjelinek /* 40841867Sgjelinek * We're making some file system changes now so we have to clean up 40851867Sgjelinek * the file system before we are done. This will either clean up the 40861300Sgjelinek * new zonepath if the zonecfg update failed or it will clean up the 40871300Sgjelinek * old zonepath if everything is ok. 40881300Sgjelinek */ 40891300Sgjelinek revert = B_TRUE; 40901300Sgjelinek 40911867Sgjelinek if (is_zonepath_zfs(zonepath) && 40921867Sgjelinek move_zfs(zonepath, new_zonepath) != Z_ERR) { 40931867Sgjelinek is_zfs = B_TRUE; 40941867Sgjelinek 40951867Sgjelinek } else if (fast) { 40961867Sgjelinek /* same file system, use rename for a quick move */ 40971300Sgjelinek 40981300Sgjelinek /* 40991300Sgjelinek * Remove the new_zonepath directory that got created above 41001300Sgjelinek * during the validation. It gets in the way of the rename. 41011300Sgjelinek */ 41021300Sgjelinek if (rmdir(new_zonepath) != 0) { 41031300Sgjelinek zperror(gettext("could not rmdir new zone path"), 41041300Sgjelinek B_FALSE); 410511276SJordan.Vaughan@Sun.com (void) zone_mount_rootfs(&mounts, zonepath); 410611276SJordan.Vaughan@Sun.com goto err_and_rele_lockfile; 41071300Sgjelinek } 41081300Sgjelinek 41091300Sgjelinek if (rename(zonepath, new_zonepath) != 0) { 41101300Sgjelinek /* 41111300Sgjelinek * If this fails we don't need to do all of the 41121300Sgjelinek * cleanup that happens for the rest of the code 41131300Sgjelinek * so just return from this error. 41141300Sgjelinek */ 41151300Sgjelinek zperror(gettext("could not move zone"), B_FALSE); 411611276SJordan.Vaughan@Sun.com (void) zone_mount_rootfs(&mounts, zonepath); 411711276SJordan.Vaughan@Sun.com goto err_and_rele_lockfile; 41181300Sgjelinek } 41191300Sgjelinek 41201300Sgjelinek } else { 41211867Sgjelinek /* 41221867Sgjelinek * Attempt to create a ZFS fs for the new zonepath. As usual, 41231867Sgjelinek * we don't care if this works or not since we always have the 41241867Sgjelinek * default behavior of a simple directory for the zonepath. 41251867Sgjelinek */ 41261867Sgjelinek create_zfs_zonepath(new_zonepath); 41271867Sgjelinek 41281300Sgjelinek (void) printf(gettext( 41291867Sgjelinek "Moving across file systems; copying zonepath %s..."), 41301300Sgjelinek zonepath); 41311300Sgjelinek (void) fflush(stdout); 41321300Sgjelinek 41331300Sgjelinek err = copy_zone(zonepath, new_zonepath); 41341300Sgjelinek 41351300Sgjelinek (void) printf("\n"); 41361300Sgjelinek if (err != Z_OK) 41371300Sgjelinek goto done; 41381300Sgjelinek } 41391300Sgjelinek 414011276SJordan.Vaughan@Sun.com /* 414111276SJordan.Vaughan@Sun.com * Mount the zone's root filesystem in the new zonepath if there was 414211276SJordan.Vaughan@Sun.com * a root mount prior to the move. 414311276SJordan.Vaughan@Sun.com */ 414411276SJordan.Vaughan@Sun.com if (zone_mount_rootfs(&mounts, new_zonepath) != 0) { 414511276SJordan.Vaughan@Sun.com err = Z_ERR; 414611276SJordan.Vaughan@Sun.com goto done; 414711276SJordan.Vaughan@Sun.com } 414811276SJordan.Vaughan@Sun.com root_fs_mounted = B_TRUE; 414911276SJordan.Vaughan@Sun.com 41501300Sgjelinek if ((err = zonecfg_set_zonepath(handle, new_zonepath)) != Z_OK) { 41511300Sgjelinek errno = err; 41521300Sgjelinek zperror(gettext("could not set new zonepath"), B_TRUE); 41531300Sgjelinek goto done; 41541300Sgjelinek } 41551300Sgjelinek 41561300Sgjelinek if ((err = zonecfg_save(handle)) != Z_OK) { 41571300Sgjelinek errno = err; 41581300Sgjelinek zperror(gettext("zonecfg save failed"), B_TRUE); 41591300Sgjelinek goto done; 41601300Sgjelinek } 41611300Sgjelinek 41621300Sgjelinek revert = B_FALSE; 41631300Sgjelinek 41641300Sgjelinek done: 41651300Sgjelinek zonecfg_fini_handle(handle); 41667089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 41671300Sgjelinek 41681300Sgjelinek /* 41691867Sgjelinek * Clean up the file system based on how things went. We either 41701300Sgjelinek * clean up the new zonepath if the operation failed for some reason 41711300Sgjelinek * or we clean up the old zonepath if everything is ok. 41721300Sgjelinek */ 41731300Sgjelinek if (revert) { 417411276SJordan.Vaughan@Sun.com /* 417511276SJordan.Vaughan@Sun.com * Check for the unlikely scenario in which the zone's 417611276SJordan.Vaughan@Sun.com * zonepath and its root file system moved but libzonecfg 417711276SJordan.Vaughan@Sun.com * couldn't save the new zonepath to the zone's configuration 417811276SJordan.Vaughan@Sun.com * file. The mounted root filesystem must be unmounted before 417911276SJordan.Vaughan@Sun.com * zoneadm restores the zone's zonepath. 418011276SJordan.Vaughan@Sun.com */ 418111276SJordan.Vaughan@Sun.com if (root_fs_mounted && zone_unmount_rootfs(&mounts, 418211276SJordan.Vaughan@Sun.com new_zonepath, B_TRUE) != 0) { 418311276SJordan.Vaughan@Sun.com /* 418411276SJordan.Vaughan@Sun.com * We can't forcibly unmount the zone's root file system 418511276SJordan.Vaughan@Sun.com * from the new zonepath. Bail! 418611276SJordan.Vaughan@Sun.com */ 418711276SJordan.Vaughan@Sun.com zerror(gettext("fatal error: cannot unmount %s/root\n"), 418811276SJordan.Vaughan@Sun.com new_zonepath); 418911276SJordan.Vaughan@Sun.com goto err_and_mounts_destroy; 419011276SJordan.Vaughan@Sun.com } 419111276SJordan.Vaughan@Sun.com 41921300Sgjelinek /* The zonecfg update failed, cleanup the new zonepath. */ 41931867Sgjelinek if (is_zfs) { 41941867Sgjelinek if (move_zfs(new_zonepath, zonepath) == Z_ERR) { 41951867Sgjelinek (void) fprintf(stderr, gettext("could not " 41961867Sgjelinek "restore zonepath, the zfs mountpoint is " 41971867Sgjelinek "set as:\n%s\n"), new_zonepath); 41981867Sgjelinek /* 41991867Sgjelinek * err is already != Z_OK since we're reverting 42001867Sgjelinek */ 420111276SJordan.Vaughan@Sun.com } else { 420211276SJordan.Vaughan@Sun.com (void) zone_mount_rootfs(&mounts, zonepath); 42031867Sgjelinek } 42041867Sgjelinek } else if (fast) { 42051300Sgjelinek if (rename(new_zonepath, zonepath) != 0) { 42061300Sgjelinek zperror(gettext("could not restore zonepath"), 42071300Sgjelinek B_FALSE); 42081300Sgjelinek /* 42091300Sgjelinek * err is already != Z_OK since we're reverting 42101300Sgjelinek */ 421111276SJordan.Vaughan@Sun.com } else { 421211276SJordan.Vaughan@Sun.com (void) zone_mount_rootfs(&mounts, zonepath); 42131300Sgjelinek } 42141300Sgjelinek } else { 42151300Sgjelinek (void) printf(gettext("Cleaning up zonepath %s..."), 42161300Sgjelinek new_zonepath); 42171300Sgjelinek (void) fflush(stdout); 42181867Sgjelinek err = cleanup_zonepath(new_zonepath, B_TRUE); 42191300Sgjelinek (void) printf("\n"); 42201300Sgjelinek 42211607Sgjelinek if (err != Z_OK) { 42221300Sgjelinek errno = err; 42231300Sgjelinek zperror(gettext("could not remove new " 42241300Sgjelinek "zonepath"), B_TRUE); 42251300Sgjelinek } else { 42261300Sgjelinek /* 42271300Sgjelinek * Because we're reverting we know the mainline 42281300Sgjelinek * code failed but we just reused the err 42291300Sgjelinek * variable so we reset it back to Z_ERR. 42301300Sgjelinek */ 42311300Sgjelinek err = Z_ERR; 42321300Sgjelinek } 423311276SJordan.Vaughan@Sun.com 423411276SJordan.Vaughan@Sun.com (void) zone_mount_rootfs(&mounts, zonepath); 42351300Sgjelinek } 42361300Sgjelinek } else { 42371300Sgjelinek /* The move was successful, cleanup the old zonepath. */ 42381867Sgjelinek if (!is_zfs && !fast) { 42391300Sgjelinek (void) printf( 42401300Sgjelinek gettext("Cleaning up zonepath %s..."), zonepath); 42411300Sgjelinek (void) fflush(stdout); 42421867Sgjelinek err = cleanup_zonepath(zonepath, B_TRUE); 42431300Sgjelinek (void) printf("\n"); 42441300Sgjelinek 42451607Sgjelinek if (err != Z_OK) { 42461300Sgjelinek errno = err; 42471300Sgjelinek zperror(gettext("could not remove zonepath"), 42481300Sgjelinek B_TRUE); 42491300Sgjelinek } 42501300Sgjelinek } 42511300Sgjelinek } 42521300Sgjelinek 425311276SJordan.Vaughan@Sun.com zone_mounts_destroy(&mounts); 42541300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 425511276SJordan.Vaughan@Sun.com 425611276SJordan.Vaughan@Sun.com err_and_rele_lockfile: 425711276SJordan.Vaughan@Sun.com zonecfg_release_lock_file(target_zone, lockfd); 425811276SJordan.Vaughan@Sun.com err_and_fini_handle: 425911276SJordan.Vaughan@Sun.com zonecfg_fini_handle(handle); 426011276SJordan.Vaughan@Sun.com err_and_mounts_destroy: 426111276SJordan.Vaughan@Sun.com zone_mounts_destroy(&mounts); 426211276SJordan.Vaughan@Sun.com return (Z_ERR); 42631300Sgjelinek } 42641300Sgjelinek 42657089Sgjelinek /* ARGSUSED */ 42661507Sgjelinek static int 42671507Sgjelinek detach_func(int argc, char *argv[]) 42681507Sgjelinek { 42698301Sgerald.jelinek@sun.com int lockfd = -1; 42701507Sgjelinek int err, arg; 42711507Sgjelinek char zonepath[MAXPATHLEN]; 42724785Sgjelinek char cmdbuf[MAXPATHLEN]; 42737089Sgjelinek char precmdbuf[MAXPATHLEN]; 42742078Sgjelinek boolean_t execute = B_TRUE; 42757089Sgjelinek boolean_t brand_help = B_FALSE; 42764785Sgjelinek brand_handle_t bh = NULL; 42777089Sgjelinek int status; 42781507Sgjelinek 42791507Sgjelinek if (zonecfg_in_alt_root()) { 42801507Sgjelinek zerror(gettext("cannot detach zone in alternate root")); 42811507Sgjelinek return (Z_ERR); 42821507Sgjelinek } 42831507Sgjelinek 42847089Sgjelinek /* Check the argv string for args we handle internally */ 42851507Sgjelinek optind = 0; 42867089Sgjelinek opterr = 0; 42877089Sgjelinek while ((arg = getopt(argc, argv, "?n")) != EOF) { 42881507Sgjelinek switch (arg) { 42891507Sgjelinek case '?': 42907089Sgjelinek if (optopt == '?') { 42917089Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 42927089Sgjelinek brand_help = B_TRUE; 42937089Sgjelinek } 42947089Sgjelinek /* Ignore unknown options - may be brand specific. */ 42957089Sgjelinek break; 42962078Sgjelinek case 'n': 42972078Sgjelinek execute = B_FALSE; 42982078Sgjelinek break; 42991507Sgjelinek default: 43007089Sgjelinek /* Ignore unknown options - may be brand specific. */ 43017089Sgjelinek break; 43021507Sgjelinek } 43031507Sgjelinek } 43042712Snn35248 43057089Sgjelinek if (brand_help) 43067089Sgjelinek execute = B_FALSE; 43077089Sgjelinek 43082078Sgjelinek if (execute) { 43092712Snn35248 if (sanity_check(target_zone, CMD_DETACH, B_FALSE, B_TRUE, 43102712Snn35248 B_FALSE) != Z_OK) 43112078Sgjelinek return (Z_ERR); 43123339Szt129084 if (verify_details(CMD_DETACH, argv) != Z_OK) 43132078Sgjelinek return (Z_ERR); 43142078Sgjelinek } else { 43152078Sgjelinek /* 43162078Sgjelinek * We want a dry-run to work for a non-privileged user so we 43172078Sgjelinek * only do minimal validation. 43182078Sgjelinek */ 43192078Sgjelinek if (target_zone == NULL) { 43202078Sgjelinek zerror(gettext("no zone specified")); 43212078Sgjelinek return (Z_ERR); 43222078Sgjelinek } 43232078Sgjelinek 43242078Sgjelinek if (strcmp(target_zone, GLOBAL_ZONENAME) == 0) { 43252078Sgjelinek zerror(gettext("%s operation is invalid for the " 43262078Sgjelinek "global zone."), cmd_to_str(CMD_DETACH)); 43272078Sgjelinek return (Z_ERR); 43282078Sgjelinek } 43292078Sgjelinek } 43301507Sgjelinek 43311507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 43321507Sgjelinek != Z_OK) { 43331507Sgjelinek errno = err; 43341507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 43351507Sgjelinek return (Z_ERR); 43361507Sgjelinek } 43371507Sgjelinek 43387089Sgjelinek /* Fetch the detach and predetach hooks from the brand configuration. */ 43394785Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 43404785Sgjelinek zerror(gettext("missing or invalid brand")); 43414785Sgjelinek return (Z_ERR); 43424785Sgjelinek } 43434785Sgjelinek 43447089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_detach, target_zone, 43457089Sgjelinek zonepath) != Z_OK) { 43467089Sgjelinek zerror("invalid brand configuration: missing detach resource"); 43477089Sgjelinek brand_close(bh); 43487089Sgjelinek return (Z_ERR); 43497089Sgjelinek } 43507089Sgjelinek 43517089Sgjelinek if (get_hook(bh, precmdbuf, sizeof (precmdbuf), brand_get_predetach, 43527089Sgjelinek target_zone, zonepath) != Z_OK) { 43534785Sgjelinek zerror("invalid brand configuration: missing predetach " 43544785Sgjelinek "resource"); 43554785Sgjelinek brand_close(bh); 43564785Sgjelinek return (Z_ERR); 43574785Sgjelinek } 43584785Sgjelinek brand_close(bh); 43594785Sgjelinek 43607089Sgjelinek /* Append all options to predetach hook. */ 43617089Sgjelinek if (addoptions(precmdbuf, argv, sizeof (precmdbuf)) != Z_OK) 43627089Sgjelinek return (Z_ERR); 43637089Sgjelinek 43647089Sgjelinek /* Append all options to detach hook. */ 43657089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) 43667089Sgjelinek return (Z_ERR); 43677089Sgjelinek 43687089Sgjelinek if (execute && zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 43691507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 43701507Sgjelinek "zoneadm"); 43711507Sgjelinek return (Z_ERR); 43721507Sgjelinek } 43731507Sgjelinek 43747089Sgjelinek /* If we have a brand predetach hook, run it. */ 43757089Sgjelinek if (!brand_help && precmdbuf[0] != '\0') { 43767089Sgjelinek status = do_subproc(precmdbuf); 43777089Sgjelinek if (subproc_status(gettext("brand-specific predetach"), 43787089Sgjelinek status, B_FALSE) != ZONE_SUBPROC_OK) { 43797089Sgjelinek 43808301Sgerald.jelinek@sun.com if (execute) { 43818301Sgerald.jelinek@sun.com assert(lockfd >= 0); 43827089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 43838301Sgerald.jelinek@sun.com lockfd = -1; 43848301Sgerald.jelinek@sun.com } 43858301Sgerald.jelinek@sun.com 43868301Sgerald.jelinek@sun.com assert(lockfd == -1); 43877089Sgjelinek return (Z_ERR); 43887089Sgjelinek } 43897089Sgjelinek } 43907089Sgjelinek 43917089Sgjelinek if (cmdbuf[0] != '\0') { 43927089Sgjelinek /* Run the detach hook */ 43939310Sgerald.jelinek@sun.com status = do_subproc(cmdbuf); 43947089Sgjelinek if ((status = subproc_status(gettext("brand-specific detach"), 43957089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 43967089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 43977089Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 43987089Sgjelinek 43998301Sgerald.jelinek@sun.com if (execute) { 44008301Sgerald.jelinek@sun.com assert(lockfd >= 0); 44017089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 44028301Sgerald.jelinek@sun.com lockfd = -1; 44038301Sgerald.jelinek@sun.com } 44048301Sgerald.jelinek@sun.com 44058301Sgerald.jelinek@sun.com assert(lockfd == -1); 44067089Sgjelinek return (Z_ERR); 44077089Sgjelinek } 44087089Sgjelinek 44097089Sgjelinek } else { 44108301Sgerald.jelinek@sun.com zone_dochandle_t handle; 44118301Sgerald.jelinek@sun.com 44127089Sgjelinek /* If just help, we're done since there is no brand help. */ 44138301Sgerald.jelinek@sun.com if (brand_help) { 44148301Sgerald.jelinek@sun.com assert(lockfd == -1); 44157089Sgjelinek return (Z_OK); 44168301Sgerald.jelinek@sun.com } 44177089Sgjelinek 44187089Sgjelinek /* 44197089Sgjelinek * Run the built-in detach support. Just generate a simple 44207089Sgjelinek * zone definition XML file and detach. 44217089Sgjelinek */ 44227089Sgjelinek 44237089Sgjelinek /* Don't detach the zone if anything is still mounted there */ 44247089Sgjelinek if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) { 44257089Sgjelinek (void) fprintf(stderr, gettext("These file systems are " 44267089Sgjelinek "mounted on subdirectories of %s.\n"), zonepath); 44277089Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 44287089Sgjelinek err = ZONE_SUBPROC_NOTCOMPLETE; 44297089Sgjelinek goto done; 44307089Sgjelinek } 44317089Sgjelinek 44327089Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 44337089Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 44347089Sgjelinek err = ZONE_SUBPROC_NOTCOMPLETE; 44357089Sgjelinek goto done; 44367089Sgjelinek } 44377089Sgjelinek 44387089Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 44397089Sgjelinek errno = err; 44407089Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 44418301Sgerald.jelinek@sun.com 44428301Sgerald.jelinek@sun.com } else if ((err = zonecfg_detach_save(handle, 44437089Sgjelinek (execute ? 0 : ZONE_DRY_RUN))) != Z_OK) { 44447089Sgjelinek errno = err; 44457089Sgjelinek zperror(gettext("saving the detach manifest failed"), 44467089Sgjelinek B_TRUE); 44477089Sgjelinek } 44487089Sgjelinek 44497089Sgjelinek zonecfg_fini_handle(handle); 44508301Sgerald.jelinek@sun.com if (err != Z_OK) 44518301Sgerald.jelinek@sun.com goto done; 44521507Sgjelinek } 44531507Sgjelinek 44542078Sgjelinek /* 44552078Sgjelinek * Set the zone state back to configured unless we are running with the 44562078Sgjelinek * no-execute option. 44572078Sgjelinek */ 44582078Sgjelinek if (execute && (err = zone_set_state(target_zone, 44592078Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 44601507Sgjelinek errno = err; 44611507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 44621507Sgjelinek } 44631507Sgjelinek 44641507Sgjelinek done: 44658301Sgerald.jelinek@sun.com if (execute) { 44668301Sgerald.jelinek@sun.com assert(lockfd >= 0); 44677089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 44688301Sgerald.jelinek@sun.com lockfd = -1; 44698301Sgerald.jelinek@sun.com } 44708301Sgerald.jelinek@sun.com 44718301Sgerald.jelinek@sun.com assert(lockfd == -1); 44721507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 44731507Sgjelinek } 44741507Sgjelinek 44751507Sgjelinek /* 44767089Sgjelinek * Determine the brand when doing a dry-run attach. The zone does not have to 44777089Sgjelinek * exist, so we have to read the incoming manifest to determine the zone's 44787089Sgjelinek * brand. 44797089Sgjelinek * 44807089Sgjelinek * Because the manifest has to be processed twice; once to determine the brand 44817089Sgjelinek * and once to do the brand-specific attach logic, we always read it into a tmp 44827089Sgjelinek * file. This handles the manifest coming from stdin or a regular file. The 44837089Sgjelinek * tmpname parameter returns the name of the temporary file that the manifest 44847089Sgjelinek * was read into. 44851507Sgjelinek */ 44861507Sgjelinek static int 44877089Sgjelinek dryrun_get_brand(char *manifest_path, char *tmpname, int size) 44882078Sgjelinek { 44892078Sgjelinek int fd; 44902078Sgjelinek int err; 44917089Sgjelinek int res = Z_OK; 44922078Sgjelinek zone_dochandle_t local_handle; 44932078Sgjelinek zone_dochandle_t rem_handle = NULL; 44947089Sgjelinek int len; 44957089Sgjelinek int ofd; 44967089Sgjelinek char buf[512]; 44972078Sgjelinek 44982078Sgjelinek if (strcmp(manifest_path, "-") == 0) { 44997089Sgjelinek fd = STDIN_FILENO; 45007089Sgjelinek } else { 45017089Sgjelinek if ((fd = open(manifest_path, O_RDONLY)) < 0) { 45027089Sgjelinek if (getcwd(buf, sizeof (buf)) == NULL) 45037089Sgjelinek (void) strlcpy(buf, "/", sizeof (buf)); 45047089Sgjelinek zerror(gettext("could not open manifest path %s%s: %s"), 45057089Sgjelinek (*manifest_path == '/' ? "" : buf), manifest_path, 45067089Sgjelinek strerror(errno)); 45077089Sgjelinek return (Z_ERR); 45087089Sgjelinek } 45097089Sgjelinek } 45107089Sgjelinek 45117089Sgjelinek (void) snprintf(tmpname, size, "/var/run/zone.%d", getpid()); 45127089Sgjelinek 45137089Sgjelinek if ((ofd = open(tmpname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { 45147089Sgjelinek zperror(gettext("could not save manifest"), B_FALSE); 45157089Sgjelinek (void) close(fd); 45167089Sgjelinek return (Z_ERR); 45177089Sgjelinek } 45187089Sgjelinek 45197089Sgjelinek while ((len = read(fd, buf, sizeof (buf))) > 0) { 45207089Sgjelinek if (write(ofd, buf, len) == -1) { 45217089Sgjelinek zperror(gettext("could not save manifest"), B_FALSE); 45227089Sgjelinek (void) close(ofd); 45237089Sgjelinek (void) close(fd); 45247089Sgjelinek return (Z_ERR); 45257089Sgjelinek } 45267089Sgjelinek } 45277089Sgjelinek 45287089Sgjelinek if (close(ofd) != 0) { 45297089Sgjelinek zperror(gettext("could not save manifest"), B_FALSE); 45307089Sgjelinek (void) close(fd); 45317089Sgjelinek return (Z_ERR); 45327089Sgjelinek } 45337089Sgjelinek 45347089Sgjelinek (void) close(fd); 45357089Sgjelinek 45367089Sgjelinek if ((fd = open(tmpname, O_RDONLY)) < 0) { 45372078Sgjelinek zperror(gettext("could not open manifest path"), B_FALSE); 45382078Sgjelinek return (Z_ERR); 45392078Sgjelinek } 45402078Sgjelinek 45412078Sgjelinek if ((local_handle = zonecfg_init_handle()) == NULL) { 45422078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 45432078Sgjelinek res = Z_ERR; 45442078Sgjelinek goto done; 45452078Sgjelinek } 45462078Sgjelinek 45472078Sgjelinek if ((rem_handle = zonecfg_init_handle()) == NULL) { 45482078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 45492078Sgjelinek res = Z_ERR; 45502078Sgjelinek goto done; 45512078Sgjelinek } 45522078Sgjelinek 45532078Sgjelinek if ((err = zonecfg_attach_manifest(fd, local_handle, rem_handle)) 45542078Sgjelinek != Z_OK) { 45553686Sgjelinek res = Z_ERR; 45563686Sgjelinek 45573686Sgjelinek if (err == Z_INVALID_DOCUMENT) { 45583686Sgjelinek struct stat st; 45593686Sgjelinek char buf[6]; 45603686Sgjelinek 45613686Sgjelinek if (strcmp(manifest_path, "-") == 0) { 45623686Sgjelinek zerror(gettext("Input is not a valid XML " 45633686Sgjelinek "file")); 45643686Sgjelinek goto done; 45653686Sgjelinek } 45663686Sgjelinek 45673686Sgjelinek if (fstat(fd, &st) == -1 || !S_ISREG(st.st_mode)) { 45683686Sgjelinek zerror(gettext("%s is not an XML file"), 45693686Sgjelinek manifest_path); 45703686Sgjelinek goto done; 45713686Sgjelinek } 45723686Sgjelinek 45733686Sgjelinek bzero(buf, sizeof (buf)); 45743686Sgjelinek (void) lseek(fd, 0L, SEEK_SET); 45753686Sgjelinek if (read(fd, buf, sizeof (buf) - 1) < 0 || 45763686Sgjelinek strncmp(buf, "<?xml", 5) != 0) 45773686Sgjelinek zerror(gettext("%s is not an XML file"), 45783686Sgjelinek manifest_path); 45793686Sgjelinek else 45803686Sgjelinek zerror(gettext("Cannot attach to an earlier " 45813686Sgjelinek "release of the operating system")); 45823686Sgjelinek } else { 45832078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 45843686Sgjelinek } 45852078Sgjelinek goto done; 45862078Sgjelinek } 45872078Sgjelinek 45887089Sgjelinek /* Retrieve remote handle brand type. */ 45893172Sgjelinek if (zonecfg_get_brand(rem_handle, target_brand, sizeof (target_brand)) 45903172Sgjelinek != Z_OK) { 45913172Sgjelinek zerror(gettext("missing or invalid brand")); 45923172Sgjelinek exit(Z_ERR); 45933172Sgjelinek } 45942078Sgjelinek 45952078Sgjelinek done: 45962078Sgjelinek zonecfg_fini_handle(local_handle); 45972078Sgjelinek zonecfg_fini_handle(rem_handle); 45987089Sgjelinek (void) close(fd); 45992078Sgjelinek 46002078Sgjelinek return ((res == Z_OK) ? Z_OK : Z_ERR); 46012078Sgjelinek } 46022078Sgjelinek 46035829Sgjelinek /* ARGSUSED */ 46041507Sgjelinek static int 46051507Sgjelinek attach_func(int argc, char *argv[]) 46061507Sgjelinek { 46078301Sgerald.jelinek@sun.com int lockfd = -1; 46081507Sgjelinek int err, arg; 46091507Sgjelinek boolean_t force = B_FALSE; 46101507Sgjelinek zone_dochandle_t handle; 46111507Sgjelinek char zonepath[MAXPATHLEN]; 46124785Sgjelinek char cmdbuf[MAXPATHLEN]; 46137089Sgjelinek char postcmdbuf[MAXPATHLEN]; 46142078Sgjelinek boolean_t execute = B_TRUE; 46157089Sgjelinek boolean_t brand_help = B_FALSE; 46162078Sgjelinek char *manifest_path; 46177089Sgjelinek char tmpmanifest[80]; 46187089Sgjelinek int manifest_pos; 46194785Sgjelinek brand_handle_t bh = NULL; 46207089Sgjelinek int status; 46218759Sgerald.jelinek@sun.com int last_index = 0; 46228759Sgerald.jelinek@sun.com int offset; 46238759Sgerald.jelinek@sun.com char *up; 46248759Sgerald.jelinek@sun.com boolean_t forced_update = B_FALSE; 46251507Sgjelinek 46261507Sgjelinek if (zonecfg_in_alt_root()) { 46271507Sgjelinek zerror(gettext("cannot attach zone in alternate root")); 46281507Sgjelinek return (Z_ERR); 46291507Sgjelinek } 46301507Sgjelinek 46317089Sgjelinek /* Check the argv string for args we handle internally */ 46321507Sgjelinek optind = 0; 46337089Sgjelinek opterr = 0; 46348759Sgerald.jelinek@sun.com while ((arg = getopt(argc, argv, "?Fn:U")) != EOF) { 46351507Sgjelinek switch (arg) { 46361507Sgjelinek case '?': 46377089Sgjelinek if (optopt == '?') { 46387089Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 46397089Sgjelinek brand_help = B_TRUE; 46407089Sgjelinek } 46417089Sgjelinek /* Ignore unknown options - may be brand specific. */ 46427089Sgjelinek break; 46431507Sgjelinek case 'F': 46441507Sgjelinek force = B_TRUE; 46451507Sgjelinek break; 46462078Sgjelinek case 'n': 46472078Sgjelinek execute = B_FALSE; 46482078Sgjelinek manifest_path = optarg; 46497089Sgjelinek manifest_pos = optind - 1; 46505829Sgjelinek break; 46518759Sgerald.jelinek@sun.com case 'U': 46528759Sgerald.jelinek@sun.com /* 46538759Sgerald.jelinek@sun.com * Undocumented 'force update' option for p2v update on 46548759Sgerald.jelinek@sun.com * attach when zone is in the incomplete state. Change 46558759Sgerald.jelinek@sun.com * the option back to 'u' and set forced_update flag. 46568759Sgerald.jelinek@sun.com */ 46578759Sgerald.jelinek@sun.com if (optind == last_index) 46588759Sgerald.jelinek@sun.com offset = optind; 46598759Sgerald.jelinek@sun.com else 46608759Sgerald.jelinek@sun.com offset = optind - 1; 46618759Sgerald.jelinek@sun.com if ((up = index(argv[offset], 'U')) != NULL) 46628759Sgerald.jelinek@sun.com *up = 'u'; 46638759Sgerald.jelinek@sun.com forced_update = B_TRUE; 46648759Sgerald.jelinek@sun.com break; 46651507Sgjelinek default: 46667089Sgjelinek /* Ignore unknown options - may be brand specific. */ 46677089Sgjelinek break; 46681507Sgjelinek } 46698759Sgerald.jelinek@sun.com last_index = optind; 46701507Sgjelinek } 46712078Sgjelinek 46727089Sgjelinek if (brand_help) { 46737089Sgjelinek force = B_FALSE; 46747089Sgjelinek execute = B_TRUE; 46757089Sgjelinek } 46767089Sgjelinek 46777089Sgjelinek /* dry-run and force flags are mutually exclusive */ 46787089Sgjelinek if (!execute && force) { 46797089Sgjelinek zerror(gettext("-F and -n flags are mutually exclusive")); 46805829Sgjelinek return (Z_ERR); 46815829Sgjelinek } 46825829Sgjelinek 46832078Sgjelinek /* 46847089Sgjelinek * If the no-execute option was specified, we don't do validation and 46857089Sgjelinek * need to figure out the brand, since there is no zone required to be 46862078Sgjelinek * configured for this option. 46872078Sgjelinek */ 46887089Sgjelinek if (execute) { 46897089Sgjelinek if (!brand_help) { 46907089Sgjelinek if (sanity_check(target_zone, CMD_ATTACH, B_FALSE, 46918759Sgerald.jelinek@sun.com B_TRUE, forced_update) != Z_OK) 46927089Sgjelinek return (Z_ERR); 46937089Sgjelinek if (verify_details(CMD_ATTACH, argv) != Z_OK) 46947089Sgjelinek return (Z_ERR); 46957089Sgjelinek } 46967089Sgjelinek 46977089Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, 46987089Sgjelinek sizeof (zonepath))) != Z_OK) { 46997089Sgjelinek errno = err; 47007089Sgjelinek zperror2(target_zone, 47017089Sgjelinek gettext("could not get zone path")); 47027089Sgjelinek return (Z_ERR); 47037089Sgjelinek } 47047089Sgjelinek } else { 47057089Sgjelinek if (dryrun_get_brand(manifest_path, tmpmanifest, 47067089Sgjelinek sizeof (tmpmanifest)) != Z_OK) 47077089Sgjelinek return (Z_ERR); 47087089Sgjelinek 47097089Sgjelinek argv[manifest_pos] = tmpmanifest; 47107089Sgjelinek target_zone = "-"; 47117089Sgjelinek (void) strlcpy(zonepath, "-", sizeof (zonepath)); 47127089Sgjelinek 47137089Sgjelinek /* Run the brand's verify_adm hook. */ 47147089Sgjelinek if (verify_brand(NULL, CMD_ATTACH, argv) != Z_OK) 47157089Sgjelinek return (Z_ERR); 47167089Sgjelinek } 47177089Sgjelinek 47187089Sgjelinek /* 47197089Sgjelinek * Fetch the attach and postattach hooks from the brand configuration. 47207089Sgjelinek */ 47214785Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 47224785Sgjelinek zerror(gettext("missing or invalid brand")); 47234785Sgjelinek return (Z_ERR); 47244785Sgjelinek } 47254785Sgjelinek 47267089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_attach, target_zone, 47277089Sgjelinek zonepath) != Z_OK) { 47287089Sgjelinek zerror("invalid brand configuration: missing attach resource"); 47297089Sgjelinek brand_close(bh); 47307089Sgjelinek return (Z_ERR); 47317089Sgjelinek } 47327089Sgjelinek 47337089Sgjelinek if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postattach, 47347089Sgjelinek target_zone, zonepath) != Z_OK) { 47354785Sgjelinek zerror("invalid brand configuration: missing postattach " 47364785Sgjelinek "resource"); 47374785Sgjelinek brand_close(bh); 47384785Sgjelinek return (Z_ERR); 47394785Sgjelinek } 47404785Sgjelinek brand_close(bh); 47414785Sgjelinek 47427089Sgjelinek /* Append all options to attach hook. */ 47437089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) 47447089Sgjelinek return (Z_ERR); 47457089Sgjelinek 47467089Sgjelinek /* Append all options to postattach hook. */ 47477089Sgjelinek if (addoptions(postcmdbuf, argv, sizeof (postcmdbuf)) != Z_OK) 47487089Sgjelinek return (Z_ERR); 47497089Sgjelinek 47507089Sgjelinek if (execute && !brand_help) { 47517089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 47527089Sgjelinek zerror(gettext("another %s may have an operation in " 47537089Sgjelinek "progress."), "zoneadm"); 47547089Sgjelinek return (Z_ERR); 47553777Sgjelinek } 47568301Sgerald.jelinek@sun.com } 47578301Sgerald.jelinek@sun.com 47588301Sgerald.jelinek@sun.com if (!force) { 47598301Sgerald.jelinek@sun.com /* 47608301Sgerald.jelinek@sun.com * Not a force-attach, so we need to actually do the work. 47618301Sgerald.jelinek@sun.com */ 47628301Sgerald.jelinek@sun.com if (cmdbuf[0] != '\0') { 47638301Sgerald.jelinek@sun.com /* Run the attach hook */ 47648759Sgerald.jelinek@sun.com status = do_subproc(cmdbuf); 47658301Sgerald.jelinek@sun.com if ((status = subproc_status(gettext("brand-specific " 47668301Sgerald.jelinek@sun.com "attach"), status, B_FALSE)) != ZONE_SUBPROC_OK) { 47678301Sgerald.jelinek@sun.com if (status == ZONE_SUBPROC_USAGE && !brand_help) 47688301Sgerald.jelinek@sun.com sub_usage(SHELP_ATTACH, CMD_ATTACH); 47698301Sgerald.jelinek@sun.com 47708301Sgerald.jelinek@sun.com if (execute && !brand_help) { 47718759Sgerald.jelinek@sun.com assert(zonecfg_lock_file_held(&lockfd)); 47728301Sgerald.jelinek@sun.com zonecfg_release_lock_file(target_zone, 47738301Sgerald.jelinek@sun.com lockfd); 47748301Sgerald.jelinek@sun.com lockfd = -1; 47758301Sgerald.jelinek@sun.com } 47768301Sgerald.jelinek@sun.com 47778301Sgerald.jelinek@sun.com assert(lockfd == -1); 47788301Sgerald.jelinek@sun.com return (Z_ERR); 47798301Sgerald.jelinek@sun.com } 47808301Sgerald.jelinek@sun.com } 47818301Sgerald.jelinek@sun.com 47828301Sgerald.jelinek@sun.com /* 47838301Sgerald.jelinek@sun.com * Else run the built-in attach support. 47848301Sgerald.jelinek@sun.com * This is a no-op since there is nothing to validate. 47858301Sgerald.jelinek@sun.com */ 47868301Sgerald.jelinek@sun.com 47878301Sgerald.jelinek@sun.com /* If dry-run or help, then we're done. */ 47888301Sgerald.jelinek@sun.com if (!execute || brand_help) { 47898301Sgerald.jelinek@sun.com if (!execute) 47908301Sgerald.jelinek@sun.com (void) unlink(tmpmanifest); 47918301Sgerald.jelinek@sun.com assert(lockfd == -1); 47928301Sgerald.jelinek@sun.com return (Z_OK); 47938301Sgerald.jelinek@sun.com } 47948301Sgerald.jelinek@sun.com } 47958301Sgerald.jelinek@sun.com 47969128Sgerald.jelinek@sun.com /* Now we can validate that the zonepath exists. */ 47979128Sgerald.jelinek@sun.com if (validate_zonepath(zonepath, CMD_ATTACH) != Z_OK) { 47989128Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("could not verify zonepath %s " 47999128Sgerald.jelinek@sun.com "because of the above errors.\n"), zonepath); 48009128Sgerald.jelinek@sun.com 48019128Sgerald.jelinek@sun.com assert(zonecfg_lock_file_held(&lockfd)); 48029128Sgerald.jelinek@sun.com zonecfg_release_lock_file(target_zone, lockfd); 48039128Sgerald.jelinek@sun.com return (Z_ERR); 48049128Sgerald.jelinek@sun.com } 48059128Sgerald.jelinek@sun.com 48068301Sgerald.jelinek@sun.com if ((handle = zonecfg_init_handle()) == NULL) { 48078301Sgerald.jelinek@sun.com zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 48088301Sgerald.jelinek@sun.com err = Z_ERR; 48098301Sgerald.jelinek@sun.com } else if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 48108301Sgerald.jelinek@sun.com errno = err; 48118301Sgerald.jelinek@sun.com zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 48128301Sgerald.jelinek@sun.com zonecfg_fini_handle(handle); 48138301Sgerald.jelinek@sun.com } else { 48148301Sgerald.jelinek@sun.com zonecfg_rm_detached(handle, force); 48158301Sgerald.jelinek@sun.com zonecfg_fini_handle(handle); 48168301Sgerald.jelinek@sun.com } 48178301Sgerald.jelinek@sun.com 48188301Sgerald.jelinek@sun.com if (err == Z_OK && 48198301Sgerald.jelinek@sun.com (err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 48201507Sgjelinek errno = err; 48211507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 48221507Sgjelinek } 48231507Sgjelinek 48248759Sgerald.jelinek@sun.com assert(zonecfg_lock_file_held(&lockfd)); 48257089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 48268301Sgerald.jelinek@sun.com lockfd = -1; 48271507Sgjelinek 48284785Sgjelinek /* If we have a brand postattach hook, run it. */ 48297089Sgjelinek if (err == Z_OK && !force && postcmdbuf[0] != '\0') { 48307089Sgjelinek status = do_subproc(postcmdbuf); 48314785Sgjelinek if (subproc_status(gettext("brand-specific postattach"), 48324785Sgjelinek status, B_FALSE) != ZONE_SUBPROC_OK) { 48334785Sgjelinek if ((err = zone_set_state(target_zone, 48344785Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 48354785Sgjelinek errno = err; 48364785Sgjelinek zperror(gettext("could not reset state"), 48374785Sgjelinek B_TRUE); 48384785Sgjelinek } 48394785Sgjelinek } 48404785Sgjelinek } 48414785Sgjelinek 48428301Sgerald.jelinek@sun.com assert(lockfd == -1); 48431507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 48441507Sgjelinek } 48451507Sgjelinek 48461300Sgjelinek /* 48470Sstevel@tonic-gate * On input, TRUE => yes, FALSE => no. 48480Sstevel@tonic-gate * On return, TRUE => 1, FALSE => 0, could not ask => -1. 48490Sstevel@tonic-gate */ 48500Sstevel@tonic-gate 48510Sstevel@tonic-gate static int 48520Sstevel@tonic-gate ask_yesno(boolean_t default_answer, const char *question) 48530Sstevel@tonic-gate { 48540Sstevel@tonic-gate char line[64]; /* should be large enough to answer yes or no */ 48550Sstevel@tonic-gate 48560Sstevel@tonic-gate if (!isatty(STDIN_FILENO)) 48570Sstevel@tonic-gate return (-1); 48580Sstevel@tonic-gate for (;;) { 48590Sstevel@tonic-gate (void) printf("%s (%s)? ", question, 48600Sstevel@tonic-gate default_answer ? "[y]/n" : "y/[n]"); 48610Sstevel@tonic-gate if (fgets(line, sizeof (line), stdin) == NULL || 48620Sstevel@tonic-gate line[0] == '\n') 48630Sstevel@tonic-gate return (default_answer ? 1 : 0); 48640Sstevel@tonic-gate if (tolower(line[0]) == 'y') 48650Sstevel@tonic-gate return (1); 48660Sstevel@tonic-gate if (tolower(line[0]) == 'n') 48670Sstevel@tonic-gate return (0); 48680Sstevel@tonic-gate } 48690Sstevel@tonic-gate } 48700Sstevel@tonic-gate 48717089Sgjelinek /* ARGSUSED */ 48720Sstevel@tonic-gate static int 48730Sstevel@tonic-gate uninstall_func(int argc, char *argv[]) 48740Sstevel@tonic-gate { 48750Sstevel@tonic-gate char line[ZONENAME_MAX + 128]; /* Enough for "Are you sure ..." */ 48761867Sgjelinek char rootpath[MAXPATHLEN], zonepath[MAXPATHLEN]; 48774785Sgjelinek char cmdbuf[MAXPATHLEN]; 48787089Sgjelinek char precmdbuf[MAXPATHLEN]; 48790Sstevel@tonic-gate boolean_t force = B_FALSE; 48800Sstevel@tonic-gate int lockfd, answer; 48810Sstevel@tonic-gate int err, arg; 48827089Sgjelinek boolean_t brand_help = B_FALSE; 48834785Sgjelinek brand_handle_t bh = NULL; 48847089Sgjelinek int status; 48850Sstevel@tonic-gate 4886766Scarlsonj if (zonecfg_in_alt_root()) { 4887766Scarlsonj zerror(gettext("cannot uninstall zone in alternate root")); 4888766Scarlsonj return (Z_ERR); 4889766Scarlsonj } 4890766Scarlsonj 48917089Sgjelinek /* Check the argv string for args we handle internally */ 48920Sstevel@tonic-gate optind = 0; 48937089Sgjelinek opterr = 0; 48940Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?F")) != EOF) { 48950Sstevel@tonic-gate switch (arg) { 48960Sstevel@tonic-gate case '?': 48977089Sgjelinek if (optopt == '?') { 48987089Sgjelinek sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 48997089Sgjelinek brand_help = B_TRUE; 49007089Sgjelinek } 49017089Sgjelinek /* Ignore unknown options - may be brand specific. */ 49027089Sgjelinek break; 49030Sstevel@tonic-gate case 'F': 49040Sstevel@tonic-gate force = B_TRUE; 49050Sstevel@tonic-gate break; 49060Sstevel@tonic-gate default: 49077089Sgjelinek /* Ignore unknown options - may be brand specific. */ 49087089Sgjelinek break; 49090Sstevel@tonic-gate } 49100Sstevel@tonic-gate } 49117089Sgjelinek 49127089Sgjelinek if (!brand_help) { 49137089Sgjelinek if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE, 49147089Sgjelinek B_FALSE) != Z_OK) 49157089Sgjelinek return (Z_ERR); 49167089Sgjelinek 49177089Sgjelinek /* 49187089Sgjelinek * Invoke brand-specific handler. 49197089Sgjelinek */ 49207089Sgjelinek if (invoke_brand_handler(CMD_UNINSTALL, argv) != Z_OK) 49210Sstevel@tonic-gate return (Z_ERR); 49227089Sgjelinek 49237089Sgjelinek if (!force) { 49247089Sgjelinek (void) snprintf(line, sizeof (line), 49257089Sgjelinek gettext("Are you sure you want to %s zone %s"), 49267089Sgjelinek cmd_to_str(CMD_UNINSTALL), target_zone); 49277089Sgjelinek if ((answer = ask_yesno(B_FALSE, line)) == 0) { 49287089Sgjelinek return (Z_OK); 49297089Sgjelinek } else if (answer == -1) { 49307089Sgjelinek zerror(gettext("Input not from terminal and -F " 49317089Sgjelinek "not specified: %s not done."), 49327089Sgjelinek cmd_to_str(CMD_UNINSTALL)); 49337089Sgjelinek return (Z_ERR); 49347089Sgjelinek } 49350Sstevel@tonic-gate } 49360Sstevel@tonic-gate } 49370Sstevel@tonic-gate 49381867Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, 49391867Sgjelinek sizeof (zonepath))) != Z_OK) { 49400Sstevel@tonic-gate errno = err; 49410Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 49420Sstevel@tonic-gate return (Z_ERR); 49430Sstevel@tonic-gate } 49440Sstevel@tonic-gate 49450Sstevel@tonic-gate /* 49467089Sgjelinek * Fetch the uninstall and preuninstall hooks from the brand 49477089Sgjelinek * configuration. 49480Sstevel@tonic-gate */ 49494785Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 49504785Sgjelinek zerror(gettext("missing or invalid brand")); 49514785Sgjelinek return (Z_ERR); 49524785Sgjelinek } 49534785Sgjelinek 49547089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_uninstall, 49557089Sgjelinek target_zone, zonepath) != Z_OK) { 49567089Sgjelinek zerror("invalid brand configuration: missing uninstall " 49577089Sgjelinek "resource"); 49587089Sgjelinek brand_close(bh); 49597089Sgjelinek return (Z_ERR); 49607089Sgjelinek } 49617089Sgjelinek 49627089Sgjelinek if (get_hook(bh, precmdbuf, sizeof (precmdbuf), brand_get_preuninstall, 49637089Sgjelinek target_zone, zonepath) != Z_OK) { 49644785Sgjelinek zerror("invalid brand configuration: missing preuninstall " 49654785Sgjelinek "resource"); 49664785Sgjelinek brand_close(bh); 49674785Sgjelinek return (Z_ERR); 49684785Sgjelinek } 49694785Sgjelinek brand_close(bh); 49704785Sgjelinek 49717089Sgjelinek /* Append all options to preuninstall hook. */ 49727089Sgjelinek if (addoptions(precmdbuf, argv, sizeof (precmdbuf)) != Z_OK) 49737089Sgjelinek return (Z_ERR); 49747089Sgjelinek 49757089Sgjelinek /* Append all options to uninstall hook. */ 49767089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) 49777089Sgjelinek return (Z_ERR); 49787089Sgjelinek 49797089Sgjelinek if (!brand_help) { 49807089Sgjelinek if ((err = zone_get_rootpath(target_zone, rootpath, 49817089Sgjelinek sizeof (rootpath))) != Z_OK) { 49827089Sgjelinek errno = err; 49837089Sgjelinek zperror2(target_zone, gettext("could not get root " 49847089Sgjelinek "path")); 49854785Sgjelinek return (Z_ERR); 49864785Sgjelinek } 49874785Sgjelinek 49887089Sgjelinek /* 49897089Sgjelinek * If there seems to be a zoneadmd running for this zone, call 49907089Sgjelinek * it to tell it that an uninstall is happening; if all goes 49917089Sgjelinek * well it will then shut itself down. 49927089Sgjelinek */ 49937089Sgjelinek if (zonecfg_ping_zoneadmd(target_zone) == Z_OK) { 49947089Sgjelinek zone_cmd_arg_t zarg; 49957089Sgjelinek zarg.cmd = Z_NOTE_UNINSTALLING; 49967089Sgjelinek /* we don't care too much if this fails, just plow on */ 49977089Sgjelinek (void) zonecfg_call_zoneadmd(target_zone, &zarg, locale, 49987089Sgjelinek B_TRUE); 49997089Sgjelinek } 50007089Sgjelinek 50017089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 50027089Sgjelinek zerror(gettext("another %s may have an operation in " 50037089Sgjelinek "progress."), "zoneadm"); 50047089Sgjelinek return (Z_ERR); 50057089Sgjelinek } 50067089Sgjelinek 50077089Sgjelinek /* Don't uninstall the zone if anything is mounted there */ 50087089Sgjelinek err = zonecfg_find_mounts(rootpath, NULL, NULL); 50097089Sgjelinek if (err) { 50107089Sgjelinek zerror(gettext("These file systems are mounted on " 50117089Sgjelinek "subdirectories of %s.\n"), rootpath); 50127089Sgjelinek (void) zonecfg_find_mounts(rootpath, zfm_print, NULL); 50137089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50147089Sgjelinek return (Z_ERR); 50157089Sgjelinek } 50167089Sgjelinek } 50177089Sgjelinek 50187089Sgjelinek /* If we have a brand preuninstall hook, run it. */ 50197089Sgjelinek if (!brand_help && precmdbuf[0] != '\0') { 5020*11771Sgerald.jelinek@sun.com status = do_subproc(precmdbuf); 50214785Sgjelinek if (subproc_status(gettext("brand-specific preuninstall"), 50224785Sgjelinek status, B_FALSE) != ZONE_SUBPROC_OK) { 50237089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50244785Sgjelinek return (Z_ERR); 50254785Sgjelinek } 50264785Sgjelinek } 50274785Sgjelinek 50287089Sgjelinek if (!brand_help) { 50297089Sgjelinek err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 50307089Sgjelinek if (err != Z_OK) { 50317089Sgjelinek errno = err; 50327089Sgjelinek zperror2(target_zone, gettext("could not set state")); 50337089Sgjelinek goto bad; 50347089Sgjelinek } 50357089Sgjelinek } 50367089Sgjelinek 50377089Sgjelinek /* 50387089Sgjelinek * If there is a brand uninstall hook, use it, otherwise use the 50397089Sgjelinek * built-in uninstall code. 50407089Sgjelinek */ 50417089Sgjelinek if (cmdbuf[0] != '\0') { 50427089Sgjelinek /* Run the uninstall hook */ 50439310Sgerald.jelinek@sun.com status = do_subproc(cmdbuf); 50447089Sgjelinek if ((status = subproc_status(gettext("brand-specific " 50457089Sgjelinek "uninstall"), status, B_FALSE)) != ZONE_SUBPROC_OK) { 50467089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 50477089Sgjelinek sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 50487089Sgjelinek if (!brand_help) 50497089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50507089Sgjelinek return (Z_ERR); 50517089Sgjelinek } 50527089Sgjelinek 50537089Sgjelinek if (brand_help) 50547089Sgjelinek return (Z_OK); 50557089Sgjelinek } else { 50567089Sgjelinek /* If just help, we're done since there is no brand help. */ 50577089Sgjelinek if (brand_help) 50587089Sgjelinek return (Z_OK); 50597089Sgjelinek 50607089Sgjelinek /* Run the built-in uninstall support. */ 50617089Sgjelinek if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) { 50627089Sgjelinek errno = err; 50637089Sgjelinek zperror2(target_zone, gettext("cleaning up zonepath " 50647089Sgjelinek "failed")); 50657089Sgjelinek goto bad; 50667089Sgjelinek } 50671867Sgjelinek } 50681867Sgjelinek 50690Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED); 50700Sstevel@tonic-gate if (err != Z_OK) { 50710Sstevel@tonic-gate errno = err; 50720Sstevel@tonic-gate zperror2(target_zone, gettext("could not reset state")); 50730Sstevel@tonic-gate } 50740Sstevel@tonic-gate bad: 50757089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50760Sstevel@tonic-gate return (err); 50770Sstevel@tonic-gate } 50780Sstevel@tonic-gate 5079766Scarlsonj /* ARGSUSED */ 5080766Scarlsonj static int 5081766Scarlsonj mount_func(int argc, char *argv[]) 5082766Scarlsonj { 5083766Scarlsonj zone_cmd_arg_t zarg; 50842712Snn35248 boolean_t force = B_FALSE; 50852712Snn35248 int arg; 50862712Snn35248 50872712Snn35248 /* 50882712Snn35248 * The only supported subargument to the "mount" subcommand is 50892712Snn35248 * "-f", which forces us to mount a zone in the INCOMPLETE state. 50902712Snn35248 */ 50912712Snn35248 optind = 0; 50922712Snn35248 if ((arg = getopt(argc, argv, "f")) != EOF) { 50932712Snn35248 switch (arg) { 50942712Snn35248 case 'f': 50952712Snn35248 force = B_TRUE; 50962712Snn35248 break; 50972712Snn35248 default: 50982712Snn35248 return (Z_USAGE); 50992712Snn35248 } 51002712Snn35248 } 51012712Snn35248 if (argc > optind) 5102766Scarlsonj return (Z_USAGE); 51032712Snn35248 51042712Snn35248 if (sanity_check(target_zone, CMD_MOUNT, B_FALSE, B_FALSE, force) 51052712Snn35248 != Z_OK) 5106766Scarlsonj return (Z_ERR); 51073339Szt129084 if (verify_details(CMD_MOUNT, argv) != Z_OK) 5108766Scarlsonj return (Z_ERR); 5109766Scarlsonj 51102712Snn35248 zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT; 51115829Sgjelinek zarg.bootbuf[0] = '\0'; 51127089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 5113766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 5114766Scarlsonj return (Z_ERR); 5115766Scarlsonj } 5116766Scarlsonj return (Z_OK); 5117766Scarlsonj } 5118766Scarlsonj 5119766Scarlsonj /* ARGSUSED */ 5120766Scarlsonj static int 5121766Scarlsonj unmount_func(int argc, char *argv[]) 5122766Scarlsonj { 5123766Scarlsonj zone_cmd_arg_t zarg; 5124766Scarlsonj 5125766Scarlsonj if (argc > 0) 5126766Scarlsonj return (Z_USAGE); 51272712Snn35248 if (sanity_check(target_zone, CMD_UNMOUNT, B_FALSE, B_FALSE, B_FALSE) 51282712Snn35248 != Z_OK) 5129766Scarlsonj return (Z_ERR); 5130766Scarlsonj 5131766Scarlsonj zarg.cmd = Z_UNMOUNT; 51327089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 5133766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 5134766Scarlsonj return (Z_ERR); 5135766Scarlsonj } 5136766Scarlsonj return (Z_OK); 5137766Scarlsonj } 5138766Scarlsonj 51390Sstevel@tonic-gate static int 51402303Scarlsonj mark_func(int argc, char *argv[]) 51412303Scarlsonj { 51422303Scarlsonj int err, lockfd; 51438759Sgerald.jelinek@sun.com int arg; 51448759Sgerald.jelinek@sun.com boolean_t force = B_FALSE; 51458759Sgerald.jelinek@sun.com int state; 51468759Sgerald.jelinek@sun.com 51478759Sgerald.jelinek@sun.com optind = 0; 51488759Sgerald.jelinek@sun.com opterr = 0; 51498759Sgerald.jelinek@sun.com while ((arg = getopt(argc, argv, "F")) != EOF) { 51508759Sgerald.jelinek@sun.com switch (arg) { 51518759Sgerald.jelinek@sun.com case 'F': 51528759Sgerald.jelinek@sun.com force = B_TRUE; 51538759Sgerald.jelinek@sun.com break; 51548759Sgerald.jelinek@sun.com default: 51558759Sgerald.jelinek@sun.com return (Z_USAGE); 51568759Sgerald.jelinek@sun.com } 51578759Sgerald.jelinek@sun.com } 51588759Sgerald.jelinek@sun.com 51598759Sgerald.jelinek@sun.com if (argc != (optind + 1)) 51602303Scarlsonj return (Z_USAGE); 51618759Sgerald.jelinek@sun.com 51628759Sgerald.jelinek@sun.com if (strcmp(argv[optind], "configured") == 0) 51638759Sgerald.jelinek@sun.com state = ZONE_STATE_CONFIGURED; 51648759Sgerald.jelinek@sun.com else if (strcmp(argv[optind], "incomplete") == 0) 51658759Sgerald.jelinek@sun.com state = ZONE_STATE_INCOMPLETE; 51668759Sgerald.jelinek@sun.com else if (strcmp(argv[optind], "installed") == 0) 51678759Sgerald.jelinek@sun.com state = ZONE_STATE_INSTALLED; 51688759Sgerald.jelinek@sun.com else 51698759Sgerald.jelinek@sun.com return (Z_USAGE); 51708759Sgerald.jelinek@sun.com 51718759Sgerald.jelinek@sun.com if (state != ZONE_STATE_INCOMPLETE && !force) 51728759Sgerald.jelinek@sun.com return (Z_USAGE); 51738759Sgerald.jelinek@sun.com 51748759Sgerald.jelinek@sun.com if (sanity_check(target_zone, CMD_MARK, B_FALSE, B_TRUE, B_FALSE) 51752712Snn35248 != Z_OK) 51762303Scarlsonj return (Z_ERR); 51772303Scarlsonj 51783339Szt129084 /* 51793339Szt129084 * Invoke brand-specific handler. 51803339Szt129084 */ 51813339Szt129084 if (invoke_brand_handler(CMD_MARK, argv) != Z_OK) 51823339Szt129084 return (Z_ERR); 51833339Szt129084 51847089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 51852303Scarlsonj zerror(gettext("another %s may have an operation in progress."), 51862303Scarlsonj "zoneadm"); 51872303Scarlsonj return (Z_ERR); 51882303Scarlsonj } 51892303Scarlsonj 51908759Sgerald.jelinek@sun.com err = zone_set_state(target_zone, state); 51912303Scarlsonj if (err != Z_OK) { 51922303Scarlsonj errno = err; 51932303Scarlsonj zperror2(target_zone, gettext("could not set state")); 51942303Scarlsonj } 51957089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 51962303Scarlsonj 51972303Scarlsonj return (err); 51982303Scarlsonj } 51992303Scarlsonj 52003247Sgjelinek /* 52013247Sgjelinek * Check what scheduling class we're running under and print a warning if 52023247Sgjelinek * we're not using FSS. 52033247Sgjelinek */ 52043247Sgjelinek static int 52053247Sgjelinek check_sched_fss(zone_dochandle_t handle) 52063247Sgjelinek { 52073247Sgjelinek char class_name[PC_CLNMSZ]; 52083247Sgjelinek 52093247Sgjelinek if (zonecfg_get_dflt_sched_class(handle, class_name, 52103247Sgjelinek sizeof (class_name)) != Z_OK) { 52113247Sgjelinek zerror(gettext("WARNING: unable to determine the zone's " 52123247Sgjelinek "scheduling class")); 52133247Sgjelinek } else if (strcmp("FSS", class_name) != 0) { 52143247Sgjelinek zerror(gettext("WARNING: The zone.cpu-shares rctl is set but\n" 52153247Sgjelinek "FSS is not the default scheduling class for this zone. " 52163247Sgjelinek "FSS will be\nused for processes in the zone but to get " 52173247Sgjelinek "the full benefit of FSS,\nit should be the default " 52183247Sgjelinek "scheduling class. See dispadmin(1M) for\nmore details.")); 52193247Sgjelinek return (Z_SYSTEM); 52203247Sgjelinek } 52213247Sgjelinek 52223247Sgjelinek return (Z_OK); 52233247Sgjelinek } 52243247Sgjelinek 52253247Sgjelinek static int 52263247Sgjelinek check_cpu_shares_sched(zone_dochandle_t handle) 52273247Sgjelinek { 52283247Sgjelinek int err; 52293247Sgjelinek int res = Z_OK; 52303247Sgjelinek struct zone_rctltab rctl; 52313247Sgjelinek 52323247Sgjelinek if ((err = zonecfg_setrctlent(handle)) != Z_OK) { 52333247Sgjelinek errno = err; 52343247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 52353247Sgjelinek return (err); 52363247Sgjelinek } 52373247Sgjelinek 52383247Sgjelinek while (zonecfg_getrctlent(handle, &rctl) == Z_OK) { 52393247Sgjelinek if (strcmp(rctl.zone_rctl_name, "zone.cpu-shares") == 0) { 52403247Sgjelinek if (check_sched_fss(handle) != Z_OK) 52413247Sgjelinek res = Z_SYSTEM; 52423247Sgjelinek break; 52433247Sgjelinek } 52443247Sgjelinek } 52453247Sgjelinek 52463247Sgjelinek (void) zonecfg_endrctlent(handle); 52473247Sgjelinek 52483247Sgjelinek return (res); 52493247Sgjelinek } 52503247Sgjelinek 52513247Sgjelinek /* 52523352Sgjelinek * Check if there is a mix of processes running in different pools within the 52533352Sgjelinek * zone. This is currently only going to be called for the global zone from 52543352Sgjelinek * apply_func but that could be generalized in the future. 52553352Sgjelinek */ 52563352Sgjelinek static boolean_t 52573352Sgjelinek mixed_pools(zoneid_t zoneid) 52583352Sgjelinek { 52593352Sgjelinek DIR *dirp; 52603352Sgjelinek dirent_t *dent; 52613352Sgjelinek boolean_t mixed = B_FALSE; 52623352Sgjelinek boolean_t poolid_set = B_FALSE; 52633352Sgjelinek poolid_t last_poolid = 0; 52643352Sgjelinek 52653352Sgjelinek if ((dirp = opendir("/proc")) == NULL) { 52663352Sgjelinek zerror(gettext("could not open /proc")); 52673352Sgjelinek return (B_FALSE); 52683352Sgjelinek } 52693352Sgjelinek 52703352Sgjelinek while ((dent = readdir(dirp)) != NULL) { 52713352Sgjelinek int procfd; 52723352Sgjelinek psinfo_t ps; 52733352Sgjelinek char procpath[MAXPATHLEN]; 52743352Sgjelinek 52753352Sgjelinek if (dent->d_name[0] == '.') 52763352Sgjelinek continue; 52773352Sgjelinek 52783352Sgjelinek (void) snprintf(procpath, sizeof (procpath), "/proc/%s/psinfo", 52793352Sgjelinek dent->d_name); 52803352Sgjelinek 52813352Sgjelinek if ((procfd = open(procpath, O_RDONLY)) == -1) 52823352Sgjelinek continue; 52833352Sgjelinek 52843352Sgjelinek if (read(procfd, &ps, sizeof (ps)) == sizeof (psinfo_t)) { 52853352Sgjelinek /* skip processes in other zones and system processes */ 52863352Sgjelinek if (zoneid != ps.pr_zoneid || ps.pr_flag & SSYS) { 52873352Sgjelinek (void) close(procfd); 52883352Sgjelinek continue; 52893352Sgjelinek } 52903352Sgjelinek 52913352Sgjelinek if (poolid_set) { 52923352Sgjelinek if (ps.pr_poolid != last_poolid) 52933352Sgjelinek mixed = B_TRUE; 52943352Sgjelinek } else { 52953352Sgjelinek last_poolid = ps.pr_poolid; 52963352Sgjelinek poolid_set = B_TRUE; 52973352Sgjelinek } 52983352Sgjelinek } 52993352Sgjelinek 53003352Sgjelinek (void) close(procfd); 53013352Sgjelinek 53023352Sgjelinek if (mixed) 53033352Sgjelinek break; 53043352Sgjelinek } 53053352Sgjelinek 53063352Sgjelinek (void) closedir(dirp); 53073352Sgjelinek 53083352Sgjelinek return (mixed); 53093352Sgjelinek } 53103352Sgjelinek 53113352Sgjelinek /* 53123352Sgjelinek * Check if a persistent or temporary pool is configured for the zone. 53133352Sgjelinek * This is currently only going to be called for the global zone from 53143352Sgjelinek * apply_func but that could be generalized in the future. 53153352Sgjelinek */ 53163352Sgjelinek static boolean_t 53173352Sgjelinek pool_configured(zone_dochandle_t handle) 53183352Sgjelinek { 53193352Sgjelinek int err1, err2; 53203352Sgjelinek struct zone_psettab pset_tab; 53213352Sgjelinek char poolname[MAXPATHLEN]; 53223352Sgjelinek 53233352Sgjelinek err1 = zonecfg_lookup_pset(handle, &pset_tab); 53243352Sgjelinek err2 = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 53253352Sgjelinek 53263352Sgjelinek if (err1 == Z_NO_ENTRY && 53273352Sgjelinek (err2 == Z_NO_ENTRY || (err2 == Z_OK && strlen(poolname) == 0))) 53283352Sgjelinek return (B_FALSE); 53293352Sgjelinek 53303352Sgjelinek return (B_TRUE); 53313352Sgjelinek } 53323352Sgjelinek 53333352Sgjelinek /* 53343247Sgjelinek * This is an undocumented interface which is currently only used to apply 53353247Sgjelinek * the global zone resource management settings when the system boots. 53363247Sgjelinek * This function does not yet properly handle updating a running system so 53373247Sgjelinek * any projects running in the zone would be trashed if this function 53383247Sgjelinek * were to run after the zone had booted. It also does not reset any 53393247Sgjelinek * rctl settings that were removed from zonecfg. There is still work to be 53403247Sgjelinek * done before we can properly support dynamically updating the resource 53413247Sgjelinek * management settings for a running zone (global or non-global). Thus, this 53423247Sgjelinek * functionality is undocumented for now. 53433247Sgjelinek */ 53443247Sgjelinek /* ARGSUSED */ 53453247Sgjelinek static int 53463247Sgjelinek apply_func(int argc, char *argv[]) 53473247Sgjelinek { 53483247Sgjelinek int err; 53493247Sgjelinek int res = Z_OK; 53503247Sgjelinek priv_set_t *privset; 53513247Sgjelinek zoneid_t zoneid; 53523247Sgjelinek zone_dochandle_t handle; 53533247Sgjelinek struct zone_mcaptab mcap; 53543247Sgjelinek char pool_err[128]; 53553247Sgjelinek 53563247Sgjelinek zoneid = getzoneid(); 53573247Sgjelinek 53583247Sgjelinek if (zonecfg_in_alt_root() || zoneid != GLOBAL_ZONEID || 53593247Sgjelinek target_zone == NULL || strcmp(target_zone, GLOBAL_ZONENAME) != 0) 53603247Sgjelinek return (usage(B_FALSE)); 53613247Sgjelinek 53623247Sgjelinek if ((privset = priv_allocset()) == NULL) { 53633247Sgjelinek zerror(gettext("%s failed"), "priv_allocset"); 53643247Sgjelinek return (Z_ERR); 53653247Sgjelinek } 53663247Sgjelinek 53673247Sgjelinek if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 53683247Sgjelinek zerror(gettext("%s failed"), "getppriv"); 53693247Sgjelinek priv_freeset(privset); 53703247Sgjelinek return (Z_ERR); 53713247Sgjelinek } 53723247Sgjelinek 53733247Sgjelinek if (priv_isfullset(privset) == B_FALSE) { 53743247Sgjelinek (void) usage(B_FALSE); 53753247Sgjelinek priv_freeset(privset); 53763247Sgjelinek return (Z_ERR); 53773247Sgjelinek } 53783247Sgjelinek priv_freeset(privset); 53793247Sgjelinek 53803247Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 53813247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 53823247Sgjelinek return (Z_ERR); 53833247Sgjelinek } 53843247Sgjelinek 53853247Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 53863247Sgjelinek errno = err; 53873247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 53883247Sgjelinek zonecfg_fini_handle(handle); 53893247Sgjelinek return (Z_ERR); 53903247Sgjelinek } 53913247Sgjelinek 53923247Sgjelinek /* specific error msgs are printed within apply_rctls */ 53933247Sgjelinek if ((err = zonecfg_apply_rctls(target_zone, handle)) != Z_OK) { 53943247Sgjelinek errno = err; 53953247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 53963247Sgjelinek res = Z_ERR; 53973247Sgjelinek } 53983247Sgjelinek 53993247Sgjelinek if ((err = check_cpu_shares_sched(handle)) != Z_OK) 54003247Sgjelinek res = Z_ERR; 54013247Sgjelinek 54023352Sgjelinek if (pool_configured(handle)) { 54033352Sgjelinek if (mixed_pools(zoneid)) { 54043352Sgjelinek zerror(gettext("Zone is using multiple resource " 54053352Sgjelinek "pools. The pool\nconfiguration cannot be " 54063352Sgjelinek "applied without rebooting.")); 54073352Sgjelinek res = Z_ERR; 54083352Sgjelinek } else { 54093352Sgjelinek 54103352Sgjelinek /* 54113352Sgjelinek * The next two blocks of code attempt to set up 54123352Sgjelinek * temporary pools as well as persistent pools. In 54133352Sgjelinek * both cases we call the functions unconditionally. 54143352Sgjelinek * Within each funtion the code will check if the zone 54153352Sgjelinek * is actually configured for a temporary pool or 54163352Sgjelinek * persistent pool and just return if there is nothing 54173352Sgjelinek * to do. 54183352Sgjelinek */ 54193352Sgjelinek if ((err = zonecfg_bind_tmp_pool(handle, zoneid, 54203352Sgjelinek pool_err, sizeof (pool_err))) != Z_OK) { 54213352Sgjelinek if (err == Z_POOL || err == Z_POOL_CREATE || 54223352Sgjelinek err == Z_POOL_BIND) 54233352Sgjelinek zerror("%s: %s", zonecfg_strerror(err), 54243352Sgjelinek pool_err); 54253352Sgjelinek else 54263352Sgjelinek zerror(gettext("could not bind zone to " 54273352Sgjelinek "temporary pool: %s"), 54283352Sgjelinek zonecfg_strerror(err)); 54293352Sgjelinek res = Z_ERR; 54303352Sgjelinek } 54313352Sgjelinek 54323352Sgjelinek if ((err = zonecfg_bind_pool(handle, zoneid, pool_err, 54333352Sgjelinek sizeof (pool_err))) != Z_OK) { 54343352Sgjelinek if (err == Z_POOL || err == Z_POOL_BIND) 54353352Sgjelinek zerror("%s: %s", zonecfg_strerror(err), 54363352Sgjelinek pool_err); 54373352Sgjelinek else 54383352Sgjelinek zerror("%s", zonecfg_strerror(err)); 54393352Sgjelinek } 54403352Sgjelinek } 54413247Sgjelinek } 54423247Sgjelinek 54433247Sgjelinek /* 54443247Sgjelinek * If a memory cap is configured, set the cap in the kernel using 54453247Sgjelinek * zone_setattr() and make sure the rcapd SMF service is enabled. 54463247Sgjelinek */ 54473247Sgjelinek if (zonecfg_getmcapent(handle, &mcap) == Z_OK) { 54483247Sgjelinek uint64_t num; 54493247Sgjelinek char smf_err[128]; 54503247Sgjelinek 54513247Sgjelinek num = (uint64_t)strtoll(mcap.zone_physmem_cap, NULL, 10); 54523247Sgjelinek if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { 54533247Sgjelinek zerror(gettext("could not set zone memory cap")); 54543247Sgjelinek res = Z_ERR; 54553247Sgjelinek } 54563247Sgjelinek 54573247Sgjelinek if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) { 54583247Sgjelinek zerror(gettext("enabling system/rcap service failed: " 54593247Sgjelinek "%s"), smf_err); 54603247Sgjelinek res = Z_ERR; 54613247Sgjelinek } 54623247Sgjelinek } 54633247Sgjelinek 54643247Sgjelinek zonecfg_fini_handle(handle); 54653247Sgjelinek 54663247Sgjelinek return (res); 54673247Sgjelinek } 54683247Sgjelinek 546910718SJordan.Vaughan@Sun.com /* 547010718SJordan.Vaughan@Sun.com * This is an undocumented interface that is invoked by the zones SMF service 547110718SJordan.Vaughan@Sun.com * for installed zones that won't automatically boot. 547210718SJordan.Vaughan@Sun.com */ 547310718SJordan.Vaughan@Sun.com /* ARGSUSED */ 547410718SJordan.Vaughan@Sun.com static int 547510718SJordan.Vaughan@Sun.com sysboot_func(int argc, char *argv[]) 547610718SJordan.Vaughan@Sun.com { 547710718SJordan.Vaughan@Sun.com int err; 547810718SJordan.Vaughan@Sun.com zone_dochandle_t zone_handle; 547910718SJordan.Vaughan@Sun.com brand_handle_t brand_handle; 548010718SJordan.Vaughan@Sun.com char cmdbuf[MAXPATHLEN]; 548110718SJordan.Vaughan@Sun.com char zonepath[MAXPATHLEN]; 548210718SJordan.Vaughan@Sun.com 548310718SJordan.Vaughan@Sun.com /* 548410718SJordan.Vaughan@Sun.com * This subcommand can only be executed in the global zone on non-global 548510718SJordan.Vaughan@Sun.com * zones. 548610718SJordan.Vaughan@Sun.com */ 548710718SJordan.Vaughan@Sun.com if (zonecfg_in_alt_root()) 548810718SJordan.Vaughan@Sun.com return (usage(B_FALSE)); 548910718SJordan.Vaughan@Sun.com if (sanity_check(target_zone, CMD_SYSBOOT, B_FALSE, B_TRUE, B_FALSE) != 549010718SJordan.Vaughan@Sun.com Z_OK) 549110718SJordan.Vaughan@Sun.com return (Z_ERR); 549210718SJordan.Vaughan@Sun.com 549310718SJordan.Vaughan@Sun.com /* 549410718SJordan.Vaughan@Sun.com * Fetch the sysboot hook from the target zone's brand. 549510718SJordan.Vaughan@Sun.com */ 549610718SJordan.Vaughan@Sun.com if ((zone_handle = zonecfg_init_handle()) == NULL) { 549710718SJordan.Vaughan@Sun.com zperror(cmd_to_str(CMD_SYSBOOT), B_TRUE); 549810718SJordan.Vaughan@Sun.com return (Z_ERR); 549910718SJordan.Vaughan@Sun.com } 550010718SJordan.Vaughan@Sun.com if ((err = zonecfg_get_handle(target_zone, zone_handle)) != Z_OK) { 550110718SJordan.Vaughan@Sun.com errno = err; 550210718SJordan.Vaughan@Sun.com zperror(cmd_to_str(CMD_SYSBOOT), B_TRUE); 550310718SJordan.Vaughan@Sun.com zonecfg_fini_handle(zone_handle); 550410718SJordan.Vaughan@Sun.com return (Z_ERR); 550510718SJordan.Vaughan@Sun.com } 550610718SJordan.Vaughan@Sun.com if ((err = zonecfg_get_zonepath(zone_handle, zonepath, 550710718SJordan.Vaughan@Sun.com sizeof (zonepath))) != Z_OK) { 550810718SJordan.Vaughan@Sun.com errno = err; 550910718SJordan.Vaughan@Sun.com zperror(cmd_to_str(CMD_SYSBOOT), B_TRUE); 551010718SJordan.Vaughan@Sun.com zonecfg_fini_handle(zone_handle); 551110718SJordan.Vaughan@Sun.com return (Z_ERR); 551210718SJordan.Vaughan@Sun.com } 551310718SJordan.Vaughan@Sun.com if ((brand_handle = brand_open(target_brand)) == NULL) { 551410718SJordan.Vaughan@Sun.com zerror(gettext("missing or invalid brand during %s operation: " 551510718SJordan.Vaughan@Sun.com "%s"), cmd_to_str(CMD_SYSBOOT), target_brand); 551610718SJordan.Vaughan@Sun.com zonecfg_fini_handle(zone_handle); 551710718SJordan.Vaughan@Sun.com return (Z_ERR); 551810718SJordan.Vaughan@Sun.com } 551910718SJordan.Vaughan@Sun.com err = get_hook(brand_handle, cmdbuf, sizeof (cmdbuf), brand_get_sysboot, 552010718SJordan.Vaughan@Sun.com target_zone, zonepath); 552110718SJordan.Vaughan@Sun.com brand_close(brand_handle); 552210718SJordan.Vaughan@Sun.com zonecfg_fini_handle(zone_handle); 552310718SJordan.Vaughan@Sun.com if (err != Z_OK) { 552410718SJordan.Vaughan@Sun.com zerror(gettext("unable to get brand hook from brand %s for %s " 552510718SJordan.Vaughan@Sun.com "operation"), target_brand, cmd_to_str(CMD_SYSBOOT)); 552610718SJordan.Vaughan@Sun.com return (Z_ERR); 552710718SJordan.Vaughan@Sun.com } 552810718SJordan.Vaughan@Sun.com 552910718SJordan.Vaughan@Sun.com /* 553010718SJordan.Vaughan@Sun.com * If the hook wasn't defined (which is OK), then indicate success and 553110718SJordan.Vaughan@Sun.com * return. Otherwise, execute the hook. 553210718SJordan.Vaughan@Sun.com */ 553310718SJordan.Vaughan@Sun.com if (cmdbuf[0] != '\0') 553410718SJordan.Vaughan@Sun.com return ((subproc_status(gettext("brand sysboot operation"), 553510718SJordan.Vaughan@Sun.com do_subproc(cmdbuf), B_FALSE) == ZONE_SUBPROC_OK) ? Z_OK : 553610718SJordan.Vaughan@Sun.com Z_BRAND_ERROR); 553710718SJordan.Vaughan@Sun.com return (Z_OK); 553810718SJordan.Vaughan@Sun.com } 553910718SJordan.Vaughan@Sun.com 55402303Scarlsonj static int 55410Sstevel@tonic-gate help_func(int argc, char *argv[]) 55420Sstevel@tonic-gate { 55430Sstevel@tonic-gate int arg, cmd_num; 55440Sstevel@tonic-gate 55450Sstevel@tonic-gate if (argc == 0) { 55460Sstevel@tonic-gate (void) usage(B_TRUE); 55470Sstevel@tonic-gate return (Z_OK); 55480Sstevel@tonic-gate } 55490Sstevel@tonic-gate optind = 0; 55500Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 55510Sstevel@tonic-gate switch (arg) { 55520Sstevel@tonic-gate case '?': 55530Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 55540Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 55550Sstevel@tonic-gate default: 55560Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 55570Sstevel@tonic-gate return (Z_USAGE); 55580Sstevel@tonic-gate } 55590Sstevel@tonic-gate } 55600Sstevel@tonic-gate while (optind < argc) { 5561988Scarlsonj /* Private commands have NULL short_usage; omit them */ 5562988Scarlsonj if ((cmd_num = cmd_match(argv[optind])) < 0 || 5563988Scarlsonj cmdtab[cmd_num].short_usage == NULL) { 55640Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 55650Sstevel@tonic-gate return (Z_USAGE); 55660Sstevel@tonic-gate } 55670Sstevel@tonic-gate sub_usage(cmdtab[cmd_num].short_usage, cmd_num); 55680Sstevel@tonic-gate optind++; 55690Sstevel@tonic-gate } 55700Sstevel@tonic-gate return (Z_OK); 55710Sstevel@tonic-gate } 55720Sstevel@tonic-gate 55730Sstevel@tonic-gate /* 55740Sstevel@tonic-gate * Returns: CMD_MIN thru CMD_MAX on success, -1 on error 55750Sstevel@tonic-gate */ 55760Sstevel@tonic-gate 55770Sstevel@tonic-gate static int 55780Sstevel@tonic-gate cmd_match(char *cmd) 55790Sstevel@tonic-gate { 55800Sstevel@tonic-gate int i; 55810Sstevel@tonic-gate 55820Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 55830Sstevel@tonic-gate /* return only if there is an exact match */ 55840Sstevel@tonic-gate if (strcmp(cmd, cmdtab[i].cmd_name) == 0) 55850Sstevel@tonic-gate return (cmdtab[i].cmd_num); 55860Sstevel@tonic-gate } 55870Sstevel@tonic-gate return (-1); 55880Sstevel@tonic-gate } 55890Sstevel@tonic-gate 55900Sstevel@tonic-gate static int 55910Sstevel@tonic-gate parse_and_run(int argc, char *argv[]) 55920Sstevel@tonic-gate { 55930Sstevel@tonic-gate int i = cmd_match(argv[0]); 55940Sstevel@tonic-gate 55950Sstevel@tonic-gate if (i < 0) 55960Sstevel@tonic-gate return (usage(B_FALSE)); 55970Sstevel@tonic-gate return (cmdtab[i].handler(argc - 1, &(argv[1]))); 55980Sstevel@tonic-gate } 55990Sstevel@tonic-gate 56000Sstevel@tonic-gate static char * 56010Sstevel@tonic-gate get_execbasename(char *execfullname) 56020Sstevel@tonic-gate { 56030Sstevel@tonic-gate char *last_slash, *execbasename; 56040Sstevel@tonic-gate 56050Sstevel@tonic-gate /* guard against '/' at end of command invocation */ 56060Sstevel@tonic-gate for (;;) { 56070Sstevel@tonic-gate last_slash = strrchr(execfullname, '/'); 56080Sstevel@tonic-gate if (last_slash == NULL) { 56090Sstevel@tonic-gate execbasename = execfullname; 56100Sstevel@tonic-gate break; 56110Sstevel@tonic-gate } else { 56120Sstevel@tonic-gate execbasename = last_slash + 1; 56130Sstevel@tonic-gate if (*execbasename == '\0') { 56140Sstevel@tonic-gate *last_slash = '\0'; 56150Sstevel@tonic-gate continue; 56160Sstevel@tonic-gate } 56170Sstevel@tonic-gate break; 56180Sstevel@tonic-gate } 56190Sstevel@tonic-gate } 56200Sstevel@tonic-gate return (execbasename); 56210Sstevel@tonic-gate } 56220Sstevel@tonic-gate 56230Sstevel@tonic-gate int 56240Sstevel@tonic-gate main(int argc, char **argv) 56250Sstevel@tonic-gate { 56260Sstevel@tonic-gate int arg; 56270Sstevel@tonic-gate zoneid_t zid; 5628766Scarlsonj struct stat st; 56292712Snn35248 char *zone_lock_env; 56302712Snn35248 int err; 56310Sstevel@tonic-gate 56320Sstevel@tonic-gate if ((locale = setlocale(LC_ALL, "")) == NULL) 56330Sstevel@tonic-gate locale = "C"; 56340Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 56350Sstevel@tonic-gate setbuf(stdout, NULL); 56360Sstevel@tonic-gate (void) sigset(SIGHUP, SIG_IGN); 56370Sstevel@tonic-gate execname = get_execbasename(argv[0]); 56380Sstevel@tonic-gate target_zone = NULL; 56390Sstevel@tonic-gate if (chdir("/") != 0) { 56400Sstevel@tonic-gate zerror(gettext("could not change directory to /.")); 56410Sstevel@tonic-gate exit(Z_ERR); 56420Sstevel@tonic-gate } 56439049SSudheer.Abdul-Salam@Sun.COM /* 56449049SSudheer.Abdul-Salam@Sun.COM * Use the default system mask rather than anything that may have been 56459049SSudheer.Abdul-Salam@Sun.COM * set by the caller. 56469049SSudheer.Abdul-Salam@Sun.COM */ 56479049SSudheer.Abdul-Salam@Sun.COM (void) umask(CMASK); 56480Sstevel@tonic-gate 56492082Seschrock if (init_zfs() != Z_OK) 56502082Seschrock exit(Z_ERR); 56512082Seschrock 56522303Scarlsonj while ((arg = getopt(argc, argv, "?u:z:R:")) != EOF) { 56530Sstevel@tonic-gate switch (arg) { 56540Sstevel@tonic-gate case '?': 56550Sstevel@tonic-gate return (usage(B_TRUE)); 56562303Scarlsonj case 'u': 56572303Scarlsonj target_uuid = optarg; 56582303Scarlsonj break; 56590Sstevel@tonic-gate case 'z': 56600Sstevel@tonic-gate target_zone = optarg; 56610Sstevel@tonic-gate break; 5662766Scarlsonj case 'R': /* private option for admin/install use */ 5663766Scarlsonj if (*optarg != '/') { 5664766Scarlsonj zerror(gettext("root path must be absolute.")); 5665766Scarlsonj exit(Z_ERR); 5666766Scarlsonj } 5667766Scarlsonj if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { 5668766Scarlsonj zerror( 5669766Scarlsonj gettext("root path must be a directory.")); 5670766Scarlsonj exit(Z_ERR); 5671766Scarlsonj } 5672766Scarlsonj zonecfg_set_root(optarg); 5673766Scarlsonj break; 56740Sstevel@tonic-gate default: 56750Sstevel@tonic-gate return (usage(B_FALSE)); 56760Sstevel@tonic-gate } 56770Sstevel@tonic-gate } 56780Sstevel@tonic-gate 56790Sstevel@tonic-gate if (optind >= argc) 56800Sstevel@tonic-gate return (usage(B_FALSE)); 56812303Scarlsonj 56822303Scarlsonj if (target_uuid != NULL && *target_uuid != '\0') { 56832303Scarlsonj uuid_t uuid; 56842303Scarlsonj static char newtarget[ZONENAME_MAX]; 56852303Scarlsonj 56862303Scarlsonj if (uuid_parse(target_uuid, uuid) == -1) { 56872303Scarlsonj zerror(gettext("illegal UUID value specified")); 56882303Scarlsonj exit(Z_ERR); 56892303Scarlsonj } 56902303Scarlsonj if (zonecfg_get_name_by_uuid(uuid, newtarget, 56912303Scarlsonj sizeof (newtarget)) == Z_OK) 56922303Scarlsonj target_zone = newtarget; 56932303Scarlsonj } 56942303Scarlsonj 56950Sstevel@tonic-gate if (target_zone != NULL && zone_get_id(target_zone, &zid) != 0) { 56960Sstevel@tonic-gate errno = Z_NO_ZONE; 56970Sstevel@tonic-gate zperror(target_zone, B_TRUE); 56980Sstevel@tonic-gate exit(Z_ERR); 56990Sstevel@tonic-gate } 57002712Snn35248 57012712Snn35248 /* 57022712Snn35248 * See if we have inherited the right to manipulate this zone from 57032712Snn35248 * a zoneadm instance in our ancestry. If so, set zone_lock_cnt to 57042712Snn35248 * indicate it. If not, make that explicit in our environment. 57052712Snn35248 */ 57067089Sgjelinek zonecfg_init_lock_file(target_zone, &zone_lock_env); 57072712Snn35248 570810943SEdward.Pilatowicz@Sun.COM /* Figure out what the system's default brand is */ 570910943SEdward.Pilatowicz@Sun.COM if (zonecfg_default_brand(default_brand, 571010943SEdward.Pilatowicz@Sun.COM sizeof (default_brand)) != Z_OK) { 571110943SEdward.Pilatowicz@Sun.COM zerror(gettext("unable to determine default brand")); 571210943SEdward.Pilatowicz@Sun.COM return (Z_ERR); 571310943SEdward.Pilatowicz@Sun.COM } 571410943SEdward.Pilatowicz@Sun.COM 57152712Snn35248 /* 57162712Snn35248 * If we are going to be operating on a single zone, retrieve its 57172712Snn35248 * brand type and determine whether it is native or not. 57182712Snn35248 */ 57192712Snn35248 if ((target_zone != NULL) && 57208057SJordan.Vaughan@Sun.com (strcmp(target_zone, GLOBAL_ZONENAME) != 0)) { 57212712Snn35248 if (zone_get_brand(target_zone, target_brand, 57222712Snn35248 sizeof (target_brand)) != Z_OK) { 57232712Snn35248 zerror(gettext("missing or invalid brand")); 57242712Snn35248 exit(Z_ERR); 57252712Snn35248 } 572610796SStephen.Lawrence@Sun.COM /* 572710796SStephen.Lawrence@Sun.COM * In the alternate root environment, the only supported 572810796SStephen.Lawrence@Sun.COM * operations are mount and unmount. In this case, just treat 572910796SStephen.Lawrence@Sun.COM * the zone as native if it is cluster. Cluster zones can be 573010796SStephen.Lawrence@Sun.COM * native for the purpose of LU or upgrade, and the cluster 573110796SStephen.Lawrence@Sun.COM * brand may not exist in the miniroot (such as in net install 573210796SStephen.Lawrence@Sun.COM * upgrade). 573310796SStephen.Lawrence@Sun.COM */ 573410796SStephen.Lawrence@Sun.COM if (strcmp(target_brand, CLUSTER_BRAND_NAME) == 0) { 573510796SStephen.Lawrence@Sun.COM if (zonecfg_in_alt_root()) { 573610943SEdward.Pilatowicz@Sun.COM (void) strlcpy(target_brand, default_brand, 573710796SStephen.Lawrence@Sun.COM sizeof (target_brand)); 573810796SStephen.Lawrence@Sun.COM } 573910796SStephen.Lawrence@Sun.COM } 57402712Snn35248 } 57412712Snn35248 57422712Snn35248 err = parse_and_run(argc - optind, &argv[optind]); 57432712Snn35248 57442712Snn35248 return (err); 57450Sstevel@tonic-gate } 5746