]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/script.c
script: cleanup info logging
[thirdparty/util-linux.git] / term-utils / script.c
CommitLineData
6dbe3af9
KZ
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:
edc7e420
SK
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
6dbe3af9
KZ
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
66ee8158 34/*
b50945d4 35 * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
7eda085c 36 * - added Native Language Support
66ee8158
KZ
37 *
38 * 2000-07-30 Per Andreas Buer <per@linpro.no> - added "q"-option
26ed9fb8
CK
39 *
40 * 2014-05-30 Csaba Kos <csaba.kos@gmail.com>
41 * - fixed a rare deadlock after child termination
7eda085c
KZ
42 */
43
22853e4a
KZ
44#include <stdio.h>
45#include <stdlib.h>
46#include <paths.h>
47#include <time.h>
6dbe3af9
KZ
48#include <sys/stat.h>
49#include <termios.h>
50#include <sys/ioctl.h>
51#include <sys/time.h>
172b3c27 52#include <signal.h>
1f58c445 53#include <errno.h>
2b7ff0d9
ST
54#include <string.h>
55#include <getopt.h>
56#include <unistd.h>
8fb810ff 57#include <fcntl.h>
8fb810ff
SK
58#include <limits.h>
59#include <locale.h>
60#include <stddef.h>
3822032d
KZ
61#include <sys/wait.h>
62#include <poll.h>
cc1a88fb
SK
63#include <sys/signalfd.h>
64#include <assert.h>
c2f03da9 65#include <inttypes.h>
2b7ff0d9 66
cdd2a8c3 67#include "closestream.h"
7eda085c 68#include "nls.h"
91239874 69#include "c.h"
3822032d 70#include "ttyutils.h"
c6fca22e 71#include "all-io.h"
04639805 72#include "monotonic.h"
bdef362d 73#include "timeutils.h"
aefe9893 74#include "strutils.h"
596f4202 75#include "xalloc.h"
fc58044f 76#include "optutils.h"
fbed5507 77#include "signames.h"
6dbe3af9 78
a2b4dec5
KZ
79#include "debug.h"
80
2ba641e5 81static UL_DEBUG_DEFINE_MASK(script);
a2b4dec5
KZ
82UL_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
e11a5e63
SK
94#if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
95# include <pty.h>
5c36a0eb
KZ
96#endif
97
f0bc3fa0 98#ifdef HAVE_LIBUTEMPTER
e11a5e63 99# include <utempter.h>
f0bc3fa0
KZ
100#endif
101
7e5796c9 102#define DEFAULT_TYPESCRIPT_FILENAME "typescript"
05644dab 103
fec06a06
KZ
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 */
596f4202
KZ
113enum {
114 SCRIPT_FMT_RAW = 1, /* raw slave/master data */
daee4d95
KZ
115 SCRIPT_FMT_TIMING_SIMPLE, /* (classic) in format "<delta> <offset>" */
116 SCRIPT_FMT_TIMING_MULTI, /* (advanced) multiple streams in format "<type> <delta> <offset|etc> */
596f4202
KZ
117};
118
119struct script_log {
120 FILE *fp; /* file pointer (handler) */
121 int format; /* SCRIPT_FMT_* */
122 char *filename; /* on command line specified name */
88025c74 123 struct timeval oldtime; /* previous entry log time (SCRIPT_FMT_TIMING_* only) */
c1c2ee0b
KZ
124
125 unsigned int initialized : 1;
596f4202
KZ
126};
127
128struct script_stream {
9f822648 129 struct script_log **logs; /* logs where to write data from stream */
596f4202 130 size_t nlogs; /* number of logs */
c1c2ee0b 131 char ident; /* stream identifier */
596f4202
KZ
132};
133
edc7e420
SK
134struct script_control {
135 char *shell; /* shell to be executed */
3f19b85f 136 char *command; /* command to be executed */
596f4202
KZ
137 uint64_t outsz; /* current output files size */
138 uint64_t maxsz; /* maximum output files size */
139
edc7e420
SK
140 int master; /* pseudoterminal master file descriptor */
141 int slave; /* pseudoterminal slave file descriptor */
596f4202
KZ
142
143 struct script_stream out; /* output */
144 struct script_stream in; /* input */
145
3cecd176
KZ
146 struct script_log *siglog; /* log for signal entries */
147 struct script_log *infolog; /* log for info entries */
3cecd176
KZ
148
149 const char *ttyname;
150 const char *ttytype;
151 int ttycols;
152 int ttylines;
fbed5507 153
89a859d4 154 int poll_timeout; /* poll() timeout, used in end of execution */
edc7e420 155 pid_t child; /* child pid */
edc7e420 156 int childstatus; /* child process exit value */
3f19b85f 157 struct termios attrs; /* slave terminal runtime attributes */
edc7e420 158 struct winsize win; /* terminal window size */
9779651e 159#if !HAVE_LIBUTIL || !HAVE_PTY_H
8d6fdd2f 160 char *line; /* terminal line */
5c36a0eb 161#endif
edc7e420 162 unsigned int
3f19b85f 163 append:1, /* append output */
7e5796c9 164 rc_wanted:1, /* return child exit value */
3f19b85f
KZ
165 flush:1, /* flush after each write */
166 quiet:1, /* suppress most output */
3f19b85f 167 force:1, /* write output to links */
edc7e420 168 isterm:1, /* is child process running as terminal */
edc7e420 169 die:1; /* terminate program */
7e5796c9 170
cc1a88fb 171 sigset_t sigset; /* catch SIGCHLD and SIGWINCH with signalfd() */
d35ffe80 172 sigset_t sigorg; /* original signal mask */
cc1a88fb 173 int sigfd; /* file descriptor for signalfd() */
edc7e420 174};
1f58c445 175
596f4202
KZ
176static void restore_tty(struct script_control *ctl, int mode);
177static void __attribute__((__noreturn__)) fail(struct script_control *ctl);
3cecd176 178static uint64_t log_info(struct script_control *ctl, const char *name, const char *msgfmt, ...);
596f4202 179
a2b4dec5
KZ
180static void script_init_debug(void)
181{
a15dca2f 182 __UL_INIT_DEBUG_FROM_ENV(script, SCRIPT_DEBUG_, 0, SCRIPT_DEBUG);
a2b4dec5
KZ
183}
184
3cecd176
KZ
185static 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
6a40c65f
SK
195/*
196 * For tests we want to be able to control time output
197 */
198#ifdef TEST_SCRIPT
199static inline time_t script_time(time_t *t)
200{
201 const char *str = getenv("SCRIPT_TEST_SECOND_SINCE_EPOCH");
345208a5 202 int64_t sec;
6a40c65f 203
c2f03da9 204 if (!str || sscanf(str, "%"SCNi64, &sec) != 1)
345208a5
ID
205 return time(t);
206 if (t)
207 *t = (time_t)sec;
208 return (time_t)sec;
6a40c65f
SK
209}
210#else /* !TEST_SCRIPT */
211# define script_time(x) time(x)
212#endif
213
86be6a32 214static void __attribute__((__noreturn__)) usage(void)
3ff52639 215{
86be6a32 216 FILE *out = stdout;
db433bf7 217 fputs(USAGE_HEADER, out);
edc7e420 218 fprintf(out, _(" %s [options] [file]\n"), program_invocation_short_name);
87d6050b 219
451dbcfa
BS
220 fputs(USAGE_SEPARATOR, out);
221 fputs(_("Make a typescript of a terminal session.\n"), out);
222
db433bf7 223 fputs(USAGE_OPTIONS, out);
70062aad 224 fputs(_(" -I, --log-in <file> log stdin to file\n"), out);
ddbdb792 225 fputs(_(" -O, --log-out <file> log stdout to file (default)\n"), out);
c1c2ee0b 226 fputs(_(" -B, --log-io <file> log stdin and stdout to file\n"), out);
65dc24ab
KZ
227 fputs(USAGE_SEPARATOR, out);
228
fc58044f 229 fputs(_(" -T, --log-timing <file> log timing information to file\n"), out);
65dc24ab 230 fputs(_(" -t[<file>], --timing[=<file>] deprecated alias to -T (default file is stderr)\n"), out);
daee4d95 231 fputs(_(" -m, --logging-format <name> force to 'classic' or 'advanced' format\n"), out);
65dc24ab
KZ
232 fputs(USAGE_SEPARATOR, out);
233
234 fputs(_(" -a, --append append to the log file\n"), out);
c64963f8
KZ
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);
3ff52639 241
c64963f8
KZ
242 fputs(USAGE_SEPARATOR, out);
243 printf(USAGE_HELP_OPTIONS(31));
f45f3ec3 244 printf(USAGE_MAN_TAIL("script(1)"));
c64963f8 245
86be6a32 246 exit(EXIT_SUCCESS);
3ff52639
SK
247}
248
596f4202
KZ
249static struct script_log *get_log_by_name(struct script_stream *stream,
250 const char *name)
4d9b788d 251{
596f4202 252 size_t i;
4d9b788d 253
596f4202 254 for (i = 0; i < stream->nlogs; i++) {
9f822648
KZ
255 struct script_log *log = stream->logs[i];
256 if (strcmp(log->filename, name) == 0)
596f4202
KZ
257 return log;
258 }
259 return NULL;
260}
4d9b788d 261
596f4202
KZ
262static 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
c1c2ee0b
KZ
268 DBG(MISC, ul_debug("associate %s with stream", filename));
269
9f822648
KZ
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);
596f4202
KZ
279 if (!log) {
280 /* create a new log */
9f822648
KZ
281 log = xcalloc(1, sizeof(*log));
282 log->filename = xstrdup(filename);
596f4202
KZ
283 log->format = format;
284 }
4d9b788d 285
9f822648
KZ
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
fbed5507 292 /* remember where to write info about signals */
3cecd176
KZ
293 if (format == SCRIPT_FMT_TIMING_MULTI) {
294 if (!ctl->siglog)
295 ctl->siglog = log;
296 if (!ctl->infolog)
297 ctl->infolog = log;
3cecd176 298 }
fbed5507 299
596f4202
KZ
300 return log;
301}
4d9b788d 302
d805688a 303static void log_close(struct script_control *ctl __attribute__((unused)),
596f4202
KZ
304 struct script_log *log,
305 const char *msg,
306 int status)
307{
c1c2ee0b
KZ
308 if (!log->initialized)
309 return;
310
596f4202 311 DBG(MISC, ul_debug("closing %s", log->filename));
4d9b788d 312
596f4202
KZ
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);
4d9b788d 318
596f4202
KZ
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);
596f4202
KZ
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);
4d9b788d 332
596f4202 333 log->fp = NULL;
c1c2ee0b 334 log->initialized = 0;
4d9b788d
KZ
335}
336
596f4202
KZ
337static void log_start(struct script_control *ctl,
338 struct script_log *log)
6343ee8c 339{
c1c2ee0b
KZ
340 if (log->initialized)
341 return;
6343ee8c 342
596f4202 343 DBG(MISC, ul_debug("opening %s", log->filename));
6343ee8c 344
c1c2ee0b
KZ
345 assert(log->fp == NULL);
346
596f4202
KZ
347 /* open the log */
348 log->fp = fopen(log->filename,
349 ctl->append && log->format == SCRIPT_FMT_RAW ?
c1c2ee0b
KZ
350 "a" UL_CLOEXECSTR :
351 "w" UL_CLOEXECSTR);
596f4202
KZ
352 if (!log->fp) {
353 restore_tty(ctl, TCSANOW);
354 warn(_("cannot open %s"), log->filename);
355 fail(ctl);
356 }
6343ee8c 357
596f4202
KZ
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) {
3cecd176 369 init_terminal_info(ctl);
596f4202 370
3cecd176
KZ
371 if (ctl->ttytype)
372 fprintf(log->fp, "TERM=\"%s\" ", ctl->ttytype);
373 if (ctl->ttyname)
374 fprintf(log->fp, "TTY=\"%s\" ", ctl->ttyname);
596f4202 375
3cecd176 376 fprintf(log->fp, "COLUMNS=\"%d\" LINES=\"%d\"", ctl->ttycols, ctl->ttylines);
596f4202
KZ
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:
36b1369b 384 case SCRIPT_FMT_TIMING_MULTI:
596f4202
KZ
385 gettime_monotonic(&log->oldtime);
386 break;
387 }
c1c2ee0b
KZ
388
389 log->initialized = 1;
596f4202
KZ
390}
391
fd42a45b
KZ
392static 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
596f4202 405static size_t log_write(struct script_control *ctl,
c1c2ee0b 406 struct script_stream *stream,
596f4202
KZ
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 }
c1c2ee0b
KZ
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 }
596f4202
KZ
452 default:
453 break;
454 }
455
456 if (ctl->flush)
457 fflush(log->fp);
458
459 return bytes;
6343ee8c
KZ
460}
461
596f4202
KZ
462static 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++)
c1c2ee0b 471 outsz += log_write(ctl, stream, stream->logs[i], buf, bytes);
596f4202
KZ
472
473 return outsz;
474}
475
fbed5507
KZ
476static 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);
fbed5507
KZ
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)
fd42a45b 506 sz = fprintf(log->fp, "S %ld.%06ld SIG%s %s\n",
fbed5507
KZ
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}
596f4202 517
3cecd176
KZ
518static 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)
fd42a45b 544 sz = fprintf(log->fp, "H 0 %s %s\n", name, msg);
3cecd176
KZ
545 else
546 sz = fprintf(log->fp, "H 0 %s\n", name);
547
548 return sz > 0 ? sz : 0;
549}
550
596f4202 551static void die_if_link(struct script_control *ctl, const char *filename)
93af8d8b
SK
552{
553 struct stat s;
6dbe3af9 554
3f19b85f 555 if (ctl->force)
93af8d8b 556 return;
596f4202 557 if (lstat(filename, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1))
93af8d8b
SK
558 errx(EXIT_FAILURE,
559 _("output file `%s' is a link\n"
560 "Use --force if you really want to use it.\n"
596f4202 561 "Program not started."), filename);
93af8d8b 562}
fd4c1f63 563
8198052c
SK
564static 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
7fb65db1
KZ
575static 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
596f4202 588static void __attribute__((__noreturn__)) done_log(struct script_control *ctl, const char *msg)
93af8d8b 589{
596f4202
KZ
590 int status;
591 size_t i;
6343ee8c 592
a2b4dec5
KZ
593 DBG(MISC, ul_debug("done!"));
594
8198052c
SK
595 restore_tty(ctl, TCSADRAIN);
596
6343ee8c 597 if (WIFSIGNALED(ctl->childstatus))
596f4202 598 status = WTERMSIG(ctl->childstatus) + 0x80;
6343ee8c 599 else
596f4202
KZ
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++)
9f822648 607 log_close(ctl, ctl->out.logs[i], msg, status);
596f4202
KZ
608
609 /* close all input logs */
610 for (i = 0; i < ctl->in.nlogs; i++)
9f822648 611 log_close(ctl, ctl->in.logs[i], msg, status);
93df6a58 612
d805688a
KZ
613 if (!ctl->quiet)
614 printf(_("Script done.\n"));
615
f0bc3fa0 616#ifdef HAVE_LIBUTEMPTER
6ddf53a5
SK
617 if (ctl->master >= 0)
618 utempter_remove_record(ctl->master);
f0bc3fa0 619#endif
6ddf53a5 620 kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
596f4202 621 exit(ctl->rc_wanted ? status : EXIT_SUCCESS);
6343ee8c
KZ
622}
623
624static void __attribute__((__noreturn__)) done(struct script_control *ctl)
625{
626 done_log(ctl, NULL);
93af8d8b 627}
5c36a0eb 628
2ddadb5e 629static void __attribute__((__noreturn__)) fail(struct script_control *ctl)
edc7e420 630{
a2b4dec5 631 DBG(MISC, ul_debug("fail!"));
93af8d8b 632 kill(0, SIGTERM);
edc7e420 633 done(ctl);
6dbe3af9
KZ
634}
635
5860c45e 636static void wait_for_child(struct script_control *ctl, int wait)
93af8d8b
SK
637{
638 int status;
639 pid_t pid;
93af8d8b
SK
640 int options = wait ? 0 : WNOHANG;
641
a2b4dec5
KZ
642 DBG(MISC, ul_debug("waiting for child"));
643
b09feab9 644 while ((pid = wait3(&status, options, NULL)) > 0)
89a859d4 645 if (pid == ctl->child)
edc7e420 646 ctl->childstatus = status;
93af8d8b
SK
647}
648
596f4202 649/* data from master to stdout */
cf470183 650static void write_output(struct script_control *ctl, char *obuf,
04639805 651 ssize_t bytes)
93af8d8b 652{
a2b4dec5
KZ
653 DBG(IO, ul_debug(" writing to output"));
654
cf470183 655 if (write_all(STDOUT_FILENO, obuf, bytes)) {
54c6611d 656 DBG(IO, ul_debug(" writing output *failed*"));
cf470183
SK
657 warn(_("write failed"));
658 fail(ctl);
659 }
54c6611d
KZ
660}
661
b2bff066
KZ
662static int write_to_shell(struct script_control *ctl,
663 char *buf, size_t bufsz)
54c6611d 664{
b2bff066 665 return write_all(ctl->master, buf, bufsz);
54c6611d
KZ
666}
667
668/*
23e35eca
RM
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
54c6611d
KZ
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
b2bff066
KZ
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.
54c6611d 684 */
54c6611d
KZ
685static void write_eof_to_shell(struct script_control *ctl)
686{
b2bff066
KZ
687 unsigned int tries = 0;
688 struct pollfd fds[] = {
689 { .fd = ctl->slave, .events = POLLIN }
690 };
54c6611d
KZ
691 char c = DEF_EOF;
692
b2bff066
KZ
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
54c6611d
KZ
702 DBG(IO, ul_debug(" sending EOF to master"));
703 write_to_shell(ctl, &c, sizeof(char));
364cda48
KZ
704}
705
54c6611d 706static void handle_io(struct script_control *ctl, int fd, int *eof)
93af8d8b 707{
6ddf53a5 708 char buf[BUFSIZ];
a8896ad5 709 ssize_t bytes;
a2b4dec5 710 DBG(IO, ul_debug("%d FD active", fd));
54c6611d 711 *eof = 0;
a2b4dec5 712
da26af4e 713 /* read from active FD */
a8896ad5
SK
714 bytes = read(fd, buf, sizeof(buf));
715 if (bytes < 0) {
54c6611d 716 if (errno == EAGAIN || errno == EINTR)
a8896ad5
SK
717 return;
718 fail(ctl);
719 }
da26af4e 720
54c6611d 721 if (bytes == 0) {
7dbcd80e 722 *eof = 1;
54c6611d
KZ
723 return;
724 }
725
da26af4e
KZ
726 /* from stdin (user) to command */
727 if (fd == STDIN_FILENO) {
2e9418b7 728 DBG(IO, ul_debug(" stdin --> master %zd bytes", bytes));
a2b4dec5 729
54c6611d 730 if (write_to_shell(ctl, buf, bytes)) {
a8896ad5
SK
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);
596f4202 737 ctl->outsz += log_stream_activity(ctl, &ctl->in, buf, (size_t) bytes);
da26af4e 738
aefe9893 739 /* from command (master) to stdout and log */
a2b4dec5 740 } else if (fd == ctl->master) {
2e9418b7 741 DBG(IO, ul_debug(" master --> stdout %zd bytes", bytes));
04639805 742 write_output(ctl, buf, bytes);
596f4202
KZ
743 ctl->outsz += log_stream_activity(ctl, &ctl->out, buf, (size_t) bytes);
744 }
aefe9893 745
596f4202
KZ
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"));
a2b4dec5 752 }
a8896ad5
SK
753}
754
755static void handle_signal(struct script_control *ctl, int fd)
756{
757 struct signalfd_siginfo info;
758 ssize_t bytes;
759
a2b4dec5
KZ
760 DBG(SIGNAL, ul_debug("signal FD %d active", fd));
761
a8896ad5 762 bytes = read(fd, &info, sizeof(info));
d35ffe80 763 if (bytes != sizeof(info)) {
7dbcd80e 764 if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
d35ffe80
KZ
765 return;
766 fail(ctl);
767 }
da26af4e 768
a8896ad5
SK
769 switch (info.ssi_signo) {
770 case SIGCHLD:
27afe501
KZ
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) {
2e7a9227
KZ
776 wait_for_child(ctl, 0);
777 ctl->poll_timeout = 10;
27afe501
KZ
778
779 /* In case of ssi_code is CLD_TRAPPED, CLD_STOPPED, or CLD_CONTINUED */
2e7a9227
KZ
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 }
a8896ad5
SK
786 return;
787 case SIGWINCH:
b2bff066 788 DBG(SIGNAL, ul_debug(" get signal SIGWINCH"));
a8896ad5
SK
789 if (ctl->isterm) {
790 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
791 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
fbed5507 792 log_signal(ctl, info.ssi_signo,
3cecd176 793 "ROWS=%d COLS=%d",
fbed5507
KZ
794 ctl->win.ws_row,
795 ctl->win.ws_col);
a8896ad5
SK
796 }
797 break;
5860c45e
KZ
798 case SIGTERM:
799 /* fallthrough */
800 case SIGINT:
801 /* fallthrough */
802 case SIGQUIT:
fbed5507 803 log_signal(ctl, info.ssi_signo, NULL);
b2bff066 804 DBG(SIGNAL, ul_debug(" get signal SIG{TERM,INT,QUIT}"));
5860c45e
KZ
805 fprintf(stderr, _("\nSession terminated.\n"));
806 /* Child termination is going to generate SIGCHILD (see above) */
807 kill(ctl->child, SIGTERM);
808 return;
a8896ad5
SK
809 default:
810 abort();
811 }
27afe501 812 DBG(SIGNAL, ul_debug("signal handle on FD %d done", fd));
a8896ad5
SK
813}
814
815static void do_io(struct script_control *ctl)
816{
d88b739f 817 int ret, eof = 0;
da26af4e 818 enum {
54c6611d 819 POLLFD_SIGNAL = 0,
da26af4e 820 POLLFD_MASTER,
d88b739f 821 POLLFD_STDIN
54c6611d 822
da26af4e
KZ
823 };
824 struct pollfd pfd[] = {
0664d41d
SK
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 }
da26af4e
KZ
828 };
829
6ddf53a5 830 while (!ctl->die) {
da26af4e 831 size_t i;
7dbcd80e 832 int errsv;
da26af4e 833
a2b4dec5
KZ
834 DBG(POLL, ul_debug("calling poll()"));
835
cf470183 836 /* wait for input or signal */
d88b739f 837 ret = poll(pfd, ARRAY_SIZE(pfd), ctl->poll_timeout);
7dbcd80e 838 errsv = errno;
a2b4dec5
KZ
839 DBG(POLL, ul_debug("poll() rc=%d", ret));
840
cf470183 841 if (ret < 0) {
7dbcd80e 842 if (errsv == EAGAIN)
cf470183
SK
843 continue;
844 warn(_("poll failed"));
edc7e420 845 fail(ctl);
36c1d79b 846 }
a2b4dec5
KZ
847 if (ret == 0) {
848 DBG(POLL, ul_debug("setting die=1"));
89a859d4 849 ctl->die = 1;
5860c45e 850 break;
a2b4dec5 851 }
da26af4e 852
d88b739f 853 for (i = 0; i < ARRAY_SIZE(pfd); i++) {
cf470183
SK
854 if (pfd[i].revents == 0)
855 continue;
a2b4dec5 856
2e9418b7 857 DBG(POLL, ul_debug(" active pfd[%s].fd=%d %s %s %s",
54c6611d 858 i == POLLFD_STDIN ? "stdin" :
2e9418b7
KZ
859 i == POLLFD_MASTER ? "master" :
860 i == POLLFD_SIGNAL ? "signal" : "???",
861 pfd[i].fd,
54c6611d 862 pfd[i].revents & POLLIN ? "POLLIN" : "",
1200cfbf
KZ
863 pfd[i].revents & POLLHUP ? "POLLHUP" : "",
864 pfd[i].revents & POLLERR ? "POLLERR" : ""));
da26af4e
KZ
865 switch (i) {
866 case POLLFD_STDIN:
867 case POLLFD_MASTER:
54c6611d
KZ
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()
7dbcd80e 873 * B) read() returns 0 (no data) */
54c6611d
KZ
874 if ((pfd[i].revents & POLLHUP) || eof) {
875 DBG(POLL, ul_debug(" ignore FD"));
876 pfd[i].fd = -1;
54c6611d 877 if (i == POLLFD_STDIN) {
54c6611d 878 write_eof_to_shell(ctl);
b2bff066 879 DBG(POLL, ul_debug(" ignore STDIN"));
54c6611d
KZ
880 }
881 }
cf470183 882 continue;
da26af4e 883 case POLLFD_SIGNAL:
a8896ad5 884 handle_signal(ctl, pfd[i].fd);
da26af4e 885 break;
cf470183 886 }
968e632c 887 }
cf470183 888 }
a2b4dec5
KZ
889
890 DBG(POLL, ul_debug("poll() done"));
891
6ddf53a5 892 if (!ctl->die)
5860c45e 893 wait_for_child(ctl, 1);
6f3c9c34 894
6ddf53a5 895 done(ctl);
6dbe3af9
KZ
896}
897
edc7e420 898static void getslave(struct script_control *ctl)
93af8d8b
SK
899{
900#ifndef HAVE_LIBUTIL
edc7e420 901 ctl->line[strlen("/dev/")] = 't';
760e5e68 902 ctl->slave = open(ctl->line, O_RDWR | O_CLOEXEC);
edc7e420
SK
903 if (ctl->slave < 0) {
904 warn(_("cannot open %s"), ctl->line);
905 fail(ctl);
93af8d8b 906 }
edc7e420 907 if (ctl->isterm) {
3f19b85f 908 tcsetattr(ctl->slave, TCSANOW, &ctl->attrs);
edc7e420 909 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
93af8d8b
SK
910 }
911#endif
912 setsid();
edc7e420 913 ioctl(ctl->slave, TIOCSCTTY, 0);
93af8d8b
SK
914}
915
54c6611d 916/* don't use DBG() stuff here otherwise it will be in the typescript file */
d35ffe80 917static void __attribute__((__noreturn__)) do_shell(struct script_control *ctl)
93af8d8b 918{
df1dddf9
KZ
919 char *shname;
920
edc7e420 921 getslave(ctl);
4368e3e6
KZ
922
923 /* close things irrelevant for this process */
edc7e420 924 close(ctl->master);
d35ffe80 925 close(ctl->sigfd);
4368e3e6 926
edc7e420
SK
927 dup2(ctl->slave, STDIN_FILENO);
928 dup2(ctl->slave, STDOUT_FILENO);
929 dup2(ctl->slave, STDERR_FILENO);
930 close(ctl->slave);
df1dddf9 931
edc7e420 932 ctl->master = -1;
f0bc3fa0 933
edc7e420 934 shname = strrchr(ctl->shell, '/');
df1dddf9
KZ
935 if (shname)
936 shname++;
937 else
edc7e420 938 shname = ctl->shell;
df1dddf9 939
d35ffe80
KZ
940 sigprocmask(SIG_SETMASK, &ctl->sigorg, NULL);
941
a17f3264
KZ
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
ee312c65 945 * because csh has a documented behavior wherein it disables all
a17f3264
KZ
946 * signals when processing the /etc/csh.* files.
947 *
948 * Let's restore the default behavior.
949 */
950 signal(SIGTERM, SIG_DFL);
951
edc7e420 952 if (access(ctl->shell, X_OK) == 0) {
3f19b85f
KZ
953 if (ctl->command)
954 execl(ctl->shell, shname, "-c", ctl->command, NULL);
b4ff2f54 955 else
edc7e420 956 execl(ctl->shell, shname, "-i", NULL);
b4ff2f54 957 } else {
3f19b85f
KZ
958 if (ctl->command)
959 execlp(shname, "-c", ctl->command, NULL);
b4ff2f54
SK
960 else
961 execlp(shname, "-i", NULL);
962 }
edc7e420
SK
963 warn(_("failed to execute %s"), ctl->shell);
964 fail(ctl);
6dbe3af9
KZ
965}
966
6dbe3af9 967
edc7e420 968static void getmaster(struct script_control *ctl)
93af8d8b 969{
e11a5e63 970#if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
3822032d
KZ
971 int rc;
972
edc7e420 973 ctl->isterm = isatty(STDIN_FILENO);
3822032d 974
edc7e420 975 if (ctl->isterm) {
3f19b85f 976 if (tcgetattr(STDIN_FILENO, &ctl->attrs) != 0)
3822032d 977 err(EXIT_FAILURE, _("failed to get terminal attributes"));
edc7e420 978 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
3f19b85f 979 rc = openpty(&ctl->master, &ctl->slave, NULL, &ctl->attrs, &ctl->win);
3822032d 980 } else
edc7e420 981 rc = openpty(&ctl->master, &ctl->slave, NULL, NULL, NULL);
3822032d
KZ
982
983 if (rc < 0) {
360d5005 984 warn(_("openpty failed"));
edc7e420 985 fail(ctl);
5c36a0eb
KZ
986 }
987#else
6dbe3af9 988 char *pty, *bank, *cp;
6dbe3af9 989
edc7e420 990 ctl->isterm = isatty(STDIN_FILENO);
3822032d 991
edc7e420 992 pty = &ctl->line[strlen("/dev/ptyp")];
6dbe3af9 993 for (bank = "pqrs"; *bank; bank++) {
edc7e420 994 ctl->line[strlen("/dev/pty")] = *bank;
6dbe3af9 995 *pty = '0';
a4aeb5bd 996 if (access(ctl->line, F_OK) != 0)
6dbe3af9
KZ
997 break;
998 for (cp = "0123456789abcdef"; *cp; cp++) {
999 *pty = *cp;
760e5e68 1000 ctl->master = open(ctl->line, O_RDWR | O_CLOEXEC);
edc7e420
SK
1001 if (ctl->master >= 0) {
1002 char *tp = &ctl->line[strlen("/dev/")];
6dbe3af9
KZ
1003 int ok;
1004
1005 /* verify slave side is usable */
1006 *tp = 't';
edc7e420 1007 ok = access(ctl->line, R_OK | W_OK) == 0;
6dbe3af9
KZ
1008 *tp = 'p';
1009 if (ok) {
edc7e420 1010 if (ctl->isterm) {
3f19b85f 1011 tcgetattr(STDIN_FILENO, &ctl->attrs);
edc7e420 1012 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
3822032d 1013 }
6dbe3af9
KZ
1014 return;
1015 }
edc7e420
SK
1016 close(ctl->master);
1017 ctl->master = -1;
6dbe3af9
KZ
1018 }
1019 }
1020 }
edc7e420 1021 ctl->master = -1;
360d5005 1022 warn(_("out of pty's"));
edc7e420
SK
1023 fail(ctl);
1024#endif /* not HAVE_LIBUTIL */
54c6611d
KZ
1025
1026 DBG(IO, ul_debug("master fd: %d", ctl->master));
6dbe3af9
KZ
1027}
1028
93af8d8b
SK
1029int main(int argc, char **argv)
1030{
edc7e420
SK
1031 struct script_control ctl = {
1032#if !HAVE_LIBUTIL || !HAVE_PTY_H
1033 .line = "/dev/ptyXX",
1034#endif
1035 .master = -1,
596f4202
KZ
1036 .slave = -1,
1037
c1c2ee0b
KZ
1038 .out = { .ident = 'O' },
1039 .in = { .ident = 'I' },
1040
aefe9893 1041 .poll_timeout = -1
edc7e420 1042 };
c1c2ee0b 1043 int ch, format = 0;
70062aad 1044 const char *outfile = NULL, *infile = NULL;
d805688a 1045 const char *timingfile = NULL;
93af8d8b
SK
1046
1047 enum { FORCE_OPTION = CHAR_MAX + 1 };
1048
1049 static const struct option longopts[] = {
edc7e420
SK
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,},
70062aad 1055 {"log-in", required_argument, NULL, 'I'},
ddbdb792 1056 {"log-out", required_argument, NULL, 'O'},
c1c2ee0b 1057 {"log-io", required_argument, NULL, 'B'},
fc58044f 1058 {"log-timing", required_argument, NULL, 'T'},
daee4d95 1059 {"logging-format", required_argument, NULL, 'm'},
aefe9893 1060 {"output-limit", required_argument, NULL, 'o'},
edc7e420
SK
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}
93af8d8b 1066 };
fc58044f
KZ
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;
93af8d8b 1072 setlocale(LC_ALL, "");
b09feab9
SK
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");
93af8d8b
SK
1081 bindtextdomain(PACKAGE, LOCALEDIR);
1082 textdomain(PACKAGE);
2c308875 1083 close_stdout_atexit();
93af8d8b 1084
a2b4dec5
KZ
1085 script_init_debug();
1086
daee4d95 1087 while ((ch = getopt_long(argc, argv, "aB:c:efI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) {
fc58044f
KZ
1088
1089 err_exclusive_options(ch, longopts, excl, excl_st);
1090
edc7e420 1091 switch (ch) {
93af8d8b 1092 case 'a':
3f19b85f 1093 ctl.append = 1;
93af8d8b
SK
1094 break;
1095 case 'c':
3f19b85f 1096 ctl.command = optarg;
93af8d8b
SK
1097 break;
1098 case 'e':
7e5796c9 1099 ctl.rc_wanted = 1;
93af8d8b
SK
1100 break;
1101 case 'f':
3f19b85f 1102 ctl.flush = 1;
93af8d8b
SK
1103 break;
1104 case FORCE_OPTION:
3f19b85f 1105 ctl.force = 1;
93af8d8b 1106 break;
c1c2ee0b
KZ
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;
70062aad
KZ
1112 case 'I':
1113 log_associate(&ctl, &ctl.in, optarg, SCRIPT_FMT_RAW);
1114 infile = optarg;
1115 break;
ddbdb792 1116 case 'O':
70062aad
KZ
1117 log_associate(&ctl, &ctl.out, optarg, SCRIPT_FMT_RAW);
1118 outfile = optarg;
ddbdb792 1119 break;
aefe9893
FM
1120 case 'o':
1121 ctl.maxsz = strtosize_or_err(optarg, _("failed to parse output limit size"));
1122 break;
93af8d8b 1123 case 'q':
3f19b85f 1124 ctl.quiet = 1;
93af8d8b 1125 break;
daee4d95
KZ
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;
93af8d8b 1134 case 't':
4f7521d6
KZ
1135 if (optarg && *optarg == '=')
1136 optarg++;
596f4202
KZ
1137 log_associate(&ctl, &ctl.out,
1138 optarg ? optarg : "/dev/stderr",
1139 SCRIPT_FMT_TIMING_SIMPLE);
d805688a
KZ
1140 /* used for message only */
1141 timingfile = optarg ? optarg : "stderr";
93af8d8b 1142 break;
fc58044f 1143 case 'T' :
fc58044f
KZ
1144 timingfile = optarg;
1145 break;
93af8d8b 1146 case 'V':
2c308875 1147 print_version(EXIT_SUCCESS);
93af8d8b 1148 case 'h':
86be6a32 1149 usage();
93af8d8b 1150 default:
677ec86c 1151 errtryhelp(EXIT_FAILURE);
93af8d8b 1152 }
fc58044f 1153 }
93af8d8b
SK
1154 argc -= optind;
1155 argv += optind;
1156
70062aad
KZ
1157 /* default if no --log-* specified */
1158 if (!outfile && !infile) {
ddbdb792 1159 if (argc > 0)
70062aad
KZ
1160 outfile = argv[0];
1161 else {
ddbdb792 1162 die_if_link(&ctl, DEFAULT_TYPESCRIPT_FILENAME);
70062aad
KZ
1163 outfile = DEFAULT_TYPESCRIPT_FILENAME;
1164 }
596f4202 1165
70062aad
KZ
1166 /* associate stdout with typescript file */
1167 log_associate(&ctl, &ctl.out, outfile, SCRIPT_FMT_RAW);
1168 }
93af8d8b 1169
c1c2ee0b 1170 if (timingfile) {
d628e9cf
KZ
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. */
c1c2ee0b 1174 if (!format)
d628e9cf 1175 format = infile || (outfile && infile) ?
c1c2ee0b
KZ
1176 SCRIPT_FMT_TIMING_MULTI :
1177 SCRIPT_FMT_TIMING_SIMPLE;
daee4d95
KZ
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"));
c1c2ee0b
KZ
1182 if (outfile)
1183 log_associate(&ctl, &ctl.out, timingfile, format);
1184 if (infile)
1185 log_associate(&ctl, &ctl.in, timingfile, format);
1186 }
1187
edc7e420
SK
1188 ctl.shell = getenv("SHELL");
1189 if (ctl.shell == NULL)
1190 ctl.shell = _PATH_BSHELL;
93af8d8b 1191
edc7e420 1192 getmaster(&ctl);
3cecd176 1193
d805688a 1194 if (!ctl.quiet) {
70062aad
KZ
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"));
d805688a 1203 }
7fb65db1 1204 enable_rawmode_tty(&ctl);
93af8d8b
SK
1205
1206#ifdef HAVE_LIBUTEMPTER
edc7e420 1207 utempter_add_record(ctl.master, NULL);
5c36a0eb 1208#endif
cc1a88fb 1209 /* setup signal handler */
d35ffe80
KZ
1210 sigemptyset(&ctl.sigset);
1211 sigaddset(&ctl.sigset, SIGCHLD);
1212 sigaddset(&ctl.sigset, SIGWINCH);
5860c45e
KZ
1213 sigaddset(&ctl.sigset, SIGTERM);
1214 sigaddset(&ctl.sigset, SIGINT);
1215 sigaddset(&ctl.sigset, SIGQUIT);
d35ffe80
KZ
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);
a2b4dec5 1220
760e5e68 1221 if ((ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC)) < 0)
cc1a88fb 1222 err(EXIT_FAILURE, _("cannot set signal handler"));
93af8d8b 1223
a2b4dec5
KZ
1224 DBG(SIGNAL, ul_debug("signal fd=%d", ctl.sigfd));
1225
93af8d8b 1226 fflush(stdout);
edc7e420 1227 ctl.child = fork();
93af8d8b 1228
d35ffe80
KZ
1229 switch (ctl.child) {
1230 case -1: /* error */
93af8d8b 1231 warn(_("fork failed"));
edc7e420 1232 fail(&ctl);
d35ffe80
KZ
1233 break;
1234 case 0: /* child */
1235 do_shell(&ctl);
1236 break;
1237 default: /* parent */
fd42a45b
KZ
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 }
d35ffe80
KZ
1255 do_io(&ctl);
1256 break;
93af8d8b 1257 }
d35ffe80
KZ
1258
1259 /* should not happen, all used functions are non-return */
b09feab9 1260 return EXIT_FAILURE;
6dbe3af9 1261}