diff options
Diffstat (limited to 'usr/src/cmd/zlogin')
| -rw-r--r-- | usr/src/cmd/zlogin/zlogin.c | 590 |
1 files changed, 467 insertions, 123 deletions
diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c index 0ad4013e84..5438549648 100644 --- a/usr/src/cmd/zlogin/zlogin.c +++ b/usr/src/cmd/zlogin/zlogin.c @@ -23,12 +23,12 @@ * Copyright 2013 DEY Storage Systems, Inc. * Copyright (c) 2014 Gary Mills * Copyright 2015 Nexenta Systems, Inc. All rights reserved. - * Copyright 2019 Joyent, Inc. + * Copyright 2020 Joyent, Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ /* - * zlogin provides three types of login which allow users in the global + * zlogin provides five types of login which allow users in the global * zone to access non-global zones. * * - "interactive login" is similar to rlogin(1); for example, the user could @@ -44,12 +44,22 @@ * In this mode, zlogin sets up pipes as the communication channel, and * 'su' is used to do the login setup work. * + * - "interactive command" is a combination of the above two modes where + * a command is provide like the non-interactive case, but the -i option is + * also provided to make things interactive. For example, the user could + * issue 'zlogin -i my-zone /bin/sh'. In this mode neither 'login -c' nor + * 'su root -c' is prepended to the command invocation. Because of this + * there will be no wtmpx login record within the zone. + * * - "console login" is the equivalent to accessing the tip line for a * zone. For example, the user can issue 'zlogin -C my-zone'. * In this mode, zlogin contacts the zoneadmd process via unix domain * socket. If zoneadmd is not running, it starts it. This allows the * console to be available anytime the zone is installed, regardless of * whether it is running. + * + * - "standalone-processs interactive" is specified with -I and connects to + * the zone's stdin, stdout and stderr zfd(7D) devices. */ #include <sys/socket.h> @@ -94,7 +104,8 @@ #include <auth_attr.h> #include <secdb.h> -static int masterfd; +static int masterfd = -1; +static int ctlfd = -1; static struct termios save_termios; static struct termios effective_termios; static int save_fd; @@ -103,12 +114,13 @@ static volatile int dead; static volatile pid_t child_pid = -1; static int interactive = 0; static priv_set_t *dropprivs; +static unsigned int connect_flags = 0; static int nocmdchar = 0; static int failsafe = 0; -static int disconnect = 0; static char cmdchar = '~'; static int quiet = 0; +static char zonebrand[MAXNAMELEN]; static int pollerr = 0; @@ -125,10 +137,14 @@ static boolean_t forced_login = B_FALSE; #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif -#define SUPATH "/usr/bin/su" +#define SUPATH1 "/usr/bin/su" +#define SUPATH2 "/bin/su" #define FAILSAFESHELL "/sbin/sh" #define DEFAULTSHELL "/sbin/sh" #define DEF_PATH "/usr/sbin:/usr/bin" +#define LX_DEF_PATH "/bin:/usr/sbin:/usr/bin" + +#define MAX_RETRY 30 #define CLUSTER_BRAND_NAME "cluster" @@ -155,7 +171,7 @@ static boolean_t forced_login = B_FALSE; static void usage(void) { - (void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] " + (void) fprintf(stderr, gettext("usage: %s [-dinCEINQS] [-e cmdchar] " "[-l user] zonename [command [args ...] ]\n"), pname); exit(2); } @@ -250,57 +266,60 @@ postfork_dropprivs() } } -/* - * Create the unix domain socket and call the zoneadmd server; handshake - * with it to determine whether it will allow us to connect. - */ static int -get_console_master(const char *zname) +connect_zone_sock(const char *zname, const char *suffix, boolean_t verbose) { int sockfd = -1; struct sockaddr_un servaddr; - char clientid[MAXPATHLEN]; - char handshake[MAXPATHLEN], c; - int msglen; - int i = 0, err = 0; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - zperror(gettext("could not create socket")); + if (verbose) + zperror(gettext("could not create socket")); return (-1); } bzero(&servaddr, sizeof (servaddr)); servaddr.sun_family = AF_UNIX; (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), - "%s/%s.console_sock", ZONES_TMPDIR, zname); - + "%s/%s.%s", ZONES_TMPDIR, zname, suffix); if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1) { - zperror(gettext("Could not connect to zone console")); - goto bad; + if (verbose) + zperror(gettext("Could not connect to zone")); + close(sockfd); + return (-1); } - masterfd = sockfd; + return (sockfd); +} - msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n", - getpid(), setlocale(LC_MESSAGES, NULL), disconnect); + +static int +handshake_zone_sock(int sockfd, unsigned int flags) +{ + char clientid[MAXPATHLEN]; + char handshake[MAXPATHLEN], c; + int msglen; + int i = 0, err = 0; + + msglen = snprintf(clientid, sizeof (clientid), "IDENT %s %u\n", + setlocale(LC_MESSAGES, NULL), flags); if (msglen >= sizeof (clientid) || msglen < 0) { zerror("protocol error"); - goto bad; + return (-1); } - if (write(masterfd, clientid, msglen) != msglen) { + if (write(sockfd, clientid, msglen) != msglen) { zerror("protocol error"); - goto bad; + return (-1); } - bzero(handshake, sizeof (handshake)); - /* * Take care not to accumulate more than our fill, and leave room for * the NUL at the end. */ - while ((err = read(masterfd, &c, 1)) == 1) { + bzero(handshake, sizeof (handshake)); + while ((err = read(sockfd, &c, 1)) == 1) { if (i >= (sizeof (handshake) - 1)) break; if (c == '\n') @@ -310,26 +329,48 @@ get_console_master(const char *zname) } /* - * If something went wrong during the handshake we bail; perhaps - * the server died off. + * If something went wrong during the handshake we bail. + * Perhaps the server died off. */ if (err == -1) { - zperror(gettext("Could not connect to zone console")); - goto bad; + zperror(gettext("Could not connect to zone")); + return (-1); } - if (strncmp(handshake, "OK", sizeof (handshake)) == 0) - return (0); + if (strncmp(handshake, "OK", sizeof (handshake)) != 0) { + zerror(gettext("Zone is already in use by process ID %s."), + handshake); + return (-1); + } - zerror(gettext("Console is already in use by process ID %s."), - handshake); -bad: - (void) close(sockfd); - masterfd = -1; - return (-1); + return (0); } - +static int +send_ctl_sock(const char *buf, size_t len) +{ + char rbuf[BUFSIZ]; + int i; + if (ctlfd == -1) { + return (-1); + } + if (write(ctlfd, buf, len) != len) { + return (-1); + } + /* read the response */ + for (i = 0; i < (BUFSIZ - 1); i++) { + char c; + if (read(ctlfd, &c, 1) != 1 || c == '\n' || c == '\0') { + break; + } + rbuf[i] = c; + } + rbuf[i+1] = '\0'; + if (strncmp("OK", rbuf, BUFSIZ) != 0) { + return (-1); + } + return (0); +} /* * Routines to handle pty creation upon zone entry and to shuttle I/O back * and forth between the two terminals. We also compute and store the @@ -518,8 +559,32 @@ sigwinch(int s) { struct winsize ws; - if (ioctl(0, TIOCGWINSZ, &ws) == 0) - (void) ioctl(masterfd, TIOCSWINSZ, &ws); + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + if (ctlfd != -1) { + char buf[BUFSIZ]; + snprintf(buf, sizeof (buf), "TIOCSWINSZ %hu %hu\n", + ws.ws_row, ws.ws_col); + (void) send_ctl_sock(buf, strlen(buf)); + } else { + (void) ioctl(masterfd, TIOCSWINSZ, &ws); + } + } +} + +/* + * Toggle zfd EOF mode and notify zoneadmd + */ +/*ARGSUSED*/ +static void +sigusr1(int s) +{ + connect_flags ^= ZLOGIN_ZFD_EOF; + if (ctlfd != -1) { + char buf[BUFSIZ]; + snprintf(buf, sizeof (buf), "SETFLAGS %u\n", + connect_flags); + (void) send_ctl_sock(buf, strlen(buf)); + } } static volatile int close_on_sig = -1; @@ -863,28 +928,34 @@ doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd, int sig_fd, break; } - /* event from master side stdout */ - if (pollfds[0].revents) { - if (pollfds[0].revents & + /* event from master side stderr */ + if (pollfds[1].revents) { + if (pollfds[1].revents & POLLHUP) + break; + + if (pollfds[1].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - if (process_output(stdout_fd, STDOUT_FILENO) + if (process_output(stderr_fd, STDERR_FILENO) != 0) break; } else { - pollerr = pollfds[0].revents; + pollerr = pollfds[1].revents; break; } } - /* event from master side stderr */ - if (pollfds[1].revents) { - if (pollfds[1].revents & + /* event from master side stdout */ + if (pollfds[0].revents) { + if (pollfds[0].revents & POLLHUP) + break; + + if (pollfds[0].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - if (process_output(stderr_fd, STDERR_FILENO) + if (process_output(stdout_fd, STDOUT_FILENO) != 0) break; } else { - pollerr = pollfds[1].revents; + pollerr = pollfds[0].revents; break; } } @@ -1054,7 +1125,7 @@ zone_login_cmd(brand_handle_t bh, const char *login) * but we're going to be very simplistic about it and break stuff * up based on spaces. We're not even going to support any kind * of quoting or escape characters. It's truly amazing that - * there is no library function in OpenSolaris to do this for us. + * there is no library function in Illumos to do this for us. */ /* @@ -1100,62 +1171,148 @@ zone_login_cmd(brand_handle_t bh, const char *login) * checks). */ static char ** -prep_args(brand_handle_t bh, const char *login, char **argv) +prep_args(brand_handle_t bh, char *zonename, const char *login, char **argv) { - int argc = 0, a = 0, i, n = -1; - char **new_argv; + int argc = 0, i; + size_t subshell_len = 1; + char *subshell = NULL, *supath = NULL; + char **new_argv = NULL; - if (argv != NULL) { - size_t subshell_len = 1; - char *subshell; + if (argv == NULL) { + if (failsafe) { + if ((new_argv = malloc(sizeof (char *) * 2)) == NULL) + return (NULL); + new_argv[0] = FAILSAFESHELL; + new_argv[1] = NULL; + } else { + new_argv = zone_login_cmd(bh, login); + } + return (new_argv); + } - while (argv[argc] != NULL) - argc++; + /* + * Attempt to locate a 'su' binary if not using the failsafe shell. + */ + if (!failsafe) { + struct stat sb; + char zonepath[MAXPATHLEN]; + char supath_check[MAXPATHLEN]; + + if (zone_get_zonepath(zonename, zonepath, + sizeof (zonepath)) != Z_OK) { + zerror(gettext("unable to determine zone " + "path")); + return (NULL); + } - for (i = 0; i < argc; i++) { - subshell_len += strlen(argv[i]) + 1; + (void) snprintf(supath_check, sizeof (supath), "%s/root/%s", + zonepath, SUPATH1); + if (stat(supath_check, &sb) == 0) { + supath = SUPATH1; + } else { + (void) snprintf(supath_check, sizeof (supath_check), + "%s/root/%s", zonepath, SUPATH2); + if (stat(supath_check, &sb) == 0) { + supath = SUPATH2; + } } - if ((subshell = calloc(1, subshell_len)) == NULL) + } + + /* + * With no failsafe shell or supath to wrap the incoming command, the + * arguments are passed straight through. + */ + if (!failsafe && supath == NULL) { + /* + * Such an outcome is not acceptable, however, if the caller + * expressed a desire to switch users. + */ + if (strcmp(login, "root") != 0) { + zerror(gettext("unable to find 'su' command")); return (NULL); + } + return (argv); + } - for (i = 0; i < argc; i++) { - (void) strcat(subshell, argv[i]); + /* + * Inventory arguments and allocate a buffer to escape them for the + * subshell. + */ + while (argv[argc] != NULL) { + /* + * Allocate enough space for the delimiter and 2 + * quotes which might be needed. + */ + subshell_len += strlen(argv[argc]) + 3; + argc++; + } + if ((subshell = calloc(1, subshell_len)) == NULL) { + return (NULL); + } + + /* + * The handling of quotes in the following block may seem unusual, but + * it is done this way for backward compatibility. + * When running a command, zlogin is documented as: + * zlogin zonename command args + * However, some code has come to depend on the following usage: + * zlogin zonename 'command args' + * This relied on the fact that the single argument would be re-parsed + * within the zone and excuted as a command with an argument. To remain + * compatible with this (incorrect) usage, if there is only a single + * argument, it is not quoted, even if it has embedded spaces. + * + * Here are two examples which both need to work: + * 1) zlogin foo 'echo hello' + * This has a single argv member with a space in it but will not be + * quoted on the command passed into the zone. + * 2) zlogin foo bash -c 'echo hello' + * This has 3 argv members. The 3rd arg has a space and must be + * quoted on the command passed into the zone. + */ + for (i = 0; i < argc; i++) { + if (i > 0) (void) strcat(subshell, " "); + + if (argc > 1 && (strchr(argv[i], ' ') != NULL || + strchr(argv[i], '\t') != NULL)) { + (void) strcat(subshell, "'"); + (void) strcat(subshell, argv[i]); + (void) strcat(subshell, "'"); + } else { + (void) strcat(subshell, argv[i]); } + } - if (failsafe) { - n = 4; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); + if (failsafe) { + int a = 0, n = 4; - new_argv[a++] = FAILSAFESHELL; - } else { - n = 5; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); + if ((new_argv = malloc(sizeof (char *) * n)) == NULL) + return (NULL); - new_argv[a++] = SUPATH; - if (strcmp(login, "root") != 0) { - new_argv[a++] = "-"; - n++; - } - new_argv[a++] = (char *)login; - } + new_argv[a++] = FAILSAFESHELL; new_argv[a++] = "-c"; new_argv[a++] = subshell; new_argv[a++] = NULL; assert(a == n); } else { - if (failsafe) { - n = 2; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); - new_argv[a++] = FAILSAFESHELL; - new_argv[a++] = NULL; - assert(n == a); + int a = 0, n = 6; + + assert(supath != NULL); + if ((new_argv = malloc(sizeof (char *) * n)) == NULL) + return (NULL); + + new_argv[a++] = supath; + if (strcmp(login, "root") != 0) { + new_argv[a++] = "-"; } else { - new_argv = zone_login_cmd(bh, login); + n--; } + new_argv[a++] = (char *)login; + new_argv[a++] = "-c"; + new_argv[a++] = subshell; + new_argv[a++] = NULL; + assert(a == n); } return (new_argv); @@ -1186,6 +1343,7 @@ prep_env() int e = 0, size = 1; char **new_env, *estr; char *term = getenv("TERM"); + char *path; size++; /* for $PATH */ if (term != NULL) @@ -1202,7 +1360,12 @@ prep_env() if ((new_env = malloc(sizeof (char *) * size)) == NULL) return (NULL); - if ((estr = add_env("PATH", DEF_PATH)) == NULL) + if (strcmp(zonebrand, "lx") == 0) + path = LX_DEF_PATH; + else + path = DEF_PATH; + + if ((estr = add_env("PATH", path)) == NULL) return (NULL); new_env[e++] = estr; @@ -1724,24 +1887,61 @@ get_username() return (nptr->pw_name); } +static boolean_t +zlog_mode_logging(char *zonename, boolean_t *found) +{ + boolean_t lm = B_FALSE; + zone_dochandle_t handle; + struct zone_attrtab attr; + + *found = B_FALSE; + if ((handle = zonecfg_init_handle()) == NULL) + return (lm); + + if (zonecfg_get_handle(zonename, handle) != Z_OK) + goto done; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp("zlog-mode", attr.zone_attr_name) == 0) { + int len = strlen(attr.zone_attr_value); + + *found = B_TRUE; + if (strncmp("log", attr.zone_attr_value, 3) == 0 || + strncmp("nolog", attr.zone_attr_value, 5) == 0 || + (len >= 3 && attr.zone_attr_value[len - 2] == '-')) + lm = B_TRUE; + break; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); + return (lm); +} + int main(int argc, char **argv) { - int arg, console = 0; + int arg, console = 0, imode = 0; + int estatus = 0; zoneid_t zoneid; zone_state_t st; char *login = "root"; + int iflag = 0; int lflag = 0; int nflag = 0; char *zonename = NULL; char **proc_args = NULL; char **new_args, **new_env; sigset_t block_cld; + siginfo_t si; char devroot[MAXPATHLEN]; char *slavename, slaveshortname[MAXPATHLEN]; priv_set_t *privset; int tmpl_fd; - char zonebrand[MAXNAMELEN]; char default_brand[MAXNAMELEN]; struct stat sb; char kernzone[ZONENAME_MAX]; @@ -1755,7 +1955,7 @@ main(int argc, char **argv) (void) getpname(argv[0]); username = get_username(); - while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) { + while ((arg = getopt(argc, argv, "diNnECIR:Se:l:Q")) != EOF) { switch (arg) { case 'C': console = 1; @@ -1763,6 +1963,16 @@ main(int argc, char **argv) case 'E': nocmdchar = 1; break; + case 'I': + /* + * interactive mode is just a slight variation on the + * console mode. + */ + console = 1; + imode = 1; + /* The default is HUP, disconnect on EOF */ + connect_flags ^= ZLOGIN_ZFD_EOF; + break; case 'R': /* undocumented */ if (*optarg != '/') { zerror(gettext("root path must be absolute.")); @@ -1782,15 +1992,22 @@ main(int argc, char **argv) failsafe = 1; break; case 'd': - disconnect = 1; + connect_flags |= ZLOGIN_DISCONNECT; break; case 'e': set_cmdchar(optarg); break; + case 'i': + iflag = 1; + break; case 'l': login = optarg; lflag = 1; break; + case 'N': + /* NOHUP - do not send EOF */ + connect_flags ^= ZLOGIN_ZFD_EOF; + break; case 'n': nflag = 1; break; @@ -1801,6 +2018,12 @@ main(int argc, char **argv) if (console != 0) { + /* + * The only connect option in console mode is ZLOGIN_DISCONNECT + */ + if (imode == 0) + connect_flags &= ZLOGIN_DISCONNECT; + if (lflag != 0) { zerror(gettext( "-l may not be specified for console login")); @@ -1827,17 +2050,27 @@ main(int argc, char **argv) } + if (iflag != 0 && nflag != 0) { + zerror(gettext("-i and -n flags are incompatible")); + usage(); + } + if (failsafe != 0 && lflag != 0) { zerror(gettext("-l may not be specified for failsafe login")); usage(); } - if (!console && disconnect != 0) { + if (!console && (connect_flags & ZLOGIN_DISCONNECT) != 0) { zerror(gettext( "-d may only be specified with console login")); usage(); } + if (imode == 0 && (connect_flags & ZLOGIN_ZFD_EOF) != 0) { + zerror(gettext("-N may only be specified with -I")); + usage(); + } + if (optind == (argc - 1)) { /* * zone name, no process name; this should be an interactive @@ -1860,7 +2093,8 @@ main(int argc, char **argv) /* zone name and process name, and possibly some args */ zonename = argv[optind]; proc_args = &argv[optind + 1]; - interactive = 0; + if (iflag && isatty(STDIN_FILENO)) + interactive = 1; } else { usage(); } @@ -1946,10 +2180,31 @@ main(int argc, char **argv) } /* - * The console is a separate case from the rest of the code; handle - * it first. + * The console (or standalong interactive mode) is a separate case from + * the rest of the code; handle it first. */ if (console) { + int gz_stderr_fd = -1; + int retry; + boolean_t set_raw = B_TRUE; + + if (imode) { + boolean_t has_zfd_config; + + if (zlog_mode_logging(zonename, &has_zfd_config)) + set_raw = B_FALSE; + + /* + * Asked for standalone interactive mode but the + * zlog-mode attribute is not configured on the zone. + */ + if (!has_zfd_config) { + zerror(gettext("'%s' is not configured on " + "the zone"), "zlog-mode"); + return (1); + } + } + /* * Ensure that zoneadmd for this zone is running. */ @@ -1958,16 +2213,56 @@ main(int argc, char **argv) /* * Make contact with zoneadmd. + * + * Handshake with the control socket first. We handle retries + * here since the relevant thread in zoneadmd might not have + * finished setting up yet. */ - if (get_console_master(zonename) == -1) + for (retry = 0; retry < MAX_RETRY; retry++) { + masterfd = connect_zone_sock(zonename, + (imode ? "server_ctl" : "console_sock"), B_FALSE); + if (masterfd != -1) + break; + sleep(1); + } + + if (retry == MAX_RETRY) { + zerror(gettext("unable to connect for %d seconds"), + MAX_RETRY); return (1); + } - if (!quiet) - (void) printf( - gettext("[Connected to zone '%s' console]\n"), - zonename); + if (handshake_zone_sock(masterfd, connect_flags) != 0) { + (void) close(masterfd); + return (1); + } + + if (imode) { + ctlfd = masterfd; + + /* Now open the io-related sockets */ + masterfd = connect_zone_sock(zonename, "server_out", + B_TRUE); + gz_stderr_fd = connect_zone_sock(zonename, + "server_err", B_TRUE); + if (masterfd == -1 || gz_stderr_fd == -1) { + (void) close(ctlfd); + (void) close(masterfd); + (void) close(gz_stderr_fd); + return (1); + } + } + + if (!quiet) { + if (imode) + (void) printf(gettext("[Connected to zone '%s' " + "interactively]\n"), zonename); + else + (void) printf(gettext("[Connected to zone '%s' " + "console]\n"), zonename); + } - if (set_tty_rawmode(STDIN_FILENO) == -1) { + if (set_raw && set_tty_rawmode(STDIN_FILENO) == -1) { reset_tty(); zperror(gettext("failed to set stdin pty to raw mode")); return (1); @@ -1976,15 +2271,25 @@ main(int argc, char **argv) (void) sigset(SIGWINCH, sigwinch); (void) sigwinch(0); + if (imode) { + /* Allow EOF mode toggling via SIGUSR1 */ + (void) sigset(SIGUSR1, sigusr1); + } + /* * Run the I/O loop until we get disconnected. */ - doio(masterfd, -1, masterfd, -1, -1, B_FALSE); + doio(masterfd, -1, masterfd, gz_stderr_fd, -1, B_FALSE); reset_tty(); - if (!quiet) - (void) printf( - gettext("\n[Connection to zone '%s' console " - "closed]\n"), zonename); + if (!quiet) { + if (imode) + (void) printf(gettext("\n[Interactive " + "connection to zone '%s' closed]\n"), + zonename); + else + (void) printf(gettext("\n[Connection to zone " + "'%s' console closed]\n"), zonename); + } return (0); } @@ -2052,11 +2357,23 @@ main(int argc, char **argv) return (1); } - if ((new_args = prep_args(bh, login, proc_args)) == NULL) { - zperror(gettext("could not assemble new arguments")); - brand_close(bh); - return (1); + /* + * The 'interactive' parameter (-i option) indicates that we're running + * a command interactively. In this case we skip prep_args so that we + * don't prepend the 'su root -c' preamble to the command invocation + * since the 'su' command typically will execute a setpgrp which will + * disassociate the actual command from the controlling terminal that + * we (zlogin) setup. + */ + if (!iflag) { + if ((new_args = prep_args(bh, zonename, login, proc_args)) + == NULL) { + zperror(gettext("could not assemble new arguments")); + brand_close(bh); + return (1); + } } + /* * Get the brand specific user_cmd. This command is used to get * a passwd(5) entry for login. @@ -2202,6 +2519,8 @@ main(int argc, char **argv) return (1); } + /* Note: we're now inside the zone, can't use gettext anymore */ + if (slavefd != STDERR_FILENO) (void) close(STDERR_FILENO); @@ -2243,8 +2562,18 @@ main(int argc, char **argv) /* * In failsafe mode, we don't use login(1), so don't try * setting up a utmpx entry. + * + * A branded zone may have very different utmpx semantics. + * At the moment, we only have two brand types: + * Illumos-like (native, sn1) and Linux. In the Illumos + * case, we know exactly how to do the necessary utmpx + * setup. Fortunately for us, the Linux /bin/login is + * prepared to deal with a non-initialized utmpx entry, so + * we can simply skip it. If future brands don't fall into + * either category, we'll have to add a per-brand utmpx + * setup hook. */ - if (!failsafe) + if (!failsafe && (strcmp(zonebrand, "lx") != 0)) if (setup_utmpx(slaveshortname) == -1) return (1); @@ -2253,13 +2582,17 @@ main(int argc, char **argv) * execute the brand's login program. */ if (setuid(0) == -1) { - zperror(gettext("insufficient privilege")); + zperror("insufficient privilege"); return (1); } - (void) execve(new_args[0], new_args, new_env); - zperror(gettext("exec failure")); - return (1); + if (iflag) { + (void) execve(proc_args[0], proc_args, new_env); + } else { + (void) execve(new_args[0], new_args, new_env); + } + zperror("exec failure"); + return (ENOEXEC); } (void) ct_tmpl_clear(tmpl_fd); @@ -2284,8 +2617,19 @@ main(int argc, char **argv) if (pollerr != 0) { (void) fprintf(stderr, gettext("Error: connection closed due " "to unexpected pollevents=0x%x.\n"), pollerr); - return (1); + return (EPIPE); } - return (0); + /* reap child and get its status */ + if (waitid(P_PID, child_pid, &si, WEXITED | WNOHANG) == -1) { + estatus = errno; + } else if (si.si_pid == 0) { + estatus = ECHILD; + } else if (si.si_code == CLD_EXITED) { + estatus = si.si_status; + } else { + estatus = ECONNABORTED; + } + + return (estatus); } |
