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> 670Sstevel@tonic-gate 680Sstevel@tonic-gate #include <fcntl.h> 690Sstevel@tonic-gate #include <door.h> 700Sstevel@tonic-gate #include <macros.h> 710Sstevel@tonic-gate #include <libgen.h> 721300Sgjelinek #include <fnmatch.h> 731931Sgjelinek #include <sys/modctl.h> 740Sstevel@tonic-gate 750Sstevel@tonic-gate #include <pool.h> 760Sstevel@tonic-gate #include <sys/pool.h> 770Sstevel@tonic-gate 781867Sgjelinek #include "zoneadm.h" 791867Sgjelinek 800Sstevel@tonic-gate #define MAXARGS 8 810Sstevel@tonic-gate 820Sstevel@tonic-gate /* Reflects kernel zone entries */ 830Sstevel@tonic-gate typedef struct zone_entry { 840Sstevel@tonic-gate zoneid_t zid; 850Sstevel@tonic-gate char zname[ZONENAME_MAX]; 860Sstevel@tonic-gate char *zstate_str; 870Sstevel@tonic-gate zone_state_t zstate_num; 880Sstevel@tonic-gate char zroot[MAXPATHLEN]; 890Sstevel@tonic-gate } zone_entry_t; 900Sstevel@tonic-gate 910Sstevel@tonic-gate static zone_entry_t *zents; 920Sstevel@tonic-gate static size_t nzents; 930Sstevel@tonic-gate 941915Sgjelinek #define LOOPBACK_IF "lo0" 951915Sgjelinek #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af)) 961915Sgjelinek 971915Sgjelinek struct net_if { 981915Sgjelinek char *name; 991915Sgjelinek int af; 1001915Sgjelinek }; 1011915Sgjelinek 1020Sstevel@tonic-gate /* 0755 is the default directory mode. */ 1030Sstevel@tonic-gate #define DEFAULT_DIR_MODE \ 1040Sstevel@tonic-gate (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate struct cmd { 1070Sstevel@tonic-gate uint_t cmd_num; /* command number */ 1080Sstevel@tonic-gate char *cmd_name; /* command name */ 1090Sstevel@tonic-gate char *short_usage; /* short form help */ 1100Sstevel@tonic-gate int (*handler)(int argc, char *argv[]); /* function to call */ 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate }; 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate #define SHELP_HELP "help" 1150Sstevel@tonic-gate #define SHELP_BOOT "boot [-s]" 1160Sstevel@tonic-gate #define SHELP_HALT "halt" 1170Sstevel@tonic-gate #define SHELP_READY "ready" 1180Sstevel@tonic-gate #define SHELP_REBOOT "reboot" 1190Sstevel@tonic-gate #define SHELP_LIST "list [-cipv]" 1200Sstevel@tonic-gate #define SHELP_VERIFY "verify" 1211867Sgjelinek #define SHELP_INSTALL "install [-x nodataset]" 1220Sstevel@tonic-gate #define SHELP_UNINSTALL "uninstall [-F]" 1231867Sgjelinek #define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] zonename" 1241300Sgjelinek #define SHELP_MOVE "move zonepath" 1252078Sgjelinek #define SHELP_DETACH "detach [-n]" 1262078Sgjelinek #define SHELP_ATTACH "attach [-F] [-n <path>]" 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate static int help_func(int argc, char *argv[]); 1290Sstevel@tonic-gate static int ready_func(int argc, char *argv[]); 1300Sstevel@tonic-gate static int boot_func(int argc, char *argv[]); 1310Sstevel@tonic-gate static int halt_func(int argc, char *argv[]); 1320Sstevel@tonic-gate static int reboot_func(int argc, char *argv[]); 1330Sstevel@tonic-gate static int list_func(int argc, char *argv[]); 1340Sstevel@tonic-gate static int verify_func(int argc, char *argv[]); 1350Sstevel@tonic-gate static int install_func(int argc, char *argv[]); 1360Sstevel@tonic-gate static int uninstall_func(int argc, char *argv[]); 137766Scarlsonj static int mount_func(int argc, char *argv[]); 138766Scarlsonj static int unmount_func(int argc, char *argv[]); 1391300Sgjelinek static int clone_func(int argc, char *argv[]); 1401300Sgjelinek static int move_func(int argc, char *argv[]); 1411507Sgjelinek static int detach_func(int argc, char *argv[]); 1421507Sgjelinek static int attach_func(int argc, char *argv[]); 1430Sstevel@tonic-gate static int sanity_check(char *zone, int cmd_num, boolean_t running, 1440Sstevel@tonic-gate boolean_t unsafe_when_running); 1450Sstevel@tonic-gate static int cmd_match(char *cmd); 1460Sstevel@tonic-gate static int verify_details(int); 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate static struct cmd cmdtab[] = { 1490Sstevel@tonic-gate { CMD_HELP, "help", SHELP_HELP, help_func }, 1500Sstevel@tonic-gate { CMD_BOOT, "boot", SHELP_BOOT, boot_func }, 1510Sstevel@tonic-gate { CMD_HALT, "halt", SHELP_HALT, halt_func }, 1520Sstevel@tonic-gate { CMD_READY, "ready", SHELP_READY, ready_func }, 1530Sstevel@tonic-gate { CMD_REBOOT, "reboot", SHELP_REBOOT, reboot_func }, 1540Sstevel@tonic-gate { CMD_LIST, "list", SHELP_LIST, list_func }, 1550Sstevel@tonic-gate { CMD_VERIFY, "verify", SHELP_VERIFY, verify_func }, 1560Sstevel@tonic-gate { CMD_INSTALL, "install", SHELP_INSTALL, install_func }, 1570Sstevel@tonic-gate { CMD_UNINSTALL, "uninstall", SHELP_UNINSTALL, 158766Scarlsonj uninstall_func }, 1591300Sgjelinek /* mount and unmount are private commands for admin/install */ 160766Scarlsonj { CMD_MOUNT, "mount", NULL, mount_func }, 1611300Sgjelinek { CMD_UNMOUNT, "unmount", NULL, unmount_func }, 1621300Sgjelinek { CMD_CLONE, "clone", SHELP_CLONE, clone_func }, 1631507Sgjelinek { CMD_MOVE, "move", SHELP_MOVE, move_func }, 1641507Sgjelinek { CMD_DETACH, "detach", SHELP_DETACH, detach_func }, 1651507Sgjelinek { CMD_ATTACH, "attach", SHELP_ATTACH, attach_func } 1660Sstevel@tonic-gate }; 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate /* global variables */ 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate /* set early in main(), never modified thereafter, used all over the place */ 1710Sstevel@tonic-gate static char *execname; 1720Sstevel@tonic-gate static char *locale; 1731867Sgjelinek char *target_zone; 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate /* used in do_subproc() and signal handler */ 1760Sstevel@tonic-gate static volatile boolean_t child_killed; 1770Sstevel@tonic-gate 1781867Sgjelinek char * 1790Sstevel@tonic-gate cmd_to_str(int cmd_num) 1800Sstevel@tonic-gate { 1810Sstevel@tonic-gate assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 1820Sstevel@tonic-gate return (cmdtab[cmd_num].cmd_name); 1830Sstevel@tonic-gate } 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate /* This is a separate function because of gettext() wrapping. */ 1860Sstevel@tonic-gate static char * 1870Sstevel@tonic-gate long_help(int cmd_num) 1880Sstevel@tonic-gate { 189222Scomay assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 1900Sstevel@tonic-gate switch (cmd_num) { 1911634Sgjelinek case CMD_HELP: 1921634Sgjelinek return (gettext("Print usage message.")); 1931634Sgjelinek case CMD_BOOT: 1941634Sgjelinek return (gettext("Activates (boots) specified zone. " 1951634Sgjelinek "The -s flag can be used\n\tto boot the zone in " 1961634Sgjelinek "the single-user state.")); 1971634Sgjelinek case CMD_HALT: 1981634Sgjelinek return (gettext("Halts specified zone, bypassing shutdown " 1991634Sgjelinek "scripts and removing runtime\n\tresources of the zone.")); 2001634Sgjelinek case CMD_READY: 2011634Sgjelinek return (gettext("Prepares a zone for running applications but " 2021634Sgjelinek "does not start any user\n\tprocesses in the zone.")); 2031634Sgjelinek case CMD_REBOOT: 2041634Sgjelinek return (gettext("Restarts the zone (equivalent to a halt / " 2051634Sgjelinek "boot sequence).\n\tFails if the zone is not active.")); 2061634Sgjelinek case CMD_LIST: 2071634Sgjelinek return (gettext("Lists the current zones, or a " 2081634Sgjelinek "specific zone if indicated. By default,\n\tall " 2091634Sgjelinek "running zones are listed, though this can be " 2101634Sgjelinek "expanded to all\n\tinstalled zones with the -i " 2111634Sgjelinek "option or all configured zones with the\n\t-c " 2121634Sgjelinek "option. When used with the general -z <zone> " 2131634Sgjelinek "option, lists only the\n\tspecified zone, but " 2141634Sgjelinek "lists it regardless of its state, and the -i " 2151634Sgjelinek "and -c\n\toptions are disallowed. The -v option " 2161634Sgjelinek "can be used to display verbose\n\tinformation: " 2171634Sgjelinek "zone name, id, current state, root directory and " 2181634Sgjelinek "options.\n\tThe -p option can be used to request " 2191634Sgjelinek "machine-parsable output. The -v\n\tand -p " 2201634Sgjelinek "options are mutually exclusive. If neither -v " 2211634Sgjelinek "nor -p is used,\n\tjust the zone name is listed.")); 2221634Sgjelinek case CMD_VERIFY: 2231634Sgjelinek return (gettext("Check to make sure the configuration " 2241634Sgjelinek "can safely be instantiated\n\ton the machine: " 2251634Sgjelinek "physical network interfaces exist, etc.")); 2261634Sgjelinek case CMD_INSTALL: 2271867Sgjelinek return (gettext("Install the configuration on to the system. " 2281867Sgjelinek "The -x nodataset option\n\tcan be used to prevent the " 2291867Sgjelinek "creation of a new ZFS file system for the\n\tzone " 2301867Sgjelinek "(assuming the zonepath is within a ZFS file system).")); 2311634Sgjelinek case CMD_UNINSTALL: 2321634Sgjelinek return (gettext("Uninstall the configuration from the system. " 2331634Sgjelinek "The -F flag can be used\n\tto force the action.")); 2341634Sgjelinek case CMD_CLONE: 2351867Sgjelinek return (gettext("Clone the installation of another zone. " 2361867Sgjelinek "The -m option can be used to\n\tspecify 'copy' which " 2371867Sgjelinek "forces a copy of the source zone. The -s option\n\t" 2381867Sgjelinek "can be used to specify the name of a ZFS snapshot " 2391867Sgjelinek "that was taken from\n\ta previous clone command. The " 2401867Sgjelinek "snapshot will be used as the source\n\tinstead of " 2411867Sgjelinek "creating a new ZFS snapshot.")); 2421634Sgjelinek case CMD_MOVE: 2431634Sgjelinek return (gettext("Move the zone to a new zonepath.")); 2441634Sgjelinek case CMD_DETACH: 2451634Sgjelinek return (gettext("Detach the zone from the system. The zone " 2461634Sgjelinek "state is changed to\n\t'configured' (but the files under " 2471634Sgjelinek "the zonepath are untouched).\n\tThe zone can subsequently " 2481634Sgjelinek "be attached, or can be moved to another\n\tsystem and " 2492078Sgjelinek "attached there. The -n option can be used to specify\n\t" 2502078Sgjelinek "'no-execute' mode. When -n is used, the information " 2512078Sgjelinek "needed to attach\n\tthe zone is sent to standard output " 2522078Sgjelinek "but the zone is not actually\n\tdetached.")); 2531634Sgjelinek case CMD_ATTACH: 2541634Sgjelinek return (gettext("Attach the zone to the system. The zone " 2551634Sgjelinek "state must be 'configured'\n\tprior to attach; upon " 2561634Sgjelinek "successful completion, the zone state will be\n\t" 2571634Sgjelinek "'installed'. The system software on the current " 2581634Sgjelinek "system must be\n\tcompatible with the software on the " 2591634Sgjelinek "zone's original system.\n\tSpecify -F to force the attach " 2602078Sgjelinek "and skip software compatibility tests.\n\tThe -n option " 2612078Sgjelinek "can be used to specify 'no-execute' mode. When -n is\n\t" 2622078Sgjelinek "used, the information needed to attach the zone is read " 2632078Sgjelinek "from the\n\tspecified path and the configuration is only " 2642078Sgjelinek "validated. The path can\n\tbe '-' to specify standard " 2652078Sgjelinek "input.")); 2661634Sgjelinek default: 2671634Sgjelinek return (""); 2680Sstevel@tonic-gate } 2690Sstevel@tonic-gate /* NOTREACHED */ 270222Scomay return (NULL); 2710Sstevel@tonic-gate } 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate /* 2740Sstevel@tonic-gate * Called with explicit B_TRUE when help is explicitly requested, B_FALSE for 2750Sstevel@tonic-gate * unexpected errors. 2760Sstevel@tonic-gate */ 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate static int 2790Sstevel@tonic-gate usage(boolean_t explicit) 2800Sstevel@tonic-gate { 2810Sstevel@tonic-gate int i; 2820Sstevel@tonic-gate FILE *fd = explicit ? stdout : stderr; 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate (void) fprintf(fd, "%s:\t%s help\n", gettext("usage"), execname); 2850Sstevel@tonic-gate (void) fprintf(fd, "\t%s [-z <zone>] list\n", execname); 2860Sstevel@tonic-gate (void) fprintf(fd, "\t%s -z <zone> <%s>\n", execname, 2870Sstevel@tonic-gate gettext("subcommand")); 2880Sstevel@tonic-gate (void) fprintf(fd, "\n%s:\n\n", gettext("Subcommands")); 2890Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 290766Scarlsonj if (cmdtab[i].short_usage == NULL) 291766Scarlsonj continue; 2920Sstevel@tonic-gate (void) fprintf(fd, "%s\n", cmdtab[i].short_usage); 2930Sstevel@tonic-gate if (explicit) 2940Sstevel@tonic-gate (void) fprintf(fd, "\t%s\n\n", long_help(i)); 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate if (!explicit) 2970Sstevel@tonic-gate (void) fputs("\n", fd); 2980Sstevel@tonic-gate return (Z_USAGE); 2990Sstevel@tonic-gate } 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate static void 3020Sstevel@tonic-gate sub_usage(char *short_usage, int cmd_num) 3030Sstevel@tonic-gate { 3040Sstevel@tonic-gate (void) fprintf(stderr, "%s:\t%s\n", gettext("usage"), short_usage); 3050Sstevel@tonic-gate (void) fprintf(stderr, "\t%s\n", long_help(cmd_num)); 3060Sstevel@tonic-gate } 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate /* 3090Sstevel@tonic-gate * zperror() is like perror(3c) except that this also prints the executable 3100Sstevel@tonic-gate * name at the start of the message, and takes a boolean indicating whether 3110Sstevel@tonic-gate * to call libc'c strerror() or that from libzonecfg. 3120Sstevel@tonic-gate */ 3130Sstevel@tonic-gate 3141867Sgjelinek void 3150Sstevel@tonic-gate zperror(const char *str, boolean_t zonecfg_error) 3160Sstevel@tonic-gate { 3170Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", execname, str, 3180Sstevel@tonic-gate zonecfg_error ? zonecfg_strerror(errno) : strerror(errno)); 3190Sstevel@tonic-gate } 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate /* 3220Sstevel@tonic-gate * zperror2() is very similar to zperror() above, except it also prints a 3230Sstevel@tonic-gate * supplied zone name after the executable. 3240Sstevel@tonic-gate * 3250Sstevel@tonic-gate * All current consumers of this function want libzonecfg's strerror() rather 3260Sstevel@tonic-gate * than libc's; if this ever changes, this function can be made more generic 3270Sstevel@tonic-gate * like zperror() above. 3280Sstevel@tonic-gate */ 3290Sstevel@tonic-gate 3301867Sgjelinek void 3310Sstevel@tonic-gate zperror2(const char *zone, const char *str) 3320Sstevel@tonic-gate { 3330Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s: %s\n", execname, zone, str, 3340Sstevel@tonic-gate zonecfg_strerror(errno)); 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate /* PRINTFLIKE1 */ 3381867Sgjelinek void 3390Sstevel@tonic-gate zerror(const char *fmt, ...) 3400Sstevel@tonic-gate { 3410Sstevel@tonic-gate va_list alist; 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate va_start(alist, fmt); 3440Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", execname); 3450Sstevel@tonic-gate if (target_zone != NULL) 3460Sstevel@tonic-gate (void) fprintf(stderr, "zone '%s': ", target_zone); 3470Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 3480Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3490Sstevel@tonic-gate va_end(alist); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate static void * 3530Sstevel@tonic-gate safe_calloc(size_t nelem, size_t elsize) 3540Sstevel@tonic-gate { 3550Sstevel@tonic-gate void *r = calloc(nelem, elsize); 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate if (r == NULL) { 3580Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), 3590Sstevel@tonic-gate (ulong_t)nelem * elsize, strerror(errno)); 3600Sstevel@tonic-gate exit(Z_ERR); 3610Sstevel@tonic-gate } 3620Sstevel@tonic-gate return (r); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate static void 3660Sstevel@tonic-gate zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) 3670Sstevel@tonic-gate { 3680Sstevel@tonic-gate static boolean_t firsttime = B_TRUE; 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate assert(!(verbose && parsable)); 3710Sstevel@tonic-gate if (firsttime && verbose) { 3720Sstevel@tonic-gate firsttime = B_FALSE; 3730Sstevel@tonic-gate (void) printf("%*s %-16s %-14s %-30s\n", ZONEID_WIDTH, "ID", 3740Sstevel@tonic-gate "NAME", "STATUS", "PATH"); 3750Sstevel@tonic-gate } 3760Sstevel@tonic-gate if (!verbose) { 3770Sstevel@tonic-gate if (!parsable) { 3780Sstevel@tonic-gate (void) printf("%s\n", zent->zname); 3790Sstevel@tonic-gate return; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 3820Sstevel@tonic-gate (void) printf("-"); 3830Sstevel@tonic-gate else 3840Sstevel@tonic-gate (void) printf("%lu", zent->zid); 3850Sstevel@tonic-gate (void) printf(":%s:%s:%s\n", zent->zname, zent->zstate_str, 3860Sstevel@tonic-gate zent->zroot); 3870Sstevel@tonic-gate return; 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate if (zent->zstate_str != NULL) { 3900Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 3910Sstevel@tonic-gate (void) printf("%*s", ZONEID_WIDTH, "-"); 3920Sstevel@tonic-gate else 3930Sstevel@tonic-gate (void) printf("%*lu", ZONEID_WIDTH, zent->zid); 3940Sstevel@tonic-gate (void) printf(" %-16s %-14s %-30s\n", zent->zname, 3950Sstevel@tonic-gate zent->zstate_str, zent->zroot); 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate } 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate static int 400766Scarlsonj lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) 4010Sstevel@tonic-gate { 4021676Sjpk char root[MAXPATHLEN], *cp; 4030Sstevel@tonic-gate int err; 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate (void) strlcpy(zent->zname, zone_name, sizeof (zent->zname)); 4060Sstevel@tonic-gate (void) strlcpy(zent->zroot, "???", sizeof (zent->zroot)); 4070Sstevel@tonic-gate zent->zstate_str = "???"; 4080Sstevel@tonic-gate 409766Scarlsonj zent->zid = zid; 4100Sstevel@tonic-gate 4111676Sjpk /* 4121676Sjpk * For labeled zones which query the zone path of lower-level 4131676Sjpk * zones, the path needs to be adjusted to drop the final 4141676Sjpk * "/root" component. This adjusted path is then useful 4151676Sjpk * for reading down any exported directories from the 4161676Sjpk * lower-level zone. 4171676Sjpk */ 4181676Sjpk if (is_system_labeled() && zent->zid != ZONE_ID_UNDEFINED) { 4191676Sjpk if (zone_getattr(zent->zid, ZONE_ATTR_ROOT, zent->zroot, 4201676Sjpk sizeof (zent->zroot)) == -1) { 4211676Sjpk zperror2(zent->zname, 4221676Sjpk gettext("could not get zone path.")); 4231676Sjpk return (Z_ERR); 4241676Sjpk } 4251676Sjpk cp = zent->zroot + strlen(zent->zroot) - 5; 4261676Sjpk if (cp > zent->zroot && strcmp(cp, "/root") == 0) 4271676Sjpk *cp = 0; 4281676Sjpk } else { 4291676Sjpk if ((err = zone_get_zonepath(zent->zname, root, 4301676Sjpk sizeof (root))) != Z_OK) { 4311676Sjpk errno = err; 4321676Sjpk zperror2(zent->zname, 4331676Sjpk gettext("could not get zone path.")); 4341676Sjpk return (Z_ERR); 4351676Sjpk } 4361676Sjpk (void) strlcpy(zent->zroot, root, sizeof (zent->zroot)); 4371676Sjpk } 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, &zent->zstate_num)) != Z_OK) { 4400Sstevel@tonic-gate errno = err; 4410Sstevel@tonic-gate zperror2(zent->zname, gettext("could not get state")); 4420Sstevel@tonic-gate return (Z_ERR); 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate zent->zstate_str = zone_state_str(zent->zstate_num); 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate return (Z_OK); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate /* 4500Sstevel@tonic-gate * fetch_zents() calls zone_list(2) to find out how many zones are running 4510Sstevel@tonic-gate * (which is stored in the global nzents), then calls zone_list(2) again 4520Sstevel@tonic-gate * to fetch the list of running zones (stored in the global zents). This 4530Sstevel@tonic-gate * function may be called multiple times, so if zents is already set, we 4540Sstevel@tonic-gate * return immediately to save work. 4550Sstevel@tonic-gate */ 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate static int 458766Scarlsonj fetch_zents(void) 4590Sstevel@tonic-gate { 4600Sstevel@tonic-gate zoneid_t *zids = NULL; 4610Sstevel@tonic-gate uint_t nzents_saved; 462766Scarlsonj int i, retv; 463766Scarlsonj FILE *fp; 464766Scarlsonj boolean_t inaltroot; 465766Scarlsonj zone_entry_t *zentp; 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate if (nzents > 0) 4680Sstevel@tonic-gate return (Z_OK); 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate if (zone_list(NULL, &nzents) != 0) { 4710Sstevel@tonic-gate zperror(gettext("failed to get zoneid list"), B_FALSE); 4720Sstevel@tonic-gate return (Z_ERR); 4730Sstevel@tonic-gate } 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate again: 4760Sstevel@tonic-gate if (nzents == 0) 4770Sstevel@tonic-gate return (Z_OK); 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate zids = safe_calloc(nzents, sizeof (zoneid_t)); 4800Sstevel@tonic-gate nzents_saved = nzents; 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate if (zone_list(zids, &nzents) != 0) { 4830Sstevel@tonic-gate zperror(gettext("failed to get zone list"), B_FALSE); 4840Sstevel@tonic-gate free(zids); 4850Sstevel@tonic-gate return (Z_ERR); 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate if (nzents != nzents_saved) { 4880Sstevel@tonic-gate /* list changed, try again */ 4890Sstevel@tonic-gate free(zids); 4900Sstevel@tonic-gate goto again; 4910Sstevel@tonic-gate } 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate zents = safe_calloc(nzents, sizeof (zone_entry_t)); 4940Sstevel@tonic-gate 495766Scarlsonj inaltroot = zonecfg_in_alt_root(); 496766Scarlsonj if (inaltroot) 497766Scarlsonj fp = zonecfg_open_scratch("", B_FALSE); 498766Scarlsonj else 499766Scarlsonj fp = NULL; 500766Scarlsonj zentp = zents; 501766Scarlsonj retv = Z_OK; 5020Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 5030Sstevel@tonic-gate char name[ZONENAME_MAX]; 504766Scarlsonj char altname[ZONENAME_MAX]; 5050Sstevel@tonic-gate 506766Scarlsonj if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { 5070Sstevel@tonic-gate zperror(gettext("failed to get zone name"), B_FALSE); 508766Scarlsonj retv = Z_ERR; 509766Scarlsonj continue; 510766Scarlsonj } 511766Scarlsonj if (zonecfg_is_scratch(name)) { 512766Scarlsonj /* Ignore scratch zones by default */ 513766Scarlsonj if (!inaltroot) 514766Scarlsonj continue; 515766Scarlsonj if (fp == NULL || 516766Scarlsonj zonecfg_reverse_scratch(fp, name, altname, 517766Scarlsonj sizeof (altname), NULL, 0) == -1) { 518924Sgjelinek zerror(gettext("could not resolve scratch " 519766Scarlsonj "zone %s"), name); 520766Scarlsonj retv = Z_ERR; 521766Scarlsonj continue; 522766Scarlsonj } 523766Scarlsonj (void) strcpy(name, altname); 524766Scarlsonj } else { 525766Scarlsonj /* Ignore non-scratch when in an alternate root */ 526766Scarlsonj if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0) 527766Scarlsonj continue; 528766Scarlsonj } 529766Scarlsonj if (lookup_zone_info(name, zids[i], zentp) != Z_OK) { 530766Scarlsonj zerror(gettext("failed to get zone data")); 531766Scarlsonj retv = Z_ERR; 532766Scarlsonj continue; 533766Scarlsonj } 534766Scarlsonj zentp++; 5350Sstevel@tonic-gate } 536766Scarlsonj nzents = zentp - zents; 537766Scarlsonj if (fp != NULL) 538766Scarlsonj zonecfg_close_scratch(fp); 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate free(zids); 541766Scarlsonj return (retv); 5420Sstevel@tonic-gate } 5430Sstevel@tonic-gate 544766Scarlsonj static int 5450Sstevel@tonic-gate zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable) 5460Sstevel@tonic-gate { 5470Sstevel@tonic-gate int i; 5480Sstevel@tonic-gate zone_entry_t zent; 5490Sstevel@tonic-gate FILE *cookie; 5500Sstevel@tonic-gate char *name; 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate /* 5530Sstevel@tonic-gate * First get the list of running zones from the kernel and print them. 5540Sstevel@tonic-gate * If that is all we need, then return. 5550Sstevel@tonic-gate */ 556766Scarlsonj if ((i = fetch_zents()) != Z_OK) { 5570Sstevel@tonic-gate /* 5580Sstevel@tonic-gate * No need for error messages; fetch_zents() has already taken 5590Sstevel@tonic-gate * care of this. 5600Sstevel@tonic-gate */ 561766Scarlsonj return (i); 5620Sstevel@tonic-gate } 563766Scarlsonj for (i = 0; i < nzents; i++) 5640Sstevel@tonic-gate zone_print(&zents[i], verbose, parsable); 5650Sstevel@tonic-gate if (min_state >= ZONE_STATE_RUNNING) 566766Scarlsonj return (Z_OK); 5670Sstevel@tonic-gate /* 5680Sstevel@tonic-gate * Next, get the full list of zones from the configuration, skipping 5690Sstevel@tonic-gate * any we have already printed. 5700Sstevel@tonic-gate */ 5710Sstevel@tonic-gate cookie = setzoneent(); 5720Sstevel@tonic-gate while ((name = getzoneent(cookie)) != NULL) { 5730Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 5740Sstevel@tonic-gate if (strcmp(zents[i].zname, name) == 0) 5750Sstevel@tonic-gate break; 5760Sstevel@tonic-gate } 5770Sstevel@tonic-gate if (i < nzents) { 5780Sstevel@tonic-gate free(name); 5790Sstevel@tonic-gate continue; 5800Sstevel@tonic-gate } 581766Scarlsonj if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { 5820Sstevel@tonic-gate free(name); 5830Sstevel@tonic-gate continue; 5840Sstevel@tonic-gate } 5850Sstevel@tonic-gate free(name); 5860Sstevel@tonic-gate if (zent.zstate_num >= min_state) 5870Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 5880Sstevel@tonic-gate } 5890Sstevel@tonic-gate endzoneent(cookie); 590766Scarlsonj return (Z_OK); 5910Sstevel@tonic-gate } 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate static zone_entry_t * 5940Sstevel@tonic-gate lookup_running_zone(char *str) 5950Sstevel@tonic-gate { 5960Sstevel@tonic-gate zoneid_t zoneid; 5970Sstevel@tonic-gate char *cp; 5980Sstevel@tonic-gate int i; 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate if (fetch_zents() != Z_OK) 6010Sstevel@tonic-gate return (NULL); 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6040Sstevel@tonic-gate if (strcmp(str, zents[i].zname) == 0) 6050Sstevel@tonic-gate return (&zents[i]); 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate errno = 0; 6080Sstevel@tonic-gate zoneid = strtol(str, &cp, 0); 6090Sstevel@tonic-gate if (zoneid < MIN_ZONEID || zoneid > MAX_ZONEID || 6100Sstevel@tonic-gate errno != 0 || *cp != '\0') 6110Sstevel@tonic-gate return (NULL); 6120Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6130Sstevel@tonic-gate if (zoneid == zents[i].zid) 6140Sstevel@tonic-gate return (&zents[i]); 6150Sstevel@tonic-gate } 6160Sstevel@tonic-gate return (NULL); 6170Sstevel@tonic-gate } 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate /* 6200Sstevel@tonic-gate * Check a bit in a mode_t: if on is B_TRUE, that bit should be on; if 6210Sstevel@tonic-gate * B_FALSE, it should be off. Return B_TRUE if the mode is bad (incorrect). 6220Sstevel@tonic-gate */ 6230Sstevel@tonic-gate static boolean_t 6240Sstevel@tonic-gate bad_mode_bit(mode_t mode, mode_t bit, boolean_t on, char *file) 6250Sstevel@tonic-gate { 6260Sstevel@tonic-gate char *str; 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate assert(bit == S_IRUSR || bit == S_IWUSR || bit == S_IXUSR || 6290Sstevel@tonic-gate bit == S_IRGRP || bit == S_IWGRP || bit == S_IXGRP || 6300Sstevel@tonic-gate bit == S_IROTH || bit == S_IWOTH || bit == S_IXOTH); 6310Sstevel@tonic-gate /* 6320Sstevel@tonic-gate * TRANSLATION_NOTE 6330Sstevel@tonic-gate * The strings below will be used as part of a larger message, 6340Sstevel@tonic-gate * either: 6350Sstevel@tonic-gate * (file name) must be (owner|group|world) (read|writ|execut)able 6360Sstevel@tonic-gate * or 6370Sstevel@tonic-gate * (file name) must not be (owner|group|world) (read|writ|execut)able 6380Sstevel@tonic-gate */ 6390Sstevel@tonic-gate switch (bit) { 6400Sstevel@tonic-gate case S_IRUSR: 6410Sstevel@tonic-gate str = gettext("owner readable"); 6420Sstevel@tonic-gate break; 6430Sstevel@tonic-gate case S_IWUSR: 6440Sstevel@tonic-gate str = gettext("owner writable"); 6450Sstevel@tonic-gate break; 6460Sstevel@tonic-gate case S_IXUSR: 6470Sstevel@tonic-gate str = gettext("owner executable"); 6480Sstevel@tonic-gate break; 6490Sstevel@tonic-gate case S_IRGRP: 6500Sstevel@tonic-gate str = gettext("group readable"); 6510Sstevel@tonic-gate break; 6520Sstevel@tonic-gate case S_IWGRP: 6530Sstevel@tonic-gate str = gettext("group writable"); 6540Sstevel@tonic-gate break; 6550Sstevel@tonic-gate case S_IXGRP: 6560Sstevel@tonic-gate str = gettext("group executable"); 6570Sstevel@tonic-gate break; 6580Sstevel@tonic-gate case S_IROTH: 6590Sstevel@tonic-gate str = gettext("world readable"); 6600Sstevel@tonic-gate break; 6610Sstevel@tonic-gate case S_IWOTH: 6620Sstevel@tonic-gate str = gettext("world writable"); 6630Sstevel@tonic-gate break; 6640Sstevel@tonic-gate case S_IXOTH: 6650Sstevel@tonic-gate str = gettext("world executable"); 6660Sstevel@tonic-gate break; 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate if ((mode & bit) == (on ? 0 : bit)) { 6690Sstevel@tonic-gate /* 6700Sstevel@tonic-gate * TRANSLATION_NOTE 6710Sstevel@tonic-gate * The first parameter below is a file name; the second 6720Sstevel@tonic-gate * is one of the "(owner|group|world) (read|writ|execut)able" 6730Sstevel@tonic-gate * strings from above. 6740Sstevel@tonic-gate */ 6750Sstevel@tonic-gate /* 6760Sstevel@tonic-gate * The code below could be simplified but not in a way 6770Sstevel@tonic-gate * that would easily translate to non-English locales. 6780Sstevel@tonic-gate */ 6790Sstevel@tonic-gate if (on) { 6800Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must be %s.\n"), 6810Sstevel@tonic-gate file, str); 6820Sstevel@tonic-gate } else { 6830Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must not be %s.\n"), 6840Sstevel@tonic-gate file, str); 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate return (B_TRUE); 6870Sstevel@tonic-gate } 6880Sstevel@tonic-gate return (B_FALSE); 6890Sstevel@tonic-gate } 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate /* 6920Sstevel@tonic-gate * We want to make sure that no zone has its zone path as a child node 6930Sstevel@tonic-gate * (in the directory sense) of any other. We do that by comparing this 6940Sstevel@tonic-gate * zone's path to the path of all other (non-global) zones. The comparison 6950Sstevel@tonic-gate * in each case is simple: add '/' to the end of the path, then do a 6960Sstevel@tonic-gate * strncmp() of the two paths, using the length of the shorter one. 6970Sstevel@tonic-gate */ 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate static int 7000Sstevel@tonic-gate crosscheck_zonepaths(char *path) 7010Sstevel@tonic-gate { 7020Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 7030Sstevel@tonic-gate char path_copy[MAXPATHLEN]; /* copy of original path */ 7040Sstevel@tonic-gate char rpath_copy[MAXPATHLEN]; /* copy of original rpath */ 7050Sstevel@tonic-gate struct zoneent *ze; 7060Sstevel@tonic-gate int res, err; 7070Sstevel@tonic-gate FILE *cookie; 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate cookie = setzoneent(); 7100Sstevel@tonic-gate while ((ze = getzoneent_private(cookie)) != NULL) { 7110Sstevel@tonic-gate /* Skip zones which are not installed. */ 7120Sstevel@tonic-gate if (ze->zone_state < ZONE_STATE_INSTALLED) { 7130Sstevel@tonic-gate free(ze); 7140Sstevel@tonic-gate continue; 7150Sstevel@tonic-gate } 7160Sstevel@tonic-gate /* Skip the global zone and the current target zone. */ 7170Sstevel@tonic-gate if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0 || 7180Sstevel@tonic-gate strcmp(ze->zone_name, target_zone) == 0) { 7190Sstevel@tonic-gate free(ze); 7200Sstevel@tonic-gate continue; 7210Sstevel@tonic-gate } 7220Sstevel@tonic-gate if (strlen(ze->zone_path) == 0) { 7230Sstevel@tonic-gate /* old index file without path, fall back */ 7240Sstevel@tonic-gate if ((err = zone_get_zonepath(ze->zone_name, 7250Sstevel@tonic-gate ze->zone_path, sizeof (ze->zone_path))) != Z_OK) { 7260Sstevel@tonic-gate errno = err; 7270Sstevel@tonic-gate zperror2(ze->zone_name, 7280Sstevel@tonic-gate gettext("could not get zone path")); 7290Sstevel@tonic-gate free(ze); 7300Sstevel@tonic-gate continue; 7310Sstevel@tonic-gate } 7320Sstevel@tonic-gate } 733766Scarlsonj (void) snprintf(path_copy, sizeof (path_copy), "%s%s", 734766Scarlsonj zonecfg_get_root(), ze->zone_path); 735766Scarlsonj res = resolvepath(path_copy, rpath, sizeof (rpath)); 7360Sstevel@tonic-gate if (res == -1) { 7370Sstevel@tonic-gate if (errno != ENOENT) { 738766Scarlsonj zperror(path_copy, B_FALSE); 7390Sstevel@tonic-gate free(ze); 7400Sstevel@tonic-gate return (Z_ERR); 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate (void) printf(gettext("WARNING: zone %s is installed, " 7430Sstevel@tonic-gate "but its %s %s does not exist.\n"), ze->zone_name, 744766Scarlsonj "zonepath", path_copy); 7450Sstevel@tonic-gate free(ze); 7460Sstevel@tonic-gate continue; 7470Sstevel@tonic-gate } 7480Sstevel@tonic-gate rpath[res] = '\0'; 7490Sstevel@tonic-gate (void) snprintf(path_copy, sizeof (path_copy), "%s/", path); 7500Sstevel@tonic-gate (void) snprintf(rpath_copy, sizeof (rpath_copy), "%s/", rpath); 7510Sstevel@tonic-gate if (strncmp(path_copy, rpath_copy, 7520Sstevel@tonic-gate min(strlen(path_copy), strlen(rpath_copy))) == 0) { 753924Sgjelinek /* 754924Sgjelinek * TRANSLATION_NOTE 755924Sgjelinek * zonepath is a literal that should not be translated. 756924Sgjelinek */ 7570Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s zonepath (%s) and " 7580Sstevel@tonic-gate "%s zonepath (%s) overlap.\n"), 7590Sstevel@tonic-gate target_zone, path, ze->zone_name, rpath); 7600Sstevel@tonic-gate free(ze); 7610Sstevel@tonic-gate return (Z_ERR); 7620Sstevel@tonic-gate } 7630Sstevel@tonic-gate free(ze); 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate endzoneent(cookie); 7660Sstevel@tonic-gate return (Z_OK); 7670Sstevel@tonic-gate } 7680Sstevel@tonic-gate 7690Sstevel@tonic-gate static int 7700Sstevel@tonic-gate validate_zonepath(char *path, int cmd_num) 7710Sstevel@tonic-gate { 7720Sstevel@tonic-gate int res; /* result of last library/system call */ 7730Sstevel@tonic-gate boolean_t err = B_FALSE; /* have we run into an error? */ 7740Sstevel@tonic-gate struct stat stbuf; 7750Sstevel@tonic-gate struct statvfs vfsbuf; 7760Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 7770Sstevel@tonic-gate char ppath[MAXPATHLEN]; /* parent path */ 7780Sstevel@tonic-gate char rppath[MAXPATHLEN]; /* resolved parent path */ 7790Sstevel@tonic-gate char rootpath[MAXPATHLEN]; /* root path */ 7800Sstevel@tonic-gate zone_state_t state; 7810Sstevel@tonic-gate 7820Sstevel@tonic-gate if (path[0] != '/') { 7830Sstevel@tonic-gate (void) fprintf(stderr, 7840Sstevel@tonic-gate gettext("%s is not an absolute path.\n"), path); 7850Sstevel@tonic-gate return (Z_ERR); 7860Sstevel@tonic-gate } 7870Sstevel@tonic-gate if ((res = resolvepath(path, rpath, sizeof (rpath))) == -1) { 7880Sstevel@tonic-gate if ((errno != ENOENT) || 7891300Sgjelinek (cmd_num != CMD_VERIFY && cmd_num != CMD_INSTALL && 7901300Sgjelinek cmd_num != CMD_CLONE && cmd_num != CMD_MOVE)) { 7910Sstevel@tonic-gate zperror(path, B_FALSE); 7920Sstevel@tonic-gate return (Z_ERR); 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate if (cmd_num == CMD_VERIFY) { 795924Sgjelinek /* 796924Sgjelinek * TRANSLATION_NOTE 797924Sgjelinek * zoneadm is a literal that should not be translated. 798924Sgjelinek */ 7990Sstevel@tonic-gate (void) fprintf(stderr, gettext("WARNING: %s does not " 800924Sgjelinek "exist, so it could not be verified.\nWhen " 801924Sgjelinek "'zoneadm %s' is run, '%s' will try to create\n%s, " 802924Sgjelinek "and '%s' will be tried again,\nbut the '%s' may " 803924Sgjelinek "fail if:\nthe parent directory of %s is group- or " 804924Sgjelinek "other-writable\nor\n%s overlaps with any other " 8050Sstevel@tonic-gate "installed zones.\n"), path, 8060Sstevel@tonic-gate cmd_to_str(CMD_INSTALL), cmd_to_str(CMD_INSTALL), 8070Sstevel@tonic-gate path, cmd_to_str(CMD_VERIFY), 8080Sstevel@tonic-gate cmd_to_str(CMD_VERIFY), path, path); 8090Sstevel@tonic-gate return (Z_OK); 8100Sstevel@tonic-gate } 8110Sstevel@tonic-gate /* 8120Sstevel@tonic-gate * The zonepath is supposed to be mode 700 but its 8130Sstevel@tonic-gate * parent(s) 755. So use 755 on the mkdirp() then 8140Sstevel@tonic-gate * chmod() the zonepath itself to 700. 8150Sstevel@tonic-gate */ 8160Sstevel@tonic-gate if (mkdirp(path, DEFAULT_DIR_MODE) < 0) { 8170Sstevel@tonic-gate zperror(path, B_FALSE); 8180Sstevel@tonic-gate return (Z_ERR); 8190Sstevel@tonic-gate } 8200Sstevel@tonic-gate /* 8210Sstevel@tonic-gate * If the chmod() fails, report the error, but might 8220Sstevel@tonic-gate * as well continue the verify procedure. 8230Sstevel@tonic-gate */ 8240Sstevel@tonic-gate if (chmod(path, S_IRWXU) != 0) 8250Sstevel@tonic-gate zperror(path, B_FALSE); 8260Sstevel@tonic-gate /* 8270Sstevel@tonic-gate * Since the mkdir() succeeded, we should not have to 8280Sstevel@tonic-gate * worry about a subsequent ENOENT, thus this should 8290Sstevel@tonic-gate * only recurse once. 8300Sstevel@tonic-gate */ 8311300Sgjelinek return (validate_zonepath(path, cmd_num)); 8320Sstevel@tonic-gate } 8330Sstevel@tonic-gate rpath[res] = '\0'; 8340Sstevel@tonic-gate if (strcmp(path, rpath) != 0) { 8350Sstevel@tonic-gate errno = Z_RESOLVED_PATH; 8360Sstevel@tonic-gate zperror(path, B_TRUE); 8370Sstevel@tonic-gate return (Z_ERR); 8380Sstevel@tonic-gate } 8390Sstevel@tonic-gate if ((res = stat(rpath, &stbuf)) != 0) { 8400Sstevel@tonic-gate zperror(rpath, B_FALSE); 8410Sstevel@tonic-gate return (Z_ERR); 8420Sstevel@tonic-gate } 8430Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 8440Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 8450Sstevel@tonic-gate rpath); 8460Sstevel@tonic-gate return (Z_ERR); 8470Sstevel@tonic-gate } 8480Sstevel@tonic-gate if ((strcmp(stbuf.st_fstype, MNTTYPE_TMPFS) == 0) || 8490Sstevel@tonic-gate (strcmp(stbuf.st_fstype, MNTTYPE_XMEMFS) == 0)) { 8500Sstevel@tonic-gate (void) printf(gettext("WARNING: %s is on a temporary " 8511867Sgjelinek "file system.\n"), rpath); 8520Sstevel@tonic-gate } 8530Sstevel@tonic-gate if (crosscheck_zonepaths(rpath) != Z_OK) 8540Sstevel@tonic-gate return (Z_ERR); 8550Sstevel@tonic-gate /* 8560Sstevel@tonic-gate * Try to collect and report as many minor errors as possible 8570Sstevel@tonic-gate * before returning, so the user can learn everything that needs 8580Sstevel@tonic-gate * to be fixed up front. 8590Sstevel@tonic-gate */ 8600Sstevel@tonic-gate if (stbuf.st_uid != 0) { 8610Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 8620Sstevel@tonic-gate rpath); 8630Sstevel@tonic-gate err = B_TRUE; 8640Sstevel@tonic-gate } 8650Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rpath); 8660Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rpath); 8670Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rpath); 8680Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRGRP, B_FALSE, rpath); 8690Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rpath); 8700Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXGRP, B_FALSE, rpath); 8710Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IROTH, B_FALSE, rpath); 8720Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rpath); 8730Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXOTH, B_FALSE, rpath); 8740Sstevel@tonic-gate 8750Sstevel@tonic-gate (void) snprintf(ppath, sizeof (ppath), "%s/..", path); 8760Sstevel@tonic-gate if ((res = resolvepath(ppath, rppath, sizeof (rppath))) == -1) { 8770Sstevel@tonic-gate zperror(ppath, B_FALSE); 8780Sstevel@tonic-gate return (Z_ERR); 8790Sstevel@tonic-gate } 8800Sstevel@tonic-gate rppath[res] = '\0'; 8810Sstevel@tonic-gate if ((res = stat(rppath, &stbuf)) != 0) { 8820Sstevel@tonic-gate zperror(rppath, B_FALSE); 8830Sstevel@tonic-gate return (Z_ERR); 8840Sstevel@tonic-gate } 8850Sstevel@tonic-gate /* theoretically impossible */ 8860Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 8870Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 8880Sstevel@tonic-gate rppath); 8890Sstevel@tonic-gate return (Z_ERR); 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate if (stbuf.st_uid != 0) { 8920Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 8930Sstevel@tonic-gate rppath); 8940Sstevel@tonic-gate err = B_TRUE; 8950Sstevel@tonic-gate } 8960Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rppath); 8970Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rppath); 8980Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rppath); 8990Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rppath); 9000Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rppath); 9010Sstevel@tonic-gate if (strcmp(rpath, rppath) == 0) { 9020Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is its own parent.\n"), 9030Sstevel@tonic-gate rppath); 9040Sstevel@tonic-gate err = B_TRUE; 9050Sstevel@tonic-gate } 9060Sstevel@tonic-gate 9070Sstevel@tonic-gate if (statvfs(rpath, &vfsbuf) != 0) { 9080Sstevel@tonic-gate zperror(rpath, B_FALSE); 9090Sstevel@tonic-gate return (Z_ERR); 9100Sstevel@tonic-gate } 911823Sgjelinek if (strcmp(vfsbuf.f_basetype, MNTTYPE_NFS) == 0) { 912924Sgjelinek /* 913924Sgjelinek * TRANSLATION_NOTE 914924Sgjelinek * Zonepath and NFS are literals that should not be translated. 915924Sgjelinek */ 916924Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on an NFS " 9171867Sgjelinek "mounted file system.\n" 9181867Sgjelinek "\tA local file system must be used.\n"), rpath); 919823Sgjelinek return (Z_ERR); 920823Sgjelinek } 921823Sgjelinek if (vfsbuf.f_flag & ST_NOSUID) { 922924Sgjelinek /* 923924Sgjelinek * TRANSLATION_NOTE 924924Sgjelinek * Zonepath and nosuid are literals that should not be 925924Sgjelinek * translated. 926924Sgjelinek */ 927823Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on a nosuid " 9281867Sgjelinek "file system.\n"), rpath); 9290Sstevel@tonic-gate return (Z_ERR); 9300Sstevel@tonic-gate } 9310Sstevel@tonic-gate 9320Sstevel@tonic-gate if ((res = zone_get_state(target_zone, &state)) != Z_OK) { 9330Sstevel@tonic-gate errno = res; 9340Sstevel@tonic-gate zperror2(target_zone, gettext("could not get state")); 9350Sstevel@tonic-gate return (Z_ERR); 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate /* 9380Sstevel@tonic-gate * The existence of the root path is only bad in the configured state, 9390Sstevel@tonic-gate * as it is *supposed* to be there at the installed and later states. 9401507Sgjelinek * However, the root path is expected to be there if the zone is 9411507Sgjelinek * detached. 9420Sstevel@tonic-gate * State/command mismatches are caught earlier in verify_details(). 9430Sstevel@tonic-gate */ 9441507Sgjelinek if (state == ZONE_STATE_CONFIGURED && cmd_num != CMD_ATTACH) { 9450Sstevel@tonic-gate if (snprintf(rootpath, sizeof (rootpath), "%s/root", rpath) >= 9460Sstevel@tonic-gate sizeof (rootpath)) { 947924Sgjelinek /* 948924Sgjelinek * TRANSLATION_NOTE 949924Sgjelinek * Zonepath is a literal that should not be translated. 950924Sgjelinek */ 9510Sstevel@tonic-gate (void) fprintf(stderr, 9520Sstevel@tonic-gate gettext("Zonepath %s is too long.\n"), rpath); 9530Sstevel@tonic-gate return (Z_ERR); 9540Sstevel@tonic-gate } 9550Sstevel@tonic-gate if ((res = stat(rootpath, &stbuf)) == 0) { 9561507Sgjelinek if (zonecfg_detached(rpath)) 9571507Sgjelinek (void) fprintf(stderr, 9581507Sgjelinek gettext("Cannot %s detached " 9591507Sgjelinek "zone.\nUse attach or remove %s " 9601507Sgjelinek "directory.\n"), cmd_to_str(cmd_num), 9611507Sgjelinek rpath); 9621507Sgjelinek else 9631507Sgjelinek (void) fprintf(stderr, 9641507Sgjelinek gettext("Rootpath %s exists; " 9651507Sgjelinek "remove or move aside prior to %s.\n"), 9661507Sgjelinek rootpath, cmd_to_str(cmd_num)); 9670Sstevel@tonic-gate return (Z_ERR); 9680Sstevel@tonic-gate } 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate return (err ? Z_ERR : Z_OK); 9720Sstevel@tonic-gate } 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate static void 9750Sstevel@tonic-gate release_lock_file(int lockfd) 9760Sstevel@tonic-gate { 9770Sstevel@tonic-gate (void) close(lockfd); 9780Sstevel@tonic-gate } 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate static int 9810Sstevel@tonic-gate grab_lock_file(const char *zone_name, int *lockfd) 9820Sstevel@tonic-gate { 9830Sstevel@tonic-gate char pathbuf[PATH_MAX]; 9840Sstevel@tonic-gate struct flock flock; 9850Sstevel@tonic-gate 986766Scarlsonj if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(), 987766Scarlsonj ZONES_TMPDIR) >= sizeof (pathbuf)) { 988766Scarlsonj zerror(gettext("alternate root path is too long")); 989766Scarlsonj return (Z_ERR); 990766Scarlsonj } 991766Scarlsonj if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) { 992766Scarlsonj zerror(gettext("could not mkdir %s: %s"), pathbuf, 9930Sstevel@tonic-gate strerror(errno)); 9940Sstevel@tonic-gate return (Z_ERR); 9950Sstevel@tonic-gate } 996766Scarlsonj (void) chmod(pathbuf, S_IRWXU); 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate /* 9990Sstevel@tonic-gate * One of these lock files is created for each zone (when needed). 10000Sstevel@tonic-gate * The lock files are not cleaned up (except on system reboot), 10010Sstevel@tonic-gate * but since there is only one per zone, there is no resource 10020Sstevel@tonic-gate * starvation issue. 10030Sstevel@tonic-gate */ 1004766Scarlsonj if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock", 1005766Scarlsonj zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) { 1006766Scarlsonj zerror(gettext("alternate root path is too long")); 1007766Scarlsonj return (Z_ERR); 1008766Scarlsonj } 10090Sstevel@tonic-gate if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 10100Sstevel@tonic-gate zerror(gettext("could not open %s: %s"), pathbuf, 10110Sstevel@tonic-gate strerror(errno)); 10120Sstevel@tonic-gate return (Z_ERR); 10130Sstevel@tonic-gate } 10140Sstevel@tonic-gate /* 10150Sstevel@tonic-gate * Lock the file to synchronize with other zoneadmds 10160Sstevel@tonic-gate */ 10170Sstevel@tonic-gate flock.l_type = F_WRLCK; 10180Sstevel@tonic-gate flock.l_whence = SEEK_SET; 10190Sstevel@tonic-gate flock.l_start = (off_t)0; 10200Sstevel@tonic-gate flock.l_len = (off_t)0; 10210Sstevel@tonic-gate if (fcntl(*lockfd, F_SETLKW, &flock) < 0) { 10220Sstevel@tonic-gate zerror(gettext("unable to lock %s: %s"), pathbuf, 10230Sstevel@tonic-gate strerror(errno)); 10240Sstevel@tonic-gate release_lock_file(*lockfd); 10250Sstevel@tonic-gate return (Z_ERR); 10260Sstevel@tonic-gate } 10270Sstevel@tonic-gate return (Z_OK); 10280Sstevel@tonic-gate } 10290Sstevel@tonic-gate 1030766Scarlsonj static boolean_t 10310Sstevel@tonic-gate get_doorname(const char *zone_name, char *buffer) 10320Sstevel@tonic-gate { 1033766Scarlsonj return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH, 1034766Scarlsonj zonecfg_get_root(), zone_name) < PATH_MAX); 10350Sstevel@tonic-gate } 10360Sstevel@tonic-gate 10370Sstevel@tonic-gate /* 10380Sstevel@tonic-gate * system daemons are not audited. For the global zone, this occurs 10390Sstevel@tonic-gate * "naturally" since init is started with the default audit 10400Sstevel@tonic-gate * characteristics. Since zoneadmd is a system daemon and it starts 10410Sstevel@tonic-gate * init for a zone, it is necessary to clear out the audit 10420Sstevel@tonic-gate * characteristics inherited from whomever started zoneadmd. This is 10430Sstevel@tonic-gate * indicated by the audit id, which is set from the ruid parameter of 10440Sstevel@tonic-gate * adt_set_user(), below. 10450Sstevel@tonic-gate */ 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate static void 10480Sstevel@tonic-gate prepare_audit_context() 10490Sstevel@tonic-gate { 10500Sstevel@tonic-gate adt_session_data_t *ah; 10510Sstevel@tonic-gate char *failure = gettext("audit failure: %s"); 10520Sstevel@tonic-gate 10530Sstevel@tonic-gate if (adt_start_session(&ah, NULL, 0)) { 10540Sstevel@tonic-gate zerror(failure, strerror(errno)); 10550Sstevel@tonic-gate return; 10560Sstevel@tonic-gate } 10570Sstevel@tonic-gate if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT, 10580Sstevel@tonic-gate ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) { 10590Sstevel@tonic-gate zerror(failure, strerror(errno)); 10600Sstevel@tonic-gate (void) adt_end_session(ah); 10610Sstevel@tonic-gate return; 10620Sstevel@tonic-gate } 10630Sstevel@tonic-gate if (adt_set_proc(ah)) 10640Sstevel@tonic-gate zerror(failure, strerror(errno)); 10650Sstevel@tonic-gate 10660Sstevel@tonic-gate (void) adt_end_session(ah); 10670Sstevel@tonic-gate } 10680Sstevel@tonic-gate 10690Sstevel@tonic-gate static int 10700Sstevel@tonic-gate start_zoneadmd(const char *zone_name) 10710Sstevel@tonic-gate { 10720Sstevel@tonic-gate char doorpath[PATH_MAX]; 10730Sstevel@tonic-gate pid_t child_pid; 10740Sstevel@tonic-gate int error = Z_ERR; 10750Sstevel@tonic-gate int doorfd, lockfd; 10760Sstevel@tonic-gate struct door_info info; 10770Sstevel@tonic-gate 1078766Scarlsonj if (!get_doorname(zone_name, doorpath)) 1079766Scarlsonj return (Z_ERR); 10800Sstevel@tonic-gate 10810Sstevel@tonic-gate if (grab_lock_file(zone_name, &lockfd) != Z_OK) 10820Sstevel@tonic-gate return (Z_ERR); 10830Sstevel@tonic-gate 10840Sstevel@tonic-gate /* 10850Sstevel@tonic-gate * Now that we have the lock, re-confirm that the daemon is 10860Sstevel@tonic-gate * *not* up and working fine. If it is still down, we have a green 10870Sstevel@tonic-gate * light to start it. 10880Sstevel@tonic-gate */ 10890Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 10900Sstevel@tonic-gate if (errno != ENOENT) { 10910Sstevel@tonic-gate zperror(doorpath, B_FALSE); 10920Sstevel@tonic-gate goto out; 10930Sstevel@tonic-gate } 10940Sstevel@tonic-gate } else { 10950Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 10960Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 10970Sstevel@tonic-gate error = Z_OK; 10980Sstevel@tonic-gate (void) close(doorfd); 10990Sstevel@tonic-gate goto out; 11000Sstevel@tonic-gate } 11010Sstevel@tonic-gate (void) close(doorfd); 11020Sstevel@tonic-gate } 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 11050Sstevel@tonic-gate zperror(gettext("could not fork"), B_FALSE); 11060Sstevel@tonic-gate goto out; 11070Sstevel@tonic-gate } else if (child_pid == 0) { 1108766Scarlsonj const char *argv[6], **ap; 1109766Scarlsonj 11100Sstevel@tonic-gate /* child process */ 11110Sstevel@tonic-gate prepare_audit_context(); 11120Sstevel@tonic-gate 1113766Scarlsonj ap = argv; 1114766Scarlsonj *ap++ = "zoneadmd"; 1115766Scarlsonj *ap++ = "-z"; 1116766Scarlsonj *ap++ = zone_name; 1117766Scarlsonj if (zonecfg_in_alt_root()) { 1118766Scarlsonj *ap++ = "-R"; 1119766Scarlsonj *ap++ = zonecfg_get_root(); 1120766Scarlsonj } 1121766Scarlsonj *ap = NULL; 1122766Scarlsonj 1123766Scarlsonj (void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv); 1124924Sgjelinek /* 1125924Sgjelinek * TRANSLATION_NOTE 1126924Sgjelinek * zoneadmd is a literal that should not be translated. 1127924Sgjelinek */ 11280Sstevel@tonic-gate zperror(gettext("could not exec zoneadmd"), B_FALSE); 11290Sstevel@tonic-gate _exit(Z_ERR); 11300Sstevel@tonic-gate } else { 11310Sstevel@tonic-gate /* parent process */ 11320Sstevel@tonic-gate pid_t retval; 11330Sstevel@tonic-gate int pstatus = 0; 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate do { 11360Sstevel@tonic-gate retval = waitpid(child_pid, &pstatus, 0); 11370Sstevel@tonic-gate } while (retval != child_pid); 11380Sstevel@tonic-gate if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) && 11390Sstevel@tonic-gate WEXITSTATUS(pstatus) != 0)) { 11400Sstevel@tonic-gate zerror(gettext("could not start %s"), "zoneadmd"); 11410Sstevel@tonic-gate goto out; 11420Sstevel@tonic-gate } 11430Sstevel@tonic-gate } 11440Sstevel@tonic-gate error = Z_OK; 11450Sstevel@tonic-gate out: 11460Sstevel@tonic-gate release_lock_file(lockfd); 11470Sstevel@tonic-gate return (error); 11480Sstevel@tonic-gate } 11490Sstevel@tonic-gate 11500Sstevel@tonic-gate static int 11510Sstevel@tonic-gate ping_zoneadmd(const char *zone_name) 11520Sstevel@tonic-gate { 11530Sstevel@tonic-gate char doorpath[PATH_MAX]; 11540Sstevel@tonic-gate int doorfd; 11550Sstevel@tonic-gate struct door_info info; 11560Sstevel@tonic-gate 1157766Scarlsonj if (!get_doorname(zone_name, doorpath)) 1158766Scarlsonj return (Z_ERR); 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 11610Sstevel@tonic-gate return (Z_ERR); 11620Sstevel@tonic-gate } 11630Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 11640Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 11650Sstevel@tonic-gate (void) close(doorfd); 11660Sstevel@tonic-gate return (Z_OK); 11670Sstevel@tonic-gate } 11680Sstevel@tonic-gate (void) close(doorfd); 11690Sstevel@tonic-gate return (Z_ERR); 11700Sstevel@tonic-gate } 11710Sstevel@tonic-gate 11720Sstevel@tonic-gate static int 11730Sstevel@tonic-gate call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg) 11740Sstevel@tonic-gate { 11750Sstevel@tonic-gate char doorpath[PATH_MAX]; 11760Sstevel@tonic-gate int doorfd, result; 11770Sstevel@tonic-gate door_arg_t darg; 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate zoneid_t zoneid; 11800Sstevel@tonic-gate uint64_t uniqid = 0; 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate zone_cmd_rval_t *rvalp; 11830Sstevel@tonic-gate size_t rlen; 11840Sstevel@tonic-gate char *cp, *errbuf; 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate rlen = getpagesize(); 11870Sstevel@tonic-gate if ((rvalp = malloc(rlen)) == NULL) { 11880Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rlen, 11890Sstevel@tonic-gate strerror(errno)); 11900Sstevel@tonic-gate return (-1); 11910Sstevel@tonic-gate } 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) { 11940Sstevel@tonic-gate (void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid, 11950Sstevel@tonic-gate sizeof (uniqid)); 11960Sstevel@tonic-gate } 11970Sstevel@tonic-gate arg->uniqid = uniqid; 11980Sstevel@tonic-gate (void) strlcpy(arg->locale, locale, sizeof (arg->locale)); 1199766Scarlsonj if (!get_doorname(zone_name, doorpath)) { 1200766Scarlsonj zerror(gettext("alternate root path is too long")); 1201766Scarlsonj free(rvalp); 1202766Scarlsonj return (-1); 1203766Scarlsonj } 12040Sstevel@tonic-gate 12050Sstevel@tonic-gate /* 12060Sstevel@tonic-gate * Loop trying to start zoneadmd; if something goes seriously 12070Sstevel@tonic-gate * wrong we break out and fail. 12080Sstevel@tonic-gate */ 12090Sstevel@tonic-gate for (;;) { 12100Sstevel@tonic-gate if (start_zoneadmd(zone_name) != Z_OK) 12110Sstevel@tonic-gate break; 12120Sstevel@tonic-gate 12130Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 12140Sstevel@tonic-gate zperror(gettext("failed to open zone door"), B_FALSE); 12150Sstevel@tonic-gate break; 12160Sstevel@tonic-gate } 12170Sstevel@tonic-gate 12180Sstevel@tonic-gate darg.data_ptr = (char *)arg; 12190Sstevel@tonic-gate darg.data_size = sizeof (*arg); 12200Sstevel@tonic-gate darg.desc_ptr = NULL; 12210Sstevel@tonic-gate darg.desc_num = 0; 12220Sstevel@tonic-gate darg.rbuf = (char *)rvalp; 12230Sstevel@tonic-gate darg.rsize = rlen; 12240Sstevel@tonic-gate if (door_call(doorfd, &darg) != 0) { 12250Sstevel@tonic-gate (void) close(doorfd); 12260Sstevel@tonic-gate /* 12270Sstevel@tonic-gate * We'll get EBADF if the door has been revoked. 12280Sstevel@tonic-gate */ 12290Sstevel@tonic-gate if (errno != EBADF) { 12300Sstevel@tonic-gate zperror(gettext("door_call failed"), B_FALSE); 12310Sstevel@tonic-gate break; 12320Sstevel@tonic-gate } 12330Sstevel@tonic-gate continue; /* take another lap */ 12340Sstevel@tonic-gate } 12350Sstevel@tonic-gate (void) close(doorfd); 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate if (darg.data_size == 0) { 12380Sstevel@tonic-gate /* Door server is going away; kick it again. */ 12390Sstevel@tonic-gate continue; 12400Sstevel@tonic-gate } 12410Sstevel@tonic-gate 12420Sstevel@tonic-gate errbuf = rvalp->errbuf; 12430Sstevel@tonic-gate while (*errbuf != '\0') { 12440Sstevel@tonic-gate /* 12450Sstevel@tonic-gate * Remove any newlines since zerror() 12460Sstevel@tonic-gate * will append one automatically. 12470Sstevel@tonic-gate */ 12480Sstevel@tonic-gate cp = strchr(errbuf, '\n'); 12490Sstevel@tonic-gate if (cp != NULL) 12500Sstevel@tonic-gate *cp = '\0'; 12510Sstevel@tonic-gate zerror("%s", errbuf); 12520Sstevel@tonic-gate if (cp == NULL) 12530Sstevel@tonic-gate break; 12540Sstevel@tonic-gate errbuf = cp + 1; 12550Sstevel@tonic-gate } 12560Sstevel@tonic-gate result = rvalp->rval == 0 ? 0 : -1; 12570Sstevel@tonic-gate free(rvalp); 12580Sstevel@tonic-gate return (result); 12590Sstevel@tonic-gate } 12600Sstevel@tonic-gate 12610Sstevel@tonic-gate free(rvalp); 12620Sstevel@tonic-gate return (-1); 12630Sstevel@tonic-gate } 12640Sstevel@tonic-gate 12650Sstevel@tonic-gate static int 12660Sstevel@tonic-gate ready_func(int argc, char *argv[]) 12670Sstevel@tonic-gate { 12680Sstevel@tonic-gate zone_cmd_arg_t zarg; 12690Sstevel@tonic-gate int arg; 12700Sstevel@tonic-gate 1271766Scarlsonj if (zonecfg_in_alt_root()) { 1272766Scarlsonj zerror(gettext("cannot ready zone in alternate root")); 1273766Scarlsonj return (Z_ERR); 1274766Scarlsonj } 1275766Scarlsonj 12760Sstevel@tonic-gate optind = 0; 12770Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 12780Sstevel@tonic-gate switch (arg) { 12790Sstevel@tonic-gate case '?': 12800Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12810Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 12820Sstevel@tonic-gate default: 12830Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12840Sstevel@tonic-gate return (Z_USAGE); 12850Sstevel@tonic-gate } 12860Sstevel@tonic-gate } 12870Sstevel@tonic-gate if (argc > optind) { 12880Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12890Sstevel@tonic-gate return (Z_USAGE); 12900Sstevel@tonic-gate } 12910Sstevel@tonic-gate if (sanity_check(target_zone, CMD_READY, B_FALSE, B_FALSE) != Z_OK) 12920Sstevel@tonic-gate return (Z_ERR); 12930Sstevel@tonic-gate if (verify_details(CMD_READY) != Z_OK) 12940Sstevel@tonic-gate return (Z_ERR); 12950Sstevel@tonic-gate 12960Sstevel@tonic-gate zarg.cmd = Z_READY; 12970Sstevel@tonic-gate if (call_zoneadmd(target_zone, &zarg) != 0) { 12980Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 12990Sstevel@tonic-gate return (Z_ERR); 13000Sstevel@tonic-gate } 13010Sstevel@tonic-gate return (Z_OK); 13020Sstevel@tonic-gate } 13030Sstevel@tonic-gate 13040Sstevel@tonic-gate static int 13050Sstevel@tonic-gate boot_func(int argc, char *argv[]) 13060Sstevel@tonic-gate { 13070Sstevel@tonic-gate zone_cmd_arg_t zarg; 13080Sstevel@tonic-gate int arg; 13090Sstevel@tonic-gate 1310766Scarlsonj if (zonecfg_in_alt_root()) { 1311766Scarlsonj zerror(gettext("cannot boot zone in alternate root")); 1312766Scarlsonj return (Z_ERR); 1313766Scarlsonj } 1314766Scarlsonj 13150Sstevel@tonic-gate zarg.bootbuf[0] = '\0'; 13160Sstevel@tonic-gate 13170Sstevel@tonic-gate /* 13180Sstevel@tonic-gate * At the current time, the only supported subargument to the 13190Sstevel@tonic-gate * "boot" subcommand is "-s" which specifies a single-user boot. 13200Sstevel@tonic-gate * In the future, other boot arguments should be supported 13210Sstevel@tonic-gate * including "-m" for specifying alternate smf(5) milestones. 13220Sstevel@tonic-gate */ 13230Sstevel@tonic-gate optind = 0; 13240Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?s")) != EOF) { 13250Sstevel@tonic-gate switch (arg) { 13260Sstevel@tonic-gate case '?': 13270Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13280Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13290Sstevel@tonic-gate case 's': 13300Sstevel@tonic-gate (void) strlcpy(zarg.bootbuf, "-s", 13310Sstevel@tonic-gate sizeof (zarg.bootbuf)); 13320Sstevel@tonic-gate break; 13330Sstevel@tonic-gate default: 13340Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13350Sstevel@tonic-gate return (Z_USAGE); 13360Sstevel@tonic-gate } 13370Sstevel@tonic-gate } 13380Sstevel@tonic-gate if (argc > optind) { 13390Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13400Sstevel@tonic-gate return (Z_USAGE); 13410Sstevel@tonic-gate } 13420Sstevel@tonic-gate if (sanity_check(target_zone, CMD_BOOT, B_FALSE, B_FALSE) != Z_OK) 13430Sstevel@tonic-gate return (Z_ERR); 13440Sstevel@tonic-gate if (verify_details(CMD_BOOT) != Z_OK) 13450Sstevel@tonic-gate return (Z_ERR); 13460Sstevel@tonic-gate zarg.cmd = Z_BOOT; 13470Sstevel@tonic-gate if (call_zoneadmd(target_zone, &zarg) != 0) { 13480Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 13490Sstevel@tonic-gate return (Z_ERR); 13500Sstevel@tonic-gate } 13510Sstevel@tonic-gate return (Z_OK); 13520Sstevel@tonic-gate } 13530Sstevel@tonic-gate 13540Sstevel@tonic-gate static void 13550Sstevel@tonic-gate fake_up_local_zone(zoneid_t zid, zone_entry_t *zeptr) 13560Sstevel@tonic-gate { 13570Sstevel@tonic-gate ssize_t result; 13580Sstevel@tonic-gate 13590Sstevel@tonic-gate zeptr->zid = zid; 13600Sstevel@tonic-gate /* 13610Sstevel@tonic-gate * Since we're looking up our own (non-global) zone name, 13620Sstevel@tonic-gate * we can be assured that it will succeed. 13630Sstevel@tonic-gate */ 13640Sstevel@tonic-gate result = getzonenamebyid(zid, zeptr->zname, sizeof (zeptr->zname)); 13650Sstevel@tonic-gate assert(result >= 0); 13661676Sjpk if (!is_system_labeled()) { 13671676Sjpk (void) strlcpy(zeptr->zroot, "/", sizeof (zeptr->zroot)); 13681676Sjpk } else { 13691676Sjpk (void) zone_getattr(zid, ZONE_ATTR_ROOT, zeptr->zroot, 13701676Sjpk sizeof (zeptr->zroot)); 13711676Sjpk } 13720Sstevel@tonic-gate zeptr->zstate_str = "running"; 13730Sstevel@tonic-gate } 13740Sstevel@tonic-gate 13750Sstevel@tonic-gate static int 13760Sstevel@tonic-gate list_func(int argc, char *argv[]) 13770Sstevel@tonic-gate { 13780Sstevel@tonic-gate zone_entry_t *zentp, zent; 1379766Scarlsonj int arg, retv; 13800Sstevel@tonic-gate boolean_t output = B_FALSE, verbose = B_FALSE, parsable = B_FALSE; 13810Sstevel@tonic-gate zone_state_t min_state = ZONE_STATE_RUNNING; 13820Sstevel@tonic-gate zoneid_t zone_id = getzoneid(); 13830Sstevel@tonic-gate 13840Sstevel@tonic-gate if (target_zone == NULL) { 13850Sstevel@tonic-gate /* all zones: default view to running but allow override */ 13860Sstevel@tonic-gate optind = 0; 13870Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?cipv")) != EOF) { 13880Sstevel@tonic-gate switch (arg) { 13890Sstevel@tonic-gate case '?': 13900Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13910Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13921339Sjonb /* 13931339Sjonb * The 'i' and 'c' options are not mutually 13941339Sjonb * exclusive so if 'c' is given, then min_state 13951339Sjonb * is set to 0 (ZONE_STATE_CONFIGURED) which is 13961339Sjonb * the lowest possible state. If 'i' is given, 13971339Sjonb * then min_state is set to be the lowest state 13981339Sjonb * so far. 13991339Sjonb */ 14000Sstevel@tonic-gate case 'c': 14010Sstevel@tonic-gate min_state = ZONE_STATE_CONFIGURED; 14020Sstevel@tonic-gate break; 14030Sstevel@tonic-gate case 'i': 14041339Sjonb min_state = min(ZONE_STATE_INSTALLED, 14051339Sjonb min_state); 14061339Sjonb 14070Sstevel@tonic-gate break; 14080Sstevel@tonic-gate case 'p': 14090Sstevel@tonic-gate parsable = B_TRUE; 14100Sstevel@tonic-gate break; 14110Sstevel@tonic-gate case 'v': 14120Sstevel@tonic-gate verbose = B_TRUE; 14130Sstevel@tonic-gate break; 14140Sstevel@tonic-gate default: 14150Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14160Sstevel@tonic-gate return (Z_USAGE); 14170Sstevel@tonic-gate } 14180Sstevel@tonic-gate } 14190Sstevel@tonic-gate if (parsable && verbose) { 14200Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14210Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14220Sstevel@tonic-gate return (Z_ERR); 14230Sstevel@tonic-gate } 14241676Sjpk if (zone_id == GLOBAL_ZONEID || is_system_labeled()) { 1425766Scarlsonj retv = zone_print_list(min_state, verbose, parsable); 14260Sstevel@tonic-gate } else { 1427766Scarlsonj retv = Z_OK; 14280Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14290Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14300Sstevel@tonic-gate } 1431766Scarlsonj return (retv); 14320Sstevel@tonic-gate } 14330Sstevel@tonic-gate 14340Sstevel@tonic-gate /* 14350Sstevel@tonic-gate * Specific target zone: disallow -i/-c suboptions. 14360Sstevel@tonic-gate */ 14370Sstevel@tonic-gate optind = 0; 14380Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?pv")) != EOF) { 14390Sstevel@tonic-gate switch (arg) { 14400Sstevel@tonic-gate case '?': 14410Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14420Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 14430Sstevel@tonic-gate case 'p': 14440Sstevel@tonic-gate parsable = B_TRUE; 14450Sstevel@tonic-gate break; 14460Sstevel@tonic-gate case 'v': 14470Sstevel@tonic-gate verbose = B_TRUE; 14480Sstevel@tonic-gate break; 14490Sstevel@tonic-gate default: 14500Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14510Sstevel@tonic-gate return (Z_USAGE); 14520Sstevel@tonic-gate } 14530Sstevel@tonic-gate } 14540Sstevel@tonic-gate if (parsable && verbose) { 14550Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14560Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14570Sstevel@tonic-gate return (Z_ERR); 14580Sstevel@tonic-gate } 14590Sstevel@tonic-gate if (argc > optind) { 14600Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14610Sstevel@tonic-gate return (Z_USAGE); 14620Sstevel@tonic-gate } 14630Sstevel@tonic-gate if (zone_id != GLOBAL_ZONEID) { 14640Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14650Sstevel@tonic-gate /* 14660Sstevel@tonic-gate * main() will issue a Z_NO_ZONE error if it cannot get an 14670Sstevel@tonic-gate * id for target_zone, which in a non-global zone should 14680Sstevel@tonic-gate * happen for any zone name except `zonename`. Thus we 14690Sstevel@tonic-gate * assert() that here but don't otherwise check. 14700Sstevel@tonic-gate */ 14710Sstevel@tonic-gate assert(strcmp(zent.zname, target_zone) == 0); 14720Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14730Sstevel@tonic-gate output = B_TRUE; 14740Sstevel@tonic-gate } else if ((zentp = lookup_running_zone(target_zone)) != NULL) { 14750Sstevel@tonic-gate zone_print(zentp, verbose, parsable); 14760Sstevel@tonic-gate output = B_TRUE; 1477766Scarlsonj } else if (lookup_zone_info(target_zone, ZONE_ID_UNDEFINED, 1478766Scarlsonj &zent) == Z_OK) { 14790Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14800Sstevel@tonic-gate output = B_TRUE; 14810Sstevel@tonic-gate } 14820Sstevel@tonic-gate return (output ? Z_OK : Z_ERR); 14830Sstevel@tonic-gate } 14840Sstevel@tonic-gate 14850Sstevel@tonic-gate static void 14860Sstevel@tonic-gate sigterm(int sig) 14870Sstevel@tonic-gate { 14880Sstevel@tonic-gate /* 14890Sstevel@tonic-gate * Ignore SIG{INT,TERM}, so we don't end up in an infinite loop, 14900Sstevel@tonic-gate * then propagate the signal to our process group. 14910Sstevel@tonic-gate */ 14920Sstevel@tonic-gate (void) sigset(SIGINT, SIG_IGN); 14930Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_IGN); 14940Sstevel@tonic-gate (void) kill(0, sig); 14950Sstevel@tonic-gate child_killed = B_TRUE; 14960Sstevel@tonic-gate } 14970Sstevel@tonic-gate 14980Sstevel@tonic-gate static int 14990Sstevel@tonic-gate do_subproc(char *cmdbuf) 15000Sstevel@tonic-gate { 15010Sstevel@tonic-gate char inbuf[1024]; /* arbitrary large amount */ 15020Sstevel@tonic-gate FILE *file; 15030Sstevel@tonic-gate 15040Sstevel@tonic-gate child_killed = B_FALSE; 15050Sstevel@tonic-gate /* 15060Sstevel@tonic-gate * We use popen(3c) to launch child processes for [un]install; 15070Sstevel@tonic-gate * this library call does not return a PID, so we have to kill 15080Sstevel@tonic-gate * the whole process group. To avoid killing our parent, we 15090Sstevel@tonic-gate * become a process group leader here. But doing so can wreak 15100Sstevel@tonic-gate * havoc with reading from stdin when launched by a non-job-control 15110Sstevel@tonic-gate * shell, so we close stdin and reopen it as /dev/null first. 15120Sstevel@tonic-gate */ 15130Sstevel@tonic-gate (void) close(STDIN_FILENO); 15140Sstevel@tonic-gate (void) open("/dev/null", O_RDONLY); 15150Sstevel@tonic-gate (void) setpgid(0, 0); 15160Sstevel@tonic-gate (void) sigset(SIGINT, sigterm); 15170Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm); 15180Sstevel@tonic-gate file = popen(cmdbuf, "r"); 15190Sstevel@tonic-gate for (;;) { 15200Sstevel@tonic-gate if (child_killed || fgets(inbuf, sizeof (inbuf), file) == NULL) 15210Sstevel@tonic-gate break; 15220Sstevel@tonic-gate (void) fputs(inbuf, stdout); 15230Sstevel@tonic-gate } 15240Sstevel@tonic-gate (void) sigset(SIGINT, SIG_DFL); 15250Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_DFL); 15260Sstevel@tonic-gate return (pclose(file)); 15270Sstevel@tonic-gate } 15280Sstevel@tonic-gate 15290Sstevel@tonic-gate static int 15300Sstevel@tonic-gate subproc_status(const char *cmd, int status) 15310Sstevel@tonic-gate { 15320Sstevel@tonic-gate if (WIFEXITED(status)) { 15330Sstevel@tonic-gate int exit_code = WEXITSTATUS(status); 15340Sstevel@tonic-gate 15350Sstevel@tonic-gate if (exit_code == 0) 15360Sstevel@tonic-gate return (Z_OK); 15370Sstevel@tonic-gate zerror(gettext("'%s' failed with exit code %d."), cmd, 15380Sstevel@tonic-gate exit_code); 15390Sstevel@tonic-gate } else if (WIFSIGNALED(status)) { 15400Sstevel@tonic-gate int signal = WTERMSIG(status); 15410Sstevel@tonic-gate char sigstr[SIG2STR_MAX]; 15420Sstevel@tonic-gate 15430Sstevel@tonic-gate if (sig2str(signal, sigstr) == 0) { 15440Sstevel@tonic-gate zerror(gettext("'%s' terminated by signal SIG%s."), cmd, 15450Sstevel@tonic-gate sigstr); 15460Sstevel@tonic-gate } else { 15470Sstevel@tonic-gate zerror(gettext("'%s' terminated by an unknown signal."), 15480Sstevel@tonic-gate cmd); 15490Sstevel@tonic-gate } 15500Sstevel@tonic-gate } else { 15510Sstevel@tonic-gate zerror(gettext("'%s' failed for unknown reasons."), cmd); 15520Sstevel@tonic-gate } 15530Sstevel@tonic-gate return (Z_ERR); 15540Sstevel@tonic-gate } 15550Sstevel@tonic-gate 15560Sstevel@tonic-gate /* 15570Sstevel@tonic-gate * Various sanity checks; make sure: 15580Sstevel@tonic-gate * 1. We're in the global zone. 15590Sstevel@tonic-gate * 2. The calling user has sufficient privilege. 15600Sstevel@tonic-gate * 3. The target zone is neither the global zone nor anything starting with 15610Sstevel@tonic-gate * "SUNW". 15620Sstevel@tonic-gate * 4a. If we're looking for a 'not running' (i.e., configured or installed) 15630Sstevel@tonic-gate * zone, the name service knows about it. 15640Sstevel@tonic-gate * 4b. For some operations which expect a zone not to be running, that it is 15650Sstevel@tonic-gate * not already running (or ready). 15660Sstevel@tonic-gate */ 15670Sstevel@tonic-gate static int 15680Sstevel@tonic-gate sanity_check(char *zone, int cmd_num, boolean_t running, 15690Sstevel@tonic-gate boolean_t unsafe_when_running) 15700Sstevel@tonic-gate { 15710Sstevel@tonic-gate zone_entry_t *zent; 15720Sstevel@tonic-gate priv_set_t *privset; 15730Sstevel@tonic-gate zone_state_t state; 1574766Scarlsonj char kernzone[ZONENAME_MAX]; 1575766Scarlsonj FILE *fp; 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 15781645Scomay switch (cmd_num) { 15791645Scomay case CMD_HALT: 15801645Scomay zerror(gettext("use %s to %s this zone."), "halt(1M)", 15811645Scomay cmd_to_str(cmd_num)); 15821645Scomay break; 15831645Scomay case CMD_REBOOT: 15841645Scomay zerror(gettext("use %s to %s this zone."), 15851645Scomay "reboot(1M)", cmd_to_str(cmd_num)); 15861645Scomay break; 15871645Scomay default: 15881645Scomay zerror(gettext("must be in the global zone to %s a " 15891645Scomay "zone."), cmd_to_str(cmd_num)); 15901645Scomay break; 15911645Scomay } 15920Sstevel@tonic-gate return (Z_ERR); 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 15960Sstevel@tonic-gate zerror(gettext("%s failed"), "priv_allocset"); 15970Sstevel@tonic-gate return (Z_ERR); 15980Sstevel@tonic-gate } 15990Sstevel@tonic-gate 16000Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 16010Sstevel@tonic-gate zerror(gettext("%s failed"), "getppriv"); 16020Sstevel@tonic-gate priv_freeset(privset); 16030Sstevel@tonic-gate return (Z_ERR); 16040Sstevel@tonic-gate } 16050Sstevel@tonic-gate 16060Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 16070Sstevel@tonic-gate zerror(gettext("only a privileged user may %s a zone."), 16080Sstevel@tonic-gate cmd_to_str(cmd_num)); 16090Sstevel@tonic-gate priv_freeset(privset); 16100Sstevel@tonic-gate return (Z_ERR); 16110Sstevel@tonic-gate } 16120Sstevel@tonic-gate priv_freeset(privset); 16130Sstevel@tonic-gate 16140Sstevel@tonic-gate if (zone == NULL) { 16150Sstevel@tonic-gate zerror(gettext("no zone specified")); 16160Sstevel@tonic-gate return (Z_ERR); 16170Sstevel@tonic-gate } 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate if (strcmp(zone, GLOBAL_ZONENAME) == 0) { 16200Sstevel@tonic-gate zerror(gettext("%s operation is invalid for the global zone."), 16210Sstevel@tonic-gate cmd_to_str(cmd_num)); 16220Sstevel@tonic-gate return (Z_ERR); 16230Sstevel@tonic-gate } 16240Sstevel@tonic-gate 16250Sstevel@tonic-gate if (strncmp(zone, "SUNW", 4) == 0) { 16260Sstevel@tonic-gate zerror(gettext("%s operation is invalid for zones starting " 16270Sstevel@tonic-gate "with SUNW."), cmd_to_str(cmd_num)); 16280Sstevel@tonic-gate return (Z_ERR); 16290Sstevel@tonic-gate } 16300Sstevel@tonic-gate 1631766Scarlsonj if (!zonecfg_in_alt_root()) { 1632766Scarlsonj zent = lookup_running_zone(zone); 1633766Scarlsonj } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) { 1634766Scarlsonj zent = NULL; 1635766Scarlsonj } else { 1636766Scarlsonj if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), 1637766Scarlsonj kernzone, sizeof (kernzone)) == 0) 1638766Scarlsonj zent = lookup_running_zone(kernzone); 1639766Scarlsonj else 1640766Scarlsonj zent = NULL; 1641766Scarlsonj zonecfg_close_scratch(fp); 1642766Scarlsonj } 1643766Scarlsonj 16440Sstevel@tonic-gate /* 16450Sstevel@tonic-gate * Look up from the kernel for 'running' zones. 16460Sstevel@tonic-gate */ 16470Sstevel@tonic-gate if (running) { 16480Sstevel@tonic-gate if (zent == NULL) { 16490Sstevel@tonic-gate zerror(gettext("not running")); 16500Sstevel@tonic-gate return (Z_ERR); 16510Sstevel@tonic-gate } 16520Sstevel@tonic-gate } else { 16530Sstevel@tonic-gate int err; 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate if (unsafe_when_running && zent != NULL) { 16560Sstevel@tonic-gate /* check whether the zone is ready or running */ 16570Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, 16580Sstevel@tonic-gate &zent->zstate_num)) != Z_OK) { 16590Sstevel@tonic-gate errno = err; 16600Sstevel@tonic-gate zperror2(zent->zname, 16610Sstevel@tonic-gate gettext("could not get state")); 16620Sstevel@tonic-gate /* can't tell, so hedge */ 16630Sstevel@tonic-gate zent->zstate_str = "ready/running"; 16640Sstevel@tonic-gate } else { 16650Sstevel@tonic-gate zent->zstate_str = 16660Sstevel@tonic-gate zone_state_str(zent->zstate_num); 16670Sstevel@tonic-gate } 16680Sstevel@tonic-gate zerror(gettext("%s operation is invalid for %s zones."), 16690Sstevel@tonic-gate cmd_to_str(cmd_num), zent->zstate_str); 16700Sstevel@tonic-gate return (Z_ERR); 16710Sstevel@tonic-gate } 16720Sstevel@tonic-gate if ((err = zone_get_state(zone, &state)) != Z_OK) { 16730Sstevel@tonic-gate errno = err; 16740Sstevel@tonic-gate zperror2(zone, gettext("could not get state")); 16750Sstevel@tonic-gate return (Z_ERR); 16760Sstevel@tonic-gate } 16770Sstevel@tonic-gate switch (cmd_num) { 16780Sstevel@tonic-gate case CMD_UNINSTALL: 16790Sstevel@tonic-gate if (state == ZONE_STATE_CONFIGURED) { 16800Sstevel@tonic-gate zerror(gettext("is already in state '%s'."), 16810Sstevel@tonic-gate zone_state_str(ZONE_STATE_CONFIGURED)); 16820Sstevel@tonic-gate return (Z_ERR); 16830Sstevel@tonic-gate } 16840Sstevel@tonic-gate break; 16851507Sgjelinek case CMD_ATTACH: 16861300Sgjelinek case CMD_CLONE: 16870Sstevel@tonic-gate case CMD_INSTALL: 16880Sstevel@tonic-gate if (state == ZONE_STATE_INSTALLED) { 16890Sstevel@tonic-gate zerror(gettext("is already %s."), 16900Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED)); 16910Sstevel@tonic-gate return (Z_ERR); 16920Sstevel@tonic-gate } else if (state == ZONE_STATE_INCOMPLETE) { 16930Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 16940Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 16950Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 16960Sstevel@tonic-gate return (Z_ERR); 16970Sstevel@tonic-gate } 16980Sstevel@tonic-gate break; 16991507Sgjelinek case CMD_DETACH: 17001300Sgjelinek case CMD_MOVE: 17010Sstevel@tonic-gate case CMD_READY: 17020Sstevel@tonic-gate case CMD_BOOT: 1703766Scarlsonj case CMD_MOUNT: 17040Sstevel@tonic-gate if (state < ZONE_STATE_INSTALLED) { 17050Sstevel@tonic-gate zerror(gettext("must be %s before %s."), 17060Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED), 17070Sstevel@tonic-gate cmd_to_str(cmd_num)); 17080Sstevel@tonic-gate return (Z_ERR); 17090Sstevel@tonic-gate } 17100Sstevel@tonic-gate break; 17110Sstevel@tonic-gate case CMD_VERIFY: 17120Sstevel@tonic-gate if (state == ZONE_STATE_INCOMPLETE) { 17130Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 17140Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 17150Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 17160Sstevel@tonic-gate return (Z_ERR); 17170Sstevel@tonic-gate } 17180Sstevel@tonic-gate break; 1719766Scarlsonj case CMD_UNMOUNT: 1720766Scarlsonj if (state != ZONE_STATE_MOUNTED) { 1721766Scarlsonj zerror(gettext("must be %s before %s."), 1722766Scarlsonj zone_state_str(ZONE_STATE_MOUNTED), 1723766Scarlsonj cmd_to_str(cmd_num)); 1724766Scarlsonj return (Z_ERR); 1725766Scarlsonj } 1726766Scarlsonj break; 17270Sstevel@tonic-gate } 17280Sstevel@tonic-gate } 17290Sstevel@tonic-gate return (Z_OK); 17300Sstevel@tonic-gate } 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate static int 17330Sstevel@tonic-gate halt_func(int argc, char *argv[]) 17340Sstevel@tonic-gate { 17350Sstevel@tonic-gate zone_cmd_arg_t zarg; 17360Sstevel@tonic-gate int arg; 17370Sstevel@tonic-gate 1738766Scarlsonj if (zonecfg_in_alt_root()) { 1739766Scarlsonj zerror(gettext("cannot halt zone in alternate root")); 1740766Scarlsonj return (Z_ERR); 1741766Scarlsonj } 1742766Scarlsonj 17430Sstevel@tonic-gate optind = 0; 17440Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17450Sstevel@tonic-gate switch (arg) { 17460Sstevel@tonic-gate case '?': 17470Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17480Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 17490Sstevel@tonic-gate default: 17500Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17510Sstevel@tonic-gate return (Z_USAGE); 17520Sstevel@tonic-gate } 17530Sstevel@tonic-gate } 17540Sstevel@tonic-gate if (argc > optind) { 17550Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17560Sstevel@tonic-gate return (Z_USAGE); 17570Sstevel@tonic-gate } 17580Sstevel@tonic-gate /* 17590Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 17600Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 17610Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 17620Sstevel@tonic-gate */ 17630Sstevel@tonic-gate if (sanity_check(target_zone, CMD_HALT, B_FALSE, B_FALSE) != Z_OK) 17640Sstevel@tonic-gate return (Z_ERR); 17650Sstevel@tonic-gate 17660Sstevel@tonic-gate zarg.cmd = Z_HALT; 17670Sstevel@tonic-gate return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR); 17680Sstevel@tonic-gate } 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate static int 17710Sstevel@tonic-gate reboot_func(int argc, char *argv[]) 17720Sstevel@tonic-gate { 17730Sstevel@tonic-gate zone_cmd_arg_t zarg; 17740Sstevel@tonic-gate int arg; 17750Sstevel@tonic-gate 1776766Scarlsonj if (zonecfg_in_alt_root()) { 1777766Scarlsonj zerror(gettext("cannot reboot zone in alternate root")); 1778766Scarlsonj return (Z_ERR); 1779766Scarlsonj } 1780766Scarlsonj 17810Sstevel@tonic-gate optind = 0; 17820Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17830Sstevel@tonic-gate switch (arg) { 17840Sstevel@tonic-gate case '?': 17850Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17860Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 17870Sstevel@tonic-gate default: 17880Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17890Sstevel@tonic-gate return (Z_USAGE); 17900Sstevel@tonic-gate } 17910Sstevel@tonic-gate } 17920Sstevel@tonic-gate if (argc > 0) { 17930Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17940Sstevel@tonic-gate return (Z_USAGE); 17950Sstevel@tonic-gate } 17960Sstevel@tonic-gate /* 17970Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 17980Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 17990Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 18000Sstevel@tonic-gate */ 18010Sstevel@tonic-gate if (sanity_check(target_zone, CMD_REBOOT, B_TRUE, B_FALSE) != Z_OK) 18020Sstevel@tonic-gate return (Z_ERR); 1803823Sgjelinek if (verify_details(CMD_REBOOT) != Z_OK) 1804823Sgjelinek return (Z_ERR); 18050Sstevel@tonic-gate 18060Sstevel@tonic-gate zarg.cmd = Z_REBOOT; 18070Sstevel@tonic-gate return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR); 18080Sstevel@tonic-gate } 18090Sstevel@tonic-gate 18100Sstevel@tonic-gate static int 18110Sstevel@tonic-gate verify_rctls(zone_dochandle_t handle) 18120Sstevel@tonic-gate { 18130Sstevel@tonic-gate struct zone_rctltab rctltab; 18140Sstevel@tonic-gate size_t rbs = rctlblk_size(); 18150Sstevel@tonic-gate rctlblk_t *rctlblk; 18160Sstevel@tonic-gate int error = Z_INVAL; 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate if ((rctlblk = malloc(rbs)) == NULL) { 18190Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rbs, 18200Sstevel@tonic-gate strerror(errno)); 18210Sstevel@tonic-gate return (Z_NOMEM); 18220Sstevel@tonic-gate } 18230Sstevel@tonic-gate 18240Sstevel@tonic-gate if (zonecfg_setrctlent(handle) != Z_OK) { 18250Sstevel@tonic-gate zerror(gettext("zonecfg_setrctlent failed")); 18260Sstevel@tonic-gate free(rctlblk); 18270Sstevel@tonic-gate return (error); 18280Sstevel@tonic-gate } 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 18310Sstevel@tonic-gate while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { 18320Sstevel@tonic-gate struct zone_rctlvaltab *rctlval; 18330Sstevel@tonic-gate const char *name = rctltab.zone_rctl_name; 18340Sstevel@tonic-gate 18350Sstevel@tonic-gate if (!zonecfg_is_rctl(name)) { 18360Sstevel@tonic-gate zerror(gettext("WARNING: Ignoring unrecognized rctl " 18370Sstevel@tonic-gate "'%s'."), name); 18380Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 18390Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 18400Sstevel@tonic-gate continue; 18410Sstevel@tonic-gate } 18420Sstevel@tonic-gate 18430Sstevel@tonic-gate for (rctlval = rctltab.zone_rctl_valptr; rctlval != NULL; 18440Sstevel@tonic-gate rctlval = rctlval->zone_rctlval_next) { 18450Sstevel@tonic-gate if (zonecfg_construct_rctlblk(rctlval, rctlblk) 18460Sstevel@tonic-gate != Z_OK) { 18470Sstevel@tonic-gate zerror(gettext("invalid rctl value: " 18480Sstevel@tonic-gate "(priv=%s,limit=%s,action%s)"), 18490Sstevel@tonic-gate rctlval->zone_rctlval_priv, 18500Sstevel@tonic-gate rctlval->zone_rctlval_limit, 18510Sstevel@tonic-gate rctlval->zone_rctlval_action); 18520Sstevel@tonic-gate goto out; 18530Sstevel@tonic-gate } 18540Sstevel@tonic-gate if (!zonecfg_valid_rctl(name, rctlblk)) { 18550Sstevel@tonic-gate zerror(gettext("(priv=%s,limit=%s,action=%s) " 18560Sstevel@tonic-gate "is not a valid value for rctl '%s'"), 18570Sstevel@tonic-gate rctlval->zone_rctlval_priv, 18580Sstevel@tonic-gate rctlval->zone_rctlval_limit, 18590Sstevel@tonic-gate rctlval->zone_rctlval_action, 18600Sstevel@tonic-gate name); 18610Sstevel@tonic-gate goto out; 18620Sstevel@tonic-gate } 18630Sstevel@tonic-gate } 18640Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 18650Sstevel@tonic-gate } 18660Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 18670Sstevel@tonic-gate error = Z_OK; 18680Sstevel@tonic-gate out: 18690Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 18700Sstevel@tonic-gate (void) zonecfg_endrctlent(handle); 18710Sstevel@tonic-gate free(rctlblk); 18720Sstevel@tonic-gate return (error); 18730Sstevel@tonic-gate } 18740Sstevel@tonic-gate 18750Sstevel@tonic-gate static int 18760Sstevel@tonic-gate verify_pool(zone_dochandle_t handle) 18770Sstevel@tonic-gate { 18780Sstevel@tonic-gate char poolname[MAXPATHLEN]; 18790Sstevel@tonic-gate pool_conf_t *poolconf; 18800Sstevel@tonic-gate pool_t *pool; 18810Sstevel@tonic-gate int status; 18820Sstevel@tonic-gate int error; 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate /* 18850Sstevel@tonic-gate * This ends up being very similar to the check done in zoneadmd. 18860Sstevel@tonic-gate */ 18870Sstevel@tonic-gate error = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 18880Sstevel@tonic-gate if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) { 18890Sstevel@tonic-gate /* 18900Sstevel@tonic-gate * No pool specified. 18910Sstevel@tonic-gate */ 18920Sstevel@tonic-gate return (0); 18930Sstevel@tonic-gate } 18940Sstevel@tonic-gate if (error != Z_OK) { 18950Sstevel@tonic-gate zperror(gettext("Unable to retrieve pool name from " 18960Sstevel@tonic-gate "configuration"), B_TRUE); 18970Sstevel@tonic-gate return (error); 18980Sstevel@tonic-gate } 18990Sstevel@tonic-gate /* 19000Sstevel@tonic-gate * Don't do anything if pools aren't enabled. 19010Sstevel@tonic-gate */ 19020Sstevel@tonic-gate if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) { 19030Sstevel@tonic-gate zerror(gettext("WARNING: pools facility not active; " 19040Sstevel@tonic-gate "zone will not be bound to pool '%s'."), poolname); 19050Sstevel@tonic-gate return (Z_OK); 19060Sstevel@tonic-gate } 19070Sstevel@tonic-gate /* 19080Sstevel@tonic-gate * Try to provide a sane error message if the requested pool doesn't 19090Sstevel@tonic-gate * exist. It isn't clear that pools-related failures should 19100Sstevel@tonic-gate * necessarily translate to a failure to verify the zone configuration, 19110Sstevel@tonic-gate * hence they are not considered errors. 19120Sstevel@tonic-gate */ 19130Sstevel@tonic-gate if ((poolconf = pool_conf_alloc()) == NULL) { 19140Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_alloc failed; " 19150Sstevel@tonic-gate "using default pool")); 19160Sstevel@tonic-gate return (Z_OK); 19170Sstevel@tonic-gate } 19180Sstevel@tonic-gate if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) != 19190Sstevel@tonic-gate PO_SUCCESS) { 19200Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_open failed; " 19210Sstevel@tonic-gate "using default pool")); 19220Sstevel@tonic-gate pool_conf_free(poolconf); 19230Sstevel@tonic-gate return (Z_OK); 19240Sstevel@tonic-gate } 19250Sstevel@tonic-gate pool = pool_get_pool(poolconf, poolname); 19260Sstevel@tonic-gate (void) pool_conf_close(poolconf); 19270Sstevel@tonic-gate pool_conf_free(poolconf); 19280Sstevel@tonic-gate if (pool == NULL) { 19290Sstevel@tonic-gate zerror(gettext("WARNING: pool '%s' not found. " 19300Sstevel@tonic-gate "using default pool"), poolname); 19310Sstevel@tonic-gate } 19320Sstevel@tonic-gate 19330Sstevel@tonic-gate return (Z_OK); 19340Sstevel@tonic-gate } 19350Sstevel@tonic-gate 19360Sstevel@tonic-gate static int 1937823Sgjelinek verify_ipd(zone_dochandle_t handle) 1938823Sgjelinek { 1939823Sgjelinek int return_code = Z_OK; 1940823Sgjelinek struct zone_fstab fstab; 1941823Sgjelinek struct stat st; 1942823Sgjelinek char specdir[MAXPATHLEN]; 1943823Sgjelinek 1944823Sgjelinek if (zonecfg_setipdent(handle) != Z_OK) { 1945924Sgjelinek /* 1946924Sgjelinek * TRANSLATION_NOTE 1947924Sgjelinek * inherit-pkg-dirs is a literal that should not be translated. 1948924Sgjelinek */ 1949924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 1950823Sgjelinek "inherit-pkg-dirs: unable to enumerate mounts\n")); 1951823Sgjelinek return (Z_ERR); 1952823Sgjelinek } 1953823Sgjelinek while (zonecfg_getipdent(handle, &fstab) == Z_OK) { 1954823Sgjelinek /* 1955823Sgjelinek * Verify fs_dir exists. 1956823Sgjelinek */ 1957823Sgjelinek (void) snprintf(specdir, sizeof (specdir), "%s%s", 1958823Sgjelinek zonecfg_get_root(), fstab.zone_fs_dir); 1959823Sgjelinek if (stat(specdir, &st) != 0) { 1960924Sgjelinek /* 1961924Sgjelinek * TRANSLATION_NOTE 1962924Sgjelinek * inherit-pkg-dir is a literal that should not be 1963924Sgjelinek * translated. 1964924Sgjelinek */ 1965924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 1966823Sgjelinek "inherit-pkg-dir %s: %s\n"), 1967823Sgjelinek fstab.zone_fs_dir, strerror(errno)); 1968823Sgjelinek return_code = Z_ERR; 1969823Sgjelinek } 1970823Sgjelinek if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 1971924Sgjelinek /* 1972924Sgjelinek * TRANSLATION_NOTE 1973924Sgjelinek * inherit-pkg-dir and NFS are literals that should 1974924Sgjelinek * not be translated. 1975924Sgjelinek */ 1976823Sgjelinek (void) fprintf(stderr, gettext("cannot verify " 19771867Sgjelinek "inherit-pkg-dir %s: NFS mounted file system.\n" 19781867Sgjelinek "\tA local file system must be used.\n"), 1979823Sgjelinek fstab.zone_fs_dir); 1980823Sgjelinek return_code = Z_ERR; 1981823Sgjelinek } 1982823Sgjelinek } 1983823Sgjelinek (void) zonecfg_endipdent(handle); 1984823Sgjelinek 1985823Sgjelinek return (return_code); 1986823Sgjelinek } 1987823Sgjelinek 19881393Slling /* 19891867Sgjelinek * Verify that the special device/file system exists and is valid. 19901393Slling */ 19911393Slling static int 19921393Slling verify_fs_special(struct zone_fstab *fstab) 19931393Slling { 19941393Slling struct stat st; 19951393Slling 19961393Slling if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) 19971393Slling return (verify_fs_zfs(fstab)); 19981393Slling 19991393Slling if (stat(fstab->zone_fs_special, &st) != 0) { 20001397Slling (void) fprintf(stderr, gettext("could not verify fs " 20011393Slling "%s: could not access %s: %s\n"), fstab->zone_fs_dir, 20021393Slling fstab->zone_fs_special, strerror(errno)); 20031393Slling return (Z_ERR); 20041393Slling } 20051393Slling 20061393Slling if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 20071393Slling /* 20081393Slling * TRANSLATION_NOTE 20091393Slling * fs and NFS are literals that should 20101393Slling * not be translated. 20111393Slling */ 20121393Slling (void) fprintf(stderr, gettext("cannot verify " 20131867Sgjelinek "fs %s: NFS mounted file system.\n" 20141867Sgjelinek "\tA local file system must be used.\n"), 20151393Slling fstab->zone_fs_special); 20161393Slling return (Z_ERR); 20171393Slling } 20181393Slling 20191393Slling return (Z_OK); 20201393Slling } 20211393Slling 2022823Sgjelinek static int 20230Sstevel@tonic-gate verify_filesystems(zone_dochandle_t handle) 20240Sstevel@tonic-gate { 20250Sstevel@tonic-gate int return_code = Z_OK; 20260Sstevel@tonic-gate struct zone_fstab fstab; 20270Sstevel@tonic-gate char cmdbuf[MAXPATHLEN]; 20280Sstevel@tonic-gate struct stat st; 20290Sstevel@tonic-gate 20300Sstevel@tonic-gate /* 20310Sstevel@tonic-gate * No need to verify inherit-pkg-dir fs types, as their type is 20320Sstevel@tonic-gate * implicitly lofs, which is known. Therefore, the types are only 20331867Sgjelinek * verified for regular file systems below. 20340Sstevel@tonic-gate * 20350Sstevel@tonic-gate * Since the actual mount point is not known until the dependent mounts 20360Sstevel@tonic-gate * are performed, we don't attempt any path validation here: that will 20370Sstevel@tonic-gate * happen later when zoneadmd actually does the mounts. 20380Sstevel@tonic-gate */ 20390Sstevel@tonic-gate if (zonecfg_setfsent(handle) != Z_OK) { 20401867Sgjelinek (void) fprintf(stderr, gettext("could not verify file systems: " 20410Sstevel@tonic-gate "unable to enumerate mounts\n")); 20420Sstevel@tonic-gate return (Z_ERR); 20430Sstevel@tonic-gate } 20440Sstevel@tonic-gate while (zonecfg_getfsent(handle, &fstab) == Z_OK) { 20450Sstevel@tonic-gate if (!zonecfg_valid_fs_type(fstab.zone_fs_type)) { 20460Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 20470Sstevel@tonic-gate "type %s is not allowed.\n"), fstab.zone_fs_dir, 20480Sstevel@tonic-gate fstab.zone_fs_type); 20490Sstevel@tonic-gate return_code = Z_ERR; 20500Sstevel@tonic-gate goto next_fs; 20510Sstevel@tonic-gate } 20520Sstevel@tonic-gate /* 20530Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/mount exists. 20540Sstevel@tonic-gate */ 20550Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/mount", 20560Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 20570Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 20580Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 20590Sstevel@tonic-gate fstab.zone_fs_type); 20600Sstevel@tonic-gate return_code = Z_ERR; 20610Sstevel@tonic-gate goto next_fs; 20620Sstevel@tonic-gate } 20630Sstevel@tonic-gate if (stat(cmdbuf, &st) != 0) { 2064924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2065924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 20660Sstevel@tonic-gate cmdbuf, strerror(errno)); 20670Sstevel@tonic-gate return_code = Z_ERR; 20680Sstevel@tonic-gate goto next_fs; 20690Sstevel@tonic-gate } 20700Sstevel@tonic-gate if (!S_ISREG(st.st_mode)) { 2071924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2072924Sgjelinek "%s: %s is not a regular file\n"), 2073924Sgjelinek fstab.zone_fs_dir, cmdbuf); 20740Sstevel@tonic-gate return_code = Z_ERR; 20750Sstevel@tonic-gate goto next_fs; 20760Sstevel@tonic-gate } 20770Sstevel@tonic-gate /* 20780Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/fsck exists iff zone_fs_raw is 20790Sstevel@tonic-gate * set. 20800Sstevel@tonic-gate */ 20810Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/fsck", 20820Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 20830Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 20840Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 20850Sstevel@tonic-gate fstab.zone_fs_type); 20860Sstevel@tonic-gate return_code = Z_ERR; 20870Sstevel@tonic-gate goto next_fs; 20880Sstevel@tonic-gate } 20890Sstevel@tonic-gate if (fstab.zone_fs_raw[0] == '\0' && stat(cmdbuf, &st) == 0) { 2090924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2091924Sgjelinek "%s: must specify 'raw' device for %s " 20921867Sgjelinek "file systems\n"), 20930Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 20940Sstevel@tonic-gate return_code = Z_ERR; 20950Sstevel@tonic-gate goto next_fs; 20960Sstevel@tonic-gate } 20970Sstevel@tonic-gate if (fstab.zone_fs_raw[0] != '\0' && 20980Sstevel@tonic-gate (stat(cmdbuf, &st) != 0 || !S_ISREG(st.st_mode))) { 20990Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21000Sstevel@tonic-gate "'raw' device specified but " 21010Sstevel@tonic-gate "no fsck executable exists for %s\n"), 21020Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 21030Sstevel@tonic-gate return_code = Z_ERR; 21040Sstevel@tonic-gate goto next_fs; 21050Sstevel@tonic-gate } 21061393Slling 21071393Slling /* Verify fs_special. */ 21081393Slling if ((return_code = verify_fs_special(&fstab)) != Z_OK) 2109823Sgjelinek goto next_fs; 21101393Slling 21111393Slling /* Verify fs_raw. */ 2112823Sgjelinek if (fstab.zone_fs_raw[0] != '\0' && 2113823Sgjelinek stat(fstab.zone_fs_raw, &st) != 0) { 2114924Sgjelinek /* 2115924Sgjelinek * TRANSLATION_NOTE 2116924Sgjelinek * fs is a literal that should not be translated. 2117924Sgjelinek */ 2118924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2119924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 2120823Sgjelinek fstab.zone_fs_raw, strerror(errno)); 2121823Sgjelinek return_code = Z_ERR; 2122823Sgjelinek goto next_fs; 2123823Sgjelinek } 21240Sstevel@tonic-gate next_fs: 21250Sstevel@tonic-gate zonecfg_free_fs_option_list(fstab.zone_fs_options); 21260Sstevel@tonic-gate } 21270Sstevel@tonic-gate (void) zonecfg_endfsent(handle); 21280Sstevel@tonic-gate 21290Sstevel@tonic-gate return (return_code); 21300Sstevel@tonic-gate } 21310Sstevel@tonic-gate 21320Sstevel@tonic-gate static int 21331645Scomay verify_limitpriv(zone_dochandle_t handle) 21341645Scomay { 21351645Scomay char *privname = NULL; 21361645Scomay int err; 21371645Scomay priv_set_t *privs; 21381645Scomay 21391645Scomay if ((privs = priv_allocset()) == NULL) { 21401645Scomay zperror(gettext("failed to allocate privilege set"), B_FALSE); 21411645Scomay return (Z_NOMEM); 21421645Scomay } 21431645Scomay err = zonecfg_get_privset(handle, privs, &privname); 21441645Scomay switch (err) { 21451645Scomay case Z_OK: 21461645Scomay break; 21471645Scomay case Z_PRIV_PROHIBITED: 21481645Scomay (void) fprintf(stderr, gettext("privilege \"%s\" is not " 21491645Scomay "permitted within the zone's privilege set\n"), privname); 21501645Scomay break; 21511645Scomay case Z_PRIV_REQUIRED: 21521645Scomay (void) fprintf(stderr, gettext("required privilege \"%s\" is " 21531645Scomay "missing from the zone's privilege set\n"), privname); 21541645Scomay break; 21551645Scomay case Z_PRIV_UNKNOWN: 21561645Scomay (void) fprintf(stderr, gettext("unknown privilege \"%s\" " 21571645Scomay "specified in the zone's privilege set\n"), privname); 21581645Scomay break; 21591645Scomay default: 21601645Scomay zperror( 21611645Scomay gettext("failed to determine the zone's privilege set"), 21621645Scomay B_TRUE); 21631645Scomay break; 21641645Scomay } 21651645Scomay free(privname); 21661645Scomay priv_freeset(privs); 21671645Scomay return (err); 21681645Scomay } 21691645Scomay 21701915Sgjelinek static void 21711915Sgjelinek free_local_netifs(int if_cnt, struct net_if **if_list) 21721915Sgjelinek { 21731915Sgjelinek int i; 21741915Sgjelinek 21751915Sgjelinek for (i = 0; i < if_cnt; i++) { 21761915Sgjelinek free(if_list[i]->name); 21771915Sgjelinek free(if_list[i]); 21781915Sgjelinek } 21791915Sgjelinek free(if_list); 21801915Sgjelinek } 21811915Sgjelinek 21821915Sgjelinek /* 21831915Sgjelinek * Get a list of the network interfaces, along with their address families, 21841915Sgjelinek * that are plumbed in the global zone. See if_tcp(7p) for a description 21851915Sgjelinek * of the ioctls used here. 21861915Sgjelinek */ 21871915Sgjelinek static int 21881915Sgjelinek get_local_netifs(int *if_cnt, struct net_if ***if_list) 21891915Sgjelinek { 21901915Sgjelinek int s; 21911915Sgjelinek int i; 21921915Sgjelinek int res = Z_OK; 21931915Sgjelinek int space_needed; 21941915Sgjelinek int cnt = 0; 21951915Sgjelinek struct lifnum if_num; 21961915Sgjelinek struct lifconf if_conf; 21971915Sgjelinek struct lifreq *if_reqp; 21981915Sgjelinek char *if_buf; 21991915Sgjelinek struct net_if **local_ifs = NULL; 22001915Sgjelinek 22011915Sgjelinek *if_cnt = 0; 22021915Sgjelinek *if_list = NULL; 22031915Sgjelinek 22041915Sgjelinek if ((s = socket(SOCKET_AF(AF_INET), SOCK_DGRAM, 0)) < 0) 22051915Sgjelinek return (Z_ERR); 22061915Sgjelinek 22071915Sgjelinek /* 22081915Sgjelinek * Come back here in the unlikely event that the number of interfaces 22091915Sgjelinek * increases between the time we get the count and the time we do the 22101915Sgjelinek * SIOCGLIFCONF ioctl. 22111915Sgjelinek */ 22121915Sgjelinek retry: 22131915Sgjelinek /* Get the number of interfaces. */ 22141915Sgjelinek if_num.lifn_family = AF_UNSPEC; 22151915Sgjelinek if_num.lifn_flags = LIFC_NOXMIT; 22161915Sgjelinek if (ioctl(s, SIOCGLIFNUM, &if_num) < 0) { 22171915Sgjelinek (void) close(s); 22181915Sgjelinek return (Z_ERR); 22191915Sgjelinek } 22201915Sgjelinek 22211915Sgjelinek /* Get the interface configuration list. */ 22221915Sgjelinek space_needed = if_num.lifn_count * sizeof (struct lifreq); 22231915Sgjelinek if ((if_buf = malloc(space_needed)) == NULL) { 22241915Sgjelinek (void) close(s); 22251915Sgjelinek return (Z_ERR); 22261915Sgjelinek } 22271915Sgjelinek if_conf.lifc_family = AF_UNSPEC; 22281915Sgjelinek if_conf.lifc_flags = LIFC_NOXMIT; 22291915Sgjelinek if_conf.lifc_len = space_needed; 22301915Sgjelinek if_conf.lifc_buf = if_buf; 22311915Sgjelinek if (ioctl(s, SIOCGLIFCONF, &if_conf) < 0) { 22321915Sgjelinek free(if_buf); 22331915Sgjelinek /* 22341915Sgjelinek * SIOCGLIFCONF returns EINVAL if the buffer we passed in is 22351915Sgjelinek * too small. In this case go back and get the new if cnt. 22361915Sgjelinek */ 22371915Sgjelinek if (errno == EINVAL) 22381915Sgjelinek goto retry; 22391915Sgjelinek 22401915Sgjelinek (void) close(s); 22411915Sgjelinek return (Z_ERR); 22421915Sgjelinek } 22431915Sgjelinek (void) close(s); 22441915Sgjelinek 22451915Sgjelinek /* Get the name and address family for each interface. */ 22461915Sgjelinek if_reqp = if_conf.lifc_req; 22471915Sgjelinek for (i = 0; i < (if_conf.lifc_len / sizeof (struct lifreq)); i++) { 22481915Sgjelinek struct net_if **p; 22491915Sgjelinek struct lifreq req; 22501915Sgjelinek 22511915Sgjelinek if (strcmp(LOOPBACK_IF, if_reqp->lifr_name) == 0) { 22521915Sgjelinek if_reqp++; 22531915Sgjelinek continue; 22541915Sgjelinek } 22551915Sgjelinek 22561915Sgjelinek if ((s = socket(SOCKET_AF(if_reqp->lifr_addr.ss_family), 22571915Sgjelinek SOCK_DGRAM, 0)) == -1) { 22581915Sgjelinek res = Z_ERR; 22591915Sgjelinek break; 22601915Sgjelinek } 22611915Sgjelinek 22621915Sgjelinek (void) strncpy(req.lifr_name, if_reqp->lifr_name, 22631915Sgjelinek sizeof (req.lifr_name)); 22641915Sgjelinek if (ioctl(s, SIOCGLIFADDR, &req) < 0) { 22651915Sgjelinek (void) close(s); 22661915Sgjelinek if_reqp++; 22671915Sgjelinek continue; 22681915Sgjelinek } 22691915Sgjelinek 22701915Sgjelinek if ((p = (struct net_if **)realloc(local_ifs, 22711915Sgjelinek sizeof (struct net_if *) * (cnt + 1))) == NULL) { 22721915Sgjelinek res = Z_ERR; 22731915Sgjelinek break; 22741915Sgjelinek } 22751915Sgjelinek local_ifs = p; 22761915Sgjelinek 22771915Sgjelinek if ((local_ifs[cnt] = malloc(sizeof (struct net_if))) == NULL) { 22781915Sgjelinek res = Z_ERR; 22791915Sgjelinek break; 22801915Sgjelinek } 22811915Sgjelinek 22821915Sgjelinek if ((local_ifs[cnt]->name = strdup(if_reqp->lifr_name)) 22831915Sgjelinek == NULL) { 22841915Sgjelinek free(local_ifs[cnt]); 22851915Sgjelinek res = Z_ERR; 22861915Sgjelinek break; 22871915Sgjelinek } 22881915Sgjelinek local_ifs[cnt]->af = req.lifr_addr.ss_family; 22891915Sgjelinek cnt++; 22901915Sgjelinek 22911915Sgjelinek (void) close(s); 22921915Sgjelinek if_reqp++; 22931915Sgjelinek } 22941915Sgjelinek 22951915Sgjelinek free(if_buf); 22961915Sgjelinek 22971915Sgjelinek if (res != Z_OK) { 22981915Sgjelinek free_local_netifs(cnt, local_ifs); 22991915Sgjelinek } else { 23001915Sgjelinek *if_cnt = cnt; 23011915Sgjelinek *if_list = local_ifs; 23021915Sgjelinek } 23031915Sgjelinek 23041915Sgjelinek return (res); 23051915Sgjelinek } 23061915Sgjelinek 23071915Sgjelinek static char * 23081915Sgjelinek af2str(int af) 23091915Sgjelinek { 23101915Sgjelinek switch (af) { 23111915Sgjelinek case AF_INET: 23121915Sgjelinek return ("IPv4"); 23131915Sgjelinek case AF_INET6: 23141915Sgjelinek return ("IPv6"); 23151915Sgjelinek default: 23161915Sgjelinek return ("Unknown"); 23171915Sgjelinek } 23181915Sgjelinek } 23191915Sgjelinek 23201915Sgjelinek /* 23211915Sgjelinek * Cross check the network interface name and address family with the 23221915Sgjelinek * interfaces that are set up in the global zone so that we can print the 23231915Sgjelinek * appropriate error message. 23241915Sgjelinek */ 23251915Sgjelinek static void 23261915Sgjelinek print_net_err(char *phys, char *addr, int af, char *msg) 23271915Sgjelinek { 23281915Sgjelinek int i; 23291915Sgjelinek int local_if_cnt = 0; 23301915Sgjelinek struct net_if **local_ifs = NULL; 23311915Sgjelinek boolean_t found_if = B_FALSE; 23321915Sgjelinek boolean_t found_af = B_FALSE; 23331915Sgjelinek 23341915Sgjelinek if (get_local_netifs(&local_if_cnt, &local_ifs) != Z_OK) { 23351915Sgjelinek (void) fprintf(stderr, 23361915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 23371915Sgjelinek "net", "address", addr, "physical", phys, msg); 23381915Sgjelinek return; 23391915Sgjelinek } 23401915Sgjelinek 23411915Sgjelinek for (i = 0; i < local_if_cnt; i++) { 23421915Sgjelinek if (strcmp(phys, local_ifs[i]->name) == 0) { 23431915Sgjelinek found_if = B_TRUE; 23441915Sgjelinek if (af == local_ifs[i]->af) { 23451915Sgjelinek found_af = B_TRUE; 23461915Sgjelinek break; 23471915Sgjelinek } 23481915Sgjelinek } 23491915Sgjelinek } 23501915Sgjelinek 23511915Sgjelinek free_local_netifs(local_if_cnt, local_ifs); 23521915Sgjelinek 23531915Sgjelinek if (!found_if) { 23541915Sgjelinek (void) fprintf(stderr, 23551915Sgjelinek gettext("could not verify %s %s=%s\n\t" 23561915Sgjelinek "network interface %s is not plumbed in the global zone\n"), 23571915Sgjelinek "net", "physical", phys, phys); 23581915Sgjelinek return; 23591915Sgjelinek } 23601915Sgjelinek 23611915Sgjelinek /* 23621915Sgjelinek * Print this error if we were unable to find the address family 23631915Sgjelinek * for this interface. If the af variable is not initialized to 23641915Sgjelinek * to something meaningful by the caller (not AF_UNSPEC) then we 23651915Sgjelinek * also skip this message since it wouldn't be informative. 23661915Sgjelinek */ 23671915Sgjelinek if (!found_af && af != AF_UNSPEC) { 23681915Sgjelinek (void) fprintf(stderr, 23691915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\tthe %s address " 23701915Sgjelinek "family is not configured on this interface in the\n\t" 23711915Sgjelinek "global zone\n"), 23721915Sgjelinek "net", "address", addr, "physical", phys, af2str(af)); 23731915Sgjelinek return; 23741915Sgjelinek } 23751915Sgjelinek 23761915Sgjelinek (void) fprintf(stderr, 23771915Sgjelinek gettext("could not verify %s %s=%s %s=%s\n\t%s\n"), 23781915Sgjelinek "net", "address", addr, "physical", phys, msg); 23791915Sgjelinek } 23801915Sgjelinek 23811645Scomay static int 23822078Sgjelinek verify_handle(int cmd_num, zone_dochandle_t handle) 23830Sstevel@tonic-gate { 23840Sstevel@tonic-gate struct zone_nwiftab nwiftab; 23850Sstevel@tonic-gate int return_code = Z_OK; 23860Sstevel@tonic-gate int err; 2387766Scarlsonj boolean_t in_alt_root; 23880Sstevel@tonic-gate 2389766Scarlsonj in_alt_root = zonecfg_in_alt_root(); 2390766Scarlsonj if (in_alt_root) 2391766Scarlsonj goto no_net; 2392766Scarlsonj 23930Sstevel@tonic-gate if ((err = zonecfg_setnwifent(handle)) != Z_OK) { 23940Sstevel@tonic-gate errno = err; 23950Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 23960Sstevel@tonic-gate zonecfg_fini_handle(handle); 23970Sstevel@tonic-gate return (Z_ERR); 23980Sstevel@tonic-gate } 23990Sstevel@tonic-gate while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { 24000Sstevel@tonic-gate struct lifreq lifr; 24011915Sgjelinek sa_family_t af = AF_UNSPEC; 24020Sstevel@tonic-gate int so, res; 24030Sstevel@tonic-gate 24040Sstevel@tonic-gate /* skip any loopback interfaces */ 24050Sstevel@tonic-gate if (strcmp(nwiftab.zone_nwif_physical, "lo0") == 0) 24060Sstevel@tonic-gate continue; 24070Sstevel@tonic-gate if ((res = zonecfg_valid_net_address(nwiftab.zone_nwif_address, 24080Sstevel@tonic-gate &lifr)) != Z_OK) { 24091915Sgjelinek print_net_err(nwiftab.zone_nwif_physical, 24101915Sgjelinek nwiftab.zone_nwif_address, af, 24111915Sgjelinek zonecfg_strerror(res)); 24120Sstevel@tonic-gate return_code = Z_ERR; 24130Sstevel@tonic-gate continue; 24140Sstevel@tonic-gate } 24150Sstevel@tonic-gate af = lifr.lifr_addr.ss_family; 24160Sstevel@tonic-gate (void) memset(&lifr, 0, sizeof (lifr)); 24170Sstevel@tonic-gate (void) strlcpy(lifr.lifr_name, nwiftab.zone_nwif_physical, 24180Sstevel@tonic-gate sizeof (lifr.lifr_name)); 24190Sstevel@tonic-gate lifr.lifr_addr.ss_family = af; 24200Sstevel@tonic-gate if ((so = socket(af, SOCK_DGRAM, 0)) < 0) { 24210Sstevel@tonic-gate (void) fprintf(stderr, gettext("could not verify %s " 24220Sstevel@tonic-gate "%s=%s %s=%s: could not get socket: %s\n"), "net", 24230Sstevel@tonic-gate "address", nwiftab.zone_nwif_address, "physical", 24240Sstevel@tonic-gate nwiftab.zone_nwif_physical, strerror(errno)); 24250Sstevel@tonic-gate return_code = Z_ERR; 24260Sstevel@tonic-gate continue; 24270Sstevel@tonic-gate } 24281915Sgjelinek if (ioctl(so, SIOCGLIFFLAGS, &lifr) < 0) { 24291915Sgjelinek print_net_err(nwiftab.zone_nwif_physical, 24301915Sgjelinek nwiftab.zone_nwif_address, af, 24310Sstevel@tonic-gate strerror(errno)); 24320Sstevel@tonic-gate return_code = Z_ERR; 24330Sstevel@tonic-gate } 24340Sstevel@tonic-gate (void) close(so); 24350Sstevel@tonic-gate } 24360Sstevel@tonic-gate (void) zonecfg_endnwifent(handle); 2437766Scarlsonj no_net: 24380Sstevel@tonic-gate 24391931Sgjelinek /* verify that lofs has not been excluded from the kernel */ 24402078Sgjelinek if (!(cmd_num == CMD_DETACH || cmd_num == CMD_ATTACH || 24412078Sgjelinek cmd_num == CMD_MOVE || cmd_num == CMD_CLONE) && 24422078Sgjelinek modctl(MODLOAD, 1, "fs/lofs", NULL) != 0) { 24431931Sgjelinek if (errno == ENXIO) 24441931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 24451931Sgjelinek "lofs(7FS): possibly excluded in /etc/system\n")); 24461931Sgjelinek else 24471931Sgjelinek (void) fprintf(stderr, gettext("could not verify " 24481931Sgjelinek "lofs(7FS): %s\n"), strerror(errno)); 24491931Sgjelinek return_code = Z_ERR; 24501931Sgjelinek } 24511931Sgjelinek 24520Sstevel@tonic-gate if (verify_filesystems(handle) != Z_OK) 24530Sstevel@tonic-gate return_code = Z_ERR; 2454823Sgjelinek if (verify_ipd(handle) != Z_OK) 2455823Sgjelinek return_code = Z_ERR; 2456766Scarlsonj if (!in_alt_root && verify_rctls(handle) != Z_OK) 24570Sstevel@tonic-gate return_code = Z_ERR; 2458766Scarlsonj if (!in_alt_root && verify_pool(handle) != Z_OK) 24590Sstevel@tonic-gate return_code = Z_ERR; 2460789Sahrens if (!in_alt_root && verify_datasets(handle) != Z_OK) 2461789Sahrens return_code = Z_ERR; 24621645Scomay 24631645Scomay /* 24641645Scomay * As the "mount" command is used for patching/upgrading of zones 24651645Scomay * or other maintenance processes, the zone's privilege set is not 24661645Scomay * checked in this case. Instead, the default, safe set of 24671645Scomay * privileges will be used when this zone is created in the 24681645Scomay * kernel. 24691645Scomay */ 24701645Scomay if (!in_alt_root && cmd_num != CMD_MOUNT && 24711645Scomay verify_limitpriv(handle) != Z_OK) 24721645Scomay return_code = Z_ERR; 24732078Sgjelinek 24742078Sgjelinek return (return_code); 24752078Sgjelinek } 24762078Sgjelinek 24772078Sgjelinek static int 24782078Sgjelinek verify_details(int cmd_num) 24792078Sgjelinek { 24802078Sgjelinek zone_dochandle_t handle; 24812078Sgjelinek char zonepath[MAXPATHLEN], checkpath[MAXPATHLEN]; 24822078Sgjelinek int return_code = Z_OK; 24832078Sgjelinek int err; 24842078Sgjelinek 24852078Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 24862078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 24872078Sgjelinek return (Z_ERR); 24882078Sgjelinek } 24892078Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 24902078Sgjelinek errno = err; 24912078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 24922078Sgjelinek zonecfg_fini_handle(handle); 24932078Sgjelinek return (Z_ERR); 24942078Sgjelinek } 24952078Sgjelinek if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) != 24962078Sgjelinek Z_OK) { 24972078Sgjelinek errno = err; 24982078Sgjelinek zperror(cmd_to_str(cmd_num), B_TRUE); 24992078Sgjelinek zonecfg_fini_handle(handle); 25002078Sgjelinek return (Z_ERR); 25012078Sgjelinek } 25022078Sgjelinek /* 25032078Sgjelinek * zonecfg_get_zonepath() gets its data from the XML repository. 25042078Sgjelinek * Verify this against the index file, which is checked first by 25052078Sgjelinek * zone_get_zonepath(). If they don't match, bail out. 25062078Sgjelinek */ 25072078Sgjelinek if ((err = zone_get_zonepath(target_zone, checkpath, 25082078Sgjelinek sizeof (checkpath))) != Z_OK) { 25092078Sgjelinek errno = err; 25102078Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 25112078Sgjelinek return (Z_ERR); 25122078Sgjelinek } 25132078Sgjelinek if (strcmp(zonepath, checkpath) != 0) { 25142078Sgjelinek /* 25152078Sgjelinek * TRANSLATION_NOTE 25162078Sgjelinek * XML and zonepath are literals that should not be translated. 25172078Sgjelinek */ 25182078Sgjelinek (void) fprintf(stderr, gettext("The XML repository has " 25192078Sgjelinek "zonepath '%s',\nbut the index file has zonepath '%s'.\n" 25202078Sgjelinek "These must match, so fix the incorrect entry.\n"), 25212078Sgjelinek zonepath, checkpath); 25222078Sgjelinek return (Z_ERR); 25232078Sgjelinek } 25242078Sgjelinek if (validate_zonepath(zonepath, cmd_num) != Z_OK) { 25252078Sgjelinek (void) fprintf(stderr, gettext("could not verify zonepath %s " 25262078Sgjelinek "because of the above errors.\n"), zonepath); 25272078Sgjelinek return_code = Z_ERR; 25282078Sgjelinek } 25292078Sgjelinek 25302078Sgjelinek if (verify_handle(cmd_num, handle) != Z_OK) 25312078Sgjelinek return_code = Z_ERR; 25322078Sgjelinek 25330Sstevel@tonic-gate zonecfg_fini_handle(handle); 25340Sstevel@tonic-gate if (return_code == Z_ERR) 25350Sstevel@tonic-gate (void) fprintf(stderr, 25360Sstevel@tonic-gate gettext("%s: zone %s failed to verify\n"), 25370Sstevel@tonic-gate execname, target_zone); 25380Sstevel@tonic-gate return (return_code); 25390Sstevel@tonic-gate } 25400Sstevel@tonic-gate 25410Sstevel@tonic-gate static int 25420Sstevel@tonic-gate verify_func(int argc, char *argv[]) 25430Sstevel@tonic-gate { 25440Sstevel@tonic-gate int arg; 25450Sstevel@tonic-gate 25460Sstevel@tonic-gate optind = 0; 25470Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 25480Sstevel@tonic-gate switch (arg) { 25490Sstevel@tonic-gate case '?': 25500Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 25510Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 25520Sstevel@tonic-gate default: 25530Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 25540Sstevel@tonic-gate return (Z_USAGE); 25550Sstevel@tonic-gate } 25560Sstevel@tonic-gate } 25570Sstevel@tonic-gate if (argc > optind) { 25580Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 25590Sstevel@tonic-gate return (Z_USAGE); 25600Sstevel@tonic-gate } 25610Sstevel@tonic-gate if (sanity_check(target_zone, CMD_VERIFY, B_FALSE, B_FALSE) != Z_OK) 25620Sstevel@tonic-gate return (Z_ERR); 25630Sstevel@tonic-gate return (verify_details(CMD_VERIFY)); 25640Sstevel@tonic-gate } 25650Sstevel@tonic-gate 25660Sstevel@tonic-gate #define LUCREATEZONE "/usr/lib/lu/lucreatezone" 25670Sstevel@tonic-gate 25680Sstevel@tonic-gate static int 25690Sstevel@tonic-gate install_func(int argc, char *argv[]) 25700Sstevel@tonic-gate { 25710Sstevel@tonic-gate /* 9: "exec " and " -z " */ 25720Sstevel@tonic-gate char cmdbuf[sizeof (LUCREATEZONE) + ZONENAME_MAX + 9]; 25730Sstevel@tonic-gate int lockfd; 25740Sstevel@tonic-gate int err, arg; 25750Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 25760Sstevel@tonic-gate int status; 25771867Sgjelinek boolean_t nodataset = B_FALSE; 25780Sstevel@tonic-gate 2579766Scarlsonj if (zonecfg_in_alt_root()) { 2580766Scarlsonj zerror(gettext("cannot install zone in alternate root")); 2581766Scarlsonj return (Z_ERR); 2582766Scarlsonj } 2583766Scarlsonj 25840Sstevel@tonic-gate optind = 0; 25851867Sgjelinek if ((arg = getopt(argc, argv, "?x:")) != EOF) { 25860Sstevel@tonic-gate switch (arg) { 25870Sstevel@tonic-gate case '?': 25880Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 25890Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 25901867Sgjelinek case 'x': 25911867Sgjelinek if (strcmp(optarg, "nodataset") != 0) { 25921867Sgjelinek sub_usage(SHELP_INSTALL, CMD_INSTALL); 25931867Sgjelinek return (Z_USAGE); 25941867Sgjelinek } 25951867Sgjelinek nodataset = B_TRUE; 25961867Sgjelinek break; 25970Sstevel@tonic-gate default: 25980Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 25990Sstevel@tonic-gate return (Z_USAGE); 26000Sstevel@tonic-gate } 26010Sstevel@tonic-gate } 26020Sstevel@tonic-gate if (argc > optind) { 26030Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 26040Sstevel@tonic-gate return (Z_USAGE); 26050Sstevel@tonic-gate } 26060Sstevel@tonic-gate if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE) != Z_OK) 26070Sstevel@tonic-gate return (Z_ERR); 26080Sstevel@tonic-gate if (verify_details(CMD_INSTALL) != Z_OK) 26090Sstevel@tonic-gate return (Z_ERR); 26100Sstevel@tonic-gate 26110Sstevel@tonic-gate if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 26120Sstevel@tonic-gate zerror(gettext("another %s may have an operation in progress."), 26131300Sgjelinek "zoneadm"); 26140Sstevel@tonic-gate return (Z_ERR); 26150Sstevel@tonic-gate } 26160Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 26170Sstevel@tonic-gate if (err != Z_OK) { 26180Sstevel@tonic-gate errno = err; 26190Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 26200Sstevel@tonic-gate goto done; 26210Sstevel@tonic-gate } 26220Sstevel@tonic-gate 26230Sstevel@tonic-gate /* 26240Sstevel@tonic-gate * According to the Application Packaging Developer's Guide, a 26250Sstevel@tonic-gate * "checkinstall" script when included in a package is executed as 26260Sstevel@tonic-gate * the user "install", if such a user exists, or by the user 26270Sstevel@tonic-gate * "nobody". In order to support this dubious behavior, the path 26280Sstevel@tonic-gate * to the zone being constructed is opened up during the life of 26290Sstevel@tonic-gate * the command laying down the zone's root file system. Once this 26300Sstevel@tonic-gate * has completed, regardless of whether it was successful, the 26310Sstevel@tonic-gate * path to the zone is again restricted. 26320Sstevel@tonic-gate */ 26330Sstevel@tonic-gate if ((err = zone_get_zonepath(target_zone, zonepath, 26340Sstevel@tonic-gate sizeof (zonepath))) != Z_OK) { 26350Sstevel@tonic-gate errno = err; 26360Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 26370Sstevel@tonic-gate goto done; 26380Sstevel@tonic-gate } 26391867Sgjelinek 26401867Sgjelinek if (!nodataset) 26411867Sgjelinek create_zfs_zonepath(zonepath); 26421867Sgjelinek 26430Sstevel@tonic-gate if (chmod(zonepath, DEFAULT_DIR_MODE) != 0) { 26440Sstevel@tonic-gate zperror(zonepath, B_FALSE); 26450Sstevel@tonic-gate err = Z_ERR; 26460Sstevel@tonic-gate goto done; 26470Sstevel@tonic-gate } 26480Sstevel@tonic-gate 26490Sstevel@tonic-gate /* 26500Sstevel@tonic-gate * "exec" the command so that the returned status is that of 26510Sstevel@tonic-gate * LUCREATEZONE and not the shell. 26520Sstevel@tonic-gate */ 26530Sstevel@tonic-gate (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " LUCREATEZONE " -z %s", 26540Sstevel@tonic-gate target_zone); 26550Sstevel@tonic-gate status = do_subproc(cmdbuf); 26560Sstevel@tonic-gate if (chmod(zonepath, S_IRWXU) != 0) { 26570Sstevel@tonic-gate zperror(zonepath, B_FALSE); 26580Sstevel@tonic-gate err = Z_ERR; 26590Sstevel@tonic-gate goto done; 26600Sstevel@tonic-gate } 26610Sstevel@tonic-gate if ((err = subproc_status(LUCREATEZONE, status)) != Z_OK) 26620Sstevel@tonic-gate goto done; 26630Sstevel@tonic-gate 26640Sstevel@tonic-gate if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 26650Sstevel@tonic-gate errno = err; 26660Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 26670Sstevel@tonic-gate goto done; 26680Sstevel@tonic-gate } 26690Sstevel@tonic-gate 26700Sstevel@tonic-gate done: 26710Sstevel@tonic-gate release_lock_file(lockfd); 26720Sstevel@tonic-gate return ((err == Z_OK) ? Z_OK : Z_ERR); 26730Sstevel@tonic-gate } 26740Sstevel@tonic-gate 26750Sstevel@tonic-gate /* 26761300Sgjelinek * Check that the inherited pkg dirs are the same for the clone and its source. 26771300Sgjelinek * The easiest way to do that is check that the list of ipds is the same 26781300Sgjelinek * by matching each one against the other. This algorithm should be fine since 26791300Sgjelinek * the list of ipds should not be that long. 26801300Sgjelinek */ 26811300Sgjelinek static int 26821300Sgjelinek valid_ipd_clone(zone_dochandle_t s_handle, char *source_zone, 26831300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 26841300Sgjelinek { 26851300Sgjelinek int err; 26861300Sgjelinek int res = Z_OK; 26871300Sgjelinek int s_cnt = 0; 26881300Sgjelinek int t_cnt = 0; 26891300Sgjelinek struct zone_fstab s_fstab; 26901300Sgjelinek struct zone_fstab t_fstab; 26911300Sgjelinek 26921300Sgjelinek /* 26931300Sgjelinek * First check the source of the clone against the target. 26941300Sgjelinek */ 26951300Sgjelinek if ((err = zonecfg_setipdent(s_handle)) != Z_OK) { 26961300Sgjelinek errno = err; 26971300Sgjelinek zperror2(source_zone, gettext("could not enumerate " 26981300Sgjelinek "inherit-pkg-dirs")); 26991300Sgjelinek return (Z_ERR); 27001300Sgjelinek } 27011300Sgjelinek 27021300Sgjelinek while (zonecfg_getipdent(s_handle, &s_fstab) == Z_OK) { 27031300Sgjelinek boolean_t match = B_FALSE; 27041300Sgjelinek 27051300Sgjelinek s_cnt++; 27061300Sgjelinek 27071300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 27081300Sgjelinek errno = err; 27091300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 27101300Sgjelinek "inherit-pkg-dirs")); 27111300Sgjelinek (void) zonecfg_endipdent(s_handle); 27121300Sgjelinek return (Z_ERR); 27131300Sgjelinek } 27141300Sgjelinek 27151300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) { 27161300Sgjelinek if (strcmp(s_fstab.zone_fs_dir, t_fstab.zone_fs_dir) 27171300Sgjelinek == 0) { 27181300Sgjelinek match = B_TRUE; 27191300Sgjelinek break; 27201300Sgjelinek } 27211300Sgjelinek } 27221300Sgjelinek (void) zonecfg_endipdent(t_handle); 27231300Sgjelinek 27241300Sgjelinek if (!match) { 27251300Sgjelinek (void) fprintf(stderr, gettext("inherit-pkg-dir " 27261300Sgjelinek "'%s' is not configured in zone %s.\n"), 27271300Sgjelinek s_fstab.zone_fs_dir, target_zone); 27281300Sgjelinek res = Z_ERR; 27291300Sgjelinek } 27301300Sgjelinek } 27311300Sgjelinek 27321300Sgjelinek (void) zonecfg_endipdent(s_handle); 27331300Sgjelinek 27341300Sgjelinek /* skip the next check if we already have errors */ 27351300Sgjelinek if (res == Z_ERR) 27361300Sgjelinek return (res); 27371300Sgjelinek 27381300Sgjelinek /* 27391300Sgjelinek * Now check the number of ipds in the target so we can verify 27401300Sgjelinek * that the source is not a subset of the target. 27411300Sgjelinek */ 27421300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 27431300Sgjelinek errno = err; 27441300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 27451300Sgjelinek "inherit-pkg-dirs")); 27461300Sgjelinek return (Z_ERR); 27471300Sgjelinek } 27481300Sgjelinek 27491300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) 27501300Sgjelinek t_cnt++; 27511300Sgjelinek 27521300Sgjelinek (void) zonecfg_endipdent(t_handle); 27531300Sgjelinek 27541300Sgjelinek if (t_cnt != s_cnt) { 27551300Sgjelinek (void) fprintf(stderr, gettext("Zone %s is configured " 27561300Sgjelinek "with inherit-pkg-dirs that are not configured in zone " 27571300Sgjelinek "%s.\n"), target_zone, source_zone); 27581300Sgjelinek res = Z_ERR; 27591300Sgjelinek } 27601300Sgjelinek 27611300Sgjelinek return (res); 27621300Sgjelinek } 27631300Sgjelinek 27641300Sgjelinek static void 27651300Sgjelinek warn_dev_match(zone_dochandle_t s_handle, char *source_zone, 27661300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 27671300Sgjelinek { 27681300Sgjelinek int err; 27691300Sgjelinek struct zone_devtab s_devtab; 27701300Sgjelinek struct zone_devtab t_devtab; 27711300Sgjelinek 27721300Sgjelinek if ((err = zonecfg_setdevent(t_handle)) != Z_OK) { 27731300Sgjelinek errno = err; 27741300Sgjelinek zperror2(target_zone, gettext("could not enumerate devices")); 27751300Sgjelinek return; 27761300Sgjelinek } 27771300Sgjelinek 27781300Sgjelinek while (zonecfg_getdevent(t_handle, &t_devtab) == Z_OK) { 27791300Sgjelinek if ((err = zonecfg_setdevent(s_handle)) != Z_OK) { 27801300Sgjelinek errno = err; 27811300Sgjelinek zperror2(source_zone, 27821300Sgjelinek gettext("could not enumerate devices")); 27831300Sgjelinek (void) zonecfg_enddevent(t_handle); 27841300Sgjelinek return; 27851300Sgjelinek } 27861300Sgjelinek 27871300Sgjelinek while (zonecfg_getdevent(s_handle, &s_devtab) == Z_OK) { 27881300Sgjelinek /* 27891300Sgjelinek * Use fnmatch to catch the case where wildcards 27901300Sgjelinek * were used in one zone and the other has an 27911300Sgjelinek * explicit entry (e.g. /dev/dsk/c0t0d0s6 vs. 27921300Sgjelinek * /dev/\*dsk/c0t0d0s6). 27931300Sgjelinek */ 27941300Sgjelinek if (fnmatch(t_devtab.zone_dev_match, 27951300Sgjelinek s_devtab.zone_dev_match, FNM_PATHNAME) == 0 || 27961300Sgjelinek fnmatch(s_devtab.zone_dev_match, 27971300Sgjelinek t_devtab.zone_dev_match, FNM_PATHNAME) == 0) { 27981300Sgjelinek (void) fprintf(stderr, 27991300Sgjelinek gettext("WARNING: device '%s' " 28001300Sgjelinek "is configured in both zones.\n"), 28011300Sgjelinek t_devtab.zone_dev_match); 28021300Sgjelinek break; 28031300Sgjelinek } 28041300Sgjelinek } 28051300Sgjelinek (void) zonecfg_enddevent(s_handle); 28061300Sgjelinek } 28071300Sgjelinek 28081300Sgjelinek (void) zonecfg_enddevent(t_handle); 28091300Sgjelinek } 28101300Sgjelinek 28111300Sgjelinek /* 28121300Sgjelinek * Check if the specified mount option (opt) is contained within the 28131300Sgjelinek * options string. 28141300Sgjelinek */ 28151300Sgjelinek static boolean_t 28161300Sgjelinek opt_match(char *opt, char *options) 28171300Sgjelinek { 28181300Sgjelinek char *p; 28191300Sgjelinek char *lastp; 28201300Sgjelinek 28211300Sgjelinek if ((p = strtok_r(options, ",", &lastp)) != NULL) { 28221300Sgjelinek if (strcmp(p, opt) == 0) 28231300Sgjelinek return (B_TRUE); 28241300Sgjelinek while ((p = strtok_r(NULL, ",", &lastp)) != NULL) { 28251300Sgjelinek if (strcmp(p, opt) == 0) 28261300Sgjelinek return (B_TRUE); 28271300Sgjelinek } 28281300Sgjelinek } 28291300Sgjelinek 28301300Sgjelinek return (B_FALSE); 28311300Sgjelinek } 28321300Sgjelinek 28331867Sgjelinek #define RW_LOFS "WARNING: read-write lofs file system on '%s' is configured " \ 28341300Sgjelinek "in both zones.\n" 28351300Sgjelinek 28361300Sgjelinek static void 28371300Sgjelinek print_fs_warnings(struct zone_fstab *s_fstab, struct zone_fstab *t_fstab) 28381300Sgjelinek { 28391300Sgjelinek /* 28401300Sgjelinek * It is ok to have shared lofs mounted fs but we want to warn if 28411300Sgjelinek * either is rw since this will effect the other zone. 28421300Sgjelinek */ 28431300Sgjelinek if (strcmp(t_fstab->zone_fs_type, "lofs") == 0) { 28441300Sgjelinek zone_fsopt_t *optp; 28451300Sgjelinek 28461300Sgjelinek /* The default is rw so no options means rw */ 28471300Sgjelinek if (t_fstab->zone_fs_options == NULL || 28481300Sgjelinek s_fstab->zone_fs_options == NULL) { 28491300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 28501300Sgjelinek t_fstab->zone_fs_special); 28511300Sgjelinek return; 28521300Sgjelinek } 28531300Sgjelinek 28541300Sgjelinek for (optp = s_fstab->zone_fs_options; optp != NULL; 28551300Sgjelinek optp = optp->zone_fsopt_next) { 28561300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 28571300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 28581300Sgjelinek s_fstab->zone_fs_special); 28591300Sgjelinek return; 28601300Sgjelinek } 28611300Sgjelinek } 28621300Sgjelinek 28631300Sgjelinek for (optp = t_fstab->zone_fs_options; optp != NULL; 28641300Sgjelinek optp = optp->zone_fsopt_next) { 28651300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 28661300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 28671300Sgjelinek t_fstab->zone_fs_special); 28681300Sgjelinek return; 28691300Sgjelinek } 28701300Sgjelinek } 28711300Sgjelinek 28721300Sgjelinek return; 28731300Sgjelinek } 28741300Sgjelinek 28751300Sgjelinek /* 28761300Sgjelinek * TRANSLATION_NOTE 28771867Sgjelinek * The first variable is the file system type and the second is 28781867Sgjelinek * the file system special device. For example, 28791867Sgjelinek * WARNING: ufs file system on '/dev/dsk/c0t0d0s0' ... 28801300Sgjelinek */ 28811867Sgjelinek (void) fprintf(stderr, gettext("WARNING: %s file system on '%s' " 28821300Sgjelinek "is configured in both zones.\n"), t_fstab->zone_fs_type, 28831300Sgjelinek t_fstab->zone_fs_special); 28841300Sgjelinek } 28851300Sgjelinek 28861300Sgjelinek static void 28871300Sgjelinek warn_fs_match(zone_dochandle_t s_handle, char *source_zone, 28881300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 28891300Sgjelinek { 28901300Sgjelinek int err; 28911300Sgjelinek struct zone_fstab s_fstab; 28921300Sgjelinek struct zone_fstab t_fstab; 28931300Sgjelinek 28941300Sgjelinek if ((err = zonecfg_setfsent(t_handle)) != Z_OK) { 28951300Sgjelinek errno = err; 28961300Sgjelinek zperror2(target_zone, 28971867Sgjelinek gettext("could not enumerate file systems")); 28981300Sgjelinek return; 28991300Sgjelinek } 29001300Sgjelinek 29011300Sgjelinek while (zonecfg_getfsent(t_handle, &t_fstab) == Z_OK) { 29021300Sgjelinek if ((err = zonecfg_setfsent(s_handle)) != Z_OK) { 29031300Sgjelinek errno = err; 29041300Sgjelinek zperror2(source_zone, 29051867Sgjelinek gettext("could not enumerate file systems")); 29061300Sgjelinek (void) zonecfg_endfsent(t_handle); 29071300Sgjelinek return; 29081300Sgjelinek } 29091300Sgjelinek 29101300Sgjelinek while (zonecfg_getfsent(s_handle, &s_fstab) == Z_OK) { 29111300Sgjelinek if (strcmp(t_fstab.zone_fs_special, 29121300Sgjelinek s_fstab.zone_fs_special) == 0) { 29131300Sgjelinek print_fs_warnings(&s_fstab, &t_fstab); 29141300Sgjelinek break; 29151300Sgjelinek } 29161300Sgjelinek } 29171300Sgjelinek (void) zonecfg_endfsent(s_handle); 29181300Sgjelinek } 29191300Sgjelinek 29201300Sgjelinek (void) zonecfg_endfsent(t_handle); 29211300Sgjelinek } 29221300Sgjelinek 29231300Sgjelinek /* 29241300Sgjelinek * We don't catch the case where you used the same IP address but 29251300Sgjelinek * it is not an exact string match. For example, 192.9.0.128 vs. 192.09.0.128. 29261300Sgjelinek * However, we're not going to worry about that but we will check for 29271300Sgjelinek * a possible netmask on one of the addresses (e.g. 10.0.0.1 and 10.0.0.1/24) 29281300Sgjelinek * and handle that case as a match. 29291300Sgjelinek */ 29301300Sgjelinek static void 29311300Sgjelinek warn_ip_match(zone_dochandle_t s_handle, char *source_zone, 29321300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 29331300Sgjelinek { 29341300Sgjelinek int err; 29351300Sgjelinek struct zone_nwiftab s_nwiftab; 29361300Sgjelinek struct zone_nwiftab t_nwiftab; 29371300Sgjelinek 29381300Sgjelinek if ((err = zonecfg_setnwifent(t_handle)) != Z_OK) { 29391300Sgjelinek errno = err; 29401300Sgjelinek zperror2(target_zone, 29411300Sgjelinek gettext("could not enumerate network interfaces")); 29421300Sgjelinek return; 29431300Sgjelinek } 29441300Sgjelinek 29451300Sgjelinek while (zonecfg_getnwifent(t_handle, &t_nwiftab) == Z_OK) { 29461300Sgjelinek char *p; 29471300Sgjelinek 29481300Sgjelinek /* remove an (optional) netmask from the address */ 29491300Sgjelinek if ((p = strchr(t_nwiftab.zone_nwif_address, '/')) != NULL) 29501300Sgjelinek *p = '\0'; 29511300Sgjelinek 29521300Sgjelinek if ((err = zonecfg_setnwifent(s_handle)) != Z_OK) { 29531300Sgjelinek errno = err; 29541300Sgjelinek zperror2(source_zone, 29551300Sgjelinek gettext("could not enumerate network interfaces")); 29561300Sgjelinek (void) zonecfg_endnwifent(t_handle); 29571300Sgjelinek return; 29581300Sgjelinek } 29591300Sgjelinek 29601300Sgjelinek while (zonecfg_getnwifent(s_handle, &s_nwiftab) == Z_OK) { 29611300Sgjelinek /* remove an (optional) netmask from the address */ 29621300Sgjelinek if ((p = strchr(s_nwiftab.zone_nwif_address, '/')) 29631300Sgjelinek != NULL) 29641300Sgjelinek *p = '\0'; 29651300Sgjelinek 29661300Sgjelinek if (strcmp(t_nwiftab.zone_nwif_address, 29671300Sgjelinek s_nwiftab.zone_nwif_address) == 0) { 29681300Sgjelinek (void) fprintf(stderr, 29691300Sgjelinek gettext("WARNING: network address '%s' " 29701300Sgjelinek "is configured in both zones.\n"), 29711300Sgjelinek t_nwiftab.zone_nwif_address); 29721300Sgjelinek break; 29731300Sgjelinek } 29741300Sgjelinek } 29751300Sgjelinek (void) zonecfg_endnwifent(s_handle); 29761300Sgjelinek } 29771300Sgjelinek 29781300Sgjelinek (void) zonecfg_endnwifent(t_handle); 29791300Sgjelinek } 29801300Sgjelinek 29811300Sgjelinek static void 29821300Sgjelinek warn_dataset_match(zone_dochandle_t s_handle, char *source_zone, 29831300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 29841300Sgjelinek { 29851300Sgjelinek int err; 29861300Sgjelinek struct zone_dstab s_dstab; 29871300Sgjelinek struct zone_dstab t_dstab; 29881300Sgjelinek 29891300Sgjelinek if ((err = zonecfg_setdsent(t_handle)) != Z_OK) { 29901300Sgjelinek errno = err; 29911300Sgjelinek zperror2(target_zone, gettext("could not enumerate datasets")); 29921300Sgjelinek return; 29931300Sgjelinek } 29941300Sgjelinek 29951300Sgjelinek while (zonecfg_getdsent(t_handle, &t_dstab) == Z_OK) { 29961300Sgjelinek if ((err = zonecfg_setdsent(s_handle)) != Z_OK) { 29971300Sgjelinek errno = err; 29981300Sgjelinek zperror2(source_zone, 29991300Sgjelinek gettext("could not enumerate datasets")); 30001300Sgjelinek (void) zonecfg_enddsent(t_handle); 30011300Sgjelinek return; 30021300Sgjelinek } 30031300Sgjelinek 30041300Sgjelinek while (zonecfg_getdsent(s_handle, &s_dstab) == Z_OK) { 30051300Sgjelinek if (strcmp(t_dstab.zone_dataset_name, 30061300Sgjelinek s_dstab.zone_dataset_name) == 0) { 30071300Sgjelinek (void) fprintf(stderr, 30081300Sgjelinek gettext("WARNING: dataset '%s' " 30091300Sgjelinek "is configured in both zones.\n"), 30101300Sgjelinek t_dstab.zone_dataset_name); 30111300Sgjelinek break; 30121300Sgjelinek } 30131300Sgjelinek } 30141300Sgjelinek (void) zonecfg_enddsent(s_handle); 30151300Sgjelinek } 30161300Sgjelinek 30171300Sgjelinek (void) zonecfg_enddsent(t_handle); 30181300Sgjelinek } 30191300Sgjelinek 30201300Sgjelinek static int 30211300Sgjelinek validate_clone(char *source_zone, char *target_zone) 30221300Sgjelinek { 30231300Sgjelinek int err = Z_OK; 30241300Sgjelinek zone_dochandle_t s_handle; 30251300Sgjelinek zone_dochandle_t t_handle; 30261300Sgjelinek 30271300Sgjelinek if ((t_handle = zonecfg_init_handle()) == NULL) { 30281300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 30291300Sgjelinek return (Z_ERR); 30301300Sgjelinek } 30311300Sgjelinek if ((err = zonecfg_get_handle(target_zone, t_handle)) != Z_OK) { 30321300Sgjelinek errno = err; 30331300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 30341300Sgjelinek zonecfg_fini_handle(t_handle); 30351300Sgjelinek return (Z_ERR); 30361300Sgjelinek } 30371300Sgjelinek 30381300Sgjelinek if ((s_handle = zonecfg_init_handle()) == NULL) { 30391300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 30401300Sgjelinek zonecfg_fini_handle(t_handle); 30411300Sgjelinek return (Z_ERR); 30421300Sgjelinek } 30431300Sgjelinek if ((err = zonecfg_get_handle(source_zone, s_handle)) != Z_OK) { 30441300Sgjelinek errno = err; 30451300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 30461300Sgjelinek goto done; 30471300Sgjelinek } 30481300Sgjelinek 30491300Sgjelinek /* verify new zone has same inherit-pkg-dirs */ 30501300Sgjelinek err = valid_ipd_clone(s_handle, source_zone, t_handle, target_zone); 30511300Sgjelinek 30521300Sgjelinek /* warn about imported fs's which are the same */ 30531300Sgjelinek warn_fs_match(s_handle, source_zone, t_handle, target_zone); 30541300Sgjelinek 30551300Sgjelinek /* warn about imported IP addresses which are the same */ 30561300Sgjelinek warn_ip_match(s_handle, source_zone, t_handle, target_zone); 30571300Sgjelinek 30581300Sgjelinek /* warn about imported devices which are the same */ 30591300Sgjelinek warn_dev_match(s_handle, source_zone, t_handle, target_zone); 30601300Sgjelinek 30611300Sgjelinek /* warn about imported datasets which are the same */ 30621300Sgjelinek warn_dataset_match(s_handle, source_zone, t_handle, target_zone); 30631300Sgjelinek 30641300Sgjelinek done: 30651300Sgjelinek zonecfg_fini_handle(t_handle); 30661300Sgjelinek zonecfg_fini_handle(s_handle); 30671300Sgjelinek 30681300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 30691300Sgjelinek } 30701300Sgjelinek 30711300Sgjelinek static int 30721300Sgjelinek copy_zone(char *src, char *dst) 30731300Sgjelinek { 30741300Sgjelinek boolean_t out_null = B_FALSE; 30751300Sgjelinek int status; 30761300Sgjelinek int err; 30771300Sgjelinek char *outfile; 30781300Sgjelinek char cmdbuf[MAXPATHLEN * 2 + 128]; 30791300Sgjelinek 30801300Sgjelinek if ((outfile = tempnam("/var/log", "zone")) == NULL) { 30811300Sgjelinek outfile = "/dev/null"; 30821300Sgjelinek out_null = B_TRUE; 30831300Sgjelinek } 30841300Sgjelinek 30851867Sgjelinek /* 30861867Sgjelinek * Use find to get the list of files to copy. We need to skip 30871867Sgjelinek * files of type "socket" since cpio can't handle those but that 30881867Sgjelinek * should be ok since the app will recreate the socket when it runs. 30891867Sgjelinek * We also need to filter out anything under the .zfs subdir. Since 30901867Sgjelinek * find is running depth-first, we need the extra egrep to filter .zfs. 30911867Sgjelinek */ 30921300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 30931867Sgjelinek "cd %s && /usr/bin/find . -type s -prune -o -depth -print | " 30941607Sgjelinek "/usr/bin/egrep -v '^\\./\\.zfs$|^\\./\\.zfs/' | " 30951300Sgjelinek "/usr/bin/cpio -pdmuP@ %s > %s 2>&1", 30961300Sgjelinek src, dst, outfile); 30971300Sgjelinek 30981300Sgjelinek status = do_subproc(cmdbuf); 30991300Sgjelinek 31001300Sgjelinek if ((err = subproc_status("copy", status)) != Z_OK) { 31011300Sgjelinek if (!out_null) 31021300Sgjelinek (void) fprintf(stderr, gettext("\nThe copy failed.\n" 31031300Sgjelinek "More information can be found in %s\n"), outfile); 31041300Sgjelinek return (err); 31051300Sgjelinek } 31061300Sgjelinek 31071300Sgjelinek if (!out_null) 31081300Sgjelinek (void) unlink(outfile); 31091300Sgjelinek 31101300Sgjelinek return (Z_OK); 31111300Sgjelinek } 31121300Sgjelinek 31131300Sgjelinek /* 31141568Sgjelinek * Run sys-unconfig on a zone. This will leave the zone in the installed 31151568Sgjelinek * state as long as there were no errors during the sys-unconfig. 31161300Sgjelinek */ 31171300Sgjelinek static int 31181568Sgjelinek unconfigure_zone(char *zonepath) 31191300Sgjelinek { 31201568Sgjelinek int err; 31211568Sgjelinek int status; 31221568Sgjelinek struct stat unconfig_buf; 31231568Sgjelinek zone_cmd_arg_t zarg; 31241568Sgjelinek char cmdbuf[MAXPATHLEN + 51]; 31251568Sgjelinek 31261568Sgjelinek /* The zone has to be installed in order to mount the scratch zone. */ 31271568Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 31281568Sgjelinek errno = err; 31291568Sgjelinek zperror2(target_zone, gettext("could not set state")); 31301568Sgjelinek return (Z_ERR); 31311568Sgjelinek } 31321568Sgjelinek 31331568Sgjelinek /* 31341676Sjpk * Trusted Extensions requires that cloned zones use the 31351676Sjpk * same sysid configuration, so it is not appropriate to 31361676Sjpk * unconfigure the zone. 31371676Sjpk */ 31381676Sjpk if (is_system_labeled()) 31391676Sjpk return (Z_OK); 31401676Sjpk 31411676Sjpk /* 31421568Sgjelinek * Check if the zone is already sys-unconfiged. This saves us 31431568Sgjelinek * the work of bringing up the scratch zone so we can unconfigure it. 31441568Sgjelinek */ 31451568Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/root/etc/.UNCONFIGURED", 31461568Sgjelinek zonepath); 31471568Sgjelinek if (stat(cmdbuf, &unconfig_buf) == 0) 31481568Sgjelinek return (Z_OK); 31491568Sgjelinek 31501568Sgjelinek zarg.cmd = Z_MOUNT; 31511568Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 31521568Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 31531568Sgjelinek (void) zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 31541568Sgjelinek return (Z_ERR); 31551568Sgjelinek } 31561300Sgjelinek 31571300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 31581568Sgjelinek "/usr/sbin/zlogin -S %s /usr/sbin/sys-unconfig -R /a", target_zone); 31591568Sgjelinek 31601568Sgjelinek status = do_subproc(cmdbuf); 31611568Sgjelinek if ((err = subproc_status("sys-unconfig", status)) != Z_OK) { 31621568Sgjelinek errno = err; 31631568Sgjelinek zperror2(target_zone, gettext("sys-unconfig failed\n")); 31641568Sgjelinek (void) zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 31651300Sgjelinek } 31661300Sgjelinek 31671568Sgjelinek zarg.cmd = Z_UNMOUNT; 31681568Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 31691568Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 31701568Sgjelinek (void) fprintf(stderr, gettext("could not unmount zone\n")); 31711568Sgjelinek return (Z_ERR); 31721568Sgjelinek } 31731568Sgjelinek 31741568Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 31751300Sgjelinek } 31761300Sgjelinek 31771300Sgjelinek /* ARGSUSED */ 31781867Sgjelinek static int 31791300Sgjelinek zfm_print(const char *p, void *r) { 31801300Sgjelinek zerror(" %s\n", p); 31811300Sgjelinek return (0); 31821300Sgjelinek } 31831300Sgjelinek 31841867Sgjelinek int 31851867Sgjelinek clone_copy(char *source_zonepath, char *zonepath) 31861867Sgjelinek { 31871867Sgjelinek int err; 31881867Sgjelinek 31891867Sgjelinek /* Don't clone the zone if anything is still mounted there */ 31901867Sgjelinek if (zonecfg_find_mounts(source_zonepath, NULL, NULL)) { 31911867Sgjelinek zerror(gettext("These file systems are mounted on " 31921867Sgjelinek "subdirectories of %s.\n"), source_zonepath); 31931867Sgjelinek (void) zonecfg_find_mounts(source_zonepath, zfm_print, NULL); 31941867Sgjelinek return (Z_ERR); 31951867Sgjelinek } 31961867Sgjelinek 31971867Sgjelinek /* 31981867Sgjelinek * Attempt to create a ZFS fs for the zonepath. As usual, we don't 31991867Sgjelinek * care if this works or not since we always have the default behavior 32001867Sgjelinek * of a simple directory for the zonepath. 32011867Sgjelinek */ 32021867Sgjelinek create_zfs_zonepath(zonepath); 32031867Sgjelinek 32041867Sgjelinek (void) printf(gettext("Copying %s..."), source_zonepath); 32051867Sgjelinek (void) fflush(stdout); 32061867Sgjelinek 32071867Sgjelinek err = copy_zone(source_zonepath, zonepath); 32081867Sgjelinek 32091867Sgjelinek (void) printf("\n"); 32101867Sgjelinek 32111867Sgjelinek return (err); 32121867Sgjelinek } 32131867Sgjelinek 32141300Sgjelinek static int 32151300Sgjelinek clone_func(int argc, char *argv[]) 32161300Sgjelinek { 32171300Sgjelinek char *source_zone = NULL; 32181300Sgjelinek int lockfd; 32191300Sgjelinek int err, arg; 32201300Sgjelinek char zonepath[MAXPATHLEN]; 32211300Sgjelinek char source_zonepath[MAXPATHLEN]; 32221300Sgjelinek zone_state_t state; 32231300Sgjelinek zone_entry_t *zent; 32241867Sgjelinek char *method = NULL; 32251867Sgjelinek char *snapshot = NULL; 32261300Sgjelinek 32271300Sgjelinek if (zonecfg_in_alt_root()) { 32281300Sgjelinek zerror(gettext("cannot clone zone in alternate root")); 32291300Sgjelinek return (Z_ERR); 32301300Sgjelinek } 32311300Sgjelinek 32321300Sgjelinek optind = 0; 32331867Sgjelinek if ((arg = getopt(argc, argv, "?m:s:")) != EOF) { 32341300Sgjelinek switch (arg) { 32351300Sgjelinek case '?': 32361300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 32371300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 32381300Sgjelinek case 'm': 32391300Sgjelinek method = optarg; 32401300Sgjelinek break; 32411867Sgjelinek case 's': 32421867Sgjelinek snapshot = optarg; 32431867Sgjelinek break; 32441300Sgjelinek default: 32451300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 32461300Sgjelinek return (Z_USAGE); 32471300Sgjelinek } 32481300Sgjelinek } 32491867Sgjelinek if (argc != (optind + 1) || 32501867Sgjelinek (method != NULL && strcmp(method, "copy") != 0)) { 32511300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 32521300Sgjelinek return (Z_USAGE); 32531300Sgjelinek } 32541300Sgjelinek source_zone = argv[optind]; 32551300Sgjelinek if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE) != Z_OK) 32561300Sgjelinek return (Z_ERR); 32571300Sgjelinek if (verify_details(CMD_CLONE) != Z_OK) 32581300Sgjelinek return (Z_ERR); 32591300Sgjelinek 32601300Sgjelinek /* 32611300Sgjelinek * We also need to do some extra validation on the source zone. 32621300Sgjelinek */ 32631300Sgjelinek if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) { 32641300Sgjelinek zerror(gettext("%s operation is invalid for the global zone."), 32651300Sgjelinek cmd_to_str(CMD_CLONE)); 32661300Sgjelinek return (Z_ERR); 32671300Sgjelinek } 32681300Sgjelinek 32691300Sgjelinek if (strncmp(source_zone, "SUNW", 4) == 0) { 32701300Sgjelinek zerror(gettext("%s operation is invalid for zones starting " 32711300Sgjelinek "with SUNW."), cmd_to_str(CMD_CLONE)); 32721300Sgjelinek return (Z_ERR); 32731300Sgjelinek } 32741300Sgjelinek 32751300Sgjelinek zent = lookup_running_zone(source_zone); 32761300Sgjelinek if (zent != NULL) { 32771300Sgjelinek /* check whether the zone is ready or running */ 32781300Sgjelinek if ((err = zone_get_state(zent->zname, &zent->zstate_num)) 32791300Sgjelinek != Z_OK) { 32801300Sgjelinek errno = err; 32811300Sgjelinek zperror2(zent->zname, gettext("could not get state")); 32821300Sgjelinek /* can't tell, so hedge */ 32831300Sgjelinek zent->zstate_str = "ready/running"; 32841300Sgjelinek } else { 32851300Sgjelinek zent->zstate_str = zone_state_str(zent->zstate_num); 32861300Sgjelinek } 32871300Sgjelinek zerror(gettext("%s operation is invalid for %s zones."), 32881300Sgjelinek cmd_to_str(CMD_CLONE), zent->zstate_str); 32891300Sgjelinek return (Z_ERR); 32901300Sgjelinek } 32911300Sgjelinek 32921300Sgjelinek if ((err = zone_get_state(source_zone, &state)) != Z_OK) { 32931300Sgjelinek errno = err; 32941300Sgjelinek zperror2(source_zone, gettext("could not get state")); 32951300Sgjelinek return (Z_ERR); 32961300Sgjelinek } 32971300Sgjelinek if (state != ZONE_STATE_INSTALLED) { 32981300Sgjelinek (void) fprintf(stderr, 32991300Sgjelinek gettext("%s: zone %s is %s; %s is required.\n"), 33001300Sgjelinek execname, source_zone, zone_state_str(state), 33011300Sgjelinek zone_state_str(ZONE_STATE_INSTALLED)); 33021300Sgjelinek return (Z_ERR); 33031300Sgjelinek } 33041300Sgjelinek 33051300Sgjelinek /* 33061300Sgjelinek * The source zone checks out ok, continue with the clone. 33071300Sgjelinek */ 33081300Sgjelinek 33091300Sgjelinek if (validate_clone(source_zone, target_zone) != Z_OK) 33101300Sgjelinek return (Z_ERR); 33111300Sgjelinek 33121300Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 33131300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 33141300Sgjelinek "zoneadm"); 33151300Sgjelinek return (Z_ERR); 33161300Sgjelinek } 33171300Sgjelinek 33181300Sgjelinek if ((err = zone_get_zonepath(source_zone, source_zonepath, 33191300Sgjelinek sizeof (source_zonepath))) != Z_OK) { 33201300Sgjelinek errno = err; 33211300Sgjelinek zperror2(source_zone, gettext("could not get zone path")); 33221300Sgjelinek goto done; 33231300Sgjelinek } 33241300Sgjelinek 33251300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 33261300Sgjelinek != Z_OK) { 33271300Sgjelinek errno = err; 33281300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 33291300Sgjelinek goto done; 33301300Sgjelinek } 33311300Sgjelinek 33321300Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE)) 33331300Sgjelinek != Z_OK) { 33341300Sgjelinek errno = err; 33351300Sgjelinek zperror2(target_zone, gettext("could not set state")); 33361300Sgjelinek goto done; 33371300Sgjelinek } 33381300Sgjelinek 33391867Sgjelinek if (snapshot != NULL) { 33401867Sgjelinek err = clone_snapshot_zfs(snapshot, zonepath); 33411867Sgjelinek } else { 33421867Sgjelinek /* 33431867Sgjelinek * We always copy the clone unless the source is ZFS and a 33441867Sgjelinek * ZFS clone worked. We fallback to copying if the ZFS clone 33451867Sgjelinek * fails for some reason. 33461867Sgjelinek */ 33471867Sgjelinek err = Z_ERR; 33481867Sgjelinek if (method == NULL && is_zonepath_zfs(source_zonepath)) 33491867Sgjelinek err = clone_zfs(source_zone, source_zonepath, zonepath); 33501867Sgjelinek 33511867Sgjelinek if (err != Z_OK) 33521867Sgjelinek err = clone_copy(source_zonepath, zonepath); 33531867Sgjelinek } 33541867Sgjelinek 33551867Sgjelinek if (err == Z_OK) 33561867Sgjelinek err = unconfigure_zone(zonepath); 33571300Sgjelinek 33581300Sgjelinek done: 33591300Sgjelinek release_lock_file(lockfd); 33601300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 33611300Sgjelinek } 33621300Sgjelinek 33631300Sgjelinek #define RMCOMMAND "/usr/bin/rm -rf" 33641300Sgjelinek 33651607Sgjelinek /* 33661867Sgjelinek * Used when removing a zonepath after uninstalling or cleaning up after 33671867Sgjelinek * the move subcommand. This handles a zonepath that has non-standard 33681867Sgjelinek * contents so that we will only cleanup the stuff we know about and leave 33691867Sgjelinek * any user data alone. 33701607Sgjelinek * 33711867Sgjelinek * If the "all" parameter is true then we should remove the whole zonepath 33721867Sgjelinek * even if it has non-standard files/directories in it. This can be used when 33731867Sgjelinek * we need to cleanup after moving the zonepath across file systems. 33741867Sgjelinek * 33751867Sgjelinek * We "exec" the RMCOMMAND so that the returned status is that of RMCOMMAND 33761867Sgjelinek * and not the shell. 33771607Sgjelinek */ 33781607Sgjelinek static int 33791867Sgjelinek cleanup_zonepath(char *zonepath, boolean_t all) 33801607Sgjelinek { 33811867Sgjelinek int status; 33821867Sgjelinek int i; 33831867Sgjelinek boolean_t non_std = B_FALSE; 33841867Sgjelinek struct dirent *dp; 33851867Sgjelinek DIR *dirp; 33861867Sgjelinek char *std_entries[] = {"dev", "lu", "root", NULL}; 33871867Sgjelinek /* (MAXPATHLEN * 3) is for the 3 std_entries dirs */ 33881867Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64]; 33891867Sgjelinek 33901867Sgjelinek /* 33911867Sgjelinek * We shouldn't need these checks but lets be paranoid since we 33921867Sgjelinek * could blow away the whole system here if we got the wrong zonepath. 33931867Sgjelinek */ 33941867Sgjelinek if (*zonepath == NULL || strcmp(zonepath, "/") == 0) { 33951867Sgjelinek (void) fprintf(stderr, "invalid zonepath '%s'\n", zonepath); 33961867Sgjelinek return (Z_INVAL); 33971867Sgjelinek } 33981867Sgjelinek 33991867Sgjelinek /* 34001867Sgjelinek * If the dirpath is already gone (maybe it was manually removed) then 34011867Sgjelinek * we just return Z_OK so that the cleanup is successful. 34021867Sgjelinek */ 34031867Sgjelinek if ((dirp = opendir(zonepath)) == NULL) 34041867Sgjelinek return (Z_OK); 34051867Sgjelinek 34061867Sgjelinek /* 34071867Sgjelinek * Look through the zonepath directory to see if there are any 34081867Sgjelinek * non-standard files/dirs. Also skip .zfs since that might be 34091867Sgjelinek * there but we'll handle ZFS file systems as a special case. 34101867Sgjelinek */ 34111867Sgjelinek while ((dp = readdir(dirp)) != NULL) { 34121867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 34131867Sgjelinek strcmp(dp->d_name, "..") == 0 || 34141867Sgjelinek strcmp(dp->d_name, ".zfs") == 0) 34151867Sgjelinek continue; 34161867Sgjelinek 34171867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) 34181867Sgjelinek if (strcmp(dp->d_name, std_entries[i]) == 0) 34191867Sgjelinek break; 34201867Sgjelinek 34211867Sgjelinek if (std_entries[i] == NULL) 34221867Sgjelinek non_std = B_TRUE; 34231867Sgjelinek } 34241867Sgjelinek (void) closedir(dirp); 34251867Sgjelinek 34261867Sgjelinek if (!all && non_std) { 34271607Sgjelinek /* 34281867Sgjelinek * There are extra, non-standard directories/files in the 34291867Sgjelinek * zonepath so we don't want to remove the zonepath. We 34301867Sgjelinek * just want to remove the standard directories and leave 34311867Sgjelinek * the user data alone. 34321607Sgjelinek */ 34331867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND); 34341867Sgjelinek 34351867Sgjelinek for (i = 0; std_entries[i] != NULL; i++) { 34361867Sgjelinek char tmpbuf[MAXPATHLEN]; 34371867Sgjelinek 34381867Sgjelinek if (snprintf(tmpbuf, sizeof (tmpbuf), " %s/%s", 34391867Sgjelinek zonepath, std_entries[i]) >= sizeof (tmpbuf) || 34401867Sgjelinek strlcat(cmdbuf, tmpbuf, sizeof (cmdbuf)) >= 34411867Sgjelinek sizeof (cmdbuf)) { 34421867Sgjelinek (void) fprintf(stderr, 34431867Sgjelinek gettext("path is too long\n")); 34441867Sgjelinek return (Z_INVAL); 34451867Sgjelinek } 34461867Sgjelinek } 34471867Sgjelinek 34481867Sgjelinek status = do_subproc(cmdbuf); 34491867Sgjelinek 34501867Sgjelinek (void) fprintf(stderr, gettext("WARNING: Unable to completely " 34511867Sgjelinek "remove %s\nbecause it contains additional user data. " 34521867Sgjelinek "Only the standard directory\nentries have been " 34531867Sgjelinek "removed.\n"), 34541867Sgjelinek zonepath); 34551867Sgjelinek 34561867Sgjelinek return (subproc_status(RMCOMMAND, status)); 34571607Sgjelinek } 34581607Sgjelinek 34591867Sgjelinek /* 34601867Sgjelinek * There is nothing unexpected in the zonepath, try to get rid of the 34611867Sgjelinek * whole zonepath directory. 34621867Sgjelinek * 34631867Sgjelinek * If the zonepath is its own zfs file system, try to destroy the 34641867Sgjelinek * file system. If that fails for some reason (e.g. it has clones) 34651867Sgjelinek * then we'll just remove the contents of the zonepath. 34661867Sgjelinek */ 34671867Sgjelinek if (is_zonepath_zfs(zonepath)) { 34681867Sgjelinek if (destroy_zfs(zonepath) == Z_OK) 34691867Sgjelinek return (Z_OK); 34701867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND 34711867Sgjelinek " %s/*", zonepath); 34721867Sgjelinek status = do_subproc(cmdbuf); 34731867Sgjelinek return (subproc_status(RMCOMMAND, status)); 34741867Sgjelinek } 34751867Sgjelinek 34761867Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 34771867Sgjelinek zonepath); 34781607Sgjelinek status = do_subproc(cmdbuf); 34791867Sgjelinek return (subproc_status(RMCOMMAND, status)); 34801607Sgjelinek } 34811607Sgjelinek 34821300Sgjelinek static int 34831300Sgjelinek move_func(int argc, char *argv[]) 34841300Sgjelinek { 34851300Sgjelinek char *new_zonepath = NULL; 34861300Sgjelinek int lockfd; 34871300Sgjelinek int err, arg; 34881300Sgjelinek char zonepath[MAXPATHLEN]; 34891300Sgjelinek zone_dochandle_t handle; 34901300Sgjelinek boolean_t fast; 34911867Sgjelinek boolean_t is_zfs = B_FALSE; 34921867Sgjelinek struct dirent *dp; 34931867Sgjelinek DIR *dirp; 34941867Sgjelinek boolean_t empty = B_TRUE; 34951300Sgjelinek boolean_t revert; 34961300Sgjelinek struct stat zonepath_buf; 34971300Sgjelinek struct stat new_zonepath_buf; 34981300Sgjelinek 34991300Sgjelinek if (zonecfg_in_alt_root()) { 35001300Sgjelinek zerror(gettext("cannot move zone in alternate root")); 35011300Sgjelinek return (Z_ERR); 35021300Sgjelinek } 35031300Sgjelinek 35041300Sgjelinek optind = 0; 35051300Sgjelinek if ((arg = getopt(argc, argv, "?")) != EOF) { 35061300Sgjelinek switch (arg) { 35071300Sgjelinek case '?': 35081300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 35091300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 35101300Sgjelinek default: 35111300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 35121300Sgjelinek return (Z_USAGE); 35131300Sgjelinek } 35141300Sgjelinek } 35151300Sgjelinek if (argc != (optind + 1)) { 35161300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 35171300Sgjelinek return (Z_USAGE); 35181300Sgjelinek } 35191300Sgjelinek new_zonepath = argv[optind]; 35201300Sgjelinek if (sanity_check(target_zone, CMD_MOVE, B_FALSE, B_TRUE) != Z_OK) 35211300Sgjelinek return (Z_ERR); 35221300Sgjelinek if (verify_details(CMD_MOVE) != Z_OK) 35231300Sgjelinek return (Z_ERR); 35241300Sgjelinek 35251300Sgjelinek /* 35261300Sgjelinek * Check out the new zonepath. This has the side effect of creating 35271300Sgjelinek * a directory for the new zonepath. We depend on this later when we 35281867Sgjelinek * stat to see if we are doing a cross file system move or not. 35291300Sgjelinek */ 35301300Sgjelinek if (validate_zonepath(new_zonepath, CMD_MOVE) != Z_OK) 35311300Sgjelinek return (Z_ERR); 35321300Sgjelinek 35331300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 35341300Sgjelinek != Z_OK) { 35351300Sgjelinek errno = err; 35361300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 35371300Sgjelinek return (Z_ERR); 35381300Sgjelinek } 35391300Sgjelinek 35401300Sgjelinek if (stat(zonepath, &zonepath_buf) == -1) { 35411300Sgjelinek zperror(gettext("could not stat zone path"), B_FALSE); 35421300Sgjelinek return (Z_ERR); 35431300Sgjelinek } 35441300Sgjelinek 35451300Sgjelinek if (stat(new_zonepath, &new_zonepath_buf) == -1) { 35461300Sgjelinek zperror(gettext("could not stat new zone path"), B_FALSE); 35471300Sgjelinek return (Z_ERR); 35481300Sgjelinek } 35491300Sgjelinek 35501867Sgjelinek /* 35511867Sgjelinek * Check if the destination directory is empty. 35521867Sgjelinek */ 35531867Sgjelinek if ((dirp = opendir(new_zonepath)) == NULL) { 35541867Sgjelinek zperror(gettext("could not open new zone path"), B_FALSE); 35551867Sgjelinek return (Z_ERR); 35561867Sgjelinek } 35571867Sgjelinek while ((dp = readdir(dirp)) != (struct dirent *)0) { 35581867Sgjelinek if (strcmp(dp->d_name, ".") == 0 || 35591867Sgjelinek strcmp(dp->d_name, "..") == 0) 35601867Sgjelinek continue; 35611867Sgjelinek empty = B_FALSE; 35621867Sgjelinek break; 35631867Sgjelinek } 35641867Sgjelinek (void) closedir(dirp); 35651867Sgjelinek 35661867Sgjelinek /* Error if there is anything in the destination directory. */ 35671867Sgjelinek if (!empty) { 35681867Sgjelinek (void) fprintf(stderr, gettext("could not move zone to %s: " 35691867Sgjelinek "directory not empty\n"), new_zonepath); 35701867Sgjelinek return (Z_ERR); 35711867Sgjelinek } 35721867Sgjelinek 35731300Sgjelinek /* Don't move the zone if anything is still mounted there */ 35741300Sgjelinek if (zonecfg_find_mounts(zonepath, NULL, NULL)) { 35751867Sgjelinek zerror(gettext("These file systems are mounted on " 35761300Sgjelinek "subdirectories of %s.\n"), zonepath); 35771300Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 35781300Sgjelinek return (Z_ERR); 35791300Sgjelinek } 35801300Sgjelinek 35811300Sgjelinek /* 35821867Sgjelinek * Check if we are moving in the same file system and can do a fast 35831867Sgjelinek * move or if we are crossing file systems and have to copy the data. 35841300Sgjelinek */ 35851300Sgjelinek fast = (zonepath_buf.st_dev == new_zonepath_buf.st_dev); 35861300Sgjelinek 35871300Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 35881300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 35891300Sgjelinek return (Z_ERR); 35901300Sgjelinek } 35911300Sgjelinek 35921300Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 35931300Sgjelinek errno = err; 35941300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 35951300Sgjelinek zonecfg_fini_handle(handle); 35961300Sgjelinek return (Z_ERR); 35971300Sgjelinek } 35981300Sgjelinek 35991300Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 36001300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 36011300Sgjelinek "zoneadm"); 36021300Sgjelinek zonecfg_fini_handle(handle); 36031300Sgjelinek return (Z_ERR); 36041300Sgjelinek } 36051300Sgjelinek 36061300Sgjelinek /* 36071867Sgjelinek * We're making some file system changes now so we have to clean up 36081867Sgjelinek * the file system before we are done. This will either clean up the 36091300Sgjelinek * new zonepath if the zonecfg update failed or it will clean up the 36101300Sgjelinek * old zonepath if everything is ok. 36111300Sgjelinek */ 36121300Sgjelinek revert = B_TRUE; 36131300Sgjelinek 36141867Sgjelinek if (is_zonepath_zfs(zonepath) && 36151867Sgjelinek move_zfs(zonepath, new_zonepath) != Z_ERR) { 36161867Sgjelinek is_zfs = B_TRUE; 36171867Sgjelinek 36181867Sgjelinek } else if (fast) { 36191867Sgjelinek /* same file system, use rename for a quick move */ 36201300Sgjelinek 36211300Sgjelinek /* 36221300Sgjelinek * Remove the new_zonepath directory that got created above 36231300Sgjelinek * during the validation. It gets in the way of the rename. 36241300Sgjelinek */ 36251300Sgjelinek if (rmdir(new_zonepath) != 0) { 36261300Sgjelinek zperror(gettext("could not rmdir new zone path"), 36271300Sgjelinek B_FALSE); 36281300Sgjelinek zonecfg_fini_handle(handle); 36291300Sgjelinek release_lock_file(lockfd); 36301300Sgjelinek return (Z_ERR); 36311300Sgjelinek } 36321300Sgjelinek 36331300Sgjelinek if (rename(zonepath, new_zonepath) != 0) { 36341300Sgjelinek /* 36351300Sgjelinek * If this fails we don't need to do all of the 36361300Sgjelinek * cleanup that happens for the rest of the code 36371300Sgjelinek * so just return from this error. 36381300Sgjelinek */ 36391300Sgjelinek zperror(gettext("could not move zone"), B_FALSE); 36401300Sgjelinek zonecfg_fini_handle(handle); 36411300Sgjelinek release_lock_file(lockfd); 36421300Sgjelinek return (Z_ERR); 36431300Sgjelinek } 36441300Sgjelinek 36451300Sgjelinek } else { 36461867Sgjelinek /* 36471867Sgjelinek * Attempt to create a ZFS fs for the new zonepath. As usual, 36481867Sgjelinek * we don't care if this works or not since we always have the 36491867Sgjelinek * default behavior of a simple directory for the zonepath. 36501867Sgjelinek */ 36511867Sgjelinek create_zfs_zonepath(new_zonepath); 36521867Sgjelinek 36531300Sgjelinek (void) printf(gettext( 36541867Sgjelinek "Moving across file systems; copying zonepath %s..."), 36551300Sgjelinek zonepath); 36561300Sgjelinek (void) fflush(stdout); 36571300Sgjelinek 36581300Sgjelinek err = copy_zone(zonepath, new_zonepath); 36591300Sgjelinek 36601300Sgjelinek (void) printf("\n"); 36611300Sgjelinek if (err != Z_OK) 36621300Sgjelinek goto done; 36631300Sgjelinek } 36641300Sgjelinek 36651300Sgjelinek if ((err = zonecfg_set_zonepath(handle, new_zonepath)) != Z_OK) { 36661300Sgjelinek errno = err; 36671300Sgjelinek zperror(gettext("could not set new zonepath"), B_TRUE); 36681300Sgjelinek goto done; 36691300Sgjelinek } 36701300Sgjelinek 36711300Sgjelinek if ((err = zonecfg_save(handle)) != Z_OK) { 36721300Sgjelinek errno = err; 36731300Sgjelinek zperror(gettext("zonecfg save failed"), B_TRUE); 36741300Sgjelinek goto done; 36751300Sgjelinek } 36761300Sgjelinek 36771300Sgjelinek revert = B_FALSE; 36781300Sgjelinek 36791300Sgjelinek done: 36801300Sgjelinek zonecfg_fini_handle(handle); 36811300Sgjelinek release_lock_file(lockfd); 36821300Sgjelinek 36831300Sgjelinek /* 36841867Sgjelinek * Clean up the file system based on how things went. We either 36851300Sgjelinek * clean up the new zonepath if the operation failed for some reason 36861300Sgjelinek * or we clean up the old zonepath if everything is ok. 36871300Sgjelinek */ 36881300Sgjelinek if (revert) { 36891300Sgjelinek /* The zonecfg update failed, cleanup the new zonepath. */ 36901867Sgjelinek if (is_zfs) { 36911867Sgjelinek if (move_zfs(new_zonepath, zonepath) == Z_ERR) { 36921867Sgjelinek (void) fprintf(stderr, gettext("could not " 36931867Sgjelinek "restore zonepath, the zfs mountpoint is " 36941867Sgjelinek "set as:\n%s\n"), new_zonepath); 36951867Sgjelinek /* 36961867Sgjelinek * err is already != Z_OK since we're reverting 36971867Sgjelinek */ 36981867Sgjelinek } 36991867Sgjelinek 37001867Sgjelinek } else if (fast) { 37011300Sgjelinek if (rename(new_zonepath, zonepath) != 0) { 37021300Sgjelinek zperror(gettext("could not restore zonepath"), 37031300Sgjelinek B_FALSE); 37041300Sgjelinek /* 37051300Sgjelinek * err is already != Z_OK since we're reverting 37061300Sgjelinek */ 37071300Sgjelinek } 37081300Sgjelinek } else { 37091300Sgjelinek (void) printf(gettext("Cleaning up zonepath %s..."), 37101300Sgjelinek new_zonepath); 37111300Sgjelinek (void) fflush(stdout); 37121867Sgjelinek err = cleanup_zonepath(new_zonepath, B_TRUE); 37131300Sgjelinek (void) printf("\n"); 37141300Sgjelinek 37151607Sgjelinek if (err != Z_OK) { 37161300Sgjelinek errno = err; 37171300Sgjelinek zperror(gettext("could not remove new " 37181300Sgjelinek "zonepath"), B_TRUE); 37191300Sgjelinek } else { 37201300Sgjelinek /* 37211300Sgjelinek * Because we're reverting we know the mainline 37221300Sgjelinek * code failed but we just reused the err 37231300Sgjelinek * variable so we reset it back to Z_ERR. 37241300Sgjelinek */ 37251300Sgjelinek err = Z_ERR; 37261300Sgjelinek } 37271300Sgjelinek } 37281300Sgjelinek 37291300Sgjelinek } else { 37301300Sgjelinek /* The move was successful, cleanup the old zonepath. */ 37311867Sgjelinek if (!is_zfs && !fast) { 37321300Sgjelinek (void) printf( 37331300Sgjelinek gettext("Cleaning up zonepath %s..."), zonepath); 37341300Sgjelinek (void) fflush(stdout); 37351867Sgjelinek err = cleanup_zonepath(zonepath, B_TRUE); 37361300Sgjelinek (void) printf("\n"); 37371300Sgjelinek 37381607Sgjelinek if (err != Z_OK) { 37391300Sgjelinek errno = err; 37401300Sgjelinek zperror(gettext("could not remove zonepath"), 37411300Sgjelinek B_TRUE); 37421300Sgjelinek } 37431300Sgjelinek } 37441300Sgjelinek } 37451300Sgjelinek 37461300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 37471300Sgjelinek } 37481300Sgjelinek 37491507Sgjelinek static int 37501507Sgjelinek detach_func(int argc, char *argv[]) 37511507Sgjelinek { 37521507Sgjelinek int lockfd; 37531507Sgjelinek int err, arg; 37541507Sgjelinek char zonepath[MAXPATHLEN]; 37551507Sgjelinek zone_dochandle_t handle; 37562078Sgjelinek boolean_t execute = B_TRUE; 37571507Sgjelinek 37581507Sgjelinek if (zonecfg_in_alt_root()) { 37591507Sgjelinek zerror(gettext("cannot detach zone in alternate root")); 37601507Sgjelinek return (Z_ERR); 37611507Sgjelinek } 37621507Sgjelinek 37631507Sgjelinek optind = 0; 37642078Sgjelinek if ((arg = getopt(argc, argv, "?n")) != EOF) { 37651507Sgjelinek switch (arg) { 37661507Sgjelinek case '?': 37671507Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 37681507Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 37692078Sgjelinek case 'n': 37702078Sgjelinek execute = B_FALSE; 37712078Sgjelinek break; 37721507Sgjelinek default: 37731507Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 37741507Sgjelinek return (Z_USAGE); 37751507Sgjelinek } 37761507Sgjelinek } 37772078Sgjelinek if (execute) { 37782078Sgjelinek if (sanity_check(target_zone, CMD_DETACH, B_FALSE, B_TRUE) 37792078Sgjelinek != Z_OK) 37802078Sgjelinek return (Z_ERR); 37812078Sgjelinek if (verify_details(CMD_DETACH) != Z_OK) 37822078Sgjelinek return (Z_ERR); 37832078Sgjelinek } else { 37842078Sgjelinek /* 37852078Sgjelinek * We want a dry-run to work for a non-privileged user so we 37862078Sgjelinek * only do minimal validation. 37872078Sgjelinek */ 37882078Sgjelinek if (getzoneid() != GLOBAL_ZONEID) { 37892078Sgjelinek zerror(gettext("must be in the global zone to %s a " 37902078Sgjelinek "zone."), cmd_to_str(CMD_DETACH)); 37912078Sgjelinek return (Z_ERR); 37922078Sgjelinek } 37932078Sgjelinek 37942078Sgjelinek if (target_zone == NULL) { 37952078Sgjelinek zerror(gettext("no zone specified")); 37962078Sgjelinek return (Z_ERR); 37972078Sgjelinek } 37982078Sgjelinek 37992078Sgjelinek if (strcmp(target_zone, GLOBAL_ZONENAME) == 0) { 38002078Sgjelinek zerror(gettext("%s operation is invalid for the " 38012078Sgjelinek "global zone."), cmd_to_str(CMD_DETACH)); 38022078Sgjelinek return (Z_ERR); 38032078Sgjelinek } 38042078Sgjelinek } 38051507Sgjelinek 38061507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 38071507Sgjelinek != Z_OK) { 38081507Sgjelinek errno = err; 38091507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 38101507Sgjelinek return (Z_ERR); 38111507Sgjelinek } 38121507Sgjelinek 38131507Sgjelinek /* Don't detach the zone if anything is still mounted there */ 38142078Sgjelinek if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) { 38151867Sgjelinek zerror(gettext("These file systems are mounted on " 38161507Sgjelinek "subdirectories of %s.\n"), zonepath); 38171507Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 38181507Sgjelinek return (Z_ERR); 38191507Sgjelinek } 38201507Sgjelinek 38211507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 38221507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 38231507Sgjelinek return (Z_ERR); 38241507Sgjelinek } 38251507Sgjelinek 38261507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 38271507Sgjelinek errno = err; 38281507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 38291507Sgjelinek zonecfg_fini_handle(handle); 38301507Sgjelinek return (Z_ERR); 38311507Sgjelinek } 38321507Sgjelinek 38332078Sgjelinek if (execute && grab_lock_file(target_zone, &lockfd) != Z_OK) { 38341507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 38351507Sgjelinek "zoneadm"); 38361507Sgjelinek zonecfg_fini_handle(handle); 38371507Sgjelinek return (Z_ERR); 38381507Sgjelinek } 38391507Sgjelinek 38401507Sgjelinek if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) { 38411507Sgjelinek errno = err; 38421507Sgjelinek zperror(gettext("getting the detach information failed"), 38431507Sgjelinek B_TRUE); 38441507Sgjelinek goto done; 38451507Sgjelinek } 38461507Sgjelinek 38472078Sgjelinek if ((err = zonecfg_detach_save(handle, (execute ? 0 : ZONE_DRY_RUN))) 38482078Sgjelinek != Z_OK) { 38491507Sgjelinek errno = err; 38501507Sgjelinek zperror(gettext("saving the detach manifest failed"), B_TRUE); 38511507Sgjelinek goto done; 38521507Sgjelinek } 38531507Sgjelinek 38542078Sgjelinek /* 38552078Sgjelinek * Set the zone state back to configured unless we are running with the 38562078Sgjelinek * no-execute option. 38572078Sgjelinek */ 38582078Sgjelinek if (execute && (err = zone_set_state(target_zone, 38592078Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 38601507Sgjelinek errno = err; 38611507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 38621507Sgjelinek } 38631507Sgjelinek 38641507Sgjelinek done: 38651507Sgjelinek zonecfg_fini_handle(handle); 38662078Sgjelinek if (execute) 38672078Sgjelinek release_lock_file(lockfd); 38681507Sgjelinek 38691507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 38701507Sgjelinek } 38711507Sgjelinek 38721507Sgjelinek /* 38731507Sgjelinek * During attach we go through and fix up the /dev entries for the zone 38741507Sgjelinek * we are attaching. In order to regenerate /dev with the correct devices, 38751507Sgjelinek * the old /dev will be removed, the zone readied (which generates a new 38761507Sgjelinek * /dev) then halted, then we use the info from the manifest to update 38771507Sgjelinek * the modes, owners, etc. on the new /dev. 38781507Sgjelinek */ 38791507Sgjelinek static int 38801507Sgjelinek dev_fix(zone_dochandle_t handle) 38811507Sgjelinek { 38821507Sgjelinek int res; 38831507Sgjelinek int err; 38841507Sgjelinek int status; 38851507Sgjelinek struct zone_devpermtab devtab; 38861507Sgjelinek zone_cmd_arg_t zarg; 38871507Sgjelinek char devpath[MAXPATHLEN]; 38881507Sgjelinek /* 6: "exec " and " " */ 38891507Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 6]; 38901507Sgjelinek 38911507Sgjelinek if ((res = zonecfg_get_zonepath(handle, devpath, sizeof (devpath))) 38921507Sgjelinek != Z_OK) 38931507Sgjelinek return (res); 38941507Sgjelinek 38951507Sgjelinek if (strlcat(devpath, "/dev", sizeof (devpath)) >= sizeof (devpath)) 38961507Sgjelinek return (Z_TOO_BIG); 38971507Sgjelinek 38981507Sgjelinek /* 38991507Sgjelinek * "exec" the command so that the returned status is that of 39001507Sgjelinek * RMCOMMAND and not the shell. 39011507Sgjelinek */ 39021507Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 39031507Sgjelinek devpath); 39041507Sgjelinek status = do_subproc(cmdbuf); 39051507Sgjelinek if ((err = subproc_status(RMCOMMAND, status)) != Z_OK) { 39061507Sgjelinek (void) fprintf(stderr, 39071507Sgjelinek gettext("could not remove existing /dev\n")); 39081507Sgjelinek return (Z_ERR); 39091507Sgjelinek } 39101507Sgjelinek 39111507Sgjelinek /* In order to ready the zone, it must be in the installed state */ 39121507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 39131507Sgjelinek errno = err; 39141507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 39151507Sgjelinek return (Z_ERR); 39161507Sgjelinek } 39171507Sgjelinek 39181507Sgjelinek /* We have to ready the zone to regen the dev tree */ 39191507Sgjelinek zarg.cmd = Z_READY; 39201507Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 39211507Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 39221507Sgjelinek return (Z_ERR); 39231507Sgjelinek } 39241507Sgjelinek 39251507Sgjelinek zarg.cmd = Z_HALT; 39261507Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 39271507Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 39281507Sgjelinek return (Z_ERR); 39291507Sgjelinek } 39301507Sgjelinek 39311507Sgjelinek if (zonecfg_setdevperment(handle) != Z_OK) { 39321507Sgjelinek (void) fprintf(stderr, 39331507Sgjelinek gettext("unable to enumerate device entries\n")); 39341507Sgjelinek return (Z_ERR); 39351507Sgjelinek } 39361507Sgjelinek 39371507Sgjelinek while (zonecfg_getdevperment(handle, &devtab) == Z_OK) { 39381507Sgjelinek int err; 39391507Sgjelinek 39401507Sgjelinek if ((err = zonecfg_devperms_apply(handle, 39411507Sgjelinek devtab.zone_devperm_name, devtab.zone_devperm_uid, 39421507Sgjelinek devtab.zone_devperm_gid, devtab.zone_devperm_mode, 39431507Sgjelinek devtab.zone_devperm_acl)) != Z_OK && err != Z_INVAL) 39441507Sgjelinek (void) fprintf(stderr, gettext("error updating device " 39451507Sgjelinek "%s: %s\n"), devtab.zone_devperm_name, 39461507Sgjelinek zonecfg_strerror(err)); 39471507Sgjelinek 39481507Sgjelinek free(devtab.zone_devperm_acl); 39491507Sgjelinek } 39501507Sgjelinek 39511507Sgjelinek (void) zonecfg_enddevperment(handle); 39521507Sgjelinek 39531507Sgjelinek return (Z_OK); 39541507Sgjelinek } 39551507Sgjelinek 39562078Sgjelinek /* 39572078Sgjelinek * Validate attaching a zone but don't actually do the work. The zone 39582078Sgjelinek * does not have to exist, so there is some complexity getting a new zone 39592078Sgjelinek * configuration set up so that we can perform the validation. This is 39602078Sgjelinek * handled within zonecfg_attach_manifest() which returns two handles; one 39612078Sgjelinek * for the the full configuration to validate (rem_handle) and the other 39622078Sgjelinek * (local_handle) containing only the zone configuration derived from the 39632078Sgjelinek * manifest. 39642078Sgjelinek */ 39652078Sgjelinek static int 39662078Sgjelinek dryrun_attach(char *manifest_path) 39672078Sgjelinek { 39682078Sgjelinek int fd; 39692078Sgjelinek int err; 39702078Sgjelinek int res; 39712078Sgjelinek zone_dochandle_t local_handle; 39722078Sgjelinek zone_dochandle_t rem_handle = NULL; 39732078Sgjelinek 39742078Sgjelinek if (strcmp(manifest_path, "-") == 0) { 39752078Sgjelinek fd = 0; 39762078Sgjelinek } else if ((fd = open(manifest_path, O_RDONLY)) < 0) { 39772078Sgjelinek zperror(gettext("could not open manifest path"), B_FALSE); 39782078Sgjelinek return (Z_ERR); 39792078Sgjelinek } 39802078Sgjelinek 39812078Sgjelinek if ((local_handle = zonecfg_init_handle()) == NULL) { 39822078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 39832078Sgjelinek res = Z_ERR; 39842078Sgjelinek goto done; 39852078Sgjelinek } 39862078Sgjelinek 39872078Sgjelinek if ((rem_handle = zonecfg_init_handle()) == NULL) { 39882078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 39892078Sgjelinek res = Z_ERR; 39902078Sgjelinek goto done; 39912078Sgjelinek } 39922078Sgjelinek 39932078Sgjelinek if ((err = zonecfg_attach_manifest(fd, local_handle, rem_handle)) 39942078Sgjelinek != Z_OK) { 39952078Sgjelinek if (err == Z_INVALID_DOCUMENT) 39962078Sgjelinek zerror(gettext("Cannot attach to an earlier release " 39972078Sgjelinek "of the operating system")); 39982078Sgjelinek else 39992078Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40002078Sgjelinek res = Z_ERR; 40012078Sgjelinek goto done; 40022078Sgjelinek } 40032078Sgjelinek 40042078Sgjelinek res = verify_handle(CMD_ATTACH, local_handle); 40052078Sgjelinek 40062078Sgjelinek /* Get the detach information for the locally defined zone. */ 40072078Sgjelinek if ((err = zonecfg_get_detach_info(local_handle, B_FALSE)) != Z_OK) { 40082078Sgjelinek errno = err; 40092078Sgjelinek zperror(gettext("getting the attach information failed"), 40102078Sgjelinek B_TRUE); 40112078Sgjelinek res = Z_ERR; 40122078Sgjelinek } else { 40132078Sgjelinek /* sw_cmp prints error msgs as necessary */ 40142078Sgjelinek if (sw_cmp(local_handle, rem_handle, SW_CMP_NONE) != Z_OK) 40152078Sgjelinek res = Z_ERR; 40162078Sgjelinek } 40172078Sgjelinek 40182078Sgjelinek done: 40192078Sgjelinek if (strcmp(manifest_path, "-") != 0) 40202078Sgjelinek (void) close(fd); 40212078Sgjelinek 40222078Sgjelinek zonecfg_fini_handle(local_handle); 40232078Sgjelinek zonecfg_fini_handle(rem_handle); 40242078Sgjelinek 40252078Sgjelinek return ((res == Z_OK) ? Z_OK : Z_ERR); 40262078Sgjelinek } 40272078Sgjelinek 40281507Sgjelinek static int 40291507Sgjelinek attach_func(int argc, char *argv[]) 40301507Sgjelinek { 40311507Sgjelinek int lockfd; 40321507Sgjelinek int err, arg; 40331507Sgjelinek boolean_t force = B_FALSE; 40341507Sgjelinek zone_dochandle_t handle; 40351507Sgjelinek zone_dochandle_t athandle = NULL; 40361507Sgjelinek char zonepath[MAXPATHLEN]; 40372078Sgjelinek boolean_t execute = B_TRUE; 40382078Sgjelinek char *manifest_path; 40391507Sgjelinek 40401507Sgjelinek if (zonecfg_in_alt_root()) { 40411507Sgjelinek zerror(gettext("cannot attach zone in alternate root")); 40421507Sgjelinek return (Z_ERR); 40431507Sgjelinek } 40441507Sgjelinek 40451507Sgjelinek optind = 0; 40462078Sgjelinek if ((arg = getopt(argc, argv, "?Fn:")) != EOF) { 40471507Sgjelinek switch (arg) { 40481507Sgjelinek case '?': 40491507Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 40501507Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 40511507Sgjelinek case 'F': 40521507Sgjelinek force = B_TRUE; 40531507Sgjelinek break; 40542078Sgjelinek case 'n': 40552078Sgjelinek execute = B_FALSE; 40562078Sgjelinek manifest_path = optarg; 40572078Sgjelinek break; 40581507Sgjelinek default: 40591507Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 40601507Sgjelinek return (Z_USAGE); 40611507Sgjelinek } 40621507Sgjelinek } 40632078Sgjelinek 40642078Sgjelinek /* 40652078Sgjelinek * If the no-execute option was specified, we need to branch down 40662078Sgjelinek * a completely different path since there is no zone required to be 40672078Sgjelinek * configured for this option. 40682078Sgjelinek */ 40692078Sgjelinek if (!execute) 40702078Sgjelinek return (dryrun_attach(manifest_path)); 40712078Sgjelinek 40721507Sgjelinek if (sanity_check(target_zone, CMD_ATTACH, B_FALSE, B_TRUE) != Z_OK) 40731507Sgjelinek return (Z_ERR); 40741507Sgjelinek if (verify_details(CMD_ATTACH) != Z_OK) 40751507Sgjelinek return (Z_ERR); 40761507Sgjelinek 40771507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 40781507Sgjelinek != Z_OK) { 40791507Sgjelinek errno = err; 40801507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 40811507Sgjelinek return (Z_ERR); 40821507Sgjelinek } 40831507Sgjelinek 40841507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 40851507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40861507Sgjelinek return (Z_ERR); 40871507Sgjelinek } 40881507Sgjelinek 40891507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 40901507Sgjelinek errno = err; 40911507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40921507Sgjelinek zonecfg_fini_handle(handle); 40931507Sgjelinek return (Z_ERR); 40941507Sgjelinek } 40951507Sgjelinek 40961507Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 40971507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 40981507Sgjelinek "zoneadm"); 40991507Sgjelinek zonecfg_fini_handle(handle); 41001507Sgjelinek return (Z_ERR); 41011507Sgjelinek } 41021507Sgjelinek 41031507Sgjelinek if (force) 41041507Sgjelinek goto forced; 41051507Sgjelinek 41061507Sgjelinek if ((athandle = zonecfg_init_handle()) == NULL) { 41071507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 41081507Sgjelinek goto done; 41091507Sgjelinek } 41101507Sgjelinek 41111507Sgjelinek if ((err = zonecfg_get_attach_handle(zonepath, target_zone, B_TRUE, 41121507Sgjelinek athandle)) != Z_OK) { 41131507Sgjelinek if (err == Z_NO_ZONE) 41141507Sgjelinek zerror(gettext("Not a detached zone")); 41151507Sgjelinek else if (err == Z_INVALID_DOCUMENT) 41161507Sgjelinek zerror(gettext("Cannot attach to an earlier release " 41171507Sgjelinek "of the operating system")); 41181507Sgjelinek else 41191507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 41201507Sgjelinek goto done; 41211507Sgjelinek } 41221507Sgjelinek 41231507Sgjelinek /* Get the detach information for the locally defined zone. */ 41241507Sgjelinek if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) { 41251507Sgjelinek errno = err; 41261507Sgjelinek zperror(gettext("getting the attach information failed"), 41271507Sgjelinek B_TRUE); 41281507Sgjelinek goto done; 41291507Sgjelinek } 41301507Sgjelinek 41311507Sgjelinek /* sw_cmp prints error msgs as necessary */ 41321867Sgjelinek if ((err = sw_cmp(handle, athandle, SW_CMP_NONE)) != Z_OK) 41331507Sgjelinek goto done; 41341507Sgjelinek 41351507Sgjelinek if ((err = dev_fix(athandle)) != Z_OK) 41361507Sgjelinek goto done; 41371507Sgjelinek 41381507Sgjelinek forced: 41391507Sgjelinek 41401507Sgjelinek zonecfg_rm_detached(handle, force); 41411507Sgjelinek 41421507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 41431507Sgjelinek errno = err; 41441507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 41451507Sgjelinek } 41461507Sgjelinek 41471507Sgjelinek done: 41481507Sgjelinek zonecfg_fini_handle(handle); 41491507Sgjelinek release_lock_file(lockfd); 41501507Sgjelinek if (athandle != NULL) 41511507Sgjelinek zonecfg_fini_handle(athandle); 41521507Sgjelinek 41531507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 41541507Sgjelinek } 41551507Sgjelinek 41561300Sgjelinek /* 41570Sstevel@tonic-gate * On input, TRUE => yes, FALSE => no. 41580Sstevel@tonic-gate * On return, TRUE => 1, FALSE => 0, could not ask => -1. 41590Sstevel@tonic-gate */ 41600Sstevel@tonic-gate 41610Sstevel@tonic-gate static int 41620Sstevel@tonic-gate ask_yesno(boolean_t default_answer, const char *question) 41630Sstevel@tonic-gate { 41640Sstevel@tonic-gate char line[64]; /* should be large enough to answer yes or no */ 41650Sstevel@tonic-gate 41660Sstevel@tonic-gate if (!isatty(STDIN_FILENO)) 41670Sstevel@tonic-gate return (-1); 41680Sstevel@tonic-gate for (;;) { 41690Sstevel@tonic-gate (void) printf("%s (%s)? ", question, 41700Sstevel@tonic-gate default_answer ? "[y]/n" : "y/[n]"); 41710Sstevel@tonic-gate if (fgets(line, sizeof (line), stdin) == NULL || 41720Sstevel@tonic-gate line[0] == '\n') 41730Sstevel@tonic-gate return (default_answer ? 1 : 0); 41740Sstevel@tonic-gate if (tolower(line[0]) == 'y') 41750Sstevel@tonic-gate return (1); 41760Sstevel@tonic-gate if (tolower(line[0]) == 'n') 41770Sstevel@tonic-gate return (0); 41780Sstevel@tonic-gate } 41790Sstevel@tonic-gate } 41800Sstevel@tonic-gate 41810Sstevel@tonic-gate static int 41820Sstevel@tonic-gate uninstall_func(int argc, char *argv[]) 41830Sstevel@tonic-gate { 41840Sstevel@tonic-gate char line[ZONENAME_MAX + 128]; /* Enough for "Are you sure ..." */ 41851867Sgjelinek char rootpath[MAXPATHLEN], zonepath[MAXPATHLEN]; 41860Sstevel@tonic-gate boolean_t force = B_FALSE; 41870Sstevel@tonic-gate int lockfd, answer; 41880Sstevel@tonic-gate int err, arg; 41890Sstevel@tonic-gate 4190766Scarlsonj if (zonecfg_in_alt_root()) { 4191766Scarlsonj zerror(gettext("cannot uninstall zone in alternate root")); 4192766Scarlsonj return (Z_ERR); 4193766Scarlsonj } 4194766Scarlsonj 41950Sstevel@tonic-gate optind = 0; 41960Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?F")) != EOF) { 41970Sstevel@tonic-gate switch (arg) { 41980Sstevel@tonic-gate case '?': 41990Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 42000Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 42010Sstevel@tonic-gate case 'F': 42020Sstevel@tonic-gate force = B_TRUE; 42030Sstevel@tonic-gate break; 42040Sstevel@tonic-gate default: 42050Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 42060Sstevel@tonic-gate return (Z_USAGE); 42070Sstevel@tonic-gate } 42080Sstevel@tonic-gate } 42090Sstevel@tonic-gate if (argc > optind) { 42100Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 42110Sstevel@tonic-gate return (Z_USAGE); 42120Sstevel@tonic-gate } 42130Sstevel@tonic-gate 42140Sstevel@tonic-gate if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE) != Z_OK) 42150Sstevel@tonic-gate return (Z_ERR); 42160Sstevel@tonic-gate 42170Sstevel@tonic-gate if (!force) { 42180Sstevel@tonic-gate (void) snprintf(line, sizeof (line), 42190Sstevel@tonic-gate gettext("Are you sure you want to %s zone %s"), 42200Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL), target_zone); 42210Sstevel@tonic-gate if ((answer = ask_yesno(B_FALSE, line)) == 0) { 42220Sstevel@tonic-gate return (Z_OK); 42230Sstevel@tonic-gate } else if (answer == -1) { 42240Sstevel@tonic-gate zerror(gettext("Input not from terminal and -F " 42250Sstevel@tonic-gate "not specified: %s not done."), 42260Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 42270Sstevel@tonic-gate return (Z_ERR); 42280Sstevel@tonic-gate } 42290Sstevel@tonic-gate } 42300Sstevel@tonic-gate 42311867Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, 42321867Sgjelinek sizeof (zonepath))) != Z_OK) { 42330Sstevel@tonic-gate errno = err; 42340Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 42350Sstevel@tonic-gate return (Z_ERR); 42360Sstevel@tonic-gate } 42370Sstevel@tonic-gate if ((err = zone_get_rootpath(target_zone, rootpath, 42380Sstevel@tonic-gate sizeof (rootpath))) != Z_OK) { 42390Sstevel@tonic-gate errno = err; 42400Sstevel@tonic-gate zperror2(target_zone, gettext("could not get root path")); 42410Sstevel@tonic-gate return (Z_ERR); 42420Sstevel@tonic-gate } 42430Sstevel@tonic-gate 42440Sstevel@tonic-gate /* 42450Sstevel@tonic-gate * If there seems to be a zoneadmd running for this zone, call it 42460Sstevel@tonic-gate * to tell it that an uninstall is happening; if all goes well it 42470Sstevel@tonic-gate * will then shut itself down. 42480Sstevel@tonic-gate */ 42490Sstevel@tonic-gate if (ping_zoneadmd(target_zone) == Z_OK) { 42500Sstevel@tonic-gate zone_cmd_arg_t zarg; 42510Sstevel@tonic-gate zarg.cmd = Z_NOTE_UNINSTALLING; 42520Sstevel@tonic-gate /* we don't care too much if this fails... just plow on */ 42530Sstevel@tonic-gate (void) call_zoneadmd(target_zone, &zarg); 42540Sstevel@tonic-gate } 42550Sstevel@tonic-gate 42560Sstevel@tonic-gate if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 42570Sstevel@tonic-gate zerror(gettext("another %s may have an operation in progress."), 42581300Sgjelinek "zoneadm"); 42590Sstevel@tonic-gate return (Z_ERR); 42600Sstevel@tonic-gate } 42610Sstevel@tonic-gate 42620Sstevel@tonic-gate /* Don't uninstall the zone if anything is mounted there */ 42630Sstevel@tonic-gate err = zonecfg_find_mounts(rootpath, NULL, NULL); 42640Sstevel@tonic-gate if (err) { 42651867Sgjelinek zerror(gettext("These file systems are mounted on " 42661645Scomay "subdirectories of %s.\n"), rootpath); 42670Sstevel@tonic-gate (void) zonecfg_find_mounts(rootpath, zfm_print, NULL); 42680Sstevel@tonic-gate return (Z_ERR); 42690Sstevel@tonic-gate } 42700Sstevel@tonic-gate 42710Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 42720Sstevel@tonic-gate if (err != Z_OK) { 42730Sstevel@tonic-gate errno = err; 42740Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 42750Sstevel@tonic-gate goto bad; 42760Sstevel@tonic-gate } 42770Sstevel@tonic-gate 42781867Sgjelinek if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) { 42791867Sgjelinek errno = err; 42801867Sgjelinek zperror2(target_zone, gettext("cleaning up zonepath failed")); 42810Sstevel@tonic-gate goto bad; 42821867Sgjelinek } 42831867Sgjelinek 42840Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED); 42850Sstevel@tonic-gate if (err != Z_OK) { 42860Sstevel@tonic-gate errno = err; 42870Sstevel@tonic-gate zperror2(target_zone, gettext("could not reset state")); 42880Sstevel@tonic-gate } 42890Sstevel@tonic-gate bad: 42900Sstevel@tonic-gate release_lock_file(lockfd); 42910Sstevel@tonic-gate return (err); 42920Sstevel@tonic-gate } 42930Sstevel@tonic-gate 4294766Scarlsonj /* ARGSUSED */ 4295766Scarlsonj static int 4296766Scarlsonj mount_func(int argc, char *argv[]) 4297766Scarlsonj { 4298766Scarlsonj zone_cmd_arg_t zarg; 4299766Scarlsonj 4300766Scarlsonj if (argc > 0) 4301766Scarlsonj return (Z_USAGE); 4302766Scarlsonj if (sanity_check(target_zone, CMD_MOUNT, B_FALSE, B_FALSE) != Z_OK) 4303766Scarlsonj return (Z_ERR); 4304766Scarlsonj if (verify_details(CMD_MOUNT) != Z_OK) 4305766Scarlsonj return (Z_ERR); 4306766Scarlsonj 4307766Scarlsonj zarg.cmd = Z_MOUNT; 4308766Scarlsonj if (call_zoneadmd(target_zone, &zarg) != 0) { 4309766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 4310766Scarlsonj return (Z_ERR); 4311766Scarlsonj } 4312766Scarlsonj return (Z_OK); 4313766Scarlsonj } 4314766Scarlsonj 4315766Scarlsonj /* ARGSUSED */ 4316766Scarlsonj static int 4317766Scarlsonj unmount_func(int argc, char *argv[]) 4318766Scarlsonj { 4319766Scarlsonj zone_cmd_arg_t zarg; 4320766Scarlsonj 4321766Scarlsonj if (argc > 0) 4322766Scarlsonj return (Z_USAGE); 4323766Scarlsonj if (sanity_check(target_zone, CMD_UNMOUNT, B_FALSE, B_FALSE) != Z_OK) 4324766Scarlsonj return (Z_ERR); 4325766Scarlsonj 4326766Scarlsonj zarg.cmd = Z_UNMOUNT; 4327766Scarlsonj if (call_zoneadmd(target_zone, &zarg) != 0) { 4328766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 4329766Scarlsonj return (Z_ERR); 4330766Scarlsonj } 4331766Scarlsonj return (Z_OK); 4332766Scarlsonj } 4333766Scarlsonj 43340Sstevel@tonic-gate static int 43350Sstevel@tonic-gate help_func(int argc, char *argv[]) 43360Sstevel@tonic-gate { 43370Sstevel@tonic-gate int arg, cmd_num; 43380Sstevel@tonic-gate 43390Sstevel@tonic-gate if (argc == 0) { 43400Sstevel@tonic-gate (void) usage(B_TRUE); 43410Sstevel@tonic-gate return (Z_OK); 43420Sstevel@tonic-gate } 43430Sstevel@tonic-gate optind = 0; 43440Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 43450Sstevel@tonic-gate switch (arg) { 43460Sstevel@tonic-gate case '?': 43470Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 43480Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 43490Sstevel@tonic-gate default: 43500Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 43510Sstevel@tonic-gate return (Z_USAGE); 43520Sstevel@tonic-gate } 43530Sstevel@tonic-gate } 43540Sstevel@tonic-gate while (optind < argc) { 4355988Scarlsonj /* Private commands have NULL short_usage; omit them */ 4356988Scarlsonj if ((cmd_num = cmd_match(argv[optind])) < 0 || 4357988Scarlsonj cmdtab[cmd_num].short_usage == NULL) { 43580Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 43590Sstevel@tonic-gate return (Z_USAGE); 43600Sstevel@tonic-gate } 43610Sstevel@tonic-gate sub_usage(cmdtab[cmd_num].short_usage, cmd_num); 43620Sstevel@tonic-gate optind++; 43630Sstevel@tonic-gate } 43640Sstevel@tonic-gate return (Z_OK); 43650Sstevel@tonic-gate } 43660Sstevel@tonic-gate 43670Sstevel@tonic-gate /* 43680Sstevel@tonic-gate * Returns: CMD_MIN thru CMD_MAX on success, -1 on error 43690Sstevel@tonic-gate */ 43700Sstevel@tonic-gate 43710Sstevel@tonic-gate static int 43720Sstevel@tonic-gate cmd_match(char *cmd) 43730Sstevel@tonic-gate { 43740Sstevel@tonic-gate int i; 43750Sstevel@tonic-gate 43760Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 43770Sstevel@tonic-gate /* return only if there is an exact match */ 43780Sstevel@tonic-gate if (strcmp(cmd, cmdtab[i].cmd_name) == 0) 43790Sstevel@tonic-gate return (cmdtab[i].cmd_num); 43800Sstevel@tonic-gate } 43810Sstevel@tonic-gate return (-1); 43820Sstevel@tonic-gate } 43830Sstevel@tonic-gate 43840Sstevel@tonic-gate static int 43850Sstevel@tonic-gate parse_and_run(int argc, char *argv[]) 43860Sstevel@tonic-gate { 43870Sstevel@tonic-gate int i = cmd_match(argv[0]); 43880Sstevel@tonic-gate 43890Sstevel@tonic-gate if (i < 0) 43900Sstevel@tonic-gate return (usage(B_FALSE)); 43910Sstevel@tonic-gate return (cmdtab[i].handler(argc - 1, &(argv[1]))); 43920Sstevel@tonic-gate } 43930Sstevel@tonic-gate 43940Sstevel@tonic-gate static char * 43950Sstevel@tonic-gate get_execbasename(char *execfullname) 43960Sstevel@tonic-gate { 43970Sstevel@tonic-gate char *last_slash, *execbasename; 43980Sstevel@tonic-gate 43990Sstevel@tonic-gate /* guard against '/' at end of command invocation */ 44000Sstevel@tonic-gate for (;;) { 44010Sstevel@tonic-gate last_slash = strrchr(execfullname, '/'); 44020Sstevel@tonic-gate if (last_slash == NULL) { 44030Sstevel@tonic-gate execbasename = execfullname; 44040Sstevel@tonic-gate break; 44050Sstevel@tonic-gate } else { 44060Sstevel@tonic-gate execbasename = last_slash + 1; 44070Sstevel@tonic-gate if (*execbasename == '\0') { 44080Sstevel@tonic-gate *last_slash = '\0'; 44090Sstevel@tonic-gate continue; 44100Sstevel@tonic-gate } 44110Sstevel@tonic-gate break; 44120Sstevel@tonic-gate } 44130Sstevel@tonic-gate } 44140Sstevel@tonic-gate return (execbasename); 44150Sstevel@tonic-gate } 44160Sstevel@tonic-gate 44170Sstevel@tonic-gate int 44180Sstevel@tonic-gate main(int argc, char **argv) 44190Sstevel@tonic-gate { 44200Sstevel@tonic-gate int arg; 44210Sstevel@tonic-gate zoneid_t zid; 4422766Scarlsonj struct stat st; 44230Sstevel@tonic-gate 44240Sstevel@tonic-gate if ((locale = setlocale(LC_ALL, "")) == NULL) 44250Sstevel@tonic-gate locale = "C"; 44260Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 44270Sstevel@tonic-gate setbuf(stdout, NULL); 44280Sstevel@tonic-gate (void) sigset(SIGHUP, SIG_IGN); 44290Sstevel@tonic-gate execname = get_execbasename(argv[0]); 44300Sstevel@tonic-gate target_zone = NULL; 44310Sstevel@tonic-gate if (chdir("/") != 0) { 44320Sstevel@tonic-gate zerror(gettext("could not change directory to /.")); 44330Sstevel@tonic-gate exit(Z_ERR); 44340Sstevel@tonic-gate } 44350Sstevel@tonic-gate 4436*2082Seschrock if (init_zfs() != Z_OK) 4437*2082Seschrock exit(Z_ERR); 4438*2082Seschrock 4439766Scarlsonj while ((arg = getopt(argc, argv, "?z:R:")) != EOF) { 44400Sstevel@tonic-gate switch (arg) { 44410Sstevel@tonic-gate case '?': 44420Sstevel@tonic-gate return (usage(B_TRUE)); 44430Sstevel@tonic-gate case 'z': 44440Sstevel@tonic-gate target_zone = optarg; 44450Sstevel@tonic-gate break; 4446766Scarlsonj case 'R': /* private option for admin/install use */ 4447766Scarlsonj if (*optarg != '/') { 4448766Scarlsonj zerror(gettext("root path must be absolute.")); 4449766Scarlsonj exit(Z_ERR); 4450766Scarlsonj } 4451766Scarlsonj if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { 4452766Scarlsonj zerror( 4453766Scarlsonj gettext("root path must be a directory.")); 4454766Scarlsonj exit(Z_ERR); 4455766Scarlsonj } 4456766Scarlsonj zonecfg_set_root(optarg); 4457766Scarlsonj break; 44580Sstevel@tonic-gate default: 44590Sstevel@tonic-gate return (usage(B_FALSE)); 44600Sstevel@tonic-gate } 44610Sstevel@tonic-gate } 44620Sstevel@tonic-gate 44630Sstevel@tonic-gate if (optind >= argc) 44640Sstevel@tonic-gate return (usage(B_FALSE)); 44650Sstevel@tonic-gate if (target_zone != NULL && zone_get_id(target_zone, &zid) != 0) { 44660Sstevel@tonic-gate errno = Z_NO_ZONE; 44670Sstevel@tonic-gate zperror(target_zone, B_TRUE); 44680Sstevel@tonic-gate exit(Z_ERR); 44690Sstevel@tonic-gate } 44700Sstevel@tonic-gate return (parse_and_run(argc - optind, &argv[optind])); 44710Sstevel@tonic-gate } 4472