diff options
| author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2015-04-08 16:01:25 +0000 |
|---|---|---|
| committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2015-04-08 16:01:25 +0000 |
| commit | a72f8b51ab96881c2bb6022f67bb3d835b6a5642 (patch) | |
| tree | a83b38e3b34a112ec7ed4bf8a679dfc2b9ee2f9a | |
| parent | 910e97a0dff98308e6a74265d3e2bbec4e75e6a7 (diff) | |
| download | illumos-joyent-a72f8b51ab96881c2bb6022f67bb3d835b6a5642.tar.gz | |
OS-4108 'zlogin -I' should have some way to know that /dev/zfd/0 is being read inside the zone
| -rw-r--r-- | usr/src/cmd/zoneadmd/zfd.c | 114 | ||||
| -rw-r--r-- | usr/src/uts/common/io/zfd.c | 15 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/zfd.h | 5 |
3 files changed, 111 insertions, 23 deletions
diff --git a/usr/src/cmd/zoneadmd/zfd.c b/usr/src/cmd/zoneadmd/zfd.c index a08eb3f0d1..ccf2c8e2c9 100644 --- a/usr/src/cmd/zoneadmd/zfd.c +++ b/usr/src/cmd/zoneadmd/zfd.c @@ -680,6 +680,26 @@ wr_log_msg(char *buf, int len, int from) } /* + * We want to sleep for a little while but need to be responsive if the zone is + * halting. We poll/sleep on the event stream so we can notice if we're halting. + * Return true if halting, otherwise false. + */ +static boolean_t +halt_sleep(int slptime) +{ + struct pollfd evfd[1]; + + evfd[0].fd = eventstream[1]; + evfd[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + if (poll(evfd, 1, slptime) > 0) { + /* zone halting */ + return (B_TRUE); + } + return (B_FALSE); +} + +/* * This routine drives the logging and interactive I/O loop. It polls for * input from the zone side of the fd (output to stdout/stderr), and from the * client (input to the zone's stdin). Additionally, it polls on the server @@ -697,9 +717,18 @@ wr_log_msg(char *buf, int len, int from) * * We need to handle the case where there is no server within the zone (or * the server gets stuck) and data that we're writing to the zone server's - * stdin fills the pipe. Because open_fd() always opens non-blocking our - * writes could return -1 with EAGAIN. Since we ignore errors on the write - * to stdin, we won't get blocked. + * stdin fills the pipe. Because of the way the zfd device works writes can + * flow into the stream and simply be dropped, if there is no server, or writes + * could return -1 with EAGAIN if the server is stuck. Since we ignore errors + * on the write to stdin, we won't get blocked in that case but we'd like to + * avoid dropping initial input if the server within the zone hasn't started + * yet. To handle this we wait to read initial input until we detect that there + * is a server inside the zone. We have to poll for this so that we can + * re-run the ioctl to notice when a server shows up. This poll/wait is handled + * by halt_sleep() so that we can be responsive if the zone wants to halt. + * We only do this check to avoid dropping initial input so it is possible for + * the server within the zone to go away later. At that point zfd will just + * drop any new input flowing into the stream. */ static void do_zfd_io(int gzservfd, int gzerrfd, int stdinfd, int stdoutfd, int stderrfd) @@ -713,6 +742,8 @@ do_zfd_io(int gzservfd, int gzerrfd, int stdinfd, int stdoutfd, int stderrfd) char clilocale[MAXPATHLEN]; pid_t clipid = 0; uint_t flags = 0; + boolean_t stdin_ready = B_FALSE; + int slptime = 250; /* initial poll sleep time in ms */ /* client, watch for read events */ pollfds[0].fd = clifd; @@ -753,30 +784,67 @@ do_zfd_io(int gzservfd, int gzerrfd, int stdinfd, int stdoutfd, int stderrfd) /* event from client side */ if (pollfds[0].revents) { - if (pollfds[0].revents & POLLHUP && - flags & ZLOGIN_ZFD_EOF) { - /* Let the client know */ - (void) ioctl(stdinfd, ZFD_EOF); - } + if (stdin_ready) { + if (pollfds[0].revents & (POLLIN | + POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(clifd, ibuf, BUFSIZ); + if (cc > 0) { + /* + * See comment for this + * function on what happens if + * there is no reader in the + * zone. EOF is handled below. + */ + (void) write(stdinfd, ibuf, cc); + } + } else if (pollfds[0].revents & (POLLERR | + POLLNVAL)) { + pollerr = pollfds[0].revents; + zerror(zlogp, B_FALSE, + "closing connection " + "with client, pollerr %d\n", + pollerr); + break; + } - if (pollfds[0].revents & - (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - errno = 0; - cc = read(clifd, ibuf, BUFSIZ); - if (cc <= 0 && (errno != EINTR) && - (errno != EAGAIN)) { + if (pollfds[0].revents & POLLHUP) { + if (flags & ZLOGIN_ZFD_EOF) { + /* + * Let the client know. We've + * already serviced any pending + * regular input. Let the + * stream clear since the EOF + * ioctl jumps to the head. + */ + (void) ioctl(stdinfd, I_FLUSH); + if (halt_sleep(250)) + break; + (void) ioctl(stdinfd, ZFD_EOF); + } break; } - /* - * See comment for this function on what - * happens if there is no reader in the zone. - */ - (void) write(stdinfd, ibuf, cc); } else { - pollerr = pollfds[0].revents; - zerror(zlogp, B_FALSE, "closing connection " - "with client, pollerr %d\n", pollerr); - break; + if (ioctl(stdinfd, ZFD_HAS_SLAVE) == 0) { + stdin_ready = B_TRUE; + } else { + /* + * There is nothing in the zone to read + * our input. Presumably the user + * providing input expects something to + * show up, but that is no guarantee. + * Since we haven't serviced the pending + * input poll yet, we don't want to + * immediately loop around but we also + * need to be responsive if the zone is + * halting. + */ + if (halt_sleep(slptime)) + break; + + if (slptime < 5000) + slptime += 250; + } } } diff --git a/usr/src/uts/common/io/zfd.c b/usr/src/uts/common/io/zfd.c index 0b52a9179d..3edaa62bbe 100644 --- a/usr/src/uts/common/io/zfd.c +++ b/usr/src/uts/common/io/zfd.c @@ -694,6 +694,21 @@ zfd_wput(queue_t *qp, mblk_t *mp) M_HANGUP); miocack(qp, mp, 0, 0); return; + case ZFD_HAS_SLAVE: + /* + * The process that passed the ioctl must be running in + * the global zone. + */ + if (crgetzoneid(iocbp->ioc_cr) != GLOBAL_ZONEID) { + miocack(qp, mp, 0, EINVAL); + return; + } + if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) { + miocack(qp, mp, 0, 0); + } else { + miocack(qp, mp, 0, ENOTTY); + } + return; default: break; } diff --git a/usr/src/uts/common/sys/zfd.h b/usr/src/uts/common/sys/zfd.h index 21c52460f2..acf9ce8d33 100644 --- a/usr/src/uts/common/sys/zfd.h +++ b/usr/src/uts/common/sys/zfd.h @@ -51,6 +51,11 @@ extern "C" { */ #define ZFD_EOF (ZFD_IOC | 1) +/* + * This ioctl succeeds if the slave side is open. + */ +#define ZFD_HAS_SLAVE (ZFD_IOC | 2) + #ifdef __cplusplus } #endif |
