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 /* 231300Sgjelinek * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * zoneadm is a command interpreter for zone administration. It is all in 310Sstevel@tonic-gate * C (i.e., no lex/yacc), and all the argument passing is argc/argv based. 320Sstevel@tonic-gate * main() calls parse_and_run() which calls cmd_match(), then invokes the 330Sstevel@tonic-gate * appropriate command's handler function. The rest of the program is the 340Sstevel@tonic-gate * handler functions and their helper functions. 350Sstevel@tonic-gate * 360Sstevel@tonic-gate * Some of the helper functions are used largely to simplify I18N: reducing 370Sstevel@tonic-gate * the need for translation notes. This is particularly true of many of 380Sstevel@tonic-gate * the zerror() calls: doing e.g. zerror(gettext("%s failed"), "foo") rather 390Sstevel@tonic-gate * than zerror(gettext("foo failed")) with a translation note indicating 400Sstevel@tonic-gate * that "foo" need not be translated. 410Sstevel@tonic-gate */ 420Sstevel@tonic-gate 430Sstevel@tonic-gate #include <stdio.h> 440Sstevel@tonic-gate #include <errno.h> 450Sstevel@tonic-gate #include <unistd.h> 460Sstevel@tonic-gate #include <signal.h> 470Sstevel@tonic-gate #include <stdarg.h> 480Sstevel@tonic-gate #include <ctype.h> 490Sstevel@tonic-gate #include <stdlib.h> 500Sstevel@tonic-gate #include <string.h> 510Sstevel@tonic-gate #include <wait.h> 520Sstevel@tonic-gate #include <zone.h> 530Sstevel@tonic-gate #include <priv.h> 540Sstevel@tonic-gate #include <locale.h> 550Sstevel@tonic-gate #include <libintl.h> 560Sstevel@tonic-gate #include <libzonecfg.h> 570Sstevel@tonic-gate #include <bsm/adt.h> 580Sstevel@tonic-gate #include <sys/param.h> 590Sstevel@tonic-gate #include <sys/types.h> 600Sstevel@tonic-gate #include <sys/stat.h> 610Sstevel@tonic-gate #include <sys/statvfs.h> 620Sstevel@tonic-gate #include <assert.h> 630Sstevel@tonic-gate #include <sys/sockio.h> 640Sstevel@tonic-gate #include <sys/mntent.h> 650Sstevel@tonic-gate #include <limits.h> 661867Sgjelinek #include <dirent.h> 672303Scarlsonj #include <uuid/uuid.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> 750Sstevel@tonic-gate 760Sstevel@tonic-gate #include <pool.h> 770Sstevel@tonic-gate #include <sys/pool.h> 780Sstevel@tonic-gate 791867Sgjelinek #include "zoneadm.h" 801867Sgjelinek 810Sstevel@tonic-gate #define MAXARGS 8 820Sstevel@tonic-gate 830Sstevel@tonic-gate /* Reflects kernel zone entries */ 840Sstevel@tonic-gate typedef struct zone_entry { 850Sstevel@tonic-gate zoneid_t zid; 860Sstevel@tonic-gate char zname[ZONENAME_MAX]; 870Sstevel@tonic-gate char *zstate_str; 880Sstevel@tonic-gate zone_state_t zstate_num; 890Sstevel@tonic-gate char zroot[MAXPATHLEN]; 902303Scarlsonj char zuuid[UUID_PRINTABLE_STRING_LENGTH]; 910Sstevel@tonic-gate } zone_entry_t; 920Sstevel@tonic-gate 930Sstevel@tonic-gate static zone_entry_t *zents; 940Sstevel@tonic-gate static size_t nzents; 950Sstevel@tonic-gate 961915Sgjelinek #define LOOPBACK_IF "lo0" 971915Sgjelinek #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af)) 981915Sgjelinek 991915Sgjelinek struct net_if { 1001915Sgjelinek char *name; 1011915Sgjelinek int af; 1021915Sgjelinek }; 1031915Sgjelinek 1040Sstevel@tonic-gate /* 0755 is the default directory mode. */ 1050Sstevel@tonic-gate #define DEFAULT_DIR_MODE \ 1060Sstevel@tonic-gate (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate struct cmd { 1090Sstevel@tonic-gate uint_t cmd_num; /* command number */ 1100Sstevel@tonic-gate char *cmd_name; /* command name */ 1110Sstevel@tonic-gate char *short_usage; /* short form help */ 1120Sstevel@tonic-gate int (*handler)(int argc, char *argv[]); /* function to call */ 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate }; 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate #define SHELP_HELP "help" 1172267Sdp #define SHELP_BOOT "boot [-- boot_arguments]" 1180Sstevel@tonic-gate #define SHELP_HALT "halt" 1190Sstevel@tonic-gate #define SHELP_READY "ready" 1202267Sdp #define SHELP_REBOOT "reboot [-- boot_arguments]" 1210Sstevel@tonic-gate #define SHELP_LIST "list [-cipv]" 1220Sstevel@tonic-gate #define SHELP_VERIFY "verify" 1231867Sgjelinek #define SHELP_INSTALL "install [-x nodataset]" 1240Sstevel@tonic-gate #define SHELP_UNINSTALL "uninstall [-F]" 1251867Sgjelinek #define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] zonename" 1261300Sgjelinek #define SHELP_MOVE "move zonepath" 1272078Sgjelinek #define SHELP_DETACH "detach [-n]" 1282078Sgjelinek #define SHELP_ATTACH "attach [-F] [-n <path>]" 1292303Scarlsonj #define SHELP_MARK "mark incomplete" 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate static int help_func(int argc, char *argv[]); 1320Sstevel@tonic-gate static int ready_func(int argc, char *argv[]); 1330Sstevel@tonic-gate static int boot_func(int argc, char *argv[]); 1340Sstevel@tonic-gate static int halt_func(int argc, char *argv[]); 1350Sstevel@tonic-gate static int reboot_func(int argc, char *argv[]); 1360Sstevel@tonic-gate static int list_func(int argc, char *argv[]); 1370Sstevel@tonic-gate static int verify_func(int argc, char *argv[]); 1380Sstevel@tonic-gate static int install_func(int argc, char *argv[]); 1390Sstevel@tonic-gate static int uninstall_func(int argc, char *argv[]); 140766Scarlsonj static int mount_func(int argc, char *argv[]); 141766Scarlsonj static int unmount_func(int argc, char *argv[]); 1421300Sgjelinek static int clone_func(int argc, char *argv[]); 1431300Sgjelinek static int move_func(int argc, char *argv[]); 1441507Sgjelinek static int detach_func(int argc, char *argv[]); 1451507Sgjelinek static int attach_func(int argc, char *argv[]); 1462303Scarlsonj static int mark_func(int argc, char *argv[]); 1470Sstevel@tonic-gate static int sanity_check(char *zone, int cmd_num, boolean_t running, 1480Sstevel@tonic-gate boolean_t unsafe_when_running); 1490Sstevel@tonic-gate static int cmd_match(char *cmd); 1500Sstevel@tonic-gate static int verify_details(int); 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate static struct cmd cmdtab[] = { 1530Sstevel@tonic-gate { CMD_HELP, "help", SHELP_HELP, help_func }, 1540Sstevel@tonic-gate { CMD_BOOT, "boot", SHELP_BOOT, boot_func }, 1550Sstevel@tonic-gate { CMD_HALT, "halt", SHELP_HALT, halt_func }, 1560Sstevel@tonic-gate { CMD_READY, "ready", SHELP_READY, ready_func }, 1570Sstevel@tonic-gate { CMD_REBOOT, "reboot", SHELP_REBOOT, reboot_func }, 1580Sstevel@tonic-gate { CMD_LIST, "list", SHELP_LIST, list_func }, 1590Sstevel@tonic-gate { CMD_VERIFY, "verify", SHELP_VERIFY, verify_func }, 1600Sstevel@tonic-gate { CMD_INSTALL, "install", SHELP_INSTALL, install_func }, 1610Sstevel@tonic-gate { CMD_UNINSTALL, "uninstall", SHELP_UNINSTALL, 162766Scarlsonj uninstall_func }, 1631300Sgjelinek /* mount and unmount are private commands for admin/install */ 164766Scarlsonj { CMD_MOUNT, "mount", NULL, mount_func }, 1651300Sgjelinek { CMD_UNMOUNT, "unmount", NULL, unmount_func }, 1661300Sgjelinek { CMD_CLONE, "clone", SHELP_CLONE, clone_func }, 1671507Sgjelinek { CMD_MOVE, "move", SHELP_MOVE, move_func }, 1681507Sgjelinek { CMD_DETACH, "detach", SHELP_DETACH, detach_func }, 1692303Scarlsonj { CMD_ATTACH, "attach", SHELP_ATTACH, attach_func }, 1702303Scarlsonj { CMD_MARK, "mark", SHELP_MARK, mark_func } 1710Sstevel@tonic-gate }; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* global variables */ 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate /* set early in main(), never modified thereafter, used all over the place */ 1760Sstevel@tonic-gate static char *execname; 1770Sstevel@tonic-gate static char *locale; 1781867Sgjelinek char *target_zone; 1792303Scarlsonj static char *target_uuid; 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate /* used in do_subproc() and signal handler */ 1820Sstevel@tonic-gate static volatile boolean_t child_killed; 1830Sstevel@tonic-gate 1841867Sgjelinek char * 1850Sstevel@tonic-gate cmd_to_str(int cmd_num) 1860Sstevel@tonic-gate { 1870Sstevel@tonic-gate assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 1880Sstevel@tonic-gate return (cmdtab[cmd_num].cmd_name); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate /* This is a separate function because of gettext() wrapping. */ 1920Sstevel@tonic-gate static char * 1930Sstevel@tonic-gate long_help(int cmd_num) 1940Sstevel@tonic-gate { 195222Scomay assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 1960Sstevel@tonic-gate switch (cmd_num) { 1971634Sgjelinek case CMD_HELP: 1981634Sgjelinek return (gettext("Print usage message.")); 1991634Sgjelinek case CMD_BOOT: 2002267Sdp return (gettext("Activates (boots) specified zone. See " 2012267Sdp "zoneadm(1m) for valid boot\n\targuments.")); 2021634Sgjelinek case CMD_HALT: 2031634Sgjelinek return (gettext("Halts specified zone, bypassing shutdown " 2041634Sgjelinek "scripts and removing runtime\n\tresources of the zone.")); 2051634Sgjelinek case CMD_READY: 2061634Sgjelinek return (gettext("Prepares a zone for running applications but " 2071634Sgjelinek "does not start any user\n\tprocesses in the zone.")); 2081634Sgjelinek case CMD_REBOOT: 2091634Sgjelinek return (gettext("Restarts the zone (equivalent to a halt / " 2102267Sdp "boot sequence).\n\tFails if the zone is not active. " 2112267Sdp "See zoneadm(1m) for valid boot\n\targuments.")); 2121634Sgjelinek case CMD_LIST: 2131634Sgjelinek return (gettext("Lists the current zones, or a " 2141634Sgjelinek "specific zone if indicated. By default,\n\tall " 2151634Sgjelinek "running zones are listed, though this can be " 2161634Sgjelinek "expanded to all\n\tinstalled zones with the -i " 2171634Sgjelinek "option or all configured zones with the\n\t-c " 2182303Scarlsonj "option. When used with the general -z <zone> and/or -u " 2192303Scarlsonj "<uuid-match>\n\toptions, lists only the specified " 2202303Scarlsonj "matching zone, but lists it\n\tregardless of its state, " 2212303Scarlsonj "and the -i and -c options are disallowed. The\n\t-v " 2222303Scarlsonj "option can be used to display verbose information: zone " 2232303Scarlsonj "name, id,\n\tcurrent state, root directory and options. " 2242303Scarlsonj "The -p option can be used\n\tto request machine-parsable " 2252303Scarlsonj "output. The -v and -p options are mutually\n\texclusive." 2262303Scarlsonj " If neither -v nor -p is used, just the zone name is " 2272303Scarlsonj "listed.")); 2281634Sgjelinek case CMD_VERIFY: 2291634Sgjelinek return (gettext("Check to make sure the configuration " 2301634Sgjelinek "can safely be instantiated\n\ton the machine: " 2311634Sgjelinek "physical network interfaces exist, etc.")); 2321634Sgjelinek case CMD_INSTALL: 2331867Sgjelinek return (gettext("Install the configuration on to the system. " 2341867Sgjelinek "The -x nodataset option\n\tcan be used to prevent the " 2351867Sgjelinek "creation of a new ZFS file system for the\n\tzone " 2361867Sgjelinek "(assuming the zonepath is within a ZFS file system).")); 2371634Sgjelinek case CMD_UNINSTALL: 2381634Sgjelinek return (gettext("Uninstall the configuration from the system. " 2391634Sgjelinek "The -F flag can be used\n\tto force the action.")); 2401634Sgjelinek case CMD_CLONE: 2411867Sgjelinek return (gettext("Clone the installation of another zone. " 2421867Sgjelinek "The -m option can be used to\n\tspecify 'copy' which " 2431867Sgjelinek "forces a copy of the source zone. The -s option\n\t" 2441867Sgjelinek "can be used to specify the name of a ZFS snapshot " 2451867Sgjelinek "that was taken from\n\ta previous clone command. The " 2461867Sgjelinek "snapshot will be used as the source\n\tinstead of " 2471867Sgjelinek "creating a new ZFS snapshot.")); 2481634Sgjelinek case CMD_MOVE: 2491634Sgjelinek return (gettext("Move the zone to a new zonepath.")); 2501634Sgjelinek case CMD_DETACH: 2511634Sgjelinek return (gettext("Detach the zone from the system. The zone " 2521634Sgjelinek "state is changed to\n\t'configured' (but the files under " 2531634Sgjelinek "the zonepath are untouched).\n\tThe zone can subsequently " 2541634Sgjelinek "be attached, or can be moved to another\n\tsystem and " 2552078Sgjelinek "attached there. The -n option can be used to specify\n\t" 2562078Sgjelinek "'no-execute' mode. When -n is used, the information " 2572078Sgjelinek "needed to attach\n\tthe zone is sent to standard output " 2582078Sgjelinek "but the zone is not actually\n\tdetached.")); 2591634Sgjelinek case CMD_ATTACH: 2601634Sgjelinek return (gettext("Attach the zone to the system. The zone " 2611634Sgjelinek "state must be 'configured'\n\tprior to attach; upon " 2621634Sgjelinek "successful completion, the zone state will be\n\t" 2631634Sgjelinek "'installed'. The system software on the current " 2641634Sgjelinek "system must be\n\tcompatible with the software on the " 2651634Sgjelinek "zone's original system.\n\tSpecify -F to force the attach " 2662078Sgjelinek "and skip software compatibility tests.\n\tThe -n option " 2672078Sgjelinek "can be used to specify 'no-execute' mode. When -n is\n\t" 2682078Sgjelinek "used, the information needed to attach the zone is read " 2692078Sgjelinek "from the\n\tspecified path and the configuration is only " 2702078Sgjelinek "validated. The path can\n\tbe '-' to specify standard " 2712078Sgjelinek "input.")); 2722303Scarlsonj case CMD_MARK: 2732303Scarlsonj return (gettext("Set the state of the zone. This can be used " 2742303Scarlsonj "to force the zone\n\tstate to 'incomplete' " 2752303Scarlsonj "administratively if some activity has rendered\n\tthe " 2762303Scarlsonj "zone permanently unusable. The only valid state that " 2772303Scarlsonj "may be\n\tspecified is 'incomplete'.")); 2781634Sgjelinek default: 2791634Sgjelinek return (""); 2800Sstevel@tonic-gate } 2810Sstevel@tonic-gate /* NOTREACHED */ 282222Scomay return (NULL); 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate /* 2860Sstevel@tonic-gate * Called with explicit B_TRUE when help is explicitly requested, B_FALSE for 2870Sstevel@tonic-gate * unexpected errors. 2880Sstevel@tonic-gate */ 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate static int 2910Sstevel@tonic-gate usage(boolean_t explicit) 2920Sstevel@tonic-gate { 2930Sstevel@tonic-gate int i; 2940Sstevel@tonic-gate FILE *fd = explicit ? stdout : stderr; 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate (void) fprintf(fd, "%s:\t%s help\n", gettext("usage"), execname); 2972303Scarlsonj (void) fprintf(fd, "\t%s [-z <zone>] [-u <uuid-match>] list\n", 2982303Scarlsonj execname); 2992303Scarlsonj (void) fprintf(fd, "\t%s {-z <zone>|-u <uuid-match>} <%s>\n", execname, 3000Sstevel@tonic-gate gettext("subcommand")); 3010Sstevel@tonic-gate (void) fprintf(fd, "\n%s:\n\n", gettext("Subcommands")); 3020Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 303766Scarlsonj if (cmdtab[i].short_usage == NULL) 304766Scarlsonj continue; 3050Sstevel@tonic-gate (void) fprintf(fd, "%s\n", cmdtab[i].short_usage); 3060Sstevel@tonic-gate if (explicit) 3070Sstevel@tonic-gate (void) fprintf(fd, "\t%s\n\n", long_help(i)); 3080Sstevel@tonic-gate } 3090Sstevel@tonic-gate if (!explicit) 3100Sstevel@tonic-gate (void) fputs("\n", fd); 3110Sstevel@tonic-gate return (Z_USAGE); 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate static void 3150Sstevel@tonic-gate sub_usage(char *short_usage, int cmd_num) 3160Sstevel@tonic-gate { 3170Sstevel@tonic-gate (void) fprintf(stderr, "%s:\t%s\n", gettext("usage"), short_usage); 3180Sstevel@tonic-gate (void) fprintf(stderr, "\t%s\n", long_help(cmd_num)); 3190Sstevel@tonic-gate } 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate /* 3220Sstevel@tonic-gate * zperror() is like perror(3c) except that this also prints the executable 3230Sstevel@tonic-gate * name at the start of the message, and takes a boolean indicating whether 3240Sstevel@tonic-gate * to call libc'c strerror() or that from libzonecfg. 3250Sstevel@tonic-gate */ 3260Sstevel@tonic-gate 3271867Sgjelinek void 3280Sstevel@tonic-gate zperror(const char *str, boolean_t zonecfg_error) 3290Sstevel@tonic-gate { 3300Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", execname, str, 3310Sstevel@tonic-gate zonecfg_error ? zonecfg_strerror(errno) : strerror(errno)); 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate /* 3350Sstevel@tonic-gate * zperror2() is very similar to zperror() above, except it also prints a 3360Sstevel@tonic-gate * supplied zone name after the executable. 3370Sstevel@tonic-gate * 3380Sstevel@tonic-gate * All current consumers of this function want libzonecfg's strerror() rather 3390Sstevel@tonic-gate * than libc's; if this ever changes, this function can be made more generic 3400Sstevel@tonic-gate * like zperror() above. 3410Sstevel@tonic-gate */ 3420Sstevel@tonic-gate 3431867Sgjelinek void 3440Sstevel@tonic-gate zperror2(const char *zone, const char *str) 3450Sstevel@tonic-gate { 3460Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s: %s\n", execname, zone, str, 3470Sstevel@tonic-gate zonecfg_strerror(errno)); 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate /* PRINTFLIKE1 */ 3511867Sgjelinek void 3520Sstevel@tonic-gate zerror(const char *fmt, ...) 3530Sstevel@tonic-gate { 3540Sstevel@tonic-gate va_list alist; 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate va_start(alist, fmt); 3570Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", execname); 3580Sstevel@tonic-gate if (target_zone != NULL) 3590Sstevel@tonic-gate (void) fprintf(stderr, "zone '%s': ", target_zone); 3600Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 3610Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3620Sstevel@tonic-gate va_end(alist); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate static void * 3660Sstevel@tonic-gate safe_calloc(size_t nelem, size_t elsize) 3670Sstevel@tonic-gate { 3680Sstevel@tonic-gate void *r = calloc(nelem, elsize); 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate if (r == NULL) { 3710Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), 3720Sstevel@tonic-gate (ulong_t)nelem * elsize, strerror(errno)); 3730Sstevel@tonic-gate exit(Z_ERR); 3740Sstevel@tonic-gate } 3750Sstevel@tonic-gate return (r); 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate static void 3790Sstevel@tonic-gate zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) 3800Sstevel@tonic-gate { 3810Sstevel@tonic-gate static boolean_t firsttime = B_TRUE; 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate assert(!(verbose && parsable)); 3840Sstevel@tonic-gate if (firsttime && verbose) { 3850Sstevel@tonic-gate firsttime = B_FALSE; 3860Sstevel@tonic-gate (void) printf("%*s %-16s %-14s %-30s\n", ZONEID_WIDTH, "ID", 3870Sstevel@tonic-gate "NAME", "STATUS", "PATH"); 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate if (!verbose) { 3902303Scarlsonj char *cp, *clim; 3912303Scarlsonj 3920Sstevel@tonic-gate if (!parsable) { 3930Sstevel@tonic-gate (void) printf("%s\n", zent->zname); 3940Sstevel@tonic-gate return; 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 3970Sstevel@tonic-gate (void) printf("-"); 3980Sstevel@tonic-gate else 3990Sstevel@tonic-gate (void) printf("%lu", zent->zid); 4002303Scarlsonj (void) printf(":%s:%s:", zent->zname, zent->zstate_str); 4012303Scarlsonj cp = zent->zroot; 4022303Scarlsonj while ((clim = strchr(cp, ':')) != NULL) { 4032303Scarlsonj (void) printf("%.*s\\:", clim - cp, cp); 4042303Scarlsonj cp = clim + 1; 4052303Scarlsonj } 4062303Scarlsonj (void) printf("%s:%s\n", cp, zent->zuuid); 4070Sstevel@tonic-gate return; 4080Sstevel@tonic-gate } 4090Sstevel@tonic-gate if (zent->zstate_str != NULL) { 4100Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 4110Sstevel@tonic-gate (void) printf("%*s", ZONEID_WIDTH, "-"); 4120Sstevel@tonic-gate else 4130Sstevel@tonic-gate (void) printf("%*lu", ZONEID_WIDTH, zent->zid); 4140Sstevel@tonic-gate (void) printf(" %-16s %-14s %-30s\n", zent->zname, 4150Sstevel@tonic-gate zent->zstate_str, zent->zroot); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate } 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate static int 420766Scarlsonj lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) 4210Sstevel@tonic-gate { 4221676Sjpk char root[MAXPATHLEN], *cp; 4230Sstevel@tonic-gate int err; 4242303Scarlsonj uuid_t uuid; 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate (void) strlcpy(zent->zname, zone_name, sizeof (zent->zname)); 4270Sstevel@tonic-gate (void) strlcpy(zent->zroot, "???", sizeof (zent->zroot)); 4280Sstevel@tonic-gate zent->zstate_str = "???"; 4290Sstevel@tonic-gate 430766Scarlsonj zent->zid = zid; 4310Sstevel@tonic-gate 4322303Scarlsonj if (zonecfg_get_uuid(zone_name, uuid) == Z_OK && 4332303Scarlsonj !uuid_is_null(uuid)) 4342303Scarlsonj uuid_unparse(uuid, zent->zuuid); 4352303Scarlsonj else 4362303Scarlsonj zent->zuuid[0] = '\0'; 4372303Scarlsonj 4381676Sjpk /* 4391676Sjpk * For labeled zones which query the zone path of lower-level 4401676Sjpk * zones, the path needs to be adjusted to drop the final 4411676Sjpk * "/root" component. This adjusted path is then useful 4421676Sjpk * for reading down any exported directories from the 4431676Sjpk * lower-level zone. 4441676Sjpk */ 4451676Sjpk if (is_system_labeled() && zent->zid != ZONE_ID_UNDEFINED) { 4461676Sjpk if (zone_getattr(zent->zid, ZONE_ATTR_ROOT, zent->zroot, 4471676Sjpk sizeof (zent->zroot)) == -1) { 4481676Sjpk zperror2(zent->zname, 4491676Sjpk gettext("could not get zone path.")); 4501676Sjpk return (Z_ERR); 4511676Sjpk } 4521676Sjpk cp = zent->zroot + strlen(zent->zroot) - 5; 4531676Sjpk if (cp > zent->zroot && strcmp(cp, "/root") == 0) 4541676Sjpk *cp = 0; 4551676Sjpk } else { 4561676Sjpk if ((err = zone_get_zonepath(zent->zname, root, 4571676Sjpk sizeof (root))) != Z_OK) { 4581676Sjpk errno = err; 4591676Sjpk zperror2(zent->zname, 4601676Sjpk gettext("could not get zone path.")); 4611676Sjpk return (Z_ERR); 4621676Sjpk } 4631676Sjpk (void) strlcpy(zent->zroot, root, sizeof (zent->zroot)); 4641676Sjpk } 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, &zent->zstate_num)) != Z_OK) { 4670Sstevel@tonic-gate errno = err; 4680Sstevel@tonic-gate zperror2(zent->zname, gettext("could not get state")); 4690Sstevel@tonic-gate return (Z_ERR); 4700Sstevel@tonic-gate } 4710Sstevel@tonic-gate zent->zstate_str = zone_state_str(zent->zstate_num); 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate return (Z_OK); 4740Sstevel@tonic-gate } 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate /* 4770Sstevel@tonic-gate * fetch_zents() calls zone_list(2) to find out how many zones are running 4780Sstevel@tonic-gate * (which is stored in the global nzents), then calls zone_list(2) again 4790Sstevel@tonic-gate * to fetch the list of running zones (stored in the global zents). This 4800Sstevel@tonic-gate * function may be called multiple times, so if zents is already set, we 4810Sstevel@tonic-gate * return immediately to save work. 4820Sstevel@tonic-gate */ 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate static int 485766Scarlsonj fetch_zents(void) 4860Sstevel@tonic-gate { 4870Sstevel@tonic-gate zoneid_t *zids = NULL; 4880Sstevel@tonic-gate uint_t nzents_saved; 489766Scarlsonj int i, retv; 490766Scarlsonj FILE *fp; 491766Scarlsonj boolean_t inaltroot; 492766Scarlsonj zone_entry_t *zentp; 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate if (nzents > 0) 4950Sstevel@tonic-gate return (Z_OK); 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate if (zone_list(NULL, &nzents) != 0) { 4980Sstevel@tonic-gate zperror(gettext("failed to get zoneid list"), B_FALSE); 4990Sstevel@tonic-gate return (Z_ERR); 5000Sstevel@tonic-gate } 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate again: 5030Sstevel@tonic-gate if (nzents == 0) 5040Sstevel@tonic-gate return (Z_OK); 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate zids = safe_calloc(nzents, sizeof (zoneid_t)); 5070Sstevel@tonic-gate nzents_saved = nzents; 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate if (zone_list(zids, &nzents) != 0) { 5100Sstevel@tonic-gate zperror(gettext("failed to get zone list"), B_FALSE); 5110Sstevel@tonic-gate free(zids); 5120Sstevel@tonic-gate return (Z_ERR); 5130Sstevel@tonic-gate } 5140Sstevel@tonic-gate if (nzents != nzents_saved) { 5150Sstevel@tonic-gate /* list changed, try again */ 5160Sstevel@tonic-gate free(zids); 5170Sstevel@tonic-gate goto again; 5180Sstevel@tonic-gate } 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate zents = safe_calloc(nzents, sizeof (zone_entry_t)); 5210Sstevel@tonic-gate 522766Scarlsonj inaltroot = zonecfg_in_alt_root(); 523766Scarlsonj if (inaltroot) 524766Scarlsonj fp = zonecfg_open_scratch("", B_FALSE); 525766Scarlsonj else 526766Scarlsonj fp = NULL; 527766Scarlsonj zentp = zents; 528766Scarlsonj retv = Z_OK; 5290Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 5300Sstevel@tonic-gate char name[ZONENAME_MAX]; 531766Scarlsonj char altname[ZONENAME_MAX]; 5320Sstevel@tonic-gate 533766Scarlsonj if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { 5340Sstevel@tonic-gate zperror(gettext("failed to get zone name"), B_FALSE); 535766Scarlsonj retv = Z_ERR; 536766Scarlsonj continue; 537766Scarlsonj } 538766Scarlsonj if (zonecfg_is_scratch(name)) { 539766Scarlsonj /* Ignore scratch zones by default */ 540766Scarlsonj if (!inaltroot) 541766Scarlsonj continue; 542766Scarlsonj if (fp == NULL || 543766Scarlsonj zonecfg_reverse_scratch(fp, name, altname, 544766Scarlsonj sizeof (altname), NULL, 0) == -1) { 545924Sgjelinek zerror(gettext("could not resolve scratch " 546766Scarlsonj "zone %s"), name); 547766Scarlsonj retv = Z_ERR; 548766Scarlsonj continue; 549766Scarlsonj } 550766Scarlsonj (void) strcpy(name, altname); 551766Scarlsonj } else { 552766Scarlsonj /* Ignore non-scratch when in an alternate root */ 553766Scarlsonj if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0) 554766Scarlsonj continue; 555766Scarlsonj } 556766Scarlsonj if (lookup_zone_info(name, zids[i], zentp) != Z_OK) { 557766Scarlsonj zerror(gettext("failed to get zone data")); 558766Scarlsonj retv = Z_ERR; 559766Scarlsonj continue; 560766Scarlsonj } 561766Scarlsonj zentp++; 5620Sstevel@tonic-gate } 563766Scarlsonj nzents = zentp - zents; 564766Scarlsonj if (fp != NULL) 565766Scarlsonj zonecfg_close_scratch(fp); 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate free(zids); 568766Scarlsonj return (retv); 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate 571766Scarlsonj static int 5720Sstevel@tonic-gate zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable) 5730Sstevel@tonic-gate { 5740Sstevel@tonic-gate int i; 5750Sstevel@tonic-gate zone_entry_t zent; 5760Sstevel@tonic-gate FILE *cookie; 5770Sstevel@tonic-gate char *name; 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate /* 5800Sstevel@tonic-gate * First get the list of running zones from the kernel and print them. 5810Sstevel@tonic-gate * If that is all we need, then return. 5820Sstevel@tonic-gate */ 583766Scarlsonj if ((i = fetch_zents()) != Z_OK) { 5840Sstevel@tonic-gate /* 5850Sstevel@tonic-gate * No need for error messages; fetch_zents() has already taken 5860Sstevel@tonic-gate * care of this. 5870Sstevel@tonic-gate */ 588766Scarlsonj return (i); 5890Sstevel@tonic-gate } 590766Scarlsonj for (i = 0; i < nzents; i++) 5910Sstevel@tonic-gate zone_print(&zents[i], verbose, parsable); 5920Sstevel@tonic-gate if (min_state >= ZONE_STATE_RUNNING) 593766Scarlsonj return (Z_OK); 5940Sstevel@tonic-gate /* 5950Sstevel@tonic-gate * Next, get the full list of zones from the configuration, skipping 5960Sstevel@tonic-gate * any we have already printed. 5970Sstevel@tonic-gate */ 5980Sstevel@tonic-gate cookie = setzoneent(); 5990Sstevel@tonic-gate while ((name = getzoneent(cookie)) != NULL) { 6000Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6010Sstevel@tonic-gate if (strcmp(zents[i].zname, name) == 0) 6020Sstevel@tonic-gate break; 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate if (i < nzents) { 6050Sstevel@tonic-gate free(name); 6060Sstevel@tonic-gate continue; 6070Sstevel@tonic-gate } 608766Scarlsonj if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { 6090Sstevel@tonic-gate free(name); 6100Sstevel@tonic-gate continue; 6110Sstevel@tonic-gate } 6120Sstevel@tonic-gate free(name); 6130Sstevel@tonic-gate if (zent.zstate_num >= min_state) 6140Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 6150Sstevel@tonic-gate } 6160Sstevel@tonic-gate endzoneent(cookie); 617766Scarlsonj return (Z_OK); 6180Sstevel@tonic-gate } 6190Sstevel@tonic-gate 6200Sstevel@tonic-gate static zone_entry_t * 6210Sstevel@tonic-gate lookup_running_zone(char *str) 6220Sstevel@tonic-gate { 6230Sstevel@tonic-gate zoneid_t zoneid; 6240Sstevel@tonic-gate char *cp; 6250Sstevel@tonic-gate int i; 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate if (fetch_zents() != Z_OK) 6280Sstevel@tonic-gate return (NULL); 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6310Sstevel@tonic-gate if (strcmp(str, zents[i].zname) == 0) 6320Sstevel@tonic-gate return (&zents[i]); 6330Sstevel@tonic-gate } 6340Sstevel@tonic-gate errno = 0; 6350Sstevel@tonic-gate zoneid = strtol(str, &cp, 0); 6360Sstevel@tonic-gate if (zoneid < MIN_ZONEID || zoneid > MAX_ZONEID || 6370Sstevel@tonic-gate errno != 0 || *cp != '\0') 6380Sstevel@tonic-gate return (NULL); 6390Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6400Sstevel@tonic-gate if (zoneid == zents[i].zid) 6410Sstevel@tonic-gate return (&zents[i]); 6420Sstevel@tonic-gate } 6430Sstevel@tonic-gate return (NULL); 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate /* 6470Sstevel@tonic-gate * Check a bit in a mode_t: if on is B_TRUE, that bit should be on; if 6480Sstevel@tonic-gate * B_FALSE, it should be off. Return B_TRUE if the mode is bad (incorrect). 6490Sstevel@tonic-gate */ 6500Sstevel@tonic-gate static boolean_t 6510Sstevel@tonic-gate bad_mode_bit(mode_t mode, mode_t bit, boolean_t on, char *file) 6520Sstevel@tonic-gate { 6530Sstevel@tonic-gate char *str; 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate assert(bit == S_IRUSR || bit == S_IWUSR || bit == S_IXUSR || 6560Sstevel@tonic-gate bit == S_IRGRP || bit == S_IWGRP || bit == S_IXGRP || 6570Sstevel@tonic-gate bit == S_IROTH || bit == S_IWOTH || bit == S_IXOTH); 6580Sstevel@tonic-gate /* 6590Sstevel@tonic-gate * TRANSLATION_NOTE 6600Sstevel@tonic-gate * The strings below will be used as part of a larger message, 6610Sstevel@tonic-gate * either: 6620Sstevel@tonic-gate * (file name) must be (owner|group|world) (read|writ|execut)able 6630Sstevel@tonic-gate * or 6640Sstevel@tonic-gate * (file name) must not be (owner|group|world) (read|writ|execut)able 6650Sstevel@tonic-gate */ 6660Sstevel@tonic-gate switch (bit) { 6670Sstevel@tonic-gate case S_IRUSR: 6680Sstevel@tonic-gate str = gettext("owner readable"); 6690Sstevel@tonic-gate break; 6700Sstevel@tonic-gate case S_IWUSR: 6710Sstevel@tonic-gate str = gettext("owner writable"); 6720Sstevel@tonic-gate break; 6730Sstevel@tonic-gate case S_IXUSR: 6740Sstevel@tonic-gate str = gettext("owner executable"); 6750Sstevel@tonic-gate break; 6760Sstevel@tonic-gate case S_IRGRP: 6770Sstevel@tonic-gate str = gettext("group readable"); 6780Sstevel@tonic-gate break; 6790Sstevel@tonic-gate case S_IWGRP: 6800Sstevel@tonic-gate str = gettext("group writable"); 6810Sstevel@tonic-gate break; 6820Sstevel@tonic-gate case S_IXGRP: 6830Sstevel@tonic-gate str = gettext("group executable"); 6840Sstevel@tonic-gate break; 6850Sstevel@tonic-gate case S_IROTH: 6860Sstevel@tonic-gate str = gettext("world readable"); 6870Sstevel@tonic-gate break; 6880Sstevel@tonic-gate case S_IWOTH: 6890Sstevel@tonic-gate str = gettext("world writable"); 6900Sstevel@tonic-gate break; 6910Sstevel@tonic-gate case S_IXOTH: 6920Sstevel@tonic-gate str = gettext("world executable"); 6930Sstevel@tonic-gate break; 6940Sstevel@tonic-gate } 6950Sstevel@tonic-gate if ((mode & bit) == (on ? 0 : bit)) { 6960Sstevel@tonic-gate /* 6970Sstevel@tonic-gate * TRANSLATION_NOTE 6980Sstevel@tonic-gate * The first parameter below is a file name; the second 6990Sstevel@tonic-gate * is one of the "(owner|group|world) (read|writ|execut)able" 7000Sstevel@tonic-gate * strings from above. 7010Sstevel@tonic-gate */ 7020Sstevel@tonic-gate /* 7030Sstevel@tonic-gate * The code below could be simplified but not in a way 7040Sstevel@tonic-gate * that would easily translate to non-English locales. 7050Sstevel@tonic-gate */ 7060Sstevel@tonic-gate if (on) { 7070Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must be %s.\n"), 7080Sstevel@tonic-gate file, str); 7090Sstevel@tonic-gate } else { 7100Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must not be %s.\n"), 7110Sstevel@tonic-gate file, str); 7120Sstevel@tonic-gate } 7130Sstevel@tonic-gate return (B_TRUE); 7140Sstevel@tonic-gate } 7150Sstevel@tonic-gate return (B_FALSE); 7160Sstevel@tonic-gate } 7170Sstevel@tonic-gate 7180Sstevel@tonic-gate /* 7190Sstevel@tonic-gate * We want to make sure that no zone has its zone path as a child node 7200Sstevel@tonic-gate * (in the directory sense) of any other. We do that by comparing this 7210Sstevel@tonic-gate * zone's path to the path of all other (non-global) zones. The comparison 7220Sstevel@tonic-gate * in each case is simple: add '/' to the end of the path, then do a 7230Sstevel@tonic-gate * strncmp() of the two paths, using the length of the shorter one. 7240Sstevel@tonic-gate */ 7250Sstevel@tonic-gate 7260Sstevel@tonic-gate static int 7270Sstevel@tonic-gate crosscheck_zonepaths(char *path) 7280Sstevel@tonic-gate { 7290Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 7300Sstevel@tonic-gate char path_copy[MAXPATHLEN]; /* copy of original path */ 7310Sstevel@tonic-gate char rpath_copy[MAXPATHLEN]; /* copy of original rpath */ 7320Sstevel@tonic-gate struct zoneent *ze; 7330Sstevel@tonic-gate int res, err; 7340Sstevel@tonic-gate FILE *cookie; 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate cookie = setzoneent(); 7370Sstevel@tonic-gate while ((ze = getzoneent_private(cookie)) != NULL) { 7380Sstevel@tonic-gate /* Skip zones which are not installed. */ 7390Sstevel@tonic-gate if (ze->zone_state < ZONE_STATE_INSTALLED) { 7400Sstevel@tonic-gate free(ze); 7410Sstevel@tonic-gate continue; 7420Sstevel@tonic-gate } 7430Sstevel@tonic-gate /* Skip the global zone and the current target zone. */ 7440Sstevel@tonic-gate if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0 || 7450Sstevel@tonic-gate strcmp(ze->zone_name, target_zone) == 0) { 7460Sstevel@tonic-gate free(ze); 7470Sstevel@tonic-gate continue; 7480Sstevel@tonic-gate } 7490Sstevel@tonic-gate if (strlen(ze->zone_path) == 0) { 7500Sstevel@tonic-gate /* old index file without path, fall back */ 7510Sstevel@tonic-gate if ((err = zone_get_zonepath(ze->zone_name, 7520Sstevel@tonic-gate ze->zone_path, sizeof (ze->zone_path))) != Z_OK) { 7530Sstevel@tonic-gate errno = err; 7540Sstevel@tonic-gate zperror2(ze->zone_name, 7550Sstevel@tonic-gate gettext("could not get zone path")); 7560Sstevel@tonic-gate free(ze); 7570Sstevel@tonic-gate continue; 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate } 760766Scarlsonj (void) snprintf(path_copy, sizeof (path_copy), "%s%s", 761766Scarlsonj zonecfg_get_root(), ze->zone_path); 762766Scarlsonj res = resolvepath(path_copy, rpath, sizeof (rpath)); 7630Sstevel@tonic-gate if (res == -1) { 7640Sstevel@tonic-gate if (errno != ENOENT) { 765766Scarlsonj zperror(path_copy, B_FALSE); 7660Sstevel@tonic-gate free(ze); 7670Sstevel@tonic-gate return (Z_ERR); 7680Sstevel@tonic-gate } 7690Sstevel@tonic-gate (void) printf(gettext("WARNING: zone %s is installed, " 7700Sstevel@tonic-gate "but its %s %s does not exist.\n"), ze->zone_name, 771766Scarlsonj "zonepath", path_copy); 7720Sstevel@tonic-gate free(ze); 7730Sstevel@tonic-gate continue; 7740Sstevel@tonic-gate } 7750Sstevel@tonic-gate rpath[res] = '\0'; 7760Sstevel@tonic-gate (void) snprintf(path_copy, sizeof (path_copy), "%s/", path); 7770Sstevel@tonic-gate (void) snprintf(rpath_copy, sizeof (rpath_copy), "%s/", rpath); 7780Sstevel@tonic-gate if (strncmp(path_copy, rpath_copy, 7790Sstevel@tonic-gate min(strlen(path_copy), strlen(rpath_copy))) == 0) { 780924Sgjelinek /* 781924Sgjelinek * TRANSLATION_NOTE 782924Sgjelinek * zonepath is a literal that should not be translated. 783924Sgjelinek */ 7840Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s zonepath (%s) and " 7850Sstevel@tonic-gate "%s zonepath (%s) overlap.\n"), 7860Sstevel@tonic-gate target_zone, path, ze->zone_name, rpath); 7870Sstevel@tonic-gate free(ze); 7880Sstevel@tonic-gate return (Z_ERR); 7890Sstevel@tonic-gate } 7900Sstevel@tonic-gate free(ze); 7910Sstevel@tonic-gate } 7920Sstevel@tonic-gate endzoneent(cookie); 7930Sstevel@tonic-gate return (Z_OK); 7940Sstevel@tonic-gate } 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate static int 7970Sstevel@tonic-gate validate_zonepath(char *path, int cmd_num) 7980Sstevel@tonic-gate { 7990Sstevel@tonic-gate int res; /* result of last library/system call */ 8000Sstevel@tonic-gate boolean_t err = B_FALSE; /* have we run into an error? */ 8010Sstevel@tonic-gate struct stat stbuf; 8022267Sdp struct statvfs64 vfsbuf; 8030Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 8040Sstevel@tonic-gate char ppath[MAXPATHLEN]; /* parent path */ 8050Sstevel@tonic-gate char rppath[MAXPATHLEN]; /* resolved parent path */ 8060Sstevel@tonic-gate char rootpath[MAXPATHLEN]; /* root path */ 8070Sstevel@tonic-gate zone_state_t state; 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate if (path[0] != '/') { 8100Sstevel@tonic-gate (void) fprintf(stderr, 8110Sstevel@tonic-gate gettext("%s is not an absolute path.\n"), path); 8120Sstevel@tonic-gate return (Z_ERR); 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate if ((res = resolvepath(path, rpath, sizeof (rpath))) == -1) { 8150Sstevel@tonic-gate if ((errno != ENOENT) || 8161300Sgjelinek (cmd_num != CMD_VERIFY && cmd_num != CMD_INSTALL && 8171300Sgjelinek cmd_num != CMD_CLONE && cmd_num != CMD_MOVE)) { 8180Sstevel@tonic-gate zperror(path, B_FALSE); 8190Sstevel@tonic-gate return (Z_ERR); 8200Sstevel@tonic-gate } 8210Sstevel@tonic-gate if (cmd_num == CMD_VERIFY) { 822924Sgjelinek /* 823924Sgjelinek * TRANSLATION_NOTE 824924Sgjelinek * zoneadm is a literal that should not be translated. 825924Sgjelinek */ 8260Sstevel@tonic-gate (void) fprintf(stderr, gettext("WARNING: %s does not " 827924Sgjelinek "exist, so it could not be verified.\nWhen " 828924Sgjelinek "'zoneadm %s' is run, '%s' will try to create\n%s, " 829924Sgjelinek "and '%s' will be tried again,\nbut the '%s' may " 830924Sgjelinek "fail if:\nthe parent directory of %s is group- or " 831924Sgjelinek "other-writable\nor\n%s overlaps with any other " 8320Sstevel@tonic-gate "installed zones.\n"), path, 8330Sstevel@tonic-gate cmd_to_str(CMD_INSTALL), cmd_to_str(CMD_INSTALL), 8340Sstevel@tonic-gate path, cmd_to_str(CMD_VERIFY), 8350Sstevel@tonic-gate cmd_to_str(CMD_VERIFY), path, path); 8360Sstevel@tonic-gate return (Z_OK); 8370Sstevel@tonic-gate } 8380Sstevel@tonic-gate /* 8390Sstevel@tonic-gate * The zonepath is supposed to be mode 700 but its 8400Sstevel@tonic-gate * parent(s) 755. So use 755 on the mkdirp() then 8410Sstevel@tonic-gate * chmod() the zonepath itself to 700. 8420Sstevel@tonic-gate */ 8430Sstevel@tonic-gate if (mkdirp(path, DEFAULT_DIR_MODE) < 0) { 8440Sstevel@tonic-gate zperror(path, B_FALSE); 8450Sstevel@tonic-gate return (Z_ERR); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate /* 8480Sstevel@tonic-gate * If the chmod() fails, report the error, but might 8490Sstevel@tonic-gate * as well continue the verify procedure. 8500Sstevel@tonic-gate */ 8510Sstevel@tonic-gate if (chmod(path, S_IRWXU) != 0) 8520Sstevel@tonic-gate zperror(path, B_FALSE); 8530Sstevel@tonic-gate /* 8540Sstevel@tonic-gate * Since the mkdir() succeeded, we should not have to 8550Sstevel@tonic-gate * worry about a subsequent ENOENT, thus this should 8560Sstevel@tonic-gate * only recurse once. 8570Sstevel@tonic-gate */ 8581300Sgjelinek return (validate_zonepath(path, cmd_num)); 8590Sstevel@tonic-gate } 8600Sstevel@tonic-gate rpath[res] = '\0'; 8610Sstevel@tonic-gate if (strcmp(path, rpath) != 0) { 8620Sstevel@tonic-gate errno = Z_RESOLVED_PATH; 8630Sstevel@tonic-gate zperror(path, B_TRUE); 8640Sstevel@tonic-gate return (Z_ERR); 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate if ((res = stat(rpath, &stbuf)) != 0) { 8670Sstevel@tonic-gate zperror(rpath, B_FALSE); 8680Sstevel@tonic-gate return (Z_ERR); 8690Sstevel@tonic-gate } 8700Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 8710Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 8720Sstevel@tonic-gate rpath); 8730Sstevel@tonic-gate return (Z_ERR); 8740Sstevel@tonic-gate } 8750Sstevel@tonic-gate if ((strcmp(stbuf.st_fstype, MNTTYPE_TMPFS) == 0) || 8760Sstevel@tonic-gate (strcmp(stbuf.st_fstype, MNTTYPE_XMEMFS) == 0)) { 8770Sstevel@tonic-gate (void) printf(gettext("WARNING: %s is on a temporary " 8781867Sgjelinek "file system.\n"), rpath); 8790Sstevel@tonic-gate } 8800Sstevel@tonic-gate if (crosscheck_zonepaths(rpath) != Z_OK) 8810Sstevel@tonic-gate return (Z_ERR); 8820Sstevel@tonic-gate /* 8830Sstevel@tonic-gate * Try to collect and report as many minor errors as possible 8840Sstevel@tonic-gate * before returning, so the user can learn everything that needs 8850Sstevel@tonic-gate * to be fixed up front. 8860Sstevel@tonic-gate */ 8870Sstevel@tonic-gate if (stbuf.st_uid != 0) { 8880Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 8890Sstevel@tonic-gate rpath); 8900Sstevel@tonic-gate err = B_TRUE; 8910Sstevel@tonic-gate } 8920Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rpath); 8930Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rpath); 8940Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rpath); 8950Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRGRP, B_FALSE, rpath); 8960Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rpath); 8970Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXGRP, B_FALSE, rpath); 8980Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IROTH, B_FALSE, rpath); 8990Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rpath); 9000Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXOTH, B_FALSE, rpath); 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate (void) snprintf(ppath, sizeof (ppath), "%s/..", path); 9030Sstevel@tonic-gate if ((res = resolvepath(ppath, rppath, sizeof (rppath))) == -1) { 9040Sstevel@tonic-gate zperror(ppath, B_FALSE); 9050Sstevel@tonic-gate return (Z_ERR); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate rppath[res] = '\0'; 9080Sstevel@tonic-gate if ((res = stat(rppath, &stbuf)) != 0) { 9090Sstevel@tonic-gate zperror(rppath, B_FALSE); 9100Sstevel@tonic-gate return (Z_ERR); 9110Sstevel@tonic-gate } 9120Sstevel@tonic-gate /* theoretically impossible */ 9130Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 9140Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 9150Sstevel@tonic-gate rppath); 9160Sstevel@tonic-gate return (Z_ERR); 9170Sstevel@tonic-gate } 9180Sstevel@tonic-gate if (stbuf.st_uid != 0) { 9190Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 9200Sstevel@tonic-gate rppath); 9210Sstevel@tonic-gate err = B_TRUE; 9220Sstevel@tonic-gate } 9230Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rppath); 9240Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rppath); 9250Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rppath); 9260Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rppath); 9270Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rppath); 9280Sstevel@tonic-gate if (strcmp(rpath, rppath) == 0) { 9290Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is its own parent.\n"), 9300Sstevel@tonic-gate rppath); 9310Sstevel@tonic-gate err = B_TRUE; 9320Sstevel@tonic-gate } 9330Sstevel@tonic-gate 9342267Sdp if (statvfs64(rpath, &vfsbuf) != 0) { 9350Sstevel@tonic-gate zperror(rpath, B_FALSE); 9360Sstevel@tonic-gate return (Z_ERR); 9370Sstevel@tonic-gate } 938823Sgjelinek if (strcmp(vfsbuf.f_basetype, MNTTYPE_NFS) == 0) { 939924Sgjelinek /* 940924Sgjelinek * TRANSLATION_NOTE 941924Sgjelinek * Zonepath and NFS are literals that should not be translated. 942924Sgjelinek */ 943924Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on an NFS " 9441867Sgjelinek "mounted file system.\n" 9451867Sgjelinek "\tA local file system must be used.\n"), rpath); 946823Sgjelinek return (Z_ERR); 947823Sgjelinek } 948823Sgjelinek if (vfsbuf.f_flag & ST_NOSUID) { 949924Sgjelinek /* 950924Sgjelinek * TRANSLATION_NOTE 951924Sgjelinek * Zonepath and nosuid are literals that should not be 952924Sgjelinek * translated. 953924Sgjelinek */ 954823Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on a nosuid " 9551867Sgjelinek "file system.\n"), rpath); 9560Sstevel@tonic-gate return (Z_ERR); 9570Sstevel@tonic-gate } 9580Sstevel@tonic-gate 9590Sstevel@tonic-gate if ((res = zone_get_state(target_zone, &state)) != Z_OK) { 9600Sstevel@tonic-gate errno = res; 9610Sstevel@tonic-gate zperror2(target_zone, gettext("could not get state")); 9620Sstevel@tonic-gate return (Z_ERR); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate /* 9650Sstevel@tonic-gate * The existence of the root path is only bad in the configured state, 9660Sstevel@tonic-gate * as it is *supposed* to be there at the installed and later states. 9671507Sgjelinek * However, the root path is expected to be there if the zone is 9681507Sgjelinek * detached. 9690Sstevel@tonic-gate * State/command mismatches are caught earlier in verify_details(). 9700Sstevel@tonic-gate */ 9711507Sgjelinek if (state == ZONE_STATE_CONFIGURED && cmd_num != CMD_ATTACH) { 9720Sstevel@tonic-gate if (snprintf(rootpath, sizeof (rootpath), "%s/root", rpath) >= 9730Sstevel@tonic-gate sizeof (rootpath)) { 974924Sgjelinek /* 975924Sgjelinek * TRANSLATION_NOTE 976924Sgjelinek * Zonepath is a literal that should not be translated. 977924Sgjelinek */ 9780Sstevel@tonic-gate (void) fprintf(stderr, 9790Sstevel@tonic-gate gettext("Zonepath %s is too long.\n"), rpath); 9800Sstevel@tonic-gate return (Z_ERR); 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate if ((res = stat(rootpath, &stbuf)) == 0) { 9831507Sgjelinek if (zonecfg_detached(rpath)) 9841507Sgjelinek (void) fprintf(stderr, 9851507Sgjelinek gettext("Cannot %s detached " 9861507Sgjelinek "zone.\nUse attach or remove %s " 9871507Sgjelinek "directory.\n"), cmd_to_str(cmd_num), 9881507Sgjelinek rpath); 9891507Sgjelinek else 9901507Sgjelinek (void) fprintf(stderr, 9911507Sgjelinek gettext("Rootpath %s exists; " 9921507Sgjelinek "remove or move aside prior to %s.\n"), 9931507Sgjelinek rootpath, cmd_to_str(cmd_num)); 9940Sstevel@tonic-gate return (Z_ERR); 9950Sstevel@tonic-gate } 9960Sstevel@tonic-gate } 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate return (err ? Z_ERR : Z_OK); 9990Sstevel@tonic-gate } 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate static void 10020Sstevel@tonic-gate release_lock_file(int lockfd) 10030Sstevel@tonic-gate { 10040Sstevel@tonic-gate (void) close(lockfd); 10050Sstevel@tonic-gate } 10060Sstevel@tonic-gate 10070Sstevel@tonic-gate static int 10080Sstevel@tonic-gate grab_lock_file(const char *zone_name, int *lockfd) 10090Sstevel@tonic-gate { 10100Sstevel@tonic-gate char pathbuf[PATH_MAX]; 10110Sstevel@tonic-gate struct flock flock; 10120Sstevel@tonic-gate 1013766Scarlsonj if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(), 1014766Scarlsonj ZONES_TMPDIR) >= sizeof (pathbuf)) { 1015766Scarlsonj zerror(gettext("alternate root path is too long")); 1016766Scarlsonj return (Z_ERR); 1017766Scarlsonj } 1018766Scarlsonj if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) { 1019766Scarlsonj zerror(gettext("could not mkdir %s: %s"), pathbuf, 10200Sstevel@tonic-gate strerror(errno)); 10210Sstevel@tonic-gate return (Z_ERR); 10220Sstevel@tonic-gate } 1023766Scarlsonj (void) chmod(pathbuf, S_IRWXU); 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate /* 10260Sstevel@tonic-gate * One of these lock files is created for each zone (when needed). 10270Sstevel@tonic-gate * The lock files are not cleaned up (except on system reboot), 10280Sstevel@tonic-gate * but since there is only one per zone, there is no resource 10290Sstevel@tonic-gate * starvation issue. 10300Sstevel@tonic-gate */ 1031766Scarlsonj if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock", 1032766Scarlsonj zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) { 1033766Scarlsonj zerror(gettext("alternate root path is too long")); 1034766Scarlsonj return (Z_ERR); 1035766Scarlsonj } 10360Sstevel@tonic-gate if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 10370Sstevel@tonic-gate zerror(gettext("could not open %s: %s"), pathbuf, 10380Sstevel@tonic-gate strerror(errno)); 10390Sstevel@tonic-gate return (Z_ERR); 10400Sstevel@tonic-gate } 10410Sstevel@tonic-gate /* 10420Sstevel@tonic-gate * Lock the file to synchronize with other zoneadmds 10430Sstevel@tonic-gate */ 10440Sstevel@tonic-gate flock.l_type = F_WRLCK; 10450Sstevel@tonic-gate flock.l_whence = SEEK_SET; 10460Sstevel@tonic-gate flock.l_start = (off_t)0; 10470Sstevel@tonic-gate flock.l_len = (off_t)0; 10480Sstevel@tonic-gate if (fcntl(*lockfd, F_SETLKW, &flock) < 0) { 10490Sstevel@tonic-gate zerror(gettext("unable to lock %s: %s"), pathbuf, 10500Sstevel@tonic-gate strerror(errno)); 10510Sstevel@tonic-gate release_lock_file(*lockfd); 10520Sstevel@tonic-gate return (Z_ERR); 10530Sstevel@tonic-gate } 10540Sstevel@tonic-gate return (Z_OK); 10550Sstevel@tonic-gate } 10560Sstevel@tonic-gate 1057766Scarlsonj static boolean_t 10580Sstevel@tonic-gate get_doorname(const char *zone_name, char *buffer) 10590Sstevel@tonic-gate { 1060766Scarlsonj return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH, 1061766Scarlsonj zonecfg_get_root(), zone_name) < PATH_MAX); 10620Sstevel@tonic-gate } 10630Sstevel@tonic-gate 10640Sstevel@tonic-gate /* 10650Sstevel@tonic-gate * system daemons are not audited. For the global zone, this occurs 10660Sstevel@tonic-gate * "naturally" since init is started with the default audit 10670Sstevel@tonic-gate * characteristics. Since zoneadmd is a system daemon and it starts 10680Sstevel@tonic-gate * init for a zone, it is necessary to clear out the audit 10690Sstevel@tonic-gate * characteristics inherited from whomever started zoneadmd. This is 10700Sstevel@tonic-gate * indicated by the audit id, which is set from the ruid parameter of 10710Sstevel@tonic-gate * adt_set_user(), below. 10720Sstevel@tonic-gate */ 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate static void 10750Sstevel@tonic-gate prepare_audit_context() 10760Sstevel@tonic-gate { 10770Sstevel@tonic-gate adt_session_data_t *ah; 10780Sstevel@tonic-gate char *failure = gettext("audit failure: %s"); 10790Sstevel@tonic-gate 10800Sstevel@tonic-gate if (adt_start_session(&ah, NULL, 0)) { 10810Sstevel@tonic-gate zerror(failure, strerror(errno)); 10820Sstevel@tonic-gate return; 10830Sstevel@tonic-gate } 10840Sstevel@tonic-gate if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT, 10850Sstevel@tonic-gate ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) { 10860Sstevel@tonic-gate zerror(failure, strerror(errno)); 10870Sstevel@tonic-gate (void) adt_end_session(ah); 10880Sstevel@tonic-gate return; 10890Sstevel@tonic-gate } 10900Sstevel@tonic-gate if (adt_set_proc(ah)) 10910Sstevel@tonic-gate zerror(failure, strerror(errno)); 10920Sstevel@tonic-gate 10930Sstevel@tonic-gate (void) adt_end_session(ah); 10940Sstevel@tonic-gate } 10950Sstevel@tonic-gate 10960Sstevel@tonic-gate static int 10970Sstevel@tonic-gate start_zoneadmd(const char *zone_name) 10980Sstevel@tonic-gate { 10990Sstevel@tonic-gate char doorpath[PATH_MAX]; 11000Sstevel@tonic-gate pid_t child_pid; 11010Sstevel@tonic-gate int error = Z_ERR; 11020Sstevel@tonic-gate int doorfd, lockfd; 11030Sstevel@tonic-gate struct door_info info; 11040Sstevel@tonic-gate 1105766Scarlsonj if (!get_doorname(zone_name, doorpath)) 1106766Scarlsonj return (Z_ERR); 11070Sstevel@tonic-gate 11080Sstevel@tonic-gate if (grab_lock_file(zone_name, &lockfd) != Z_OK) 11090Sstevel@tonic-gate return (Z_ERR); 11100Sstevel@tonic-gate 11110Sstevel@tonic-gate /* 11120Sstevel@tonic-gate * Now that we have the lock, re-confirm that the daemon is 11130Sstevel@tonic-gate * *not* up and working fine. If it is still down, we have a green 11140Sstevel@tonic-gate * light to start it. 11150Sstevel@tonic-gate */ 11160Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 11170Sstevel@tonic-gate if (errno != ENOENT) { 11180Sstevel@tonic-gate zperror(doorpath, B_FALSE); 11190Sstevel@tonic-gate goto out; 11200Sstevel@tonic-gate } 11210Sstevel@tonic-gate } else { 11220Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 11230Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 11240Sstevel@tonic-gate error = Z_OK; 11250Sstevel@tonic-gate (void) close(doorfd); 11260Sstevel@tonic-gate goto out; 11270Sstevel@tonic-gate } 11280Sstevel@tonic-gate (void) close(doorfd); 11290Sstevel@tonic-gate } 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 11320Sstevel@tonic-gate zperror(gettext("could not fork"), B_FALSE); 11330Sstevel@tonic-gate goto out; 11340Sstevel@tonic-gate } else if (child_pid == 0) { 1135766Scarlsonj const char *argv[6], **ap; 1136766Scarlsonj 11370Sstevel@tonic-gate /* child process */ 11380Sstevel@tonic-gate prepare_audit_context(); 11390Sstevel@tonic-gate 1140766Scarlsonj ap = argv; 1141766Scarlsonj *ap++ = "zoneadmd"; 1142766Scarlsonj *ap++ = "-z"; 1143766Scarlsonj *ap++ = zone_name; 1144766Scarlsonj if (zonecfg_in_alt_root()) { 1145766Scarlsonj *ap++ = "-R"; 1146766Scarlsonj *ap++ = zonecfg_get_root(); 1147766Scarlsonj } 1148766Scarlsonj *ap = NULL; 1149766Scarlsonj 1150766Scarlsonj (void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv); 1151924Sgjelinek /* 1152924Sgjelinek * TRANSLATION_NOTE 1153924Sgjelinek * zoneadmd is a literal that should not be translated. 1154924Sgjelinek */ 11550Sstevel@tonic-gate zperror(gettext("could not exec zoneadmd"), B_FALSE); 11560Sstevel@tonic-gate _exit(Z_ERR); 11570Sstevel@tonic-gate } else { 11580Sstevel@tonic-gate /* parent process */ 11590Sstevel@tonic-gate pid_t retval; 11600Sstevel@tonic-gate int pstatus = 0; 11610Sstevel@tonic-gate 11620Sstevel@tonic-gate do { 11630Sstevel@tonic-gate retval = waitpid(child_pid, &pstatus, 0); 11640Sstevel@tonic-gate } while (retval != child_pid); 11650Sstevel@tonic-gate if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) && 11660Sstevel@tonic-gate WEXITSTATUS(pstatus) != 0)) { 11670Sstevel@tonic-gate zerror(gettext("could not start %s"), "zoneadmd"); 11680Sstevel@tonic-gate goto out; 11690Sstevel@tonic-gate } 11700Sstevel@tonic-gate } 11710Sstevel@tonic-gate error = Z_OK; 11720Sstevel@tonic-gate out: 11730Sstevel@tonic-gate release_lock_file(lockfd); 11740Sstevel@tonic-gate return (error); 11750Sstevel@tonic-gate } 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate static int 11780Sstevel@tonic-gate ping_zoneadmd(const char *zone_name) 11790Sstevel@tonic-gate { 11800Sstevel@tonic-gate char doorpath[PATH_MAX]; 11810Sstevel@tonic-gate int doorfd; 11820Sstevel@tonic-gate struct door_info info; 11830Sstevel@tonic-gate 1184766Scarlsonj if (!get_doorname(zone_name, doorpath)) 1185766Scarlsonj return (Z_ERR); 11860Sstevel@tonic-gate 11870Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 11880Sstevel@tonic-gate return (Z_ERR); 11890Sstevel@tonic-gate } 11900Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 11910Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 11920Sstevel@tonic-gate (void) close(doorfd); 11930Sstevel@tonic-gate return (Z_OK); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate (void) close(doorfd); 11960Sstevel@tonic-gate return (Z_ERR); 11970Sstevel@tonic-gate } 11980Sstevel@tonic-gate 11990Sstevel@tonic-gate static int 12000Sstevel@tonic-gate call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg) 12010Sstevel@tonic-gate { 12020Sstevel@tonic-gate char doorpath[PATH_MAX]; 12030Sstevel@tonic-gate int doorfd, result; 12040Sstevel@tonic-gate door_arg_t darg; 12050Sstevel@tonic-gate 12060Sstevel@tonic-gate zoneid_t zoneid; 12070Sstevel@tonic-gate uint64_t uniqid = 0; 12080Sstevel@tonic-gate 12090Sstevel@tonic-gate zone_cmd_rval_t *rvalp; 12100Sstevel@tonic-gate size_t rlen; 12110Sstevel@tonic-gate char *cp, *errbuf; 12120Sstevel@tonic-gate 12130Sstevel@tonic-gate rlen = getpagesize(); 12140Sstevel@tonic-gate if ((rvalp = malloc(rlen)) == NULL) { 12150Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rlen, 12160Sstevel@tonic-gate strerror(errno)); 12170Sstevel@tonic-gate return (-1); 12180Sstevel@tonic-gate } 12190Sstevel@tonic-gate 12200Sstevel@tonic-gate if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) { 12210Sstevel@tonic-gate (void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid, 12220Sstevel@tonic-gate sizeof (uniqid)); 12230Sstevel@tonic-gate } 12240Sstevel@tonic-gate arg->uniqid = uniqid; 12250Sstevel@tonic-gate (void) strlcpy(arg->locale, locale, sizeof (arg->locale)); 1226766Scarlsonj if (!get_doorname(zone_name, doorpath)) { 1227766Scarlsonj zerror(gettext("alternate root path is too long")); 1228766Scarlsonj free(rvalp); 1229766Scarlsonj return (-1); 1230766Scarlsonj } 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate /* 12330Sstevel@tonic-gate * Loop trying to start zoneadmd; if something goes seriously 12340Sstevel@tonic-gate * wrong we break out and fail. 12350Sstevel@tonic-gate */ 12360Sstevel@tonic-gate for (;;) { 12370Sstevel@tonic-gate if (start_zoneadmd(zone_name) != Z_OK) 12380Sstevel@tonic-gate break; 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 12410Sstevel@tonic-gate zperror(gettext("failed to open zone door"), B_FALSE); 12420Sstevel@tonic-gate break; 12430Sstevel@tonic-gate } 12440Sstevel@tonic-gate 12450Sstevel@tonic-gate darg.data_ptr = (char *)arg; 12460Sstevel@tonic-gate darg.data_size = sizeof (*arg); 12470Sstevel@tonic-gate darg.desc_ptr = NULL; 12480Sstevel@tonic-gate darg.desc_num = 0; 12490Sstevel@tonic-gate darg.rbuf = (char *)rvalp; 12500Sstevel@tonic-gate darg.rsize = rlen; 12510Sstevel@tonic-gate if (door_call(doorfd, &darg) != 0) { 12520Sstevel@tonic-gate (void) close(doorfd); 12530Sstevel@tonic-gate /* 12540Sstevel@tonic-gate * We'll get EBADF if the door has been revoked. 12550Sstevel@tonic-gate */ 12560Sstevel@tonic-gate if (errno != EBADF) { 12570Sstevel@tonic-gate zperror(gettext("door_call failed"), B_FALSE); 12580Sstevel@tonic-gate break; 12590Sstevel@tonic-gate } 12600Sstevel@tonic-gate continue; /* take another lap */ 12610Sstevel@tonic-gate } 12620Sstevel@tonic-gate (void) close(doorfd); 12630Sstevel@tonic-gate 12640Sstevel@tonic-gate if (darg.data_size == 0) { 12650Sstevel@tonic-gate /* Door server is going away; kick it again. */ 12660Sstevel@tonic-gate continue; 12670Sstevel@tonic-gate } 12680Sstevel@tonic-gate 12690Sstevel@tonic-gate errbuf = rvalp->errbuf; 12700Sstevel@tonic-gate while (*errbuf != '\0') { 12710Sstevel@tonic-gate /* 12720Sstevel@tonic-gate * Remove any newlines since zerror() 12730Sstevel@tonic-gate * will append one automatically. 12740Sstevel@tonic-gate */ 12750Sstevel@tonic-gate cp = strchr(errbuf, '\n'); 12760Sstevel@tonic-gate if (cp != NULL) 12770Sstevel@tonic-gate *cp = '\0'; 12780Sstevel@tonic-gate zerror("%s", errbuf); 12790Sstevel@tonic-gate if (cp == NULL) 12800Sstevel@tonic-gate break; 12810Sstevel@tonic-gate errbuf = cp + 1; 12820Sstevel@tonic-gate } 12830Sstevel@tonic-gate result = rvalp->rval == 0 ? 0 : -1; 12840Sstevel@tonic-gate free(rvalp); 12850Sstevel@tonic-gate return (result); 12860Sstevel@tonic-gate } 12870Sstevel@tonic-gate 12880Sstevel@tonic-gate free(rvalp); 12890Sstevel@tonic-gate return (-1); 12900Sstevel@tonic-gate } 12910Sstevel@tonic-gate 12920Sstevel@tonic-gate static int 12930Sstevel@tonic-gate ready_func(int argc, char *argv[]) 12940Sstevel@tonic-gate { 12950Sstevel@tonic-gate zone_cmd_arg_t zarg; 12960Sstevel@tonic-gate int arg; 12970Sstevel@tonic-gate 1298766Scarlsonj if (zonecfg_in_alt_root()) { 1299766Scarlsonj zerror(gettext("cannot ready zone in alternate root")); 1300766Scarlsonj return (Z_ERR); 1301766Scarlsonj } 1302766Scarlsonj 13030Sstevel@tonic-gate optind = 0; 13040Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 13050Sstevel@tonic-gate switch (arg) { 13060Sstevel@tonic-gate case '?': 13070Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 13080Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13090Sstevel@tonic-gate default: 13100Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 13110Sstevel@tonic-gate return (Z_USAGE); 13120Sstevel@tonic-gate } 13130Sstevel@tonic-gate } 13140Sstevel@tonic-gate if (argc > optind) { 13150Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 13160Sstevel@tonic-gate return (Z_USAGE); 13170Sstevel@tonic-gate } 13180Sstevel@tonic-gate if (sanity_check(target_zone, CMD_READY, B_FALSE, B_FALSE) != Z_OK) 13190Sstevel@tonic-gate return (Z_ERR); 13200Sstevel@tonic-gate if (verify_details(CMD_READY) != Z_OK) 13210Sstevel@tonic-gate return (Z_ERR); 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate zarg.cmd = Z_READY; 13240Sstevel@tonic-gate if (call_zoneadmd(target_zone, &zarg) != 0) { 13250Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 13260Sstevel@tonic-gate return (Z_ERR); 13270Sstevel@tonic-gate } 13280Sstevel@tonic-gate return (Z_OK); 13290Sstevel@tonic-gate } 13300Sstevel@tonic-gate 13310Sstevel@tonic-gate static int 13320Sstevel@tonic-gate boot_func(int argc, char *argv[]) 13330Sstevel@tonic-gate { 13340Sstevel@tonic-gate zone_cmd_arg_t zarg; 13350Sstevel@tonic-gate int arg; 13360Sstevel@tonic-gate 1337766Scarlsonj if (zonecfg_in_alt_root()) { 1338766Scarlsonj zerror(gettext("cannot boot zone in alternate root")); 1339766Scarlsonj return (Z_ERR); 1340766Scarlsonj } 1341766Scarlsonj 13420Sstevel@tonic-gate zarg.bootbuf[0] = '\0'; 13430Sstevel@tonic-gate 13440Sstevel@tonic-gate /* 13452267Sdp * The following getopt processes arguments to zone boot; that 13462267Sdp * is to say, the [here] portion of the argument string: 13472267Sdp * 13482267Sdp * zoneadm -z myzone boot [here] -- -v -m verbose 13492267Sdp * 13502267Sdp * Where [here] can either be nothing, -? (in which case we bail 13512267Sdp * and print usage), or -s. Support for -s is vestigal and 13522267Sdp * obsolete, but is retained because it was a documented interface 13532267Sdp * and there are known consumers including admin/install; the 13542267Sdp * proper way to specify boot arguments like -s is: 13552267Sdp * 13562267Sdp * zoneadm -z myzone boot -- -s -v -m verbose. 13570Sstevel@tonic-gate */ 13580Sstevel@tonic-gate optind = 0; 13590Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?s")) != EOF) { 13600Sstevel@tonic-gate switch (arg) { 13610Sstevel@tonic-gate case '?': 13620Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13630Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13640Sstevel@tonic-gate case 's': 13650Sstevel@tonic-gate (void) strlcpy(zarg.bootbuf, "-s", 13660Sstevel@tonic-gate sizeof (zarg.bootbuf)); 13670Sstevel@tonic-gate break; 13680Sstevel@tonic-gate default: 13690Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13700Sstevel@tonic-gate return (Z_USAGE); 13710Sstevel@tonic-gate } 13720Sstevel@tonic-gate } 13732267Sdp 13742267Sdp for (; optind < argc; optind++) { 13752267Sdp if (strlcat(zarg.bootbuf, argv[optind], 13762267Sdp sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) { 13772267Sdp zerror(gettext("Boot argument list too long")); 13782267Sdp return (Z_ERR); 13792267Sdp } 13802267Sdp if (optind < argc - 1) 13812267Sdp if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >= 13822267Sdp sizeof (zarg.bootbuf)) { 13832267Sdp zerror(gettext("Boot argument list too long")); 13842267Sdp return (Z_ERR); 13852267Sdp } 13862267Sdp } 13872267Sdp 13880Sstevel@tonic-gate if (sanity_check(target_zone, CMD_BOOT, B_FALSE, B_FALSE) != Z_OK) 13890Sstevel@tonic-gate return (Z_ERR); 13900Sstevel@tonic-gate if (verify_details(CMD_BOOT) != Z_OK) 13910Sstevel@tonic-gate return (Z_ERR); 13920Sstevel@tonic-gate zarg.cmd = Z_BOOT; 13930Sstevel@tonic-gate if (call_zoneadmd(target_zone, &zarg) != 0) { 13940Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 13950Sstevel@tonic-gate return (Z_ERR); 13960Sstevel@tonic-gate } 13970Sstevel@tonic-gate return (Z_OK); 13980Sstevel@tonic-gate } 13990Sstevel@tonic-gate 14000Sstevel@tonic-gate static void 14010Sstevel@tonic-gate fake_up_local_zone(zoneid_t zid, zone_entry_t *zeptr) 14020Sstevel@tonic-gate { 14030Sstevel@tonic-gate ssize_t result; 14042303Scarlsonj uuid_t uuid; 14052303Scarlsonj FILE *fp; 14062303Scarlsonj 14072303Scarlsonj (void) memset(zeptr, 0, sizeof (*zeptr)); 14080Sstevel@tonic-gate 14090Sstevel@tonic-gate zeptr->zid = zid; 14102303Scarlsonj 14110Sstevel@tonic-gate /* 14120Sstevel@tonic-gate * Since we're looking up our own (non-global) zone name, 14130Sstevel@tonic-gate * we can be assured that it will succeed. 14140Sstevel@tonic-gate */ 14150Sstevel@tonic-gate result = getzonenamebyid(zid, zeptr->zname, sizeof (zeptr->zname)); 14160Sstevel@tonic-gate assert(result >= 0); 14172303Scarlsonj if (zonecfg_is_scratch(zeptr->zname) && 14182303Scarlsonj (fp = zonecfg_open_scratch("", B_FALSE)) != NULL) { 14192303Scarlsonj (void) zonecfg_reverse_scratch(fp, zeptr->zname, zeptr->zname, 14202303Scarlsonj sizeof (zeptr->zname), NULL, 0); 14212303Scarlsonj zonecfg_close_scratch(fp); 14222303Scarlsonj } 14232303Scarlsonj 14242303Scarlsonj if (is_system_labeled()) { 14251676Sjpk (void) zone_getattr(zid, ZONE_ATTR_ROOT, zeptr->zroot, 14261676Sjpk sizeof (zeptr->zroot)); 14272303Scarlsonj } else { 14282303Scarlsonj (void) strlcpy(zeptr->zroot, "/", sizeof (zeptr->zroot)); 14292303Scarlsonj } 14302303Scarlsonj 14310Sstevel@tonic-gate zeptr->zstate_str = "running"; 14322303Scarlsonj 14332303Scarlsonj if (zonecfg_get_uuid(zeptr->zname, uuid) == Z_OK && 14342303Scarlsonj !uuid_is_null(uuid)) 14352303Scarlsonj uuid_unparse(uuid, zeptr->zuuid); 14360Sstevel@tonic-gate } 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate static int 14390Sstevel@tonic-gate list_func(int argc, char *argv[]) 14400Sstevel@tonic-gate { 14410Sstevel@tonic-gate zone_entry_t *zentp, zent; 1442766Scarlsonj int arg, retv; 14430Sstevel@tonic-gate boolean_t output = B_FALSE, verbose = B_FALSE, parsable = B_FALSE; 14440Sstevel@tonic-gate zone_state_t min_state = ZONE_STATE_RUNNING; 14450Sstevel@tonic-gate zoneid_t zone_id = getzoneid(); 14460Sstevel@tonic-gate 14470Sstevel@tonic-gate if (target_zone == NULL) { 14480Sstevel@tonic-gate /* all zones: default view to running but allow override */ 14490Sstevel@tonic-gate optind = 0; 14500Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?cipv")) != EOF) { 14510Sstevel@tonic-gate switch (arg) { 14520Sstevel@tonic-gate case '?': 14530Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14540Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 14551339Sjonb /* 14561339Sjonb * The 'i' and 'c' options are not mutually 14571339Sjonb * exclusive so if 'c' is given, then min_state 14581339Sjonb * is set to 0 (ZONE_STATE_CONFIGURED) which is 14591339Sjonb * the lowest possible state. If 'i' is given, 14601339Sjonb * then min_state is set to be the lowest state 14611339Sjonb * so far. 14621339Sjonb */ 14630Sstevel@tonic-gate case 'c': 14640Sstevel@tonic-gate min_state = ZONE_STATE_CONFIGURED; 14650Sstevel@tonic-gate break; 14660Sstevel@tonic-gate case 'i': 14671339Sjonb min_state = min(ZONE_STATE_INSTALLED, 14681339Sjonb min_state); 14691339Sjonb 14700Sstevel@tonic-gate break; 14710Sstevel@tonic-gate case 'p': 14720Sstevel@tonic-gate parsable = B_TRUE; 14730Sstevel@tonic-gate break; 14740Sstevel@tonic-gate case 'v': 14750Sstevel@tonic-gate verbose = B_TRUE; 14760Sstevel@tonic-gate break; 14770Sstevel@tonic-gate default: 14780Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14790Sstevel@tonic-gate return (Z_USAGE); 14800Sstevel@tonic-gate } 14810Sstevel@tonic-gate } 14820Sstevel@tonic-gate if (parsable && verbose) { 14830Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14840Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14850Sstevel@tonic-gate return (Z_ERR); 14860Sstevel@tonic-gate } 14871676Sjpk if (zone_id == GLOBAL_ZONEID || is_system_labeled()) { 1488766Scarlsonj retv = zone_print_list(min_state, verbose, parsable); 14890Sstevel@tonic-gate } else { 1490766Scarlsonj retv = Z_OK; 14910Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14920Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14930Sstevel@tonic-gate } 1494766Scarlsonj return (retv); 14950Sstevel@tonic-gate } 14960Sstevel@tonic-gate 14970Sstevel@tonic-gate /* 14980Sstevel@tonic-gate * Specific target zone: disallow -i/-c suboptions. 14990Sstevel@tonic-gate */ 15000Sstevel@tonic-gate optind = 0; 15010Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?pv")) != EOF) { 15020Sstevel@tonic-gate switch (arg) { 15030Sstevel@tonic-gate case '?': 15040Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 15050Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 15060Sstevel@tonic-gate case 'p': 15070Sstevel@tonic-gate parsable = B_TRUE; 15080Sstevel@tonic-gate break; 15090Sstevel@tonic-gate case 'v': 15100Sstevel@tonic-gate verbose = B_TRUE; 15110Sstevel@tonic-gate break; 15120Sstevel@tonic-gate default: 15130Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 15140Sstevel@tonic-gate return (Z_USAGE); 15150Sstevel@tonic-gate } 15160Sstevel@tonic-gate } 15170Sstevel@tonic-gate if (parsable && verbose) { 15180Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 15190Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 15200Sstevel@tonic-gate return (Z_ERR); 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate if (argc > optind) { 15230Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 15240Sstevel@tonic-gate return (Z_USAGE); 15250Sstevel@tonic-gate } 15260Sstevel@tonic-gate if (zone_id != GLOBAL_ZONEID) { 15270Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 15280Sstevel@tonic-gate /* 15290Sstevel@tonic-gate * main() will issue a Z_NO_ZONE error if it cannot get an 15300Sstevel@tonic-gate * id for target_zone, which in a non-global zone should 15310Sstevel@tonic-gate * happen for any zone name except `zonename`. Thus we 15320Sstevel@tonic-gate * assert() that here but don't otherwise check. 15330Sstevel@tonic-gate */ 15340Sstevel@tonic-gate assert(strcmp(zent.zname, target_zone) == 0); 15350Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 15360Sstevel@tonic-gate output = B_TRUE; 15370Sstevel@tonic-gate } else if ((zentp = lookup_running_zone(target_zone)) != NULL) { 15380Sstevel@tonic-gate zone_print(zentp, verbose, parsable); 15390Sstevel@tonic-gate output = B_TRUE; 1540766Scarlsonj } else if (lookup_zone_info(target_zone, ZONE_ID_UNDEFINED, 1541766Scarlsonj &zent) == Z_OK) { 15420Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 15430Sstevel@tonic-gate output = B_TRUE; 15440Sstevel@tonic-gate } 15450Sstevel@tonic-gate return (output ? Z_OK : Z_ERR); 15460Sstevel@tonic-gate } 15470Sstevel@tonic-gate 15480Sstevel@tonic-gate static void 15490Sstevel@tonic-gate sigterm(int sig) 15500Sstevel@tonic-gate { 15510Sstevel@tonic-gate /* 15520Sstevel@tonic-gate * Ignore SIG{INT,TERM}, so we don't end up in an infinite loop, 15530Sstevel@tonic-gate * then propagate the signal to our process group. 15540Sstevel@tonic-gate */ 15550Sstevel@tonic-gate (void) sigset(SIGINT, SIG_IGN); 15560Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_IGN); 15570Sstevel@tonic-gate (void) kill(0, sig); 15580Sstevel@tonic-gate child_killed = B_TRUE; 15590Sstevel@tonic-gate } 15600Sstevel@tonic-gate 15610Sstevel@tonic-gate static int 15620Sstevel@tonic-gate do_subproc(char *cmdbuf) 15630Sstevel@tonic-gate { 15640Sstevel@tonic-gate char inbuf[1024]; /* arbitrary large amount */ 15650Sstevel@tonic-gate FILE *file; 15660Sstevel@tonic-gate 15670Sstevel@tonic-gate child_killed = B_FALSE; 15680Sstevel@tonic-gate /* 15690Sstevel@tonic-gate * We use popen(3c) to launch child processes for [un]install; 15700Sstevel@tonic-gate * this library call does not return a PID, so we have to kill 15710Sstevel@tonic-gate * the whole process group. To avoid killing our parent, we 15720Sstevel@tonic-gate * become a process group leader here. But doing so can wreak 15730Sstevel@tonic-gate * havoc with reading from stdin when launched by a non-job-control 15740Sstevel@tonic-gate * shell, so we close stdin and reopen it as /dev/null first. 15750Sstevel@tonic-gate */ 15760Sstevel@tonic-gate (void) close(STDIN_FILENO); 15770Sstevel@tonic-gate (void) open("/dev/null", O_RDONLY); 15780Sstevel@tonic-gate (void) setpgid(0, 0); 15790Sstevel@tonic-gate (void) sigset(SIGINT, sigterm); 15800Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm); 15810Sstevel@tonic-gate file = popen(cmdbuf, "r"); 15820Sstevel@tonic-gate for (;;) { 15830Sstevel@tonic-gate if (child_killed || fgets(inbuf, sizeof (inbuf), file) == NULL) 15840Sstevel@tonic-gate break; 15850Sstevel@tonic-gate (void) fputs(inbuf, stdout); 15860Sstevel@tonic-gate } 15870Sstevel@tonic-gate (void) sigset(SIGINT, SIG_DFL); 15880Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_DFL); 15890Sstevel@tonic-gate return (pclose(file)); 15900Sstevel@tonic-gate } 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate static int 15930Sstevel@tonic-gate subproc_status(const char *cmd, int status) 15940Sstevel@tonic-gate { 15950Sstevel@tonic-gate if (WIFEXITED(status)) { 15960Sstevel@tonic-gate int exit_code = WEXITSTATUS(status); 15970Sstevel@tonic-gate 15980Sstevel@tonic-gate if (exit_code == 0) 15990Sstevel@tonic-gate return (Z_OK); 16000Sstevel@tonic-gate zerror(gettext("'%s' failed with exit code %d."), cmd, 16010Sstevel@tonic-gate exit_code); 16020Sstevel@tonic-gate } else if (WIFSIGNALED(status)) { 16030Sstevel@tonic-gate int signal = WTERMSIG(status); 16040Sstevel@tonic-gate char sigstr[SIG2STR_MAX]; 16050Sstevel@tonic-gate 16060Sstevel@tonic-gate if (sig2str(signal, sigstr) == 0) { 16070Sstevel@tonic-gate zerror(gettext("'%s' terminated by signal SIG%s."), cmd, 16080Sstevel@tonic-gate sigstr); 16090Sstevel@tonic-gate } else { 16100Sstevel@tonic-gate zerror(gettext("'%s' terminated by an unknown signal."), 16110Sstevel@tonic-gate cmd); 16120Sstevel@tonic-gate } 16130Sstevel@tonic-gate } else { 16140Sstevel@tonic-gate zerror(gettext("'%s' failed for unknown reasons."), cmd); 16150Sstevel@tonic-gate } 16160Sstevel@tonic-gate return (Z_ERR); 16170Sstevel@tonic-gate } 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate /* 16200Sstevel@tonic-gate * Various sanity checks; make sure: 16210Sstevel@tonic-gate * 1. We're in the global zone. 16220Sstevel@tonic-gate * 2. The calling user has sufficient privilege. 16230Sstevel@tonic-gate * 3. The target zone is neither the global zone nor anything starting with 16240Sstevel@tonic-gate * "SUNW". 16250Sstevel@tonic-gate * 4a. If we're looking for a 'not running' (i.e., configured or installed) 16260Sstevel@tonic-gate * zone, the name service knows about it. 16270Sstevel@tonic-gate * 4b. For some operations which expect a zone not to be running, that it is 16280Sstevel@tonic-gate * not already running (or ready). 16290Sstevel@tonic-gate */ 16300Sstevel@tonic-gate static int 16310Sstevel@tonic-gate sanity_check(char *zone, int cmd_num, boolean_t running, 16320Sstevel@tonic-gate boolean_t unsafe_when_running) 16330Sstevel@tonic-gate { 16340Sstevel@tonic-gate zone_entry_t *zent; 16350Sstevel@tonic-gate priv_set_t *privset; 16360Sstevel@tonic-gate zone_state_t state; 1637766Scarlsonj char kernzone[ZONENAME_MAX]; 1638766Scarlsonj FILE *fp; 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 16411645Scomay switch (cmd_num) { 16421645Scomay case CMD_HALT: 16431645Scomay zerror(gettext("use %s to %s this zone."), "halt(1M)", 16441645Scomay cmd_to_str(cmd_num)); 16451645Scomay break; 16461645Scomay case CMD_REBOOT: 16471645Scomay zerror(gettext("use %s to %s this zone."), 16481645Scomay "reboot(1M)", cmd_to_str(cmd_num)); 16491645Scomay break; 16501645Scomay default: 16511645Scomay zerror(gettext("must be in the global zone to %s a " 16521645Scomay "zone."), cmd_to_str(cmd_num)); 16531645Scomay break; 16541645Scomay } 16550Sstevel@tonic-gate return (Z_ERR); 16560Sstevel@tonic-gate } 16570Sstevel@tonic-gate 16580Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 16590Sstevel@tonic-gate zerror(gettext("%s failed"), "priv_allocset"); 16600Sstevel@tonic-gate return (Z_ERR); 16610Sstevel@tonic-gate } 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 16640Sstevel@tonic-gate zerror(gettext("%s failed"), "getppriv"); 16650Sstevel@tonic-gate priv_freeset(privset); 16660Sstevel@tonic-gate return (Z_ERR); 16670Sstevel@tonic-gate } 16680Sstevel@tonic-gate 16690Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 16700Sstevel@tonic-gate zerror(gettext("only a privileged user may %s a zone."), 16710Sstevel@tonic-gate cmd_to_str(cmd_num)); 16720Sstevel@tonic-gate priv_freeset(privset); 16730Sstevel@tonic-gate return (Z_ERR); 16740Sstevel@tonic-gate } 16750Sstevel@tonic-gate priv_freeset(privset); 16760Sstevel@tonic-gate 16770Sstevel@tonic-gate if (zone == NULL) { 16780Sstevel@tonic-gate zerror(gettext("no zone specified")); 16790Sstevel@tonic-gate return (Z_ERR); 16800Sstevel@tonic-gate } 16810Sstevel@tonic-gate 16820Sstevel@tonic-gate if (strcmp(zone, GLOBAL_ZONENAME) == 0) { 16830Sstevel@tonic-gate zerror(gettext("%s operation is invalid for the global zone."), 16840Sstevel@tonic-gate cmd_to_str(cmd_num)); 16850Sstevel@tonic-gate return (Z_ERR); 16860Sstevel@tonic-gate } 16870Sstevel@tonic-gate 16880Sstevel@tonic-gate if (strncmp(zone, "SUNW", 4) == 0) { 16890Sstevel@tonic-gate zerror(gettext("%s operation is invalid for zones starting " 16900Sstevel@tonic-gate "with SUNW."), cmd_to_str(cmd_num)); 16910Sstevel@tonic-gate return (Z_ERR); 16920Sstevel@tonic-gate } 16930Sstevel@tonic-gate 1694766Scarlsonj if (!zonecfg_in_alt_root()) { 1695766Scarlsonj zent = lookup_running_zone(zone); 1696766Scarlsonj } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) { 1697766Scarlsonj zent = NULL; 1698766Scarlsonj } else { 1699766Scarlsonj if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), 1700766Scarlsonj kernzone, sizeof (kernzone)) == 0) 1701766Scarlsonj zent = lookup_running_zone(kernzone); 1702766Scarlsonj else 1703766Scarlsonj zent = NULL; 1704766Scarlsonj zonecfg_close_scratch(fp); 1705766Scarlsonj } 1706766Scarlsonj 17070Sstevel@tonic-gate /* 17080Sstevel@tonic-gate * Look up from the kernel for 'running' zones. 17090Sstevel@tonic-gate */ 17100Sstevel@tonic-gate if (running) { 17110Sstevel@tonic-gate if (zent == NULL) { 17120Sstevel@tonic-gate zerror(gettext("not running")); 17130Sstevel@tonic-gate return (Z_ERR); 17140Sstevel@tonic-gate } 17150Sstevel@tonic-gate } else { 17160Sstevel@tonic-gate int err; 17170Sstevel@tonic-gate 17180Sstevel@tonic-gate if (unsafe_when_running && zent != NULL) { 17190Sstevel@tonic-gate /* check whether the zone is ready or running */ 17200Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, 17210Sstevel@tonic-gate &zent->zstate_num)) != Z_OK) { 17220Sstevel@tonic-gate errno = err; 17230Sstevel@tonic-gate zperror2(zent->zname, 17240Sstevel@tonic-gate gettext("could not get state")); 17250Sstevel@tonic-gate /* can't tell, so hedge */ 17260Sstevel@tonic-gate zent->zstate_str = "ready/running"; 17270Sstevel@tonic-gate } else { 17280Sstevel@tonic-gate zent->zstate_str = 17290Sstevel@tonic-gate zone_state_str(zent->zstate_num); 17300Sstevel@tonic-gate } 17310Sstevel@tonic-gate zerror(gettext("%s operation is invalid for %s zones."), 17320Sstevel@tonic-gate cmd_to_str(cmd_num), zent->zstate_str); 17330Sstevel@tonic-gate return (Z_ERR); 17340Sstevel@tonic-gate } 17350Sstevel@tonic-gate if ((err = zone_get_state(zone, &state)) != Z_OK) { 17360Sstevel@tonic-gate errno = err; 17370Sstevel@tonic-gate zperror2(zone, gettext("could not get state")); 17380Sstevel@tonic-gate return (Z_ERR); 17390Sstevel@tonic-gate } 17400Sstevel@tonic-gate switch (cmd_num) { 17410Sstevel@tonic-gate case CMD_UNINSTALL: 17420Sstevel@tonic-gate if (state == ZONE_STATE_CONFIGURED) { 17430Sstevel@tonic-gate zerror(gettext("is already in state '%s'."), 17440Sstevel@tonic-gate zone_state_str(ZONE_STATE_CONFIGURED)); 17450Sstevel@tonic-gate return (Z_ERR); 17460Sstevel@tonic-gate } 17470Sstevel@tonic-gate break; 17481507Sgjelinek case CMD_ATTACH: 17491300Sgjelinek case CMD_CLONE: 17500Sstevel@tonic-gate case CMD_INSTALL: 17510Sstevel@tonic-gate if (state == ZONE_STATE_INSTALLED) { 17520Sstevel@tonic-gate zerror(gettext("is already %s."), 17530Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED)); 17540Sstevel@tonic-gate return (Z_ERR); 17550Sstevel@tonic-gate } else if (state == ZONE_STATE_INCOMPLETE) { 17560Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 17570Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 17580Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 17590Sstevel@tonic-gate return (Z_ERR); 17600Sstevel@tonic-gate } 17610Sstevel@tonic-gate break; 17621507Sgjelinek case CMD_DETACH: 17631300Sgjelinek case CMD_MOVE: 17640Sstevel@tonic-gate case CMD_READY: 17650Sstevel@tonic-gate case CMD_BOOT: 1766766Scarlsonj case CMD_MOUNT: 17672303Scarlsonj case CMD_MARK: 17680Sstevel@tonic-gate if (state < ZONE_STATE_INSTALLED) { 17690Sstevel@tonic-gate zerror(gettext("must be %s before %s."), 17700Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED), 17710Sstevel@tonic-gate cmd_to_str(cmd_num)); 17720Sstevel@tonic-gate return (Z_ERR); 17730Sstevel@tonic-gate } 17740Sstevel@tonic-gate break; 17750Sstevel@tonic-gate case CMD_VERIFY: 17760Sstevel@tonic-gate if (state == ZONE_STATE_INCOMPLETE) { 17770Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 17780Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 17790Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 17800Sstevel@tonic-gate return (Z_ERR); 17810Sstevel@tonic-gate } 17820Sstevel@tonic-gate break; 1783766Scarlsonj case CMD_UNMOUNT: 1784766Scarlsonj if (state != ZONE_STATE_MOUNTED) { 1785766Scarlsonj zerror(gettext("must be %s before %s."), 1786766Scarlsonj zone_state_str(ZONE_STATE_MOUNTED), 1787766Scarlsonj cmd_to_str(cmd_num)); 1788766Scarlsonj return (Z_ERR); 1789766Scarlsonj } 1790766Scarlsonj break; 17910Sstevel@tonic-gate } 17920Sstevel@tonic-gate } 17930Sstevel@tonic-gate return (Z_OK); 17940Sstevel@tonic-gate } 17950Sstevel@tonic-gate 17960Sstevel@tonic-gate static int 17970Sstevel@tonic-gate halt_func(int argc, char *argv[]) 17980Sstevel@tonic-gate { 17990Sstevel@tonic-gate zone_cmd_arg_t zarg; 18000Sstevel@tonic-gate int arg; 18010Sstevel@tonic-gate 1802766Scarlsonj if (zonecfg_in_alt_root()) { 1803766Scarlsonj zerror(gettext("cannot halt zone in alternate root")); 1804766Scarlsonj return (Z_ERR); 1805766Scarlsonj } 1806766Scarlsonj 18070Sstevel@tonic-gate optind = 0; 18080Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 18090Sstevel@tonic-gate switch (arg) { 18100Sstevel@tonic-gate case '?': 18110Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 18120Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 18130Sstevel@tonic-gate default: 18140Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 18150Sstevel@tonic-gate return (Z_USAGE); 18160Sstevel@tonic-gate } 18170Sstevel@tonic-gate } 18180Sstevel@tonic-gate if (argc > optind) { 18190Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 18200Sstevel@tonic-gate return (Z_USAGE); 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate /* 18230Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 18240Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 18250Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 18260Sstevel@tonic-gate */ 18270Sstevel@tonic-gate if (sanity_check(target_zone, CMD_HALT, B_FALSE, B_FALSE) != Z_OK) 18280Sstevel@tonic-gate return (Z_ERR); 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate zarg.cmd = Z_HALT; 18310Sstevel@tonic-gate return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR); 18320Sstevel@tonic-gate } 18330Sstevel@tonic-gate 18340Sstevel@tonic-gate static int 18350Sstevel@tonic-gate reboot_func(int argc, char *argv[]) 18360Sstevel@tonic-gate { 18370Sstevel@tonic-gate zone_cmd_arg_t zarg; 18380Sstevel@tonic-gate int arg; 18390Sstevel@tonic-gate 1840766Scarlsonj if (zonecfg_in_alt_root()) { 1841766Scarlsonj zerror(gettext("cannot reboot zone in alternate root")); 1842766Scarlsonj return (Z_ERR); 1843766Scarlsonj } 1844766Scarlsonj 18450Sstevel@tonic-gate optind = 0; 18460Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 18470Sstevel@tonic-gate switch (arg) { 18480Sstevel@tonic-gate case '?': 18490Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 18500Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 18510Sstevel@tonic-gate default: 18520Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 18530Sstevel@tonic-gate return (Z_USAGE); 18540Sstevel@tonic-gate } 18550Sstevel@tonic-gate } 18562267Sdp 18572267Sdp zarg.bootbuf[0] = '\0'; 18582267Sdp for (; optind < argc; optind++) { 18592267Sdp if (strlcat(zarg.bootbuf, argv[optind], 18602267Sdp sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) { 18612267Sdp zerror(gettext("Boot argument list too long")); 18622267Sdp return (Z_ERR); 18632267Sdp } 18642267Sdp if (optind < argc - 1) 18652267Sdp if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >= 18662267Sdp sizeof (zarg.bootbuf)) { 18672267Sdp zerror(gettext("Boot argument list too long")); 18682267Sdp return (Z_ERR); 18692267Sdp } 18702267Sdp } 18712267Sdp 18722267Sdp 18730Sstevel@tonic-gate /* 18740Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 18750Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 18760Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 18770Sstevel@tonic-gate */ 18780Sstevel@tonic-gate if (sanity_check(target_zone, CMD_REBOOT, B_TRUE, B_FALSE) != Z_OK) 18790Sstevel@tonic-gate return (Z_ERR); 1880823Sgjelinek if (verify_details(CMD_REBOOT) != Z_OK) 1881823Sgjelinek return (Z_ERR); 18820Sstevel@tonic-gate 18830Sstevel@tonic-gate zarg.cmd = Z_REBOOT; 18840Sstevel@tonic-gate return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR); 18850Sstevel@tonic-gate } 18860Sstevel@tonic-gate 18870Sstevel@tonic-gate static int 18880Sstevel@tonic-gate verify_rctls(zone_dochandle_t handle) 18890Sstevel@tonic-gate { 18900Sstevel@tonic-gate struct zone_rctltab rctltab; 18910Sstevel@tonic-gate size_t rbs = rctlblk_size(); 18920Sstevel@tonic-gate rctlblk_t *rctlblk; 18930Sstevel@tonic-gate int error = Z_INVAL; 18940Sstevel@tonic-gate 18950Sstevel@tonic-gate if ((rctlblk = malloc(rbs)) == NULL) { 18960Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rbs, 18970Sstevel@tonic-gate strerror(errno)); 18980Sstevel@tonic-gate return (Z_NOMEM); 18990Sstevel@tonic-gate } 19000Sstevel@tonic-gate 19010Sstevel@tonic-gate if (zonecfg_setrctlent(handle) != Z_OK) { 19020Sstevel@tonic-gate zerror(gettext("zonecfg_setrctlent failed")); 19030Sstevel@tonic-gate free(rctlblk); 19040Sstevel@tonic-gate return (error); 19050Sstevel@tonic-gate } 19060Sstevel@tonic-gate 19070Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19080Sstevel@tonic-gate while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { 19090Sstevel@tonic-gate struct zone_rctlvaltab *rctlval; 19100Sstevel@tonic-gate const char *name = rctltab.zone_rctl_name; 19110Sstevel@tonic-gate 19120Sstevel@tonic-gate if (!zonecfg_is_rctl(name)) { 19130Sstevel@tonic-gate zerror(gettext("WARNING: Ignoring unrecognized rctl " 19140Sstevel@tonic-gate "'%s'."), name); 19150Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19160Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19170Sstevel@tonic-gate continue; 19180Sstevel@tonic-gate } 19190Sstevel@tonic-gate 19200Sstevel@tonic-gate for (rctlval = rctltab.zone_rctl_valptr; rctlval != NULL; 19210Sstevel@tonic-gate rctlval = rctlval->zone_rctlval_next) { 19220Sstevel@tonic-gate if (zonecfg_construct_rctlblk(rctlval, rctlblk) 19230Sstevel@tonic-gate != Z_OK) { 19240Sstevel@tonic-gate zerror(gettext("invalid rctl value: " 19250Sstevel@tonic-gate "(priv=%s,limit=%s,action%s)"), 19260Sstevel@tonic-gate rctlval->zone_rctlval_priv, 19270Sstevel@tonic-gate rctlval->zone_rctlval_limit, 19280Sstevel@tonic-gate rctlval->zone_rctlval_action); 19290Sstevel@tonic-gate goto out; 19300Sstevel@tonic-gate } 19310Sstevel@tonic-gate if (!zonecfg_valid_rctl(name, rctlblk)) { 19320Sstevel@tonic-gate zerror(gettext("(priv=%s,limit=%s,action=%s) " 19330Sstevel@tonic-gate "is not a valid value for rctl '%s'"), 19340Sstevel@tonic-gate rctlval->zone_rctlval_priv, 19350Sstevel@tonic-gate rctlval->zone_rctlval_limit, 19360Sstevel@tonic-gate rctlval->zone_rctlval_action, 19370Sstevel@tonic-gate name); 19380Sstevel@tonic-gate goto out; 19390Sstevel@tonic-gate } 19400Sstevel@tonic-gate } 19410Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19420Sstevel@tonic-gate } 19430Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 19440Sstevel@tonic-gate error = Z_OK; 19450Sstevel@tonic-gate out: 19460Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 19470Sstevel@tonic-gate (void) zonecfg_endrctlent(handle); 19480Sstevel@tonic-gate free(rctlblk); 19490Sstevel@tonic-gate return (error); 19500Sstevel@tonic-gate } 19510Sstevel@tonic-gate 19520Sstevel@tonic-gate static int 19530Sstevel@tonic-gate verify_pool(zone_dochandle_t handle) 19540Sstevel@tonic-gate { 19550Sstevel@tonic-gate char poolname[MAXPATHLEN]; 19560Sstevel@tonic-gate pool_conf_t *poolconf; 19570Sstevel@tonic-gate pool_t *pool; 19580Sstevel@tonic-gate int status; 19590Sstevel@tonic-gate int error; 19600Sstevel@tonic-gate 19610Sstevel@tonic-gate /* 19620Sstevel@tonic-gate * This ends up being very similar to the check done in zoneadmd. 19630Sstevel@tonic-gate */ 19640Sstevel@tonic-gate error = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 19650Sstevel@tonic-gate if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) { 19660Sstevel@tonic-gate /* 19670Sstevel@tonic-gate * No pool specified. 19680Sstevel@tonic-gate */ 19690Sstevel@tonic-gate return (0); 19700Sstevel@tonic-gate } 19710Sstevel@tonic-gate if (error != Z_OK) { 19720Sstevel@tonic-gate zperror(gettext("Unable to retrieve pool name from " 19730Sstevel@tonic-gate "configuration"), B_TRUE); 19740Sstevel@tonic-gate return (error); 19750Sstevel@tonic-gate } 19760Sstevel@tonic-gate /* 19770Sstevel@tonic-gate * Don't do anything if pools aren't enabled. 19780Sstevel@tonic-gate */ 19790Sstevel@tonic-gate if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) { 19800Sstevel@tonic-gate zerror(gettext("WARNING: pools facility not active; " 19810Sstevel@tonic-gate "zone will not be bound to pool '%s'."), poolname); 19820Sstevel@tonic-gate return (Z_OK); 19830Sstevel@tonic-gate } 19840Sstevel@tonic-gate /* 19850Sstevel@tonic-gate * Try to provide a sane error message if the requested pool doesn't 19860Sstevel@tonic-gate * exist. It isn't clear that pools-related failures should 19870Sstevel@tonic-gate * necessarily translate to a failure to verify the zone configuration, 19880Sstevel@tonic-gate * hence they are not considered errors. 19890Sstevel@tonic-gate */ 19900Sstevel@tonic-gate if ((poolconf = pool_conf_alloc()) == NULL) { 19910Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_alloc failed; " 19920Sstevel@tonic-gate "using default pool")); 19930Sstevel@tonic-gate return (Z_OK); 19940Sstevel@tonic-gate } 19950Sstevel@tonic-gate if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) != 19960Sstevel@tonic-gate PO_SUCCESS) { 19970Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_open failed; " 19980Sstevel@tonic-gate "using default pool")); 19990Sstevel@tonic-gate pool_conf_free(poolconf); 20000Sstevel@tonic-gate return (Z_OK); 20010Sstevel@tonic-gate } 20020Sstevel@tonic-gate pool = pool_get_pool(poolconf, poolname); 20030Sstevel@tonic-gate (void) pool_conf_close(poolconf); 20040Sstevel@tonic-gate pool_conf_free(poolconf); 20050Sstevel@tonic-gate if (pool == NULL) { 20060Sstevel@tonic-gate zerror(gettext("WARNING: pool '%s' not found. " 20070Sstevel@tonic-gate "using default pool"), poolname); 20080Sstevel@tonic-gate } 20090Sstevel@tonic-gate 20100Sstevel@tonic-gate return (Z_OK); 20110Sstevel@tonic-gate } 20120Sstevel@tonic-gate 20130Sstevel@tonic-gate static int 2014823Sgjelinek verify_ipd(zone_dochandle_t handle) 2015823Sgjelinek { 2016823Sgjelinek int return_code = Z_OK; 2017823Sgjelinek struct zone_fstab fstab; 2018823Sgjelinek struct stat st; 2019823Sgjelinek char specdir[MAXPATHLEN]; 2020823Sgjelinek 2021823Sgjelinek if (zonecfg_setipdent(handle) != Z_OK) { 2022924Sgjelinek /* 2023924Sgjelinek * TRANSLATION_NOTE 2024924Sgjelinek * inherit-pkg-dirs is a literal that should not be translated. 2025924Sgjelinek */ 2026924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 2027823Sgjelinek "inherit-pkg-dirs: unable to enumerate mounts\n")); 2028823Sgjelinek return (Z_ERR); 2029823Sgjelinek } 2030823Sgjelinek while (zonecfg_getipdent(handle, &fstab) == Z_OK) { 2031823Sgjelinek /* 2032823Sgjelinek * Verify fs_dir exists. 2033823Sgjelinek */ 2034823Sgjelinek (void) snprintf(specdir, sizeof (specdir), "%s%s", 2035823Sgjelinek zonecfg_get_root(), fstab.zone_fs_dir); 2036823Sgjelinek if (stat(specdir, &st) != 0) { 2037924Sgjelinek /* 2038924Sgjelinek * TRANSLATION_NOTE 2039924Sgjelinek * inherit-pkg-dir is a literal that should not be 2040924Sgjelinek * translated. 2041924Sgjelinek */ 2042924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 2043823Sgjelinek "inherit-pkg-dir %s: %s\n"), 2044823Sgjelinek fstab.zone_fs_dir, strerror(errno)); 2045823Sgjelinek return_code = Z_ERR; 2046823Sgjelinek } 2047823Sgjelinek if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 2048924Sgjelinek /* 2049924Sgjelinek * TRANSLATION_NOTE 2050924Sgjelinek * inherit-pkg-dir and NFS are literals that should 2051924Sgjelinek * not be translated. 2052924Sgjelinek */ 2053823Sgjelinek (void) fprintf(stderr, gettext("cannot verify " 20541867Sgjelinek "inherit-pkg-dir %s: NFS mounted file system.\n" 20551867Sgjelinek "\tA local file system must be used.\n"), 2056823Sgjelinek fstab.zone_fs_dir); 2057823Sgjelinek return_code = Z_ERR; 2058823Sgjelinek } 2059823Sgjelinek } 2060823Sgjelinek (void) zonecfg_endipdent(handle); 2061823Sgjelinek 2062823Sgjelinek return (return_code); 2063823Sgjelinek } 2064823Sgjelinek 20651393Slling /* 20661867Sgjelinek * Verify that the special device/file system exists and is valid. 20671393Slling */ 20681393Slling static int 20691393Slling verify_fs_special(struct zone_fstab *fstab) 20701393Slling { 20711393Slling struct stat st; 20721393Slling 20731393Slling if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) 20741393Slling return (verify_fs_zfs(fstab)); 20751393Slling 20761393Slling if (stat(fstab->zone_fs_special, &st) != 0) { 20771397Slling (void) fprintf(stderr, gettext("could not verify fs " 20781393Slling "%s: could not access %s: %s\n"), fstab->zone_fs_dir, 20791393Slling fstab->zone_fs_special, strerror(errno)); 20801393Slling return (Z_ERR); 20811393Slling } 20821393Slling 20831393Slling if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 20841393Slling /* 20851393Slling * TRANSLATION_NOTE 20861393Slling * fs and NFS are literals that should 20871393Slling * not be translated. 20881393Slling */ 20891393Slling (void) fprintf(stderr, gettext("cannot verify " 20901867Sgjelinek "fs %s: NFS mounted file system.\n" 20911867Sgjelinek "\tA local file system must be used.\n"), 20921393Slling fstab->zone_fs_special); 20931393Slling return (Z_ERR); 20941393Slling } 20951393Slling 20961393Slling return (Z_OK); 20971393Slling } 20981393Slling 2099823Sgjelinek static int 21000Sstevel@tonic-gate verify_filesystems(zone_dochandle_t handle) 21010Sstevel@tonic-gate { 21020Sstevel@tonic-gate int return_code = Z_OK; 21030Sstevel@tonic-gate struct zone_fstab fstab; 21040Sstevel@tonic-gate char cmdbuf[MAXPATHLEN]; 21050Sstevel@tonic-gate struct stat st; 21060Sstevel@tonic-gate 21070Sstevel@tonic-gate /* 21080Sstevel@tonic-gate * No need to verify inherit-pkg-dir fs types, as their type is 21090Sstevel@tonic-gate * implicitly lofs, which is known. Therefore, the types are only 21101867Sgjelinek * verified for regular file systems below. 21110Sstevel@tonic-gate * 21120Sstevel@tonic-gate * Since the actual mount point is not known until the dependent mounts 21130Sstevel@tonic-gate * are performed, we don't attempt any path validation here: that will 21140Sstevel@tonic-gate * happen later when zoneadmd actually does the mounts. 21150Sstevel@tonic-gate */ 21160Sstevel@tonic-gate if (zonecfg_setfsent(handle) != Z_OK) { 21171867Sgjelinek (void) fprintf(stderr, gettext("could not verify file systems: " 21180Sstevel@tonic-gate "unable to enumerate mounts\n")); 21190Sstevel@tonic-gate return (Z_ERR); 21200Sstevel@tonic-gate } 21210Sstevel@tonic-gate while (zonecfg_getfsent(handle, &fstab) == Z_OK) { 21220Sstevel@tonic-gate if (!zonecfg_valid_fs_type(fstab.zone_fs_type)) { 21230Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21240Sstevel@tonic-gate "type %s is not allowed.\n"), fstab.zone_fs_dir, 21250Sstevel@tonic-gate fstab.zone_fs_type); 21260Sstevel@tonic-gate return_code = Z_ERR; 21270Sstevel@tonic-gate goto next_fs; 21280Sstevel@tonic-gate } 21290Sstevel@tonic-gate /* 21300Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/mount exists. 21310Sstevel@tonic-gate */ 21320Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/mount", 21330Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 21340Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21350Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 21360Sstevel@tonic-gate fstab.zone_fs_type); 21370Sstevel@tonic-gate return_code = Z_ERR; 21380Sstevel@tonic-gate goto next_fs; 21390Sstevel@tonic-gate } 21400Sstevel@tonic-gate if (stat(cmdbuf, &st) != 0) { 2141924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2142924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 21430Sstevel@tonic-gate cmdbuf, strerror(errno)); 21440Sstevel@tonic-gate return_code = Z_ERR; 21450Sstevel@tonic-gate goto next_fs; 21460Sstevel@tonic-gate } 21470Sstevel@tonic-gate if (!S_ISREG(st.st_mode)) { 2148924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2149924Sgjelinek "%s: %s is not a regular file\n"), 2150924Sgjelinek fstab.zone_fs_dir, cmdbuf); 21510Sstevel@tonic-gate return_code = Z_ERR; 21520Sstevel@tonic-gate goto next_fs; 21530Sstevel@tonic-gate } 21540Sstevel@tonic-gate /* 21550Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/fsck exists iff zone_fs_raw is 21560Sstevel@tonic-gate * set. 21570Sstevel@tonic-gate */ 21580Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/fsck", 21590Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 21600Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21610Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 21620Sstevel@tonic-gate fstab.zone_fs_type); 21630Sstevel@tonic-gate return_code = Z_ERR; 21640Sstevel@tonic-gate goto next_fs; 21650Sstevel@tonic-gate } 21660Sstevel@tonic-gate if (fstab.zone_fs_raw[0] == '\0' && stat(cmdbuf, &st) == 0) { 2167924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2168924Sgjelinek "%s: must specify 'raw' device for %s " 21691867Sgjelinek "file systems\n"), 21700Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 21710Sstevel@tonic-gate return_code = Z_ERR; 21720Sstevel@tonic-gate goto next_fs; 21730Sstevel@tonic-gate } 21740Sstevel@tonic-gate if (fstab.zone_fs_raw[0] != '\0' && 21750Sstevel@tonic-gate (stat(cmdbuf, &st) != 0 || !S_ISREG(st.st_mode))) { 21760Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21770Sstevel@tonic-gate "'raw' device specified but " 21780Sstevel@tonic-gate "no fsck executable exists for %s\n"), 21790Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 21800Sstevel@tonic-gate return_code = Z_ERR; 21810Sstevel@tonic-gate goto next_fs; 21820Sstevel@tonic-gate } 21831393Slling 21841393Slling /* Verify fs_special. */ 21851393Slling if ((return_code = verify_fs_special(&fstab)) != Z_OK) 2186823Sgjelinek goto next_fs; 21871393Slling 21881393Slling /* Verify fs_raw. */ 2189823Sgjelinek if (fstab.zone_fs_raw[0] != '\0' && 2190823Sgjelinek stat(fstab.zone_fs_raw, &st) != 0) { 2191924Sgjelinek /* 2192924Sgjelinek * TRANSLATION_NOTE 2193924Sgjelinek * fs is a literal that should not be translated. 2194924Sgjelinek */ 2195924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2196924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 2197823Sgjelinek fstab.zone_fs_raw, strerror(errno)); 2198823Sgjelinek return_code = Z_ERR; 2199823Sgjelinek goto next_fs; 2200823Sgjelinek } 22010Sstevel@tonic-gate next_fs: 22020Sstevel@tonic-gate zonecfg_free_fs_option_list(fstab.zone_fs_options); 22030Sstevel@tonic-gate } 22040Sstevel@tonic-gate (void) zonecfg_endfsent(handle); 22050Sstevel@tonic-gate 22060Sstevel@tonic-gate return (return_code); 22070Sstevel@tonic-gate } 22080Sstevel@tonic-gate 22090Sstevel@tonic-gate static int 22101645Scomay verify_limitpriv(zone_dochandle_t handle) 22111645Scomay { 22121645Scomay char *privname = NULL; 22131645Scomay int err; 22141645Scomay priv_set_t *privs; 22151645Scomay 22161645Scomay if ((privs = priv_allocset()) == NULL) { 22171645Scomay zperror(gettext("failed to allocate privilege set"), B_FALSE); 22181645Scomay return (Z_NOMEM); 22191645Scomay } 22201645Scomay err = zonecfg_get_privset(handle, privs, &privname); 22211645Scomay switch (err) { 22221645Scomay case Z_OK: 22231645Scomay break; 22241645Scomay case Z_PRIV_PROHIBITED: 22251645Scomay (void) fprintf(stderr, gettext("privilege \"%s\" is not " 22261645Scomay "permitted within the zone's privilege set\n"), privname); 22271645Scomay break; 22281645Scomay case Z_PRIV_REQUIRED: 22291645Scomay (void) fprintf(stderr, gettext("required privilege \"%s\" is " 22301645Scomay "missing from the zone's privilege set\n"), privname); 22311645Scomay break; 22321645Scomay case Z_PRIV_UNKNOWN: 22331645Scomay (void) fprintf(stderr, gettext("unknown privilege \"%s\" " 22341645Scomay "specified in the zone's privilege set\n"), privname); 22351645Scomay break; 22361645Scomay default: 22371645Scomay zperror( 22381645Scomay gettext("failed to determine the zone's privilege set"), 22391645Scomay B_TRUE); 22401645Scomay break; 22411645Scomay } 22421645Scomay free(privname); 22431645Scomay priv_freeset(privs); 22441645Scomay return (err); 22451645Scomay } 22461645Scomay 22471915Sgjelinek static void 22481915Sgjelinek free_local_netifs(int if_cnt, struct net_if **if_list) 22491915Sgjelinek { 22501915Sgjelinek int i; 22511915Sgjelinek 22521915Sgjelinek for (i = 0; i < if_cnt; i++) { 22531915Sgjelinek free(if_list[i]->name); 22541915Sgjelinek free(if_list[i]); 22551915Sgjelinek } 22561915Sgjelinek free(if_list); 22571915Sgjelinek } 22581915Sgjelinek 22591915Sgjelinek /* 22601915Sgjelinek * Get a list of the network interfaces, along with their address families, 22611915Sgjelinek * that are plumbed in the global zone. See if_tcp(7p) for a description 22621915Sgjelinek * of the ioctls used here. 22631915Sgjelinek */ 22641915Sgjelinek static int 22651915Sgjelinek get_local_netifs(int *if_cnt, struct net_if ***if_list) 22661915Sgjelinek { 22671915Sgjelinek int s; 22681915Sgjelinek int i; 22691915Sgjelinek int res = Z_OK; 22701915Sgjelinek int space_needed; 22711915Sgjelinek int cnt = 0; 22721915Sgjelinek struct lifnum if_num; 22731915Sgjelinek struct lifconf if_conf; 22741915Sgjelinek struct lifreq *if_reqp; 22751915Sgjelinek char *if_buf; 22761915Sgjelinek struct net_if **local_ifs = NULL; 22771915Sgjelinek 22781915Sgjelinek *if_cnt = 0; 22791915Sgjelinek *if_list = NULL; 22801915Sgjelinek 22811915Sgjelinek if ((s = socket(SOCKET_AF(AF_INET), SOCK_DGRAM, 0)) < 0) 22821915Sgjelinek return (Z_ERR); 22831915Sgjelinek 22841915Sgjelinek /* 22851915Sgjelinek * Come back here in the unlikely event that the number of interfaces 22861915Sgjelinek * increases between the time we get the count and the time we do the 22871915Sgjelinek * SIOCGLIFCONF ioctl. 22881915Sgjelinek */ 22891915Sgjelinek retry: 22901915Sgjelinek /* Get the number of interfaces. */ 22911915Sgjelinek if_num.lifn_family = AF_UNSPEC; 22921915Sgjelinek if_num.lifn_flags = LIFC_NOXMIT; 22931915Sgjelinek if (ioctl(s, SIOCGLIFNUM, &if_num) < 0) { 22941915Sgjelinek (void) close(s); 22951915Sgjelinek return (Z_ERR); 22961915Sgjelinek } 22971915Sgjelinek 22981915Sgjelinek /* Get the interface configuration list. */ 22991915Sgjelinek space_needed = if_num.lifn_count * sizeof (struct lifreq); 23001915Sgjelinek if ((if_buf = malloc(space_needed)) == NULL) { 23011915Sgjelinek (void) close(s); 23021915Sgjelinek return (Z_ERR); 23031915Sgjelinek } 23041915Sgjelinek if_conf.lifc_family = AF_UNSPEC; 23051915Sgjelinek if_conf.lifc_flags = LIFC_NOXMIT; 23061915Sgjelinek if_conf.lifc_len = space_needed; 23071915Sgjelinek if_conf.lifc_buf = if_buf; 23081915Sgjelinek if (ioctl(s, SIOCGLIFCONF, &if_conf) < 0) { 23091915Sgjelinek free(if_buf); 23101915Sgjelinek /* 23111915Sgjelinek * SIOCGLIFCONF returns EINVAL if the buffer we passed in is 23121915Sgjelinek * too small. In this case go back and get the new if cnt. 23131915Sgjelinek */ 23141915Sgjelinek if (errno == EINVAL) 23151915Sgjelinek goto retry; 23161915Sgjelinek 23171915Sgjelinek (void) close(s); 23181915Sgjelinek return (Z_ERR); 23191915Sgjelinek } 23201915Sgjelinek (void) close(s); 23211915Sgjelinek 23221915Sgjelinek /* Get the name and address family for each interface. */ 23231915Sgjelinek if_reqp = if_conf.lifc_req; 23241915Sgjelinek for (i = 0; i < (if_conf.lifc_len / sizeof (struct lifreq)); i++) { 23251915Sgjelinek struct net_if **p; 23261915Sgjelinek struct lifreq req; 23271915Sgjelinek 23281915Sgjelinek if (strcmp(LOOPBACK_IF, if_reqp->lifr_name) == 0) { 23291915Sgjelinek if_reqp++; 23301915Sgjelinek continue; 23311915Sgjelinek } 23321915Sgjelinek 23331915Sgjelinek if ((s = socket(SOCKET_AF(if_reqp->lifr_addr.ss_family), 23341915Sgjelinek SOCK_DGRAM, 0)) == -1) { 23351915Sgjelinek res = Z_ERR; 23361915Sgjelinek break; 23371915Sgjelinek } 23381915Sgjelinek 23391915Sgjelinek (void) strncpy(req.lifr_name, if_reqp->lifr_name, 23401915Sgjelinek sizeof (req.lifr_name)); 23411915Sgjelinek if (ioctl(s, SIOCGLIFADDR, &req) < 0) { 23421915Sgjelinek (void) close(s); 23431915Sgjelinek if_reqp++; 23441915Sgjelinek continue; 23451915Sgjelinek } 23461915Sgjelinek 23471915Sgjelinek if ((p = (struct net_if **)realloc(local_ifs, 23481915Sgjelinek sizeof (struct net_if *) * (cnt + 1))) == NULL) { 23491915Sgjelinek res = Z_ERR; 23501915Sgjelinek break; 23511915Sgjelinek } 23521915Sgjelinek local_ifs = p; 23531915Sgjelinek 23541915Sgjelinek if ((local_ifs[cnt] = malloc(sizeof (struct net_if))) == NULL) { 23551915Sgjelinek res = Z_ERR; 23561915Sgjelinek break; 23571915Sgjelinek } 23581915Sgjelinek 23591915Sgjelinek if ((local_ifs[cnt]->name = strdup(if_reqp->lifr_name)) 23601915Sgjelinek == NULL) { 23611915Sgjelinek free(local_ifs[cnt]); 23621915Sgjelinek res = Z_ERR; 23631915Sgjelinek break; 23641915Sgjelinek } 23651915Sgjelinek local_ifs[cnt]->af = req.lifr_addr.ss_family; 23661915Sgjelinek cnt++; 23671915Sgjelinek 23681915Sgjelinek (void) close(s); 23691915Sgjelinek if_reqp++; 23701915Sgjelinek } 23711915Sgjelinek 23721915Sgjelinek free(if_buf); 23731915Sgjelinek 23741915Sgjelinek if (res != Z_OK) { 23751915Sgjelinek free_local_netifs(cnt, local_ifs); 23761915Sgjelinek } else { 23771915Sgjelinek *if_cnt = cnt; 23781915Sgjelinek *if_list = local_ifs; 23791915Sgjelinek } 23801915Sgjelinek 23811915Sgjelinek return (res); 23821915Sgjelinek } 23831915Sgjelinek 23841915Sgjelinek static char * 23851915Sgjelinek af2str(int af) 23861915Sgjelinek { 23871915Sgjelinek switch (af) { 23881915Sgjelinek case AF_INET: 23891915Sgjelinek return ("IPv4"); 23901915Sgjelinek case AF_INET6: 23911915Sgjelinek return ("IPv6"); 23921915Sgjelinek default: 23931915Sgjelinek return ("Unknown"); 23941915Sgjelinek } 23951915Sgjelinek } 23961915Sgjelinek 23971915Sgjelinek /* 23981915Sgjelinek * Cross check the network interface name and address family with the 23991915Sgjelinek * interfaces that are set up in the global zone so that we can print the 24001915Sgjelinek * appropriate error message. 24011915Sgjelinek */ 24021915Sgjelinek static void 24031915Sgjelinek print_net_err(char *phys, char *addr, int af, char *msg) 24041915Sgjelinek { 24051915Sgjelinek int i; 24061915Sgjelinek int local_if_cnt = 0; 24071915Sgjelinek struct net_if **local_ifs = NULL; 24081915Sgjelinek boolean_t found_if = B_FALSE; 24091915Sgjelinek boolean_t found_af = B_FALSE; 24101915Sgjelinek 24111915Sgjelinek if (get_local_netifs(&local_if_cnt, &local_ifs) != Z_OK) { 24121915Sgjelinek (void) fprintf(stderr, 24131915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 24141915Sgjelinek "net", "address", addr, "physical", phys, msg); 24151915Sgjelinek return; 24161915Sgjelinek } 24171915Sgjelinek 24181915Sgjelinek for (i = 0; i < local_if_cnt; i++) { 24191915Sgjelinek if (strcmp(phys, local_ifs[i]->name) == 0) { 24201915Sgjelinek found_if = B_TRUE; 24211915Sgjelinek if (af == local_ifs[i]->af) { 24221915Sgjelinek found_af = B_TRUE; 24231915Sgjelinek break; 24241915Sgjelinek } 24251915Sgjelinek } 24261915Sgjelinek } 24271915Sgjelinek 24281915Sgjelinek free_local_netifs(local_if_cnt, local_ifs); 24291915Sgjelinek 24301915Sgjelinek if (!found_if) { 24311915Sgjelinek (void) fprintf(stderr, 24321915Sgjelinek gettext("could not verify %s %s=%s\n\t" 24331915Sgjelinek "network interface %s is not plumbed in the global zone\n"), 24341915Sgjelinek "net", "physical", phys, phys); 24351915Sgjelinek return; 24361915Sgjelinek } 24371915Sgjelinek 24381915Sgjelinek /* 24391915Sgjelinek * Print this error if we were unable to find the address family 24401915Sgjelinek * for this interface. If the af variable is not initialized to 24411915Sgjelinek * to something meaningful by the caller (not AF_UNSPEC) then we 24421915Sgjelinek * also skip this message since it wouldn't be informative. 24431915Sgjelinek */ 24441915Sgjelinek if (!found_af && af != AF_UNSPEC) { 24451915Sgjelinek (void) fprintf(stderr, 24461915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\tthe %s address " 24471915Sgjelinek "family is not configured on this interface in the\n\t" 24481915Sgjelinek "global zone\n"), 24491915Sgjelinek "net", "address", addr, "physical", phys, af2str(af)); 24501915Sgjelinek return; 24511915Sgjelinek } 24521915Sgjelinek 24531915Sgjelinek (void) fprintf(stderr, 24541915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 24551915Sgjelinek "net", "address", addr, "physical", phys, msg); 24561915Sgjelinek } 24571915Sgjelinek 24581645Scomay static int 24592078Sgjelinek verify_handle(int cmd_num, zone_dochandle_t handle) 24600Sstevel@tonic-gate { 24610Sstevel@tonic-gate struct zone_nwiftab nwiftab; 24620Sstevel@tonic-gate int return_code = Z_OK; 24630Sstevel@tonic-gate int err; 2464766Scarlsonj boolean_t in_alt_root; 24650Sstevel@tonic-gate 2466766Scarlsonj in_alt_root = zonecfg_in_alt_root(); 2467766Scarlsonj if (in_alt_root) 2468766Scarlsonj goto no_net; 2469766Scarlsonj 24700Sstevel@tonic-gate if ((err = zonecfg_setnwifent(handle)) != Z_OK) { 24710Sstevel@tonic-gate errno = err; 24720Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 24730Sstevel@tonic-gate zonecfg_fini_handle(handle); 24740Sstevel@tonic-gate return (Z_ERR); 24750Sstevel@tonic-gate } 24760Sstevel@tonic-gate while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { 24770Sstevel@tonic-gate struct lifreq lifr; 24781915Sgjelinek sa_family_t af = AF_UNSPEC; 24790Sstevel@tonic-gate int so, res; 24800Sstevel@tonic-gate 24810Sstevel@tonic-gate /* skip any loopback interfaces */ 24820Sstevel@tonic-gate if (strcmp(nwiftab.zone_nwif_physical, "lo0") == 0) 24830Sstevel@tonic-gate continue; 24840Sstevel@tonic-gate if ((res = zonecfg_valid_net_address(nwiftab.zone_nwif_address, 24850Sstevel@tonic-gate &lifr)) != Z_OK) { 24861915Sgjelinek print_net_err(nwiftab.zone_nwif_physical, 24871915Sgjelinek nwiftab.zone_nwif_address, af, 24881915Sgjelinek zonecfg_strerror(res)); 24890Sstevel@tonic-gate return_code = Z_ERR; 24900Sstevel@tonic-gate continue; 24910Sstevel@tonic-gate } 24920Sstevel@tonic-gate af = lifr.lifr_addr.ss_family; 24930Sstevel@tonic-gate (void) memset(&lifr, 0, sizeof (lifr)); 24940Sstevel@tonic-gate (void) strlcpy(lifr.lifr_name, nwiftab.zone_nwif_physical, 24950Sstevel@tonic-gate sizeof (lifr.lifr_name)); 24960Sstevel@tonic-gate lifr.lifr_addr.ss_family = af; 24970Sstevel@tonic-gate if ((so = socket(af, SOCK_DGRAM, 0)) < 0) { 24980Sstevel@tonic-gate (void) fprintf(stderr, gettext("could not verify %s " 24990Sstevel@tonic-gate "%s=%s %s=%s: could not get socket: %s\n"), "net", 25000Sstevel@tonic-gate "address", nwiftab.zone_nwif_address, "physical", 25010Sstevel@tonic-gate nwiftab.zone_nwif_physical, strerror(errno)); 25020Sstevel@tonic-gate return_code = Z_ERR; 25030Sstevel@tonic-gate continue; 25040Sstevel@tonic-gate } 25051915Sgjelinek if (ioctl(so, SIOCGLIFFLAGS, &lifr) < 0) { 2506*2611Svp157776 /* 2507*2611Svp157776 * The interface failed to come up. We continue on 2508*2611Svp157776 * anyway for the sake of consistency: a zone is not 2509*2611Svp157776 * shut down if the interface fails any time after 2510*2611Svp157776 * boot, nor does the global zone fail to boot if an 2511*2611Svp157776 * interface fails. 2512*2611Svp157776 */ 2513*2611Svp157776 (void) fprintf(stderr, 2514*2611Svp157776 gettext("WARNING: skipping interface '%s' which " 2515*2611Svp157776 "may not be present/plumbed in the global zone.\n"), 2516*2611Svp157776 nwiftab.zone_nwif_physical); 2517*2611Svp157776 25180Sstevel@tonic-gate } 25190Sstevel@tonic-gate (void) close(so); 25200Sstevel@tonic-gate } 25210Sstevel@tonic-gate (void) zonecfg_endnwifent(handle); 2522766Scarlsonj no_net: 25230Sstevel@tonic-gate 25241931Sgjelinek /* verify that lofs has not been excluded from the kernel */ 25252078Sgjelinek if (!(cmd_num == CMD_DETACH || cmd_num == CMD_ATTACH || 25262078Sgjelinek cmd_num == CMD_MOVE || cmd_num == CMD_CLONE) && 25272078Sgjelinek modctl(MODLOAD, 1, "fs/lofs", NULL) != 0) { 25281931Sgjelinek if (errno == ENXIO) 25291931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 25301931Sgjelinek "lofs(7FS): possibly excluded in /etc/system\n")); 25311931Sgjelinek else 25321931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 25331931Sgjelinek "lofs(7FS): %s\n"), strerror(errno)); 25341931Sgjelinek return_code = Z_ERR; 25351931Sgjelinek } 25361931Sgjelinek 25370Sstevel@tonic-gate if (verify_filesystems(handle) != Z_OK) 25380Sstevel@tonic-gate return_code = Z_ERR; 2539823Sgjelinek if (verify_ipd(handle) != Z_OK) 2540823Sgjelinek return_code = Z_ERR; 2541766Scarlsonj if (!in_alt_root && verify_rctls(handle) != Z_OK) 25420Sstevel@tonic-gate return_code = Z_ERR; 2543766Scarlsonj if (!in_alt_root && verify_pool(handle) != Z_OK) 25440Sstevel@tonic-gate return_code = Z_ERR; 2545789Sahrens if (!in_alt_root && verify_datasets(handle) != Z_OK) 2546789Sahrens return_code = Z_ERR; 25471645Scomay 25481645Scomay /* 25491645Scomay * As the "mount" command is used for patching/upgrading of zones 25501645Scomay * or other maintenance processes, the zone's privilege set is not 25511645Scomay * checked in this case. Instead, the default, safe set of 25521645Scomay * privileges will be used when this zone is created in the 25531645Scomay * kernel. 25541645Scomay */ 25551645Scomay if (!in_alt_root && cmd_num != CMD_MOUNT && 25561645Scomay verify_limitpriv(handle) != Z_OK) 25571645Scomay return_code = Z_ERR; 25582078Sgjelinek 25592078Sgjelinek return (return_code); 25602078Sgjelinek } 25612078Sgjelinek 25622078Sgjelinek static int 25632078Sgjelinek verify_details(int cmd_num) 25642078Sgjelinek { 25652078Sgjelinek zone_dochandle_t handle; 25662078Sgjelinek char zonepath[MAXPATHLEN], checkpath[MAXPATHLEN]; 25672078Sgjelinek int return_code = Z_OK; 25682078Sgjelinek int err; 25692078Sgjelinek 25702078Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 25712078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 25722078Sgjelinek return (Z_ERR); 25732078Sgjelinek } 25742078Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 25752078Sgjelinek errno = err; 25762078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 25772078Sgjelinek zonecfg_fini_handle(handle); 25782078Sgjelinek return (Z_ERR); 25792078Sgjelinek } 25802078Sgjelinek if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) != 25812078Sgjelinek Z_OK) { 25822078Sgjelinek errno = err; 25832078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 25842078Sgjelinek zonecfg_fini_handle(handle); 25852078Sgjelinek return (Z_ERR); 25862078Sgjelinek } 25872078Sgjelinek /* 25882078Sgjelinek * zonecfg_get_zonepath() gets its data from the XML repository. 25892078Sgjelinek * Verify this against the index file, which is checked first by 25902078Sgjelinek * zone_get_zonepath(). If they don't match, bail out. 25912078Sgjelinek */ 25922078Sgjelinek if ((err = zone_get_zonepath(target_zone, checkpath, 25932078Sgjelinek sizeof (checkpath))) != Z_OK) { 25942078Sgjelinek errno = err; 25952078Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 25962078Sgjelinek return (Z_ERR); 25972078Sgjelinek } 25982078Sgjelinek if (strcmp(zonepath, checkpath) != 0) { 25992078Sgjelinek /* 26002078Sgjelinek * TRANSLATION_NOTE 26012078Sgjelinek * XML and zonepath are literals that should not be translated. 26022078Sgjelinek */ 26032078Sgjelinek (void) fprintf(stderr, gettext("The XML repository has " 26042078Sgjelinek "zonepath '%s',\nbut the index file has zonepath '%s'.\n" 26052078Sgjelinek "These must match, so fix the incorrect entry.\n"), 26062078Sgjelinek zonepath, checkpath); 26072078Sgjelinek return (Z_ERR); 26082078Sgjelinek } 26092078Sgjelinek if (validate_zonepath(zonepath, cmd_num) != Z_OK) { 26102078Sgjelinek (void) fprintf(stderr, gettext("could not verify zonepath %s " 26112078Sgjelinek "because of the above errors.\n"), zonepath); 26122078Sgjelinek return_code = Z_ERR; 26132078Sgjelinek } 26142078Sgjelinek 26152078Sgjelinek if (verify_handle(cmd_num, handle) != Z_OK) 26162078Sgjelinek return_code = Z_ERR; 26172078Sgjelinek 26180Sstevel@tonic-gate zonecfg_fini_handle(handle); 26190Sstevel@tonic-gate if (return_code == Z_ERR) 26200Sstevel@tonic-gate (void) fprintf(stderr, 26210Sstevel@tonic-gate gettext("%s: zone %s failed to verify\n"), 26220Sstevel@tonic-gate execname, target_zone); 26230Sstevel@tonic-gate return (return_code); 26240Sstevel@tonic-gate } 26250Sstevel@tonic-gate 26260Sstevel@tonic-gate static int 26270Sstevel@tonic-gate verify_func(int argc, char *argv[]) 26280Sstevel@tonic-gate { 26290Sstevel@tonic-gate int arg; 26300Sstevel@tonic-gate 26310Sstevel@tonic-gate optind = 0; 26320Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 26330Sstevel@tonic-gate switch (arg) { 26340Sstevel@tonic-gate case '?': 26350Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 26360Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 26370Sstevel@tonic-gate default: 26380Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 26390Sstevel@tonic-gate return (Z_USAGE); 26400Sstevel@tonic-gate } 26410Sstevel@tonic-gate } 26420Sstevel@tonic-gate if (argc > optind) { 26430Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 26440Sstevel@tonic-gate return (Z_USAGE); 26450Sstevel@tonic-gate } 26460Sstevel@tonic-gate if (sanity_check(target_zone, CMD_VERIFY, B_FALSE, B_FALSE) != Z_OK) 26470Sstevel@tonic-gate return (Z_ERR); 26480Sstevel@tonic-gate return (verify_details(CMD_VERIFY)); 26490Sstevel@tonic-gate } 26500Sstevel@tonic-gate 26510Sstevel@tonic-gate #define LUCREATEZONE "/usr/lib/lu/lucreatezone" 26520Sstevel@tonic-gate 26530Sstevel@tonic-gate static int 26540Sstevel@tonic-gate install_func(int argc, char *argv[]) 26550Sstevel@tonic-gate { 26560Sstevel@tonic-gate /* 9: "exec " and " -z " */ 26570Sstevel@tonic-gate char cmdbuf[sizeof (LUCREATEZONE) + ZONENAME_MAX + 9]; 26580Sstevel@tonic-gate int lockfd; 26590Sstevel@tonic-gate int err, arg; 26600Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 26610Sstevel@tonic-gate int status; 26621867Sgjelinek boolean_t nodataset = B_FALSE; 26630Sstevel@tonic-gate 2664766Scarlsonj if (zonecfg_in_alt_root()) { 2665766Scarlsonj zerror(gettext("cannot install zone in alternate root")); 2666766Scarlsonj return (Z_ERR); 2667766Scarlsonj } 2668766Scarlsonj 26690Sstevel@tonic-gate optind = 0; 26701867Sgjelinek if ((arg = getopt(argc, argv, "?x:")) != EOF) { 26710Sstevel@tonic-gate switch (arg) { 26720Sstevel@tonic-gate case '?': 26730Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 26740Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 26751867Sgjelinek case 'x': 26761867Sgjelinek if (strcmp(optarg, "nodataset") != 0) { 26771867Sgjelinek sub_usage(SHELP_INSTALL, CMD_INSTALL); 26781867Sgjelinek return (Z_USAGE); 26791867Sgjelinek } 26801867Sgjelinek nodataset = B_TRUE; 26811867Sgjelinek break; 26820Sstevel@tonic-gate default: 26830Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 26840Sstevel@tonic-gate return (Z_USAGE); 26850Sstevel@tonic-gate } 26860Sstevel@tonic-gate } 26870Sstevel@tonic-gate if (argc > optind) { 26880Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 26890Sstevel@tonic-gate return (Z_USAGE); 26900Sstevel@tonic-gate } 26910Sstevel@tonic-gate if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE) != Z_OK) 26920Sstevel@tonic-gate return (Z_ERR); 26930Sstevel@tonic-gate if (verify_details(CMD_INSTALL) != Z_OK) 26940Sstevel@tonic-gate return (Z_ERR); 26950Sstevel@tonic-gate 26960Sstevel@tonic-gate if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 26970Sstevel@tonic-gate zerror(gettext("another %s may have an operation in progress."), 26981300Sgjelinek "zoneadm"); 26990Sstevel@tonic-gate return (Z_ERR); 27000Sstevel@tonic-gate } 27010Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 27020Sstevel@tonic-gate if (err != Z_OK) { 27030Sstevel@tonic-gate errno = err; 27040Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 27050Sstevel@tonic-gate goto done; 27060Sstevel@tonic-gate } 27070Sstevel@tonic-gate 27080Sstevel@tonic-gate /* 27090Sstevel@tonic-gate * According to the Application Packaging Developer's Guide, a 27100Sstevel@tonic-gate * "checkinstall" script when included in a package is executed as 27110Sstevel@tonic-gate * the user "install", if such a user exists, or by the user 27120Sstevel@tonic-gate * "nobody". In order to support this dubious behavior, the path 27130Sstevel@tonic-gate * to the zone being constructed is opened up during the life of 27140Sstevel@tonic-gate * the command laying down the zone's root file system. Once this 27150Sstevel@tonic-gate * has completed, regardless of whether it was successful, the 27160Sstevel@tonic-gate * path to the zone is again restricted. 27170Sstevel@tonic-gate */ 27180Sstevel@tonic-gate if ((err = zone_get_zonepath(target_zone, zonepath, 27190Sstevel@tonic-gate sizeof (zonepath))) != Z_OK) { 27200Sstevel@tonic-gate errno = err; 27210Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 27220Sstevel@tonic-gate goto done; 27230Sstevel@tonic-gate } 27241867Sgjelinek 27251867Sgjelinek if (!nodataset) 27261867Sgjelinek create_zfs_zonepath(zonepath); 27271867Sgjelinek 27280Sstevel@tonic-gate if (chmod(zonepath, DEFAULT_DIR_MODE) != 0) { 27290Sstevel@tonic-gate zperror(zonepath, B_FALSE); 27300Sstevel@tonic-gate err = Z_ERR; 27310Sstevel@tonic-gate goto done; 27320Sstevel@tonic-gate } 27330Sstevel@tonic-gate 27340Sstevel@tonic-gate /* 27350Sstevel@tonic-gate * "exec" the command so that the returned status is that of 27360Sstevel@tonic-gate * LUCREATEZONE and not the shell. 27370Sstevel@tonic-gate */ 27380Sstevel@tonic-gate (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " LUCREATEZONE " -z %s", 27390Sstevel@tonic-gate target_zone); 27400Sstevel@tonic-gate status = do_subproc(cmdbuf); 27410Sstevel@tonic-gate if (chmod(zonepath, S_IRWXU) != 0) { 27420Sstevel@tonic-gate zperror(zonepath, B_FALSE); 27430Sstevel@tonic-gate err = Z_ERR; 27440Sstevel@tonic-gate goto done; 27450Sstevel@tonic-gate } 27460Sstevel@tonic-gate if ((err = subproc_status(LUCREATEZONE, status)) != Z_OK) 27470Sstevel@tonic-gate goto done; 27480Sstevel@tonic-gate 27490Sstevel@tonic-gate if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 27500Sstevel@tonic-gate errno = err; 27510Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 27520Sstevel@tonic-gate goto done; 27530Sstevel@tonic-gate } 27540Sstevel@tonic-gate 27550Sstevel@tonic-gate done: 27560Sstevel@tonic-gate release_lock_file(lockfd); 27570Sstevel@tonic-gate return ((err == Z_OK) ? Z_OK : Z_ERR); 27580Sstevel@tonic-gate } 27590Sstevel@tonic-gate 27600Sstevel@tonic-gate /* 27611300Sgjelinek * Check that the inherited pkg dirs are the same for the clone and its source. 27621300Sgjelinek * The easiest way to do that is check that the list of ipds is the same 27631300Sgjelinek * by matching each one against the other. This algorithm should be fine since 27641300Sgjelinek * the list of ipds should not be that long. 27651300Sgjelinek */ 27661300Sgjelinek static int 27671300Sgjelinek valid_ipd_clone(zone_dochandle_t s_handle, char *source_zone, 27681300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 27691300Sgjelinek { 27701300Sgjelinek int err; 27711300Sgjelinek int res = Z_OK; 27721300Sgjelinek int s_cnt = 0; 27731300Sgjelinek int t_cnt = 0; 27741300Sgjelinek struct zone_fstab s_fstab; 27751300Sgjelinek struct zone_fstab t_fstab; 27761300Sgjelinek 27771300Sgjelinek /* 27781300Sgjelinek * First check the source of the clone against the target. 27791300Sgjelinek */ 27801300Sgjelinek if ((err = zonecfg_setipdent(s_handle)) != Z_OK) { 27811300Sgjelinek errno = err; 27821300Sgjelinek zperror2(source_zone, gettext("could not enumerate " 27831300Sgjelinek "inherit-pkg-dirs")); 27841300Sgjelinek return (Z_ERR); 27851300Sgjelinek } 27861300Sgjelinek 27871300Sgjelinek while (zonecfg_getipdent(s_handle, &s_fstab) == Z_OK) { 27881300Sgjelinek boolean_t match = B_FALSE; 27891300Sgjelinek 27901300Sgjelinek s_cnt++; 27911300Sgjelinek 27921300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 27931300Sgjelinek errno = err; 27941300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 27951300Sgjelinek "inherit-pkg-dirs")); 27961300Sgjelinek (void) zonecfg_endipdent(s_handle); 27971300Sgjelinek return (Z_ERR); 27981300Sgjelinek } 27991300Sgjelinek 28001300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) { 28011300Sgjelinek if (strcmp(s_fstab.zone_fs_dir, t_fstab.zone_fs_dir) 28021300Sgjelinek == 0) { 28031300Sgjelinek match = B_TRUE; 28041300Sgjelinek break; 28051300Sgjelinek } 28061300Sgjelinek } 28071300Sgjelinek (void) zonecfg_endipdent(t_handle); 28081300Sgjelinek 28091300Sgjelinek if (!match) { 28101300Sgjelinek (void) fprintf(stderr, gettext("inherit-pkg-dir " 28111300Sgjelinek "'%s' is not configured in zone %s.\n"), 28121300Sgjelinek s_fstab.zone_fs_dir, target_zone); 28131300Sgjelinek res = Z_ERR; 28141300Sgjelinek } 28151300Sgjelinek } 28161300Sgjelinek 28171300Sgjelinek (void) zonecfg_endipdent(s_handle); 28181300Sgjelinek 28191300Sgjelinek /* skip the next check if we already have errors */ 28201300Sgjelinek if (res == Z_ERR) 28211300Sgjelinek return (res); 28221300Sgjelinek 28231300Sgjelinek /* 28241300Sgjelinek * Now check the number of ipds in the target so we can verify 28251300Sgjelinek * that the source is not a subset of the target. 28261300Sgjelinek */ 28271300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 28281300Sgjelinek errno = err; 28291300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 28301300Sgjelinek "inherit-pkg-dirs")); 28311300Sgjelinek return (Z_ERR); 28321300Sgjelinek } 28331300Sgjelinek 28341300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) 28351300Sgjelinek t_cnt++; 28361300Sgjelinek 28371300Sgjelinek (void) zonecfg_endipdent(t_handle); 28381300Sgjelinek 28391300Sgjelinek if (t_cnt != s_cnt) { 28401300Sgjelinek (void) fprintf(stderr, gettext("Zone %s is configured " 28411300Sgjelinek "with inherit-pkg-dirs that are not configured in zone " 28421300Sgjelinek "%s.\n"), target_zone, source_zone); 28431300Sgjelinek res = Z_ERR; 28441300Sgjelinek } 28451300Sgjelinek 28461300Sgjelinek return (res); 28471300Sgjelinek } 28481300Sgjelinek 28491300Sgjelinek static void 28501300Sgjelinek warn_dev_match(zone_dochandle_t s_handle, char *source_zone, 28511300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 28521300Sgjelinek { 28531300Sgjelinek int err; 28541300Sgjelinek struct zone_devtab s_devtab; 28551300Sgjelinek struct zone_devtab t_devtab; 28561300Sgjelinek 28571300Sgjelinek if ((err = zonecfg_setdevent(t_handle)) != Z_OK) { 28581300Sgjelinek errno = err; 28591300Sgjelinek zperror2(target_zone, gettext("could not enumerate devices")); 28601300Sgjelinek return; 28611300Sgjelinek } 28621300Sgjelinek 28631300Sgjelinek while (zonecfg_getdevent(t_handle, &t_devtab) == Z_OK) { 28641300Sgjelinek if ((err = zonecfg_setdevent(s_handle)) != Z_OK) { 28651300Sgjelinek errno = err; 28661300Sgjelinek zperror2(source_zone, 28671300Sgjelinek gettext("could not enumerate devices")); 28681300Sgjelinek (void) zonecfg_enddevent(t_handle); 28691300Sgjelinek return; 28701300Sgjelinek } 28711300Sgjelinek 28721300Sgjelinek while (zonecfg_getdevent(s_handle, &s_devtab) == Z_OK) { 28731300Sgjelinek /* 28741300Sgjelinek * Use fnmatch to catch the case where wildcards 28751300Sgjelinek * were used in one zone and the other has an 28761300Sgjelinek * explicit entry (e.g. /dev/dsk/c0t0d0s6 vs. 28771300Sgjelinek * /dev/\*dsk/c0t0d0s6). 28781300Sgjelinek */ 28791300Sgjelinek if (fnmatch(t_devtab.zone_dev_match, 28801300Sgjelinek s_devtab.zone_dev_match, FNM_PATHNAME) == 0 || 28811300Sgjelinek fnmatch(s_devtab.zone_dev_match, 28821300Sgjelinek t_devtab.zone_dev_match, FNM_PATHNAME) == 0) { 28831300Sgjelinek (void) fprintf(stderr, 28841300Sgjelinek gettext("WARNING: device '%s' " 28851300Sgjelinek "is configured in both zones.\n"), 28861300Sgjelinek t_devtab.zone_dev_match); 28871300Sgjelinek break; 28881300Sgjelinek } 28891300Sgjelinek } 28901300Sgjelinek (void) zonecfg_enddevent(s_handle); 28911300Sgjelinek } 28921300Sgjelinek 28931300Sgjelinek (void) zonecfg_enddevent(t_handle); 28941300Sgjelinek } 28951300Sgjelinek 28961300Sgjelinek /* 28971300Sgjelinek * Check if the specified mount option (opt) is contained within the 28981300Sgjelinek * options string. 28991300Sgjelinek */ 29001300Sgjelinek static boolean_t 29011300Sgjelinek opt_match(char *opt, char *options) 29021300Sgjelinek { 29031300Sgjelinek char *p; 29041300Sgjelinek char *lastp; 29051300Sgjelinek 29061300Sgjelinek if ((p = strtok_r(options, ",", &lastp)) != NULL) { 29071300Sgjelinek if (strcmp(p, opt) == 0) 29081300Sgjelinek return (B_TRUE); 29091300Sgjelinek while ((p = strtok_r(NULL, ",", &lastp)) != NULL) { 29101300Sgjelinek if (strcmp(p, opt) == 0) 29111300Sgjelinek return (B_TRUE); 29121300Sgjelinek } 29131300Sgjelinek } 29141300Sgjelinek 29151300Sgjelinek return (B_FALSE); 29161300Sgjelinek } 29171300Sgjelinek 29181867Sgjelinek #define RW_LOFS "WARNING: read-write lofs file system on '%s' is configured " \ 29191300Sgjelinek "in both zones.\n" 29201300Sgjelinek 29211300Sgjelinek static void 29221300Sgjelinek print_fs_warnings(struct zone_fstab *s_fstab, struct zone_fstab *t_fstab) 29231300Sgjelinek { 29241300Sgjelinek /* 29251300Sgjelinek * It is ok to have shared lofs mounted fs but we want to warn if 29261300Sgjelinek * either is rw since this will effect the other zone. 29271300Sgjelinek */ 29281300Sgjelinek if (strcmp(t_fstab->zone_fs_type, "lofs") == 0) { 29291300Sgjelinek zone_fsopt_t *optp; 29301300Sgjelinek 29311300Sgjelinek /* The default is rw so no options means rw */ 29321300Sgjelinek if (t_fstab->zone_fs_options == NULL || 29331300Sgjelinek s_fstab->zone_fs_options == NULL) { 29341300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 29351300Sgjelinek t_fstab->zone_fs_special); 29361300Sgjelinek return; 29371300Sgjelinek } 29381300Sgjelinek 29391300Sgjelinek for (optp = s_fstab->zone_fs_options; optp != NULL; 29401300Sgjelinek optp = optp->zone_fsopt_next) { 29411300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 29421300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 29431300Sgjelinek s_fstab->zone_fs_special); 29441300Sgjelinek return; 29451300Sgjelinek } 29461300Sgjelinek } 29471300Sgjelinek 29481300Sgjelinek for (optp = t_fstab->zone_fs_options; optp != NULL; 29491300Sgjelinek optp = optp->zone_fsopt_next) { 29501300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 29511300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 29521300Sgjelinek t_fstab->zone_fs_special); 29531300Sgjelinek return; 29541300Sgjelinek } 29551300Sgjelinek } 29561300Sgjelinek 29571300Sgjelinek return; 29581300Sgjelinek } 29591300Sgjelinek 29601300Sgjelinek /* 29611300Sgjelinek * TRANSLATION_NOTE 29621867Sgjelinek * The first variable is the file system type and the second is 29631867Sgjelinek * the file system special device. For example, 29641867Sgjelinek * WARNING: ufs file system on '/dev/dsk/c0t0d0s0' ... 29651300Sgjelinek */ 29661867Sgjelinek (void) fprintf(stderr, gettext("WARNING: %s file system on '%s' " 29671300Sgjelinek "is configured in both zones.\n"), t_fstab->zone_fs_type, 29681300Sgjelinek t_fstab->zone_fs_special); 29691300Sgjelinek } 29701300Sgjelinek 29711300Sgjelinek static void 29721300Sgjelinek warn_fs_match(zone_dochandle_t s_handle, char *source_zone, 29731300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 29741300Sgjelinek { 29751300Sgjelinek int err; 29761300Sgjelinek struct zone_fstab s_fstab; 29771300Sgjelinek struct zone_fstab t_fstab; 29781300Sgjelinek 29791300Sgjelinek if ((err = zonecfg_setfsent(t_handle)) != Z_OK) { 29801300Sgjelinek errno = err; 29811300Sgjelinek zperror2(target_zone, 29821867Sgjelinek gettext("could not enumerate file systems")); 29831300Sgjelinek return; 29841300Sgjelinek } 29851300Sgjelinek 29861300Sgjelinek while (zonecfg_getfsent(t_handle, &t_fstab) == Z_OK) { 29871300Sgjelinek if ((err = zonecfg_setfsent(s_handle)) != Z_OK) { 29881300Sgjelinek errno = err; 29891300Sgjelinek zperror2(source_zone, 29901867Sgjelinek gettext("could not enumerate file systems")); 29911300Sgjelinek (void) zonecfg_endfsent(t_handle); 29921300Sgjelinek return; 29931300Sgjelinek } 29941300Sgjelinek 29951300Sgjelinek while (zonecfg_getfsent(s_handle, &s_fstab) == Z_OK) { 29961300Sgjelinek if (strcmp(t_fstab.zone_fs_special, 29971300Sgjelinek s_fstab.zone_fs_special) == 0) { 29981300Sgjelinek print_fs_warnings(&s_fstab, &t_fstab); 29991300Sgjelinek break; 30001300Sgjelinek } 30011300Sgjelinek } 30021300Sgjelinek (void) zonecfg_endfsent(s_handle); 30031300Sgjelinek } 30041300Sgjelinek 30051300Sgjelinek (void) zonecfg_endfsent(t_handle); 30061300Sgjelinek } 30071300Sgjelinek 30081300Sgjelinek /* 30091300Sgjelinek * We don't catch the case where you used the same IP address but 30101300Sgjelinek * it is not an exact string match. For example, 192.9.0.128 vs. 192.09.0.128. 30111300Sgjelinek * However, we're not going to worry about that but we will check for 30121300Sgjelinek * a possible netmask on one of the addresses (e.g. 10.0.0.1 and 10.0.0.1/24) 30131300Sgjelinek * and handle that case as a match. 30141300Sgjelinek */ 30151300Sgjelinek static void 30161300Sgjelinek warn_ip_match(zone_dochandle_t s_handle, char *source_zone, 30171300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 30181300Sgjelinek { 30191300Sgjelinek int err; 30201300Sgjelinek struct zone_nwiftab s_nwiftab; 30211300Sgjelinek struct zone_nwiftab t_nwiftab; 30221300Sgjelinek 30231300Sgjelinek if ((err = zonecfg_setnwifent(t_handle)) != Z_OK) { 30241300Sgjelinek errno = err; 30251300Sgjelinek zperror2(target_zone, 30261300Sgjelinek gettext("could not enumerate network interfaces")); 30271300Sgjelinek return; 30281300Sgjelinek } 30291300Sgjelinek 30301300Sgjelinek while (zonecfg_getnwifent(t_handle, &t_nwiftab) == Z_OK) { 30311300Sgjelinek char *p; 30321300Sgjelinek 30331300Sgjelinek /* remove an (optional) netmask from the address */ 30341300Sgjelinek if ((p = strchr(t_nwiftab.zone_nwif_address, '/')) != NULL) 30351300Sgjelinek *p = '\0'; 30361300Sgjelinek 30371300Sgjelinek if ((err = zonecfg_setnwifent(s_handle)) != Z_OK) { 30381300Sgjelinek errno = err; 30391300Sgjelinek zperror2(source_zone, 30401300Sgjelinek gettext("could not enumerate network interfaces")); 30411300Sgjelinek (void) zonecfg_endnwifent(t_handle); 30421300Sgjelinek return; 30431300Sgjelinek } 30441300Sgjelinek 30451300Sgjelinek while (zonecfg_getnwifent(s_handle, &s_nwiftab) == Z_OK) { 30461300Sgjelinek /* remove an (optional) netmask from the address */ 30471300Sgjelinek if ((p = strchr(s_nwiftab.zone_nwif_address, '/')) 30481300Sgjelinek != NULL) 30491300Sgjelinek *p = '\0'; 30501300Sgjelinek 30511300Sgjelinek if (strcmp(t_nwiftab.zone_nwif_address, 30521300Sgjelinek s_nwiftab.zone_nwif_address) == 0) { 30531300Sgjelinek (void) fprintf(stderr, 30541300Sgjelinek gettext("WARNING: network address '%s' " 30551300Sgjelinek "is configured in both zones.\n"), 30561300Sgjelinek t_nwiftab.zone_nwif_address); 30571300Sgjelinek break; 30581300Sgjelinek } 30591300Sgjelinek } 30601300Sgjelinek (void) zonecfg_endnwifent(s_handle); 30611300Sgjelinek } 30621300Sgjelinek 30631300Sgjelinek (void) zonecfg_endnwifent(t_handle); 30641300Sgjelinek } 30651300Sgjelinek 30661300Sgjelinek static void 30671300Sgjelinek warn_dataset_match(zone_dochandle_t s_handle, char *source_zone, 30681300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 30691300Sgjelinek { 30701300Sgjelinek int err; 30711300Sgjelinek struct zone_dstab s_dstab; 30721300Sgjelinek struct zone_dstab t_dstab; 30731300Sgjelinek 30741300Sgjelinek if ((err = zonecfg_setdsent(t_handle)) != Z_OK) { 30751300Sgjelinek errno = err; 30761300Sgjelinek zperror2(target_zone, gettext("could not enumerate datasets")); 30771300Sgjelinek return; 30781300Sgjelinek } 30791300Sgjelinek 30801300Sgjelinek while (zonecfg_getdsent(t_handle, &t_dstab) == Z_OK) { 30811300Sgjelinek if ((err = zonecfg_setdsent(s_handle)) != Z_OK) { 30821300Sgjelinek errno = err; 30831300Sgjelinek zperror2(source_zone, 30841300Sgjelinek gettext("could not enumerate datasets")); 30851300Sgjelinek (void) zonecfg_enddsent(t_handle); 30861300Sgjelinek return; 30871300Sgjelinek } 30881300Sgjelinek 30891300Sgjelinek while (zonecfg_getdsent(s_handle, &s_dstab) == Z_OK) { 30901300Sgjelinek if (strcmp(t_dstab.zone_dataset_name, 30911300Sgjelinek s_dstab.zone_dataset_name) == 0) { 30921300Sgjelinek (void) fprintf(stderr, 30931300Sgjelinek gettext("WARNING: dataset '%s' " 30941300Sgjelinek "is configured in both zones.\n"), 30951300Sgjelinek t_dstab.zone_dataset_name); 30961300Sgjelinek break; 30971300Sgjelinek } 30981300Sgjelinek } 30991300Sgjelinek (void) zonecfg_enddsent(s_handle); 31001300Sgjelinek } 31011300Sgjelinek 31021300Sgjelinek (void) zonecfg_enddsent(t_handle); 31031300Sgjelinek } 31041300Sgjelinek 31051300Sgjelinek static int 31061300Sgjelinek validate_clone(char *source_zone, char *target_zone) 31071300Sgjelinek { 31081300Sgjelinek int err = Z_OK; 31091300Sgjelinek zone_dochandle_t s_handle; 31101300Sgjelinek zone_dochandle_t t_handle; 31111300Sgjelinek 31121300Sgjelinek if ((t_handle = zonecfg_init_handle()) == NULL) { 31131300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 31141300Sgjelinek return (Z_ERR); 31151300Sgjelinek } 31161300Sgjelinek if ((err = zonecfg_get_handle(target_zone, t_handle)) != Z_OK) { 31171300Sgjelinek errno = err; 31181300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 31191300Sgjelinek zonecfg_fini_handle(t_handle); 31201300Sgjelinek return (Z_ERR); 31211300Sgjelinek } 31221300Sgjelinek 31231300Sgjelinek if ((s_handle = zonecfg_init_handle()) == NULL) { 31241300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 31251300Sgjelinek zonecfg_fini_handle(t_handle); 31261300Sgjelinek return (Z_ERR); 31271300Sgjelinek } 31281300Sgjelinek if ((err = zonecfg_get_handle(source_zone, s_handle)) != Z_OK) { 31291300Sgjelinek errno = err; 31301300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 31311300Sgjelinek goto done; 31321300Sgjelinek } 31331300Sgjelinek 31341300Sgjelinek /* verify new zone has same inherit-pkg-dirs */ 31351300Sgjelinek err = valid_ipd_clone(s_handle, source_zone, t_handle, target_zone); 31361300Sgjelinek 31371300Sgjelinek /* warn about imported fs's which are the same */ 31381300Sgjelinek warn_fs_match(s_handle, source_zone, t_handle, target_zone); 31391300Sgjelinek 31401300Sgjelinek /* warn about imported IP addresses which are the same */ 31411300Sgjelinek warn_ip_match(s_handle, source_zone, t_handle, target_zone); 31421300Sgjelinek 31431300Sgjelinek /* warn about imported devices which are the same */ 31441300Sgjelinek warn_dev_match(s_handle, source_zone, t_handle, target_zone); 31451300Sgjelinek 31461300Sgjelinek /* warn about imported datasets which are the same */ 31471300Sgjelinek warn_dataset_match(s_handle, source_zone, t_handle, target_zone); 31481300Sgjelinek 31491300Sgjelinek done: 31501300Sgjelinek zonecfg_fini_handle(t_handle); 31511300Sgjelinek zonecfg_fini_handle(s_handle); 31521300Sgjelinek 31531300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 31541300Sgjelinek } 31551300Sgjelinek 31561300Sgjelinek static int 31571300Sgjelinek copy_zone(char *src, char *dst) 31581300Sgjelinek { 31591300Sgjelinek boolean_t out_null = B_FALSE; 31601300Sgjelinek int status; 31611300Sgjelinek int err; 31621300Sgjelinek char *outfile; 31631300Sgjelinek char cmdbuf[MAXPATHLEN * 2 + 128]; 31641300Sgjelinek 31651300Sgjelinek if ((outfile = tempnam("/var/log", "zone")) == NULL) { 31661300Sgjelinek outfile = "/dev/null"; 31671300Sgjelinek out_null = B_TRUE; 31681300Sgjelinek } 31691300Sgjelinek 31701867Sgjelinek /* 31711867Sgjelinek * Use find to get the list of files to copy. We need to skip 31721867Sgjelinek * files of type "socket" since cpio can't handle those but that 31731867Sgjelinek * should be ok since the app will recreate the socket when it runs. 31741867Sgjelinek * We also need to filter out anything under the .zfs subdir. Since 31751867Sgjelinek * find is running depth-first, we need the extra egrep to filter .zfs. 31761867Sgjelinek */ 31771300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 31781867Sgjelinek "cd %s && /usr/bin/find . -type s -prune -o -depth -print | " 31791607Sgjelinek "/usr/bin/egrep -v '^\\./\\.zfs$|^\\./\\.zfs/' | " 31801300Sgjelinek "/usr/bin/cpio -pdmuP@ %s > %s 2>&1", 31811300Sgjelinek src, dst, outfile); 31821300Sgjelinek 31831300Sgjelinek status = do_subproc(cmdbuf); 31841300Sgjelinek 31851300Sgjelinek if ((err = subproc_status("copy", status)) != Z_OK) { 31861300Sgjelinek if (!out_null) 31871300Sgjelinek (void) fprintf(stderr, gettext("\nThe copy failed.\n" 31881300Sgjelinek "More information can be found in %s\n"), outfile); 31891300Sgjelinek return (err); 31901300Sgjelinek } 31911300Sgjelinek 31921300Sgjelinek if (!out_null) 31931300Sgjelinek (void) unlink(outfile); 31941300Sgjelinek 31951300Sgjelinek return (Z_OK); 31961300Sgjelinek } 31971300Sgjelinek 31981300Sgjelinek /* 31991568Sgjelinek * Run sys-unconfig on a zone. This will leave the zone in the installed 32001568Sgjelinek * state as long as there were no errors during the sys-unconfig. 32011300Sgjelinek */ 32021300Sgjelinek static int 32031568Sgjelinek unconfigure_zone(char *zonepath) 32041300Sgjelinek { 32051568Sgjelinek int err; 32061568Sgjelinek int status; 32071568Sgjelinek struct stat unconfig_buf; 32081568Sgjelinek zone_cmd_arg_t zarg; 32091568Sgjelinek char cmdbuf[MAXPATHLEN + 51]; 32101568Sgjelinek 32111568Sgjelinek /* The zone has to be installed in order to mount the scratch zone. */ 32121568Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 32131568Sgjelinek errno = err; 32141568Sgjelinek zperror2(target_zone, gettext("could not set state")); 32151568Sgjelinek return (Z_ERR); 32161568Sgjelinek } 32171568Sgjelinek 32181568Sgjelinek /* 32191676Sjpk * Trusted Extensions requires that cloned zones use the 32201676Sjpk * same sysid configuration, so it is not appropriate to 32211676Sjpk * unconfigure the zone. 32221676Sjpk */ 32231676Sjpk if (is_system_labeled()) 32241676Sjpk return (Z_OK); 32251676Sjpk 32261676Sjpk /* 32271568Sgjelinek * Check if the zone is already sys-unconfiged. This saves us 32281568Sgjelinek * the work of bringing up the scratch zone so we can unconfigure it. 32291568Sgjelinek */ 32301568Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/root/etc/.UNCONFIGURED", 32311568Sgjelinek zonepath); 32321568Sgjelinek if (stat(cmdbuf, &unconfig_buf) == 0) 32331568Sgjelinek return (Z_OK); 32341568Sgjelinek 32351568Sgjelinek zarg.cmd = Z_MOUNT; 32361568Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 32371568Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 32381568Sgjelinek (void) zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 32391568Sgjelinek return (Z_ERR); 32401568Sgjelinek } 32411300Sgjelinek 32421300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 32431568Sgjelinek "/usr/sbin/zlogin -S %s /usr/sbin/sys-unconfig -R /a", target_zone); 32441568Sgjelinek 32451568Sgjelinek status = do_subproc(cmdbuf); 32461568Sgjelinek if ((err = subproc_status("sys-unconfig", status)) != Z_OK) { 32471568Sgjelinek errno = err; 32481568Sgjelinek zperror2(target_zone, gettext("sys-unconfig failed\n")); 32491568Sgjelinek (void) zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 32501300Sgjelinek } 32511300Sgjelinek 32521568Sgjelinek zarg.cmd = Z_UNMOUNT; 32531568Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 32541568Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 32551568Sgjelinek (void) fprintf(stderr, gettext("could not unmount zone\n")); 32561568Sgjelinek return (Z_ERR); 32571568Sgjelinek } 32581568Sgjelinek 32591568Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 32601300Sgjelinek } 32611300Sgjelinek 32621300Sgjelinek /* ARGSUSED */ 32631867Sgjelinek static int 32641300Sgjelinek zfm_print(const char *p, void *r) { 32651300Sgjelinek zerror(" %s\n", p); 32661300Sgjelinek return (0); 32671300Sgjelinek } 32681300Sgjelinek 32691867Sgjelinek int 32701867Sgjelinek clone_copy(char *source_zonepath, char *zonepath) 32711867Sgjelinek { 32721867Sgjelinek int err; 32731867Sgjelinek 32741867Sgjelinek /* Don't clone the zone if anything is still mounted there */ 32751867Sgjelinek if (zonecfg_find_mounts(source_zonepath, NULL, NULL)) { 32761867Sgjelinek zerror(gettext("These file systems are mounted on " 32771867Sgjelinek "subdirectories of %s.\n"), source_zonepath); 32781867Sgjelinek (void) zonecfg_find_mounts(source_zonepath, zfm_print, NULL); 32791867Sgjelinek return (Z_ERR); 32801867Sgjelinek } 32811867Sgjelinek 32821867Sgjelinek /* 32831867Sgjelinek * Attempt to create a ZFS fs for the zonepath. As usual, we don't 32841867Sgjelinek * care if this works or not since we always have the default behavior 32851867Sgjelinek * of a simple directory for the zonepath. 32861867Sgjelinek */ 32871867Sgjelinek create_zfs_zonepath(zonepath); 32881867Sgjelinek 32891867Sgjelinek (void) printf(gettext("Copying %s..."), source_zonepath); 32901867Sgjelinek (void) fflush(stdout); 32911867Sgjelinek 32921867Sgjelinek err = copy_zone(source_zonepath, zonepath); 32931867Sgjelinek 32941867Sgjelinek (void) printf("\n"); 32951867Sgjelinek 32961867Sgjelinek return (err); 32971867Sgjelinek } 32981867Sgjelinek 32991300Sgjelinek static int 33001300Sgjelinek clone_func(int argc, char *argv[]) 33011300Sgjelinek { 33021300Sgjelinek char *source_zone = NULL; 33031300Sgjelinek int lockfd; 33041300Sgjelinek int err, arg; 33051300Sgjelinek char zonepath[MAXPATHLEN]; 33061300Sgjelinek char source_zonepath[MAXPATHLEN]; 33071300Sgjelinek zone_state_t state; 33081300Sgjelinek zone_entry_t *zent; 33091867Sgjelinek char *method = NULL; 33101867Sgjelinek char *snapshot = NULL; 33111300Sgjelinek 33121300Sgjelinek if (zonecfg_in_alt_root()) { 33131300Sgjelinek zerror(gettext("cannot clone zone in alternate root")); 33141300Sgjelinek return (Z_ERR); 33151300Sgjelinek } 33161300Sgjelinek 33171300Sgjelinek optind = 0; 33181867Sgjelinek if ((arg = getopt(argc, argv, "?m:s:")) != EOF) { 33191300Sgjelinek switch (arg) { 33201300Sgjelinek case '?': 33211300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 33221300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 33231300Sgjelinek case 'm': 33241300Sgjelinek method = optarg; 33251300Sgjelinek break; 33261867Sgjelinek case 's': 33271867Sgjelinek snapshot = optarg; 33281867Sgjelinek break; 33291300Sgjelinek default: 33301300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 33311300Sgjelinek return (Z_USAGE); 33321300Sgjelinek } 33331300Sgjelinek } 33341867Sgjelinek if (argc != (optind + 1) || 33351867Sgjelinek (method != NULL && strcmp(method, "copy") != 0)) { 33361300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 33371300Sgjelinek return (Z_USAGE); 33381300Sgjelinek } 33391300Sgjelinek source_zone = argv[optind]; 33401300Sgjelinek if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE) != Z_OK) 33411300Sgjelinek return (Z_ERR); 33421300Sgjelinek if (verify_details(CMD_CLONE) != Z_OK) 33431300Sgjelinek return (Z_ERR); 33441300Sgjelinek 33451300Sgjelinek /* 33461300Sgjelinek * We also need to do some extra validation on the source zone. 33471300Sgjelinek */ 33481300Sgjelinek if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) { 33491300Sgjelinek zerror(gettext("%s operation is invalid for the global zone."), 33501300Sgjelinek cmd_to_str(CMD_CLONE)); 33511300Sgjelinek return (Z_ERR); 33521300Sgjelinek } 33531300Sgjelinek 33541300Sgjelinek if (strncmp(source_zone, "SUNW", 4) == 0) { 33551300Sgjelinek zerror(gettext("%s operation is invalid for zones starting " 33561300Sgjelinek "with SUNW."), cmd_to_str(CMD_CLONE)); 33571300Sgjelinek return (Z_ERR); 33581300Sgjelinek } 33591300Sgjelinek 33601300Sgjelinek zent = lookup_running_zone(source_zone); 33611300Sgjelinek if (zent != NULL) { 33621300Sgjelinek /* check whether the zone is ready or running */ 33631300Sgjelinek if ((err = zone_get_state(zent->zname, &zent->zstate_num)) 33641300Sgjelinek != Z_OK) { 33651300Sgjelinek errno = err; 33661300Sgjelinek zperror2(zent->zname, gettext("could not get state")); 33671300Sgjelinek /* can't tell, so hedge */ 33681300Sgjelinek zent->zstate_str = "ready/running"; 33691300Sgjelinek } else { 33701300Sgjelinek zent->zstate_str = zone_state_str(zent->zstate_num); 33711300Sgjelinek } 33721300Sgjelinek zerror(gettext("%s operation is invalid for %s zones."), 33731300Sgjelinek cmd_to_str(CMD_CLONE), zent->zstate_str); 33741300Sgjelinek return (Z_ERR); 33751300Sgjelinek } 33761300Sgjelinek 33771300Sgjelinek if ((err = zone_get_state(source_zone, &state)) != Z_OK) { 33781300Sgjelinek errno = err; 33791300Sgjelinek zperror2(source_zone, gettext("could not get state")); 33801300Sgjelinek return (Z_ERR); 33811300Sgjelinek } 33821300Sgjelinek if (state != ZONE_STATE_INSTALLED) { 33831300Sgjelinek (void) fprintf(stderr, 33841300Sgjelinek gettext("%s: zone %s is %s; %s is required.\n"), 33851300Sgjelinek execname, source_zone, zone_state_str(state), 33861300Sgjelinek zone_state_str(ZONE_STATE_INSTALLED)); 33871300Sgjelinek return (Z_ERR); 33881300Sgjelinek } 33891300Sgjelinek 33901300Sgjelinek /* 33911300Sgjelinek * The source zone checks out ok, continue with the clone. 33921300Sgjelinek */ 33931300Sgjelinek 33941300Sgjelinek if (validate_clone(source_zone, target_zone) != Z_OK) 33951300Sgjelinek return (Z_ERR); 33961300Sgjelinek 33971300Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 33981300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 33991300Sgjelinek "zoneadm"); 34001300Sgjelinek return (Z_ERR); 34011300Sgjelinek } 34021300Sgjelinek 34031300Sgjelinek if ((err = zone_get_zonepath(source_zone, source_zonepath, 34041300Sgjelinek sizeof (source_zonepath))) != Z_OK) { 34051300Sgjelinek errno = err; 34061300Sgjelinek zperror2(source_zone, gettext("could not get zone path")); 34071300Sgjelinek goto done; 34081300Sgjelinek } 34091300Sgjelinek 34101300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 34111300Sgjelinek != Z_OK) { 34121300Sgjelinek errno = err; 34131300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 34141300Sgjelinek goto done; 34151300Sgjelinek } 34161300Sgjelinek 34171300Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE)) 34181300Sgjelinek != Z_OK) { 34191300Sgjelinek errno = err; 34201300Sgjelinek zperror2(target_zone, gettext("could not set state")); 34211300Sgjelinek goto done; 34221300Sgjelinek } 34231300Sgjelinek 34241867Sgjelinek if (snapshot != NULL) { 34251867Sgjelinek err = clone_snapshot_zfs(snapshot, zonepath); 34261867Sgjelinek } else { 34271867Sgjelinek /* 34281867Sgjelinek * We always copy the clone unless the source is ZFS and a 34291867Sgjelinek * ZFS clone worked. We fallback to copying if the ZFS clone 34301867Sgjelinek * fails for some reason. 34311867Sgjelinek */ 34321867Sgjelinek err = Z_ERR; 34331867Sgjelinek if (method == NULL && is_zonepath_zfs(source_zonepath)) 34341867Sgjelinek err = clone_zfs(source_zone, source_zonepath, zonepath); 34351867Sgjelinek 34361867Sgjelinek if (err != Z_OK) 34371867Sgjelinek err = clone_copy(source_zonepath, zonepath); 34381867Sgjelinek } 34391867Sgjelinek 34401867Sgjelinek if (err == Z_OK) 34411867Sgjelinek err = unconfigure_zone(zonepath); 34421300Sgjelinek 34431300Sgjelinek done: 34441300Sgjelinek release_lock_file(lockfd); 34451300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 34461300Sgjelinek } 34471300Sgjelinek 34481300Sgjelinek #define RMCOMMAND "/usr/bin/rm -rf" 34491300Sgjelinek 34501607Sgjelinek /* 34511867Sgjelinek * Used when removing a zonepath after uninstalling or cleaning up after 34521867Sgjelinek * the move subcommand. This handles a zonepath that has non-standard 34531867Sgjelinek * contents so that we will only cleanup the stuff we know about and leave 34541867Sgjelinek * any user data alone. 34551607Sgjelinek * 34561867Sgjelinek * If the "all" parameter is true then we should remove the whole zonepath 34571867Sgjelinek * even if it has non-standard files/directories in it. This can be used when 34581867Sgjelinek * we need to cleanup after moving the zonepath across file systems. 34591867Sgjelinek * 34601867Sgjelinek * We "exec" the RMCOMMAND so that the returned status is that of RMCOMMAND 34611867Sgjelinek * and not the shell. 34621607Sgjelinek */ 34631607Sgjelinek static int 34641867Sgjelinek cleanup_zonepath(char *zonepath, boolean_t all) 34651607Sgjelinek { 34661867Sgjelinek int status; 34671867Sgjelinek int i; 34681867Sgjelinek boolean_t non_std = B_FALSE; 34691867Sgjelinek struct dirent *dp; 34701867Sgjelinek DIR *dirp; 34711867Sgjelinek char *std_entries[] = {"dev", "lu", "root", NULL}; 34721867Sgjelinek /* (MAXPATHLEN * 3) is for the 3 std_entries dirs */ 34731867Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64]; 34741867Sgjelinek 34751867Sgjelinek /* 34761867Sgjelinek * We shouldn't need these checks but lets be paranoid since we 34771867Sgjelinek * could blow away the whole system here if we got the wrong zonepath. 34781867Sgjelinek */ 34791867Sgjelinek if (*zonepath == NULL || strcmp(zonepath, "/") == 0) { 34801867Sgjelinek (void) fprintf(stderr, "invalid zonepath '%s'\n", zonepath); 34811867Sgjelinek return (Z_INVAL); 34821867Sgjelinek } 34831867Sgjelinek 34841867Sgjelinek /* 34851867Sgjelinek * If the dirpath is already gone (maybe it was manually removed) then 34861867Sgjelinek * we just return Z_OK so that the cleanup is successful. 34871867Sgjelinek */ 34881867Sgjelinek if ((dirp = opendir(zonepath)) == NULL) 34891867Sgjelinek return (Z_OK); 34901867Sgjelinek 34911867Sgjelinek /* 34921867Sgjelinek * Look through the zonepath directory to see if there are any 34931867Sgjelinek * non-standard files/dirs. Also skip .zfs since that might be 34941867Sgjelinek * there but we'll handle ZFS file systems as a special case. 34951867Sgjelinek */ 34961867Sgjelinek while ((dp = readdir(dirp)) != NULL) { 34971867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 34981867Sgjelinek strcmp(dp->d_name, "..") == 0 || 34991867Sgjelinek strcmp(dp->d_name, ".zfs") == 0) 35001867Sgjelinek continue; 35011867Sgjelinek 35021867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) 35031867Sgjelinek if (strcmp(dp->d_name, std_entries[i]) == 0) 35041867Sgjelinek break; 35051867Sgjelinek 35061867Sgjelinek if (std_entries[i] == NULL) 35071867Sgjelinek non_std = B_TRUE; 35081867Sgjelinek } 35091867Sgjelinek (void) closedir(dirp); 35101867Sgjelinek 35111867Sgjelinek if (!all && non_std) { 35121607Sgjelinek /* 35131867Sgjelinek * There are extra, non-standard directories/files in the 35141867Sgjelinek * zonepath so we don't want to remove the zonepath. We 35151867Sgjelinek * just want to remove the standard directories and leave 35161867Sgjelinek * the user data alone. 35171607Sgjelinek */ 35181867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND); 35191867Sgjelinek 35201867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) { 35211867Sgjelinek char tmpbuf[MAXPATHLEN]; 35221867Sgjelinek 35231867Sgjelinek if (snprintf(tmpbuf, sizeof (tmpbuf), " %s/%s", 35241867Sgjelinek zonepath, std_entries[i]) >= sizeof (tmpbuf) || 35251867Sgjelinek strlcat(cmdbuf, tmpbuf, sizeof (cmdbuf)) >= 35261867Sgjelinek sizeof (cmdbuf)) { 35271867Sgjelinek (void) fprintf(stderr, 35281867Sgjelinek gettext("path is too long\n")); 35291867Sgjelinek return (Z_INVAL); 35301867Sgjelinek } 35311867Sgjelinek } 35321867Sgjelinek 35331867Sgjelinek status = do_subproc(cmdbuf); 35341867Sgjelinek 35351867Sgjelinek (void) fprintf(stderr, gettext("WARNING: Unable to completely " 35361867Sgjelinek "remove %s\nbecause it contains additional user data. " 35371867Sgjelinek "Only the standard directory\nentries have been " 35381867Sgjelinek "removed.\n"), 35391867Sgjelinek zonepath); 35401867Sgjelinek 35411867Sgjelinek return (subproc_status(RMCOMMAND, status)); 35421607Sgjelinek } 35431607Sgjelinek 35441867Sgjelinek /* 35451867Sgjelinek * There is nothing unexpected in the zonepath, try to get rid of the 35461867Sgjelinek * whole zonepath directory. 35471867Sgjelinek * 35481867Sgjelinek * If the zonepath is its own zfs file system, try to destroy the 35491867Sgjelinek * file system. If that fails for some reason (e.g. it has clones) 35501867Sgjelinek * then we'll just remove the contents of the zonepath. 35511867Sgjelinek */ 35521867Sgjelinek if (is_zonepath_zfs(zonepath)) { 35531867Sgjelinek if (destroy_zfs(zonepath) == Z_OK) 35541867Sgjelinek return (Z_OK); 35551867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND 35561867Sgjelinek " %s/*", zonepath); 35571867Sgjelinek status = do_subproc(cmdbuf); 35581867Sgjelinek return (subproc_status(RMCOMMAND, status)); 35591867Sgjelinek } 35601867Sgjelinek 35611867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 35621867Sgjelinek zonepath); 35631607Sgjelinek status = do_subproc(cmdbuf); 35641867Sgjelinek return (subproc_status(RMCOMMAND, status)); 35651607Sgjelinek } 35661607Sgjelinek 35671300Sgjelinek static int 35681300Sgjelinek move_func(int argc, char *argv[]) 35691300Sgjelinek { 35701300Sgjelinek char *new_zonepath = NULL; 35711300Sgjelinek int lockfd; 35721300Sgjelinek int err, arg; 35731300Sgjelinek char zonepath[MAXPATHLEN]; 35741300Sgjelinek zone_dochandle_t handle; 35751300Sgjelinek boolean_t fast; 35761867Sgjelinek boolean_t is_zfs = B_FALSE; 35771867Sgjelinek struct dirent *dp; 35781867Sgjelinek DIR *dirp; 35791867Sgjelinek boolean_t empty = B_TRUE; 35801300Sgjelinek boolean_t revert; 35811300Sgjelinek struct stat zonepath_buf; 35821300Sgjelinek struct stat new_zonepath_buf; 35831300Sgjelinek 35841300Sgjelinek if (zonecfg_in_alt_root()) { 35851300Sgjelinek zerror(gettext("cannot move zone in alternate root")); 35861300Sgjelinek return (Z_ERR); 35871300Sgjelinek } 35881300Sgjelinek 35891300Sgjelinek optind = 0; 35901300Sgjelinek if ((arg = getopt(argc, argv, "?")) != EOF) { 35911300Sgjelinek switch (arg) { 35921300Sgjelinek case '?': 35931300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 35941300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 35951300Sgjelinek default: 35961300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 35971300Sgjelinek return (Z_USAGE); 35981300Sgjelinek } 35991300Sgjelinek } 36001300Sgjelinek if (argc != (optind + 1)) { 36011300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 36021300Sgjelinek return (Z_USAGE); 36031300Sgjelinek } 36041300Sgjelinek new_zonepath = argv[optind]; 36051300Sgjelinek if (sanity_check(target_zone, CMD_MOVE, B_FALSE, B_TRUE) != Z_OK) 36061300Sgjelinek return (Z_ERR); 36071300Sgjelinek if (verify_details(CMD_MOVE) != Z_OK) 36081300Sgjelinek return (Z_ERR); 36091300Sgjelinek 36101300Sgjelinek /* 36111300Sgjelinek * Check out the new zonepath. This has the side effect of creating 36121300Sgjelinek * a directory for the new zonepath. We depend on this later when we 36131867Sgjelinek * stat to see if we are doing a cross file system move or not. 36141300Sgjelinek */ 36151300Sgjelinek if (validate_zonepath(new_zonepath, CMD_MOVE) != Z_OK) 36161300Sgjelinek return (Z_ERR); 36171300Sgjelinek 36181300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 36191300Sgjelinek != Z_OK) { 36201300Sgjelinek errno = err; 36211300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 36221300Sgjelinek return (Z_ERR); 36231300Sgjelinek } 36241300Sgjelinek 36251300Sgjelinek if (stat(zonepath, &zonepath_buf) == -1) { 36261300Sgjelinek zperror(gettext("could not stat zone path"), B_FALSE); 36271300Sgjelinek return (Z_ERR); 36281300Sgjelinek } 36291300Sgjelinek 36301300Sgjelinek if (stat(new_zonepath, &new_zonepath_buf) == -1) { 36311300Sgjelinek zperror(gettext("could not stat new zone path"), B_FALSE); 36321300Sgjelinek return (Z_ERR); 36331300Sgjelinek } 36341300Sgjelinek 36351867Sgjelinek /* 36361867Sgjelinek * Check if the destination directory is empty. 36371867Sgjelinek */ 36381867Sgjelinek if ((dirp = opendir(new_zonepath)) == NULL) { 36391867Sgjelinek zperror(gettext("could not open new zone path"), B_FALSE); 36401867Sgjelinek return (Z_ERR); 36411867Sgjelinek } 36421867Sgjelinek while ((dp = readdir(dirp)) != (struct dirent *)0) { 36431867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 36441867Sgjelinek strcmp(dp->d_name, "..") == 0) 36451867Sgjelinek continue; 36461867Sgjelinek empty = B_FALSE; 36471867Sgjelinek break; 36481867Sgjelinek } 36491867Sgjelinek (void) closedir(dirp); 36501867Sgjelinek 36511867Sgjelinek /* Error if there is anything in the destination directory. */ 36521867Sgjelinek if (!empty) { 36531867Sgjelinek (void) fprintf(stderr, gettext("could not move zone to %s: " 36541867Sgjelinek "directory not empty\n"), new_zonepath); 36551867Sgjelinek return (Z_ERR); 36561867Sgjelinek } 36571867Sgjelinek 36581300Sgjelinek /* Don't move the zone if anything is still mounted there */ 36591300Sgjelinek if (zonecfg_find_mounts(zonepath, NULL, NULL)) { 36601867Sgjelinek zerror(gettext("These file systems are mounted on " 36611300Sgjelinek "subdirectories of %s.\n"), zonepath); 36621300Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 36631300Sgjelinek return (Z_ERR); 36641300Sgjelinek } 36651300Sgjelinek 36661300Sgjelinek /* 36671867Sgjelinek * Check if we are moving in the same file system and can do a fast 36681867Sgjelinek * move or if we are crossing file systems and have to copy the data. 36691300Sgjelinek */ 36701300Sgjelinek fast = (zonepath_buf.st_dev == new_zonepath_buf.st_dev); 36711300Sgjelinek 36721300Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 36731300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 36741300Sgjelinek return (Z_ERR); 36751300Sgjelinek } 36761300Sgjelinek 36771300Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 36781300Sgjelinek errno = err; 36791300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 36801300Sgjelinek zonecfg_fini_handle(handle); 36811300Sgjelinek return (Z_ERR); 36821300Sgjelinek } 36831300Sgjelinek 36841300Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 36851300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 36861300Sgjelinek "zoneadm"); 36871300Sgjelinek zonecfg_fini_handle(handle); 36881300Sgjelinek return (Z_ERR); 36891300Sgjelinek } 36901300Sgjelinek 36911300Sgjelinek /* 36921867Sgjelinek * We're making some file system changes now so we have to clean up 36931867Sgjelinek * the file system before we are done. This will either clean up the 36941300Sgjelinek * new zonepath if the zonecfg update failed or it will clean up the 36951300Sgjelinek * old zonepath if everything is ok. 36961300Sgjelinek */ 36971300Sgjelinek revert = B_TRUE; 36981300Sgjelinek 36991867Sgjelinek if (is_zonepath_zfs(zonepath) && 37001867Sgjelinek move_zfs(zonepath, new_zonepath) != Z_ERR) { 37011867Sgjelinek is_zfs = B_TRUE; 37021867Sgjelinek 37031867Sgjelinek } else if (fast) { 37041867Sgjelinek /* same file system, use rename for a quick move */ 37051300Sgjelinek 37061300Sgjelinek /* 37071300Sgjelinek * Remove the new_zonepath directory that got created above 37081300Sgjelinek * during the validation. It gets in the way of the rename. 37091300Sgjelinek */ 37101300Sgjelinek if (rmdir(new_zonepath) != 0) { 37111300Sgjelinek zperror(gettext("could not rmdir new zone path"), 37121300Sgjelinek B_FALSE); 37131300Sgjelinek zonecfg_fini_handle(handle); 37141300Sgjelinek release_lock_file(lockfd); 37151300Sgjelinek return (Z_ERR); 37161300Sgjelinek } 37171300Sgjelinek 37181300Sgjelinek if (rename(zonepath, new_zonepath) != 0) { 37191300Sgjelinek /* 37201300Sgjelinek * If this fails we don't need to do all of the 37211300Sgjelinek * cleanup that happens for the rest of the code 37221300Sgjelinek * so just return from this error. 37231300Sgjelinek */ 37241300Sgjelinek zperror(gettext("could not move zone"), B_FALSE); 37251300Sgjelinek zonecfg_fini_handle(handle); 37261300Sgjelinek release_lock_file(lockfd); 37271300Sgjelinek return (Z_ERR); 37281300Sgjelinek } 37291300Sgjelinek 37301300Sgjelinek } else { 37311867Sgjelinek /* 37321867Sgjelinek * Attempt to create a ZFS fs for the new zonepath. As usual, 37331867Sgjelinek * we don't care if this works or not since we always have the 37341867Sgjelinek * default behavior of a simple directory for the zonepath. 37351867Sgjelinek */ 37361867Sgjelinek create_zfs_zonepath(new_zonepath); 37371867Sgjelinek 37381300Sgjelinek (void) printf(gettext( 37391867Sgjelinek "Moving across file systems; copying zonepath %s..."), 37401300Sgjelinek zonepath); 37411300Sgjelinek (void) fflush(stdout); 37421300Sgjelinek 37431300Sgjelinek err = copy_zone(zonepath, new_zonepath); 37441300Sgjelinek 37451300Sgjelinek (void) printf("\n"); 37461300Sgjelinek if (err != Z_OK) 37471300Sgjelinek goto done; 37481300Sgjelinek } 37491300Sgjelinek 37501300Sgjelinek if ((err = zonecfg_set_zonepath(handle, new_zonepath)) != Z_OK) { 37511300Sgjelinek errno = err; 37521300Sgjelinek zperror(gettext("could not set new zonepath"), B_TRUE); 37531300Sgjelinek goto done; 37541300Sgjelinek } 37551300Sgjelinek 37561300Sgjelinek if ((err = zonecfg_save(handle)) != Z_OK) { 37571300Sgjelinek errno = err; 37581300Sgjelinek zperror(gettext("zonecfg save failed"), B_TRUE); 37591300Sgjelinek goto done; 37601300Sgjelinek } 37611300Sgjelinek 37621300Sgjelinek revert = B_FALSE; 37631300Sgjelinek 37641300Sgjelinek done: 37651300Sgjelinek zonecfg_fini_handle(handle); 37661300Sgjelinek release_lock_file(lockfd); 37671300Sgjelinek 37681300Sgjelinek /* 37691867Sgjelinek * Clean up the file system based on how things went. We either 37701300Sgjelinek * clean up the new zonepath if the operation failed for some reason 37711300Sgjelinek * or we clean up the old zonepath if everything is ok. 37721300Sgjelinek */ 37731300Sgjelinek if (revert) { 37741300Sgjelinek /* The zonecfg update failed, cleanup the new zonepath. */ 37751867Sgjelinek if (is_zfs) { 37761867Sgjelinek if (move_zfs(new_zonepath, zonepath) == Z_ERR) { 37771867Sgjelinek (void) fprintf(stderr, gettext("could not " 37781867Sgjelinek "restore zonepath, the zfs mountpoint is " 37791867Sgjelinek "set as:\n%s\n"), new_zonepath); 37801867Sgjelinek /* 37811867Sgjelinek * err is already != Z_OK since we're reverting 37821867Sgjelinek */ 37831867Sgjelinek } 37841867Sgjelinek 37851867Sgjelinek } else if (fast) { 37861300Sgjelinek if (rename(new_zonepath, zonepath) != 0) { 37871300Sgjelinek zperror(gettext("could not restore zonepath"), 37881300Sgjelinek B_FALSE); 37891300Sgjelinek /* 37901300Sgjelinek * err is already != Z_OK since we're reverting 37911300Sgjelinek */ 37921300Sgjelinek } 37931300Sgjelinek } else { 37941300Sgjelinek (void) printf(gettext("Cleaning up zonepath %s..."), 37951300Sgjelinek new_zonepath); 37961300Sgjelinek (void) fflush(stdout); 37971867Sgjelinek err = cleanup_zonepath(new_zonepath, B_TRUE); 37981300Sgjelinek (void) printf("\n"); 37991300Sgjelinek 38001607Sgjelinek if (err != Z_OK) { 38011300Sgjelinek errno = err; 38021300Sgjelinek zperror(gettext("could not remove new " 38031300Sgjelinek "zonepath"), B_TRUE); 38041300Sgjelinek } else { 38051300Sgjelinek /* 38061300Sgjelinek * Because we're reverting we know the mainline 38071300Sgjelinek * code failed but we just reused the err 38081300Sgjelinek * variable so we reset it back to Z_ERR. 38091300Sgjelinek */ 38101300Sgjelinek err = Z_ERR; 38111300Sgjelinek } 38121300Sgjelinek } 38131300Sgjelinek 38141300Sgjelinek } else { 38151300Sgjelinek /* The move was successful, cleanup the old zonepath. */ 38161867Sgjelinek if (!is_zfs && !fast) { 38171300Sgjelinek (void) printf( 38181300Sgjelinek gettext("Cleaning up zonepath %s..."), zonepath); 38191300Sgjelinek (void) fflush(stdout); 38201867Sgjelinek err = cleanup_zonepath(zonepath, B_TRUE); 38211300Sgjelinek (void) printf("\n"); 38221300Sgjelinek 38231607Sgjelinek if (err != Z_OK) { 38241300Sgjelinek errno = err; 38251300Sgjelinek zperror(gettext("could not remove zonepath"), 38261300Sgjelinek B_TRUE); 38271300Sgjelinek } 38281300Sgjelinek } 38291300Sgjelinek } 38301300Sgjelinek 38311300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 38321300Sgjelinek } 38331300Sgjelinek 38341507Sgjelinek static int 38351507Sgjelinek detach_func(int argc, char *argv[]) 38361507Sgjelinek { 38371507Sgjelinek int lockfd; 38381507Sgjelinek int err, arg; 38391507Sgjelinek char zonepath[MAXPATHLEN]; 38401507Sgjelinek zone_dochandle_t handle; 38412078Sgjelinek boolean_t execute = B_TRUE; 38421507Sgjelinek 38431507Sgjelinek if (zonecfg_in_alt_root()) { 38441507Sgjelinek zerror(gettext("cannot detach zone in alternate root")); 38451507Sgjelinek return (Z_ERR); 38461507Sgjelinek } 38471507Sgjelinek 38481507Sgjelinek optind = 0; 38492078Sgjelinek if ((arg = getopt(argc, argv, "?n")) != EOF) { 38501507Sgjelinek switch (arg) { 38511507Sgjelinek case '?': 38521507Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 38531507Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 38542078Sgjelinek case 'n': 38552078Sgjelinek execute = B_FALSE; 38562078Sgjelinek break; 38571507Sgjelinek default: 38581507Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 38591507Sgjelinek return (Z_USAGE); 38601507Sgjelinek } 38611507Sgjelinek } 38622078Sgjelinek if (execute) { 38632078Sgjelinek if (sanity_check(target_zone, CMD_DETACH, B_FALSE, B_TRUE) 38642078Sgjelinek != Z_OK) 38652078Sgjelinek return (Z_ERR); 38662078Sgjelinek if (verify_details(CMD_DETACH) != Z_OK) 38672078Sgjelinek return (Z_ERR); 38682078Sgjelinek } else { 38692078Sgjelinek /* 38702078Sgjelinek * We want a dry-run to work for a non-privileged user so we 38712078Sgjelinek * only do minimal validation. 38722078Sgjelinek */ 38732078Sgjelinek if (getzoneid() != GLOBAL_ZONEID) { 38742078Sgjelinek zerror(gettext("must be in the global zone to %s a " 38752078Sgjelinek "zone."), cmd_to_str(CMD_DETACH)); 38762078Sgjelinek return (Z_ERR); 38772078Sgjelinek } 38782078Sgjelinek 38792078Sgjelinek if (target_zone == NULL) { 38802078Sgjelinek zerror(gettext("no zone specified")); 38812078Sgjelinek return (Z_ERR); 38822078Sgjelinek } 38832078Sgjelinek 38842078Sgjelinek if (strcmp(target_zone, GLOBAL_ZONENAME) == 0) { 38852078Sgjelinek zerror(gettext("%s operation is invalid for the " 38862078Sgjelinek "global zone."), cmd_to_str(CMD_DETACH)); 38872078Sgjelinek return (Z_ERR); 38882078Sgjelinek } 38892078Sgjelinek } 38901507Sgjelinek 38911507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 38921507Sgjelinek != Z_OK) { 38931507Sgjelinek errno = err; 38941507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 38951507Sgjelinek return (Z_ERR); 38961507Sgjelinek } 38971507Sgjelinek 38981507Sgjelinek /* Don't detach the zone if anything is still mounted there */ 38992078Sgjelinek if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) { 39001867Sgjelinek zerror(gettext("These file systems are mounted on " 39011507Sgjelinek "subdirectories of %s.\n"), zonepath); 39021507Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 39031507Sgjelinek return (Z_ERR); 39041507Sgjelinek } 39051507Sgjelinek 39061507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 39071507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 39081507Sgjelinek return (Z_ERR); 39091507Sgjelinek } 39101507Sgjelinek 39111507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 39121507Sgjelinek errno = err; 39131507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 39141507Sgjelinek zonecfg_fini_handle(handle); 39151507Sgjelinek return (Z_ERR); 39161507Sgjelinek } 39171507Sgjelinek 39182078Sgjelinek if (execute && grab_lock_file(target_zone, &lockfd) != Z_OK) { 39191507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 39201507Sgjelinek "zoneadm"); 39211507Sgjelinek zonecfg_fini_handle(handle); 39221507Sgjelinek return (Z_ERR); 39231507Sgjelinek } 39241507Sgjelinek 39251507Sgjelinek if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) { 39261507Sgjelinek errno = err; 39271507Sgjelinek zperror(gettext("getting the detach information failed"), 39281507Sgjelinek B_TRUE); 39291507Sgjelinek goto done; 39301507Sgjelinek } 39311507Sgjelinek 39322078Sgjelinek if ((err = zonecfg_detach_save(handle, (execute ? 0 : ZONE_DRY_RUN))) 39332078Sgjelinek != Z_OK) { 39341507Sgjelinek errno = err; 39351507Sgjelinek zperror(gettext("saving the detach manifest failed"), B_TRUE); 39361507Sgjelinek goto done; 39371507Sgjelinek } 39381507Sgjelinek 39392078Sgjelinek /* 39402078Sgjelinek * Set the zone state back to configured unless we are running with the 39412078Sgjelinek * no-execute option. 39422078Sgjelinek */ 39432078Sgjelinek if (execute && (err = zone_set_state(target_zone, 39442078Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 39451507Sgjelinek errno = err; 39461507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 39471507Sgjelinek } 39481507Sgjelinek 39491507Sgjelinek done: 39501507Sgjelinek zonecfg_fini_handle(handle); 39512078Sgjelinek if (execute) 39522078Sgjelinek release_lock_file(lockfd); 39531507Sgjelinek 39541507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 39551507Sgjelinek } 39561507Sgjelinek 39571507Sgjelinek /* 39581507Sgjelinek * During attach we go through and fix up the /dev entries for the zone 39591507Sgjelinek * we are attaching. In order to regenerate /dev with the correct devices, 39601507Sgjelinek * the old /dev will be removed, the zone readied (which generates a new 39611507Sgjelinek * /dev) then halted, then we use the info from the manifest to update 39621507Sgjelinek * the modes, owners, etc. on the new /dev. 39631507Sgjelinek */ 39641507Sgjelinek static int 39651507Sgjelinek dev_fix(zone_dochandle_t handle) 39661507Sgjelinek { 39671507Sgjelinek int res; 39681507Sgjelinek int err; 39691507Sgjelinek int status; 39701507Sgjelinek struct zone_devpermtab devtab; 39711507Sgjelinek zone_cmd_arg_t zarg; 39721507Sgjelinek char devpath[MAXPATHLEN]; 39731507Sgjelinek /* 6: "exec " and " " */ 39741507Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 6]; 39751507Sgjelinek 39761507Sgjelinek if ((res = zonecfg_get_zonepath(handle, devpath, sizeof (devpath))) 39771507Sgjelinek != Z_OK) 39781507Sgjelinek return (res); 39791507Sgjelinek 39801507Sgjelinek if (strlcat(devpath, "/dev", sizeof (devpath)) >= sizeof (devpath)) 39811507Sgjelinek return (Z_TOO_BIG); 39821507Sgjelinek 39831507Sgjelinek /* 39841507Sgjelinek * "exec" the command so that the returned status is that of 39851507Sgjelinek * RMCOMMAND and not the shell. 39861507Sgjelinek */ 39871507Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 39881507Sgjelinek devpath); 39891507Sgjelinek status = do_subproc(cmdbuf); 39901507Sgjelinek if ((err = subproc_status(RMCOMMAND, status)) != Z_OK) { 39911507Sgjelinek (void) fprintf(stderr, 39921507Sgjelinek gettext("could not remove existing /dev\n")); 39931507Sgjelinek return (Z_ERR); 39941507Sgjelinek } 39951507Sgjelinek 39961507Sgjelinek /* In order to ready the zone, it must be in the installed state */ 39971507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 39981507Sgjelinek errno = err; 39991507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 40001507Sgjelinek return (Z_ERR); 40011507Sgjelinek } 40021507Sgjelinek 40031507Sgjelinek /* We have to ready the zone to regen the dev tree */ 40041507Sgjelinek zarg.cmd = Z_READY; 40051507Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 40061507Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 40071507Sgjelinek return (Z_ERR); 40081507Sgjelinek } 40091507Sgjelinek 40101507Sgjelinek zarg.cmd = Z_HALT; 40111507Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 40121507Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 40131507Sgjelinek return (Z_ERR); 40141507Sgjelinek } 40151507Sgjelinek 40161507Sgjelinek if (zonecfg_setdevperment(handle) != Z_OK) { 40171507Sgjelinek (void) fprintf(stderr, 40181507Sgjelinek gettext("unable to enumerate device entries\n")); 40191507Sgjelinek return (Z_ERR); 40201507Sgjelinek } 40211507Sgjelinek 40221507Sgjelinek while (zonecfg_getdevperment(handle, &devtab) == Z_OK) { 40231507Sgjelinek int err; 40241507Sgjelinek 40251507Sgjelinek if ((err = zonecfg_devperms_apply(handle, 40261507Sgjelinek devtab.zone_devperm_name, devtab.zone_devperm_uid, 40271507Sgjelinek devtab.zone_devperm_gid, devtab.zone_devperm_mode, 40281507Sgjelinek devtab.zone_devperm_acl)) != Z_OK && err != Z_INVAL) 40291507Sgjelinek (void) fprintf(stderr, gettext("error updating device " 40301507Sgjelinek "%s: %s\n"), devtab.zone_devperm_name, 40311507Sgjelinek zonecfg_strerror(err)); 40321507Sgjelinek 40331507Sgjelinek free(devtab.zone_devperm_acl); 40341507Sgjelinek } 40351507Sgjelinek 40361507Sgjelinek (void) zonecfg_enddevperment(handle); 40371507Sgjelinek 40381507Sgjelinek return (Z_OK); 40391507Sgjelinek } 40401507Sgjelinek 40412078Sgjelinek /* 40422078Sgjelinek * Validate attaching a zone but don't actually do the work. The zone 40432078Sgjelinek * does not have to exist, so there is some complexity getting a new zone 40442078Sgjelinek * configuration set up so that we can perform the validation. This is 40452078Sgjelinek * handled within zonecfg_attach_manifest() which returns two handles; one 40462078Sgjelinek * for the the full configuration to validate (rem_handle) and the other 40472078Sgjelinek * (local_handle) containing only the zone configuration derived from the 40482078Sgjelinek * manifest. 40492078Sgjelinek */ 40502078Sgjelinek static int 40512078Sgjelinek dryrun_attach(char *manifest_path) 40522078Sgjelinek { 40532078Sgjelinek int fd; 40542078Sgjelinek int err; 40552078Sgjelinek int res; 40562078Sgjelinek zone_dochandle_t local_handle; 40572078Sgjelinek zone_dochandle_t rem_handle = NULL; 40582078Sgjelinek 40592078Sgjelinek if (strcmp(manifest_path, "-") == 0) { 40602078Sgjelinek fd = 0; 40612078Sgjelinek } else if ((fd = open(manifest_path, O_RDONLY)) < 0) { 40622078Sgjelinek zperror(gettext("could not open manifest path"), B_FALSE); 40632078Sgjelinek return (Z_ERR); 40642078Sgjelinek } 40652078Sgjelinek 40662078Sgjelinek if ((local_handle = zonecfg_init_handle()) == NULL) { 40672078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40682078Sgjelinek res = Z_ERR; 40692078Sgjelinek goto done; 40702078Sgjelinek } 40712078Sgjelinek 40722078Sgjelinek if ((rem_handle = zonecfg_init_handle()) == NULL) { 40732078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40742078Sgjelinek res = Z_ERR; 40752078Sgjelinek goto done; 40762078Sgjelinek } 40772078Sgjelinek 40782078Sgjelinek if ((err = zonecfg_attach_manifest(fd, local_handle, rem_handle)) 40792078Sgjelinek != Z_OK) { 40802078Sgjelinek if (err == Z_INVALID_DOCUMENT) 40812078Sgjelinek zerror(gettext("Cannot attach to an earlier release " 40822078Sgjelinek "of the operating system")); 40832078Sgjelinek else 40842078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40852078Sgjelinek res = Z_ERR; 40862078Sgjelinek goto done; 40872078Sgjelinek } 40882078Sgjelinek 40892078Sgjelinek res = verify_handle(CMD_ATTACH, local_handle); 40902078Sgjelinek 40912078Sgjelinek /* Get the detach information for the locally defined zone. */ 40922078Sgjelinek if ((err = zonecfg_get_detach_info(local_handle, B_FALSE)) != Z_OK) { 40932078Sgjelinek errno = err; 40942078Sgjelinek zperror(gettext("getting the attach information failed"), 40952078Sgjelinek B_TRUE); 40962078Sgjelinek res = Z_ERR; 40972078Sgjelinek } else { 40982078Sgjelinek /* sw_cmp prints error msgs as necessary */ 40992078Sgjelinek if (sw_cmp(local_handle, rem_handle, SW_CMP_NONE) != Z_OK) 41002078Sgjelinek res = Z_ERR; 41012078Sgjelinek } 41022078Sgjelinek 41032078Sgjelinek done: 41042078Sgjelinek if (strcmp(manifest_path, "-") != 0) 41052078Sgjelinek (void) close(fd); 41062078Sgjelinek 41072078Sgjelinek zonecfg_fini_handle(local_handle); 41082078Sgjelinek zonecfg_fini_handle(rem_handle); 41092078Sgjelinek 41102078Sgjelinek return ((res == Z_OK) ? Z_OK : Z_ERR); 41112078Sgjelinek } 41122078Sgjelinek 41131507Sgjelinek static int 41141507Sgjelinek attach_func(int argc, char *argv[]) 41151507Sgjelinek { 41161507Sgjelinek int lockfd; 41171507Sgjelinek int err, arg; 41181507Sgjelinek boolean_t force = B_FALSE; 41191507Sgjelinek zone_dochandle_t handle; 41201507Sgjelinek zone_dochandle_t athandle = NULL; 41211507Sgjelinek char zonepath[MAXPATHLEN]; 41222078Sgjelinek boolean_t execute = B_TRUE; 41232078Sgjelinek char *manifest_path; 41241507Sgjelinek 41251507Sgjelinek if (zonecfg_in_alt_root()) { 41261507Sgjelinek zerror(gettext("cannot attach zone in alternate root")); 41271507Sgjelinek return (Z_ERR); 41281507Sgjelinek } 41291507Sgjelinek 41301507Sgjelinek optind = 0; 41312078Sgjelinek if ((arg = getopt(argc, argv, "?Fn:")) != EOF) { 41321507Sgjelinek switch (arg) { 41331507Sgjelinek case '?': 41341507Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 41351507Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 41361507Sgjelinek case 'F': 41371507Sgjelinek force = B_TRUE; 41381507Sgjelinek break; 41392078Sgjelinek case 'n': 41402078Sgjelinek execute = B_FALSE; 41412078Sgjelinek manifest_path = optarg; 41422078Sgjelinek break; 41431507Sgjelinek default: 41441507Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 41451507Sgjelinek return (Z_USAGE); 41461507Sgjelinek } 41471507Sgjelinek } 41482078Sgjelinek 41492078Sgjelinek /* 41502078Sgjelinek * If the no-execute option was specified, we need to branch down 41512078Sgjelinek * a completely different path since there is no zone required to be 41522078Sgjelinek * configured for this option. 41532078Sgjelinek */ 41542078Sgjelinek if (!execute) 41552078Sgjelinek return (dryrun_attach(manifest_path)); 41562078Sgjelinek 41571507Sgjelinek if (sanity_check(target_zone, CMD_ATTACH, B_FALSE, B_TRUE) != Z_OK) 41581507Sgjelinek return (Z_ERR); 41591507Sgjelinek if (verify_details(CMD_ATTACH) != Z_OK) 41601507Sgjelinek return (Z_ERR); 41611507Sgjelinek 41621507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 41631507Sgjelinek != Z_OK) { 41641507Sgjelinek errno = err; 41651507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 41661507Sgjelinek return (Z_ERR); 41671507Sgjelinek } 41681507Sgjelinek 41691507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 41701507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 41711507Sgjelinek return (Z_ERR); 41721507Sgjelinek } 41731507Sgjelinek 41741507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 41751507Sgjelinek errno = err; 41761507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 41771507Sgjelinek zonecfg_fini_handle(handle); 41781507Sgjelinek return (Z_ERR); 41791507Sgjelinek } 41801507Sgjelinek 41811507Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 41821507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 41831507Sgjelinek "zoneadm"); 41841507Sgjelinek zonecfg_fini_handle(handle); 41851507Sgjelinek return (Z_ERR); 41861507Sgjelinek } 41871507Sgjelinek 41881507Sgjelinek if (force) 41891507Sgjelinek goto forced; 41901507Sgjelinek 41911507Sgjelinek if ((athandle = zonecfg_init_handle()) == NULL) { 41921507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 41931507Sgjelinek goto done; 41941507Sgjelinek } 41951507Sgjelinek 41961507Sgjelinek if ((err = zonecfg_get_attach_handle(zonepath, target_zone, B_TRUE, 41971507Sgjelinek athandle)) != Z_OK) { 41981507Sgjelinek if (err == Z_NO_ZONE) 41991507Sgjelinek zerror(gettext("Not a detached zone")); 42001507Sgjelinek else if (err == Z_INVALID_DOCUMENT) 42011507Sgjelinek zerror(gettext("Cannot attach to an earlier release " 42021507Sgjelinek "of the operating system")); 42031507Sgjelinek else 42041507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 42051507Sgjelinek goto done; 42061507Sgjelinek } 42071507Sgjelinek 42081507Sgjelinek /* Get the detach information for the locally defined zone. */ 42091507Sgjelinek if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) { 42101507Sgjelinek errno = err; 42111507Sgjelinek zperror(gettext("getting the attach information failed"), 42121507Sgjelinek B_TRUE); 42131507Sgjelinek goto done; 42141507Sgjelinek } 42151507Sgjelinek 42161507Sgjelinek /* sw_cmp prints error msgs as necessary */ 42171867Sgjelinek if ((err = sw_cmp(handle, athandle, SW_CMP_NONE)) != Z_OK) 42181507Sgjelinek goto done; 42191507Sgjelinek 42201507Sgjelinek if ((err = dev_fix(athandle)) != Z_OK) 42211507Sgjelinek goto done; 42221507Sgjelinek 42231507Sgjelinek forced: 42241507Sgjelinek 42251507Sgjelinek zonecfg_rm_detached(handle, force); 42261507Sgjelinek 42271507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 42281507Sgjelinek errno = err; 42291507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 42301507Sgjelinek } 42311507Sgjelinek 42321507Sgjelinek done: 42331507Sgjelinek zonecfg_fini_handle(handle); 42341507Sgjelinek release_lock_file(lockfd); 42351507Sgjelinek if (athandle != NULL) 42361507Sgjelinek zonecfg_fini_handle(athandle); 42371507Sgjelinek 42381507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 42391507Sgjelinek } 42401507Sgjelinek 42411300Sgjelinek /* 42420Sstevel@tonic-gate * On input, TRUE => yes, FALSE => no. 42430Sstevel@tonic-gate * On return, TRUE => 1, FALSE => 0, could not ask => -1. 42440Sstevel@tonic-gate */ 42450Sstevel@tonic-gate 42460Sstevel@tonic-gate static int 42470Sstevel@tonic-gate ask_yesno(boolean_t default_answer, const char *question) 42480Sstevel@tonic-gate { 42490Sstevel@tonic-gate char line[64]; /* should be large enough to answer yes or no */ 42500Sstevel@tonic-gate 42510Sstevel@tonic-gate if (!isatty(STDIN_FILENO)) 42520Sstevel@tonic-gate return (-1); 42530Sstevel@tonic-gate for (;;) { 42540Sstevel@tonic-gate (void) printf("%s (%s)? ", question, 42550Sstevel@tonic-gate default_answer ? "[y]/n" : "y/[n]"); 42560Sstevel@tonic-gate if (fgets(line, sizeof (line), stdin) == NULL || 42570Sstevel@tonic-gate line[0] == '\n') 42580Sstevel@tonic-gate return (default_answer ? 1 : 0); 42590Sstevel@tonic-gate if (tolower(line[0]) == 'y') 42600Sstevel@tonic-gate return (1); 42610Sstevel@tonic-gate if (tolower(line[0]) == 'n') 42620Sstevel@tonic-gate return (0); 42630Sstevel@tonic-gate } 42640Sstevel@tonic-gate } 42650Sstevel@tonic-gate 42660Sstevel@tonic-gate static int 42670Sstevel@tonic-gate uninstall_func(int argc, char *argv[]) 42680Sstevel@tonic-gate { 42690Sstevel@tonic-gate char line[ZONENAME_MAX + 128]; /* Enough for "Are you sure ..." */ 42701867Sgjelinek char rootpath[MAXPATHLEN], zonepath[MAXPATHLEN]; 42710Sstevel@tonic-gate boolean_t force = B_FALSE; 42720Sstevel@tonic-gate int lockfd, answer; 42730Sstevel@tonic-gate int err, arg; 42740Sstevel@tonic-gate 4275766Scarlsonj if (zonecfg_in_alt_root()) { 4276766Scarlsonj zerror(gettext("cannot uninstall zone in alternate root")); 4277766Scarlsonj return (Z_ERR); 4278766Scarlsonj } 4279766Scarlsonj 42800Sstevel@tonic-gate optind = 0; 42810Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?F")) != EOF) { 42820Sstevel@tonic-gate switch (arg) { 42830Sstevel@tonic-gate case '?': 42840Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 42850Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 42860Sstevel@tonic-gate case 'F': 42870Sstevel@tonic-gate force = B_TRUE; 42880Sstevel@tonic-gate break; 42890Sstevel@tonic-gate default: 42900Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 42910Sstevel@tonic-gate return (Z_USAGE); 42920Sstevel@tonic-gate } 42930Sstevel@tonic-gate } 42940Sstevel@tonic-gate if (argc > optind) { 42950Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 42960Sstevel@tonic-gate return (Z_USAGE); 42970Sstevel@tonic-gate } 42980Sstevel@tonic-gate 42990Sstevel@tonic-gate if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE) != Z_OK) 43000Sstevel@tonic-gate return (Z_ERR); 43010Sstevel@tonic-gate 43020Sstevel@tonic-gate if (!force) { 43030Sstevel@tonic-gate (void) snprintf(line, sizeof (line), 43040Sstevel@tonic-gate gettext("Are you sure you want to %s zone %s"), 43050Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL), target_zone); 43060Sstevel@tonic-gate if ((answer = ask_yesno(B_FALSE, line)) == 0) { 43070Sstevel@tonic-gate return (Z_OK); 43080Sstevel@tonic-gate } else if (answer == -1) { 43090Sstevel@tonic-gate zerror(gettext("Input not from terminal and -F " 43100Sstevel@tonic-gate "not specified: %s not done."), 43110Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 43120Sstevel@tonic-gate return (Z_ERR); 43130Sstevel@tonic-gate } 43140Sstevel@tonic-gate } 43150Sstevel@tonic-gate 43161867Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, 43171867Sgjelinek sizeof (zonepath))) != Z_OK) { 43180Sstevel@tonic-gate errno = err; 43190Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 43200Sstevel@tonic-gate return (Z_ERR); 43210Sstevel@tonic-gate } 43220Sstevel@tonic-gate if ((err = zone_get_rootpath(target_zone, rootpath, 43230Sstevel@tonic-gate sizeof (rootpath))) != Z_OK) { 43240Sstevel@tonic-gate errno = err; 43250Sstevel@tonic-gate zperror2(target_zone, gettext("could not get root path")); 43260Sstevel@tonic-gate return (Z_ERR); 43270Sstevel@tonic-gate } 43280Sstevel@tonic-gate 43290Sstevel@tonic-gate /* 43300Sstevel@tonic-gate * If there seems to be a zoneadmd running for this zone, call it 43310Sstevel@tonic-gate * to tell it that an uninstall is happening; if all goes well it 43320Sstevel@tonic-gate * will then shut itself down. 43330Sstevel@tonic-gate */ 43340Sstevel@tonic-gate if (ping_zoneadmd(target_zone) == Z_OK) { 43350Sstevel@tonic-gate zone_cmd_arg_t zarg; 43360Sstevel@tonic-gate zarg.cmd = Z_NOTE_UNINSTALLING; 43370Sstevel@tonic-gate /* we don't care too much if this fails... just plow on */ 43380Sstevel@tonic-gate (void) call_zoneadmd(target_zone, &zarg); 43390Sstevel@tonic-gate } 43400Sstevel@tonic-gate 43410Sstevel@tonic-gate if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 43420Sstevel@tonic-gate zerror(gettext("another %s may have an operation in progress."), 43431300Sgjelinek "zoneadm"); 43440Sstevel@tonic-gate return (Z_ERR); 43450Sstevel@tonic-gate } 43460Sstevel@tonic-gate 43470Sstevel@tonic-gate /* Don't uninstall the zone if anything is mounted there */ 43480Sstevel@tonic-gate err = zonecfg_find_mounts(rootpath, NULL, NULL); 43490Sstevel@tonic-gate if (err) { 43501867Sgjelinek zerror(gettext("These file systems are mounted on " 43511645Scomay "subdirectories of %s.\n"), rootpath); 43520Sstevel@tonic-gate (void) zonecfg_find_mounts(rootpath, zfm_print, NULL); 43530Sstevel@tonic-gate return (Z_ERR); 43540Sstevel@tonic-gate } 43550Sstevel@tonic-gate 43560Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 43570Sstevel@tonic-gate if (err != Z_OK) { 43580Sstevel@tonic-gate errno = err; 43590Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 43600Sstevel@tonic-gate goto bad; 43610Sstevel@tonic-gate } 43620Sstevel@tonic-gate 43631867Sgjelinek if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) { 43641867Sgjelinek errno = err; 43651867Sgjelinek zperror2(target_zone, gettext("cleaning up zonepath failed")); 43660Sstevel@tonic-gate goto bad; 43671867Sgjelinek } 43681867Sgjelinek 43690Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED); 43700Sstevel@tonic-gate if (err != Z_OK) { 43710Sstevel@tonic-gate errno = err; 43720Sstevel@tonic-gate zperror2(target_zone, gettext("could not reset state")); 43730Sstevel@tonic-gate } 43740Sstevel@tonic-gate bad: 43750Sstevel@tonic-gate release_lock_file(lockfd); 43760Sstevel@tonic-gate return (err); 43770Sstevel@tonic-gate } 43780Sstevel@tonic-gate 4379766Scarlsonj /* ARGSUSED */ 4380766Scarlsonj static int 4381766Scarlsonj mount_func(int argc, char *argv[]) 4382766Scarlsonj { 4383766Scarlsonj zone_cmd_arg_t zarg; 4384766Scarlsonj 4385766Scarlsonj if (argc > 0) 4386766Scarlsonj return (Z_USAGE); 4387766Scarlsonj if (sanity_check(target_zone, CMD_MOUNT, B_FALSE, B_FALSE) != Z_OK) 4388766Scarlsonj return (Z_ERR); 4389766Scarlsonj if (verify_details(CMD_MOUNT) != Z_OK) 4390766Scarlsonj return (Z_ERR); 4391766Scarlsonj 4392766Scarlsonj zarg.cmd = Z_MOUNT; 4393766Scarlsonj if (call_zoneadmd(target_zone, &zarg) != 0) { 4394766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 4395766Scarlsonj return (Z_ERR); 4396766Scarlsonj } 4397766Scarlsonj return (Z_OK); 4398766Scarlsonj } 4399766Scarlsonj 4400766Scarlsonj /* ARGSUSED */ 4401766Scarlsonj static int 4402766Scarlsonj unmount_func(int argc, char *argv[]) 4403766Scarlsonj { 4404766Scarlsonj zone_cmd_arg_t zarg; 4405766Scarlsonj 4406766Scarlsonj if (argc > 0) 4407766Scarlsonj return (Z_USAGE); 4408766Scarlsonj if (sanity_check(target_zone, CMD_UNMOUNT, B_FALSE, B_FALSE) != Z_OK) 4409766Scarlsonj return (Z_ERR); 4410766Scarlsonj 4411766Scarlsonj zarg.cmd = Z_UNMOUNT; 4412766Scarlsonj if (call_zoneadmd(target_zone, &zarg) != 0) { 4413766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 4414766Scarlsonj return (Z_ERR); 4415766Scarlsonj } 4416766Scarlsonj return (Z_OK); 4417766Scarlsonj } 4418766Scarlsonj 44190Sstevel@tonic-gate static int 44202303Scarlsonj mark_func(int argc, char *argv[]) 44212303Scarlsonj { 44222303Scarlsonj int err, lockfd; 44232303Scarlsonj 44242303Scarlsonj if (argc != 1 || strcmp(argv[0], "incomplete") != 0) 44252303Scarlsonj return (Z_USAGE); 44262303Scarlsonj if (sanity_check(target_zone, CMD_MARK, B_FALSE, B_FALSE) != Z_OK) 44272303Scarlsonj return (Z_ERR); 44282303Scarlsonj 44292303Scarlsonj if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 44302303Scarlsonj zerror(gettext("another %s may have an operation in progress."), 44312303Scarlsonj "zoneadm"); 44322303Scarlsonj return (Z_ERR); 44332303Scarlsonj } 44342303Scarlsonj 44352303Scarlsonj err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 44362303Scarlsonj if (err != Z_OK) { 44372303Scarlsonj errno = err; 44382303Scarlsonj zperror2(target_zone, gettext("could not set state")); 44392303Scarlsonj } 44402303Scarlsonj release_lock_file(lockfd); 44412303Scarlsonj 44422303Scarlsonj return (err); 44432303Scarlsonj } 44442303Scarlsonj 44452303Scarlsonj static int 44460Sstevel@tonic-gate help_func(int argc, char *argv[]) 44470Sstevel@tonic-gate { 44480Sstevel@tonic-gate int arg, cmd_num; 44490Sstevel@tonic-gate 44500Sstevel@tonic-gate if (argc == 0) { 44510Sstevel@tonic-gate (void) usage(B_TRUE); 44520Sstevel@tonic-gate return (Z_OK); 44530Sstevel@tonic-gate } 44540Sstevel@tonic-gate optind = 0; 44550Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 44560Sstevel@tonic-gate switch (arg) { 44570Sstevel@tonic-gate case '?': 44580Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 44590Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 44600Sstevel@tonic-gate default: 44610Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 44620Sstevel@tonic-gate return (Z_USAGE); 44630Sstevel@tonic-gate } 44640Sstevel@tonic-gate } 44650Sstevel@tonic-gate while (optind < argc) { 4466988Scarlsonj /* Private commands have NULL short_usage; omit them */ 4467988Scarlsonj if ((cmd_num = cmd_match(argv[optind])) < 0 || 4468988Scarlsonj cmdtab[cmd_num].short_usage == NULL) { 44690Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 44700Sstevel@tonic-gate return (Z_USAGE); 44710Sstevel@tonic-gate } 44720Sstevel@tonic-gate sub_usage(cmdtab[cmd_num].short_usage, cmd_num); 44730Sstevel@tonic-gate optind++; 44740Sstevel@tonic-gate } 44750Sstevel@tonic-gate return (Z_OK); 44760Sstevel@tonic-gate } 44770Sstevel@tonic-gate 44780Sstevel@tonic-gate /* 44790Sstevel@tonic-gate * Returns: CMD_MIN thru CMD_MAX on success, -1 on error 44800Sstevel@tonic-gate */ 44810Sstevel@tonic-gate 44820Sstevel@tonic-gate static int 44830Sstevel@tonic-gate cmd_match(char *cmd) 44840Sstevel@tonic-gate { 44850Sstevel@tonic-gate int i; 44860Sstevel@tonic-gate 44870Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 44880Sstevel@tonic-gate /* return only if there is an exact match */ 44890Sstevel@tonic-gate if (strcmp(cmd, cmdtab[i].cmd_name) == 0) 44900Sstevel@tonic-gate return (cmdtab[i].cmd_num); 44910Sstevel@tonic-gate } 44920Sstevel@tonic-gate return (-1); 44930Sstevel@tonic-gate } 44940Sstevel@tonic-gate 44950Sstevel@tonic-gate static int 44960Sstevel@tonic-gate parse_and_run(int argc, char *argv[]) 44970Sstevel@tonic-gate { 44980Sstevel@tonic-gate int i = cmd_match(argv[0]); 44990Sstevel@tonic-gate 45000Sstevel@tonic-gate if (i < 0) 45010Sstevel@tonic-gate return (usage(B_FALSE)); 45020Sstevel@tonic-gate return (cmdtab[i].handler(argc - 1, &(argv[1]))); 45030Sstevel@tonic-gate } 45040Sstevel@tonic-gate 45050Sstevel@tonic-gate static char * 45060Sstevel@tonic-gate get_execbasename(char *execfullname) 45070Sstevel@tonic-gate { 45080Sstevel@tonic-gate char *last_slash, *execbasename; 45090Sstevel@tonic-gate 45100Sstevel@tonic-gate /* guard against '/' at end of command invocation */ 45110Sstevel@tonic-gate for (;;) { 45120Sstevel@tonic-gate last_slash = strrchr(execfullname, '/'); 45130Sstevel@tonic-gate if (last_slash == NULL) { 45140Sstevel@tonic-gate execbasename = execfullname; 45150Sstevel@tonic-gate break; 45160Sstevel@tonic-gate } else { 45170Sstevel@tonic-gate execbasename = last_slash + 1; 45180Sstevel@tonic-gate if (*execbasename == '\0') { 45190Sstevel@tonic-gate *last_slash = '\0'; 45200Sstevel@tonic-gate continue; 45210Sstevel@tonic-gate } 45220Sstevel@tonic-gate break; 45230Sstevel@tonic-gate } 45240Sstevel@tonic-gate } 45250Sstevel@tonic-gate return (execbasename); 45260Sstevel@tonic-gate } 45270Sstevel@tonic-gate 45280Sstevel@tonic-gate int 45290Sstevel@tonic-gate main(int argc, char **argv) 45300Sstevel@tonic-gate { 45310Sstevel@tonic-gate int arg; 45320Sstevel@tonic-gate zoneid_t zid; 4533766Scarlsonj struct stat st; 45340Sstevel@tonic-gate 45350Sstevel@tonic-gate if ((locale = setlocale(LC_ALL, "")) == NULL) 45360Sstevel@tonic-gate locale = "C"; 45370Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 45380Sstevel@tonic-gate setbuf(stdout, NULL); 45390Sstevel@tonic-gate (void) sigset(SIGHUP, SIG_IGN); 45400Sstevel@tonic-gate execname = get_execbasename(argv[0]); 45410Sstevel@tonic-gate target_zone = NULL; 45420Sstevel@tonic-gate if (chdir("/") != 0) { 45430Sstevel@tonic-gate zerror(gettext("could not change directory to /.")); 45440Sstevel@tonic-gate exit(Z_ERR); 45450Sstevel@tonic-gate } 45460Sstevel@tonic-gate 45472082Seschrock if (init_zfs() != Z_OK) 45482082Seschrock exit(Z_ERR); 45492082Seschrock 45502303Scarlsonj while ((arg = getopt(argc, argv, "?u:z:R:")) != EOF) { 45510Sstevel@tonic-gate switch (arg) { 45520Sstevel@tonic-gate case '?': 45530Sstevel@tonic-gate return (usage(B_TRUE)); 45542303Scarlsonj case 'u': 45552303Scarlsonj target_uuid = optarg; 45562303Scarlsonj break; 45570Sstevel@tonic-gate case 'z': 45580Sstevel@tonic-gate target_zone = optarg; 45590Sstevel@tonic-gate break; 4560766Scarlsonj case 'R': /* private option for admin/install use */ 4561766Scarlsonj if (*optarg != '/') { 4562766Scarlsonj zerror(gettext("root path must be absolute.")); 4563766Scarlsonj exit(Z_ERR); 4564766Scarlsonj } 4565766Scarlsonj if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { 4566766Scarlsonj zerror( 4567766Scarlsonj gettext("root path must be a directory.")); 4568766Scarlsonj exit(Z_ERR); 4569766Scarlsonj } 4570766Scarlsonj zonecfg_set_root(optarg); 4571766Scarlsonj break; 45720Sstevel@tonic-gate default: 45730Sstevel@tonic-gate return (usage(B_FALSE)); 45740Sstevel@tonic-gate } 45750Sstevel@tonic-gate } 45760Sstevel@tonic-gate 45770Sstevel@tonic-gate if (optind >= argc) 45780Sstevel@tonic-gate return (usage(B_FALSE)); 45792303Scarlsonj 45802303Scarlsonj if (target_uuid != NULL && *target_uuid != '\0') { 45812303Scarlsonj uuid_t uuid; 45822303Scarlsonj static char newtarget[ZONENAME_MAX]; 45832303Scarlsonj 45842303Scarlsonj if (uuid_parse(target_uuid, uuid) == -1) { 45852303Scarlsonj zerror(gettext("illegal UUID value specified")); 45862303Scarlsonj exit(Z_ERR); 45872303Scarlsonj } 45882303Scarlsonj if (zonecfg_get_name_by_uuid(uuid, newtarget, 45892303Scarlsonj sizeof (newtarget)) == Z_OK) 45902303Scarlsonj target_zone = newtarget; 45912303Scarlsonj } 45922303Scarlsonj 45930Sstevel@tonic-gate if (target_zone != NULL && zone_get_id(target_zone, &zid) != 0) { 45940Sstevel@tonic-gate errno = Z_NO_ZONE; 45950Sstevel@tonic-gate zperror(target_zone, B_TRUE); 45960Sstevel@tonic-gate exit(Z_ERR); 45970Sstevel@tonic-gate } 45980Sstevel@tonic-gate return (parse_and_run(argc - optind, &argv[optind])); 45990Sstevel@tonic-gate } 4600