Print this page
Merge cleanup from previous six commits
OS-2564 zone boot failed: could not start zoneadmd
OS-4166 zlogin to zfd needs TIOCSWINSZ support
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-3075 zone long boot args aren't passed through
        
*** 20,30 ****
   */
  
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
!  * Copyright 2012 Joyent, Inc.  All rights reserved.
   * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
   */
  
  /*
   * Console support for zones requires a significant infrastructure.  The
--- 20,30 ----
   */
  
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
!  * Copyright 2015 Joyent, Inc.
   * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
   */
  
  /*
   * Console support for zones requires a significant infrastructure.  The
*** 116,139 ****
  #define ZCONSNEX_DEVTREEPATH    "/pseudo/zconsnex@1"
  #define ZCONSNEX_FILEPATH       "/devices/pseudo/zconsnex@1"
  
  #define CONSOLE_SOCKPATH        ZONES_TMPDIR "/%s.console_sock"
  
  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
   * door server to the console subsystem, implemented with a pipe.
   * It is used to wake up the console poller when it needs to take action,
   * message the user, die off, etc.
   */
  static int eventstream[2];
  
  
- 
  int
  eventstream_init()
  {
          if (pipe(eventstream) == -1)
                  return (-1);
--- 116,143 ----
  #define ZCONSNEX_DEVTREEPATH    "/pseudo/zconsnex@1"
  #define ZCONSNEX_FILEPATH       "/devices/pseudo/zconsnex@1"
  
  #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];
  
  /*
   * The eventstream is a simple one-directional flow of messages from the
   * door server to the console subsystem, implemented with a pipe.
   * It is used to wake up the console poller when it needs to take action,
   * message the user, die off, etc.
   */
  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()
  {
          if (pipe(eventstream) == -1)
                  return (-1);
*** 406,451 ****
  
          /*
           * 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.
           */
          (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
              zone_name, ZCONS_MASTER_NAME);
          if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
                  zerror(zlogp, B_TRUE, "ERROR: could not open master side of "
                      "zone console for %s to acquire slave handle", zone_name);
!                 goto error;
          }
          (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
              zone_name, ZCONS_SLAVE_NAME);
!         if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -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;
                  }
                  (void) sleep(1);
          }
          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);
  
  error:
          if (ddef_hdl)
                  devctl_ddef_free(ddef_hdl);
--- 410,468 ----
  
          /*
           * 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) {
                  zerror(zlogp, B_TRUE, "ERROR: could not open master side of "
                      "zone console for %s to acquire slave handle", zone_name);
!                 master_zcons_failed = B_TRUE;
          }
          (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
              zone_name, ZCONS_SLAVE_NAME);
!         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);
! 
          /*
           * 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.
           */
!         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);
                  }
                  if (rv != 0)
!                         zerror(zlogp, B_TRUE, "ERROR: error while acquiring "
!                             "slave handle of zone console for %s", zone_name);
!         }
  
+         if (slavefd != -1)
                  (void) close(slavefd);
+         if (masterfd != -1)
                  (void) close(masterfd);
  
  error:
          if (ddef_hdl)
                  devctl_ddef_free(ddef_hdl);
*** 515,524 ****
--- 532,542 ----
  {
          char buf[BUFSIZ], *bufp;
          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) {
                  assert(locale == NULL && locale_len == 0);
                  while (read(clifd, &c, 1) == 1) {
*** 548,569 ****
                  while ((r = read(clifd, &c, sizeof (c))) > 0)
                          if (c == '\n')
                                  break;
          }
  
          /*
           * Parse buffer for message of the form:
!          * IDENT <pid> <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++;
          buflen = strlen(bufp) - 1;
          *disconnect = atoi(&bufp[buflen]);
--- 566,591 ----
                  while ((r = read(clifd, &c, sizeof (c))) > 0)
                          if (c == '\n')
                                  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 <locale> <disconnect flag>
           */
          bufp = buf;
          if (strncmp(bufp, "IDENT ", 6) != 0)
                  return (-1);
          bufp += 6;
          errno = 0;
  
          while (*bufp != '\0' && isspace(*bufp))
                  bufp++;
          buflen = strlen(bufp) - 1;
          *disconnect = atoi(&bufp[buflen]);
*** 665,682 ****
                  if (dflag)
                          str = "NOTICE: Zone boot failed.  Disconnecting...";
                  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;
          }
  
          if (lstr == NULL)
--- 687,696 ----
*** 876,886 ****
  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) {
                  zerror(zlogp, B_FALSE,
                      "console setup: socket initialization failed");
--- 890,899 ----
*** 888,897 ****
--- 901,921 ----
          }
          return (0);
  }
  
  /*
+  * 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.
   *
   * The rules for shutdown are: there must be no console client, and the zone
*** 905,921 ****
--- 929,986 ----
  serve_console(zlog_t *zlogp)
  {
          int masterfd;
          zone_state_t zstate;
          char conspath[MAXPATHLEN];
+         static boolean_t cons_warned = B_FALSE;
  
          (void) snprintf(conspath, sizeof (conspath),
              "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME);
  
          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;
                  }