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