1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. 29 * Copyright 2024 Oxide Computer Company 30 */ 31 32 /* Copyright (c) 1988 AT&T */ 33 /* All Rights Reserved */ 34 35 #pragma weak _ptsname = ptsname 36 #pragma weak _grantpt = grantpt 37 #pragma weak _unlockpt = unlockpt 38 39 #include "lint.h" 40 #include "libc.h" 41 #include "mtlib.h" 42 #include <sys/types.h> 43 #include <signal.h> 44 #include <sys/param.h> 45 #include <sys/mkdev.h> 46 #include <sys/stream.h> 47 #include <sys/stropts.h> 48 #include <sys/wait.h> 49 #include <sys/signal.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <sys/stat.h> 53 #include <sys/ptms.h> 54 #include <string.h> 55 #include <stdlib.h> 56 #include <unistd.h> 57 #include <wait.h> 58 #include <spawn.h> 59 #include <grp.h> 60 #include "tsd.h" 61 62 #define PTSNAME "/dev/pts/" /* slave name */ 63 #define PTLEN 32 /* slave name length */ 64 #define DEFAULT_TTY_GROUP "tty" /* slave device group owner */ 65 66 /* 67 * Check that fd argument is a file descriptor of an opened master. 68 * Do this by sending an ISPTM ioctl message down stream. Ioctl() 69 * will fail if:(1) fd is not a valid file descriptor.(2) the file 70 * represented by fd does not understand ISPTM(not a master device). 71 * If we have a valid master, get its minor number via fstat(). 72 * Concatenate it to PTSNAME and return it as the name of the slave 73 * device. 74 */ 75 static dev_t 76 ptsdev(int fd) 77 { 78 struct stat64 status; 79 struct strioctl istr; 80 81 istr.ic_cmd = ISPTM; 82 istr.ic_len = 0; 83 istr.ic_timout = 0; 84 istr.ic_dp = NULL; 85 86 if (ioctl(fd, I_STR, &istr) < 0 || fstat64(fd, &status) < 0) 87 return (NODEV); 88 89 return (minor(status.st_rdev)); 90 } 91 92 int 93 ptsname_r(int fd, char *name, size_t len) 94 { 95 dev_t dev; 96 97 if (name == NULL) 98 return (EINVAL); 99 100 if ((dev = ptsdev(fd)) == NODEV) 101 return (errno); 102 103 if (snprintf(name, len, "%s%d", PTSNAME, dev) >= len) 104 return (ERANGE); 105 106 /* 107 * This lookup will create the /dev/pts node (if the corresponding pty 108 * exists). POSIX basically never really indicated whether or not 109 * ptsname() was allowed to return errors or not, though we did. If we 110 * played strictly by the book, this should probably be an EINVAL or 111 * ENOTTY return; however, to help someone have a chance of debugging 112 * this if something goes wrong we stick with our traditional behavior 113 * and return a slightly broader errno set. If this causes portability 114 * issues in practice, then it should be changed to just return EINVAL. 115 */ 116 if (access(name, F_OK) != 0) 117 return (errno); 118 119 return (0); 120 } 121 122 char * 123 ptsname(int fd) 124 { 125 int ret; 126 char *sname; 127 128 sname = tsdalloc(_T_PTSNAME, PTLEN, NULL); 129 if (sname == NULL) 130 return (NULL); 131 132 if ((ret = ptsname_r(fd, sname, PTLEN)) != 0) { 133 errno = ret; 134 return (NULL); 135 } 136 137 return (sname); 138 } 139 140 /* 141 * Send an ioctl down to the master device requesting the 142 * master/slave pair be unlocked. 143 */ 144 int 145 unlockpt(int fd) 146 { 147 struct strioctl istr; 148 149 istr.ic_cmd = UNLKPT; 150 istr.ic_len = 0; 151 istr.ic_timout = 0; 152 istr.ic_dp = NULL; 153 154 if (ioctl(fd, I_STR, &istr) < 0) 155 return (-1); 156 157 return (0); 158 } 159 160 /* 161 * XPG4v2 requires that open of a slave pseudo terminal device 162 * provides the process with an interface that is identical to 163 * the terminal interface. 164 * 165 * To satisfy this, in strict XPG4v2 mode, this routine also sends 166 * a message down the stream that sets a flag in the kernel module 167 * so that additional actions are performed when opening an 168 * associated slave PTY device. When this happens, modules are 169 * automatically pushed onto the stream to provide terminal 170 * semantics and those modules are then informed that they should 171 * behave in strict XPG4v2 mode which modifies their behaviour. In 172 * particular, in strict XPG4v2 mode, empty blocks will be sent up 173 * the master side of the stream rather than being suppressed. 174 * 175 * Most applications do not expect this behaviour so it is only 176 * enabled for programs compiled in strict XPG4v2 mode (see 177 * stdlib.h). 178 */ 179 int 180 __unlockpt_xpg4(int fd) 181 { 182 int ret; 183 184 if ((ret = unlockpt(fd)) == 0) { 185 struct strioctl istr; 186 187 istr.ic_cmd = PTSSTTY; 188 istr.ic_len = 0; 189 istr.ic_timout = 0; 190 istr.ic_dp = NULL; 191 192 if (ioctl(fd, I_STR, &istr) < 0) 193 ret = -1; 194 } 195 196 return (ret); 197 } 198 199 int 200 grantpt(int fd) 201 { 202 struct strioctl istr; 203 pt_own_t pto; 204 struct group *gr_name; 205 206 /* validate the file descriptor before proceeding */ 207 if (ptsdev(fd) == NODEV) 208 return (-1); 209 210 pto.pto_ruid = getuid(); 211 212 gr_name = getgrnam(DEFAULT_TTY_GROUP); 213 if (gr_name) 214 pto.pto_rgid = gr_name->gr_gid; 215 else 216 pto.pto_rgid = getgid(); 217 218 istr.ic_cmd = OWNERPT; 219 istr.ic_len = sizeof (pt_own_t); 220 istr.ic_timout = 0; 221 istr.ic_dp = (char *)&pto; 222 223 if (ioctl(fd, I_STR, &istr) != 0) { 224 errno = EACCES; 225 return (-1); 226 } 227 228 return (0); 229 } 230 231 /* 232 * Send an ioctl down to the master device requesting the master/slave pair 233 * be assigned to the given zone. 234 */ 235 int 236 zonept(int fd, zoneid_t zoneid) 237 { 238 struct strioctl istr; 239 240 istr.ic_cmd = ZONEPT; 241 istr.ic_len = sizeof (zoneid); 242 istr.ic_timout = 0; 243 istr.ic_dp = (char *)&zoneid; 244 245 if (ioctl(fd, I_STR, &istr) != 0) { 246 return (-1); 247 } 248 return (0); 249 } 250 251 252 /* 253 * added for SUSv3 standard 254 * 255 * Open a pseudo-terminal device. External interface. 256 */ 257 int 258 posix_openpt(int oflag) 259 { 260 return (open("/dev/ptmx", oflag)); 261 } 262