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