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