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