1/* 2Copyright (c) 2001 by Juliusz Chroboczek 3 4Permission is hereby granted, free of charge, to any person obtaining a copy 5of this software and associated documentation files (the "Software"), to deal 6in the Software without restriction, including without limitation the rights 7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8copies of the Software, and to permit persons to whom the Software is 9furnished to do so, subject to the following conditions: 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20THE SOFTWARE. 21*/ 22 23#ifdef HAVE_CONFIG_H 24# include "config.h" 25#endif 26 27#include <stdlib.h> 28#include <string.h> 29#include <stdio.h> 30#include <sys/types.h> 31#include <unistd.h> 32#include <fcntl.h> 33#include <sys/ioctl.h> 34#include <sys/stat.h> 35#include <sys/time.h> 36#include <termios.h> 37#include <signal.h> 38#include <errno.h> 39 40#ifdef HAVE_WORKING_POLL 41#ifdef HAVE_POLL_H 42#include <poll.h> 43#else 44#include <sys/poll.h> 45#endif 46#undef HAVE_SELECT 47#endif 48 49#ifdef HAVE_SELECT 50#if !(defined(_MINIX) || defined(__BEOS__)) 51#define HAVE_WORKING_SELECT 1 52#endif 53#endif 54 55#ifdef HAVE_WORKING_SELECT 56#if defined(HAVE_SYS_SELECT_H) && defined(HAVE_SYS_TIME_SELECT) 57#include <sys/select.h> 58#endif 59#endif 60 61#ifdef HAVE_PTY_H 62#include <pty.h> 63#endif 64 65#ifdef HAVE_STROPTS_H 66#include <stropts.h> 67#endif 68 69#ifdef HAVE_SYS_PARAM 70#include <sys/param.h> 71#endif 72 73#ifdef HAVE_OPENPTY 74#if defined(HAVE_UTIL_H) 75#include <util.h> 76#elif defined(HAVE_LIBUTIL_H) 77#include <libutil.h> 78#elif defined(HAVE_PTY_H) 79#include <pty.h> 80#endif 81#endif /* HAVE_OPENPTY */ 82 83#include "sys.h" 84 85#ifdef USE_IGNORE_RC 86int ignore_unused; 87#endif 88 89#if defined(HAVE_OPENPTY) 90static int opened_tty = -1; 91#endif 92 93static int saved_tio_valid = 0; 94static struct termios saved_tio; 95 96int 97waitForOutput(int fd) 98{ 99 int ret = 0; 100 101#if defined(HAVE_WORKING_POLL) 102 struct pollfd pfd[1]; 103 int rc; 104 105 pfd[0].fd = fd; 106 pfd[0].events = POLLOUT; 107 pfd[0].revents = 0; 108 109 rc = poll(pfd, 1, -1); 110 if (rc < 0) 111 ret = -1; 112 else if (pfd[0].revents & (POLLOUT | POLLERR | POLLHUP)) 113 ret = 1; 114 115#elif defined(HAVE_WORKING_SELECT) 116 fd_set fds; 117 int rc; 118 119 FD_ZERO(&fds); 120 FD_SET(fd, &fds); 121 rc = select(FD_SETSIZE, NULL, &fds, NULL, NULL); 122 if (rc < 0) 123 ret = -1; 124 else if (FD_ISSET(fd, &fds)) 125 ret = 1; 126 127#else 128 ret = 1; 129#endif 130 131 return ret; 132} 133 134int 135waitForInput(int fd1, int fd2) 136{ 137 int ret = 0; 138 139#if defined(HAVE_WORKING_POLL) 140 struct pollfd pfd[2]; 141 int rc; 142 143 pfd[0].fd = fd1; 144 pfd[1].fd = fd2; 145 pfd[0].events = pfd[1].events = POLLIN; 146 pfd[0].revents = pfd[1].revents = 0; 147 148 rc = poll(pfd, 2, -1); 149 if (rc < 0) { 150 ret = -1; 151 } else { 152 if (pfd[0].revents & (POLLIN | POLLERR | POLLHUP)) 153 ret |= 1; 154 if (pfd[1].revents & (POLLIN | POLLERR | POLLHUP)) 155 ret |= 2; 156 } 157 158#elif defined(HAVE_WORKING_SELECT) 159 fd_set fds; 160 int rc; 161 162 FD_ZERO(&fds); 163 FD_SET(fd1, &fds); 164 FD_SET(fd2, &fds); 165 rc = select(FD_SETSIZE, &fds, NULL, NULL, NULL); 166 if (rc < 0) { 167 ret = -1; 168 } else { 169 if (FD_ISSET(fd1, &fds)) 170 ret |= 1; 171 if (FD_ISSET(fd2, &fds)) 172 ret |= 2; 173 } 174#else 175 ret = (1 | 2); 176#endif 177 178 return ret; 179} 180 181int 182setWindowSize(int sfd, int dfd) 183{ 184#ifdef TIOCGWINSZ 185 int rc; 186 struct winsize ws; 187 rc = ioctl(sfd, TIOCGWINSZ, (char *) &ws); 188 if (rc < 0) 189 return -1; 190 rc = ioctl(dfd, TIOCSWINSZ, (char *) &ws); 191 if (rc < 0) 192 return -1; 193#endif 194 return 0; 195} 196 197int 198installHandler(int signum, void (*handler) (int)) 199{ 200 struct sigaction sa; 201 sigset_t ss; 202 int rc; 203 204 sigemptyset(&ss); 205 206 sa.sa_handler = handler; 207 sa.sa_mask = ss; 208 sa.sa_flags = 0; 209 rc = sigaction(signum, &sa, NULL); 210 return rc; 211} 212 213int 214copyTermios(int sfd, int dfd) 215{ 216 struct termios tio; 217 int rc; 218 219 rc = tcgetattr(sfd, &tio); 220 if (rc < 0) 221 return -1; 222 223 rc = tcsetattr(dfd, TCSAFLUSH, &tio); 224 if (rc < 0) 225 return -1; 226 227 return 0; 228} 229 230int 231saveTermios(void) 232{ 233 int rc; 234 rc = tcgetattr(0, &saved_tio); 235 if (rc >= 0) 236 saved_tio_valid = 1; 237 return rc; 238} 239 240int 241restoreTermios(void) 242{ 243 if (!saved_tio_valid) 244 return -1; 245 return tcsetattr(0, TCSAFLUSH, &saved_tio); 246} 247 248int 249setRawTermios(void) 250{ 251 struct termios tio; 252 int rc; 253 254 if (!saved_tio_valid) 255 saveTermios(); 256 rc = tcgetattr(0, &tio); 257 if (rc < 0) 258 return rc; 259 tio.c_lflag &= (unsigned) ~(ECHO | ICANON | ISIG); 260 tio.c_iflag &= (unsigned) ~(ICRNL | IXOFF | IXON | ISTRIP); 261#ifdef ONLCR 262 tio.c_oflag &= (unsigned) ~ONLCR; 263#endif 264#ifdef OCRNL 265 tio.c_oflag &= (unsigned) ~OCRNL; 266#endif 267#ifdef ONOCR 268 tio.c_oflag &= (unsigned) ~ONOCR; 269#endif 270 271#ifdef VMIN 272 tio.c_cc[VMIN] = 0; 273 tio.c_cc[VTIME] = 0; 274#endif 275 rc = tcsetattr(0, TCSAFLUSH, &tio); 276 if (rc < 0) 277 return rc; 278 return 0; 279} 280 281char * 282my_basename(char *path) 283{ 284 char *p; 285 286 p = strrchr(path, '/'); 287 if (!p) 288 p = path; 289 else 290 p++; 291 return p; 292} 293 294static int 295fix_pty_perms(char *line) 296{ 297 int rc; 298 struct stat s; 299 300 rc = stat(line, &s); 301 if (rc < 0) 302 return -1; 303 if (s.st_uid != getuid() || s.st_gid != getgid()) { 304 rc = chown(line, getuid(), getgid()); 305 if (rc < 0) { 306 fprintf(stderr, 307 "Warning: could not change ownership of tty -- " 308 "pty is insecure!\n"); 309 return 0; 310 } 311 } 312 if ((s.st_mode & 0777) != (S_IRUSR | S_IWUSR | S_IWGRP)) { 313 rc = chmod(line, S_IRUSR | S_IWUSR | S_IWGRP); 314 if (rc < 0) { 315 fprintf(stderr, 316 "Warning: could not change permissions of tty -- " 317 "pty is insecure!\n"); 318 return 0; 319 } 320 } 321 return 1; 322} 323 324int 325allocatePty(int *pty_return, char **line_return) 326{ 327 char name[12], *line = NULL; 328 int pty = -1; 329 const char *name1 = "pqrstuvwxyzPQRST"; 330 char buffer[80]; 331 char *name2 = strcpy(buffer, "0123456789abcdefghijklmnopqrstuv"); 332 const char *p1; 333 char *p2; 334 335#if defined(HAVE_GRANTPT) 336 int rc; 337 338#ifdef HAVE_POSIX_OPENPT 339 pty = posix_openpt(O_RDWR); 340#else 341 pty = open("/dev/ptmx", O_RDWR); 342#endif 343 if (pty < 0) 344 goto bsd; 345 346 rc = grantpt(pty); 347 if (rc < 0) { 348 close(pty); 349 goto bsd; 350 } 351 352 rc = unlockpt(pty); 353 if (rc < 0) { 354 close(pty); 355 goto bsd; 356 } 357 358 line = strmalloc(ptsname(pty)); 359 if (!line) { 360 close(pty); 361 goto bsd; 362 } 363 364 fix_pty_perms(line); 365 366 *pty_return = pty; 367 *line_return = line; 368 return 0; 369 370 bsd: 371#elif defined(HAVE_OPENPTY) 372 int rc; 373 char ttydev[80]; /* OpenBSD says at least 16 bytes */ 374 375 rc = openpty(&pty, &opened_tty, ttydev, NULL, NULL); 376 if (rc < 0) { 377 close(pty); 378 goto bsd; 379 } 380 line = strmalloc(ttydev); 381 if (!line) { 382 close(pty); 383 goto bsd; 384 } 385 386 fix_pty_perms(line); 387 388 *pty_return = pty; 389 *line_return = line; 390 return 0; 391 392 bsd: 393#endif /* HAVE_GRANTPT, etc */ 394 395 strcpy(name, "/dev/pty??"); 396 for (p1 = name1; *p1; p1++) { 397 name[8] = *p1; 398 for (p2 = name2; *p2; p2++) { 399 name[9] = *p2; 400 pty = open(name, O_RDWR); 401 if (pty >= 0) 402 goto found; 403 /* Systems derived from 4.4BSD differ in their pty names, 404 so ENOENT doesn't necessarily imply we're done. */ 405 continue; 406 } 407 } 408 409 goto bail; 410 411 found: 412 if ((line = strmalloc(name)) != 0) { 413 line[5] = 't'; 414 fix_pty_perms(line); 415 *pty_return = pty; 416 *line_return = line; 417 return 0; 418 } 419 420 bail: 421 if (pty >= 0) 422 close(pty); 423 if (line) 424 free(line); 425 return -1; 426} 427 428int 429openTty(char *line) 430{ 431 int rc; 432 int tty = -1; 433 434 tty = open(line, O_RDWR 435#if defined(TIOCSCTTY) && defined(O_NOCTTY) 436 /* 437 * Do not make this our controlling terminal, yet just in case it fails 438 * in some intermediate state. But do not add this flag if we haven't 439 * the corresponding ioctl. 440 */ 441 | O_NOCTTY 442#endif 443 ); 444 445 if (tty < 0) 446 goto bail; 447 448#if defined(HAVE_OPENPTY) 449 if (opened_tty >= 0) { 450 close(opened_tty); 451 opened_tty = -1; 452 } 453#endif 454 455#ifdef TIOCSCTTY 456 /* 457 * Now that we've successfully opened the terminal, make it the controlling 458 * terminal. This call works only if the process does not already have a 459 * controlling terminal. 460 * 461 * Cygwin as of 2009/10/12 lacks this call, but has O_NOCTTY. 462 */ 463 rc = ioctl(tty, TIOCSCTTY, (char *) 0); 464 if (rc < 0) { 465 goto bail; 466 } 467#endif 468 469#if defined(I_PUSH) && (defined(SVR4) || defined(__SVR4)) 470 rc = ioctl(tty, I_PUSH, "ptem"); 471 if (rc < 0) 472 goto bail; 473 474 rc = ioctl(tty, I_PUSH, "ldterm"); 475 if (rc < 0) 476 goto bail; 477 478 rc = ioctl(tty, I_PUSH, "ttcompat"); 479 if (rc < 0) 480 goto bail; 481#endif 482 483 return tty; 484 485 bail: 486 if (tty >= 0) 487 close(tty); 488 return -1; 489} 490 491/* Post-4.4 BSD systems have POSIX semantics (_POSIX_SAVED_IDS 492 or not, depending on the version). 4.3BSD and Minix do not have 493 saved IDs at all, so there's no issue. */ 494int 495droppriv(void) 496{ 497 int rc; 498#if defined(_POSIX_SAVED_IDS) 499 uid_t uid = getuid(); 500 uid_t euid = geteuid(); 501 gid_t gid = getgid(); 502 gid_t egid = getegid(); 503 504 if ((uid != euid || gid != egid) && euid != 0) { 505 errno = ENOSYS; 506 rc = -1; 507 } else { 508 rc = setuid(uid); 509 if (rc >= 0) 510 rc = setgid(gid); 511 } 512#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(_MINIX) 513 rc = setuid(getuid()); 514 if (rc >= 0) { 515 rc = setgid(getgid()); 516 } 517#else 518 uid_t uid = getuid(); 519 uid_t euid = geteuid(); 520 gid_t gid = getgid(); 521 gid_t egid = getegid(); 522 523 if (uid != euid || gid != egid) { 524 errno = ENOSYS; 525 rc = -1; 526 } else { 527 rc = 0; 528 } 529#endif 530 return rc; 531} 532 533char * 534strmalloc(const char *value) 535{ 536 char *result = 0; 537 538 if (value != 0) { 539#ifdef HAVE_STRDUP 540 result = strdup(value); 541#else 542 result = malloc(strlen(value) + 1); 543 if (result != 0) 544 strcpy(result, value); 545#endif 546 } 547 return result; 548} 549 550#ifdef NO_LEAKS 551void 552ExitProgram(int code) 553{ 554 luit_leaks(); 555 iso2022_leaks(); 556 charset_leaks(); 557 exit(code); 558} 559#endif 560