1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * zlogin provides three types of login which allow users in the global 31*7c478bd9Sstevel@tonic-gate * zone to access non-global zones. 32*7c478bd9Sstevel@tonic-gate * 33*7c478bd9Sstevel@tonic-gate * - "interactive login" is similar to rlogin(1); for example, the user could 34*7c478bd9Sstevel@tonic-gate * issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'. The user is 35*7c478bd9Sstevel@tonic-gate * granted a new pty (which is then shoved into the zone), and an I/O 36*7c478bd9Sstevel@tonic-gate * loop between parent and child processes takes care of the interactive 37*7c478bd9Sstevel@tonic-gate * session. In this mode, login(1) (and its -c option, which means 38*7c478bd9Sstevel@tonic-gate * "already authenticated") is employed to take care of the initialization 39*7c478bd9Sstevel@tonic-gate * of the user's session. 40*7c478bd9Sstevel@tonic-gate * 41*7c478bd9Sstevel@tonic-gate * - "non-interactive login" is similar to su(1M); the user could issue 42*7c478bd9Sstevel@tonic-gate * 'zlogin my-zone ls -l' and the command would be run as specified. 43*7c478bd9Sstevel@tonic-gate * In this mode, zlogin sets up pipes as the communication channel, and 44*7c478bd9Sstevel@tonic-gate * 'su' is used to do the login setup work. 45*7c478bd9Sstevel@tonic-gate * 46*7c478bd9Sstevel@tonic-gate * - "console login" is the equivalent to accessing the tip line for a 47*7c478bd9Sstevel@tonic-gate * zone. For example, the user can issue 'zlogin -C my-zone'. 48*7c478bd9Sstevel@tonic-gate * In this mode, zlogin contacts the zoneadmd process via unix domain 49*7c478bd9Sstevel@tonic-gate * socket. If zoneadmd is not running, it starts it. This allows the 50*7c478bd9Sstevel@tonic-gate * console to be available anytime the zone is installed, regardless of 51*7c478bd9Sstevel@tonic-gate * whether it is running. 52*7c478bd9Sstevel@tonic-gate */ 53*7c478bd9Sstevel@tonic-gate 54*7c478bd9Sstevel@tonic-gate #include <sys/socket.h> 55*7c478bd9Sstevel@tonic-gate #include <sys/termios.h> 56*7c478bd9Sstevel@tonic-gate #include <sys/utsname.h> 57*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 58*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 59*7c478bd9Sstevel@tonic-gate #include <sys/contract/process.h> 60*7c478bd9Sstevel@tonic-gate #include <sys/ctfs.h> 61*7c478bd9Sstevel@tonic-gate 62*7c478bd9Sstevel@tonic-gate #include <alloca.h> 63*7c478bd9Sstevel@tonic-gate #include <assert.h> 64*7c478bd9Sstevel@tonic-gate #include <ctype.h> 65*7c478bd9Sstevel@tonic-gate #include <door.h> 66*7c478bd9Sstevel@tonic-gate #include <errno.h> 67*7c478bd9Sstevel@tonic-gate #include <poll.h> 68*7c478bd9Sstevel@tonic-gate #include <priv.h> 69*7c478bd9Sstevel@tonic-gate #include <pwd.h> 70*7c478bd9Sstevel@tonic-gate #include <unistd.h> 71*7c478bd9Sstevel@tonic-gate #include <utmpx.h> 72*7c478bd9Sstevel@tonic-gate #include <sac.h> 73*7c478bd9Sstevel@tonic-gate #include <signal.h> 74*7c478bd9Sstevel@tonic-gate #include <stdarg.h> 75*7c478bd9Sstevel@tonic-gate #include <stdio.h> 76*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 77*7c478bd9Sstevel@tonic-gate #include <string.h> 78*7c478bd9Sstevel@tonic-gate #include <strings.h> 79*7c478bd9Sstevel@tonic-gate #include <stropts.h> 80*7c478bd9Sstevel@tonic-gate #include <wait.h> 81*7c478bd9Sstevel@tonic-gate #include <zone.h> 82*7c478bd9Sstevel@tonic-gate #include <fcntl.h> 83*7c478bd9Sstevel@tonic-gate #include <libdevinfo.h> 84*7c478bd9Sstevel@tonic-gate #include <libintl.h> 85*7c478bd9Sstevel@tonic-gate #include <locale.h> 86*7c478bd9Sstevel@tonic-gate #include <libzonecfg.h> 87*7c478bd9Sstevel@tonic-gate #include <libcontract.h> 88*7c478bd9Sstevel@tonic-gate 89*7c478bd9Sstevel@tonic-gate static int masterfd; 90*7c478bd9Sstevel@tonic-gate static struct termios save_termios; 91*7c478bd9Sstevel@tonic-gate static struct termios effective_termios; 92*7c478bd9Sstevel@tonic-gate static int save_fd; 93*7c478bd9Sstevel@tonic-gate static struct winsize winsize; 94*7c478bd9Sstevel@tonic-gate static volatile int dead; 95*7c478bd9Sstevel@tonic-gate static volatile pid_t child_pid = -1; 96*7c478bd9Sstevel@tonic-gate static int interactive = 0; 97*7c478bd9Sstevel@tonic-gate static priv_set_t *dropprivs; 98*7c478bd9Sstevel@tonic-gate 99*7c478bd9Sstevel@tonic-gate static int nocmdchar = 0; 100*7c478bd9Sstevel@tonic-gate static int failsafe = 0; 101*7c478bd9Sstevel@tonic-gate static char cmdchar = '~'; 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate static int pollerr = 0; 104*7c478bd9Sstevel@tonic-gate 105*7c478bd9Sstevel@tonic-gate static const char *pname; 106*7c478bd9Sstevel@tonic-gate 107*7c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 108*7c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 109*7c478bd9Sstevel@tonic-gate #endif 110*7c478bd9Sstevel@tonic-gate 111*7c478bd9Sstevel@tonic-gate #define SUPATH "/usr/bin/su" 112*7c478bd9Sstevel@tonic-gate #define FAILSAFESHELL "/sbin/sh" 113*7c478bd9Sstevel@tonic-gate #define DEFAULTSHELL "/sbin/sh" 114*7c478bd9Sstevel@tonic-gate #define LOGINPATH "/usr/bin/login" 115*7c478bd9Sstevel@tonic-gate #define DEF_PATH "/usr/sbin:/usr/bin" 116*7c478bd9Sstevel@tonic-gate 117*7c478bd9Sstevel@tonic-gate /* 118*7c478bd9Sstevel@tonic-gate * See canonify() below. CANONIFY_LEN is the maximum length that a 119*7c478bd9Sstevel@tonic-gate * "canonical" sequence will expand to (backslash, three octal digits, NUL). 120*7c478bd9Sstevel@tonic-gate */ 121*7c478bd9Sstevel@tonic-gate #define CANONIFY_LEN 5 122*7c478bd9Sstevel@tonic-gate 123*7c478bd9Sstevel@tonic-gate static void 124*7c478bd9Sstevel@tonic-gate usage(void) 125*7c478bd9Sstevel@tonic-gate { 126*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("usage: %s [ -CES ] [ -e cmdchar ] " 127*7c478bd9Sstevel@tonic-gate "[-l user] zonename [command [args ...] ]\n"), pname); 128*7c478bd9Sstevel@tonic-gate exit(2); 129*7c478bd9Sstevel@tonic-gate } 130*7c478bd9Sstevel@tonic-gate 131*7c478bd9Sstevel@tonic-gate static const char * 132*7c478bd9Sstevel@tonic-gate getpname(const char *arg0) 133*7c478bd9Sstevel@tonic-gate { 134*7c478bd9Sstevel@tonic-gate const char *p = strrchr(arg0, '/'); 135*7c478bd9Sstevel@tonic-gate 136*7c478bd9Sstevel@tonic-gate if (p == NULL) 137*7c478bd9Sstevel@tonic-gate p = arg0; 138*7c478bd9Sstevel@tonic-gate else 139*7c478bd9Sstevel@tonic-gate p++; 140*7c478bd9Sstevel@tonic-gate 141*7c478bd9Sstevel@tonic-gate pname = p; 142*7c478bd9Sstevel@tonic-gate return (p); 143*7c478bd9Sstevel@tonic-gate } 144*7c478bd9Sstevel@tonic-gate 145*7c478bd9Sstevel@tonic-gate static void 146*7c478bd9Sstevel@tonic-gate zerror(const char *fmt, ...) 147*7c478bd9Sstevel@tonic-gate { 148*7c478bd9Sstevel@tonic-gate va_list alist; 149*7c478bd9Sstevel@tonic-gate 150*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", pname); 151*7c478bd9Sstevel@tonic-gate va_start(alist, fmt); 152*7c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 153*7c478bd9Sstevel@tonic-gate va_end(alist); 154*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 155*7c478bd9Sstevel@tonic-gate } 156*7c478bd9Sstevel@tonic-gate 157*7c478bd9Sstevel@tonic-gate static void 158*7c478bd9Sstevel@tonic-gate zperror(const char *str) 159*7c478bd9Sstevel@tonic-gate { 160*7c478bd9Sstevel@tonic-gate const char *estr; 161*7c478bd9Sstevel@tonic-gate 162*7c478bd9Sstevel@tonic-gate if ((estr = strerror(errno)) != NULL) 163*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", pname, str, estr); 164*7c478bd9Sstevel@tonic-gate else 165*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: errno %d\n", pname, str, errno); 166*7c478bd9Sstevel@tonic-gate } 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate /* 169*7c478bd9Sstevel@tonic-gate * The first part of our privilege dropping scheme needs to be called before 170*7c478bd9Sstevel@tonic-gate * fork(), since we must have it for security; we don't want to be surprised 171*7c478bd9Sstevel@tonic-gate * later that we couldn't allocate the privset. 172*7c478bd9Sstevel@tonic-gate */ 173*7c478bd9Sstevel@tonic-gate static int 174*7c478bd9Sstevel@tonic-gate prefork_dropprivs() 175*7c478bd9Sstevel@tonic-gate { 176*7c478bd9Sstevel@tonic-gate if ((dropprivs = priv_allocset()) == NULL) 177*7c478bd9Sstevel@tonic-gate return (1); 178*7c478bd9Sstevel@tonic-gate priv_emptyset(dropprivs); 179*7c478bd9Sstevel@tonic-gate 180*7c478bd9Sstevel@tonic-gate /* 181*7c478bd9Sstevel@tonic-gate * We need these privileges in order to query session information and 182*7c478bd9Sstevel@tonic-gate * send signals. 183*7c478bd9Sstevel@tonic-gate */ 184*7c478bd9Sstevel@tonic-gate if (interactive == 0) { 185*7c478bd9Sstevel@tonic-gate if (priv_addset(dropprivs, "proc_session") == -1) 186*7c478bd9Sstevel@tonic-gate return (1); 187*7c478bd9Sstevel@tonic-gate if (priv_addset(dropprivs, "proc_zone") == -1) 188*7c478bd9Sstevel@tonic-gate return (1); 189*7c478bd9Sstevel@tonic-gate if (priv_addset(dropprivs, "proc_owner") == -1) 190*7c478bd9Sstevel@tonic-gate return (1); 191*7c478bd9Sstevel@tonic-gate } 192*7c478bd9Sstevel@tonic-gate 193*7c478bd9Sstevel@tonic-gate return (0); 194*7c478bd9Sstevel@tonic-gate } 195*7c478bd9Sstevel@tonic-gate 196*7c478bd9Sstevel@tonic-gate /* 197*7c478bd9Sstevel@tonic-gate * The second part of the privilege drop. We are paranoid about being attacked 198*7c478bd9Sstevel@tonic-gate * by the zone, so we drop all privileges. This should prevent a compromise 199*7c478bd9Sstevel@tonic-gate * which gets us to fork(), exec(), symlink(), etc. 200*7c478bd9Sstevel@tonic-gate */ 201*7c478bd9Sstevel@tonic-gate static void 202*7c478bd9Sstevel@tonic-gate postfork_dropprivs() 203*7c478bd9Sstevel@tonic-gate { 204*7c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_PERMITTED, dropprivs)) == -1) { 205*7c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set permitted privileges")); 206*7c478bd9Sstevel@tonic-gate } 207*7c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_LIMIT, dropprivs)) == -1) { 208*7c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set limit privileges")); 209*7c478bd9Sstevel@tonic-gate } 210*7c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_INHERITABLE, dropprivs)) == -1) { 211*7c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set inheritable " 212*7c478bd9Sstevel@tonic-gate "privileges")); 213*7c478bd9Sstevel@tonic-gate } 214*7c478bd9Sstevel@tonic-gate } 215*7c478bd9Sstevel@tonic-gate 216*7c478bd9Sstevel@tonic-gate /* 217*7c478bd9Sstevel@tonic-gate * Create the unix domain socket and call the zoneadmd server; handshake 218*7c478bd9Sstevel@tonic-gate * with it to determine whether it will allow us to connect. 219*7c478bd9Sstevel@tonic-gate */ 220*7c478bd9Sstevel@tonic-gate static int 221*7c478bd9Sstevel@tonic-gate get_console_master(const char *zname) 222*7c478bd9Sstevel@tonic-gate { 223*7c478bd9Sstevel@tonic-gate int sockfd = -1; 224*7c478bd9Sstevel@tonic-gate struct sockaddr_un servaddr; 225*7c478bd9Sstevel@tonic-gate char clientid[MAXPATHLEN]; 226*7c478bd9Sstevel@tonic-gate char handshake[MAXPATHLEN], c; 227*7c478bd9Sstevel@tonic-gate int msglen; 228*7c478bd9Sstevel@tonic-gate int i = 0, err = 0; 229*7c478bd9Sstevel@tonic-gate 230*7c478bd9Sstevel@tonic-gate if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 231*7c478bd9Sstevel@tonic-gate zperror(gettext("could not create socket")); 232*7c478bd9Sstevel@tonic-gate return (-1); 233*7c478bd9Sstevel@tonic-gate } 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate bzero(&servaddr, sizeof (servaddr)); 236*7c478bd9Sstevel@tonic-gate servaddr.sun_family = AF_UNIX; 237*7c478bd9Sstevel@tonic-gate (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), 238*7c478bd9Sstevel@tonic-gate "%s/%s.console_sock", ZONES_TMPDIR, zname); 239*7c478bd9Sstevel@tonic-gate 240*7c478bd9Sstevel@tonic-gate if (connect(sockfd, (struct sockaddr *)&servaddr, 241*7c478bd9Sstevel@tonic-gate sizeof (servaddr)) == -1) { 242*7c478bd9Sstevel@tonic-gate zperror(gettext("Could not connect to zone console")); 243*7c478bd9Sstevel@tonic-gate goto bad; 244*7c478bd9Sstevel@tonic-gate } 245*7c478bd9Sstevel@tonic-gate masterfd = sockfd; 246*7c478bd9Sstevel@tonic-gate 247*7c478bd9Sstevel@tonic-gate msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s\n", 248*7c478bd9Sstevel@tonic-gate getpid(), setlocale(LC_MESSAGES, NULL)); 249*7c478bd9Sstevel@tonic-gate 250*7c478bd9Sstevel@tonic-gate if (msglen >= sizeof (clientid) || msglen < 0) { 251*7c478bd9Sstevel@tonic-gate zerror("protocol error"); 252*7c478bd9Sstevel@tonic-gate goto bad; 253*7c478bd9Sstevel@tonic-gate } 254*7c478bd9Sstevel@tonic-gate 255*7c478bd9Sstevel@tonic-gate if (write(masterfd, clientid, msglen) != msglen) { 256*7c478bd9Sstevel@tonic-gate zerror("protocol error"); 257*7c478bd9Sstevel@tonic-gate goto bad; 258*7c478bd9Sstevel@tonic-gate } 259*7c478bd9Sstevel@tonic-gate 260*7c478bd9Sstevel@tonic-gate bzero(handshake, sizeof (handshake)); 261*7c478bd9Sstevel@tonic-gate 262*7c478bd9Sstevel@tonic-gate /* 263*7c478bd9Sstevel@tonic-gate * Take care not to accumulate more than our fill, and leave room for 264*7c478bd9Sstevel@tonic-gate * the NUL at the end. 265*7c478bd9Sstevel@tonic-gate */ 266*7c478bd9Sstevel@tonic-gate while ((err = read(masterfd, &c, 1)) == 1) { 267*7c478bd9Sstevel@tonic-gate if (i >= (sizeof (handshake) - 1)) 268*7c478bd9Sstevel@tonic-gate break; 269*7c478bd9Sstevel@tonic-gate if (c == '\n') 270*7c478bd9Sstevel@tonic-gate break; 271*7c478bd9Sstevel@tonic-gate handshake[i] = c; 272*7c478bd9Sstevel@tonic-gate i++; 273*7c478bd9Sstevel@tonic-gate } 274*7c478bd9Sstevel@tonic-gate 275*7c478bd9Sstevel@tonic-gate /* 276*7c478bd9Sstevel@tonic-gate * If something went wrong during the handshake we bail; perhaps 277*7c478bd9Sstevel@tonic-gate * the server died off. 278*7c478bd9Sstevel@tonic-gate */ 279*7c478bd9Sstevel@tonic-gate if (err == -1) { 280*7c478bd9Sstevel@tonic-gate zperror(gettext("Could not connect to zone console")); 281*7c478bd9Sstevel@tonic-gate goto bad; 282*7c478bd9Sstevel@tonic-gate } 283*7c478bd9Sstevel@tonic-gate 284*7c478bd9Sstevel@tonic-gate if (strncmp(handshake, "OK", sizeof (handshake)) == 0) 285*7c478bd9Sstevel@tonic-gate return (0); 286*7c478bd9Sstevel@tonic-gate 287*7c478bd9Sstevel@tonic-gate zerror(gettext("Console is already in use by process ID %s."), 288*7c478bd9Sstevel@tonic-gate handshake); 289*7c478bd9Sstevel@tonic-gate bad: 290*7c478bd9Sstevel@tonic-gate (void) close(sockfd); 291*7c478bd9Sstevel@tonic-gate masterfd = -1; 292*7c478bd9Sstevel@tonic-gate return (-1); 293*7c478bd9Sstevel@tonic-gate } 294*7c478bd9Sstevel@tonic-gate 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate /* 297*7c478bd9Sstevel@tonic-gate * Routines to handle pty creation upon zone entry and to shuttle I/O back 298*7c478bd9Sstevel@tonic-gate * and forth between the two terminals. We also compute and store the 299*7c478bd9Sstevel@tonic-gate * name of the slave terminal associated with the master side. 300*7c478bd9Sstevel@tonic-gate */ 301*7c478bd9Sstevel@tonic-gate static int 302*7c478bd9Sstevel@tonic-gate get_master_pty() 303*7c478bd9Sstevel@tonic-gate { 304*7c478bd9Sstevel@tonic-gate if ((masterfd = open("/dev/ptmx", O_RDWR|O_NONBLOCK)) < 0) { 305*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to obtain a pseudo-tty")); 306*7c478bd9Sstevel@tonic-gate return (-1); 307*7c478bd9Sstevel@tonic-gate } 308*7c478bd9Sstevel@tonic-gate if (tcgetattr(STDIN_FILENO, &save_termios) == -1) { 309*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to get terminal settings from stdin")); 310*7c478bd9Sstevel@tonic-gate return (-1); 311*7c478bd9Sstevel@tonic-gate } 312*7c478bd9Sstevel@tonic-gate (void) ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&winsize); 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate return (0); 315*7c478bd9Sstevel@tonic-gate } 316*7c478bd9Sstevel@tonic-gate 317*7c478bd9Sstevel@tonic-gate /* 318*7c478bd9Sstevel@tonic-gate * This is a bit tricky; normally a pts device will belong to the zone it 319*7c478bd9Sstevel@tonic-gate * is granted to. But in the case of "entering" a zone, we need to establish 320*7c478bd9Sstevel@tonic-gate * the pty before entering the zone so that we can vector I/O to and from it 321*7c478bd9Sstevel@tonic-gate * from the global zone. 322*7c478bd9Sstevel@tonic-gate * 323*7c478bd9Sstevel@tonic-gate * We use the zonept() call to let the ptm driver know what we are up to; 324*7c478bd9Sstevel@tonic-gate * the only other hairy bit is the setting of zoneslavename (which happens 325*7c478bd9Sstevel@tonic-gate * above, in get_master_pty()). 326*7c478bd9Sstevel@tonic-gate */ 327*7c478bd9Sstevel@tonic-gate static int 328*7c478bd9Sstevel@tonic-gate init_slave_pty(zoneid_t zoneid, char *zonepath) 329*7c478bd9Sstevel@tonic-gate { 330*7c478bd9Sstevel@tonic-gate int slavefd = -1; 331*7c478bd9Sstevel@tonic-gate char *slavename, zoneslavename[MAXPATHLEN]; 332*7c478bd9Sstevel@tonic-gate 333*7c478bd9Sstevel@tonic-gate /* 334*7c478bd9Sstevel@tonic-gate * Set slave permissions, zone the pts, then unlock it. 335*7c478bd9Sstevel@tonic-gate */ 336*7c478bd9Sstevel@tonic-gate if (grantpt(masterfd) != 0) { 337*7c478bd9Sstevel@tonic-gate zperror(gettext("grantpt failed")); 338*7c478bd9Sstevel@tonic-gate return (-1); 339*7c478bd9Sstevel@tonic-gate } 340*7c478bd9Sstevel@tonic-gate 341*7c478bd9Sstevel@tonic-gate if (unlockpt(masterfd) != 0) { 342*7c478bd9Sstevel@tonic-gate zperror(gettext("unlockpt failed")); 343*7c478bd9Sstevel@tonic-gate return (-1); 344*7c478bd9Sstevel@tonic-gate } 345*7c478bd9Sstevel@tonic-gate 346*7c478bd9Sstevel@tonic-gate /* 347*7c478bd9Sstevel@tonic-gate * We must open the slave side before zoning this pty; otherwise 348*7c478bd9Sstevel@tonic-gate * the kernel would refuse us the open-- zoning a pty makes it 349*7c478bd9Sstevel@tonic-gate * inaccessible to the global zone. Since we are trying to 350*7c478bd9Sstevel@tonic-gate * open the device node via the $ZONEPATH/dev path, we may have to 351*7c478bd9Sstevel@tonic-gate * give devfsadm a kick to get it to create the device node for 352*7c478bd9Sstevel@tonic-gate * us. Normally this would "just work" because pt_chmod inside 353*7c478bd9Sstevel@tonic-gate * the zone would take care of it for us. 354*7c478bd9Sstevel@tonic-gate * 355*7c478bd9Sstevel@tonic-gate * Later we'll close the slave out when once we've opened it again 356*7c478bd9Sstevel@tonic-gate * from within the target zone. Blarg. 357*7c478bd9Sstevel@tonic-gate */ 358*7c478bd9Sstevel@tonic-gate if ((slavename = ptsname(masterfd)) == NULL) { 359*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to get name for pseudo-tty")); 360*7c478bd9Sstevel@tonic-gate return (-1); 361*7c478bd9Sstevel@tonic-gate } 362*7c478bd9Sstevel@tonic-gate 363*7c478bd9Sstevel@tonic-gate (void) snprintf(zoneslavename, sizeof (zoneslavename), "%s%s", 364*7c478bd9Sstevel@tonic-gate zonepath, slavename); 365*7c478bd9Sstevel@tonic-gate 366*7c478bd9Sstevel@tonic-gate if ((slavefd = open(zoneslavename, O_RDWR)) < 0) { 367*7c478bd9Sstevel@tonic-gate di_devlink_handle_t h = di_devlink_init("pts", DI_MAKE_LINK); 368*7c478bd9Sstevel@tonic-gate if (h != NULL) { 369*7c478bd9Sstevel@tonic-gate (void) di_devlink_fini(&h); 370*7c478bd9Sstevel@tonic-gate } 371*7c478bd9Sstevel@tonic-gate if ((slavefd = open(zoneslavename, O_RDWR)) < 0) { 372*7c478bd9Sstevel@tonic-gate zerror(gettext("failed to open %s: %s"), zoneslavename, 373*7c478bd9Sstevel@tonic-gate strerror(errno)); 374*7c478bd9Sstevel@tonic-gate return (-1); 375*7c478bd9Sstevel@tonic-gate } 376*7c478bd9Sstevel@tonic-gate } 377*7c478bd9Sstevel@tonic-gate 378*7c478bd9Sstevel@tonic-gate /* 379*7c478bd9Sstevel@tonic-gate * Push hardware emulation (ptem), line discipline (ldterm), 380*7c478bd9Sstevel@tonic-gate * and V7/4BSD/Xenix compatibility (ttcompat) modules. 381*7c478bd9Sstevel@tonic-gate */ 382*7c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ptem") == -1) { 383*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ptem module")); 384*7c478bd9Sstevel@tonic-gate if (!failsafe) 385*7c478bd9Sstevel@tonic-gate goto bad; 386*7c478bd9Sstevel@tonic-gate } 387*7c478bd9Sstevel@tonic-gate 388*7c478bd9Sstevel@tonic-gate /* 389*7c478bd9Sstevel@tonic-gate * Anchor the stream to prevent malicious I_POPs; we prefer to do 390*7c478bd9Sstevel@tonic-gate * this prior to entering the zone so that we can detect any errors 391*7c478bd9Sstevel@tonic-gate * early, and so that we can set the anchor from the global zone. 392*7c478bd9Sstevel@tonic-gate */ 393*7c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_ANCHOR) == -1) { 394*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stream anchor")); 395*7c478bd9Sstevel@tonic-gate if (!failsafe) 396*7c478bd9Sstevel@tonic-gate goto bad; 397*7c478bd9Sstevel@tonic-gate } 398*7c478bd9Sstevel@tonic-gate 399*7c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ldterm") == -1) { 400*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ldterm module")); 401*7c478bd9Sstevel@tonic-gate if (!failsafe) 402*7c478bd9Sstevel@tonic-gate goto bad; 403*7c478bd9Sstevel@tonic-gate } 404*7c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) { 405*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ttcompat module")); 406*7c478bd9Sstevel@tonic-gate if (!failsafe) 407*7c478bd9Sstevel@tonic-gate goto bad; 408*7c478bd9Sstevel@tonic-gate } 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate /* 411*7c478bd9Sstevel@tonic-gate * Propagate terminal settings from the external term to the new one. 412*7c478bd9Sstevel@tonic-gate */ 413*7c478bd9Sstevel@tonic-gate if (tcsetattr(slavefd, TCSAFLUSH, &save_termios) == -1) { 414*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to set terminal settings")); 415*7c478bd9Sstevel@tonic-gate if (!failsafe) 416*7c478bd9Sstevel@tonic-gate goto bad; 417*7c478bd9Sstevel@tonic-gate } 418*7c478bd9Sstevel@tonic-gate (void) ioctl(slavefd, TIOCSWINSZ, (char *)&winsize); 419*7c478bd9Sstevel@tonic-gate 420*7c478bd9Sstevel@tonic-gate if (zonept(masterfd, zoneid) != 0) { 421*7c478bd9Sstevel@tonic-gate zperror(gettext("could not set zoneid of pty")); 422*7c478bd9Sstevel@tonic-gate goto bad; 423*7c478bd9Sstevel@tonic-gate } 424*7c478bd9Sstevel@tonic-gate 425*7c478bd9Sstevel@tonic-gate return (slavefd); 426*7c478bd9Sstevel@tonic-gate 427*7c478bd9Sstevel@tonic-gate bad: 428*7c478bd9Sstevel@tonic-gate (void) close(slavefd); 429*7c478bd9Sstevel@tonic-gate return (-1); 430*7c478bd9Sstevel@tonic-gate } 431*7c478bd9Sstevel@tonic-gate 432*7c478bd9Sstevel@tonic-gate /* 433*7c478bd9Sstevel@tonic-gate * Place terminal into raw mode. 434*7c478bd9Sstevel@tonic-gate */ 435*7c478bd9Sstevel@tonic-gate static int 436*7c478bd9Sstevel@tonic-gate set_tty_rawmode(int fd) 437*7c478bd9Sstevel@tonic-gate { 438*7c478bd9Sstevel@tonic-gate struct termios term; 439*7c478bd9Sstevel@tonic-gate if (tcgetattr(fd, &term) < 0) { 440*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to get user terminal settings")); 441*7c478bd9Sstevel@tonic-gate return (-1); 442*7c478bd9Sstevel@tonic-gate } 443*7c478bd9Sstevel@tonic-gate 444*7c478bd9Sstevel@tonic-gate /* Stash for later, so we can revert back to previous mode */ 445*7c478bd9Sstevel@tonic-gate save_termios = term; 446*7c478bd9Sstevel@tonic-gate save_fd = fd; 447*7c478bd9Sstevel@tonic-gate 448*7c478bd9Sstevel@tonic-gate /* disable 8->7 bit strip, start/stop, enable any char to restart */ 449*7c478bd9Sstevel@tonic-gate term.c_iflag &= ~(ISTRIP|IXON|IXANY); 450*7c478bd9Sstevel@tonic-gate /* disable NL->CR, CR->NL, ignore CR, UPPER->lower */ 451*7c478bd9Sstevel@tonic-gate term.c_iflag &= ~(INLCR|ICRNL|IGNCR|IUCLC); 452*7c478bd9Sstevel@tonic-gate /* disable output post-processing */ 453*7c478bd9Sstevel@tonic-gate term.c_oflag &= ~OPOST; 454*7c478bd9Sstevel@tonic-gate /* disable canonical mode, signal chars, echo & extended functions */ 455*7c478bd9Sstevel@tonic-gate term.c_lflag &= ~(ICANON|ISIG|ECHO|IEXTEN); 456*7c478bd9Sstevel@tonic-gate 457*7c478bd9Sstevel@tonic-gate term.c_cc[VMIN] = 1; /* byte-at-a-time */ 458*7c478bd9Sstevel@tonic-gate term.c_cc[VTIME] = 0; 459*7c478bd9Sstevel@tonic-gate 460*7c478bd9Sstevel@tonic-gate if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)) { 461*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to set user terminal to raw mode")); 462*7c478bd9Sstevel@tonic-gate return (-1); 463*7c478bd9Sstevel@tonic-gate } 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate /* 466*7c478bd9Sstevel@tonic-gate * We need to know the value of VEOF so that we can properly process for 467*7c478bd9Sstevel@tonic-gate * client-side ~<EOF>. But we have obliterated VEOF in term, 468*7c478bd9Sstevel@tonic-gate * because VMIN overloads the same array slot in non-canonical mode. 469*7c478bd9Sstevel@tonic-gate * Stupid @&^%! 470*7c478bd9Sstevel@tonic-gate * 471*7c478bd9Sstevel@tonic-gate * So here we construct the "effective" termios from the current 472*7c478bd9Sstevel@tonic-gate * terminal settings, and the corrected VEOF and VEOL settings. 473*7c478bd9Sstevel@tonic-gate */ 474*7c478bd9Sstevel@tonic-gate if (tcgetattr(STDIN_FILENO, &effective_termios) < 0) { 475*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to get user terminal settings")); 476*7c478bd9Sstevel@tonic-gate return (-1); 477*7c478bd9Sstevel@tonic-gate } 478*7c478bd9Sstevel@tonic-gate effective_termios.c_cc[VEOF] = save_termios.c_cc[VEOF]; 479*7c478bd9Sstevel@tonic-gate effective_termios.c_cc[VEOL] = save_termios.c_cc[VEOL]; 480*7c478bd9Sstevel@tonic-gate 481*7c478bd9Sstevel@tonic-gate return (0); 482*7c478bd9Sstevel@tonic-gate } 483*7c478bd9Sstevel@tonic-gate 484*7c478bd9Sstevel@tonic-gate /* 485*7c478bd9Sstevel@tonic-gate * Copy terminal window size from our terminal to the pts. 486*7c478bd9Sstevel@tonic-gate */ 487*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 488*7c478bd9Sstevel@tonic-gate static void 489*7c478bd9Sstevel@tonic-gate sigwinch(int s) 490*7c478bd9Sstevel@tonic-gate { 491*7c478bd9Sstevel@tonic-gate struct winsize ws; 492*7c478bd9Sstevel@tonic-gate 493*7c478bd9Sstevel@tonic-gate if (ioctl(0, TIOCGWINSZ, &ws) == 0) 494*7c478bd9Sstevel@tonic-gate (void) ioctl(masterfd, TIOCSWINSZ, &ws); 495*7c478bd9Sstevel@tonic-gate } 496*7c478bd9Sstevel@tonic-gate 497*7c478bd9Sstevel@tonic-gate static void 498*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 499*7c478bd9Sstevel@tonic-gate sigcld(int s) 500*7c478bd9Sstevel@tonic-gate { 501*7c478bd9Sstevel@tonic-gate int status; 502*7c478bd9Sstevel@tonic-gate pid_t pid; 503*7c478bd9Sstevel@tonic-gate 504*7c478bd9Sstevel@tonic-gate /* 505*7c478bd9Sstevel@tonic-gate * Peek at the exit status. If this isn't the process we cared 506*7c478bd9Sstevel@tonic-gate * about, then just reap it. 507*7c478bd9Sstevel@tonic-gate */ 508*7c478bd9Sstevel@tonic-gate if ((pid = waitpid(child_pid, &status, WNOHANG|WNOWAIT)) != -1) { 509*7c478bd9Sstevel@tonic-gate if (pid == child_pid && 510*7c478bd9Sstevel@tonic-gate (WIFEXITED(status) || WIFSIGNALED(status))) 511*7c478bd9Sstevel@tonic-gate dead = 1; 512*7c478bd9Sstevel@tonic-gate else 513*7c478bd9Sstevel@tonic-gate (void) waitpid(pid, &status, WNOHANG); 514*7c478bd9Sstevel@tonic-gate } 515*7c478bd9Sstevel@tonic-gate } 516*7c478bd9Sstevel@tonic-gate 517*7c478bd9Sstevel@tonic-gate /* 518*7c478bd9Sstevel@tonic-gate * Some signals (currently, SIGINT) must be forwarded on to the process 519*7c478bd9Sstevel@tonic-gate * group of the child process. 520*7c478bd9Sstevel@tonic-gate */ 521*7c478bd9Sstevel@tonic-gate static void 522*7c478bd9Sstevel@tonic-gate sig_forward(int s) 523*7c478bd9Sstevel@tonic-gate { 524*7c478bd9Sstevel@tonic-gate if (child_pid != -1) { 525*7c478bd9Sstevel@tonic-gate pid_t pgid = getpgid(child_pid); 526*7c478bd9Sstevel@tonic-gate if (pgid != -1) 527*7c478bd9Sstevel@tonic-gate (void) sigsend(P_PGID, pgid, s); 528*7c478bd9Sstevel@tonic-gate } 529*7c478bd9Sstevel@tonic-gate } 530*7c478bd9Sstevel@tonic-gate 531*7c478bd9Sstevel@tonic-gate /* 532*7c478bd9Sstevel@tonic-gate * reset terminal settings for global environment 533*7c478bd9Sstevel@tonic-gate */ 534*7c478bd9Sstevel@tonic-gate static void 535*7c478bd9Sstevel@tonic-gate reset_tty() 536*7c478bd9Sstevel@tonic-gate { 537*7c478bd9Sstevel@tonic-gate (void) tcsetattr(save_fd, TCSADRAIN, &save_termios); 538*7c478bd9Sstevel@tonic-gate } 539*7c478bd9Sstevel@tonic-gate 540*7c478bd9Sstevel@tonic-gate /* 541*7c478bd9Sstevel@tonic-gate * Convert character to printable representation, for display with locally 542*7c478bd9Sstevel@tonic-gate * echoed command characters (like when we need to display ~^D) 543*7c478bd9Sstevel@tonic-gate */ 544*7c478bd9Sstevel@tonic-gate static void 545*7c478bd9Sstevel@tonic-gate canonify(char c, char *cc) 546*7c478bd9Sstevel@tonic-gate { 547*7c478bd9Sstevel@tonic-gate if (isprint(c)) { 548*7c478bd9Sstevel@tonic-gate cc[0] = c; 549*7c478bd9Sstevel@tonic-gate cc[1] = '\0'; 550*7c478bd9Sstevel@tonic-gate } else if (c >= 0 && c <= 31) { /* ^@ through ^_ */ 551*7c478bd9Sstevel@tonic-gate cc[0] = '^'; 552*7c478bd9Sstevel@tonic-gate cc[1] = c + '@'; 553*7c478bd9Sstevel@tonic-gate cc[2] = '\0'; 554*7c478bd9Sstevel@tonic-gate } else { 555*7c478bd9Sstevel@tonic-gate cc[0] = '\\'; 556*7c478bd9Sstevel@tonic-gate cc[1] = ((c >> 6) & 7) + '0'; 557*7c478bd9Sstevel@tonic-gate cc[2] = ((c >> 3) & 7) + '0'; 558*7c478bd9Sstevel@tonic-gate cc[3] = (c & 7) + '0'; 559*7c478bd9Sstevel@tonic-gate cc[4] = '\0'; 560*7c478bd9Sstevel@tonic-gate } 561*7c478bd9Sstevel@tonic-gate } 562*7c478bd9Sstevel@tonic-gate 563*7c478bd9Sstevel@tonic-gate /* 564*7c478bd9Sstevel@tonic-gate * process_user_input watches the input stream for the escape sequence for 565*7c478bd9Sstevel@tonic-gate * 'quit' (by default, tilde-period). Because we might be fed just one 566*7c478bd9Sstevel@tonic-gate * keystroke at a time, state associated with the user input (are we at the 567*7c478bd9Sstevel@tonic-gate * beginning of the line? are we locally echoing the next character?) is 568*7c478bd9Sstevel@tonic-gate * maintained by beginning_of_line and local_echo across calls to the routine. 569*7c478bd9Sstevel@tonic-gate * 570*7c478bd9Sstevel@tonic-gate * This routine returns -1 when the 'quit' escape sequence has been issued, 571*7c478bd9Sstevel@tonic-gate * and 0 otherwise. 572*7c478bd9Sstevel@tonic-gate */ 573*7c478bd9Sstevel@tonic-gate static int 574*7c478bd9Sstevel@tonic-gate process_user_input(int outfd, char *buf, size_t nbytes) 575*7c478bd9Sstevel@tonic-gate { 576*7c478bd9Sstevel@tonic-gate static boolean_t beginning_of_line = B_TRUE; 577*7c478bd9Sstevel@tonic-gate static boolean_t local_echo = B_FALSE; 578*7c478bd9Sstevel@tonic-gate 579*7c478bd9Sstevel@tonic-gate char c = *buf; 580*7c478bd9Sstevel@tonic-gate for (c = *buf; nbytes > 0; c = *buf, --nbytes) { 581*7c478bd9Sstevel@tonic-gate buf++; 582*7c478bd9Sstevel@tonic-gate if (beginning_of_line && !nocmdchar) { 583*7c478bd9Sstevel@tonic-gate beginning_of_line = B_FALSE; 584*7c478bd9Sstevel@tonic-gate if (c == cmdchar) { 585*7c478bd9Sstevel@tonic-gate local_echo = B_TRUE; 586*7c478bd9Sstevel@tonic-gate continue; 587*7c478bd9Sstevel@tonic-gate } 588*7c478bd9Sstevel@tonic-gate } else if (local_echo) { 589*7c478bd9Sstevel@tonic-gate local_echo = B_FALSE; 590*7c478bd9Sstevel@tonic-gate if (c == '.' || c == effective_termios.c_cc[VEOF]) { 591*7c478bd9Sstevel@tonic-gate char cc[CANONIFY_LEN]; 592*7c478bd9Sstevel@tonic-gate canonify(c, cc); 593*7c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, &cmdchar, 1); 594*7c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, cc, strlen(cc)); 595*7c478bd9Sstevel@tonic-gate return (-1); 596*7c478bd9Sstevel@tonic-gate } 597*7c478bd9Sstevel@tonic-gate } 598*7c478bd9Sstevel@tonic-gate if (write(outfd, &c, 1) <= 0) 599*7c478bd9Sstevel@tonic-gate return (-1); 600*7c478bd9Sstevel@tonic-gate beginning_of_line = (c == '\r' || c == '\n' || 601*7c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VKILL] || 602*7c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VEOL] || 603*7c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VSUSP] || 604*7c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VINTR]); 605*7c478bd9Sstevel@tonic-gate } 606*7c478bd9Sstevel@tonic-gate return (0); 607*7c478bd9Sstevel@tonic-gate } 608*7c478bd9Sstevel@tonic-gate 609*7c478bd9Sstevel@tonic-gate /* 610*7c478bd9Sstevel@tonic-gate * This is the main I/O loop, and is shared across all zlogin modes. 611*7c478bd9Sstevel@tonic-gate * Parameters: 612*7c478bd9Sstevel@tonic-gate * stdin_fd: The fd representing 'stdin' for the slave side; input to 613*7c478bd9Sstevel@tonic-gate * the zone will be written here. 614*7c478bd9Sstevel@tonic-gate * 615*7c478bd9Sstevel@tonic-gate * stdout_fd: The fd representing 'stdout' for the slave side; output 616*7c478bd9Sstevel@tonic-gate * from the zone will arrive here. 617*7c478bd9Sstevel@tonic-gate * 618*7c478bd9Sstevel@tonic-gate * stderr_fd: The fd representing 'stderr' for the slave side; output 619*7c478bd9Sstevel@tonic-gate * from the zone will arrive here. 620*7c478bd9Sstevel@tonic-gate * 621*7c478bd9Sstevel@tonic-gate * raw_mode: If TRUE, then no processing (for example, for '~.') will 622*7c478bd9Sstevel@tonic-gate * be performed on the input coming from STDIN. 623*7c478bd9Sstevel@tonic-gate * 624*7c478bd9Sstevel@tonic-gate * stderr_fd may be specified as -1 if there is no stderr (only non-interactive 625*7c478bd9Sstevel@tonic-gate * mode supplies a stderr). 626*7c478bd9Sstevel@tonic-gate * 627*7c478bd9Sstevel@tonic-gate */ 628*7c478bd9Sstevel@tonic-gate static void 629*7c478bd9Sstevel@tonic-gate doio(int stdin_fd, int stdout_fd, int stderr_fd, boolean_t raw_mode) 630*7c478bd9Sstevel@tonic-gate { 631*7c478bd9Sstevel@tonic-gate struct pollfd pollfds[3]; 632*7c478bd9Sstevel@tonic-gate char ibuf[BUFSIZ]; 633*7c478bd9Sstevel@tonic-gate int cc, ret; 634*7c478bd9Sstevel@tonic-gate 635*7c478bd9Sstevel@tonic-gate /* read from stdout of zone and write to stdout of global zone */ 636*7c478bd9Sstevel@tonic-gate pollfds[0].fd = stdout_fd; 637*7c478bd9Sstevel@tonic-gate pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; 638*7c478bd9Sstevel@tonic-gate 639*7c478bd9Sstevel@tonic-gate /* read from stderr of zone and write to stderr of global zone */ 640*7c478bd9Sstevel@tonic-gate pollfds[1].fd = stderr_fd; 641*7c478bd9Sstevel@tonic-gate pollfds[1].events = pollfds[0].events; 642*7c478bd9Sstevel@tonic-gate 643*7c478bd9Sstevel@tonic-gate /* read from stdin of global zone and write to stdin of zone */ 644*7c478bd9Sstevel@tonic-gate pollfds[2].fd = STDIN_FILENO; 645*7c478bd9Sstevel@tonic-gate pollfds[2].events = pollfds[0].events; 646*7c478bd9Sstevel@tonic-gate 647*7c478bd9Sstevel@tonic-gate for (;;) { 648*7c478bd9Sstevel@tonic-gate pollfds[0].revents = pollfds[1].revents = 649*7c478bd9Sstevel@tonic-gate pollfds[2].revents = 0; 650*7c478bd9Sstevel@tonic-gate 651*7c478bd9Sstevel@tonic-gate if (dead) 652*7c478bd9Sstevel@tonic-gate break; 653*7c478bd9Sstevel@tonic-gate 654*7c478bd9Sstevel@tonic-gate ret = poll(pollfds, 655*7c478bd9Sstevel@tonic-gate sizeof (pollfds) / sizeof (struct pollfd), -1); 656*7c478bd9Sstevel@tonic-gate if (ret == -1 && errno != EINTR) { 657*7c478bd9Sstevel@tonic-gate perror("poll failed"); 658*7c478bd9Sstevel@tonic-gate break; 659*7c478bd9Sstevel@tonic-gate } 660*7c478bd9Sstevel@tonic-gate 661*7c478bd9Sstevel@tonic-gate if (errno == EINTR && dead) { 662*7c478bd9Sstevel@tonic-gate break; 663*7c478bd9Sstevel@tonic-gate } 664*7c478bd9Sstevel@tonic-gate 665*7c478bd9Sstevel@tonic-gate /* event from master side stdout */ 666*7c478bd9Sstevel@tonic-gate if (pollfds[0].revents) { 667*7c478bd9Sstevel@tonic-gate if (pollfds[0].revents & 668*7c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 669*7c478bd9Sstevel@tonic-gate cc = read(stdout_fd, ibuf, BUFSIZ); 670*7c478bd9Sstevel@tonic-gate if (cc == -1 && (errno != EINTR || dead)) 671*7c478bd9Sstevel@tonic-gate break; 672*7c478bd9Sstevel@tonic-gate if (cc == 0) /* EOF */ 673*7c478bd9Sstevel@tonic-gate break; 674*7c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, ibuf, cc); 675*7c478bd9Sstevel@tonic-gate } else { 676*7c478bd9Sstevel@tonic-gate pollerr = pollfds[0].revents; 677*7c478bd9Sstevel@tonic-gate break; 678*7c478bd9Sstevel@tonic-gate } 679*7c478bd9Sstevel@tonic-gate } 680*7c478bd9Sstevel@tonic-gate 681*7c478bd9Sstevel@tonic-gate /* event from master side stderr */ 682*7c478bd9Sstevel@tonic-gate if (pollfds[1].revents) { 683*7c478bd9Sstevel@tonic-gate if (pollfds[1].revents & 684*7c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 685*7c478bd9Sstevel@tonic-gate cc = read(stderr_fd, ibuf, BUFSIZ); 686*7c478bd9Sstevel@tonic-gate if (cc == -1 && (errno != EINTR || dead)) 687*7c478bd9Sstevel@tonic-gate break; 688*7c478bd9Sstevel@tonic-gate if (cc == 0) /* EOF */ 689*7c478bd9Sstevel@tonic-gate break; 690*7c478bd9Sstevel@tonic-gate (void) write(STDERR_FILENO, ibuf, cc); 691*7c478bd9Sstevel@tonic-gate } else { 692*7c478bd9Sstevel@tonic-gate pollerr = pollfds[1].revents; 693*7c478bd9Sstevel@tonic-gate break; 694*7c478bd9Sstevel@tonic-gate } 695*7c478bd9Sstevel@tonic-gate } 696*7c478bd9Sstevel@tonic-gate 697*7c478bd9Sstevel@tonic-gate /* event from user STDIN side */ 698*7c478bd9Sstevel@tonic-gate if (pollfds[2].revents) { 699*7c478bd9Sstevel@tonic-gate if (pollfds[2].revents & 700*7c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 701*7c478bd9Sstevel@tonic-gate cc = read(STDIN_FILENO, ibuf, BUFSIZ); 702*7c478bd9Sstevel@tonic-gate if (cc == -1 && (errno != EINTR || dead)) 703*7c478bd9Sstevel@tonic-gate break; 704*7c478bd9Sstevel@tonic-gate 705*7c478bd9Sstevel@tonic-gate /* 706*7c478bd9Sstevel@tonic-gate * stdin fd is stdin of the target; so, 707*7c478bd9Sstevel@tonic-gate * the thing we'll write the user data *to*. 708*7c478bd9Sstevel@tonic-gate * 709*7c478bd9Sstevel@tonic-gate * Also, unlike on the output side, we 710*7c478bd9Sstevel@tonic-gate * propagate zero-length messages to the 711*7c478bd9Sstevel@tonic-gate * other side. 712*7c478bd9Sstevel@tonic-gate */ 713*7c478bd9Sstevel@tonic-gate if (raw_mode == B_TRUE) { 714*7c478bd9Sstevel@tonic-gate if (write(stdin_fd, ibuf, cc) == -1) 715*7c478bd9Sstevel@tonic-gate break; 716*7c478bd9Sstevel@tonic-gate } else { 717*7c478bd9Sstevel@tonic-gate if (process_user_input(stdin_fd, ibuf, 718*7c478bd9Sstevel@tonic-gate cc) == -1) 719*7c478bd9Sstevel@tonic-gate break; 720*7c478bd9Sstevel@tonic-gate } 721*7c478bd9Sstevel@tonic-gate } else if (raw_mode == B_TRUE && 722*7c478bd9Sstevel@tonic-gate pollfds[2].revents & POLLHUP) { 723*7c478bd9Sstevel@tonic-gate /* 724*7c478bd9Sstevel@tonic-gate * It's OK to get a POLLHUP on STDIN-- it 725*7c478bd9Sstevel@tonic-gate * always happens if you do: 726*7c478bd9Sstevel@tonic-gate * 727*7c478bd9Sstevel@tonic-gate * echo foo | zlogin <zone> <command> 728*7c478bd9Sstevel@tonic-gate * 729*7c478bd9Sstevel@tonic-gate * We reset fd to -1 in this case to clear 730*7c478bd9Sstevel@tonic-gate * the condition and write an EOF to the 731*7c478bd9Sstevel@tonic-gate * other side in order to wrap things up. 732*7c478bd9Sstevel@tonic-gate */ 733*7c478bd9Sstevel@tonic-gate pollfds[2].fd = -1; 734*7c478bd9Sstevel@tonic-gate (void) write(stdin_fd, ibuf, 0); 735*7c478bd9Sstevel@tonic-gate } else { 736*7c478bd9Sstevel@tonic-gate pollerr = pollfds[2].revents; 737*7c478bd9Sstevel@tonic-gate break; 738*7c478bd9Sstevel@tonic-gate } 739*7c478bd9Sstevel@tonic-gate } 740*7c478bd9Sstevel@tonic-gate } 741*7c478bd9Sstevel@tonic-gate 742*7c478bd9Sstevel@tonic-gate /* 743*7c478bd9Sstevel@tonic-gate * We are in the midst of dying, but try to poll with a short 744*7c478bd9Sstevel@tonic-gate * timeout to see if we can catch the last bit of I/O from the 745*7c478bd9Sstevel@tonic-gate * children. 746*7c478bd9Sstevel@tonic-gate */ 747*7c478bd9Sstevel@tonic-gate pollfds[0].revents = pollfds[1].revents = pollfds[2].revents = 0; 748*7c478bd9Sstevel@tonic-gate (void) poll(pollfds, 749*7c478bd9Sstevel@tonic-gate sizeof (pollfds) / sizeof (struct pollfd), 100); 750*7c478bd9Sstevel@tonic-gate if (pollfds[0].revents & 751*7c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 752*7c478bd9Sstevel@tonic-gate if ((cc = read(stdout_fd, ibuf, BUFSIZ)) > 0) 753*7c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, ibuf, cc); 754*7c478bd9Sstevel@tonic-gate } 755*7c478bd9Sstevel@tonic-gate if (pollfds[1].revents & 756*7c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 757*7c478bd9Sstevel@tonic-gate if ((cc = read(stderr_fd, ibuf, BUFSIZ)) > 0) 758*7c478bd9Sstevel@tonic-gate (void) write(STDERR_FILENO, ibuf, cc); 759*7c478bd9Sstevel@tonic-gate } 760*7c478bd9Sstevel@tonic-gate } 761*7c478bd9Sstevel@tonic-gate 762*7c478bd9Sstevel@tonic-gate /* 763*7c478bd9Sstevel@tonic-gate * Prepare argv array for exec'd process; if we're passing commands to the 764*7c478bd9Sstevel@tonic-gate * new process, then use su(1M) to do the invocation. Otherwise, use 765*7c478bd9Sstevel@tonic-gate * 'login -z <from_zonename> -f' (-z is an undocumented option which tells 766*7c478bd9Sstevel@tonic-gate * login that we're coming from another zone, and to disregard its CONSOLE 767*7c478bd9Sstevel@tonic-gate * checks). 768*7c478bd9Sstevel@tonic-gate */ 769*7c478bd9Sstevel@tonic-gate static char ** 770*7c478bd9Sstevel@tonic-gate prep_args(char *login, char **argv) 771*7c478bd9Sstevel@tonic-gate { 772*7c478bd9Sstevel@tonic-gate int argc = 0, a = 0, i, n = -1; 773*7c478bd9Sstevel@tonic-gate char **new_argv; 774*7c478bd9Sstevel@tonic-gate 775*7c478bd9Sstevel@tonic-gate if (argv != NULL) { 776*7c478bd9Sstevel@tonic-gate size_t subshell_len = 1; 777*7c478bd9Sstevel@tonic-gate char *subshell; 778*7c478bd9Sstevel@tonic-gate 779*7c478bd9Sstevel@tonic-gate while (argv[argc] != NULL) 780*7c478bd9Sstevel@tonic-gate argc++; 781*7c478bd9Sstevel@tonic-gate 782*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 783*7c478bd9Sstevel@tonic-gate subshell_len += strlen(argv[i]) + 1; 784*7c478bd9Sstevel@tonic-gate } 785*7c478bd9Sstevel@tonic-gate if ((subshell = calloc(1, subshell_len)) == NULL) 786*7c478bd9Sstevel@tonic-gate return (NULL); 787*7c478bd9Sstevel@tonic-gate 788*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 789*7c478bd9Sstevel@tonic-gate (void) strcat(subshell, argv[i]); 790*7c478bd9Sstevel@tonic-gate (void) strcat(subshell, " "); 791*7c478bd9Sstevel@tonic-gate } 792*7c478bd9Sstevel@tonic-gate 793*7c478bd9Sstevel@tonic-gate if (failsafe) { 794*7c478bd9Sstevel@tonic-gate n = 4; 795*7c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 796*7c478bd9Sstevel@tonic-gate return (NULL); 797*7c478bd9Sstevel@tonic-gate 798*7c478bd9Sstevel@tonic-gate new_argv[a++] = FAILSAFESHELL; 799*7c478bd9Sstevel@tonic-gate } else { 800*7c478bd9Sstevel@tonic-gate n = 5; 801*7c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 802*7c478bd9Sstevel@tonic-gate return (NULL); 803*7c478bd9Sstevel@tonic-gate 804*7c478bd9Sstevel@tonic-gate new_argv[a++] = SUPATH; 805*7c478bd9Sstevel@tonic-gate new_argv[a++] = login; 806*7c478bd9Sstevel@tonic-gate } 807*7c478bd9Sstevel@tonic-gate new_argv[a++] = "-c"; 808*7c478bd9Sstevel@tonic-gate new_argv[a++] = subshell; 809*7c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 810*7c478bd9Sstevel@tonic-gate assert(a == n); 811*7c478bd9Sstevel@tonic-gate } else { 812*7c478bd9Sstevel@tonic-gate if (failsafe) { 813*7c478bd9Sstevel@tonic-gate n = 2; 814*7c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 815*7c478bd9Sstevel@tonic-gate return (NULL); 816*7c478bd9Sstevel@tonic-gate new_argv[a++] = FAILSAFESHELL; 817*7c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 818*7c478bd9Sstevel@tonic-gate } else { 819*7c478bd9Sstevel@tonic-gate n = 6; 820*7c478bd9Sstevel@tonic-gate 821*7c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 822*7c478bd9Sstevel@tonic-gate return (NULL); 823*7c478bd9Sstevel@tonic-gate 824*7c478bd9Sstevel@tonic-gate new_argv[a++] = LOGINPATH; 825*7c478bd9Sstevel@tonic-gate new_argv[a++] = "-z"; 826*7c478bd9Sstevel@tonic-gate new_argv[a++] = "global"; /* hardcode, for now */ 827*7c478bd9Sstevel@tonic-gate new_argv[a++] = "-f"; 828*7c478bd9Sstevel@tonic-gate new_argv[a++] = login; 829*7c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 830*7c478bd9Sstevel@tonic-gate } 831*7c478bd9Sstevel@tonic-gate } 832*7c478bd9Sstevel@tonic-gate /* 833*7c478bd9Sstevel@tonic-gate * If this assert ever trips, it's because we've botched the setup 834*7c478bd9Sstevel@tonic-gate * of ARGV above-- it's too large or too small. 835*7c478bd9Sstevel@tonic-gate */ 836*7c478bd9Sstevel@tonic-gate assert(n == a); 837*7c478bd9Sstevel@tonic-gate return (new_argv); 838*7c478bd9Sstevel@tonic-gate } 839*7c478bd9Sstevel@tonic-gate 840*7c478bd9Sstevel@tonic-gate /* 841*7c478bd9Sstevel@tonic-gate * Helper routine for prep_env below. 842*7c478bd9Sstevel@tonic-gate */ 843*7c478bd9Sstevel@tonic-gate static char * 844*7c478bd9Sstevel@tonic-gate add_env(char *name, char *value) 845*7c478bd9Sstevel@tonic-gate { 846*7c478bd9Sstevel@tonic-gate size_t sz = strlen(name) + strlen(value) + 2; /* name, =, value, NUL */ 847*7c478bd9Sstevel@tonic-gate char *str; 848*7c478bd9Sstevel@tonic-gate 849*7c478bd9Sstevel@tonic-gate if ((str = malloc(sz)) == NULL) 850*7c478bd9Sstevel@tonic-gate return (NULL); 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate (void) snprintf(str, sz, "%s=%s", name, value); 853*7c478bd9Sstevel@tonic-gate return (str); 854*7c478bd9Sstevel@tonic-gate } 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate /* 857*7c478bd9Sstevel@tonic-gate * Prepare envp array for exec'd process. 858*7c478bd9Sstevel@tonic-gate */ 859*7c478bd9Sstevel@tonic-gate static char ** 860*7c478bd9Sstevel@tonic-gate prep_env() 861*7c478bd9Sstevel@tonic-gate { 862*7c478bd9Sstevel@tonic-gate int e = 0, size = 1; 863*7c478bd9Sstevel@tonic-gate char **new_env, *estr; 864*7c478bd9Sstevel@tonic-gate char *term = getenv("TERM"); 865*7c478bd9Sstevel@tonic-gate 866*7c478bd9Sstevel@tonic-gate size++; /* for $PATH */ 867*7c478bd9Sstevel@tonic-gate if (term != NULL) 868*7c478bd9Sstevel@tonic-gate size++; 869*7c478bd9Sstevel@tonic-gate 870*7c478bd9Sstevel@tonic-gate /* 871*7c478bd9Sstevel@tonic-gate * In failsafe mode we set $HOME, since '-l' isn't valid in this mode. 872*7c478bd9Sstevel@tonic-gate * We also set $SHELL, since neither login nor su will be around to do 873*7c478bd9Sstevel@tonic-gate * it. 874*7c478bd9Sstevel@tonic-gate */ 875*7c478bd9Sstevel@tonic-gate if (failsafe) 876*7c478bd9Sstevel@tonic-gate size += 2; 877*7c478bd9Sstevel@tonic-gate 878*7c478bd9Sstevel@tonic-gate if ((new_env = malloc(sizeof (char *) * size)) == NULL) 879*7c478bd9Sstevel@tonic-gate return (NULL); 880*7c478bd9Sstevel@tonic-gate 881*7c478bd9Sstevel@tonic-gate if ((estr = add_env("PATH", DEF_PATH)) == NULL) 882*7c478bd9Sstevel@tonic-gate return (NULL); 883*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate if (term != NULL) { 886*7c478bd9Sstevel@tonic-gate if ((estr = add_env("TERM", term)) == NULL) 887*7c478bd9Sstevel@tonic-gate return (NULL); 888*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 889*7c478bd9Sstevel@tonic-gate } 890*7c478bd9Sstevel@tonic-gate 891*7c478bd9Sstevel@tonic-gate if (failsafe) { 892*7c478bd9Sstevel@tonic-gate if ((estr = add_env("HOME", "/")) == NULL) 893*7c478bd9Sstevel@tonic-gate return (NULL); 894*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 895*7c478bd9Sstevel@tonic-gate 896*7c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", FAILSAFESHELL)) == NULL) 897*7c478bd9Sstevel@tonic-gate return (NULL); 898*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 899*7c478bd9Sstevel@tonic-gate } 900*7c478bd9Sstevel@tonic-gate 901*7c478bd9Sstevel@tonic-gate new_env[e++] = NULL; 902*7c478bd9Sstevel@tonic-gate 903*7c478bd9Sstevel@tonic-gate assert(e == size); 904*7c478bd9Sstevel@tonic-gate 905*7c478bd9Sstevel@tonic-gate return (new_env); 906*7c478bd9Sstevel@tonic-gate } 907*7c478bd9Sstevel@tonic-gate 908*7c478bd9Sstevel@tonic-gate /* 909*7c478bd9Sstevel@tonic-gate * Finish the preparation of the envp array for exec'd non-interactive 910*7c478bd9Sstevel@tonic-gate * zlogins. This is called in the child process *after* we zone_enter(), since 911*7c478bd9Sstevel@tonic-gate * it derives things we can only know within the zone, such as $HOME, $SHELL, 912*7c478bd9Sstevel@tonic-gate * etc. We need only do this in the non-interactive, mode, since otherwise 913*7c478bd9Sstevel@tonic-gate * login(1) will do it. We don't do this in failsafe mode, since it presents 914*7c478bd9Sstevel@tonic-gate * additional ways in which the command could fail, and we'd prefer to avoid 915*7c478bd9Sstevel@tonic-gate * that. 916*7c478bd9Sstevel@tonic-gate */ 917*7c478bd9Sstevel@tonic-gate static char ** 918*7c478bd9Sstevel@tonic-gate prep_env_noninteractive(char *login, char **env) 919*7c478bd9Sstevel@tonic-gate { 920*7c478bd9Sstevel@tonic-gate size_t size; 921*7c478bd9Sstevel@tonic-gate struct passwd *pw; 922*7c478bd9Sstevel@tonic-gate char **new_env; 923*7c478bd9Sstevel@tonic-gate int e, i; 924*7c478bd9Sstevel@tonic-gate char *estr; 925*7c478bd9Sstevel@tonic-gate char varmail[LOGNAME_MAX + 11]; /* strlen(/var/mail/) = 10, NUL */ 926*7c478bd9Sstevel@tonic-gate 927*7c478bd9Sstevel@tonic-gate assert(env != NULL); 928*7c478bd9Sstevel@tonic-gate assert(failsafe == 0); 929*7c478bd9Sstevel@tonic-gate 930*7c478bd9Sstevel@tonic-gate /* 931*7c478bd9Sstevel@tonic-gate * Get existing envp size. 932*7c478bd9Sstevel@tonic-gate */ 933*7c478bd9Sstevel@tonic-gate for (size = 0; env[size] != NULL; size++) 934*7c478bd9Sstevel@tonic-gate ; 935*7c478bd9Sstevel@tonic-gate e = size; 936*7c478bd9Sstevel@tonic-gate 937*7c478bd9Sstevel@tonic-gate /* 938*7c478bd9Sstevel@tonic-gate * Finish filling out the environment; we duplicate the environment 939*7c478bd9Sstevel@tonic-gate * setup described in login(1), for lack of a better precedent. 940*7c478bd9Sstevel@tonic-gate */ 941*7c478bd9Sstevel@tonic-gate if ((pw = getpwnam(login)) != NULL) { 942*7c478bd9Sstevel@tonic-gate size += 3; /* LOGNAME, HOME, MAIL */ 943*7c478bd9Sstevel@tonic-gate } 944*7c478bd9Sstevel@tonic-gate size++; /* always fill in SHELL */ 945*7c478bd9Sstevel@tonic-gate size++; /* terminating NULL */ 946*7c478bd9Sstevel@tonic-gate 947*7c478bd9Sstevel@tonic-gate if ((new_env = malloc(sizeof (char *) * size)) == NULL) 948*7c478bd9Sstevel@tonic-gate goto malloc_fail; 949*7c478bd9Sstevel@tonic-gate 950*7c478bd9Sstevel@tonic-gate /* 951*7c478bd9Sstevel@tonic-gate * Copy existing elements of env into new_env. 952*7c478bd9Sstevel@tonic-gate */ 953*7c478bd9Sstevel@tonic-gate for (i = 0; env[i] != NULL; i++) { 954*7c478bd9Sstevel@tonic-gate if ((new_env[i] = strdup(env[i])) == NULL) 955*7c478bd9Sstevel@tonic-gate goto malloc_fail; 956*7c478bd9Sstevel@tonic-gate } 957*7c478bd9Sstevel@tonic-gate assert(e == i); 958*7c478bd9Sstevel@tonic-gate 959*7c478bd9Sstevel@tonic-gate if (pw != NULL) { 960*7c478bd9Sstevel@tonic-gate if ((estr = add_env("LOGNAME", pw->pw_name)) == NULL) 961*7c478bd9Sstevel@tonic-gate goto malloc_fail; 962*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 963*7c478bd9Sstevel@tonic-gate 964*7c478bd9Sstevel@tonic-gate if ((estr = add_env("HOME", pw->pw_dir)) == NULL) 965*7c478bd9Sstevel@tonic-gate goto malloc_fail; 966*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 967*7c478bd9Sstevel@tonic-gate 968*7c478bd9Sstevel@tonic-gate if (chdir(pw->pw_dir) != 0) 969*7c478bd9Sstevel@tonic-gate zerror(gettext("Could not chdir to home directory " 970*7c478bd9Sstevel@tonic-gate "%s: %s"), pw->pw_dir, strerror(errno)); 971*7c478bd9Sstevel@tonic-gate 972*7c478bd9Sstevel@tonic-gate (void) snprintf(varmail, sizeof (varmail), "/var/mail/%s", 973*7c478bd9Sstevel@tonic-gate pw->pw_name); 974*7c478bd9Sstevel@tonic-gate if ((estr = add_env("MAIL", varmail)) == NULL) 975*7c478bd9Sstevel@tonic-gate goto malloc_fail; 976*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 977*7c478bd9Sstevel@tonic-gate } 978*7c478bd9Sstevel@tonic-gate 979*7c478bd9Sstevel@tonic-gate if (pw != NULL && strlen(pw->pw_shell) > 0) { 980*7c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", pw->pw_shell)) == NULL) 981*7c478bd9Sstevel@tonic-gate goto malloc_fail; 982*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 983*7c478bd9Sstevel@tonic-gate } else { 984*7c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", DEFAULTSHELL)) == NULL) 985*7c478bd9Sstevel@tonic-gate goto malloc_fail; 986*7c478bd9Sstevel@tonic-gate new_env[e++] = estr; 987*7c478bd9Sstevel@tonic-gate } 988*7c478bd9Sstevel@tonic-gate 989*7c478bd9Sstevel@tonic-gate new_env[e++] = NULL; /* add terminating NULL */ 990*7c478bd9Sstevel@tonic-gate 991*7c478bd9Sstevel@tonic-gate assert(e == size); 992*7c478bd9Sstevel@tonic-gate return (new_env); 993*7c478bd9Sstevel@tonic-gate 994*7c478bd9Sstevel@tonic-gate malloc_fail: 995*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to allocate memory for process environment")); 996*7c478bd9Sstevel@tonic-gate return (NULL); 997*7c478bd9Sstevel@tonic-gate } 998*7c478bd9Sstevel@tonic-gate 999*7c478bd9Sstevel@tonic-gate static int 1000*7c478bd9Sstevel@tonic-gate close_func(void *slavefd, int fd) 1001*7c478bd9Sstevel@tonic-gate { 1002*7c478bd9Sstevel@tonic-gate if (fd != *(int *)slavefd) 1003*7c478bd9Sstevel@tonic-gate (void) close(fd); 1004*7c478bd9Sstevel@tonic-gate return (0); 1005*7c478bd9Sstevel@tonic-gate } 1006*7c478bd9Sstevel@tonic-gate 1007*7c478bd9Sstevel@tonic-gate static void 1008*7c478bd9Sstevel@tonic-gate set_cmdchar(char *cmdcharstr) 1009*7c478bd9Sstevel@tonic-gate { 1010*7c478bd9Sstevel@tonic-gate char c; 1011*7c478bd9Sstevel@tonic-gate long lc; 1012*7c478bd9Sstevel@tonic-gate 1013*7c478bd9Sstevel@tonic-gate if ((c = *cmdcharstr) != '\\') { 1014*7c478bd9Sstevel@tonic-gate cmdchar = c; 1015*7c478bd9Sstevel@tonic-gate return; 1016*7c478bd9Sstevel@tonic-gate } 1017*7c478bd9Sstevel@tonic-gate 1018*7c478bd9Sstevel@tonic-gate c = cmdcharstr[1]; 1019*7c478bd9Sstevel@tonic-gate if (c == '\0' || c == '\\') { 1020*7c478bd9Sstevel@tonic-gate cmdchar = '\\'; 1021*7c478bd9Sstevel@tonic-gate return; 1022*7c478bd9Sstevel@tonic-gate } 1023*7c478bd9Sstevel@tonic-gate 1024*7c478bd9Sstevel@tonic-gate if (c < '0' || c > '7') { 1025*7c478bd9Sstevel@tonic-gate zerror(gettext("Unrecognized escape character option %s"), 1026*7c478bd9Sstevel@tonic-gate cmdcharstr); 1027*7c478bd9Sstevel@tonic-gate usage(); 1028*7c478bd9Sstevel@tonic-gate } 1029*7c478bd9Sstevel@tonic-gate 1030*7c478bd9Sstevel@tonic-gate lc = strtol(cmdcharstr + 1, NULL, 8); 1031*7c478bd9Sstevel@tonic-gate if (lc < 0 || lc > 255) { 1032*7c478bd9Sstevel@tonic-gate zerror(gettext("Octal escape character '%s' too large"), 1033*7c478bd9Sstevel@tonic-gate cmdcharstr); 1034*7c478bd9Sstevel@tonic-gate usage(); 1035*7c478bd9Sstevel@tonic-gate } 1036*7c478bd9Sstevel@tonic-gate cmdchar = (char)lc; 1037*7c478bd9Sstevel@tonic-gate } 1038*7c478bd9Sstevel@tonic-gate 1039*7c478bd9Sstevel@tonic-gate static int 1040*7c478bd9Sstevel@tonic-gate setup_utmpx(char *slavename) 1041*7c478bd9Sstevel@tonic-gate { 1042*7c478bd9Sstevel@tonic-gate struct utmpx ut; 1043*7c478bd9Sstevel@tonic-gate 1044*7c478bd9Sstevel@tonic-gate bzero(&ut, sizeof (ut)); 1045*7c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_user, ".zlogin", sizeof (ut.ut_user)); 1046*7c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_line, slavename, sizeof (ut.ut_line)); 1047*7c478bd9Sstevel@tonic-gate ut.ut_pid = getpid(); 1048*7c478bd9Sstevel@tonic-gate ut.ut_id[0] = 'z'; 1049*7c478bd9Sstevel@tonic-gate ut.ut_id[1] = ut.ut_id[2] = ut.ut_id[3] = (char)SC_WILDC; 1050*7c478bd9Sstevel@tonic-gate ut.ut_type = LOGIN_PROCESS; 1051*7c478bd9Sstevel@tonic-gate (void) time(&ut.ut_tv.tv_sec); 1052*7c478bd9Sstevel@tonic-gate 1053*7c478bd9Sstevel@tonic-gate if (makeutx(&ut) == NULL) { 1054*7c478bd9Sstevel@tonic-gate zerror(gettext("makeutx failed")); 1055*7c478bd9Sstevel@tonic-gate return (-1); 1056*7c478bd9Sstevel@tonic-gate } 1057*7c478bd9Sstevel@tonic-gate return (0); 1058*7c478bd9Sstevel@tonic-gate } 1059*7c478bd9Sstevel@tonic-gate 1060*7c478bd9Sstevel@tonic-gate static void 1061*7c478bd9Sstevel@tonic-gate release_lock_file(int lockfd) 1062*7c478bd9Sstevel@tonic-gate { 1063*7c478bd9Sstevel@tonic-gate (void) close(lockfd); 1064*7c478bd9Sstevel@tonic-gate } 1065*7c478bd9Sstevel@tonic-gate 1066*7c478bd9Sstevel@tonic-gate static int 1067*7c478bd9Sstevel@tonic-gate grab_lock_file(const char *zone_name, int *lockfd) 1068*7c478bd9Sstevel@tonic-gate { 1069*7c478bd9Sstevel@tonic-gate char pathbuf[PATH_MAX]; 1070*7c478bd9Sstevel@tonic-gate struct flock flock; 1071*7c478bd9Sstevel@tonic-gate 1072*7c478bd9Sstevel@tonic-gate if (mkdir(ZONES_TMPDIR, S_IRWXU) < 0 && errno != EEXIST) { 1073*7c478bd9Sstevel@tonic-gate zerror(gettext("could not mkdir %s: %s"), ZONES_TMPDIR, 1074*7c478bd9Sstevel@tonic-gate strerror(errno)); 1075*7c478bd9Sstevel@tonic-gate return (-1); 1076*7c478bd9Sstevel@tonic-gate } 1077*7c478bd9Sstevel@tonic-gate (void) chmod(ZONES_TMPDIR, S_IRWXU); 1078*7c478bd9Sstevel@tonic-gate (void) snprintf(pathbuf, sizeof (pathbuf), "%s/%s.zoneadm.lock", 1079*7c478bd9Sstevel@tonic-gate ZONES_TMPDIR, zone_name); 1080*7c478bd9Sstevel@tonic-gate 1081*7c478bd9Sstevel@tonic-gate if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 1082*7c478bd9Sstevel@tonic-gate zerror(gettext("could not open %s: %s"), pathbuf, 1083*7c478bd9Sstevel@tonic-gate strerror(errno)); 1084*7c478bd9Sstevel@tonic-gate return (-1); 1085*7c478bd9Sstevel@tonic-gate } 1086*7c478bd9Sstevel@tonic-gate /* 1087*7c478bd9Sstevel@tonic-gate * Lock the file to synchronize with other zoneadmds 1088*7c478bd9Sstevel@tonic-gate */ 1089*7c478bd9Sstevel@tonic-gate flock.l_type = F_WRLCK; 1090*7c478bd9Sstevel@tonic-gate flock.l_whence = SEEK_SET; 1091*7c478bd9Sstevel@tonic-gate flock.l_start = (off_t)0; 1092*7c478bd9Sstevel@tonic-gate flock.l_len = (off_t)0; 1093*7c478bd9Sstevel@tonic-gate if (fcntl(*lockfd, F_SETLKW, &flock) < 0) { 1094*7c478bd9Sstevel@tonic-gate zerror(gettext("unable to lock %s: %s"), pathbuf, 1095*7c478bd9Sstevel@tonic-gate strerror(errno)); 1096*7c478bd9Sstevel@tonic-gate release_lock_file(*lockfd); 1097*7c478bd9Sstevel@tonic-gate return (-1); 1098*7c478bd9Sstevel@tonic-gate } 1099*7c478bd9Sstevel@tonic-gate return (Z_OK); 1100*7c478bd9Sstevel@tonic-gate } 1101*7c478bd9Sstevel@tonic-gate 1102*7c478bd9Sstevel@tonic-gate static int 1103*7c478bd9Sstevel@tonic-gate start_zoneadmd(const char *zone_name) 1104*7c478bd9Sstevel@tonic-gate { 1105*7c478bd9Sstevel@tonic-gate pid_t retval; 1106*7c478bd9Sstevel@tonic-gate int pstatus = 0, error = -1, lockfd, doorfd; 1107*7c478bd9Sstevel@tonic-gate struct door_info info; 1108*7c478bd9Sstevel@tonic-gate char doorpath[MAXPATHLEN]; 1109*7c478bd9Sstevel@tonic-gate 1110*7c478bd9Sstevel@tonic-gate (void) snprintf(doorpath, sizeof (doorpath), ZONE_DOOR_PATH, zone_name); 1111*7c478bd9Sstevel@tonic-gate 1112*7c478bd9Sstevel@tonic-gate if (grab_lock_file(zone_name, &lockfd) != Z_OK) 1113*7c478bd9Sstevel@tonic-gate return (-1); 1114*7c478bd9Sstevel@tonic-gate /* 1115*7c478bd9Sstevel@tonic-gate * We must do the door check with the lock held. Otherwise, we 1116*7c478bd9Sstevel@tonic-gate * might race against another zoneadm/zlogin process and wind 1117*7c478bd9Sstevel@tonic-gate * up with two processes trying to start zoneadmd at the same 1118*7c478bd9Sstevel@tonic-gate * time. zoneadmd will detect this, and fail, but we prefer this 1119*7c478bd9Sstevel@tonic-gate * to be as seamless as is practical, from a user perspective. 1120*7c478bd9Sstevel@tonic-gate */ 1121*7c478bd9Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 1122*7c478bd9Sstevel@tonic-gate if (errno != ENOENT) { 1123*7c478bd9Sstevel@tonic-gate zerror("failed to open %s: %s", doorpath, 1124*7c478bd9Sstevel@tonic-gate strerror(errno)); 1125*7c478bd9Sstevel@tonic-gate goto out; 1126*7c478bd9Sstevel@tonic-gate } 1127*7c478bd9Sstevel@tonic-gate } else { 1128*7c478bd9Sstevel@tonic-gate /* 1129*7c478bd9Sstevel@tonic-gate * Seems to be working ok. 1130*7c478bd9Sstevel@tonic-gate */ 1131*7c478bd9Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 1132*7c478bd9Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 1133*7c478bd9Sstevel@tonic-gate error = 0; 1134*7c478bd9Sstevel@tonic-gate goto out; 1135*7c478bd9Sstevel@tonic-gate } 1136*7c478bd9Sstevel@tonic-gate } 1137*7c478bd9Sstevel@tonic-gate 1138*7c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 1139*7c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 1140*7c478bd9Sstevel@tonic-gate goto out; 1141*7c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { 1142*7c478bd9Sstevel@tonic-gate /* child process */ 1143*7c478bd9Sstevel@tonic-gate (void) execl("/usr/lib/zones/zoneadmd", "zoneadmd", "-z", 1144*7c478bd9Sstevel@tonic-gate zone_name, NULL); 1145*7c478bd9Sstevel@tonic-gate zperror(gettext("could not exec zoneadmd")); 1146*7c478bd9Sstevel@tonic-gate _exit(1); 1147*7c478bd9Sstevel@tonic-gate } 1148*7c478bd9Sstevel@tonic-gate 1149*7c478bd9Sstevel@tonic-gate /* parent process */ 1150*7c478bd9Sstevel@tonic-gate do { 1151*7c478bd9Sstevel@tonic-gate retval = waitpid(child_pid, &pstatus, 0); 1152*7c478bd9Sstevel@tonic-gate } while (retval != child_pid); 1153*7c478bd9Sstevel@tonic-gate if (WIFSIGNALED(pstatus) || 1154*7c478bd9Sstevel@tonic-gate (WIFEXITED(pstatus) && WEXITSTATUS(pstatus) != 0)) { 1155*7c478bd9Sstevel@tonic-gate zerror(gettext("could not start %s"), "zoneadmd"); 1156*7c478bd9Sstevel@tonic-gate goto out; 1157*7c478bd9Sstevel@tonic-gate } 1158*7c478bd9Sstevel@tonic-gate error = 0; 1159*7c478bd9Sstevel@tonic-gate out: 1160*7c478bd9Sstevel@tonic-gate release_lock_file(lockfd); 1161*7c478bd9Sstevel@tonic-gate (void) close(doorfd); 1162*7c478bd9Sstevel@tonic-gate return (error); 1163*7c478bd9Sstevel@tonic-gate } 1164*7c478bd9Sstevel@tonic-gate 1165*7c478bd9Sstevel@tonic-gate static int 1166*7c478bd9Sstevel@tonic-gate init_template(void) 1167*7c478bd9Sstevel@tonic-gate { 1168*7c478bd9Sstevel@tonic-gate int fd; 1169*7c478bd9Sstevel@tonic-gate int err = 0; 1170*7c478bd9Sstevel@tonic-gate 1171*7c478bd9Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/template", O_RDWR); 1172*7c478bd9Sstevel@tonic-gate if (fd == -1) 1173*7c478bd9Sstevel@tonic-gate return (-1); 1174*7c478bd9Sstevel@tonic-gate 1175*7c478bd9Sstevel@tonic-gate /* 1176*7c478bd9Sstevel@tonic-gate * zlogin doesn't do anything with the contract. 1177*7c478bd9Sstevel@tonic-gate * Deliver no events, don't inherit, and allow it to be orphaned. 1178*7c478bd9Sstevel@tonic-gate */ 1179*7c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_critical(fd, 0); 1180*7c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_informative(fd, 0); 1181*7c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); 1182*7c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); 1183*7c478bd9Sstevel@tonic-gate if (err || ct_tmpl_activate(fd)) { 1184*7c478bd9Sstevel@tonic-gate (void) close(fd); 1185*7c478bd9Sstevel@tonic-gate return (-1); 1186*7c478bd9Sstevel@tonic-gate } 1187*7c478bd9Sstevel@tonic-gate 1188*7c478bd9Sstevel@tonic-gate return (fd); 1189*7c478bd9Sstevel@tonic-gate } 1190*7c478bd9Sstevel@tonic-gate 1191*7c478bd9Sstevel@tonic-gate static int 1192*7c478bd9Sstevel@tonic-gate noninteractive_login(char *zonename, zoneid_t zoneid, char *login, 1193*7c478bd9Sstevel@tonic-gate char **new_args, char **new_env) 1194*7c478bd9Sstevel@tonic-gate { 1195*7c478bd9Sstevel@tonic-gate pid_t retval; 1196*7c478bd9Sstevel@tonic-gate int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2]; 1197*7c478bd9Sstevel@tonic-gate int child_status; 1198*7c478bd9Sstevel@tonic-gate int tmpl_fd; 1199*7c478bd9Sstevel@tonic-gate sigset_t block_cld; 1200*7c478bd9Sstevel@tonic-gate 1201*7c478bd9Sstevel@tonic-gate if ((tmpl_fd = init_template()) == -1) { 1202*7c478bd9Sstevel@tonic-gate reset_tty(); 1203*7c478bd9Sstevel@tonic-gate zperror(gettext("could not create contract")); 1204*7c478bd9Sstevel@tonic-gate return (1); 1205*7c478bd9Sstevel@tonic-gate } 1206*7c478bd9Sstevel@tonic-gate 1207*7c478bd9Sstevel@tonic-gate if (pipe(stdin_pipe) != 0) { 1208*7c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDIN pipe")); 1209*7c478bd9Sstevel@tonic-gate return (1); 1210*7c478bd9Sstevel@tonic-gate } 1211*7c478bd9Sstevel@tonic-gate /* 1212*7c478bd9Sstevel@tonic-gate * When the user types ^D, we get a zero length message on STDIN. 1213*7c478bd9Sstevel@tonic-gate * We need to echo that down the pipe to send it to the other side; 1214*7c478bd9Sstevel@tonic-gate * but by default, pipes don't propagate zero-length messages. We 1215*7c478bd9Sstevel@tonic-gate * toggle that behavior off using I_SWROPT. See streamio(7i). 1216*7c478bd9Sstevel@tonic-gate */ 1217*7c478bd9Sstevel@tonic-gate if (ioctl(stdin_pipe[0], I_SWROPT, SNDZERO) != 0) { 1218*7c478bd9Sstevel@tonic-gate zperror(gettext("could not configure STDIN pipe")); 1219*7c478bd9Sstevel@tonic-gate return (1); 1220*7c478bd9Sstevel@tonic-gate 1221*7c478bd9Sstevel@tonic-gate } 1222*7c478bd9Sstevel@tonic-gate if (pipe(stdout_pipe) != 0) { 1223*7c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDOUT pipe")); 1224*7c478bd9Sstevel@tonic-gate return (1); 1225*7c478bd9Sstevel@tonic-gate } 1226*7c478bd9Sstevel@tonic-gate if (pipe(stderr_pipe) != 0) { 1227*7c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDERR pipe")); 1228*7c478bd9Sstevel@tonic-gate return (1); 1229*7c478bd9Sstevel@tonic-gate } 1230*7c478bd9Sstevel@tonic-gate 1231*7c478bd9Sstevel@tonic-gate /* 1232*7c478bd9Sstevel@tonic-gate * If any of the pipe FD's winds up being less than STDERR, then we 1233*7c478bd9Sstevel@tonic-gate * have a mess on our hands-- and we are lacking some of the I/O 1234*7c478bd9Sstevel@tonic-gate * streams we would expect anyway. So we bail. 1235*7c478bd9Sstevel@tonic-gate */ 1236*7c478bd9Sstevel@tonic-gate if (stdin_pipe[0] <= STDERR_FILENO || 1237*7c478bd9Sstevel@tonic-gate stdin_pipe[1] <= STDERR_FILENO || 1238*7c478bd9Sstevel@tonic-gate stdout_pipe[0] <= STDERR_FILENO || 1239*7c478bd9Sstevel@tonic-gate stdout_pipe[1] <= STDERR_FILENO || 1240*7c478bd9Sstevel@tonic-gate stderr_pipe[0] <= STDERR_FILENO || 1241*7c478bd9Sstevel@tonic-gate stderr_pipe[1] <= STDERR_FILENO) { 1242*7c478bd9Sstevel@tonic-gate zperror(gettext("process lacks valid STDIN, STDOUT, STDERR")); 1243*7c478bd9Sstevel@tonic-gate return (1); 1244*7c478bd9Sstevel@tonic-gate } 1245*7c478bd9Sstevel@tonic-gate 1246*7c478bd9Sstevel@tonic-gate if (prefork_dropprivs() != 0) { 1247*7c478bd9Sstevel@tonic-gate zperror(gettext("could not allocate privilege set")); 1248*7c478bd9Sstevel@tonic-gate return (1); 1249*7c478bd9Sstevel@tonic-gate } 1250*7c478bd9Sstevel@tonic-gate 1251*7c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, sigcld); 1252*7c478bd9Sstevel@tonic-gate (void) sigemptyset(&block_cld); 1253*7c478bd9Sstevel@tonic-gate (void) sigaddset(&block_cld, SIGCLD); 1254*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); 1255*7c478bd9Sstevel@tonic-gate 1256*7c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 1257*7c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 1258*7c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 1259*7c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 1260*7c478bd9Sstevel@tonic-gate return (1); 1261*7c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { /* child process */ 1262*7c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 1263*7c478bd9Sstevel@tonic-gate 1264*7c478bd9Sstevel@tonic-gate /* 1265*7c478bd9Sstevel@tonic-gate * Do a dance to get the pipes hooked up as FD's 0, 1 and 2. 1266*7c478bd9Sstevel@tonic-gate */ 1267*7c478bd9Sstevel@tonic-gate (void) close(STDIN_FILENO); 1268*7c478bd9Sstevel@tonic-gate (void) close(STDOUT_FILENO); 1269*7c478bd9Sstevel@tonic-gate (void) close(STDERR_FILENO); 1270*7c478bd9Sstevel@tonic-gate (void) dup2(stdin_pipe[1], STDIN_FILENO); 1271*7c478bd9Sstevel@tonic-gate (void) dup2(stdout_pipe[1], STDOUT_FILENO); 1272*7c478bd9Sstevel@tonic-gate (void) dup2(stderr_pipe[1], STDERR_FILENO); 1273*7c478bd9Sstevel@tonic-gate (void) closefrom(STDERR_FILENO + 1); 1274*7c478bd9Sstevel@tonic-gate 1275*7c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 1276*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 1277*7c478bd9Sstevel@tonic-gate /* 1278*7c478bd9Sstevel@tonic-gate * In case any of stdin, stdout or stderr are streams, 1279*7c478bd9Sstevel@tonic-gate * anchor them to prevent malicious I_POPs. 1280*7c478bd9Sstevel@tonic-gate */ 1281*7c478bd9Sstevel@tonic-gate (void) ioctl(STDIN_FILENO, I_ANCHOR); 1282*7c478bd9Sstevel@tonic-gate (void) ioctl(STDOUT_FILENO, I_ANCHOR); 1283*7c478bd9Sstevel@tonic-gate (void) ioctl(STDERR_FILENO, I_ANCHOR); 1284*7c478bd9Sstevel@tonic-gate 1285*7c478bd9Sstevel@tonic-gate if (zone_enter(zoneid) == -1) { 1286*7c478bd9Sstevel@tonic-gate zerror(gettext("could not enter zone %s: %s"), 1287*7c478bd9Sstevel@tonic-gate zonename, strerror(errno)); 1288*7c478bd9Sstevel@tonic-gate _exit(1); 1289*7c478bd9Sstevel@tonic-gate } 1290*7c478bd9Sstevel@tonic-gate 1291*7c478bd9Sstevel@tonic-gate if (!failsafe) 1292*7c478bd9Sstevel@tonic-gate new_env = prep_env_noninteractive(login, new_env); 1293*7c478bd9Sstevel@tonic-gate 1294*7c478bd9Sstevel@tonic-gate if (new_env == NULL) { 1295*7c478bd9Sstevel@tonic-gate _exit(1); 1296*7c478bd9Sstevel@tonic-gate } 1297*7c478bd9Sstevel@tonic-gate 1298*7c478bd9Sstevel@tonic-gate /* 1299*7c478bd9Sstevel@tonic-gate * Move into a new process group; the zone_enter will have 1300*7c478bd9Sstevel@tonic-gate * placed us into zsched's session, and we want to be in 1301*7c478bd9Sstevel@tonic-gate * a unique process group. 1302*7c478bd9Sstevel@tonic-gate */ 1303*7c478bd9Sstevel@tonic-gate (void) setpgid(getpid(), getpid()); 1304*7c478bd9Sstevel@tonic-gate 1305*7c478bd9Sstevel@tonic-gate (void) execve(new_args[0], new_args, new_env); 1306*7c478bd9Sstevel@tonic-gate zperror(gettext("exec failure")); 1307*7c478bd9Sstevel@tonic-gate _exit(1); 1308*7c478bd9Sstevel@tonic-gate } 1309*7c478bd9Sstevel@tonic-gate /* parent */ 1310*7c478bd9Sstevel@tonic-gate (void) sigset(SIGINT, sig_forward); 1311*7c478bd9Sstevel@tonic-gate 1312*7c478bd9Sstevel@tonic-gate postfork_dropprivs(); 1313*7c478bd9Sstevel@tonic-gate 1314*7c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 1315*7c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 1316*7c478bd9Sstevel@tonic-gate 1317*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 1318*7c478bd9Sstevel@tonic-gate doio(stdin_pipe[0], stdout_pipe[0], stderr_pipe[0], B_TRUE); 1319*7c478bd9Sstevel@tonic-gate do { 1320*7c478bd9Sstevel@tonic-gate retval = waitpid(child_pid, &child_status, 0); 1321*7c478bd9Sstevel@tonic-gate if (retval == -1) { 1322*7c478bd9Sstevel@tonic-gate child_status = 0; 1323*7c478bd9Sstevel@tonic-gate } 1324*7c478bd9Sstevel@tonic-gate } while (retval != child_pid && errno != ECHILD); 1325*7c478bd9Sstevel@tonic-gate 1326*7c478bd9Sstevel@tonic-gate return (WEXITSTATUS(child_status)); 1327*7c478bd9Sstevel@tonic-gate } 1328*7c478bd9Sstevel@tonic-gate 1329*7c478bd9Sstevel@tonic-gate int 1330*7c478bd9Sstevel@tonic-gate main(int argc, char **argv) 1331*7c478bd9Sstevel@tonic-gate { 1332*7c478bd9Sstevel@tonic-gate int arg, console = 0; 1333*7c478bd9Sstevel@tonic-gate zoneid_t zoneid; 1334*7c478bd9Sstevel@tonic-gate zone_state_t st; 1335*7c478bd9Sstevel@tonic-gate char *login = "root"; 1336*7c478bd9Sstevel@tonic-gate int lflag = 0; 1337*7c478bd9Sstevel@tonic-gate char *zonename = NULL; 1338*7c478bd9Sstevel@tonic-gate char **proc_args = NULL; 1339*7c478bd9Sstevel@tonic-gate char **new_args, **new_env; 1340*7c478bd9Sstevel@tonic-gate sigset_t block_cld; 1341*7c478bd9Sstevel@tonic-gate char zonepath[MAXPATHLEN]; 1342*7c478bd9Sstevel@tonic-gate char *slavename, slaveshortname[MAXPATHLEN]; 1343*7c478bd9Sstevel@tonic-gate priv_set_t *privset; 1344*7c478bd9Sstevel@tonic-gate int tmpl_fd; 1345*7c478bd9Sstevel@tonic-gate 1346*7c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1347*7c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1348*7c478bd9Sstevel@tonic-gate 1349*7c478bd9Sstevel@tonic-gate (void) getpname(argv[0]); 1350*7c478bd9Sstevel@tonic-gate 1351*7c478bd9Sstevel@tonic-gate while ((arg = getopt(argc, argv, "ECSe:l:")) != EOF) { 1352*7c478bd9Sstevel@tonic-gate switch (arg) { 1353*7c478bd9Sstevel@tonic-gate case 'C': 1354*7c478bd9Sstevel@tonic-gate console = 1; 1355*7c478bd9Sstevel@tonic-gate break; 1356*7c478bd9Sstevel@tonic-gate case 'E': 1357*7c478bd9Sstevel@tonic-gate nocmdchar = 1; 1358*7c478bd9Sstevel@tonic-gate break; 1359*7c478bd9Sstevel@tonic-gate case 'S': 1360*7c478bd9Sstevel@tonic-gate failsafe = 1; 1361*7c478bd9Sstevel@tonic-gate break; 1362*7c478bd9Sstevel@tonic-gate case 'e': 1363*7c478bd9Sstevel@tonic-gate set_cmdchar(optarg); 1364*7c478bd9Sstevel@tonic-gate break; 1365*7c478bd9Sstevel@tonic-gate case 'l': 1366*7c478bd9Sstevel@tonic-gate login = optarg; 1367*7c478bd9Sstevel@tonic-gate lflag = 1; 1368*7c478bd9Sstevel@tonic-gate break; 1369*7c478bd9Sstevel@tonic-gate default: 1370*7c478bd9Sstevel@tonic-gate usage(); 1371*7c478bd9Sstevel@tonic-gate } 1372*7c478bd9Sstevel@tonic-gate } 1373*7c478bd9Sstevel@tonic-gate 1374*7c478bd9Sstevel@tonic-gate if (console != 0 && lflag != 0) { 1375*7c478bd9Sstevel@tonic-gate zerror(gettext("-l may not be specified for console login")); 1376*7c478bd9Sstevel@tonic-gate usage(); 1377*7c478bd9Sstevel@tonic-gate } 1378*7c478bd9Sstevel@tonic-gate 1379*7c478bd9Sstevel@tonic-gate if (console != 0 && failsafe != 0) { 1380*7c478bd9Sstevel@tonic-gate zerror(gettext("-S may not be specified for console login")); 1381*7c478bd9Sstevel@tonic-gate usage(); 1382*7c478bd9Sstevel@tonic-gate } 1383*7c478bd9Sstevel@tonic-gate 1384*7c478bd9Sstevel@tonic-gate if (failsafe != 0 && lflag != 0) { 1385*7c478bd9Sstevel@tonic-gate zerror(gettext("-l may not be specified for failsafe login")); 1386*7c478bd9Sstevel@tonic-gate usage(); 1387*7c478bd9Sstevel@tonic-gate } 1388*7c478bd9Sstevel@tonic-gate 1389*7c478bd9Sstevel@tonic-gate if (optind == (argc - 1)) { 1390*7c478bd9Sstevel@tonic-gate /* 1391*7c478bd9Sstevel@tonic-gate * zone name, no process name; this should be an interactive 1392*7c478bd9Sstevel@tonic-gate * as long as STDIN is really a tty. 1393*7c478bd9Sstevel@tonic-gate */ 1394*7c478bd9Sstevel@tonic-gate if (isatty(STDIN_FILENO)) 1395*7c478bd9Sstevel@tonic-gate interactive = 1; 1396*7c478bd9Sstevel@tonic-gate zonename = argv[optind]; 1397*7c478bd9Sstevel@tonic-gate } else if (optind < (argc - 1)) { 1398*7c478bd9Sstevel@tonic-gate if (console) { 1399*7c478bd9Sstevel@tonic-gate zerror(gettext("Commands may not be specified for " 1400*7c478bd9Sstevel@tonic-gate "console login.")); 1401*7c478bd9Sstevel@tonic-gate usage(); 1402*7c478bd9Sstevel@tonic-gate } 1403*7c478bd9Sstevel@tonic-gate /* zone name and process name, and possibly some args */ 1404*7c478bd9Sstevel@tonic-gate zonename = argv[optind]; 1405*7c478bd9Sstevel@tonic-gate proc_args = &argv[optind + 1]; 1406*7c478bd9Sstevel@tonic-gate interactive = 0; 1407*7c478bd9Sstevel@tonic-gate } else { 1408*7c478bd9Sstevel@tonic-gate usage(); 1409*7c478bd9Sstevel@tonic-gate } 1410*7c478bd9Sstevel@tonic-gate 1411*7c478bd9Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 1412*7c478bd9Sstevel@tonic-gate zerror(gettext("'%s' may only be used from the global zone"), 1413*7c478bd9Sstevel@tonic-gate pname); 1414*7c478bd9Sstevel@tonic-gate return (1); 1415*7c478bd9Sstevel@tonic-gate } 1416*7c478bd9Sstevel@tonic-gate 1417*7c478bd9Sstevel@tonic-gate if (strcmp(zonename, GLOBAL_ZONENAME) == 0) { 1418*7c478bd9Sstevel@tonic-gate zerror(gettext("'%s' not applicable to the global zone"), 1419*7c478bd9Sstevel@tonic-gate pname); 1420*7c478bd9Sstevel@tonic-gate return (1); 1421*7c478bd9Sstevel@tonic-gate } 1422*7c478bd9Sstevel@tonic-gate 1423*7c478bd9Sstevel@tonic-gate if (zone_get_state(zonename, &st) != Z_OK) { 1424*7c478bd9Sstevel@tonic-gate zerror(gettext("zone '%s' unknown"), zonename); 1425*7c478bd9Sstevel@tonic-gate return (1); 1426*7c478bd9Sstevel@tonic-gate } 1427*7c478bd9Sstevel@tonic-gate 1428*7c478bd9Sstevel@tonic-gate if (st < ZONE_STATE_INSTALLED) { 1429*7c478bd9Sstevel@tonic-gate zerror(gettext("cannot login to a zone which is '%s'"), 1430*7c478bd9Sstevel@tonic-gate zone_state_str(st)); 1431*7c478bd9Sstevel@tonic-gate return (1); 1432*7c478bd9Sstevel@tonic-gate } 1433*7c478bd9Sstevel@tonic-gate 1434*7c478bd9Sstevel@tonic-gate /* 1435*7c478bd9Sstevel@tonic-gate * In both console and non-console cases, we require all privs. 1436*7c478bd9Sstevel@tonic-gate * In the console case, because we may need to startup zoneadmd. 1437*7c478bd9Sstevel@tonic-gate * In the non-console case in order to do zone_enter(2), zonept() 1438*7c478bd9Sstevel@tonic-gate * and other tasks. 1439*7c478bd9Sstevel@tonic-gate * 1440*7c478bd9Sstevel@tonic-gate * Future work: this solution is temporary. Ultimately, we need to 1441*7c478bd9Sstevel@tonic-gate * move to a flexible system which allows the global admin to 1442*7c478bd9Sstevel@tonic-gate * designate that a particular user can zlogin (and probably zlogin 1443*7c478bd9Sstevel@tonic-gate * -C) to a particular zone. This all-root business we have now is 1444*7c478bd9Sstevel@tonic-gate * quite sketchy. 1445*7c478bd9Sstevel@tonic-gate */ 1446*7c478bd9Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 1447*7c478bd9Sstevel@tonic-gate zperror(gettext("priv_allocset failed")); 1448*7c478bd9Sstevel@tonic-gate return (1); 1449*7c478bd9Sstevel@tonic-gate } 1450*7c478bd9Sstevel@tonic-gate 1451*7c478bd9Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 1452*7c478bd9Sstevel@tonic-gate zperror(gettext("getppriv failed")); 1453*7c478bd9Sstevel@tonic-gate priv_freeset(privset); 1454*7c478bd9Sstevel@tonic-gate return (1); 1455*7c478bd9Sstevel@tonic-gate } 1456*7c478bd9Sstevel@tonic-gate 1457*7c478bd9Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 1458*7c478bd9Sstevel@tonic-gate zerror(gettext("You lack sufficient privilege to run " 1459*7c478bd9Sstevel@tonic-gate "this command (all privs required)")); 1460*7c478bd9Sstevel@tonic-gate priv_freeset(privset); 1461*7c478bd9Sstevel@tonic-gate return (1); 1462*7c478bd9Sstevel@tonic-gate } 1463*7c478bd9Sstevel@tonic-gate priv_freeset(privset); 1464*7c478bd9Sstevel@tonic-gate 1465*7c478bd9Sstevel@tonic-gate /* 1466*7c478bd9Sstevel@tonic-gate * The console is a separate case from the rest of the code; handle 1467*7c478bd9Sstevel@tonic-gate * it first. 1468*7c478bd9Sstevel@tonic-gate */ 1469*7c478bd9Sstevel@tonic-gate if (console) { 1470*7c478bd9Sstevel@tonic-gate 1471*7c478bd9Sstevel@tonic-gate /* 1472*7c478bd9Sstevel@tonic-gate * Ensure that zoneadmd for this zone is running. 1473*7c478bd9Sstevel@tonic-gate */ 1474*7c478bd9Sstevel@tonic-gate if (start_zoneadmd(zonename) == -1) 1475*7c478bd9Sstevel@tonic-gate return (1); 1476*7c478bd9Sstevel@tonic-gate 1477*7c478bd9Sstevel@tonic-gate /* 1478*7c478bd9Sstevel@tonic-gate * Make contact with zoneadmd. 1479*7c478bd9Sstevel@tonic-gate */ 1480*7c478bd9Sstevel@tonic-gate if (get_console_master(zonename) == -1) 1481*7c478bd9Sstevel@tonic-gate return (1); 1482*7c478bd9Sstevel@tonic-gate 1483*7c478bd9Sstevel@tonic-gate (void) printf(gettext("[Connected to zone '%s' console]\n"), 1484*7c478bd9Sstevel@tonic-gate zonename); 1485*7c478bd9Sstevel@tonic-gate 1486*7c478bd9Sstevel@tonic-gate if (set_tty_rawmode(STDIN_FILENO) == -1) { 1487*7c478bd9Sstevel@tonic-gate reset_tty(); 1488*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stdin pty to raw mode")); 1489*7c478bd9Sstevel@tonic-gate return (1); 1490*7c478bd9Sstevel@tonic-gate } 1491*7c478bd9Sstevel@tonic-gate 1492*7c478bd9Sstevel@tonic-gate (void) sigset(SIGWINCH, sigwinch); 1493*7c478bd9Sstevel@tonic-gate (void) sigwinch(0); 1494*7c478bd9Sstevel@tonic-gate 1495*7c478bd9Sstevel@tonic-gate /* 1496*7c478bd9Sstevel@tonic-gate * Run the I/O loop until we get disconnected. 1497*7c478bd9Sstevel@tonic-gate */ 1498*7c478bd9Sstevel@tonic-gate doio(masterfd, masterfd, -1, B_FALSE); 1499*7c478bd9Sstevel@tonic-gate reset_tty(); 1500*7c478bd9Sstevel@tonic-gate (void) printf(gettext("\n[Connection to zone '%s' console " 1501*7c478bd9Sstevel@tonic-gate "closed]\n"), zonename); 1502*7c478bd9Sstevel@tonic-gate 1503*7c478bd9Sstevel@tonic-gate return (0); 1504*7c478bd9Sstevel@tonic-gate } 1505*7c478bd9Sstevel@tonic-gate 1506*7c478bd9Sstevel@tonic-gate if (st != ZONE_STATE_RUNNING) { 1507*7c478bd9Sstevel@tonic-gate zerror(gettext("login allowed only to running zones " 1508*7c478bd9Sstevel@tonic-gate "(%s is '%s')."), zonename, zone_state_str(st)); 1509*7c478bd9Sstevel@tonic-gate return (1); 1510*7c478bd9Sstevel@tonic-gate } 1511*7c478bd9Sstevel@tonic-gate 1512*7c478bd9Sstevel@tonic-gate /* 1513*7c478bd9Sstevel@tonic-gate * We only need the zone root path and zoneid if we are setting up a 1514*7c478bd9Sstevel@tonic-gate * pty. 1515*7c478bd9Sstevel@tonic-gate */ 1516*7c478bd9Sstevel@tonic-gate if ((zoneid = getzoneidbyname(zonename)) == -1) { 1517*7c478bd9Sstevel@tonic-gate zerror(gettext("failed to get zoneid for zone '%s'"), 1518*7c478bd9Sstevel@tonic-gate zonename); 1519*7c478bd9Sstevel@tonic-gate return (1); 1520*7c478bd9Sstevel@tonic-gate } 1521*7c478bd9Sstevel@tonic-gate 1522*7c478bd9Sstevel@tonic-gate if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) == -1) { 1523*7c478bd9Sstevel@tonic-gate zerror(gettext("could not get root path for zone %s"), 1524*7c478bd9Sstevel@tonic-gate zonename); 1525*7c478bd9Sstevel@tonic-gate return (1); 1526*7c478bd9Sstevel@tonic-gate } 1527*7c478bd9Sstevel@tonic-gate 1528*7c478bd9Sstevel@tonic-gate if ((new_args = prep_args(login, proc_args)) == NULL) { 1529*7c478bd9Sstevel@tonic-gate zperror(gettext("could not assemble new arguments")); 1530*7c478bd9Sstevel@tonic-gate return (1); 1531*7c478bd9Sstevel@tonic-gate } 1532*7c478bd9Sstevel@tonic-gate 1533*7c478bd9Sstevel@tonic-gate if ((new_env = prep_env()) == NULL) { 1534*7c478bd9Sstevel@tonic-gate zperror(gettext("could not assemble new environment")); 1535*7c478bd9Sstevel@tonic-gate return (1); 1536*7c478bd9Sstevel@tonic-gate } 1537*7c478bd9Sstevel@tonic-gate 1538*7c478bd9Sstevel@tonic-gate if (!interactive) 1539*7c478bd9Sstevel@tonic-gate return (noninteractive_login(zonename, zoneid, login, new_args, 1540*7c478bd9Sstevel@tonic-gate new_env)); 1541*7c478bd9Sstevel@tonic-gate 1542*7c478bd9Sstevel@tonic-gate /* 1543*7c478bd9Sstevel@tonic-gate * Things are more complex in interactive mode; we get the 1544*7c478bd9Sstevel@tonic-gate * master side of the pty, then place the user's terminal into 1545*7c478bd9Sstevel@tonic-gate * raw mode. 1546*7c478bd9Sstevel@tonic-gate */ 1547*7c478bd9Sstevel@tonic-gate if (get_master_pty() == -1) { 1548*7c478bd9Sstevel@tonic-gate zerror(gettext("could not setup master pty device")); 1549*7c478bd9Sstevel@tonic-gate return (1); 1550*7c478bd9Sstevel@tonic-gate } 1551*7c478bd9Sstevel@tonic-gate 1552*7c478bd9Sstevel@tonic-gate /* 1553*7c478bd9Sstevel@tonic-gate * Compute the "short name" of the pts. /dev/pts/2 --> pts/2 1554*7c478bd9Sstevel@tonic-gate */ 1555*7c478bd9Sstevel@tonic-gate if ((slavename = ptsname(masterfd)) == NULL) { 1556*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to get name for pseudo-tty")); 1557*7c478bd9Sstevel@tonic-gate return (1); 1558*7c478bd9Sstevel@tonic-gate } 1559*7c478bd9Sstevel@tonic-gate if (strncmp(slavename, "/dev/", strlen("/dev/")) == 0) 1560*7c478bd9Sstevel@tonic-gate (void) strlcpy(slaveshortname, slavename + strlen("/dev/"), 1561*7c478bd9Sstevel@tonic-gate sizeof (slaveshortname)); 1562*7c478bd9Sstevel@tonic-gate else 1563*7c478bd9Sstevel@tonic-gate (void) strlcpy(slaveshortname, slavename, 1564*7c478bd9Sstevel@tonic-gate sizeof (slaveshortname)); 1565*7c478bd9Sstevel@tonic-gate 1566*7c478bd9Sstevel@tonic-gate (void) printf(gettext("[Connected to zone '%s' %s]\n"), zonename, 1567*7c478bd9Sstevel@tonic-gate slaveshortname); 1568*7c478bd9Sstevel@tonic-gate 1569*7c478bd9Sstevel@tonic-gate if (set_tty_rawmode(STDIN_FILENO) == -1) { 1570*7c478bd9Sstevel@tonic-gate reset_tty(); 1571*7c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stdin pty to raw mode")); 1572*7c478bd9Sstevel@tonic-gate return (1); 1573*7c478bd9Sstevel@tonic-gate } 1574*7c478bd9Sstevel@tonic-gate 1575*7c478bd9Sstevel@tonic-gate if (prefork_dropprivs() != 0) { 1576*7c478bd9Sstevel@tonic-gate reset_tty(); 1577*7c478bd9Sstevel@tonic-gate zperror(gettext("could not allocate privilege set")); 1578*7c478bd9Sstevel@tonic-gate return (1); 1579*7c478bd9Sstevel@tonic-gate } 1580*7c478bd9Sstevel@tonic-gate 1581*7c478bd9Sstevel@tonic-gate /* 1582*7c478bd9Sstevel@tonic-gate * We must mask SIGCLD until after we have coped with the fork 1583*7c478bd9Sstevel@tonic-gate * sufficiently to deal with it; otherwise we can race and receive the 1584*7c478bd9Sstevel@tonic-gate * signal before child_pid has been initialized (yes, this really 1585*7c478bd9Sstevel@tonic-gate * happens). 1586*7c478bd9Sstevel@tonic-gate */ 1587*7c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, sigcld); 1588*7c478bd9Sstevel@tonic-gate (void) sigemptyset(&block_cld); 1589*7c478bd9Sstevel@tonic-gate (void) sigaddset(&block_cld, SIGCLD); 1590*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); 1591*7c478bd9Sstevel@tonic-gate 1592*7c478bd9Sstevel@tonic-gate /* 1593*7c478bd9Sstevel@tonic-gate * We activate the contract template at the last minute to 1594*7c478bd9Sstevel@tonic-gate * avoid intermediate functions that could be using fork(2) 1595*7c478bd9Sstevel@tonic-gate * internally. 1596*7c478bd9Sstevel@tonic-gate */ 1597*7c478bd9Sstevel@tonic-gate if ((tmpl_fd = init_template()) == -1) { 1598*7c478bd9Sstevel@tonic-gate reset_tty(); 1599*7c478bd9Sstevel@tonic-gate zperror(gettext("could not create contract")); 1600*7c478bd9Sstevel@tonic-gate return (1); 1601*7c478bd9Sstevel@tonic-gate } 1602*7c478bd9Sstevel@tonic-gate 1603*7c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 1604*7c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 1605*7c478bd9Sstevel@tonic-gate reset_tty(); 1606*7c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 1607*7c478bd9Sstevel@tonic-gate return (1); 1608*7c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { /* child process */ 1609*7c478bd9Sstevel@tonic-gate int slavefd, newslave; 1610*7c478bd9Sstevel@tonic-gate 1611*7c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 1612*7c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 1613*7c478bd9Sstevel@tonic-gate 1614*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 1615*7c478bd9Sstevel@tonic-gate 1616*7c478bd9Sstevel@tonic-gate if ((slavefd = init_slave_pty(zoneid, zonepath)) == -1) 1617*7c478bd9Sstevel@tonic-gate return (1); 1618*7c478bd9Sstevel@tonic-gate 1619*7c478bd9Sstevel@tonic-gate /* 1620*7c478bd9Sstevel@tonic-gate * Close all fds except for the slave pty. 1621*7c478bd9Sstevel@tonic-gate */ 1622*7c478bd9Sstevel@tonic-gate (void) fdwalk(close_func, &slavefd); 1623*7c478bd9Sstevel@tonic-gate 1624*7c478bd9Sstevel@tonic-gate /* 1625*7c478bd9Sstevel@tonic-gate * Temporarily dup slavefd to stderr; that way if we have 1626*7c478bd9Sstevel@tonic-gate * to print out that zone_enter failed, the output will 1627*7c478bd9Sstevel@tonic-gate * have somewhere to go. 1628*7c478bd9Sstevel@tonic-gate */ 1629*7c478bd9Sstevel@tonic-gate if (slavefd != STDERR_FILENO) 1630*7c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDERR_FILENO); 1631*7c478bd9Sstevel@tonic-gate 1632*7c478bd9Sstevel@tonic-gate if (zone_enter(zoneid) == -1) { 1633*7c478bd9Sstevel@tonic-gate zerror(gettext("could not enter zone %s: %s"), 1634*7c478bd9Sstevel@tonic-gate zonename, strerror(errno)); 1635*7c478bd9Sstevel@tonic-gate return (1); 1636*7c478bd9Sstevel@tonic-gate } 1637*7c478bd9Sstevel@tonic-gate 1638*7c478bd9Sstevel@tonic-gate if (slavefd != STDERR_FILENO) 1639*7c478bd9Sstevel@tonic-gate (void) close(STDERR_FILENO); 1640*7c478bd9Sstevel@tonic-gate 1641*7c478bd9Sstevel@tonic-gate /* 1642*7c478bd9Sstevel@tonic-gate * We take pains to get this process into a new process 1643*7c478bd9Sstevel@tonic-gate * group, and subsequently a new session. In this way, 1644*7c478bd9Sstevel@tonic-gate * we'll have a session which doesn't yet have a controlling 1645*7c478bd9Sstevel@tonic-gate * terminal. When we open the slave, it will become the 1646*7c478bd9Sstevel@tonic-gate * controlling terminal; no PIDs concerning pgrps or sids 1647*7c478bd9Sstevel@tonic-gate * will leak inappropriately into the zone. 1648*7c478bd9Sstevel@tonic-gate */ 1649*7c478bd9Sstevel@tonic-gate (void) setpgrp(); 1650*7c478bd9Sstevel@tonic-gate 1651*7c478bd9Sstevel@tonic-gate /* 1652*7c478bd9Sstevel@tonic-gate * We need the slave pty to be referenced from the zone's 1653*7c478bd9Sstevel@tonic-gate * /dev in order to ensure that the devt's, etc are all 1654*7c478bd9Sstevel@tonic-gate * correct. Otherwise we break ttyname and the like. 1655*7c478bd9Sstevel@tonic-gate */ 1656*7c478bd9Sstevel@tonic-gate if ((newslave = open(slavename, O_RDWR)) == -1) { 1657*7c478bd9Sstevel@tonic-gate (void) close(slavefd); 1658*7c478bd9Sstevel@tonic-gate return (1); 1659*7c478bd9Sstevel@tonic-gate } 1660*7c478bd9Sstevel@tonic-gate (void) close(slavefd); 1661*7c478bd9Sstevel@tonic-gate slavefd = newslave; 1662*7c478bd9Sstevel@tonic-gate 1663*7c478bd9Sstevel@tonic-gate /* 1664*7c478bd9Sstevel@tonic-gate * dup the slave to the various FDs, so that when the 1665*7c478bd9Sstevel@tonic-gate * spawned process does a write/read it maps to the slave 1666*7c478bd9Sstevel@tonic-gate * pty. 1667*7c478bd9Sstevel@tonic-gate */ 1668*7c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDIN_FILENO); 1669*7c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDOUT_FILENO); 1670*7c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDERR_FILENO); 1671*7c478bd9Sstevel@tonic-gate if (slavefd != STDIN_FILENO && slavefd != STDOUT_FILENO && 1672*7c478bd9Sstevel@tonic-gate slavefd != STDERR_FILENO) { 1673*7c478bd9Sstevel@tonic-gate (void) close(slavefd); 1674*7c478bd9Sstevel@tonic-gate } 1675*7c478bd9Sstevel@tonic-gate 1676*7c478bd9Sstevel@tonic-gate /* 1677*7c478bd9Sstevel@tonic-gate * In failsafe mode, we don't use login(1), so don't try 1678*7c478bd9Sstevel@tonic-gate * setting up a utmpx entry. 1679*7c478bd9Sstevel@tonic-gate */ 1680*7c478bd9Sstevel@tonic-gate if (!failsafe) { 1681*7c478bd9Sstevel@tonic-gate if (setup_utmpx(slaveshortname) == -1) 1682*7c478bd9Sstevel@tonic-gate return (1); 1683*7c478bd9Sstevel@tonic-gate } 1684*7c478bd9Sstevel@tonic-gate 1685*7c478bd9Sstevel@tonic-gate (void) execve(new_args[0], new_args, new_env); 1686*7c478bd9Sstevel@tonic-gate zperror(gettext("exec failure")); 1687*7c478bd9Sstevel@tonic-gate return (1); 1688*7c478bd9Sstevel@tonic-gate } 1689*7c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 1690*7c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 1691*7c478bd9Sstevel@tonic-gate 1692*7c478bd9Sstevel@tonic-gate /* 1693*7c478bd9Sstevel@tonic-gate * The rest is only for the parent process. 1694*7c478bd9Sstevel@tonic-gate */ 1695*7c478bd9Sstevel@tonic-gate (void) sigset(SIGWINCH, sigwinch); 1696*7c478bd9Sstevel@tonic-gate 1697*7c478bd9Sstevel@tonic-gate postfork_dropprivs(); 1698*7c478bd9Sstevel@tonic-gate 1699*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 1700*7c478bd9Sstevel@tonic-gate doio(masterfd, masterfd, -1, B_FALSE); 1701*7c478bd9Sstevel@tonic-gate 1702*7c478bd9Sstevel@tonic-gate reset_tty(); 1703*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1704*7c478bd9Sstevel@tonic-gate gettext("\n[Connection to zone '%s' %s closed]\n"), zonename, 1705*7c478bd9Sstevel@tonic-gate slaveshortname); 1706*7c478bd9Sstevel@tonic-gate 1707*7c478bd9Sstevel@tonic-gate if (pollerr != 0) { 1708*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("Error: connection closed due " 1709*7c478bd9Sstevel@tonic-gate "to unexpected pollevents=0x%x.\n"), pollerr); 1710*7c478bd9Sstevel@tonic-gate return (1); 1711*7c478bd9Sstevel@tonic-gate } 1712*7c478bd9Sstevel@tonic-gate 1713*7c478bd9Sstevel@tonic-gate return (0); 1714*7c478bd9Sstevel@tonic-gate } 1715