1 /* $OpenBSD: ex_script.c,v 1.16 2009/10/27 23:59:47 deraadt Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1992, 1993, 1994, 1995, 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Brian Hirt. 11 * 12 * See the LICENSE file for redistribution information. 13 */ 14 15 #include "config.h" 16 17 #include <sys/types.h> 18 #include <sys/ioctl.h> 19 #include <sys/queue.h> 20 #include <sys/stat.h> 21 #ifdef HAVE_SYS5_PTY 22 #include <sys/stropts.h> 23 #endif 24 #include <sys/time.h> 25 #include <sys/wait.h> 26 27 #include <bitstring.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <stdio.h> /* XXX: OSF/1 bug: include before <grp.h> */ 31 #include <grp.h> 32 #include <limits.h> 33 #include <poll.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <termios.h> 37 #include <unistd.h> 38 39 #include "../common/common.h" 40 #include "../vi/vi.h" 41 #include "script.h" 42 #include "pathnames.h" 43 44 static void sscr_check(SCR *); 45 static int sscr_getprompt(SCR *); 46 static int sscr_init(SCR *); 47 static int sscr_insert(SCR *); 48 static int sscr_matchprompt(SCR *, char *, size_t, size_t *); 49 static int sscr_pty(int *, int *, char *, size_t, struct termios *, void *); 50 static int sscr_setprompt(SCR *, char *, size_t); 51 52 /* 53 * ex_script -- : sc[ript][!] [file] 54 * Switch to script mode. 55 * 56 * PUBLIC: int ex_script(SCR *, EXCMD *); 57 */ 58 int 59 ex_script(sp, cmdp) 60 SCR *sp; 61 EXCMD *cmdp; 62 { 63 /* Vi only command. */ 64 if (!F_ISSET(sp, SC_VI)) { 65 msgq(sp, M_ERR, 66 "150|The script command is only available in vi mode"); 67 return (1); 68 } 69 70 /* Switch to the new file. */ 71 if (cmdp->argc != 0 && ex_edit(sp, cmdp)) 72 return (1); 73 74 /* Create the shell, figure out the prompt. */ 75 if (sscr_init(sp)) 76 return (1); 77 78 return (0); 79 } 80 81 /* 82 * sscr_init -- 83 * Create a pty setup for a shell. 84 */ 85 static int 86 sscr_init(sp) 87 SCR *sp; 88 { 89 SCRIPT *sc; 90 char *sh, *sh_path; 91 92 /* We're going to need a shell. */ 93 if (opts_empty(sp, O_SHELL, 0)) 94 return (1); 95 96 MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT)); 97 sp->script = sc; 98 sc->sh_prompt = NULL; 99 sc->sh_prompt_len = 0; 100 101 /* 102 * There are two different processes running through this code. 103 * They are the shell and the parent. 104 */ 105 sc->sh_master = sc->sh_slave = -1; 106 107 if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) { 108 msgq(sp, M_SYSERR, "tcgetattr"); 109 goto err; 110 } 111 112 /* 113 * Turn off output postprocessing and echo. 114 */ 115 sc->sh_term.c_oflag &= ~OPOST; 116 sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK); 117 118 #ifdef TIOCGWINSZ 119 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) { 120 msgq(sp, M_SYSERR, "tcgetattr"); 121 goto err; 122 } 123 124 if (sscr_pty(&sc->sh_master, 125 &sc->sh_slave, sc->sh_name, sizeof(sc->sh_name), 126 &sc->sh_term, &sc->sh_win) == -1) { 127 msgq(sp, M_SYSERR, "pty"); 128 goto err; 129 } 130 #else 131 if (sscr_pty(&sc->sh_master, 132 &sc->sh_slave, sc->sh_name, sizeof(sc->sh_name), 133 &sc->sh_term, NULL) == -1) { 134 msgq(sp, M_SYSERR, "pty"); 135 goto err; 136 } 137 #endif 138 139 /* 140 * __TK__ huh? 141 * Don't use vfork() here, because the signal semantics differ from 142 * implementation to implementation. 143 */ 144 switch (sc->sh_pid = fork()) { 145 case -1: /* Error. */ 146 msgq(sp, M_SYSERR, "fork"); 147 err: if (sc->sh_master != -1) 148 (void)close(sc->sh_master); 149 if (sc->sh_slave != -1) 150 (void)close(sc->sh_slave); 151 return (1); 152 case 0: /* Utility. */ 153 /* 154 * XXX 155 * So that shells that do command line editing turn it off. 156 */ 157 if (setenv("TERM", "emacs", 1) == -1 || 158 setenv("TERMCAP", "emacs:", 1) == -1 || 159 setenv("EMACS", "t", 1) == -1) 160 _exit(126); 161 162 (void)setsid(); 163 #ifdef TIOCSCTTY 164 /* 165 * 4.4BSD allocates a controlling terminal using the TIOCSCTTY 166 * ioctl, not by opening a terminal device file. POSIX 1003.1 167 * doesn't define a portable way to do this. If TIOCSCTTY is 168 * not available, hope that the open does it. 169 */ 170 (void)ioctl(sc->sh_slave, TIOCSCTTY, 0); 171 #endif 172 (void)close(sc->sh_master); 173 (void)dup2(sc->sh_slave, STDIN_FILENO); 174 (void)dup2(sc->sh_slave, STDOUT_FILENO); 175 (void)dup2(sc->sh_slave, STDERR_FILENO); 176 (void)close(sc->sh_slave); 177 178 /* Assumes that all shells have -i. */ 179 sh_path = O_STR(sp, O_SHELL); 180 if ((sh = strrchr(sh_path, '/')) == NULL) 181 sh = sh_path; 182 else 183 ++sh; 184 execl(sh_path, sh, "-i", (char *)NULL); 185 msgq_str(sp, M_SYSERR, sh_path, "execl: %s"); 186 _exit(127); 187 default: /* Parent. */ 188 break; 189 } 190 191 if (sscr_getprompt(sp)) 192 return (1); 193 194 F_SET(sp, SC_SCRIPT); 195 F_SET(sp->gp, G_SCRWIN); 196 return (0); 197 } 198 199 /* 200 * sscr_getprompt -- 201 * Eat lines printed by the shell until a line with no trailing 202 * carriage return comes; set the prompt from that line. 203 */ 204 static int 205 sscr_getprompt(sp) 206 SCR *sp; 207 { 208 CHAR_T *endp, *p, *t, buf[1024]; 209 SCRIPT *sc; 210 struct pollfd pfd[1]; 211 recno_t lline; 212 size_t llen, len; 213 u_int value; 214 int nr; 215 216 endp = buf; 217 len = sizeof(buf); 218 219 /* Wait up to a second for characters to read. */ 220 sc = sp->script; 221 pfd[0].fd = sc->sh_master; 222 pfd[0].events = POLLIN; 223 switch (poll(pfd, 1, 5 * 1000)) { 224 case -1: /* Error or interrupt. */ 225 msgq(sp, M_SYSERR, "poll"); 226 goto prompterr; 227 case 0: /* Timeout */ 228 msgq(sp, M_ERR, "Error: timed out"); 229 goto prompterr; 230 default: /* Characters to read. */ 231 break; 232 } 233 234 /* Read the characters. */ 235 more: len = sizeof(buf) - (endp - buf); 236 switch (nr = read(sc->sh_master, endp, len)) { 237 case 0: /* EOF. */ 238 msgq(sp, M_ERR, "Error: shell: EOF"); 239 goto prompterr; 240 case -1: /* Error or interrupt. */ 241 msgq(sp, M_SYSERR, "shell"); 242 goto prompterr; 243 default: 244 endp += nr; 245 break; 246 } 247 248 /* If any complete lines, push them into the file. */ 249 for (p = t = buf; p < endp; ++p) { 250 value = KEY_VAL(sp, *p); 251 if (value == K_CR || value == K_NL) { 252 if (db_last(sp, &lline) || 253 db_append(sp, 0, lline, t, p - t)) 254 goto prompterr; 255 t = p + 1; 256 } 257 } 258 if (p > buf) { 259 memmove(buf, t, endp - t); 260 endp = buf + (endp - t); 261 } 262 if (endp == buf) 263 goto more; 264 265 /* Wait up 1/10 of a second to make sure that we got it all. */ 266 switch (poll(pfd, 1, 100)) { 267 case -1: /* Error or interrupt. */ 268 msgq(sp, M_SYSERR, "poll"); 269 goto prompterr; 270 case 0: /* Timeout */ 271 break; 272 default: /* Characters to read. */ 273 goto more; 274 } 275 276 /* Timed out, so theoretically we have a prompt. */ 277 llen = endp - buf; 278 endp = buf; 279 280 /* Append the line into the file. */ 281 if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) { 282 prompterr: sscr_end(sp); 283 return (1); 284 } 285 286 return (sscr_setprompt(sp, buf, llen)); 287 } 288 289 /* 290 * sscr_exec -- 291 * Take a line and hand it off to the shell. 292 * 293 * PUBLIC: int sscr_exec(SCR *, recno_t); 294 */ 295 int 296 sscr_exec(sp, lno) 297 SCR *sp; 298 recno_t lno; 299 { 300 SCRIPT *sc; 301 recno_t last_lno; 302 size_t blen, len, last_len, tlen; 303 int isempty, matchprompt, nw, rval; 304 char *bp, *p; 305 306 /* If there's a prompt on the last line, append the command. */ 307 if (db_last(sp, &last_lno)) 308 return (1); 309 if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len)) 310 return (1); 311 if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) { 312 matchprompt = 1; 313 GET_SPACE_RET(sp, bp, blen, last_len + 128); 314 memmove(bp, p, last_len); 315 } else 316 matchprompt = 0; 317 318 /* Get something to execute. */ 319 if (db_eget(sp, lno, &p, &len, &isempty)) { 320 if (isempty) 321 goto empty; 322 goto err1; 323 } 324 325 /* Empty lines aren't interesting. */ 326 if (len == 0) 327 goto empty; 328 329 /* Delete any prompt. */ 330 if (sscr_matchprompt(sp, p, len, &tlen)) { 331 if (tlen == len) { 332 empty: msgq(sp, M_BERR, "151|No command to execute"); 333 goto err1; 334 } 335 p += (len - tlen); 336 len = tlen; 337 } 338 339 /* Push the line to the shell. */ 340 sc = sp->script; 341 if ((nw = write(sc->sh_master, p, len)) != len) 342 goto err2; 343 rval = 0; 344 if (write(sc->sh_master, "\n", 1) != 1) { 345 err2: if (nw == 0) 346 errno = EIO; 347 msgq(sp, M_SYSERR, "shell"); 348 goto err1; 349 } 350 351 if (matchprompt) { 352 ADD_SPACE_RET(sp, bp, blen, last_len + len); 353 memmove(bp + last_len, p, len); 354 if (db_set(sp, last_lno, bp, last_len + len)) 355 err1: rval = 1; 356 } 357 if (matchprompt) 358 FREE_SPACE(sp, bp, blen); 359 return (rval); 360 } 361 362 /* 363 * sscr_check_input - 364 * Check for input from command input or scripting windows. 365 * 366 * PUBLIC: int sscr_check_input(SCR *sp); 367 */ 368 int 369 sscr_check_input(SCR *sp) 370 { 371 GS *gp; 372 SCR *tsp; 373 struct pollfd *pfd; 374 int nfds, rval; 375 376 gp = sp->gp; 377 rval = 0; 378 379 /* Allocate space for pfd. */ 380 nfds = 1; 381 CIRCLEQ_FOREACH(tsp, &gp->dq, q) 382 if (F_ISSET(sp, SC_SCRIPT)) 383 nfds++; 384 pfd = calloc(nfds, sizeof(struct pollfd)); 385 if (pfd == NULL) { 386 msgq(sp, M_SYSERR, "malloc"); 387 return (1); 388 } 389 390 /* Setup events bitmasks. */ 391 pfd[0].fd = STDIN_FILENO; 392 pfd[0].events = POLLIN; 393 nfds = 1; 394 CIRCLEQ_FOREACH(tsp, &gp->dq, q) 395 if (F_ISSET(sp, SC_SCRIPT)) { 396 pfd[nfds].fd = sp->script->sh_master; 397 pfd[nfds].events = POLLIN; 398 nfds++; 399 } 400 401 loop: 402 /* Check for input. */ 403 switch (poll(pfd, nfds, INFTIM)) { 404 case -1: 405 msgq(sp, M_SYSERR, "poll"); 406 rval = 1; 407 /* FALLTHROUGH */ 408 case 0: 409 goto done; 410 default: 411 break; 412 } 413 414 /* Only insert from the scripting windows if no command input */ 415 if (!(pfd[0].revents & POLLIN)) { 416 nfds = 1; 417 CIRCLEQ_FOREACH(tsp, &gp->dq, q) 418 if (F_ISSET(sp, SC_SCRIPT)) { 419 if ((pfd[nfds++].revents & POLLIN) && sscr_insert(sp)) 420 goto done; 421 } 422 goto loop; 423 } 424 done: 425 free(pfd); 426 return (rval); 427 } 428 429 /* 430 * sscr_input -- 431 * Read any waiting shell input. 432 * 433 * PUBLIC: int sscr_input(SCR *); 434 */ 435 int 436 sscr_input(sp) 437 SCR *sp; 438 { 439 GS *gp; 440 struct pollfd *pfd; 441 int nfds, rval; 442 443 gp = sp->gp; 444 rval = 0; 445 446 /* Allocate space for pfd. */ 447 nfds = 0; 448 CIRCLEQ_FOREACH(sp, &gp->dq, q) 449 if (F_ISSET(sp, SC_SCRIPT)) 450 nfds++; 451 if (nfds == 0) 452 return (0); 453 pfd = calloc(nfds, sizeof(struct pollfd)); 454 if (pfd == NULL) { 455 msgq(sp, M_SYSERR, "malloc"); 456 return (1); 457 } 458 459 /* Setup events bitmasks. */ 460 nfds = 0; 461 CIRCLEQ_FOREACH(sp, &gp->dq, q) 462 if (F_ISSET(sp, SC_SCRIPT)) { 463 pfd[nfds].fd = sp->script->sh_master; 464 pfd[nfds].events = POLLIN; 465 nfds++; 466 } 467 468 loop: 469 /* Check for input. */ 470 switch (poll(pfd, nfds, 0)) { 471 case -1: 472 msgq(sp, M_SYSERR, "poll"); 473 rval = 1; 474 /* FALLTHROUGH */ 475 case 0: 476 goto done; 477 default: 478 break; 479 } 480 481 /* Read the input. */ 482 nfds = 0; 483 CIRCLEQ_FOREACH(sp, &gp->dq, q) 484 if (F_ISSET(sp, SC_SCRIPT)) { 485 if ((pfd[nfds++].revents & POLLIN) && sscr_insert(sp)) 486 goto done; 487 } 488 goto loop; 489 done: 490 free(pfd); 491 return (rval); 492 } 493 494 /* 495 * sscr_insert -- 496 * Take a line from the shell and insert it into the file. 497 */ 498 static int 499 sscr_insert(sp) 500 SCR *sp; 501 { 502 CHAR_T *endp, *p, *t; 503 SCRIPT *sc; 504 struct pollfd pfd[1]; 505 recno_t lno; 506 size_t blen, len, tlen; 507 u_int value; 508 int nr, rval; 509 char *bp; 510 511 /* Find out where the end of the file is. */ 512 if (db_last(sp, &lno)) 513 return (1); 514 515 #define MINREAD 1024 516 GET_SPACE_RET(sp, bp, blen, MINREAD); 517 endp = bp; 518 519 /* Read the characters. */ 520 rval = 1; 521 sc = sp->script; 522 more: switch (nr = read(sc->sh_master, endp, MINREAD)) { 523 case 0: /* EOF; shell just exited. */ 524 sscr_end(sp); 525 rval = 0; 526 goto ret; 527 case -1: /* Error or interrupt. */ 528 msgq(sp, M_SYSERR, "shell"); 529 goto ret; 530 default: 531 endp += nr; 532 break; 533 } 534 535 /* Append the lines into the file. */ 536 for (p = t = bp; p < endp; ++p) { 537 value = KEY_VAL(sp, *p); 538 if (value == K_CR || value == K_NL) { 539 len = p - t; 540 if (db_append(sp, 1, lno++, t, len)) 541 goto ret; 542 t = p + 1; 543 } 544 } 545 if (p > t) { 546 len = p - t; 547 /* 548 * If the last thing from the shell isn't another prompt, wait 549 * up to 1/10 of a second for more stuff to show up, so that 550 * we don't break the output into two separate lines. Don't 551 * want to hang indefinitely because some program is hanging, 552 * confused the shell, or whatever. 553 */ 554 if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) { 555 pfd[0].fd = sc->sh_master; 556 pfd[0].events = POLLIN; 557 if (poll(pfd, 1, 100) > 0) { 558 memmove(bp, t, len); 559 endp = bp + len; 560 goto more; 561 } 562 } 563 if (sscr_setprompt(sp, t, len)) 564 return (1); 565 if (db_append(sp, 1, lno++, t, len)) 566 goto ret; 567 } 568 569 /* The cursor moves to EOF. */ 570 sp->lno = lno; 571 sp->cno = len ? len - 1 : 0; 572 rval = vs_refresh(sp, 1); 573 574 ret: FREE_SPACE(sp, bp, blen); 575 return (rval); 576 } 577 578 /* 579 * sscr_setprompt -- 580 * 581 * Set the prompt to the last line we got from the shell. 582 * 583 */ 584 static int 585 sscr_setprompt(sp, buf, len) 586 SCR *sp; 587 char *buf; 588 size_t len; 589 { 590 SCRIPT *sc; 591 592 sc = sp->script; 593 if (sc->sh_prompt) 594 free(sc->sh_prompt); 595 MALLOC(sp, sc->sh_prompt, char *, len + 1); 596 if (sc->sh_prompt == NULL) { 597 sscr_end(sp); 598 return (1); 599 } 600 memmove(sc->sh_prompt, buf, len); 601 sc->sh_prompt_len = len; 602 sc->sh_prompt[len] = '\0'; 603 return (0); 604 } 605 606 /* 607 * sscr_matchprompt -- 608 * Check to see if a line matches the prompt. Nul's indicate 609 * parts that can change, in both content and size. 610 */ 611 static int 612 sscr_matchprompt(sp, lp, line_len, lenp) 613 SCR *sp; 614 char *lp; 615 size_t line_len, *lenp; 616 { 617 SCRIPT *sc; 618 size_t prompt_len; 619 char *pp; 620 621 sc = sp->script; 622 if (line_len < (prompt_len = sc->sh_prompt_len)) 623 return (0); 624 625 for (pp = sc->sh_prompt; 626 prompt_len && line_len; --prompt_len, --line_len) { 627 if (*pp == '\0') { 628 for (; prompt_len && *pp == '\0'; --prompt_len, ++pp); 629 if (!prompt_len) 630 return (0); 631 for (; line_len && *lp != *pp; --line_len, ++lp); 632 if (!line_len) 633 return (0); 634 } 635 if (*pp++ != *lp++) 636 break; 637 } 638 639 if (prompt_len) 640 return (0); 641 if (lenp != NULL) 642 *lenp = line_len; 643 return (1); 644 } 645 646 /* 647 * sscr_end -- 648 * End the pipe to a shell. 649 * 650 * PUBLIC: int sscr_end(SCR *); 651 */ 652 int 653 sscr_end(sp) 654 SCR *sp; 655 { 656 SCRIPT *sc; 657 658 if ((sc = sp->script) == NULL) 659 return (0); 660 661 /* Turn off the script flags. */ 662 F_CLR(sp, SC_SCRIPT); 663 sscr_check(sp); 664 665 /* Close down the parent's file descriptors. */ 666 if (sc->sh_master != -1) 667 (void)close(sc->sh_master); 668 if (sc->sh_slave != -1) 669 (void)close(sc->sh_slave); 670 671 /* This should have killed the child. */ 672 (void)proc_wait(sp, sc->sh_pid, "script-shell", 0, 0); 673 674 /* Free memory. */ 675 free(sc->sh_prompt); 676 free(sc); 677 sp->script = NULL; 678 679 return (0); 680 } 681 682 /* 683 * sscr_check -- 684 * Set/clear the global scripting bit. 685 */ 686 static void 687 sscr_check(sp) 688 SCR *sp; 689 { 690 GS *gp; 691 692 gp = sp->gp; 693 CIRCLEQ_FOREACH(sp, &gp->dq, q) 694 if (F_ISSET(sp, SC_SCRIPT)) { 695 F_SET(gp, G_SCRWIN); 696 return; 697 } 698 F_CLR(gp, G_SCRWIN); 699 } 700 701 #ifdef HAVE_SYS5_PTY 702 static int ptys_open(int, char *); 703 static int ptym_open(char *, size_t); 704 705 static int 706 sscr_pty(amaster, aslave, name, namelen, termp, winp) 707 int *amaster, *aslave; 708 char *name; 709 size_t namelen; 710 struct termios *termp; 711 void *winp; 712 { 713 int master, slave, ttygid; 714 715 /* open master terminal */ 716 if ((master = ptym_open(name, namelen)) < 0) { 717 errno = ENOENT; /* out of ptys */ 718 return (-1); 719 } 720 721 /* open slave terminal */ 722 if ((slave = ptys_open(master, name)) >= 0) { 723 *amaster = master; 724 *aslave = slave; 725 } else { 726 errno = ENOENT; /* out of ptys */ 727 return (-1); 728 } 729 730 if (termp) 731 (void) tcsetattr(slave, TCSAFLUSH, termp); 732 #ifdef TIOCSWINSZ 733 if (winp != NULL) 734 (void) ioctl(slave, TIOCSWINSZ, (struct winsize *)winp); 735 #endif 736 return (0); 737 } 738 739 /* 740 * ptym_open -- 741 * This function opens a master pty and returns the file descriptor 742 * to it. pts_name is also returned which is the name of the slave. 743 */ 744 static int 745 ptym_open(pts_name, pts_namelen) 746 char *pts_name; 747 size_t pts_namelen; 748 { 749 int fdm; 750 char *ptr, *ptsname(); 751 752 strlcpy(pts_name, _PATH_SYSV_PTY, pts_namelen); 753 if ((fdm = open(pts_name, O_RDWR)) < 0 ) 754 return (-1); 755 756 if (grantpt(fdm) < 0) { 757 close(fdm); 758 return (-2); 759 } 760 761 if (unlockpt(fdm) < 0) { 762 close(fdm); 763 return (-3); 764 } 765 766 if (unlockpt(fdm) < 0) { 767 close(fdm); 768 return (-3); 769 } 770 771 /* get slave's name */ 772 if ((ptr = ptsname(fdm)) == NULL) { 773 close(fdm); 774 return (-3); 775 } 776 strlcpy(pts_name, ptr, pts_namelen); 777 return (fdm); 778 } 779 780 /* 781 * ptys_open -- 782 * This function opens the slave pty. 783 */ 784 static int 785 ptys_open(fdm, pts_name) 786 int fdm; 787 char *pts_name; 788 { 789 int fds; 790 791 if ((fds = open(pts_name, O_RDWR)) < 0) { 792 close(fdm); 793 return (-5); 794 } 795 796 if (ioctl(fds, I_PUSH, "ptem") < 0) { 797 close(fds); 798 close(fdm); 799 return (-6); 800 } 801 802 if (ioctl(fds, I_PUSH, "ldterm") < 0) { 803 close(fds); 804 close(fdm); 805 return (-7); 806 } 807 808 if (ioctl(fds, I_PUSH, "ttcompat") < 0) { 809 close(fds); 810 close(fdm); 811 return (-8); 812 } 813 814 return (fds); 815 } 816 817 #else /* !HAVE_SYS5_PTY */ 818 819 static int 820 sscr_pty(amaster, aslave, name, namelen, termp, winp) 821 int *amaster, *aslave; 822 char *name; 823 size_t namelen; 824 struct termios *termp; 825 void *winp; 826 { 827 static char line[] = "/dev/ptyXX"; 828 char *cp1, *cp2; 829 int master, slave, ttygid; 830 struct group *gr; 831 832 if ((gr = getgrnam("tty")) != NULL) 833 ttygid = gr->gr_gid; 834 else 835 ttygid = -1; 836 837 for (cp1 = "pqrs"; *cp1; cp1++) { 838 line[8] = *cp1; 839 for (cp2 = "0123456789abcdef"; *cp2; cp2++) { 840 line[5] = 'p'; 841 line[9] = *cp2; 842 if ((master = open(line, O_RDWR, 0)) == -1) { 843 if (errno == ENOENT) 844 return (-1); /* out of ptys */ 845 } else { 846 line[5] = 't'; 847 (void) chown(line, getuid(), ttygid); 848 (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); 849 #ifdef HAVE_REVOKE 850 (void) revoke(line); 851 #endif 852 if ((slave = open(line, O_RDWR, 0)) != -1) { 853 *amaster = master; 854 *aslave = slave; 855 if (name) 856 strlcpy(name, line, namelen); 857 if (termp) 858 (void) tcsetattr(slave, 859 TCSAFLUSH, termp); 860 #ifdef TIOCSWINSZ 861 if (winp) 862 (void) ioctl(slave, TIOCSWINSZ, 863 (char *)winp); 864 #endif 865 return (0); 866 } 867 (void) close(master); 868 } 869 } 870 } 871 errno = ENOENT; /* out of ptys */ 872 return (-1); 873 } 874 #endif /* HAVE_SYS5_PTY */ 875