]> git.ipfire.org Git - thirdparty/util-linux.git/blob - term-utils/script.c
script: cleanup info logging
[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 #include "signames.h"
78
79 #include "debug.h"
80
81 static UL_DEBUG_DEFINE_MASK(script);
82 UL_DEBUG_DEFINE_MASKNAMES(script) = UL_DEBUG_EMPTY_MASKNAMES;
83
84 #define SCRIPT_DEBUG_INIT (1 << 1)
85 #define SCRIPT_DEBUG_POLL (1 << 2)
86 #define SCRIPT_DEBUG_SIGNAL (1 << 3)
87 #define SCRIPT_DEBUG_IO (1 << 4)
88 #define SCRIPT_DEBUG_MISC (1 << 5)
89 #define SCRIPT_DEBUG_ALL 0xFFFF
90
91 #define DBG(m, x) __UL_DBG(script, SCRIPT_DEBUG_, m, x)
92 #define ON_DBG(m, x) __UL_DBG_CALL(script, SCRIPT_DEBUG_, m, x)
93
94 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
95 # include <pty.h>
96 #endif
97
98 #ifdef HAVE_LIBUTEMPTER
99 # include <utempter.h>
100 #endif
101
102 #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
103
104 /*
105 * Script is driven by stream (stdout/stdin) activity. It's possible to
106 * associate arbitrary number of log files with the stream. We have two basic
107 * types of log files: "timing file" (simple or multistream) and "data file"
108 * (raw).
109 *
110 * The same log file maybe be shared between both streams. For exmaple
111 * multi-stream timing file is possible to use for stdin as well as for stdout.
112 */
113 enum {
114 SCRIPT_FMT_RAW = 1, /* raw slave/master data */
115 SCRIPT_FMT_TIMING_SIMPLE, /* (classic) in format "<delta> <offset>" */
116 SCRIPT_FMT_TIMING_MULTI, /* (advanced) multiple streams in format "<type> <delta> <offset|etc> */
117 };
118
119 struct script_log {
120 FILE *fp; /* file pointer (handler) */
121 int format; /* SCRIPT_FMT_* */
122 char *filename; /* on command line specified name */
123 struct timeval oldtime; /* previous entry log time (SCRIPT_FMT_TIMING_* only) */
124
125 unsigned int initialized : 1;
126 };
127
128 struct script_stream {
129 struct script_log **logs; /* logs where to write data from stream */
130 size_t nlogs; /* number of logs */
131 char ident; /* stream identifier */
132 };
133
134 struct script_control {
135 char *shell; /* shell to be executed */
136 char *command; /* command to be executed */
137 uint64_t outsz; /* current output files size */
138 uint64_t maxsz; /* maximum output files size */
139
140 int master; /* pseudoterminal master file descriptor */
141 int slave; /* pseudoterminal slave file descriptor */
142
143 struct script_stream out; /* output */
144 struct script_stream in; /* input */
145
146 struct script_log *siglog; /* log for signal entries */
147 struct script_log *infolog; /* log for info entries */
148
149 const char *ttyname;
150 const char *ttytype;
151 int ttycols;
152 int ttylines;
153
154 int poll_timeout; /* poll() timeout, used in end of execution */
155 pid_t child; /* child pid */
156 int childstatus; /* child process exit value */
157 struct termios attrs; /* slave terminal runtime attributes */
158 struct winsize win; /* terminal window size */
159 #if !HAVE_LIBUTIL || !HAVE_PTY_H
160 char *line; /* terminal line */
161 #endif
162 unsigned int
163 append:1, /* append output */
164 rc_wanted:1, /* return child exit value */
165 flush:1, /* flush after each write */
166 quiet:1, /* suppress most output */
167 force:1, /* write output to links */
168 isterm:1, /* is child process running as terminal */
169 die:1; /* terminate program */
170
171 sigset_t sigset; /* catch SIGCHLD and SIGWINCH with signalfd() */
172 sigset_t sigorg; /* original signal mask */
173 int sigfd; /* file descriptor for signalfd() */
174 };
175
176 static void restore_tty(struct script_control *ctl, int mode);
177 static void __attribute__((__noreturn__)) fail(struct script_control *ctl);
178 static uint64_t log_info(struct script_control *ctl, const char *name, const char *msgfmt, ...);
179
180 static void script_init_debug(void)
181 {
182 __UL_INIT_DEBUG_FROM_ENV(script, SCRIPT_DEBUG_, 0, SCRIPT_DEBUG);
183 }
184
185 static void init_terminal_info(struct script_control *ctl)
186 {
187 if (ctl->ttyname || !ctl->isterm)
188 return; /* already initialized */
189
190 get_terminal_dimension(&ctl->ttycols, &ctl->ttylines);
191 get_terminal_name(&ctl->ttyname, NULL, NULL);
192 get_terminal_type(&ctl->ttytype);
193 }
194
195 /*
196 * For tests we want to be able to control time output
197 */
198 #ifdef TEST_SCRIPT
199 static inline time_t script_time(time_t *t)
200 {
201 const char *str = getenv("SCRIPT_TEST_SECOND_SINCE_EPOCH");
202 int64_t sec;
203
204 if (!str || sscanf(str, "%"SCNi64, &sec) != 1)
205 return time(t);
206 if (t)
207 *t = (time_t)sec;
208 return (time_t)sec;
209 }
210 #else /* !TEST_SCRIPT */
211 # define script_time(x) time(x)
212 #endif
213
214 static void __attribute__((__noreturn__)) usage(void)
215 {
216 FILE *out = stdout;
217 fputs(USAGE_HEADER, out);
218 fprintf(out, _(" %s [options] [file]\n"), program_invocation_short_name);
219
220 fputs(USAGE_SEPARATOR, out);
221 fputs(_("Make a typescript of a terminal session.\n"), out);
222
223 fputs(USAGE_OPTIONS, out);
224 fputs(_(" -I, --log-in <file> log stdin to file\n"), out);
225 fputs(_(" -O, --log-out <file> log stdout to file (default)\n"), out);
226 fputs(_(" -B, --log-io <file> log stdin and stdout to file\n"), out);
227 fputs(USAGE_SEPARATOR, out);
228
229 fputs(_(" -T, --log-timing <file> log timing information to file\n"), out);
230 fputs(_(" -t[<file>], --timing[=<file>] deprecated alias to -T (default file is stderr)\n"), out);
231 fputs(_(" -m, --logging-format <name> force to 'classic' or 'advanced' format\n"), out);
232 fputs(USAGE_SEPARATOR, out);
233
234 fputs(_(" -a, --append append to the log file\n"), out);
235 fputs(_(" -c, --command <command> run command rather than interactive shell\n"), out);
236 fputs(_(" -e, --return return exit code of the child process\n"), out);
237 fputs(_(" -f, --flush run flush after each write\n"), out);
238 fputs(_(" --force use output file even when it is a link\n"), out);
239 fputs(_(" -o, --output-limit <size> terminate if output files exceed size\n"), out);
240 fputs(_(" -q, --quiet be quiet\n"), out);
241
242 fputs(USAGE_SEPARATOR, out);
243 printf(USAGE_HELP_OPTIONS(31));
244 printf(USAGE_MAN_TAIL("script(1)"));
245
246 exit(EXIT_SUCCESS);
247 }
248
249 static struct script_log *get_log_by_name(struct script_stream *stream,
250 const char *name)
251 {
252 size_t i;
253
254 for (i = 0; i < stream->nlogs; i++) {
255 struct script_log *log = stream->logs[i];
256 if (strcmp(log->filename, name) == 0)
257 return log;
258 }
259 return NULL;
260 }
261
262 static struct script_log *log_associate(struct script_control *ctl,
263 struct script_stream *stream,
264 const char *filename, int format)
265 {
266 struct script_log *log;
267
268 DBG(MISC, ul_debug("associate %s with stream", filename));
269
270 assert(ctl);
271 assert(filename);
272 assert(stream);
273
274 log = get_log_by_name(stream, filename);
275 if (log)
276 return log; /* already defined */
277
278 log = get_log_by_name(stream == &ctl->out ? &ctl->in : &ctl->out, filename);
279 if (!log) {
280 /* create a new log */
281 log = xcalloc(1, sizeof(*log));
282 log->filename = xstrdup(filename);
283 log->format = format;
284 }
285
286 /* add log to the stream */
287 stream->logs = xrealloc(stream->logs,
288 (stream->nlogs + 1) * sizeof(log));
289 stream->logs[stream->nlogs] = log;
290 stream->nlogs++;
291
292 /* remember where to write info about signals */
293 if (format == SCRIPT_FMT_TIMING_MULTI) {
294 if (!ctl->siglog)
295 ctl->siglog = log;
296 if (!ctl->infolog)
297 ctl->infolog = log;
298 }
299
300 return log;
301 }
302
303 static void log_close(struct script_control *ctl __attribute__((unused)),
304 struct script_log *log,
305 const char *msg,
306 int status)
307 {
308 if (!log->initialized)
309 return;
310
311 DBG(MISC, ul_debug("closing %s", log->filename));
312
313 switch (log->format) {
314 case SCRIPT_FMT_RAW:
315 {
316 char buf[FORMAT_TIMESTAMP_MAX];
317 time_t tvec = script_time((time_t *)NULL);
318
319 strtime_iso(&tvec, ISO_TIMESTAMP, buf, sizeof(buf));
320 if (msg)
321 fprintf(log->fp, _("\nScript done on %s [<%s>]\n"), buf, msg);
322 else
323 fprintf(log->fp, _("\nScript done on %s [COMMAND_EXIT_CODE=\"%d\"]\n"), buf, status);
324 break;
325 }
326 case SCRIPT_FMT_TIMING_SIMPLE:
327 break;
328 }
329
330 if (close_stream(log->fp) != 0)
331 err(EXIT_FAILURE, "write failed: %s", log->filename);
332
333 log->fp = NULL;
334 log->initialized = 0;
335 }
336
337 static void log_start(struct script_control *ctl,
338 struct script_log *log)
339 {
340 if (log->initialized)
341 return;
342
343 DBG(MISC, ul_debug("opening %s", log->filename));
344
345 assert(log->fp == NULL);
346
347 /* open the log */
348 log->fp = fopen(log->filename,
349 ctl->append && log->format == SCRIPT_FMT_RAW ?
350 "a" UL_CLOEXECSTR :
351 "w" UL_CLOEXECSTR);
352 if (!log->fp) {
353 restore_tty(ctl, TCSANOW);
354 warn(_("cannot open %s"), log->filename);
355 fail(ctl);
356 }
357
358 /* write header, etc. */
359 switch (log->format) {
360 case SCRIPT_FMT_RAW:
361 {
362 char buf[FORMAT_TIMESTAMP_MAX];
363 time_t tvec = script_time((time_t *)NULL);
364
365 strtime_iso(&tvec, ISO_TIMESTAMP, buf, sizeof(buf));
366 fprintf(log->fp, _("Script started on %s ["), buf);
367
368 if (ctl->isterm) {
369 init_terminal_info(ctl);
370
371 if (ctl->ttytype)
372 fprintf(log->fp, "TERM=\"%s\" ", ctl->ttytype);
373 if (ctl->ttyname)
374 fprintf(log->fp, "TTY=\"%s\" ", ctl->ttyname);
375
376 fprintf(log->fp, "COLUMNS=\"%d\" LINES=\"%d\"", ctl->ttycols, ctl->ttylines);
377 } else
378 fprintf(log->fp, _("<not executed on terminal>"));
379
380 fputs("]\n", log->fp);
381 break;
382 }
383 case SCRIPT_FMT_TIMING_SIMPLE:
384 case SCRIPT_FMT_TIMING_MULTI:
385 gettime_monotonic(&log->oldtime);
386 break;
387 }
388
389 log->initialized = 1;
390 }
391
392 static void start_logging(struct script_control *ctl)
393 {
394 size_t i;
395
396 /* start all output logs */
397 for (i = 0; i < ctl->out.nlogs; i++)
398 log_start(ctl, ctl->out.logs[i]);
399
400 /* start all input logs */
401 for (i = 0; i < ctl->in.nlogs; i++)
402 log_start(ctl, ctl->in.logs[i]);
403 }
404
405 static size_t log_write(struct script_control *ctl,
406 struct script_stream *stream,
407 struct script_log *log,
408 char *obuf, size_t bytes)
409 {
410 if (!log->fp)
411 return 0;
412
413 DBG(IO, ul_debug(" writining %s", log->filename));
414
415 switch (log->format) {
416 case SCRIPT_FMT_RAW:
417 if (fwrite_all(obuf, 1, bytes, log->fp)) {
418 warn(_("cannot write %s"), log->filename);
419 fail(ctl);
420 }
421 break;
422 case SCRIPT_FMT_TIMING_SIMPLE:
423 {
424 struct timeval now, delta;
425 int sz;
426
427 DBG(IO, ul_debug(" writing timing info"));
428
429 gettime_monotonic(&now);
430 timersub(&now, &log->oldtime, &delta);
431 sz = fprintf(log->fp, "%ld.%06ld %zd\n",
432 (long)delta.tv_sec, (long)delta.tv_usec, bytes);
433 log->oldtime = now;
434 bytes = sz > 0 ? sz : 0;
435 break;
436 }
437 case SCRIPT_FMT_TIMING_MULTI:
438 {
439 struct timeval now, delta;
440 int sz;
441
442 DBG(IO, ul_debug(" writing multi-stream timing info"));
443
444 gettime_monotonic(&now);
445 timersub(&now, &log->oldtime, &delta);
446 sz = fprintf(log->fp, "%c %ld.%06ld %zd\n",
447 stream->ident,
448 (long)delta.tv_sec, (long)delta.tv_usec, bytes);
449 log->oldtime = now;
450 bytes = sz > 0 ? sz : 0;
451 }
452 default:
453 break;
454 }
455
456 if (ctl->flush)
457 fflush(log->fp);
458
459 return bytes;
460 }
461
462 static uint64_t log_stream_activity(
463 struct script_control *ctl,
464 struct script_stream *stream,
465 char *buf, size_t bytes)
466 {
467 size_t i;
468 uint64_t outsz = 0;
469
470 for (i = 0; i < stream->nlogs; i++)
471 outsz += log_write(ctl, stream, stream->logs[i], buf, bytes);
472
473 return outsz;
474 }
475
476 static uint64_t log_signal(struct script_control *ctl, int signum, char *msgfmt, ...)
477 {
478 struct script_log *log;
479 struct timeval now, delta;
480 char msg[BUFSIZ] = {0};
481 va_list ap;
482 int sz;
483
484 assert(ctl);
485
486 log = ctl->siglog;
487 if (!log)
488 return 0;
489
490 assert(log->format == SCRIPT_FMT_TIMING_MULTI);
491 DBG(IO, ul_debug(" writing signal to multi-stream timing"));
492
493 gettime_monotonic(&now);
494 timersub(&now, &log->oldtime, &delta);
495
496 if (msgfmt) {
497 int rc;
498 va_start(ap, msgfmt);
499 rc = vsnprintf(msg, sizeof(msg), msgfmt, ap);
500 va_end(ap);
501 if (rc < 0)
502 *msg = '\0';;
503 }
504
505 if (*msg)
506 sz = fprintf(log->fp, "S %ld.%06ld SIG%s %s\n",
507 (long)delta.tv_sec, (long)delta.tv_usec,
508 signum_to_signame(signum), msg);
509 else
510 sz = fprintf(log->fp, "S %ld.%06ld SIG%s\n",
511 (long)delta.tv_sec, (long)delta.tv_usec,
512 signum_to_signame(signum));
513
514 log->oldtime = now;
515 return sz > 0 ? sz : 0;
516 }
517
518 static uint64_t log_info(struct script_control *ctl, const char *name, const char *msgfmt, ...)
519 {
520 struct script_log *log;
521 char msg[BUFSIZ] = {0};
522 va_list ap;
523 int sz;
524
525 assert(ctl);
526
527 log = ctl->infolog;
528 if (!log)
529 return 0;
530
531 assert(log->format == SCRIPT_FMT_TIMING_MULTI);
532 DBG(IO, ul_debug(" writing info to multi-stream log"));
533
534 if (msgfmt) {
535 int rc;
536 va_start(ap, msgfmt);
537 rc = vsnprintf(msg, sizeof(msg), msgfmt, ap);
538 va_end(ap);
539 if (rc < 0)
540 *msg = '\0';;
541 }
542
543 if (*msg)
544 sz = fprintf(log->fp, "H 0 %s %s\n", name, msg);
545 else
546 sz = fprintf(log->fp, "H 0 %s\n", name);
547
548 return sz > 0 ? sz : 0;
549 }
550
551 static void die_if_link(struct script_control *ctl, const char *filename)
552 {
553 struct stat s;
554
555 if (ctl->force)
556 return;
557 if (lstat(filename, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1))
558 errx(EXIT_FAILURE,
559 _("output file `%s' is a link\n"
560 "Use --force if you really want to use it.\n"
561 "Program not started."), filename);
562 }
563
564 static void restore_tty(struct script_control *ctl, int mode)
565 {
566 struct termios rtt;
567
568 if (!ctl->isterm)
569 return;
570
571 rtt = ctl->attrs;
572 tcsetattr(STDIN_FILENO, mode, &rtt);
573 }
574
575 static void enable_rawmode_tty(struct script_control *ctl)
576 {
577 struct termios rtt;
578
579 if (!ctl->isterm)
580 return;
581
582 rtt = ctl->attrs;
583 cfmakeraw(&rtt);
584 rtt.c_lflag &= ~ECHO;
585 tcsetattr(STDIN_FILENO, TCSANOW, &rtt);
586 }
587
588 static void __attribute__((__noreturn__)) done_log(struct script_control *ctl, const char *msg)
589 {
590 int status;
591 size_t i;
592
593 DBG(MISC, ul_debug("done!"));
594
595 restore_tty(ctl, TCSADRAIN);
596
597 if (WIFSIGNALED(ctl->childstatus))
598 status = WTERMSIG(ctl->childstatus) + 0x80;
599 else
600 status = WEXITSTATUS(ctl->childstatus);
601
602
603 DBG(MISC, ul_debug(" status=%d", status));
604
605 /* close all output logs */
606 for (i = 0; i < ctl->out.nlogs; i++)
607 log_close(ctl, ctl->out.logs[i], msg, status);
608
609 /* close all input logs */
610 for (i = 0; i < ctl->in.nlogs; i++)
611 log_close(ctl, ctl->in.logs[i], msg, status);
612
613 if (!ctl->quiet)
614 printf(_("Script done.\n"));
615
616 #ifdef HAVE_LIBUTEMPTER
617 if (ctl->master >= 0)
618 utempter_remove_record(ctl->master);
619 #endif
620 kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
621 exit(ctl->rc_wanted ? status : EXIT_SUCCESS);
622 }
623
624 static void __attribute__((__noreturn__)) done(struct script_control *ctl)
625 {
626 done_log(ctl, NULL);
627 }
628
629 static void __attribute__((__noreturn__)) fail(struct script_control *ctl)
630 {
631 DBG(MISC, ul_debug("fail!"));
632 kill(0, SIGTERM);
633 done(ctl);
634 }
635
636 static void wait_for_child(struct script_control *ctl, int wait)
637 {
638 int status;
639 pid_t pid;
640 int options = wait ? 0 : WNOHANG;
641
642 DBG(MISC, ul_debug("waiting for child"));
643
644 while ((pid = wait3(&status, options, NULL)) > 0)
645 if (pid == ctl->child)
646 ctl->childstatus = status;
647 }
648
649 /* data from master to stdout */
650 static void write_output(struct script_control *ctl, char *obuf,
651 ssize_t bytes)
652 {
653 DBG(IO, ul_debug(" writing to output"));
654
655 if (write_all(STDOUT_FILENO, obuf, bytes)) {
656 DBG(IO, ul_debug(" writing output *failed*"));
657 warn(_("write failed"));
658 fail(ctl);
659 }
660 }
661
662 static int write_to_shell(struct script_control *ctl,
663 char *buf, size_t bufsz)
664 {
665 return write_all(ctl->master, buf, bufsz);
666 }
667
668 /*
669 * The script(1) is usually faster than shell, so it's a good idea to wait until
670 * the previous message has been already read by shell from slave before we
671 * write to master. This is necessary especially for EOF situation when we can
672 * send EOF to master before shell is fully initialized, to workaround this
673 * problem we wait until slave is empty. For example:
674 *
675 * echo "date" | script
676 *
677 * Unfortunately, the child (usually shell) can ignore stdin at all, so we
678 * don't wait forever to avoid dead locks...
679 *
680 * Note that script is primarily designed for interactive sessions as it
681 * maintains master+slave tty stuff within the session. Use pipe to write to
682 * script(1) and assume non-interactive (tee-like) behavior is NOT well
683 * supported.
684 */
685 static void write_eof_to_shell(struct script_control *ctl)
686 {
687 unsigned int tries = 0;
688 struct pollfd fds[] = {
689 { .fd = ctl->slave, .events = POLLIN }
690 };
691 char c = DEF_EOF;
692
693 DBG(IO, ul_debug(" waiting for empty slave"));
694 while (poll(fds, 1, 10) == 1 && tries < 8) {
695 DBG(IO, ul_debug(" slave is not empty"));
696 xusleep(250000);
697 tries++;
698 }
699 if (tries < 8)
700 DBG(IO, ul_debug(" slave is empty now"));
701
702 DBG(IO, ul_debug(" sending EOF to master"));
703 write_to_shell(ctl, &c, sizeof(char));
704 }
705
706 static void handle_io(struct script_control *ctl, int fd, int *eof)
707 {
708 char buf[BUFSIZ];
709 ssize_t bytes;
710 DBG(IO, ul_debug("%d FD active", fd));
711 *eof = 0;
712
713 /* read from active FD */
714 bytes = read(fd, buf, sizeof(buf));
715 if (bytes < 0) {
716 if (errno == EAGAIN || errno == EINTR)
717 return;
718 fail(ctl);
719 }
720
721 if (bytes == 0) {
722 *eof = 1;
723 return;
724 }
725
726 /* from stdin (user) to command */
727 if (fd == STDIN_FILENO) {
728 DBG(IO, ul_debug(" stdin --> master %zd bytes", bytes));
729
730 if (write_to_shell(ctl, buf, bytes)) {
731 warn(_("write failed"));
732 fail(ctl);
733 }
734 /* without sync write_output() will write both input &
735 * shell output that looks like double echoing */
736 fdatasync(ctl->master);
737 ctl->outsz += log_stream_activity(ctl, &ctl->in, buf, (size_t) bytes);
738
739 /* from command (master) to stdout and log */
740 } else if (fd == ctl->master) {
741 DBG(IO, ul_debug(" master --> stdout %zd bytes", bytes));
742 write_output(ctl, buf, bytes);
743 ctl->outsz += log_stream_activity(ctl, &ctl->out, buf, (size_t) bytes);
744 }
745
746 /* check output limit */
747 if (ctl->maxsz != 0 && ctl->outsz >= ctl->maxsz) {
748 if (!ctl->quiet)
749 printf(_("Script terminated, max output files size %"PRIu64" exceeded.\n"), ctl->maxsz);
750 DBG(IO, ul_debug("output size %"PRIu64", exceeded limit %"PRIu64, ctl->outsz, ctl->maxsz));
751 done_log(ctl, _("max output size exceeded"));
752 }
753 }
754
755 static void handle_signal(struct script_control *ctl, int fd)
756 {
757 struct signalfd_siginfo info;
758 ssize_t bytes;
759
760 DBG(SIGNAL, ul_debug("signal FD %d active", fd));
761
762 bytes = read(fd, &info, sizeof(info));
763 if (bytes != sizeof(info)) {
764 if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
765 return;
766 fail(ctl);
767 }
768
769 switch (info.ssi_signo) {
770 case SIGCHLD:
771 DBG(SIGNAL, ul_debug(" get signal SIGCHLD [ssi_code=%d, ssi_status=%d]",
772 info.ssi_code, info.ssi_status));
773 if (info.ssi_code == CLD_EXITED
774 || info.ssi_code == CLD_KILLED
775 || info.ssi_code == CLD_DUMPED) {
776 wait_for_child(ctl, 0);
777 ctl->poll_timeout = 10;
778
779 /* In case of ssi_code is CLD_TRAPPED, CLD_STOPPED, or CLD_CONTINUED */
780 } else if (info.ssi_status == SIGSTOP && ctl->child) {
781 DBG(SIGNAL, ul_debug(" child stop by SIGSTOP -- stop parent too"));
782 kill(getpid(), SIGSTOP);
783 DBG(SIGNAL, ul_debug(" resume"));
784 kill(ctl->child, SIGCONT);
785 }
786 return;
787 case SIGWINCH:
788 DBG(SIGNAL, ul_debug(" get signal SIGWINCH"));
789 if (ctl->isterm) {
790 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
791 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
792 log_signal(ctl, info.ssi_signo,
793 "ROWS=%d COLS=%d",
794 ctl->win.ws_row,
795 ctl->win.ws_col);
796 }
797 break;
798 case SIGTERM:
799 /* fallthrough */
800 case SIGINT:
801 /* fallthrough */
802 case SIGQUIT:
803 log_signal(ctl, info.ssi_signo, NULL);
804 DBG(SIGNAL, ul_debug(" get signal SIG{TERM,INT,QUIT}"));
805 fprintf(stderr, _("\nSession terminated.\n"));
806 /* Child termination is going to generate SIGCHILD (see above) */
807 kill(ctl->child, SIGTERM);
808 return;
809 default:
810 abort();
811 }
812 DBG(SIGNAL, ul_debug("signal handle on FD %d done", fd));
813 }
814
815 static void do_io(struct script_control *ctl)
816 {
817 int ret, eof = 0;
818 enum {
819 POLLFD_SIGNAL = 0,
820 POLLFD_MASTER,
821 POLLFD_STDIN
822
823 };
824 struct pollfd pfd[] = {
825 [POLLFD_SIGNAL] = { .fd = ctl->sigfd, .events = POLLIN | POLLERR | POLLHUP },
826 [POLLFD_MASTER] = { .fd = ctl->master, .events = POLLIN | POLLERR | POLLHUP },
827 [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP }
828 };
829
830 while (!ctl->die) {
831 size_t i;
832 int errsv;
833
834 DBG(POLL, ul_debug("calling poll()"));
835
836 /* wait for input or signal */
837 ret = poll(pfd, ARRAY_SIZE(pfd), ctl->poll_timeout);
838 errsv = errno;
839 DBG(POLL, ul_debug("poll() rc=%d", ret));
840
841 if (ret < 0) {
842 if (errsv == EAGAIN)
843 continue;
844 warn(_("poll failed"));
845 fail(ctl);
846 }
847 if (ret == 0) {
848 DBG(POLL, ul_debug("setting die=1"));
849 ctl->die = 1;
850 break;
851 }
852
853 for (i = 0; i < ARRAY_SIZE(pfd); i++) {
854 if (pfd[i].revents == 0)
855 continue;
856
857 DBG(POLL, ul_debug(" active pfd[%s].fd=%d %s %s %s",
858 i == POLLFD_STDIN ? "stdin" :
859 i == POLLFD_MASTER ? "master" :
860 i == POLLFD_SIGNAL ? "signal" : "???",
861 pfd[i].fd,
862 pfd[i].revents & POLLIN ? "POLLIN" : "",
863 pfd[i].revents & POLLHUP ? "POLLHUP" : "",
864 pfd[i].revents & POLLERR ? "POLLERR" : ""));
865 switch (i) {
866 case POLLFD_STDIN:
867 case POLLFD_MASTER:
868 /* data */
869 if (pfd[i].revents & POLLIN)
870 handle_io(ctl, pfd[i].fd, &eof);
871 /* EOF maybe detected by two ways:
872 * A) poll() return POLLHUP event after close()
873 * B) read() returns 0 (no data) */
874 if ((pfd[i].revents & POLLHUP) || eof) {
875 DBG(POLL, ul_debug(" ignore FD"));
876 pfd[i].fd = -1;
877 if (i == POLLFD_STDIN) {
878 write_eof_to_shell(ctl);
879 DBG(POLL, ul_debug(" ignore STDIN"));
880 }
881 }
882 continue;
883 case POLLFD_SIGNAL:
884 handle_signal(ctl, pfd[i].fd);
885 break;
886 }
887 }
888 }
889
890 DBG(POLL, ul_debug("poll() done"));
891
892 if (!ctl->die)
893 wait_for_child(ctl, 1);
894
895 done(ctl);
896 }
897
898 static void getslave(struct script_control *ctl)
899 {
900 #ifndef HAVE_LIBUTIL
901 ctl->line[strlen("/dev/")] = 't';
902 ctl->slave = open(ctl->line, O_RDWR | O_CLOEXEC);
903 if (ctl->slave < 0) {
904 warn(_("cannot open %s"), ctl->line);
905 fail(ctl);
906 }
907 if (ctl->isterm) {
908 tcsetattr(ctl->slave, TCSANOW, &ctl->attrs);
909 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
910 }
911 #endif
912 setsid();
913 ioctl(ctl->slave, TIOCSCTTY, 0);
914 }
915
916 /* don't use DBG() stuff here otherwise it will be in the typescript file */
917 static void __attribute__((__noreturn__)) do_shell(struct script_control *ctl)
918 {
919 char *shname;
920
921 getslave(ctl);
922
923 /* close things irrelevant for this process */
924 close(ctl->master);
925 close(ctl->sigfd);
926
927 dup2(ctl->slave, STDIN_FILENO);
928 dup2(ctl->slave, STDOUT_FILENO);
929 dup2(ctl->slave, STDERR_FILENO);
930 close(ctl->slave);
931
932 ctl->master = -1;
933
934 shname = strrchr(ctl->shell, '/');
935 if (shname)
936 shname++;
937 else
938 shname = ctl->shell;
939
940 sigprocmask(SIG_SETMASK, &ctl->sigorg, NULL);
941
942 /*
943 * When invoked from within /etc/csh.login, script spawns a csh shell
944 * that spawns programs that cannot be killed with a SIGTERM. This is
945 * because csh has a documented behavior wherein it disables all
946 * signals when processing the /etc/csh.* files.
947 *
948 * Let's restore the default behavior.
949 */
950 signal(SIGTERM, SIG_DFL);
951
952 if (access(ctl->shell, X_OK) == 0) {
953 if (ctl->command)
954 execl(ctl->shell, shname, "-c", ctl->command, NULL);
955 else
956 execl(ctl->shell, shname, "-i", NULL);
957 } else {
958 if (ctl->command)
959 execlp(shname, "-c", ctl->command, NULL);
960 else
961 execlp(shname, "-i", NULL);
962 }
963 warn(_("failed to execute %s"), ctl->shell);
964 fail(ctl);
965 }
966
967
968 static void getmaster(struct script_control *ctl)
969 {
970 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
971 int rc;
972
973 ctl->isterm = isatty(STDIN_FILENO);
974
975 if (ctl->isterm) {
976 if (tcgetattr(STDIN_FILENO, &ctl->attrs) != 0)
977 err(EXIT_FAILURE, _("failed to get terminal attributes"));
978 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
979 rc = openpty(&ctl->master, &ctl->slave, NULL, &ctl->attrs, &ctl->win);
980 } else
981 rc = openpty(&ctl->master, &ctl->slave, NULL, NULL, NULL);
982
983 if (rc < 0) {
984 warn(_("openpty failed"));
985 fail(ctl);
986 }
987 #else
988 char *pty, *bank, *cp;
989
990 ctl->isterm = isatty(STDIN_FILENO);
991
992 pty = &ctl->line[strlen("/dev/ptyp")];
993 for (bank = "pqrs"; *bank; bank++) {
994 ctl->line[strlen("/dev/pty")] = *bank;
995 *pty = '0';
996 if (access(ctl->line, F_OK) != 0)
997 break;
998 for (cp = "0123456789abcdef"; *cp; cp++) {
999 *pty = *cp;
1000 ctl->master = open(ctl->line, O_RDWR | O_CLOEXEC);
1001 if (ctl->master >= 0) {
1002 char *tp = &ctl->line[strlen("/dev/")];
1003 int ok;
1004
1005 /* verify slave side is usable */
1006 *tp = 't';
1007 ok = access(ctl->line, R_OK | W_OK) == 0;
1008 *tp = 'p';
1009 if (ok) {
1010 if (ctl->isterm) {
1011 tcgetattr(STDIN_FILENO, &ctl->attrs);
1012 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
1013 }
1014 return;
1015 }
1016 close(ctl->master);
1017 ctl->master = -1;
1018 }
1019 }
1020 }
1021 ctl->master = -1;
1022 warn(_("out of pty's"));
1023 fail(ctl);
1024 #endif /* not HAVE_LIBUTIL */
1025
1026 DBG(IO, ul_debug("master fd: %d", ctl->master));
1027 }
1028
1029 int main(int argc, char **argv)
1030 {
1031 struct script_control ctl = {
1032 #if !HAVE_LIBUTIL || !HAVE_PTY_H
1033 .line = "/dev/ptyXX",
1034 #endif
1035 .master = -1,
1036 .slave = -1,
1037
1038 .out = { .ident = 'O' },
1039 .in = { .ident = 'I' },
1040
1041 .poll_timeout = -1
1042 };
1043 int ch, format = 0;
1044 const char *outfile = NULL, *infile = NULL;
1045 const char *timingfile = NULL;
1046
1047 enum { FORCE_OPTION = CHAR_MAX + 1 };
1048
1049 static const struct option longopts[] = {
1050 {"append", no_argument, NULL, 'a'},
1051 {"command", required_argument, NULL, 'c'},
1052 {"return", no_argument, NULL, 'e'},
1053 {"flush", no_argument, NULL, 'f'},
1054 {"force", no_argument, NULL, FORCE_OPTION,},
1055 {"log-in", required_argument, NULL, 'I'},
1056 {"log-out", required_argument, NULL, 'O'},
1057 {"log-io", required_argument, NULL, 'B'},
1058 {"log-timing", required_argument, NULL, 'T'},
1059 {"logging-format", required_argument, NULL, 'm'},
1060 {"output-limit", required_argument, NULL, 'o'},
1061 {"quiet", no_argument, NULL, 'q'},
1062 {"timing", optional_argument, NULL, 't'},
1063 {"version", no_argument, NULL, 'V'},
1064 {"help", no_argument, NULL, 'h'},
1065 {NULL, 0, NULL, 0}
1066 };
1067 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
1068 { 'T', 't' },
1069 { 0 }
1070 };
1071 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
1072 setlocale(LC_ALL, "");
1073 /*
1074 * script -t prints time delays as floating point numbers. The example
1075 * program (scriptreplay) that we provide to handle this timing output
1076 * is a perl script, and does not handle numbers in locale format (not
1077 * even when "use locale;" is added). So, since these numbers are not
1078 * for human consumption, it seems easiest to set LC_NUMERIC here.
1079 */
1080 setlocale(LC_NUMERIC, "C");
1081 bindtextdomain(PACKAGE, LOCALEDIR);
1082 textdomain(PACKAGE);
1083 close_stdout_atexit();
1084
1085 script_init_debug();
1086
1087 while ((ch = getopt_long(argc, argv, "aB:c:efI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) {
1088
1089 err_exclusive_options(ch, longopts, excl, excl_st);
1090
1091 switch (ch) {
1092 case 'a':
1093 ctl.append = 1;
1094 break;
1095 case 'c':
1096 ctl.command = optarg;
1097 break;
1098 case 'e':
1099 ctl.rc_wanted = 1;
1100 break;
1101 case 'f':
1102 ctl.flush = 1;
1103 break;
1104 case FORCE_OPTION:
1105 ctl.force = 1;
1106 break;
1107 case 'B':
1108 log_associate(&ctl, &ctl.in, optarg, SCRIPT_FMT_RAW);
1109 log_associate(&ctl, &ctl.out, optarg, SCRIPT_FMT_RAW);
1110 infile = outfile = optarg;
1111 break;
1112 case 'I':
1113 log_associate(&ctl, &ctl.in, optarg, SCRIPT_FMT_RAW);
1114 infile = optarg;
1115 break;
1116 case 'O':
1117 log_associate(&ctl, &ctl.out, optarg, SCRIPT_FMT_RAW);
1118 outfile = optarg;
1119 break;
1120 case 'o':
1121 ctl.maxsz = strtosize_or_err(optarg, _("failed to parse output limit size"));
1122 break;
1123 case 'q':
1124 ctl.quiet = 1;
1125 break;
1126 case 'm':
1127 if (strcasecmp(optarg, "classic") == 0)
1128 format = SCRIPT_FMT_TIMING_SIMPLE;
1129 else if (strcasecmp(optarg, "advanced") == 0)
1130 format = SCRIPT_FMT_TIMING_MULTI;
1131 else
1132 errx(EXIT_FAILURE, _("unssuported logging format: '%s'"), optarg);
1133 break;
1134 case 't':
1135 if (optarg && *optarg == '=')
1136 optarg++;
1137 log_associate(&ctl, &ctl.out,
1138 optarg ? optarg : "/dev/stderr",
1139 SCRIPT_FMT_TIMING_SIMPLE);
1140 /* used for message only */
1141 timingfile = optarg ? optarg : "stderr";
1142 break;
1143 case 'T' :
1144 timingfile = optarg;
1145 break;
1146 case 'V':
1147 print_version(EXIT_SUCCESS);
1148 case 'h':
1149 usage();
1150 default:
1151 errtryhelp(EXIT_FAILURE);
1152 }
1153 }
1154 argc -= optind;
1155 argv += optind;
1156
1157 /* default if no --log-* specified */
1158 if (!outfile && !infile) {
1159 if (argc > 0)
1160 outfile = argv[0];
1161 else {
1162 die_if_link(&ctl, DEFAULT_TYPESCRIPT_FILENAME);
1163 outfile = DEFAULT_TYPESCRIPT_FILENAME;
1164 }
1165
1166 /* associate stdout with typescript file */
1167 log_associate(&ctl, &ctl.out, outfile, SCRIPT_FMT_RAW);
1168 }
1169
1170 if (timingfile) {
1171 /* the old SCRIPT_FMT_TIMING_SIMPLE should be used when
1172 * recoding output only (just for backward compatibility),
1173 * otherwise switch to new format. */
1174 if (!format)
1175 format = infile || (outfile && infile) ?
1176 SCRIPT_FMT_TIMING_MULTI :
1177 SCRIPT_FMT_TIMING_SIMPLE;
1178
1179 else if (format == SCRIPT_FMT_TIMING_SIMPLE && outfile && infile)
1180 errx(EXIT_FAILURE, _("log multiple streams is mutually "
1181 "exclusive with 'classic' format"));
1182 if (outfile)
1183 log_associate(&ctl, &ctl.out, timingfile, format);
1184 if (infile)
1185 log_associate(&ctl, &ctl.in, timingfile, format);
1186 }
1187
1188 ctl.shell = getenv("SHELL");
1189 if (ctl.shell == NULL)
1190 ctl.shell = _PATH_BSHELL;
1191
1192 getmaster(&ctl);
1193
1194 if (!ctl.quiet) {
1195 printf(_("Script started"));
1196 if (outfile)
1197 printf(_(", output log file is '%s'"), outfile);
1198 if (infile)
1199 printf(_(", input log file is '%s'"), infile);
1200 if (timingfile)
1201 printf(_(", timing file is '%s'"), timingfile);
1202 printf(_(".\n"));
1203 }
1204 enable_rawmode_tty(&ctl);
1205
1206 #ifdef HAVE_LIBUTEMPTER
1207 utempter_add_record(ctl.master, NULL);
1208 #endif
1209 /* setup signal handler */
1210 sigemptyset(&ctl.sigset);
1211 sigaddset(&ctl.sigset, SIGCHLD);
1212 sigaddset(&ctl.sigset, SIGWINCH);
1213 sigaddset(&ctl.sigset, SIGTERM);
1214 sigaddset(&ctl.sigset, SIGINT);
1215 sigaddset(&ctl.sigset, SIGQUIT);
1216
1217 /* block signals used for signalfd() to prevent the signals being
1218 * handled according to their default dispositions */
1219 sigprocmask(SIG_BLOCK, &ctl.sigset, &ctl.sigorg);
1220
1221 if ((ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC)) < 0)
1222 err(EXIT_FAILURE, _("cannot set signal handler"));
1223
1224 DBG(SIGNAL, ul_debug("signal fd=%d", ctl.sigfd));
1225
1226 fflush(stdout);
1227 ctl.child = fork();
1228
1229 switch (ctl.child) {
1230 case -1: /* error */
1231 warn(_("fork failed"));
1232 fail(&ctl);
1233 break;
1234 case 0: /* child */
1235 do_shell(&ctl);
1236 break;
1237 default: /* parent */
1238 start_logging(&ctl);
1239
1240 if (timingfile && format == SCRIPT_FMT_TIMING_MULTI) {
1241 if (ctl.isterm) {
1242 init_terminal_info(&ctl);
1243 log_info(&ctl, "TERM", ctl.ttytype);
1244 log_info(&ctl, "TTY", ctl.ttyname);
1245 log_info(&ctl, "COLUMNS", "%d", ctl.ttycols);
1246 log_info(&ctl, "LINES", "%d", ctl.ttylines);
1247 }
1248 log_info(&ctl, "SHELL", ctl.shell);
1249 log_info(&ctl, "TIMING_LOG", timingfile);
1250 if (outfile)
1251 log_info(&ctl, "OUTPUT_LOG", outfile);
1252 if (infile)
1253 log_info(&ctl, "INPUT_LOG", infile);
1254 }
1255 do_io(&ctl);
1256 break;
1257 }
1258
1259 /* should not happen, all used functions are non-return */
1260 return EXIT_FAILURE;
1261 }