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