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 /* 235829Sgjelinek * Copyright 2008 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> 674456Sss150715 #include <libdlpi.h> 680Sstevel@tonic-gate 690Sstevel@tonic-gate #include <fcntl.h> 700Sstevel@tonic-gate #include <door.h> 710Sstevel@tonic-gate #include <macros.h> 720Sstevel@tonic-gate #include <libgen.h> 731300Sgjelinek #include <fnmatch.h> 741931Sgjelinek #include <sys/modctl.h> 752712Snn35248 #include <libbrand.h> 763247Sgjelinek #include <libscf.h> 773352Sgjelinek #include <procfs.h> 783686Sgjelinek #include <strings.h> 790Sstevel@tonic-gate 800Sstevel@tonic-gate #include <pool.h> 810Sstevel@tonic-gate #include <sys/pool.h> 823247Sgjelinek #include <sys/priocntl.h> 833247Sgjelinek #include <sys/fsspriocntl.h> 840Sstevel@tonic-gate 851867Sgjelinek #include "zoneadm.h" 861867Sgjelinek 870Sstevel@tonic-gate #define MAXARGS 8 880Sstevel@tonic-gate 890Sstevel@tonic-gate /* Reflects kernel zone entries */ 900Sstevel@tonic-gate typedef struct zone_entry { 910Sstevel@tonic-gate zoneid_t zid; 920Sstevel@tonic-gate char zname[ZONENAME_MAX]; 930Sstevel@tonic-gate char *zstate_str; 940Sstevel@tonic-gate zone_state_t zstate_num; 952712Snn35248 char zbrand[MAXNAMELEN]; 960Sstevel@tonic-gate char zroot[MAXPATHLEN]; 972303Scarlsonj char zuuid[UUID_PRINTABLE_STRING_LENGTH]; 983448Sdh155122 zone_iptype_t ziptype; 990Sstevel@tonic-gate } zone_entry_t; 1000Sstevel@tonic-gate 1014350Std153743 #define CLUSTER_BRAND_NAME "cluster" 1024350Std153743 1030Sstevel@tonic-gate static zone_entry_t *zents; 1040Sstevel@tonic-gate static size_t nzents; 1052712Snn35248 static boolean_t is_native_zone = B_TRUE; 1060Sstevel@tonic-gate 1071915Sgjelinek #define LOOPBACK_IF "lo0" 1081915Sgjelinek #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af)) 1091915Sgjelinek 1101915Sgjelinek struct net_if { 1111915Sgjelinek char *name; 1121915Sgjelinek int af; 1131915Sgjelinek }; 1141915Sgjelinek 1150Sstevel@tonic-gate /* 0755 is the default directory mode. */ 1160Sstevel@tonic-gate #define DEFAULT_DIR_MODE \ 1170Sstevel@tonic-gate (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate struct cmd { 1200Sstevel@tonic-gate uint_t cmd_num; /* command number */ 1210Sstevel@tonic-gate char *cmd_name; /* command name */ 1220Sstevel@tonic-gate char *short_usage; /* short form help */ 1230Sstevel@tonic-gate int (*handler)(int argc, char *argv[]); /* function to call */ 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate }; 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate #define SHELP_HELP "help" 1282267Sdp #define SHELP_BOOT "boot [-- boot_arguments]" 1290Sstevel@tonic-gate #define SHELP_HALT "halt" 1300Sstevel@tonic-gate #define SHELP_READY "ready" 1312267Sdp #define SHELP_REBOOT "reboot [-- boot_arguments]" 1320Sstevel@tonic-gate #define SHELP_LIST "list [-cipv]" 1330Sstevel@tonic-gate #define SHELP_VERIFY "verify" 1342712Snn35248 #define SHELP_INSTALL "install [-x nodataset] [brand-specific args]" 1357089Sgjelinek #define SHELP_UNINSTALL "uninstall [-F] [brand-specific args]" 1367089Sgjelinek #define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] "\ 1377089Sgjelinek "[brand-specific args] zonename" 1381300Sgjelinek #define SHELP_MOVE "move zonepath" 1397089Sgjelinek #define SHELP_DETACH "detach [-n] [brand-specific args]" 1407089Sgjelinek #define SHELP_ATTACH "attach [-F] [-n <path>] [brand-specific args]" 1412303Scarlsonj #define SHELP_MARK "mark incomplete" 1420Sstevel@tonic-gate 1432712Snn35248 #define EXEC_PREFIX "exec " 1442712Snn35248 #define EXEC_LEN (strlen(EXEC_PREFIX)) 1452712Snn35248 #define RMCOMMAND "/usr/bin/rm -rf" 1462712Snn35248 1472712Snn35248 static int cleanup_zonepath(char *, boolean_t); 1482712Snn35248 1493448Sdh155122 1500Sstevel@tonic-gate static int help_func(int argc, char *argv[]); 1510Sstevel@tonic-gate static int ready_func(int argc, char *argv[]); 1520Sstevel@tonic-gate static int boot_func(int argc, char *argv[]); 1530Sstevel@tonic-gate static int halt_func(int argc, char *argv[]); 1540Sstevel@tonic-gate static int reboot_func(int argc, char *argv[]); 1550Sstevel@tonic-gate static int list_func(int argc, char *argv[]); 1560Sstevel@tonic-gate static int verify_func(int argc, char *argv[]); 1570Sstevel@tonic-gate static int install_func(int argc, char *argv[]); 1580Sstevel@tonic-gate static int uninstall_func(int argc, char *argv[]); 159766Scarlsonj static int mount_func(int argc, char *argv[]); 160766Scarlsonj static int unmount_func(int argc, char *argv[]); 1611300Sgjelinek static int clone_func(int argc, char *argv[]); 1621300Sgjelinek static int move_func(int argc, char *argv[]); 1631507Sgjelinek static int detach_func(int argc, char *argv[]); 1641507Sgjelinek static int attach_func(int argc, char *argv[]); 1652303Scarlsonj static int mark_func(int argc, char *argv[]); 1663247Sgjelinek static int apply_func(int argc, char *argv[]); 1670Sstevel@tonic-gate static int sanity_check(char *zone, int cmd_num, boolean_t running, 1682712Snn35248 boolean_t unsafe_when_running, boolean_t force); 1690Sstevel@tonic-gate static int cmd_match(char *cmd); 1703339Szt129084 static int verify_details(int, char *argv[]); 1713339Szt129084 static int verify_brand(zone_dochandle_t, int, char *argv[]); 1723339Szt129084 static int invoke_brand_handler(int, char *argv[]); 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate static struct cmd cmdtab[] = { 1750Sstevel@tonic-gate { CMD_HELP, "help", SHELP_HELP, help_func }, 1760Sstevel@tonic-gate { CMD_BOOT, "boot", SHELP_BOOT, boot_func }, 1770Sstevel@tonic-gate { CMD_HALT, "halt", SHELP_HALT, halt_func }, 1780Sstevel@tonic-gate { CMD_READY, "ready", SHELP_READY, ready_func }, 1790Sstevel@tonic-gate { CMD_REBOOT, "reboot", SHELP_REBOOT, reboot_func }, 1800Sstevel@tonic-gate { CMD_LIST, "list", SHELP_LIST, list_func }, 1810Sstevel@tonic-gate { CMD_VERIFY, "verify", SHELP_VERIFY, verify_func }, 1820Sstevel@tonic-gate { CMD_INSTALL, "install", SHELP_INSTALL, install_func }, 1830Sstevel@tonic-gate { CMD_UNINSTALL, "uninstall", SHELP_UNINSTALL, 184766Scarlsonj uninstall_func }, 1851300Sgjelinek /* mount and unmount are private commands for admin/install */ 186766Scarlsonj { CMD_MOUNT, "mount", NULL, mount_func }, 1871300Sgjelinek { CMD_UNMOUNT, "unmount", NULL, unmount_func }, 1881300Sgjelinek { CMD_CLONE, "clone", SHELP_CLONE, clone_func }, 1891507Sgjelinek { CMD_MOVE, "move", SHELP_MOVE, move_func }, 1901507Sgjelinek { CMD_DETACH, "detach", SHELP_DETACH, detach_func }, 1912303Scarlsonj { CMD_ATTACH, "attach", SHELP_ATTACH, attach_func }, 1923247Sgjelinek { CMD_MARK, "mark", SHELP_MARK, mark_func }, 1933247Sgjelinek { CMD_APPLY, "apply", NULL, apply_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]; 2010Sstevel@tonic-gate static char *locale; 2021867Sgjelinek char *target_zone; 2032303Scarlsonj static char *target_uuid; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate /* used in do_subproc() and signal handler */ 2060Sstevel@tonic-gate static volatile boolean_t child_killed; 2072712Snn35248 static int do_subproc_cnt = 0; 2082712Snn35248 2092712Snn35248 /* 2102712Snn35248 * Used to indicate whether this zoneadm instance has another zoneadm 2112712Snn35248 * instance in its ancestry. 2122712Snn35248 */ 2132712Snn35248 static boolean_t zoneadm_is_nested = B_FALSE; 2142712Snn35248 2151867Sgjelinek char * 2160Sstevel@tonic-gate cmd_to_str(int cmd_num) 2170Sstevel@tonic-gate { 2180Sstevel@tonic-gate assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 2190Sstevel@tonic-gate return (cmdtab[cmd_num].cmd_name); 2200Sstevel@tonic-gate } 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate /* This is a separate function because of gettext() wrapping. */ 2230Sstevel@tonic-gate static char * 2240Sstevel@tonic-gate long_help(int cmd_num) 2250Sstevel@tonic-gate { 226222Scomay assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 2270Sstevel@tonic-gate switch (cmd_num) { 2281634Sgjelinek case CMD_HELP: 2291634Sgjelinek return (gettext("Print usage message.")); 2301634Sgjelinek case CMD_BOOT: 2312267Sdp return (gettext("Activates (boots) specified zone. See " 2322267Sdp "zoneadm(1m) for valid boot\n\targuments.")); 2331634Sgjelinek case CMD_HALT: 2341634Sgjelinek return (gettext("Halts specified zone, bypassing shutdown " 2351634Sgjelinek "scripts and removing runtime\n\tresources of the zone.")); 2361634Sgjelinek case CMD_READY: 2371634Sgjelinek return (gettext("Prepares a zone for running applications but " 2381634Sgjelinek "does not start any user\n\tprocesses in the zone.")); 2391634Sgjelinek case CMD_REBOOT: 2401634Sgjelinek return (gettext("Restarts the zone (equivalent to a halt / " 2412267Sdp "boot sequence).\n\tFails if the zone is not active. " 2422267Sdp "See zoneadm(1m) for valid boot\n\targuments.")); 2431634Sgjelinek case CMD_LIST: 2441634Sgjelinek return (gettext("Lists the current zones, or a " 2451634Sgjelinek "specific zone if indicated. By default,\n\tall " 2461634Sgjelinek "running zones are listed, though this can be " 2471634Sgjelinek "expanded to all\n\tinstalled zones with the -i " 2481634Sgjelinek "option or all configured zones with the\n\t-c " 2492303Scarlsonj "option. When used with the general -z <zone> and/or -u " 2502303Scarlsonj "<uuid-match>\n\toptions, lists only the specified " 2512303Scarlsonj "matching zone, but lists it\n\tregardless of its state, " 2522303Scarlsonj "and the -i and -c options are disallowed. The\n\t-v " 2532303Scarlsonj "option can be used to display verbose information: zone " 2542303Scarlsonj "name, id,\n\tcurrent state, root directory and options. " 2552303Scarlsonj "The -p option can be used\n\tto request machine-parsable " 2562303Scarlsonj "output. The -v and -p options are mutually\n\texclusive." 2572303Scarlsonj " If neither -v nor -p is used, just the zone name is " 2582303Scarlsonj "listed.")); 2591634Sgjelinek case CMD_VERIFY: 2601634Sgjelinek return (gettext("Check to make sure the configuration " 2611634Sgjelinek "can safely be instantiated\n\ton the machine: " 2621634Sgjelinek "physical network interfaces exist, etc.")); 2631634Sgjelinek case CMD_INSTALL: 2641867Sgjelinek return (gettext("Install the configuration on to the system. " 2651867Sgjelinek "The -x nodataset option\n\tcan be used to prevent the " 2661867Sgjelinek "creation of a new ZFS file system for the\n\tzone " 2672712Snn35248 "(assuming the zonepath is within a ZFS file system).\n\t" 2682712Snn35248 "All other arguments are passed to the brand installation " 2697089Sgjelinek "function;\n\tsee brands(5) for more information.")); 2701634Sgjelinek case CMD_UNINSTALL: 2711634Sgjelinek return (gettext("Uninstall the configuration from the system. " 2727089Sgjelinek "The -F flag can be used\n\tto force the action. All " 2737089Sgjelinek "other arguments are passed to the brand\n\tuninstall " 2747089Sgjelinek "function; see brands(5) for more information.")); 2751634Sgjelinek case CMD_CLONE: 2761867Sgjelinek return (gettext("Clone the installation of another zone. " 2771867Sgjelinek "The -m option can be used to\n\tspecify 'copy' which " 2781867Sgjelinek "forces a copy of the source zone. The -s option\n\t" 2791867Sgjelinek "can be used to specify the name of a ZFS snapshot " 2801867Sgjelinek "that was taken from\n\ta previous clone command. The " 2811867Sgjelinek "snapshot will be used as the source\n\tinstead of " 2827089Sgjelinek "creating a new ZFS snapshot. All other arguments are " 2837089Sgjelinek "passed\n\tto the brand clone function; see " 2847089Sgjelinek "brands(5) for more information.")); 2851634Sgjelinek case CMD_MOVE: 2861634Sgjelinek return (gettext("Move the zone to a new zonepath.")); 2871634Sgjelinek case CMD_DETACH: 2881634Sgjelinek return (gettext("Detach the zone from the system. The zone " 2891634Sgjelinek "state is changed to\n\t'configured' (but the files under " 2901634Sgjelinek "the zonepath are untouched).\n\tThe zone can subsequently " 2911634Sgjelinek "be attached, or can be moved to another\n\tsystem and " 2922078Sgjelinek "attached there. The -n option can be used to specify\n\t" 2932078Sgjelinek "'no-execute' mode. When -n is used, the information " 2942078Sgjelinek "needed to attach\n\tthe zone is sent to standard output " 2957089Sgjelinek "but the zone is not actually\n\tdetached. All other " 2967089Sgjelinek "arguments are passed to the brand detach function;\n\tsee " 2977089Sgjelinek "brands(5) for more information.")); 2981634Sgjelinek case CMD_ATTACH: 2991634Sgjelinek return (gettext("Attach the zone to the system. The zone " 3001634Sgjelinek "state must be 'configured'\n\tprior to attach; upon " 3011634Sgjelinek "successful completion, the zone state will be\n\t" 3021634Sgjelinek "'installed'. The system software on the current " 3031634Sgjelinek "system must be\n\tcompatible with the software on the " 3047089Sgjelinek "zone's original system.\n\tSpecify -F " 3055829Sgjelinek "to force the attach and skip software compatibility " 3065829Sgjelinek "tests.\n\tThe -n option can be used to specify " 3075829Sgjelinek "'no-execute' mode. When -n is\n\tused, the information " 3085829Sgjelinek "needed to attach the zone is read from the\n\tspecified " 3095829Sgjelinek "path and the configuration is only validated. The path " 3107089Sgjelinek "can\n\tbe '-' to specify standard input. The -F and -n " 3117089Sgjelinek "options are mutually\n\texclusive. All other arguments " 3127089Sgjelinek "are passed to the brand attach\n\tfunction; see " 3137089Sgjelinek "brands(5) for more information.")); 3142303Scarlsonj case CMD_MARK: 3152303Scarlsonj return (gettext("Set the state of the zone. This can be used " 3162303Scarlsonj "to force the zone\n\tstate to 'incomplete' " 3172303Scarlsonj "administratively if some activity has rendered\n\tthe " 3182303Scarlsonj "zone permanently unusable. The only valid state that " 3192303Scarlsonj "may be\n\tspecified is 'incomplete'.")); 3201634Sgjelinek default: 3211634Sgjelinek return (""); 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate /* NOTREACHED */ 324222Scomay return (NULL); 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* 3280Sstevel@tonic-gate * Called with explicit B_TRUE when help is explicitly requested, B_FALSE for 3290Sstevel@tonic-gate * unexpected errors. 3300Sstevel@tonic-gate */ 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate static int 3330Sstevel@tonic-gate usage(boolean_t explicit) 3340Sstevel@tonic-gate { 3350Sstevel@tonic-gate int i; 3360Sstevel@tonic-gate FILE *fd = explicit ? stdout : stderr; 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate (void) fprintf(fd, "%s:\t%s help\n", gettext("usage"), execname); 3392303Scarlsonj (void) fprintf(fd, "\t%s [-z <zone>] [-u <uuid-match>] list\n", 3402303Scarlsonj execname); 3412303Scarlsonj (void) fprintf(fd, "\t%s {-z <zone>|-u <uuid-match>} <%s>\n", execname, 3420Sstevel@tonic-gate gettext("subcommand")); 3430Sstevel@tonic-gate (void) fprintf(fd, "\n%s:\n\n", gettext("Subcommands")); 3440Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 345766Scarlsonj if (cmdtab[i].short_usage == NULL) 346766Scarlsonj continue; 3470Sstevel@tonic-gate (void) fprintf(fd, "%s\n", cmdtab[i].short_usage); 3480Sstevel@tonic-gate if (explicit) 3490Sstevel@tonic-gate (void) fprintf(fd, "\t%s\n\n", long_help(i)); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate if (!explicit) 3520Sstevel@tonic-gate (void) fputs("\n", fd); 3530Sstevel@tonic-gate return (Z_USAGE); 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate static void 3570Sstevel@tonic-gate sub_usage(char *short_usage, int cmd_num) 3580Sstevel@tonic-gate { 3590Sstevel@tonic-gate (void) fprintf(stderr, "%s:\t%s\n", gettext("usage"), short_usage); 3600Sstevel@tonic-gate (void) fprintf(stderr, "\t%s\n", long_help(cmd_num)); 3610Sstevel@tonic-gate } 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate /* 3640Sstevel@tonic-gate * zperror() is like perror(3c) except that this also prints the executable 3650Sstevel@tonic-gate * name at the start of the message, and takes a boolean indicating whether 3660Sstevel@tonic-gate * to call libc'c strerror() or that from libzonecfg. 3670Sstevel@tonic-gate */ 3680Sstevel@tonic-gate 3691867Sgjelinek void 3700Sstevel@tonic-gate zperror(const char *str, boolean_t zonecfg_error) 3710Sstevel@tonic-gate { 3720Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", execname, str, 3730Sstevel@tonic-gate zonecfg_error ? zonecfg_strerror(errno) : strerror(errno)); 3740Sstevel@tonic-gate } 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate /* 3770Sstevel@tonic-gate * zperror2() is very similar to zperror() above, except it also prints a 3780Sstevel@tonic-gate * supplied zone name after the executable. 3790Sstevel@tonic-gate * 3800Sstevel@tonic-gate * All current consumers of this function want libzonecfg's strerror() rather 3810Sstevel@tonic-gate * than libc's; if this ever changes, this function can be made more generic 3820Sstevel@tonic-gate * like zperror() above. 3830Sstevel@tonic-gate */ 3840Sstevel@tonic-gate 3851867Sgjelinek void 3860Sstevel@tonic-gate zperror2(const char *zone, const char *str) 3870Sstevel@tonic-gate { 3880Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s: %s\n", execname, zone, str, 3890Sstevel@tonic-gate zonecfg_strerror(errno)); 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate /* PRINTFLIKE1 */ 3931867Sgjelinek void 3940Sstevel@tonic-gate zerror(const char *fmt, ...) 3950Sstevel@tonic-gate { 3960Sstevel@tonic-gate va_list alist; 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate va_start(alist, fmt); 3990Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", execname); 4000Sstevel@tonic-gate if (target_zone != NULL) 4010Sstevel@tonic-gate (void) fprintf(stderr, "zone '%s': ", target_zone); 4020Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 4030Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 4040Sstevel@tonic-gate va_end(alist); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate static void * 4080Sstevel@tonic-gate safe_calloc(size_t nelem, size_t elsize) 4090Sstevel@tonic-gate { 4100Sstevel@tonic-gate void *r = calloc(nelem, elsize); 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate if (r == NULL) { 4130Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), 4140Sstevel@tonic-gate (ulong_t)nelem * elsize, strerror(errno)); 4150Sstevel@tonic-gate exit(Z_ERR); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate return (r); 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate static void 4210Sstevel@tonic-gate zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) 4220Sstevel@tonic-gate { 4230Sstevel@tonic-gate static boolean_t firsttime = B_TRUE; 4243448Sdh155122 char *ip_type_str; 4253448Sdh155122 4263448Sdh155122 if (zent->ziptype == ZS_EXCLUSIVE) 4273448Sdh155122 ip_type_str = "excl"; 4283448Sdh155122 else 4293448Sdh155122 ip_type_str = "shared"; 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate assert(!(verbose && parsable)); 4320Sstevel@tonic-gate if (firsttime && verbose) { 4330Sstevel@tonic-gate firsttime = B_FALSE; 4343448Sdh155122 (void) printf("%*s %-16s %-10s %-30s %-8s %-6s\n", 4353448Sdh155122 ZONEID_WIDTH, "ID", "NAME", "STATUS", "PATH", "BRAND", 4363448Sdh155122 "IP"); 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate if (!verbose) { 4392303Scarlsonj char *cp, *clim; 4402303Scarlsonj 4410Sstevel@tonic-gate if (!parsable) { 4420Sstevel@tonic-gate (void) printf("%s\n", zent->zname); 4430Sstevel@tonic-gate return; 4440Sstevel@tonic-gate } 4450Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 4460Sstevel@tonic-gate (void) printf("-"); 4470Sstevel@tonic-gate else 4480Sstevel@tonic-gate (void) printf("%lu", zent->zid); 4492303Scarlsonj (void) printf(":%s:%s:", zent->zname, zent->zstate_str); 4502303Scarlsonj cp = zent->zroot; 4512303Scarlsonj while ((clim = strchr(cp, ':')) != NULL) { 4522303Scarlsonj (void) printf("%.*s\\:", clim - cp, cp); 4532303Scarlsonj cp = clim + 1; 4542303Scarlsonj } 4553448Sdh155122 (void) printf("%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand, 4563448Sdh155122 ip_type_str); 4570Sstevel@tonic-gate return; 4580Sstevel@tonic-gate } 4590Sstevel@tonic-gate if (zent->zstate_str != NULL) { 4600Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 4610Sstevel@tonic-gate (void) printf("%*s", ZONEID_WIDTH, "-"); 4620Sstevel@tonic-gate else 4630Sstevel@tonic-gate (void) printf("%*lu", ZONEID_WIDTH, zent->zid); 4643448Sdh155122 (void) printf(" %-16s %-10s %-30s %-8s %-6s\n", zent->zname, 4653448Sdh155122 zent->zstate_str, zent->zroot, zent->zbrand, ip_type_str); 4660Sstevel@tonic-gate } 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate static int 470766Scarlsonj lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) 4710Sstevel@tonic-gate { 4721676Sjpk char root[MAXPATHLEN], *cp; 4730Sstevel@tonic-gate int err; 4742303Scarlsonj uuid_t uuid; 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate (void) strlcpy(zent->zname, zone_name, sizeof (zent->zname)); 4770Sstevel@tonic-gate (void) strlcpy(zent->zroot, "???", sizeof (zent->zroot)); 4782712Snn35248 (void) strlcpy(zent->zbrand, "???", sizeof (zent->zbrand)); 4790Sstevel@tonic-gate zent->zstate_str = "???"; 4800Sstevel@tonic-gate 481766Scarlsonj zent->zid = zid; 4820Sstevel@tonic-gate 4832303Scarlsonj if (zonecfg_get_uuid(zone_name, uuid) == Z_OK && 4842303Scarlsonj !uuid_is_null(uuid)) 4852303Scarlsonj uuid_unparse(uuid, zent->zuuid); 4862303Scarlsonj else 4872303Scarlsonj zent->zuuid[0] = '\0'; 4882303Scarlsonj 4891676Sjpk /* 4901676Sjpk * For labeled zones which query the zone path of lower-level 4911676Sjpk * zones, the path needs to be adjusted to drop the final 4921676Sjpk * "/root" component. This adjusted path is then useful 4931676Sjpk * for reading down any exported directories from the 4941676Sjpk * lower-level zone. 4951676Sjpk */ 4961676Sjpk if (is_system_labeled() && zent->zid != ZONE_ID_UNDEFINED) { 4971676Sjpk if (zone_getattr(zent->zid, ZONE_ATTR_ROOT, zent->zroot, 4981676Sjpk sizeof (zent->zroot)) == -1) { 4991676Sjpk zperror2(zent->zname, 5001676Sjpk gettext("could not get zone path.")); 5011676Sjpk return (Z_ERR); 5021676Sjpk } 5031676Sjpk cp = zent->zroot + strlen(zent->zroot) - 5; 5041676Sjpk if (cp > zent->zroot && strcmp(cp, "/root") == 0) 5051676Sjpk *cp = 0; 5061676Sjpk } else { 5071676Sjpk if ((err = zone_get_zonepath(zent->zname, root, 5081676Sjpk sizeof (root))) != Z_OK) { 5091676Sjpk errno = err; 5101676Sjpk zperror2(zent->zname, 5111676Sjpk gettext("could not get zone path.")); 5121676Sjpk return (Z_ERR); 5131676Sjpk } 5141676Sjpk (void) strlcpy(zent->zroot, root, sizeof (zent->zroot)); 5151676Sjpk } 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, &zent->zstate_num)) != Z_OK) { 5180Sstevel@tonic-gate errno = err; 5190Sstevel@tonic-gate zperror2(zent->zname, gettext("could not get state")); 5200Sstevel@tonic-gate return (Z_ERR); 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate zent->zstate_str = zone_state_str(zent->zstate_num); 5233009Snn35248 5243009Snn35248 /* 5253009Snn35248 * A zone's brand is only available in the .xml file describing it, 5263009Snn35248 * which is only visible to the global zone. This causes 5273009Snn35248 * zone_get_brand() to fail when called from within a non-global 5283009Snn35248 * zone. Fortunately we only do this on labeled systems, where we 5293009Snn35248 * know all zones are native. 5303009Snn35248 */ 5313009Snn35248 if (getzoneid() != GLOBAL_ZONEID) { 5323009Snn35248 assert(is_system_labeled() != 0); 5333009Snn35248 (void) strlcpy(zent->zbrand, NATIVE_BRAND_NAME, 5343009Snn35248 sizeof (zent->zbrand)); 5353009Snn35248 } else if (zone_get_brand(zent->zname, zent->zbrand, 5362712Snn35248 sizeof (zent->zbrand)) != Z_OK) { 5372712Snn35248 zperror2(zent->zname, gettext("could not get brand name")); 5382712Snn35248 return (Z_ERR); 5392712Snn35248 } 5400Sstevel@tonic-gate 5413448Sdh155122 /* 5423448Sdh155122 * Get ip type of the zone. 5433448Sdh155122 * Note for global zone, ZS_SHARED is set always. 5443448Sdh155122 */ 5453448Sdh155122 if (zid == GLOBAL_ZONEID) { 5463448Sdh155122 zent->ziptype = ZS_SHARED; 5473448Sdh155122 } else { 5483448Sdh155122 5493448Sdh155122 if (zent->zstate_num == ZONE_STATE_RUNNING) { 5503448Sdh155122 ushort_t flags; 5513448Sdh155122 5523448Sdh155122 if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags, 5533448Sdh155122 sizeof (flags)) < 0) { 5543448Sdh155122 zperror2(zent->zname, 5553448Sdh155122 gettext("could not get zone flags")); 5563448Sdh155122 return (Z_ERR); 5573448Sdh155122 } 5583448Sdh155122 if (flags & ZF_NET_EXCL) 5593448Sdh155122 zent->ziptype = ZS_EXCLUSIVE; 5603448Sdh155122 else 5613448Sdh155122 zent->ziptype = ZS_SHARED; 5623448Sdh155122 } else { 5633448Sdh155122 zone_dochandle_t handle; 5643448Sdh155122 5653448Sdh155122 if ((handle = zonecfg_init_handle()) == NULL) { 5663448Sdh155122 zperror2(zent->zname, 5673448Sdh155122 gettext("could not init handle")); 5683448Sdh155122 return (Z_ERR); 5693448Sdh155122 } 5703448Sdh155122 if ((err = zonecfg_get_handle(zent->zname, handle)) 5713448Sdh155122 != Z_OK) { 5723448Sdh155122 zperror2(zent->zname, 5733448Sdh155122 gettext("could not get handle")); 5743448Sdh155122 zonecfg_fini_handle(handle); 5753448Sdh155122 return (Z_ERR); 5763448Sdh155122 } 5773448Sdh155122 5783448Sdh155122 if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) 5793448Sdh155122 != Z_OK) { 5803448Sdh155122 zperror2(zent->zname, 5813448Sdh155122 gettext("could not get ip-type")); 5823448Sdh155122 zonecfg_fini_handle(handle); 5833448Sdh155122 return (Z_ERR); 5843448Sdh155122 } 5853448Sdh155122 zonecfg_fini_handle(handle); 5863448Sdh155122 } 5873448Sdh155122 } 5883448Sdh155122 5890Sstevel@tonic-gate return (Z_OK); 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * fetch_zents() calls zone_list(2) to find out how many zones are running 5940Sstevel@tonic-gate * (which is stored in the global nzents), then calls zone_list(2) again 5950Sstevel@tonic-gate * to fetch the list of running zones (stored in the global zents). This 5960Sstevel@tonic-gate * function may be called multiple times, so if zents is already set, we 5970Sstevel@tonic-gate * return immediately to save work. 5980Sstevel@tonic-gate */ 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate static int 601766Scarlsonj fetch_zents(void) 6020Sstevel@tonic-gate { 6030Sstevel@tonic-gate zoneid_t *zids = NULL; 6040Sstevel@tonic-gate uint_t nzents_saved; 605766Scarlsonj int i, retv; 606766Scarlsonj FILE *fp; 607766Scarlsonj boolean_t inaltroot; 608766Scarlsonj zone_entry_t *zentp; 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate if (nzents > 0) 6110Sstevel@tonic-gate return (Z_OK); 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate if (zone_list(NULL, &nzents) != 0) { 6140Sstevel@tonic-gate zperror(gettext("failed to get zoneid list"), B_FALSE); 6150Sstevel@tonic-gate return (Z_ERR); 6160Sstevel@tonic-gate } 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate again: 6190Sstevel@tonic-gate if (nzents == 0) 6200Sstevel@tonic-gate return (Z_OK); 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate zids = safe_calloc(nzents, sizeof (zoneid_t)); 6230Sstevel@tonic-gate nzents_saved = nzents; 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate if (zone_list(zids, &nzents) != 0) { 6260Sstevel@tonic-gate zperror(gettext("failed to get zone list"), B_FALSE); 6270Sstevel@tonic-gate free(zids); 6280Sstevel@tonic-gate return (Z_ERR); 6290Sstevel@tonic-gate } 6300Sstevel@tonic-gate if (nzents != nzents_saved) { 6310Sstevel@tonic-gate /* list changed, try again */ 6320Sstevel@tonic-gate free(zids); 6330Sstevel@tonic-gate goto again; 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate 6360Sstevel@tonic-gate zents = safe_calloc(nzents, sizeof (zone_entry_t)); 6370Sstevel@tonic-gate 638766Scarlsonj inaltroot = zonecfg_in_alt_root(); 639766Scarlsonj if (inaltroot) 640766Scarlsonj fp = zonecfg_open_scratch("", B_FALSE); 641766Scarlsonj else 642766Scarlsonj fp = NULL; 643766Scarlsonj zentp = zents; 644766Scarlsonj retv = Z_OK; 6450Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6460Sstevel@tonic-gate char name[ZONENAME_MAX]; 647766Scarlsonj char altname[ZONENAME_MAX]; 6480Sstevel@tonic-gate 649766Scarlsonj if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { 6500Sstevel@tonic-gate zperror(gettext("failed to get zone name"), B_FALSE); 651766Scarlsonj retv = Z_ERR; 652766Scarlsonj continue; 653766Scarlsonj } 654766Scarlsonj if (zonecfg_is_scratch(name)) { 655766Scarlsonj /* Ignore scratch zones by default */ 656766Scarlsonj if (!inaltroot) 657766Scarlsonj continue; 658766Scarlsonj if (fp == NULL || 659766Scarlsonj zonecfg_reverse_scratch(fp, name, altname, 660766Scarlsonj sizeof (altname), NULL, 0) == -1) { 661924Sgjelinek zerror(gettext("could not resolve scratch " 662766Scarlsonj "zone %s"), name); 663766Scarlsonj retv = Z_ERR; 664766Scarlsonj continue; 665766Scarlsonj } 666766Scarlsonj (void) strcpy(name, altname); 667766Scarlsonj } else { 668766Scarlsonj /* Ignore non-scratch when in an alternate root */ 669766Scarlsonj if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0) 670766Scarlsonj continue; 671766Scarlsonj } 672766Scarlsonj if (lookup_zone_info(name, zids[i], zentp) != Z_OK) { 673766Scarlsonj zerror(gettext("failed to get zone data")); 674766Scarlsonj retv = Z_ERR; 675766Scarlsonj continue; 676766Scarlsonj } 677766Scarlsonj zentp++; 6780Sstevel@tonic-gate } 679766Scarlsonj nzents = zentp - zents; 680766Scarlsonj if (fp != NULL) 681766Scarlsonj zonecfg_close_scratch(fp); 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate free(zids); 684766Scarlsonj return (retv); 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate 687766Scarlsonj static int 6880Sstevel@tonic-gate zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable) 6890Sstevel@tonic-gate { 6900Sstevel@tonic-gate int i; 6910Sstevel@tonic-gate zone_entry_t zent; 6920Sstevel@tonic-gate FILE *cookie; 6930Sstevel@tonic-gate char *name; 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate /* 6960Sstevel@tonic-gate * First get the list of running zones from the kernel and print them. 6970Sstevel@tonic-gate * If that is all we need, then return. 6980Sstevel@tonic-gate */ 699766Scarlsonj if ((i = fetch_zents()) != Z_OK) { 7000Sstevel@tonic-gate /* 7010Sstevel@tonic-gate * No need for error messages; fetch_zents() has already taken 7020Sstevel@tonic-gate * care of this. 7030Sstevel@tonic-gate */ 704766Scarlsonj return (i); 7050Sstevel@tonic-gate } 706766Scarlsonj for (i = 0; i < nzents; i++) 7070Sstevel@tonic-gate zone_print(&zents[i], verbose, parsable); 7080Sstevel@tonic-gate if (min_state >= ZONE_STATE_RUNNING) 709766Scarlsonj return (Z_OK); 7100Sstevel@tonic-gate /* 7110Sstevel@tonic-gate * Next, get the full list of zones from the configuration, skipping 7120Sstevel@tonic-gate * any we have already printed. 7130Sstevel@tonic-gate */ 7140Sstevel@tonic-gate cookie = setzoneent(); 7150Sstevel@tonic-gate while ((name = getzoneent(cookie)) != NULL) { 7160Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 7170Sstevel@tonic-gate if (strcmp(zents[i].zname, name) == 0) 7180Sstevel@tonic-gate break; 7190Sstevel@tonic-gate } 7200Sstevel@tonic-gate if (i < nzents) { 7210Sstevel@tonic-gate free(name); 7220Sstevel@tonic-gate continue; 7230Sstevel@tonic-gate } 724766Scarlsonj if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { 7250Sstevel@tonic-gate free(name); 7260Sstevel@tonic-gate continue; 7270Sstevel@tonic-gate } 7280Sstevel@tonic-gate free(name); 7290Sstevel@tonic-gate if (zent.zstate_num >= min_state) 7300Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 7310Sstevel@tonic-gate } 7320Sstevel@tonic-gate endzoneent(cookie); 733766Scarlsonj return (Z_OK); 7340Sstevel@tonic-gate } 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate static zone_entry_t * 7370Sstevel@tonic-gate lookup_running_zone(char *str) 7380Sstevel@tonic-gate { 7390Sstevel@tonic-gate zoneid_t zoneid; 7400Sstevel@tonic-gate char *cp; 7410Sstevel@tonic-gate int i; 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate if (fetch_zents() != Z_OK) 7440Sstevel@tonic-gate return (NULL); 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 7470Sstevel@tonic-gate if (strcmp(str, zents[i].zname) == 0) 7480Sstevel@tonic-gate return (&zents[i]); 7490Sstevel@tonic-gate } 7500Sstevel@tonic-gate errno = 0; 7510Sstevel@tonic-gate zoneid = strtol(str, &cp, 0); 7520Sstevel@tonic-gate if (zoneid < MIN_ZONEID || zoneid > MAX_ZONEID || 7530Sstevel@tonic-gate errno != 0 || *cp != '\0') 7540Sstevel@tonic-gate return (NULL); 7550Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 7560Sstevel@tonic-gate if (zoneid == zents[i].zid) 7570Sstevel@tonic-gate return (&zents[i]); 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate return (NULL); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* 7630Sstevel@tonic-gate * Check a bit in a mode_t: if on is B_TRUE, that bit should be on; if 7640Sstevel@tonic-gate * B_FALSE, it should be off. Return B_TRUE if the mode is bad (incorrect). 7650Sstevel@tonic-gate */ 7660Sstevel@tonic-gate static boolean_t 7670Sstevel@tonic-gate bad_mode_bit(mode_t mode, mode_t bit, boolean_t on, char *file) 7680Sstevel@tonic-gate { 7690Sstevel@tonic-gate char *str; 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate assert(bit == S_IRUSR || bit == S_IWUSR || bit == S_IXUSR || 7720Sstevel@tonic-gate bit == S_IRGRP || bit == S_IWGRP || bit == S_IXGRP || 7730Sstevel@tonic-gate bit == S_IROTH || bit == S_IWOTH || bit == S_IXOTH); 7740Sstevel@tonic-gate /* 7750Sstevel@tonic-gate * TRANSLATION_NOTE 7760Sstevel@tonic-gate * The strings below will be used as part of a larger message, 7770Sstevel@tonic-gate * either: 7780Sstevel@tonic-gate * (file name) must be (owner|group|world) (read|writ|execut)able 7790Sstevel@tonic-gate * or 7800Sstevel@tonic-gate * (file name) must not be (owner|group|world) (read|writ|execut)able 7810Sstevel@tonic-gate */ 7820Sstevel@tonic-gate switch (bit) { 7830Sstevel@tonic-gate case S_IRUSR: 7840Sstevel@tonic-gate str = gettext("owner readable"); 7850Sstevel@tonic-gate break; 7860Sstevel@tonic-gate case S_IWUSR: 7870Sstevel@tonic-gate str = gettext("owner writable"); 7880Sstevel@tonic-gate break; 7890Sstevel@tonic-gate case S_IXUSR: 7900Sstevel@tonic-gate str = gettext("owner executable"); 7910Sstevel@tonic-gate break; 7920Sstevel@tonic-gate case S_IRGRP: 7930Sstevel@tonic-gate str = gettext("group readable"); 7940Sstevel@tonic-gate break; 7950Sstevel@tonic-gate case S_IWGRP: 7960Sstevel@tonic-gate str = gettext("group writable"); 7970Sstevel@tonic-gate break; 7980Sstevel@tonic-gate case S_IXGRP: 7990Sstevel@tonic-gate str = gettext("group executable"); 8000Sstevel@tonic-gate break; 8010Sstevel@tonic-gate case S_IROTH: 8020Sstevel@tonic-gate str = gettext("world readable"); 8030Sstevel@tonic-gate break; 8040Sstevel@tonic-gate case S_IWOTH: 8050Sstevel@tonic-gate str = gettext("world writable"); 8060Sstevel@tonic-gate break; 8070Sstevel@tonic-gate case S_IXOTH: 8080Sstevel@tonic-gate str = gettext("world executable"); 8090Sstevel@tonic-gate break; 8100Sstevel@tonic-gate } 8110Sstevel@tonic-gate if ((mode & bit) == (on ? 0 : bit)) { 8120Sstevel@tonic-gate /* 8130Sstevel@tonic-gate * TRANSLATION_NOTE 8140Sstevel@tonic-gate * The first parameter below is a file name; the second 8150Sstevel@tonic-gate * is one of the "(owner|group|world) (read|writ|execut)able" 8160Sstevel@tonic-gate * strings from above. 8170Sstevel@tonic-gate */ 8180Sstevel@tonic-gate /* 8190Sstevel@tonic-gate * The code below could be simplified but not in a way 8200Sstevel@tonic-gate * that would easily translate to non-English locales. 8210Sstevel@tonic-gate */ 8220Sstevel@tonic-gate if (on) { 8230Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must be %s.\n"), 8240Sstevel@tonic-gate file, str); 8250Sstevel@tonic-gate } else { 8260Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must not be %s.\n"), 8270Sstevel@tonic-gate file, str); 8280Sstevel@tonic-gate } 8290Sstevel@tonic-gate return (B_TRUE); 8300Sstevel@tonic-gate } 8310Sstevel@tonic-gate return (B_FALSE); 8320Sstevel@tonic-gate } 8330Sstevel@tonic-gate 8340Sstevel@tonic-gate /* 8350Sstevel@tonic-gate * We want to make sure that no zone has its zone path as a child node 8360Sstevel@tonic-gate * (in the directory sense) of any other. We do that by comparing this 8370Sstevel@tonic-gate * zone's path to the path of all other (non-global) zones. The comparison 8380Sstevel@tonic-gate * in each case is simple: add '/' to the end of the path, then do a 8390Sstevel@tonic-gate * strncmp() of the two paths, using the length of the shorter one. 8400Sstevel@tonic-gate */ 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate static int 8430Sstevel@tonic-gate crosscheck_zonepaths(char *path) 8440Sstevel@tonic-gate { 8450Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 8460Sstevel@tonic-gate char path_copy[MAXPATHLEN]; /* copy of original path */ 8470Sstevel@tonic-gate char rpath_copy[MAXPATHLEN]; /* copy of original rpath */ 8480Sstevel@tonic-gate struct zoneent *ze; 8490Sstevel@tonic-gate int res, err; 8500Sstevel@tonic-gate FILE *cookie; 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate cookie = setzoneent(); 8530Sstevel@tonic-gate while ((ze = getzoneent_private(cookie)) != NULL) { 8540Sstevel@tonic-gate /* Skip zones which are not installed. */ 8550Sstevel@tonic-gate if (ze->zone_state < ZONE_STATE_INSTALLED) { 8560Sstevel@tonic-gate free(ze); 8570Sstevel@tonic-gate continue; 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate /* Skip the global zone and the current target zone. */ 8600Sstevel@tonic-gate if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0 || 8610Sstevel@tonic-gate strcmp(ze->zone_name, target_zone) == 0) { 8620Sstevel@tonic-gate free(ze); 8630Sstevel@tonic-gate continue; 8640Sstevel@tonic-gate } 8650Sstevel@tonic-gate if (strlen(ze->zone_path) == 0) { 8660Sstevel@tonic-gate /* old index file without path, fall back */ 8670Sstevel@tonic-gate if ((err = zone_get_zonepath(ze->zone_name, 8680Sstevel@tonic-gate ze->zone_path, sizeof (ze->zone_path))) != Z_OK) { 8690Sstevel@tonic-gate errno = err; 8700Sstevel@tonic-gate zperror2(ze->zone_name, 8710Sstevel@tonic-gate gettext("could not get zone path")); 8720Sstevel@tonic-gate free(ze); 8730Sstevel@tonic-gate continue; 8740Sstevel@tonic-gate } 8750Sstevel@tonic-gate } 876766Scarlsonj (void) snprintf(path_copy, sizeof (path_copy), "%s%s", 877766Scarlsonj zonecfg_get_root(), ze->zone_path); 878766Scarlsonj res = resolvepath(path_copy, rpath, sizeof (rpath)); 8790Sstevel@tonic-gate if (res == -1) { 8800Sstevel@tonic-gate if (errno != ENOENT) { 881766Scarlsonj zperror(path_copy, B_FALSE); 8820Sstevel@tonic-gate free(ze); 8830Sstevel@tonic-gate return (Z_ERR); 8840Sstevel@tonic-gate } 8850Sstevel@tonic-gate (void) printf(gettext("WARNING: zone %s is installed, " 8860Sstevel@tonic-gate "but its %s %s does not exist.\n"), ze->zone_name, 887766Scarlsonj "zonepath", path_copy); 8880Sstevel@tonic-gate free(ze); 8890Sstevel@tonic-gate continue; 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate rpath[res] = '\0'; 8920Sstevel@tonic-gate (void) snprintf(path_copy, sizeof (path_copy), "%s/", path); 8930Sstevel@tonic-gate (void) snprintf(rpath_copy, sizeof (rpath_copy), "%s/", rpath); 8940Sstevel@tonic-gate if (strncmp(path_copy, rpath_copy, 8950Sstevel@tonic-gate min(strlen(path_copy), strlen(rpath_copy))) == 0) { 896924Sgjelinek /* 897924Sgjelinek * TRANSLATION_NOTE 898924Sgjelinek * zonepath is a literal that should not be translated. 899924Sgjelinek */ 9000Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s zonepath (%s) and " 9010Sstevel@tonic-gate "%s zonepath (%s) overlap.\n"), 9020Sstevel@tonic-gate target_zone, path, ze->zone_name, rpath); 9030Sstevel@tonic-gate free(ze); 9040Sstevel@tonic-gate return (Z_ERR); 9050Sstevel@tonic-gate } 9060Sstevel@tonic-gate free(ze); 9070Sstevel@tonic-gate } 9080Sstevel@tonic-gate endzoneent(cookie); 9090Sstevel@tonic-gate return (Z_OK); 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate 9120Sstevel@tonic-gate static int 9130Sstevel@tonic-gate validate_zonepath(char *path, int cmd_num) 9140Sstevel@tonic-gate { 9150Sstevel@tonic-gate int res; /* result of last library/system call */ 9160Sstevel@tonic-gate boolean_t err = B_FALSE; /* have we run into an error? */ 9170Sstevel@tonic-gate struct stat stbuf; 9182267Sdp struct statvfs64 vfsbuf; 9190Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 9200Sstevel@tonic-gate char ppath[MAXPATHLEN]; /* parent path */ 9210Sstevel@tonic-gate char rppath[MAXPATHLEN]; /* resolved parent path */ 9220Sstevel@tonic-gate char rootpath[MAXPATHLEN]; /* root path */ 9230Sstevel@tonic-gate zone_state_t state; 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate if (path[0] != '/') { 9260Sstevel@tonic-gate (void) fprintf(stderr, 9270Sstevel@tonic-gate gettext("%s is not an absolute path.\n"), path); 9280Sstevel@tonic-gate return (Z_ERR); 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate if ((res = resolvepath(path, rpath, sizeof (rpath))) == -1) { 9310Sstevel@tonic-gate if ((errno != ENOENT) || 9321300Sgjelinek (cmd_num != CMD_VERIFY && cmd_num != CMD_INSTALL && 9331300Sgjelinek cmd_num != CMD_CLONE && cmd_num != CMD_MOVE)) { 9340Sstevel@tonic-gate zperror(path, B_FALSE); 9350Sstevel@tonic-gate return (Z_ERR); 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate if (cmd_num == CMD_VERIFY) { 938924Sgjelinek /* 939924Sgjelinek * TRANSLATION_NOTE 940924Sgjelinek * zoneadm is a literal that should not be translated. 941924Sgjelinek */ 9420Sstevel@tonic-gate (void) fprintf(stderr, gettext("WARNING: %s does not " 943924Sgjelinek "exist, so it could not be verified.\nWhen " 944924Sgjelinek "'zoneadm %s' is run, '%s' will try to create\n%s, " 945924Sgjelinek "and '%s' will be tried again,\nbut the '%s' may " 946924Sgjelinek "fail if:\nthe parent directory of %s is group- or " 947924Sgjelinek "other-writable\nor\n%s overlaps with any other " 9480Sstevel@tonic-gate "installed zones.\n"), path, 9490Sstevel@tonic-gate cmd_to_str(CMD_INSTALL), cmd_to_str(CMD_INSTALL), 9500Sstevel@tonic-gate path, cmd_to_str(CMD_VERIFY), 9510Sstevel@tonic-gate cmd_to_str(CMD_VERIFY), path, path); 9520Sstevel@tonic-gate return (Z_OK); 9530Sstevel@tonic-gate } 9540Sstevel@tonic-gate /* 9550Sstevel@tonic-gate * The zonepath is supposed to be mode 700 but its 9560Sstevel@tonic-gate * parent(s) 755. So use 755 on the mkdirp() then 9570Sstevel@tonic-gate * chmod() the zonepath itself to 700. 9580Sstevel@tonic-gate */ 9590Sstevel@tonic-gate if (mkdirp(path, DEFAULT_DIR_MODE) < 0) { 9600Sstevel@tonic-gate zperror(path, B_FALSE); 9610Sstevel@tonic-gate return (Z_ERR); 9620Sstevel@tonic-gate } 9630Sstevel@tonic-gate /* 9640Sstevel@tonic-gate * If the chmod() fails, report the error, but might 9650Sstevel@tonic-gate * as well continue the verify procedure. 9660Sstevel@tonic-gate */ 9670Sstevel@tonic-gate if (chmod(path, S_IRWXU) != 0) 9680Sstevel@tonic-gate zperror(path, B_FALSE); 9690Sstevel@tonic-gate /* 9700Sstevel@tonic-gate * Since the mkdir() succeeded, we should not have to 9710Sstevel@tonic-gate * worry about a subsequent ENOENT, thus this should 9720Sstevel@tonic-gate * only recurse once. 9730Sstevel@tonic-gate */ 9741300Sgjelinek return (validate_zonepath(path, cmd_num)); 9750Sstevel@tonic-gate } 9760Sstevel@tonic-gate rpath[res] = '\0'; 9770Sstevel@tonic-gate if (strcmp(path, rpath) != 0) { 9780Sstevel@tonic-gate errno = Z_RESOLVED_PATH; 9790Sstevel@tonic-gate zperror(path, B_TRUE); 9800Sstevel@tonic-gate return (Z_ERR); 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate if ((res = stat(rpath, &stbuf)) != 0) { 9830Sstevel@tonic-gate zperror(rpath, B_FALSE); 9840Sstevel@tonic-gate return (Z_ERR); 9850Sstevel@tonic-gate } 9860Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 9870Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 9880Sstevel@tonic-gate rpath); 9890Sstevel@tonic-gate return (Z_ERR); 9900Sstevel@tonic-gate } 9913445Sblakej if (strcmp(stbuf.st_fstype, MNTTYPE_TMPFS) == 0) { 9920Sstevel@tonic-gate (void) printf(gettext("WARNING: %s is on a temporary " 9931867Sgjelinek "file system.\n"), rpath); 9940Sstevel@tonic-gate } 9950Sstevel@tonic-gate if (crosscheck_zonepaths(rpath) != Z_OK) 9960Sstevel@tonic-gate return (Z_ERR); 9970Sstevel@tonic-gate /* 9980Sstevel@tonic-gate * Try to collect and report as many minor errors as possible 9990Sstevel@tonic-gate * before returning, so the user can learn everything that needs 10000Sstevel@tonic-gate * to be fixed up front. 10010Sstevel@tonic-gate */ 10020Sstevel@tonic-gate if (stbuf.st_uid != 0) { 10030Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 10040Sstevel@tonic-gate rpath); 10050Sstevel@tonic-gate err = B_TRUE; 10060Sstevel@tonic-gate } 10070Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rpath); 10080Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rpath); 10090Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rpath); 10100Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRGRP, B_FALSE, rpath); 10110Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rpath); 10120Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXGRP, B_FALSE, rpath); 10130Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IROTH, B_FALSE, rpath); 10140Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rpath); 10150Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXOTH, B_FALSE, rpath); 10160Sstevel@tonic-gate 10170Sstevel@tonic-gate (void) snprintf(ppath, sizeof (ppath), "%s/..", path); 10180Sstevel@tonic-gate if ((res = resolvepath(ppath, rppath, sizeof (rppath))) == -1) { 10190Sstevel@tonic-gate zperror(ppath, B_FALSE); 10200Sstevel@tonic-gate return (Z_ERR); 10210Sstevel@tonic-gate } 10220Sstevel@tonic-gate rppath[res] = '\0'; 10230Sstevel@tonic-gate if ((res = stat(rppath, &stbuf)) != 0) { 10240Sstevel@tonic-gate zperror(rppath, B_FALSE); 10250Sstevel@tonic-gate return (Z_ERR); 10260Sstevel@tonic-gate } 10270Sstevel@tonic-gate /* theoretically impossible */ 10280Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 10290Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 10300Sstevel@tonic-gate rppath); 10310Sstevel@tonic-gate return (Z_ERR); 10320Sstevel@tonic-gate } 10330Sstevel@tonic-gate if (stbuf.st_uid != 0) { 10340Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 10350Sstevel@tonic-gate rppath); 10360Sstevel@tonic-gate err = B_TRUE; 10370Sstevel@tonic-gate } 10380Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rppath); 10390Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rppath); 10400Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rppath); 10410Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rppath); 10420Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rppath); 10430Sstevel@tonic-gate if (strcmp(rpath, rppath) == 0) { 10440Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is its own parent.\n"), 10450Sstevel@tonic-gate rppath); 10460Sstevel@tonic-gate err = B_TRUE; 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate 10492267Sdp if (statvfs64(rpath, &vfsbuf) != 0) { 10500Sstevel@tonic-gate zperror(rpath, B_FALSE); 10510Sstevel@tonic-gate return (Z_ERR); 10520Sstevel@tonic-gate } 1053823Sgjelinek if (strcmp(vfsbuf.f_basetype, MNTTYPE_NFS) == 0) { 1054924Sgjelinek /* 1055924Sgjelinek * TRANSLATION_NOTE 1056924Sgjelinek * Zonepath and NFS are literals that should not be translated. 1057924Sgjelinek */ 1058924Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on an NFS " 10591867Sgjelinek "mounted file system.\n" 10601867Sgjelinek "\tA local file system must be used.\n"), rpath); 1061823Sgjelinek return (Z_ERR); 1062823Sgjelinek } 1063823Sgjelinek if (vfsbuf.f_flag & ST_NOSUID) { 1064924Sgjelinek /* 1065924Sgjelinek * TRANSLATION_NOTE 1066924Sgjelinek * Zonepath and nosuid are literals that should not be 1067924Sgjelinek * translated. 1068924Sgjelinek */ 1069823Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on a nosuid " 10701867Sgjelinek "file system.\n"), rpath); 10710Sstevel@tonic-gate return (Z_ERR); 10720Sstevel@tonic-gate } 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate if ((res = zone_get_state(target_zone, &state)) != Z_OK) { 10750Sstevel@tonic-gate errno = res; 10760Sstevel@tonic-gate zperror2(target_zone, gettext("could not get state")); 10770Sstevel@tonic-gate return (Z_ERR); 10780Sstevel@tonic-gate } 10790Sstevel@tonic-gate /* 10800Sstevel@tonic-gate * The existence of the root path is only bad in the configured state, 10810Sstevel@tonic-gate * as it is *supposed* to be there at the installed and later states. 10821507Sgjelinek * However, the root path is expected to be there if the zone is 10831507Sgjelinek * detached. 10840Sstevel@tonic-gate * State/command mismatches are caught earlier in verify_details(). 10850Sstevel@tonic-gate */ 10861507Sgjelinek if (state == ZONE_STATE_CONFIGURED && cmd_num != CMD_ATTACH) { 10870Sstevel@tonic-gate if (snprintf(rootpath, sizeof (rootpath), "%s/root", rpath) >= 10880Sstevel@tonic-gate sizeof (rootpath)) { 1089924Sgjelinek /* 1090924Sgjelinek * TRANSLATION_NOTE 1091924Sgjelinek * Zonepath is a literal that should not be translated. 1092924Sgjelinek */ 10930Sstevel@tonic-gate (void) fprintf(stderr, 10940Sstevel@tonic-gate gettext("Zonepath %s is too long.\n"), rpath); 10950Sstevel@tonic-gate return (Z_ERR); 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate if ((res = stat(rootpath, &stbuf)) == 0) { 1098*7782Sgerald.jelinek@sun.com struct dirent *dp; 1099*7782Sgerald.jelinek@sun.com DIR *dirp; 1100*7782Sgerald.jelinek@sun.com boolean_t empty = B_TRUE; 1101*7782Sgerald.jelinek@sun.com 1102*7782Sgerald.jelinek@sun.com if (zonecfg_detached(rpath)) { 11031507Sgjelinek (void) fprintf(stderr, 11041507Sgjelinek gettext("Cannot %s detached " 11051507Sgjelinek "zone.\nUse attach or remove %s " 11061507Sgjelinek "directory.\n"), cmd_to_str(cmd_num), 11071507Sgjelinek rpath); 1108*7782Sgerald.jelinek@sun.com return (Z_ERR); 1109*7782Sgerald.jelinek@sun.com } 1110*7782Sgerald.jelinek@sun.com 1111*7782Sgerald.jelinek@sun.com /* Not detached, check if it really looks ok. */ 1112*7782Sgerald.jelinek@sun.com 1113*7782Sgerald.jelinek@sun.com if (!S_ISDIR(stbuf.st_mode)) { 1114*7782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("%s is not a " 1115*7782Sgerald.jelinek@sun.com "directory.\n"), rootpath); 1116*7782Sgerald.jelinek@sun.com return (Z_ERR); 1117*7782Sgerald.jelinek@sun.com } 1118*7782Sgerald.jelinek@sun.com 1119*7782Sgerald.jelinek@sun.com if (stbuf.st_uid != 0) { 1120*7782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("%s is not " 1121*7782Sgerald.jelinek@sun.com "owned by root.\n"), rootpath); 1122*7782Sgerald.jelinek@sun.com return (Z_ERR); 1123*7782Sgerald.jelinek@sun.com } 1124*7782Sgerald.jelinek@sun.com 1125*7782Sgerald.jelinek@sun.com if ((stbuf.st_mode & 0777) != 0755) { 1126*7782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("%s mode is not " 1127*7782Sgerald.jelinek@sun.com "0755.\n"), rootpath); 1128*7782Sgerald.jelinek@sun.com return (Z_ERR); 1129*7782Sgerald.jelinek@sun.com } 1130*7782Sgerald.jelinek@sun.com 1131*7782Sgerald.jelinek@sun.com if ((dirp = opendir(rootpath)) == NULL) { 1132*7782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("Could not " 1133*7782Sgerald.jelinek@sun.com "open rootpath %s\n"), rootpath); 1134*7782Sgerald.jelinek@sun.com return (Z_ERR); 1135*7782Sgerald.jelinek@sun.com } 1136*7782Sgerald.jelinek@sun.com 1137*7782Sgerald.jelinek@sun.com /* Verify that the dir is empty. */ 1138*7782Sgerald.jelinek@sun.com while ((dp = readdir(dirp)) != NULL) { 1139*7782Sgerald.jelinek@sun.com if (strcmp(dp->d_name, ".") == 0 || 1140*7782Sgerald.jelinek@sun.com strcmp(dp->d_name, "..") == 0) 1141*7782Sgerald.jelinek@sun.com continue; 1142*7782Sgerald.jelinek@sun.com 1143*7782Sgerald.jelinek@sun.com empty = B_FALSE; 1144*7782Sgerald.jelinek@sun.com break; 1145*7782Sgerald.jelinek@sun.com } 1146*7782Sgerald.jelinek@sun.com (void) closedir(dirp); 1147*7782Sgerald.jelinek@sun.com 1148*7782Sgerald.jelinek@sun.com if (!empty) { 1149*7782Sgerald.jelinek@sun.com (void) fprintf(stderr, gettext("Rootpath %s " 1150*7782Sgerald.jelinek@sun.com "exists and contains data; remove or move " 1151*7782Sgerald.jelinek@sun.com "aside prior to %s.\n"), rootpath, 1152*7782Sgerald.jelinek@sun.com cmd_to_str(cmd_num)); 1153*7782Sgerald.jelinek@sun.com return (Z_ERR); 1154*7782Sgerald.jelinek@sun.com } 1155*7782Sgerald.jelinek@sun.com 11560Sstevel@tonic-gate } 11570Sstevel@tonic-gate } 11580Sstevel@tonic-gate 11590Sstevel@tonic-gate return (err ? Z_ERR : Z_OK); 11600Sstevel@tonic-gate } 11610Sstevel@tonic-gate 11620Sstevel@tonic-gate static int 11633339Szt129084 invoke_brand_handler(int cmd_num, char *argv[]) 11643339Szt129084 { 11653339Szt129084 zone_dochandle_t handle; 11663339Szt129084 int err; 11673339Szt129084 11683339Szt129084 if ((handle = zonecfg_init_handle()) == NULL) { 11693339Szt129084 zperror(cmd_to_str(cmd_num), B_TRUE); 11703339Szt129084 return (Z_ERR); 11713339Szt129084 } 11723339Szt129084 if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 11733339Szt129084 errno = err; 11743339Szt129084 zperror(cmd_to_str(cmd_num), B_TRUE); 11753339Szt129084 zonecfg_fini_handle(handle); 11763339Szt129084 return (Z_ERR); 11773339Szt129084 } 11783339Szt129084 if (verify_brand(handle, cmd_num, argv) != Z_OK) { 11793339Szt129084 zonecfg_fini_handle(handle); 11803339Szt129084 return (Z_ERR); 11813339Szt129084 } 11823339Szt129084 zonecfg_fini_handle(handle); 11833339Szt129084 return (Z_OK); 11843339Szt129084 } 11853339Szt129084 11863339Szt129084 static int 11870Sstevel@tonic-gate ready_func(int argc, char *argv[]) 11880Sstevel@tonic-gate { 11890Sstevel@tonic-gate zone_cmd_arg_t zarg; 11900Sstevel@tonic-gate int arg; 11910Sstevel@tonic-gate 1192766Scarlsonj if (zonecfg_in_alt_root()) { 1193766Scarlsonj zerror(gettext("cannot ready zone in alternate root")); 1194766Scarlsonj return (Z_ERR); 1195766Scarlsonj } 1196766Scarlsonj 11970Sstevel@tonic-gate optind = 0; 11980Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 11990Sstevel@tonic-gate switch (arg) { 12000Sstevel@tonic-gate case '?': 12010Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12020Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 12030Sstevel@tonic-gate default: 12040Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12050Sstevel@tonic-gate return (Z_USAGE); 12060Sstevel@tonic-gate } 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate if (argc > optind) { 12090Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12100Sstevel@tonic-gate return (Z_USAGE); 12110Sstevel@tonic-gate } 12122712Snn35248 if (sanity_check(target_zone, CMD_READY, B_FALSE, B_FALSE, B_FALSE) 12132712Snn35248 != Z_OK) 12140Sstevel@tonic-gate return (Z_ERR); 12153339Szt129084 if (verify_details(CMD_READY, argv) != Z_OK) 12160Sstevel@tonic-gate return (Z_ERR); 12170Sstevel@tonic-gate 12180Sstevel@tonic-gate zarg.cmd = Z_READY; 12197089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 12200Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 12210Sstevel@tonic-gate return (Z_ERR); 12220Sstevel@tonic-gate } 12230Sstevel@tonic-gate return (Z_OK); 12240Sstevel@tonic-gate } 12250Sstevel@tonic-gate 12260Sstevel@tonic-gate static int 12270Sstevel@tonic-gate boot_func(int argc, char *argv[]) 12280Sstevel@tonic-gate { 12290Sstevel@tonic-gate zone_cmd_arg_t zarg; 12302712Snn35248 boolean_t force = B_FALSE; 12310Sstevel@tonic-gate int arg; 12320Sstevel@tonic-gate 1233766Scarlsonj if (zonecfg_in_alt_root()) { 1234766Scarlsonj zerror(gettext("cannot boot zone in alternate root")); 1235766Scarlsonj return (Z_ERR); 1236766Scarlsonj } 1237766Scarlsonj 12380Sstevel@tonic-gate zarg.bootbuf[0] = '\0'; 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate /* 12412267Sdp * The following getopt processes arguments to zone boot; that 12422267Sdp * is to say, the [here] portion of the argument string: 12432267Sdp * 12442267Sdp * zoneadm -z myzone boot [here] -- -v -m verbose 12452267Sdp * 12462267Sdp * Where [here] can either be nothing, -? (in which case we bail 12472712Snn35248 * and print usage), -f (a private option to indicate that the 12482712Snn35248 * boot operation should be 'forced'), or -s. Support for -s is 12492712Snn35248 * vestigal and obsolete, but is retained because it was a 12502712Snn35248 * documented interface and there are known consumers including 12512712Snn35248 * admin/install; the proper way to specify boot arguments like -s 12522712Snn35248 * is: 12532267Sdp * 12542267Sdp * zoneadm -z myzone boot -- -s -v -m verbose. 12550Sstevel@tonic-gate */ 12560Sstevel@tonic-gate optind = 0; 12572712Snn35248 while ((arg = getopt(argc, argv, "?fs")) != EOF) { 12580Sstevel@tonic-gate switch (arg) { 12590Sstevel@tonic-gate case '?': 12600Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 12610Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 12620Sstevel@tonic-gate case 's': 12630Sstevel@tonic-gate (void) strlcpy(zarg.bootbuf, "-s", 12640Sstevel@tonic-gate sizeof (zarg.bootbuf)); 12650Sstevel@tonic-gate break; 12662712Snn35248 case 'f': 12672712Snn35248 force = B_TRUE; 12682712Snn35248 break; 12690Sstevel@tonic-gate default: 12700Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 12710Sstevel@tonic-gate return (Z_USAGE); 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate } 12742267Sdp 12752267Sdp for (; optind < argc; optind++) { 12762267Sdp if (strlcat(zarg.bootbuf, argv[optind], 12772267Sdp sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) { 12782267Sdp zerror(gettext("Boot argument list too long")); 12792267Sdp return (Z_ERR); 12802267Sdp } 12812267Sdp if (optind < argc - 1) 12822267Sdp if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >= 12832267Sdp sizeof (zarg.bootbuf)) { 12842267Sdp zerror(gettext("Boot argument list too long")); 12852267Sdp return (Z_ERR); 12862267Sdp } 12872267Sdp } 12882712Snn35248 if (sanity_check(target_zone, CMD_BOOT, B_FALSE, B_FALSE, force) 12892712Snn35248 != Z_OK) 12900Sstevel@tonic-gate return (Z_ERR); 12913339Szt129084 if (verify_details(CMD_BOOT, argv) != Z_OK) 12920Sstevel@tonic-gate return (Z_ERR); 12932712Snn35248 zarg.cmd = force ? Z_FORCEBOOT : Z_BOOT; 12947089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 12950Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 12960Sstevel@tonic-gate return (Z_ERR); 12970Sstevel@tonic-gate } 12983247Sgjelinek 12990Sstevel@tonic-gate return (Z_OK); 13000Sstevel@tonic-gate } 13010Sstevel@tonic-gate 13020Sstevel@tonic-gate static void 13030Sstevel@tonic-gate fake_up_local_zone(zoneid_t zid, zone_entry_t *zeptr) 13040Sstevel@tonic-gate { 13050Sstevel@tonic-gate ssize_t result; 13062303Scarlsonj uuid_t uuid; 13072303Scarlsonj FILE *fp; 13083448Sdh155122 ushort_t flags; 13092303Scarlsonj 13102303Scarlsonj (void) memset(zeptr, 0, sizeof (*zeptr)); 13110Sstevel@tonic-gate 13120Sstevel@tonic-gate zeptr->zid = zid; 13132303Scarlsonj 13140Sstevel@tonic-gate /* 13150Sstevel@tonic-gate * Since we're looking up our own (non-global) zone name, 13160Sstevel@tonic-gate * we can be assured that it will succeed. 13170Sstevel@tonic-gate */ 13180Sstevel@tonic-gate result = getzonenamebyid(zid, zeptr->zname, sizeof (zeptr->zname)); 13190Sstevel@tonic-gate assert(result >= 0); 13202303Scarlsonj if (zonecfg_is_scratch(zeptr->zname) && 13212303Scarlsonj (fp = zonecfg_open_scratch("", B_FALSE)) != NULL) { 13222303Scarlsonj (void) zonecfg_reverse_scratch(fp, zeptr->zname, zeptr->zname, 13232303Scarlsonj sizeof (zeptr->zname), NULL, 0); 13242303Scarlsonj zonecfg_close_scratch(fp); 13252303Scarlsonj } 13262303Scarlsonj 13272303Scarlsonj if (is_system_labeled()) { 13281676Sjpk (void) zone_getattr(zid, ZONE_ATTR_ROOT, zeptr->zroot, 13291676Sjpk sizeof (zeptr->zroot)); 13302712Snn35248 (void) strlcpy(zeptr->zbrand, NATIVE_BRAND_NAME, 13314350Std153743 sizeof (zeptr->zbrand)); 13322303Scarlsonj } else { 13332303Scarlsonj (void) strlcpy(zeptr->zroot, "/", sizeof (zeptr->zroot)); 13342712Snn35248 (void) zone_getattr(zid, ZONE_ATTR_BRAND, zeptr->zbrand, 13352712Snn35248 sizeof (zeptr->zbrand)); 13362303Scarlsonj } 13372303Scarlsonj 13380Sstevel@tonic-gate zeptr->zstate_str = "running"; 13392303Scarlsonj if (zonecfg_get_uuid(zeptr->zname, uuid) == Z_OK && 13402303Scarlsonj !uuid_is_null(uuid)) 13412303Scarlsonj uuid_unparse(uuid, zeptr->zuuid); 13423448Sdh155122 13433448Sdh155122 if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags, sizeof (flags)) < 0) { 13443448Sdh155122 zperror2(zeptr->zname, gettext("could not get zone flags")); 13453448Sdh155122 exit(Z_ERR); 13463448Sdh155122 } 13473448Sdh155122 if (flags & ZF_NET_EXCL) 13483448Sdh155122 zeptr->ziptype = ZS_EXCLUSIVE; 13493448Sdh155122 else 13503448Sdh155122 zeptr->ziptype = ZS_SHARED; 13510Sstevel@tonic-gate } 13520Sstevel@tonic-gate 13530Sstevel@tonic-gate static int 13540Sstevel@tonic-gate list_func(int argc, char *argv[]) 13550Sstevel@tonic-gate { 13560Sstevel@tonic-gate zone_entry_t *zentp, zent; 1357766Scarlsonj int arg, retv; 13580Sstevel@tonic-gate boolean_t output = B_FALSE, verbose = B_FALSE, parsable = B_FALSE; 13590Sstevel@tonic-gate zone_state_t min_state = ZONE_STATE_RUNNING; 13600Sstevel@tonic-gate zoneid_t zone_id = getzoneid(); 13610Sstevel@tonic-gate 13620Sstevel@tonic-gate if (target_zone == NULL) { 13630Sstevel@tonic-gate /* all zones: default view to running but allow override */ 13640Sstevel@tonic-gate optind = 0; 13650Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?cipv")) != EOF) { 13660Sstevel@tonic-gate switch (arg) { 13670Sstevel@tonic-gate case '?': 13680Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13690Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13701339Sjonb /* 13711339Sjonb * The 'i' and 'c' options are not mutually 13721339Sjonb * exclusive so if 'c' is given, then min_state 13731339Sjonb * is set to 0 (ZONE_STATE_CONFIGURED) which is 13741339Sjonb * the lowest possible state. If 'i' is given, 13751339Sjonb * then min_state is set to be the lowest state 13761339Sjonb * so far. 13771339Sjonb */ 13780Sstevel@tonic-gate case 'c': 13790Sstevel@tonic-gate min_state = ZONE_STATE_CONFIGURED; 13800Sstevel@tonic-gate break; 13810Sstevel@tonic-gate case 'i': 13821339Sjonb min_state = min(ZONE_STATE_INSTALLED, 13831339Sjonb min_state); 13841339Sjonb 13850Sstevel@tonic-gate break; 13860Sstevel@tonic-gate case 'p': 13870Sstevel@tonic-gate parsable = B_TRUE; 13880Sstevel@tonic-gate break; 13890Sstevel@tonic-gate case 'v': 13900Sstevel@tonic-gate verbose = B_TRUE; 13910Sstevel@tonic-gate break; 13920Sstevel@tonic-gate default: 13930Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13940Sstevel@tonic-gate return (Z_USAGE); 13950Sstevel@tonic-gate } 13960Sstevel@tonic-gate } 13970Sstevel@tonic-gate if (parsable && verbose) { 13980Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 13990Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14000Sstevel@tonic-gate return (Z_ERR); 14010Sstevel@tonic-gate } 14021676Sjpk if (zone_id == GLOBAL_ZONEID || is_system_labeled()) { 1403766Scarlsonj retv = zone_print_list(min_state, verbose, parsable); 14040Sstevel@tonic-gate } else { 14052712Snn35248 fake_up_local_zone(zone_id, &zent); 1406766Scarlsonj retv = Z_OK; 14070Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14080Sstevel@tonic-gate } 1409766Scarlsonj return (retv); 14100Sstevel@tonic-gate } 14110Sstevel@tonic-gate 14120Sstevel@tonic-gate /* 14130Sstevel@tonic-gate * Specific target zone: disallow -i/-c suboptions. 14140Sstevel@tonic-gate */ 14150Sstevel@tonic-gate optind = 0; 14160Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?pv")) != EOF) { 14170Sstevel@tonic-gate switch (arg) { 14180Sstevel@tonic-gate case '?': 14190Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14200Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 14210Sstevel@tonic-gate case 'p': 14220Sstevel@tonic-gate parsable = B_TRUE; 14230Sstevel@tonic-gate break; 14240Sstevel@tonic-gate case 'v': 14250Sstevel@tonic-gate verbose = B_TRUE; 14260Sstevel@tonic-gate break; 14270Sstevel@tonic-gate default: 14280Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14290Sstevel@tonic-gate return (Z_USAGE); 14300Sstevel@tonic-gate } 14310Sstevel@tonic-gate } 14320Sstevel@tonic-gate if (parsable && verbose) { 14330Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14340Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14350Sstevel@tonic-gate return (Z_ERR); 14360Sstevel@tonic-gate } 14370Sstevel@tonic-gate if (argc > optind) { 14380Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14390Sstevel@tonic-gate return (Z_USAGE); 14400Sstevel@tonic-gate } 14413979Sgjelinek if (zone_id != GLOBAL_ZONEID && !is_system_labeled()) { 14420Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14430Sstevel@tonic-gate /* 14440Sstevel@tonic-gate * main() will issue a Z_NO_ZONE error if it cannot get an 14450Sstevel@tonic-gate * id for target_zone, which in a non-global zone should 14460Sstevel@tonic-gate * happen for any zone name except `zonename`. Thus we 14470Sstevel@tonic-gate * assert() that here but don't otherwise check. 14480Sstevel@tonic-gate */ 14490Sstevel@tonic-gate assert(strcmp(zent.zname, target_zone) == 0); 14500Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14510Sstevel@tonic-gate output = B_TRUE; 14520Sstevel@tonic-gate } else if ((zentp = lookup_running_zone(target_zone)) != NULL) { 14530Sstevel@tonic-gate zone_print(zentp, verbose, parsable); 14540Sstevel@tonic-gate output = B_TRUE; 1455766Scarlsonj } else if (lookup_zone_info(target_zone, ZONE_ID_UNDEFINED, 1456766Scarlsonj &zent) == Z_OK) { 14570Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14580Sstevel@tonic-gate output = B_TRUE; 14590Sstevel@tonic-gate } 14603339Szt129084 14613339Szt129084 /* 14623339Szt129084 * Invoke brand-specific handler. Note that we do this 14633542Sgjelinek * only if we're in the global zone, and target_zone is specified 14643542Sgjelinek * and it is not the global zone. 14653339Szt129084 */ 14663542Sgjelinek if (zone_id == GLOBAL_ZONEID && target_zone != NULL && 14673542Sgjelinek strcmp(target_zone, GLOBAL_ZONENAME) != 0) 14683339Szt129084 if (invoke_brand_handler(CMD_LIST, argv) != Z_OK) 14693339Szt129084 return (Z_ERR); 14703339Szt129084 14710Sstevel@tonic-gate return (output ? Z_OK : Z_ERR); 14720Sstevel@tonic-gate } 14730Sstevel@tonic-gate 14740Sstevel@tonic-gate static void 14750Sstevel@tonic-gate sigterm(int sig) 14760Sstevel@tonic-gate { 14770Sstevel@tonic-gate /* 14780Sstevel@tonic-gate * Ignore SIG{INT,TERM}, so we don't end up in an infinite loop, 14790Sstevel@tonic-gate * then propagate the signal to our process group. 14800Sstevel@tonic-gate */ 14812712Snn35248 assert(sig == SIGINT || sig == SIGTERM); 14820Sstevel@tonic-gate (void) sigset(SIGINT, SIG_IGN); 14830Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_IGN); 14840Sstevel@tonic-gate (void) kill(0, sig); 14850Sstevel@tonic-gate child_killed = B_TRUE; 14860Sstevel@tonic-gate } 14870Sstevel@tonic-gate 14880Sstevel@tonic-gate static int 14890Sstevel@tonic-gate do_subproc(char *cmdbuf) 14900Sstevel@tonic-gate { 14910Sstevel@tonic-gate char inbuf[1024]; /* arbitrary large amount */ 14920Sstevel@tonic-gate FILE *file; 14930Sstevel@tonic-gate 14942712Snn35248 do_subproc_cnt++; 14950Sstevel@tonic-gate child_killed = B_FALSE; 14960Sstevel@tonic-gate /* 14970Sstevel@tonic-gate * We use popen(3c) to launch child processes for [un]install; 14980Sstevel@tonic-gate * this library call does not return a PID, so we have to kill 14990Sstevel@tonic-gate * the whole process group. To avoid killing our parent, we 15000Sstevel@tonic-gate * become a process group leader here. But doing so can wreak 15010Sstevel@tonic-gate * havoc with reading from stdin when launched by a non-job-control 15020Sstevel@tonic-gate * shell, so we close stdin and reopen it as /dev/null first. 15030Sstevel@tonic-gate */ 15040Sstevel@tonic-gate (void) close(STDIN_FILENO); 15052712Snn35248 (void) openat(STDIN_FILENO, "/dev/null", O_RDONLY); 15062712Snn35248 if (!zoneadm_is_nested) 15072712Snn35248 (void) setpgid(0, 0); 15080Sstevel@tonic-gate (void) sigset(SIGINT, sigterm); 15090Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm); 15100Sstevel@tonic-gate file = popen(cmdbuf, "r"); 15110Sstevel@tonic-gate for (;;) { 15120Sstevel@tonic-gate if (child_killed || fgets(inbuf, sizeof (inbuf), file) == NULL) 15130Sstevel@tonic-gate break; 15140Sstevel@tonic-gate (void) fputs(inbuf, stdout); 15150Sstevel@tonic-gate } 15160Sstevel@tonic-gate (void) sigset(SIGINT, SIG_DFL); 15170Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_DFL); 15180Sstevel@tonic-gate return (pclose(file)); 15190Sstevel@tonic-gate } 15200Sstevel@tonic-gate 15217089Sgjelinek int 15222712Snn35248 do_subproc_interactive(char *cmdbuf) 15232712Snn35248 { 15242712Snn35248 void (*saveint)(int); 15252712Snn35248 void (*saveterm)(int); 15262712Snn35248 void (*savequit)(int); 15272712Snn35248 void (*savehup)(int); 15282712Snn35248 int pid, child, status; 15292712Snn35248 15302712Snn35248 /* 15312712Snn35248 * do_subproc() links stdin to /dev/null, which would break any 15322712Snn35248 * interactive subprocess we try to launch here. Similarly, we 15332712Snn35248 * can't have been launched as a subprocess ourselves. 15342712Snn35248 */ 15352712Snn35248 assert(do_subproc_cnt == 0 && !zoneadm_is_nested); 15362712Snn35248 15372712Snn35248 if ((child = vfork()) == 0) { 15382712Snn35248 (void) execl("/bin/sh", "sh", "-c", cmdbuf, (char *)NULL); 15392712Snn35248 } 15402712Snn35248 15412712Snn35248 if (child == -1) 15422712Snn35248 return (-1); 15432712Snn35248 15442712Snn35248 saveint = sigset(SIGINT, SIG_IGN); 15452712Snn35248 saveterm = sigset(SIGTERM, SIG_IGN); 15462712Snn35248 savequit = sigset(SIGQUIT, SIG_IGN); 15472712Snn35248 savehup = sigset(SIGHUP, SIG_IGN); 15482712Snn35248 15492712Snn35248 while ((pid = waitpid(child, &status, 0)) != child && pid != -1) 15502712Snn35248 ; 15512712Snn35248 15522712Snn35248 (void) sigset(SIGINT, saveint); 15532712Snn35248 (void) sigset(SIGTERM, saveterm); 15542712Snn35248 (void) sigset(SIGQUIT, savequit); 15552712Snn35248 (void) sigset(SIGHUP, savehup); 15562712Snn35248 15572712Snn35248 return (pid == -1 ? -1 : status); 15582712Snn35248 } 15592712Snn35248 15607089Sgjelinek int 15612712Snn35248 subproc_status(const char *cmd, int status, boolean_t verbose_failure) 15620Sstevel@tonic-gate { 15630Sstevel@tonic-gate if (WIFEXITED(status)) { 15640Sstevel@tonic-gate int exit_code = WEXITSTATUS(status); 15650Sstevel@tonic-gate 15662712Snn35248 if ((verbose_failure) && (exit_code != ZONE_SUBPROC_OK)) 15672712Snn35248 zerror(gettext("'%s' failed with exit code %d."), cmd, 15682712Snn35248 exit_code); 15692712Snn35248 15702712Snn35248 return (exit_code); 15710Sstevel@tonic-gate } else if (WIFSIGNALED(status)) { 15720Sstevel@tonic-gate int signal = WTERMSIG(status); 15730Sstevel@tonic-gate char sigstr[SIG2STR_MAX]; 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate if (sig2str(signal, sigstr) == 0) { 15760Sstevel@tonic-gate zerror(gettext("'%s' terminated by signal SIG%s."), cmd, 15770Sstevel@tonic-gate sigstr); 15780Sstevel@tonic-gate } else { 15790Sstevel@tonic-gate zerror(gettext("'%s' terminated by an unknown signal."), 15800Sstevel@tonic-gate cmd); 15810Sstevel@tonic-gate } 15820Sstevel@tonic-gate } else { 15830Sstevel@tonic-gate zerror(gettext("'%s' failed for unknown reasons."), cmd); 15840Sstevel@tonic-gate } 15852712Snn35248 15862712Snn35248 /* 15872712Snn35248 * Assume a subprocess that died due to a signal or an unknown error 15882712Snn35248 * should be considered an exit code of ZONE_SUBPROC_FATAL, as the 15892712Snn35248 * user will likely need to do some manual cleanup. 15902712Snn35248 */ 15912712Snn35248 return (ZONE_SUBPROC_FATAL); 15920Sstevel@tonic-gate } 15930Sstevel@tonic-gate 15940Sstevel@tonic-gate /* 15950Sstevel@tonic-gate * Various sanity checks; make sure: 15960Sstevel@tonic-gate * 1. We're in the global zone. 15970Sstevel@tonic-gate * 2. The calling user has sufficient privilege. 15980Sstevel@tonic-gate * 3. The target zone is neither the global zone nor anything starting with 15990Sstevel@tonic-gate * "SUNW". 16000Sstevel@tonic-gate * 4a. If we're looking for a 'not running' (i.e., configured or installed) 16010Sstevel@tonic-gate * zone, the name service knows about it. 16020Sstevel@tonic-gate * 4b. For some operations which expect a zone not to be running, that it is 16030Sstevel@tonic-gate * not already running (or ready). 16040Sstevel@tonic-gate */ 16050Sstevel@tonic-gate static int 16060Sstevel@tonic-gate sanity_check(char *zone, int cmd_num, boolean_t running, 16072712Snn35248 boolean_t unsafe_when_running, boolean_t force) 16080Sstevel@tonic-gate { 16090Sstevel@tonic-gate zone_entry_t *zent; 16100Sstevel@tonic-gate priv_set_t *privset; 16112712Snn35248 zone_state_t state, min_state; 1612766Scarlsonj char kernzone[ZONENAME_MAX]; 1613766Scarlsonj FILE *fp; 16140Sstevel@tonic-gate 16150Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 16161645Scomay switch (cmd_num) { 16171645Scomay case CMD_HALT: 16181645Scomay zerror(gettext("use %s to %s this zone."), "halt(1M)", 16191645Scomay cmd_to_str(cmd_num)); 16201645Scomay break; 16211645Scomay case CMD_REBOOT: 16221645Scomay zerror(gettext("use %s to %s this zone."), 16231645Scomay "reboot(1M)", cmd_to_str(cmd_num)); 16241645Scomay break; 16251645Scomay default: 16261645Scomay zerror(gettext("must be in the global zone to %s a " 16271645Scomay "zone."), cmd_to_str(cmd_num)); 16281645Scomay break; 16291645Scomay } 16300Sstevel@tonic-gate return (Z_ERR); 16310Sstevel@tonic-gate } 16320Sstevel@tonic-gate 16330Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 16340Sstevel@tonic-gate zerror(gettext("%s failed"), "priv_allocset"); 16350Sstevel@tonic-gate return (Z_ERR); 16360Sstevel@tonic-gate } 16370Sstevel@tonic-gate 16380Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 16390Sstevel@tonic-gate zerror(gettext("%s failed"), "getppriv"); 16400Sstevel@tonic-gate priv_freeset(privset); 16410Sstevel@tonic-gate return (Z_ERR); 16420Sstevel@tonic-gate } 16430Sstevel@tonic-gate 16440Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 16450Sstevel@tonic-gate zerror(gettext("only a privileged user may %s a zone."), 16460Sstevel@tonic-gate cmd_to_str(cmd_num)); 16470Sstevel@tonic-gate priv_freeset(privset); 16480Sstevel@tonic-gate return (Z_ERR); 16490Sstevel@tonic-gate } 16500Sstevel@tonic-gate priv_freeset(privset); 16510Sstevel@tonic-gate 16520Sstevel@tonic-gate if (zone == NULL) { 16530Sstevel@tonic-gate zerror(gettext("no zone specified")); 16540Sstevel@tonic-gate return (Z_ERR); 16550Sstevel@tonic-gate } 16560Sstevel@tonic-gate 16570Sstevel@tonic-gate if (strcmp(zone, GLOBAL_ZONENAME) == 0) { 16580Sstevel@tonic-gate zerror(gettext("%s operation is invalid for the global zone."), 16590Sstevel@tonic-gate cmd_to_str(cmd_num)); 16600Sstevel@tonic-gate return (Z_ERR); 16610Sstevel@tonic-gate } 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate if (strncmp(zone, "SUNW", 4) == 0) { 16640Sstevel@tonic-gate zerror(gettext("%s operation is invalid for zones starting " 16650Sstevel@tonic-gate "with SUNW."), cmd_to_str(cmd_num)); 16660Sstevel@tonic-gate return (Z_ERR); 16670Sstevel@tonic-gate } 16680Sstevel@tonic-gate 1669766Scarlsonj if (!zonecfg_in_alt_root()) { 1670766Scarlsonj zent = lookup_running_zone(zone); 1671766Scarlsonj } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) { 1672766Scarlsonj zent = NULL; 1673766Scarlsonj } else { 1674766Scarlsonj if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), 1675766Scarlsonj kernzone, sizeof (kernzone)) == 0) 1676766Scarlsonj zent = lookup_running_zone(kernzone); 1677766Scarlsonj else 1678766Scarlsonj zent = NULL; 1679766Scarlsonj zonecfg_close_scratch(fp); 1680766Scarlsonj } 1681766Scarlsonj 16820Sstevel@tonic-gate /* 16830Sstevel@tonic-gate * Look up from the kernel for 'running' zones. 16840Sstevel@tonic-gate */ 16852712Snn35248 if (running && !force) { 16860Sstevel@tonic-gate if (zent == NULL) { 16870Sstevel@tonic-gate zerror(gettext("not running")); 16880Sstevel@tonic-gate return (Z_ERR); 16890Sstevel@tonic-gate } 16900Sstevel@tonic-gate } else { 16910Sstevel@tonic-gate int err; 16920Sstevel@tonic-gate 16930Sstevel@tonic-gate if (unsafe_when_running && zent != NULL) { 16940Sstevel@tonic-gate /* check whether the zone is ready or running */ 16950Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, 16960Sstevel@tonic-gate &zent->zstate_num)) != Z_OK) { 16970Sstevel@tonic-gate errno = err; 16980Sstevel@tonic-gate zperror2(zent->zname, 16990Sstevel@tonic-gate gettext("could not get state")); 17000Sstevel@tonic-gate /* can't tell, so hedge */ 17010Sstevel@tonic-gate zent->zstate_str = "ready/running"; 17020Sstevel@tonic-gate } else { 17030Sstevel@tonic-gate zent->zstate_str = 17040Sstevel@tonic-gate zone_state_str(zent->zstate_num); 17050Sstevel@tonic-gate } 17060Sstevel@tonic-gate zerror(gettext("%s operation is invalid for %s zones."), 17070Sstevel@tonic-gate cmd_to_str(cmd_num), zent->zstate_str); 17080Sstevel@tonic-gate return (Z_ERR); 17090Sstevel@tonic-gate } 17100Sstevel@tonic-gate if ((err = zone_get_state(zone, &state)) != Z_OK) { 17110Sstevel@tonic-gate errno = err; 17120Sstevel@tonic-gate zperror2(zone, gettext("could not get state")); 17130Sstevel@tonic-gate return (Z_ERR); 17140Sstevel@tonic-gate } 17150Sstevel@tonic-gate switch (cmd_num) { 17160Sstevel@tonic-gate case CMD_UNINSTALL: 17170Sstevel@tonic-gate if (state == ZONE_STATE_CONFIGURED) { 17180Sstevel@tonic-gate zerror(gettext("is already in state '%s'."), 17190Sstevel@tonic-gate zone_state_str(ZONE_STATE_CONFIGURED)); 17200Sstevel@tonic-gate return (Z_ERR); 17210Sstevel@tonic-gate } 17220Sstevel@tonic-gate break; 17231507Sgjelinek case CMD_ATTACH: 17241300Sgjelinek case CMD_CLONE: 17250Sstevel@tonic-gate case CMD_INSTALL: 17260Sstevel@tonic-gate if (state == ZONE_STATE_INSTALLED) { 17270Sstevel@tonic-gate zerror(gettext("is already %s."), 17280Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED)); 17290Sstevel@tonic-gate return (Z_ERR); 17300Sstevel@tonic-gate } else if (state == ZONE_STATE_INCOMPLETE) { 17310Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 17320Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 17330Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 17340Sstevel@tonic-gate return (Z_ERR); 17350Sstevel@tonic-gate } 17360Sstevel@tonic-gate break; 17371507Sgjelinek case CMD_DETACH: 17381300Sgjelinek case CMD_MOVE: 17390Sstevel@tonic-gate case CMD_READY: 17400Sstevel@tonic-gate case CMD_BOOT: 1741766Scarlsonj case CMD_MOUNT: 17422303Scarlsonj case CMD_MARK: 17432712Snn35248 if ((cmd_num == CMD_BOOT || cmd_num == CMD_MOUNT) && 17442712Snn35248 force) 17452712Snn35248 min_state = ZONE_STATE_INCOMPLETE; 17462712Snn35248 else 17472712Snn35248 min_state = ZONE_STATE_INSTALLED; 17482712Snn35248 17492712Snn35248 if (force && cmd_num == CMD_BOOT && is_native_zone) { 17502712Snn35248 zerror(gettext("Only branded zones may be " 17512712Snn35248 "force-booted.")); 17522712Snn35248 return (Z_ERR); 17532712Snn35248 } 17542712Snn35248 17552712Snn35248 if (state < min_state) { 17560Sstevel@tonic-gate zerror(gettext("must be %s before %s."), 17572712Snn35248 zone_state_str(min_state), 17580Sstevel@tonic-gate cmd_to_str(cmd_num)); 17590Sstevel@tonic-gate return (Z_ERR); 17600Sstevel@tonic-gate } 17610Sstevel@tonic-gate break; 17620Sstevel@tonic-gate case CMD_VERIFY: 17630Sstevel@tonic-gate if (state == ZONE_STATE_INCOMPLETE) { 17640Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 17650Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 17660Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 17670Sstevel@tonic-gate return (Z_ERR); 17680Sstevel@tonic-gate } 17690Sstevel@tonic-gate break; 1770766Scarlsonj case CMD_UNMOUNT: 1771766Scarlsonj if (state != ZONE_STATE_MOUNTED) { 1772766Scarlsonj zerror(gettext("must be %s before %s."), 1773766Scarlsonj zone_state_str(ZONE_STATE_MOUNTED), 1774766Scarlsonj cmd_to_str(cmd_num)); 1775766Scarlsonj return (Z_ERR); 1776766Scarlsonj } 1777766Scarlsonj break; 17780Sstevel@tonic-gate } 17790Sstevel@tonic-gate } 17800Sstevel@tonic-gate return (Z_OK); 17810Sstevel@tonic-gate } 17820Sstevel@tonic-gate 17830Sstevel@tonic-gate static int 17840Sstevel@tonic-gate halt_func(int argc, char *argv[]) 17850Sstevel@tonic-gate { 17860Sstevel@tonic-gate zone_cmd_arg_t zarg; 17870Sstevel@tonic-gate int arg; 17880Sstevel@tonic-gate 1789766Scarlsonj if (zonecfg_in_alt_root()) { 1790766Scarlsonj zerror(gettext("cannot halt zone in alternate root")); 1791766Scarlsonj return (Z_ERR); 1792766Scarlsonj } 1793766Scarlsonj 17940Sstevel@tonic-gate optind = 0; 17950Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17960Sstevel@tonic-gate switch (arg) { 17970Sstevel@tonic-gate case '?': 17980Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17990Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 18000Sstevel@tonic-gate default: 18010Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 18020Sstevel@tonic-gate return (Z_USAGE); 18030Sstevel@tonic-gate } 18040Sstevel@tonic-gate } 18050Sstevel@tonic-gate if (argc > optind) { 18060Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 18070Sstevel@tonic-gate return (Z_USAGE); 18080Sstevel@tonic-gate } 18090Sstevel@tonic-gate /* 18100Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 18110Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 18120Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 18130Sstevel@tonic-gate */ 18142712Snn35248 if (sanity_check(target_zone, CMD_HALT, B_FALSE, B_FALSE, B_FALSE) 18152712Snn35248 != Z_OK) 18160Sstevel@tonic-gate return (Z_ERR); 18170Sstevel@tonic-gate 18183339Szt129084 /* 18193339Szt129084 * Invoke brand-specific handler. 18203339Szt129084 */ 18213339Szt129084 if (invoke_brand_handler(CMD_HALT, argv) != Z_OK) 18223339Szt129084 return (Z_ERR); 18233339Szt129084 18240Sstevel@tonic-gate zarg.cmd = Z_HALT; 18257089Sgjelinek return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, 18267089Sgjelinek B_TRUE) == 0) ? Z_OK : Z_ERR); 18270Sstevel@tonic-gate } 18280Sstevel@tonic-gate 18290Sstevel@tonic-gate static int 18300Sstevel@tonic-gate reboot_func(int argc, char *argv[]) 18310Sstevel@tonic-gate { 18320Sstevel@tonic-gate zone_cmd_arg_t zarg; 18330Sstevel@tonic-gate int arg; 18340Sstevel@tonic-gate 1835766Scarlsonj if (zonecfg_in_alt_root()) { 1836766Scarlsonj zerror(gettext("cannot reboot zone in alternate root")); 1837766Scarlsonj return (Z_ERR); 1838766Scarlsonj } 1839766Scarlsonj 18400Sstevel@tonic-gate optind = 0; 18410Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 18420Sstevel@tonic-gate switch (arg) { 18430Sstevel@tonic-gate case '?': 18440Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 18450Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 18460Sstevel@tonic-gate default: 18470Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 18480Sstevel@tonic-gate return (Z_USAGE); 18490Sstevel@tonic-gate } 18500Sstevel@tonic-gate } 18512267Sdp 18522267Sdp zarg.bootbuf[0] = '\0'; 18532267Sdp for (; optind < argc; optind++) { 18542267Sdp if (strlcat(zarg.bootbuf, argv[optind], 18552267Sdp sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) { 18562267Sdp zerror(gettext("Boot argument list too long")); 18572267Sdp return (Z_ERR); 18582267Sdp } 18592267Sdp if (optind < argc - 1) 18602267Sdp if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >= 18612267Sdp sizeof (zarg.bootbuf)) { 18622267Sdp zerror(gettext("Boot argument list too long")); 18632267Sdp return (Z_ERR); 18642267Sdp } 18652267Sdp } 18662267Sdp 18672267Sdp 18680Sstevel@tonic-gate /* 18690Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 18700Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 18710Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 18720Sstevel@tonic-gate */ 18732712Snn35248 if (sanity_check(target_zone, CMD_REBOOT, B_TRUE, B_FALSE, B_FALSE) 18742712Snn35248 != Z_OK) 18750Sstevel@tonic-gate return (Z_ERR); 18763339Szt129084 if (verify_details(CMD_REBOOT, argv) != Z_OK) 1877823Sgjelinek return (Z_ERR); 18780Sstevel@tonic-gate 18790Sstevel@tonic-gate zarg.cmd = Z_REBOOT; 18807089Sgjelinek return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0) 18817089Sgjelinek ? Z_OK : Z_ERR); 18827089Sgjelinek } 18837089Sgjelinek 18847089Sgjelinek static int 18857089Sgjelinek get_hook(brand_handle_t bh, char *cmd, size_t len, int (*bp)(brand_handle_t, 18867089Sgjelinek const char *, const char *, char *, size_t), char *zonename, char *zonepath) 18877089Sgjelinek { 18887089Sgjelinek if (strlcpy(cmd, EXEC_PREFIX, len) >= len) 18897089Sgjelinek return (Z_ERR); 18907089Sgjelinek 18917089Sgjelinek if (bp(bh, zonename, zonepath, cmd + EXEC_LEN, len - EXEC_LEN) != 0) 18927089Sgjelinek return (Z_ERR); 18937089Sgjelinek 18947089Sgjelinek if (strlen(cmd) <= EXEC_LEN) 18957089Sgjelinek cmd[0] = '\0'; 18967089Sgjelinek 18977089Sgjelinek return (Z_OK); 18980Sstevel@tonic-gate } 18990Sstevel@tonic-gate 19000Sstevel@tonic-gate static int 19013339Szt129084 verify_brand(zone_dochandle_t handle, int cmd_num, char *argv[]) 19022712Snn35248 { 19032712Snn35248 char cmdbuf[MAXPATHLEN]; 19042712Snn35248 int err; 19052712Snn35248 char zonepath[MAXPATHLEN]; 19062727Sedp brand_handle_t bh = NULL; 19073339Szt129084 int status, i; 19082712Snn35248 19092712Snn35248 /* 19102712Snn35248 * Fetch the verify command from the brand configuration. 19112712Snn35248 * "exec" the command so that the returned status is that of 19122712Snn35248 * the command and not the shell. 19132712Snn35248 */ 19147089Sgjelinek if (handle == NULL) { 19157089Sgjelinek (void) strlcpy(zonepath, "-", sizeof (zonepath)); 19167089Sgjelinek } else if ((err = zonecfg_get_zonepath(handle, zonepath, 19177089Sgjelinek sizeof (zonepath))) != Z_OK) { 19182712Snn35248 errno = err; 19193339Szt129084 zperror(cmd_to_str(cmd_num), B_TRUE); 19202712Snn35248 return (Z_ERR); 19212712Snn35248 } 19222727Sedp if ((bh = brand_open(target_brand)) == NULL) { 19232712Snn35248 zerror(gettext("missing or invalid brand")); 19242712Snn35248 return (Z_ERR); 19252712Snn35248 } 19262712Snn35248 19272712Snn35248 /* 19282712Snn35248 * If the brand has its own verification routine, execute it now. 19293339Szt129084 * The verification routine validates the intended zoneadm 19303339Szt129084 * operation for the specific brand. The zoneadm subcommand and 19313339Szt129084 * all its arguments are passed to the routine. 19322712Snn35248 */ 19337089Sgjelinek err = get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_verify_adm, 19347089Sgjelinek target_zone, zonepath); 19352727Sedp brand_close(bh); 19367089Sgjelinek if (err != Z_OK) 19373339Szt129084 return (Z_BRAND_ERROR); 19387089Sgjelinek if (cmdbuf[0] == '\0') 19393339Szt129084 return (Z_OK); 19403339Szt129084 19413339Szt129084 if (strlcat(cmdbuf, cmd_to_str(cmd_num), 19423339Szt129084 sizeof (cmdbuf)) >= sizeof (cmdbuf)) 19433339Szt129084 return (Z_ERR); 19443339Szt129084 19453339Szt129084 /* Build the argv string */ 19463339Szt129084 i = 0; 19473339Szt129084 while (argv[i] != NULL) { 19483339Szt129084 if ((strlcat(cmdbuf, " ", 19493339Szt129084 sizeof (cmdbuf)) >= sizeof (cmdbuf)) || 19503339Szt129084 (strlcat(cmdbuf, argv[i++], 19513339Szt129084 sizeof (cmdbuf)) >= sizeof (cmdbuf))) 19523339Szt129084 return (Z_ERR); 19533339Szt129084 } 19543339Szt129084 19553686Sgjelinek if (zoneadm_is_nested) 19563686Sgjelinek status = do_subproc(cmdbuf); 19573686Sgjelinek else 19583686Sgjelinek status = do_subproc_interactive(cmdbuf); 19593339Szt129084 err = subproc_status(gettext("brand-specific verification"), 19603339Szt129084 status, B_FALSE); 19613339Szt129084 19623339Szt129084 return ((err == ZONE_SUBPROC_OK) ? Z_OK : Z_BRAND_ERROR); 19632712Snn35248 } 19642712Snn35248 19652712Snn35248 static int 19660Sstevel@tonic-gate verify_rctls(zone_dochandle_t handle) 19670Sstevel@tonic-gate { 19680Sstevel@tonic-gate struct zone_rctltab rctltab; 19690Sstevel@tonic-gate size_t rbs = rctlblk_size(); 19700Sstevel@tonic-gate rctlblk_t *rctlblk; 19710Sstevel@tonic-gate int error = Z_INVAL; 19720Sstevel@tonic-gate 19730Sstevel@tonic-gate if ((rctlblk = malloc(rbs)) == NULL) { 19740Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rbs, 19750Sstevel@tonic-gate strerror(errno)); 19760Sstevel@tonic-gate return (Z_NOMEM); 19770Sstevel@tonic-gate } 19780Sstevel@tonic-gate 19790Sstevel@tonic-gate if (zonecfg_setrctlent(handle) != Z_OK) { 19800Sstevel@tonic-gate zerror(gettext("zonecfg_setrctlent failed")); 19810Sstevel@tonic-gate free(rctlblk); 19820Sstevel@tonic-gate return (error); 19830Sstevel@tonic-gate } 19840Sstevel@tonic-gate 19850Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19860Sstevel@tonic-gate while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { 19870Sstevel@tonic-gate struct zone_rctlvaltab *rctlval; 19880Sstevel@tonic-gate const char *name = rctltab.zone_rctl_name; 19890Sstevel@tonic-gate 19900Sstevel@tonic-gate if (!zonecfg_is_rctl(name)) { 19910Sstevel@tonic-gate zerror(gettext("WARNING: Ignoring unrecognized rctl " 19920Sstevel@tonic-gate "'%s'."), name); 19930Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19940Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19950Sstevel@tonic-gate continue; 19960Sstevel@tonic-gate } 19970Sstevel@tonic-gate 19980Sstevel@tonic-gate for (rctlval = rctltab.zone_rctl_valptr; rctlval != NULL; 19990Sstevel@tonic-gate rctlval = rctlval->zone_rctlval_next) { 20000Sstevel@tonic-gate if (zonecfg_construct_rctlblk(rctlval, rctlblk) 20010Sstevel@tonic-gate != Z_OK) { 20020Sstevel@tonic-gate zerror(gettext("invalid rctl value: " 20030Sstevel@tonic-gate "(priv=%s,limit=%s,action%s)"), 20040Sstevel@tonic-gate rctlval->zone_rctlval_priv, 20050Sstevel@tonic-gate rctlval->zone_rctlval_limit, 20060Sstevel@tonic-gate rctlval->zone_rctlval_action); 20070Sstevel@tonic-gate goto out; 20080Sstevel@tonic-gate } 20090Sstevel@tonic-gate if (!zonecfg_valid_rctl(name, rctlblk)) { 20100Sstevel@tonic-gate zerror(gettext("(priv=%s,limit=%s,action=%s) " 20110Sstevel@tonic-gate "is not a valid value for rctl '%s'"), 20120Sstevel@tonic-gate rctlval->zone_rctlval_priv, 20130Sstevel@tonic-gate rctlval->zone_rctlval_limit, 20140Sstevel@tonic-gate rctlval->zone_rctlval_action, 20150Sstevel@tonic-gate name); 20160Sstevel@tonic-gate goto out; 20170Sstevel@tonic-gate } 20180Sstevel@tonic-gate } 20190Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 20200Sstevel@tonic-gate } 20210Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 20220Sstevel@tonic-gate error = Z_OK; 20230Sstevel@tonic-gate out: 20240Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 20250Sstevel@tonic-gate (void) zonecfg_endrctlent(handle); 20260Sstevel@tonic-gate free(rctlblk); 20270Sstevel@tonic-gate return (error); 20280Sstevel@tonic-gate } 20290Sstevel@tonic-gate 20300Sstevel@tonic-gate static int 20310Sstevel@tonic-gate verify_pool(zone_dochandle_t handle) 20320Sstevel@tonic-gate { 20330Sstevel@tonic-gate char poolname[MAXPATHLEN]; 20340Sstevel@tonic-gate pool_conf_t *poolconf; 20350Sstevel@tonic-gate pool_t *pool; 20360Sstevel@tonic-gate int status; 20370Sstevel@tonic-gate int error; 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate /* 20400Sstevel@tonic-gate * This ends up being very similar to the check done in zoneadmd. 20410Sstevel@tonic-gate */ 20420Sstevel@tonic-gate error = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 20430Sstevel@tonic-gate if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) { 20440Sstevel@tonic-gate /* 20450Sstevel@tonic-gate * No pool specified. 20460Sstevel@tonic-gate */ 20470Sstevel@tonic-gate return (0); 20480Sstevel@tonic-gate } 20490Sstevel@tonic-gate if (error != Z_OK) { 20500Sstevel@tonic-gate zperror(gettext("Unable to retrieve pool name from " 20510Sstevel@tonic-gate "configuration"), B_TRUE); 20520Sstevel@tonic-gate return (error); 20530Sstevel@tonic-gate } 20540Sstevel@tonic-gate /* 20550Sstevel@tonic-gate * Don't do anything if pools aren't enabled. 20560Sstevel@tonic-gate */ 20570Sstevel@tonic-gate if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) { 20580Sstevel@tonic-gate zerror(gettext("WARNING: pools facility not active; " 20590Sstevel@tonic-gate "zone will not be bound to pool '%s'."), poolname); 20600Sstevel@tonic-gate return (Z_OK); 20610Sstevel@tonic-gate } 20620Sstevel@tonic-gate /* 20630Sstevel@tonic-gate * Try to provide a sane error message if the requested pool doesn't 20640Sstevel@tonic-gate * exist. It isn't clear that pools-related failures should 20650Sstevel@tonic-gate * necessarily translate to a failure to verify the zone configuration, 20660Sstevel@tonic-gate * hence they are not considered errors. 20670Sstevel@tonic-gate */ 20680Sstevel@tonic-gate if ((poolconf = pool_conf_alloc()) == NULL) { 20690Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_alloc failed; " 20700Sstevel@tonic-gate "using default pool")); 20710Sstevel@tonic-gate return (Z_OK); 20720Sstevel@tonic-gate } 20730Sstevel@tonic-gate if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) != 20740Sstevel@tonic-gate PO_SUCCESS) { 20750Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_open failed; " 20760Sstevel@tonic-gate "using default pool")); 20770Sstevel@tonic-gate pool_conf_free(poolconf); 20780Sstevel@tonic-gate return (Z_OK); 20790Sstevel@tonic-gate } 20800Sstevel@tonic-gate pool = pool_get_pool(poolconf, poolname); 20810Sstevel@tonic-gate (void) pool_conf_close(poolconf); 20820Sstevel@tonic-gate pool_conf_free(poolconf); 20830Sstevel@tonic-gate if (pool == NULL) { 20840Sstevel@tonic-gate zerror(gettext("WARNING: pool '%s' not found. " 20850Sstevel@tonic-gate "using default pool"), poolname); 20860Sstevel@tonic-gate } 20870Sstevel@tonic-gate 20880Sstevel@tonic-gate return (Z_OK); 20890Sstevel@tonic-gate } 20900Sstevel@tonic-gate 20910Sstevel@tonic-gate static int 2092823Sgjelinek verify_ipd(zone_dochandle_t handle) 2093823Sgjelinek { 2094823Sgjelinek int return_code = Z_OK; 2095823Sgjelinek struct zone_fstab fstab; 2096823Sgjelinek struct stat st; 2097823Sgjelinek char specdir[MAXPATHLEN]; 2098823Sgjelinek 2099823Sgjelinek if (zonecfg_setipdent(handle) != Z_OK) { 2100924Sgjelinek /* 2101924Sgjelinek * TRANSLATION_NOTE 2102924Sgjelinek * inherit-pkg-dirs is a literal that should not be translated. 2103924Sgjelinek */ 2104924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 2105823Sgjelinek "inherit-pkg-dirs: unable to enumerate mounts\n")); 2106823Sgjelinek return (Z_ERR); 2107823Sgjelinek } 2108823Sgjelinek while (zonecfg_getipdent(handle, &fstab) == Z_OK) { 2109823Sgjelinek /* 2110823Sgjelinek * Verify fs_dir exists. 2111823Sgjelinek */ 2112823Sgjelinek (void) snprintf(specdir, sizeof (specdir), "%s%s", 2113823Sgjelinek zonecfg_get_root(), fstab.zone_fs_dir); 2114823Sgjelinek if (stat(specdir, &st) != 0) { 2115924Sgjelinek /* 2116924Sgjelinek * TRANSLATION_NOTE 2117924Sgjelinek * inherit-pkg-dir is a literal that should not be 2118924Sgjelinek * translated. 2119924Sgjelinek */ 2120924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 2121823Sgjelinek "inherit-pkg-dir %s: %s\n"), 2122823Sgjelinek fstab.zone_fs_dir, strerror(errno)); 2123823Sgjelinek return_code = Z_ERR; 2124823Sgjelinek } 2125823Sgjelinek if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 2126924Sgjelinek /* 2127924Sgjelinek * TRANSLATION_NOTE 2128924Sgjelinek * inherit-pkg-dir and NFS are literals that should 2129924Sgjelinek * not be translated. 2130924Sgjelinek */ 2131823Sgjelinek (void) fprintf(stderr, gettext("cannot verify " 21321867Sgjelinek "inherit-pkg-dir %s: NFS mounted file system.\n" 21331867Sgjelinek "\tA local file system must be used.\n"), 2134823Sgjelinek fstab.zone_fs_dir); 2135823Sgjelinek return_code = Z_ERR; 2136823Sgjelinek } 2137823Sgjelinek } 2138823Sgjelinek (void) zonecfg_endipdent(handle); 2139823Sgjelinek 2140823Sgjelinek return (return_code); 2141823Sgjelinek } 2142823Sgjelinek 21431393Slling /* 21441867Sgjelinek * Verify that the special device/file system exists and is valid. 21451393Slling */ 21461393Slling static int 21471393Slling verify_fs_special(struct zone_fstab *fstab) 21481393Slling { 21496734Sjohnlev struct stat64 st; 21501393Slling 21512971Sgjelinek /* 21522971Sgjelinek * This validation is really intended for standard zone administration. 21532971Sgjelinek * If we are in a mini-root or some other upgrade situation where 21542971Sgjelinek * we are using the scratch zone, just by-pass this. 21552971Sgjelinek */ 21562971Sgjelinek if (zonecfg_in_alt_root()) 21572971Sgjelinek return (Z_OK); 21582971Sgjelinek 21591393Slling if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) 21601393Slling return (verify_fs_zfs(fstab)); 21611393Slling 21626734Sjohnlev if (stat64(fstab->zone_fs_special, &st) != 0) { 21631397Slling (void) fprintf(stderr, gettext("could not verify fs " 21641393Slling "%s: could not access %s: %s\n"), fstab->zone_fs_dir, 21651393Slling fstab->zone_fs_special, strerror(errno)); 21661393Slling return (Z_ERR); 21671393Slling } 21681393Slling 21691393Slling if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 21701393Slling /* 21711393Slling * TRANSLATION_NOTE 21721393Slling * fs and NFS are literals that should 21731393Slling * not be translated. 21741393Slling */ 21751393Slling (void) fprintf(stderr, gettext("cannot verify " 21761867Sgjelinek "fs %s: NFS mounted file system.\n" 21771867Sgjelinek "\tA local file system must be used.\n"), 21781393Slling fstab->zone_fs_special); 21791393Slling return (Z_ERR); 21801393Slling } 21811393Slling 21821393Slling return (Z_OK); 21831393Slling } 21841393Slling 2185823Sgjelinek static int 21866734Sjohnlev isregfile(const char *path) 21876734Sjohnlev { 21886734Sjohnlev struct stat64 st; 21896734Sjohnlev 21906734Sjohnlev if (stat64(path, &st) == -1) 21916734Sjohnlev return (-1); 21926734Sjohnlev 21936734Sjohnlev return (S_ISREG(st.st_mode)); 21946734Sjohnlev } 21956734Sjohnlev 21966734Sjohnlev static int 21970Sstevel@tonic-gate verify_filesystems(zone_dochandle_t handle) 21980Sstevel@tonic-gate { 21990Sstevel@tonic-gate int return_code = Z_OK; 22000Sstevel@tonic-gate struct zone_fstab fstab; 22010Sstevel@tonic-gate char cmdbuf[MAXPATHLEN]; 22020Sstevel@tonic-gate struct stat st; 22030Sstevel@tonic-gate 22040Sstevel@tonic-gate /* 22050Sstevel@tonic-gate * No need to verify inherit-pkg-dir fs types, as their type is 22060Sstevel@tonic-gate * implicitly lofs, which is known. Therefore, the types are only 22071867Sgjelinek * verified for regular file systems below. 22080Sstevel@tonic-gate * 22090Sstevel@tonic-gate * Since the actual mount point is not known until the dependent mounts 22100Sstevel@tonic-gate * are performed, we don't attempt any path validation here: that will 22110Sstevel@tonic-gate * happen later when zoneadmd actually does the mounts. 22120Sstevel@tonic-gate */ 22130Sstevel@tonic-gate if (zonecfg_setfsent(handle) != Z_OK) { 22141867Sgjelinek (void) fprintf(stderr, gettext("could not verify file systems: " 22150Sstevel@tonic-gate "unable to enumerate mounts\n")); 22160Sstevel@tonic-gate return (Z_ERR); 22170Sstevel@tonic-gate } 22180Sstevel@tonic-gate while (zonecfg_getfsent(handle, &fstab) == Z_OK) { 22190Sstevel@tonic-gate if (!zonecfg_valid_fs_type(fstab.zone_fs_type)) { 22200Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 22210Sstevel@tonic-gate "type %s is not allowed.\n"), fstab.zone_fs_dir, 22220Sstevel@tonic-gate fstab.zone_fs_type); 22230Sstevel@tonic-gate return_code = Z_ERR; 22240Sstevel@tonic-gate goto next_fs; 22250Sstevel@tonic-gate } 22260Sstevel@tonic-gate /* 22270Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/mount exists. 22280Sstevel@tonic-gate */ 22290Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/mount", 22300Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 22310Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 22320Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 22330Sstevel@tonic-gate fstab.zone_fs_type); 22340Sstevel@tonic-gate return_code = Z_ERR; 22350Sstevel@tonic-gate goto next_fs; 22360Sstevel@tonic-gate } 22370Sstevel@tonic-gate if (stat(cmdbuf, &st) != 0) { 2238924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2239924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 22400Sstevel@tonic-gate cmdbuf, strerror(errno)); 22410Sstevel@tonic-gate return_code = Z_ERR; 22420Sstevel@tonic-gate goto next_fs; 22430Sstevel@tonic-gate } 22440Sstevel@tonic-gate if (!S_ISREG(st.st_mode)) { 2245924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2246924Sgjelinek "%s: %s is not a regular file\n"), 2247924Sgjelinek fstab.zone_fs_dir, cmdbuf); 22480Sstevel@tonic-gate return_code = Z_ERR; 22490Sstevel@tonic-gate goto next_fs; 22500Sstevel@tonic-gate } 22510Sstevel@tonic-gate /* 22526734Sjohnlev * If zone_fs_raw is set, verify that there's an fsck 22536734Sjohnlev * binary for it. If zone_fs_raw is not set, and it's 22546734Sjohnlev * not a regular file (lofi mount), and there's an fsck 22556734Sjohnlev * binary for it, complain. 22560Sstevel@tonic-gate */ 22570Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/fsck", 22580Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 22590Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 22600Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 22610Sstevel@tonic-gate fstab.zone_fs_type); 22620Sstevel@tonic-gate return_code = Z_ERR; 22630Sstevel@tonic-gate goto next_fs; 22640Sstevel@tonic-gate } 22650Sstevel@tonic-gate if (fstab.zone_fs_raw[0] != '\0' && 22660Sstevel@tonic-gate (stat(cmdbuf, &st) != 0 || !S_ISREG(st.st_mode))) { 22670Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 22680Sstevel@tonic-gate "'raw' device specified but " 22690Sstevel@tonic-gate "no fsck executable exists for %s\n"), 22700Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 22710Sstevel@tonic-gate return_code = Z_ERR; 22720Sstevel@tonic-gate goto next_fs; 22736734Sjohnlev } else if (fstab.zone_fs_raw[0] == '\0' && 22746734Sjohnlev stat(cmdbuf, &st) == 0 && 22756734Sjohnlev isregfile(fstab.zone_fs_special) != 1) { 22766734Sjohnlev (void) fprintf(stderr, gettext("could not verify fs " 22776734Sjohnlev "%s: must specify 'raw' device for %s " 22786734Sjohnlev "file systems\n"), 22796734Sjohnlev fstab.zone_fs_dir, fstab.zone_fs_type); 22806734Sjohnlev return_code = Z_ERR; 22816734Sjohnlev goto next_fs; 22820Sstevel@tonic-gate } 22831393Slling 22841393Slling /* Verify fs_special. */ 22851393Slling if ((return_code = verify_fs_special(&fstab)) != Z_OK) 2286823Sgjelinek goto next_fs; 22871393Slling 22881393Slling /* Verify fs_raw. */ 2289823Sgjelinek if (fstab.zone_fs_raw[0] != '\0' && 2290823Sgjelinek stat(fstab.zone_fs_raw, &st) != 0) { 2291924Sgjelinek /* 2292924Sgjelinek * TRANSLATION_NOTE 2293924Sgjelinek * fs is a literal that should not be translated. 2294924Sgjelinek */ 2295924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2296924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 2297823Sgjelinek fstab.zone_fs_raw, strerror(errno)); 2298823Sgjelinek return_code = Z_ERR; 2299823Sgjelinek goto next_fs; 2300823Sgjelinek } 23010Sstevel@tonic-gate next_fs: 23020Sstevel@tonic-gate zonecfg_free_fs_option_list(fstab.zone_fs_options); 23030Sstevel@tonic-gate } 23040Sstevel@tonic-gate (void) zonecfg_endfsent(handle); 23050Sstevel@tonic-gate 23060Sstevel@tonic-gate return (return_code); 23070Sstevel@tonic-gate } 23080Sstevel@tonic-gate 23090Sstevel@tonic-gate static int 23101645Scomay verify_limitpriv(zone_dochandle_t handle) 23111645Scomay { 23121645Scomay char *privname = NULL; 23131645Scomay int err; 23141645Scomay priv_set_t *privs; 23151645Scomay 23161645Scomay if ((privs = priv_allocset()) == NULL) { 23171645Scomay zperror(gettext("failed to allocate privilege set"), B_FALSE); 23181645Scomay return (Z_NOMEM); 23191645Scomay } 23201645Scomay err = zonecfg_get_privset(handle, privs, &privname); 23211645Scomay switch (err) { 23221645Scomay case Z_OK: 23231645Scomay break; 23241645Scomay case Z_PRIV_PROHIBITED: 23251645Scomay (void) fprintf(stderr, gettext("privilege \"%s\" is not " 23261645Scomay "permitted within the zone's privilege set\n"), privname); 23271645Scomay break; 23281645Scomay case Z_PRIV_REQUIRED: 23291645Scomay (void) fprintf(stderr, gettext("required privilege \"%s\" is " 23301645Scomay "missing from the zone's privilege set\n"), privname); 23311645Scomay break; 23321645Scomay case Z_PRIV_UNKNOWN: 23331645Scomay (void) fprintf(stderr, gettext("unknown privilege \"%s\" " 23341645Scomay "specified in the zone's privilege set\n"), privname); 23351645Scomay break; 23361645Scomay default: 23371645Scomay zperror( 23381645Scomay gettext("failed to determine the zone's privilege set"), 23391645Scomay B_TRUE); 23401645Scomay break; 23411645Scomay } 23421645Scomay free(privname); 23431645Scomay priv_freeset(privs); 23441645Scomay return (err); 23451645Scomay } 23461645Scomay 23471915Sgjelinek static void 23481915Sgjelinek free_local_netifs(int if_cnt, struct net_if **if_list) 23491915Sgjelinek { 23501915Sgjelinek int i; 23511915Sgjelinek 23521915Sgjelinek for (i = 0; i < if_cnt; i++) { 23531915Sgjelinek free(if_list[i]->name); 23541915Sgjelinek free(if_list[i]); 23551915Sgjelinek } 23561915Sgjelinek free(if_list); 23571915Sgjelinek } 23581915Sgjelinek 23591915Sgjelinek /* 23601915Sgjelinek * Get a list of the network interfaces, along with their address families, 23611915Sgjelinek * that are plumbed in the global zone. See if_tcp(7p) for a description 23621915Sgjelinek * of the ioctls used here. 23631915Sgjelinek */ 23641915Sgjelinek static int 23651915Sgjelinek get_local_netifs(int *if_cnt, struct net_if ***if_list) 23661915Sgjelinek { 23671915Sgjelinek int s; 23681915Sgjelinek int i; 23691915Sgjelinek int res = Z_OK; 23701915Sgjelinek int space_needed; 23711915Sgjelinek int cnt = 0; 23721915Sgjelinek struct lifnum if_num; 23731915Sgjelinek struct lifconf if_conf; 23741915Sgjelinek struct lifreq *if_reqp; 23751915Sgjelinek char *if_buf; 23761915Sgjelinek struct net_if **local_ifs = NULL; 23771915Sgjelinek 23781915Sgjelinek *if_cnt = 0; 23791915Sgjelinek *if_list = NULL; 23801915Sgjelinek 23811915Sgjelinek if ((s = socket(SOCKET_AF(AF_INET), SOCK_DGRAM, 0)) < 0) 23821915Sgjelinek return (Z_ERR); 23831915Sgjelinek 23841915Sgjelinek /* 23851915Sgjelinek * Come back here in the unlikely event that the number of interfaces 23861915Sgjelinek * increases between the time we get the count and the time we do the 23871915Sgjelinek * SIOCGLIFCONF ioctl. 23881915Sgjelinek */ 23891915Sgjelinek retry: 23901915Sgjelinek /* Get the number of interfaces. */ 23911915Sgjelinek if_num.lifn_family = AF_UNSPEC; 23921915Sgjelinek if_num.lifn_flags = LIFC_NOXMIT; 23931915Sgjelinek if (ioctl(s, SIOCGLIFNUM, &if_num) < 0) { 23941915Sgjelinek (void) close(s); 23951915Sgjelinek return (Z_ERR); 23961915Sgjelinek } 23971915Sgjelinek 23981915Sgjelinek /* Get the interface configuration list. */ 23991915Sgjelinek space_needed = if_num.lifn_count * sizeof (struct lifreq); 24001915Sgjelinek if ((if_buf = malloc(space_needed)) == NULL) { 24011915Sgjelinek (void) close(s); 24021915Sgjelinek return (Z_ERR); 24031915Sgjelinek } 24041915Sgjelinek if_conf.lifc_family = AF_UNSPEC; 24051915Sgjelinek if_conf.lifc_flags = LIFC_NOXMIT; 24061915Sgjelinek if_conf.lifc_len = space_needed; 24071915Sgjelinek if_conf.lifc_buf = if_buf; 24081915Sgjelinek if (ioctl(s, SIOCGLIFCONF, &if_conf) < 0) { 24091915Sgjelinek free(if_buf); 24101915Sgjelinek /* 24111915Sgjelinek * SIOCGLIFCONF returns EINVAL if the buffer we passed in is 24121915Sgjelinek * too small. In this case go back and get the new if cnt. 24131915Sgjelinek */ 24141915Sgjelinek if (errno == EINVAL) 24151915Sgjelinek goto retry; 24161915Sgjelinek 24171915Sgjelinek (void) close(s); 24181915Sgjelinek return (Z_ERR); 24191915Sgjelinek } 24201915Sgjelinek (void) close(s); 24211915Sgjelinek 24221915Sgjelinek /* Get the name and address family for each interface. */ 24231915Sgjelinek if_reqp = if_conf.lifc_req; 24241915Sgjelinek for (i = 0; i < (if_conf.lifc_len / sizeof (struct lifreq)); i++) { 24251915Sgjelinek struct net_if **p; 24261915Sgjelinek struct lifreq req; 24271915Sgjelinek 24281915Sgjelinek if (strcmp(LOOPBACK_IF, if_reqp->lifr_name) == 0) { 24291915Sgjelinek if_reqp++; 24301915Sgjelinek continue; 24311915Sgjelinek } 24321915Sgjelinek 24331915Sgjelinek if ((s = socket(SOCKET_AF(if_reqp->lifr_addr.ss_family), 24341915Sgjelinek SOCK_DGRAM, 0)) == -1) { 24351915Sgjelinek res = Z_ERR; 24361915Sgjelinek break; 24371915Sgjelinek } 24381915Sgjelinek 24391915Sgjelinek (void) strncpy(req.lifr_name, if_reqp->lifr_name, 24401915Sgjelinek sizeof (req.lifr_name)); 24411915Sgjelinek if (ioctl(s, SIOCGLIFADDR, &req) < 0) { 24421915Sgjelinek (void) close(s); 24431915Sgjelinek if_reqp++; 24441915Sgjelinek continue; 24451915Sgjelinek } 24461915Sgjelinek 24471915Sgjelinek if ((p = (struct net_if **)realloc(local_ifs, 24481915Sgjelinek sizeof (struct net_if *) * (cnt + 1))) == NULL) { 24491915Sgjelinek res = Z_ERR; 24501915Sgjelinek break; 24511915Sgjelinek } 24521915Sgjelinek local_ifs = p; 24531915Sgjelinek 24541915Sgjelinek if ((local_ifs[cnt] = malloc(sizeof (struct net_if))) == NULL) { 24551915Sgjelinek res = Z_ERR; 24561915Sgjelinek break; 24571915Sgjelinek } 24581915Sgjelinek 24591915Sgjelinek if ((local_ifs[cnt]->name = strdup(if_reqp->lifr_name)) 24601915Sgjelinek == NULL) { 24611915Sgjelinek free(local_ifs[cnt]); 24621915Sgjelinek res = Z_ERR; 24631915Sgjelinek break; 24641915Sgjelinek } 24651915Sgjelinek local_ifs[cnt]->af = req.lifr_addr.ss_family; 24661915Sgjelinek cnt++; 24671915Sgjelinek 24681915Sgjelinek (void) close(s); 24691915Sgjelinek if_reqp++; 24701915Sgjelinek } 24711915Sgjelinek 24721915Sgjelinek free(if_buf); 24731915Sgjelinek 24741915Sgjelinek if (res != Z_OK) { 24751915Sgjelinek free_local_netifs(cnt, local_ifs); 24761915Sgjelinek } else { 24771915Sgjelinek *if_cnt = cnt; 24781915Sgjelinek *if_list = local_ifs; 24791915Sgjelinek } 24801915Sgjelinek 24811915Sgjelinek return (res); 24821915Sgjelinek } 24831915Sgjelinek 24841915Sgjelinek static char * 24851915Sgjelinek af2str(int af) 24861915Sgjelinek { 24871915Sgjelinek switch (af) { 24881915Sgjelinek case AF_INET: 24891915Sgjelinek return ("IPv4"); 24901915Sgjelinek case AF_INET6: 24911915Sgjelinek return ("IPv6"); 24921915Sgjelinek default: 24931915Sgjelinek return ("Unknown"); 24941915Sgjelinek } 24951915Sgjelinek } 24961915Sgjelinek 24971915Sgjelinek /* 24981915Sgjelinek * Cross check the network interface name and address family with the 24991915Sgjelinek * interfaces that are set up in the global zone so that we can print the 25001915Sgjelinek * appropriate error message. 25011915Sgjelinek */ 25021915Sgjelinek static void 25031915Sgjelinek print_net_err(char *phys, char *addr, int af, char *msg) 25041915Sgjelinek { 25051915Sgjelinek int i; 25061915Sgjelinek int local_if_cnt = 0; 25071915Sgjelinek struct net_if **local_ifs = NULL; 25081915Sgjelinek boolean_t found_if = B_FALSE; 25091915Sgjelinek boolean_t found_af = B_FALSE; 25101915Sgjelinek 25111915Sgjelinek if (get_local_netifs(&local_if_cnt, &local_ifs) != Z_OK) { 25121915Sgjelinek (void) fprintf(stderr, 25131915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 25141915Sgjelinek "net", "address", addr, "physical", phys, msg); 25151915Sgjelinek return; 25161915Sgjelinek } 25171915Sgjelinek 25181915Sgjelinek for (i = 0; i < local_if_cnt; i++) { 25191915Sgjelinek if (strcmp(phys, local_ifs[i]->name) == 0) { 25201915Sgjelinek found_if = B_TRUE; 25211915Sgjelinek if (af == local_ifs[i]->af) { 25221915Sgjelinek found_af = B_TRUE; 25231915Sgjelinek break; 25241915Sgjelinek } 25251915Sgjelinek } 25261915Sgjelinek } 25271915Sgjelinek 25281915Sgjelinek free_local_netifs(local_if_cnt, local_ifs); 25291915Sgjelinek 25301915Sgjelinek if (!found_if) { 25311915Sgjelinek (void) fprintf(stderr, 25321915Sgjelinek gettext("could not verify %s %s=%s\n\t" 25331915Sgjelinek "network interface %s is not plumbed in the global zone\n"), 25341915Sgjelinek "net", "physical", phys, phys); 25351915Sgjelinek return; 25361915Sgjelinek } 25371915Sgjelinek 25381915Sgjelinek /* 25391915Sgjelinek * Print this error if we were unable to find the address family 25401915Sgjelinek * for this interface. If the af variable is not initialized to 25411915Sgjelinek * to something meaningful by the caller (not AF_UNSPEC) then we 25421915Sgjelinek * also skip this message since it wouldn't be informative. 25431915Sgjelinek */ 25441915Sgjelinek if (!found_af && af != AF_UNSPEC) { 25451915Sgjelinek (void) fprintf(stderr, 25461915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\tthe %s address " 25473448Sdh155122 "family is not configured on this network interface in " 25483448Sdh155122 "the\n\tglobal zone\n"), 25491915Sgjelinek "net", "address", addr, "physical", phys, af2str(af)); 25501915Sgjelinek return; 25511915Sgjelinek } 25521915Sgjelinek 25531915Sgjelinek (void) fprintf(stderr, 25541915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 25551915Sgjelinek "net", "address", addr, "physical", phys, msg); 25561915Sgjelinek } 25571915Sgjelinek 25581645Scomay static int 25593339Szt129084 verify_handle(int cmd_num, zone_dochandle_t handle, char *argv[]) 25600Sstevel@tonic-gate { 25610Sstevel@tonic-gate struct zone_nwiftab nwiftab; 25620Sstevel@tonic-gate int return_code = Z_OK; 25630Sstevel@tonic-gate int err; 2564766Scarlsonj boolean_t in_alt_root; 25653448Sdh155122 zone_iptype_t iptype; 25664456Sss150715 dlpi_handle_t dh; 25670Sstevel@tonic-gate 2568766Scarlsonj in_alt_root = zonecfg_in_alt_root(); 2569766Scarlsonj if (in_alt_root) 2570766Scarlsonj goto no_net; 2571766Scarlsonj 25723448Sdh155122 if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK) { 25733448Sdh155122 errno = err; 25743448Sdh155122 zperror(cmd_to_str(cmd_num), B_TRUE); 25753448Sdh155122 zonecfg_fini_handle(handle); 25763448Sdh155122 return (Z_ERR); 25773448Sdh155122 } 25780Sstevel@tonic-gate if ((err = zonecfg_setnwifent(handle)) != Z_OK) { 25790Sstevel@tonic-gate errno = err; 25800Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 25810Sstevel@tonic-gate zonecfg_fini_handle(handle); 25820Sstevel@tonic-gate return (Z_ERR); 25830Sstevel@tonic-gate } 25840Sstevel@tonic-gate while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { 25850Sstevel@tonic-gate struct lifreq lifr; 25861915Sgjelinek sa_family_t af = AF_UNSPEC; 25873448Sdh155122 char dl_owner_zname[ZONENAME_MAX]; 25883448Sdh155122 zoneid_t dl_owner_zid; 25893448Sdh155122 zoneid_t target_zid; 25903448Sdh155122 int res; 25910Sstevel@tonic-gate 25920Sstevel@tonic-gate /* skip any loopback interfaces */ 25930Sstevel@tonic-gate if (strcmp(nwiftab.zone_nwif_physical, "lo0") == 0) 25940Sstevel@tonic-gate continue; 25953448Sdh155122 switch (iptype) { 25963448Sdh155122 case ZS_SHARED: 25973448Sdh155122 if ((res = zonecfg_valid_net_address( 25983448Sdh155122 nwiftab.zone_nwif_address, &lifr)) != Z_OK) { 25993448Sdh155122 print_net_err(nwiftab.zone_nwif_physical, 26003448Sdh155122 nwiftab.zone_nwif_address, af, 26013448Sdh155122 zonecfg_strerror(res)); 26024350Std153743 return_code = Z_ERR; 26034350Std153743 continue; 26043448Sdh155122 } 26053448Sdh155122 af = lifr.lifr_addr.ss_family; 26063448Sdh155122 if (!zonecfg_ifname_exists(af, 26073448Sdh155122 nwiftab.zone_nwif_physical)) { 26083448Sdh155122 /* 26093448Sdh155122 * The interface failed to come up. We continue 26103448Sdh155122 * on anyway for the sake of consistency: a 26113448Sdh155122 * zone is not shut down if the interface fails 26123448Sdh155122 * any time after boot, nor does the global zone 26133448Sdh155122 * fail to boot if an interface fails. 26143448Sdh155122 */ 26153448Sdh155122 (void) fprintf(stderr, 26163448Sdh155122 gettext("WARNING: skipping network " 26174350Std153743 "interface '%s' which may not be " 26184350Std153743 "present/plumbed in the global " 26194350Std153743 "zone.\n"), 26203448Sdh155122 nwiftab.zone_nwif_physical); 26213448Sdh155122 } 26223448Sdh155122 break; 26233448Sdh155122 case ZS_EXCLUSIVE: 26243448Sdh155122 /* Warning if it exists for either IPv4 or IPv6 */ 26253448Sdh155122 26263448Sdh155122 if (zonecfg_ifname_exists(AF_INET, 26273448Sdh155122 nwiftab.zone_nwif_physical) || 26283448Sdh155122 zonecfg_ifname_exists(AF_INET6, 26293448Sdh155122 nwiftab.zone_nwif_physical)) { 26303448Sdh155122 (void) fprintf(stderr, 26313448Sdh155122 gettext("WARNING: skipping network " 26323448Sdh155122 "interface '%s' which is used in the " 26333448Sdh155122 "global zone.\n"), 26343448Sdh155122 nwiftab.zone_nwif_physical); 26353448Sdh155122 break; 26363448Sdh155122 } 26374456Sss150715 26382611Svp157776 /* 26394456Sss150715 * Verify that the physical interface can be opened. 26403448Sdh155122 */ 26414456Sss150715 err = dlpi_open(nwiftab.zone_nwif_physical, &dh, 0); 26424456Sss150715 if (err != DLPI_SUCCESS) { 26433448Sdh155122 (void) fprintf(stderr, 26443448Sdh155122 gettext("WARNING: skipping network " 26454456Sss150715 "interface '%s' which cannot be opened: " 26464456Sss150715 "dlpi error (%s).\n"), 26474456Sss150715 nwiftab.zone_nwif_physical, 26484456Sss150715 dlpi_strerror(err)); 26493448Sdh155122 break; 26503448Sdh155122 } else { 26514456Sss150715 dlpi_close(dh); 26523448Sdh155122 } 26533448Sdh155122 /* 26543448Sdh155122 * Verify whether the physical interface is already 26553448Sdh155122 * used by a zone. 26563448Sdh155122 */ 26573448Sdh155122 dl_owner_zid = ALL_ZONES; 26583448Sdh155122 if (zone_check_datalink(&dl_owner_zid, 26593448Sdh155122 nwiftab.zone_nwif_physical) != 0) 26603448Sdh155122 break; 26613448Sdh155122 26623448Sdh155122 /* 26633448Sdh155122 * If the zone being verified is 26643448Sdh155122 * running and owns the interface 26653448Sdh155122 */ 26663448Sdh155122 target_zid = getzoneidbyname(target_zone); 26673448Sdh155122 if (target_zid == dl_owner_zid) 26683448Sdh155122 break; 26693448Sdh155122 26703448Sdh155122 /* Zone id match failed, use name to check */ 26713448Sdh155122 if (getzonenamebyid(dl_owner_zid, dl_owner_zname, 26723448Sdh155122 ZONENAME_MAX) < 0) { 26733448Sdh155122 /* No name, show ID instead */ 26743448Sdh155122 (void) snprintf(dl_owner_zname, ZONENAME_MAX, 26753448Sdh155122 "<%d>", dl_owner_zid); 26763448Sdh155122 } else if (strcmp(dl_owner_zname, target_zone) == 0) 26773448Sdh155122 break; 26783448Sdh155122 26793448Sdh155122 /* 26803448Sdh155122 * Note here we only report a warning that 26813448Sdh155122 * the interface is already in use by another 26823448Sdh155122 * running zone, and the verify process just 26833448Sdh155122 * goes on, if the interface is still in use 26843448Sdh155122 * when this zone really boots up, zoneadmd 26853448Sdh155122 * will find it. If the name of the zone which 26863448Sdh155122 * owns this interface cannot be determined, 26873448Sdh155122 * then it is not possible to determine if there 26883448Sdh155122 * is a conflict so just report it as a warning. 26892611Svp157776 */ 26902611Svp157776 (void) fprintf(stderr, 26913448Sdh155122 gettext("WARNING: skipping network interface " 26923448Sdh155122 "'%s' which is used by the non-global zone " 26933448Sdh155122 "'%s'.\n"), nwiftab.zone_nwif_physical, 26943448Sdh155122 dl_owner_zname); 26953448Sdh155122 break; 26960Sstevel@tonic-gate } 26970Sstevel@tonic-gate } 26980Sstevel@tonic-gate (void) zonecfg_endnwifent(handle); 2699766Scarlsonj no_net: 27000Sstevel@tonic-gate 27011931Sgjelinek /* verify that lofs has not been excluded from the kernel */ 27022078Sgjelinek if (!(cmd_num == CMD_DETACH || cmd_num == CMD_ATTACH || 27032078Sgjelinek cmd_num == CMD_MOVE || cmd_num == CMD_CLONE) && 27042078Sgjelinek modctl(MODLOAD, 1, "fs/lofs", NULL) != 0) { 27051931Sgjelinek if (errno == ENXIO) 27061931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 27071931Sgjelinek "lofs(7FS): possibly excluded in /etc/system\n")); 27081931Sgjelinek else 27091931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 27101931Sgjelinek "lofs(7FS): %s\n"), strerror(errno)); 27111931Sgjelinek return_code = Z_ERR; 27121931Sgjelinek } 27131931Sgjelinek 27140Sstevel@tonic-gate if (verify_filesystems(handle) != Z_OK) 27150Sstevel@tonic-gate return_code = Z_ERR; 2716823Sgjelinek if (verify_ipd(handle) != Z_OK) 2717823Sgjelinek return_code = Z_ERR; 2718766Scarlsonj if (!in_alt_root && verify_rctls(handle) != Z_OK) 27190Sstevel@tonic-gate return_code = Z_ERR; 2720766Scarlsonj if (!in_alt_root && verify_pool(handle) != Z_OK) 27210Sstevel@tonic-gate return_code = Z_ERR; 27223339Szt129084 if (!in_alt_root && verify_brand(handle, cmd_num, argv) != Z_OK) 27232712Snn35248 return_code = Z_ERR; 2724789Sahrens if (!in_alt_root && verify_datasets(handle) != Z_OK) 2725789Sahrens return_code = Z_ERR; 27261645Scomay 27271645Scomay /* 27281645Scomay * As the "mount" command is used for patching/upgrading of zones 27291645Scomay * or other maintenance processes, the zone's privilege set is not 27301645Scomay * checked in this case. Instead, the default, safe set of 27311645Scomay * privileges will be used when this zone is created in the 27321645Scomay * kernel. 27331645Scomay */ 27341645Scomay if (!in_alt_root && cmd_num != CMD_MOUNT && 27351645Scomay verify_limitpriv(handle) != Z_OK) 27361645Scomay return_code = Z_ERR; 27372078Sgjelinek 27382078Sgjelinek return (return_code); 27392078Sgjelinek } 27402078Sgjelinek 27412078Sgjelinek static int 27423339Szt129084 verify_details(int cmd_num, char *argv[]) 27432078Sgjelinek { 27442078Sgjelinek zone_dochandle_t handle; 27452078Sgjelinek char zonepath[MAXPATHLEN], checkpath[MAXPATHLEN]; 27462078Sgjelinek int return_code = Z_OK; 27472078Sgjelinek int err; 27482078Sgjelinek 27492078Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 27502078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 27512078Sgjelinek return (Z_ERR); 27522078Sgjelinek } 27532078Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 27542078Sgjelinek errno = err; 27552078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 27562078Sgjelinek zonecfg_fini_handle(handle); 27572078Sgjelinek return (Z_ERR); 27582078Sgjelinek } 27592078Sgjelinek if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) != 27602078Sgjelinek Z_OK) { 27612078Sgjelinek errno = err; 27622078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 27632078Sgjelinek zonecfg_fini_handle(handle); 27642078Sgjelinek return (Z_ERR); 27652078Sgjelinek } 27662078Sgjelinek /* 27672078Sgjelinek * zonecfg_get_zonepath() gets its data from the XML repository. 27682078Sgjelinek * Verify this against the index file, which is checked first by 27692078Sgjelinek * zone_get_zonepath(). If they don't match, bail out. 27702078Sgjelinek */ 27712078Sgjelinek if ((err = zone_get_zonepath(target_zone, checkpath, 27722078Sgjelinek sizeof (checkpath))) != Z_OK) { 27732078Sgjelinek errno = err; 27742078Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 27753716Sgjelinek zonecfg_fini_handle(handle); 27762078Sgjelinek return (Z_ERR); 27772078Sgjelinek } 27782078Sgjelinek if (strcmp(zonepath, checkpath) != 0) { 27792078Sgjelinek /* 27802078Sgjelinek * TRANSLATION_NOTE 27812078Sgjelinek * XML and zonepath are literals that should not be translated. 27822078Sgjelinek */ 27832078Sgjelinek (void) fprintf(stderr, gettext("The XML repository has " 27842078Sgjelinek "zonepath '%s',\nbut the index file has zonepath '%s'.\n" 27852078Sgjelinek "These must match, so fix the incorrect entry.\n"), 27862078Sgjelinek zonepath, checkpath); 27873716Sgjelinek zonecfg_fini_handle(handle); 27882078Sgjelinek return (Z_ERR); 27892078Sgjelinek } 27902078Sgjelinek if (validate_zonepath(zonepath, cmd_num) != Z_OK) { 27912078Sgjelinek (void) fprintf(stderr, gettext("could not verify zonepath %s " 27922078Sgjelinek "because of the above errors.\n"), zonepath); 27932078Sgjelinek return_code = Z_ERR; 27942078Sgjelinek } 27952078Sgjelinek 27963339Szt129084 if (verify_handle(cmd_num, handle, argv) != Z_OK) 27972078Sgjelinek return_code = Z_ERR; 27982078Sgjelinek 27990Sstevel@tonic-gate zonecfg_fini_handle(handle); 28000Sstevel@tonic-gate if (return_code == Z_ERR) 28010Sstevel@tonic-gate (void) fprintf(stderr, 28020Sstevel@tonic-gate gettext("%s: zone %s failed to verify\n"), 28030Sstevel@tonic-gate execname, target_zone); 28040Sstevel@tonic-gate return (return_code); 28050Sstevel@tonic-gate } 28060Sstevel@tonic-gate 28070Sstevel@tonic-gate static int 28080Sstevel@tonic-gate verify_func(int argc, char *argv[]) 28090Sstevel@tonic-gate { 28100Sstevel@tonic-gate int arg; 28110Sstevel@tonic-gate 28120Sstevel@tonic-gate optind = 0; 28130Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 28140Sstevel@tonic-gate switch (arg) { 28150Sstevel@tonic-gate case '?': 28160Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 28170Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 28180Sstevel@tonic-gate default: 28190Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 28200Sstevel@tonic-gate return (Z_USAGE); 28210Sstevel@tonic-gate } 28220Sstevel@tonic-gate } 28230Sstevel@tonic-gate if (argc > optind) { 28240Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 28250Sstevel@tonic-gate return (Z_USAGE); 28260Sstevel@tonic-gate } 28272712Snn35248 if (sanity_check(target_zone, CMD_VERIFY, B_FALSE, B_FALSE, B_FALSE) 28282712Snn35248 != Z_OK) 28290Sstevel@tonic-gate return (Z_ERR); 28303339Szt129084 return (verify_details(CMD_VERIFY, argv)); 28310Sstevel@tonic-gate } 28320Sstevel@tonic-gate 28332712Snn35248 static int 28347089Sgjelinek addoptions(char *buf, char *argv[], size_t len) 28357089Sgjelinek { 28367089Sgjelinek int i = 0; 28377089Sgjelinek 28387089Sgjelinek if (buf[0] == '\0') 28397089Sgjelinek return (Z_OK); 28407089Sgjelinek 28417089Sgjelinek while (argv[i] != NULL) { 28427089Sgjelinek if (strlcat(buf, " ", len) >= len || 28437089Sgjelinek strlcat(buf, argv[i++], len) >= len) { 28447089Sgjelinek zerror("Command line too long"); 28457089Sgjelinek return (Z_ERR); 28467089Sgjelinek } 28477089Sgjelinek } 28487089Sgjelinek 28497089Sgjelinek return (Z_OK); 28507089Sgjelinek } 28517089Sgjelinek 28527089Sgjelinek static int 28532712Snn35248 addopt(char *buf, int opt, char *optarg, size_t bufsize) 28542712Snn35248 { 28552712Snn35248 char optstring[4]; 28562712Snn35248 28572712Snn35248 if (opt > 0) 28582712Snn35248 (void) sprintf(optstring, " -%c", opt); 28592712Snn35248 else 28602712Snn35248 (void) strcpy(optstring, " "); 28612712Snn35248 28622712Snn35248 if ((strlcat(buf, optstring, bufsize) > bufsize)) 28632712Snn35248 return (Z_ERR); 28647089Sgjelinek 28652712Snn35248 if ((optarg != NULL) && (strlcat(buf, optarg, bufsize) > bufsize)) 28662712Snn35248 return (Z_ERR); 28677089Sgjelinek 28682712Snn35248 return (Z_OK); 28692712Snn35248 } 28700Sstevel@tonic-gate 28717089Sgjelinek /* ARGSUSED */ 28720Sstevel@tonic-gate static int 28730Sstevel@tonic-gate install_func(int argc, char *argv[]) 28740Sstevel@tonic-gate { 28752712Snn35248 char cmdbuf[MAXPATHLEN]; 28764586Sgjelinek char postcmdbuf[MAXPATHLEN]; 28770Sstevel@tonic-gate int lockfd; 28782712Snn35248 int arg, err, subproc_err; 28790Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 28802727Sedp brand_handle_t bh = NULL; 28810Sstevel@tonic-gate int status; 28821867Sgjelinek boolean_t nodataset = B_FALSE; 28834586Sgjelinek boolean_t do_postinstall = B_FALSE; 28847089Sgjelinek boolean_t brand_help = B_FALSE; 28852712Snn35248 char opts[128]; 28862712Snn35248 28872712Snn35248 if (target_zone == NULL) { 28882712Snn35248 sub_usage(SHELP_INSTALL, CMD_INSTALL); 28892712Snn35248 return (Z_USAGE); 28902712Snn35248 } 28910Sstevel@tonic-gate 2892766Scarlsonj if (zonecfg_in_alt_root()) { 2893766Scarlsonj zerror(gettext("cannot install zone in alternate root")); 2894766Scarlsonj return (Z_ERR); 2895766Scarlsonj } 2896766Scarlsonj 28972712Snn35248 if ((err = zone_get_zonepath(target_zone, zonepath, 28982712Snn35248 sizeof (zonepath))) != Z_OK) { 28992712Snn35248 errno = err; 29002712Snn35248 zperror2(target_zone, gettext("could not get zone path")); 29012712Snn35248 return (Z_ERR); 29022712Snn35248 } 29032712Snn35248 29042712Snn35248 /* Fetch the install command from the brand configuration. */ 29052727Sedp if ((bh = brand_open(target_brand)) == NULL) { 29062712Snn35248 zerror(gettext("missing or invalid brand")); 29072712Snn35248 return (Z_ERR); 29082712Snn35248 } 29092712Snn35248 29107089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_install, 29117089Sgjelinek target_zone, zonepath) != Z_OK) { 29122712Snn35248 zerror("invalid brand configuration: missing install resource"); 29132727Sedp brand_close(bh); 29142712Snn35248 return (Z_ERR); 29152712Snn35248 } 29162712Snn35248 29177089Sgjelinek if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postinstall, 29187089Sgjelinek target_zone, zonepath) != Z_OK) { 29194586Sgjelinek zerror("invalid brand configuration: missing postinstall " 29204586Sgjelinek "resource"); 29214586Sgjelinek brand_close(bh); 29224586Sgjelinek return (Z_ERR); 29237089Sgjelinek } 29247089Sgjelinek 29257089Sgjelinek if (postcmdbuf[0] != '\0') 29264586Sgjelinek do_postinstall = B_TRUE; 29274586Sgjelinek 29282712Snn35248 (void) strcpy(opts, "?x:"); 29297089Sgjelinek /* 29307089Sgjelinek * Fetch the list of recognized command-line options from 29317089Sgjelinek * the brand configuration file. 29327089Sgjelinek */ 29337089Sgjelinek if (brand_get_installopts(bh, opts + strlen(opts), 29347089Sgjelinek sizeof (opts) - strlen(opts)) != 0) { 29357089Sgjelinek zerror("invalid brand configuration: missing " 29367089Sgjelinek "install options resource"); 29377089Sgjelinek brand_close(bh); 29387089Sgjelinek return (Z_ERR); 29397089Sgjelinek } 29407089Sgjelinek 29412727Sedp brand_close(bh); 29422712Snn35248 29437089Sgjelinek if (cmdbuf[0] == '\0') { 29447089Sgjelinek zerror("Missing brand install command"); 29457089Sgjelinek return (Z_ERR); 29467089Sgjelinek } 29477089Sgjelinek 29487089Sgjelinek /* Check the argv string for args we handle internally */ 29490Sstevel@tonic-gate optind = 0; 29507089Sgjelinek opterr = 0; 29512712Snn35248 while ((arg = getopt(argc, argv, opts)) != EOF) { 29520Sstevel@tonic-gate switch (arg) { 29530Sstevel@tonic-gate case '?': 29547089Sgjelinek if (optopt == '?') { 29551867Sgjelinek sub_usage(SHELP_INSTALL, CMD_INSTALL); 29567089Sgjelinek brand_help = B_TRUE; 29572712Snn35248 } 29587089Sgjelinek /* Ignore unknown options - may be brand specific. */ 29597089Sgjelinek break; 29607089Sgjelinek case 'x': 29617089Sgjelinek /* Handle this option internally, don't pass to brand */ 29627089Sgjelinek if (strcmp(optarg, "nodataset") == 0) { 29637089Sgjelinek /* Handle this option internally */ 29647089Sgjelinek nodataset = B_TRUE; 29652712Snn35248 } 29667089Sgjelinek continue; 29677089Sgjelinek default: 29687089Sgjelinek /* Ignore unknown options - may be brand specific. */ 29692712Snn35248 break; 29700Sstevel@tonic-gate } 29717089Sgjelinek 29727089Sgjelinek /* 29737089Sgjelinek * Append the option to the command line passed to the 29747089Sgjelinek * brand-specific install and postinstall routines. 29757089Sgjelinek */ 29767089Sgjelinek if (addopt(cmdbuf, optopt, optarg, sizeof (cmdbuf)) != Z_OK) { 29777089Sgjelinek zerror("Install command line too long"); 29787089Sgjelinek return (Z_ERR); 29797089Sgjelinek } 29807089Sgjelinek if (addopt(postcmdbuf, optopt, optarg, sizeof (postcmdbuf)) 29817089Sgjelinek != Z_OK) { 29827089Sgjelinek zerror("Post-Install command line too long"); 29837089Sgjelinek return (Z_ERR); 29847089Sgjelinek } 29857089Sgjelinek } 29867089Sgjelinek 29877089Sgjelinek for (; optind < argc; optind++) { 29887089Sgjelinek if (addopt(cmdbuf, 0, argv[optind], sizeof (cmdbuf)) != Z_OK) { 29897089Sgjelinek zerror("Install command line too long"); 29907089Sgjelinek return (Z_ERR); 29917089Sgjelinek } 29927089Sgjelinek 29937089Sgjelinek if (addopt(postcmdbuf, 0, argv[optind], sizeof (postcmdbuf)) 29947089Sgjelinek != Z_OK) { 29957089Sgjelinek zerror("Post-Install command line too long"); 29967089Sgjelinek return (Z_ERR); 29972712Snn35248 } 29982712Snn35248 } 29992712Snn35248 30007089Sgjelinek if (!brand_help) { 30017089Sgjelinek if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE, 30027089Sgjelinek B_FALSE) != Z_OK) 30037089Sgjelinek return (Z_ERR); 30047089Sgjelinek if (verify_details(CMD_INSTALL, argv) != Z_OK) 30057089Sgjelinek return (Z_ERR); 30067089Sgjelinek 30077089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 30087089Sgjelinek zerror(gettext("another %s may have an operation in " 30097089Sgjelinek "progress."), "zoneadm"); 30107089Sgjelinek return (Z_ERR); 30117089Sgjelinek } 30127089Sgjelinek err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 30137089Sgjelinek if (err != Z_OK) { 30147089Sgjelinek errno = err; 30157089Sgjelinek zperror2(target_zone, gettext("could not set state")); 30167089Sgjelinek goto done; 30177089Sgjelinek } 30187089Sgjelinek 30197089Sgjelinek if (!nodataset) 30207089Sgjelinek create_zfs_zonepath(zonepath); 30217089Sgjelinek } 30227089Sgjelinek 30237089Sgjelinek status = do_subproc_interactive(cmdbuf); 30242712Snn35248 if ((subproc_err = 30252712Snn35248 subproc_status(gettext("brand-specific installation"), status, 30262712Snn35248 B_FALSE)) != ZONE_SUBPROC_OK) { 30277089Sgjelinek if (subproc_err == ZONE_SUBPROC_USAGE && !brand_help) { 30287089Sgjelinek sub_usage(SHELP_INSTALL, CMD_INSTALL); 30297089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 30307089Sgjelinek return (Z_ERR); 30317089Sgjelinek } 30322712Snn35248 err = Z_ERR; 30330Sstevel@tonic-gate goto done; 30342712Snn35248 } 30350Sstevel@tonic-gate 30367089Sgjelinek if (brand_help) 30377089Sgjelinek return (Z_OK); 30387089Sgjelinek 30390Sstevel@tonic-gate if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 30400Sstevel@tonic-gate errno = err; 30410Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 30420Sstevel@tonic-gate goto done; 30430Sstevel@tonic-gate } 30440Sstevel@tonic-gate 30454586Sgjelinek if (do_postinstall) { 30464586Sgjelinek status = do_subproc(postcmdbuf); 30474586Sgjelinek 30484586Sgjelinek if ((subproc_err = 30494586Sgjelinek subproc_status(gettext("brand-specific post-install"), 30504586Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 30514586Sgjelinek err = Z_ERR; 30524586Sgjelinek (void) zone_set_state(target_zone, 30534586Sgjelinek ZONE_STATE_INCOMPLETE); 30544586Sgjelinek } 30554586Sgjelinek } 30564586Sgjelinek 30570Sstevel@tonic-gate done: 30582712Snn35248 /* 30597089Sgjelinek * If the install script exited with ZONE_SUBPROC_NOTCOMPLETE, try to 30607089Sgjelinek * clean up the zone and leave the zone in the CONFIGURED state so that 30617089Sgjelinek * another install can be attempted without requiring an uninstall 30627089Sgjelinek * first. 30632712Snn35248 */ 30647089Sgjelinek if (subproc_err == ZONE_SUBPROC_NOTCOMPLETE) { 30652712Snn35248 if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) { 30662712Snn35248 errno = err; 30672712Snn35248 zperror2(target_zone, 30682712Snn35248 gettext("cleaning up zonepath failed")); 30692712Snn35248 } else if ((err = zone_set_state(target_zone, 30702712Snn35248 ZONE_STATE_CONFIGURED)) != Z_OK) { 30712712Snn35248 errno = err; 30722712Snn35248 zperror2(target_zone, gettext("could not set state")); 30732712Snn35248 } 30742712Snn35248 } 30752712Snn35248 30767089Sgjelinek if (!brand_help) 30777089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 30780Sstevel@tonic-gate return ((err == Z_OK) ? Z_OK : Z_ERR); 30790Sstevel@tonic-gate } 30800Sstevel@tonic-gate 30810Sstevel@tonic-gate /* 30821300Sgjelinek * Check that the inherited pkg dirs are the same for the clone and its source. 30831300Sgjelinek * The easiest way to do that is check that the list of ipds is the same 30841300Sgjelinek * by matching each one against the other. This algorithm should be fine since 30851300Sgjelinek * the list of ipds should not be that long. 30861300Sgjelinek */ 30871300Sgjelinek static int 30881300Sgjelinek valid_ipd_clone(zone_dochandle_t s_handle, char *source_zone, 30891300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 30901300Sgjelinek { 30911300Sgjelinek int err; 30921300Sgjelinek int res = Z_OK; 30931300Sgjelinek int s_cnt = 0; 30941300Sgjelinek int t_cnt = 0; 30951300Sgjelinek struct zone_fstab s_fstab; 30961300Sgjelinek struct zone_fstab t_fstab; 30971300Sgjelinek 30981300Sgjelinek /* 30991300Sgjelinek * First check the source of the clone against the target. 31001300Sgjelinek */ 31011300Sgjelinek if ((err = zonecfg_setipdent(s_handle)) != Z_OK) { 31021300Sgjelinek errno = err; 31031300Sgjelinek zperror2(source_zone, gettext("could not enumerate " 31041300Sgjelinek "inherit-pkg-dirs")); 31051300Sgjelinek return (Z_ERR); 31061300Sgjelinek } 31071300Sgjelinek 31081300Sgjelinek while (zonecfg_getipdent(s_handle, &s_fstab) == Z_OK) { 31091300Sgjelinek boolean_t match = B_FALSE; 31101300Sgjelinek 31111300Sgjelinek s_cnt++; 31121300Sgjelinek 31131300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 31141300Sgjelinek errno = err; 31151300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 31161300Sgjelinek "inherit-pkg-dirs")); 31171300Sgjelinek (void) zonecfg_endipdent(s_handle); 31181300Sgjelinek return (Z_ERR); 31191300Sgjelinek } 31201300Sgjelinek 31211300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) { 31221300Sgjelinek if (strcmp(s_fstab.zone_fs_dir, t_fstab.zone_fs_dir) 31231300Sgjelinek == 0) { 31241300Sgjelinek match = B_TRUE; 31251300Sgjelinek break; 31261300Sgjelinek } 31271300Sgjelinek } 31281300Sgjelinek (void) zonecfg_endipdent(t_handle); 31291300Sgjelinek 31301300Sgjelinek if (!match) { 31311300Sgjelinek (void) fprintf(stderr, gettext("inherit-pkg-dir " 31321300Sgjelinek "'%s' is not configured in zone %s.\n"), 31331300Sgjelinek s_fstab.zone_fs_dir, target_zone); 31341300Sgjelinek res = Z_ERR; 31351300Sgjelinek } 31361300Sgjelinek } 31371300Sgjelinek 31381300Sgjelinek (void) zonecfg_endipdent(s_handle); 31391300Sgjelinek 31401300Sgjelinek /* skip the next check if we already have errors */ 31411300Sgjelinek if (res == Z_ERR) 31421300Sgjelinek return (res); 31431300Sgjelinek 31441300Sgjelinek /* 31451300Sgjelinek * Now check the number of ipds in the target so we can verify 31461300Sgjelinek * that the source is not a subset of the target. 31471300Sgjelinek */ 31481300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 31491300Sgjelinek errno = err; 31501300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 31511300Sgjelinek "inherit-pkg-dirs")); 31521300Sgjelinek return (Z_ERR); 31531300Sgjelinek } 31541300Sgjelinek 31551300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) 31561300Sgjelinek t_cnt++; 31571300Sgjelinek 31581300Sgjelinek (void) zonecfg_endipdent(t_handle); 31591300Sgjelinek 31601300Sgjelinek if (t_cnt != s_cnt) { 31611300Sgjelinek (void) fprintf(stderr, gettext("Zone %s is configured " 31621300Sgjelinek "with inherit-pkg-dirs that are not configured in zone " 31631300Sgjelinek "%s.\n"), target_zone, source_zone); 31641300Sgjelinek res = Z_ERR; 31651300Sgjelinek } 31661300Sgjelinek 31671300Sgjelinek return (res); 31681300Sgjelinek } 31691300Sgjelinek 31701300Sgjelinek static void 31711300Sgjelinek warn_dev_match(zone_dochandle_t s_handle, char *source_zone, 31721300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 31731300Sgjelinek { 31741300Sgjelinek int err; 31751300Sgjelinek struct zone_devtab s_devtab; 31761300Sgjelinek struct zone_devtab t_devtab; 31771300Sgjelinek 31781300Sgjelinek if ((err = zonecfg_setdevent(t_handle)) != Z_OK) { 31791300Sgjelinek errno = err; 31801300Sgjelinek zperror2(target_zone, gettext("could not enumerate devices")); 31811300Sgjelinek return; 31821300Sgjelinek } 31831300Sgjelinek 31841300Sgjelinek while (zonecfg_getdevent(t_handle, &t_devtab) == Z_OK) { 31851300Sgjelinek if ((err = zonecfg_setdevent(s_handle)) != Z_OK) { 31861300Sgjelinek errno = err; 31871300Sgjelinek zperror2(source_zone, 31881300Sgjelinek gettext("could not enumerate devices")); 31891300Sgjelinek (void) zonecfg_enddevent(t_handle); 31901300Sgjelinek return; 31911300Sgjelinek } 31921300Sgjelinek 31931300Sgjelinek while (zonecfg_getdevent(s_handle, &s_devtab) == Z_OK) { 31941300Sgjelinek /* 31951300Sgjelinek * Use fnmatch to catch the case where wildcards 31961300Sgjelinek * were used in one zone and the other has an 31971300Sgjelinek * explicit entry (e.g. /dev/dsk/c0t0d0s6 vs. 31981300Sgjelinek * /dev/\*dsk/c0t0d0s6). 31991300Sgjelinek */ 32001300Sgjelinek if (fnmatch(t_devtab.zone_dev_match, 32011300Sgjelinek s_devtab.zone_dev_match, FNM_PATHNAME) == 0 || 32021300Sgjelinek fnmatch(s_devtab.zone_dev_match, 32031300Sgjelinek t_devtab.zone_dev_match, FNM_PATHNAME) == 0) { 32041300Sgjelinek (void) fprintf(stderr, 32051300Sgjelinek gettext("WARNING: device '%s' " 32061300Sgjelinek "is configured in both zones.\n"), 32071300Sgjelinek t_devtab.zone_dev_match); 32081300Sgjelinek break; 32091300Sgjelinek } 32101300Sgjelinek } 32111300Sgjelinek (void) zonecfg_enddevent(s_handle); 32121300Sgjelinek } 32131300Sgjelinek 32141300Sgjelinek (void) zonecfg_enddevent(t_handle); 32151300Sgjelinek } 32161300Sgjelinek 32171300Sgjelinek /* 32181300Sgjelinek * Check if the specified mount option (opt) is contained within the 32191300Sgjelinek * options string. 32201300Sgjelinek */ 32211300Sgjelinek static boolean_t 32221300Sgjelinek opt_match(char *opt, char *options) 32231300Sgjelinek { 32241300Sgjelinek char *p; 32251300Sgjelinek char *lastp; 32261300Sgjelinek 32271300Sgjelinek if ((p = strtok_r(options, ",", &lastp)) != NULL) { 32281300Sgjelinek if (strcmp(p, opt) == 0) 32291300Sgjelinek return (B_TRUE); 32301300Sgjelinek while ((p = strtok_r(NULL, ",", &lastp)) != NULL) { 32311300Sgjelinek if (strcmp(p, opt) == 0) 32321300Sgjelinek return (B_TRUE); 32331300Sgjelinek } 32341300Sgjelinek } 32351300Sgjelinek 32361300Sgjelinek return (B_FALSE); 32371300Sgjelinek } 32381300Sgjelinek 32391867Sgjelinek #define RW_LOFS "WARNING: read-write lofs file system on '%s' is configured " \ 32401300Sgjelinek "in both zones.\n" 32411300Sgjelinek 32421300Sgjelinek static void 32431300Sgjelinek print_fs_warnings(struct zone_fstab *s_fstab, struct zone_fstab *t_fstab) 32441300Sgjelinek { 32451300Sgjelinek /* 32461300Sgjelinek * It is ok to have shared lofs mounted fs but we want to warn if 32471300Sgjelinek * either is rw since this will effect the other zone. 32481300Sgjelinek */ 32491300Sgjelinek if (strcmp(t_fstab->zone_fs_type, "lofs") == 0) { 32501300Sgjelinek zone_fsopt_t *optp; 32511300Sgjelinek 32521300Sgjelinek /* The default is rw so no options means rw */ 32531300Sgjelinek if (t_fstab->zone_fs_options == NULL || 32541300Sgjelinek s_fstab->zone_fs_options == NULL) { 32551300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 32561300Sgjelinek t_fstab->zone_fs_special); 32571300Sgjelinek return; 32581300Sgjelinek } 32591300Sgjelinek 32601300Sgjelinek for (optp = s_fstab->zone_fs_options; optp != NULL; 32611300Sgjelinek optp = optp->zone_fsopt_next) { 32621300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 32631300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 32641300Sgjelinek s_fstab->zone_fs_special); 32651300Sgjelinek return; 32661300Sgjelinek } 32671300Sgjelinek } 32681300Sgjelinek 32691300Sgjelinek for (optp = t_fstab->zone_fs_options; optp != NULL; 32701300Sgjelinek optp = optp->zone_fsopt_next) { 32711300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 32721300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 32731300Sgjelinek t_fstab->zone_fs_special); 32741300Sgjelinek return; 32751300Sgjelinek } 32761300Sgjelinek } 32771300Sgjelinek 32781300Sgjelinek return; 32791300Sgjelinek } 32801300Sgjelinek 32811300Sgjelinek /* 32821300Sgjelinek * TRANSLATION_NOTE 32831867Sgjelinek * The first variable is the file system type and the second is 32841867Sgjelinek * the file system special device. For example, 32851867Sgjelinek * WARNING: ufs file system on '/dev/dsk/c0t0d0s0' ... 32861300Sgjelinek */ 32871867Sgjelinek (void) fprintf(stderr, gettext("WARNING: %s file system on '%s' " 32881300Sgjelinek "is configured in both zones.\n"), t_fstab->zone_fs_type, 32891300Sgjelinek t_fstab->zone_fs_special); 32901300Sgjelinek } 32911300Sgjelinek 32921300Sgjelinek static void 32931300Sgjelinek warn_fs_match(zone_dochandle_t s_handle, char *source_zone, 32941300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 32951300Sgjelinek { 32961300Sgjelinek int err; 32971300Sgjelinek struct zone_fstab s_fstab; 32981300Sgjelinek struct zone_fstab t_fstab; 32991300Sgjelinek 33001300Sgjelinek if ((err = zonecfg_setfsent(t_handle)) != Z_OK) { 33011300Sgjelinek errno = err; 33021300Sgjelinek zperror2(target_zone, 33031867Sgjelinek gettext("could not enumerate file systems")); 33041300Sgjelinek return; 33051300Sgjelinek } 33061300Sgjelinek 33071300Sgjelinek while (zonecfg_getfsent(t_handle, &t_fstab) == Z_OK) { 33081300Sgjelinek if ((err = zonecfg_setfsent(s_handle)) != Z_OK) { 33091300Sgjelinek errno = err; 33101300Sgjelinek zperror2(source_zone, 33111867Sgjelinek gettext("could not enumerate file systems")); 33121300Sgjelinek (void) zonecfg_endfsent(t_handle); 33131300Sgjelinek return; 33141300Sgjelinek } 33151300Sgjelinek 33161300Sgjelinek while (zonecfg_getfsent(s_handle, &s_fstab) == Z_OK) { 33171300Sgjelinek if (strcmp(t_fstab.zone_fs_special, 33181300Sgjelinek s_fstab.zone_fs_special) == 0) { 33191300Sgjelinek print_fs_warnings(&s_fstab, &t_fstab); 33201300Sgjelinek break; 33211300Sgjelinek } 33221300Sgjelinek } 33231300Sgjelinek (void) zonecfg_endfsent(s_handle); 33241300Sgjelinek } 33251300Sgjelinek 33261300Sgjelinek (void) zonecfg_endfsent(t_handle); 33271300Sgjelinek } 33281300Sgjelinek 33291300Sgjelinek /* 33301300Sgjelinek * We don't catch the case where you used the same IP address but 33311300Sgjelinek * it is not an exact string match. For example, 192.9.0.128 vs. 192.09.0.128. 33321300Sgjelinek * However, we're not going to worry about that but we will check for 33331300Sgjelinek * a possible netmask on one of the addresses (e.g. 10.0.0.1 and 10.0.0.1/24) 33341300Sgjelinek * and handle that case as a match. 33351300Sgjelinek */ 33361300Sgjelinek static void 33371300Sgjelinek warn_ip_match(zone_dochandle_t s_handle, char *source_zone, 33381300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 33391300Sgjelinek { 33401300Sgjelinek int err; 33411300Sgjelinek struct zone_nwiftab s_nwiftab; 33421300Sgjelinek struct zone_nwiftab t_nwiftab; 33431300Sgjelinek 33441300Sgjelinek if ((err = zonecfg_setnwifent(t_handle)) != Z_OK) { 33451300Sgjelinek errno = err; 33461300Sgjelinek zperror2(target_zone, 33471300Sgjelinek gettext("could not enumerate network interfaces")); 33481300Sgjelinek return; 33491300Sgjelinek } 33501300Sgjelinek 33511300Sgjelinek while (zonecfg_getnwifent(t_handle, &t_nwiftab) == Z_OK) { 33521300Sgjelinek char *p; 33531300Sgjelinek 33541300Sgjelinek /* remove an (optional) netmask from the address */ 33551300Sgjelinek if ((p = strchr(t_nwiftab.zone_nwif_address, '/')) != NULL) 33561300Sgjelinek *p = '\0'; 33571300Sgjelinek 33581300Sgjelinek if ((err = zonecfg_setnwifent(s_handle)) != Z_OK) { 33591300Sgjelinek errno = err; 33601300Sgjelinek zperror2(source_zone, 33611300Sgjelinek gettext("could not enumerate network interfaces")); 33621300Sgjelinek (void) zonecfg_endnwifent(t_handle); 33631300Sgjelinek return; 33641300Sgjelinek } 33651300Sgjelinek 33661300Sgjelinek while (zonecfg_getnwifent(s_handle, &s_nwiftab) == Z_OK) { 33671300Sgjelinek /* remove an (optional) netmask from the address */ 33681300Sgjelinek if ((p = strchr(s_nwiftab.zone_nwif_address, '/')) 33691300Sgjelinek != NULL) 33701300Sgjelinek *p = '\0'; 33711300Sgjelinek 33723448Sdh155122 /* For exclusive-IP zones, address is not specified. */ 33733448Sdh155122 if (strlen(s_nwiftab.zone_nwif_address) == 0) 33743448Sdh155122 continue; 33753448Sdh155122 33761300Sgjelinek if (strcmp(t_nwiftab.zone_nwif_address, 33771300Sgjelinek s_nwiftab.zone_nwif_address) == 0) { 33781300Sgjelinek (void) fprintf(stderr, 33791300Sgjelinek gettext("WARNING: network address '%s' " 33801300Sgjelinek "is configured in both zones.\n"), 33811300Sgjelinek t_nwiftab.zone_nwif_address); 33821300Sgjelinek break; 33831300Sgjelinek } 33841300Sgjelinek } 33851300Sgjelinek (void) zonecfg_endnwifent(s_handle); 33861300Sgjelinek } 33871300Sgjelinek 33881300Sgjelinek (void) zonecfg_endnwifent(t_handle); 33891300Sgjelinek } 33901300Sgjelinek 33911300Sgjelinek static void 33922712Snn35248 warn_dataset_match(zone_dochandle_t s_handle, char *source, 33932712Snn35248 zone_dochandle_t t_handle, char *target) 33941300Sgjelinek { 33951300Sgjelinek int err; 33961300Sgjelinek struct zone_dstab s_dstab; 33971300Sgjelinek struct zone_dstab t_dstab; 33981300Sgjelinek 33991300Sgjelinek if ((err = zonecfg_setdsent(t_handle)) != Z_OK) { 34001300Sgjelinek errno = err; 34012712Snn35248 zperror2(target, gettext("could not enumerate datasets")); 34021300Sgjelinek return; 34031300Sgjelinek } 34041300Sgjelinek 34051300Sgjelinek while (zonecfg_getdsent(t_handle, &t_dstab) == Z_OK) { 34061300Sgjelinek if ((err = zonecfg_setdsent(s_handle)) != Z_OK) { 34071300Sgjelinek errno = err; 34082712Snn35248 zperror2(source, 34091300Sgjelinek gettext("could not enumerate datasets")); 34101300Sgjelinek (void) zonecfg_enddsent(t_handle); 34111300Sgjelinek return; 34121300Sgjelinek } 34131300Sgjelinek 34141300Sgjelinek while (zonecfg_getdsent(s_handle, &s_dstab) == Z_OK) { 34151300Sgjelinek if (strcmp(t_dstab.zone_dataset_name, 34161300Sgjelinek s_dstab.zone_dataset_name) == 0) { 34172712Snn35248 target_zone = source; 34182712Snn35248 zerror(gettext("WARNING: dataset '%s' " 34191300Sgjelinek "is configured in both zones.\n"), 34201300Sgjelinek t_dstab.zone_dataset_name); 34211300Sgjelinek break; 34221300Sgjelinek } 34231300Sgjelinek } 34241300Sgjelinek (void) zonecfg_enddsent(s_handle); 34251300Sgjelinek } 34261300Sgjelinek 34271300Sgjelinek (void) zonecfg_enddsent(t_handle); 34281300Sgjelinek } 34291300Sgjelinek 34302712Snn35248 /* 34312712Snn35248 * Check that the clone and its source have the same brand type. 34322712Snn35248 */ 34332712Snn35248 static int 34342712Snn35248 valid_brand_clone(char *source_zone, char *target_zone) 34352712Snn35248 { 34362727Sedp brand_handle_t bh; 34372712Snn35248 char source_brand[MAXNAMELEN]; 34382712Snn35248 34392712Snn35248 if ((zone_get_brand(source_zone, source_brand, 34402712Snn35248 sizeof (source_brand))) != Z_OK) { 34412712Snn35248 (void) fprintf(stderr, "%s: zone '%s': %s\n", 34422712Snn35248 execname, source_zone, gettext("missing or invalid brand")); 34432712Snn35248 return (Z_ERR); 34442712Snn35248 } 34452712Snn35248 34462712Snn35248 if (strcmp(source_brand, target_brand) != NULL) { 34472712Snn35248 (void) fprintf(stderr, 34482712Snn35248 gettext("%s: Zones '%s' and '%s' have different brand " 34492712Snn35248 "types.\n"), execname, source_zone, target_zone); 34502712Snn35248 return (Z_ERR); 34512712Snn35248 } 34522712Snn35248 34532727Sedp if ((bh = brand_open(target_brand)) == NULL) { 34542712Snn35248 zerror(gettext("missing or invalid brand")); 34552712Snn35248 return (Z_ERR); 34562712Snn35248 } 34572727Sedp brand_close(bh); 34582712Snn35248 return (Z_OK); 34592712Snn35248 } 34602712Snn35248 34611300Sgjelinek static int 34621300Sgjelinek validate_clone(char *source_zone, char *target_zone) 34631300Sgjelinek { 34641300Sgjelinek int err = Z_OK; 34651300Sgjelinek zone_dochandle_t s_handle; 34661300Sgjelinek zone_dochandle_t t_handle; 34671300Sgjelinek 34681300Sgjelinek if ((t_handle = zonecfg_init_handle()) == NULL) { 34691300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34701300Sgjelinek return (Z_ERR); 34711300Sgjelinek } 34721300Sgjelinek if ((err = zonecfg_get_handle(target_zone, t_handle)) != Z_OK) { 34731300Sgjelinek errno = err; 34741300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34751300Sgjelinek zonecfg_fini_handle(t_handle); 34761300Sgjelinek return (Z_ERR); 34771300Sgjelinek } 34781300Sgjelinek 34791300Sgjelinek if ((s_handle = zonecfg_init_handle()) == NULL) { 34801300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34811300Sgjelinek zonecfg_fini_handle(t_handle); 34821300Sgjelinek return (Z_ERR); 34831300Sgjelinek } 34841300Sgjelinek if ((err = zonecfg_get_handle(source_zone, s_handle)) != Z_OK) { 34851300Sgjelinek errno = err; 34861300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 34871300Sgjelinek goto done; 34881300Sgjelinek } 34891300Sgjelinek 34902712Snn35248 /* verify new zone has same brand type */ 34912712Snn35248 err = valid_brand_clone(source_zone, target_zone); 34922712Snn35248 if (err != Z_OK) 34932712Snn35248 goto done; 34942712Snn35248 34951300Sgjelinek /* verify new zone has same inherit-pkg-dirs */ 34961300Sgjelinek err = valid_ipd_clone(s_handle, source_zone, t_handle, target_zone); 34971300Sgjelinek 34981300Sgjelinek /* warn about imported fs's which are the same */ 34991300Sgjelinek warn_fs_match(s_handle, source_zone, t_handle, target_zone); 35001300Sgjelinek 35011300Sgjelinek /* warn about imported IP addresses which are the same */ 35021300Sgjelinek warn_ip_match(s_handle, source_zone, t_handle, target_zone); 35031300Sgjelinek 35041300Sgjelinek /* warn about imported devices which are the same */ 35051300Sgjelinek warn_dev_match(s_handle, source_zone, t_handle, target_zone); 35061300Sgjelinek 35071300Sgjelinek /* warn about imported datasets which are the same */ 35081300Sgjelinek warn_dataset_match(s_handle, source_zone, t_handle, target_zone); 35091300Sgjelinek 35101300Sgjelinek done: 35111300Sgjelinek zonecfg_fini_handle(t_handle); 35121300Sgjelinek zonecfg_fini_handle(s_handle); 35131300Sgjelinek 35141300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 35151300Sgjelinek } 35161300Sgjelinek 35171300Sgjelinek static int 35181300Sgjelinek copy_zone(char *src, char *dst) 35191300Sgjelinek { 35201300Sgjelinek boolean_t out_null = B_FALSE; 35211300Sgjelinek int status; 35221300Sgjelinek char *outfile; 35231300Sgjelinek char cmdbuf[MAXPATHLEN * 2 + 128]; 35241300Sgjelinek 35251300Sgjelinek if ((outfile = tempnam("/var/log", "zone")) == NULL) { 35261300Sgjelinek outfile = "/dev/null"; 35271300Sgjelinek out_null = B_TRUE; 35281300Sgjelinek } 35291300Sgjelinek 35301867Sgjelinek /* 35311867Sgjelinek * Use find to get the list of files to copy. We need to skip 35321867Sgjelinek * files of type "socket" since cpio can't handle those but that 35331867Sgjelinek * should be ok since the app will recreate the socket when it runs. 35341867Sgjelinek * We also need to filter out anything under the .zfs subdir. Since 35351867Sgjelinek * find is running depth-first, we need the extra egrep to filter .zfs. 35361867Sgjelinek */ 35371300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 35381867Sgjelinek "cd %s && /usr/bin/find . -type s -prune -o -depth -print | " 35391607Sgjelinek "/usr/bin/egrep -v '^\\./\\.zfs$|^\\./\\.zfs/' | " 35401300Sgjelinek "/usr/bin/cpio -pdmuP@ %s > %s 2>&1", 35411300Sgjelinek src, dst, outfile); 35421300Sgjelinek 35431300Sgjelinek status = do_subproc(cmdbuf); 35441300Sgjelinek 35452712Snn35248 if (subproc_status("copy", status, B_TRUE) != ZONE_SUBPROC_OK) { 35461300Sgjelinek if (!out_null) 35471300Sgjelinek (void) fprintf(stderr, gettext("\nThe copy failed.\n" 35481300Sgjelinek "More information can be found in %s\n"), outfile); 35492712Snn35248 return (Z_ERR); 35501300Sgjelinek } 35511300Sgjelinek 35521300Sgjelinek if (!out_null) 35531300Sgjelinek (void) unlink(outfile); 35541300Sgjelinek 35551300Sgjelinek return (Z_OK); 35561300Sgjelinek } 35571300Sgjelinek 35581300Sgjelinek /* ARGSUSED */ 35591867Sgjelinek static int 35601300Sgjelinek zfm_print(const char *p, void *r) { 35611300Sgjelinek zerror(" %s\n", p); 35621300Sgjelinek return (0); 35631300Sgjelinek } 35641300Sgjelinek 35651867Sgjelinek int 35661867Sgjelinek clone_copy(char *source_zonepath, char *zonepath) 35671867Sgjelinek { 35681867Sgjelinek int err; 35691867Sgjelinek 35701867Sgjelinek /* Don't clone the zone if anything is still mounted there */ 35711867Sgjelinek if (zonecfg_find_mounts(source_zonepath, NULL, NULL)) { 35721867Sgjelinek zerror(gettext("These file systems are mounted on " 35731867Sgjelinek "subdirectories of %s.\n"), source_zonepath); 35741867Sgjelinek (void) zonecfg_find_mounts(source_zonepath, zfm_print, NULL); 35751867Sgjelinek return (Z_ERR); 35761867Sgjelinek } 35771867Sgjelinek 35781867Sgjelinek /* 35791867Sgjelinek * Attempt to create a ZFS fs for the zonepath. As usual, we don't 35801867Sgjelinek * care if this works or not since we always have the default behavior 35811867Sgjelinek * of a simple directory for the zonepath. 35821867Sgjelinek */ 35831867Sgjelinek create_zfs_zonepath(zonepath); 35841867Sgjelinek 35851867Sgjelinek (void) printf(gettext("Copying %s..."), source_zonepath); 35861867Sgjelinek (void) fflush(stdout); 35871867Sgjelinek 35881867Sgjelinek err = copy_zone(source_zonepath, zonepath); 35891867Sgjelinek 35901867Sgjelinek (void) printf("\n"); 35911867Sgjelinek 35921867Sgjelinek return (err); 35931867Sgjelinek } 35941867Sgjelinek 35951300Sgjelinek static int 35961300Sgjelinek clone_func(int argc, char *argv[]) 35971300Sgjelinek { 35981300Sgjelinek char *source_zone = NULL; 35991300Sgjelinek int lockfd; 36001300Sgjelinek int err, arg; 36011300Sgjelinek char zonepath[MAXPATHLEN]; 36021300Sgjelinek char source_zonepath[MAXPATHLEN]; 36031300Sgjelinek zone_state_t state; 36041300Sgjelinek zone_entry_t *zent; 36051867Sgjelinek char *method = NULL; 36061867Sgjelinek char *snapshot = NULL; 36077089Sgjelinek char cmdbuf[MAXPATHLEN]; 36087089Sgjelinek char postcmdbuf[MAXPATHLEN]; 36097089Sgjelinek char presnapbuf[MAXPATHLEN]; 36107089Sgjelinek char postsnapbuf[MAXPATHLEN]; 36117089Sgjelinek char validsnapbuf[MAXPATHLEN]; 36127089Sgjelinek brand_handle_t bh = NULL; 36137089Sgjelinek int status; 36147089Sgjelinek boolean_t brand_help = B_FALSE; 36151300Sgjelinek 36161300Sgjelinek if (zonecfg_in_alt_root()) { 36171300Sgjelinek zerror(gettext("cannot clone zone in alternate root")); 36181300Sgjelinek return (Z_ERR); 36191300Sgjelinek } 36201300Sgjelinek 36217089Sgjelinek /* Check the argv string for args we handle internally */ 36221300Sgjelinek optind = 0; 36237089Sgjelinek opterr = 0; 36247089Sgjelinek while ((arg = getopt(argc, argv, "?m:s:")) != EOF) { 36251300Sgjelinek switch (arg) { 36261300Sgjelinek case '?': 36277089Sgjelinek if (optopt == '?') { 36287089Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 36297089Sgjelinek brand_help = B_TRUE; 36307089Sgjelinek } 36317089Sgjelinek /* Ignore unknown options - may be brand specific. */ 36327089Sgjelinek break; 36331300Sgjelinek case 'm': 36341300Sgjelinek method = optarg; 36351300Sgjelinek break; 36361867Sgjelinek case 's': 36371867Sgjelinek snapshot = optarg; 36381867Sgjelinek break; 36391300Sgjelinek default: 36407089Sgjelinek /* Ignore unknown options - may be brand specific. */ 36417089Sgjelinek break; 36421300Sgjelinek } 36431300Sgjelinek } 36447089Sgjelinek 36457089Sgjelinek if (argc != (optind + 1)) { 36461300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 36471300Sgjelinek return (Z_USAGE); 36481300Sgjelinek } 36497089Sgjelinek 36501300Sgjelinek source_zone = argv[optind]; 36517089Sgjelinek 36527089Sgjelinek if (!brand_help) { 36537089Sgjelinek if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE, 36547089Sgjelinek B_FALSE) != Z_OK) 36557089Sgjelinek return (Z_ERR); 36567089Sgjelinek if (verify_details(CMD_CLONE, argv) != Z_OK) 36577089Sgjelinek return (Z_ERR); 36587089Sgjelinek 36597089Sgjelinek /* 36607089Sgjelinek * We also need to do some extra validation on the source zone. 36617089Sgjelinek */ 36627089Sgjelinek if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) { 36637089Sgjelinek zerror(gettext("%s operation is invalid for the " 36647089Sgjelinek "global zone."), cmd_to_str(CMD_CLONE)); 36657089Sgjelinek return (Z_ERR); 36667089Sgjelinek } 36677089Sgjelinek 36687089Sgjelinek if (strncmp(source_zone, "SUNW", 4) == 0) { 36697089Sgjelinek zerror(gettext("%s operation is invalid for zones " 36707089Sgjelinek "starting with SUNW."), cmd_to_str(CMD_CLONE)); 36717089Sgjelinek return (Z_ERR); 36727089Sgjelinek } 36737089Sgjelinek 36747089Sgjelinek zent = lookup_running_zone(source_zone); 36757089Sgjelinek if (zent != NULL) { 36767089Sgjelinek /* check whether the zone is ready or running */ 36777089Sgjelinek if ((err = zone_get_state(zent->zname, 36787089Sgjelinek &zent->zstate_num)) != Z_OK) { 36797089Sgjelinek errno = err; 36807089Sgjelinek zperror2(zent->zname, gettext("could not get " 36817089Sgjelinek "state")); 36827089Sgjelinek /* can't tell, so hedge */ 36837089Sgjelinek zent->zstate_str = "ready/running"; 36847089Sgjelinek } else { 36857089Sgjelinek zent->zstate_str = 36867089Sgjelinek zone_state_str(zent->zstate_num); 36877089Sgjelinek } 36887089Sgjelinek zerror(gettext("%s operation is invalid for %s zones."), 36897089Sgjelinek cmd_to_str(CMD_CLONE), zent->zstate_str); 36907089Sgjelinek return (Z_ERR); 36917089Sgjelinek } 36927089Sgjelinek 36937089Sgjelinek if ((err = zone_get_state(source_zone, &state)) != Z_OK) { 36941300Sgjelinek errno = err; 36957089Sgjelinek zperror2(source_zone, gettext("could not get state")); 36967089Sgjelinek return (Z_ERR); 36977089Sgjelinek } 36987089Sgjelinek if (state != ZONE_STATE_INSTALLED) { 36997089Sgjelinek (void) fprintf(stderr, 37007089Sgjelinek gettext("%s: zone %s is %s; %s is required.\n"), 37017089Sgjelinek execname, source_zone, zone_state_str(state), 37027089Sgjelinek zone_state_str(ZONE_STATE_INSTALLED)); 37037089Sgjelinek return (Z_ERR); 37041300Sgjelinek } 37057089Sgjelinek 37067089Sgjelinek /* 37077089Sgjelinek * The source zone checks out ok, continue with the clone. 37087089Sgjelinek */ 37097089Sgjelinek 37107089Sgjelinek if (validate_clone(source_zone, target_zone) != Z_OK) 37117089Sgjelinek return (Z_ERR); 37127089Sgjelinek 37137089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 37147089Sgjelinek zerror(gettext("another %s may have an operation in " 37157089Sgjelinek "progress."), "zoneadm"); 37167089Sgjelinek return (Z_ERR); 37177089Sgjelinek } 37181300Sgjelinek } 37191300Sgjelinek 37201300Sgjelinek if ((err = zone_get_zonepath(source_zone, source_zonepath, 37211300Sgjelinek sizeof (source_zonepath))) != Z_OK) { 37221300Sgjelinek errno = err; 37231300Sgjelinek zperror2(source_zone, gettext("could not get zone path")); 37241300Sgjelinek goto done; 37251300Sgjelinek } 37261300Sgjelinek 37271300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 37281300Sgjelinek != Z_OK) { 37291300Sgjelinek errno = err; 37301300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 37311300Sgjelinek goto done; 37321300Sgjelinek } 37331300Sgjelinek 37347089Sgjelinek /* 37357089Sgjelinek * Fetch the clone and postclone hooks from the brand configuration. 37367089Sgjelinek */ 37377089Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 37387089Sgjelinek zerror(gettext("missing or invalid brand")); 37397089Sgjelinek err = Z_ERR; 37407089Sgjelinek goto done; 37417089Sgjelinek } 37427089Sgjelinek 37437089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_clone, target_zone, 37447089Sgjelinek zonepath) != Z_OK) { 37457089Sgjelinek zerror("invalid brand configuration: missing clone resource"); 37467089Sgjelinek brand_close(bh); 37477089Sgjelinek err = Z_ERR; 37487089Sgjelinek goto done; 37497089Sgjelinek } 37507089Sgjelinek 37517089Sgjelinek if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postclone, 37527089Sgjelinek target_zone, zonepath) != Z_OK) { 37537089Sgjelinek zerror("invalid brand configuration: missing postclone " 37547089Sgjelinek "resource"); 37557089Sgjelinek brand_close(bh); 37567089Sgjelinek err = Z_ERR; 37577089Sgjelinek goto done; 37587089Sgjelinek } 37597089Sgjelinek 37607089Sgjelinek if (get_hook(bh, presnapbuf, sizeof (presnapbuf), brand_get_presnap, 37617089Sgjelinek source_zone, source_zonepath) != Z_OK) { 37627089Sgjelinek zerror("invalid brand configuration: missing presnap " 37637089Sgjelinek "resource"); 37647089Sgjelinek brand_close(bh); 37657089Sgjelinek err = Z_ERR; 37661300Sgjelinek goto done; 37671300Sgjelinek } 37681300Sgjelinek 37697089Sgjelinek if (get_hook(bh, postsnapbuf, sizeof (postsnapbuf), brand_get_postsnap, 37707089Sgjelinek source_zone, source_zonepath) != Z_OK) { 37717089Sgjelinek zerror("invalid brand configuration: missing postsnap " 37727089Sgjelinek "resource"); 37737089Sgjelinek brand_close(bh); 37747089Sgjelinek err = Z_ERR; 37757089Sgjelinek goto done; 37767089Sgjelinek } 37777089Sgjelinek 37787089Sgjelinek if (get_hook(bh, validsnapbuf, sizeof (validsnapbuf), 37797089Sgjelinek brand_get_validatesnap, target_zone, zonepath) != Z_OK) { 37807089Sgjelinek zerror("invalid brand configuration: missing validatesnap " 37817089Sgjelinek "resource"); 37827089Sgjelinek brand_close(bh); 37831867Sgjelinek err = Z_ERR; 37847089Sgjelinek goto done; 37857089Sgjelinek } 37867089Sgjelinek brand_close(bh); 37877089Sgjelinek 37887089Sgjelinek /* Append all options to clone hook. */ 37897089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) { 37907089Sgjelinek err = Z_ERR; 37917089Sgjelinek goto done; 37927089Sgjelinek } 37937089Sgjelinek 37947089Sgjelinek /* Append all options to postclone hook. */ 37957089Sgjelinek if (addoptions(postcmdbuf, argv, sizeof (postcmdbuf)) != Z_OK) { 37967089Sgjelinek err = Z_ERR; 37977089Sgjelinek goto done; 37987089Sgjelinek } 37997089Sgjelinek 38007089Sgjelinek if (!brand_help) { 38017089Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE)) 38027089Sgjelinek != Z_OK) { 38037089Sgjelinek errno = err; 38047089Sgjelinek zperror2(target_zone, gettext("could not set state")); 38057089Sgjelinek goto done; 38067089Sgjelinek } 38071867Sgjelinek } 38081867Sgjelinek 38092712Snn35248 /* 38107089Sgjelinek * The clone hook is optional. If it exists, use the hook for 38117089Sgjelinek * cloning, otherwise use the built-in clone support 38122712Snn35248 */ 38137089Sgjelinek if (cmdbuf[0] != '\0') { 38147089Sgjelinek /* Run the clone hook */ 38157089Sgjelinek status = do_subproc_interactive(cmdbuf); 38167089Sgjelinek if ((status = subproc_status(gettext("brand-specific clone"), 38177089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 38187089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 38197089Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 38207089Sgjelinek err = Z_ERR; 38217089Sgjelinek goto done; 38227089Sgjelinek } 38237089Sgjelinek 38247089Sgjelinek if (brand_help) 38257089Sgjelinek return (Z_OK); 38267089Sgjelinek 38277089Sgjelinek } else { 38287089Sgjelinek /* If just help, we're done since there is no brand help. */ 38297089Sgjelinek if (brand_help) 38307089Sgjelinek return (Z_OK); 38317089Sgjelinek 38327089Sgjelinek /* Run the built-in clone support. */ 38337089Sgjelinek 38347089Sgjelinek /* The only explicit built-in method is "copy". */ 38357089Sgjelinek if (method != NULL && strcmp(method, "copy") != 0) { 38367089Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 38377089Sgjelinek err = Z_USAGE; 38387089Sgjelinek goto done; 38397089Sgjelinek } 38407089Sgjelinek 38417089Sgjelinek if (snapshot != NULL) { 38427089Sgjelinek err = clone_snapshot_zfs(snapshot, zonepath, 38437089Sgjelinek validsnapbuf); 38447089Sgjelinek } else { 38457089Sgjelinek /* 38467089Sgjelinek * We always copy the clone unless the source is ZFS 38477089Sgjelinek * and a ZFS clone worked. We fallback to copying if 38487089Sgjelinek * the ZFS clone fails for some reason. 38497089Sgjelinek */ 38507089Sgjelinek err = Z_ERR; 38517089Sgjelinek if (method == NULL && is_zonepath_zfs(source_zonepath)) 38527089Sgjelinek err = clone_zfs(source_zonepath, zonepath, 38537089Sgjelinek presnapbuf, postsnapbuf); 38547089Sgjelinek 38557089Sgjelinek if (err != Z_OK) 38567089Sgjelinek err = clone_copy(source_zonepath, zonepath); 38577089Sgjelinek } 38587089Sgjelinek } 38597089Sgjelinek 38607089Sgjelinek if (err == Z_OK && postcmdbuf[0] != '\0') { 38617089Sgjelinek status = do_subproc(postcmdbuf); 38627089Sgjelinek if ((err = subproc_status("postclone", status, B_FALSE)) 38637089Sgjelinek != ZONE_SUBPROC_OK) { 38647089Sgjelinek zerror(gettext("post-clone configuration failed.")); 38657089Sgjelinek err = Z_ERR; 38667089Sgjelinek } 38677089Sgjelinek } 38681300Sgjelinek 38691300Sgjelinek done: 38702712Snn35248 /* 38712712Snn35248 * If everything went well, we mark the zone as installed. 38722712Snn35248 */ 38732712Snn35248 if (err == Z_OK) { 38742712Snn35248 err = zone_set_state(target_zone, ZONE_STATE_INSTALLED); 38752712Snn35248 if (err != Z_OK) { 38762712Snn35248 errno = err; 38772712Snn35248 zperror2(target_zone, gettext("could not set state")); 38782712Snn35248 } 38792712Snn35248 } 38807089Sgjelinek if (!brand_help) 38817089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 38821300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 38831300Sgjelinek } 38841300Sgjelinek 38851607Sgjelinek /* 38861867Sgjelinek * Used when removing a zonepath after uninstalling or cleaning up after 38871867Sgjelinek * the move subcommand. This handles a zonepath that has non-standard 38881867Sgjelinek * contents so that we will only cleanup the stuff we know about and leave 38891867Sgjelinek * any user data alone. 38901607Sgjelinek * 38911867Sgjelinek * If the "all" parameter is true then we should remove the whole zonepath 38921867Sgjelinek * even if it has non-standard files/directories in it. This can be used when 38931867Sgjelinek * we need to cleanup after moving the zonepath across file systems. 38941867Sgjelinek * 38951867Sgjelinek * We "exec" the RMCOMMAND so that the returned status is that of RMCOMMAND 38961867Sgjelinek * and not the shell. 38971607Sgjelinek */ 38981607Sgjelinek static int 38991867Sgjelinek cleanup_zonepath(char *zonepath, boolean_t all) 39001607Sgjelinek { 39011867Sgjelinek int status; 39021867Sgjelinek int i; 39031867Sgjelinek boolean_t non_std = B_FALSE; 39041867Sgjelinek struct dirent *dp; 39051867Sgjelinek DIR *dirp; 39063686Sgjelinek /* 39073686Sgjelinek * The SUNWattached.xml file is expected since it might 39083686Sgjelinek * exist if the zone was force-attached after a 39093686Sgjelinek * migration. 39103686Sgjelinek */ 39113686Sgjelinek char *std_entries[] = {"dev", "lu", "root", 39123686Sgjelinek "SUNWattached.xml", NULL}; 39131867Sgjelinek /* (MAXPATHLEN * 3) is for the 3 std_entries dirs */ 39141867Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64]; 39151867Sgjelinek 39161867Sgjelinek /* 39171867Sgjelinek * We shouldn't need these checks but lets be paranoid since we 39181867Sgjelinek * could blow away the whole system here if we got the wrong zonepath. 39191867Sgjelinek */ 39201867Sgjelinek if (*zonepath == NULL || strcmp(zonepath, "/") == 0) { 39211867Sgjelinek (void) fprintf(stderr, "invalid zonepath '%s'\n", zonepath); 39221867Sgjelinek return (Z_INVAL); 39231867Sgjelinek } 39241867Sgjelinek 39251867Sgjelinek /* 39261867Sgjelinek * If the dirpath is already gone (maybe it was manually removed) then 39271867Sgjelinek * we just return Z_OK so that the cleanup is successful. 39281867Sgjelinek */ 39291867Sgjelinek if ((dirp = opendir(zonepath)) == NULL) 39301867Sgjelinek return (Z_OK); 39311867Sgjelinek 39321867Sgjelinek /* 39331867Sgjelinek * Look through the zonepath directory to see if there are any 39341867Sgjelinek * non-standard files/dirs. Also skip .zfs since that might be 39351867Sgjelinek * there but we'll handle ZFS file systems as a special case. 39361867Sgjelinek */ 39371867Sgjelinek while ((dp = readdir(dirp)) != NULL) { 39381867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 39391867Sgjelinek strcmp(dp->d_name, "..") == 0 || 39401867Sgjelinek strcmp(dp->d_name, ".zfs") == 0) 39411867Sgjelinek continue; 39421867Sgjelinek 39431867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) 39441867Sgjelinek if (strcmp(dp->d_name, std_entries[i]) == 0) 39451867Sgjelinek break; 39461867Sgjelinek 39471867Sgjelinek if (std_entries[i] == NULL) 39481867Sgjelinek non_std = B_TRUE; 39491867Sgjelinek } 39501867Sgjelinek (void) closedir(dirp); 39511867Sgjelinek 39521867Sgjelinek if (!all && non_std) { 39531607Sgjelinek /* 39541867Sgjelinek * There are extra, non-standard directories/files in the 39551867Sgjelinek * zonepath so we don't want to remove the zonepath. We 39561867Sgjelinek * just want to remove the standard directories and leave 39571867Sgjelinek * the user data alone. 39581607Sgjelinek */ 39591867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND); 39601867Sgjelinek 39611867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) { 39621867Sgjelinek char tmpbuf[MAXPATHLEN]; 39631867Sgjelinek 39641867Sgjelinek if (snprintf(tmpbuf, sizeof (tmpbuf), " %s/%s", 39651867Sgjelinek zonepath, std_entries[i]) >= sizeof (tmpbuf) || 39661867Sgjelinek strlcat(cmdbuf, tmpbuf, sizeof (cmdbuf)) >= 39671867Sgjelinek sizeof (cmdbuf)) { 39681867Sgjelinek (void) fprintf(stderr, 39691867Sgjelinek gettext("path is too long\n")); 39701867Sgjelinek return (Z_INVAL); 39711867Sgjelinek } 39721867Sgjelinek } 39731867Sgjelinek 39741867Sgjelinek status = do_subproc(cmdbuf); 39751867Sgjelinek 39761867Sgjelinek (void) fprintf(stderr, gettext("WARNING: Unable to completely " 39771867Sgjelinek "remove %s\nbecause it contains additional user data. " 39781867Sgjelinek "Only the standard directory\nentries have been " 39791867Sgjelinek "removed.\n"), 39801867Sgjelinek zonepath); 39811867Sgjelinek 39822712Snn35248 return ((subproc_status(RMCOMMAND, status, B_TRUE) == 39832712Snn35248 ZONE_SUBPROC_OK) ? Z_OK : Z_ERR); 39841607Sgjelinek } 39851607Sgjelinek 39861867Sgjelinek /* 39871867Sgjelinek * There is nothing unexpected in the zonepath, try to get rid of the 39881867Sgjelinek * whole zonepath directory. 39891867Sgjelinek * 39901867Sgjelinek * If the zonepath is its own zfs file system, try to destroy the 39911867Sgjelinek * file system. If that fails for some reason (e.g. it has clones) 39921867Sgjelinek * then we'll just remove the contents of the zonepath. 39931867Sgjelinek */ 39941867Sgjelinek if (is_zonepath_zfs(zonepath)) { 39951867Sgjelinek if (destroy_zfs(zonepath) == Z_OK) 39961867Sgjelinek return (Z_OK); 39971867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND 39981867Sgjelinek " %s/*", zonepath); 39991867Sgjelinek status = do_subproc(cmdbuf); 40002712Snn35248 return ((subproc_status(RMCOMMAND, status, B_TRUE) == 40012712Snn35248 ZONE_SUBPROC_OK) ? Z_OK : Z_ERR); 40021867Sgjelinek } 40031867Sgjelinek 40041867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 40051867Sgjelinek zonepath); 40061607Sgjelinek status = do_subproc(cmdbuf); 40072712Snn35248 40082712Snn35248 return ((subproc_status(RMCOMMAND, status, B_TRUE) == ZONE_SUBPROC_OK) 40092712Snn35248 ? Z_OK : Z_ERR); 40101607Sgjelinek } 40111607Sgjelinek 40121300Sgjelinek static int 40131300Sgjelinek move_func(int argc, char *argv[]) 40141300Sgjelinek { 40151300Sgjelinek char *new_zonepath = NULL; 40161300Sgjelinek int lockfd; 40171300Sgjelinek int err, arg; 40181300Sgjelinek char zonepath[MAXPATHLEN]; 40191300Sgjelinek zone_dochandle_t handle; 40201300Sgjelinek boolean_t fast; 40211867Sgjelinek boolean_t is_zfs = B_FALSE; 40221867Sgjelinek struct dirent *dp; 40231867Sgjelinek DIR *dirp; 40241867Sgjelinek boolean_t empty = B_TRUE; 40251300Sgjelinek boolean_t revert; 40261300Sgjelinek struct stat zonepath_buf; 40271300Sgjelinek struct stat new_zonepath_buf; 40281300Sgjelinek 40291300Sgjelinek if (zonecfg_in_alt_root()) { 40301300Sgjelinek zerror(gettext("cannot move zone in alternate root")); 40311300Sgjelinek return (Z_ERR); 40321300Sgjelinek } 40331300Sgjelinek 40341300Sgjelinek optind = 0; 40351300Sgjelinek if ((arg = getopt(argc, argv, "?")) != EOF) { 40361300Sgjelinek switch (arg) { 40371300Sgjelinek case '?': 40381300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 40391300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 40401300Sgjelinek default: 40411300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 40421300Sgjelinek return (Z_USAGE); 40431300Sgjelinek } 40441300Sgjelinek } 40451300Sgjelinek if (argc != (optind + 1)) { 40461300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 40471300Sgjelinek return (Z_USAGE); 40481300Sgjelinek } 40491300Sgjelinek new_zonepath = argv[optind]; 40502712Snn35248 if (sanity_check(target_zone, CMD_MOVE, B_FALSE, B_TRUE, B_FALSE) 40512712Snn35248 != Z_OK) 40521300Sgjelinek return (Z_ERR); 40533339Szt129084 if (verify_details(CMD_MOVE, argv) != Z_OK) 40541300Sgjelinek return (Z_ERR); 40551300Sgjelinek 40561300Sgjelinek /* 40571300Sgjelinek * Check out the new zonepath. This has the side effect of creating 40581300Sgjelinek * a directory for the new zonepath. We depend on this later when we 40591867Sgjelinek * stat to see if we are doing a cross file system move or not. 40601300Sgjelinek */ 40611300Sgjelinek if (validate_zonepath(new_zonepath, CMD_MOVE) != Z_OK) 40621300Sgjelinek return (Z_ERR); 40631300Sgjelinek 40641300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 40651300Sgjelinek != Z_OK) { 40661300Sgjelinek errno = err; 40671300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 40681300Sgjelinek return (Z_ERR); 40691300Sgjelinek } 40701300Sgjelinek 40711300Sgjelinek if (stat(zonepath, &zonepath_buf) == -1) { 40721300Sgjelinek zperror(gettext("could not stat zone path"), B_FALSE); 40731300Sgjelinek return (Z_ERR); 40741300Sgjelinek } 40751300Sgjelinek 40761300Sgjelinek if (stat(new_zonepath, &new_zonepath_buf) == -1) { 40771300Sgjelinek zperror(gettext("could not stat new zone path"), B_FALSE); 40781300Sgjelinek return (Z_ERR); 40791300Sgjelinek } 40801300Sgjelinek 40811867Sgjelinek /* 40821867Sgjelinek * Check if the destination directory is empty. 40831867Sgjelinek */ 40841867Sgjelinek if ((dirp = opendir(new_zonepath)) == NULL) { 40851867Sgjelinek zperror(gettext("could not open new zone path"), B_FALSE); 40861867Sgjelinek return (Z_ERR); 40871867Sgjelinek } 40881867Sgjelinek while ((dp = readdir(dirp)) != (struct dirent *)0) { 40891867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 40901867Sgjelinek strcmp(dp->d_name, "..") == 0) 40911867Sgjelinek continue; 40921867Sgjelinek empty = B_FALSE; 40931867Sgjelinek break; 40941867Sgjelinek } 40951867Sgjelinek (void) closedir(dirp); 40961867Sgjelinek 40971867Sgjelinek /* Error if there is anything in the destination directory. */ 40981867Sgjelinek if (!empty) { 40991867Sgjelinek (void) fprintf(stderr, gettext("could not move zone to %s: " 41001867Sgjelinek "directory not empty\n"), new_zonepath); 41011867Sgjelinek return (Z_ERR); 41021867Sgjelinek } 41031867Sgjelinek 41041300Sgjelinek /* Don't move the zone if anything is still mounted there */ 41051300Sgjelinek if (zonecfg_find_mounts(zonepath, NULL, NULL)) { 41061867Sgjelinek zerror(gettext("These file systems are mounted on " 41071300Sgjelinek "subdirectories of %s.\n"), zonepath); 41081300Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 41091300Sgjelinek return (Z_ERR); 41101300Sgjelinek } 41111300Sgjelinek 41121300Sgjelinek /* 41131867Sgjelinek * Check if we are moving in the same file system and can do a fast 41141867Sgjelinek * move or if we are crossing file systems and have to copy the data. 41151300Sgjelinek */ 41161300Sgjelinek fast = (zonepath_buf.st_dev == new_zonepath_buf.st_dev); 41171300Sgjelinek 41181300Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 41191300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 41201300Sgjelinek return (Z_ERR); 41211300Sgjelinek } 41221300Sgjelinek 41231300Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 41241300Sgjelinek errno = err; 41251300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 41261300Sgjelinek zonecfg_fini_handle(handle); 41271300Sgjelinek return (Z_ERR); 41281300Sgjelinek } 41291300Sgjelinek 41307089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 41311300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 41321300Sgjelinek "zoneadm"); 41331300Sgjelinek zonecfg_fini_handle(handle); 41341300Sgjelinek return (Z_ERR); 41351300Sgjelinek } 41361300Sgjelinek 41371300Sgjelinek /* 41381867Sgjelinek * We're making some file system changes now so we have to clean up 41391867Sgjelinek * the file system before we are done. This will either clean up the 41401300Sgjelinek * new zonepath if the zonecfg update failed or it will clean up the 41411300Sgjelinek * old zonepath if everything is ok. 41421300Sgjelinek */ 41431300Sgjelinek revert = B_TRUE; 41441300Sgjelinek 41451867Sgjelinek if (is_zonepath_zfs(zonepath) && 41461867Sgjelinek move_zfs(zonepath, new_zonepath) != Z_ERR) { 41471867Sgjelinek is_zfs = B_TRUE; 41481867Sgjelinek 41491867Sgjelinek } else if (fast) { 41501867Sgjelinek /* same file system, use rename for a quick move */ 41511300Sgjelinek 41521300Sgjelinek /* 41531300Sgjelinek * Remove the new_zonepath directory that got created above 41541300Sgjelinek * during the validation. It gets in the way of the rename. 41551300Sgjelinek */ 41561300Sgjelinek if (rmdir(new_zonepath) != 0) { 41571300Sgjelinek zperror(gettext("could not rmdir new zone path"), 41581300Sgjelinek B_FALSE); 41591300Sgjelinek zonecfg_fini_handle(handle); 41607089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 41611300Sgjelinek return (Z_ERR); 41621300Sgjelinek } 41631300Sgjelinek 41641300Sgjelinek if (rename(zonepath, new_zonepath) != 0) { 41651300Sgjelinek /* 41661300Sgjelinek * If this fails we don't need to do all of the 41671300Sgjelinek * cleanup that happens for the rest of the code 41681300Sgjelinek * so just return from this error. 41691300Sgjelinek */ 41701300Sgjelinek zperror(gettext("could not move zone"), B_FALSE); 41711300Sgjelinek zonecfg_fini_handle(handle); 41727089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 41731300Sgjelinek return (Z_ERR); 41741300Sgjelinek } 41751300Sgjelinek 41761300Sgjelinek } else { 41771867Sgjelinek /* 41781867Sgjelinek * Attempt to create a ZFS fs for the new zonepath. As usual, 41791867Sgjelinek * we don't care if this works or not since we always have the 41801867Sgjelinek * default behavior of a simple directory for the zonepath. 41811867Sgjelinek */ 41821867Sgjelinek create_zfs_zonepath(new_zonepath); 41831867Sgjelinek 41841300Sgjelinek (void) printf(gettext( 41851867Sgjelinek "Moving across file systems; copying zonepath %s..."), 41861300Sgjelinek zonepath); 41871300Sgjelinek (void) fflush(stdout); 41881300Sgjelinek 41891300Sgjelinek err = copy_zone(zonepath, new_zonepath); 41901300Sgjelinek 41911300Sgjelinek (void) printf("\n"); 41921300Sgjelinek if (err != Z_OK) 41931300Sgjelinek goto done; 41941300Sgjelinek } 41951300Sgjelinek 41961300Sgjelinek if ((err = zonecfg_set_zonepath(handle, new_zonepath)) != Z_OK) { 41971300Sgjelinek errno = err; 41981300Sgjelinek zperror(gettext("could not set new zonepath"), B_TRUE); 41991300Sgjelinek goto done; 42001300Sgjelinek } 42011300Sgjelinek 42021300Sgjelinek if ((err = zonecfg_save(handle)) != Z_OK) { 42031300Sgjelinek errno = err; 42041300Sgjelinek zperror(gettext("zonecfg save failed"), B_TRUE); 42051300Sgjelinek goto done; 42061300Sgjelinek } 42071300Sgjelinek 42081300Sgjelinek revert = B_FALSE; 42091300Sgjelinek 42101300Sgjelinek done: 42111300Sgjelinek zonecfg_fini_handle(handle); 42127089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 42131300Sgjelinek 42141300Sgjelinek /* 42151867Sgjelinek * Clean up the file system based on how things went. We either 42161300Sgjelinek * clean up the new zonepath if the operation failed for some reason 42171300Sgjelinek * or we clean up the old zonepath if everything is ok. 42181300Sgjelinek */ 42191300Sgjelinek if (revert) { 42201300Sgjelinek /* The zonecfg update failed, cleanup the new zonepath. */ 42211867Sgjelinek if (is_zfs) { 42221867Sgjelinek if (move_zfs(new_zonepath, zonepath) == Z_ERR) { 42231867Sgjelinek (void) fprintf(stderr, gettext("could not " 42241867Sgjelinek "restore zonepath, the zfs mountpoint is " 42251867Sgjelinek "set as:\n%s\n"), new_zonepath); 42261867Sgjelinek /* 42271867Sgjelinek * err is already != Z_OK since we're reverting 42281867Sgjelinek */ 42291867Sgjelinek } 42301867Sgjelinek 42311867Sgjelinek } else if (fast) { 42321300Sgjelinek if (rename(new_zonepath, zonepath) != 0) { 42331300Sgjelinek zperror(gettext("could not restore zonepath"), 42341300Sgjelinek B_FALSE); 42351300Sgjelinek /* 42361300Sgjelinek * err is already != Z_OK since we're reverting 42371300Sgjelinek */ 42381300Sgjelinek } 42391300Sgjelinek } else { 42401300Sgjelinek (void) printf(gettext("Cleaning up zonepath %s..."), 42411300Sgjelinek new_zonepath); 42421300Sgjelinek (void) fflush(stdout); 42431867Sgjelinek err = cleanup_zonepath(new_zonepath, B_TRUE); 42441300Sgjelinek (void) printf("\n"); 42451300Sgjelinek 42461607Sgjelinek if (err != Z_OK) { 42471300Sgjelinek errno = err; 42481300Sgjelinek zperror(gettext("could not remove new " 42491300Sgjelinek "zonepath"), B_TRUE); 42501300Sgjelinek } else { 42511300Sgjelinek /* 42521300Sgjelinek * Because we're reverting we know the mainline 42531300Sgjelinek * code failed but we just reused the err 42541300Sgjelinek * variable so we reset it back to Z_ERR. 42551300Sgjelinek */ 42561300Sgjelinek err = Z_ERR; 42571300Sgjelinek } 42581300Sgjelinek } 42591300Sgjelinek 42601300Sgjelinek } else { 42611300Sgjelinek /* The move was successful, cleanup the old zonepath. */ 42621867Sgjelinek if (!is_zfs && !fast) { 42631300Sgjelinek (void) printf( 42641300Sgjelinek gettext("Cleaning up zonepath %s..."), zonepath); 42651300Sgjelinek (void) fflush(stdout); 42661867Sgjelinek err = cleanup_zonepath(zonepath, B_TRUE); 42671300Sgjelinek (void) printf("\n"); 42681300Sgjelinek 42691607Sgjelinek if (err != Z_OK) { 42701300Sgjelinek errno = err; 42711300Sgjelinek zperror(gettext("could not remove zonepath"), 42721300Sgjelinek B_TRUE); 42731300Sgjelinek } 42741300Sgjelinek } 42751300Sgjelinek } 42761300Sgjelinek 42771300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 42781300Sgjelinek } 42791300Sgjelinek 42807089Sgjelinek /* ARGSUSED */ 42811507Sgjelinek static int 42821507Sgjelinek detach_func(int argc, char *argv[]) 42831507Sgjelinek { 42841507Sgjelinek int lockfd; 42851507Sgjelinek int err, arg; 42861507Sgjelinek char zonepath[MAXPATHLEN]; 42874785Sgjelinek char cmdbuf[MAXPATHLEN]; 42887089Sgjelinek char precmdbuf[MAXPATHLEN]; 42891507Sgjelinek zone_dochandle_t handle; 42902078Sgjelinek boolean_t execute = B_TRUE; 42917089Sgjelinek boolean_t brand_help = B_FALSE; 42924785Sgjelinek brand_handle_t bh = NULL; 42937089Sgjelinek int status; 42941507Sgjelinek 42951507Sgjelinek if (zonecfg_in_alt_root()) { 42961507Sgjelinek zerror(gettext("cannot detach zone in alternate root")); 42971507Sgjelinek return (Z_ERR); 42981507Sgjelinek } 42991507Sgjelinek 43007089Sgjelinek /* Check the argv string for args we handle internally */ 43011507Sgjelinek optind = 0; 43027089Sgjelinek opterr = 0; 43037089Sgjelinek while ((arg = getopt(argc, argv, "?n")) != EOF) { 43041507Sgjelinek switch (arg) { 43051507Sgjelinek case '?': 43067089Sgjelinek if (optopt == '?') { 43077089Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 43087089Sgjelinek brand_help = B_TRUE; 43097089Sgjelinek } 43107089Sgjelinek /* Ignore unknown options - may be brand specific. */ 43117089Sgjelinek break; 43122078Sgjelinek case 'n': 43132078Sgjelinek execute = B_FALSE; 43142078Sgjelinek break; 43151507Sgjelinek default: 43167089Sgjelinek /* Ignore unknown options - may be brand specific. */ 43177089Sgjelinek break; 43181507Sgjelinek } 43191507Sgjelinek } 43202712Snn35248 43217089Sgjelinek if (brand_help) 43227089Sgjelinek execute = B_FALSE; 43237089Sgjelinek 43242078Sgjelinek if (execute) { 43252712Snn35248 if (sanity_check(target_zone, CMD_DETACH, B_FALSE, B_TRUE, 43262712Snn35248 B_FALSE) != Z_OK) 43272078Sgjelinek return (Z_ERR); 43283339Szt129084 if (verify_details(CMD_DETACH, argv) != Z_OK) 43292078Sgjelinek return (Z_ERR); 43302078Sgjelinek } else { 43312078Sgjelinek /* 43322078Sgjelinek * We want a dry-run to work for a non-privileged user so we 43332078Sgjelinek * only do minimal validation. 43342078Sgjelinek */ 43352078Sgjelinek if (target_zone == NULL) { 43362078Sgjelinek zerror(gettext("no zone specified")); 43372078Sgjelinek return (Z_ERR); 43382078Sgjelinek } 43392078Sgjelinek 43402078Sgjelinek if (strcmp(target_zone, GLOBAL_ZONENAME) == 0) { 43412078Sgjelinek zerror(gettext("%s operation is invalid for the " 43422078Sgjelinek "global zone."), cmd_to_str(CMD_DETACH)); 43432078Sgjelinek return (Z_ERR); 43442078Sgjelinek } 43452078Sgjelinek } 43461507Sgjelinek 43471507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 43481507Sgjelinek != Z_OK) { 43491507Sgjelinek errno = err; 43501507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 43511507Sgjelinek return (Z_ERR); 43521507Sgjelinek } 43531507Sgjelinek 43541507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 43551507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 43561507Sgjelinek return (Z_ERR); 43571507Sgjelinek } 43581507Sgjelinek 43591507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 43601507Sgjelinek errno = err; 43611507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 43621507Sgjelinek zonecfg_fini_handle(handle); 43631507Sgjelinek return (Z_ERR); 43641507Sgjelinek } 43651507Sgjelinek 43667089Sgjelinek /* Fetch the detach and predetach hooks from the brand configuration. */ 43674785Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 43684785Sgjelinek zerror(gettext("missing or invalid brand")); 43694785Sgjelinek return (Z_ERR); 43704785Sgjelinek } 43714785Sgjelinek 43727089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_detach, target_zone, 43737089Sgjelinek zonepath) != Z_OK) { 43747089Sgjelinek zerror("invalid brand configuration: missing detach resource"); 43757089Sgjelinek brand_close(bh); 43767089Sgjelinek return (Z_ERR); 43777089Sgjelinek } 43787089Sgjelinek 43797089Sgjelinek if (get_hook(bh, precmdbuf, sizeof (precmdbuf), brand_get_predetach, 43807089Sgjelinek target_zone, zonepath) != Z_OK) { 43814785Sgjelinek zerror("invalid brand configuration: missing predetach " 43824785Sgjelinek "resource"); 43834785Sgjelinek brand_close(bh); 43844785Sgjelinek return (Z_ERR); 43854785Sgjelinek } 43864785Sgjelinek brand_close(bh); 43874785Sgjelinek 43887089Sgjelinek /* Append all options to predetach hook. */ 43897089Sgjelinek if (addoptions(precmdbuf, argv, sizeof (precmdbuf)) != Z_OK) 43907089Sgjelinek return (Z_ERR); 43917089Sgjelinek 43927089Sgjelinek /* Append all options to detach hook. */ 43937089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) 43947089Sgjelinek return (Z_ERR); 43957089Sgjelinek 43967089Sgjelinek if (execute && zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 43971507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 43981507Sgjelinek "zoneadm"); 43991507Sgjelinek zonecfg_fini_handle(handle); 44001507Sgjelinek return (Z_ERR); 44011507Sgjelinek } 44021507Sgjelinek 44037089Sgjelinek /* If we have a brand predetach hook, run it. */ 44047089Sgjelinek if (!brand_help && precmdbuf[0] != '\0') { 44057089Sgjelinek status = do_subproc(precmdbuf); 44067089Sgjelinek if (subproc_status(gettext("brand-specific predetach"), 44077089Sgjelinek status, B_FALSE) != ZONE_SUBPROC_OK) { 44087089Sgjelinek 44097089Sgjelinek if (execute) 44107089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 44117089Sgjelinek return (Z_ERR); 44127089Sgjelinek } 44137089Sgjelinek } 44147089Sgjelinek 44157089Sgjelinek if (cmdbuf[0] != '\0') { 44167089Sgjelinek /* Run the detach hook */ 44177089Sgjelinek status = do_subproc_interactive(cmdbuf); 44187089Sgjelinek if ((status = subproc_status(gettext("brand-specific detach"), 44197089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 44207089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 44217089Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 44227089Sgjelinek 44237089Sgjelinek if (execute) 44247089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 44257089Sgjelinek 44267089Sgjelinek return (Z_ERR); 44277089Sgjelinek } 44287089Sgjelinek 44297089Sgjelinek } else { 44307089Sgjelinek /* If just help, we're done since there is no brand help. */ 44317089Sgjelinek if (brand_help) 44327089Sgjelinek return (Z_OK); 44337089Sgjelinek 44347089Sgjelinek /* 44357089Sgjelinek * Run the built-in detach support. Just generate a simple 44367089Sgjelinek * zone definition XML file and detach. 44377089Sgjelinek */ 44387089Sgjelinek 44397089Sgjelinek /* Don't detach the zone if anything is still mounted there */ 44407089Sgjelinek if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) { 44417089Sgjelinek (void) fprintf(stderr, gettext("These file systems are " 44427089Sgjelinek "mounted on subdirectories of %s.\n"), zonepath); 44437089Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 44447089Sgjelinek err = ZONE_SUBPROC_NOTCOMPLETE; 44457089Sgjelinek goto done; 44467089Sgjelinek } 44477089Sgjelinek 44487089Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 44497089Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 44507089Sgjelinek err = ZONE_SUBPROC_NOTCOMPLETE; 44517089Sgjelinek goto done; 44527089Sgjelinek } 44537089Sgjelinek 44547089Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 44557089Sgjelinek errno = err; 44567089Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 44577089Sgjelinek zonecfg_fini_handle(handle); 44587089Sgjelinek goto done; 44597089Sgjelinek } 44607089Sgjelinek 44617089Sgjelinek if ((err = zonecfg_detach_save(handle, 44627089Sgjelinek (execute ? 0 : ZONE_DRY_RUN))) != Z_OK) { 44637089Sgjelinek errno = err; 44647089Sgjelinek zperror(gettext("saving the detach manifest failed"), 44657089Sgjelinek B_TRUE); 44667089Sgjelinek goto done; 44677089Sgjelinek } 44687089Sgjelinek 44697089Sgjelinek zonecfg_fini_handle(handle); 44701507Sgjelinek } 44711507Sgjelinek 44722078Sgjelinek /* 44732078Sgjelinek * Set the zone state back to configured unless we are running with the 44742078Sgjelinek * no-execute option. 44752078Sgjelinek */ 44762078Sgjelinek if (execute && (err = zone_set_state(target_zone, 44772078Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 44781507Sgjelinek errno = err; 44791507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 44801507Sgjelinek } 44811507Sgjelinek 44821507Sgjelinek done: 44831507Sgjelinek zonecfg_fini_handle(handle); 44842078Sgjelinek if (execute) 44857089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 44861507Sgjelinek 44871507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 44881507Sgjelinek } 44891507Sgjelinek 44901507Sgjelinek /* 44917089Sgjelinek * Determine the brand when doing a dry-run attach. The zone does not have to 44927089Sgjelinek * exist, so we have to read the incoming manifest to determine the zone's 44937089Sgjelinek * brand. 44947089Sgjelinek * 44957089Sgjelinek * Because the manifest has to be processed twice; once to determine the brand 44967089Sgjelinek * and once to do the brand-specific attach logic, we always read it into a tmp 44977089Sgjelinek * file. This handles the manifest coming from stdin or a regular file. The 44987089Sgjelinek * tmpname parameter returns the name of the temporary file that the manifest 44997089Sgjelinek * was read into. 45001507Sgjelinek */ 45011507Sgjelinek static int 45027089Sgjelinek dryrun_get_brand(char *manifest_path, char *tmpname, int size) 45032078Sgjelinek { 45042078Sgjelinek int fd; 45052078Sgjelinek int err; 45067089Sgjelinek int res = Z_OK; 45072078Sgjelinek zone_dochandle_t local_handle; 45082078Sgjelinek zone_dochandle_t rem_handle = NULL; 45097089Sgjelinek int len; 45107089Sgjelinek int ofd; 45117089Sgjelinek char buf[512]; 45122078Sgjelinek 45132078Sgjelinek if (strcmp(manifest_path, "-") == 0) { 45147089Sgjelinek fd = STDIN_FILENO; 45157089Sgjelinek } else { 45167089Sgjelinek if ((fd = open(manifest_path, O_RDONLY)) < 0) { 45177089Sgjelinek if (getcwd(buf, sizeof (buf)) == NULL) 45187089Sgjelinek (void) strlcpy(buf, "/", sizeof (buf)); 45197089Sgjelinek zerror(gettext("could not open manifest path %s%s: %s"), 45207089Sgjelinek (*manifest_path == '/' ? "" : buf), manifest_path, 45217089Sgjelinek strerror(errno)); 45227089Sgjelinek return (Z_ERR); 45237089Sgjelinek } 45247089Sgjelinek } 45257089Sgjelinek 45267089Sgjelinek (void) snprintf(tmpname, size, "/var/run/zone.%d", getpid()); 45277089Sgjelinek 45287089Sgjelinek if ((ofd = open(tmpname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { 45297089Sgjelinek zperror(gettext("could not save manifest"), B_FALSE); 45307089Sgjelinek (void) close(fd); 45317089Sgjelinek return (Z_ERR); 45327089Sgjelinek } 45337089Sgjelinek 45347089Sgjelinek while ((len = read(fd, buf, sizeof (buf))) > 0) { 45357089Sgjelinek if (write(ofd, buf, len) == -1) { 45367089Sgjelinek zperror(gettext("could not save manifest"), B_FALSE); 45377089Sgjelinek (void) close(ofd); 45387089Sgjelinek (void) close(fd); 45397089Sgjelinek return (Z_ERR); 45407089Sgjelinek } 45417089Sgjelinek } 45427089Sgjelinek 45437089Sgjelinek if (close(ofd) != 0) { 45447089Sgjelinek zperror(gettext("could not save manifest"), B_FALSE); 45457089Sgjelinek (void) close(fd); 45467089Sgjelinek return (Z_ERR); 45477089Sgjelinek } 45487089Sgjelinek 45497089Sgjelinek (void) close(fd); 45507089Sgjelinek 45517089Sgjelinek if ((fd = open(tmpname, O_RDONLY)) < 0) { 45522078Sgjelinek zperror(gettext("could not open manifest path"), B_FALSE); 45532078Sgjelinek return (Z_ERR); 45542078Sgjelinek } 45552078Sgjelinek 45562078Sgjelinek if ((local_handle = zonecfg_init_handle()) == NULL) { 45572078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 45582078Sgjelinek res = Z_ERR; 45592078Sgjelinek goto done; 45602078Sgjelinek } 45612078Sgjelinek 45622078Sgjelinek if ((rem_handle = zonecfg_init_handle()) == NULL) { 45632078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 45642078Sgjelinek res = Z_ERR; 45652078Sgjelinek goto done; 45662078Sgjelinek } 45672078Sgjelinek 45682078Sgjelinek if ((err = zonecfg_attach_manifest(fd, local_handle, rem_handle)) 45692078Sgjelinek != Z_OK) { 45703686Sgjelinek res = Z_ERR; 45713686Sgjelinek 45723686Sgjelinek if (err == Z_INVALID_DOCUMENT) { 45733686Sgjelinek struct stat st; 45743686Sgjelinek char buf[6]; 45753686Sgjelinek 45763686Sgjelinek if (strcmp(manifest_path, "-") == 0) { 45773686Sgjelinek zerror(gettext("Input is not a valid XML " 45783686Sgjelinek "file")); 45793686Sgjelinek goto done; 45803686Sgjelinek } 45813686Sgjelinek 45823686Sgjelinek if (fstat(fd, &st) == -1 || !S_ISREG(st.st_mode)) { 45833686Sgjelinek zerror(gettext("%s is not an XML file"), 45843686Sgjelinek manifest_path); 45853686Sgjelinek goto done; 45863686Sgjelinek } 45873686Sgjelinek 45883686Sgjelinek bzero(buf, sizeof (buf)); 45893686Sgjelinek (void) lseek(fd, 0L, SEEK_SET); 45903686Sgjelinek if (read(fd, buf, sizeof (buf) - 1) < 0 || 45913686Sgjelinek strncmp(buf, "<?xml", 5) != 0) 45923686Sgjelinek zerror(gettext("%s is not an XML file"), 45933686Sgjelinek manifest_path); 45943686Sgjelinek else 45953686Sgjelinek zerror(gettext("Cannot attach to an earlier " 45963686Sgjelinek "release of the operating system")); 45973686Sgjelinek } else { 45982078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 45993686Sgjelinek } 46002078Sgjelinek goto done; 46012078Sgjelinek } 46022078Sgjelinek 46037089Sgjelinek /* Retrieve remote handle brand type. */ 46043172Sgjelinek if (zonecfg_get_brand(rem_handle, target_brand, sizeof (target_brand)) 46053172Sgjelinek != Z_OK) { 46063172Sgjelinek zerror(gettext("missing or invalid brand")); 46073172Sgjelinek exit(Z_ERR); 46083172Sgjelinek } 46092078Sgjelinek 46102078Sgjelinek done: 46112078Sgjelinek zonecfg_fini_handle(local_handle); 46122078Sgjelinek zonecfg_fini_handle(rem_handle); 46137089Sgjelinek (void) close(fd); 46142078Sgjelinek 46152078Sgjelinek return ((res == Z_OK) ? Z_OK : Z_ERR); 46162078Sgjelinek } 46172078Sgjelinek 46185829Sgjelinek /* ARGSUSED */ 46191507Sgjelinek static int 46201507Sgjelinek attach_func(int argc, char *argv[]) 46211507Sgjelinek { 46221507Sgjelinek int lockfd; 46231507Sgjelinek int err, arg; 46241507Sgjelinek boolean_t force = B_FALSE; 46251507Sgjelinek zone_dochandle_t handle; 46261507Sgjelinek char zonepath[MAXPATHLEN]; 46274785Sgjelinek char cmdbuf[MAXPATHLEN]; 46287089Sgjelinek char postcmdbuf[MAXPATHLEN]; 46292078Sgjelinek boolean_t execute = B_TRUE; 46307089Sgjelinek boolean_t brand_help = B_FALSE; 46312078Sgjelinek char *manifest_path; 46327089Sgjelinek char tmpmanifest[80]; 46337089Sgjelinek int manifest_pos; 46344785Sgjelinek brand_handle_t bh = NULL; 46357089Sgjelinek int status; 46361507Sgjelinek 46371507Sgjelinek if (zonecfg_in_alt_root()) { 46381507Sgjelinek zerror(gettext("cannot attach zone in alternate root")); 46391507Sgjelinek return (Z_ERR); 46401507Sgjelinek } 46411507Sgjelinek 46427089Sgjelinek /* Check the argv string for args we handle internally */ 46431507Sgjelinek optind = 0; 46447089Sgjelinek opterr = 0; 46457089Sgjelinek while ((arg = getopt(argc, argv, "?Fn:")) != EOF) { 46461507Sgjelinek switch (arg) { 46471507Sgjelinek case '?': 46487089Sgjelinek if (optopt == '?') { 46497089Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 46507089Sgjelinek brand_help = B_TRUE; 46517089Sgjelinek } 46527089Sgjelinek /* Ignore unknown options - may be brand specific. */ 46537089Sgjelinek break; 46541507Sgjelinek case 'F': 46551507Sgjelinek force = B_TRUE; 46561507Sgjelinek break; 46572078Sgjelinek case 'n': 46582078Sgjelinek execute = B_FALSE; 46592078Sgjelinek manifest_path = optarg; 46607089Sgjelinek manifest_pos = optind - 1; 46615829Sgjelinek break; 46621507Sgjelinek default: 46637089Sgjelinek /* Ignore unknown options - may be brand specific. */ 46647089Sgjelinek break; 46651507Sgjelinek } 46661507Sgjelinek } 46672078Sgjelinek 46687089Sgjelinek if (brand_help) { 46697089Sgjelinek force = B_FALSE; 46707089Sgjelinek execute = B_TRUE; 46717089Sgjelinek } 46727089Sgjelinek 46737089Sgjelinek /* dry-run and force flags are mutually exclusive */ 46747089Sgjelinek if (!execute && force) { 46757089Sgjelinek zerror(gettext("-F and -n flags are mutually exclusive")); 46765829Sgjelinek return (Z_ERR); 46775829Sgjelinek } 46785829Sgjelinek 46792078Sgjelinek /* 46807089Sgjelinek * If the no-execute option was specified, we don't do validation and 46817089Sgjelinek * need to figure out the brand, since there is no zone required to be 46822078Sgjelinek * configured for this option. 46832078Sgjelinek */ 46847089Sgjelinek if (execute) { 46857089Sgjelinek if (!brand_help) { 46867089Sgjelinek if (sanity_check(target_zone, CMD_ATTACH, B_FALSE, 46877089Sgjelinek B_TRUE, B_FALSE) != Z_OK) 46887089Sgjelinek return (Z_ERR); 46897089Sgjelinek if (verify_details(CMD_ATTACH, argv) != Z_OK) 46907089Sgjelinek return (Z_ERR); 46917089Sgjelinek } 46927089Sgjelinek 46937089Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, 46947089Sgjelinek sizeof (zonepath))) != Z_OK) { 46957089Sgjelinek errno = err; 46967089Sgjelinek zperror2(target_zone, 46977089Sgjelinek gettext("could not get zone path")); 46987089Sgjelinek return (Z_ERR); 46997089Sgjelinek } 47007089Sgjelinek 47017089Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 47027089Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 47037089Sgjelinek return (Z_ERR); 47047089Sgjelinek } 47057089Sgjelinek 47067089Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 47077089Sgjelinek errno = err; 47087089Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 47097089Sgjelinek zonecfg_fini_handle(handle); 47107089Sgjelinek return (Z_ERR); 47117089Sgjelinek } 47127089Sgjelinek 47137089Sgjelinek } else { 47147089Sgjelinek if (dryrun_get_brand(manifest_path, tmpmanifest, 47157089Sgjelinek sizeof (tmpmanifest)) != Z_OK) 47167089Sgjelinek return (Z_ERR); 47177089Sgjelinek 47187089Sgjelinek argv[manifest_pos] = tmpmanifest; 47197089Sgjelinek target_zone = "-"; 47207089Sgjelinek (void) strlcpy(zonepath, "-", sizeof (zonepath)); 47217089Sgjelinek 47227089Sgjelinek /* Run the brand's verify_adm hook. */ 47237089Sgjelinek if (verify_brand(NULL, CMD_ATTACH, argv) != Z_OK) 47247089Sgjelinek return (Z_ERR); 47257089Sgjelinek } 47267089Sgjelinek 47277089Sgjelinek /* 47287089Sgjelinek * Fetch the attach and postattach hooks from the brand configuration. 47297089Sgjelinek */ 47304785Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 47314785Sgjelinek zerror(gettext("missing or invalid brand")); 47324785Sgjelinek return (Z_ERR); 47334785Sgjelinek } 47344785Sgjelinek 47357089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_attach, target_zone, 47367089Sgjelinek zonepath) != Z_OK) { 47377089Sgjelinek zerror("invalid brand configuration: missing attach resource"); 47387089Sgjelinek brand_close(bh); 47397089Sgjelinek return (Z_ERR); 47407089Sgjelinek } 47417089Sgjelinek 47427089Sgjelinek if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postattach, 47437089Sgjelinek target_zone, zonepath) != Z_OK) { 47444785Sgjelinek zerror("invalid brand configuration: missing postattach " 47454785Sgjelinek "resource"); 47464785Sgjelinek brand_close(bh); 47474785Sgjelinek return (Z_ERR); 47484785Sgjelinek } 47494785Sgjelinek brand_close(bh); 47504785Sgjelinek 47517089Sgjelinek /* Append all options to attach hook. */ 47527089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) 47537089Sgjelinek return (Z_ERR); 47547089Sgjelinek 47557089Sgjelinek /* Append all options to postattach hook. */ 47567089Sgjelinek if (addoptions(postcmdbuf, argv, sizeof (postcmdbuf)) != Z_OK) 47577089Sgjelinek return (Z_ERR); 47587089Sgjelinek 47597089Sgjelinek if (execute && !brand_help) { 47607089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 47617089Sgjelinek zerror(gettext("another %s may have an operation in " 47627089Sgjelinek "progress."), "zoneadm"); 47637089Sgjelinek zonecfg_fini_handle(handle); 47644785Sgjelinek return (Z_ERR); 47654785Sgjelinek } 47664785Sgjelinek } 47674785Sgjelinek 47681507Sgjelinek if (force) 47691507Sgjelinek goto done; 47707089Sgjelinek 47717089Sgjelinek if (cmdbuf[0] != '\0') { 47727089Sgjelinek /* Run the attach hook */ 47737089Sgjelinek status = do_subproc_interactive(cmdbuf); 47747089Sgjelinek if ((status = subproc_status(gettext("brand-specific attach"), 47757089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) { 47767089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 47777089Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 47787089Sgjelinek 47797089Sgjelinek if (execute && !brand_help) 47807089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 47817089Sgjelinek 47827089Sgjelinek return (Z_ERR); 47833777Sgjelinek } 47847089Sgjelinek 47851507Sgjelinek } 47861507Sgjelinek 47872712Snn35248 /* 47887089Sgjelinek * Else run the built-in attach support. 47897089Sgjelinek * This is a no-op since there is nothing to validate. 47905829Sgjelinek */ 47917089Sgjelinek 47927089Sgjelinek /* If dry-run or help, then we're done. */ 47937089Sgjelinek if (!execute || brand_help) { 47947089Sgjelinek if (!execute) 47957089Sgjelinek (void) unlink(tmpmanifest); 47967089Sgjelinek return (Z_OK); 47977089Sgjelinek } 47987089Sgjelinek 47997089Sgjelinek done: 48001507Sgjelinek zonecfg_rm_detached(handle, force); 48011507Sgjelinek 48021507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 48031507Sgjelinek errno = err; 48041507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 48051507Sgjelinek } 48061507Sgjelinek 48071507Sgjelinek zonecfg_fini_handle(handle); 48087089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 48091507Sgjelinek 48104785Sgjelinek /* If we have a brand postattach hook, run it. */ 48117089Sgjelinek if (err == Z_OK && !force && postcmdbuf[0] != '\0') { 48127089Sgjelinek status = do_subproc(postcmdbuf); 48134785Sgjelinek if (subproc_status(gettext("brand-specific postattach"), 48144785Sgjelinek status, B_FALSE) != ZONE_SUBPROC_OK) { 48154785Sgjelinek if ((err = zone_set_state(target_zone, 48164785Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 48174785Sgjelinek errno = err; 48184785Sgjelinek zperror(gettext("could not reset state"), 48194785Sgjelinek B_TRUE); 48204785Sgjelinek } 48214785Sgjelinek return (Z_ERR); 48224785Sgjelinek } 48234785Sgjelinek } 48244785Sgjelinek 48251507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 48261507Sgjelinek } 48271507Sgjelinek 48281300Sgjelinek /* 48290Sstevel@tonic-gate * On input, TRUE => yes, FALSE => no. 48300Sstevel@tonic-gate * On return, TRUE => 1, FALSE => 0, could not ask => -1. 48310Sstevel@tonic-gate */ 48320Sstevel@tonic-gate 48330Sstevel@tonic-gate static int 48340Sstevel@tonic-gate ask_yesno(boolean_t default_answer, const char *question) 48350Sstevel@tonic-gate { 48360Sstevel@tonic-gate char line[64]; /* should be large enough to answer yes or no */ 48370Sstevel@tonic-gate 48380Sstevel@tonic-gate if (!isatty(STDIN_FILENO)) 48390Sstevel@tonic-gate return (-1); 48400Sstevel@tonic-gate for (;;) { 48410Sstevel@tonic-gate (void) printf("%s (%s)? ", question, 48420Sstevel@tonic-gate default_answer ? "[y]/n" : "y/[n]"); 48430Sstevel@tonic-gate if (fgets(line, sizeof (line), stdin) == NULL || 48440Sstevel@tonic-gate line[0] == '\n') 48450Sstevel@tonic-gate return (default_answer ? 1 : 0); 48460Sstevel@tonic-gate if (tolower(line[0]) == 'y') 48470Sstevel@tonic-gate return (1); 48480Sstevel@tonic-gate if (tolower(line[0]) == 'n') 48490Sstevel@tonic-gate return (0); 48500Sstevel@tonic-gate } 48510Sstevel@tonic-gate } 48520Sstevel@tonic-gate 48537089Sgjelinek /* ARGSUSED */ 48540Sstevel@tonic-gate static int 48550Sstevel@tonic-gate uninstall_func(int argc, char *argv[]) 48560Sstevel@tonic-gate { 48570Sstevel@tonic-gate char line[ZONENAME_MAX + 128]; /* Enough for "Are you sure ..." */ 48581867Sgjelinek char rootpath[MAXPATHLEN], zonepath[MAXPATHLEN]; 48594785Sgjelinek char cmdbuf[MAXPATHLEN]; 48607089Sgjelinek char precmdbuf[MAXPATHLEN]; 48610Sstevel@tonic-gate boolean_t force = B_FALSE; 48620Sstevel@tonic-gate int lockfd, answer; 48630Sstevel@tonic-gate int err, arg; 48647089Sgjelinek boolean_t brand_help = B_FALSE; 48654785Sgjelinek brand_handle_t bh = NULL; 48667089Sgjelinek int status; 48670Sstevel@tonic-gate 4868766Scarlsonj if (zonecfg_in_alt_root()) { 4869766Scarlsonj zerror(gettext("cannot uninstall zone in alternate root")); 4870766Scarlsonj return (Z_ERR); 4871766Scarlsonj } 4872766Scarlsonj 48737089Sgjelinek /* Check the argv string for args we handle internally */ 48740Sstevel@tonic-gate optind = 0; 48757089Sgjelinek opterr = 0; 48760Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?F")) != EOF) { 48770Sstevel@tonic-gate switch (arg) { 48780Sstevel@tonic-gate case '?': 48797089Sgjelinek if (optopt == '?') { 48807089Sgjelinek sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 48817089Sgjelinek brand_help = B_TRUE; 48827089Sgjelinek } 48837089Sgjelinek /* Ignore unknown options - may be brand specific. */ 48847089Sgjelinek break; 48850Sstevel@tonic-gate case 'F': 48860Sstevel@tonic-gate force = B_TRUE; 48870Sstevel@tonic-gate break; 48880Sstevel@tonic-gate default: 48897089Sgjelinek /* Ignore unknown options - may be brand specific. */ 48907089Sgjelinek break; 48910Sstevel@tonic-gate } 48920Sstevel@tonic-gate } 48937089Sgjelinek 48947089Sgjelinek if (!brand_help) { 48957089Sgjelinek if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE, 48967089Sgjelinek B_FALSE) != Z_OK) 48977089Sgjelinek return (Z_ERR); 48987089Sgjelinek 48997089Sgjelinek /* 49007089Sgjelinek * Invoke brand-specific handler. 49017089Sgjelinek */ 49027089Sgjelinek if (invoke_brand_handler(CMD_UNINSTALL, argv) != Z_OK) 49030Sstevel@tonic-gate return (Z_ERR); 49047089Sgjelinek 49057089Sgjelinek if (!force) { 49067089Sgjelinek (void) snprintf(line, sizeof (line), 49077089Sgjelinek gettext("Are you sure you want to %s zone %s"), 49087089Sgjelinek cmd_to_str(CMD_UNINSTALL), target_zone); 49097089Sgjelinek if ((answer = ask_yesno(B_FALSE, line)) == 0) { 49107089Sgjelinek return (Z_OK); 49117089Sgjelinek } else if (answer == -1) { 49127089Sgjelinek zerror(gettext("Input not from terminal and -F " 49137089Sgjelinek "not specified: %s not done."), 49147089Sgjelinek cmd_to_str(CMD_UNINSTALL)); 49157089Sgjelinek return (Z_ERR); 49167089Sgjelinek } 49170Sstevel@tonic-gate } 49180Sstevel@tonic-gate } 49190Sstevel@tonic-gate 49201867Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, 49211867Sgjelinek sizeof (zonepath))) != Z_OK) { 49220Sstevel@tonic-gate errno = err; 49230Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 49240Sstevel@tonic-gate return (Z_ERR); 49250Sstevel@tonic-gate } 49260Sstevel@tonic-gate 49270Sstevel@tonic-gate /* 49287089Sgjelinek * Fetch the uninstall and preuninstall hooks from the brand 49297089Sgjelinek * configuration. 49300Sstevel@tonic-gate */ 49314785Sgjelinek if ((bh = brand_open(target_brand)) == NULL) { 49324785Sgjelinek zerror(gettext("missing or invalid brand")); 49334785Sgjelinek return (Z_ERR); 49344785Sgjelinek } 49354785Sgjelinek 49367089Sgjelinek if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_uninstall, 49377089Sgjelinek target_zone, zonepath) != Z_OK) { 49387089Sgjelinek zerror("invalid brand configuration: missing uninstall " 49397089Sgjelinek "resource"); 49407089Sgjelinek brand_close(bh); 49417089Sgjelinek return (Z_ERR); 49427089Sgjelinek } 49437089Sgjelinek 49447089Sgjelinek if (get_hook(bh, precmdbuf, sizeof (precmdbuf), brand_get_preuninstall, 49457089Sgjelinek target_zone, zonepath) != Z_OK) { 49464785Sgjelinek zerror("invalid brand configuration: missing preuninstall " 49474785Sgjelinek "resource"); 49484785Sgjelinek brand_close(bh); 49494785Sgjelinek return (Z_ERR); 49504785Sgjelinek } 49514785Sgjelinek brand_close(bh); 49524785Sgjelinek 49537089Sgjelinek /* Append all options to preuninstall hook. */ 49547089Sgjelinek if (addoptions(precmdbuf, argv, sizeof (precmdbuf)) != Z_OK) 49557089Sgjelinek return (Z_ERR); 49567089Sgjelinek 49577089Sgjelinek /* Append all options to uninstall hook. */ 49587089Sgjelinek if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) 49597089Sgjelinek return (Z_ERR); 49607089Sgjelinek 49617089Sgjelinek if (!brand_help) { 49627089Sgjelinek if ((err = zone_get_rootpath(target_zone, rootpath, 49637089Sgjelinek sizeof (rootpath))) != Z_OK) { 49647089Sgjelinek errno = err; 49657089Sgjelinek zperror2(target_zone, gettext("could not get root " 49667089Sgjelinek "path")); 49674785Sgjelinek return (Z_ERR); 49684785Sgjelinek } 49694785Sgjelinek 49707089Sgjelinek /* 49717089Sgjelinek * If there seems to be a zoneadmd running for this zone, call 49727089Sgjelinek * it to tell it that an uninstall is happening; if all goes 49737089Sgjelinek * well it will then shut itself down. 49747089Sgjelinek */ 49757089Sgjelinek if (zonecfg_ping_zoneadmd(target_zone) == Z_OK) { 49767089Sgjelinek zone_cmd_arg_t zarg; 49777089Sgjelinek zarg.cmd = Z_NOTE_UNINSTALLING; 49787089Sgjelinek /* we don't care too much if this fails, just plow on */ 49797089Sgjelinek (void) zonecfg_call_zoneadmd(target_zone, &zarg, locale, 49807089Sgjelinek B_TRUE); 49817089Sgjelinek } 49827089Sgjelinek 49837089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 49847089Sgjelinek zerror(gettext("another %s may have an operation in " 49857089Sgjelinek "progress."), "zoneadm"); 49867089Sgjelinek return (Z_ERR); 49877089Sgjelinek } 49887089Sgjelinek 49897089Sgjelinek /* Don't uninstall the zone if anything is mounted there */ 49907089Sgjelinek err = zonecfg_find_mounts(rootpath, NULL, NULL); 49917089Sgjelinek if (err) { 49927089Sgjelinek zerror(gettext("These file systems are mounted on " 49937089Sgjelinek "subdirectories of %s.\n"), rootpath); 49947089Sgjelinek (void) zonecfg_find_mounts(rootpath, zfm_print, NULL); 49957089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 49967089Sgjelinek return (Z_ERR); 49977089Sgjelinek } 49987089Sgjelinek } 49997089Sgjelinek 50007089Sgjelinek /* If we have a brand preuninstall hook, run it. */ 50017089Sgjelinek if (!brand_help && precmdbuf[0] != '\0') { 50024785Sgjelinek status = do_subproc(cmdbuf); 50034785Sgjelinek if (subproc_status(gettext("brand-specific preuninstall"), 50044785Sgjelinek status, B_FALSE) != ZONE_SUBPROC_OK) { 50057089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50064785Sgjelinek return (Z_ERR); 50074785Sgjelinek } 50084785Sgjelinek } 50094785Sgjelinek 50107089Sgjelinek if (!brand_help) { 50117089Sgjelinek err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 50127089Sgjelinek if (err != Z_OK) { 50137089Sgjelinek errno = err; 50147089Sgjelinek zperror2(target_zone, gettext("could not set state")); 50157089Sgjelinek goto bad; 50167089Sgjelinek } 50177089Sgjelinek } 50187089Sgjelinek 50197089Sgjelinek /* 50207089Sgjelinek * If there is a brand uninstall hook, use it, otherwise use the 50217089Sgjelinek * built-in uninstall code. 50227089Sgjelinek */ 50237089Sgjelinek if (cmdbuf[0] != '\0') { 50247089Sgjelinek /* Run the uninstall hook */ 50257089Sgjelinek status = do_subproc_interactive(cmdbuf); 50267089Sgjelinek if ((status = subproc_status(gettext("brand-specific " 50277089Sgjelinek "uninstall"), status, B_FALSE)) != ZONE_SUBPROC_OK) { 50287089Sgjelinek if (status == ZONE_SUBPROC_USAGE && !brand_help) 50297089Sgjelinek sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 50307089Sgjelinek if (!brand_help) 50317089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50327089Sgjelinek return (Z_ERR); 50337089Sgjelinek } 50347089Sgjelinek 50357089Sgjelinek if (brand_help) 50367089Sgjelinek return (Z_OK); 50377089Sgjelinek } else { 50387089Sgjelinek /* If just help, we're done since there is no brand help. */ 50397089Sgjelinek if (brand_help) 50407089Sgjelinek return (Z_OK); 50417089Sgjelinek 50427089Sgjelinek /* Run the built-in uninstall support. */ 50437089Sgjelinek if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) { 50447089Sgjelinek errno = err; 50457089Sgjelinek zperror2(target_zone, gettext("cleaning up zonepath " 50467089Sgjelinek "failed")); 50477089Sgjelinek goto bad; 50487089Sgjelinek } 50491867Sgjelinek } 50501867Sgjelinek 50510Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED); 50520Sstevel@tonic-gate if (err != Z_OK) { 50530Sstevel@tonic-gate errno = err; 50540Sstevel@tonic-gate zperror2(target_zone, gettext("could not reset state")); 50550Sstevel@tonic-gate } 50560Sstevel@tonic-gate bad: 50577089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 50580Sstevel@tonic-gate return (err); 50590Sstevel@tonic-gate } 50600Sstevel@tonic-gate 5061766Scarlsonj /* ARGSUSED */ 5062766Scarlsonj static int 5063766Scarlsonj mount_func(int argc, char *argv[]) 5064766Scarlsonj { 5065766Scarlsonj zone_cmd_arg_t zarg; 50662712Snn35248 boolean_t force = B_FALSE; 50672712Snn35248 int arg; 50682712Snn35248 50692712Snn35248 /* 50702712Snn35248 * The only supported subargument to the "mount" subcommand is 50712712Snn35248 * "-f", which forces us to mount a zone in the INCOMPLETE state. 50722712Snn35248 */ 50732712Snn35248 optind = 0; 50742712Snn35248 if ((arg = getopt(argc, argv, "f")) != EOF) { 50752712Snn35248 switch (arg) { 50762712Snn35248 case 'f': 50772712Snn35248 force = B_TRUE; 50782712Snn35248 break; 50792712Snn35248 default: 50802712Snn35248 return (Z_USAGE); 50812712Snn35248 } 50822712Snn35248 } 50832712Snn35248 if (argc > optind) 5084766Scarlsonj return (Z_USAGE); 50852712Snn35248 50862712Snn35248 if (sanity_check(target_zone, CMD_MOUNT, B_FALSE, B_FALSE, force) 50872712Snn35248 != Z_OK) 5088766Scarlsonj return (Z_ERR); 50893339Szt129084 if (verify_details(CMD_MOUNT, argv) != Z_OK) 5090766Scarlsonj return (Z_ERR); 5091766Scarlsonj 50922712Snn35248 zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT; 50935829Sgjelinek zarg.bootbuf[0] = '\0'; 50947089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 5095766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 5096766Scarlsonj return (Z_ERR); 5097766Scarlsonj } 5098766Scarlsonj return (Z_OK); 5099766Scarlsonj } 5100766Scarlsonj 5101766Scarlsonj /* ARGSUSED */ 5102766Scarlsonj static int 5103766Scarlsonj unmount_func(int argc, char *argv[]) 5104766Scarlsonj { 5105766Scarlsonj zone_cmd_arg_t zarg; 5106766Scarlsonj 5107766Scarlsonj if (argc > 0) 5108766Scarlsonj return (Z_USAGE); 51092712Snn35248 if (sanity_check(target_zone, CMD_UNMOUNT, B_FALSE, B_FALSE, B_FALSE) 51102712Snn35248 != Z_OK) 5111766Scarlsonj return (Z_ERR); 5112766Scarlsonj 5113766Scarlsonj zarg.cmd = Z_UNMOUNT; 51147089Sgjelinek if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { 5115766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 5116766Scarlsonj return (Z_ERR); 5117766Scarlsonj } 5118766Scarlsonj return (Z_OK); 5119766Scarlsonj } 5120766Scarlsonj 51210Sstevel@tonic-gate static int 51222303Scarlsonj mark_func(int argc, char *argv[]) 51232303Scarlsonj { 51242303Scarlsonj int err, lockfd; 51252303Scarlsonj 51262303Scarlsonj if (argc != 1 || strcmp(argv[0], "incomplete") != 0) 51272303Scarlsonj return (Z_USAGE); 51282712Snn35248 if (sanity_check(target_zone, CMD_MARK, B_FALSE, B_FALSE, B_FALSE) 51292712Snn35248 != Z_OK) 51302303Scarlsonj return (Z_ERR); 51312303Scarlsonj 51323339Szt129084 /* 51333339Szt129084 * Invoke brand-specific handler. 51343339Szt129084 */ 51353339Szt129084 if (invoke_brand_handler(CMD_MARK, argv) != Z_OK) 51363339Szt129084 return (Z_ERR); 51373339Szt129084 51387089Sgjelinek if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) { 51392303Scarlsonj zerror(gettext("another %s may have an operation in progress."), 51402303Scarlsonj "zoneadm"); 51412303Scarlsonj return (Z_ERR); 51422303Scarlsonj } 51432303Scarlsonj 51442303Scarlsonj err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 51452303Scarlsonj if (err != Z_OK) { 51462303Scarlsonj errno = err; 51472303Scarlsonj zperror2(target_zone, gettext("could not set state")); 51482303Scarlsonj } 51497089Sgjelinek zonecfg_release_lock_file(target_zone, lockfd); 51502303Scarlsonj 51512303Scarlsonj return (err); 51522303Scarlsonj } 51532303Scarlsonj 51543247Sgjelinek /* 51553247Sgjelinek * Check what scheduling class we're running under and print a warning if 51563247Sgjelinek * we're not using FSS. 51573247Sgjelinek */ 51583247Sgjelinek static int 51593247Sgjelinek check_sched_fss(zone_dochandle_t handle) 51603247Sgjelinek { 51613247Sgjelinek char class_name[PC_CLNMSZ]; 51623247Sgjelinek 51633247Sgjelinek if (zonecfg_get_dflt_sched_class(handle, class_name, 51643247Sgjelinek sizeof (class_name)) != Z_OK) { 51653247Sgjelinek zerror(gettext("WARNING: unable to determine the zone's " 51663247Sgjelinek "scheduling class")); 51673247Sgjelinek } else if (strcmp("FSS", class_name) != 0) { 51683247Sgjelinek zerror(gettext("WARNING: The zone.cpu-shares rctl is set but\n" 51693247Sgjelinek "FSS is not the default scheduling class for this zone. " 51703247Sgjelinek "FSS will be\nused for processes in the zone but to get " 51713247Sgjelinek "the full benefit of FSS,\nit should be the default " 51723247Sgjelinek "scheduling class. See dispadmin(1M) for\nmore details.")); 51733247Sgjelinek return (Z_SYSTEM); 51743247Sgjelinek } 51753247Sgjelinek 51763247Sgjelinek return (Z_OK); 51773247Sgjelinek } 51783247Sgjelinek 51793247Sgjelinek static int 51803247Sgjelinek check_cpu_shares_sched(zone_dochandle_t handle) 51813247Sgjelinek { 51823247Sgjelinek int err; 51833247Sgjelinek int res = Z_OK; 51843247Sgjelinek struct zone_rctltab rctl; 51853247Sgjelinek 51863247Sgjelinek if ((err = zonecfg_setrctlent(handle)) != Z_OK) { 51873247Sgjelinek errno = err; 51883247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 51893247Sgjelinek return (err); 51903247Sgjelinek } 51913247Sgjelinek 51923247Sgjelinek while (zonecfg_getrctlent(handle, &rctl) == Z_OK) { 51933247Sgjelinek if (strcmp(rctl.zone_rctl_name, "zone.cpu-shares") == 0) { 51943247Sgjelinek if (check_sched_fss(handle) != Z_OK) 51953247Sgjelinek res = Z_SYSTEM; 51963247Sgjelinek break; 51973247Sgjelinek } 51983247Sgjelinek } 51993247Sgjelinek 52003247Sgjelinek (void) zonecfg_endrctlent(handle); 52013247Sgjelinek 52023247Sgjelinek return (res); 52033247Sgjelinek } 52043247Sgjelinek 52053247Sgjelinek /* 52063352Sgjelinek * Check if there is a mix of processes running in different pools within the 52073352Sgjelinek * zone. This is currently only going to be called for the global zone from 52083352Sgjelinek * apply_func but that could be generalized in the future. 52093352Sgjelinek */ 52103352Sgjelinek static boolean_t 52113352Sgjelinek mixed_pools(zoneid_t zoneid) 52123352Sgjelinek { 52133352Sgjelinek DIR *dirp; 52143352Sgjelinek dirent_t *dent; 52153352Sgjelinek boolean_t mixed = B_FALSE; 52163352Sgjelinek boolean_t poolid_set = B_FALSE; 52173352Sgjelinek poolid_t last_poolid = 0; 52183352Sgjelinek 52193352Sgjelinek if ((dirp = opendir("/proc")) == NULL) { 52203352Sgjelinek zerror(gettext("could not open /proc")); 52213352Sgjelinek return (B_FALSE); 52223352Sgjelinek } 52233352Sgjelinek 52243352Sgjelinek while ((dent = readdir(dirp)) != NULL) { 52253352Sgjelinek int procfd; 52263352Sgjelinek psinfo_t ps; 52273352Sgjelinek char procpath[MAXPATHLEN]; 52283352Sgjelinek 52293352Sgjelinek if (dent->d_name[0] == '.') 52303352Sgjelinek continue; 52313352Sgjelinek 52323352Sgjelinek (void) snprintf(procpath, sizeof (procpath), "/proc/%s/psinfo", 52333352Sgjelinek dent->d_name); 52343352Sgjelinek 52353352Sgjelinek if ((procfd = open(procpath, O_RDONLY)) == -1) 52363352Sgjelinek continue; 52373352Sgjelinek 52383352Sgjelinek if (read(procfd, &ps, sizeof (ps)) == sizeof (psinfo_t)) { 52393352Sgjelinek /* skip processes in other zones and system processes */ 52403352Sgjelinek if (zoneid != ps.pr_zoneid || ps.pr_flag & SSYS) { 52413352Sgjelinek (void) close(procfd); 52423352Sgjelinek continue; 52433352Sgjelinek } 52443352Sgjelinek 52453352Sgjelinek if (poolid_set) { 52463352Sgjelinek if (ps.pr_poolid != last_poolid) 52473352Sgjelinek mixed = B_TRUE; 52483352Sgjelinek } else { 52493352Sgjelinek last_poolid = ps.pr_poolid; 52503352Sgjelinek poolid_set = B_TRUE; 52513352Sgjelinek } 52523352Sgjelinek } 52533352Sgjelinek 52543352Sgjelinek (void) close(procfd); 52553352Sgjelinek 52563352Sgjelinek if (mixed) 52573352Sgjelinek break; 52583352Sgjelinek } 52593352Sgjelinek 52603352Sgjelinek (void) closedir(dirp); 52613352Sgjelinek 52623352Sgjelinek return (mixed); 52633352Sgjelinek } 52643352Sgjelinek 52653352Sgjelinek /* 52663352Sgjelinek * Check if a persistent or temporary pool is configured for the zone. 52673352Sgjelinek * This is currently only going to be called for the global zone from 52683352Sgjelinek * apply_func but that could be generalized in the future. 52693352Sgjelinek */ 52703352Sgjelinek static boolean_t 52713352Sgjelinek pool_configured(zone_dochandle_t handle) 52723352Sgjelinek { 52733352Sgjelinek int err1, err2; 52743352Sgjelinek struct zone_psettab pset_tab; 52753352Sgjelinek char poolname[MAXPATHLEN]; 52763352Sgjelinek 52773352Sgjelinek err1 = zonecfg_lookup_pset(handle, &pset_tab); 52783352Sgjelinek err2 = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 52793352Sgjelinek 52803352Sgjelinek if (err1 == Z_NO_ENTRY && 52813352Sgjelinek (err2 == Z_NO_ENTRY || (err2 == Z_OK && strlen(poolname) == 0))) 52823352Sgjelinek return (B_FALSE); 52833352Sgjelinek 52843352Sgjelinek return (B_TRUE); 52853352Sgjelinek } 52863352Sgjelinek 52873352Sgjelinek /* 52883247Sgjelinek * This is an undocumented interface which is currently only used to apply 52893247Sgjelinek * the global zone resource management settings when the system boots. 52903247Sgjelinek * This function does not yet properly handle updating a running system so 52913247Sgjelinek * any projects running in the zone would be trashed if this function 52923247Sgjelinek * were to run after the zone had booted. It also does not reset any 52933247Sgjelinek * rctl settings that were removed from zonecfg. There is still work to be 52943247Sgjelinek * done before we can properly support dynamically updating the resource 52953247Sgjelinek * management settings for a running zone (global or non-global). Thus, this 52963247Sgjelinek * functionality is undocumented for now. 52973247Sgjelinek */ 52983247Sgjelinek /* ARGSUSED */ 52993247Sgjelinek static int 53003247Sgjelinek apply_func(int argc, char *argv[]) 53013247Sgjelinek { 53023247Sgjelinek int err; 53033247Sgjelinek int res = Z_OK; 53043247Sgjelinek priv_set_t *privset; 53053247Sgjelinek zoneid_t zoneid; 53063247Sgjelinek zone_dochandle_t handle; 53073247Sgjelinek struct zone_mcaptab mcap; 53083247Sgjelinek char pool_err[128]; 53093247Sgjelinek 53103247Sgjelinek zoneid = getzoneid(); 53113247Sgjelinek 53123247Sgjelinek if (zonecfg_in_alt_root() || zoneid != GLOBAL_ZONEID || 53133247Sgjelinek target_zone == NULL || strcmp(target_zone, GLOBAL_ZONENAME) != 0) 53143247Sgjelinek return (usage(B_FALSE)); 53153247Sgjelinek 53163247Sgjelinek if ((privset = priv_allocset()) == NULL) { 53173247Sgjelinek zerror(gettext("%s failed"), "priv_allocset"); 53183247Sgjelinek return (Z_ERR); 53193247Sgjelinek } 53203247Sgjelinek 53213247Sgjelinek if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 53223247Sgjelinek zerror(gettext("%s failed"), "getppriv"); 53233247Sgjelinek priv_freeset(privset); 53243247Sgjelinek return (Z_ERR); 53253247Sgjelinek } 53263247Sgjelinek 53273247Sgjelinek if (priv_isfullset(privset) == B_FALSE) { 53283247Sgjelinek (void) usage(B_FALSE); 53293247Sgjelinek priv_freeset(privset); 53303247Sgjelinek return (Z_ERR); 53313247Sgjelinek } 53323247Sgjelinek priv_freeset(privset); 53333247Sgjelinek 53343247Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 53353247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 53363247Sgjelinek return (Z_ERR); 53373247Sgjelinek } 53383247Sgjelinek 53393247Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 53403247Sgjelinek errno = err; 53413247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 53423247Sgjelinek zonecfg_fini_handle(handle); 53433247Sgjelinek return (Z_ERR); 53443247Sgjelinek } 53453247Sgjelinek 53463247Sgjelinek /* specific error msgs are printed within apply_rctls */ 53473247Sgjelinek if ((err = zonecfg_apply_rctls(target_zone, handle)) != Z_OK) { 53483247Sgjelinek errno = err; 53493247Sgjelinek zperror(cmd_to_str(CMD_APPLY), B_TRUE); 53503247Sgjelinek res = Z_ERR; 53513247Sgjelinek } 53523247Sgjelinek 53533247Sgjelinek if ((err = check_cpu_shares_sched(handle)) != Z_OK) 53543247Sgjelinek res = Z_ERR; 53553247Sgjelinek 53563352Sgjelinek if (pool_configured(handle)) { 53573352Sgjelinek if (mixed_pools(zoneid)) { 53583352Sgjelinek zerror(gettext("Zone is using multiple resource " 53593352Sgjelinek "pools. The pool\nconfiguration cannot be " 53603352Sgjelinek "applied without rebooting.")); 53613352Sgjelinek res = Z_ERR; 53623352Sgjelinek } else { 53633352Sgjelinek 53643352Sgjelinek /* 53653352Sgjelinek * The next two blocks of code attempt to set up 53663352Sgjelinek * temporary pools as well as persistent pools. In 53673352Sgjelinek * both cases we call the functions unconditionally. 53683352Sgjelinek * Within each funtion the code will check if the zone 53693352Sgjelinek * is actually configured for a temporary pool or 53703352Sgjelinek * persistent pool and just return if there is nothing 53713352Sgjelinek * to do. 53723352Sgjelinek */ 53733352Sgjelinek if ((err = zonecfg_bind_tmp_pool(handle, zoneid, 53743352Sgjelinek pool_err, sizeof (pool_err))) != Z_OK) { 53753352Sgjelinek if (err == Z_POOL || err == Z_POOL_CREATE || 53763352Sgjelinek err == Z_POOL_BIND) 53773352Sgjelinek zerror("%s: %s", zonecfg_strerror(err), 53783352Sgjelinek pool_err); 53793352Sgjelinek else 53803352Sgjelinek zerror(gettext("could not bind zone to " 53813352Sgjelinek "temporary pool: %s"), 53823352Sgjelinek zonecfg_strerror(err)); 53833352Sgjelinek res = Z_ERR; 53843352Sgjelinek } 53853352Sgjelinek 53863352Sgjelinek if ((err = zonecfg_bind_pool(handle, zoneid, pool_err, 53873352Sgjelinek sizeof (pool_err))) != Z_OK) { 53883352Sgjelinek if (err == Z_POOL || err == Z_POOL_BIND) 53893352Sgjelinek zerror("%s: %s", zonecfg_strerror(err), 53903352Sgjelinek pool_err); 53913352Sgjelinek else 53923352Sgjelinek zerror("%s", zonecfg_strerror(err)); 53933352Sgjelinek } 53943352Sgjelinek } 53953247Sgjelinek } 53963247Sgjelinek 53973247Sgjelinek /* 53983247Sgjelinek * If a memory cap is configured, set the cap in the kernel using 53993247Sgjelinek * zone_setattr() and make sure the rcapd SMF service is enabled. 54003247Sgjelinek */ 54013247Sgjelinek if (zonecfg_getmcapent(handle, &mcap) == Z_OK) { 54023247Sgjelinek uint64_t num; 54033247Sgjelinek char smf_err[128]; 54043247Sgjelinek 54053247Sgjelinek num = (uint64_t)strtoll(mcap.zone_physmem_cap, NULL, 10); 54063247Sgjelinek if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { 54073247Sgjelinek zerror(gettext("could not set zone memory cap")); 54083247Sgjelinek res = Z_ERR; 54093247Sgjelinek } 54103247Sgjelinek 54113247Sgjelinek if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) { 54123247Sgjelinek zerror(gettext("enabling system/rcap service failed: " 54133247Sgjelinek "%s"), smf_err); 54143247Sgjelinek res = Z_ERR; 54153247Sgjelinek } 54163247Sgjelinek } 54173247Sgjelinek 54183247Sgjelinek zonecfg_fini_handle(handle); 54193247Sgjelinek 54203247Sgjelinek return (res); 54213247Sgjelinek } 54223247Sgjelinek 54232303Scarlsonj static int 54240Sstevel@tonic-gate help_func(int argc, char *argv[]) 54250Sstevel@tonic-gate { 54260Sstevel@tonic-gate int arg, cmd_num; 54270Sstevel@tonic-gate 54280Sstevel@tonic-gate if (argc == 0) { 54290Sstevel@tonic-gate (void) usage(B_TRUE); 54300Sstevel@tonic-gate return (Z_OK); 54310Sstevel@tonic-gate } 54320Sstevel@tonic-gate optind = 0; 54330Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 54340Sstevel@tonic-gate switch (arg) { 54350Sstevel@tonic-gate case '?': 54360Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 54370Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 54380Sstevel@tonic-gate default: 54390Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 54400Sstevel@tonic-gate return (Z_USAGE); 54410Sstevel@tonic-gate } 54420Sstevel@tonic-gate } 54430Sstevel@tonic-gate while (optind < argc) { 5444988Scarlsonj /* Private commands have NULL short_usage; omit them */ 5445988Scarlsonj if ((cmd_num = cmd_match(argv[optind])) < 0 || 5446988Scarlsonj cmdtab[cmd_num].short_usage == NULL) { 54470Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 54480Sstevel@tonic-gate return (Z_USAGE); 54490Sstevel@tonic-gate } 54500Sstevel@tonic-gate sub_usage(cmdtab[cmd_num].short_usage, cmd_num); 54510Sstevel@tonic-gate optind++; 54520Sstevel@tonic-gate } 54530Sstevel@tonic-gate return (Z_OK); 54540Sstevel@tonic-gate } 54550Sstevel@tonic-gate 54560Sstevel@tonic-gate /* 54570Sstevel@tonic-gate * Returns: CMD_MIN thru CMD_MAX on success, -1 on error 54580Sstevel@tonic-gate */ 54590Sstevel@tonic-gate 54600Sstevel@tonic-gate static int 54610Sstevel@tonic-gate cmd_match(char *cmd) 54620Sstevel@tonic-gate { 54630Sstevel@tonic-gate int i; 54640Sstevel@tonic-gate 54650Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 54660Sstevel@tonic-gate /* return only if there is an exact match */ 54670Sstevel@tonic-gate if (strcmp(cmd, cmdtab[i].cmd_name) == 0) 54680Sstevel@tonic-gate return (cmdtab[i].cmd_num); 54690Sstevel@tonic-gate } 54700Sstevel@tonic-gate return (-1); 54710Sstevel@tonic-gate } 54720Sstevel@tonic-gate 54730Sstevel@tonic-gate static int 54740Sstevel@tonic-gate parse_and_run(int argc, char *argv[]) 54750Sstevel@tonic-gate { 54760Sstevel@tonic-gate int i = cmd_match(argv[0]); 54770Sstevel@tonic-gate 54780Sstevel@tonic-gate if (i < 0) 54790Sstevel@tonic-gate return (usage(B_FALSE)); 54800Sstevel@tonic-gate return (cmdtab[i].handler(argc - 1, &(argv[1]))); 54810Sstevel@tonic-gate } 54820Sstevel@tonic-gate 54830Sstevel@tonic-gate static char * 54840Sstevel@tonic-gate get_execbasename(char *execfullname) 54850Sstevel@tonic-gate { 54860Sstevel@tonic-gate char *last_slash, *execbasename; 54870Sstevel@tonic-gate 54880Sstevel@tonic-gate /* guard against '/' at end of command invocation */ 54890Sstevel@tonic-gate for (;;) { 54900Sstevel@tonic-gate last_slash = strrchr(execfullname, '/'); 54910Sstevel@tonic-gate if (last_slash == NULL) { 54920Sstevel@tonic-gate execbasename = execfullname; 54930Sstevel@tonic-gate break; 54940Sstevel@tonic-gate } else { 54950Sstevel@tonic-gate execbasename = last_slash + 1; 54960Sstevel@tonic-gate if (*execbasename == '\0') { 54970Sstevel@tonic-gate *last_slash = '\0'; 54980Sstevel@tonic-gate continue; 54990Sstevel@tonic-gate } 55000Sstevel@tonic-gate break; 55010Sstevel@tonic-gate } 55020Sstevel@tonic-gate } 55030Sstevel@tonic-gate return (execbasename); 55040Sstevel@tonic-gate } 55050Sstevel@tonic-gate 55060Sstevel@tonic-gate int 55070Sstevel@tonic-gate main(int argc, char **argv) 55080Sstevel@tonic-gate { 55090Sstevel@tonic-gate int arg; 55100Sstevel@tonic-gate zoneid_t zid; 5511766Scarlsonj struct stat st; 55122712Snn35248 char *zone_lock_env; 55132712Snn35248 int err; 55140Sstevel@tonic-gate 55150Sstevel@tonic-gate if ((locale = setlocale(LC_ALL, "")) == NULL) 55160Sstevel@tonic-gate locale = "C"; 55170Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 55180Sstevel@tonic-gate setbuf(stdout, NULL); 55190Sstevel@tonic-gate (void) sigset(SIGHUP, SIG_IGN); 55200Sstevel@tonic-gate execname = get_execbasename(argv[0]); 55210Sstevel@tonic-gate target_zone = NULL; 55220Sstevel@tonic-gate if (chdir("/") != 0) { 55230Sstevel@tonic-gate zerror(gettext("could not change directory to /.")); 55240Sstevel@tonic-gate exit(Z_ERR); 55250Sstevel@tonic-gate } 55260Sstevel@tonic-gate 55272082Seschrock if (init_zfs() != Z_OK) 55282082Seschrock exit(Z_ERR); 55292082Seschrock 55302303Scarlsonj while ((arg = getopt(argc, argv, "?u:z:R:")) != EOF) { 55310Sstevel@tonic-gate switch (arg) { 55320Sstevel@tonic-gate case '?': 55330Sstevel@tonic-gate return (usage(B_TRUE)); 55342303Scarlsonj case 'u': 55352303Scarlsonj target_uuid = optarg; 55362303Scarlsonj break; 55370Sstevel@tonic-gate case 'z': 55380Sstevel@tonic-gate target_zone = optarg; 55390Sstevel@tonic-gate break; 5540766Scarlsonj case 'R': /* private option for admin/install use */ 5541766Scarlsonj if (*optarg != '/') { 5542766Scarlsonj zerror(gettext("root path must be absolute.")); 5543766Scarlsonj exit(Z_ERR); 5544766Scarlsonj } 5545766Scarlsonj if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { 5546766Scarlsonj zerror( 5547766Scarlsonj gettext("root path must be a directory.")); 5548766Scarlsonj exit(Z_ERR); 5549766Scarlsonj } 5550766Scarlsonj zonecfg_set_root(optarg); 5551766Scarlsonj break; 55520Sstevel@tonic-gate default: 55530Sstevel@tonic-gate return (usage(B_FALSE)); 55540Sstevel@tonic-gate } 55550Sstevel@tonic-gate } 55560Sstevel@tonic-gate 55570Sstevel@tonic-gate if (optind >= argc) 55580Sstevel@tonic-gate return (usage(B_FALSE)); 55592303Scarlsonj 55602303Scarlsonj if (target_uuid != NULL && *target_uuid != '\0') { 55612303Scarlsonj uuid_t uuid; 55622303Scarlsonj static char newtarget[ZONENAME_MAX]; 55632303Scarlsonj 55642303Scarlsonj if (uuid_parse(target_uuid, uuid) == -1) { 55652303Scarlsonj zerror(gettext("illegal UUID value specified")); 55662303Scarlsonj exit(Z_ERR); 55672303Scarlsonj } 55682303Scarlsonj if (zonecfg_get_name_by_uuid(uuid, newtarget, 55692303Scarlsonj sizeof (newtarget)) == Z_OK) 55702303Scarlsonj target_zone = newtarget; 55712303Scarlsonj } 55722303Scarlsonj 55730Sstevel@tonic-gate if (target_zone != NULL && zone_get_id(target_zone, &zid) != 0) { 55740Sstevel@tonic-gate errno = Z_NO_ZONE; 55750Sstevel@tonic-gate zperror(target_zone, B_TRUE); 55760Sstevel@tonic-gate exit(Z_ERR); 55770Sstevel@tonic-gate } 55782712Snn35248 55792712Snn35248 /* 55802712Snn35248 * See if we have inherited the right to manipulate this zone from 55812712Snn35248 * a zoneadm instance in our ancestry. If so, set zone_lock_cnt to 55822712Snn35248 * indicate it. If not, make that explicit in our environment. 55832712Snn35248 */ 55847089Sgjelinek zonecfg_init_lock_file(target_zone, &zone_lock_env); 55857089Sgjelinek if (zone_lock_env != NULL) 55862712Snn35248 zoneadm_is_nested = B_TRUE; 55872712Snn35248 55882712Snn35248 /* 55892712Snn35248 * If we are going to be operating on a single zone, retrieve its 55902712Snn35248 * brand type and determine whether it is native or not. 55912712Snn35248 */ 55922712Snn35248 if ((target_zone != NULL) && 55932712Snn35248 (strcmp(target_zone, GLOBAL_ZONENAME) != NULL)) { 55942712Snn35248 if (zone_get_brand(target_zone, target_brand, 55952712Snn35248 sizeof (target_brand)) != Z_OK) { 55962712Snn35248 zerror(gettext("missing or invalid brand")); 55972712Snn35248 exit(Z_ERR); 55982712Snn35248 } 55992712Snn35248 is_native_zone = (strcmp(target_brand, NATIVE_BRAND_NAME) == 0); 56002712Snn35248 } 56012712Snn35248 56022712Snn35248 err = parse_and_run(argc - optind, &argv[optind]); 56032712Snn35248 56042712Snn35248 return (err); 56050Sstevel@tonic-gate } 5606