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 51645Scomay * Common Development and Distribution License (the "License"). 61645Scomay * 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 */ 211645Scomay 220Sstevel@tonic-gate /* 23*8770SJordan.Vaughan@Sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate /* 280Sstevel@tonic-gate * Console support for zones requires a significant infrastructure. The 290Sstevel@tonic-gate * core pieces are contained in this file, but other portions of note 300Sstevel@tonic-gate * are in the zlogin(1M) command, the zcons(7D) driver, and in the 310Sstevel@tonic-gate * devfsadm(1M) misc_link generator. 320Sstevel@tonic-gate * 330Sstevel@tonic-gate * Care is taken to make the console behave in an "intuitive" fashion for 340Sstevel@tonic-gate * administrators. Essentially, we try as much as possible to mimic the 350Sstevel@tonic-gate * experience of using a system via a tip line and system controller. 360Sstevel@tonic-gate * 370Sstevel@tonic-gate * The zone console architecture looks like this: 380Sstevel@tonic-gate * 390Sstevel@tonic-gate * Global Zone | Non-Global Zone 400Sstevel@tonic-gate * .--------------. | 410Sstevel@tonic-gate * .-----------. | zoneadmd -z | | .--------. .---------. 420Sstevel@tonic-gate * | zlogin -C | | myzone | | | ttymon | | syslogd | 430Sstevel@tonic-gate * `-----------' `--------------' | `--------' `---------' 440Sstevel@tonic-gate * | | | | | | | 450Sstevel@tonic-gate * User | | | | | V V 460Sstevel@tonic-gate * - - - - - - - - -|- - - -|- - - -|-|- - - - - - -|- - /dev/zconsole - - - 470Sstevel@tonic-gate * Kernel V V | | | 480Sstevel@tonic-gate * [AF_UNIX Socket] | `--------. .-------------' 490Sstevel@tonic-gate * | | | 500Sstevel@tonic-gate * | V V 510Sstevel@tonic-gate * | +-----------+ 520Sstevel@tonic-gate * | | ldterm, | 530Sstevel@tonic-gate * | | etc. | 540Sstevel@tonic-gate * | +-----------+ 550Sstevel@tonic-gate * | +-[Anchor]--+ 560Sstevel@tonic-gate * | | ptem | 570Sstevel@tonic-gate * V +-----------+ 580Sstevel@tonic-gate * +---master---+---slave---+ 590Sstevel@tonic-gate * | | 600Sstevel@tonic-gate * | zcons driver | 610Sstevel@tonic-gate * | zonename="myzone" | 620Sstevel@tonic-gate * +------------------------+ 630Sstevel@tonic-gate * 64*8770SJordan.Vaughan@Sun.com * There are basically two major tasks which the console subsystem in 650Sstevel@tonic-gate * zoneadmd accomplishes: 660Sstevel@tonic-gate * 670Sstevel@tonic-gate * - Setup and teardown of zcons driver instances. One zcons instance 680Sstevel@tonic-gate * is maintained per zone; we take advantage of the libdevice APIs 690Sstevel@tonic-gate * to online new instances of zcons as needed. Care is taken to 700Sstevel@tonic-gate * prune and manage these appropriately; see init_console_dev() and 710Sstevel@tonic-gate * destroy_console_dev(). The end result is the creation of the 720Sstevel@tonic-gate * zcons(7D) instance and an open file descriptor to the master side. 730Sstevel@tonic-gate * zcons instances are associated with zones via their zonename device 740Sstevel@tonic-gate * property. This the console instance to persist across reboots, 750Sstevel@tonic-gate * and while the zone is halted. 760Sstevel@tonic-gate * 770Sstevel@tonic-gate * - Acting as a server for 'zlogin -C' instances. When zlogin -C is 780Sstevel@tonic-gate * run, zlogin connects to zoneadmd via unix domain socket. zoneadmd 790Sstevel@tonic-gate * functions as a two-way proxy for console I/O, relaying user input 800Sstevel@tonic-gate * to the master side of the console, and relaying output from the 810Sstevel@tonic-gate * zone to the user. 820Sstevel@tonic-gate */ 830Sstevel@tonic-gate 840Sstevel@tonic-gate #include <sys/types.h> 850Sstevel@tonic-gate #include <sys/socket.h> 860Sstevel@tonic-gate #include <sys/stat.h> 870Sstevel@tonic-gate #include <sys/termios.h> 880Sstevel@tonic-gate #include <sys/zcons.h> 892621Sllai1 #include <sys/mkdev.h> 900Sstevel@tonic-gate 910Sstevel@tonic-gate #include <assert.h> 920Sstevel@tonic-gate #include <ctype.h> 930Sstevel@tonic-gate #include <errno.h> 940Sstevel@tonic-gate #include <fcntl.h> 950Sstevel@tonic-gate #include <stdarg.h> 960Sstevel@tonic-gate #include <stdio.h> 970Sstevel@tonic-gate #include <stdlib.h> 980Sstevel@tonic-gate #include <strings.h> 990Sstevel@tonic-gate #include <stropts.h> 1000Sstevel@tonic-gate #include <thread.h> 1010Sstevel@tonic-gate #include <ucred.h> 1020Sstevel@tonic-gate #include <unistd.h> 1030Sstevel@tonic-gate #include <zone.h> 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate #include <libdevinfo.h> 1060Sstevel@tonic-gate #include <libdevice.h> 1070Sstevel@tonic-gate #include <libzonecfg.h> 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate #include <syslog.h> 1102621Sllai1 #include <sys/modctl.h> 1112621Sllai1 #include <sys/fs/sdev_node.h> 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate #include "zoneadmd.h" 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate #define ZCONSNEX_DEVTREEPATH "/pseudo/zconsnex@1" 1160Sstevel@tonic-gate #define ZCONSNEX_FILEPATH "/devices/pseudo/zconsnex@1" 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate #define CONSOLE_SOCKPATH ZONES_TMPDIR "/%s.console_sock" 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate static int serverfd = -1; /* console server unix domain socket fd */ 1212267Sdp char boot_args[BOOTARGS_MAX]; 1222267Sdp char bad_boot_arg[BOOTARGS_MAX]; 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate /* 1250Sstevel@tonic-gate * The eventstream is a simple one-directional flow of messages from the 1260Sstevel@tonic-gate * door server to the console subsystem, implemented with a pipe. 1270Sstevel@tonic-gate * It is used to wake up the console poller when it needs to take action, 1280Sstevel@tonic-gate * message the user, die off, etc. 1290Sstevel@tonic-gate */ 1300Sstevel@tonic-gate static int eventstream[2]; 1310Sstevel@tonic-gate 1322267Sdp 1332267Sdp 1340Sstevel@tonic-gate int 1350Sstevel@tonic-gate eventstream_init() 1360Sstevel@tonic-gate { 1370Sstevel@tonic-gate if (pipe(eventstream) == -1) 1380Sstevel@tonic-gate return (-1); 1390Sstevel@tonic-gate return (0); 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate void 1430Sstevel@tonic-gate eventstream_write(zone_evt_t evt) 1440Sstevel@tonic-gate { 1450Sstevel@tonic-gate (void) write(eventstream[0], &evt, sizeof (evt)); 1460Sstevel@tonic-gate } 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate static zone_evt_t 1490Sstevel@tonic-gate eventstream_read(void) 1500Sstevel@tonic-gate { 1510Sstevel@tonic-gate zone_evt_t evt = Z_EVT_NULL; 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate (void) read(eventstream[1], &evt, sizeof (evt)); 1540Sstevel@tonic-gate return (evt); 1550Sstevel@tonic-gate } 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate /* 1580Sstevel@tonic-gate * count_console_devs() and its helper count_cb() do a walk of the 1590Sstevel@tonic-gate * subtree of the device tree where zone console nodes are represented. 1600Sstevel@tonic-gate * The goal is to count zone console instances already setup for a zone 1610Sstevel@tonic-gate * with the given name. More than 1 is anomolous, and our caller will 1620Sstevel@tonic-gate * have to deal with that if we find that's the case. 1630Sstevel@tonic-gate * 1640Sstevel@tonic-gate * Note: this algorithm is a linear search of nodes in the zconsnex subtree 1650Sstevel@tonic-gate * of the device tree, and could be a scalability problem, but I don't see 1660Sstevel@tonic-gate * how to avoid it. 1670Sstevel@tonic-gate */ 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate /* 1700Sstevel@tonic-gate * cb_data is shared by count_cb and destroy_cb for simplicity. 1710Sstevel@tonic-gate */ 1720Sstevel@tonic-gate struct cb_data { 1730Sstevel@tonic-gate zlog_t *zlogp; 1740Sstevel@tonic-gate int found; 1750Sstevel@tonic-gate int killed; 1760Sstevel@tonic-gate }; 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate static int 1790Sstevel@tonic-gate count_cb(di_node_t node, void *arg) 1800Sstevel@tonic-gate { 1810Sstevel@tonic-gate struct cb_data *cb = (struct cb_data *)arg; 1820Sstevel@tonic-gate char *prop_data; 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zonename", 1850Sstevel@tonic-gate &prop_data) != -1) { 1860Sstevel@tonic-gate assert(prop_data != NULL); 1870Sstevel@tonic-gate if (strcmp(prop_data, zone_name) == 0) { 1880Sstevel@tonic-gate cb->found++; 1890Sstevel@tonic-gate return (DI_WALK_CONTINUE); 1900Sstevel@tonic-gate } 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate return (DI_WALK_CONTINUE); 1930Sstevel@tonic-gate } 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate static int 1960Sstevel@tonic-gate count_console_devs(zlog_t *zlogp) 1970Sstevel@tonic-gate { 1980Sstevel@tonic-gate di_node_t root; 1990Sstevel@tonic-gate struct cb_data cb; 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate bzero(&cb, sizeof (cb)); 2020Sstevel@tonic-gate cb.zlogp = zlogp; 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate if ((root = di_init(ZCONSNEX_DEVTREEPATH, DINFOCPYALL)) == 2050Sstevel@tonic-gate DI_NODE_NIL) { 2060Sstevel@tonic-gate zerror(zlogp, B_TRUE, "%s failed", "di_init"); 2070Sstevel@tonic-gate return (-1); 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, count_cb); 2110Sstevel@tonic-gate di_fini(root); 2120Sstevel@tonic-gate return (cb.found); 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate /* 2160Sstevel@tonic-gate * destroy_console_devs() and its helper destroy_cb() tears down any console 2170Sstevel@tonic-gate * instances associated with this zone. If things went very wrong, we 2180Sstevel@tonic-gate * might have more than one console instance hanging around. This routine 2190Sstevel@tonic-gate * hunts down and tries to remove all of them. Of course, if the console 2200Sstevel@tonic-gate * is open, the instance will not detach, which is a potential issue. 2210Sstevel@tonic-gate */ 2220Sstevel@tonic-gate static int 2230Sstevel@tonic-gate destroy_cb(di_node_t node, void *arg) 2240Sstevel@tonic-gate { 2250Sstevel@tonic-gate struct cb_data *cb = (struct cb_data *)arg; 2260Sstevel@tonic-gate char *prop_data; 2270Sstevel@tonic-gate char *tmp; 2280Sstevel@tonic-gate char devpath[MAXPATHLEN]; 2290Sstevel@tonic-gate devctl_hdl_t hdl; 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zonename", 2320Sstevel@tonic-gate &prop_data) == -1) 2330Sstevel@tonic-gate return (DI_WALK_CONTINUE); 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate assert(prop_data != NULL); 2360Sstevel@tonic-gate if (strcmp(prop_data, zone_name) != 0) { 2370Sstevel@tonic-gate /* this is the console for a different zone */ 2380Sstevel@tonic-gate return (DI_WALK_CONTINUE); 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate cb->found++; 2420Sstevel@tonic-gate tmp = di_devfs_path(node); 2430Sstevel@tonic-gate (void) snprintf(devpath, sizeof (devpath), "/devices/%s", tmp); 2440Sstevel@tonic-gate di_devfs_path_free(tmp); 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate if ((hdl = devctl_device_acquire(devpath, 0)) == NULL) { 2470Sstevel@tonic-gate zerror(cb->zlogp, B_TRUE, "WARNING: console %s found, " 2480Sstevel@tonic-gate "but it could not be controlled.", devpath); 2490Sstevel@tonic-gate return (DI_WALK_CONTINUE); 2500Sstevel@tonic-gate } 2510Sstevel@tonic-gate if (devctl_device_remove(hdl) == 0) { 2520Sstevel@tonic-gate cb->killed++; 2530Sstevel@tonic-gate } else { 2540Sstevel@tonic-gate zerror(cb->zlogp, B_TRUE, "WARNING: console %s found, " 2550Sstevel@tonic-gate "but it could not be removed.", devpath); 2560Sstevel@tonic-gate } 2570Sstevel@tonic-gate devctl_release(hdl); 2580Sstevel@tonic-gate return (DI_WALK_CONTINUE); 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate static int 2620Sstevel@tonic-gate destroy_console_devs(zlog_t *zlogp) 2630Sstevel@tonic-gate { 264*8770SJordan.Vaughan@Sun.com char conspath[MAXPATHLEN]; 2650Sstevel@tonic-gate di_node_t root; 2660Sstevel@tonic-gate struct cb_data cb; 267*8770SJordan.Vaughan@Sun.com int masterfd; 268*8770SJordan.Vaughan@Sun.com int slavefd; 269*8770SJordan.Vaughan@Sun.com 270*8770SJordan.Vaughan@Sun.com /* 271*8770SJordan.Vaughan@Sun.com * Signal the master side to release its handle on the slave side by 272*8770SJordan.Vaughan@Sun.com * issuing a ZC_RELEASESLAVE ioctl. 273*8770SJordan.Vaughan@Sun.com */ 274*8770SJordan.Vaughan@Sun.com (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", 275*8770SJordan.Vaughan@Sun.com zone_name, ZCONS_MASTER_NAME); 276*8770SJordan.Vaughan@Sun.com if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) != -1) { 277*8770SJordan.Vaughan@Sun.com (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", 278*8770SJordan.Vaughan@Sun.com zone_name, ZCONS_SLAVE_NAME); 279*8770SJordan.Vaughan@Sun.com if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) != -1) { 280*8770SJordan.Vaughan@Sun.com if (ioctl(masterfd, ZC_RELEASESLAVE, 281*8770SJordan.Vaughan@Sun.com (caddr_t)(intptr_t)slavefd) != 0) 282*8770SJordan.Vaughan@Sun.com zerror(zlogp, B_TRUE, "WARNING: error while " 283*8770SJordan.Vaughan@Sun.com "releasing slave handle of zone console for" 284*8770SJordan.Vaughan@Sun.com " %s", zone_name); 285*8770SJordan.Vaughan@Sun.com (void) close(slavefd); 286*8770SJordan.Vaughan@Sun.com } else { 287*8770SJordan.Vaughan@Sun.com zerror(zlogp, B_TRUE, "WARNING: could not open slave " 288*8770SJordan.Vaughan@Sun.com "side of zone console for %s to release slave " 289*8770SJordan.Vaughan@Sun.com "handle", zone_name); 290*8770SJordan.Vaughan@Sun.com } 291*8770SJordan.Vaughan@Sun.com (void) close(masterfd); 292*8770SJordan.Vaughan@Sun.com } else { 293*8770SJordan.Vaughan@Sun.com zerror(zlogp, B_TRUE, "WARNING: could not open master side of " 294*8770SJordan.Vaughan@Sun.com "zone console for %s to release slave handle", zone_name); 295*8770SJordan.Vaughan@Sun.com } 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate bzero(&cb, sizeof (cb)); 2980Sstevel@tonic-gate cb.zlogp = zlogp; 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate if ((root = di_init(ZCONSNEX_DEVTREEPATH, DINFOCPYALL)) == 3010Sstevel@tonic-gate DI_NODE_NIL) { 3020Sstevel@tonic-gate zerror(zlogp, B_TRUE, "%s failed", "di_init"); 3030Sstevel@tonic-gate return (-1); 3040Sstevel@tonic-gate } 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, destroy_cb); 3070Sstevel@tonic-gate if (cb.found > 1) { 3080Sstevel@tonic-gate zerror(zlogp, B_FALSE, "WARNING: multiple zone console " 3090Sstevel@tonic-gate "instances detected for zone '%s'; %d of %d " 3100Sstevel@tonic-gate "successfully removed.", 3110Sstevel@tonic-gate zone_name, cb.killed, cb.found); 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate di_fini(root); 3150Sstevel@tonic-gate return (0); 3160Sstevel@tonic-gate } 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate /* 3190Sstevel@tonic-gate * init_console_dev() drives the device-tree configuration of the zone 3200Sstevel@tonic-gate * console device. The general strategy is to use the libdevice (devctl) 3210Sstevel@tonic-gate * interfaces to instantiate a new zone console node. We do a lot of 3220Sstevel@tonic-gate * sanity checking, and are careful to reuse a console if one exists. 3230Sstevel@tonic-gate * 3240Sstevel@tonic-gate * Once the device is in the device tree, we kick devfsadm via di_init_devs() 3250Sstevel@tonic-gate * to ensure that the appropriate symlinks (to the master and slave console 3260Sstevel@tonic-gate * devices) are placed in /dev in the global zone. 3270Sstevel@tonic-gate */ 3280Sstevel@tonic-gate static int 3290Sstevel@tonic-gate init_console_dev(zlog_t *zlogp) 3300Sstevel@tonic-gate { 331*8770SJordan.Vaughan@Sun.com char conspath[MAXPATHLEN]; 332*8770SJordan.Vaughan@Sun.com devctl_hdl_t bus_hdl = NULL; 333*8770SJordan.Vaughan@Sun.com devctl_hdl_t dev_hdl = NULL; 3340Sstevel@tonic-gate devctl_ddef_t ddef_hdl = NULL; 3350Sstevel@tonic-gate di_devlink_handle_t dl = NULL; 336*8770SJordan.Vaughan@Sun.com int rv = -1; 337*8770SJordan.Vaughan@Sun.com int ndevs; 338*8770SJordan.Vaughan@Sun.com int masterfd; 339*8770SJordan.Vaughan@Sun.com int slavefd; 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /* 3420Sstevel@tonic-gate * Don't re-setup console if it is working and ready already; just 3430Sstevel@tonic-gate * skip ahead to making devlinks, which we do for sanity's sake. 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate ndevs = count_console_devs(zlogp); 3460Sstevel@tonic-gate if (ndevs == 1) { 3470Sstevel@tonic-gate goto devlinks; 3480Sstevel@tonic-gate } else if (ndevs > 1 || ndevs == -1) { 3490Sstevel@tonic-gate /* 3500Sstevel@tonic-gate * For now, this seems like a reasonable but harsh punishment. 3510Sstevel@tonic-gate * If needed, we could try to get clever and delete all but 3520Sstevel@tonic-gate * the console which is pointed at by the current symlink. 3530Sstevel@tonic-gate */ 3540Sstevel@tonic-gate if (destroy_console_devs(zlogp) == -1) { 3550Sstevel@tonic-gate goto error; 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate } 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate /* 3600Sstevel@tonic-gate * Time to make the consoles! 3610Sstevel@tonic-gate */ 3620Sstevel@tonic-gate if ((bus_hdl = devctl_bus_acquire(ZCONSNEX_FILEPATH, 0)) == NULL) { 3630Sstevel@tonic-gate zerror(zlogp, B_TRUE, "%s failed", "devctl_bus_acquire"); 3640Sstevel@tonic-gate goto error; 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate if ((ddef_hdl = devctl_ddef_alloc("zcons", 0)) == NULL) { 3670Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to allocate ddef handle"); 3680Sstevel@tonic-gate goto error; 3690Sstevel@tonic-gate } 3700Sstevel@tonic-gate /* 3710Sstevel@tonic-gate * Set three properties on this node; the first is the name of the 3720Sstevel@tonic-gate * zone; the second is a flag which lets pseudo know that it is 3730Sstevel@tonic-gate * OK to automatically allocate an instance # for this device; 3740Sstevel@tonic-gate * the third tells the device framework not to auto-detach this 3750Sstevel@tonic-gate * node-- we need the node to still be there when we ask devfsadmd 3760Sstevel@tonic-gate * to make links, and when we need to open it. 3770Sstevel@tonic-gate */ 3780Sstevel@tonic-gate if (devctl_ddef_string(ddef_hdl, "zonename", zone_name) == -1) { 3790Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to create zonename property"); 3800Sstevel@tonic-gate goto error; 3810Sstevel@tonic-gate } 3820Sstevel@tonic-gate if (devctl_ddef_int(ddef_hdl, "auto-assign-instance", 1) == -1) { 3830Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to create auto-assign-instance " 3840Sstevel@tonic-gate "property"); 3850Sstevel@tonic-gate goto error; 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate if (devctl_ddef_int(ddef_hdl, "ddi-no-autodetach", 1) == -1) { 3880Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to create ddi-no-auto-detach " 3890Sstevel@tonic-gate "property"); 3900Sstevel@tonic-gate goto error; 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl) == -1) { 3930Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to create console node"); 3940Sstevel@tonic-gate goto error; 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate devlinks: 3980Sstevel@tonic-gate if ((dl = di_devlink_init("zcons", DI_MAKE_LINK)) != NULL) { 3990Sstevel@tonic-gate (void) di_devlink_fini(&dl); 4000Sstevel@tonic-gate } else { 4010Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to create devlinks"); 4020Sstevel@tonic-gate goto error; 4030Sstevel@tonic-gate } 4040Sstevel@tonic-gate 405*8770SJordan.Vaughan@Sun.com /* 406*8770SJordan.Vaughan@Sun.com * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl, 407*8770SJordan.Vaughan@Sun.com * which will cause the master to retain a reference to the slave. 408*8770SJordan.Vaughan@Sun.com * This prevents ttymon from blowing through the slave's STREAMS anchor. 409*8770SJordan.Vaughan@Sun.com */ 410*8770SJordan.Vaughan@Sun.com (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", 411*8770SJordan.Vaughan@Sun.com zone_name, ZCONS_MASTER_NAME); 412*8770SJordan.Vaughan@Sun.com if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { 413*8770SJordan.Vaughan@Sun.com zerror(zlogp, B_TRUE, "ERROR: could not open master side of " 414*8770SJordan.Vaughan@Sun.com "zone console for %s to acquire slave handle", zone_name); 415*8770SJordan.Vaughan@Sun.com goto error; 416*8770SJordan.Vaughan@Sun.com } 417*8770SJordan.Vaughan@Sun.com (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", 418*8770SJordan.Vaughan@Sun.com zone_name, ZCONS_SLAVE_NAME); 419*8770SJordan.Vaughan@Sun.com if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { 420*8770SJordan.Vaughan@Sun.com zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone" 421*8770SJordan.Vaughan@Sun.com " console for %s to acquire slave handle", zone_name); 422*8770SJordan.Vaughan@Sun.com (void) close(masterfd); 423*8770SJordan.Vaughan@Sun.com goto error; 424*8770SJordan.Vaughan@Sun.com } 425*8770SJordan.Vaughan@Sun.com if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd) == 0) 426*8770SJordan.Vaughan@Sun.com rv = 0; 427*8770SJordan.Vaughan@Sun.com else 428*8770SJordan.Vaughan@Sun.com zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave " 429*8770SJordan.Vaughan@Sun.com "handle of zone console for %s", zone_name); 430*8770SJordan.Vaughan@Sun.com (void) close(slavefd); 431*8770SJordan.Vaughan@Sun.com (void) close(masterfd); 432*8770SJordan.Vaughan@Sun.com 4330Sstevel@tonic-gate error: 4340Sstevel@tonic-gate if (ddef_hdl) 4350Sstevel@tonic-gate devctl_ddef_free(ddef_hdl); 4360Sstevel@tonic-gate if (bus_hdl) 4370Sstevel@tonic-gate devctl_release(bus_hdl); 4380Sstevel@tonic-gate if (dev_hdl) 4390Sstevel@tonic-gate devctl_release(dev_hdl); 4400Sstevel@tonic-gate return (rv); 4410Sstevel@tonic-gate } 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate static int 4440Sstevel@tonic-gate init_console_sock(zlog_t *zlogp) 4450Sstevel@tonic-gate { 4460Sstevel@tonic-gate int servfd; 4470Sstevel@tonic-gate struct sockaddr_un servaddr; 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate bzero(&servaddr, sizeof (servaddr)); 4500Sstevel@tonic-gate servaddr.sun_family = AF_UNIX; 4510Sstevel@tonic-gate (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), 4520Sstevel@tonic-gate CONSOLE_SOCKPATH, zone_name); 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate if ((servfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 4550Sstevel@tonic-gate zerror(zlogp, B_TRUE, "console setup: could not create socket"); 4560Sstevel@tonic-gate return (-1); 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate (void) unlink(servaddr.sun_path); 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate if (bind(servfd, (struct sockaddr *)&servaddr, 4610Sstevel@tonic-gate sizeof (servaddr)) == -1) { 4620Sstevel@tonic-gate zerror(zlogp, B_TRUE, 4630Sstevel@tonic-gate "console setup: could not bind to socket"); 4640Sstevel@tonic-gate goto out; 4650Sstevel@tonic-gate } 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate if (listen(servfd, 4) == -1) { 4680Sstevel@tonic-gate zerror(zlogp, B_TRUE, 4690Sstevel@tonic-gate "console setup: could not listen on socket"); 4700Sstevel@tonic-gate goto out; 4710Sstevel@tonic-gate } 4720Sstevel@tonic-gate return (servfd); 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate out: 4750Sstevel@tonic-gate (void) unlink(servaddr.sun_path); 4760Sstevel@tonic-gate (void) close(servfd); 4770Sstevel@tonic-gate return (-1); 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate static void 4810Sstevel@tonic-gate destroy_console_sock(int servfd) 4820Sstevel@tonic-gate { 4830Sstevel@tonic-gate char path[MAXPATHLEN]; 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate (void) snprintf(path, sizeof (path), CONSOLE_SOCKPATH, zone_name); 4860Sstevel@tonic-gate (void) unlink(path); 4870Sstevel@tonic-gate (void) shutdown(servfd, SHUT_RDWR); 4880Sstevel@tonic-gate (void) close(servfd); 4890Sstevel@tonic-gate } 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate /* 4920Sstevel@tonic-gate * Read the "ident" string from the client's descriptor; this routine also 4930Sstevel@tonic-gate * tolerates being called with pid=NULL, for times when you want to "eat" 4940Sstevel@tonic-gate * the ident string from a client without saving it. 4950Sstevel@tonic-gate */ 4960Sstevel@tonic-gate static int 4970Sstevel@tonic-gate get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len) 4980Sstevel@tonic-gate { 4990Sstevel@tonic-gate char buf[BUFSIZ], *bufp; 5000Sstevel@tonic-gate size_t buflen = sizeof (buf); 5010Sstevel@tonic-gate char c = '\0'; 5020Sstevel@tonic-gate int i = 0, r; 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate /* "eat up the ident string" case, for simplicity */ 5050Sstevel@tonic-gate if (pid == NULL) { 5060Sstevel@tonic-gate assert(locale == NULL && locale_len == 0); 5070Sstevel@tonic-gate while (read(clifd, &c, 1) == 1) { 5080Sstevel@tonic-gate if (c == '\n') 5090Sstevel@tonic-gate return (0); 5100Sstevel@tonic-gate } 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate bzero(buf, sizeof (buf)); 5140Sstevel@tonic-gate while ((buflen > 1) && (r = read(clifd, &c, 1)) == 1) { 5150Sstevel@tonic-gate buflen--; 5160Sstevel@tonic-gate if (c == '\n') 5170Sstevel@tonic-gate break; 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate buf[i] = c; 5200Sstevel@tonic-gate i++; 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate if (r == -1) 5230Sstevel@tonic-gate return (-1); 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate /* 5260Sstevel@tonic-gate * We've filled the buffer, but still haven't seen \n. Keep eating 5270Sstevel@tonic-gate * until we find it; we don't expect this to happen, but this is 5280Sstevel@tonic-gate * defensive. 5290Sstevel@tonic-gate */ 5300Sstevel@tonic-gate if (c != '\n') { 5310Sstevel@tonic-gate while ((r = read(clifd, &c, sizeof (c))) > 0) 5320Sstevel@tonic-gate if (c == '\n') 5330Sstevel@tonic-gate break; 5340Sstevel@tonic-gate } 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate /* 5370Sstevel@tonic-gate * Parse buffer for message of the form: IDENT <pid> <locale> 5380Sstevel@tonic-gate */ 5390Sstevel@tonic-gate bufp = buf; 5400Sstevel@tonic-gate if (strncmp(bufp, "IDENT ", 6) != 0) 5410Sstevel@tonic-gate return (-1); 5420Sstevel@tonic-gate bufp += 6; 5430Sstevel@tonic-gate errno = 0; 5440Sstevel@tonic-gate *pid = strtoll(bufp, &bufp, 10); 5450Sstevel@tonic-gate if (errno != 0) 5460Sstevel@tonic-gate return (-1); 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate while (*bufp != '\0' && isspace(*bufp)) 5490Sstevel@tonic-gate bufp++; 5500Sstevel@tonic-gate (void) strlcpy(locale, bufp, locale_len); 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate return (0); 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate static int 5560Sstevel@tonic-gate accept_client(int servfd, pid_t *pid, char *locale, size_t locale_len) 5570Sstevel@tonic-gate { 5580Sstevel@tonic-gate int connfd; 5590Sstevel@tonic-gate struct sockaddr_un cliaddr; 5600Sstevel@tonic-gate socklen_t clilen; 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate clilen = sizeof (cliaddr); 5630Sstevel@tonic-gate connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); 5640Sstevel@tonic-gate if (connfd == -1) 5650Sstevel@tonic-gate return (-1); 5660Sstevel@tonic-gate if (get_client_ident(connfd, pid, locale, locale_len) == -1) { 5670Sstevel@tonic-gate (void) shutdown(connfd, SHUT_RDWR); 5680Sstevel@tonic-gate (void) close(connfd); 5690Sstevel@tonic-gate return (-1); 5700Sstevel@tonic-gate } 5710Sstevel@tonic-gate (void) write(connfd, "OK\n", 3); 5720Sstevel@tonic-gate return (connfd); 5730Sstevel@tonic-gate } 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate static void 5760Sstevel@tonic-gate reject_client(int servfd, pid_t clientpid) 5770Sstevel@tonic-gate { 5780Sstevel@tonic-gate int connfd; 5790Sstevel@tonic-gate struct sockaddr_un cliaddr; 5800Sstevel@tonic-gate socklen_t clilen; 5810Sstevel@tonic-gate char nak[MAXPATHLEN]; 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate clilen = sizeof (cliaddr); 5840Sstevel@tonic-gate connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate /* 5870Sstevel@tonic-gate * After hear its ident string, tell client to get lost. 5880Sstevel@tonic-gate */ 5890Sstevel@tonic-gate if (get_client_ident(connfd, NULL, NULL, 0) == 0) { 5900Sstevel@tonic-gate (void) snprintf(nak, sizeof (nak), "%lu\n", 5910Sstevel@tonic-gate clientpid); 5920Sstevel@tonic-gate (void) write(connfd, nak, strlen(nak)); 5930Sstevel@tonic-gate } 5940Sstevel@tonic-gate (void) shutdown(connfd, SHUT_RDWR); 5950Sstevel@tonic-gate (void) close(connfd); 5960Sstevel@tonic-gate } 5970Sstevel@tonic-gate 5980Sstevel@tonic-gate static void 5990Sstevel@tonic-gate event_message(int clifd, char *clilocale, zone_evt_t evt) 6000Sstevel@tonic-gate { 6012267Sdp char *str, *lstr = NULL; 6022267Sdp char lmsg[BUFSIZ]; 6032267Sdp char outbuf[BUFSIZ]; 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate if (clifd == -1) 6060Sstevel@tonic-gate return; 6070Sstevel@tonic-gate 6080Sstevel@tonic-gate switch (evt) { 6090Sstevel@tonic-gate case Z_EVT_ZONE_BOOTING: 6102267Sdp if (*boot_args == '\0') { 6112267Sdp str = "NOTICE: Zone booting up"; 6122267Sdp break; 6132267Sdp } 6142267Sdp /*LINTED*/ 6152267Sdp (void) snprintf(lmsg, sizeof (lmsg), localize_msg(clilocale, 6162267Sdp "NOTICE: Zone booting up with arguments: %s"), boot_args); 6172267Sdp lstr = lmsg; 6180Sstevel@tonic-gate break; 6190Sstevel@tonic-gate case Z_EVT_ZONE_READIED: 6202267Sdp str = "NOTICE: Zone readied"; 6210Sstevel@tonic-gate break; 6220Sstevel@tonic-gate case Z_EVT_ZONE_HALTED: 6232267Sdp str = "NOTICE: Zone halted"; 6240Sstevel@tonic-gate break; 6250Sstevel@tonic-gate case Z_EVT_ZONE_REBOOTING: 6262267Sdp if (*boot_args == '\0') { 6272267Sdp str = "NOTICE: Zone rebooting"; 6282267Sdp break; 6292267Sdp } 6302267Sdp /*LINTED*/ 6312267Sdp (void) snprintf(lmsg, sizeof (lmsg), localize_msg(clilocale, 6322267Sdp "NOTICE: Zone rebooting with arguments: %s"), boot_args); 6332267Sdp lstr = lmsg; 6340Sstevel@tonic-gate break; 6350Sstevel@tonic-gate case Z_EVT_ZONE_UNINSTALLING: 6362267Sdp str = "NOTICE: Zone is being uninstalled. Disconnecting..."; 6370Sstevel@tonic-gate break; 6381645Scomay case Z_EVT_ZONE_BOOTFAILED: 6392267Sdp str = "NOTICE: Zone boot failed"; 6402267Sdp break; 6412267Sdp case Z_EVT_ZONE_BADARGS: 6422267Sdp /*LINTED*/ 6432267Sdp (void) snprintf(lmsg, sizeof (lmsg), 6442267Sdp localize_msg(clilocale, 6452267Sdp "WARNING: Ignoring invalid boot arguments: %s"), 6462267Sdp bad_boot_arg); 6472267Sdp lstr = lmsg; 6481645Scomay break; 6490Sstevel@tonic-gate default: 6500Sstevel@tonic-gate return; 6510Sstevel@tonic-gate } 6522267Sdp 6532267Sdp if (lstr == NULL) 6542267Sdp lstr = localize_msg(clilocale, str); 6552267Sdp (void) snprintf(outbuf, sizeof (outbuf), "\r\n[%s]\r\n", lstr); 6562267Sdp (void) write(clifd, outbuf, strlen(outbuf)); 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate /* 6600Sstevel@tonic-gate * Check to see if the client at the other end of the socket is still 6610Sstevel@tonic-gate * alive; we know it is not if it throws EPIPE at us when we try to write 6620Sstevel@tonic-gate * an otherwise harmless 0-length message to it. 6630Sstevel@tonic-gate */ 6640Sstevel@tonic-gate static int 6650Sstevel@tonic-gate test_client(int clifd) 6660Sstevel@tonic-gate { 6670Sstevel@tonic-gate if ((write(clifd, "", 0) == -1) && errno == EPIPE) 6680Sstevel@tonic-gate return (-1); 6690Sstevel@tonic-gate return (0); 6700Sstevel@tonic-gate } 6710Sstevel@tonic-gate 6720Sstevel@tonic-gate /* 6730Sstevel@tonic-gate * This routine drives the console I/O loop. It polls for input from the 6740Sstevel@tonic-gate * master side of the console (output to the console), and from the client 6750Sstevel@tonic-gate * (input from the console user). Additionally, it polls on the server fd, 6760Sstevel@tonic-gate * and disconnects any clients that might try to hook up with the zone while 6770Sstevel@tonic-gate * the console is in use. 6780Sstevel@tonic-gate * 6790Sstevel@tonic-gate * When the client first calls us up, it is expected to send a line giving 6800Sstevel@tonic-gate * its "identity"; this consists of the string 'IDENT <pid> <locale>'. 6810Sstevel@tonic-gate * This is so that we can report that the console is busy along with 6820Sstevel@tonic-gate * some diagnostics about who has it busy; the locale is used so that 6830Sstevel@tonic-gate * asynchronous messages about zone state (like the NOTICE: zone halted 6840Sstevel@tonic-gate * messages) can be output in the user's locale. 6850Sstevel@tonic-gate */ 6860Sstevel@tonic-gate static void 6870Sstevel@tonic-gate do_console_io(zlog_t *zlogp, int consfd, int servfd) 6880Sstevel@tonic-gate { 6890Sstevel@tonic-gate struct pollfd pollfds[4]; 6900Sstevel@tonic-gate char ibuf[BUFSIZ]; 6910Sstevel@tonic-gate int cc, ret; 6920Sstevel@tonic-gate int clifd = -1; 6930Sstevel@tonic-gate int pollerr = 0; 6940Sstevel@tonic-gate char clilocale[MAXPATHLEN]; 6950Sstevel@tonic-gate pid_t clipid = 0; 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate /* console side, watch for read events */ 6980Sstevel@tonic-gate pollfds[0].fd = consfd; 6990Sstevel@tonic-gate pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | 7000Sstevel@tonic-gate POLLPRI | POLLERR | POLLHUP | POLLNVAL; 7010Sstevel@tonic-gate 7020Sstevel@tonic-gate /* client side, watch for read events */ 7030Sstevel@tonic-gate pollfds[1].fd = clifd; 7040Sstevel@tonic-gate pollfds[1].events = pollfds[0].events; 7050Sstevel@tonic-gate 7060Sstevel@tonic-gate /* the server socket; watch for events (new connections) */ 7070Sstevel@tonic-gate pollfds[2].fd = servfd; 7080Sstevel@tonic-gate pollfds[2].events = pollfds[0].events; 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate /* the eventstram; watch for events (e.g.: zone halted) */ 7110Sstevel@tonic-gate pollfds[3].fd = eventstream[1]; 7120Sstevel@tonic-gate pollfds[3].events = pollfds[0].events; 7130Sstevel@tonic-gate 7140Sstevel@tonic-gate for (;;) { 7150Sstevel@tonic-gate pollfds[0].revents = pollfds[1].revents = 0; 7160Sstevel@tonic-gate pollfds[2].revents = pollfds[3].revents = 0; 7170Sstevel@tonic-gate 7180Sstevel@tonic-gate ret = poll(pollfds, 7190Sstevel@tonic-gate sizeof (pollfds) / sizeof (struct pollfd), -1); 7200Sstevel@tonic-gate if (ret == -1 && errno != EINTR) { 7210Sstevel@tonic-gate zerror(zlogp, B_TRUE, "poll failed"); 7220Sstevel@tonic-gate /* we are hosed, close connection */ 7230Sstevel@tonic-gate break; 7240Sstevel@tonic-gate } 7250Sstevel@tonic-gate 7260Sstevel@tonic-gate /* event from console side */ 7270Sstevel@tonic-gate if (pollfds[0].revents) { 7280Sstevel@tonic-gate if (pollfds[0].revents & 7290Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 7300Sstevel@tonic-gate errno = 0; 7310Sstevel@tonic-gate cc = read(consfd, ibuf, BUFSIZ); 7320Sstevel@tonic-gate if (cc <= 0 && (errno != EINTR) && 7330Sstevel@tonic-gate (errno != EAGAIN)) 7340Sstevel@tonic-gate break; 7350Sstevel@tonic-gate /* 7360Sstevel@tonic-gate * Lose I/O if no one is listening 7370Sstevel@tonic-gate */ 7380Sstevel@tonic-gate if (clifd != -1 && cc > 0) 7390Sstevel@tonic-gate (void) write(clifd, ibuf, cc); 7400Sstevel@tonic-gate } else { 7410Sstevel@tonic-gate pollerr = pollfds[0].revents; 7420Sstevel@tonic-gate zerror(zlogp, B_FALSE, 7430Sstevel@tonic-gate "closing connection with (console) " 7440Sstevel@tonic-gate "pollerr %d\n", pollerr); 7450Sstevel@tonic-gate break; 7460Sstevel@tonic-gate } 7470Sstevel@tonic-gate } 7480Sstevel@tonic-gate 7490Sstevel@tonic-gate /* event from client side */ 7500Sstevel@tonic-gate if (pollfds[1].revents) { 7510Sstevel@tonic-gate if (pollfds[1].revents & 7520Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 7530Sstevel@tonic-gate errno = 0; 7540Sstevel@tonic-gate cc = read(clifd, ibuf, BUFSIZ); 7550Sstevel@tonic-gate if (cc <= 0 && (errno != EINTR) && 7560Sstevel@tonic-gate (errno != EAGAIN)) 7570Sstevel@tonic-gate break; 7580Sstevel@tonic-gate (void) write(consfd, ibuf, cc); 7590Sstevel@tonic-gate } else { 7600Sstevel@tonic-gate pollerr = pollfds[1].revents; 7610Sstevel@tonic-gate zerror(zlogp, B_FALSE, 7620Sstevel@tonic-gate "closing connection with (client) " 7630Sstevel@tonic-gate "pollerr %d\n", pollerr); 7640Sstevel@tonic-gate break; 7650Sstevel@tonic-gate } 7660Sstevel@tonic-gate } 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate /* event from server socket */ 7690Sstevel@tonic-gate if (pollfds[2].revents && 7700Sstevel@tonic-gate (pollfds[2].revents & (POLLIN | POLLRDNORM))) { 7710Sstevel@tonic-gate if (clifd != -1) { 7720Sstevel@tonic-gate /* 7730Sstevel@tonic-gate * Test the client to see if it is really 7740Sstevel@tonic-gate * still alive. If it has died but we 7750Sstevel@tonic-gate * haven't yet detected that, we might 7760Sstevel@tonic-gate * deny a legitimate connect attempt. If it 7770Sstevel@tonic-gate * is dead, we break out; once we tear down 7780Sstevel@tonic-gate * the old connection, the new connection 7790Sstevel@tonic-gate * will happen. 7800Sstevel@tonic-gate */ 7810Sstevel@tonic-gate if (test_client(clifd) == -1) { 7820Sstevel@tonic-gate break; 7830Sstevel@tonic-gate } 7840Sstevel@tonic-gate /* we're already handling a client */ 7850Sstevel@tonic-gate reject_client(servfd, clipid); 7860Sstevel@tonic-gate 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate } else if ((clifd = accept_client(servfd, &clipid, 7890Sstevel@tonic-gate clilocale, sizeof (clilocale))) != -1) { 7900Sstevel@tonic-gate pollfds[1].fd = clifd; 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate } else { 7930Sstevel@tonic-gate break; 7940Sstevel@tonic-gate } 7950Sstevel@tonic-gate } 7960Sstevel@tonic-gate 7970Sstevel@tonic-gate /* 7980Sstevel@tonic-gate * Watch for events on the eventstream. This is how we get 7990Sstevel@tonic-gate * notified of the zone halting, etc. It provides us a 8000Sstevel@tonic-gate * "wakeup" from poll when important things happen, which 8010Sstevel@tonic-gate * is good. 8020Sstevel@tonic-gate */ 8030Sstevel@tonic-gate if (pollfds[3].revents) { 8040Sstevel@tonic-gate int evt = eventstream_read(); 8050Sstevel@tonic-gate /* 8060Sstevel@tonic-gate * After we drain out the event, if we aren't servicing 8070Sstevel@tonic-gate * a console client, we hop back out to our caller, 8080Sstevel@tonic-gate * which will check to see if it is time to shutdown 8090Sstevel@tonic-gate * the daemon, or if we should take another console 8100Sstevel@tonic-gate * service lap. 8110Sstevel@tonic-gate */ 8120Sstevel@tonic-gate if (clifd == -1) { 8130Sstevel@tonic-gate break; 8140Sstevel@tonic-gate } 8150Sstevel@tonic-gate event_message(clifd, clilocale, evt); 8160Sstevel@tonic-gate /* 8170Sstevel@tonic-gate * Special handling for the message that the zone is 8180Sstevel@tonic-gate * uninstalling; we boot the client, then break out 8190Sstevel@tonic-gate * of this function. When we return to the 8200Sstevel@tonic-gate * serve_console loop, we will see that the zone is 8210Sstevel@tonic-gate * in a state < READY, and so zoneadmd will shutdown. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate if (evt == Z_EVT_ZONE_UNINSTALLING) { 8240Sstevel@tonic-gate break; 8250Sstevel@tonic-gate } 8260Sstevel@tonic-gate } 8270Sstevel@tonic-gate 8280Sstevel@tonic-gate } 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate if (clifd != -1) { 8310Sstevel@tonic-gate (void) shutdown(clifd, SHUT_RDWR); 8320Sstevel@tonic-gate (void) close(clifd); 8330Sstevel@tonic-gate } 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate int 8370Sstevel@tonic-gate init_console(zlog_t *zlogp) 8380Sstevel@tonic-gate { 8390Sstevel@tonic-gate if (init_console_dev(zlogp) == -1) { 8400Sstevel@tonic-gate zerror(zlogp, B_FALSE, 8410Sstevel@tonic-gate "console setup: device initialization failed"); 8420Sstevel@tonic-gate return (-1); 8430Sstevel@tonic-gate } 8440Sstevel@tonic-gate 8450Sstevel@tonic-gate if ((serverfd = init_console_sock(zlogp)) == -1) { 8460Sstevel@tonic-gate zerror(zlogp, B_FALSE, 8470Sstevel@tonic-gate "console setup: socket initialization failed"); 8480Sstevel@tonic-gate return (-1); 8490Sstevel@tonic-gate } 8500Sstevel@tonic-gate return (0); 8510Sstevel@tonic-gate } 8520Sstevel@tonic-gate 8530Sstevel@tonic-gate /* 8540Sstevel@tonic-gate * serve_console() is the master loop for driving console I/O. It is also the 8550Sstevel@tonic-gate * routine which is ultimately responsible for "pulling the plug" on zoneadmd 8560Sstevel@tonic-gate * when it realizes that the daemon should shut down. 8570Sstevel@tonic-gate * 8580Sstevel@tonic-gate * The rules for shutdown are: there must be no console client, and the zone 8590Sstevel@tonic-gate * state must be < ready. However, we need to give things a chance to actually 8600Sstevel@tonic-gate * get going when the daemon starts up-- otherwise the daemon would immediately 8610Sstevel@tonic-gate * exit on startup if the zone was in the installed state, so we first drop 8620Sstevel@tonic-gate * into the do_console_io() loop in order to give *something* a chance to 8630Sstevel@tonic-gate * happen. 8640Sstevel@tonic-gate */ 8650Sstevel@tonic-gate void 8660Sstevel@tonic-gate serve_console(zlog_t *zlogp) 8670Sstevel@tonic-gate { 8680Sstevel@tonic-gate int masterfd; 8690Sstevel@tonic-gate zone_state_t zstate; 8700Sstevel@tonic-gate char conspath[MAXPATHLEN]; 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate (void) snprintf(conspath, sizeof (conspath), 8730Sstevel@tonic-gate "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); 8740Sstevel@tonic-gate 8750Sstevel@tonic-gate for (;;) { 8760Sstevel@tonic-gate masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY); 8770Sstevel@tonic-gate if (masterfd == -1) { 8780Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to open console master"); 8790Sstevel@tonic-gate (void) mutex_lock(&lock); 8800Sstevel@tonic-gate goto death; 8810Sstevel@tonic-gate } 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate /* 8840Sstevel@tonic-gate * Setting RPROTDIS on the stream means that the control 8850Sstevel@tonic-gate * portion of messages received (which we don't care about) 8860Sstevel@tonic-gate * will be discarded by the stream head. If we allowed such 8870Sstevel@tonic-gate * messages, we wouldn't be able to use read(2), as it fails 8880Sstevel@tonic-gate * (EBADMSG) when a message with a control element is received. 8890Sstevel@tonic-gate */ 8900Sstevel@tonic-gate if (ioctl(masterfd, I_SRDOPT, RNORM|RPROTDIS) == -1) { 8910Sstevel@tonic-gate zerror(zlogp, B_TRUE, "failed to set options on " 8920Sstevel@tonic-gate "console master"); 8930Sstevel@tonic-gate (void) mutex_lock(&lock); 8940Sstevel@tonic-gate goto death; 8950Sstevel@tonic-gate } 8960Sstevel@tonic-gate 8970Sstevel@tonic-gate do_console_io(zlogp, masterfd, serverfd); 8980Sstevel@tonic-gate 8990Sstevel@tonic-gate /* 9000Sstevel@tonic-gate * We would prefer not to do this, but hostile zone processes 9010Sstevel@tonic-gate * can cause the stream to become tainted, and reads will 9020Sstevel@tonic-gate * fail. So, in case something has gone seriously ill, 9030Sstevel@tonic-gate * we dismantle the stream and reopen the console when we 9040Sstevel@tonic-gate * take another lap. 9050Sstevel@tonic-gate */ 9060Sstevel@tonic-gate (void) close(masterfd); 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate (void) mutex_lock(&lock); 9090Sstevel@tonic-gate /* 9100Sstevel@tonic-gate * We need to set death_throes (see below) atomically with 9110Sstevel@tonic-gate * respect to noticing that (a) we have no console client and 9120Sstevel@tonic-gate * (b) the zone is not installed. Otherwise we could get a 9130Sstevel@tonic-gate * request to boot during this time. Once we set death_throes, 9140Sstevel@tonic-gate * any incoming door stuff will be turned away. 9150Sstevel@tonic-gate */ 9160Sstevel@tonic-gate if (zone_get_state(zone_name, &zstate) == Z_OK) { 9170Sstevel@tonic-gate if (zstate < ZONE_STATE_READY) 9180Sstevel@tonic-gate goto death; 9190Sstevel@tonic-gate } else { 9200Sstevel@tonic-gate zerror(zlogp, B_FALSE, 9210Sstevel@tonic-gate "unable to determine state of zone"); 9220Sstevel@tonic-gate goto death; 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate /* 9250Sstevel@tonic-gate * Even if zone_get_state() fails, stay conservative, and 9260Sstevel@tonic-gate * take another lap. 9270Sstevel@tonic-gate */ 9280Sstevel@tonic-gate (void) mutex_unlock(&lock); 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate death: 9320Sstevel@tonic-gate assert(MUTEX_HELD(&lock)); 9330Sstevel@tonic-gate in_death_throes = B_TRUE; 9340Sstevel@tonic-gate (void) mutex_unlock(&lock); 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate destroy_console_sock(serverfd); 9370Sstevel@tonic-gate (void) destroy_console_devs(zlogp); 9380Sstevel@tonic-gate } 939