Print this page
Reduce lint
OS-5388 zoneadmd comes up asynchronously so zlogin can happen too soon
Reviewed by: Josh Wilsdon <jwilsdon@joyent.com>
OS-5198 zlogin must handle args when su binary is missing
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-5185 zlogin fails to execute command when 'su' is missing
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-4863 regression in zlogin non-tty mode handling [tighten parsing]
OS-4863 regression in zlogin non-tty mode handling
Reviewed by: Josh Wilsdon <jwilsdon@joyent.com>
OS-4792 zlogin errors when trying to attach to a container using log-driver
OS-4569 zlogin doesn't properly quote arguments
OS-4577 regression in zlogin argument handling [backout OS-4569]
OS-4569 zlogin doesn't properly quote arguments
OS-4136 would like SIGUSR to zlogin to switch in and out of '-N' modes
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-4166 zlogin to zfd needs TIOCSWINSZ support
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-4109 'zlogin -I' should close /dev/zfd/0 when it exits
OS-3876 custr could allow management of a static buffer
OS-3879 want custr to have a printf function
Reviewed by: Joshua M. Clulow <josh@sysmgr.org>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-3869 'zlogin -Q -i <zonename> <command>' does not return exit status of <command>
OS-3777 zlogin -I needs to work with docker run when in logging mode
OS-3764 'zlogin -i <zonename> /native/usr/vm/sbin/dockerexec /bin/sh' merges argv[] into single argument
OS-3741 zlogin -I has strange echo behavior
OS-3727 Would like to be able to selectively close descriptors when using zlogin -I
OS-3728 zlogin -I and zoneadmd zfd logging can be streamlined
OS-3718 non-interactive zlogin fails to open /dev/tty, tcgetpgrp() on stderr fails
OS-3524 in order to support interaction with docker containers, need to be able to connect to stdio for init from GZ
OS-3525 in order to support 'docker logs' need to be able to get stdio from zone to log file
OS-3529 would like zlogin -i
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-3388 'zlogin <zonename> <command>' does not work for LX branded zones
OS-2834 ship lx brand

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/zlogin/zlogin.c
          +++ new/usr/src/cmd/zlogin/zlogin.c
↓ open down ↓ 15 lines elided ↑ open up ↑
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  23   23   * Copyright 2013 DEY Storage Systems, Inc.
  24   24   * Copyright (c) 2014 Gary Mills
  25   25   * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
       26 + * Copyright 2016 Joyent, Inc.
  26   27   */
  27   28  
  28   29  /*
  29      - * zlogin provides three types of login which allow users in the global
       30 + * zlogin provides five types of login which allow users in the global
  30   31   * zone to access non-global zones.
  31   32   *
  32   33   * - "interactive login" is similar to rlogin(1); for example, the user could
  33   34   *   issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'.   The user is
  34   35   *   granted a new pty (which is then shoved into the zone), and an I/O
  35   36   *   loop between parent and child processes takes care of the interactive
  36   37   *   session.  In this mode, login(1) (and its -c option, which means
  37   38   *   "already authenticated") is employed to take care of the initialization
  38   39   *   of the user's session.
  39   40   *
  40   41   * - "non-interactive login" is similar to su(1M); the user could issue
  41   42   *   'zlogin my-zone ls -l' and the command would be run as specified.
  42   43   *   In this mode, zlogin sets up pipes as the communication channel, and
  43   44   *   'su' is used to do the login setup work.
  44   45   *
       46 + * - "interactive command" is a combination of the above two modes where
       47 + *   a command is provide like the non-interactive case, but the -i option is
       48 + *   also provided to make things interactive. For example, the user could
       49 + *   issue 'zlogin -i my-zone /bin/sh'. In this mode neither 'login -c' nor
       50 + *   'su root -c' is prepended to the command invocation. Because of this
       51 + *   there will be no wtmpx login record within the zone.
       52 + *
  45   53   * - "console login" is the equivalent to accessing the tip line for a
  46   54   *   zone.  For example, the user can issue 'zlogin -C my-zone'.
  47   55   *   In this mode, zlogin contacts the zoneadmd process via unix domain
  48   56   *   socket.  If zoneadmd is not running, it starts it.  This allows the
  49   57   *   console to be available anytime the zone is installed, regardless of
  50   58   *   whether it is running.
       59 + *
       60 + * - "standalone-processs interactive" is specified with -I and connects to
       61 + *   the zone's stdin, stdout and stderr zfd(7D) devices.
  51   62   */
  52   63  
  53   64  #include <sys/socket.h>
  54   65  #include <sys/termios.h>
  55   66  #include <sys/utsname.h>
  56   67  #include <sys/stat.h>
  57   68  #include <sys/types.h>
  58   69  #include <sys/contract/process.h>
  59   70  #include <sys/ctfs.h>
  60   71  #include <sys/brand.h>
↓ open down ↓ 24 lines elided ↑ open up ↑
  85   96  #include <libdevinfo.h>
  86   97  #include <libintl.h>
  87   98  #include <locale.h>
  88   99  #include <libzonecfg.h>
  89  100  #include <libcontract.h>
  90  101  #include <libbrand.h>
  91  102  #include <auth_list.h>
  92  103  #include <auth_attr.h>
  93  104  #include <secdb.h>
  94  105  
  95      -static int masterfd;
      106 +static int masterfd = -1;
      107 +static int ctlfd = -1;
  96  108  static struct termios save_termios;
  97  109  static struct termios effective_termios;
  98  110  static int save_fd;
  99  111  static struct winsize winsize;
 100  112  static volatile int dead;
 101  113  static volatile pid_t child_pid = -1;
 102  114  static int interactive = 0;
 103  115  static priv_set_t *dropprivs;
      116 +static unsigned int connect_flags = 0;
 104  117  
 105  118  static int nocmdchar = 0;
 106  119  static int failsafe = 0;
 107      -static int disconnect = 0;
 108  120  static char cmdchar = '~';
 109  121  static int quiet = 0;
      122 +static char zonebrand[MAXNAMELEN];
 110  123  
 111  124  static int pollerr = 0;
 112  125  
 113  126  static const char *pname;
 114  127  static char *username;
 115  128  
 116  129  /*
 117  130   * When forced_login is true, the user is not prompted
 118  131   * for an authentication password in the target zone.
 119  132   */
 120  133  static boolean_t forced_login = B_FALSE;
 121  134  
 122  135  #if !defined(TEXT_DOMAIN)               /* should be defined by cc -D */
 123  136  #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it wasn't */
 124  137  #endif
 125  138  
 126      -#define SUPATH  "/usr/bin/su"
      139 +#define SUPATH1 "/usr/bin/su"
      140 +#define SUPATH2 "/bin/su"
 127  141  #define FAILSAFESHELL   "/sbin/sh"
 128  142  #define DEFAULTSHELL    "/sbin/sh"
 129  143  #define DEF_PATH        "/usr/sbin:/usr/bin"
      144 +#define LX_DEF_PATH     "/bin:/usr/sbin:/usr/bin"
 130  145  
      146 +#define MAX_RETRY       30
      147 +
 131  148  #define CLUSTER_BRAND_NAME      "cluster"
 132  149  
 133  150  /*
 134  151   * The ZLOGIN_BUFSIZ is larger than PIPE_BUF so we can be sure we're clearing
 135  152   * out the pipe when the child is exiting.  The ZLOGIN_RDBUFSIZ must be less
 136  153   * than ZLOGIN_BUFSIZ (because we share the buffer in doio).  This value is
 137  154   * also chosen in conjunction with the HI_WATER setting to make sure we
 138  155   * don't fill up the pipe.  We can write FIFOHIWAT (16k) into the pipe before
 139  156   * blocking.  By having ZLOGIN_RDBUFSIZ set to 1k and HI_WATER set to 8k, we
 140  157   * know we can always write a ZLOGIN_RDBUFSIZ chunk into the pipe when there
↓ open down ↓ 5 lines elided ↑ open up ↑
 146  163  
 147  164  /*
 148  165   * See canonify() below.  CANONIFY_LEN is the maximum length that a
 149  166   * "canonical" sequence will expand to (backslash, three octal digits, NUL).
 150  167   */
 151  168  #define CANONIFY_LEN 5
 152  169  
 153  170  static void
 154  171  usage(void)
 155  172  {
 156      -        (void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] "
      173 +        (void) fprintf(stderr, gettext("usage: %s [-dinCEINQS] [-e cmdchar] "
 157  174              "[-l user] zonename [command [args ...] ]\n"), pname);
 158  175          exit(2);
 159  176  }
 160  177  
 161  178  static const char *
 162  179  getpname(const char *arg0)
 163  180  {
 164  181          const char *p = strrchr(arg0, '/');
 165  182  
 166  183          if (p == NULL)
↓ open down ↓ 74 lines elided ↑ open up ↑
 241  258          }
 242  259          if ((setppriv(PRIV_SET, PRIV_LIMIT, dropprivs)) == -1) {
 243  260                  zperror(gettext("Warning: could not set limit privileges"));
 244  261          }
 245  262          if ((setppriv(PRIV_SET, PRIV_INHERITABLE, dropprivs)) == -1) {
 246  263                  zperror(gettext("Warning: could not set inheritable "
 247  264                      "privileges"));
 248  265          }
 249  266  }
 250  267  
 251      -/*
 252      - * Create the unix domain socket and call the zoneadmd server; handshake
 253      - * with it to determine whether it will allow us to connect.
 254      - */
 255  268  static int
 256      -get_console_master(const char *zname)
      269 +connect_zone_sock(const char *zname, const char *suffix, boolean_t verbose)
 257  270  {
 258  271          int sockfd = -1;
 259  272          struct sockaddr_un servaddr;
 260      -        char clientid[MAXPATHLEN];
 261      -        char handshake[MAXPATHLEN], c;
 262      -        int msglen;
 263      -        int i = 0, err = 0;
 264  273  
 265  274          if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
 266      -                zperror(gettext("could not create socket"));
      275 +                if (verbose)
      276 +                        zperror(gettext("could not create socket"));
 267  277                  return (-1);
 268  278          }
 269  279  
 270  280          bzero(&servaddr, sizeof (servaddr));
 271  281          servaddr.sun_family = AF_UNIX;
 272  282          (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path),
 273      -            "%s/%s.console_sock", ZONES_TMPDIR, zname);
 274      -
      283 +            "%s/%s.%s", ZONES_TMPDIR, zname, suffix);
 275  284          if (connect(sockfd, (struct sockaddr *)&servaddr,
 276  285              sizeof (servaddr)) == -1) {
 277      -                zperror(gettext("Could not connect to zone console"));
 278      -                goto bad;
      286 +                if (verbose)
      287 +                        zperror(gettext("Could not connect to zone"));
      288 +                (void) close(sockfd);
      289 +                return (-1);
 279  290          }
 280      -        masterfd = sockfd;
      291 +        return (sockfd);
      292 +}
 281  293  
 282      -        msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n",
 283      -            getpid(), setlocale(LC_MESSAGES, NULL), disconnect);
 284  294  
      295 +static int
      296 +handshake_zone_sock(int sockfd, unsigned int flags)
      297 +{
      298 +        char clientid[MAXPATHLEN];
      299 +        char handshake[MAXPATHLEN], c;
      300 +        int msglen;
      301 +        int i = 0, err = 0;
      302 +
      303 +        msglen = snprintf(clientid, sizeof (clientid), "IDENT %s %u\n",
      304 +            setlocale(LC_MESSAGES, NULL), flags);
      305 +
 285  306          if (msglen >= sizeof (clientid) || msglen < 0) {
 286  307                  zerror("protocol error");
 287      -                goto bad;
      308 +                return (-1);
 288  309          }
 289  310  
 290      -        if (write(masterfd, clientid, msglen) != msglen) {
      311 +        if (write(sockfd, clientid, msglen) != msglen) {
 291  312                  zerror("protocol error");
 292      -                goto bad;
      313 +                return (-1);
 293  314          }
 294  315  
 295      -        bzero(handshake, sizeof (handshake));
 296      -
 297  316          /*
 298  317           * Take care not to accumulate more than our fill, and leave room for
 299  318           * the NUL at the end.
 300  319           */
 301      -        while ((err = read(masterfd, &c, 1)) == 1) {
      320 +        bzero(handshake, sizeof (handshake));
      321 +        while ((err = read(sockfd, &c, 1)) == 1) {
 302  322                  if (i >= (sizeof (handshake) - 1))
 303  323                          break;
 304  324                  if (c == '\n')
 305  325                          break;
 306  326                  handshake[i] = c;
 307  327                  i++;
 308  328          }
 309  329  
 310  330          /*
 311      -         * If something went wrong during the handshake we bail; perhaps
 312      -         * the server died off.
      331 +         * If something went wrong during the handshake we bail.
      332 +         * Perhaps the server died off.
 313  333           */
 314  334          if (err == -1) {
 315      -                zperror(gettext("Could not connect to zone console"));
 316      -                goto bad;
      335 +                zperror(gettext("Could not connect to zone"));
      336 +                return (-1);
 317  337          }
 318  338  
 319      -        if (strncmp(handshake, "OK", sizeof (handshake)) == 0)
 320      -                return (0);
      339 +        if (strncmp(handshake, "OK", sizeof (handshake)) != 0) {
      340 +                zerror(gettext("Zone is already in use by process ID %s."),
      341 +                    handshake);
      342 +                return (-1);
      343 +        }
 321  344  
 322      -        zerror(gettext("Console is already in use by process ID %s."),
 323      -            handshake);
 324      -bad:
 325      -        (void) close(sockfd);
 326      -        masterfd = -1;
 327      -        return (-1);
      345 +        return (0);
 328  346  }
 329  347  
 330      -
      348 +static int
      349 +send_ctl_sock(const char *buf, size_t len)
      350 +{
      351 +        char rbuf[BUFSIZ];
      352 +        int i;
      353 +        if (ctlfd == -1) {
      354 +                return (-1);
      355 +        }
      356 +        if (write(ctlfd, buf, len) != len) {
      357 +                return (-1);
      358 +        }
      359 +        /* read the response */
      360 +        for (i = 0; i < (BUFSIZ - 1); i++) {
      361 +                char c;
      362 +                if (read(ctlfd, &c, 1) != 1 || c == '\n' || c == '\0') {
      363 +                        break;
      364 +                }
      365 +                rbuf[i] = c;
      366 +        }
      367 +        rbuf[i+1] = '\0';
      368 +        if (strncmp("OK", rbuf, BUFSIZ) != 0) {
      369 +                return (-1);
      370 +        }
      371 +        return (0);
      372 +}
 331  373  /*
 332  374   * Routines to handle pty creation upon zone entry and to shuttle I/O back
 333  375   * and forth between the two terminals.  We also compute and store the
 334  376   * name of the slave terminal associated with the master side.
 335  377   */
 336  378  static int
 337  379  get_master_pty()
 338  380  {
 339  381          if ((masterfd = open("/dev/ptmx", O_RDWR|O_NONBLOCK)) < 0) {
 340  382                  zperror(gettext("failed to obtain a pseudo-tty"));
↓ open down ↓ 168 lines elided ↑ open up ↑
 509  551  
 510  552  /*
 511  553   * Copy terminal window size from our terminal to the pts.
 512  554   */
 513  555  /*ARGSUSED*/
 514  556  static void
 515  557  sigwinch(int s)
 516  558  {
 517  559          struct winsize ws;
 518  560  
 519      -        if (ioctl(0, TIOCGWINSZ, &ws) == 0)
 520      -                (void) ioctl(masterfd, TIOCSWINSZ, &ws);
      561 +        if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
      562 +                if (ctlfd != -1) {
      563 +                        char buf[BUFSIZ];
      564 +                        (void) snprintf(buf, sizeof (buf),
      565 +                            "TIOCSWINSZ %hu %hu\n", ws.ws_row, ws.ws_col);
      566 +                        (void) send_ctl_sock(buf, strlen(buf));
      567 +                } else {
      568 +                        (void) ioctl(masterfd, TIOCSWINSZ, &ws);
      569 +                }
      570 +        }
 521  571  }
 522  572  
      573 +/*
      574 + * Toggle zfd EOF mode and notify zoneadmd
      575 + */
      576 +/*ARGSUSED*/
      577 +static void
      578 +sigusr1(int s)
      579 +{
      580 +        connect_flags ^= ZLOGIN_ZFD_EOF;
      581 +        if (ctlfd != -1) {
      582 +                char buf[BUFSIZ];
      583 +                (void) snprintf(buf, sizeof (buf), "SETFLAGS %u\n",
      584 +                    connect_flags);
      585 +                (void) send_ctl_sock(buf, strlen(buf));
      586 +        }
      587 +}
      588 +
 523  589  static volatile int close_on_sig = -1;
 524  590  
 525  591  static void
 526  592  /*ARGSUSED*/
 527  593  sigcld(int s)
 528  594  {
 529  595          int status;
 530  596          pid_t pid;
 531  597  
 532  598          /*
↓ open down ↓ 322 lines elided ↑ open up ↑
 855  921  
 856  922                  if (ret == -1 && errno != EINTR) {
 857  923                          perror("poll failed");
 858  924                          break;
 859  925                  }
 860  926  
 861  927                  if (errno == EINTR && dead) {
 862  928                          break;
 863  929                  }
 864  930  
 865      -                /* event from master side stdout */
 866      -                if (pollfds[0].revents) {
 867      -                        if (pollfds[0].revents &
      931 +                /* event from master side stderr */
      932 +                if (pollfds[1].revents) {
      933 +                        if (pollfds[1].revents &
 868  934                              (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
 869      -                                if (process_output(stdout_fd, STDOUT_FILENO)
      935 +                                if (process_output(stderr_fd, STDERR_FILENO)
 870  936                                      != 0)
 871  937                                          break;
 872  938                          } else {
 873      -                                pollerr = pollfds[0].revents;
      939 +                                pollerr = pollfds[1].revents;
 874  940                                  break;
 875  941                          }
 876  942                  }
 877  943  
 878      -                /* event from master side stderr */
 879      -                if (pollfds[1].revents) {
 880      -                        if (pollfds[1].revents &
      944 +                /* event from master side stdout */
      945 +                if (pollfds[0].revents) {
      946 +                        if (pollfds[0].revents &
 881  947                              (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
 882      -                                if (process_output(stderr_fd, STDERR_FILENO)
      948 +                                if (process_output(stdout_fd, STDOUT_FILENO)
 883  949                                      != 0)
 884  950                                          break;
 885  951                          } else {
 886      -                                pollerr = pollfds[1].revents;
      952 +                                pollerr = pollfds[0].revents;
 887  953                                  break;
 888  954                          }
 889  955                  }
 890  956  
 891  957                  /* event from user STDIN side */
 892  958                  if (pollfds[2].revents) {
 893  959                          if (pollfds[2].revents &
 894  960                              (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
 895  961                                  /*
 896  962                                   * stdin fd is stdin of the target; so,
↓ open down ↓ 149 lines elided ↑ open up ↑
1046 1112                          return (NULL);
1047 1113          }
1048 1114  
1049 1115          /*
1050 1116           * We got back a string that we'd like to execute.  But since
1051 1117           * we're not doing the execution via a shell we'll need to convert
1052 1118           * the exec string to an array of strings.  We'll do that here
1053 1119           * but we're going to be very simplistic about it and break stuff
1054 1120           * up based on spaces.  We're not even going to support any kind
1055 1121           * of quoting or escape characters.  It's truly amazing that
1056      -         * there is no library function in OpenSolaris to do this for us.
     1122 +         * there is no library function in Illumos to do this for us.
1057 1123           */
1058 1124  
1059 1125          /*
1060 1126           * Be paranoid.  Since we're deliniating based on spaces make
1061 1127           * sure there are no adjacent spaces.
1062 1128           */
1063 1129          if (strstr(result_buf, "  ") != NULL)
1064 1130                  return (NULL);
1065 1131  
1066 1132          /* Remove any trailing whitespace.  */
↓ open down ↓ 18 lines elided ↑ open up ↑
1085 1151                  while ((new_argv[a++] = strtok_r(NULL, " ", &lasts)) != NULL)
1086 1152                          ;
1087 1153          } else {
1088 1154                  new_argv[a++] = NULL;
1089 1155          }
1090 1156          assert(n == a);
1091 1157          return (new_argv);
1092 1158  }
1093 1159  
1094 1160  /*
1095      - * Prepare argv array for exec'd process; if we're passing commands to the
1096      - * new process, then use su(1M) to do the invocation.  Otherwise, use
     1161 + * Prepare argv array for exec'd process.  If commands are passed to the new
     1162 + * process and su(1M) is avalable, use it for the invocation.  Otherwise, use
1097 1163   * 'login -z <from_zonename> -f' (-z is an undocumented option which tells
1098 1164   * login that we're coming from another zone, and to disregard its CONSOLE
1099 1165   * checks).
1100 1166   */
1101 1167  static char **
1102      -prep_args(brand_handle_t bh, const char *login, char **argv)
     1168 +prep_args(brand_handle_t bh, char *zonename, const char *login, char **argv)
1103 1169  {
1104      -        int argc = 0, a = 0, i, n = -1;
1105      -        char **new_argv;
     1170 +        int argc = 0, i;
     1171 +        size_t subshell_len = 1;
     1172 +        char *subshell = NULL, *supath = NULL;
     1173 +        char **new_argv = NULL;
1106 1174  
1107      -        if (argv != NULL) {
1108      -                size_t subshell_len = 1;
1109      -                char *subshell;
     1175 +        if (argv == NULL) {
     1176 +                if (failsafe) {
     1177 +                        if ((new_argv = malloc(sizeof (char *) * 2)) == NULL)
     1178 +                                return (NULL);
     1179 +                        new_argv[0] = FAILSAFESHELL;
     1180 +                        new_argv[1] = NULL;
     1181 +                } else {
     1182 +                        new_argv = zone_login_cmd(bh, login);
     1183 +                }
     1184 +                return (new_argv);
     1185 +        }
1110 1186  
1111      -                while (argv[argc] != NULL)
1112      -                        argc++;
     1187 +        /*
     1188 +         * Attempt to locate a 'su' binary if not using the failsafe shell.
     1189 +         */
     1190 +        if (!failsafe) {
     1191 +                struct stat sb;
     1192 +                char zonepath[MAXPATHLEN];
     1193 +                char supath_check[MAXPATHLEN];
1113 1194  
1114      -                for (i = 0; i < argc; i++) {
1115      -                        subshell_len += strlen(argv[i]) + 1;
     1195 +                if (zone_get_zonepath(zonename, zonepath,
     1196 +                    sizeof (zonepath)) != Z_OK) {
     1197 +                        zerror(gettext("unable to determine zone "
     1198 +                            "path"));
     1199 +                        return (NULL);
1116 1200                  }
1117      -                if ((subshell = calloc(1, subshell_len)) == NULL)
     1201 +
     1202 +                (void) snprintf(supath_check, sizeof (supath), "%s/root/%s",
     1203 +                    zonepath, SUPATH1);
     1204 +                if (stat(supath_check, &sb) == 0) {
     1205 +                        supath = SUPATH1;
     1206 +                } else {
     1207 +                        (void) snprintf(supath_check, sizeof (supath_check),
     1208 +                            "%s/root/%s", zonepath, SUPATH2);
     1209 +                        if (stat(supath_check, &sb) == 0) {
     1210 +                                supath = SUPATH2;
     1211 +                        }
     1212 +                }
     1213 +        }
     1214 +
     1215 +        /*
     1216 +         * With no failsafe shell or supath to wrap the incoming command, the
     1217 +         * arguments are passed straight through.
     1218 +         */
     1219 +        if (!failsafe && supath == NULL) {
     1220 +                /*
     1221 +                 * Such an outcome is not acceptable, however, if the caller
     1222 +                 * expressed a desire to switch users.
     1223 +                 */
     1224 +                if (strcmp(login, "root") != 0) {
     1225 +                        zerror(gettext("unable to find 'su' command"));
1118 1226                          return (NULL);
     1227 +                }
     1228 +                return (argv);
     1229 +        }
1119 1230  
1120      -                for (i = 0; i < argc; i++) {
1121      -                        (void) strcat(subshell, argv[i]);
     1231 +        /*
     1232 +         * Inventory arguments and allocate a buffer to escape them for the
     1233 +         * subshell.
     1234 +         */
     1235 +        while (argv[argc] != NULL) {
     1236 +                /*
     1237 +                 * Allocate enough space for the delimiter and 2
     1238 +                 * quotes which might be needed.
     1239 +                 */
     1240 +                subshell_len += strlen(argv[argc]) + 3;
     1241 +                argc++;
     1242 +        }
     1243 +        if ((subshell = calloc(1, subshell_len)) == NULL) {
     1244 +                return (NULL);
     1245 +        }
     1246 +
     1247 +        /*
     1248 +         * The handling of quotes in the following block may seem unusual, but
     1249 +         * it is done this way for backward compatibility.
     1250 +         * When running a command, zlogin is documented as:
     1251 +         *    zlogin zonename command args
     1252 +         * However, some code has come to depend on the following usage:
     1253 +         *    zlogin zonename 'command args'
     1254 +         * This relied on the fact that the single argument would be re-parsed
     1255 +         * within the zone and excuted as a command with an argument. To remain
     1256 +         * compatible with this (incorrect) usage, if there is only a single
     1257 +         * argument, it is not quoted, even if it has embedded spaces.
     1258 +         *
     1259 +         * Here are two examples which both need to work:
     1260 +         * 1) zlogin foo 'echo hello'
     1261 +         *    This has a single argv member with a space in it but will not be
     1262 +         *    quoted on the command passed into the zone.
     1263 +         * 2) zlogin foo bash -c 'echo hello'
     1264 +         *    This has 3 argv members. The 3rd arg has a space and must be
     1265 +         *    quoted on the command passed into the zone.
     1266 +         */
     1267 +        for (i = 0; i < argc; i++) {
     1268 +                if (i > 0)
1122 1269                          (void) strcat(subshell, " ");
     1270 +
     1271 +                if (argc > 1 && (strchr(argv[i], ' ') != NULL ||
     1272 +                    strchr(argv[i], '\t') != NULL)) {
     1273 +                        (void) strcat(subshell, "'");
     1274 +                        (void) strcat(subshell, argv[i]);
     1275 +                        (void) strcat(subshell, "'");
     1276 +                } else {
     1277 +                        (void) strcat(subshell, argv[i]);
1123 1278                  }
     1279 +        }
1124 1280  
1125      -                if (failsafe) {
1126      -                        n = 4;
1127      -                        if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
1128      -                                return (NULL);
     1281 +        if (failsafe) {
     1282 +                int a = 0, n = 4;
1129 1283  
1130      -                        new_argv[a++] = FAILSAFESHELL;
1131      -                } else {
1132      -                        n = 5;
1133      -                        if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
1134      -                                return (NULL);
     1284 +                if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
     1285 +                        return (NULL);
1135 1286  
1136      -                        new_argv[a++] = SUPATH;
1137      -                        if (strcmp(login, "root") != 0) {
1138      -                                new_argv[a++] = "-";
1139      -                                n++;
1140      -                        }
1141      -                        new_argv[a++] = (char *)login;
1142      -                }
     1287 +                new_argv[a++] = FAILSAFESHELL;
1143 1288                  new_argv[a++] = "-c";
1144 1289                  new_argv[a++] = subshell;
1145 1290                  new_argv[a++] = NULL;
1146 1291                  assert(a == n);
1147 1292          } else {
1148      -                if (failsafe) {
1149      -                        n = 2;
1150      -                        if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
1151      -                                return (NULL);
1152      -                        new_argv[a++] = FAILSAFESHELL;
1153      -                        new_argv[a++] = NULL;
1154      -                        assert(n == a);
     1293 +                int a = 0, n = 6;
     1294 +
     1295 +                assert(supath != NULL);
     1296 +                if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
     1297 +                        return (NULL);
     1298 +
     1299 +                new_argv[a++] = supath;
     1300 +                if (strcmp(login, "root") != 0) {
     1301 +                        new_argv[a++] = "-";
1155 1302                  } else {
1156      -                        new_argv = zone_login_cmd(bh, login);
     1303 +                        n--;
1157 1304                  }
     1305 +                new_argv[a++] = (char *)login;
     1306 +                new_argv[a++] = "-c";
     1307 +                new_argv[a++] = subshell;
     1308 +                new_argv[a++] = NULL;
     1309 +                assert(a == n);
1158 1310          }
1159 1311  
1160 1312          return (new_argv);
1161 1313  }
1162 1314  
1163 1315  /*
1164 1316   * Helper routine for prep_env below.
1165 1317   */
1166 1318  static char *
1167 1319  add_env(char *name, char *value)
↓ open down ↓ 10 lines elided ↑ open up ↑
1178 1330  
1179 1331  /*
1180 1332   * Prepare envp array for exec'd process.
1181 1333   */
1182 1334  static char **
1183 1335  prep_env()
1184 1336  {
1185 1337          int e = 0, size = 1;
1186 1338          char **new_env, *estr;
1187 1339          char *term = getenv("TERM");
     1340 +        char *path;
1188 1341  
1189 1342          size++; /* for $PATH */
1190 1343          if (term != NULL)
1191 1344                  size++;
1192 1345  
1193 1346          /*
1194 1347           * In failsafe mode we set $HOME, since '-l' isn't valid in this mode.
1195 1348           * We also set $SHELL, since neither login nor su will be around to do
1196 1349           * it.
1197 1350           */
1198 1351          if (failsafe)
1199 1352                  size += 2;
1200 1353  
1201 1354          if ((new_env = malloc(sizeof (char *) * size)) == NULL)
1202 1355                  return (NULL);
1203 1356  
1204      -        if ((estr = add_env("PATH", DEF_PATH)) == NULL)
     1357 +        if (strcmp(zonebrand, "lx") == 0)
     1358 +                path = LX_DEF_PATH;
     1359 +        else
     1360 +                path = DEF_PATH;
     1361 +
     1362 +        if ((estr = add_env("PATH", path)) == NULL)
1205 1363                  return (NULL);
1206 1364          new_env[e++] = estr;
1207 1365  
1208 1366          if (term != NULL) {
1209 1367                  if ((estr = add_env("TERM", term)) == NULL)
1210 1368                          return (NULL);
1211 1369                  new_env[e++] = estr;
1212 1370          }
1213 1371  
1214 1372          if (failsafe) {
↓ open down ↓ 501 lines elided ↑ open up ↑
1716 1874           */
1717 1875  
1718 1876          uid = getuid();
1719 1877          if ((nptr = getpwuid(uid)) == NULL) {
1720 1878                  zerror(gettext("could not get user name."));
1721 1879                  _exit(1);
1722 1880          }
1723 1881          return (nptr->pw_name);
1724 1882  }
1725 1883  
     1884 +static boolean_t
     1885 +zlog_mode_logging(char *zonename, boolean_t *found)
     1886 +{
     1887 +        boolean_t lm = B_FALSE;
     1888 +        zone_dochandle_t handle;
     1889 +        struct zone_attrtab attr;
     1890 +
     1891 +        *found = B_FALSE;
     1892 +        if ((handle = zonecfg_init_handle()) == NULL)
     1893 +                return (lm);
     1894 +
     1895 +        if (zonecfg_get_handle(zonename, handle) != Z_OK)
     1896 +                goto done;
     1897 +
     1898 +        if (zonecfg_setattrent(handle) != Z_OK)
     1899 +                goto done;
     1900 +        while (zonecfg_getattrent(handle, &attr) == Z_OK) {
     1901 +                if (strcmp("zlog-mode", attr.zone_attr_name) == 0) {
     1902 +                        int len = strlen(attr.zone_attr_value);
     1903 +
     1904 +                        *found = B_TRUE;
     1905 +                        if (strncmp("log", attr.zone_attr_value, 3) == 0 ||
     1906 +                            strncmp("nolog", attr.zone_attr_value, 5) == 0 ||
     1907 +                            (len >= 3 && attr.zone_attr_value[len - 2] == '-'))
     1908 +                                lm = B_TRUE;
     1909 +                        break;
     1910 +                }
     1911 +        }
     1912 +        (void) zonecfg_endattrent(handle);
     1913 +
     1914 +done:
     1915 +        zonecfg_fini_handle(handle);
     1916 +        return (lm);
     1917 +}
     1918 +
1726 1919  int
1727 1920  main(int argc, char **argv)
1728 1921  {
1729      -        int arg, console = 0;
     1922 +        int arg, console = 0, imode = 0;
     1923 +        int estatus = 0;
1730 1924          zoneid_t zoneid;
1731 1925          zone_state_t st;
1732 1926          char *login = "root";
     1927 +        int iflag = 0;
1733 1928          int lflag = 0;
1734 1929          int nflag = 0;
1735 1930          char *zonename = NULL;
1736 1931          char **proc_args = NULL;
1737 1932          char **new_args, **new_env;
1738 1933          sigset_t block_cld;
     1934 +        siginfo_t si;
1739 1935          char devroot[MAXPATHLEN];
1740 1936          char *slavename, slaveshortname[MAXPATHLEN];
1741 1937          priv_set_t *privset;
1742 1938          int tmpl_fd;
1743      -        char zonebrand[MAXNAMELEN];
1744 1939          char default_brand[MAXNAMELEN];
1745 1940          struct stat sb;
1746 1941          char kernzone[ZONENAME_MAX];
1747 1942          brand_handle_t bh;
1748 1943          char user_cmd[MAXPATHLEN];
1749 1944          char authname[MAXAUTHS];
1750 1945  
1751 1946          (void) setlocale(LC_ALL, "");
1752 1947          (void) textdomain(TEXT_DOMAIN);
1753 1948  
1754 1949          (void) getpname(argv[0]);
1755 1950          username = get_username();
1756 1951  
1757      -        while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) {
     1952 +        while ((arg = getopt(argc, argv, "diNnECIR:Se:l:Q")) != EOF) {
1758 1953                  switch (arg) {
1759 1954                  case 'C':
1760 1955                          console = 1;
1761 1956                          break;
1762 1957                  case 'E':
1763 1958                          nocmdchar = 1;
1764 1959                          break;
     1960 +                case 'I':
     1961 +                        /*
     1962 +                         * interactive mode is just a slight variation on the
     1963 +                         * console mode.
     1964 +                         */
     1965 +                        console = 1;
     1966 +                        imode = 1;
     1967 +                        /* The default is HUP, disconnect on EOF */
     1968 +                        connect_flags ^= ZLOGIN_ZFD_EOF;
     1969 +                        break;
1765 1970                  case 'R':       /* undocumented */
1766 1971                          if (*optarg != '/') {
1767 1972                                  zerror(gettext("root path must be absolute."));
1768 1973                                  exit(2);
1769 1974                          }
1770 1975                          if (stat(optarg, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
1771 1976                                  zerror(
1772 1977                                      gettext("root path must be a directory."));
1773 1978                                  exit(2);
1774 1979                          }
1775 1980                          zonecfg_set_root(optarg);
1776 1981                          break;
1777 1982                  case 'Q':
1778 1983                          quiet = 1;
1779 1984                          break;
1780 1985                  case 'S':
1781 1986                          failsafe = 1;
1782 1987                          break;
1783 1988                  case 'd':
1784      -                        disconnect = 1;
     1989 +                        connect_flags |= ZLOGIN_DISCONNECT;
1785 1990                          break;
1786 1991                  case 'e':
1787 1992                          set_cmdchar(optarg);
1788 1993                          break;
     1994 +                case 'i':
     1995 +                        iflag = 1;
     1996 +                        break;
1789 1997                  case 'l':
1790 1998                          login = optarg;
1791 1999                          lflag = 1;
1792 2000                          break;
     2001 +                case 'N':
     2002 +                        /* NOHUP - do not send EOF */
     2003 +                        connect_flags ^= ZLOGIN_ZFD_EOF;
     2004 +                        break;
1793 2005                  case 'n':
1794 2006                          nflag = 1;
1795 2007                          break;
1796 2008                  default:
1797 2009                          usage();
1798 2010                  }
1799 2011          }
1800 2012  
1801 2013          if (console != 0) {
1802 2014  
     2015 +                /*
     2016 +                 * The only connect option in console mode is ZLOGIN_DISCONNECT
     2017 +                 */
     2018 +                if (imode == 0)
     2019 +                        connect_flags &= ZLOGIN_DISCONNECT;
     2020 +
1803 2021                  if (lflag != 0) {
1804 2022                          zerror(gettext(
1805 2023                              "-l may not be specified for console login"));
1806 2024                          usage();
1807 2025                  }
1808 2026  
1809 2027                  if (nflag != 0) {
1810 2028                          zerror(gettext(
1811 2029                              "-n may not be specified for console login"));
1812 2030                          usage();
↓ open down ↓ 6 lines elided ↑ open up ↑
1819 2037                  }
1820 2038  
1821 2039                  if (zonecfg_in_alt_root()) {
1822 2040                          zerror(gettext(
1823 2041                              "-R may not be specified for console login"));
1824 2042                          exit(2);
1825 2043                  }
1826 2044  
1827 2045          }
1828 2046  
     2047 +        if (iflag != 0 && nflag != 0) {
     2048 +                zerror(gettext("-i and -n flags are incompatible"));
     2049 +                usage();
     2050 +        }
     2051 +
1829 2052          if (failsafe != 0 && lflag != 0) {
1830 2053                  zerror(gettext("-l may not be specified for failsafe login"));
1831 2054                  usage();
1832 2055          }
1833 2056  
1834      -        if (!console && disconnect != 0) {
     2057 +        if (!console && (connect_flags & ZLOGIN_DISCONNECT) != 0) {
1835 2058                  zerror(gettext(
1836 2059                      "-d may only be specified with console login"));
1837 2060                  usage();
1838 2061          }
1839 2062  
     2063 +        if (imode == 0 && (connect_flags & ZLOGIN_ZFD_EOF) != 0) {
     2064 +                zerror(gettext("-N may only be specified with -I"));
     2065 +                usage();
     2066 +        }
     2067 +
1840 2068          if (optind == (argc - 1)) {
1841 2069                  /*
1842 2070                   * zone name, no process name; this should be an interactive
1843 2071                   * as long as STDIN is really a tty.
1844 2072                   */
1845 2073                  if (nflag != 0) {
1846 2074                          zerror(gettext(
1847 2075                              "-n may not be specified for interactive login"));
1848 2076                          usage();
1849 2077                  }
↓ open down ↓ 2 lines elided ↑ open up ↑
1852 2080                  zonename = argv[optind];
1853 2081          } else if (optind < (argc - 1)) {
1854 2082                  if (console) {
1855 2083                          zerror(gettext("Commands may not be specified for "
1856 2084                              "console login."));
1857 2085                          usage();
1858 2086                  }
1859 2087                  /* zone name and process name, and possibly some args */
1860 2088                  zonename = argv[optind];
1861 2089                  proc_args = &argv[optind + 1];
1862      -                interactive = 0;
     2090 +                if (iflag && isatty(STDIN_FILENO))
     2091 +                        interactive = 1;
1863 2092          } else {
1864 2093                  usage();
1865 2094          }
1866 2095  
1867 2096          if (getzoneid() != GLOBAL_ZONEID) {
1868 2097                  zerror(gettext("'%s' may only be used from the global zone"),
1869 2098                      pname);
1870 2099                  return (1);
1871 2100          }
1872 2101  
↓ open down ↓ 65 lines elided ↑ open up ↑
1938 2167                                      " to login to %s zone."),
1939 2168                                      username, zonename);
1940 2169                                  return (1);
1941 2170                          }
1942 2171                  }
1943 2172          } else {
1944 2173                  forced_login = B_TRUE;
1945 2174          }
1946 2175  
1947 2176          /*
1948      -         * The console is a separate case from the rest of the code; handle
1949      -         * it first.
     2177 +         * The console (or standalong interactive mode) is a separate case from
     2178 +         * the rest of the code; handle it first.
1950 2179           */
1951 2180          if (console) {
     2181 +                int gz_stderr_fd = -1;
     2182 +                int retry;
     2183 +                boolean_t set_raw = B_TRUE;
     2184 +
     2185 +                if (imode) {
     2186 +                        boolean_t has_zfd_config;
     2187 +
     2188 +                        if (zlog_mode_logging(zonename, &has_zfd_config))
     2189 +                                set_raw = B_FALSE;
     2190 +
     2191 +                        /*
     2192 +                         * Asked for standalone interactive mode but the
     2193 +                         * zlog-mode attribute is not configured on the zone.
     2194 +                         */
     2195 +                        if (!has_zfd_config) {
     2196 +                                zerror(gettext("'%s' is not configured on "
     2197 +                                    "the zone"), "zlog-mode");
     2198 +                                return (1);
     2199 +                        }
     2200 +                }
     2201 +
1952 2202                  /*
1953 2203                   * Ensure that zoneadmd for this zone is running.
1954 2204                   */
1955 2205                  if (start_zoneadmd(zonename) == -1)
1956 2206                          return (1);
1957 2207  
1958 2208                  /*
1959 2209                   * Make contact with zoneadmd.
     2210 +                 *
     2211 +                 * Handshake with the control socket first. We handle retries
     2212 +                 * here since the relevant thread in zoneadmd might not have
     2213 +                 * finished setting up yet.
1960 2214                   */
1961      -                if (get_console_master(zonename) == -1)
     2215 +                for (retry = 0; retry < MAX_RETRY; retry++) {
     2216 +                        masterfd = connect_zone_sock(zonename,
     2217 +                            (imode ? "server_ctl" : "console_sock"), B_FALSE);
     2218 +                        if (masterfd != -1)
     2219 +                                break;
     2220 +                        (void) sleep(1);
     2221 +                }
     2222 +
     2223 +                if (retry == MAX_RETRY) {
     2224 +                        zerror(gettext("unable to connect for %d seconds"),
     2225 +                            MAX_RETRY);
1962 2226                          return (1);
     2227 +                }
1963 2228  
1964      -                if (!quiet)
1965      -                        (void) printf(
1966      -                            gettext("[Connected to zone '%s' console]\n"),
1967      -                            zonename);
     2229 +                if (handshake_zone_sock(masterfd, connect_flags) != 0) {
     2230 +                        (void) close(masterfd);
     2231 +                        return (1);
     2232 +                }
1968 2233  
1969      -                if (set_tty_rawmode(STDIN_FILENO) == -1) {
     2234 +                if (imode) {
     2235 +                        ctlfd = masterfd;
     2236 +
     2237 +                        /* Now open the io-related sockets */
     2238 +                        masterfd = connect_zone_sock(zonename, "server_out",
     2239 +                            B_TRUE);
     2240 +                        gz_stderr_fd = connect_zone_sock(zonename,
     2241 +                            "server_err", B_TRUE);
     2242 +                        if (masterfd == -1 || gz_stderr_fd == -1) {
     2243 +                                (void) close(ctlfd);
     2244 +                                (void) close(masterfd);
     2245 +                                (void) close(gz_stderr_fd);
     2246 +                                return (1);
     2247 +                        }
     2248 +                }
     2249 +
     2250 +                if (!quiet) {
     2251 +                        if (imode)
     2252 +                                (void) printf(gettext("[Connected to zone '%s' "
     2253 +                                    "interactively]\n"), zonename);
     2254 +                        else
     2255 +                                (void) printf(gettext("[Connected to zone '%s' "
     2256 +                                    "console]\n"), zonename);
     2257 +                }
     2258 +
     2259 +                if (set_raw && set_tty_rawmode(STDIN_FILENO) == -1) {
1970 2260                          reset_tty();
1971 2261                          zperror(gettext("failed to set stdin pty to raw mode"));
1972 2262                          return (1);
1973 2263                  }
1974 2264  
1975 2265                  (void) sigset(SIGWINCH, sigwinch);
1976 2266                  (void) sigwinch(0);
1977 2267  
     2268 +                if (imode) {
     2269 +                        /* Allow EOF mode toggling via SIGUSR1 */
     2270 +                        (void) sigset(SIGUSR1, sigusr1);
     2271 +                }
     2272 +
1978 2273                  /*
1979 2274                   * Run the I/O loop until we get disconnected.
1980 2275                   */
1981      -                doio(masterfd, -1, masterfd, -1, -1, B_FALSE);
     2276 +                doio(masterfd, -1, masterfd, gz_stderr_fd, -1, B_FALSE);
1982 2277                  reset_tty();
1983      -                if (!quiet)
1984      -                        (void) printf(
1985      -                            gettext("\n[Connection to zone '%s' console "
1986      -                            "closed]\n"), zonename);
     2278 +                if (!quiet) {
     2279 +                        if (imode)
     2280 +                                (void) printf(gettext("\n[Interactive "
     2281 +                                    "connection to zone '%s' closed]\n"),
     2282 +                                    zonename);
     2283 +                        else
     2284 +                                (void) printf(gettext("\n[Connection to zone "
     2285 +                                    "'%s' console closed]\n"), zonename);
     2286 +                }
1987 2287  
1988 2288                  return (0);
1989 2289          }
1990 2290  
1991 2291          if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) {
1992 2292                  zerror(gettext("login allowed only to running zones "
1993 2293                      "(%s is '%s')."), zonename, zone_state_str(st));
1994 2294                  return (1);
1995 2295          }
1996 2296  
↓ open down ↓ 47 lines elided ↑ open up ↑
2044 2344          if (zonecfg_in_alt_root() &&
2045 2345              strcmp(zonebrand, CLUSTER_BRAND_NAME) == 0) {
2046 2346                  (void) strlcpy(zonebrand, default_brand, sizeof (zonebrand));
2047 2347          }
2048 2348  
2049 2349          if ((bh = brand_open(zonebrand)) == NULL) {
2050 2350                  zerror(gettext("could not open brand for zone %s"), zonename);
2051 2351                  return (1);
2052 2352          }
2053 2353  
2054      -        if ((new_args = prep_args(bh, login, proc_args)) == NULL) {
2055      -                zperror(gettext("could not assemble new arguments"));
2056      -                brand_close(bh);
2057      -                return (1);
     2354 +        /*
     2355 +         * The 'interactive' parameter (-i option) indicates that we're running
     2356 +         * a command interactively. In this case we skip prep_args so that we
     2357 +         * don't prepend the 'su root -c' preamble to the command invocation
     2358 +         * since the 'su' command typically will execute a setpgrp which will
     2359 +         * disassociate the actual command from the controlling terminal that
     2360 +         * we (zlogin) setup.
     2361 +         */
     2362 +        if (!iflag) {
     2363 +                if ((new_args = prep_args(bh, zonename, login, proc_args))
     2364 +                    == NULL) {
     2365 +                        zperror(gettext("could not assemble new arguments"));
     2366 +                        brand_close(bh);
     2367 +                        return (1);
     2368 +                }
2058 2369          }
     2370 +
2059 2371          /*
2060 2372           * Get the brand specific user_cmd.  This command is used to get
2061 2373           * a passwd(4) entry for login.
2062 2374           */
2063 2375          if (!interactive && !failsafe) {
2064 2376                  if (zone_get_user_cmd(bh, login, user_cmd,
2065 2377                      sizeof (user_cmd)) == NULL) {
2066 2378                          zerror(gettext("could not get user_cmd for zone %s"),
2067 2379                              zonename);
2068 2380                          brand_close(bh);
↓ open down ↓ 125 lines elided ↑ open up ↑
2194 2506                   */
2195 2507                  if (slavefd != STDERR_FILENO)
2196 2508                          (void) dup2(slavefd, STDERR_FILENO);
2197 2509  
2198 2510                  if (zone_enter(zoneid) == -1) {
2199 2511                          zerror(gettext("could not enter zone %s: %s"),
2200 2512                              zonename, strerror(errno));
2201 2513                          return (1);
2202 2514                  }
2203 2515  
     2516 +                /* Note: we're now inside the zone, can't use gettext anymore */
     2517 +
2204 2518                  if (slavefd != STDERR_FILENO)
2205 2519                          (void) close(STDERR_FILENO);
2206 2520  
2207 2521                  /*
2208 2522                   * We take pains to get this process into a new process
2209 2523                   * group, and subsequently a new session.  In this way,
2210 2524                   * we'll have a session which doesn't yet have a controlling
2211 2525                   * terminal.  When we open the slave, it will become the
2212 2526                   * controlling terminal; no PIDs concerning pgrps or sids
2213 2527                   * will leak inappropriately into the zone.
↓ open down ↓ 21 lines elided ↑ open up ↑
2235 2549                  (void) dup2(slavefd, STDOUT_FILENO);
2236 2550                  (void) dup2(slavefd, STDERR_FILENO);
2237 2551                  if (slavefd != STDIN_FILENO && slavefd != STDOUT_FILENO &&
2238 2552                      slavefd != STDERR_FILENO) {
2239 2553                          (void) close(slavefd);
2240 2554                  }
2241 2555  
2242 2556                  /*
2243 2557                   * In failsafe mode, we don't use login(1), so don't try
2244 2558                   * setting up a utmpx entry.
     2559 +                 *
     2560 +                 * A branded zone may have very different utmpx semantics.
     2561 +                 * At the moment, we only have two brand types:
     2562 +                 * Illumos-like (native, sn1) and Linux.  In the Illumos
     2563 +                 * case, we know exactly how to do the necessary utmpx
     2564 +                 * setup.  Fortunately for us, the Linux /bin/login is
     2565 +                 * prepared to deal with a non-initialized utmpx entry, so
     2566 +                 * we can simply skip it.  If future brands don't fall into
     2567 +                 * either category, we'll have to add a per-brand utmpx
     2568 +                 * setup hook.
2245 2569                   */
2246      -                if (!failsafe)
     2570 +                if (!failsafe && (strcmp(zonebrand, "lx") != 0))
2247 2571                          if (setup_utmpx(slaveshortname) == -1)
2248 2572                                  return (1);
2249 2573  
2250 2574                  /*
2251 2575                   * The child needs to run as root to
2252 2576                   * execute the brand's login program.
2253 2577                   */
2254 2578                  if (setuid(0) == -1) {
2255      -                        zperror(gettext("insufficient privilege"));
     2579 +                        zperror("insufficient privilege");
2256 2580                          return (1);
2257 2581                  }
2258 2582  
2259      -                (void) execve(new_args[0], new_args, new_env);
2260      -                zperror(gettext("exec failure"));
2261      -                return (1);
     2583 +                if (iflag) {
     2584 +                        (void) execve(proc_args[0], proc_args, new_env);
     2585 +                } else {
     2586 +                        (void) execve(new_args[0], new_args, new_env);
     2587 +                }
     2588 +                zperror("exec failure");
     2589 +                return (ENOEXEC);
2262 2590          }
2263 2591  
2264 2592          (void) ct_tmpl_clear(tmpl_fd);
2265 2593          (void) close(tmpl_fd);
2266 2594  
2267 2595          /*
2268 2596           * The rest is only for the parent process.
2269 2597           */
2270 2598          (void) sigset(SIGWINCH, sigwinch);
2271 2599  
↓ open down ↓ 4 lines elided ↑ open up ↑
2276 2604  
2277 2605          reset_tty();
2278 2606          if (!quiet)
2279 2607                  (void) fprintf(stderr,
2280 2608                      gettext("\n[Connection to zone '%s' %s closed]\n"),
2281 2609                      zonename, slaveshortname);
2282 2610  
2283 2611          if (pollerr != 0) {
2284 2612                  (void) fprintf(stderr, gettext("Error: connection closed due "
2285 2613                      "to unexpected pollevents=0x%x.\n"), pollerr);
2286      -                return (1);
     2614 +                return (EPIPE);
2287 2615          }
2288 2616  
2289      -        return (0);
     2617 +        /* reap child and get its status */
     2618 +        if (waitid(P_PID, child_pid, &si, WEXITED | WNOHANG) == -1) {
     2619 +                estatus = errno;
     2620 +        } else if (si.si_pid == 0) {
     2621 +                estatus = ECHILD;
     2622 +        } else if (si.si_code == CLD_EXITED) {
     2623 +                estatus = si.si_status;
     2624 +        } else {
     2625 +                estatus = ECONNABORTED;
     2626 +        }
     2627 +
     2628 +        return (estatus);
2290 2629  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX