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/utsname.h> 590Sstevel@tonic-gate #include <sys/param.h> 600Sstevel@tonic-gate #include <sys/types.h> 610Sstevel@tonic-gate #include <sys/stat.h> 620Sstevel@tonic-gate #include <sys/statvfs.h> 630Sstevel@tonic-gate #include <assert.h> 640Sstevel@tonic-gate #include <sys/sockio.h> 650Sstevel@tonic-gate #include <sys/mntent.h> 660Sstevel@tonic-gate #include <limits.h> 67789Sahrens #include <libzfs.h> 680Sstevel@tonic-gate 690Sstevel@tonic-gate #include <fcntl.h> 700Sstevel@tonic-gate #include <door.h> 710Sstevel@tonic-gate #include <macros.h> 720Sstevel@tonic-gate #include <libgen.h> 731300Sgjelinek #include <fnmatch.h> 740Sstevel@tonic-gate 750Sstevel@tonic-gate #include <pool.h> 760Sstevel@tonic-gate #include <sys/pool.h> 770Sstevel@tonic-gate 780Sstevel@tonic-gate #define MAXARGS 8 790Sstevel@tonic-gate 800Sstevel@tonic-gate /* Reflects kernel zone entries */ 810Sstevel@tonic-gate typedef struct zone_entry { 820Sstevel@tonic-gate zoneid_t zid; 830Sstevel@tonic-gate char zname[ZONENAME_MAX]; 840Sstevel@tonic-gate char *zstate_str; 850Sstevel@tonic-gate zone_state_t zstate_num; 860Sstevel@tonic-gate char zroot[MAXPATHLEN]; 870Sstevel@tonic-gate } zone_entry_t; 880Sstevel@tonic-gate 890Sstevel@tonic-gate static zone_entry_t *zents; 900Sstevel@tonic-gate static size_t nzents; 910Sstevel@tonic-gate 920Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 930Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 940Sstevel@tonic-gate #endif 950Sstevel@tonic-gate 960Sstevel@tonic-gate #define Z_ERR 1 970Sstevel@tonic-gate #define Z_USAGE 2 980Sstevel@tonic-gate 990Sstevel@tonic-gate /* 0755 is the default directory mode. */ 1000Sstevel@tonic-gate #define DEFAULT_DIR_MODE \ 1010Sstevel@tonic-gate (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate #define CMD_HELP 0 1040Sstevel@tonic-gate #define CMD_BOOT 1 1050Sstevel@tonic-gate #define CMD_HALT 2 1060Sstevel@tonic-gate #define CMD_READY 3 1070Sstevel@tonic-gate #define CMD_REBOOT 4 1080Sstevel@tonic-gate #define CMD_LIST 5 1090Sstevel@tonic-gate #define CMD_VERIFY 6 1100Sstevel@tonic-gate #define CMD_INSTALL 7 1110Sstevel@tonic-gate #define CMD_UNINSTALL 8 112766Scarlsonj #define CMD_MOUNT 9 113766Scarlsonj #define CMD_UNMOUNT 10 1141300Sgjelinek #define CMD_CLONE 11 1151300Sgjelinek #define CMD_MOVE 12 1161507Sgjelinek #define CMD_DETACH 13 1171507Sgjelinek #define CMD_ATTACH 14 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate #define CMD_MIN CMD_HELP 1201507Sgjelinek #define CMD_MAX CMD_ATTACH 1211300Sgjelinek 1220Sstevel@tonic-gate struct cmd { 1230Sstevel@tonic-gate uint_t cmd_num; /* command number */ 1240Sstevel@tonic-gate char *cmd_name; /* command name */ 1250Sstevel@tonic-gate char *short_usage; /* short form help */ 1260Sstevel@tonic-gate int (*handler)(int argc, char *argv[]); /* function to call */ 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate }; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate #define SHELP_HELP "help" 1310Sstevel@tonic-gate #define SHELP_BOOT "boot [-s]" 1320Sstevel@tonic-gate #define SHELP_HALT "halt" 1330Sstevel@tonic-gate #define SHELP_READY "ready" 1340Sstevel@tonic-gate #define SHELP_REBOOT "reboot" 1350Sstevel@tonic-gate #define SHELP_LIST "list [-cipv]" 1360Sstevel@tonic-gate #define SHELP_VERIFY "verify" 1370Sstevel@tonic-gate #define SHELP_INSTALL "install" 1380Sstevel@tonic-gate #define SHELP_UNINSTALL "uninstall [-F]" 1391300Sgjelinek #define SHELP_CLONE "clone [-m method] zonename" 1401300Sgjelinek #define SHELP_MOVE "move zonepath" 1411507Sgjelinek #define SHELP_DETACH "detach" 1421507Sgjelinek #define SHELP_ATTACH "attach [-F]" 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate static int help_func(int argc, char *argv[]); 1450Sstevel@tonic-gate static int ready_func(int argc, char *argv[]); 1460Sstevel@tonic-gate static int boot_func(int argc, char *argv[]); 1470Sstevel@tonic-gate static int halt_func(int argc, char *argv[]); 1480Sstevel@tonic-gate static int reboot_func(int argc, char *argv[]); 1490Sstevel@tonic-gate static int list_func(int argc, char *argv[]); 1500Sstevel@tonic-gate static int verify_func(int argc, char *argv[]); 1510Sstevel@tonic-gate static int install_func(int argc, char *argv[]); 1520Sstevel@tonic-gate static int uninstall_func(int argc, char *argv[]); 153766Scarlsonj static int mount_func(int argc, char *argv[]); 154766Scarlsonj static int unmount_func(int argc, char *argv[]); 1551300Sgjelinek static int clone_func(int argc, char *argv[]); 1561300Sgjelinek static int move_func(int argc, char *argv[]); 1571507Sgjelinek static int detach_func(int argc, char *argv[]); 1581507Sgjelinek static int attach_func(int argc, char *argv[]); 1590Sstevel@tonic-gate static int sanity_check(char *zone, int cmd_num, boolean_t running, 1600Sstevel@tonic-gate boolean_t unsafe_when_running); 1610Sstevel@tonic-gate static int cmd_match(char *cmd); 1620Sstevel@tonic-gate static int verify_details(int); 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate static struct cmd cmdtab[] = { 1650Sstevel@tonic-gate { CMD_HELP, "help", SHELP_HELP, help_func }, 1660Sstevel@tonic-gate { CMD_BOOT, "boot", SHELP_BOOT, boot_func }, 1670Sstevel@tonic-gate { CMD_HALT, "halt", SHELP_HALT, halt_func }, 1680Sstevel@tonic-gate { CMD_READY, "ready", SHELP_READY, ready_func }, 1690Sstevel@tonic-gate { CMD_REBOOT, "reboot", SHELP_REBOOT, reboot_func }, 1700Sstevel@tonic-gate { CMD_LIST, "list", SHELP_LIST, list_func }, 1710Sstevel@tonic-gate { CMD_VERIFY, "verify", SHELP_VERIFY, verify_func }, 1720Sstevel@tonic-gate { CMD_INSTALL, "install", SHELP_INSTALL, install_func }, 1730Sstevel@tonic-gate { CMD_UNINSTALL, "uninstall", SHELP_UNINSTALL, 174766Scarlsonj uninstall_func }, 1751300Sgjelinek /* mount and unmount are private commands for admin/install */ 176766Scarlsonj { CMD_MOUNT, "mount", NULL, mount_func }, 1771300Sgjelinek { CMD_UNMOUNT, "unmount", NULL, unmount_func }, 1781300Sgjelinek { CMD_CLONE, "clone", SHELP_CLONE, clone_func }, 1791507Sgjelinek { CMD_MOVE, "move", SHELP_MOVE, move_func }, 1801507Sgjelinek { CMD_DETACH, "detach", SHELP_DETACH, detach_func }, 1811507Sgjelinek { CMD_ATTACH, "attach", SHELP_ATTACH, attach_func } 1820Sstevel@tonic-gate }; 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate /* global variables */ 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* set early in main(), never modified thereafter, used all over the place */ 1870Sstevel@tonic-gate static char *execname; 1880Sstevel@tonic-gate static char *target_zone; 1890Sstevel@tonic-gate static char *locale; 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate /* used in do_subproc() and signal handler */ 1920Sstevel@tonic-gate static volatile boolean_t child_killed; 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate static char * 1950Sstevel@tonic-gate cmd_to_str(int cmd_num) 1960Sstevel@tonic-gate { 1970Sstevel@tonic-gate assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 1980Sstevel@tonic-gate return (cmdtab[cmd_num].cmd_name); 1990Sstevel@tonic-gate } 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate /* This is a separate function because of gettext() wrapping. */ 2020Sstevel@tonic-gate static char * 2030Sstevel@tonic-gate long_help(int cmd_num) 2040Sstevel@tonic-gate { 205222Scomay assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); 2060Sstevel@tonic-gate switch (cmd_num) { 2071634Sgjelinek case CMD_HELP: 2081634Sgjelinek return (gettext("Print usage message.")); 2091634Sgjelinek case CMD_BOOT: 2101634Sgjelinek return (gettext("Activates (boots) specified zone. " 2111634Sgjelinek "The -s flag can be used\n\tto boot the zone in " 2121634Sgjelinek "the single-user state.")); 2131634Sgjelinek case CMD_HALT: 2141634Sgjelinek return (gettext("Halts specified zone, bypassing shutdown " 2151634Sgjelinek "scripts and removing runtime\n\tresources of the zone.")); 2161634Sgjelinek case CMD_READY: 2171634Sgjelinek return (gettext("Prepares a zone for running applications but " 2181634Sgjelinek "does not start any user\n\tprocesses in the zone.")); 2191634Sgjelinek case CMD_REBOOT: 2201634Sgjelinek return (gettext("Restarts the zone (equivalent to a halt / " 2211634Sgjelinek "boot sequence).\n\tFails if the zone is not active.")); 2221634Sgjelinek case CMD_LIST: 2231634Sgjelinek return (gettext("Lists the current zones, or a " 2241634Sgjelinek "specific zone if indicated. By default,\n\tall " 2251634Sgjelinek "running zones are listed, though this can be " 2261634Sgjelinek "expanded to all\n\tinstalled zones with the -i " 2271634Sgjelinek "option or all configured zones with the\n\t-c " 2281634Sgjelinek "option. When used with the general -z <zone> " 2291634Sgjelinek "option, lists only the\n\tspecified zone, but " 2301634Sgjelinek "lists it regardless of its state, and the -i " 2311634Sgjelinek "and -c\n\toptions are disallowed. The -v option " 2321634Sgjelinek "can be used to display verbose\n\tinformation: " 2331634Sgjelinek "zone name, id, current state, root directory and " 2341634Sgjelinek "options.\n\tThe -p option can be used to request " 2351634Sgjelinek "machine-parsable output. The -v\n\tand -p " 2361634Sgjelinek "options are mutually exclusive. If neither -v " 2371634Sgjelinek "nor -p is used,\n\tjust the zone name is listed.")); 2381634Sgjelinek case CMD_VERIFY: 2391634Sgjelinek return (gettext("Check to make sure the configuration " 2401634Sgjelinek "can safely be instantiated\n\ton the machine: " 2411634Sgjelinek "physical network interfaces exist, etc.")); 2421634Sgjelinek case CMD_INSTALL: 2431634Sgjelinek return (gettext("Install the configuration on to the system.")); 2441634Sgjelinek case CMD_UNINSTALL: 2451634Sgjelinek return (gettext("Uninstall the configuration from the system. " 2461634Sgjelinek "The -F flag can be used\n\tto force the action.")); 2471634Sgjelinek case CMD_CLONE: 2481634Sgjelinek return (gettext("Clone the installation of another zone.")); 2491634Sgjelinek case CMD_MOVE: 2501634Sgjelinek return (gettext("Move the zone to a new zonepath.")); 2511634Sgjelinek case CMD_DETACH: 2521634Sgjelinek return (gettext("Detach the zone from the system. The zone " 2531634Sgjelinek "state is changed to\n\t'configured' (but the files under " 2541634Sgjelinek "the zonepath are untouched).\n\tThe zone can subsequently " 2551634Sgjelinek "be attached, or can be moved to another\n\tsystem and " 2561634Sgjelinek "attached there.")); 2571634Sgjelinek case CMD_ATTACH: 2581634Sgjelinek return (gettext("Attach the zone to the system. The zone " 2591634Sgjelinek "state must be 'configured'\n\tprior to attach; upon " 2601634Sgjelinek "successful completion, the zone state will be\n\t" 2611634Sgjelinek "'installed'. The system software on the current " 2621634Sgjelinek "system must be\n\tcompatible with the software on the " 2631634Sgjelinek "zone's original system.\n\tSpecify -F to force the attach " 2641634Sgjelinek "and skip software compatibility tests.")); 2651634Sgjelinek default: 2661634Sgjelinek return (""); 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate /* NOTREACHED */ 269222Scomay return (NULL); 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate /* 2730Sstevel@tonic-gate * Called with explicit B_TRUE when help is explicitly requested, B_FALSE for 2740Sstevel@tonic-gate * unexpected errors. 2750Sstevel@tonic-gate */ 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate static int 2780Sstevel@tonic-gate usage(boolean_t explicit) 2790Sstevel@tonic-gate { 2800Sstevel@tonic-gate int i; 2810Sstevel@tonic-gate FILE *fd = explicit ? stdout : stderr; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate (void) fprintf(fd, "%s:\t%s help\n", gettext("usage"), execname); 2840Sstevel@tonic-gate (void) fprintf(fd, "\t%s [-z <zone>] list\n", execname); 2850Sstevel@tonic-gate (void) fprintf(fd, "\t%s -z <zone> <%s>\n", execname, 2860Sstevel@tonic-gate gettext("subcommand")); 2870Sstevel@tonic-gate (void) fprintf(fd, "\n%s:\n\n", gettext("Subcommands")); 2880Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 289766Scarlsonj if (cmdtab[i].short_usage == NULL) 290766Scarlsonj continue; 2910Sstevel@tonic-gate (void) fprintf(fd, "%s\n", cmdtab[i].short_usage); 2920Sstevel@tonic-gate if (explicit) 2930Sstevel@tonic-gate (void) fprintf(fd, "\t%s\n\n", long_help(i)); 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate if (!explicit) 2960Sstevel@tonic-gate (void) fputs("\n", fd); 2970Sstevel@tonic-gate return (Z_USAGE); 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate static void 3010Sstevel@tonic-gate sub_usage(char *short_usage, int cmd_num) 3020Sstevel@tonic-gate { 3030Sstevel@tonic-gate (void) fprintf(stderr, "%s:\t%s\n", gettext("usage"), short_usage); 3040Sstevel@tonic-gate (void) fprintf(stderr, "\t%s\n", long_help(cmd_num)); 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate /* 3080Sstevel@tonic-gate * zperror() is like perror(3c) except that this also prints the executable 3090Sstevel@tonic-gate * name at the start of the message, and takes a boolean indicating whether 3100Sstevel@tonic-gate * to call libc'c strerror() or that from libzonecfg. 3110Sstevel@tonic-gate */ 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate static void 3140Sstevel@tonic-gate zperror(const char *str, boolean_t zonecfg_error) 3150Sstevel@tonic-gate { 3160Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", execname, str, 3170Sstevel@tonic-gate zonecfg_error ? zonecfg_strerror(errno) : strerror(errno)); 3180Sstevel@tonic-gate } 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate /* 3210Sstevel@tonic-gate * zperror2() is very similar to zperror() above, except it also prints a 3220Sstevel@tonic-gate * supplied zone name after the executable. 3230Sstevel@tonic-gate * 3240Sstevel@tonic-gate * All current consumers of this function want libzonecfg's strerror() rather 3250Sstevel@tonic-gate * than libc's; if this ever changes, this function can be made more generic 3260Sstevel@tonic-gate * like zperror() above. 3270Sstevel@tonic-gate */ 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate static void 3300Sstevel@tonic-gate zperror2(const char *zone, const char *str) 3310Sstevel@tonic-gate { 3320Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s: %s\n", execname, zone, str, 3330Sstevel@tonic-gate zonecfg_strerror(errno)); 3340Sstevel@tonic-gate } 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate /* PRINTFLIKE1 */ 3370Sstevel@tonic-gate static void 3380Sstevel@tonic-gate zerror(const char *fmt, ...) 3390Sstevel@tonic-gate { 3400Sstevel@tonic-gate va_list alist; 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate va_start(alist, fmt); 3430Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", execname); 3440Sstevel@tonic-gate if (target_zone != NULL) 3450Sstevel@tonic-gate (void) fprintf(stderr, "zone '%s': ", target_zone); 3460Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 3470Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3480Sstevel@tonic-gate va_end(alist); 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate static void * 3520Sstevel@tonic-gate safe_calloc(size_t nelem, size_t elsize) 3530Sstevel@tonic-gate { 3540Sstevel@tonic-gate void *r = calloc(nelem, elsize); 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate if (r == NULL) { 3570Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), 3580Sstevel@tonic-gate (ulong_t)nelem * elsize, strerror(errno)); 3590Sstevel@tonic-gate exit(Z_ERR); 3600Sstevel@tonic-gate } 3610Sstevel@tonic-gate return (r); 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate static void 3650Sstevel@tonic-gate zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) 3660Sstevel@tonic-gate { 3670Sstevel@tonic-gate static boolean_t firsttime = B_TRUE; 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate assert(!(verbose && parsable)); 3700Sstevel@tonic-gate if (firsttime && verbose) { 3710Sstevel@tonic-gate firsttime = B_FALSE; 3720Sstevel@tonic-gate (void) printf("%*s %-16s %-14s %-30s\n", ZONEID_WIDTH, "ID", 3730Sstevel@tonic-gate "NAME", "STATUS", "PATH"); 3740Sstevel@tonic-gate } 3750Sstevel@tonic-gate if (!verbose) { 3760Sstevel@tonic-gate if (!parsable) { 3770Sstevel@tonic-gate (void) printf("%s\n", zent->zname); 3780Sstevel@tonic-gate return; 3790Sstevel@tonic-gate } 3800Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 3810Sstevel@tonic-gate (void) printf("-"); 3820Sstevel@tonic-gate else 3830Sstevel@tonic-gate (void) printf("%lu", zent->zid); 3840Sstevel@tonic-gate (void) printf(":%s:%s:%s\n", zent->zname, zent->zstate_str, 3850Sstevel@tonic-gate zent->zroot); 3860Sstevel@tonic-gate return; 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate if (zent->zstate_str != NULL) { 3890Sstevel@tonic-gate if (zent->zid == ZONE_ID_UNDEFINED) 3900Sstevel@tonic-gate (void) printf("%*s", ZONEID_WIDTH, "-"); 3910Sstevel@tonic-gate else 3920Sstevel@tonic-gate (void) printf("%*lu", ZONEID_WIDTH, zent->zid); 3930Sstevel@tonic-gate (void) printf(" %-16s %-14s %-30s\n", zent->zname, 3940Sstevel@tonic-gate zent->zstate_str, zent->zroot); 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate static int 399766Scarlsonj lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) 4000Sstevel@tonic-gate { 401*1676Sjpk char root[MAXPATHLEN], *cp; 4020Sstevel@tonic-gate int err; 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate (void) strlcpy(zent->zname, zone_name, sizeof (zent->zname)); 4050Sstevel@tonic-gate (void) strlcpy(zent->zroot, "???", sizeof (zent->zroot)); 4060Sstevel@tonic-gate zent->zstate_str = "???"; 4070Sstevel@tonic-gate 408766Scarlsonj zent->zid = zid; 4090Sstevel@tonic-gate 410*1676Sjpk /* 411*1676Sjpk * For labeled zones which query the zone path of lower-level 412*1676Sjpk * zones, the path needs to be adjusted to drop the final 413*1676Sjpk * "/root" component. This adjusted path is then useful 414*1676Sjpk * for reading down any exported directories from the 415*1676Sjpk * lower-level zone. 416*1676Sjpk */ 417*1676Sjpk if (is_system_labeled() && zent->zid != ZONE_ID_UNDEFINED) { 418*1676Sjpk if (zone_getattr(zent->zid, ZONE_ATTR_ROOT, zent->zroot, 419*1676Sjpk sizeof (zent->zroot)) == -1) { 420*1676Sjpk zperror2(zent->zname, 421*1676Sjpk gettext("could not get zone path.")); 422*1676Sjpk return (Z_ERR); 423*1676Sjpk } 424*1676Sjpk cp = zent->zroot + strlen(zent->zroot) - 5; 425*1676Sjpk if (cp > zent->zroot && strcmp(cp, "/root") == 0) 426*1676Sjpk *cp = 0; 427*1676Sjpk } else { 428*1676Sjpk if ((err = zone_get_zonepath(zent->zname, root, 429*1676Sjpk sizeof (root))) != Z_OK) { 430*1676Sjpk errno = err; 431*1676Sjpk zperror2(zent->zname, 432*1676Sjpk gettext("could not get zone path.")); 433*1676Sjpk return (Z_ERR); 434*1676Sjpk } 435*1676Sjpk (void) strlcpy(zent->zroot, root, sizeof (zent->zroot)); 436*1676Sjpk } 4370Sstevel@tonic-gate 4380Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, &zent->zstate_num)) != Z_OK) { 4390Sstevel@tonic-gate errno = err; 4400Sstevel@tonic-gate zperror2(zent->zname, gettext("could not get state")); 4410Sstevel@tonic-gate return (Z_ERR); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate zent->zstate_str = zone_state_str(zent->zstate_num); 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate return (Z_OK); 4460Sstevel@tonic-gate } 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate /* 4490Sstevel@tonic-gate * fetch_zents() calls zone_list(2) to find out how many zones are running 4500Sstevel@tonic-gate * (which is stored in the global nzents), then calls zone_list(2) again 4510Sstevel@tonic-gate * to fetch the list of running zones (stored in the global zents). This 4520Sstevel@tonic-gate * function may be called multiple times, so if zents is already set, we 4530Sstevel@tonic-gate * return immediately to save work. 4540Sstevel@tonic-gate */ 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate static int 457766Scarlsonj fetch_zents(void) 4580Sstevel@tonic-gate { 4590Sstevel@tonic-gate zoneid_t *zids = NULL; 4600Sstevel@tonic-gate uint_t nzents_saved; 461766Scarlsonj int i, retv; 462766Scarlsonj FILE *fp; 463766Scarlsonj boolean_t inaltroot; 464766Scarlsonj zone_entry_t *zentp; 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate if (nzents > 0) 4670Sstevel@tonic-gate return (Z_OK); 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate if (zone_list(NULL, &nzents) != 0) { 4700Sstevel@tonic-gate zperror(gettext("failed to get zoneid list"), B_FALSE); 4710Sstevel@tonic-gate return (Z_ERR); 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate again: 4750Sstevel@tonic-gate if (nzents == 0) 4760Sstevel@tonic-gate return (Z_OK); 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate zids = safe_calloc(nzents, sizeof (zoneid_t)); 4790Sstevel@tonic-gate nzents_saved = nzents; 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate if (zone_list(zids, &nzents) != 0) { 4820Sstevel@tonic-gate zperror(gettext("failed to get zone list"), B_FALSE); 4830Sstevel@tonic-gate free(zids); 4840Sstevel@tonic-gate return (Z_ERR); 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate if (nzents != nzents_saved) { 4870Sstevel@tonic-gate /* list changed, try again */ 4880Sstevel@tonic-gate free(zids); 4890Sstevel@tonic-gate goto again; 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate zents = safe_calloc(nzents, sizeof (zone_entry_t)); 4930Sstevel@tonic-gate 494766Scarlsonj inaltroot = zonecfg_in_alt_root(); 495766Scarlsonj if (inaltroot) 496766Scarlsonj fp = zonecfg_open_scratch("", B_FALSE); 497766Scarlsonj else 498766Scarlsonj fp = NULL; 499766Scarlsonj zentp = zents; 500766Scarlsonj retv = Z_OK; 5010Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 5020Sstevel@tonic-gate char name[ZONENAME_MAX]; 503766Scarlsonj char altname[ZONENAME_MAX]; 5040Sstevel@tonic-gate 505766Scarlsonj if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { 5060Sstevel@tonic-gate zperror(gettext("failed to get zone name"), B_FALSE); 507766Scarlsonj retv = Z_ERR; 508766Scarlsonj continue; 509766Scarlsonj } 510766Scarlsonj if (zonecfg_is_scratch(name)) { 511766Scarlsonj /* Ignore scratch zones by default */ 512766Scarlsonj if (!inaltroot) 513766Scarlsonj continue; 514766Scarlsonj if (fp == NULL || 515766Scarlsonj zonecfg_reverse_scratch(fp, name, altname, 516766Scarlsonj sizeof (altname), NULL, 0) == -1) { 517924Sgjelinek zerror(gettext("could not resolve scratch " 518766Scarlsonj "zone %s"), name); 519766Scarlsonj retv = Z_ERR; 520766Scarlsonj continue; 521766Scarlsonj } 522766Scarlsonj (void) strcpy(name, altname); 523766Scarlsonj } else { 524766Scarlsonj /* Ignore non-scratch when in an alternate root */ 525766Scarlsonj if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0) 526766Scarlsonj continue; 527766Scarlsonj } 528766Scarlsonj if (lookup_zone_info(name, zids[i], zentp) != Z_OK) { 529766Scarlsonj zerror(gettext("failed to get zone data")); 530766Scarlsonj retv = Z_ERR; 531766Scarlsonj continue; 532766Scarlsonj } 533766Scarlsonj zentp++; 5340Sstevel@tonic-gate } 535766Scarlsonj nzents = zentp - zents; 536766Scarlsonj if (fp != NULL) 537766Scarlsonj zonecfg_close_scratch(fp); 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate free(zids); 540766Scarlsonj return (retv); 5410Sstevel@tonic-gate } 5420Sstevel@tonic-gate 543766Scarlsonj static int 5440Sstevel@tonic-gate zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable) 5450Sstevel@tonic-gate { 5460Sstevel@tonic-gate int i; 5470Sstevel@tonic-gate zone_entry_t zent; 5480Sstevel@tonic-gate FILE *cookie; 5490Sstevel@tonic-gate char *name; 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate /* 5520Sstevel@tonic-gate * First get the list of running zones from the kernel and print them. 5530Sstevel@tonic-gate * If that is all we need, then return. 5540Sstevel@tonic-gate */ 555766Scarlsonj if ((i = fetch_zents()) != Z_OK) { 5560Sstevel@tonic-gate /* 5570Sstevel@tonic-gate * No need for error messages; fetch_zents() has already taken 5580Sstevel@tonic-gate * care of this. 5590Sstevel@tonic-gate */ 560766Scarlsonj return (i); 5610Sstevel@tonic-gate } 562766Scarlsonj for (i = 0; i < nzents; i++) 5630Sstevel@tonic-gate zone_print(&zents[i], verbose, parsable); 5640Sstevel@tonic-gate if (min_state >= ZONE_STATE_RUNNING) 565766Scarlsonj return (Z_OK); 5660Sstevel@tonic-gate /* 5670Sstevel@tonic-gate * Next, get the full list of zones from the configuration, skipping 5680Sstevel@tonic-gate * any we have already printed. 5690Sstevel@tonic-gate */ 5700Sstevel@tonic-gate cookie = setzoneent(); 5710Sstevel@tonic-gate while ((name = getzoneent(cookie)) != NULL) { 5720Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 5730Sstevel@tonic-gate if (strcmp(zents[i].zname, name) == 0) 5740Sstevel@tonic-gate break; 5750Sstevel@tonic-gate } 5760Sstevel@tonic-gate if (i < nzents) { 5770Sstevel@tonic-gate free(name); 5780Sstevel@tonic-gate continue; 5790Sstevel@tonic-gate } 580766Scarlsonj if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { 5810Sstevel@tonic-gate free(name); 5820Sstevel@tonic-gate continue; 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate free(name); 5850Sstevel@tonic-gate if (zent.zstate_num >= min_state) 5860Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 5870Sstevel@tonic-gate } 5880Sstevel@tonic-gate endzoneent(cookie); 589766Scarlsonj return (Z_OK); 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate static zone_entry_t * 5930Sstevel@tonic-gate lookup_running_zone(char *str) 5940Sstevel@tonic-gate { 5950Sstevel@tonic-gate zoneid_t zoneid; 5960Sstevel@tonic-gate char *cp; 5970Sstevel@tonic-gate int i; 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate if (fetch_zents() != Z_OK) 6000Sstevel@tonic-gate return (NULL); 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6030Sstevel@tonic-gate if (strcmp(str, zents[i].zname) == 0) 6040Sstevel@tonic-gate return (&zents[i]); 6050Sstevel@tonic-gate } 6060Sstevel@tonic-gate errno = 0; 6070Sstevel@tonic-gate zoneid = strtol(str, &cp, 0); 6080Sstevel@tonic-gate if (zoneid < MIN_ZONEID || zoneid > MAX_ZONEID || 6090Sstevel@tonic-gate errno != 0 || *cp != '\0') 6100Sstevel@tonic-gate return (NULL); 6110Sstevel@tonic-gate for (i = 0; i < nzents; i++) { 6120Sstevel@tonic-gate if (zoneid == zents[i].zid) 6130Sstevel@tonic-gate return (&zents[i]); 6140Sstevel@tonic-gate } 6150Sstevel@tonic-gate return (NULL); 6160Sstevel@tonic-gate } 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate /* 6190Sstevel@tonic-gate * Check a bit in a mode_t: if on is B_TRUE, that bit should be on; if 6200Sstevel@tonic-gate * B_FALSE, it should be off. Return B_TRUE if the mode is bad (incorrect). 6210Sstevel@tonic-gate */ 6220Sstevel@tonic-gate static boolean_t 6230Sstevel@tonic-gate bad_mode_bit(mode_t mode, mode_t bit, boolean_t on, char *file) 6240Sstevel@tonic-gate { 6250Sstevel@tonic-gate char *str; 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate assert(bit == S_IRUSR || bit == S_IWUSR || bit == S_IXUSR || 6280Sstevel@tonic-gate bit == S_IRGRP || bit == S_IWGRP || bit == S_IXGRP || 6290Sstevel@tonic-gate bit == S_IROTH || bit == S_IWOTH || bit == S_IXOTH); 6300Sstevel@tonic-gate /* 6310Sstevel@tonic-gate * TRANSLATION_NOTE 6320Sstevel@tonic-gate * The strings below will be used as part of a larger message, 6330Sstevel@tonic-gate * either: 6340Sstevel@tonic-gate * (file name) must be (owner|group|world) (read|writ|execut)able 6350Sstevel@tonic-gate * or 6360Sstevel@tonic-gate * (file name) must not be (owner|group|world) (read|writ|execut)able 6370Sstevel@tonic-gate */ 6380Sstevel@tonic-gate switch (bit) { 6390Sstevel@tonic-gate case S_IRUSR: 6400Sstevel@tonic-gate str = gettext("owner readable"); 6410Sstevel@tonic-gate break; 6420Sstevel@tonic-gate case S_IWUSR: 6430Sstevel@tonic-gate str = gettext("owner writable"); 6440Sstevel@tonic-gate break; 6450Sstevel@tonic-gate case S_IXUSR: 6460Sstevel@tonic-gate str = gettext("owner executable"); 6470Sstevel@tonic-gate break; 6480Sstevel@tonic-gate case S_IRGRP: 6490Sstevel@tonic-gate str = gettext("group readable"); 6500Sstevel@tonic-gate break; 6510Sstevel@tonic-gate case S_IWGRP: 6520Sstevel@tonic-gate str = gettext("group writable"); 6530Sstevel@tonic-gate break; 6540Sstevel@tonic-gate case S_IXGRP: 6550Sstevel@tonic-gate str = gettext("group executable"); 6560Sstevel@tonic-gate break; 6570Sstevel@tonic-gate case S_IROTH: 6580Sstevel@tonic-gate str = gettext("world readable"); 6590Sstevel@tonic-gate break; 6600Sstevel@tonic-gate case S_IWOTH: 6610Sstevel@tonic-gate str = gettext("world writable"); 6620Sstevel@tonic-gate break; 6630Sstevel@tonic-gate case S_IXOTH: 6640Sstevel@tonic-gate str = gettext("world executable"); 6650Sstevel@tonic-gate break; 6660Sstevel@tonic-gate } 6670Sstevel@tonic-gate if ((mode & bit) == (on ? 0 : bit)) { 6680Sstevel@tonic-gate /* 6690Sstevel@tonic-gate * TRANSLATION_NOTE 6700Sstevel@tonic-gate * The first parameter below is a file name; the second 6710Sstevel@tonic-gate * is one of the "(owner|group|world) (read|writ|execut)able" 6720Sstevel@tonic-gate * strings from above. 6730Sstevel@tonic-gate */ 6740Sstevel@tonic-gate /* 6750Sstevel@tonic-gate * The code below could be simplified but not in a way 6760Sstevel@tonic-gate * that would easily translate to non-English locales. 6770Sstevel@tonic-gate */ 6780Sstevel@tonic-gate if (on) { 6790Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must be %s.\n"), 6800Sstevel@tonic-gate file, str); 6810Sstevel@tonic-gate } else { 6820Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s must not be %s.\n"), 6830Sstevel@tonic-gate file, str); 6840Sstevel@tonic-gate } 6850Sstevel@tonic-gate return (B_TRUE); 6860Sstevel@tonic-gate } 6870Sstevel@tonic-gate return (B_FALSE); 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate /* 6910Sstevel@tonic-gate * We want to make sure that no zone has its zone path as a child node 6920Sstevel@tonic-gate * (in the directory sense) of any other. We do that by comparing this 6930Sstevel@tonic-gate * zone's path to the path of all other (non-global) zones. The comparison 6940Sstevel@tonic-gate * in each case is simple: add '/' to the end of the path, then do a 6950Sstevel@tonic-gate * strncmp() of the two paths, using the length of the shorter one. 6960Sstevel@tonic-gate */ 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate static int 6990Sstevel@tonic-gate crosscheck_zonepaths(char *path) 7000Sstevel@tonic-gate { 7010Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 7020Sstevel@tonic-gate char path_copy[MAXPATHLEN]; /* copy of original path */ 7030Sstevel@tonic-gate char rpath_copy[MAXPATHLEN]; /* copy of original rpath */ 7040Sstevel@tonic-gate struct zoneent *ze; 7050Sstevel@tonic-gate int res, err; 7060Sstevel@tonic-gate FILE *cookie; 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate cookie = setzoneent(); 7090Sstevel@tonic-gate while ((ze = getzoneent_private(cookie)) != NULL) { 7100Sstevel@tonic-gate /* Skip zones which are not installed. */ 7110Sstevel@tonic-gate if (ze->zone_state < ZONE_STATE_INSTALLED) { 7120Sstevel@tonic-gate free(ze); 7130Sstevel@tonic-gate continue; 7140Sstevel@tonic-gate } 7150Sstevel@tonic-gate /* Skip the global zone and the current target zone. */ 7160Sstevel@tonic-gate if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0 || 7170Sstevel@tonic-gate strcmp(ze->zone_name, target_zone) == 0) { 7180Sstevel@tonic-gate free(ze); 7190Sstevel@tonic-gate continue; 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate if (strlen(ze->zone_path) == 0) { 7220Sstevel@tonic-gate /* old index file without path, fall back */ 7230Sstevel@tonic-gate if ((err = zone_get_zonepath(ze->zone_name, 7240Sstevel@tonic-gate ze->zone_path, sizeof (ze->zone_path))) != Z_OK) { 7250Sstevel@tonic-gate errno = err; 7260Sstevel@tonic-gate zperror2(ze->zone_name, 7270Sstevel@tonic-gate gettext("could not get zone path")); 7280Sstevel@tonic-gate free(ze); 7290Sstevel@tonic-gate continue; 7300Sstevel@tonic-gate } 7310Sstevel@tonic-gate } 732766Scarlsonj (void) snprintf(path_copy, sizeof (path_copy), "%s%s", 733766Scarlsonj zonecfg_get_root(), ze->zone_path); 734766Scarlsonj res = resolvepath(path_copy, rpath, sizeof (rpath)); 7350Sstevel@tonic-gate if (res == -1) { 7360Sstevel@tonic-gate if (errno != ENOENT) { 737766Scarlsonj zperror(path_copy, B_FALSE); 7380Sstevel@tonic-gate free(ze); 7390Sstevel@tonic-gate return (Z_ERR); 7400Sstevel@tonic-gate } 7410Sstevel@tonic-gate (void) printf(gettext("WARNING: zone %s is installed, " 7420Sstevel@tonic-gate "but its %s %s does not exist.\n"), ze->zone_name, 743766Scarlsonj "zonepath", path_copy); 7440Sstevel@tonic-gate free(ze); 7450Sstevel@tonic-gate continue; 7460Sstevel@tonic-gate } 7470Sstevel@tonic-gate rpath[res] = '\0'; 7480Sstevel@tonic-gate (void) snprintf(path_copy, sizeof (path_copy), "%s/", path); 7490Sstevel@tonic-gate (void) snprintf(rpath_copy, sizeof (rpath_copy), "%s/", rpath); 7500Sstevel@tonic-gate if (strncmp(path_copy, rpath_copy, 7510Sstevel@tonic-gate min(strlen(path_copy), strlen(rpath_copy))) == 0) { 752924Sgjelinek /* 753924Sgjelinek * TRANSLATION_NOTE 754924Sgjelinek * zonepath is a literal that should not be translated. 755924Sgjelinek */ 7560Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s zonepath (%s) and " 7570Sstevel@tonic-gate "%s zonepath (%s) overlap.\n"), 7580Sstevel@tonic-gate target_zone, path, ze->zone_name, rpath); 7590Sstevel@tonic-gate free(ze); 7600Sstevel@tonic-gate return (Z_ERR); 7610Sstevel@tonic-gate } 7620Sstevel@tonic-gate free(ze); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate endzoneent(cookie); 7650Sstevel@tonic-gate return (Z_OK); 7660Sstevel@tonic-gate } 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate static int 7690Sstevel@tonic-gate validate_zonepath(char *path, int cmd_num) 7700Sstevel@tonic-gate { 7710Sstevel@tonic-gate int res; /* result of last library/system call */ 7720Sstevel@tonic-gate boolean_t err = B_FALSE; /* have we run into an error? */ 7730Sstevel@tonic-gate struct stat stbuf; 7740Sstevel@tonic-gate struct statvfs vfsbuf; 7750Sstevel@tonic-gate char rpath[MAXPATHLEN]; /* resolved path */ 7760Sstevel@tonic-gate char ppath[MAXPATHLEN]; /* parent path */ 7770Sstevel@tonic-gate char rppath[MAXPATHLEN]; /* resolved parent path */ 7780Sstevel@tonic-gate char rootpath[MAXPATHLEN]; /* root path */ 7790Sstevel@tonic-gate zone_state_t state; 7800Sstevel@tonic-gate 7810Sstevel@tonic-gate if (path[0] != '/') { 7820Sstevel@tonic-gate (void) fprintf(stderr, 7830Sstevel@tonic-gate gettext("%s is not an absolute path.\n"), path); 7840Sstevel@tonic-gate return (Z_ERR); 7850Sstevel@tonic-gate } 7860Sstevel@tonic-gate if ((res = resolvepath(path, rpath, sizeof (rpath))) == -1) { 7870Sstevel@tonic-gate if ((errno != ENOENT) || 7881300Sgjelinek (cmd_num != CMD_VERIFY && cmd_num != CMD_INSTALL && 7891300Sgjelinek cmd_num != CMD_CLONE && cmd_num != CMD_MOVE)) { 7900Sstevel@tonic-gate zperror(path, B_FALSE); 7910Sstevel@tonic-gate return (Z_ERR); 7920Sstevel@tonic-gate } 7930Sstevel@tonic-gate if (cmd_num == CMD_VERIFY) { 794924Sgjelinek /* 795924Sgjelinek * TRANSLATION_NOTE 796924Sgjelinek * zoneadm is a literal that should not be translated. 797924Sgjelinek */ 7980Sstevel@tonic-gate (void) fprintf(stderr, gettext("WARNING: %s does not " 799924Sgjelinek "exist, so it could not be verified.\nWhen " 800924Sgjelinek "'zoneadm %s' is run, '%s' will try to create\n%s, " 801924Sgjelinek "and '%s' will be tried again,\nbut the '%s' may " 802924Sgjelinek "fail if:\nthe parent directory of %s is group- or " 803924Sgjelinek "other-writable\nor\n%s overlaps with any other " 8040Sstevel@tonic-gate "installed zones.\n"), path, 8050Sstevel@tonic-gate cmd_to_str(CMD_INSTALL), cmd_to_str(CMD_INSTALL), 8060Sstevel@tonic-gate path, cmd_to_str(CMD_VERIFY), 8070Sstevel@tonic-gate cmd_to_str(CMD_VERIFY), path, path); 8080Sstevel@tonic-gate return (Z_OK); 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate /* 8110Sstevel@tonic-gate * The zonepath is supposed to be mode 700 but its 8120Sstevel@tonic-gate * parent(s) 755. So use 755 on the mkdirp() then 8130Sstevel@tonic-gate * chmod() the zonepath itself to 700. 8140Sstevel@tonic-gate */ 8150Sstevel@tonic-gate if (mkdirp(path, DEFAULT_DIR_MODE) < 0) { 8160Sstevel@tonic-gate zperror(path, B_FALSE); 8170Sstevel@tonic-gate return (Z_ERR); 8180Sstevel@tonic-gate } 8190Sstevel@tonic-gate /* 8200Sstevel@tonic-gate * If the chmod() fails, report the error, but might 8210Sstevel@tonic-gate * as well continue the verify procedure. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate if (chmod(path, S_IRWXU) != 0) 8240Sstevel@tonic-gate zperror(path, B_FALSE); 8250Sstevel@tonic-gate /* 8260Sstevel@tonic-gate * Since the mkdir() succeeded, we should not have to 8270Sstevel@tonic-gate * worry about a subsequent ENOENT, thus this should 8280Sstevel@tonic-gate * only recurse once. 8290Sstevel@tonic-gate */ 8301300Sgjelinek return (validate_zonepath(path, cmd_num)); 8310Sstevel@tonic-gate } 8320Sstevel@tonic-gate rpath[res] = '\0'; 8330Sstevel@tonic-gate if (strcmp(path, rpath) != 0) { 8340Sstevel@tonic-gate errno = Z_RESOLVED_PATH; 8350Sstevel@tonic-gate zperror(path, B_TRUE); 8360Sstevel@tonic-gate return (Z_ERR); 8370Sstevel@tonic-gate } 8380Sstevel@tonic-gate if ((res = stat(rpath, &stbuf)) != 0) { 8390Sstevel@tonic-gate zperror(rpath, B_FALSE); 8400Sstevel@tonic-gate return (Z_ERR); 8410Sstevel@tonic-gate } 8420Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 8430Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 8440Sstevel@tonic-gate rpath); 8450Sstevel@tonic-gate return (Z_ERR); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate if ((strcmp(stbuf.st_fstype, MNTTYPE_TMPFS) == 0) || 8480Sstevel@tonic-gate (strcmp(stbuf.st_fstype, MNTTYPE_XMEMFS) == 0)) { 8490Sstevel@tonic-gate (void) printf(gettext("WARNING: %s is on a temporary " 8500Sstevel@tonic-gate "file-system.\n"), rpath); 8510Sstevel@tonic-gate } 8520Sstevel@tonic-gate if (crosscheck_zonepaths(rpath) != Z_OK) 8530Sstevel@tonic-gate return (Z_ERR); 8540Sstevel@tonic-gate /* 8550Sstevel@tonic-gate * Try to collect and report as many minor errors as possible 8560Sstevel@tonic-gate * before returning, so the user can learn everything that needs 8570Sstevel@tonic-gate * to be fixed up front. 8580Sstevel@tonic-gate */ 8590Sstevel@tonic-gate if (stbuf.st_uid != 0) { 8600Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 8610Sstevel@tonic-gate rpath); 8620Sstevel@tonic-gate err = B_TRUE; 8630Sstevel@tonic-gate } 8640Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rpath); 8650Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rpath); 8660Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rpath); 8670Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRGRP, B_FALSE, rpath); 8680Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rpath); 8690Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXGRP, B_FALSE, rpath); 8700Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IROTH, B_FALSE, rpath); 8710Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rpath); 8720Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXOTH, B_FALSE, rpath); 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate (void) snprintf(ppath, sizeof (ppath), "%s/..", path); 8750Sstevel@tonic-gate if ((res = resolvepath(ppath, rppath, sizeof (rppath))) == -1) { 8760Sstevel@tonic-gate zperror(ppath, B_FALSE); 8770Sstevel@tonic-gate return (Z_ERR); 8780Sstevel@tonic-gate } 8790Sstevel@tonic-gate rppath[res] = '\0'; 8800Sstevel@tonic-gate if ((res = stat(rppath, &stbuf)) != 0) { 8810Sstevel@tonic-gate zperror(rppath, B_FALSE); 8820Sstevel@tonic-gate return (Z_ERR); 8830Sstevel@tonic-gate } 8840Sstevel@tonic-gate /* theoretically impossible */ 8850Sstevel@tonic-gate if (!S_ISDIR(stbuf.st_mode)) { 8860Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not a directory.\n"), 8870Sstevel@tonic-gate rppath); 8880Sstevel@tonic-gate return (Z_ERR); 8890Sstevel@tonic-gate } 8900Sstevel@tonic-gate if (stbuf.st_uid != 0) { 8910Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not owned by root.\n"), 8920Sstevel@tonic-gate rppath); 8930Sstevel@tonic-gate err = B_TRUE; 8940Sstevel@tonic-gate } 8950Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IRUSR, B_TRUE, rppath); 8960Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWUSR, B_TRUE, rppath); 8970Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IXUSR, B_TRUE, rppath); 8980Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWGRP, B_FALSE, rppath); 8990Sstevel@tonic-gate err |= bad_mode_bit(stbuf.st_mode, S_IWOTH, B_FALSE, rppath); 9000Sstevel@tonic-gate if (strcmp(rpath, rppath) == 0) { 9010Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is its own parent.\n"), 9020Sstevel@tonic-gate rppath); 9030Sstevel@tonic-gate err = B_TRUE; 9040Sstevel@tonic-gate } 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate if (statvfs(rpath, &vfsbuf) != 0) { 9070Sstevel@tonic-gate zperror(rpath, B_FALSE); 9080Sstevel@tonic-gate return (Z_ERR); 9090Sstevel@tonic-gate } 910823Sgjelinek if (strcmp(vfsbuf.f_basetype, MNTTYPE_NFS) == 0) { 911924Sgjelinek /* 912924Sgjelinek * TRANSLATION_NOTE 913924Sgjelinek * Zonepath and NFS are literals that should not be translated. 914924Sgjelinek */ 915924Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on an NFS " 916924Sgjelinek "mounted file-system.\n" 917924Sgjelinek "\tA local file-system must be used.\n"), rpath); 918823Sgjelinek return (Z_ERR); 919823Sgjelinek } 920823Sgjelinek if (vfsbuf.f_flag & ST_NOSUID) { 921924Sgjelinek /* 922924Sgjelinek * TRANSLATION_NOTE 923924Sgjelinek * Zonepath and nosuid are literals that should not be 924924Sgjelinek * translated. 925924Sgjelinek */ 926823Sgjelinek (void) fprintf(stderr, gettext("Zonepath %s is on a nosuid " 927924Sgjelinek "file-system.\n"), rpath); 9280Sstevel@tonic-gate return (Z_ERR); 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate if ((res = zone_get_state(target_zone, &state)) != Z_OK) { 9320Sstevel@tonic-gate errno = res; 9330Sstevel@tonic-gate zperror2(target_zone, gettext("could not get state")); 9340Sstevel@tonic-gate return (Z_ERR); 9350Sstevel@tonic-gate } 9360Sstevel@tonic-gate /* 9370Sstevel@tonic-gate * The existence of the root path is only bad in the configured state, 9380Sstevel@tonic-gate * as it is *supposed* to be there at the installed and later states. 9391507Sgjelinek * However, the root path is expected to be there if the zone is 9401507Sgjelinek * detached. 9410Sstevel@tonic-gate * State/command mismatches are caught earlier in verify_details(). 9420Sstevel@tonic-gate */ 9431507Sgjelinek if (state == ZONE_STATE_CONFIGURED && cmd_num != CMD_ATTACH) { 9440Sstevel@tonic-gate if (snprintf(rootpath, sizeof (rootpath), "%s/root", rpath) >= 9450Sstevel@tonic-gate sizeof (rootpath)) { 946924Sgjelinek /* 947924Sgjelinek * TRANSLATION_NOTE 948924Sgjelinek * Zonepath is a literal that should not be translated. 949924Sgjelinek */ 9500Sstevel@tonic-gate (void) fprintf(stderr, 9510Sstevel@tonic-gate gettext("Zonepath %s is too long.\n"), rpath); 9520Sstevel@tonic-gate return (Z_ERR); 9530Sstevel@tonic-gate } 9540Sstevel@tonic-gate if ((res = stat(rootpath, &stbuf)) == 0) { 9551507Sgjelinek if (zonecfg_detached(rpath)) 9561507Sgjelinek (void) fprintf(stderr, 9571507Sgjelinek gettext("Cannot %s detached " 9581507Sgjelinek "zone.\nUse attach or remove %s " 9591507Sgjelinek "directory.\n"), cmd_to_str(cmd_num), 9601507Sgjelinek rpath); 9611507Sgjelinek else 9621507Sgjelinek (void) fprintf(stderr, 9631507Sgjelinek gettext("Rootpath %s exists; " 9641507Sgjelinek "remove or move aside prior to %s.\n"), 9651507Sgjelinek rootpath, cmd_to_str(cmd_num)); 9660Sstevel@tonic-gate return (Z_ERR); 9670Sstevel@tonic-gate } 9680Sstevel@tonic-gate } 9690Sstevel@tonic-gate 9700Sstevel@tonic-gate return (err ? Z_ERR : Z_OK); 9710Sstevel@tonic-gate } 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate static void 9740Sstevel@tonic-gate release_lock_file(int lockfd) 9750Sstevel@tonic-gate { 9760Sstevel@tonic-gate (void) close(lockfd); 9770Sstevel@tonic-gate } 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate static int 9800Sstevel@tonic-gate grab_lock_file(const char *zone_name, int *lockfd) 9810Sstevel@tonic-gate { 9820Sstevel@tonic-gate char pathbuf[PATH_MAX]; 9830Sstevel@tonic-gate struct flock flock; 9840Sstevel@tonic-gate 985766Scarlsonj if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(), 986766Scarlsonj ZONES_TMPDIR) >= sizeof (pathbuf)) { 987766Scarlsonj zerror(gettext("alternate root path is too long")); 988766Scarlsonj return (Z_ERR); 989766Scarlsonj } 990766Scarlsonj if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) { 991766Scarlsonj zerror(gettext("could not mkdir %s: %s"), pathbuf, 9920Sstevel@tonic-gate strerror(errno)); 9930Sstevel@tonic-gate return (Z_ERR); 9940Sstevel@tonic-gate } 995766Scarlsonj (void) chmod(pathbuf, S_IRWXU); 9960Sstevel@tonic-gate 9970Sstevel@tonic-gate /* 9980Sstevel@tonic-gate * One of these lock files is created for each zone (when needed). 9990Sstevel@tonic-gate * The lock files are not cleaned up (except on system reboot), 10000Sstevel@tonic-gate * but since there is only one per zone, there is no resource 10010Sstevel@tonic-gate * starvation issue. 10020Sstevel@tonic-gate */ 1003766Scarlsonj if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock", 1004766Scarlsonj zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) { 1005766Scarlsonj zerror(gettext("alternate root path is too long")); 1006766Scarlsonj return (Z_ERR); 1007766Scarlsonj } 10080Sstevel@tonic-gate if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 10090Sstevel@tonic-gate zerror(gettext("could not open %s: %s"), pathbuf, 10100Sstevel@tonic-gate strerror(errno)); 10110Sstevel@tonic-gate return (Z_ERR); 10120Sstevel@tonic-gate } 10130Sstevel@tonic-gate /* 10140Sstevel@tonic-gate * Lock the file to synchronize with other zoneadmds 10150Sstevel@tonic-gate */ 10160Sstevel@tonic-gate flock.l_type = F_WRLCK; 10170Sstevel@tonic-gate flock.l_whence = SEEK_SET; 10180Sstevel@tonic-gate flock.l_start = (off_t)0; 10190Sstevel@tonic-gate flock.l_len = (off_t)0; 10200Sstevel@tonic-gate if (fcntl(*lockfd, F_SETLKW, &flock) < 0) { 10210Sstevel@tonic-gate zerror(gettext("unable to lock %s: %s"), pathbuf, 10220Sstevel@tonic-gate strerror(errno)); 10230Sstevel@tonic-gate release_lock_file(*lockfd); 10240Sstevel@tonic-gate return (Z_ERR); 10250Sstevel@tonic-gate } 10260Sstevel@tonic-gate return (Z_OK); 10270Sstevel@tonic-gate } 10280Sstevel@tonic-gate 1029766Scarlsonj static boolean_t 10300Sstevel@tonic-gate get_doorname(const char *zone_name, char *buffer) 10310Sstevel@tonic-gate { 1032766Scarlsonj return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH, 1033766Scarlsonj zonecfg_get_root(), zone_name) < PATH_MAX); 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate /* 10370Sstevel@tonic-gate * system daemons are not audited. For the global zone, this occurs 10380Sstevel@tonic-gate * "naturally" since init is started with the default audit 10390Sstevel@tonic-gate * characteristics. Since zoneadmd is a system daemon and it starts 10400Sstevel@tonic-gate * init for a zone, it is necessary to clear out the audit 10410Sstevel@tonic-gate * characteristics inherited from whomever started zoneadmd. This is 10420Sstevel@tonic-gate * indicated by the audit id, which is set from the ruid parameter of 10430Sstevel@tonic-gate * adt_set_user(), below. 10440Sstevel@tonic-gate */ 10450Sstevel@tonic-gate 10460Sstevel@tonic-gate static void 10470Sstevel@tonic-gate prepare_audit_context() 10480Sstevel@tonic-gate { 10490Sstevel@tonic-gate adt_session_data_t *ah; 10500Sstevel@tonic-gate char *failure = gettext("audit failure: %s"); 10510Sstevel@tonic-gate 10520Sstevel@tonic-gate if (adt_start_session(&ah, NULL, 0)) { 10530Sstevel@tonic-gate zerror(failure, strerror(errno)); 10540Sstevel@tonic-gate return; 10550Sstevel@tonic-gate } 10560Sstevel@tonic-gate if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT, 10570Sstevel@tonic-gate ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) { 10580Sstevel@tonic-gate zerror(failure, strerror(errno)); 10590Sstevel@tonic-gate (void) adt_end_session(ah); 10600Sstevel@tonic-gate return; 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate if (adt_set_proc(ah)) 10630Sstevel@tonic-gate zerror(failure, strerror(errno)); 10640Sstevel@tonic-gate 10650Sstevel@tonic-gate (void) adt_end_session(ah); 10660Sstevel@tonic-gate } 10670Sstevel@tonic-gate 10680Sstevel@tonic-gate static int 10690Sstevel@tonic-gate start_zoneadmd(const char *zone_name) 10700Sstevel@tonic-gate { 10710Sstevel@tonic-gate char doorpath[PATH_MAX]; 10720Sstevel@tonic-gate pid_t child_pid; 10730Sstevel@tonic-gate int error = Z_ERR; 10740Sstevel@tonic-gate int doorfd, lockfd; 10750Sstevel@tonic-gate struct door_info info; 10760Sstevel@tonic-gate 1077766Scarlsonj if (!get_doorname(zone_name, doorpath)) 1078766Scarlsonj return (Z_ERR); 10790Sstevel@tonic-gate 10800Sstevel@tonic-gate if (grab_lock_file(zone_name, &lockfd) != Z_OK) 10810Sstevel@tonic-gate return (Z_ERR); 10820Sstevel@tonic-gate 10830Sstevel@tonic-gate /* 10840Sstevel@tonic-gate * Now that we have the lock, re-confirm that the daemon is 10850Sstevel@tonic-gate * *not* up and working fine. If it is still down, we have a green 10860Sstevel@tonic-gate * light to start it. 10870Sstevel@tonic-gate */ 10880Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 10890Sstevel@tonic-gate if (errno != ENOENT) { 10900Sstevel@tonic-gate zperror(doorpath, B_FALSE); 10910Sstevel@tonic-gate goto out; 10920Sstevel@tonic-gate } 10930Sstevel@tonic-gate } else { 10940Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 10950Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 10960Sstevel@tonic-gate error = Z_OK; 10970Sstevel@tonic-gate (void) close(doorfd); 10980Sstevel@tonic-gate goto out; 10990Sstevel@tonic-gate } 11000Sstevel@tonic-gate (void) close(doorfd); 11010Sstevel@tonic-gate } 11020Sstevel@tonic-gate 11030Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 11040Sstevel@tonic-gate zperror(gettext("could not fork"), B_FALSE); 11050Sstevel@tonic-gate goto out; 11060Sstevel@tonic-gate } else if (child_pid == 0) { 1107766Scarlsonj const char *argv[6], **ap; 1108766Scarlsonj 11090Sstevel@tonic-gate /* child process */ 11100Sstevel@tonic-gate prepare_audit_context(); 11110Sstevel@tonic-gate 1112766Scarlsonj ap = argv; 1113766Scarlsonj *ap++ = "zoneadmd"; 1114766Scarlsonj *ap++ = "-z"; 1115766Scarlsonj *ap++ = zone_name; 1116766Scarlsonj if (zonecfg_in_alt_root()) { 1117766Scarlsonj *ap++ = "-R"; 1118766Scarlsonj *ap++ = zonecfg_get_root(); 1119766Scarlsonj } 1120766Scarlsonj *ap = NULL; 1121766Scarlsonj 1122766Scarlsonj (void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv); 1123924Sgjelinek /* 1124924Sgjelinek * TRANSLATION_NOTE 1125924Sgjelinek * zoneadmd is a literal that should not be translated. 1126924Sgjelinek */ 11270Sstevel@tonic-gate zperror(gettext("could not exec zoneadmd"), B_FALSE); 11280Sstevel@tonic-gate _exit(Z_ERR); 11290Sstevel@tonic-gate } else { 11300Sstevel@tonic-gate /* parent process */ 11310Sstevel@tonic-gate pid_t retval; 11320Sstevel@tonic-gate int pstatus = 0; 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate do { 11350Sstevel@tonic-gate retval = waitpid(child_pid, &pstatus, 0); 11360Sstevel@tonic-gate } while (retval != child_pid); 11370Sstevel@tonic-gate if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) && 11380Sstevel@tonic-gate WEXITSTATUS(pstatus) != 0)) { 11390Sstevel@tonic-gate zerror(gettext("could not start %s"), "zoneadmd"); 11400Sstevel@tonic-gate goto out; 11410Sstevel@tonic-gate } 11420Sstevel@tonic-gate } 11430Sstevel@tonic-gate error = Z_OK; 11440Sstevel@tonic-gate out: 11450Sstevel@tonic-gate release_lock_file(lockfd); 11460Sstevel@tonic-gate return (error); 11470Sstevel@tonic-gate } 11480Sstevel@tonic-gate 11490Sstevel@tonic-gate static int 11500Sstevel@tonic-gate ping_zoneadmd(const char *zone_name) 11510Sstevel@tonic-gate { 11520Sstevel@tonic-gate char doorpath[PATH_MAX]; 11530Sstevel@tonic-gate int doorfd; 11540Sstevel@tonic-gate struct door_info info; 11550Sstevel@tonic-gate 1156766Scarlsonj if (!get_doorname(zone_name, doorpath)) 1157766Scarlsonj return (Z_ERR); 11580Sstevel@tonic-gate 11590Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 11600Sstevel@tonic-gate return (Z_ERR); 11610Sstevel@tonic-gate } 11620Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 11630Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 11640Sstevel@tonic-gate (void) close(doorfd); 11650Sstevel@tonic-gate return (Z_OK); 11660Sstevel@tonic-gate } 11670Sstevel@tonic-gate (void) close(doorfd); 11680Sstevel@tonic-gate return (Z_ERR); 11690Sstevel@tonic-gate } 11700Sstevel@tonic-gate 11710Sstevel@tonic-gate static int 11720Sstevel@tonic-gate call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg) 11730Sstevel@tonic-gate { 11740Sstevel@tonic-gate char doorpath[PATH_MAX]; 11750Sstevel@tonic-gate int doorfd, result; 11760Sstevel@tonic-gate door_arg_t darg; 11770Sstevel@tonic-gate 11780Sstevel@tonic-gate zoneid_t zoneid; 11790Sstevel@tonic-gate uint64_t uniqid = 0; 11800Sstevel@tonic-gate 11810Sstevel@tonic-gate zone_cmd_rval_t *rvalp; 11820Sstevel@tonic-gate size_t rlen; 11830Sstevel@tonic-gate char *cp, *errbuf; 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate rlen = getpagesize(); 11860Sstevel@tonic-gate if ((rvalp = malloc(rlen)) == NULL) { 11870Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rlen, 11880Sstevel@tonic-gate strerror(errno)); 11890Sstevel@tonic-gate return (-1); 11900Sstevel@tonic-gate } 11910Sstevel@tonic-gate 11920Sstevel@tonic-gate if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) { 11930Sstevel@tonic-gate (void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid, 11940Sstevel@tonic-gate sizeof (uniqid)); 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate arg->uniqid = uniqid; 11970Sstevel@tonic-gate (void) strlcpy(arg->locale, locale, sizeof (arg->locale)); 1198766Scarlsonj if (!get_doorname(zone_name, doorpath)) { 1199766Scarlsonj zerror(gettext("alternate root path is too long")); 1200766Scarlsonj free(rvalp); 1201766Scarlsonj return (-1); 1202766Scarlsonj } 12030Sstevel@tonic-gate 12040Sstevel@tonic-gate /* 12050Sstevel@tonic-gate * Loop trying to start zoneadmd; if something goes seriously 12060Sstevel@tonic-gate * wrong we break out and fail. 12070Sstevel@tonic-gate */ 12080Sstevel@tonic-gate for (;;) { 12090Sstevel@tonic-gate if (start_zoneadmd(zone_name) != Z_OK) 12100Sstevel@tonic-gate break; 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 12130Sstevel@tonic-gate zperror(gettext("failed to open zone door"), B_FALSE); 12140Sstevel@tonic-gate break; 12150Sstevel@tonic-gate } 12160Sstevel@tonic-gate 12170Sstevel@tonic-gate darg.data_ptr = (char *)arg; 12180Sstevel@tonic-gate darg.data_size = sizeof (*arg); 12190Sstevel@tonic-gate darg.desc_ptr = NULL; 12200Sstevel@tonic-gate darg.desc_num = 0; 12210Sstevel@tonic-gate darg.rbuf = (char *)rvalp; 12220Sstevel@tonic-gate darg.rsize = rlen; 12230Sstevel@tonic-gate if (door_call(doorfd, &darg) != 0) { 12240Sstevel@tonic-gate (void) close(doorfd); 12250Sstevel@tonic-gate /* 12260Sstevel@tonic-gate * We'll get EBADF if the door has been revoked. 12270Sstevel@tonic-gate */ 12280Sstevel@tonic-gate if (errno != EBADF) { 12290Sstevel@tonic-gate zperror(gettext("door_call failed"), B_FALSE); 12300Sstevel@tonic-gate break; 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate continue; /* take another lap */ 12330Sstevel@tonic-gate } 12340Sstevel@tonic-gate (void) close(doorfd); 12350Sstevel@tonic-gate 12360Sstevel@tonic-gate if (darg.data_size == 0) { 12370Sstevel@tonic-gate /* Door server is going away; kick it again. */ 12380Sstevel@tonic-gate continue; 12390Sstevel@tonic-gate } 12400Sstevel@tonic-gate 12410Sstevel@tonic-gate errbuf = rvalp->errbuf; 12420Sstevel@tonic-gate while (*errbuf != '\0') { 12430Sstevel@tonic-gate /* 12440Sstevel@tonic-gate * Remove any newlines since zerror() 12450Sstevel@tonic-gate * will append one automatically. 12460Sstevel@tonic-gate */ 12470Sstevel@tonic-gate cp = strchr(errbuf, '\n'); 12480Sstevel@tonic-gate if (cp != NULL) 12490Sstevel@tonic-gate *cp = '\0'; 12500Sstevel@tonic-gate zerror("%s", errbuf); 12510Sstevel@tonic-gate if (cp == NULL) 12520Sstevel@tonic-gate break; 12530Sstevel@tonic-gate errbuf = cp + 1; 12540Sstevel@tonic-gate } 12550Sstevel@tonic-gate result = rvalp->rval == 0 ? 0 : -1; 12560Sstevel@tonic-gate free(rvalp); 12570Sstevel@tonic-gate return (result); 12580Sstevel@tonic-gate } 12590Sstevel@tonic-gate 12600Sstevel@tonic-gate free(rvalp); 12610Sstevel@tonic-gate return (-1); 12620Sstevel@tonic-gate } 12630Sstevel@tonic-gate 12640Sstevel@tonic-gate static int 12650Sstevel@tonic-gate ready_func(int argc, char *argv[]) 12660Sstevel@tonic-gate { 12670Sstevel@tonic-gate zone_cmd_arg_t zarg; 12680Sstevel@tonic-gate int arg; 12690Sstevel@tonic-gate 1270766Scarlsonj if (zonecfg_in_alt_root()) { 1271766Scarlsonj zerror(gettext("cannot ready zone in alternate root")); 1272766Scarlsonj return (Z_ERR); 1273766Scarlsonj } 1274766Scarlsonj 12750Sstevel@tonic-gate optind = 0; 12760Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 12770Sstevel@tonic-gate switch (arg) { 12780Sstevel@tonic-gate case '?': 12790Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12800Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 12810Sstevel@tonic-gate default: 12820Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12830Sstevel@tonic-gate return (Z_USAGE); 12840Sstevel@tonic-gate } 12850Sstevel@tonic-gate } 12860Sstevel@tonic-gate if (argc > optind) { 12870Sstevel@tonic-gate sub_usage(SHELP_READY, CMD_READY); 12880Sstevel@tonic-gate return (Z_USAGE); 12890Sstevel@tonic-gate } 12900Sstevel@tonic-gate if (sanity_check(target_zone, CMD_READY, B_FALSE, B_FALSE) != Z_OK) 12910Sstevel@tonic-gate return (Z_ERR); 12920Sstevel@tonic-gate if (verify_details(CMD_READY) != Z_OK) 12930Sstevel@tonic-gate return (Z_ERR); 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate zarg.cmd = Z_READY; 12960Sstevel@tonic-gate if (call_zoneadmd(target_zone, &zarg) != 0) { 12970Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 12980Sstevel@tonic-gate return (Z_ERR); 12990Sstevel@tonic-gate } 13000Sstevel@tonic-gate return (Z_OK); 13010Sstevel@tonic-gate } 13020Sstevel@tonic-gate 13030Sstevel@tonic-gate static int 13040Sstevel@tonic-gate boot_func(int argc, char *argv[]) 13050Sstevel@tonic-gate { 13060Sstevel@tonic-gate zone_cmd_arg_t zarg; 13070Sstevel@tonic-gate int arg; 13080Sstevel@tonic-gate 1309766Scarlsonj if (zonecfg_in_alt_root()) { 1310766Scarlsonj zerror(gettext("cannot boot zone in alternate root")); 1311766Scarlsonj return (Z_ERR); 1312766Scarlsonj } 1313766Scarlsonj 13140Sstevel@tonic-gate zarg.bootbuf[0] = '\0'; 13150Sstevel@tonic-gate 13160Sstevel@tonic-gate /* 13170Sstevel@tonic-gate * At the current time, the only supported subargument to the 13180Sstevel@tonic-gate * "boot" subcommand is "-s" which specifies a single-user boot. 13190Sstevel@tonic-gate * In the future, other boot arguments should be supported 13200Sstevel@tonic-gate * including "-m" for specifying alternate smf(5) milestones. 13210Sstevel@tonic-gate */ 13220Sstevel@tonic-gate optind = 0; 13230Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?s")) != EOF) { 13240Sstevel@tonic-gate switch (arg) { 13250Sstevel@tonic-gate case '?': 13260Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13270Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13280Sstevel@tonic-gate case 's': 13290Sstevel@tonic-gate (void) strlcpy(zarg.bootbuf, "-s", 13300Sstevel@tonic-gate sizeof (zarg.bootbuf)); 13310Sstevel@tonic-gate break; 13320Sstevel@tonic-gate default: 13330Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13340Sstevel@tonic-gate return (Z_USAGE); 13350Sstevel@tonic-gate } 13360Sstevel@tonic-gate } 13370Sstevel@tonic-gate if (argc > optind) { 13380Sstevel@tonic-gate sub_usage(SHELP_BOOT, CMD_BOOT); 13390Sstevel@tonic-gate return (Z_USAGE); 13400Sstevel@tonic-gate } 13410Sstevel@tonic-gate if (sanity_check(target_zone, CMD_BOOT, B_FALSE, B_FALSE) != Z_OK) 13420Sstevel@tonic-gate return (Z_ERR); 13430Sstevel@tonic-gate if (verify_details(CMD_BOOT) != Z_OK) 13440Sstevel@tonic-gate return (Z_ERR); 13450Sstevel@tonic-gate zarg.cmd = Z_BOOT; 13460Sstevel@tonic-gate if (call_zoneadmd(target_zone, &zarg) != 0) { 13470Sstevel@tonic-gate zerror(gettext("call to %s failed"), "zoneadmd"); 13480Sstevel@tonic-gate return (Z_ERR); 13490Sstevel@tonic-gate } 13500Sstevel@tonic-gate return (Z_OK); 13510Sstevel@tonic-gate } 13520Sstevel@tonic-gate 13530Sstevel@tonic-gate static void 13540Sstevel@tonic-gate fake_up_local_zone(zoneid_t zid, zone_entry_t *zeptr) 13550Sstevel@tonic-gate { 13560Sstevel@tonic-gate ssize_t result; 13570Sstevel@tonic-gate 13580Sstevel@tonic-gate zeptr->zid = zid; 13590Sstevel@tonic-gate /* 13600Sstevel@tonic-gate * Since we're looking up our own (non-global) zone name, 13610Sstevel@tonic-gate * we can be assured that it will succeed. 13620Sstevel@tonic-gate */ 13630Sstevel@tonic-gate result = getzonenamebyid(zid, zeptr->zname, sizeof (zeptr->zname)); 13640Sstevel@tonic-gate assert(result >= 0); 1365*1676Sjpk if (!is_system_labeled()) { 1366*1676Sjpk (void) strlcpy(zeptr->zroot, "/", sizeof (zeptr->zroot)); 1367*1676Sjpk } else { 1368*1676Sjpk (void) zone_getattr(zid, ZONE_ATTR_ROOT, zeptr->zroot, 1369*1676Sjpk sizeof (zeptr->zroot)); 1370*1676Sjpk } 13710Sstevel@tonic-gate zeptr->zstate_str = "running"; 13720Sstevel@tonic-gate } 13730Sstevel@tonic-gate 13740Sstevel@tonic-gate static int 13750Sstevel@tonic-gate list_func(int argc, char *argv[]) 13760Sstevel@tonic-gate { 13770Sstevel@tonic-gate zone_entry_t *zentp, zent; 1378766Scarlsonj int arg, retv; 13790Sstevel@tonic-gate boolean_t output = B_FALSE, verbose = B_FALSE, parsable = B_FALSE; 13800Sstevel@tonic-gate zone_state_t min_state = ZONE_STATE_RUNNING; 13810Sstevel@tonic-gate zoneid_t zone_id = getzoneid(); 13820Sstevel@tonic-gate 13830Sstevel@tonic-gate if (target_zone == NULL) { 13840Sstevel@tonic-gate /* all zones: default view to running but allow override */ 13850Sstevel@tonic-gate optind = 0; 13860Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?cipv")) != EOF) { 13870Sstevel@tonic-gate switch (arg) { 13880Sstevel@tonic-gate case '?': 13890Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 13900Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 13911339Sjonb /* 13921339Sjonb * The 'i' and 'c' options are not mutually 13931339Sjonb * exclusive so if 'c' is given, then min_state 13941339Sjonb * is set to 0 (ZONE_STATE_CONFIGURED) which is 13951339Sjonb * the lowest possible state. If 'i' is given, 13961339Sjonb * then min_state is set to be the lowest state 13971339Sjonb * so far. 13981339Sjonb */ 13990Sstevel@tonic-gate case 'c': 14000Sstevel@tonic-gate min_state = ZONE_STATE_CONFIGURED; 14010Sstevel@tonic-gate break; 14020Sstevel@tonic-gate case 'i': 14031339Sjonb min_state = min(ZONE_STATE_INSTALLED, 14041339Sjonb min_state); 14051339Sjonb 14060Sstevel@tonic-gate break; 14070Sstevel@tonic-gate case 'p': 14080Sstevel@tonic-gate parsable = B_TRUE; 14090Sstevel@tonic-gate break; 14100Sstevel@tonic-gate case 'v': 14110Sstevel@tonic-gate verbose = B_TRUE; 14120Sstevel@tonic-gate break; 14130Sstevel@tonic-gate default: 14140Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14150Sstevel@tonic-gate return (Z_USAGE); 14160Sstevel@tonic-gate } 14170Sstevel@tonic-gate } 14180Sstevel@tonic-gate if (parsable && verbose) { 14190Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14200Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14210Sstevel@tonic-gate return (Z_ERR); 14220Sstevel@tonic-gate } 1423*1676Sjpk if (zone_id == GLOBAL_ZONEID || is_system_labeled()) { 1424766Scarlsonj retv = zone_print_list(min_state, verbose, parsable); 14250Sstevel@tonic-gate } else { 1426766Scarlsonj retv = Z_OK; 14270Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14280Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14290Sstevel@tonic-gate } 1430766Scarlsonj return (retv); 14310Sstevel@tonic-gate } 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate /* 14340Sstevel@tonic-gate * Specific target zone: disallow -i/-c suboptions. 14350Sstevel@tonic-gate */ 14360Sstevel@tonic-gate optind = 0; 14370Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?pv")) != EOF) { 14380Sstevel@tonic-gate switch (arg) { 14390Sstevel@tonic-gate case '?': 14400Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14410Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 14420Sstevel@tonic-gate case 'p': 14430Sstevel@tonic-gate parsable = B_TRUE; 14440Sstevel@tonic-gate break; 14450Sstevel@tonic-gate case 'v': 14460Sstevel@tonic-gate verbose = B_TRUE; 14470Sstevel@tonic-gate break; 14480Sstevel@tonic-gate default: 14490Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14500Sstevel@tonic-gate return (Z_USAGE); 14510Sstevel@tonic-gate } 14520Sstevel@tonic-gate } 14530Sstevel@tonic-gate if (parsable && verbose) { 14540Sstevel@tonic-gate zerror(gettext("%s -p and -v are mutually exclusive."), 14550Sstevel@tonic-gate cmd_to_str(CMD_LIST)); 14560Sstevel@tonic-gate return (Z_ERR); 14570Sstevel@tonic-gate } 14580Sstevel@tonic-gate if (argc > optind) { 14590Sstevel@tonic-gate sub_usage(SHELP_LIST, CMD_LIST); 14600Sstevel@tonic-gate return (Z_USAGE); 14610Sstevel@tonic-gate } 14620Sstevel@tonic-gate if (zone_id != GLOBAL_ZONEID) { 14630Sstevel@tonic-gate fake_up_local_zone(zone_id, &zent); 14640Sstevel@tonic-gate /* 14650Sstevel@tonic-gate * main() will issue a Z_NO_ZONE error if it cannot get an 14660Sstevel@tonic-gate * id for target_zone, which in a non-global zone should 14670Sstevel@tonic-gate * happen for any zone name except `zonename`. Thus we 14680Sstevel@tonic-gate * assert() that here but don't otherwise check. 14690Sstevel@tonic-gate */ 14700Sstevel@tonic-gate assert(strcmp(zent.zname, target_zone) == 0); 14710Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14720Sstevel@tonic-gate output = B_TRUE; 14730Sstevel@tonic-gate } else if ((zentp = lookup_running_zone(target_zone)) != NULL) { 14740Sstevel@tonic-gate zone_print(zentp, verbose, parsable); 14750Sstevel@tonic-gate output = B_TRUE; 1476766Scarlsonj } else if (lookup_zone_info(target_zone, ZONE_ID_UNDEFINED, 1477766Scarlsonj &zent) == Z_OK) { 14780Sstevel@tonic-gate zone_print(&zent, verbose, parsable); 14790Sstevel@tonic-gate output = B_TRUE; 14800Sstevel@tonic-gate } 14810Sstevel@tonic-gate return (output ? Z_OK : Z_ERR); 14820Sstevel@tonic-gate } 14830Sstevel@tonic-gate 14840Sstevel@tonic-gate static void 14850Sstevel@tonic-gate sigterm(int sig) 14860Sstevel@tonic-gate { 14870Sstevel@tonic-gate /* 14880Sstevel@tonic-gate * Ignore SIG{INT,TERM}, so we don't end up in an infinite loop, 14890Sstevel@tonic-gate * then propagate the signal to our process group. 14900Sstevel@tonic-gate */ 14910Sstevel@tonic-gate (void) sigset(SIGINT, SIG_IGN); 14920Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_IGN); 14930Sstevel@tonic-gate (void) kill(0, sig); 14940Sstevel@tonic-gate child_killed = B_TRUE; 14950Sstevel@tonic-gate } 14960Sstevel@tonic-gate 14970Sstevel@tonic-gate static int 14980Sstevel@tonic-gate do_subproc(char *cmdbuf) 14990Sstevel@tonic-gate { 15000Sstevel@tonic-gate char inbuf[1024]; /* arbitrary large amount */ 15010Sstevel@tonic-gate FILE *file; 15020Sstevel@tonic-gate 15030Sstevel@tonic-gate child_killed = B_FALSE; 15040Sstevel@tonic-gate /* 15050Sstevel@tonic-gate * We use popen(3c) to launch child processes for [un]install; 15060Sstevel@tonic-gate * this library call does not return a PID, so we have to kill 15070Sstevel@tonic-gate * the whole process group. To avoid killing our parent, we 15080Sstevel@tonic-gate * become a process group leader here. But doing so can wreak 15090Sstevel@tonic-gate * havoc with reading from stdin when launched by a non-job-control 15100Sstevel@tonic-gate * shell, so we close stdin and reopen it as /dev/null first. 15110Sstevel@tonic-gate */ 15120Sstevel@tonic-gate (void) close(STDIN_FILENO); 15130Sstevel@tonic-gate (void) open("/dev/null", O_RDONLY); 15140Sstevel@tonic-gate (void) setpgid(0, 0); 15150Sstevel@tonic-gate (void) sigset(SIGINT, sigterm); 15160Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm); 15170Sstevel@tonic-gate file = popen(cmdbuf, "r"); 15180Sstevel@tonic-gate for (;;) { 15190Sstevel@tonic-gate if (child_killed || fgets(inbuf, sizeof (inbuf), file) == NULL) 15200Sstevel@tonic-gate break; 15210Sstevel@tonic-gate (void) fputs(inbuf, stdout); 15220Sstevel@tonic-gate } 15230Sstevel@tonic-gate (void) sigset(SIGINT, SIG_DFL); 15240Sstevel@tonic-gate (void) sigset(SIGTERM, SIG_DFL); 15250Sstevel@tonic-gate return (pclose(file)); 15260Sstevel@tonic-gate } 15270Sstevel@tonic-gate 15280Sstevel@tonic-gate static int 15290Sstevel@tonic-gate subproc_status(const char *cmd, int status) 15300Sstevel@tonic-gate { 15310Sstevel@tonic-gate if (WIFEXITED(status)) { 15320Sstevel@tonic-gate int exit_code = WEXITSTATUS(status); 15330Sstevel@tonic-gate 15340Sstevel@tonic-gate if (exit_code == 0) 15350Sstevel@tonic-gate return (Z_OK); 15360Sstevel@tonic-gate zerror(gettext("'%s' failed with exit code %d."), cmd, 15370Sstevel@tonic-gate exit_code); 15380Sstevel@tonic-gate } else if (WIFSIGNALED(status)) { 15390Sstevel@tonic-gate int signal = WTERMSIG(status); 15400Sstevel@tonic-gate char sigstr[SIG2STR_MAX]; 15410Sstevel@tonic-gate 15420Sstevel@tonic-gate if (sig2str(signal, sigstr) == 0) { 15430Sstevel@tonic-gate zerror(gettext("'%s' terminated by signal SIG%s."), cmd, 15440Sstevel@tonic-gate sigstr); 15450Sstevel@tonic-gate } else { 15460Sstevel@tonic-gate zerror(gettext("'%s' terminated by an unknown signal."), 15470Sstevel@tonic-gate cmd); 15480Sstevel@tonic-gate } 15490Sstevel@tonic-gate } else { 15500Sstevel@tonic-gate zerror(gettext("'%s' failed for unknown reasons."), cmd); 15510Sstevel@tonic-gate } 15520Sstevel@tonic-gate return (Z_ERR); 15530Sstevel@tonic-gate } 15540Sstevel@tonic-gate 15550Sstevel@tonic-gate /* 15560Sstevel@tonic-gate * Various sanity checks; make sure: 15570Sstevel@tonic-gate * 1. We're in the global zone. 15580Sstevel@tonic-gate * 2. The calling user has sufficient privilege. 15590Sstevel@tonic-gate * 3. The target zone is neither the global zone nor anything starting with 15600Sstevel@tonic-gate * "SUNW". 15610Sstevel@tonic-gate * 4a. If we're looking for a 'not running' (i.e., configured or installed) 15620Sstevel@tonic-gate * zone, the name service knows about it. 15630Sstevel@tonic-gate * 4b. For some operations which expect a zone not to be running, that it is 15640Sstevel@tonic-gate * not already running (or ready). 15650Sstevel@tonic-gate */ 15660Sstevel@tonic-gate static int 15670Sstevel@tonic-gate sanity_check(char *zone, int cmd_num, boolean_t running, 15680Sstevel@tonic-gate boolean_t unsafe_when_running) 15690Sstevel@tonic-gate { 15700Sstevel@tonic-gate zone_entry_t *zent; 15710Sstevel@tonic-gate priv_set_t *privset; 15720Sstevel@tonic-gate zone_state_t state; 1573766Scarlsonj char kernzone[ZONENAME_MAX]; 1574766Scarlsonj FILE *fp; 15750Sstevel@tonic-gate 15760Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 15771645Scomay switch (cmd_num) { 15781645Scomay case CMD_HALT: 15791645Scomay zerror(gettext("use %s to %s this zone."), "halt(1M)", 15801645Scomay cmd_to_str(cmd_num)); 15811645Scomay break; 15821645Scomay case CMD_REBOOT: 15831645Scomay zerror(gettext("use %s to %s this zone."), 15841645Scomay "reboot(1M)", cmd_to_str(cmd_num)); 15851645Scomay break; 15861645Scomay default: 15871645Scomay zerror(gettext("must be in the global zone to %s a " 15881645Scomay "zone."), cmd_to_str(cmd_num)); 15891645Scomay break; 15901645Scomay } 15910Sstevel@tonic-gate return (Z_ERR); 15920Sstevel@tonic-gate } 15930Sstevel@tonic-gate 15940Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 15950Sstevel@tonic-gate zerror(gettext("%s failed"), "priv_allocset"); 15960Sstevel@tonic-gate return (Z_ERR); 15970Sstevel@tonic-gate } 15980Sstevel@tonic-gate 15990Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 16000Sstevel@tonic-gate zerror(gettext("%s failed"), "getppriv"); 16010Sstevel@tonic-gate priv_freeset(privset); 16020Sstevel@tonic-gate return (Z_ERR); 16030Sstevel@tonic-gate } 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 16060Sstevel@tonic-gate zerror(gettext("only a privileged user may %s a zone."), 16070Sstevel@tonic-gate cmd_to_str(cmd_num)); 16080Sstevel@tonic-gate priv_freeset(privset); 16090Sstevel@tonic-gate return (Z_ERR); 16100Sstevel@tonic-gate } 16110Sstevel@tonic-gate priv_freeset(privset); 16120Sstevel@tonic-gate 16130Sstevel@tonic-gate if (zone == NULL) { 16140Sstevel@tonic-gate zerror(gettext("no zone specified")); 16150Sstevel@tonic-gate return (Z_ERR); 16160Sstevel@tonic-gate } 16170Sstevel@tonic-gate 16180Sstevel@tonic-gate if (strcmp(zone, GLOBAL_ZONENAME) == 0) { 16190Sstevel@tonic-gate zerror(gettext("%s operation is invalid for the global zone."), 16200Sstevel@tonic-gate cmd_to_str(cmd_num)); 16210Sstevel@tonic-gate return (Z_ERR); 16220Sstevel@tonic-gate } 16230Sstevel@tonic-gate 16240Sstevel@tonic-gate if (strncmp(zone, "SUNW", 4) == 0) { 16250Sstevel@tonic-gate zerror(gettext("%s operation is invalid for zones starting " 16260Sstevel@tonic-gate "with SUNW."), cmd_to_str(cmd_num)); 16270Sstevel@tonic-gate return (Z_ERR); 16280Sstevel@tonic-gate } 16290Sstevel@tonic-gate 1630766Scarlsonj if (!zonecfg_in_alt_root()) { 1631766Scarlsonj zent = lookup_running_zone(zone); 1632766Scarlsonj } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) { 1633766Scarlsonj zent = NULL; 1634766Scarlsonj } else { 1635766Scarlsonj if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), 1636766Scarlsonj kernzone, sizeof (kernzone)) == 0) 1637766Scarlsonj zent = lookup_running_zone(kernzone); 1638766Scarlsonj else 1639766Scarlsonj zent = NULL; 1640766Scarlsonj zonecfg_close_scratch(fp); 1641766Scarlsonj } 1642766Scarlsonj 16430Sstevel@tonic-gate /* 16440Sstevel@tonic-gate * Look up from the kernel for 'running' zones. 16450Sstevel@tonic-gate */ 16460Sstevel@tonic-gate if (running) { 16470Sstevel@tonic-gate if (zent == NULL) { 16480Sstevel@tonic-gate zerror(gettext("not running")); 16490Sstevel@tonic-gate return (Z_ERR); 16500Sstevel@tonic-gate } 16510Sstevel@tonic-gate } else { 16520Sstevel@tonic-gate int err; 16530Sstevel@tonic-gate 16540Sstevel@tonic-gate if (unsafe_when_running && zent != NULL) { 16550Sstevel@tonic-gate /* check whether the zone is ready or running */ 16560Sstevel@tonic-gate if ((err = zone_get_state(zent->zname, 16570Sstevel@tonic-gate &zent->zstate_num)) != Z_OK) { 16580Sstevel@tonic-gate errno = err; 16590Sstevel@tonic-gate zperror2(zent->zname, 16600Sstevel@tonic-gate gettext("could not get state")); 16610Sstevel@tonic-gate /* can't tell, so hedge */ 16620Sstevel@tonic-gate zent->zstate_str = "ready/running"; 16630Sstevel@tonic-gate } else { 16640Sstevel@tonic-gate zent->zstate_str = 16650Sstevel@tonic-gate zone_state_str(zent->zstate_num); 16660Sstevel@tonic-gate } 16670Sstevel@tonic-gate zerror(gettext("%s operation is invalid for %s zones."), 16680Sstevel@tonic-gate cmd_to_str(cmd_num), zent->zstate_str); 16690Sstevel@tonic-gate return (Z_ERR); 16700Sstevel@tonic-gate } 16710Sstevel@tonic-gate if ((err = zone_get_state(zone, &state)) != Z_OK) { 16720Sstevel@tonic-gate errno = err; 16730Sstevel@tonic-gate zperror2(zone, gettext("could not get state")); 16740Sstevel@tonic-gate return (Z_ERR); 16750Sstevel@tonic-gate } 16760Sstevel@tonic-gate switch (cmd_num) { 16770Sstevel@tonic-gate case CMD_UNINSTALL: 16780Sstevel@tonic-gate if (state == ZONE_STATE_CONFIGURED) { 16790Sstevel@tonic-gate zerror(gettext("is already in state '%s'."), 16800Sstevel@tonic-gate zone_state_str(ZONE_STATE_CONFIGURED)); 16810Sstevel@tonic-gate return (Z_ERR); 16820Sstevel@tonic-gate } 16830Sstevel@tonic-gate break; 16841507Sgjelinek case CMD_ATTACH: 16851300Sgjelinek case CMD_CLONE: 16860Sstevel@tonic-gate case CMD_INSTALL: 16870Sstevel@tonic-gate if (state == ZONE_STATE_INSTALLED) { 16880Sstevel@tonic-gate zerror(gettext("is already %s."), 16890Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED)); 16900Sstevel@tonic-gate return (Z_ERR); 16910Sstevel@tonic-gate } else if (state == ZONE_STATE_INCOMPLETE) { 16920Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 16930Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 16940Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 16950Sstevel@tonic-gate return (Z_ERR); 16960Sstevel@tonic-gate } 16970Sstevel@tonic-gate break; 16981507Sgjelinek case CMD_DETACH: 16991300Sgjelinek case CMD_MOVE: 17000Sstevel@tonic-gate case CMD_READY: 17010Sstevel@tonic-gate case CMD_BOOT: 1702766Scarlsonj case CMD_MOUNT: 17030Sstevel@tonic-gate if (state < ZONE_STATE_INSTALLED) { 17040Sstevel@tonic-gate zerror(gettext("must be %s before %s."), 17050Sstevel@tonic-gate zone_state_str(ZONE_STATE_INSTALLED), 17060Sstevel@tonic-gate cmd_to_str(cmd_num)); 17070Sstevel@tonic-gate return (Z_ERR); 17080Sstevel@tonic-gate } 17090Sstevel@tonic-gate break; 17100Sstevel@tonic-gate case CMD_VERIFY: 17110Sstevel@tonic-gate if (state == ZONE_STATE_INCOMPLETE) { 17120Sstevel@tonic-gate zerror(gettext("zone is %s; %s required."), 17130Sstevel@tonic-gate zone_state_str(ZONE_STATE_INCOMPLETE), 17140Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 17150Sstevel@tonic-gate return (Z_ERR); 17160Sstevel@tonic-gate } 17170Sstevel@tonic-gate break; 1718766Scarlsonj case CMD_UNMOUNT: 1719766Scarlsonj if (state != ZONE_STATE_MOUNTED) { 1720766Scarlsonj zerror(gettext("must be %s before %s."), 1721766Scarlsonj zone_state_str(ZONE_STATE_MOUNTED), 1722766Scarlsonj cmd_to_str(cmd_num)); 1723766Scarlsonj return (Z_ERR); 1724766Scarlsonj } 1725766Scarlsonj break; 17260Sstevel@tonic-gate } 17270Sstevel@tonic-gate } 17280Sstevel@tonic-gate return (Z_OK); 17290Sstevel@tonic-gate } 17300Sstevel@tonic-gate 17310Sstevel@tonic-gate static int 17320Sstevel@tonic-gate halt_func(int argc, char *argv[]) 17330Sstevel@tonic-gate { 17340Sstevel@tonic-gate zone_cmd_arg_t zarg; 17350Sstevel@tonic-gate int arg; 17360Sstevel@tonic-gate 1737766Scarlsonj if (zonecfg_in_alt_root()) { 1738766Scarlsonj zerror(gettext("cannot halt zone in alternate root")); 1739766Scarlsonj return (Z_ERR); 1740766Scarlsonj } 1741766Scarlsonj 17420Sstevel@tonic-gate optind = 0; 17430Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17440Sstevel@tonic-gate switch (arg) { 17450Sstevel@tonic-gate case '?': 17460Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17470Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 17480Sstevel@tonic-gate default: 17490Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17500Sstevel@tonic-gate return (Z_USAGE); 17510Sstevel@tonic-gate } 17520Sstevel@tonic-gate } 17530Sstevel@tonic-gate if (argc > optind) { 17540Sstevel@tonic-gate sub_usage(SHELP_HALT, CMD_HALT); 17550Sstevel@tonic-gate return (Z_USAGE); 17560Sstevel@tonic-gate } 17570Sstevel@tonic-gate /* 17580Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 17590Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 17600Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 17610Sstevel@tonic-gate */ 17620Sstevel@tonic-gate if (sanity_check(target_zone, CMD_HALT, B_FALSE, B_FALSE) != Z_OK) 17630Sstevel@tonic-gate return (Z_ERR); 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate zarg.cmd = Z_HALT; 17660Sstevel@tonic-gate return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR); 17670Sstevel@tonic-gate } 17680Sstevel@tonic-gate 17690Sstevel@tonic-gate static int 17700Sstevel@tonic-gate reboot_func(int argc, char *argv[]) 17710Sstevel@tonic-gate { 17720Sstevel@tonic-gate zone_cmd_arg_t zarg; 17730Sstevel@tonic-gate int arg; 17740Sstevel@tonic-gate 1775766Scarlsonj if (zonecfg_in_alt_root()) { 1776766Scarlsonj zerror(gettext("cannot reboot zone in alternate root")); 1777766Scarlsonj return (Z_ERR); 1778766Scarlsonj } 1779766Scarlsonj 17800Sstevel@tonic-gate optind = 0; 17810Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 17820Sstevel@tonic-gate switch (arg) { 17830Sstevel@tonic-gate case '?': 17840Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17850Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 17860Sstevel@tonic-gate default: 17870Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17880Sstevel@tonic-gate return (Z_USAGE); 17890Sstevel@tonic-gate } 17900Sstevel@tonic-gate } 17910Sstevel@tonic-gate if (argc > 0) { 17920Sstevel@tonic-gate sub_usage(SHELP_REBOOT, CMD_REBOOT); 17930Sstevel@tonic-gate return (Z_USAGE); 17940Sstevel@tonic-gate } 17950Sstevel@tonic-gate /* 17960Sstevel@tonic-gate * zoneadmd should be the one to decide whether or not to proceed, 17970Sstevel@tonic-gate * so even though it seems that the fourth parameter below should 17980Sstevel@tonic-gate * perhaps be B_TRUE, it really shouldn't be. 17990Sstevel@tonic-gate */ 18000Sstevel@tonic-gate if (sanity_check(target_zone, CMD_REBOOT, B_TRUE, B_FALSE) != Z_OK) 18010Sstevel@tonic-gate return (Z_ERR); 1802823Sgjelinek if (verify_details(CMD_REBOOT) != Z_OK) 1803823Sgjelinek return (Z_ERR); 18040Sstevel@tonic-gate 18050Sstevel@tonic-gate zarg.cmd = Z_REBOOT; 18060Sstevel@tonic-gate return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR); 18070Sstevel@tonic-gate } 18080Sstevel@tonic-gate 18090Sstevel@tonic-gate static int 18100Sstevel@tonic-gate verify_rctls(zone_dochandle_t handle) 18110Sstevel@tonic-gate { 18120Sstevel@tonic-gate struct zone_rctltab rctltab; 18130Sstevel@tonic-gate size_t rbs = rctlblk_size(); 18140Sstevel@tonic-gate rctlblk_t *rctlblk; 18150Sstevel@tonic-gate int error = Z_INVAL; 18160Sstevel@tonic-gate 18170Sstevel@tonic-gate if ((rctlblk = malloc(rbs)) == NULL) { 18180Sstevel@tonic-gate zerror(gettext("failed to allocate %lu bytes: %s"), rbs, 18190Sstevel@tonic-gate strerror(errno)); 18200Sstevel@tonic-gate return (Z_NOMEM); 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate 18230Sstevel@tonic-gate if (zonecfg_setrctlent(handle) != Z_OK) { 18240Sstevel@tonic-gate zerror(gettext("zonecfg_setrctlent failed")); 18250Sstevel@tonic-gate free(rctlblk); 18260Sstevel@tonic-gate return (error); 18270Sstevel@tonic-gate } 18280Sstevel@tonic-gate 18290Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 18300Sstevel@tonic-gate while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { 18310Sstevel@tonic-gate struct zone_rctlvaltab *rctlval; 18320Sstevel@tonic-gate const char *name = rctltab.zone_rctl_name; 18330Sstevel@tonic-gate 18340Sstevel@tonic-gate if (!zonecfg_is_rctl(name)) { 18350Sstevel@tonic-gate zerror(gettext("WARNING: Ignoring unrecognized rctl " 18360Sstevel@tonic-gate "'%s'."), name); 18370Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 18380Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 18390Sstevel@tonic-gate continue; 18400Sstevel@tonic-gate } 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate for (rctlval = rctltab.zone_rctl_valptr; rctlval != NULL; 18430Sstevel@tonic-gate rctlval = rctlval->zone_rctlval_next) { 18440Sstevel@tonic-gate if (zonecfg_construct_rctlblk(rctlval, rctlblk) 18450Sstevel@tonic-gate != Z_OK) { 18460Sstevel@tonic-gate zerror(gettext("invalid rctl value: " 18470Sstevel@tonic-gate "(priv=%s,limit=%s,action%s)"), 18480Sstevel@tonic-gate rctlval->zone_rctlval_priv, 18490Sstevel@tonic-gate rctlval->zone_rctlval_limit, 18500Sstevel@tonic-gate rctlval->zone_rctlval_action); 18510Sstevel@tonic-gate goto out; 18520Sstevel@tonic-gate } 18530Sstevel@tonic-gate if (!zonecfg_valid_rctl(name, rctlblk)) { 18540Sstevel@tonic-gate zerror(gettext("(priv=%s,limit=%s,action=%s) " 18550Sstevel@tonic-gate "is not a valid value for rctl '%s'"), 18560Sstevel@tonic-gate rctlval->zone_rctlval_priv, 18570Sstevel@tonic-gate rctlval->zone_rctlval_limit, 18580Sstevel@tonic-gate rctlval->zone_rctlval_action, 18590Sstevel@tonic-gate name); 18600Sstevel@tonic-gate goto out; 18610Sstevel@tonic-gate } 18620Sstevel@tonic-gate } 18630Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 18640Sstevel@tonic-gate } 18650Sstevel@tonic-gate rctltab.zone_rctl_valptr = NULL; 18660Sstevel@tonic-gate error = Z_OK; 18670Sstevel@tonic-gate out: 18680Sstevel@tonic-gate zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); 18690Sstevel@tonic-gate (void) zonecfg_endrctlent(handle); 18700Sstevel@tonic-gate free(rctlblk); 18710Sstevel@tonic-gate return (error); 18720Sstevel@tonic-gate } 18730Sstevel@tonic-gate 18740Sstevel@tonic-gate static int 18750Sstevel@tonic-gate verify_pool(zone_dochandle_t handle) 18760Sstevel@tonic-gate { 18770Sstevel@tonic-gate char poolname[MAXPATHLEN]; 18780Sstevel@tonic-gate pool_conf_t *poolconf; 18790Sstevel@tonic-gate pool_t *pool; 18800Sstevel@tonic-gate int status; 18810Sstevel@tonic-gate int error; 18820Sstevel@tonic-gate 18830Sstevel@tonic-gate /* 18840Sstevel@tonic-gate * This ends up being very similar to the check done in zoneadmd. 18850Sstevel@tonic-gate */ 18860Sstevel@tonic-gate error = zonecfg_get_pool(handle, poolname, sizeof (poolname)); 18870Sstevel@tonic-gate if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) { 18880Sstevel@tonic-gate /* 18890Sstevel@tonic-gate * No pool specified. 18900Sstevel@tonic-gate */ 18910Sstevel@tonic-gate return (0); 18920Sstevel@tonic-gate } 18930Sstevel@tonic-gate if (error != Z_OK) { 18940Sstevel@tonic-gate zperror(gettext("Unable to retrieve pool name from " 18950Sstevel@tonic-gate "configuration"), B_TRUE); 18960Sstevel@tonic-gate return (error); 18970Sstevel@tonic-gate } 18980Sstevel@tonic-gate /* 18990Sstevel@tonic-gate * Don't do anything if pools aren't enabled. 19000Sstevel@tonic-gate */ 19010Sstevel@tonic-gate if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) { 19020Sstevel@tonic-gate zerror(gettext("WARNING: pools facility not active; " 19030Sstevel@tonic-gate "zone will not be bound to pool '%s'."), poolname); 19040Sstevel@tonic-gate return (Z_OK); 19050Sstevel@tonic-gate } 19060Sstevel@tonic-gate /* 19070Sstevel@tonic-gate * Try to provide a sane error message if the requested pool doesn't 19080Sstevel@tonic-gate * exist. It isn't clear that pools-related failures should 19090Sstevel@tonic-gate * necessarily translate to a failure to verify the zone configuration, 19100Sstevel@tonic-gate * hence they are not considered errors. 19110Sstevel@tonic-gate */ 19120Sstevel@tonic-gate if ((poolconf = pool_conf_alloc()) == NULL) { 19130Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_alloc failed; " 19140Sstevel@tonic-gate "using default pool")); 19150Sstevel@tonic-gate return (Z_OK); 19160Sstevel@tonic-gate } 19170Sstevel@tonic-gate if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) != 19180Sstevel@tonic-gate PO_SUCCESS) { 19190Sstevel@tonic-gate zerror(gettext("WARNING: pool_conf_open failed; " 19200Sstevel@tonic-gate "using default pool")); 19210Sstevel@tonic-gate pool_conf_free(poolconf); 19220Sstevel@tonic-gate return (Z_OK); 19230Sstevel@tonic-gate } 19240Sstevel@tonic-gate pool = pool_get_pool(poolconf, poolname); 19250Sstevel@tonic-gate (void) pool_conf_close(poolconf); 19260Sstevel@tonic-gate pool_conf_free(poolconf); 19270Sstevel@tonic-gate if (pool == NULL) { 19280Sstevel@tonic-gate zerror(gettext("WARNING: pool '%s' not found. " 19290Sstevel@tonic-gate "using default pool"), poolname); 19300Sstevel@tonic-gate } 19310Sstevel@tonic-gate 19320Sstevel@tonic-gate return (Z_OK); 19330Sstevel@tonic-gate } 19340Sstevel@tonic-gate 19350Sstevel@tonic-gate static int 1936823Sgjelinek verify_ipd(zone_dochandle_t handle) 1937823Sgjelinek { 1938823Sgjelinek int return_code = Z_OK; 1939823Sgjelinek struct zone_fstab fstab; 1940823Sgjelinek struct stat st; 1941823Sgjelinek char specdir[MAXPATHLEN]; 1942823Sgjelinek 1943823Sgjelinek if (zonecfg_setipdent(handle) != Z_OK) { 1944924Sgjelinek /* 1945924Sgjelinek * TRANSLATION_NOTE 1946924Sgjelinek * inherit-pkg-dirs is a literal that should not be translated. 1947924Sgjelinek */ 1948924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 1949823Sgjelinek "inherit-pkg-dirs: unable to enumerate mounts\n")); 1950823Sgjelinek return (Z_ERR); 1951823Sgjelinek } 1952823Sgjelinek while (zonecfg_getipdent(handle, &fstab) == Z_OK) { 1953823Sgjelinek /* 1954823Sgjelinek * Verify fs_dir exists. 1955823Sgjelinek */ 1956823Sgjelinek (void) snprintf(specdir, sizeof (specdir), "%s%s", 1957823Sgjelinek zonecfg_get_root(), fstab.zone_fs_dir); 1958823Sgjelinek if (stat(specdir, &st) != 0) { 1959924Sgjelinek /* 1960924Sgjelinek * TRANSLATION_NOTE 1961924Sgjelinek * inherit-pkg-dir is a literal that should not be 1962924Sgjelinek * translated. 1963924Sgjelinek */ 1964924Sgjelinek (void) fprintf(stderr, gettext("could not verify " 1965823Sgjelinek "inherit-pkg-dir %s: %s\n"), 1966823Sgjelinek fstab.zone_fs_dir, strerror(errno)); 1967823Sgjelinek return_code = Z_ERR; 1968823Sgjelinek } 1969823Sgjelinek if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 1970924Sgjelinek /* 1971924Sgjelinek * TRANSLATION_NOTE 1972924Sgjelinek * inherit-pkg-dir and NFS are literals that should 1973924Sgjelinek * not be translated. 1974924Sgjelinek */ 1975823Sgjelinek (void) fprintf(stderr, gettext("cannot verify " 1976924Sgjelinek "inherit-pkg-dir %s: NFS mounted file-system.\n" 1977924Sgjelinek "\tA local file-system must be used.\n"), 1978823Sgjelinek fstab.zone_fs_dir); 1979823Sgjelinek return_code = Z_ERR; 1980823Sgjelinek } 1981823Sgjelinek } 1982823Sgjelinek (void) zonecfg_endipdent(handle); 1983823Sgjelinek 1984823Sgjelinek return (return_code); 1985823Sgjelinek } 1986823Sgjelinek 19871393Slling /* ARGSUSED */ 19881393Slling static void 19891393Slling zfs_fs_err_handler(const char *fmt, va_list ap) 19901393Slling { 19911393Slling /* 19921393Slling * Do nothing - do not print the libzfs error messages. 19931393Slling */ 19941393Slling } 19951393Slling 19961393Slling /* 19971393Slling * Verify that the ZFS dataset exists, and its mountpoint 19981393Slling * property is set to "legacy". 19991393Slling */ 20001393Slling static int 20011393Slling verify_fs_zfs(struct zone_fstab *fstab) 20021393Slling { 20031393Slling zfs_handle_t *zhp; 20041393Slling char propbuf[ZFS_MAXPROPLEN]; 20051393Slling 20061393Slling zfs_set_error_handler(zfs_fs_err_handler); 20071393Slling 20081393Slling if ((zhp = zfs_open(fstab->zone_fs_special, ZFS_TYPE_ANY)) == NULL) { 20091397Slling (void) fprintf(stderr, gettext("could not verify fs %s: " 20101645Scomay "could not access zfs dataset '%s'\n"), 20111645Scomay fstab->zone_fs_dir, fstab->zone_fs_special); 20121393Slling return (Z_ERR); 20131393Slling } 20141393Slling 20151393Slling if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 20161393Slling (void) fprintf(stderr, gettext("cannot verify fs %s: " 20171645Scomay "'%s' is not a filesystem\n"), 20181645Scomay fstab->zone_fs_dir, fstab->zone_fs_special); 20191393Slling zfs_close(zhp); 20201393Slling return (Z_ERR); 20211393Slling } 20221393Slling 20231393Slling if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf), 20241393Slling NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) { 20251397Slling (void) fprintf(stderr, gettext("could not verify fs %s: " 20261645Scomay "zfs '%s' mountpoint is not \"legacy\"\n"), 20271645Scomay fstab->zone_fs_dir, fstab->zone_fs_special); 20281393Slling zfs_close(zhp); 20291393Slling return (Z_ERR); 20301393Slling } 20311393Slling 20321393Slling zfs_close(zhp); 20331393Slling return (Z_OK); 20341393Slling } 20351393Slling 20361393Slling /* 20371393Slling * Verify that the special device/filesystem exists and is valid. 20381393Slling */ 20391393Slling static int 20401393Slling verify_fs_special(struct zone_fstab *fstab) 20411393Slling { 20421393Slling struct stat st; 20431393Slling 20441393Slling if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) 20451393Slling return (verify_fs_zfs(fstab)); 20461393Slling 20471393Slling if (stat(fstab->zone_fs_special, &st) != 0) { 20481397Slling (void) fprintf(stderr, gettext("could not verify fs " 20491393Slling "%s: could not access %s: %s\n"), fstab->zone_fs_dir, 20501393Slling fstab->zone_fs_special, strerror(errno)); 20511393Slling return (Z_ERR); 20521393Slling } 20531393Slling 20541393Slling if (strcmp(st.st_fstype, MNTTYPE_NFS) == 0) { 20551393Slling /* 20561393Slling * TRANSLATION_NOTE 20571393Slling * fs and NFS are literals that should 20581393Slling * not be translated. 20591393Slling */ 20601393Slling (void) fprintf(stderr, gettext("cannot verify " 20611393Slling "fs %s: NFS mounted file-system.\n" 20621393Slling "\tA local file-system must be used.\n"), 20631393Slling fstab->zone_fs_special); 20641393Slling return (Z_ERR); 20651393Slling } 20661393Slling 20671393Slling return (Z_OK); 20681393Slling } 20691393Slling 2070823Sgjelinek static int 20710Sstevel@tonic-gate verify_filesystems(zone_dochandle_t handle) 20720Sstevel@tonic-gate { 20730Sstevel@tonic-gate int return_code = Z_OK; 20740Sstevel@tonic-gate struct zone_fstab fstab; 20750Sstevel@tonic-gate char cmdbuf[MAXPATHLEN]; 20760Sstevel@tonic-gate struct stat st; 20770Sstevel@tonic-gate 20780Sstevel@tonic-gate /* 20790Sstevel@tonic-gate * No need to verify inherit-pkg-dir fs types, as their type is 20800Sstevel@tonic-gate * implicitly lofs, which is known. Therefore, the types are only 20810Sstevel@tonic-gate * verified for regular filesystems below. 20820Sstevel@tonic-gate * 20830Sstevel@tonic-gate * Since the actual mount point is not known until the dependent mounts 20840Sstevel@tonic-gate * are performed, we don't attempt any path validation here: that will 20850Sstevel@tonic-gate * happen later when zoneadmd actually does the mounts. 20860Sstevel@tonic-gate */ 20870Sstevel@tonic-gate if (zonecfg_setfsent(handle) != Z_OK) { 2088924Sgjelinek (void) fprintf(stderr, gettext("could not verify file-systems: " 20890Sstevel@tonic-gate "unable to enumerate mounts\n")); 20900Sstevel@tonic-gate return (Z_ERR); 20910Sstevel@tonic-gate } 20920Sstevel@tonic-gate while (zonecfg_getfsent(handle, &fstab) == Z_OK) { 20930Sstevel@tonic-gate if (!zonecfg_valid_fs_type(fstab.zone_fs_type)) { 20940Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 20950Sstevel@tonic-gate "type %s is not allowed.\n"), fstab.zone_fs_dir, 20960Sstevel@tonic-gate fstab.zone_fs_type); 20970Sstevel@tonic-gate return_code = Z_ERR; 20980Sstevel@tonic-gate goto next_fs; 20990Sstevel@tonic-gate } 21000Sstevel@tonic-gate /* 21010Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/mount exists. 21020Sstevel@tonic-gate */ 21030Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/mount", 21040Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 21050Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21060Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 21070Sstevel@tonic-gate fstab.zone_fs_type); 21080Sstevel@tonic-gate return_code = Z_ERR; 21090Sstevel@tonic-gate goto next_fs; 21100Sstevel@tonic-gate } 21110Sstevel@tonic-gate if (stat(cmdbuf, &st) != 0) { 2112924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2113924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 21140Sstevel@tonic-gate cmdbuf, strerror(errno)); 21150Sstevel@tonic-gate return_code = Z_ERR; 21160Sstevel@tonic-gate goto next_fs; 21170Sstevel@tonic-gate } 21180Sstevel@tonic-gate if (!S_ISREG(st.st_mode)) { 2119924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2120924Sgjelinek "%s: %s is not a regular file\n"), 2121924Sgjelinek fstab.zone_fs_dir, cmdbuf); 21220Sstevel@tonic-gate return_code = Z_ERR; 21230Sstevel@tonic-gate goto next_fs; 21240Sstevel@tonic-gate } 21250Sstevel@tonic-gate /* 21260Sstevel@tonic-gate * Verify /usr/lib/fs/<fstype>/fsck exists iff zone_fs_raw is 21270Sstevel@tonic-gate * set. 21280Sstevel@tonic-gate */ 21290Sstevel@tonic-gate if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/fs/%s/fsck", 21300Sstevel@tonic-gate fstab.zone_fs_type) > sizeof (cmdbuf)) { 21310Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21320Sstevel@tonic-gate "type %s is too long.\n"), fstab.zone_fs_dir, 21330Sstevel@tonic-gate fstab.zone_fs_type); 21340Sstevel@tonic-gate return_code = Z_ERR; 21350Sstevel@tonic-gate goto next_fs; 21360Sstevel@tonic-gate } 21370Sstevel@tonic-gate if (fstab.zone_fs_raw[0] == '\0' && stat(cmdbuf, &st) == 0) { 2138924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2139924Sgjelinek "%s: must specify 'raw' device for %s " 2140924Sgjelinek "file-systems\n"), 21410Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 21420Sstevel@tonic-gate return_code = Z_ERR; 21430Sstevel@tonic-gate goto next_fs; 21440Sstevel@tonic-gate } 21450Sstevel@tonic-gate if (fstab.zone_fs_raw[0] != '\0' && 21460Sstevel@tonic-gate (stat(cmdbuf, &st) != 0 || !S_ISREG(st.st_mode))) { 21470Sstevel@tonic-gate (void) fprintf(stderr, gettext("cannot verify fs %s: " 21480Sstevel@tonic-gate "'raw' device specified but " 21490Sstevel@tonic-gate "no fsck executable exists for %s\n"), 21500Sstevel@tonic-gate fstab.zone_fs_dir, fstab.zone_fs_type); 21510Sstevel@tonic-gate return_code = Z_ERR; 21520Sstevel@tonic-gate goto next_fs; 21530Sstevel@tonic-gate } 21541393Slling 21551393Slling /* Verify fs_special. */ 21561393Slling if ((return_code = verify_fs_special(&fstab)) != Z_OK) 2157823Sgjelinek goto next_fs; 21581393Slling 21591393Slling /* Verify fs_raw. */ 2160823Sgjelinek if (fstab.zone_fs_raw[0] != '\0' && 2161823Sgjelinek stat(fstab.zone_fs_raw, &st) != 0) { 2162924Sgjelinek /* 2163924Sgjelinek * TRANSLATION_NOTE 2164924Sgjelinek * fs is a literal that should not be translated. 2165924Sgjelinek */ 2166924Sgjelinek (void) fprintf(stderr, gettext("could not verify fs " 2167924Sgjelinek "%s: could not access %s: %s\n"), fstab.zone_fs_dir, 2168823Sgjelinek fstab.zone_fs_raw, strerror(errno)); 2169823Sgjelinek return_code = Z_ERR; 2170823Sgjelinek goto next_fs; 2171823Sgjelinek } 21720Sstevel@tonic-gate next_fs: 21730Sstevel@tonic-gate zonecfg_free_fs_option_list(fstab.zone_fs_options); 21740Sstevel@tonic-gate } 21750Sstevel@tonic-gate (void) zonecfg_endfsent(handle); 21760Sstevel@tonic-gate 21770Sstevel@tonic-gate return (return_code); 21780Sstevel@tonic-gate } 21790Sstevel@tonic-gate 2180789Sahrens const char *current_dataset; 2181789Sahrens 2182789Sahrens /* 2183789Sahrens * Custom error handler for errors incurred as part of the checks below. We 2184789Sahrens * want to trim off the leading 'cannot open ...' to create a better error 2185789Sahrens * message. The only other way this can fail is if we fail to set the 'zoned' 2186789Sahrens * property. In this case we just pass the error on verbatim. 2187789Sahrens */ 2188789Sahrens static void 2189789Sahrens zfs_error_handler(const char *fmt, va_list ap) 2190789Sahrens { 2191789Sahrens char buf[1024]; 2192789Sahrens 2193789Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, ap); 2194789Sahrens 2195789Sahrens if (strncmp(gettext("cannot open "), buf, 2196789Sahrens strlen(gettext("cannot open "))) == 0) 2197924Sgjelinek /* 2198924Sgjelinek * TRANSLATION_NOTE 2199924Sgjelinek * zfs and dataset are literals that should not be translated. 2200924Sgjelinek */ 2201924Sgjelinek (void) fprintf(stderr, gettext("could not verify zfs " 2202789Sahrens "dataset %s%s\n"), current_dataset, strchr(buf, ':')); 2203789Sahrens else 2204924Sgjelinek (void) fprintf(stderr, gettext("could not verify zfs dataset " 2205789Sahrens "%s: %s\n"), current_dataset, buf); 2206789Sahrens } 2207789Sahrens 2208789Sahrens /* ARGSUSED */ 2209789Sahrens static int 2210789Sahrens check_zvol(zfs_handle_t *zhp, void *unused) 2211789Sahrens { 2212789Sahrens int ret; 2213789Sahrens 2214789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 2215924Sgjelinek /* 2216924Sgjelinek * TRANSLATION_NOTE 2217924Sgjelinek * zfs and dataset are literals that should not be translated. 2218924Sgjelinek */ 2219789Sahrens (void) fprintf(stderr, gettext("cannot verify zfs dataset %s: " 2220789Sahrens "volumes cannot be specified as a zone dataset resource\n"), 2221789Sahrens zfs_get_name(zhp)); 2222789Sahrens ret = -1; 2223789Sahrens } else { 2224789Sahrens ret = zfs_iter_children(zhp, check_zvol, NULL); 2225789Sahrens } 2226789Sahrens 2227789Sahrens zfs_close(zhp); 2228789Sahrens 2229789Sahrens return (ret); 2230789Sahrens } 2231789Sahrens 2232789Sahrens /* 2233789Sahrens * Validate that the given dataset exists on the system, and that neither it nor 2234789Sahrens * its children are zvols. 2235789Sahrens * 2236789Sahrens * Note that we don't do anything with the 'zoned' property here. All 2237789Sahrens * management is done in zoneadmd when the zone is actually rebooted. This 2238789Sahrens * allows us to automatically set the zoned property even when a zone is 2239789Sahrens * rebooted by the administrator. 2240789Sahrens */ 2241789Sahrens static int 2242789Sahrens verify_datasets(zone_dochandle_t handle) 2243789Sahrens { 2244789Sahrens int return_code = Z_OK; 2245789Sahrens struct zone_dstab dstab; 2246789Sahrens zfs_handle_t *zhp; 2247789Sahrens char propbuf[ZFS_MAXPROPLEN]; 2248789Sahrens char source[ZFS_MAXNAMELEN]; 2249789Sahrens zfs_source_t srctype; 2250789Sahrens 2251789Sahrens if (zonecfg_setdsent(handle) != Z_OK) { 2252924Sgjelinek /* 2253924Sgjelinek * TRANSLATION_NOTE 2254924Sgjelinek * zfs and dataset are literals that should not be translated. 2255924Sgjelinek */ 2256924Sgjelinek (void) fprintf(stderr, gettext("could not verify zfs datasets: " 2257789Sahrens "unable to enumerate datasets\n")); 2258789Sahrens return (Z_ERR); 2259789Sahrens } 2260789Sahrens 2261789Sahrens zfs_set_error_handler(zfs_error_handler); 2262789Sahrens 2263789Sahrens while (zonecfg_getdsent(handle, &dstab) == Z_OK) { 2264789Sahrens 2265789Sahrens current_dataset = dstab.zone_dataset_name; 2266789Sahrens 2267789Sahrens if ((zhp = zfs_open(dstab.zone_dataset_name, 2268789Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) { 2269789Sahrens return_code = Z_ERR; 2270789Sahrens continue; 2271789Sahrens } 2272789Sahrens 2273789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, 2274789Sahrens sizeof (propbuf), &srctype, source, 2275789Sahrens sizeof (source), 0) == 0 && 2276789Sahrens (srctype == ZFS_SRC_INHERITED)) { 2277924Sgjelinek (void) fprintf(stderr, gettext("could not verify zfs " 2278789Sahrens "dataset %s: mountpoint cannot be inherited\n"), 2279789Sahrens dstab.zone_dataset_name); 2280789Sahrens return_code = Z_ERR; 2281789Sahrens zfs_close(zhp); 2282789Sahrens continue; 2283789Sahrens } 2284789Sahrens 2285789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 2286789Sahrens (void) fprintf(stderr, gettext("cannot verify zfs " 2287789Sahrens "dataset %s: volumes cannot be specified as a " 2288789Sahrens "zone dataset resource\n"), 2289789Sahrens dstab.zone_dataset_name); 2290789Sahrens return_code = Z_ERR; 2291789Sahrens } 2292789Sahrens 2293789Sahrens if (zfs_iter_children(zhp, check_zvol, NULL) != 0) 2294789Sahrens return_code = Z_ERR; 2295789Sahrens 2296789Sahrens zfs_close(zhp); 2297789Sahrens } 2298789Sahrens (void) zonecfg_enddsent(handle); 2299789Sahrens 2300789Sahrens return (return_code); 2301789Sahrens } 2302789Sahrens 23030Sstevel@tonic-gate static int 23041645Scomay verify_limitpriv(zone_dochandle_t handle) 23051645Scomay { 23061645Scomay char *privname = NULL; 23071645Scomay int err; 23081645Scomay priv_set_t *privs; 23091645Scomay 23101645Scomay if ((privs = priv_allocset()) == NULL) { 23111645Scomay zperror(gettext("failed to allocate privilege set"), B_FALSE); 23121645Scomay return (Z_NOMEM); 23131645Scomay } 23141645Scomay err = zonecfg_get_privset(handle, privs, &privname); 23151645Scomay switch (err) { 23161645Scomay case Z_OK: 23171645Scomay break; 23181645Scomay case Z_PRIV_PROHIBITED: 23191645Scomay (void) fprintf(stderr, gettext("privilege \"%s\" is not " 23201645Scomay "permitted within the zone's privilege set\n"), privname); 23211645Scomay break; 23221645Scomay case Z_PRIV_REQUIRED: 23231645Scomay (void) fprintf(stderr, gettext("required privilege \"%s\" is " 23241645Scomay "missing from the zone's privilege set\n"), privname); 23251645Scomay break; 23261645Scomay case Z_PRIV_UNKNOWN: 23271645Scomay (void) fprintf(stderr, gettext("unknown privilege \"%s\" " 23281645Scomay "specified in the zone's privilege set\n"), privname); 23291645Scomay break; 23301645Scomay default: 23311645Scomay zperror( 23321645Scomay gettext("failed to determine the zone's privilege set"), 23331645Scomay B_TRUE); 23341645Scomay break; 23351645Scomay } 23361645Scomay free(privname); 23371645Scomay priv_freeset(privs); 23381645Scomay return (err); 23391645Scomay } 23401645Scomay 23411645Scomay static int 23420Sstevel@tonic-gate verify_details(int cmd_num) 23430Sstevel@tonic-gate { 23440Sstevel@tonic-gate zone_dochandle_t handle; 23450Sstevel@tonic-gate struct zone_nwiftab nwiftab; 23460Sstevel@tonic-gate char zonepath[MAXPATHLEN], checkpath[MAXPATHLEN]; 23470Sstevel@tonic-gate int return_code = Z_OK; 23480Sstevel@tonic-gate int err; 2349766Scarlsonj boolean_t in_alt_root; 23500Sstevel@tonic-gate 23510Sstevel@tonic-gate if ((handle = zonecfg_init_handle()) == NULL) { 23520Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 23530Sstevel@tonic-gate return (Z_ERR); 23540Sstevel@tonic-gate } 23550Sstevel@tonic-gate if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 23560Sstevel@tonic-gate errno = err; 23570Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 23580Sstevel@tonic-gate zonecfg_fini_handle(handle); 23590Sstevel@tonic-gate return (Z_ERR); 23600Sstevel@tonic-gate } 23610Sstevel@tonic-gate if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) != 23620Sstevel@tonic-gate Z_OK) { 23630Sstevel@tonic-gate errno = err; 23640Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 23650Sstevel@tonic-gate zonecfg_fini_handle(handle); 23660Sstevel@tonic-gate return (Z_ERR); 23670Sstevel@tonic-gate } 23680Sstevel@tonic-gate /* 23690Sstevel@tonic-gate * zonecfg_get_zonepath() gets its data from the XML repository. 23700Sstevel@tonic-gate * Verify this against the index file, which is checked first by 23710Sstevel@tonic-gate * zone_get_zonepath(). If they don't match, bail out. 23720Sstevel@tonic-gate */ 23730Sstevel@tonic-gate if ((err = zone_get_zonepath(target_zone, checkpath, 23740Sstevel@tonic-gate sizeof (checkpath))) != Z_OK) { 23750Sstevel@tonic-gate errno = err; 23760Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 23770Sstevel@tonic-gate return (Z_ERR); 23780Sstevel@tonic-gate } 23790Sstevel@tonic-gate if (strcmp(zonepath, checkpath) != 0) { 2380924Sgjelinek /* 2381924Sgjelinek * TRANSLATION_NOTE 2382924Sgjelinek * XML and zonepath are literals that should not be translated. 2383924Sgjelinek */ 23840Sstevel@tonic-gate (void) fprintf(stderr, gettext("The XML repository has " 23850Sstevel@tonic-gate "zonepath '%s',\nbut the index file has zonepath '%s'.\n" 23860Sstevel@tonic-gate "These must match, so fix the incorrect entry.\n"), 23870Sstevel@tonic-gate zonepath, checkpath); 23880Sstevel@tonic-gate return (Z_ERR); 23890Sstevel@tonic-gate } 23900Sstevel@tonic-gate if (validate_zonepath(zonepath, cmd_num) != Z_OK) { 23910Sstevel@tonic-gate (void) fprintf(stderr, gettext("could not verify zonepath %s " 23920Sstevel@tonic-gate "because of the above errors.\n"), zonepath); 23930Sstevel@tonic-gate return_code = Z_ERR; 23940Sstevel@tonic-gate } 23950Sstevel@tonic-gate 2396766Scarlsonj in_alt_root = zonecfg_in_alt_root(); 2397766Scarlsonj if (in_alt_root) 2398766Scarlsonj goto no_net; 2399766Scarlsonj 24000Sstevel@tonic-gate if ((err = zonecfg_setnwifent(handle)) != Z_OK) { 24010Sstevel@tonic-gate errno = err; 24020Sstevel@tonic-gate zperror(cmd_to_str(cmd_num), B_TRUE); 24030Sstevel@tonic-gate zonecfg_fini_handle(handle); 24040Sstevel@tonic-gate return (Z_ERR); 24050Sstevel@tonic-gate } 24060Sstevel@tonic-gate while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { 24070Sstevel@tonic-gate struct lifreq lifr; 24080Sstevel@tonic-gate sa_family_t af; 24090Sstevel@tonic-gate int so, res; 24100Sstevel@tonic-gate 24110Sstevel@tonic-gate /* skip any loopback interfaces */ 24120Sstevel@tonic-gate if (strcmp(nwiftab.zone_nwif_physical, "lo0") == 0) 24130Sstevel@tonic-gate continue; 24140Sstevel@tonic-gate if ((res = zonecfg_valid_net_address(nwiftab.zone_nwif_address, 24150Sstevel@tonic-gate &lifr)) != Z_OK) { 24160Sstevel@tonic-gate (void) fprintf(stderr, gettext("could not verify %s " 24170Sstevel@tonic-gate "%s=%s %s=%s: %s\n"), "net", "address", 24180Sstevel@tonic-gate nwiftab.zone_nwif_address, "physical", 24190Sstevel@tonic-gate nwiftab.zone_nwif_physical, zonecfg_strerror(res)); 24200Sstevel@tonic-gate return_code = Z_ERR; 24210Sstevel@tonic-gate continue; 24220Sstevel@tonic-gate } 24230Sstevel@tonic-gate af = lifr.lifr_addr.ss_family; 24240Sstevel@tonic-gate (void) memset(&lifr, 0, sizeof (lifr)); 24250Sstevel@tonic-gate (void) strlcpy(lifr.lifr_name, nwiftab.zone_nwif_physical, 24260Sstevel@tonic-gate sizeof (lifr.lifr_name)); 24270Sstevel@tonic-gate lifr.lifr_addr.ss_family = af; 24280Sstevel@tonic-gate if ((so = socket(af, SOCK_DGRAM, 0)) < 0) { 24290Sstevel@tonic-gate (void) fprintf(stderr, gettext("could not verify %s " 24300Sstevel@tonic-gate "%s=%s %s=%s: could not get socket: %s\n"), "net", 24310Sstevel@tonic-gate "address", nwiftab.zone_nwif_address, "physical", 24320Sstevel@tonic-gate nwiftab.zone_nwif_physical, strerror(errno)); 24330Sstevel@tonic-gate return_code = Z_ERR; 24340Sstevel@tonic-gate continue; 24350Sstevel@tonic-gate } 24360Sstevel@tonic-gate if (ioctl(so, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { 24370Sstevel@tonic-gate (void) fprintf(stderr, 24380Sstevel@tonic-gate gettext("could not verify %s %s=%s %s=%s: %s\n"), 24390Sstevel@tonic-gate "net", "address", nwiftab.zone_nwif_address, 24400Sstevel@tonic-gate "physical", nwiftab.zone_nwif_physical, 24410Sstevel@tonic-gate strerror(errno)); 24420Sstevel@tonic-gate return_code = Z_ERR; 24430Sstevel@tonic-gate } 24440Sstevel@tonic-gate (void) close(so); 24450Sstevel@tonic-gate } 24460Sstevel@tonic-gate (void) zonecfg_endnwifent(handle); 2447766Scarlsonj no_net: 24480Sstevel@tonic-gate 24490Sstevel@tonic-gate if (verify_filesystems(handle) != Z_OK) 24500Sstevel@tonic-gate return_code = Z_ERR; 2451823Sgjelinek if (verify_ipd(handle) != Z_OK) 2452823Sgjelinek return_code = Z_ERR; 2453766Scarlsonj if (!in_alt_root && verify_rctls(handle) != Z_OK) 24540Sstevel@tonic-gate return_code = Z_ERR; 2455766Scarlsonj if (!in_alt_root && verify_pool(handle) != Z_OK) 24560Sstevel@tonic-gate return_code = Z_ERR; 2457789Sahrens if (!in_alt_root && verify_datasets(handle) != Z_OK) 2458789Sahrens return_code = Z_ERR; 24591645Scomay 24601645Scomay /* 24611645Scomay * As the "mount" command is used for patching/upgrading of zones 24621645Scomay * or other maintenance processes, the zone's privilege set is not 24631645Scomay * checked in this case. Instead, the default, safe set of 24641645Scomay * privileges will be used when this zone is created in the 24651645Scomay * kernel. 24661645Scomay */ 24671645Scomay if (!in_alt_root && cmd_num != CMD_MOUNT && 24681645Scomay verify_limitpriv(handle) != Z_OK) 24691645Scomay return_code = Z_ERR; 24700Sstevel@tonic-gate zonecfg_fini_handle(handle); 24710Sstevel@tonic-gate if (return_code == Z_ERR) 24720Sstevel@tonic-gate (void) fprintf(stderr, 24730Sstevel@tonic-gate gettext("%s: zone %s failed to verify\n"), 24740Sstevel@tonic-gate execname, target_zone); 24750Sstevel@tonic-gate return (return_code); 24760Sstevel@tonic-gate } 24770Sstevel@tonic-gate 24780Sstevel@tonic-gate static int 24790Sstevel@tonic-gate verify_func(int argc, char *argv[]) 24800Sstevel@tonic-gate { 24810Sstevel@tonic-gate int arg; 24820Sstevel@tonic-gate 24830Sstevel@tonic-gate optind = 0; 24840Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 24850Sstevel@tonic-gate switch (arg) { 24860Sstevel@tonic-gate case '?': 24870Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 24880Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 24890Sstevel@tonic-gate default: 24900Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 24910Sstevel@tonic-gate return (Z_USAGE); 24920Sstevel@tonic-gate } 24930Sstevel@tonic-gate } 24940Sstevel@tonic-gate if (argc > optind) { 24950Sstevel@tonic-gate sub_usage(SHELP_VERIFY, CMD_VERIFY); 24960Sstevel@tonic-gate return (Z_USAGE); 24970Sstevel@tonic-gate } 24980Sstevel@tonic-gate if (sanity_check(target_zone, CMD_VERIFY, B_FALSE, B_FALSE) != Z_OK) 24990Sstevel@tonic-gate return (Z_ERR); 25000Sstevel@tonic-gate return (verify_details(CMD_VERIFY)); 25010Sstevel@tonic-gate } 25020Sstevel@tonic-gate 25030Sstevel@tonic-gate #define LUCREATEZONE "/usr/lib/lu/lucreatezone" 25040Sstevel@tonic-gate 25050Sstevel@tonic-gate static int 25060Sstevel@tonic-gate install_func(int argc, char *argv[]) 25070Sstevel@tonic-gate { 25080Sstevel@tonic-gate /* 9: "exec " and " -z " */ 25090Sstevel@tonic-gate char cmdbuf[sizeof (LUCREATEZONE) + ZONENAME_MAX + 9]; 25100Sstevel@tonic-gate int lockfd; 25110Sstevel@tonic-gate int err, arg; 25120Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 25130Sstevel@tonic-gate int status; 25140Sstevel@tonic-gate 2515766Scarlsonj if (zonecfg_in_alt_root()) { 2516766Scarlsonj zerror(gettext("cannot install zone in alternate root")); 2517766Scarlsonj return (Z_ERR); 2518766Scarlsonj } 2519766Scarlsonj 25200Sstevel@tonic-gate optind = 0; 25210Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 25220Sstevel@tonic-gate switch (arg) { 25230Sstevel@tonic-gate case '?': 25240Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 25250Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 25260Sstevel@tonic-gate default: 25270Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 25280Sstevel@tonic-gate return (Z_USAGE); 25290Sstevel@tonic-gate } 25300Sstevel@tonic-gate } 25310Sstevel@tonic-gate if (argc > optind) { 25320Sstevel@tonic-gate sub_usage(SHELP_INSTALL, CMD_INSTALL); 25330Sstevel@tonic-gate return (Z_USAGE); 25340Sstevel@tonic-gate } 25350Sstevel@tonic-gate if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE) != Z_OK) 25360Sstevel@tonic-gate return (Z_ERR); 25370Sstevel@tonic-gate if (verify_details(CMD_INSTALL) != Z_OK) 25380Sstevel@tonic-gate return (Z_ERR); 25390Sstevel@tonic-gate 25400Sstevel@tonic-gate if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 25410Sstevel@tonic-gate zerror(gettext("another %s may have an operation in progress."), 25421300Sgjelinek "zoneadm"); 25430Sstevel@tonic-gate return (Z_ERR); 25440Sstevel@tonic-gate } 25450Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 25460Sstevel@tonic-gate if (err != Z_OK) { 25470Sstevel@tonic-gate errno = err; 25480Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 25490Sstevel@tonic-gate goto done; 25500Sstevel@tonic-gate } 25510Sstevel@tonic-gate 25520Sstevel@tonic-gate /* 25530Sstevel@tonic-gate * According to the Application Packaging Developer's Guide, a 25540Sstevel@tonic-gate * "checkinstall" script when included in a package is executed as 25550Sstevel@tonic-gate * the user "install", if such a user exists, or by the user 25560Sstevel@tonic-gate * "nobody". In order to support this dubious behavior, the path 25570Sstevel@tonic-gate * to the zone being constructed is opened up during the life of 25580Sstevel@tonic-gate * the command laying down the zone's root file system. Once this 25590Sstevel@tonic-gate * has completed, regardless of whether it was successful, the 25600Sstevel@tonic-gate * path to the zone is again restricted. 25610Sstevel@tonic-gate */ 25620Sstevel@tonic-gate if ((err = zone_get_zonepath(target_zone, zonepath, 25630Sstevel@tonic-gate sizeof (zonepath))) != Z_OK) { 25640Sstevel@tonic-gate errno = err; 25650Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 25660Sstevel@tonic-gate goto done; 25670Sstevel@tonic-gate } 25680Sstevel@tonic-gate if (chmod(zonepath, DEFAULT_DIR_MODE) != 0) { 25690Sstevel@tonic-gate zperror(zonepath, B_FALSE); 25700Sstevel@tonic-gate err = Z_ERR; 25710Sstevel@tonic-gate goto done; 25720Sstevel@tonic-gate } 25730Sstevel@tonic-gate 25740Sstevel@tonic-gate /* 25750Sstevel@tonic-gate * "exec" the command so that the returned status is that of 25760Sstevel@tonic-gate * LUCREATEZONE and not the shell. 25770Sstevel@tonic-gate */ 25780Sstevel@tonic-gate (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " LUCREATEZONE " -z %s", 25790Sstevel@tonic-gate target_zone); 25800Sstevel@tonic-gate status = do_subproc(cmdbuf); 25810Sstevel@tonic-gate if (chmod(zonepath, S_IRWXU) != 0) { 25820Sstevel@tonic-gate zperror(zonepath, B_FALSE); 25830Sstevel@tonic-gate err = Z_ERR; 25840Sstevel@tonic-gate goto done; 25850Sstevel@tonic-gate } 25860Sstevel@tonic-gate if ((err = subproc_status(LUCREATEZONE, status)) != Z_OK) 25870Sstevel@tonic-gate goto done; 25880Sstevel@tonic-gate 25890Sstevel@tonic-gate if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 25900Sstevel@tonic-gate errno = err; 25910Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 25920Sstevel@tonic-gate goto done; 25930Sstevel@tonic-gate } 25940Sstevel@tonic-gate 25950Sstevel@tonic-gate done: 25960Sstevel@tonic-gate release_lock_file(lockfd); 25970Sstevel@tonic-gate return ((err == Z_OK) ? Z_OK : Z_ERR); 25980Sstevel@tonic-gate } 25990Sstevel@tonic-gate 26000Sstevel@tonic-gate /* 26011300Sgjelinek * Check that the inherited pkg dirs are the same for the clone and its source. 26021300Sgjelinek * The easiest way to do that is check that the list of ipds is the same 26031300Sgjelinek * by matching each one against the other. This algorithm should be fine since 26041300Sgjelinek * the list of ipds should not be that long. 26051300Sgjelinek */ 26061300Sgjelinek static int 26071300Sgjelinek valid_ipd_clone(zone_dochandle_t s_handle, char *source_zone, 26081300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 26091300Sgjelinek { 26101300Sgjelinek int err; 26111300Sgjelinek int res = Z_OK; 26121300Sgjelinek int s_cnt = 0; 26131300Sgjelinek int t_cnt = 0; 26141300Sgjelinek struct zone_fstab s_fstab; 26151300Sgjelinek struct zone_fstab t_fstab; 26161300Sgjelinek 26171300Sgjelinek /* 26181300Sgjelinek * First check the source of the clone against the target. 26191300Sgjelinek */ 26201300Sgjelinek if ((err = zonecfg_setipdent(s_handle)) != Z_OK) { 26211300Sgjelinek errno = err; 26221300Sgjelinek zperror2(source_zone, gettext("could not enumerate " 26231300Sgjelinek "inherit-pkg-dirs")); 26241300Sgjelinek return (Z_ERR); 26251300Sgjelinek } 26261300Sgjelinek 26271300Sgjelinek while (zonecfg_getipdent(s_handle, &s_fstab) == Z_OK) { 26281300Sgjelinek boolean_t match = B_FALSE; 26291300Sgjelinek 26301300Sgjelinek s_cnt++; 26311300Sgjelinek 26321300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 26331300Sgjelinek errno = err; 26341300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 26351300Sgjelinek "inherit-pkg-dirs")); 26361300Sgjelinek (void) zonecfg_endipdent(s_handle); 26371300Sgjelinek return (Z_ERR); 26381300Sgjelinek } 26391300Sgjelinek 26401300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) { 26411300Sgjelinek if (strcmp(s_fstab.zone_fs_dir, t_fstab.zone_fs_dir) 26421300Sgjelinek == 0) { 26431300Sgjelinek match = B_TRUE; 26441300Sgjelinek break; 26451300Sgjelinek } 26461300Sgjelinek } 26471300Sgjelinek (void) zonecfg_endipdent(t_handle); 26481300Sgjelinek 26491300Sgjelinek if (!match) { 26501300Sgjelinek (void) fprintf(stderr, gettext("inherit-pkg-dir " 26511300Sgjelinek "'%s' is not configured in zone %s.\n"), 26521300Sgjelinek s_fstab.zone_fs_dir, target_zone); 26531300Sgjelinek res = Z_ERR; 26541300Sgjelinek } 26551300Sgjelinek } 26561300Sgjelinek 26571300Sgjelinek (void) zonecfg_endipdent(s_handle); 26581300Sgjelinek 26591300Sgjelinek /* skip the next check if we already have errors */ 26601300Sgjelinek if (res == Z_ERR) 26611300Sgjelinek return (res); 26621300Sgjelinek 26631300Sgjelinek /* 26641300Sgjelinek * Now check the number of ipds in the target so we can verify 26651300Sgjelinek * that the source is not a subset of the target. 26661300Sgjelinek */ 26671300Sgjelinek if ((err = zonecfg_setipdent(t_handle)) != Z_OK) { 26681300Sgjelinek errno = err; 26691300Sgjelinek zperror2(target_zone, gettext("could not enumerate " 26701300Sgjelinek "inherit-pkg-dirs")); 26711300Sgjelinek return (Z_ERR); 26721300Sgjelinek } 26731300Sgjelinek 26741300Sgjelinek while (zonecfg_getipdent(t_handle, &t_fstab) == Z_OK) 26751300Sgjelinek t_cnt++; 26761300Sgjelinek 26771300Sgjelinek (void) zonecfg_endipdent(t_handle); 26781300Sgjelinek 26791300Sgjelinek if (t_cnt != s_cnt) { 26801300Sgjelinek (void) fprintf(stderr, gettext("Zone %s is configured " 26811300Sgjelinek "with inherit-pkg-dirs that are not configured in zone " 26821300Sgjelinek "%s.\n"), target_zone, source_zone); 26831300Sgjelinek res = Z_ERR; 26841300Sgjelinek } 26851300Sgjelinek 26861300Sgjelinek return (res); 26871300Sgjelinek } 26881300Sgjelinek 26891300Sgjelinek static void 26901300Sgjelinek warn_dev_match(zone_dochandle_t s_handle, char *source_zone, 26911300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 26921300Sgjelinek { 26931300Sgjelinek int err; 26941300Sgjelinek struct zone_devtab s_devtab; 26951300Sgjelinek struct zone_devtab t_devtab; 26961300Sgjelinek 26971300Sgjelinek if ((err = zonecfg_setdevent(t_handle)) != Z_OK) { 26981300Sgjelinek errno = err; 26991300Sgjelinek zperror2(target_zone, gettext("could not enumerate devices")); 27001300Sgjelinek return; 27011300Sgjelinek } 27021300Sgjelinek 27031300Sgjelinek while (zonecfg_getdevent(t_handle, &t_devtab) == Z_OK) { 27041300Sgjelinek if ((err = zonecfg_setdevent(s_handle)) != Z_OK) { 27051300Sgjelinek errno = err; 27061300Sgjelinek zperror2(source_zone, 27071300Sgjelinek gettext("could not enumerate devices")); 27081300Sgjelinek (void) zonecfg_enddevent(t_handle); 27091300Sgjelinek return; 27101300Sgjelinek } 27111300Sgjelinek 27121300Sgjelinek while (zonecfg_getdevent(s_handle, &s_devtab) == Z_OK) { 27131300Sgjelinek /* 27141300Sgjelinek * Use fnmatch to catch the case where wildcards 27151300Sgjelinek * were used in one zone and the other has an 27161300Sgjelinek * explicit entry (e.g. /dev/dsk/c0t0d0s6 vs. 27171300Sgjelinek * /dev/\*dsk/c0t0d0s6). 27181300Sgjelinek */ 27191300Sgjelinek if (fnmatch(t_devtab.zone_dev_match, 27201300Sgjelinek s_devtab.zone_dev_match, FNM_PATHNAME) == 0 || 27211300Sgjelinek fnmatch(s_devtab.zone_dev_match, 27221300Sgjelinek t_devtab.zone_dev_match, FNM_PATHNAME) == 0) { 27231300Sgjelinek (void) fprintf(stderr, 27241300Sgjelinek gettext("WARNING: device '%s' " 27251300Sgjelinek "is configured in both zones.\n"), 27261300Sgjelinek t_devtab.zone_dev_match); 27271300Sgjelinek break; 27281300Sgjelinek } 27291300Sgjelinek } 27301300Sgjelinek (void) zonecfg_enddevent(s_handle); 27311300Sgjelinek } 27321300Sgjelinek 27331300Sgjelinek (void) zonecfg_enddevent(t_handle); 27341300Sgjelinek } 27351300Sgjelinek 27361300Sgjelinek /* 27371300Sgjelinek * Check if the specified mount option (opt) is contained within the 27381300Sgjelinek * options string. 27391300Sgjelinek */ 27401300Sgjelinek static boolean_t 27411300Sgjelinek opt_match(char *opt, char *options) 27421300Sgjelinek { 27431300Sgjelinek char *p; 27441300Sgjelinek char *lastp; 27451300Sgjelinek 27461300Sgjelinek if ((p = strtok_r(options, ",", &lastp)) != NULL) { 27471300Sgjelinek if (strcmp(p, opt) == 0) 27481300Sgjelinek return (B_TRUE); 27491300Sgjelinek while ((p = strtok_r(NULL, ",", &lastp)) != NULL) { 27501300Sgjelinek if (strcmp(p, opt) == 0) 27511300Sgjelinek return (B_TRUE); 27521300Sgjelinek } 27531300Sgjelinek } 27541300Sgjelinek 27551300Sgjelinek return (B_FALSE); 27561300Sgjelinek } 27571300Sgjelinek 27581300Sgjelinek #define RW_LOFS "WARNING: read-write lofs file-system on '%s' is configured " \ 27591300Sgjelinek "in both zones.\n" 27601300Sgjelinek 27611300Sgjelinek static void 27621300Sgjelinek print_fs_warnings(struct zone_fstab *s_fstab, struct zone_fstab *t_fstab) 27631300Sgjelinek { 27641300Sgjelinek /* 27651300Sgjelinek * It is ok to have shared lofs mounted fs but we want to warn if 27661300Sgjelinek * either is rw since this will effect the other zone. 27671300Sgjelinek */ 27681300Sgjelinek if (strcmp(t_fstab->zone_fs_type, "lofs") == 0) { 27691300Sgjelinek zone_fsopt_t *optp; 27701300Sgjelinek 27711300Sgjelinek /* The default is rw so no options means rw */ 27721300Sgjelinek if (t_fstab->zone_fs_options == NULL || 27731300Sgjelinek s_fstab->zone_fs_options == NULL) { 27741300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 27751300Sgjelinek t_fstab->zone_fs_special); 27761300Sgjelinek return; 27771300Sgjelinek } 27781300Sgjelinek 27791300Sgjelinek for (optp = s_fstab->zone_fs_options; optp != NULL; 27801300Sgjelinek optp = optp->zone_fsopt_next) { 27811300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 27821300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 27831300Sgjelinek s_fstab->zone_fs_special); 27841300Sgjelinek return; 27851300Sgjelinek } 27861300Sgjelinek } 27871300Sgjelinek 27881300Sgjelinek for (optp = t_fstab->zone_fs_options; optp != NULL; 27891300Sgjelinek optp = optp->zone_fsopt_next) { 27901300Sgjelinek if (opt_match("rw", optp->zone_fsopt_opt)) { 27911300Sgjelinek (void) fprintf(stderr, gettext(RW_LOFS), 27921300Sgjelinek t_fstab->zone_fs_special); 27931300Sgjelinek return; 27941300Sgjelinek } 27951300Sgjelinek } 27961300Sgjelinek 27971300Sgjelinek return; 27981300Sgjelinek } 27991300Sgjelinek 28001300Sgjelinek /* 28011300Sgjelinek * TRANSLATION_NOTE 28021300Sgjelinek * The first variable is the file-system type and the second is 28031300Sgjelinek * the file-system special device. For example, 28041300Sgjelinek * WARNING: ufs file-system on '/dev/dsk/c0t0d0s0' ... 28051300Sgjelinek */ 28061300Sgjelinek (void) fprintf(stderr, gettext("WARNING: %s file-system on '%s' " 28071300Sgjelinek "is configured in both zones.\n"), t_fstab->zone_fs_type, 28081300Sgjelinek t_fstab->zone_fs_special); 28091300Sgjelinek } 28101300Sgjelinek 28111300Sgjelinek static void 28121300Sgjelinek warn_fs_match(zone_dochandle_t s_handle, char *source_zone, 28131300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 28141300Sgjelinek { 28151300Sgjelinek int err; 28161300Sgjelinek struct zone_fstab s_fstab; 28171300Sgjelinek struct zone_fstab t_fstab; 28181300Sgjelinek 28191300Sgjelinek if ((err = zonecfg_setfsent(t_handle)) != Z_OK) { 28201300Sgjelinek errno = err; 28211300Sgjelinek zperror2(target_zone, 28221300Sgjelinek gettext("could not enumerate file-systems")); 28231300Sgjelinek return; 28241300Sgjelinek } 28251300Sgjelinek 28261300Sgjelinek while (zonecfg_getfsent(t_handle, &t_fstab) == Z_OK) { 28271300Sgjelinek if ((err = zonecfg_setfsent(s_handle)) != Z_OK) { 28281300Sgjelinek errno = err; 28291300Sgjelinek zperror2(source_zone, 28301300Sgjelinek gettext("could not enumerate file-systems")); 28311300Sgjelinek (void) zonecfg_endfsent(t_handle); 28321300Sgjelinek return; 28331300Sgjelinek } 28341300Sgjelinek 28351300Sgjelinek while (zonecfg_getfsent(s_handle, &s_fstab) == Z_OK) { 28361300Sgjelinek if (strcmp(t_fstab.zone_fs_special, 28371300Sgjelinek s_fstab.zone_fs_special) == 0) { 28381300Sgjelinek print_fs_warnings(&s_fstab, &t_fstab); 28391300Sgjelinek break; 28401300Sgjelinek } 28411300Sgjelinek } 28421300Sgjelinek (void) zonecfg_endfsent(s_handle); 28431300Sgjelinek } 28441300Sgjelinek 28451300Sgjelinek (void) zonecfg_endfsent(t_handle); 28461300Sgjelinek } 28471300Sgjelinek 28481300Sgjelinek /* 28491300Sgjelinek * We don't catch the case where you used the same IP address but 28501300Sgjelinek * it is not an exact string match. For example, 192.9.0.128 vs. 192.09.0.128. 28511300Sgjelinek * However, we're not going to worry about that but we will check for 28521300Sgjelinek * a possible netmask on one of the addresses (e.g. 10.0.0.1 and 10.0.0.1/24) 28531300Sgjelinek * and handle that case as a match. 28541300Sgjelinek */ 28551300Sgjelinek static void 28561300Sgjelinek warn_ip_match(zone_dochandle_t s_handle, char *source_zone, 28571300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 28581300Sgjelinek { 28591300Sgjelinek int err; 28601300Sgjelinek struct zone_nwiftab s_nwiftab; 28611300Sgjelinek struct zone_nwiftab t_nwiftab; 28621300Sgjelinek 28631300Sgjelinek if ((err = zonecfg_setnwifent(t_handle)) != Z_OK) { 28641300Sgjelinek errno = err; 28651300Sgjelinek zperror2(target_zone, 28661300Sgjelinek gettext("could not enumerate network interfaces")); 28671300Sgjelinek return; 28681300Sgjelinek } 28691300Sgjelinek 28701300Sgjelinek while (zonecfg_getnwifent(t_handle, &t_nwiftab) == Z_OK) { 28711300Sgjelinek char *p; 28721300Sgjelinek 28731300Sgjelinek /* remove an (optional) netmask from the address */ 28741300Sgjelinek if ((p = strchr(t_nwiftab.zone_nwif_address, '/')) != NULL) 28751300Sgjelinek *p = '\0'; 28761300Sgjelinek 28771300Sgjelinek if ((err = zonecfg_setnwifent(s_handle)) != Z_OK) { 28781300Sgjelinek errno = err; 28791300Sgjelinek zperror2(source_zone, 28801300Sgjelinek gettext("could not enumerate network interfaces")); 28811300Sgjelinek (void) zonecfg_endnwifent(t_handle); 28821300Sgjelinek return; 28831300Sgjelinek } 28841300Sgjelinek 28851300Sgjelinek while (zonecfg_getnwifent(s_handle, &s_nwiftab) == Z_OK) { 28861300Sgjelinek /* remove an (optional) netmask from the address */ 28871300Sgjelinek if ((p = strchr(s_nwiftab.zone_nwif_address, '/')) 28881300Sgjelinek != NULL) 28891300Sgjelinek *p = '\0'; 28901300Sgjelinek 28911300Sgjelinek if (strcmp(t_nwiftab.zone_nwif_address, 28921300Sgjelinek s_nwiftab.zone_nwif_address) == 0) { 28931300Sgjelinek (void) fprintf(stderr, 28941300Sgjelinek gettext("WARNING: network address '%s' " 28951300Sgjelinek "is configured in both zones.\n"), 28961300Sgjelinek t_nwiftab.zone_nwif_address); 28971300Sgjelinek break; 28981300Sgjelinek } 28991300Sgjelinek } 29001300Sgjelinek (void) zonecfg_endnwifent(s_handle); 29011300Sgjelinek } 29021300Sgjelinek 29031300Sgjelinek (void) zonecfg_endnwifent(t_handle); 29041300Sgjelinek } 29051300Sgjelinek 29061300Sgjelinek static void 29071300Sgjelinek warn_dataset_match(zone_dochandle_t s_handle, char *source_zone, 29081300Sgjelinek zone_dochandle_t t_handle, char *target_zone) 29091300Sgjelinek { 29101300Sgjelinek int err; 29111300Sgjelinek struct zone_dstab s_dstab; 29121300Sgjelinek struct zone_dstab t_dstab; 29131300Sgjelinek 29141300Sgjelinek if ((err = zonecfg_setdsent(t_handle)) != Z_OK) { 29151300Sgjelinek errno = err; 29161300Sgjelinek zperror2(target_zone, gettext("could not enumerate datasets")); 29171300Sgjelinek return; 29181300Sgjelinek } 29191300Sgjelinek 29201300Sgjelinek while (zonecfg_getdsent(t_handle, &t_dstab) == Z_OK) { 29211300Sgjelinek if ((err = zonecfg_setdsent(s_handle)) != Z_OK) { 29221300Sgjelinek errno = err; 29231300Sgjelinek zperror2(source_zone, 29241300Sgjelinek gettext("could not enumerate datasets")); 29251300Sgjelinek (void) zonecfg_enddsent(t_handle); 29261300Sgjelinek return; 29271300Sgjelinek } 29281300Sgjelinek 29291300Sgjelinek while (zonecfg_getdsent(s_handle, &s_dstab) == Z_OK) { 29301300Sgjelinek if (strcmp(t_dstab.zone_dataset_name, 29311300Sgjelinek s_dstab.zone_dataset_name) == 0) { 29321300Sgjelinek (void) fprintf(stderr, 29331300Sgjelinek gettext("WARNING: dataset '%s' " 29341300Sgjelinek "is configured in both zones.\n"), 29351300Sgjelinek t_dstab.zone_dataset_name); 29361300Sgjelinek break; 29371300Sgjelinek } 29381300Sgjelinek } 29391300Sgjelinek (void) zonecfg_enddsent(s_handle); 29401300Sgjelinek } 29411300Sgjelinek 29421300Sgjelinek (void) zonecfg_enddsent(t_handle); 29431300Sgjelinek } 29441300Sgjelinek 29451300Sgjelinek static int 29461300Sgjelinek validate_clone(char *source_zone, char *target_zone) 29471300Sgjelinek { 29481300Sgjelinek int err = Z_OK; 29491300Sgjelinek zone_dochandle_t s_handle; 29501300Sgjelinek zone_dochandle_t t_handle; 29511300Sgjelinek 29521300Sgjelinek if ((t_handle = zonecfg_init_handle()) == NULL) { 29531300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 29541300Sgjelinek return (Z_ERR); 29551300Sgjelinek } 29561300Sgjelinek if ((err = zonecfg_get_handle(target_zone, t_handle)) != Z_OK) { 29571300Sgjelinek errno = err; 29581300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 29591300Sgjelinek zonecfg_fini_handle(t_handle); 29601300Sgjelinek return (Z_ERR); 29611300Sgjelinek } 29621300Sgjelinek 29631300Sgjelinek if ((s_handle = zonecfg_init_handle()) == NULL) { 29641300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 29651300Sgjelinek zonecfg_fini_handle(t_handle); 29661300Sgjelinek return (Z_ERR); 29671300Sgjelinek } 29681300Sgjelinek if ((err = zonecfg_get_handle(source_zone, s_handle)) != Z_OK) { 29691300Sgjelinek errno = err; 29701300Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 29711300Sgjelinek goto done; 29721300Sgjelinek } 29731300Sgjelinek 29741300Sgjelinek /* verify new zone has same inherit-pkg-dirs */ 29751300Sgjelinek err = valid_ipd_clone(s_handle, source_zone, t_handle, target_zone); 29761300Sgjelinek 29771300Sgjelinek /* warn about imported fs's which are the same */ 29781300Sgjelinek warn_fs_match(s_handle, source_zone, t_handle, target_zone); 29791300Sgjelinek 29801300Sgjelinek /* warn about imported IP addresses which are the same */ 29811300Sgjelinek warn_ip_match(s_handle, source_zone, t_handle, target_zone); 29821300Sgjelinek 29831300Sgjelinek /* warn about imported devices which are the same */ 29841300Sgjelinek warn_dev_match(s_handle, source_zone, t_handle, target_zone); 29851300Sgjelinek 29861300Sgjelinek /* warn about imported datasets which are the same */ 29871300Sgjelinek warn_dataset_match(s_handle, source_zone, t_handle, target_zone); 29881300Sgjelinek 29891300Sgjelinek done: 29901300Sgjelinek zonecfg_fini_handle(t_handle); 29911300Sgjelinek zonecfg_fini_handle(s_handle); 29921300Sgjelinek 29931300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 29941300Sgjelinek } 29951300Sgjelinek 29961300Sgjelinek static int 29971300Sgjelinek copy_zone(char *src, char *dst) 29981300Sgjelinek { 29991300Sgjelinek boolean_t out_null = B_FALSE; 30001300Sgjelinek int status; 30011300Sgjelinek int err; 30021300Sgjelinek char *outfile; 30031300Sgjelinek char cmdbuf[MAXPATHLEN * 2 + 128]; 30041300Sgjelinek 30051300Sgjelinek if ((outfile = tempnam("/var/log", "zone")) == NULL) { 30061300Sgjelinek outfile = "/dev/null"; 30071300Sgjelinek out_null = B_TRUE; 30081300Sgjelinek } 30091300Sgjelinek 30101300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 30111300Sgjelinek "cd %s && /usr/bin/find . -depth -print | " 30121607Sgjelinek "/usr/bin/egrep -v '^\\./\\.zfs$|^\\./\\.zfs/' | " 30131300Sgjelinek "/usr/bin/cpio -pdmuP@ %s > %s 2>&1", 30141300Sgjelinek src, dst, outfile); 30151300Sgjelinek 30161300Sgjelinek status = do_subproc(cmdbuf); 30171300Sgjelinek 30181300Sgjelinek if ((err = subproc_status("copy", status)) != Z_OK) { 30191300Sgjelinek if (!out_null) 30201300Sgjelinek (void) fprintf(stderr, gettext("\nThe copy failed.\n" 30211300Sgjelinek "More information can be found in %s\n"), outfile); 30221300Sgjelinek return (err); 30231300Sgjelinek } 30241300Sgjelinek 30251300Sgjelinek if (!out_null) 30261300Sgjelinek (void) unlink(outfile); 30271300Sgjelinek 30281300Sgjelinek return (Z_OK); 30291300Sgjelinek } 30301300Sgjelinek 30311300Sgjelinek /* 30321568Sgjelinek * Run sys-unconfig on a zone. This will leave the zone in the installed 30331568Sgjelinek * state as long as there were no errors during the sys-unconfig. 30341300Sgjelinek */ 30351300Sgjelinek static int 30361568Sgjelinek unconfigure_zone(char *zonepath) 30371300Sgjelinek { 30381568Sgjelinek int err; 30391568Sgjelinek int status; 30401568Sgjelinek struct stat unconfig_buf; 30411568Sgjelinek zone_cmd_arg_t zarg; 30421568Sgjelinek char cmdbuf[MAXPATHLEN + 51]; 30431568Sgjelinek 30441568Sgjelinek /* The zone has to be installed in order to mount the scratch zone. */ 30451568Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 30461568Sgjelinek errno = err; 30471568Sgjelinek zperror2(target_zone, gettext("could not set state")); 30481568Sgjelinek return (Z_ERR); 30491568Sgjelinek } 30501568Sgjelinek 30511568Sgjelinek /* 3052*1676Sjpk * Trusted Extensions requires that cloned zones use the 3053*1676Sjpk * same sysid configuration, so it is not appropriate to 3054*1676Sjpk * unconfigure the zone. 3055*1676Sjpk */ 3056*1676Sjpk if (is_system_labeled()) 3057*1676Sjpk return (Z_OK); 3058*1676Sjpk 3059*1676Sjpk /* 30601568Sgjelinek * Check if the zone is already sys-unconfiged. This saves us 30611568Sgjelinek * the work of bringing up the scratch zone so we can unconfigure it. 30621568Sgjelinek */ 30631568Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/root/etc/.UNCONFIGURED", 30641568Sgjelinek zonepath); 30651568Sgjelinek if (stat(cmdbuf, &unconfig_buf) == 0) 30661568Sgjelinek return (Z_OK); 30671568Sgjelinek 30681568Sgjelinek zarg.cmd = Z_MOUNT; 30691568Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 30701568Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 30711568Sgjelinek (void) zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 30721568Sgjelinek return (Z_ERR); 30731568Sgjelinek } 30741300Sgjelinek 30751300Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 30761568Sgjelinek "/usr/sbin/zlogin -S %s /usr/sbin/sys-unconfig -R /a", target_zone); 30771568Sgjelinek 30781568Sgjelinek status = do_subproc(cmdbuf); 30791568Sgjelinek if ((err = subproc_status("sys-unconfig", status)) != Z_OK) { 30801568Sgjelinek errno = err; 30811568Sgjelinek zperror2(target_zone, gettext("sys-unconfig failed\n")); 30821568Sgjelinek (void) zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 30831300Sgjelinek } 30841300Sgjelinek 30851568Sgjelinek zarg.cmd = Z_UNMOUNT; 30861568Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 30871568Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 30881568Sgjelinek (void) fprintf(stderr, gettext("could not unmount zone\n")); 30891568Sgjelinek return (Z_ERR); 30901568Sgjelinek } 30911568Sgjelinek 30921568Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 30931300Sgjelinek } 30941300Sgjelinek 30951300Sgjelinek /* ARGSUSED */ 30961300Sgjelinek int 30971300Sgjelinek zfm_print(const char *p, void *r) { 30981300Sgjelinek zerror(" %s\n", p); 30991300Sgjelinek return (0); 31001300Sgjelinek } 31011300Sgjelinek 31021300Sgjelinek static int 31031300Sgjelinek clone_func(int argc, char *argv[]) 31041300Sgjelinek { 31051300Sgjelinek char *source_zone = NULL; 31061300Sgjelinek int lockfd; 31071300Sgjelinek int err, arg; 31081300Sgjelinek char zonepath[MAXPATHLEN]; 31091300Sgjelinek char source_zonepath[MAXPATHLEN]; 31101300Sgjelinek zone_state_t state; 31111300Sgjelinek zone_entry_t *zent; 31121300Sgjelinek char *method = "copy"; 31131300Sgjelinek 31141300Sgjelinek if (zonecfg_in_alt_root()) { 31151300Sgjelinek zerror(gettext("cannot clone zone in alternate root")); 31161300Sgjelinek return (Z_ERR); 31171300Sgjelinek } 31181300Sgjelinek 31191300Sgjelinek optind = 0; 31201300Sgjelinek if ((arg = getopt(argc, argv, "?m:")) != EOF) { 31211300Sgjelinek switch (arg) { 31221300Sgjelinek case '?': 31231300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 31241300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 31251300Sgjelinek case 'm': 31261300Sgjelinek method = optarg; 31271300Sgjelinek break; 31281300Sgjelinek default: 31291300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 31301300Sgjelinek return (Z_USAGE); 31311300Sgjelinek } 31321300Sgjelinek } 31331300Sgjelinek if (argc != (optind + 1) || strcmp(method, "copy") != 0) { 31341300Sgjelinek sub_usage(SHELP_CLONE, CMD_CLONE); 31351300Sgjelinek return (Z_USAGE); 31361300Sgjelinek } 31371300Sgjelinek source_zone = argv[optind]; 31381300Sgjelinek if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE) != Z_OK) 31391300Sgjelinek return (Z_ERR); 31401300Sgjelinek if (verify_details(CMD_CLONE) != Z_OK) 31411300Sgjelinek return (Z_ERR); 31421300Sgjelinek 31431300Sgjelinek /* 31441300Sgjelinek * We also need to do some extra validation on the source zone. 31451300Sgjelinek */ 31461300Sgjelinek 31471300Sgjelinek if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) { 31481300Sgjelinek zerror(gettext("%s operation is invalid for the global zone."), 31491300Sgjelinek cmd_to_str(CMD_CLONE)); 31501300Sgjelinek return (Z_ERR); 31511300Sgjelinek } 31521300Sgjelinek 31531300Sgjelinek if (strncmp(source_zone, "SUNW", 4) == 0) { 31541300Sgjelinek zerror(gettext("%s operation is invalid for zones starting " 31551300Sgjelinek "with SUNW."), cmd_to_str(CMD_CLONE)); 31561300Sgjelinek return (Z_ERR); 31571300Sgjelinek } 31581300Sgjelinek 31591300Sgjelinek zent = lookup_running_zone(source_zone); 31601300Sgjelinek if (zent != NULL) { 31611300Sgjelinek /* check whether the zone is ready or running */ 31621300Sgjelinek if ((err = zone_get_state(zent->zname, &zent->zstate_num)) 31631300Sgjelinek != Z_OK) { 31641300Sgjelinek errno = err; 31651300Sgjelinek zperror2(zent->zname, gettext("could not get state")); 31661300Sgjelinek /* can't tell, so hedge */ 31671300Sgjelinek zent->zstate_str = "ready/running"; 31681300Sgjelinek } else { 31691300Sgjelinek zent->zstate_str = zone_state_str(zent->zstate_num); 31701300Sgjelinek } 31711300Sgjelinek zerror(gettext("%s operation is invalid for %s zones."), 31721300Sgjelinek cmd_to_str(CMD_CLONE), zent->zstate_str); 31731300Sgjelinek return (Z_ERR); 31741300Sgjelinek } 31751300Sgjelinek 31761300Sgjelinek if ((err = zone_get_state(source_zone, &state)) != Z_OK) { 31771300Sgjelinek errno = err; 31781300Sgjelinek zperror2(source_zone, gettext("could not get state")); 31791300Sgjelinek return (Z_ERR); 31801300Sgjelinek } 31811300Sgjelinek if (state != ZONE_STATE_INSTALLED) { 31821300Sgjelinek (void) fprintf(stderr, 31831300Sgjelinek gettext("%s: zone %s is %s; %s is required.\n"), 31841300Sgjelinek execname, source_zone, zone_state_str(state), 31851300Sgjelinek zone_state_str(ZONE_STATE_INSTALLED)); 31861300Sgjelinek return (Z_ERR); 31871300Sgjelinek } 31881300Sgjelinek 31891300Sgjelinek /* 31901300Sgjelinek * The source zone checks out ok, continue with the clone. 31911300Sgjelinek */ 31921300Sgjelinek 31931300Sgjelinek if (validate_clone(source_zone, target_zone) != Z_OK) 31941300Sgjelinek return (Z_ERR); 31951300Sgjelinek 31961300Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 31971300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 31981300Sgjelinek "zoneadm"); 31991300Sgjelinek return (Z_ERR); 32001300Sgjelinek } 32011300Sgjelinek 32021300Sgjelinek if ((err = zone_get_zonepath(source_zone, source_zonepath, 32031300Sgjelinek sizeof (source_zonepath))) != Z_OK) { 32041300Sgjelinek errno = err; 32051300Sgjelinek zperror2(source_zone, gettext("could not get zone path")); 32061300Sgjelinek goto done; 32071300Sgjelinek } 32081300Sgjelinek 32091300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 32101300Sgjelinek != Z_OK) { 32111300Sgjelinek errno = err; 32121300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 32131300Sgjelinek goto done; 32141300Sgjelinek } 32151300Sgjelinek 32161300Sgjelinek /* Don't clone the zone if anything is still mounted there */ 32171300Sgjelinek if (zonecfg_find_mounts(source_zonepath, NULL, NULL)) { 32181300Sgjelinek zerror(gettext("These file-systems are mounted on " 32191300Sgjelinek "subdirectories of %s.\n"), source_zonepath); 32201300Sgjelinek (void) zonecfg_find_mounts(source_zonepath, zfm_print, NULL); 32211300Sgjelinek err = Z_ERR; 32221300Sgjelinek goto done; 32231300Sgjelinek } 32241300Sgjelinek 32251300Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE)) 32261300Sgjelinek != Z_OK) { 32271300Sgjelinek errno = err; 32281300Sgjelinek zperror2(target_zone, gettext("could not set state")); 32291300Sgjelinek goto done; 32301300Sgjelinek } 32311300Sgjelinek 32321300Sgjelinek (void) printf(gettext("Cloning zonepath %s..."), source_zonepath); 32331300Sgjelinek (void) fflush(stdout); 32341300Sgjelinek 32351568Sgjelinek err = copy_zone(source_zonepath, zonepath); 32361568Sgjelinek (void) printf("\n"); 32371568Sgjelinek if (err != Z_OK) 32381300Sgjelinek goto done; 32391568Sgjelinek 32401568Sgjelinek err = unconfigure_zone(zonepath); 32411300Sgjelinek 32421300Sgjelinek done: 32431300Sgjelinek release_lock_file(lockfd); 32441300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 32451300Sgjelinek } 32461300Sgjelinek 32471300Sgjelinek #define RMCOMMAND "/usr/bin/rm -rf" 32481300Sgjelinek 32491607Sgjelinek /* 32501607Sgjelinek * Used when moving a zonepath (via copying) to clean up the old path or 32511607Sgjelinek * the new path if there was an error. 32521607Sgjelinek * 32531607Sgjelinek * This function handles the case of a zonepath being a zfs filesystem. 32541607Sgjelinek * If it is a zfs filesystem, we cannot just remove the whole zonepath 32551607Sgjelinek * since we can't remove the filesystem itself. Instead, we have to remove 32561607Sgjelinek * the contents of the filesystem, but not the .zfs directory. 32571607Sgjelinek */ 32581607Sgjelinek static int 32591607Sgjelinek remove_zonepath(char *zonepath) 32601607Sgjelinek { 32611607Sgjelinek int status; 32621607Sgjelinek boolean_t is_zfs = B_FALSE; 32631607Sgjelinek struct stat buf; 32641607Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 128]; 32651607Sgjelinek 32661607Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/.zfs", zonepath); 32671607Sgjelinek 32681607Sgjelinek if (stat(cmdbuf, &buf) == 0 && S_ISDIR(buf.st_mode)) 32691607Sgjelinek is_zfs = B_TRUE; 32701607Sgjelinek 32711607Sgjelinek if (is_zfs) { 32721607Sgjelinek /* 32731607Sgjelinek * This doesn't handle the (unlikely) case that there are 32741607Sgjelinek * directories or files in the top-level zonepath with white 32751607Sgjelinek * space in the names. 32761607Sgjelinek */ 32771607Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 32781607Sgjelinek "cd %s && /usr/bin/ls -A | /usr/bin/egrep -v '^\\.zfs$' | " 32791607Sgjelinek "/usr/bin/xargs " RMCOMMAND, zonepath); 32801607Sgjelinek } else { 32811607Sgjelinek /* 32821607Sgjelinek * "exec" the command so that the returned status is 32831607Sgjelinek * that of rm and not the shell. 32841607Sgjelinek */ 32851607Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), 32861607Sgjelinek "exec " RMCOMMAND " %s", zonepath); 32871607Sgjelinek } 32881607Sgjelinek 32891607Sgjelinek status = do_subproc(cmdbuf); 32901607Sgjelinek 32911607Sgjelinek return (subproc_status("rm", status)); 32921607Sgjelinek 32931607Sgjelinek } 32941607Sgjelinek 32951300Sgjelinek static int 32961300Sgjelinek move_func(int argc, char *argv[]) 32971300Sgjelinek { 32981300Sgjelinek char *new_zonepath = NULL; 32991300Sgjelinek int lockfd; 33001300Sgjelinek int err, arg; 33011300Sgjelinek char zonepath[MAXPATHLEN]; 33021300Sgjelinek zone_dochandle_t handle; 33031300Sgjelinek boolean_t fast; 33041300Sgjelinek boolean_t revert; 33051300Sgjelinek struct stat zonepath_buf; 33061300Sgjelinek struct stat new_zonepath_buf; 33071300Sgjelinek 33081300Sgjelinek if (zonecfg_in_alt_root()) { 33091300Sgjelinek zerror(gettext("cannot move zone in alternate root")); 33101300Sgjelinek return (Z_ERR); 33111300Sgjelinek } 33121300Sgjelinek 33131300Sgjelinek optind = 0; 33141300Sgjelinek if ((arg = getopt(argc, argv, "?")) != EOF) { 33151300Sgjelinek switch (arg) { 33161300Sgjelinek case '?': 33171300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 33181300Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 33191300Sgjelinek default: 33201300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 33211300Sgjelinek return (Z_USAGE); 33221300Sgjelinek } 33231300Sgjelinek } 33241300Sgjelinek if (argc != (optind + 1)) { 33251300Sgjelinek sub_usage(SHELP_MOVE, CMD_MOVE); 33261300Sgjelinek return (Z_USAGE); 33271300Sgjelinek } 33281300Sgjelinek new_zonepath = argv[optind]; 33291300Sgjelinek if (sanity_check(target_zone, CMD_MOVE, B_FALSE, B_TRUE) != Z_OK) 33301300Sgjelinek return (Z_ERR); 33311300Sgjelinek if (verify_details(CMD_MOVE) != Z_OK) 33321300Sgjelinek return (Z_ERR); 33331300Sgjelinek 33341300Sgjelinek /* 33351300Sgjelinek * Check out the new zonepath. This has the side effect of creating 33361300Sgjelinek * a directory for the new zonepath. We depend on this later when we 33371300Sgjelinek * stat to see if we are doing a cross file-system move or not. 33381300Sgjelinek */ 33391300Sgjelinek if (validate_zonepath(new_zonepath, CMD_MOVE) != Z_OK) 33401300Sgjelinek return (Z_ERR); 33411300Sgjelinek 33421300Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 33431300Sgjelinek != Z_OK) { 33441300Sgjelinek errno = err; 33451300Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 33461300Sgjelinek return (Z_ERR); 33471300Sgjelinek } 33481300Sgjelinek 33491300Sgjelinek if (stat(zonepath, &zonepath_buf) == -1) { 33501300Sgjelinek zperror(gettext("could not stat zone path"), B_FALSE); 33511300Sgjelinek return (Z_ERR); 33521300Sgjelinek } 33531300Sgjelinek 33541300Sgjelinek if (stat(new_zonepath, &new_zonepath_buf) == -1) { 33551300Sgjelinek zperror(gettext("could not stat new zone path"), B_FALSE); 33561300Sgjelinek return (Z_ERR); 33571300Sgjelinek } 33581300Sgjelinek 33591300Sgjelinek /* Don't move the zone if anything is still mounted there */ 33601300Sgjelinek if (zonecfg_find_mounts(zonepath, NULL, NULL)) { 33611300Sgjelinek zerror(gettext("These file-systems are mounted on " 33621300Sgjelinek "subdirectories of %s.\n"), zonepath); 33631300Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 33641300Sgjelinek return (Z_ERR); 33651300Sgjelinek } 33661300Sgjelinek 33671300Sgjelinek /* 33681300Sgjelinek * Check if we are moving in the same filesystem and can do a fast 33691300Sgjelinek * move or if we are crossing filesystems and have to copy the data. 33701300Sgjelinek */ 33711300Sgjelinek fast = (zonepath_buf.st_dev == new_zonepath_buf.st_dev); 33721300Sgjelinek 33731300Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 33741300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 33751300Sgjelinek return (Z_ERR); 33761300Sgjelinek } 33771300Sgjelinek 33781300Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 33791300Sgjelinek errno = err; 33801300Sgjelinek zperror(cmd_to_str(CMD_MOVE), B_TRUE); 33811300Sgjelinek zonecfg_fini_handle(handle); 33821300Sgjelinek return (Z_ERR); 33831300Sgjelinek } 33841300Sgjelinek 33851300Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 33861300Sgjelinek zerror(gettext("another %s may have an operation in progress."), 33871300Sgjelinek "zoneadm"); 33881300Sgjelinek zonecfg_fini_handle(handle); 33891300Sgjelinek return (Z_ERR); 33901300Sgjelinek } 33911300Sgjelinek 33921300Sgjelinek /* 33931300Sgjelinek * We're making some file-system changes now so we have to clean up 33941300Sgjelinek * the file-system before we are done. This will either clean up the 33951300Sgjelinek * new zonepath if the zonecfg update failed or it will clean up the 33961300Sgjelinek * old zonepath if everything is ok. 33971300Sgjelinek */ 33981300Sgjelinek revert = B_TRUE; 33991300Sgjelinek 34001300Sgjelinek if (fast) { 34011300Sgjelinek /* same filesystem, use rename for a quick move */ 34021300Sgjelinek 34031300Sgjelinek /* 34041300Sgjelinek * Remove the new_zonepath directory that got created above 34051300Sgjelinek * during the validation. It gets in the way of the rename. 34061300Sgjelinek */ 34071300Sgjelinek if (rmdir(new_zonepath) != 0) { 34081300Sgjelinek zperror(gettext("could not rmdir new zone path"), 34091300Sgjelinek B_FALSE); 34101300Sgjelinek zonecfg_fini_handle(handle); 34111300Sgjelinek release_lock_file(lockfd); 34121300Sgjelinek return (Z_ERR); 34131300Sgjelinek } 34141300Sgjelinek 34151300Sgjelinek if (rename(zonepath, new_zonepath) != 0) { 34161300Sgjelinek /* 34171300Sgjelinek * If this fails we don't need to do all of the 34181300Sgjelinek * cleanup that happens for the rest of the code 34191300Sgjelinek * so just return from this error. 34201300Sgjelinek */ 34211300Sgjelinek zperror(gettext("could not move zone"), B_FALSE); 34221300Sgjelinek zonecfg_fini_handle(handle); 34231300Sgjelinek release_lock_file(lockfd); 34241300Sgjelinek return (Z_ERR); 34251300Sgjelinek } 34261300Sgjelinek 34271300Sgjelinek } else { 34281300Sgjelinek (void) printf(gettext( 34291300Sgjelinek "Moving across file-systems; copying zonepath %s..."), 34301300Sgjelinek zonepath); 34311300Sgjelinek (void) fflush(stdout); 34321300Sgjelinek 34331300Sgjelinek err = copy_zone(zonepath, new_zonepath); 34341300Sgjelinek 34351300Sgjelinek (void) printf("\n"); 34361300Sgjelinek if (err != Z_OK) 34371300Sgjelinek goto done; 34381300Sgjelinek } 34391300Sgjelinek 34401300Sgjelinek if ((err = zonecfg_set_zonepath(handle, new_zonepath)) != Z_OK) { 34411300Sgjelinek errno = err; 34421300Sgjelinek zperror(gettext("could not set new zonepath"), B_TRUE); 34431300Sgjelinek goto done; 34441300Sgjelinek } 34451300Sgjelinek 34461300Sgjelinek if ((err = zonecfg_save(handle)) != Z_OK) { 34471300Sgjelinek errno = err; 34481300Sgjelinek zperror(gettext("zonecfg save failed"), B_TRUE); 34491300Sgjelinek goto done; 34501300Sgjelinek } 34511300Sgjelinek 34521300Sgjelinek revert = B_FALSE; 34531300Sgjelinek 34541300Sgjelinek done: 34551300Sgjelinek zonecfg_fini_handle(handle); 34561300Sgjelinek release_lock_file(lockfd); 34571300Sgjelinek 34581300Sgjelinek /* 34591300Sgjelinek * Clean up the file-system based on how things went. We either 34601300Sgjelinek * clean up the new zonepath if the operation failed for some reason 34611300Sgjelinek * or we clean up the old zonepath if everything is ok. 34621300Sgjelinek */ 34631300Sgjelinek if (revert) { 34641300Sgjelinek /* The zonecfg update failed, cleanup the new zonepath. */ 34651300Sgjelinek if (fast) { 34661300Sgjelinek if (rename(new_zonepath, zonepath) != 0) { 34671300Sgjelinek zperror(gettext("could not restore zonepath"), 34681300Sgjelinek B_FALSE); 34691300Sgjelinek /* 34701300Sgjelinek * err is already != Z_OK since we're reverting 34711300Sgjelinek */ 34721300Sgjelinek } 34731300Sgjelinek } else { 34741300Sgjelinek (void) printf(gettext("Cleaning up zonepath %s..."), 34751300Sgjelinek new_zonepath); 34761300Sgjelinek (void) fflush(stdout); 34771607Sgjelinek err = remove_zonepath(new_zonepath); 34781300Sgjelinek (void) printf("\n"); 34791300Sgjelinek 34801607Sgjelinek if (err != Z_OK) { 34811300Sgjelinek errno = err; 34821300Sgjelinek zperror(gettext("could not remove new " 34831300Sgjelinek "zonepath"), B_TRUE); 34841300Sgjelinek } else { 34851300Sgjelinek /* 34861300Sgjelinek * Because we're reverting we know the mainline 34871300Sgjelinek * code failed but we just reused the err 34881300Sgjelinek * variable so we reset it back to Z_ERR. 34891300Sgjelinek */ 34901300Sgjelinek err = Z_ERR; 34911300Sgjelinek } 34921300Sgjelinek } 34931300Sgjelinek 34941300Sgjelinek } else { 34951300Sgjelinek /* The move was successful, cleanup the old zonepath. */ 34961300Sgjelinek if (!fast) { 34971300Sgjelinek (void) printf( 34981300Sgjelinek gettext("Cleaning up zonepath %s..."), zonepath); 34991300Sgjelinek (void) fflush(stdout); 35001607Sgjelinek err = remove_zonepath(zonepath); 35011300Sgjelinek (void) printf("\n"); 35021300Sgjelinek 35031607Sgjelinek if (err != Z_OK) { 35041300Sgjelinek errno = err; 35051300Sgjelinek zperror(gettext("could not remove zonepath"), 35061300Sgjelinek B_TRUE); 35071300Sgjelinek } 35081300Sgjelinek } 35091300Sgjelinek } 35101300Sgjelinek 35111300Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 35121300Sgjelinek } 35131300Sgjelinek 35141507Sgjelinek static int 35151507Sgjelinek detach_func(int argc, char *argv[]) 35161507Sgjelinek { 35171507Sgjelinek int lockfd; 35181507Sgjelinek int err, arg; 35191507Sgjelinek char zonepath[MAXPATHLEN]; 35201507Sgjelinek zone_dochandle_t handle; 35211507Sgjelinek 35221507Sgjelinek if (zonecfg_in_alt_root()) { 35231507Sgjelinek zerror(gettext("cannot detach zone in alternate root")); 35241507Sgjelinek return (Z_ERR); 35251507Sgjelinek } 35261507Sgjelinek 35271507Sgjelinek optind = 0; 35281507Sgjelinek if ((arg = getopt(argc, argv, "?")) != EOF) { 35291507Sgjelinek switch (arg) { 35301507Sgjelinek case '?': 35311507Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 35321507Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 35331507Sgjelinek default: 35341507Sgjelinek sub_usage(SHELP_DETACH, CMD_DETACH); 35351507Sgjelinek return (Z_USAGE); 35361507Sgjelinek } 35371507Sgjelinek } 35381507Sgjelinek if (sanity_check(target_zone, CMD_DETACH, B_FALSE, B_TRUE) != Z_OK) 35391507Sgjelinek return (Z_ERR); 35401507Sgjelinek if (verify_details(CMD_DETACH) != Z_OK) 35411507Sgjelinek return (Z_ERR); 35421507Sgjelinek 35431507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 35441507Sgjelinek != Z_OK) { 35451507Sgjelinek errno = err; 35461507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 35471507Sgjelinek return (Z_ERR); 35481507Sgjelinek } 35491507Sgjelinek 35501507Sgjelinek /* Don't detach the zone if anything is still mounted there */ 35511507Sgjelinek if (zonecfg_find_mounts(zonepath, NULL, NULL)) { 35521507Sgjelinek zerror(gettext("These file-systems are mounted on " 35531507Sgjelinek "subdirectories of %s.\n"), zonepath); 35541507Sgjelinek (void) zonecfg_find_mounts(zonepath, zfm_print, NULL); 35551507Sgjelinek return (Z_ERR); 35561507Sgjelinek } 35571507Sgjelinek 35581507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 35591507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 35601507Sgjelinek return (Z_ERR); 35611507Sgjelinek } 35621507Sgjelinek 35631507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 35641507Sgjelinek errno = err; 35651507Sgjelinek zperror(cmd_to_str(CMD_DETACH), B_TRUE); 35661507Sgjelinek zonecfg_fini_handle(handle); 35671507Sgjelinek return (Z_ERR); 35681507Sgjelinek } 35691507Sgjelinek 35701507Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 35711507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 35721507Sgjelinek "zoneadm"); 35731507Sgjelinek zonecfg_fini_handle(handle); 35741507Sgjelinek return (Z_ERR); 35751507Sgjelinek } 35761507Sgjelinek 35771507Sgjelinek if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) { 35781507Sgjelinek errno = err; 35791507Sgjelinek zperror(gettext("getting the detach information failed"), 35801507Sgjelinek B_TRUE); 35811507Sgjelinek goto done; 35821507Sgjelinek } 35831507Sgjelinek 35841507Sgjelinek if ((err = zonecfg_detach_save(handle)) != Z_OK) { 35851507Sgjelinek errno = err; 35861507Sgjelinek zperror(gettext("saving the detach manifest failed"), B_TRUE); 35871507Sgjelinek goto done; 35881507Sgjelinek } 35891507Sgjelinek 35901507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED)) 35911507Sgjelinek != Z_OK) { 35921507Sgjelinek errno = err; 35931507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 35941507Sgjelinek } 35951507Sgjelinek 35961507Sgjelinek done: 35971507Sgjelinek zonecfg_fini_handle(handle); 35981507Sgjelinek release_lock_file(lockfd); 35991507Sgjelinek 36001507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 36011507Sgjelinek } 36021507Sgjelinek 36031507Sgjelinek /* 36041507Sgjelinek * Find the specified package in the sw inventory on the handle and check 36051507Sgjelinek * if the version matches what is passed in. 36061507Sgjelinek * Return 0 if the packages match 36071507Sgjelinek * 1 if the package is found but we have a version mismatch 36081507Sgjelinek * -1 if the package is not found 36091507Sgjelinek */ 36101507Sgjelinek static int 36111507Sgjelinek pkg_cmp(zone_dochandle_t handle, char *pkg_name, char *pkg_vers, 36121507Sgjelinek char *return_vers, int vers_size) 36131507Sgjelinek { 36141507Sgjelinek int res = -1; 36151507Sgjelinek struct zone_pkgtab pkgtab; 36161507Sgjelinek 36171507Sgjelinek if (zonecfg_setpkgent(handle) != Z_OK) { 36181507Sgjelinek (void) fprintf(stderr, 36191507Sgjelinek gettext("unable to enumerate packages\n")); 36201507Sgjelinek return (Z_ERR); 36211507Sgjelinek } 36221507Sgjelinek 36231507Sgjelinek while (zonecfg_getpkgent(handle, &pkgtab) == Z_OK) { 36241507Sgjelinek if (strcmp(pkg_name, pkgtab.zone_pkg_name) != 0) 36251507Sgjelinek continue; 36261507Sgjelinek 36271507Sgjelinek if (strcmp(pkg_vers, pkgtab.zone_pkg_version) == 0) { 36281507Sgjelinek res = 0; 36291507Sgjelinek break; 36301507Sgjelinek } 36311507Sgjelinek 36321507Sgjelinek (void) strlcpy(return_vers, pkgtab.zone_pkg_version, vers_size); 36331507Sgjelinek res = 1; 36341507Sgjelinek break; 36351507Sgjelinek } 36361507Sgjelinek 36371507Sgjelinek (void) zonecfg_endpkgent(handle); 36381507Sgjelinek return (res); 36391507Sgjelinek } 36401507Sgjelinek 36411507Sgjelinek /* 36421507Sgjelinek * Used in software comparisons to check the packages between the two zone 36431507Sgjelinek * handles. The packages have to match or we print a message telling the 36441507Sgjelinek * user what is out of sync. The src_cmp flag tells us if the first handle 36451507Sgjelinek * is the source machine global zone or not. This is used to enable the 36461507Sgjelinek * right messages to be printed and also to enable extra version checking 36471507Sgjelinek * that is not needed for the opposite comparison. 36481507Sgjelinek */ 36491507Sgjelinek static int 36501507Sgjelinek pkg_check(char *header, zone_dochandle_t handle1, zone_dochandle_t handle2, 36511507Sgjelinek boolean_t src_cmp) 36521507Sgjelinek { 36531507Sgjelinek int err; 36541507Sgjelinek int res = Z_OK; 36551507Sgjelinek boolean_t do_header = B_TRUE; 36561507Sgjelinek char other_vers[ZONE_PKG_VERSMAX]; 36571507Sgjelinek struct zone_pkgtab pkgtab; 36581507Sgjelinek 36591507Sgjelinek if (zonecfg_setpkgent(handle1) != Z_OK) { 36601507Sgjelinek (void) fprintf(stderr, 36611507Sgjelinek gettext("unable to enumerate packages\n")); 36621507Sgjelinek return (Z_ERR); 36631507Sgjelinek } 36641507Sgjelinek 36651507Sgjelinek while (zonecfg_getpkgent(handle1, &pkgtab) == Z_OK) { 36661507Sgjelinek if ((err = pkg_cmp(handle2, pkgtab.zone_pkg_name, 36671507Sgjelinek pkgtab.zone_pkg_version, other_vers, sizeof (other_vers))) 36681507Sgjelinek != 0) { 36691507Sgjelinek if (do_header && (err < 0 || src_cmp)) { 36701507Sgjelinek /* LINTED E_SEC_PRINTF_VAR_FMT */ 36711507Sgjelinek (void) fprintf(stderr, header); 36721507Sgjelinek do_header = B_FALSE; 36731507Sgjelinek } 36741507Sgjelinek if (err < 0) { 36751507Sgjelinek (void) fprintf(stderr, 36761507Sgjelinek (src_cmp == B_TRUE) ? 36771507Sgjelinek gettext("\t%s: not installed\n\t\t(%s)\n") : 36781507Sgjelinek gettext("\t%s (%s)\n"), 36791507Sgjelinek pkgtab.zone_pkg_name, 36801507Sgjelinek pkgtab.zone_pkg_version); 36811507Sgjelinek res = Z_ERR; 36821507Sgjelinek } else if (src_cmp) { 36831507Sgjelinek (void) fprintf(stderr, gettext( 36841507Sgjelinek "\t%s: version mismatch\n\t\t(%s)" 36851507Sgjelinek "\n\t\t(%s)\n"), 36861507Sgjelinek pkgtab.zone_pkg_name, 36871507Sgjelinek pkgtab.zone_pkg_version, other_vers); 36881507Sgjelinek res = Z_ERR; 36891507Sgjelinek } 36901507Sgjelinek } 36911507Sgjelinek } 36921507Sgjelinek 36931507Sgjelinek (void) zonecfg_endpkgent(handle1); 36941507Sgjelinek 36951507Sgjelinek return (res); 36961507Sgjelinek } 36971507Sgjelinek 36981507Sgjelinek /* 36991507Sgjelinek * Find the specified patch in the sw inventory on the handle and check 37001507Sgjelinek * if the version matches what is passed in. 37011507Sgjelinek * Return 0 if the patches match 37021507Sgjelinek * 1 if the patches is found but we have a version mismatch 37031507Sgjelinek * -1 if the patches is not found 37041507Sgjelinek */ 37051507Sgjelinek static int 37061507Sgjelinek patch_cmp(zone_dochandle_t handle, char *patch_id, char *patch_vers, 37071507Sgjelinek char *return_vers, int vers_size) 37081507Sgjelinek { 37091507Sgjelinek int res = -1; 37101507Sgjelinek struct zone_patchtab patchtab; 37111507Sgjelinek 37121507Sgjelinek if (zonecfg_setpatchent(handle) != Z_OK) { 37131507Sgjelinek (void) fprintf(stderr, 37141507Sgjelinek gettext("unable to enumerate patches\n")); 37151507Sgjelinek return (Z_ERR); 37161507Sgjelinek } 37171507Sgjelinek 37181507Sgjelinek while (zonecfg_getpatchent(handle, &patchtab) == Z_OK) { 37191507Sgjelinek char *p; 37201507Sgjelinek 37211507Sgjelinek if ((p = strchr(patchtab.zone_patch_id, '-')) != NULL) 37221507Sgjelinek *p++ = '\0'; 37231507Sgjelinek else 37241507Sgjelinek p = ""; 37251507Sgjelinek 37261507Sgjelinek if (strcmp(patch_id, patchtab.zone_patch_id) != 0) 37271507Sgjelinek continue; 37281507Sgjelinek 37291507Sgjelinek if (strcmp(patch_vers, p) == 0) { 37301507Sgjelinek res = 0; 37311507Sgjelinek break; 37321507Sgjelinek } 37331507Sgjelinek 37341507Sgjelinek (void) strlcpy(return_vers, p, vers_size); 37351507Sgjelinek /* 37361507Sgjelinek * Keep checking. This handles the case where multiple 37371507Sgjelinek * versions of the same patch is installed. 37381507Sgjelinek */ 37391507Sgjelinek res = 1; 37401507Sgjelinek } 37411507Sgjelinek 37421507Sgjelinek (void) zonecfg_endpatchent(handle); 37431507Sgjelinek return (res); 37441507Sgjelinek } 37451507Sgjelinek 37461507Sgjelinek /* 37471507Sgjelinek * Used in software comparisons to check the patches between the two zone 37481507Sgjelinek * handles. The patches have to match or we print a message telling the 37491507Sgjelinek * user what is out of sync. The src_cmp flag tells us if the first handle 37501507Sgjelinek * is the source machine global zone or not. This is used to enable the 37511507Sgjelinek * right messages to be printed and also to enable extra version checking 37521507Sgjelinek * that is not needed for the opposite comparison. 37531507Sgjelinek */ 37541507Sgjelinek static int 37551507Sgjelinek patch_check(char *header, zone_dochandle_t handle1, zone_dochandle_t handle2, 37561507Sgjelinek boolean_t src_cmp) 37571507Sgjelinek { 37581507Sgjelinek int err; 37591507Sgjelinek int res = Z_OK; 37601507Sgjelinek boolean_t do_header = B_TRUE; 37611507Sgjelinek char other_vers[MAXNAMELEN]; 37621507Sgjelinek struct zone_patchtab patchtab; 37631507Sgjelinek 37641507Sgjelinek if (zonecfg_setpatchent(handle1) != Z_OK) { 37651507Sgjelinek (void) fprintf(stderr, 37661507Sgjelinek gettext("unable to enumerate patches\n")); 37671507Sgjelinek return (Z_ERR); 37681507Sgjelinek } 37691507Sgjelinek 37701507Sgjelinek while (zonecfg_getpatchent(handle1, &patchtab) == Z_OK) { 37711507Sgjelinek char *patch_vers; 37721507Sgjelinek 37731507Sgjelinek if ((patch_vers = strchr(patchtab.zone_patch_id, '-')) != NULL) 37741507Sgjelinek *patch_vers++ = '\0'; 37751507Sgjelinek else 37761507Sgjelinek patch_vers = ""; 37771507Sgjelinek 37781507Sgjelinek if ((err = patch_cmp(handle2, patchtab.zone_patch_id, 37791507Sgjelinek patch_vers, other_vers, sizeof (other_vers))) != 0) { 37801507Sgjelinek if (do_header && (err < 0 || src_cmp)) { 37811507Sgjelinek /* LINTED E_SEC_PRINTF_VAR_FMT */ 37821507Sgjelinek (void) fprintf(stderr, header); 37831507Sgjelinek do_header = B_FALSE; 37841507Sgjelinek } 37851507Sgjelinek if (err < 0) { 37861507Sgjelinek (void) fprintf(stderr, 37871507Sgjelinek (src_cmp == B_TRUE) ? 37881507Sgjelinek gettext("\t%s: not installed\n") : 37891507Sgjelinek gettext("\t%s\n"), 37901507Sgjelinek patchtab.zone_patch_id); 37911507Sgjelinek res = Z_ERR; 37921507Sgjelinek } else if (src_cmp) { 37931507Sgjelinek (void) fprintf(stderr, 37941507Sgjelinek gettext("\t%s: version mismatch\n\t\t(%s) " 37951507Sgjelinek "(%s)\n"), patchtab.zone_patch_id, 37961507Sgjelinek patch_vers, other_vers); 37971507Sgjelinek res = Z_ERR; 37981507Sgjelinek } 37991507Sgjelinek } 38001507Sgjelinek } 38011507Sgjelinek 38021507Sgjelinek (void) zonecfg_endpatchent(handle1); 38031507Sgjelinek 38041507Sgjelinek return (res); 38051507Sgjelinek } 38061507Sgjelinek 38071507Sgjelinek /* 38081507Sgjelinek * Compare the software on the local global zone and source system global 38091507Sgjelinek * zone. Used when we are trying to attach a zone during migration. 38101507Sgjelinek * l_handle is for the local system and s_handle is for the source system. 38111507Sgjelinek * These have a snapshot of the appropriate packages and patches in the global 38121507Sgjelinek * zone for the two machines. 38131507Sgjelinek * The functions called here will print any messages that are needed to 38141507Sgjelinek * inform the user about package or patch problems. 38151507Sgjelinek */ 38161507Sgjelinek static int 38171507Sgjelinek sw_cmp(zone_dochandle_t l_handle, zone_dochandle_t s_handle) 38181507Sgjelinek { 38191507Sgjelinek char *hdr; 38201507Sgjelinek int res = Z_OK; 38211507Sgjelinek 38221507Sgjelinek /* 38231507Sgjelinek * Check the source host for pkgs (and versions) that are not on the 38241507Sgjelinek * local host. 38251507Sgjelinek */ 38261507Sgjelinek hdr = gettext("These packages installed on the source system are " 38271507Sgjelinek "inconsistent with this system:\n"); 38281507Sgjelinek if (pkg_check(hdr, s_handle, l_handle, B_TRUE) != Z_OK) 38291507Sgjelinek res = Z_ERR; 38301507Sgjelinek 38311507Sgjelinek /* 38321507Sgjelinek * Now check the local host for pkgs that were not on the source host. 38331507Sgjelinek * We already handled version mismatches in the loop above. 38341507Sgjelinek */ 38351507Sgjelinek hdr = gettext("These packages installed on this system were " 38361507Sgjelinek "not installed on the source system:\n"); 38371507Sgjelinek if (pkg_check(hdr, l_handle, s_handle, B_FALSE) != Z_OK) 38381507Sgjelinek res = Z_ERR; 38391507Sgjelinek 38401507Sgjelinek /* 38411507Sgjelinek * Check the source host for patches that are not on the local host. 38421507Sgjelinek */ 38431507Sgjelinek hdr = gettext("These patches installed on the source system are " 38441507Sgjelinek "inconsistent with this system:\n"); 38451507Sgjelinek if (patch_check(hdr, s_handle, l_handle, B_TRUE) != Z_OK) 38461507Sgjelinek res = Z_ERR; 38471507Sgjelinek 38481507Sgjelinek /* 38491507Sgjelinek * Check the local host for patches that were not on the source host. 38501507Sgjelinek * We already handled version mismatches in the loop above. 38511507Sgjelinek */ 38521507Sgjelinek hdr = gettext("These patches installed on this system were " 38531507Sgjelinek "not installed on the source system:\n"); 38541507Sgjelinek if (patch_check(hdr, l_handle, s_handle, B_FALSE) != Z_OK) 38551507Sgjelinek res = Z_ERR; 38561507Sgjelinek 38571507Sgjelinek return (res); 38581507Sgjelinek } 38591507Sgjelinek 38601507Sgjelinek /* 38611507Sgjelinek * During attach we go through and fix up the /dev entries for the zone 38621507Sgjelinek * we are attaching. In order to regenerate /dev with the correct devices, 38631507Sgjelinek * the old /dev will be removed, the zone readied (which generates a new 38641507Sgjelinek * /dev) then halted, then we use the info from the manifest to update 38651507Sgjelinek * the modes, owners, etc. on the new /dev. 38661507Sgjelinek */ 38671507Sgjelinek static int 38681507Sgjelinek dev_fix(zone_dochandle_t handle) 38691507Sgjelinek { 38701507Sgjelinek int res; 38711507Sgjelinek int err; 38721507Sgjelinek int status; 38731507Sgjelinek struct zone_devpermtab devtab; 38741507Sgjelinek zone_cmd_arg_t zarg; 38751507Sgjelinek char devpath[MAXPATHLEN]; 38761507Sgjelinek /* 6: "exec " and " " */ 38771507Sgjelinek char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 6]; 38781507Sgjelinek 38791507Sgjelinek if ((res = zonecfg_get_zonepath(handle, devpath, sizeof (devpath))) 38801507Sgjelinek != Z_OK) 38811507Sgjelinek return (res); 38821507Sgjelinek 38831507Sgjelinek if (strlcat(devpath, "/dev", sizeof (devpath)) >= sizeof (devpath)) 38841507Sgjelinek return (Z_TOO_BIG); 38851507Sgjelinek 38861507Sgjelinek /* 38871507Sgjelinek * "exec" the command so that the returned status is that of 38881507Sgjelinek * RMCOMMAND and not the shell. 38891507Sgjelinek */ 38901507Sgjelinek (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 38911507Sgjelinek devpath); 38921507Sgjelinek status = do_subproc(cmdbuf); 38931507Sgjelinek if ((err = subproc_status(RMCOMMAND, status)) != Z_OK) { 38941507Sgjelinek (void) fprintf(stderr, 38951507Sgjelinek gettext("could not remove existing /dev\n")); 38961507Sgjelinek return (Z_ERR); 38971507Sgjelinek } 38981507Sgjelinek 38991507Sgjelinek /* In order to ready the zone, it must be in the installed state */ 39001507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 39011507Sgjelinek errno = err; 39021507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 39031507Sgjelinek return (Z_ERR); 39041507Sgjelinek } 39051507Sgjelinek 39061507Sgjelinek /* We have to ready the zone to regen the dev tree */ 39071507Sgjelinek zarg.cmd = Z_READY; 39081507Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 39091507Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 39101507Sgjelinek return (Z_ERR); 39111507Sgjelinek } 39121507Sgjelinek 39131507Sgjelinek zarg.cmd = Z_HALT; 39141507Sgjelinek if (call_zoneadmd(target_zone, &zarg) != 0) { 39151507Sgjelinek zerror(gettext("call to %s failed"), "zoneadmd"); 39161507Sgjelinek return (Z_ERR); 39171507Sgjelinek } 39181507Sgjelinek 39191507Sgjelinek if (zonecfg_setdevperment(handle) != Z_OK) { 39201507Sgjelinek (void) fprintf(stderr, 39211507Sgjelinek gettext("unable to enumerate device entries\n")); 39221507Sgjelinek return (Z_ERR); 39231507Sgjelinek } 39241507Sgjelinek 39251507Sgjelinek while (zonecfg_getdevperment(handle, &devtab) == Z_OK) { 39261507Sgjelinek int err; 39271507Sgjelinek 39281507Sgjelinek if ((err = zonecfg_devperms_apply(handle, 39291507Sgjelinek devtab.zone_devperm_name, devtab.zone_devperm_uid, 39301507Sgjelinek devtab.zone_devperm_gid, devtab.zone_devperm_mode, 39311507Sgjelinek devtab.zone_devperm_acl)) != Z_OK && err != Z_INVAL) 39321507Sgjelinek (void) fprintf(stderr, gettext("error updating device " 39331507Sgjelinek "%s: %s\n"), devtab.zone_devperm_name, 39341507Sgjelinek zonecfg_strerror(err)); 39351507Sgjelinek 39361507Sgjelinek free(devtab.zone_devperm_acl); 39371507Sgjelinek } 39381507Sgjelinek 39391507Sgjelinek (void) zonecfg_enddevperment(handle); 39401507Sgjelinek 39411507Sgjelinek return (Z_OK); 39421507Sgjelinek } 39431507Sgjelinek 39441507Sgjelinek static int 39451507Sgjelinek attach_func(int argc, char *argv[]) 39461507Sgjelinek { 39471507Sgjelinek int lockfd; 39481507Sgjelinek int err, arg; 39491507Sgjelinek boolean_t force = B_FALSE; 39501507Sgjelinek zone_dochandle_t handle; 39511507Sgjelinek zone_dochandle_t athandle = NULL; 39521507Sgjelinek char zonepath[MAXPATHLEN]; 39531507Sgjelinek 39541507Sgjelinek if (zonecfg_in_alt_root()) { 39551507Sgjelinek zerror(gettext("cannot attach zone in alternate root")); 39561507Sgjelinek return (Z_ERR); 39571507Sgjelinek } 39581507Sgjelinek 39591507Sgjelinek optind = 0; 39601507Sgjelinek if ((arg = getopt(argc, argv, "?F")) != EOF) { 39611507Sgjelinek switch (arg) { 39621507Sgjelinek case '?': 39631507Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 39641507Sgjelinek return (optopt == '?' ? Z_OK : Z_USAGE); 39651507Sgjelinek case 'F': 39661507Sgjelinek force = B_TRUE; 39671507Sgjelinek break; 39681507Sgjelinek default: 39691507Sgjelinek sub_usage(SHELP_ATTACH, CMD_ATTACH); 39701507Sgjelinek return (Z_USAGE); 39711507Sgjelinek } 39721507Sgjelinek } 39731507Sgjelinek if (sanity_check(target_zone, CMD_ATTACH, B_FALSE, B_TRUE) != Z_OK) 39741507Sgjelinek return (Z_ERR); 39751507Sgjelinek if (verify_details(CMD_ATTACH) != Z_OK) 39761507Sgjelinek return (Z_ERR); 39771507Sgjelinek 39781507Sgjelinek if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath))) 39791507Sgjelinek != Z_OK) { 39801507Sgjelinek errno = err; 39811507Sgjelinek zperror2(target_zone, gettext("could not get zone path")); 39821507Sgjelinek return (Z_ERR); 39831507Sgjelinek } 39841507Sgjelinek 39851507Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 39861507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 39871507Sgjelinek return (Z_ERR); 39881507Sgjelinek } 39891507Sgjelinek 39901507Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 39911507Sgjelinek errno = err; 39921507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 39931507Sgjelinek zonecfg_fini_handle(handle); 39941507Sgjelinek return (Z_ERR); 39951507Sgjelinek } 39961507Sgjelinek 39971507Sgjelinek if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 39981507Sgjelinek zerror(gettext("another %s may have an operation in progress."), 39991507Sgjelinek "zoneadm"); 40001507Sgjelinek zonecfg_fini_handle(handle); 40011507Sgjelinek return (Z_ERR); 40021507Sgjelinek } 40031507Sgjelinek 40041507Sgjelinek if (force) 40051507Sgjelinek goto forced; 40061507Sgjelinek 40071507Sgjelinek if ((athandle = zonecfg_init_handle()) == NULL) { 40081507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40091507Sgjelinek goto done; 40101507Sgjelinek } 40111507Sgjelinek 40121507Sgjelinek if ((err = zonecfg_get_attach_handle(zonepath, target_zone, B_TRUE, 40131507Sgjelinek athandle)) != Z_OK) { 40141507Sgjelinek if (err == Z_NO_ZONE) 40151507Sgjelinek zerror(gettext("Not a detached zone")); 40161507Sgjelinek else if (err == Z_INVALID_DOCUMENT) 40171507Sgjelinek zerror(gettext("Cannot attach to an earlier release " 40181507Sgjelinek "of the operating system")); 40191507Sgjelinek else 40201507Sgjelinek zperror(cmd_to_str(CMD_ATTACH), B_TRUE); 40211507Sgjelinek goto done; 40221507Sgjelinek } 40231507Sgjelinek 40241507Sgjelinek /* Get the detach information for the locally defined zone. */ 40251507Sgjelinek if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) { 40261507Sgjelinek errno = err; 40271507Sgjelinek zperror(gettext("getting the attach information failed"), 40281507Sgjelinek B_TRUE); 40291507Sgjelinek goto done; 40301507Sgjelinek } 40311507Sgjelinek 40321507Sgjelinek /* sw_cmp prints error msgs as necessary */ 40331507Sgjelinek if ((err = sw_cmp(handle, athandle)) != Z_OK) 40341507Sgjelinek goto done; 40351507Sgjelinek 40361507Sgjelinek if ((err = dev_fix(athandle)) != Z_OK) 40371507Sgjelinek goto done; 40381507Sgjelinek 40391507Sgjelinek forced: 40401507Sgjelinek 40411507Sgjelinek zonecfg_rm_detached(handle, force); 40421507Sgjelinek 40431507Sgjelinek if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { 40441507Sgjelinek errno = err; 40451507Sgjelinek zperror(gettext("could not reset state"), B_TRUE); 40461507Sgjelinek } 40471507Sgjelinek 40481507Sgjelinek done: 40491507Sgjelinek zonecfg_fini_handle(handle); 40501507Sgjelinek release_lock_file(lockfd); 40511507Sgjelinek if (athandle != NULL) 40521507Sgjelinek zonecfg_fini_handle(athandle); 40531507Sgjelinek 40541507Sgjelinek return ((err == Z_OK) ? Z_OK : Z_ERR); 40551507Sgjelinek } 40561507Sgjelinek 40571300Sgjelinek /* 40580Sstevel@tonic-gate * On input, TRUE => yes, FALSE => no. 40590Sstevel@tonic-gate * On return, TRUE => 1, FALSE => 0, could not ask => -1. 40600Sstevel@tonic-gate */ 40610Sstevel@tonic-gate 40620Sstevel@tonic-gate static int 40630Sstevel@tonic-gate ask_yesno(boolean_t default_answer, const char *question) 40640Sstevel@tonic-gate { 40650Sstevel@tonic-gate char line[64]; /* should be large enough to answer yes or no */ 40660Sstevel@tonic-gate 40670Sstevel@tonic-gate if (!isatty(STDIN_FILENO)) 40680Sstevel@tonic-gate return (-1); 40690Sstevel@tonic-gate for (;;) { 40700Sstevel@tonic-gate (void) printf("%s (%s)? ", question, 40710Sstevel@tonic-gate default_answer ? "[y]/n" : "y/[n]"); 40720Sstevel@tonic-gate if (fgets(line, sizeof (line), stdin) == NULL || 40730Sstevel@tonic-gate line[0] == '\n') 40740Sstevel@tonic-gate return (default_answer ? 1 : 0); 40750Sstevel@tonic-gate if (tolower(line[0]) == 'y') 40760Sstevel@tonic-gate return (1); 40770Sstevel@tonic-gate if (tolower(line[0]) == 'n') 40780Sstevel@tonic-gate return (0); 40790Sstevel@tonic-gate } 40800Sstevel@tonic-gate } 40810Sstevel@tonic-gate 40820Sstevel@tonic-gate static int 40830Sstevel@tonic-gate uninstall_func(int argc, char *argv[]) 40840Sstevel@tonic-gate { 40850Sstevel@tonic-gate /* 6: "exec " and " " */ 40860Sstevel@tonic-gate char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 6]; 40870Sstevel@tonic-gate char line[ZONENAME_MAX + 128]; /* Enough for "Are you sure ..." */ 40880Sstevel@tonic-gate char rootpath[MAXPATHLEN], devpath[MAXPATHLEN]; 40890Sstevel@tonic-gate boolean_t force = B_FALSE; 40900Sstevel@tonic-gate int lockfd, answer; 40910Sstevel@tonic-gate int err, arg; 40920Sstevel@tonic-gate int status; 40930Sstevel@tonic-gate 4094766Scarlsonj if (zonecfg_in_alt_root()) { 4095766Scarlsonj zerror(gettext("cannot uninstall zone in alternate root")); 4096766Scarlsonj return (Z_ERR); 4097766Scarlsonj } 4098766Scarlsonj 40990Sstevel@tonic-gate optind = 0; 41000Sstevel@tonic-gate while ((arg = getopt(argc, argv, "?F")) != EOF) { 41010Sstevel@tonic-gate switch (arg) { 41020Sstevel@tonic-gate case '?': 41030Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 41040Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 41050Sstevel@tonic-gate case 'F': 41060Sstevel@tonic-gate force = B_TRUE; 41070Sstevel@tonic-gate break; 41080Sstevel@tonic-gate default: 41090Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 41100Sstevel@tonic-gate return (Z_USAGE); 41110Sstevel@tonic-gate } 41120Sstevel@tonic-gate } 41130Sstevel@tonic-gate if (argc > optind) { 41140Sstevel@tonic-gate sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL); 41150Sstevel@tonic-gate return (Z_USAGE); 41160Sstevel@tonic-gate } 41170Sstevel@tonic-gate 41180Sstevel@tonic-gate if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE) != Z_OK) 41190Sstevel@tonic-gate return (Z_ERR); 41200Sstevel@tonic-gate 41210Sstevel@tonic-gate if (!force) { 41220Sstevel@tonic-gate (void) snprintf(line, sizeof (line), 41230Sstevel@tonic-gate gettext("Are you sure you want to %s zone %s"), 41240Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL), target_zone); 41250Sstevel@tonic-gate if ((answer = ask_yesno(B_FALSE, line)) == 0) { 41260Sstevel@tonic-gate return (Z_OK); 41270Sstevel@tonic-gate } else if (answer == -1) { 41280Sstevel@tonic-gate zerror(gettext("Input not from terminal and -F " 41290Sstevel@tonic-gate "not specified: %s not done."), 41300Sstevel@tonic-gate cmd_to_str(CMD_UNINSTALL)); 41310Sstevel@tonic-gate return (Z_ERR); 41320Sstevel@tonic-gate } 41330Sstevel@tonic-gate } 41340Sstevel@tonic-gate 41350Sstevel@tonic-gate if ((err = zone_get_zonepath(target_zone, devpath, 41360Sstevel@tonic-gate sizeof (devpath))) != Z_OK) { 41370Sstevel@tonic-gate errno = err; 41380Sstevel@tonic-gate zperror2(target_zone, gettext("could not get zone path")); 41390Sstevel@tonic-gate return (Z_ERR); 41400Sstevel@tonic-gate } 41410Sstevel@tonic-gate (void) strlcat(devpath, "/dev", sizeof (devpath)); 41420Sstevel@tonic-gate if ((err = zone_get_rootpath(target_zone, rootpath, 41430Sstevel@tonic-gate sizeof (rootpath))) != Z_OK) { 41440Sstevel@tonic-gate errno = err; 41450Sstevel@tonic-gate zperror2(target_zone, gettext("could not get root path")); 41460Sstevel@tonic-gate return (Z_ERR); 41470Sstevel@tonic-gate } 41480Sstevel@tonic-gate 41490Sstevel@tonic-gate /* 41500Sstevel@tonic-gate * If there seems to be a zoneadmd running for this zone, call it 41510Sstevel@tonic-gate * to tell it that an uninstall is happening; if all goes well it 41520Sstevel@tonic-gate * will then shut itself down. 41530Sstevel@tonic-gate */ 41540Sstevel@tonic-gate if (ping_zoneadmd(target_zone) == Z_OK) { 41550Sstevel@tonic-gate zone_cmd_arg_t zarg; 41560Sstevel@tonic-gate zarg.cmd = Z_NOTE_UNINSTALLING; 41570Sstevel@tonic-gate /* we don't care too much if this fails... just plow on */ 41580Sstevel@tonic-gate (void) call_zoneadmd(target_zone, &zarg); 41590Sstevel@tonic-gate } 41600Sstevel@tonic-gate 41610Sstevel@tonic-gate if (grab_lock_file(target_zone, &lockfd) != Z_OK) { 41620Sstevel@tonic-gate zerror(gettext("another %s may have an operation in progress."), 41631300Sgjelinek "zoneadm"); 41640Sstevel@tonic-gate return (Z_ERR); 41650Sstevel@tonic-gate } 41660Sstevel@tonic-gate 41670Sstevel@tonic-gate /* Don't uninstall the zone if anything is mounted there */ 41680Sstevel@tonic-gate err = zonecfg_find_mounts(rootpath, NULL, NULL); 41690Sstevel@tonic-gate if (err) { 41700Sstevel@tonic-gate zerror(gettext("These file-systems are mounted on " 41711645Scomay "subdirectories of %s.\n"), rootpath); 41720Sstevel@tonic-gate (void) zonecfg_find_mounts(rootpath, zfm_print, NULL); 41730Sstevel@tonic-gate return (Z_ERR); 41740Sstevel@tonic-gate } 41750Sstevel@tonic-gate 41760Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE); 41770Sstevel@tonic-gate if (err != Z_OK) { 41780Sstevel@tonic-gate errno = err; 41790Sstevel@tonic-gate zperror2(target_zone, gettext("could not set state")); 41800Sstevel@tonic-gate goto bad; 41810Sstevel@tonic-gate } 41820Sstevel@tonic-gate 41830Sstevel@tonic-gate /* 41840Sstevel@tonic-gate * "exec" the command so that the returned status is that of 41850Sstevel@tonic-gate * RMCOMMAND and not the shell. 41860Sstevel@tonic-gate */ 41870Sstevel@tonic-gate (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 41880Sstevel@tonic-gate devpath); 41890Sstevel@tonic-gate status = do_subproc(cmdbuf); 41900Sstevel@tonic-gate if ((err = subproc_status(RMCOMMAND, status)) != Z_OK) 41910Sstevel@tonic-gate goto bad; 41920Sstevel@tonic-gate (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec " RMCOMMAND " %s", 41930Sstevel@tonic-gate rootpath); 41940Sstevel@tonic-gate status = do_subproc(cmdbuf); 41950Sstevel@tonic-gate if ((err = subproc_status(RMCOMMAND, status)) != Z_OK) 41960Sstevel@tonic-gate goto bad; 41970Sstevel@tonic-gate err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED); 41980Sstevel@tonic-gate if (err != Z_OK) { 41990Sstevel@tonic-gate errno = err; 42000Sstevel@tonic-gate zperror2(target_zone, gettext("could not reset state")); 42010Sstevel@tonic-gate } 42020Sstevel@tonic-gate bad: 42030Sstevel@tonic-gate release_lock_file(lockfd); 42040Sstevel@tonic-gate return (err); 42050Sstevel@tonic-gate } 42060Sstevel@tonic-gate 4207766Scarlsonj /* ARGSUSED */ 4208766Scarlsonj static int 4209766Scarlsonj mount_func(int argc, char *argv[]) 4210766Scarlsonj { 4211766Scarlsonj zone_cmd_arg_t zarg; 4212766Scarlsonj 4213766Scarlsonj if (argc > 0) 4214766Scarlsonj return (Z_USAGE); 4215766Scarlsonj if (sanity_check(target_zone, CMD_MOUNT, B_FALSE, B_FALSE) != Z_OK) 4216766Scarlsonj return (Z_ERR); 4217766Scarlsonj if (verify_details(CMD_MOUNT) != Z_OK) 4218766Scarlsonj return (Z_ERR); 4219766Scarlsonj 4220766Scarlsonj zarg.cmd = Z_MOUNT; 4221766Scarlsonj if (call_zoneadmd(target_zone, &zarg) != 0) { 4222766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 4223766Scarlsonj return (Z_ERR); 4224766Scarlsonj } 4225766Scarlsonj return (Z_OK); 4226766Scarlsonj } 4227766Scarlsonj 4228766Scarlsonj /* ARGSUSED */ 4229766Scarlsonj static int 4230766Scarlsonj unmount_func(int argc, char *argv[]) 4231766Scarlsonj { 4232766Scarlsonj zone_cmd_arg_t zarg; 4233766Scarlsonj 4234766Scarlsonj if (argc > 0) 4235766Scarlsonj return (Z_USAGE); 4236766Scarlsonj if (sanity_check(target_zone, CMD_UNMOUNT, B_FALSE, B_FALSE) != Z_OK) 4237766Scarlsonj return (Z_ERR); 4238766Scarlsonj 4239766Scarlsonj zarg.cmd = Z_UNMOUNT; 4240766Scarlsonj if (call_zoneadmd(target_zone, &zarg) != 0) { 4241766Scarlsonj zerror(gettext("call to %s failed"), "zoneadmd"); 4242766Scarlsonj return (Z_ERR); 4243766Scarlsonj } 4244766Scarlsonj return (Z_OK); 4245766Scarlsonj } 4246766Scarlsonj 42470Sstevel@tonic-gate static int 42480Sstevel@tonic-gate help_func(int argc, char *argv[]) 42490Sstevel@tonic-gate { 42500Sstevel@tonic-gate int arg, cmd_num; 42510Sstevel@tonic-gate 42520Sstevel@tonic-gate if (argc == 0) { 42530Sstevel@tonic-gate (void) usage(B_TRUE); 42540Sstevel@tonic-gate return (Z_OK); 42550Sstevel@tonic-gate } 42560Sstevel@tonic-gate optind = 0; 42570Sstevel@tonic-gate if ((arg = getopt(argc, argv, "?")) != EOF) { 42580Sstevel@tonic-gate switch (arg) { 42590Sstevel@tonic-gate case '?': 42600Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 42610Sstevel@tonic-gate return (optopt == '?' ? Z_OK : Z_USAGE); 42620Sstevel@tonic-gate default: 42630Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 42640Sstevel@tonic-gate return (Z_USAGE); 42650Sstevel@tonic-gate } 42660Sstevel@tonic-gate } 42670Sstevel@tonic-gate while (optind < argc) { 4268988Scarlsonj /* Private commands have NULL short_usage; omit them */ 4269988Scarlsonj if ((cmd_num = cmd_match(argv[optind])) < 0 || 4270988Scarlsonj cmdtab[cmd_num].short_usage == NULL) { 42710Sstevel@tonic-gate sub_usage(SHELP_HELP, CMD_HELP); 42720Sstevel@tonic-gate return (Z_USAGE); 42730Sstevel@tonic-gate } 42740Sstevel@tonic-gate sub_usage(cmdtab[cmd_num].short_usage, cmd_num); 42750Sstevel@tonic-gate optind++; 42760Sstevel@tonic-gate } 42770Sstevel@tonic-gate return (Z_OK); 42780Sstevel@tonic-gate } 42790Sstevel@tonic-gate 42800Sstevel@tonic-gate /* 42810Sstevel@tonic-gate * Returns: CMD_MIN thru CMD_MAX on success, -1 on error 42820Sstevel@tonic-gate */ 42830Sstevel@tonic-gate 42840Sstevel@tonic-gate static int 42850Sstevel@tonic-gate cmd_match(char *cmd) 42860Sstevel@tonic-gate { 42870Sstevel@tonic-gate int i; 42880Sstevel@tonic-gate 42890Sstevel@tonic-gate for (i = CMD_MIN; i <= CMD_MAX; i++) { 42900Sstevel@tonic-gate /* return only if there is an exact match */ 42910Sstevel@tonic-gate if (strcmp(cmd, cmdtab[i].cmd_name) == 0) 42920Sstevel@tonic-gate return (cmdtab[i].cmd_num); 42930Sstevel@tonic-gate } 42940Sstevel@tonic-gate return (-1); 42950Sstevel@tonic-gate } 42960Sstevel@tonic-gate 42970Sstevel@tonic-gate static int 42980Sstevel@tonic-gate parse_and_run(int argc, char *argv[]) 42990Sstevel@tonic-gate { 43000Sstevel@tonic-gate int i = cmd_match(argv[0]); 43010Sstevel@tonic-gate 43020Sstevel@tonic-gate if (i < 0) 43030Sstevel@tonic-gate return (usage(B_FALSE)); 43040Sstevel@tonic-gate return (cmdtab[i].handler(argc - 1, &(argv[1]))); 43050Sstevel@tonic-gate } 43060Sstevel@tonic-gate 43070Sstevel@tonic-gate static char * 43080Sstevel@tonic-gate get_execbasename(char *execfullname) 43090Sstevel@tonic-gate { 43100Sstevel@tonic-gate char *last_slash, *execbasename; 43110Sstevel@tonic-gate 43120Sstevel@tonic-gate /* guard against '/' at end of command invocation */ 43130Sstevel@tonic-gate for (;;) { 43140Sstevel@tonic-gate last_slash = strrchr(execfullname, '/'); 43150Sstevel@tonic-gate if (last_slash == NULL) { 43160Sstevel@tonic-gate execbasename = execfullname; 43170Sstevel@tonic-gate break; 43180Sstevel@tonic-gate } else { 43190Sstevel@tonic-gate execbasename = last_slash + 1; 43200Sstevel@tonic-gate if (*execbasename == '\0') { 43210Sstevel@tonic-gate *last_slash = '\0'; 43220Sstevel@tonic-gate continue; 43230Sstevel@tonic-gate } 43240Sstevel@tonic-gate break; 43250Sstevel@tonic-gate } 43260Sstevel@tonic-gate } 43270Sstevel@tonic-gate return (execbasename); 43280Sstevel@tonic-gate } 43290Sstevel@tonic-gate 43300Sstevel@tonic-gate int 43310Sstevel@tonic-gate main(int argc, char **argv) 43320Sstevel@tonic-gate { 43330Sstevel@tonic-gate int arg; 43340Sstevel@tonic-gate zoneid_t zid; 4335766Scarlsonj struct stat st; 43360Sstevel@tonic-gate 43370Sstevel@tonic-gate if ((locale = setlocale(LC_ALL, "")) == NULL) 43380Sstevel@tonic-gate locale = "C"; 43390Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 43400Sstevel@tonic-gate setbuf(stdout, NULL); 43410Sstevel@tonic-gate (void) sigset(SIGHUP, SIG_IGN); 43420Sstevel@tonic-gate execname = get_execbasename(argv[0]); 43430Sstevel@tonic-gate target_zone = NULL; 43440Sstevel@tonic-gate if (chdir("/") != 0) { 43450Sstevel@tonic-gate zerror(gettext("could not change directory to /.")); 43460Sstevel@tonic-gate exit(Z_ERR); 43470Sstevel@tonic-gate } 43480Sstevel@tonic-gate 4349766Scarlsonj while ((arg = getopt(argc, argv, "?z:R:")) != EOF) { 43500Sstevel@tonic-gate switch (arg) { 43510Sstevel@tonic-gate case '?': 43520Sstevel@tonic-gate return (usage(B_TRUE)); 43530Sstevel@tonic-gate case 'z': 43540Sstevel@tonic-gate target_zone = optarg; 43550Sstevel@tonic-gate break; 4356766Scarlsonj case 'R': /* private option for admin/install use */ 4357766Scarlsonj if (*optarg != '/') { 4358766Scarlsonj zerror(gettext("root path must be absolute.")); 4359766Scarlsonj exit(Z_ERR); 4360766Scarlsonj } 4361766Scarlsonj if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { 4362766Scarlsonj zerror( 4363766Scarlsonj gettext("root path must be a directory.")); 4364766Scarlsonj exit(Z_ERR); 4365766Scarlsonj } 4366766Scarlsonj zonecfg_set_root(optarg); 4367766Scarlsonj break; 43680Sstevel@tonic-gate default: 43690Sstevel@tonic-gate return (usage(B_FALSE)); 43700Sstevel@tonic-gate } 43710Sstevel@tonic-gate } 43720Sstevel@tonic-gate 43730Sstevel@tonic-gate if (optind >= argc) 43740Sstevel@tonic-gate return (usage(B_FALSE)); 43750Sstevel@tonic-gate if (target_zone != NULL && zone_get_id(target_zone, &zid) != 0) { 43760Sstevel@tonic-gate errno = Z_NO_ZONE; 43770Sstevel@tonic-gate zperror(target_zone, B_TRUE); 43780Sstevel@tonic-gate exit(Z_ERR); 43790Sstevel@tonic-gate } 43800Sstevel@tonic-gate return (parse_and_run(argc - optind, &argv[optind])); 43810Sstevel@tonic-gate } 4382