diff options
Diffstat (limited to 'usr/src/cmd/zoneadmd/zcons.c')
-rw-r--r-- | usr/src/cmd/zoneadmd/zcons.c | 186 |
1 files changed, 136 insertions, 50 deletions
diff --git a/usr/src/cmd/zoneadmd/zcons.c b/usr/src/cmd/zoneadmd/zcons.c index 5f6fc4973c..5f415b0210 100644 --- a/usr/src/cmd/zoneadmd/zcons.c +++ b/usr/src/cmd/zoneadmd/zcons.c @@ -22,7 +22,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2012 Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ @@ -40,10 +40,10 @@ * * Global Zone | Non-Global Zone * .--------------. | - * .-----------. | zoneadmd -z | | .--------. .---------. - * | zlogin -C | | myzone | | | ttymon | | syslogd | - * `-----------' `--------------' | `--------' `---------' - * | | | | | | | + * .-----------. | zoneadmd -z |--. | .--------. .---------. + * | zlogin -C | | myzone | | | | ttymon | | syslogd | + * `-----------' `--------------' V | `--------' `---------' + * | | | | console.log | | | * User | | | | | V V * - - - - - - - - -|- - - -|- - - -|-|- - - - - - -|- - /dev/zconsole - - - * Kernel V V | | | @@ -81,6 +81,8 @@ * functions as a two-way proxy for console I/O, relaying user input * to the master side of the console, and relaying output from the * zone to the user. + * + * - Logging output to <zonepath>/logs/console.log. */ #include <sys/types.h> @@ -118,9 +120,10 @@ #define CONSOLE_SOCKPATH ZONES_TMPDIR "/%s.console_sock" +#define ZCONS_RETRY 10 + static int serverfd = -1; /* console server unix domain socket fd */ char boot_args[BOOTARGS_MAX]; -char bad_boot_arg[BOOTARGS_MAX]; /* * The eventstream is a simple one-directional flow of messages from the @@ -130,7 +133,10 @@ char bad_boot_arg[BOOTARGS_MAX]; */ static int eventstream[2]; - +/* flag used to cope with race creating master zcons devlink */ +static boolean_t master_zcons_failed = B_FALSE; +/* flag to track if we've seen a state change when there is no master zcons */ +static boolean_t state_changed = B_FALSE; int eventstream_init() @@ -322,7 +328,7 @@ destroy_console_devs(zlog_t *zlogp) * interfaces to instantiate a new zone console node. We do a lot of * sanity checking, and are careful to reuse a console if one exists. * - * Once the device is in the device tree, we kick devfsadm via di_init_devs() + * Once the device is in the device tree, we kick devfsadm via di_devlink_init() * to ensure that the appropriate symlinks (to the master and slave console * devices) are placed in /dev in the global zone. */ @@ -408,43 +414,63 @@ devlinks: * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl, * which will cause the master to retain a reference to the slave. * This prevents ttymon from blowing through the slave's STREAMS anchor. + * + * In very rare cases the open returns ENOENT if devfs doesn't have + * everything setup yet due to heavy zone startup load. Wait for + * 1 sec. and retry a few times. Even if we can't setup the zone's + * console, we still go ahead and boot the zone. */ (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); - if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + masterfd = open(conspath, O_RDWR | O_NOCTTY); + if (masterfd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (masterfd == -1) { zerror(zlogp, B_TRUE, "ERROR: could not open master side of " "zone console for %s to acquire slave handle", zone_name); - goto error; + master_zcons_failed = B_TRUE; } + (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_SLAVE_NAME); - if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + slavefd = open(conspath, O_RDWR | O_NOCTTY); + if (slavefd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (slavefd == -1) zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone" " console for %s to acquire slave handle", zone_name); - (void) close(masterfd); - goto error; - } + /* * This ioctl can occasionally return ENXIO if devfs doesn't have * everything plumbed up yet due to heavy zone startup load. Wait for * 1 sec. and retry a few times before we fail to boot the zone. */ - for (i = 0; i < 5; i++) { - if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd) - == 0) { - rv = 0; - break; - } else if (errno != ENXIO) { - break; + if (masterfd != -1 && slavefd != -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + if (ioctl(masterfd, ZC_HOLDSLAVE, + (caddr_t)(intptr_t)slavefd) == 0) { + rv = 0; + break; + } else if (errno != ENXIO) { + break; + } + (void) sleep(1); } - (void) sleep(1); + if (rv != 0) + zerror(zlogp, B_TRUE, "ERROR: error while acquiring " + "slave handle of zone console for %s", zone_name); } - if (rv != 0) - zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave " - "handle of zone console for %s", zone_name); - (void) close(slavefd); - (void) close(masterfd); + if (slavefd != -1) + (void) close(slavefd); + if (masterfd != -1) + (void) close(masterfd); error: if (ddef_hdl) @@ -517,6 +543,7 @@ get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, size_t buflen = sizeof (buf); char c = '\0'; int i = 0, r; + ucred_t *cred = NULL; /* "eat up the ident string" case, for simplicity */ if (pid == NULL) { @@ -550,18 +577,22 @@ get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, break; } + if (getpeerucred(clifd, &cred) == 0) { + *pid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } else { + return (-1); + } + /* * Parse buffer for message of the form: - * IDENT <pid> <locale> <disconnect flag> + * IDENT <locale> <disconnect flag> */ bufp = buf; if (strncmp(bufp, "IDENT ", 6) != 0) return (-1); bufp += 6; errno = 0; - *pid = strtoll(bufp, &bufp, 10); - if (errno != 0) - return (-1); while (*bufp != '\0' && isspace(*bufp)) bufp++; @@ -667,14 +698,6 @@ event_message(int clifd, char *clilocale, zone_evt_t evt, int dflag) else str = "NOTICE: Zone boot failed"; break; - case Z_EVT_ZONE_BADARGS: - /*LINTED*/ - (void) snprintf(lmsg, sizeof (lmsg), - localize_msg(clilocale, - "WARNING: Ignoring invalid boot arguments: %s"), - bad_boot_arg); - lstr = lmsg; - break; default: return; } @@ -713,7 +736,7 @@ test_client(int clifd) * messages) can be output in the user's locale. */ static void -do_console_io(zlog_t *zlogp, int consfd, int servfd) +do_console_io(zlog_t *zlogp, int consfd, int servfd, int conslog) { struct pollfd pollfds[4]; char ibuf[BUFSIZ]; @@ -759,14 +782,21 @@ do_console_io(zlog_t *zlogp, int consfd, int servfd) (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { errno = 0; cc = read(consfd, ibuf, BUFSIZ); - if (cc <= 0 && (errno != EINTR) && - (errno != EAGAIN)) - break; - /* - * Lose I/O if no one is listening - */ - if (clifd != -1 && cc > 0) - (void) write(clifd, ibuf, cc); + if (cc <= 0) { + if (errno != EINTR && + errno != EAGAIN) { + break; + } + } else { + logstream_write(conslog, ibuf, cc); + + /* + * Lose I/O if no one is listening + */ + if (clifd != -1) { + (void) write(clifd, ibuf, cc); + } + } } else { pollerr = pollfds[0].revents; zerror(zlogp, B_FALSE, @@ -878,7 +908,6 @@ init_console(zlog_t *zlogp) if (init_console_dev(zlogp) == -1) { zerror(zlogp, B_FALSE, "console setup: device initialization failed"); - return (-1); } if ((serverfd = init_console_sock(zlogp)) == -1) { @@ -890,6 +919,17 @@ init_console(zlog_t *zlogp) } /* + * Maintain a simple flag that tracks if we have seen at least one state + * change. This is currently only used to handle the special case where we are + * running without a console device, which is what normally drives shutdown. + */ +void +zcons_statechanged() +{ + state_changed = B_TRUE; +} + +/* * serve_console() is the master loop for driving console I/O. It is also the * routine which is ultimately responsible for "pulling the plug" on zoneadmd * when it realizes that the daemon should shut down. @@ -907,6 +947,10 @@ serve_console(zlog_t *zlogp) int masterfd; zone_state_t zstate; char conspath[MAXPATHLEN]; + static boolean_t cons_warned = B_FALSE; + int conslog; + + conslog = logstream_open("console.log", "console", LS_LINE_BUFFERED); (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); @@ -914,6 +958,46 @@ serve_console(zlog_t *zlogp) for (;;) { masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY); if (masterfd == -1) { + if (master_zcons_failed) { + /* + * If we don't have a console and the zone is + * not shutting down, there may have been a + * race/failure with devfs while creating the + * console. In this case we want to leave the + * zone up, even without a console, so + * periodically recheck. + */ + int i; + + /* + * In the normal flow of this loop, we use + * do_console_io to give things a chance to get + * going first. However, in this case we can't + * use that, so we have to wait for at least + * one state change before checking the state. + */ + for (i = 0; i < 60; i++) { + if (state_changed) + break; + (void) sleep(1); + } + + if (i < 60 && zone_get_state(zone_name, + &zstate) == Z_OK && + (zstate == ZONE_STATE_READY || + zstate == ZONE_STATE_RUNNING)) { + if (!cons_warned) { + zerror(zlogp, B_FALSE, + "WARNING: missing zone " + "console for %s", + zone_name); + cons_warned = B_TRUE; + } + (void) sleep(ZCONS_RETRY); + continue; + } + } + zerror(zlogp, B_TRUE, "failed to open console master"); (void) mutex_lock(&lock); goto death; @@ -933,7 +1017,7 @@ serve_console(zlog_t *zlogp) goto death; } - do_console_io(zlogp, masterfd, serverfd); + do_console_io(zlogp, masterfd, serverfd, conslog); /* * We would prefer not to do this, but hostile zone processes @@ -974,4 +1058,6 @@ death: destroy_console_sock(serverfd); (void) destroy_console_devs(zlogp); + + logstream_close(conslog, B_FALSE); } |