17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 23*108322fbScarlsonj * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * zlogin provides three types of login which allow users in the global 317c478bd9Sstevel@tonic-gate * zone to access non-global zones. 327c478bd9Sstevel@tonic-gate * 337c478bd9Sstevel@tonic-gate * - "interactive login" is similar to rlogin(1); for example, the user could 347c478bd9Sstevel@tonic-gate * issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'. The user is 357c478bd9Sstevel@tonic-gate * granted a new pty (which is then shoved into the zone), and an I/O 367c478bd9Sstevel@tonic-gate * loop between parent and child processes takes care of the interactive 377c478bd9Sstevel@tonic-gate * session. In this mode, login(1) (and its -c option, which means 387c478bd9Sstevel@tonic-gate * "already authenticated") is employed to take care of the initialization 397c478bd9Sstevel@tonic-gate * of the user's session. 407c478bd9Sstevel@tonic-gate * 417c478bd9Sstevel@tonic-gate * - "non-interactive login" is similar to su(1M); the user could issue 427c478bd9Sstevel@tonic-gate * 'zlogin my-zone ls -l' and the command would be run as specified. 437c478bd9Sstevel@tonic-gate * In this mode, zlogin sets up pipes as the communication channel, and 447c478bd9Sstevel@tonic-gate * 'su' is used to do the login setup work. 457c478bd9Sstevel@tonic-gate * 467c478bd9Sstevel@tonic-gate * - "console login" is the equivalent to accessing the tip line for a 477c478bd9Sstevel@tonic-gate * zone. For example, the user can issue 'zlogin -C my-zone'. 487c478bd9Sstevel@tonic-gate * In this mode, zlogin contacts the zoneadmd process via unix domain 497c478bd9Sstevel@tonic-gate * socket. If zoneadmd is not running, it starts it. This allows the 507c478bd9Sstevel@tonic-gate * console to be available anytime the zone is installed, regardless of 517c478bd9Sstevel@tonic-gate * whether it is running. 527c478bd9Sstevel@tonic-gate */ 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate #include <sys/socket.h> 557c478bd9Sstevel@tonic-gate #include <sys/termios.h> 567c478bd9Sstevel@tonic-gate #include <sys/utsname.h> 577c478bd9Sstevel@tonic-gate #include <sys/stat.h> 587c478bd9Sstevel@tonic-gate #include <sys/types.h> 597c478bd9Sstevel@tonic-gate #include <sys/contract/process.h> 607c478bd9Sstevel@tonic-gate #include <sys/ctfs.h> 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate #include <alloca.h> 637c478bd9Sstevel@tonic-gate #include <assert.h> 647c478bd9Sstevel@tonic-gate #include <ctype.h> 657c478bd9Sstevel@tonic-gate #include <door.h> 667c478bd9Sstevel@tonic-gate #include <errno.h> 677c478bd9Sstevel@tonic-gate #include <poll.h> 687c478bd9Sstevel@tonic-gate #include <priv.h> 697c478bd9Sstevel@tonic-gate #include <pwd.h> 707c478bd9Sstevel@tonic-gate #include <unistd.h> 717c478bd9Sstevel@tonic-gate #include <utmpx.h> 727c478bd9Sstevel@tonic-gate #include <sac.h> 737c478bd9Sstevel@tonic-gate #include <signal.h> 747c478bd9Sstevel@tonic-gate #include <stdarg.h> 757c478bd9Sstevel@tonic-gate #include <stdio.h> 767c478bd9Sstevel@tonic-gate #include <stdlib.h> 777c478bd9Sstevel@tonic-gate #include <string.h> 787c478bd9Sstevel@tonic-gate #include <strings.h> 797c478bd9Sstevel@tonic-gate #include <stropts.h> 807c478bd9Sstevel@tonic-gate #include <wait.h> 817c478bd9Sstevel@tonic-gate #include <zone.h> 827c478bd9Sstevel@tonic-gate #include <fcntl.h> 837c478bd9Sstevel@tonic-gate #include <libdevinfo.h> 847c478bd9Sstevel@tonic-gate #include <libintl.h> 857c478bd9Sstevel@tonic-gate #include <locale.h> 867c478bd9Sstevel@tonic-gate #include <libzonecfg.h> 877c478bd9Sstevel@tonic-gate #include <libcontract.h> 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate static int masterfd; 907c478bd9Sstevel@tonic-gate static struct termios save_termios; 917c478bd9Sstevel@tonic-gate static struct termios effective_termios; 927c478bd9Sstevel@tonic-gate static int save_fd; 937c478bd9Sstevel@tonic-gate static struct winsize winsize; 947c478bd9Sstevel@tonic-gate static volatile int dead; 957c478bd9Sstevel@tonic-gate static volatile pid_t child_pid = -1; 967c478bd9Sstevel@tonic-gate static int interactive = 0; 977c478bd9Sstevel@tonic-gate static priv_set_t *dropprivs; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate static int nocmdchar = 0; 1007c478bd9Sstevel@tonic-gate static int failsafe = 0; 1017c478bd9Sstevel@tonic-gate static char cmdchar = '~'; 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate static int pollerr = 0; 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate static const char *pname; 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 1087c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 1097c478bd9Sstevel@tonic-gate #endif 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate #define SUPATH "/usr/bin/su" 1127c478bd9Sstevel@tonic-gate #define FAILSAFESHELL "/sbin/sh" 1137c478bd9Sstevel@tonic-gate #define DEFAULTSHELL "/sbin/sh" 1147c478bd9Sstevel@tonic-gate #define LOGINPATH "/usr/bin/login" 1157c478bd9Sstevel@tonic-gate #define DEF_PATH "/usr/sbin:/usr/bin" 1167c478bd9Sstevel@tonic-gate 1177c478bd9Sstevel@tonic-gate /* 1187c478bd9Sstevel@tonic-gate * See canonify() below. CANONIFY_LEN is the maximum length that a 1197c478bd9Sstevel@tonic-gate * "canonical" sequence will expand to (backslash, three octal digits, NUL). 1207c478bd9Sstevel@tonic-gate */ 1217c478bd9Sstevel@tonic-gate #define CANONIFY_LEN 5 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate static void 1247c478bd9Sstevel@tonic-gate usage(void) 1257c478bd9Sstevel@tonic-gate { 1267c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("usage: %s [ -CES ] [ -e cmdchar ] " 1277c478bd9Sstevel@tonic-gate "[-l user] zonename [command [args ...] ]\n"), pname); 1287c478bd9Sstevel@tonic-gate exit(2); 1297c478bd9Sstevel@tonic-gate } 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate static const char * 1327c478bd9Sstevel@tonic-gate getpname(const char *arg0) 1337c478bd9Sstevel@tonic-gate { 1347c478bd9Sstevel@tonic-gate const char *p = strrchr(arg0, '/'); 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate if (p == NULL) 1377c478bd9Sstevel@tonic-gate p = arg0; 1387c478bd9Sstevel@tonic-gate else 1397c478bd9Sstevel@tonic-gate p++; 1407c478bd9Sstevel@tonic-gate 1417c478bd9Sstevel@tonic-gate pname = p; 1427c478bd9Sstevel@tonic-gate return (p); 1437c478bd9Sstevel@tonic-gate } 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate static void 1467c478bd9Sstevel@tonic-gate zerror(const char *fmt, ...) 1477c478bd9Sstevel@tonic-gate { 1487c478bd9Sstevel@tonic-gate va_list alist; 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", pname); 1517c478bd9Sstevel@tonic-gate va_start(alist, fmt); 1527c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 1537c478bd9Sstevel@tonic-gate va_end(alist); 1547c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate static void 1587c478bd9Sstevel@tonic-gate zperror(const char *str) 1597c478bd9Sstevel@tonic-gate { 1607c478bd9Sstevel@tonic-gate const char *estr; 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate if ((estr = strerror(errno)) != NULL) 1637c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", pname, str, estr); 1647c478bd9Sstevel@tonic-gate else 1657c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: errno %d\n", pname, str, errno); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate /* 1697c478bd9Sstevel@tonic-gate * The first part of our privilege dropping scheme needs to be called before 1707c478bd9Sstevel@tonic-gate * fork(), since we must have it for security; we don't want to be surprised 1717c478bd9Sstevel@tonic-gate * later that we couldn't allocate the privset. 1727c478bd9Sstevel@tonic-gate */ 1737c478bd9Sstevel@tonic-gate static int 1747c478bd9Sstevel@tonic-gate prefork_dropprivs() 1757c478bd9Sstevel@tonic-gate { 1767c478bd9Sstevel@tonic-gate if ((dropprivs = priv_allocset()) == NULL) 1777c478bd9Sstevel@tonic-gate return (1); 1787c478bd9Sstevel@tonic-gate priv_emptyset(dropprivs); 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate /* 1817c478bd9Sstevel@tonic-gate * We need these privileges in order to query session information and 1827c478bd9Sstevel@tonic-gate * send signals. 1837c478bd9Sstevel@tonic-gate */ 1847c478bd9Sstevel@tonic-gate if (interactive == 0) { 1857c478bd9Sstevel@tonic-gate if (priv_addset(dropprivs, "proc_session") == -1) 1867c478bd9Sstevel@tonic-gate return (1); 1877c478bd9Sstevel@tonic-gate if (priv_addset(dropprivs, "proc_zone") == -1) 1887c478bd9Sstevel@tonic-gate return (1); 1897c478bd9Sstevel@tonic-gate if (priv_addset(dropprivs, "proc_owner") == -1) 1907c478bd9Sstevel@tonic-gate return (1); 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate return (0); 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate /* 1977c478bd9Sstevel@tonic-gate * The second part of the privilege drop. We are paranoid about being attacked 1987c478bd9Sstevel@tonic-gate * by the zone, so we drop all privileges. This should prevent a compromise 1997c478bd9Sstevel@tonic-gate * which gets us to fork(), exec(), symlink(), etc. 2007c478bd9Sstevel@tonic-gate */ 2017c478bd9Sstevel@tonic-gate static void 2027c478bd9Sstevel@tonic-gate postfork_dropprivs() 2037c478bd9Sstevel@tonic-gate { 2047c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_PERMITTED, dropprivs)) == -1) { 2057c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set permitted privileges")); 2067c478bd9Sstevel@tonic-gate } 2077c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_LIMIT, dropprivs)) == -1) { 2087c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set limit privileges")); 2097c478bd9Sstevel@tonic-gate } 2107c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_INHERITABLE, dropprivs)) == -1) { 2117c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set inheritable " 2127c478bd9Sstevel@tonic-gate "privileges")); 2137c478bd9Sstevel@tonic-gate } 2147c478bd9Sstevel@tonic-gate } 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate * Create the unix domain socket and call the zoneadmd server; handshake 2187c478bd9Sstevel@tonic-gate * with it to determine whether it will allow us to connect. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate static int 2217c478bd9Sstevel@tonic-gate get_console_master(const char *zname) 2227c478bd9Sstevel@tonic-gate { 2237c478bd9Sstevel@tonic-gate int sockfd = -1; 2247c478bd9Sstevel@tonic-gate struct sockaddr_un servaddr; 2257c478bd9Sstevel@tonic-gate char clientid[MAXPATHLEN]; 2267c478bd9Sstevel@tonic-gate char handshake[MAXPATHLEN], c; 2277c478bd9Sstevel@tonic-gate int msglen; 2287c478bd9Sstevel@tonic-gate int i = 0, err = 0; 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 2317c478bd9Sstevel@tonic-gate zperror(gettext("could not create socket")); 2327c478bd9Sstevel@tonic-gate return (-1); 2337c478bd9Sstevel@tonic-gate } 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate bzero(&servaddr, sizeof (servaddr)); 2367c478bd9Sstevel@tonic-gate servaddr.sun_family = AF_UNIX; 2377c478bd9Sstevel@tonic-gate (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), 2387c478bd9Sstevel@tonic-gate "%s/%s.console_sock", ZONES_TMPDIR, zname); 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate if (connect(sockfd, (struct sockaddr *)&servaddr, 2417c478bd9Sstevel@tonic-gate sizeof (servaddr)) == -1) { 2427c478bd9Sstevel@tonic-gate zperror(gettext("Could not connect to zone console")); 2437c478bd9Sstevel@tonic-gate goto bad; 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate masterfd = sockfd; 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s\n", 2487c478bd9Sstevel@tonic-gate getpid(), setlocale(LC_MESSAGES, NULL)); 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate if (msglen >= sizeof (clientid) || msglen < 0) { 2517c478bd9Sstevel@tonic-gate zerror("protocol error"); 2527c478bd9Sstevel@tonic-gate goto bad; 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate if (write(masterfd, clientid, msglen) != msglen) { 2567c478bd9Sstevel@tonic-gate zerror("protocol error"); 2577c478bd9Sstevel@tonic-gate goto bad; 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate bzero(handshake, sizeof (handshake)); 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate /* 2637c478bd9Sstevel@tonic-gate * Take care not to accumulate more than our fill, and leave room for 2647c478bd9Sstevel@tonic-gate * the NUL at the end. 2657c478bd9Sstevel@tonic-gate */ 2667c478bd9Sstevel@tonic-gate while ((err = read(masterfd, &c, 1)) == 1) { 2677c478bd9Sstevel@tonic-gate if (i >= (sizeof (handshake) - 1)) 2687c478bd9Sstevel@tonic-gate break; 2697c478bd9Sstevel@tonic-gate if (c == '\n') 2707c478bd9Sstevel@tonic-gate break; 2717c478bd9Sstevel@tonic-gate handshake[i] = c; 2727c478bd9Sstevel@tonic-gate i++; 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate /* 2767c478bd9Sstevel@tonic-gate * If something went wrong during the handshake we bail; perhaps 2777c478bd9Sstevel@tonic-gate * the server died off. 2787c478bd9Sstevel@tonic-gate */ 2797c478bd9Sstevel@tonic-gate if (err == -1) { 2807c478bd9Sstevel@tonic-gate zperror(gettext("Could not connect to zone console")); 2817c478bd9Sstevel@tonic-gate goto bad; 2827c478bd9Sstevel@tonic-gate } 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate if (strncmp(handshake, "OK", sizeof (handshake)) == 0) 2857c478bd9Sstevel@tonic-gate return (0); 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate zerror(gettext("Console is already in use by process ID %s."), 2887c478bd9Sstevel@tonic-gate handshake); 2897c478bd9Sstevel@tonic-gate bad: 2907c478bd9Sstevel@tonic-gate (void) close(sockfd); 2917c478bd9Sstevel@tonic-gate masterfd = -1; 2927c478bd9Sstevel@tonic-gate return (-1); 2937c478bd9Sstevel@tonic-gate } 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate /* 2977c478bd9Sstevel@tonic-gate * Routines to handle pty creation upon zone entry and to shuttle I/O back 2987c478bd9Sstevel@tonic-gate * and forth between the two terminals. We also compute and store the 2997c478bd9Sstevel@tonic-gate * name of the slave terminal associated with the master side. 3007c478bd9Sstevel@tonic-gate */ 3017c478bd9Sstevel@tonic-gate static int 3027c478bd9Sstevel@tonic-gate get_master_pty() 3037c478bd9Sstevel@tonic-gate { 3047c478bd9Sstevel@tonic-gate if ((masterfd = open("/dev/ptmx", O_RDWR|O_NONBLOCK)) < 0) { 3057c478bd9Sstevel@tonic-gate zperror(gettext("failed to obtain a pseudo-tty")); 3067c478bd9Sstevel@tonic-gate return (-1); 3077c478bd9Sstevel@tonic-gate } 3087c478bd9Sstevel@tonic-gate if (tcgetattr(STDIN_FILENO, &save_termios) == -1) { 3097c478bd9Sstevel@tonic-gate zperror(gettext("failed to get terminal settings from stdin")); 3107c478bd9Sstevel@tonic-gate return (-1); 3117c478bd9Sstevel@tonic-gate } 3127c478bd9Sstevel@tonic-gate (void) ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&winsize); 3137c478bd9Sstevel@tonic-gate 3147c478bd9Sstevel@tonic-gate return (0); 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* 3187c478bd9Sstevel@tonic-gate * This is a bit tricky; normally a pts device will belong to the zone it 3197c478bd9Sstevel@tonic-gate * is granted to. But in the case of "entering" a zone, we need to establish 3207c478bd9Sstevel@tonic-gate * the pty before entering the zone so that we can vector I/O to and from it 3217c478bd9Sstevel@tonic-gate * from the global zone. 3227c478bd9Sstevel@tonic-gate * 3237c478bd9Sstevel@tonic-gate * We use the zonept() call to let the ptm driver know what we are up to; 3247c478bd9Sstevel@tonic-gate * the only other hairy bit is the setting of zoneslavename (which happens 3257c478bd9Sstevel@tonic-gate * above, in get_master_pty()). 3267c478bd9Sstevel@tonic-gate */ 3277c478bd9Sstevel@tonic-gate static int 3287c478bd9Sstevel@tonic-gate init_slave_pty(zoneid_t zoneid, char *zonepath) 3297c478bd9Sstevel@tonic-gate { 3307c478bd9Sstevel@tonic-gate int slavefd = -1; 3317c478bd9Sstevel@tonic-gate char *slavename, zoneslavename[MAXPATHLEN]; 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate /* 3347c478bd9Sstevel@tonic-gate * Set slave permissions, zone the pts, then unlock it. 3357c478bd9Sstevel@tonic-gate */ 3367c478bd9Sstevel@tonic-gate if (grantpt(masterfd) != 0) { 3377c478bd9Sstevel@tonic-gate zperror(gettext("grantpt failed")); 3387c478bd9Sstevel@tonic-gate return (-1); 3397c478bd9Sstevel@tonic-gate } 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate if (unlockpt(masterfd) != 0) { 3427c478bd9Sstevel@tonic-gate zperror(gettext("unlockpt failed")); 3437c478bd9Sstevel@tonic-gate return (-1); 3447c478bd9Sstevel@tonic-gate } 3457c478bd9Sstevel@tonic-gate 3467c478bd9Sstevel@tonic-gate /* 3477c478bd9Sstevel@tonic-gate * We must open the slave side before zoning this pty; otherwise 3487c478bd9Sstevel@tonic-gate * the kernel would refuse us the open-- zoning a pty makes it 3497c478bd9Sstevel@tonic-gate * inaccessible to the global zone. Since we are trying to 3507c478bd9Sstevel@tonic-gate * open the device node via the $ZONEPATH/dev path, we may have to 3517c478bd9Sstevel@tonic-gate * give devfsadm a kick to get it to create the device node for 3527c478bd9Sstevel@tonic-gate * us. Normally this would "just work" because pt_chmod inside 3537c478bd9Sstevel@tonic-gate * the zone would take care of it for us. 3547c478bd9Sstevel@tonic-gate * 3557c478bd9Sstevel@tonic-gate * Later we'll close the slave out when once we've opened it again 3567c478bd9Sstevel@tonic-gate * from within the target zone. Blarg. 3577c478bd9Sstevel@tonic-gate */ 3587c478bd9Sstevel@tonic-gate if ((slavename = ptsname(masterfd)) == NULL) { 3597c478bd9Sstevel@tonic-gate zperror(gettext("failed to get name for pseudo-tty")); 3607c478bd9Sstevel@tonic-gate return (-1); 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate (void) snprintf(zoneslavename, sizeof (zoneslavename), "%s%s", 3647c478bd9Sstevel@tonic-gate zonepath, slavename); 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate if ((slavefd = open(zoneslavename, O_RDWR)) < 0) { 3677c478bd9Sstevel@tonic-gate di_devlink_handle_t h = di_devlink_init("pts", DI_MAKE_LINK); 3687c478bd9Sstevel@tonic-gate if (h != NULL) { 3697c478bd9Sstevel@tonic-gate (void) di_devlink_fini(&h); 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate if ((slavefd = open(zoneslavename, O_RDWR)) < 0) { 3727c478bd9Sstevel@tonic-gate zerror(gettext("failed to open %s: %s"), zoneslavename, 3737c478bd9Sstevel@tonic-gate strerror(errno)); 3747c478bd9Sstevel@tonic-gate return (-1); 3757c478bd9Sstevel@tonic-gate } 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* 3797c478bd9Sstevel@tonic-gate * Push hardware emulation (ptem), line discipline (ldterm), 3807c478bd9Sstevel@tonic-gate * and V7/4BSD/Xenix compatibility (ttcompat) modules. 3817c478bd9Sstevel@tonic-gate */ 3827c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ptem") == -1) { 3837c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ptem module")); 3847c478bd9Sstevel@tonic-gate if (!failsafe) 3857c478bd9Sstevel@tonic-gate goto bad; 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate /* 3897c478bd9Sstevel@tonic-gate * Anchor the stream to prevent malicious I_POPs; we prefer to do 3907c478bd9Sstevel@tonic-gate * this prior to entering the zone so that we can detect any errors 3917c478bd9Sstevel@tonic-gate * early, and so that we can set the anchor from the global zone. 3927c478bd9Sstevel@tonic-gate */ 3937c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_ANCHOR) == -1) { 3947c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stream anchor")); 3957c478bd9Sstevel@tonic-gate if (!failsafe) 3967c478bd9Sstevel@tonic-gate goto bad; 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ldterm") == -1) { 4007c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ldterm module")); 4017c478bd9Sstevel@tonic-gate if (!failsafe) 4027c478bd9Sstevel@tonic-gate goto bad; 4037c478bd9Sstevel@tonic-gate } 4047c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) { 4057c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ttcompat module")); 4067c478bd9Sstevel@tonic-gate if (!failsafe) 4077c478bd9Sstevel@tonic-gate goto bad; 4087c478bd9Sstevel@tonic-gate } 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate /* 4117c478bd9Sstevel@tonic-gate * Propagate terminal settings from the external term to the new one. 4127c478bd9Sstevel@tonic-gate */ 4137c478bd9Sstevel@tonic-gate if (tcsetattr(slavefd, TCSAFLUSH, &save_termios) == -1) { 4147c478bd9Sstevel@tonic-gate zperror(gettext("failed to set terminal settings")); 4157c478bd9Sstevel@tonic-gate if (!failsafe) 4167c478bd9Sstevel@tonic-gate goto bad; 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate (void) ioctl(slavefd, TIOCSWINSZ, (char *)&winsize); 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate if (zonept(masterfd, zoneid) != 0) { 4217c478bd9Sstevel@tonic-gate zperror(gettext("could not set zoneid of pty")); 4227c478bd9Sstevel@tonic-gate goto bad; 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate return (slavefd); 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate bad: 4287c478bd9Sstevel@tonic-gate (void) close(slavefd); 4297c478bd9Sstevel@tonic-gate return (-1); 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate /* 4337c478bd9Sstevel@tonic-gate * Place terminal into raw mode. 4347c478bd9Sstevel@tonic-gate */ 4357c478bd9Sstevel@tonic-gate static int 4367c478bd9Sstevel@tonic-gate set_tty_rawmode(int fd) 4377c478bd9Sstevel@tonic-gate { 4387c478bd9Sstevel@tonic-gate struct termios term; 4397c478bd9Sstevel@tonic-gate if (tcgetattr(fd, &term) < 0) { 4407c478bd9Sstevel@tonic-gate zperror(gettext("failed to get user terminal settings")); 4417c478bd9Sstevel@tonic-gate return (-1); 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate /* Stash for later, so we can revert back to previous mode */ 4457c478bd9Sstevel@tonic-gate save_termios = term; 4467c478bd9Sstevel@tonic-gate save_fd = fd; 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate /* disable 8->7 bit strip, start/stop, enable any char to restart */ 4497c478bd9Sstevel@tonic-gate term.c_iflag &= ~(ISTRIP|IXON|IXANY); 4507c478bd9Sstevel@tonic-gate /* disable NL->CR, CR->NL, ignore CR, UPPER->lower */ 4517c478bd9Sstevel@tonic-gate term.c_iflag &= ~(INLCR|ICRNL|IGNCR|IUCLC); 4527c478bd9Sstevel@tonic-gate /* disable output post-processing */ 4537c478bd9Sstevel@tonic-gate term.c_oflag &= ~OPOST; 4547c478bd9Sstevel@tonic-gate /* disable canonical mode, signal chars, echo & extended functions */ 4557c478bd9Sstevel@tonic-gate term.c_lflag &= ~(ICANON|ISIG|ECHO|IEXTEN); 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate term.c_cc[VMIN] = 1; /* byte-at-a-time */ 4587c478bd9Sstevel@tonic-gate term.c_cc[VTIME] = 0; 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)) { 4617c478bd9Sstevel@tonic-gate zperror(gettext("failed to set user terminal to raw mode")); 4627c478bd9Sstevel@tonic-gate return (-1); 4637c478bd9Sstevel@tonic-gate } 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate /* 4667c478bd9Sstevel@tonic-gate * We need to know the value of VEOF so that we can properly process for 4677c478bd9Sstevel@tonic-gate * client-side ~<EOF>. But we have obliterated VEOF in term, 4687c478bd9Sstevel@tonic-gate * because VMIN overloads the same array slot in non-canonical mode. 4697c478bd9Sstevel@tonic-gate * Stupid @&^%! 4707c478bd9Sstevel@tonic-gate * 4717c478bd9Sstevel@tonic-gate * So here we construct the "effective" termios from the current 4727c478bd9Sstevel@tonic-gate * terminal settings, and the corrected VEOF and VEOL settings. 4737c478bd9Sstevel@tonic-gate */ 4747c478bd9Sstevel@tonic-gate if (tcgetattr(STDIN_FILENO, &effective_termios) < 0) { 4757c478bd9Sstevel@tonic-gate zperror(gettext("failed to get user terminal settings")); 4767c478bd9Sstevel@tonic-gate return (-1); 4777c478bd9Sstevel@tonic-gate } 4787c478bd9Sstevel@tonic-gate effective_termios.c_cc[VEOF] = save_termios.c_cc[VEOF]; 4797c478bd9Sstevel@tonic-gate effective_termios.c_cc[VEOL] = save_termios.c_cc[VEOL]; 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate return (0); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate /* 4857c478bd9Sstevel@tonic-gate * Copy terminal window size from our terminal to the pts. 4867c478bd9Sstevel@tonic-gate */ 4877c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4887c478bd9Sstevel@tonic-gate static void 4897c478bd9Sstevel@tonic-gate sigwinch(int s) 4907c478bd9Sstevel@tonic-gate { 4917c478bd9Sstevel@tonic-gate struct winsize ws; 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate if (ioctl(0, TIOCGWINSZ, &ws) == 0) 4947c478bd9Sstevel@tonic-gate (void) ioctl(masterfd, TIOCSWINSZ, &ws); 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate static void 4987c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4997c478bd9Sstevel@tonic-gate sigcld(int s) 5007c478bd9Sstevel@tonic-gate { 5017c478bd9Sstevel@tonic-gate int status; 5027c478bd9Sstevel@tonic-gate pid_t pid; 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate /* 5057c478bd9Sstevel@tonic-gate * Peek at the exit status. If this isn't the process we cared 5067c478bd9Sstevel@tonic-gate * about, then just reap it. 5077c478bd9Sstevel@tonic-gate */ 5087c478bd9Sstevel@tonic-gate if ((pid = waitpid(child_pid, &status, WNOHANG|WNOWAIT)) != -1) { 5097c478bd9Sstevel@tonic-gate if (pid == child_pid && 5107c478bd9Sstevel@tonic-gate (WIFEXITED(status) || WIFSIGNALED(status))) 5117c478bd9Sstevel@tonic-gate dead = 1; 5127c478bd9Sstevel@tonic-gate else 5137c478bd9Sstevel@tonic-gate (void) waitpid(pid, &status, WNOHANG); 5147c478bd9Sstevel@tonic-gate } 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate /* 5187c478bd9Sstevel@tonic-gate * Some signals (currently, SIGINT) must be forwarded on to the process 5197c478bd9Sstevel@tonic-gate * group of the child process. 5207c478bd9Sstevel@tonic-gate */ 5217c478bd9Sstevel@tonic-gate static void 5227c478bd9Sstevel@tonic-gate sig_forward(int s) 5237c478bd9Sstevel@tonic-gate { 5247c478bd9Sstevel@tonic-gate if (child_pid != -1) { 5257c478bd9Sstevel@tonic-gate pid_t pgid = getpgid(child_pid); 5267c478bd9Sstevel@tonic-gate if (pgid != -1) 5277c478bd9Sstevel@tonic-gate (void) sigsend(P_PGID, pgid, s); 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate /* 5327c478bd9Sstevel@tonic-gate * reset terminal settings for global environment 5337c478bd9Sstevel@tonic-gate */ 5347c478bd9Sstevel@tonic-gate static void 5357c478bd9Sstevel@tonic-gate reset_tty() 5367c478bd9Sstevel@tonic-gate { 5377c478bd9Sstevel@tonic-gate (void) tcsetattr(save_fd, TCSADRAIN, &save_termios); 5387c478bd9Sstevel@tonic-gate } 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate /* 5417c478bd9Sstevel@tonic-gate * Convert character to printable representation, for display with locally 5427c478bd9Sstevel@tonic-gate * echoed command characters (like when we need to display ~^D) 5437c478bd9Sstevel@tonic-gate */ 5447c478bd9Sstevel@tonic-gate static void 5457c478bd9Sstevel@tonic-gate canonify(char c, char *cc) 5467c478bd9Sstevel@tonic-gate { 5477c478bd9Sstevel@tonic-gate if (isprint(c)) { 5487c478bd9Sstevel@tonic-gate cc[0] = c; 5497c478bd9Sstevel@tonic-gate cc[1] = '\0'; 5507c478bd9Sstevel@tonic-gate } else if (c >= 0 && c <= 31) { /* ^@ through ^_ */ 5517c478bd9Sstevel@tonic-gate cc[0] = '^'; 5527c478bd9Sstevel@tonic-gate cc[1] = c + '@'; 5537c478bd9Sstevel@tonic-gate cc[2] = '\0'; 5547c478bd9Sstevel@tonic-gate } else { 5557c478bd9Sstevel@tonic-gate cc[0] = '\\'; 5567c478bd9Sstevel@tonic-gate cc[1] = ((c >> 6) & 7) + '0'; 5577c478bd9Sstevel@tonic-gate cc[2] = ((c >> 3) & 7) + '0'; 5587c478bd9Sstevel@tonic-gate cc[3] = (c & 7) + '0'; 5597c478bd9Sstevel@tonic-gate cc[4] = '\0'; 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate /* 5647c478bd9Sstevel@tonic-gate * process_user_input watches the input stream for the escape sequence for 5657c478bd9Sstevel@tonic-gate * 'quit' (by default, tilde-period). Because we might be fed just one 5667c478bd9Sstevel@tonic-gate * keystroke at a time, state associated with the user input (are we at the 5677c478bd9Sstevel@tonic-gate * beginning of the line? are we locally echoing the next character?) is 5687c478bd9Sstevel@tonic-gate * maintained by beginning_of_line and local_echo across calls to the routine. 5697c478bd9Sstevel@tonic-gate * 5707c478bd9Sstevel@tonic-gate * This routine returns -1 when the 'quit' escape sequence has been issued, 5717c478bd9Sstevel@tonic-gate * and 0 otherwise. 5727c478bd9Sstevel@tonic-gate */ 5737c478bd9Sstevel@tonic-gate static int 5747c478bd9Sstevel@tonic-gate process_user_input(int outfd, char *buf, size_t nbytes) 5757c478bd9Sstevel@tonic-gate { 5767c478bd9Sstevel@tonic-gate static boolean_t beginning_of_line = B_TRUE; 5777c478bd9Sstevel@tonic-gate static boolean_t local_echo = B_FALSE; 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate char c = *buf; 5807c478bd9Sstevel@tonic-gate for (c = *buf; nbytes > 0; c = *buf, --nbytes) { 5817c478bd9Sstevel@tonic-gate buf++; 5827c478bd9Sstevel@tonic-gate if (beginning_of_line && !nocmdchar) { 5837c478bd9Sstevel@tonic-gate beginning_of_line = B_FALSE; 5847c478bd9Sstevel@tonic-gate if (c == cmdchar) { 5857c478bd9Sstevel@tonic-gate local_echo = B_TRUE; 5867c478bd9Sstevel@tonic-gate continue; 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate } else if (local_echo) { 5897c478bd9Sstevel@tonic-gate local_echo = B_FALSE; 5907c478bd9Sstevel@tonic-gate if (c == '.' || c == effective_termios.c_cc[VEOF]) { 5917c478bd9Sstevel@tonic-gate char cc[CANONIFY_LEN]; 5927c478bd9Sstevel@tonic-gate canonify(c, cc); 5937c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, &cmdchar, 1); 5947c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, cc, strlen(cc)); 5957c478bd9Sstevel@tonic-gate return (-1); 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate } 5987c478bd9Sstevel@tonic-gate if (write(outfd, &c, 1) <= 0) 5997c478bd9Sstevel@tonic-gate return (-1); 6007c478bd9Sstevel@tonic-gate beginning_of_line = (c == '\r' || c == '\n' || 6017c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VKILL] || 6027c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VEOL] || 6037c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VSUSP] || 6047c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VINTR]); 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate return (0); 6077c478bd9Sstevel@tonic-gate } 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate /* 6107c478bd9Sstevel@tonic-gate * This is the main I/O loop, and is shared across all zlogin modes. 6117c478bd9Sstevel@tonic-gate * Parameters: 6127c478bd9Sstevel@tonic-gate * stdin_fd: The fd representing 'stdin' for the slave side; input to 6137c478bd9Sstevel@tonic-gate * the zone will be written here. 6147c478bd9Sstevel@tonic-gate * 6157c478bd9Sstevel@tonic-gate * stdout_fd: The fd representing 'stdout' for the slave side; output 6167c478bd9Sstevel@tonic-gate * from the zone will arrive here. 6177c478bd9Sstevel@tonic-gate * 6187c478bd9Sstevel@tonic-gate * stderr_fd: The fd representing 'stderr' for the slave side; output 6197c478bd9Sstevel@tonic-gate * from the zone will arrive here. 6207c478bd9Sstevel@tonic-gate * 6217c478bd9Sstevel@tonic-gate * raw_mode: If TRUE, then no processing (for example, for '~.') will 6227c478bd9Sstevel@tonic-gate * be performed on the input coming from STDIN. 6237c478bd9Sstevel@tonic-gate * 6247c478bd9Sstevel@tonic-gate * stderr_fd may be specified as -1 if there is no stderr (only non-interactive 6257c478bd9Sstevel@tonic-gate * mode supplies a stderr). 6267c478bd9Sstevel@tonic-gate * 6277c478bd9Sstevel@tonic-gate */ 6287c478bd9Sstevel@tonic-gate static void 6297c478bd9Sstevel@tonic-gate doio(int stdin_fd, int stdout_fd, int stderr_fd, boolean_t raw_mode) 6307c478bd9Sstevel@tonic-gate { 6317c478bd9Sstevel@tonic-gate struct pollfd pollfds[3]; 6327c478bd9Sstevel@tonic-gate char ibuf[BUFSIZ]; 6337c478bd9Sstevel@tonic-gate int cc, ret; 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate /* read from stdout of zone and write to stdout of global zone */ 6367c478bd9Sstevel@tonic-gate pollfds[0].fd = stdout_fd; 6377c478bd9Sstevel@tonic-gate pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; 6387c478bd9Sstevel@tonic-gate 6397c478bd9Sstevel@tonic-gate /* read from stderr of zone and write to stderr of global zone */ 6407c478bd9Sstevel@tonic-gate pollfds[1].fd = stderr_fd; 6417c478bd9Sstevel@tonic-gate pollfds[1].events = pollfds[0].events; 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate /* read from stdin of global zone and write to stdin of zone */ 6447c478bd9Sstevel@tonic-gate pollfds[2].fd = STDIN_FILENO; 6457c478bd9Sstevel@tonic-gate pollfds[2].events = pollfds[0].events; 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate for (;;) { 6487c478bd9Sstevel@tonic-gate pollfds[0].revents = pollfds[1].revents = 6497c478bd9Sstevel@tonic-gate pollfds[2].revents = 0; 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate if (dead) 6527c478bd9Sstevel@tonic-gate break; 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate ret = poll(pollfds, 6557c478bd9Sstevel@tonic-gate sizeof (pollfds) / sizeof (struct pollfd), -1); 6567c478bd9Sstevel@tonic-gate if (ret == -1 && errno != EINTR) { 6577c478bd9Sstevel@tonic-gate perror("poll failed"); 6587c478bd9Sstevel@tonic-gate break; 6597c478bd9Sstevel@tonic-gate } 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate if (errno == EINTR && dead) { 6627c478bd9Sstevel@tonic-gate break; 6637c478bd9Sstevel@tonic-gate } 6647c478bd9Sstevel@tonic-gate 6657c478bd9Sstevel@tonic-gate /* event from master side stdout */ 6667c478bd9Sstevel@tonic-gate if (pollfds[0].revents) { 6677c478bd9Sstevel@tonic-gate if (pollfds[0].revents & 6687c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 6697c478bd9Sstevel@tonic-gate cc = read(stdout_fd, ibuf, BUFSIZ); 6707c478bd9Sstevel@tonic-gate if (cc == -1 && (errno != EINTR || dead)) 6717c478bd9Sstevel@tonic-gate break; 6727c478bd9Sstevel@tonic-gate if (cc == 0) /* EOF */ 6737c478bd9Sstevel@tonic-gate break; 6747c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, ibuf, cc); 6757c478bd9Sstevel@tonic-gate } else { 6767c478bd9Sstevel@tonic-gate pollerr = pollfds[0].revents; 6777c478bd9Sstevel@tonic-gate break; 6787c478bd9Sstevel@tonic-gate } 6797c478bd9Sstevel@tonic-gate } 6807c478bd9Sstevel@tonic-gate 6817c478bd9Sstevel@tonic-gate /* event from master side stderr */ 6827c478bd9Sstevel@tonic-gate if (pollfds[1].revents) { 6837c478bd9Sstevel@tonic-gate if (pollfds[1].revents & 6847c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 6857c478bd9Sstevel@tonic-gate cc = read(stderr_fd, ibuf, BUFSIZ); 6867c478bd9Sstevel@tonic-gate if (cc == -1 && (errno != EINTR || dead)) 6877c478bd9Sstevel@tonic-gate break; 6887c478bd9Sstevel@tonic-gate if (cc == 0) /* EOF */ 6897c478bd9Sstevel@tonic-gate break; 6907c478bd9Sstevel@tonic-gate (void) write(STDERR_FILENO, ibuf, cc); 6917c478bd9Sstevel@tonic-gate } else { 6927c478bd9Sstevel@tonic-gate pollerr = pollfds[1].revents; 6937c478bd9Sstevel@tonic-gate break; 6947c478bd9Sstevel@tonic-gate } 6957c478bd9Sstevel@tonic-gate } 6967c478bd9Sstevel@tonic-gate 6977c478bd9Sstevel@tonic-gate /* event from user STDIN side */ 6987c478bd9Sstevel@tonic-gate if (pollfds[2].revents) { 6997c478bd9Sstevel@tonic-gate if (pollfds[2].revents & 7007c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 7017c478bd9Sstevel@tonic-gate cc = read(STDIN_FILENO, ibuf, BUFSIZ); 7027c478bd9Sstevel@tonic-gate if (cc == -1 && (errno != EINTR || dead)) 7037c478bd9Sstevel@tonic-gate break; 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate /* 7067c478bd9Sstevel@tonic-gate * stdin fd is stdin of the target; so, 7077c478bd9Sstevel@tonic-gate * the thing we'll write the user data *to*. 7087c478bd9Sstevel@tonic-gate * 7097c478bd9Sstevel@tonic-gate * Also, unlike on the output side, we 7107c478bd9Sstevel@tonic-gate * propagate zero-length messages to the 7117c478bd9Sstevel@tonic-gate * other side. 7127c478bd9Sstevel@tonic-gate */ 7137c478bd9Sstevel@tonic-gate if (raw_mode == B_TRUE) { 7147c478bd9Sstevel@tonic-gate if (write(stdin_fd, ibuf, cc) == -1) 7157c478bd9Sstevel@tonic-gate break; 7167c478bd9Sstevel@tonic-gate } else { 7177c478bd9Sstevel@tonic-gate if (process_user_input(stdin_fd, ibuf, 7187c478bd9Sstevel@tonic-gate cc) == -1) 7197c478bd9Sstevel@tonic-gate break; 7207c478bd9Sstevel@tonic-gate } 7217c478bd9Sstevel@tonic-gate } else if (raw_mode == B_TRUE && 7227c478bd9Sstevel@tonic-gate pollfds[2].revents & POLLHUP) { 7237c478bd9Sstevel@tonic-gate /* 7247c478bd9Sstevel@tonic-gate * It's OK to get a POLLHUP on STDIN-- it 7257c478bd9Sstevel@tonic-gate * always happens if you do: 7267c478bd9Sstevel@tonic-gate * 7277c478bd9Sstevel@tonic-gate * echo foo | zlogin <zone> <command> 7287c478bd9Sstevel@tonic-gate * 7297c478bd9Sstevel@tonic-gate * We reset fd to -1 in this case to clear 7307c478bd9Sstevel@tonic-gate * the condition and write an EOF to the 7317c478bd9Sstevel@tonic-gate * other side in order to wrap things up. 7327c478bd9Sstevel@tonic-gate */ 7337c478bd9Sstevel@tonic-gate pollfds[2].fd = -1; 7347c478bd9Sstevel@tonic-gate (void) write(stdin_fd, ibuf, 0); 7357c478bd9Sstevel@tonic-gate } else { 7367c478bd9Sstevel@tonic-gate pollerr = pollfds[2].revents; 7377c478bd9Sstevel@tonic-gate break; 7387c478bd9Sstevel@tonic-gate } 7397c478bd9Sstevel@tonic-gate } 7407c478bd9Sstevel@tonic-gate } 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate /* 7437c478bd9Sstevel@tonic-gate * We are in the midst of dying, but try to poll with a short 7447c478bd9Sstevel@tonic-gate * timeout to see if we can catch the last bit of I/O from the 7457c478bd9Sstevel@tonic-gate * children. 7467c478bd9Sstevel@tonic-gate */ 7477c478bd9Sstevel@tonic-gate pollfds[0].revents = pollfds[1].revents = pollfds[2].revents = 0; 7487c478bd9Sstevel@tonic-gate (void) poll(pollfds, 7497c478bd9Sstevel@tonic-gate sizeof (pollfds) / sizeof (struct pollfd), 100); 7507c478bd9Sstevel@tonic-gate if (pollfds[0].revents & 7517c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 7527c478bd9Sstevel@tonic-gate if ((cc = read(stdout_fd, ibuf, BUFSIZ)) > 0) 7537c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, ibuf, cc); 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate if (pollfds[1].revents & 7567c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 7577c478bd9Sstevel@tonic-gate if ((cc = read(stderr_fd, ibuf, BUFSIZ)) > 0) 7587c478bd9Sstevel@tonic-gate (void) write(STDERR_FILENO, ibuf, cc); 7597c478bd9Sstevel@tonic-gate } 7607c478bd9Sstevel@tonic-gate } 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate /* 7637c478bd9Sstevel@tonic-gate * Prepare argv array for exec'd process; if we're passing commands to the 7647c478bd9Sstevel@tonic-gate * new process, then use su(1M) to do the invocation. Otherwise, use 7657c478bd9Sstevel@tonic-gate * 'login -z <from_zonename> -f' (-z is an undocumented option which tells 7667c478bd9Sstevel@tonic-gate * login that we're coming from another zone, and to disregard its CONSOLE 7677c478bd9Sstevel@tonic-gate * checks). 7687c478bd9Sstevel@tonic-gate */ 7697c478bd9Sstevel@tonic-gate static char ** 7707c478bd9Sstevel@tonic-gate prep_args(char *login, char **argv) 7717c478bd9Sstevel@tonic-gate { 7727c478bd9Sstevel@tonic-gate int argc = 0, a = 0, i, n = -1; 7737c478bd9Sstevel@tonic-gate char **new_argv; 7747c478bd9Sstevel@tonic-gate 7757c478bd9Sstevel@tonic-gate if (argv != NULL) { 7767c478bd9Sstevel@tonic-gate size_t subshell_len = 1; 7777c478bd9Sstevel@tonic-gate char *subshell; 7787c478bd9Sstevel@tonic-gate 7797c478bd9Sstevel@tonic-gate while (argv[argc] != NULL) 7807c478bd9Sstevel@tonic-gate argc++; 7817c478bd9Sstevel@tonic-gate 7827c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 7837c478bd9Sstevel@tonic-gate subshell_len += strlen(argv[i]) + 1; 7847c478bd9Sstevel@tonic-gate } 7857c478bd9Sstevel@tonic-gate if ((subshell = calloc(1, subshell_len)) == NULL) 7867c478bd9Sstevel@tonic-gate return (NULL); 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 7897c478bd9Sstevel@tonic-gate (void) strcat(subshell, argv[i]); 7907c478bd9Sstevel@tonic-gate (void) strcat(subshell, " "); 7917c478bd9Sstevel@tonic-gate } 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate if (failsafe) { 7947c478bd9Sstevel@tonic-gate n = 4; 7957c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 7967c478bd9Sstevel@tonic-gate return (NULL); 7977c478bd9Sstevel@tonic-gate 7987c478bd9Sstevel@tonic-gate new_argv[a++] = FAILSAFESHELL; 7997c478bd9Sstevel@tonic-gate } else { 8007c478bd9Sstevel@tonic-gate n = 5; 8017c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 8027c478bd9Sstevel@tonic-gate return (NULL); 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate new_argv[a++] = SUPATH; 8057c478bd9Sstevel@tonic-gate new_argv[a++] = login; 8067c478bd9Sstevel@tonic-gate } 8077c478bd9Sstevel@tonic-gate new_argv[a++] = "-c"; 8087c478bd9Sstevel@tonic-gate new_argv[a++] = subshell; 8097c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 8107c478bd9Sstevel@tonic-gate assert(a == n); 8117c478bd9Sstevel@tonic-gate } else { 8127c478bd9Sstevel@tonic-gate if (failsafe) { 8137c478bd9Sstevel@tonic-gate n = 2; 8147c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 8157c478bd9Sstevel@tonic-gate return (NULL); 8167c478bd9Sstevel@tonic-gate new_argv[a++] = FAILSAFESHELL; 8177c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 8187c478bd9Sstevel@tonic-gate } else { 8197c478bd9Sstevel@tonic-gate n = 6; 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 8227c478bd9Sstevel@tonic-gate return (NULL); 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate new_argv[a++] = LOGINPATH; 8257c478bd9Sstevel@tonic-gate new_argv[a++] = "-z"; 8267c478bd9Sstevel@tonic-gate new_argv[a++] = "global"; /* hardcode, for now */ 8277c478bd9Sstevel@tonic-gate new_argv[a++] = "-f"; 8287c478bd9Sstevel@tonic-gate new_argv[a++] = login; 8297c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate /* 8337c478bd9Sstevel@tonic-gate * If this assert ever trips, it's because we've botched the setup 8347c478bd9Sstevel@tonic-gate * of ARGV above-- it's too large or too small. 8357c478bd9Sstevel@tonic-gate */ 8367c478bd9Sstevel@tonic-gate assert(n == a); 8377c478bd9Sstevel@tonic-gate return (new_argv); 8387c478bd9Sstevel@tonic-gate } 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate /* 8417c478bd9Sstevel@tonic-gate * Helper routine for prep_env below. 8427c478bd9Sstevel@tonic-gate */ 8437c478bd9Sstevel@tonic-gate static char * 8447c478bd9Sstevel@tonic-gate add_env(char *name, char *value) 8457c478bd9Sstevel@tonic-gate { 8467c478bd9Sstevel@tonic-gate size_t sz = strlen(name) + strlen(value) + 2; /* name, =, value, NUL */ 8477c478bd9Sstevel@tonic-gate char *str; 8487c478bd9Sstevel@tonic-gate 8497c478bd9Sstevel@tonic-gate if ((str = malloc(sz)) == NULL) 8507c478bd9Sstevel@tonic-gate return (NULL); 8517c478bd9Sstevel@tonic-gate 8527c478bd9Sstevel@tonic-gate (void) snprintf(str, sz, "%s=%s", name, value); 8537c478bd9Sstevel@tonic-gate return (str); 8547c478bd9Sstevel@tonic-gate } 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate /* 8577c478bd9Sstevel@tonic-gate * Prepare envp array for exec'd process. 8587c478bd9Sstevel@tonic-gate */ 8597c478bd9Sstevel@tonic-gate static char ** 8607c478bd9Sstevel@tonic-gate prep_env() 8617c478bd9Sstevel@tonic-gate { 8627c478bd9Sstevel@tonic-gate int e = 0, size = 1; 8637c478bd9Sstevel@tonic-gate char **new_env, *estr; 8647c478bd9Sstevel@tonic-gate char *term = getenv("TERM"); 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate size++; /* for $PATH */ 8677c478bd9Sstevel@tonic-gate if (term != NULL) 8687c478bd9Sstevel@tonic-gate size++; 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate /* 8717c478bd9Sstevel@tonic-gate * In failsafe mode we set $HOME, since '-l' isn't valid in this mode. 8727c478bd9Sstevel@tonic-gate * We also set $SHELL, since neither login nor su will be around to do 8737c478bd9Sstevel@tonic-gate * it. 8747c478bd9Sstevel@tonic-gate */ 8757c478bd9Sstevel@tonic-gate if (failsafe) 8767c478bd9Sstevel@tonic-gate size += 2; 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate if ((new_env = malloc(sizeof (char *) * size)) == NULL) 8797c478bd9Sstevel@tonic-gate return (NULL); 8807c478bd9Sstevel@tonic-gate 8817c478bd9Sstevel@tonic-gate if ((estr = add_env("PATH", DEF_PATH)) == NULL) 8827c478bd9Sstevel@tonic-gate return (NULL); 8837c478bd9Sstevel@tonic-gate new_env[e++] = estr; 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate if (term != NULL) { 8867c478bd9Sstevel@tonic-gate if ((estr = add_env("TERM", term)) == NULL) 8877c478bd9Sstevel@tonic-gate return (NULL); 8887c478bd9Sstevel@tonic-gate new_env[e++] = estr; 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate if (failsafe) { 8927c478bd9Sstevel@tonic-gate if ((estr = add_env("HOME", "/")) == NULL) 8937c478bd9Sstevel@tonic-gate return (NULL); 8947c478bd9Sstevel@tonic-gate new_env[e++] = estr; 8957c478bd9Sstevel@tonic-gate 8967c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", FAILSAFESHELL)) == NULL) 8977c478bd9Sstevel@tonic-gate return (NULL); 8987c478bd9Sstevel@tonic-gate new_env[e++] = estr; 8997c478bd9Sstevel@tonic-gate } 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate new_env[e++] = NULL; 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate assert(e == size); 9047c478bd9Sstevel@tonic-gate 9057c478bd9Sstevel@tonic-gate return (new_env); 9067c478bd9Sstevel@tonic-gate } 9077c478bd9Sstevel@tonic-gate 9087c478bd9Sstevel@tonic-gate /* 9097c478bd9Sstevel@tonic-gate * Finish the preparation of the envp array for exec'd non-interactive 9107c478bd9Sstevel@tonic-gate * zlogins. This is called in the child process *after* we zone_enter(), since 9117c478bd9Sstevel@tonic-gate * it derives things we can only know within the zone, such as $HOME, $SHELL, 9127c478bd9Sstevel@tonic-gate * etc. We need only do this in the non-interactive, mode, since otherwise 9137c478bd9Sstevel@tonic-gate * login(1) will do it. We don't do this in failsafe mode, since it presents 9147c478bd9Sstevel@tonic-gate * additional ways in which the command could fail, and we'd prefer to avoid 9157c478bd9Sstevel@tonic-gate * that. 9167c478bd9Sstevel@tonic-gate */ 9177c478bd9Sstevel@tonic-gate static char ** 9187c478bd9Sstevel@tonic-gate prep_env_noninteractive(char *login, char **env) 9197c478bd9Sstevel@tonic-gate { 9207c478bd9Sstevel@tonic-gate size_t size; 9217c478bd9Sstevel@tonic-gate struct passwd *pw; 9227c478bd9Sstevel@tonic-gate char **new_env; 9237c478bd9Sstevel@tonic-gate int e, i; 9247c478bd9Sstevel@tonic-gate char *estr; 9257c478bd9Sstevel@tonic-gate char varmail[LOGNAME_MAX + 11]; /* strlen(/var/mail/) = 10, NUL */ 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate assert(env != NULL); 9287c478bd9Sstevel@tonic-gate assert(failsafe == 0); 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate /* 9317c478bd9Sstevel@tonic-gate * Get existing envp size. 9327c478bd9Sstevel@tonic-gate */ 9337c478bd9Sstevel@tonic-gate for (size = 0; env[size] != NULL; size++) 9347c478bd9Sstevel@tonic-gate ; 9357c478bd9Sstevel@tonic-gate e = size; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate /* 9387c478bd9Sstevel@tonic-gate * Finish filling out the environment; we duplicate the environment 9397c478bd9Sstevel@tonic-gate * setup described in login(1), for lack of a better precedent. 9407c478bd9Sstevel@tonic-gate */ 9417c478bd9Sstevel@tonic-gate if ((pw = getpwnam(login)) != NULL) { 9427c478bd9Sstevel@tonic-gate size += 3; /* LOGNAME, HOME, MAIL */ 9437c478bd9Sstevel@tonic-gate } 9447c478bd9Sstevel@tonic-gate size++; /* always fill in SHELL */ 9457c478bd9Sstevel@tonic-gate size++; /* terminating NULL */ 9467c478bd9Sstevel@tonic-gate 9477c478bd9Sstevel@tonic-gate if ((new_env = malloc(sizeof (char *) * size)) == NULL) 9487c478bd9Sstevel@tonic-gate goto malloc_fail; 9497c478bd9Sstevel@tonic-gate 9507c478bd9Sstevel@tonic-gate /* 9517c478bd9Sstevel@tonic-gate * Copy existing elements of env into new_env. 9527c478bd9Sstevel@tonic-gate */ 9537c478bd9Sstevel@tonic-gate for (i = 0; env[i] != NULL; i++) { 9547c478bd9Sstevel@tonic-gate if ((new_env[i] = strdup(env[i])) == NULL) 9557c478bd9Sstevel@tonic-gate goto malloc_fail; 9567c478bd9Sstevel@tonic-gate } 9577c478bd9Sstevel@tonic-gate assert(e == i); 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate if (pw != NULL) { 9607c478bd9Sstevel@tonic-gate if ((estr = add_env("LOGNAME", pw->pw_name)) == NULL) 9617c478bd9Sstevel@tonic-gate goto malloc_fail; 9627c478bd9Sstevel@tonic-gate new_env[e++] = estr; 9637c478bd9Sstevel@tonic-gate 9647c478bd9Sstevel@tonic-gate if ((estr = add_env("HOME", pw->pw_dir)) == NULL) 9657c478bd9Sstevel@tonic-gate goto malloc_fail; 9667c478bd9Sstevel@tonic-gate new_env[e++] = estr; 9677c478bd9Sstevel@tonic-gate 9687c478bd9Sstevel@tonic-gate if (chdir(pw->pw_dir) != 0) 9697c478bd9Sstevel@tonic-gate zerror(gettext("Could not chdir to home directory " 9707c478bd9Sstevel@tonic-gate "%s: %s"), pw->pw_dir, strerror(errno)); 9717c478bd9Sstevel@tonic-gate 9727c478bd9Sstevel@tonic-gate (void) snprintf(varmail, sizeof (varmail), "/var/mail/%s", 9737c478bd9Sstevel@tonic-gate pw->pw_name); 9747c478bd9Sstevel@tonic-gate if ((estr = add_env("MAIL", varmail)) == NULL) 9757c478bd9Sstevel@tonic-gate goto malloc_fail; 9767c478bd9Sstevel@tonic-gate new_env[e++] = estr; 9777c478bd9Sstevel@tonic-gate } 9787c478bd9Sstevel@tonic-gate 9797c478bd9Sstevel@tonic-gate if (pw != NULL && strlen(pw->pw_shell) > 0) { 9807c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", pw->pw_shell)) == NULL) 9817c478bd9Sstevel@tonic-gate goto malloc_fail; 9827c478bd9Sstevel@tonic-gate new_env[e++] = estr; 9837c478bd9Sstevel@tonic-gate } else { 9847c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", DEFAULTSHELL)) == NULL) 9857c478bd9Sstevel@tonic-gate goto malloc_fail; 9867c478bd9Sstevel@tonic-gate new_env[e++] = estr; 9877c478bd9Sstevel@tonic-gate } 9887c478bd9Sstevel@tonic-gate 9897c478bd9Sstevel@tonic-gate new_env[e++] = NULL; /* add terminating NULL */ 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate assert(e == size); 9927c478bd9Sstevel@tonic-gate return (new_env); 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate malloc_fail: 9957c478bd9Sstevel@tonic-gate zperror(gettext("failed to allocate memory for process environment")); 9967c478bd9Sstevel@tonic-gate return (NULL); 9977c478bd9Sstevel@tonic-gate } 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate static int 10007c478bd9Sstevel@tonic-gate close_func(void *slavefd, int fd) 10017c478bd9Sstevel@tonic-gate { 10027c478bd9Sstevel@tonic-gate if (fd != *(int *)slavefd) 10037c478bd9Sstevel@tonic-gate (void) close(fd); 10047c478bd9Sstevel@tonic-gate return (0); 10057c478bd9Sstevel@tonic-gate } 10067c478bd9Sstevel@tonic-gate 10077c478bd9Sstevel@tonic-gate static void 10087c478bd9Sstevel@tonic-gate set_cmdchar(char *cmdcharstr) 10097c478bd9Sstevel@tonic-gate { 10107c478bd9Sstevel@tonic-gate char c; 10117c478bd9Sstevel@tonic-gate long lc; 10127c478bd9Sstevel@tonic-gate 10137c478bd9Sstevel@tonic-gate if ((c = *cmdcharstr) != '\\') { 10147c478bd9Sstevel@tonic-gate cmdchar = c; 10157c478bd9Sstevel@tonic-gate return; 10167c478bd9Sstevel@tonic-gate } 10177c478bd9Sstevel@tonic-gate 10187c478bd9Sstevel@tonic-gate c = cmdcharstr[1]; 10197c478bd9Sstevel@tonic-gate if (c == '\0' || c == '\\') { 10207c478bd9Sstevel@tonic-gate cmdchar = '\\'; 10217c478bd9Sstevel@tonic-gate return; 10227c478bd9Sstevel@tonic-gate } 10237c478bd9Sstevel@tonic-gate 10247c478bd9Sstevel@tonic-gate if (c < '0' || c > '7') { 10257c478bd9Sstevel@tonic-gate zerror(gettext("Unrecognized escape character option %s"), 10267c478bd9Sstevel@tonic-gate cmdcharstr); 10277c478bd9Sstevel@tonic-gate usage(); 10287c478bd9Sstevel@tonic-gate } 10297c478bd9Sstevel@tonic-gate 10307c478bd9Sstevel@tonic-gate lc = strtol(cmdcharstr + 1, NULL, 8); 10317c478bd9Sstevel@tonic-gate if (lc < 0 || lc > 255) { 10327c478bd9Sstevel@tonic-gate zerror(gettext("Octal escape character '%s' too large"), 10337c478bd9Sstevel@tonic-gate cmdcharstr); 10347c478bd9Sstevel@tonic-gate usage(); 10357c478bd9Sstevel@tonic-gate } 10367c478bd9Sstevel@tonic-gate cmdchar = (char)lc; 10377c478bd9Sstevel@tonic-gate } 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate static int 10407c478bd9Sstevel@tonic-gate setup_utmpx(char *slavename) 10417c478bd9Sstevel@tonic-gate { 10427c478bd9Sstevel@tonic-gate struct utmpx ut; 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate bzero(&ut, sizeof (ut)); 10457c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_user, ".zlogin", sizeof (ut.ut_user)); 10467c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_line, slavename, sizeof (ut.ut_line)); 10477c478bd9Sstevel@tonic-gate ut.ut_pid = getpid(); 10487c478bd9Sstevel@tonic-gate ut.ut_id[0] = 'z'; 10497c478bd9Sstevel@tonic-gate ut.ut_id[1] = ut.ut_id[2] = ut.ut_id[3] = (char)SC_WILDC; 10507c478bd9Sstevel@tonic-gate ut.ut_type = LOGIN_PROCESS; 10517c478bd9Sstevel@tonic-gate (void) time(&ut.ut_tv.tv_sec); 10527c478bd9Sstevel@tonic-gate 10537c478bd9Sstevel@tonic-gate if (makeutx(&ut) == NULL) { 10547c478bd9Sstevel@tonic-gate zerror(gettext("makeutx failed")); 10557c478bd9Sstevel@tonic-gate return (-1); 10567c478bd9Sstevel@tonic-gate } 10577c478bd9Sstevel@tonic-gate return (0); 10587c478bd9Sstevel@tonic-gate } 10597c478bd9Sstevel@tonic-gate 10607c478bd9Sstevel@tonic-gate static void 10617c478bd9Sstevel@tonic-gate release_lock_file(int lockfd) 10627c478bd9Sstevel@tonic-gate { 10637c478bd9Sstevel@tonic-gate (void) close(lockfd); 10647c478bd9Sstevel@tonic-gate } 10657c478bd9Sstevel@tonic-gate 10667c478bd9Sstevel@tonic-gate static int 10677c478bd9Sstevel@tonic-gate grab_lock_file(const char *zone_name, int *lockfd) 10687c478bd9Sstevel@tonic-gate { 10697c478bd9Sstevel@tonic-gate char pathbuf[PATH_MAX]; 10707c478bd9Sstevel@tonic-gate struct flock flock; 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate if (mkdir(ZONES_TMPDIR, S_IRWXU) < 0 && errno != EEXIST) { 10737c478bd9Sstevel@tonic-gate zerror(gettext("could not mkdir %s: %s"), ZONES_TMPDIR, 10747c478bd9Sstevel@tonic-gate strerror(errno)); 10757c478bd9Sstevel@tonic-gate return (-1); 10767c478bd9Sstevel@tonic-gate } 10777c478bd9Sstevel@tonic-gate (void) chmod(ZONES_TMPDIR, S_IRWXU); 10787c478bd9Sstevel@tonic-gate (void) snprintf(pathbuf, sizeof (pathbuf), "%s/%s.zoneadm.lock", 10797c478bd9Sstevel@tonic-gate ZONES_TMPDIR, zone_name); 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 10827c478bd9Sstevel@tonic-gate zerror(gettext("could not open %s: %s"), pathbuf, 10837c478bd9Sstevel@tonic-gate strerror(errno)); 10847c478bd9Sstevel@tonic-gate return (-1); 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate /* 10877c478bd9Sstevel@tonic-gate * Lock the file to synchronize with other zoneadmds 10887c478bd9Sstevel@tonic-gate */ 10897c478bd9Sstevel@tonic-gate flock.l_type = F_WRLCK; 10907c478bd9Sstevel@tonic-gate flock.l_whence = SEEK_SET; 10917c478bd9Sstevel@tonic-gate flock.l_start = (off_t)0; 10927c478bd9Sstevel@tonic-gate flock.l_len = (off_t)0; 10937c478bd9Sstevel@tonic-gate if (fcntl(*lockfd, F_SETLKW, &flock) < 0) { 10947c478bd9Sstevel@tonic-gate zerror(gettext("unable to lock %s: %s"), pathbuf, 10957c478bd9Sstevel@tonic-gate strerror(errno)); 10967c478bd9Sstevel@tonic-gate release_lock_file(*lockfd); 10977c478bd9Sstevel@tonic-gate return (-1); 10987c478bd9Sstevel@tonic-gate } 10997c478bd9Sstevel@tonic-gate return (Z_OK); 11007c478bd9Sstevel@tonic-gate } 11017c478bd9Sstevel@tonic-gate 11027c478bd9Sstevel@tonic-gate static int 11037c478bd9Sstevel@tonic-gate start_zoneadmd(const char *zone_name) 11047c478bd9Sstevel@tonic-gate { 11057c478bd9Sstevel@tonic-gate pid_t retval; 11067c478bd9Sstevel@tonic-gate int pstatus = 0, error = -1, lockfd, doorfd; 11077c478bd9Sstevel@tonic-gate struct door_info info; 11087c478bd9Sstevel@tonic-gate char doorpath[MAXPATHLEN]; 11097c478bd9Sstevel@tonic-gate 11107c478bd9Sstevel@tonic-gate (void) snprintf(doorpath, sizeof (doorpath), ZONE_DOOR_PATH, zone_name); 11117c478bd9Sstevel@tonic-gate 11127c478bd9Sstevel@tonic-gate if (grab_lock_file(zone_name, &lockfd) != Z_OK) 11137c478bd9Sstevel@tonic-gate return (-1); 11147c478bd9Sstevel@tonic-gate /* 11157c478bd9Sstevel@tonic-gate * We must do the door check with the lock held. Otherwise, we 11167c478bd9Sstevel@tonic-gate * might race against another zoneadm/zlogin process and wind 11177c478bd9Sstevel@tonic-gate * up with two processes trying to start zoneadmd at the same 11187c478bd9Sstevel@tonic-gate * time. zoneadmd will detect this, and fail, but we prefer this 11197c478bd9Sstevel@tonic-gate * to be as seamless as is practical, from a user perspective. 11207c478bd9Sstevel@tonic-gate */ 11217c478bd9Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 11227c478bd9Sstevel@tonic-gate if (errno != ENOENT) { 11237c478bd9Sstevel@tonic-gate zerror("failed to open %s: %s", doorpath, 11247c478bd9Sstevel@tonic-gate strerror(errno)); 11257c478bd9Sstevel@tonic-gate goto out; 11267c478bd9Sstevel@tonic-gate } 11277c478bd9Sstevel@tonic-gate } else { 11287c478bd9Sstevel@tonic-gate /* 11297c478bd9Sstevel@tonic-gate * Seems to be working ok. 11307c478bd9Sstevel@tonic-gate */ 11317c478bd9Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 11327c478bd9Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 11337c478bd9Sstevel@tonic-gate error = 0; 11347c478bd9Sstevel@tonic-gate goto out; 11357c478bd9Sstevel@tonic-gate } 11367c478bd9Sstevel@tonic-gate } 11377c478bd9Sstevel@tonic-gate 11387c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 11397c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 11407c478bd9Sstevel@tonic-gate goto out; 11417c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { 11427c478bd9Sstevel@tonic-gate /* child process */ 11437c478bd9Sstevel@tonic-gate (void) execl("/usr/lib/zones/zoneadmd", "zoneadmd", "-z", 11447c478bd9Sstevel@tonic-gate zone_name, NULL); 11457c478bd9Sstevel@tonic-gate zperror(gettext("could not exec zoneadmd")); 11467c478bd9Sstevel@tonic-gate _exit(1); 11477c478bd9Sstevel@tonic-gate } 11487c478bd9Sstevel@tonic-gate 11497c478bd9Sstevel@tonic-gate /* parent process */ 11507c478bd9Sstevel@tonic-gate do { 11517c478bd9Sstevel@tonic-gate retval = waitpid(child_pid, &pstatus, 0); 11527c478bd9Sstevel@tonic-gate } while (retval != child_pid); 11537c478bd9Sstevel@tonic-gate if (WIFSIGNALED(pstatus) || 11547c478bd9Sstevel@tonic-gate (WIFEXITED(pstatus) && WEXITSTATUS(pstatus) != 0)) { 11557c478bd9Sstevel@tonic-gate zerror(gettext("could not start %s"), "zoneadmd"); 11567c478bd9Sstevel@tonic-gate goto out; 11577c478bd9Sstevel@tonic-gate } 11587c478bd9Sstevel@tonic-gate error = 0; 11597c478bd9Sstevel@tonic-gate out: 11607c478bd9Sstevel@tonic-gate release_lock_file(lockfd); 11617c478bd9Sstevel@tonic-gate (void) close(doorfd); 11627c478bd9Sstevel@tonic-gate return (error); 11637c478bd9Sstevel@tonic-gate } 11647c478bd9Sstevel@tonic-gate 11657c478bd9Sstevel@tonic-gate static int 11667c478bd9Sstevel@tonic-gate init_template(void) 11677c478bd9Sstevel@tonic-gate { 11687c478bd9Sstevel@tonic-gate int fd; 11697c478bd9Sstevel@tonic-gate int err = 0; 11707c478bd9Sstevel@tonic-gate 11717c478bd9Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/template", O_RDWR); 11727c478bd9Sstevel@tonic-gate if (fd == -1) 11737c478bd9Sstevel@tonic-gate return (-1); 11747c478bd9Sstevel@tonic-gate 11757c478bd9Sstevel@tonic-gate /* 11767c478bd9Sstevel@tonic-gate * zlogin doesn't do anything with the contract. 11777c478bd9Sstevel@tonic-gate * Deliver no events, don't inherit, and allow it to be orphaned. 11787c478bd9Sstevel@tonic-gate */ 11797c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_critical(fd, 0); 11807c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_informative(fd, 0); 11817c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); 11827c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); 11837c478bd9Sstevel@tonic-gate if (err || ct_tmpl_activate(fd)) { 11847c478bd9Sstevel@tonic-gate (void) close(fd); 11857c478bd9Sstevel@tonic-gate return (-1); 11867c478bd9Sstevel@tonic-gate } 11877c478bd9Sstevel@tonic-gate 11887c478bd9Sstevel@tonic-gate return (fd); 11897c478bd9Sstevel@tonic-gate } 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate static int 11927c478bd9Sstevel@tonic-gate noninteractive_login(char *zonename, zoneid_t zoneid, char *login, 11937c478bd9Sstevel@tonic-gate char **new_args, char **new_env) 11947c478bd9Sstevel@tonic-gate { 11957c478bd9Sstevel@tonic-gate pid_t retval; 11967c478bd9Sstevel@tonic-gate int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2]; 11977c478bd9Sstevel@tonic-gate int child_status; 11987c478bd9Sstevel@tonic-gate int tmpl_fd; 11997c478bd9Sstevel@tonic-gate sigset_t block_cld; 12007c478bd9Sstevel@tonic-gate 12017c478bd9Sstevel@tonic-gate if ((tmpl_fd = init_template()) == -1) { 12027c478bd9Sstevel@tonic-gate reset_tty(); 12037c478bd9Sstevel@tonic-gate zperror(gettext("could not create contract")); 12047c478bd9Sstevel@tonic-gate return (1); 12057c478bd9Sstevel@tonic-gate } 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate if (pipe(stdin_pipe) != 0) { 12087c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDIN pipe")); 12097c478bd9Sstevel@tonic-gate return (1); 12107c478bd9Sstevel@tonic-gate } 12117c478bd9Sstevel@tonic-gate /* 12127c478bd9Sstevel@tonic-gate * When the user types ^D, we get a zero length message on STDIN. 12137c478bd9Sstevel@tonic-gate * We need to echo that down the pipe to send it to the other side; 12147c478bd9Sstevel@tonic-gate * but by default, pipes don't propagate zero-length messages. We 12157c478bd9Sstevel@tonic-gate * toggle that behavior off using I_SWROPT. See streamio(7i). 12167c478bd9Sstevel@tonic-gate */ 12177c478bd9Sstevel@tonic-gate if (ioctl(stdin_pipe[0], I_SWROPT, SNDZERO) != 0) { 12187c478bd9Sstevel@tonic-gate zperror(gettext("could not configure STDIN pipe")); 12197c478bd9Sstevel@tonic-gate return (1); 12207c478bd9Sstevel@tonic-gate 12217c478bd9Sstevel@tonic-gate } 12227c478bd9Sstevel@tonic-gate if (pipe(stdout_pipe) != 0) { 12237c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDOUT pipe")); 12247c478bd9Sstevel@tonic-gate return (1); 12257c478bd9Sstevel@tonic-gate } 12267c478bd9Sstevel@tonic-gate if (pipe(stderr_pipe) != 0) { 12277c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDERR pipe")); 12287c478bd9Sstevel@tonic-gate return (1); 12297c478bd9Sstevel@tonic-gate } 12307c478bd9Sstevel@tonic-gate 12317c478bd9Sstevel@tonic-gate /* 12327c478bd9Sstevel@tonic-gate * If any of the pipe FD's winds up being less than STDERR, then we 12337c478bd9Sstevel@tonic-gate * have a mess on our hands-- and we are lacking some of the I/O 12347c478bd9Sstevel@tonic-gate * streams we would expect anyway. So we bail. 12357c478bd9Sstevel@tonic-gate */ 12367c478bd9Sstevel@tonic-gate if (stdin_pipe[0] <= STDERR_FILENO || 12377c478bd9Sstevel@tonic-gate stdin_pipe[1] <= STDERR_FILENO || 12387c478bd9Sstevel@tonic-gate stdout_pipe[0] <= STDERR_FILENO || 12397c478bd9Sstevel@tonic-gate stdout_pipe[1] <= STDERR_FILENO || 12407c478bd9Sstevel@tonic-gate stderr_pipe[0] <= STDERR_FILENO || 12417c478bd9Sstevel@tonic-gate stderr_pipe[1] <= STDERR_FILENO) { 12427c478bd9Sstevel@tonic-gate zperror(gettext("process lacks valid STDIN, STDOUT, STDERR")); 12437c478bd9Sstevel@tonic-gate return (1); 12447c478bd9Sstevel@tonic-gate } 12457c478bd9Sstevel@tonic-gate 12467c478bd9Sstevel@tonic-gate if (prefork_dropprivs() != 0) { 12477c478bd9Sstevel@tonic-gate zperror(gettext("could not allocate privilege set")); 12487c478bd9Sstevel@tonic-gate return (1); 12497c478bd9Sstevel@tonic-gate } 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, sigcld); 12527c478bd9Sstevel@tonic-gate (void) sigemptyset(&block_cld); 12537c478bd9Sstevel@tonic-gate (void) sigaddset(&block_cld, SIGCLD); 12547c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); 12557c478bd9Sstevel@tonic-gate 12567c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 12577c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 12587c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 12597c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 12607c478bd9Sstevel@tonic-gate return (1); 12617c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { /* child process */ 12627c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 12637c478bd9Sstevel@tonic-gate 12647c478bd9Sstevel@tonic-gate /* 12657c478bd9Sstevel@tonic-gate * Do a dance to get the pipes hooked up as FD's 0, 1 and 2. 12667c478bd9Sstevel@tonic-gate */ 12677c478bd9Sstevel@tonic-gate (void) close(STDIN_FILENO); 12687c478bd9Sstevel@tonic-gate (void) close(STDOUT_FILENO); 12697c478bd9Sstevel@tonic-gate (void) close(STDERR_FILENO); 12707c478bd9Sstevel@tonic-gate (void) dup2(stdin_pipe[1], STDIN_FILENO); 12717c478bd9Sstevel@tonic-gate (void) dup2(stdout_pipe[1], STDOUT_FILENO); 12727c478bd9Sstevel@tonic-gate (void) dup2(stderr_pipe[1], STDERR_FILENO); 12737c478bd9Sstevel@tonic-gate (void) closefrom(STDERR_FILENO + 1); 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 12767c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 12777c478bd9Sstevel@tonic-gate /* 12787c478bd9Sstevel@tonic-gate * In case any of stdin, stdout or stderr are streams, 12797c478bd9Sstevel@tonic-gate * anchor them to prevent malicious I_POPs. 12807c478bd9Sstevel@tonic-gate */ 12817c478bd9Sstevel@tonic-gate (void) ioctl(STDIN_FILENO, I_ANCHOR); 12827c478bd9Sstevel@tonic-gate (void) ioctl(STDOUT_FILENO, I_ANCHOR); 12837c478bd9Sstevel@tonic-gate (void) ioctl(STDERR_FILENO, I_ANCHOR); 12847c478bd9Sstevel@tonic-gate 12857c478bd9Sstevel@tonic-gate if (zone_enter(zoneid) == -1) { 12867c478bd9Sstevel@tonic-gate zerror(gettext("could not enter zone %s: %s"), 12877c478bd9Sstevel@tonic-gate zonename, strerror(errno)); 12887c478bd9Sstevel@tonic-gate _exit(1); 12897c478bd9Sstevel@tonic-gate } 12907c478bd9Sstevel@tonic-gate 12917c478bd9Sstevel@tonic-gate if (!failsafe) 12927c478bd9Sstevel@tonic-gate new_env = prep_env_noninteractive(login, new_env); 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate if (new_env == NULL) { 12957c478bd9Sstevel@tonic-gate _exit(1); 12967c478bd9Sstevel@tonic-gate } 12977c478bd9Sstevel@tonic-gate 12987c478bd9Sstevel@tonic-gate /* 12997c478bd9Sstevel@tonic-gate * Move into a new process group; the zone_enter will have 13007c478bd9Sstevel@tonic-gate * placed us into zsched's session, and we want to be in 13017c478bd9Sstevel@tonic-gate * a unique process group. 13027c478bd9Sstevel@tonic-gate */ 13037c478bd9Sstevel@tonic-gate (void) setpgid(getpid(), getpid()); 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate (void) execve(new_args[0], new_args, new_env); 13067c478bd9Sstevel@tonic-gate zperror(gettext("exec failure")); 13077c478bd9Sstevel@tonic-gate _exit(1); 13087c478bd9Sstevel@tonic-gate } 13097c478bd9Sstevel@tonic-gate /* parent */ 13107c478bd9Sstevel@tonic-gate (void) sigset(SIGINT, sig_forward); 13117c478bd9Sstevel@tonic-gate 13127c478bd9Sstevel@tonic-gate postfork_dropprivs(); 13137c478bd9Sstevel@tonic-gate 13147c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 13157c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 13167c478bd9Sstevel@tonic-gate 13177c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 13187c478bd9Sstevel@tonic-gate doio(stdin_pipe[0], stdout_pipe[0], stderr_pipe[0], B_TRUE); 13197c478bd9Sstevel@tonic-gate do { 13207c478bd9Sstevel@tonic-gate retval = waitpid(child_pid, &child_status, 0); 13217c478bd9Sstevel@tonic-gate if (retval == -1) { 13227c478bd9Sstevel@tonic-gate child_status = 0; 13237c478bd9Sstevel@tonic-gate } 13247c478bd9Sstevel@tonic-gate } while (retval != child_pid && errno != ECHILD); 13257c478bd9Sstevel@tonic-gate 13267c478bd9Sstevel@tonic-gate return (WEXITSTATUS(child_status)); 13277c478bd9Sstevel@tonic-gate } 13287c478bd9Sstevel@tonic-gate 13297c478bd9Sstevel@tonic-gate int 13307c478bd9Sstevel@tonic-gate main(int argc, char **argv) 13317c478bd9Sstevel@tonic-gate { 13327c478bd9Sstevel@tonic-gate int arg, console = 0; 13337c478bd9Sstevel@tonic-gate zoneid_t zoneid; 13347c478bd9Sstevel@tonic-gate zone_state_t st; 13357c478bd9Sstevel@tonic-gate char *login = "root"; 13367c478bd9Sstevel@tonic-gate int lflag = 0; 13377c478bd9Sstevel@tonic-gate char *zonename = NULL; 13387c478bd9Sstevel@tonic-gate char **proc_args = NULL; 13397c478bd9Sstevel@tonic-gate char **new_args, **new_env; 13407c478bd9Sstevel@tonic-gate sigset_t block_cld; 13417c478bd9Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 13427c478bd9Sstevel@tonic-gate char *slavename, slaveshortname[MAXPATHLEN]; 13437c478bd9Sstevel@tonic-gate priv_set_t *privset; 13447c478bd9Sstevel@tonic-gate int tmpl_fd; 1345*108322fbScarlsonj struct stat sb; 1346*108322fbScarlsonj char kernzone[ZONENAME_MAX]; 13477c478bd9Sstevel@tonic-gate 13487c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 13497c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 13507c478bd9Sstevel@tonic-gate 13517c478bd9Sstevel@tonic-gate (void) getpname(argv[0]); 13527c478bd9Sstevel@tonic-gate 1353*108322fbScarlsonj while ((arg = getopt(argc, argv, "ECR:Se:l:")) != EOF) { 13547c478bd9Sstevel@tonic-gate switch (arg) { 13557c478bd9Sstevel@tonic-gate case 'C': 13567c478bd9Sstevel@tonic-gate console = 1; 13577c478bd9Sstevel@tonic-gate break; 13587c478bd9Sstevel@tonic-gate case 'E': 13597c478bd9Sstevel@tonic-gate nocmdchar = 1; 13607c478bd9Sstevel@tonic-gate break; 1361*108322fbScarlsonj case 'R': /* undocumented */ 1362*108322fbScarlsonj if (*optarg != '/') { 1363*108322fbScarlsonj zerror(gettext("root path must be absolute.")); 1364*108322fbScarlsonj exit(2); 1365*108322fbScarlsonj } 1366*108322fbScarlsonj if (stat(optarg, &sb) == -1 || !S_ISDIR(sb.st_mode)) { 1367*108322fbScarlsonj zerror( 1368*108322fbScarlsonj gettext("root path must be a directory.")); 1369*108322fbScarlsonj exit(2); 1370*108322fbScarlsonj } 1371*108322fbScarlsonj zonecfg_set_root(optarg); 1372*108322fbScarlsonj break; 13737c478bd9Sstevel@tonic-gate case 'S': 13747c478bd9Sstevel@tonic-gate failsafe = 1; 13757c478bd9Sstevel@tonic-gate break; 13767c478bd9Sstevel@tonic-gate case 'e': 13777c478bd9Sstevel@tonic-gate set_cmdchar(optarg); 13787c478bd9Sstevel@tonic-gate break; 13797c478bd9Sstevel@tonic-gate case 'l': 13807c478bd9Sstevel@tonic-gate login = optarg; 13817c478bd9Sstevel@tonic-gate lflag = 1; 13827c478bd9Sstevel@tonic-gate break; 13837c478bd9Sstevel@tonic-gate default: 13847c478bd9Sstevel@tonic-gate usage(); 13857c478bd9Sstevel@tonic-gate } 13867c478bd9Sstevel@tonic-gate } 13877c478bd9Sstevel@tonic-gate 13887c478bd9Sstevel@tonic-gate if (console != 0 && lflag != 0) { 13897c478bd9Sstevel@tonic-gate zerror(gettext("-l may not be specified for console login")); 13907c478bd9Sstevel@tonic-gate usage(); 13917c478bd9Sstevel@tonic-gate } 13927c478bd9Sstevel@tonic-gate 13937c478bd9Sstevel@tonic-gate if (console != 0 && failsafe != 0) { 13947c478bd9Sstevel@tonic-gate zerror(gettext("-S may not be specified for console login")); 13957c478bd9Sstevel@tonic-gate usage(); 13967c478bd9Sstevel@tonic-gate } 13977c478bd9Sstevel@tonic-gate 1398*108322fbScarlsonj if (console != 0 && zonecfg_in_alt_root()) { 1399*108322fbScarlsonj zerror(gettext("-R may not be specified for console login")); 1400*108322fbScarlsonj exit(2); 1401*108322fbScarlsonj } 1402*108322fbScarlsonj 14037c478bd9Sstevel@tonic-gate if (failsafe != 0 && lflag != 0) { 14047c478bd9Sstevel@tonic-gate zerror(gettext("-l may not be specified for failsafe login")); 14057c478bd9Sstevel@tonic-gate usage(); 14067c478bd9Sstevel@tonic-gate } 14077c478bd9Sstevel@tonic-gate 14087c478bd9Sstevel@tonic-gate if (optind == (argc - 1)) { 14097c478bd9Sstevel@tonic-gate /* 14107c478bd9Sstevel@tonic-gate * zone name, no process name; this should be an interactive 14117c478bd9Sstevel@tonic-gate * as long as STDIN is really a tty. 14127c478bd9Sstevel@tonic-gate */ 14137c478bd9Sstevel@tonic-gate if (isatty(STDIN_FILENO)) 14147c478bd9Sstevel@tonic-gate interactive = 1; 14157c478bd9Sstevel@tonic-gate zonename = argv[optind]; 14167c478bd9Sstevel@tonic-gate } else if (optind < (argc - 1)) { 14177c478bd9Sstevel@tonic-gate if (console) { 14187c478bd9Sstevel@tonic-gate zerror(gettext("Commands may not be specified for " 14197c478bd9Sstevel@tonic-gate "console login.")); 14207c478bd9Sstevel@tonic-gate usage(); 14217c478bd9Sstevel@tonic-gate } 14227c478bd9Sstevel@tonic-gate /* zone name and process name, and possibly some args */ 14237c478bd9Sstevel@tonic-gate zonename = argv[optind]; 14247c478bd9Sstevel@tonic-gate proc_args = &argv[optind + 1]; 14257c478bd9Sstevel@tonic-gate interactive = 0; 14267c478bd9Sstevel@tonic-gate } else { 14277c478bd9Sstevel@tonic-gate usage(); 14287c478bd9Sstevel@tonic-gate } 14297c478bd9Sstevel@tonic-gate 14307c478bd9Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 14317c478bd9Sstevel@tonic-gate zerror(gettext("'%s' may only be used from the global zone"), 14327c478bd9Sstevel@tonic-gate pname); 14337c478bd9Sstevel@tonic-gate return (1); 14347c478bd9Sstevel@tonic-gate } 14357c478bd9Sstevel@tonic-gate 14367c478bd9Sstevel@tonic-gate if (strcmp(zonename, GLOBAL_ZONENAME) == 0) { 14377c478bd9Sstevel@tonic-gate zerror(gettext("'%s' not applicable to the global zone"), 14387c478bd9Sstevel@tonic-gate pname); 14397c478bd9Sstevel@tonic-gate return (1); 14407c478bd9Sstevel@tonic-gate } 14417c478bd9Sstevel@tonic-gate 14427c478bd9Sstevel@tonic-gate if (zone_get_state(zonename, &st) != Z_OK) { 14437c478bd9Sstevel@tonic-gate zerror(gettext("zone '%s' unknown"), zonename); 14447c478bd9Sstevel@tonic-gate return (1); 14457c478bd9Sstevel@tonic-gate } 14467c478bd9Sstevel@tonic-gate 14477c478bd9Sstevel@tonic-gate if (st < ZONE_STATE_INSTALLED) { 14487c478bd9Sstevel@tonic-gate zerror(gettext("cannot login to a zone which is '%s'"), 14497c478bd9Sstevel@tonic-gate zone_state_str(st)); 14507c478bd9Sstevel@tonic-gate return (1); 14517c478bd9Sstevel@tonic-gate } 14527c478bd9Sstevel@tonic-gate 14537c478bd9Sstevel@tonic-gate /* 14547c478bd9Sstevel@tonic-gate * In both console and non-console cases, we require all privs. 14557c478bd9Sstevel@tonic-gate * In the console case, because we may need to startup zoneadmd. 14567c478bd9Sstevel@tonic-gate * In the non-console case in order to do zone_enter(2), zonept() 14577c478bd9Sstevel@tonic-gate * and other tasks. 14587c478bd9Sstevel@tonic-gate * 14597c478bd9Sstevel@tonic-gate * Future work: this solution is temporary. Ultimately, we need to 14607c478bd9Sstevel@tonic-gate * move to a flexible system which allows the global admin to 14617c478bd9Sstevel@tonic-gate * designate that a particular user can zlogin (and probably zlogin 14627c478bd9Sstevel@tonic-gate * -C) to a particular zone. This all-root business we have now is 14637c478bd9Sstevel@tonic-gate * quite sketchy. 14647c478bd9Sstevel@tonic-gate */ 14657c478bd9Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 14667c478bd9Sstevel@tonic-gate zperror(gettext("priv_allocset failed")); 14677c478bd9Sstevel@tonic-gate return (1); 14687c478bd9Sstevel@tonic-gate } 14697c478bd9Sstevel@tonic-gate 14707c478bd9Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 14717c478bd9Sstevel@tonic-gate zperror(gettext("getppriv failed")); 14727c478bd9Sstevel@tonic-gate priv_freeset(privset); 14737c478bd9Sstevel@tonic-gate return (1); 14747c478bd9Sstevel@tonic-gate } 14757c478bd9Sstevel@tonic-gate 14767c478bd9Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 14777c478bd9Sstevel@tonic-gate zerror(gettext("You lack sufficient privilege to run " 14787c478bd9Sstevel@tonic-gate "this command (all privs required)")); 14797c478bd9Sstevel@tonic-gate priv_freeset(privset); 14807c478bd9Sstevel@tonic-gate return (1); 14817c478bd9Sstevel@tonic-gate } 14827c478bd9Sstevel@tonic-gate priv_freeset(privset); 14837c478bd9Sstevel@tonic-gate 14847c478bd9Sstevel@tonic-gate /* 14857c478bd9Sstevel@tonic-gate * The console is a separate case from the rest of the code; handle 14867c478bd9Sstevel@tonic-gate * it first. 14877c478bd9Sstevel@tonic-gate */ 14887c478bd9Sstevel@tonic-gate if (console) { 14897c478bd9Sstevel@tonic-gate 14907c478bd9Sstevel@tonic-gate /* 14917c478bd9Sstevel@tonic-gate * Ensure that zoneadmd for this zone is running. 14927c478bd9Sstevel@tonic-gate */ 14937c478bd9Sstevel@tonic-gate if (start_zoneadmd(zonename) == -1) 14947c478bd9Sstevel@tonic-gate return (1); 14957c478bd9Sstevel@tonic-gate 14967c478bd9Sstevel@tonic-gate /* 14977c478bd9Sstevel@tonic-gate * Make contact with zoneadmd. 14987c478bd9Sstevel@tonic-gate */ 14997c478bd9Sstevel@tonic-gate if (get_console_master(zonename) == -1) 15007c478bd9Sstevel@tonic-gate return (1); 15017c478bd9Sstevel@tonic-gate 15027c478bd9Sstevel@tonic-gate (void) printf(gettext("[Connected to zone '%s' console]\n"), 15037c478bd9Sstevel@tonic-gate zonename); 15047c478bd9Sstevel@tonic-gate 15057c478bd9Sstevel@tonic-gate if (set_tty_rawmode(STDIN_FILENO) == -1) { 15067c478bd9Sstevel@tonic-gate reset_tty(); 15077c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stdin pty to raw mode")); 15087c478bd9Sstevel@tonic-gate return (1); 15097c478bd9Sstevel@tonic-gate } 15107c478bd9Sstevel@tonic-gate 15117c478bd9Sstevel@tonic-gate (void) sigset(SIGWINCH, sigwinch); 15127c478bd9Sstevel@tonic-gate (void) sigwinch(0); 15137c478bd9Sstevel@tonic-gate 15147c478bd9Sstevel@tonic-gate /* 15157c478bd9Sstevel@tonic-gate * Run the I/O loop until we get disconnected. 15167c478bd9Sstevel@tonic-gate */ 15177c478bd9Sstevel@tonic-gate doio(masterfd, masterfd, -1, B_FALSE); 15187c478bd9Sstevel@tonic-gate reset_tty(); 15197c478bd9Sstevel@tonic-gate (void) printf(gettext("\n[Connection to zone '%s' console " 15207c478bd9Sstevel@tonic-gate "closed]\n"), zonename); 15217c478bd9Sstevel@tonic-gate 15227c478bd9Sstevel@tonic-gate return (0); 15237c478bd9Sstevel@tonic-gate } 15247c478bd9Sstevel@tonic-gate 1525*108322fbScarlsonj if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) { 15267c478bd9Sstevel@tonic-gate zerror(gettext("login allowed only to running zones " 15277c478bd9Sstevel@tonic-gate "(%s is '%s')."), zonename, zone_state_str(st)); 15287c478bd9Sstevel@tonic-gate return (1); 15297c478bd9Sstevel@tonic-gate } 15307c478bd9Sstevel@tonic-gate 1531*108322fbScarlsonj (void) strlcpy(kernzone, zonename, sizeof (kernzone)); 1532*108322fbScarlsonj if (zonecfg_in_alt_root()) { 1533*108322fbScarlsonj FILE *fp = zonecfg_open_scratch("", B_FALSE); 1534*108322fbScarlsonj 1535*108322fbScarlsonj if (fp == NULL || zonecfg_find_scratch(fp, zonename, 1536*108322fbScarlsonj zonecfg_get_root(), kernzone, sizeof (kernzone)) == -1) { 1537*108322fbScarlsonj zerror(gettext("cannot find scratch zone %s"), 1538*108322fbScarlsonj zonename); 1539*108322fbScarlsonj if (fp != NULL) 1540*108322fbScarlsonj zonecfg_close_scratch(fp); 1541*108322fbScarlsonj return (1); 1542*108322fbScarlsonj } 1543*108322fbScarlsonj zonecfg_close_scratch(fp); 1544*108322fbScarlsonj } 1545*108322fbScarlsonj 1546*108322fbScarlsonj if ((zoneid = getzoneidbyname(kernzone)) == -1) { 15477c478bd9Sstevel@tonic-gate zerror(gettext("failed to get zoneid for zone '%s'"), 15487c478bd9Sstevel@tonic-gate zonename); 15497c478bd9Sstevel@tonic-gate return (1); 15507c478bd9Sstevel@tonic-gate } 15517c478bd9Sstevel@tonic-gate 1552*108322fbScarlsonj /* 1553*108322fbScarlsonj * We need the zone path only if we are setting up a pty. 1554*108322fbScarlsonj */ 15557c478bd9Sstevel@tonic-gate if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) == -1) { 15567c478bd9Sstevel@tonic-gate zerror(gettext("could not get root path for zone %s"), 15577c478bd9Sstevel@tonic-gate zonename); 15587c478bd9Sstevel@tonic-gate return (1); 15597c478bd9Sstevel@tonic-gate } 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate if ((new_args = prep_args(login, proc_args)) == NULL) { 15627c478bd9Sstevel@tonic-gate zperror(gettext("could not assemble new arguments")); 15637c478bd9Sstevel@tonic-gate return (1); 15647c478bd9Sstevel@tonic-gate } 15657c478bd9Sstevel@tonic-gate 15667c478bd9Sstevel@tonic-gate if ((new_env = prep_env()) == NULL) { 15677c478bd9Sstevel@tonic-gate zperror(gettext("could not assemble new environment")); 15687c478bd9Sstevel@tonic-gate return (1); 15697c478bd9Sstevel@tonic-gate } 15707c478bd9Sstevel@tonic-gate 15717c478bd9Sstevel@tonic-gate if (!interactive) 15727c478bd9Sstevel@tonic-gate return (noninteractive_login(zonename, zoneid, login, new_args, 15737c478bd9Sstevel@tonic-gate new_env)); 15747c478bd9Sstevel@tonic-gate 1575*108322fbScarlsonj if (zonecfg_in_alt_root()) { 1576*108322fbScarlsonj zerror(gettext("cannot use interactive login with scratch " 1577*108322fbScarlsonj "zone")); 1578*108322fbScarlsonj return (1); 1579*108322fbScarlsonj } 1580*108322fbScarlsonj 15817c478bd9Sstevel@tonic-gate /* 15827c478bd9Sstevel@tonic-gate * Things are more complex in interactive mode; we get the 15837c478bd9Sstevel@tonic-gate * master side of the pty, then place the user's terminal into 15847c478bd9Sstevel@tonic-gate * raw mode. 15857c478bd9Sstevel@tonic-gate */ 15867c478bd9Sstevel@tonic-gate if (get_master_pty() == -1) { 15877c478bd9Sstevel@tonic-gate zerror(gettext("could not setup master pty device")); 15887c478bd9Sstevel@tonic-gate return (1); 15897c478bd9Sstevel@tonic-gate } 15907c478bd9Sstevel@tonic-gate 15917c478bd9Sstevel@tonic-gate /* 15927c478bd9Sstevel@tonic-gate * Compute the "short name" of the pts. /dev/pts/2 --> pts/2 15937c478bd9Sstevel@tonic-gate */ 15947c478bd9Sstevel@tonic-gate if ((slavename = ptsname(masterfd)) == NULL) { 15957c478bd9Sstevel@tonic-gate zperror(gettext("failed to get name for pseudo-tty")); 15967c478bd9Sstevel@tonic-gate return (1); 15977c478bd9Sstevel@tonic-gate } 15987c478bd9Sstevel@tonic-gate if (strncmp(slavename, "/dev/", strlen("/dev/")) == 0) 15997c478bd9Sstevel@tonic-gate (void) strlcpy(slaveshortname, slavename + strlen("/dev/"), 16007c478bd9Sstevel@tonic-gate sizeof (slaveshortname)); 16017c478bd9Sstevel@tonic-gate else 16027c478bd9Sstevel@tonic-gate (void) strlcpy(slaveshortname, slavename, 16037c478bd9Sstevel@tonic-gate sizeof (slaveshortname)); 16047c478bd9Sstevel@tonic-gate 16057c478bd9Sstevel@tonic-gate (void) printf(gettext("[Connected to zone '%s' %s]\n"), zonename, 16067c478bd9Sstevel@tonic-gate slaveshortname); 16077c478bd9Sstevel@tonic-gate 16087c478bd9Sstevel@tonic-gate if (set_tty_rawmode(STDIN_FILENO) == -1) { 16097c478bd9Sstevel@tonic-gate reset_tty(); 16107c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stdin pty to raw mode")); 16117c478bd9Sstevel@tonic-gate return (1); 16127c478bd9Sstevel@tonic-gate } 16137c478bd9Sstevel@tonic-gate 16147c478bd9Sstevel@tonic-gate if (prefork_dropprivs() != 0) { 16157c478bd9Sstevel@tonic-gate reset_tty(); 16167c478bd9Sstevel@tonic-gate zperror(gettext("could not allocate privilege set")); 16177c478bd9Sstevel@tonic-gate return (1); 16187c478bd9Sstevel@tonic-gate } 16197c478bd9Sstevel@tonic-gate 16207c478bd9Sstevel@tonic-gate /* 16217c478bd9Sstevel@tonic-gate * We must mask SIGCLD until after we have coped with the fork 16227c478bd9Sstevel@tonic-gate * sufficiently to deal with it; otherwise we can race and receive the 16237c478bd9Sstevel@tonic-gate * signal before child_pid has been initialized (yes, this really 16247c478bd9Sstevel@tonic-gate * happens). 16257c478bd9Sstevel@tonic-gate */ 16267c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, sigcld); 16277c478bd9Sstevel@tonic-gate (void) sigemptyset(&block_cld); 16287c478bd9Sstevel@tonic-gate (void) sigaddset(&block_cld, SIGCLD); 16297c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); 16307c478bd9Sstevel@tonic-gate 16317c478bd9Sstevel@tonic-gate /* 16327c478bd9Sstevel@tonic-gate * We activate the contract template at the last minute to 16337c478bd9Sstevel@tonic-gate * avoid intermediate functions that could be using fork(2) 16347c478bd9Sstevel@tonic-gate * internally. 16357c478bd9Sstevel@tonic-gate */ 16367c478bd9Sstevel@tonic-gate if ((tmpl_fd = init_template()) == -1) { 16377c478bd9Sstevel@tonic-gate reset_tty(); 16387c478bd9Sstevel@tonic-gate zperror(gettext("could not create contract")); 16397c478bd9Sstevel@tonic-gate return (1); 16407c478bd9Sstevel@tonic-gate } 16417c478bd9Sstevel@tonic-gate 16427c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 16437c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 16447c478bd9Sstevel@tonic-gate reset_tty(); 16457c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 16467c478bd9Sstevel@tonic-gate return (1); 16477c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { /* child process */ 16487c478bd9Sstevel@tonic-gate int slavefd, newslave; 16497c478bd9Sstevel@tonic-gate 16507c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 16517c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 16527c478bd9Sstevel@tonic-gate 16537c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 16547c478bd9Sstevel@tonic-gate 16557c478bd9Sstevel@tonic-gate if ((slavefd = init_slave_pty(zoneid, zonepath)) == -1) 16567c478bd9Sstevel@tonic-gate return (1); 16577c478bd9Sstevel@tonic-gate 16587c478bd9Sstevel@tonic-gate /* 16597c478bd9Sstevel@tonic-gate * Close all fds except for the slave pty. 16607c478bd9Sstevel@tonic-gate */ 16617c478bd9Sstevel@tonic-gate (void) fdwalk(close_func, &slavefd); 16627c478bd9Sstevel@tonic-gate 16637c478bd9Sstevel@tonic-gate /* 16647c478bd9Sstevel@tonic-gate * Temporarily dup slavefd to stderr; that way if we have 16657c478bd9Sstevel@tonic-gate * to print out that zone_enter failed, the output will 16667c478bd9Sstevel@tonic-gate * have somewhere to go. 16677c478bd9Sstevel@tonic-gate */ 16687c478bd9Sstevel@tonic-gate if (slavefd != STDERR_FILENO) 16697c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDERR_FILENO); 16707c478bd9Sstevel@tonic-gate 16717c478bd9Sstevel@tonic-gate if (zone_enter(zoneid) == -1) { 16727c478bd9Sstevel@tonic-gate zerror(gettext("could not enter zone %s: %s"), 16737c478bd9Sstevel@tonic-gate zonename, strerror(errno)); 16747c478bd9Sstevel@tonic-gate return (1); 16757c478bd9Sstevel@tonic-gate } 16767c478bd9Sstevel@tonic-gate 16777c478bd9Sstevel@tonic-gate if (slavefd != STDERR_FILENO) 16787c478bd9Sstevel@tonic-gate (void) close(STDERR_FILENO); 16797c478bd9Sstevel@tonic-gate 16807c478bd9Sstevel@tonic-gate /* 16817c478bd9Sstevel@tonic-gate * We take pains to get this process into a new process 16827c478bd9Sstevel@tonic-gate * group, and subsequently a new session. In this way, 16837c478bd9Sstevel@tonic-gate * we'll have a session which doesn't yet have a controlling 16847c478bd9Sstevel@tonic-gate * terminal. When we open the slave, it will become the 16857c478bd9Sstevel@tonic-gate * controlling terminal; no PIDs concerning pgrps or sids 16867c478bd9Sstevel@tonic-gate * will leak inappropriately into the zone. 16877c478bd9Sstevel@tonic-gate */ 16887c478bd9Sstevel@tonic-gate (void) setpgrp(); 16897c478bd9Sstevel@tonic-gate 16907c478bd9Sstevel@tonic-gate /* 16917c478bd9Sstevel@tonic-gate * We need the slave pty to be referenced from the zone's 16927c478bd9Sstevel@tonic-gate * /dev in order to ensure that the devt's, etc are all 16937c478bd9Sstevel@tonic-gate * correct. Otherwise we break ttyname and the like. 16947c478bd9Sstevel@tonic-gate */ 16957c478bd9Sstevel@tonic-gate if ((newslave = open(slavename, O_RDWR)) == -1) { 16967c478bd9Sstevel@tonic-gate (void) close(slavefd); 16977c478bd9Sstevel@tonic-gate return (1); 16987c478bd9Sstevel@tonic-gate } 16997c478bd9Sstevel@tonic-gate (void) close(slavefd); 17007c478bd9Sstevel@tonic-gate slavefd = newslave; 17017c478bd9Sstevel@tonic-gate 17027c478bd9Sstevel@tonic-gate /* 17037c478bd9Sstevel@tonic-gate * dup the slave to the various FDs, so that when the 17047c478bd9Sstevel@tonic-gate * spawned process does a write/read it maps to the slave 17057c478bd9Sstevel@tonic-gate * pty. 17067c478bd9Sstevel@tonic-gate */ 17077c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDIN_FILENO); 17087c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDOUT_FILENO); 17097c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDERR_FILENO); 17107c478bd9Sstevel@tonic-gate if (slavefd != STDIN_FILENO && slavefd != STDOUT_FILENO && 17117c478bd9Sstevel@tonic-gate slavefd != STDERR_FILENO) { 17127c478bd9Sstevel@tonic-gate (void) close(slavefd); 17137c478bd9Sstevel@tonic-gate } 17147c478bd9Sstevel@tonic-gate 17157c478bd9Sstevel@tonic-gate /* 17167c478bd9Sstevel@tonic-gate * In failsafe mode, we don't use login(1), so don't try 17177c478bd9Sstevel@tonic-gate * setting up a utmpx entry. 17187c478bd9Sstevel@tonic-gate */ 17197c478bd9Sstevel@tonic-gate if (!failsafe) { 17207c478bd9Sstevel@tonic-gate if (setup_utmpx(slaveshortname) == -1) 17217c478bd9Sstevel@tonic-gate return (1); 17227c478bd9Sstevel@tonic-gate } 17237c478bd9Sstevel@tonic-gate 17247c478bd9Sstevel@tonic-gate (void) execve(new_args[0], new_args, new_env); 17257c478bd9Sstevel@tonic-gate zperror(gettext("exec failure")); 17267c478bd9Sstevel@tonic-gate return (1); 17277c478bd9Sstevel@tonic-gate } 17287c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 17297c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 17307c478bd9Sstevel@tonic-gate 17317c478bd9Sstevel@tonic-gate /* 17327c478bd9Sstevel@tonic-gate * The rest is only for the parent process. 17337c478bd9Sstevel@tonic-gate */ 17347c478bd9Sstevel@tonic-gate (void) sigset(SIGWINCH, sigwinch); 17357c478bd9Sstevel@tonic-gate 17367c478bd9Sstevel@tonic-gate postfork_dropprivs(); 17377c478bd9Sstevel@tonic-gate 17387c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 17397c478bd9Sstevel@tonic-gate doio(masterfd, masterfd, -1, B_FALSE); 17407c478bd9Sstevel@tonic-gate 17417c478bd9Sstevel@tonic-gate reset_tty(); 17427c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 17437c478bd9Sstevel@tonic-gate gettext("\n[Connection to zone '%s' %s closed]\n"), zonename, 17447c478bd9Sstevel@tonic-gate slaveshortname); 17457c478bd9Sstevel@tonic-gate 17467c478bd9Sstevel@tonic-gate if (pollerr != 0) { 17477c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("Error: connection closed due " 17487c478bd9Sstevel@tonic-gate "to unexpected pollevents=0x%x.\n"), pollerr); 17497c478bd9Sstevel@tonic-gate return (1); 17507c478bd9Sstevel@tonic-gate } 17517c478bd9Sstevel@tonic-gate 17527c478bd9Sstevel@tonic-gate return (0); 17537c478bd9Sstevel@tonic-gate } 1754