]> git.ipfire.org Git - thirdparty/util-linux.git/blob - term-utils/script.c
script: add --log-in
[thirdparty/util-linux.git] / term-utils / script.c
1 /*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 /*
35 * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
36 * - added Native Language Support
37 *
38 * 2000-07-30 Per Andreas Buer <per@linpro.no> - added "q"-option
39 *
40 * 2014-05-30 Csaba Kos <csaba.kos@gmail.com>
41 * - fixed a rare deadlock after child termination
42 */
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <paths.h>
47 #include <time.h>
48 #include <sys/stat.h>
49 #include <termios.h>
50 #include <sys/ioctl.h>
51 #include <sys/time.h>
52 #include <signal.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <getopt.h>
56 #include <unistd.h>
57 #include <fcntl.h>
58 #include <limits.h>
59 #include <locale.h>
60 #include <stddef.h>
61 #include <sys/wait.h>
62 #include <poll.h>
63 #include <sys/signalfd.h>
64 #include <assert.h>
65 #include <inttypes.h>
66
67 #include "closestream.h"
68 #include "nls.h"
69 #include "c.h"
70 #include "ttyutils.h"
71 #include "all-io.h"
72 #include "monotonic.h"
73 #include "timeutils.h"
74 #include "strutils.h"
75 #include "xalloc.h"
76 #include "optutils.h"
77
78 #include "debug.h"
79
80 static UL_DEBUG_DEFINE_MASK(script);
81 UL_DEBUG_DEFINE_MASKNAMES(script) = UL_DEBUG_EMPTY_MASKNAMES;
82
83 #define SCRIPT_DEBUG_INIT (1 << 1)
84 #define SCRIPT_DEBUG_POLL (1 << 2)
85 #define SCRIPT_DEBUG_SIGNAL (1 << 3)
86 #define SCRIPT_DEBUG_IO (1 << 4)
87 #define SCRIPT_DEBUG_MISC (1 << 5)
88 #define SCRIPT_DEBUG_ALL 0xFFFF
89
90 #define DBG(m, x) __UL_DBG(script, SCRIPT_DEBUG_, m, x)
91 #define ON_DBG(m, x) __UL_DBG_CALL(script, SCRIPT_DEBUG_, m, x)
92
93 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
94 # include <pty.h>
95 #endif
96
97 #ifdef HAVE_LIBUTEMPTER
98 # include <utempter.h>
99 #endif
100
101 #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
102
103 enum {
104 SCRIPT_FMT_RAW = 1, /* raw slave/master data */
105 SCRIPT_FMT_TIMING_SIMPLE, /* timing info in classic "<time> <delta>" format */
106 };
107
108 struct script_log {
109 FILE *fp; /* file pointer (handler) */
110 int format; /* SCRIPT_FMT_* */
111 char *filename; /* on command line specified name */
112 struct timeval oldtime; /* previous entry log time */
113 };
114
115 struct script_stream {
116 struct timeval oldtime; /* last update */
117 struct script_log **logs; /* logs where to write data from stream */
118 size_t nlogs; /* number of logs */
119 };
120
121 struct script_control {
122 char *shell; /* shell to be executed */
123 char *command; /* command to be executed */
124 uint64_t outsz; /* current output files size */
125 uint64_t maxsz; /* maximum output files size */
126
127 int master; /* pseudoterminal master file descriptor */
128 int slave; /* pseudoterminal slave file descriptor */
129
130 struct script_stream out; /* output */
131 struct script_stream in; /* input */
132
133 int poll_timeout; /* poll() timeout, used in end of execution */
134 pid_t child; /* child pid */
135 int childstatus; /* child process exit value */
136 struct termios attrs; /* slave terminal runtime attributes */
137 struct winsize win; /* terminal window size */
138 #if !HAVE_LIBUTIL || !HAVE_PTY_H
139 char *line; /* terminal line */
140 #endif
141 unsigned int
142 append:1, /* append output */
143 rc_wanted:1, /* return child exit value */
144 flush:1, /* flush after each write */
145 quiet:1, /* suppress most output */
146 force:1, /* write output to links */
147 isterm:1, /* is child process running as terminal */
148 die:1; /* terminate program */
149
150 sigset_t sigset; /* catch SIGCHLD and SIGWINCH with signalfd() */
151 sigset_t sigorg; /* original signal mask */
152 int sigfd; /* file descriptor for signalfd() */
153 };
154
155 static void restore_tty(struct script_control *ctl, int mode);
156 static void __attribute__((__noreturn__)) fail(struct script_control *ctl);
157
158 static void script_init_debug(void)
159 {
160 __UL_INIT_DEBUG_FROM_ENV(script, SCRIPT_DEBUG_, 0, SCRIPT_DEBUG);
161 }
162
163 /*
164 * For tests we want to be able to control time output
165 */
166 #ifdef TEST_SCRIPT
167 static inline time_t script_time(time_t *t)
168 {
169 const char *str = getenv("SCRIPT_TEST_SECOND_SINCE_EPOCH");
170 int64_t sec;
171
172 if (!str || sscanf(str, "%"SCNi64, &sec) != 1)
173 return time(t);
174 if (t)
175 *t = (time_t)sec;
176 return (time_t)sec;
177 }
178 #else /* !TEST_SCRIPT */
179 # define script_time(x) time(x)
180 #endif
181
182 static void __attribute__((__noreturn__)) usage(void)
183 {
184 FILE *out = stdout;
185 fputs(USAGE_HEADER, out);
186 fprintf(out, _(" %s [options] [file]\n"), program_invocation_short_name);
187
188 fputs(USAGE_SEPARATOR, out);
189 fputs(_("Make a typescript of a terminal session.\n"), out);
190
191 fputs(USAGE_OPTIONS, out);
192 fputs(_(" -I, --log-in <file> log stdin to file\n"), out);
193 fputs(_(" -O, --log-out <file> log stdout to file (default)\n"), out);
194 fputs(_(" -T, --log-timing <file> log timing information to file\n"), out);
195 fputs(_(" -a, --append append the output\n"), out);
196 fputs(_(" -c, --command <command> run command rather than interactive shell\n"), out);
197 fputs(_(" -e, --return return exit code of the child process\n"), out);
198 fputs(_(" -f, --flush run flush after each write\n"), out);
199 fputs(_(" --force use output file even when it is a link\n"), out);
200 fputs(_(" -o, --output-limit <size> terminate if output files exceed size\n"), out);
201 fputs(_(" -q, --quiet be quiet\n"), out);
202 fputs(_(" -t[<file>], --timing[=<file>] deprecated alias to -T (default file is stderr)\n"), out);
203
204 fputs(USAGE_SEPARATOR, out);
205 printf(USAGE_HELP_OPTIONS(31));
206 printf(USAGE_MAN_TAIL("script(1)"));
207
208 exit(EXIT_SUCCESS);
209 }
210
211 static struct script_log *get_log_by_name(struct script_stream *stream,
212 const char *name)
213 {
214 size_t i;
215
216 for (i = 0; i < stream->nlogs; i++) {
217 struct script_log *log = stream->logs[i];
218 if (strcmp(log->filename, name) == 0)
219 return log;
220 }
221 return NULL;
222 }
223
224 static struct script_log *log_associate(struct script_control *ctl,
225 struct script_stream *stream,
226 const char *filename, int format)
227 {
228 struct script_log *log;
229
230 assert(ctl);
231 assert(filename);
232 assert(stream);
233
234 log = get_log_by_name(stream, filename);
235 if (log)
236 return log; /* already defined */
237
238 log = get_log_by_name(stream == &ctl->out ? &ctl->in : &ctl->out, filename);
239 if (!log) {
240 /* create a new log */
241 log = xcalloc(1, sizeof(*log));
242 log->filename = xstrdup(filename);
243 log->format = format;
244 }
245
246 /* add log to the stream */
247 stream->logs = xrealloc(stream->logs,
248 (stream->nlogs + 1) * sizeof(log));
249 stream->logs[stream->nlogs] = log;
250 stream->nlogs++;
251
252 return log;
253 }
254
255 static void log_close(struct script_control *ctl __attribute__((unused)),
256 struct script_log *log,
257 const char *msg,
258 int status)
259 {
260 DBG(MISC, ul_debug("closing %s", log->filename));
261
262 switch (log->format) {
263 case SCRIPT_FMT_RAW:
264 {
265 char buf[FORMAT_TIMESTAMP_MAX];
266 time_t tvec = script_time((time_t *)NULL);
267
268 strtime_iso(&tvec, ISO_TIMESTAMP, buf, sizeof(buf));
269 if (msg)
270 fprintf(log->fp, _("\nScript done on %s [<%s>]\n"), buf, msg);
271 else
272 fprintf(log->fp, _("\nScript done on %s [COMMAND_EXIT_CODE=\"%d\"]\n"), buf, status);
273 break;
274 }
275 case SCRIPT_FMT_TIMING_SIMPLE:
276 break;
277 }
278
279 if (close_stream(log->fp) != 0)
280 err(EXIT_FAILURE, "write failed: %s", log->filename);
281
282 log->fp = NULL;
283 }
284
285 static void log_start(struct script_control *ctl,
286 struct script_log *log)
287 {
288
289 assert(log->fp == NULL);
290
291 DBG(MISC, ul_debug("opening %s", log->filename));
292
293 /* open the log */
294 log->fp = fopen(log->filename,
295 ctl->append && log->format == SCRIPT_FMT_RAW ?
296 "a" UL_CLOEXECSTR :
297 "w" UL_CLOEXECSTR);
298 if (!log->fp) {
299 restore_tty(ctl, TCSANOW);
300 warn(_("cannot open %s"), log->filename);
301 fail(ctl);
302 }
303
304 /* write header, etc. */
305 switch (log->format) {
306 case SCRIPT_FMT_RAW:
307 {
308 char buf[FORMAT_TIMESTAMP_MAX];
309 time_t tvec = script_time((time_t *)NULL);
310
311 strtime_iso(&tvec, ISO_TIMESTAMP, buf, sizeof(buf));
312 fprintf(log->fp, _("Script started on %s ["), buf);
313
314 if (ctl->isterm) {
315 int cols = 0, lines = 0;
316 const char *tty = NULL, *term = NULL;
317
318 get_terminal_dimension(&cols, &lines);
319 get_terminal_name(&tty, NULL, NULL);
320 get_terminal_type(&term);
321
322 if (term)
323 fprintf(log->fp, "TERM=\"%s\" ", term);
324 if (tty)
325 fprintf(log->fp, "TTY=\"%s\" ", tty);
326
327 fprintf(log->fp, "COLUMNS=\"%d\" LINES=\"%d\"", cols, lines);
328 } else
329 fprintf(log->fp, _("<not executed on terminal>"));
330
331 fputs("]\n", log->fp);
332 break;
333 }
334 case SCRIPT_FMT_TIMING_SIMPLE:
335 gettime_monotonic(&log->oldtime);
336 break;
337 }
338 }
339
340 static size_t log_write(struct script_control *ctl,
341 struct script_log *log,
342 char *obuf, size_t bytes)
343 {
344 if (!log->fp)
345 return 0;
346
347 DBG(IO, ul_debug(" writining %s", log->filename));
348
349 switch (log->format) {
350 case SCRIPT_FMT_RAW:
351 if (fwrite_all(obuf, 1, bytes, log->fp)) {
352 warn(_("cannot write %s"), log->filename);
353 fail(ctl);
354 }
355 break;
356 case SCRIPT_FMT_TIMING_SIMPLE:
357 {
358 struct timeval now, delta;
359 int sz;
360
361 DBG(IO, ul_debug(" writing timing info"));
362
363 gettime_monotonic(&now);
364 timersub(&now, &log->oldtime, &delta);
365 sz = fprintf(log->fp, "%ld.%06ld %zd\n",
366 (long)delta.tv_sec, (long)delta.tv_usec, bytes);
367 log->oldtime = now;
368 bytes = sz > 0 ? sz : 0;
369 break;
370 }
371 default:
372 break;
373 }
374
375 if (ctl->flush)
376 fflush(log->fp);
377
378 return bytes;
379 }
380
381 static uint64_t log_stream_activity(
382 struct script_control *ctl,
383 struct script_stream *stream,
384 char *buf, size_t bytes)
385 {
386 size_t i;
387 uint64_t outsz = 0;
388
389 for (i = 0; i < stream->nlogs; i++)
390 outsz += log_write(ctl, stream->logs[i], buf, bytes);
391
392 return outsz;
393 }
394
395
396 static void die_if_link(struct script_control *ctl, const char *filename)
397 {
398 struct stat s;
399
400 if (ctl->force)
401 return;
402 if (lstat(filename, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1))
403 errx(EXIT_FAILURE,
404 _("output file `%s' is a link\n"
405 "Use --force if you really want to use it.\n"
406 "Program not started."), filename);
407 }
408
409 static void restore_tty(struct script_control *ctl, int mode)
410 {
411 struct termios rtt;
412
413 if (!ctl->isterm)
414 return;
415
416 rtt = ctl->attrs;
417 tcsetattr(STDIN_FILENO, mode, &rtt);
418 }
419
420 static void enable_rawmode_tty(struct script_control *ctl)
421 {
422 struct termios rtt;
423
424 if (!ctl->isterm)
425 return;
426
427 rtt = ctl->attrs;
428 cfmakeraw(&rtt);
429 rtt.c_lflag &= ~ECHO;
430 tcsetattr(STDIN_FILENO, TCSANOW, &rtt);
431 }
432
433 static void __attribute__((__noreturn__)) done_log(struct script_control *ctl, const char *msg)
434 {
435 int status;
436 size_t i;
437
438 DBG(MISC, ul_debug("done!"));
439
440 restore_tty(ctl, TCSADRAIN);
441
442 if (WIFSIGNALED(ctl->childstatus))
443 status = WTERMSIG(ctl->childstatus) + 0x80;
444 else
445 status = WEXITSTATUS(ctl->childstatus);
446
447
448 DBG(MISC, ul_debug(" status=%d", status));
449
450 /* close all output logs */
451 for (i = 0; i < ctl->out.nlogs; i++)
452 log_close(ctl, ctl->out.logs[i], msg, status);
453
454 /* close all input logs */
455 for (i = 0; i < ctl->in.nlogs; i++)
456 log_close(ctl, ctl->in.logs[i], msg, status);
457
458 if (!ctl->quiet)
459 printf(_("Script done.\n"));
460
461 #ifdef HAVE_LIBUTEMPTER
462 if (ctl->master >= 0)
463 utempter_remove_record(ctl->master);
464 #endif
465 kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
466 exit(ctl->rc_wanted ? status : EXIT_SUCCESS);
467 }
468
469 static void __attribute__((__noreturn__)) done(struct script_control *ctl)
470 {
471 done_log(ctl, NULL);
472 }
473
474 static void __attribute__((__noreturn__)) fail(struct script_control *ctl)
475 {
476 DBG(MISC, ul_debug("fail!"));
477 kill(0, SIGTERM);
478 done(ctl);
479 }
480
481 static void wait_for_child(struct script_control *ctl, int wait)
482 {
483 int status;
484 pid_t pid;
485 int options = wait ? 0 : WNOHANG;
486
487 DBG(MISC, ul_debug("waiting for child"));
488
489 while ((pid = wait3(&status, options, NULL)) > 0)
490 if (pid == ctl->child)
491 ctl->childstatus = status;
492 }
493
494 /* data from master to stdout */
495 static void write_output(struct script_control *ctl, char *obuf,
496 ssize_t bytes)
497 {
498 DBG(IO, ul_debug(" writing to output"));
499
500 if (write_all(STDOUT_FILENO, obuf, bytes)) {
501 DBG(IO, ul_debug(" writing output *failed*"));
502 warn(_("write failed"));
503 fail(ctl);
504 }
505 }
506
507 static int write_to_shell(struct script_control *ctl,
508 char *buf, size_t bufsz)
509 {
510 return write_all(ctl->master, buf, bufsz);
511 }
512
513 /*
514 * The script(1) is usually faster than shell, so it's a good idea to wait until
515 * the previous message has been already read by shell from slave before we
516 * write to master. This is necessary especially for EOF situation when we can
517 * send EOF to master before shell is fully initialized, to workaround this
518 * problem we wait until slave is empty. For example:
519 *
520 * echo "date" | script
521 *
522 * Unfortunately, the child (usually shell) can ignore stdin at all, so we
523 * don't wait forever to avoid dead locks...
524 *
525 * Note that script is primarily designed for interactive sessions as it
526 * maintains master+slave tty stuff within the session. Use pipe to write to
527 * script(1) and assume non-interactive (tee-like) behavior is NOT well
528 * supported.
529 */
530 static void write_eof_to_shell(struct script_control *ctl)
531 {
532 unsigned int tries = 0;
533 struct pollfd fds[] = {
534 { .fd = ctl->slave, .events = POLLIN }
535 };
536 char c = DEF_EOF;
537
538 DBG(IO, ul_debug(" waiting for empty slave"));
539 while (poll(fds, 1, 10) == 1 && tries < 8) {
540 DBG(IO, ul_debug(" slave is not empty"));
541 xusleep(250000);
542 tries++;
543 }
544 if (tries < 8)
545 DBG(IO, ul_debug(" slave is empty now"));
546
547 DBG(IO, ul_debug(" sending EOF to master"));
548 write_to_shell(ctl, &c, sizeof(char));
549 }
550
551 static void handle_io(struct script_control *ctl, int fd, int *eof)
552 {
553 char buf[BUFSIZ];
554 ssize_t bytes;
555 DBG(IO, ul_debug("%d FD active", fd));
556 *eof = 0;
557
558 /* read from active FD */
559 bytes = read(fd, buf, sizeof(buf));
560 if (bytes < 0) {
561 if (errno == EAGAIN || errno == EINTR)
562 return;
563 fail(ctl);
564 }
565
566 if (bytes == 0) {
567 *eof = 1;
568 return;
569 }
570
571 /* from stdin (user) to command */
572 if (fd == STDIN_FILENO) {
573 DBG(IO, ul_debug(" stdin --> master %zd bytes", bytes));
574
575 if (write_to_shell(ctl, buf, bytes)) {
576 warn(_("write failed"));
577 fail(ctl);
578 }
579 /* without sync write_output() will write both input &
580 * shell output that looks like double echoing */
581 fdatasync(ctl->master);
582 ctl->outsz += log_stream_activity(ctl, &ctl->in, buf, (size_t) bytes);
583
584 /* from command (master) to stdout and log */
585 } else if (fd == ctl->master) {
586 DBG(IO, ul_debug(" master --> stdout %zd bytes", bytes));
587 write_output(ctl, buf, bytes);
588 ctl->outsz += log_stream_activity(ctl, &ctl->out, buf, (size_t) bytes);
589 }
590
591 /* check output limit */
592 if (ctl->maxsz != 0 && ctl->outsz >= ctl->maxsz) {
593 if (!ctl->quiet)
594 printf(_("Script terminated, max output files size %"PRIu64" exceeded.\n"), ctl->maxsz);
595 DBG(IO, ul_debug("output size %"PRIu64", exceeded limit %"PRIu64, ctl->outsz, ctl->maxsz));
596 done_log(ctl, _("max output size exceeded"));
597 }
598 }
599
600 static void handle_signal(struct script_control *ctl, int fd)
601 {
602 struct signalfd_siginfo info;
603 ssize_t bytes;
604
605 DBG(SIGNAL, ul_debug("signal FD %d active", fd));
606
607 bytes = read(fd, &info, sizeof(info));
608 if (bytes != sizeof(info)) {
609 if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
610 return;
611 fail(ctl);
612 }
613
614 switch (info.ssi_signo) {
615 case SIGCHLD:
616 DBG(SIGNAL, ul_debug(" get signal SIGCHLD [ssi_code=%d, ssi_status=%d]",
617 info.ssi_code, info.ssi_status));
618 if (info.ssi_code == CLD_EXITED
619 || info.ssi_code == CLD_KILLED
620 || info.ssi_code == CLD_DUMPED) {
621 wait_for_child(ctl, 0);
622 ctl->poll_timeout = 10;
623
624 /* In case of ssi_code is CLD_TRAPPED, CLD_STOPPED, or CLD_CONTINUED */
625 } else if (info.ssi_status == SIGSTOP && ctl->child) {
626 DBG(SIGNAL, ul_debug(" child stop by SIGSTOP -- stop parent too"));
627 kill(getpid(), SIGSTOP);
628 DBG(SIGNAL, ul_debug(" resume"));
629 kill(ctl->child, SIGCONT);
630 }
631 return;
632 case SIGWINCH:
633 DBG(SIGNAL, ul_debug(" get signal SIGWINCH"));
634 if (ctl->isterm) {
635 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
636 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
637 }
638 break;
639 case SIGTERM:
640 /* fallthrough */
641 case SIGINT:
642 /* fallthrough */
643 case SIGQUIT:
644 DBG(SIGNAL, ul_debug(" get signal SIG{TERM,INT,QUIT}"));
645 fprintf(stderr, _("\nSession terminated.\n"));
646 /* Child termination is going to generate SIGCHILD (see above) */
647 kill(ctl->child, SIGTERM);
648 return;
649 default:
650 abort();
651 }
652 DBG(SIGNAL, ul_debug("signal handle on FD %d done", fd));
653 }
654
655 static void do_io(struct script_control *ctl)
656 {
657 int ret, eof = 0;
658 size_t i;
659 enum {
660 POLLFD_SIGNAL = 0,
661 POLLFD_MASTER,
662 POLLFD_STDIN
663
664 };
665 struct pollfd pfd[] = {
666 [POLLFD_SIGNAL] = { .fd = ctl->sigfd, .events = POLLIN | POLLERR | POLLHUP },
667 [POLLFD_MASTER] = { .fd = ctl->master, .events = POLLIN | POLLERR | POLLHUP },
668 [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP }
669 };
670
671
672 /* start all output logs */
673 for (i = 0; i < ctl->out.nlogs; i++)
674 log_start(ctl, ctl->out.logs[i]);
675
676 /* start all input logs */
677 for (i = 0; i < ctl->in.nlogs; i++)
678 log_start(ctl, ctl->in.logs[i]);
679
680 while (!ctl->die) {
681 size_t i;
682 int errsv;
683
684 DBG(POLL, ul_debug("calling poll()"));
685
686 /* wait for input or signal */
687 ret = poll(pfd, ARRAY_SIZE(pfd), ctl->poll_timeout);
688 errsv = errno;
689 DBG(POLL, ul_debug("poll() rc=%d", ret));
690
691 if (ret < 0) {
692 if (errsv == EAGAIN)
693 continue;
694 warn(_("poll failed"));
695 fail(ctl);
696 }
697 if (ret == 0) {
698 DBG(POLL, ul_debug("setting die=1"));
699 ctl->die = 1;
700 break;
701 }
702
703 for (i = 0; i < ARRAY_SIZE(pfd); i++) {
704 if (pfd[i].revents == 0)
705 continue;
706
707 DBG(POLL, ul_debug(" active pfd[%s].fd=%d %s %s %s",
708 i == POLLFD_STDIN ? "stdin" :
709 i == POLLFD_MASTER ? "master" :
710 i == POLLFD_SIGNAL ? "signal" : "???",
711 pfd[i].fd,
712 pfd[i].revents & POLLIN ? "POLLIN" : "",
713 pfd[i].revents & POLLHUP ? "POLLHUP" : "",
714 pfd[i].revents & POLLERR ? "POLLERR" : ""));
715 switch (i) {
716 case POLLFD_STDIN:
717 case POLLFD_MASTER:
718 /* data */
719 if (pfd[i].revents & POLLIN)
720 handle_io(ctl, pfd[i].fd, &eof);
721 /* EOF maybe detected by two ways:
722 * A) poll() return POLLHUP event after close()
723 * B) read() returns 0 (no data) */
724 if ((pfd[i].revents & POLLHUP) || eof) {
725 DBG(POLL, ul_debug(" ignore FD"));
726 pfd[i].fd = -1;
727 if (i == POLLFD_STDIN) {
728 write_eof_to_shell(ctl);
729 DBG(POLL, ul_debug(" ignore STDIN"));
730 }
731 }
732 continue;
733 case POLLFD_SIGNAL:
734 handle_signal(ctl, pfd[i].fd);
735 break;
736 }
737 }
738 }
739
740 DBG(POLL, ul_debug("poll() done"));
741
742 if (!ctl->die)
743 wait_for_child(ctl, 1);
744
745 done(ctl);
746 }
747
748 static void getslave(struct script_control *ctl)
749 {
750 #ifndef HAVE_LIBUTIL
751 ctl->line[strlen("/dev/")] = 't';
752 ctl->slave = open(ctl->line, O_RDWR | O_CLOEXEC);
753 if (ctl->slave < 0) {
754 warn(_("cannot open %s"), ctl->line);
755 fail(ctl);
756 }
757 if (ctl->isterm) {
758 tcsetattr(ctl->slave, TCSANOW, &ctl->attrs);
759 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
760 }
761 #endif
762 setsid();
763 ioctl(ctl->slave, TIOCSCTTY, 0);
764 }
765
766 /* don't use DBG() stuff here otherwise it will be in the typescript file */
767 static void __attribute__((__noreturn__)) do_shell(struct script_control *ctl)
768 {
769 char *shname;
770
771 getslave(ctl);
772
773 /* close things irrelevant for this process */
774 close(ctl->master);
775 close(ctl->sigfd);
776
777 dup2(ctl->slave, STDIN_FILENO);
778 dup2(ctl->slave, STDOUT_FILENO);
779 dup2(ctl->slave, STDERR_FILENO);
780 close(ctl->slave);
781
782 ctl->master = -1;
783
784 shname = strrchr(ctl->shell, '/');
785 if (shname)
786 shname++;
787 else
788 shname = ctl->shell;
789
790 sigprocmask(SIG_SETMASK, &ctl->sigorg, NULL);
791
792 /*
793 * When invoked from within /etc/csh.login, script spawns a csh shell
794 * that spawns programs that cannot be killed with a SIGTERM. This is
795 * because csh has a documented behavior wherein it disables all
796 * signals when processing the /etc/csh.* files.
797 *
798 * Let's restore the default behavior.
799 */
800 signal(SIGTERM, SIG_DFL);
801
802 if (access(ctl->shell, X_OK) == 0) {
803 if (ctl->command)
804 execl(ctl->shell, shname, "-c", ctl->command, NULL);
805 else
806 execl(ctl->shell, shname, "-i", NULL);
807 } else {
808 if (ctl->command)
809 execlp(shname, "-c", ctl->command, NULL);
810 else
811 execlp(shname, "-i", NULL);
812 }
813 warn(_("failed to execute %s"), ctl->shell);
814 fail(ctl);
815 }
816
817
818 static void getmaster(struct script_control *ctl)
819 {
820 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
821 int rc;
822
823 ctl->isterm = isatty(STDIN_FILENO);
824
825 if (ctl->isterm) {
826 if (tcgetattr(STDIN_FILENO, &ctl->attrs) != 0)
827 err(EXIT_FAILURE, _("failed to get terminal attributes"));
828 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
829 rc = openpty(&ctl->master, &ctl->slave, NULL, &ctl->attrs, &ctl->win);
830 } else
831 rc = openpty(&ctl->master, &ctl->slave, NULL, NULL, NULL);
832
833 if (rc < 0) {
834 warn(_("openpty failed"));
835 fail(ctl);
836 }
837 #else
838 char *pty, *bank, *cp;
839
840 ctl->isterm = isatty(STDIN_FILENO);
841
842 pty = &ctl->line[strlen("/dev/ptyp")];
843 for (bank = "pqrs"; *bank; bank++) {
844 ctl->line[strlen("/dev/pty")] = *bank;
845 *pty = '0';
846 if (access(ctl->line, F_OK) != 0)
847 break;
848 for (cp = "0123456789abcdef"; *cp; cp++) {
849 *pty = *cp;
850 ctl->master = open(ctl->line, O_RDWR | O_CLOEXEC);
851 if (ctl->master >= 0) {
852 char *tp = &ctl->line[strlen("/dev/")];
853 int ok;
854
855 /* verify slave side is usable */
856 *tp = 't';
857 ok = access(ctl->line, R_OK | W_OK) == 0;
858 *tp = 'p';
859 if (ok) {
860 if (ctl->isterm) {
861 tcgetattr(STDIN_FILENO, &ctl->attrs);
862 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
863 }
864 return;
865 }
866 close(ctl->master);
867 ctl->master = -1;
868 }
869 }
870 }
871 ctl->master = -1;
872 warn(_("out of pty's"));
873 fail(ctl);
874 #endif /* not HAVE_LIBUTIL */
875
876 DBG(IO, ul_debug("master fd: %d", ctl->master));
877 }
878
879 int main(int argc, char **argv)
880 {
881 struct script_control ctl = {
882 #if !HAVE_LIBUTIL || !HAVE_PTY_H
883 .line = "/dev/ptyXX",
884 #endif
885 .master = -1,
886 .slave = -1,
887
888 .poll_timeout = -1
889 };
890 int ch;
891 const char *outfile = NULL, *infile = NULL;
892 const char *timingfile = NULL;
893
894 enum { FORCE_OPTION = CHAR_MAX + 1 };
895
896 static const struct option longopts[] = {
897 {"append", no_argument, NULL, 'a'},
898 {"command", required_argument, NULL, 'c'},
899 {"return", no_argument, NULL, 'e'},
900 {"flush", no_argument, NULL, 'f'},
901 {"force", no_argument, NULL, FORCE_OPTION,},
902 {"log-in", required_argument, NULL, 'I'},
903 {"log-out", required_argument, NULL, 'O'},
904 {"log-timing", required_argument, NULL, 'T'},
905 {"output-limit", required_argument, NULL, 'o'},
906 {"quiet", no_argument, NULL, 'q'},
907 {"timing", optional_argument, NULL, 't'},
908 {"version", no_argument, NULL, 'V'},
909 {"help", no_argument, NULL, 'h'},
910 {NULL, 0, NULL, 0}
911 };
912 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
913 { 'T', 't' },
914 { 0 }
915 };
916 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
917 setlocale(LC_ALL, "");
918 /*
919 * script -t prints time delays as floating point numbers. The example
920 * program (scriptreplay) that we provide to handle this timing output
921 * is a perl script, and does not handle numbers in locale format (not
922 * even when "use locale;" is added). So, since these numbers are not
923 * for human consumption, it seems easiest to set LC_NUMERIC here.
924 */
925 setlocale(LC_NUMERIC, "C");
926 bindtextdomain(PACKAGE, LOCALEDIR);
927 textdomain(PACKAGE);
928 close_stdout_atexit();
929
930 script_init_debug();
931
932 while ((ch = getopt_long(argc, argv, "ac:efI:O:o:qT:t::Vh", longopts, NULL)) != -1) {
933
934 err_exclusive_options(ch, longopts, excl, excl_st);
935
936 switch (ch) {
937 case 'a':
938 ctl.append = 1;
939 break;
940 case 'c':
941 ctl.command = optarg;
942 break;
943 case 'e':
944 ctl.rc_wanted = 1;
945 break;
946 case 'f':
947 ctl.flush = 1;
948 break;
949 case FORCE_OPTION:
950 ctl.force = 1;
951 break;
952 case 'I':
953 log_associate(&ctl, &ctl.in, optarg, SCRIPT_FMT_RAW);
954 infile = optarg;
955 break;
956 case 'O':
957 log_associate(&ctl, &ctl.out, optarg, SCRIPT_FMT_RAW);
958 outfile = optarg;
959 break;
960 case 'o':
961 ctl.maxsz = strtosize_or_err(optarg, _("failed to parse output limit size"));
962 break;
963 case 'q':
964 ctl.quiet = 1;
965 break;
966 case 't':
967 if (optarg && *optarg == '=')
968 optarg++;
969 log_associate(&ctl, &ctl.out,
970 optarg ? optarg : "/dev/stderr",
971 SCRIPT_FMT_TIMING_SIMPLE);
972 /* used for message only */
973 timingfile = optarg ? optarg : "stderr";
974 break;
975 case 'T' :
976 log_associate(&ctl, &ctl.out, optarg, SCRIPT_FMT_TIMING_SIMPLE);
977 timingfile = optarg;
978 break;
979 case 'V':
980 print_version(EXIT_SUCCESS);
981 case 'h':
982 usage();
983 default:
984 errtryhelp(EXIT_FAILURE);
985 }
986 }
987 argc -= optind;
988 argv += optind;
989
990 /* default if no --log-* specified */
991 if (!outfile && !infile) {
992 if (argc > 0)
993 outfile = argv[0];
994 else {
995 die_if_link(&ctl, DEFAULT_TYPESCRIPT_FILENAME);
996 outfile = DEFAULT_TYPESCRIPT_FILENAME;
997 }
998
999 /* associate stdout with typescript file */
1000 log_associate(&ctl, &ctl.out, outfile, SCRIPT_FMT_RAW);
1001 }
1002
1003 ctl.shell = getenv("SHELL");
1004 if (ctl.shell == NULL)
1005 ctl.shell = _PATH_BSHELL;
1006
1007 getmaster(&ctl);
1008 if (!ctl.quiet) {
1009 printf(_("Script started"));
1010 if (outfile)
1011 printf(_(", output log file is '%s'"), outfile);
1012 if (infile)
1013 printf(_(", input log file is '%s'"), infile);
1014 if (timingfile)
1015 printf(_(", timing file is '%s'"), timingfile);
1016 printf(_(".\n"));
1017 }
1018 enable_rawmode_tty(&ctl);
1019
1020 #ifdef HAVE_LIBUTEMPTER
1021 utempter_add_record(ctl.master, NULL);
1022 #endif
1023 /* setup signal handler */
1024 sigemptyset(&ctl.sigset);
1025 sigaddset(&ctl.sigset, SIGCHLD);
1026 sigaddset(&ctl.sigset, SIGWINCH);
1027 sigaddset(&ctl.sigset, SIGTERM);
1028 sigaddset(&ctl.sigset, SIGINT);
1029 sigaddset(&ctl.sigset, SIGQUIT);
1030
1031 /* block signals used for signalfd() to prevent the signals being
1032 * handled according to their default dispositions */
1033 sigprocmask(SIG_BLOCK, &ctl.sigset, &ctl.sigorg);
1034
1035 if ((ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC)) < 0)
1036 err(EXIT_FAILURE, _("cannot set signal handler"));
1037
1038 DBG(SIGNAL, ul_debug("signal fd=%d", ctl.sigfd));
1039
1040 fflush(stdout);
1041 ctl.child = fork();
1042
1043 switch (ctl.child) {
1044 case -1: /* error */
1045 warn(_("fork failed"));
1046 fail(&ctl);
1047 break;
1048 case 0: /* child */
1049 do_shell(&ctl);
1050 break;
1051 default: /* parent */
1052 do_io(&ctl);
1053 break;
1054 }
1055
1056 /* should not happen, all used functions are non-return */
1057 return EXIT_FAILURE;
1058 }