2 * Copyright (c) 1980 Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
35 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
36 * - added Native Language Support
38 * 2000-07-30 Per Andreas Buer <per@linpro.no> - added "q"-option
40 * 2014-05-30 Csaba Kos <csaba.kos@gmail.com>
41 * - fixed a rare deadlock after child termination
50 #include <sys/ioctl.h>
63 #include <sys/signalfd.h>
67 #include "closestream.h"
72 #include "monotonic.h"
73 #include "timeutils.h"
81 static UL_DEBUG_DEFINE_MASK(script
);
82 UL_DEBUG_DEFINE_MASKNAMES(script
) = UL_DEBUG_EMPTY_MASKNAMES
;
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
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)
94 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
98 #ifdef HAVE_LIBUTEMPTER
99 # include <utempter.h>
102 #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
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"
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.
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> */
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
;
126 unsigned int initialized
: 1;
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 */
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 */
141 int master
; /* pseudoterminal master file descriptor */
142 int slave
; /* pseudoterminal slave file descriptor */
144 struct script_stream out
; /* output */
145 struct script_stream in
; /* input */
147 struct script_log
*siglog
; /* log for signal entries */
148 struct script_log
*infolog
; /* log for info entries */
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 */
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 */
172 sigset_t sigset
; /* catch SIGCHLD and SIGWINCH with signalfd() */
173 sigset_t sigorg
; /* original signal mask */
174 int sigfd
; /* file descriptor for signalfd() */
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
, ...);
181 static void script_init_debug(void)
183 __UL_INIT_DEBUG_FROM_ENV(script
, SCRIPT_DEBUG_
, 0, SCRIPT_DEBUG
);
186 static void init_terminal_info(struct script_control
*ctl
)
188 if (ctl
->ttyname
|| !ctl
->isterm
)
189 return; /* already initialized */
191 get_terminal_dimension(&ctl
->ttycols
, &ctl
->ttylines
);
192 get_terminal_name(&ctl
->ttyname
, NULL
, NULL
);
193 get_terminal_type(&ctl
->ttytype
);
197 * For tests we want to be able to control time output
200 static inline time_t script_time(time_t *t
)
202 const char *str
= getenv("SCRIPT_TEST_SECOND_SINCE_EPOCH");
205 if (!str
|| sscanf(str
, "%"SCNi64
, &sec
) != 1)
211 #else /* !TEST_SCRIPT */
212 # define script_time(x) time(x)
215 static void __attribute__((__noreturn__
)) usage(void)
218 fputs(USAGE_HEADER
, out
);
219 fprintf(out
, _(" %s [options] [file]\n"), program_invocation_short_name
);
221 fputs(USAGE_SEPARATOR
, out
);
222 fputs(_("Make a typescript of a terminal session.\n"), out
);
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
);
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
);
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
);
243 fputs(USAGE_SEPARATOR
, out
);
244 printf(USAGE_HELP_OPTIONS(31));
245 printf(USAGE_MAN_TAIL("script(1)"));
250 static struct script_log
*get_log_by_name(struct script_stream
*stream
,
255 for (i
= 0; i
< stream
->nlogs
; i
++) {
256 struct script_log
*log
= stream
->logs
[i
];
257 if (strcmp(log
->filename
, name
) == 0)
263 static struct script_log
*log_associate(struct script_control
*ctl
,
264 struct script_stream
*stream
,
265 const char *filename
, int format
)
267 struct script_log
*log
;
269 DBG(MISC
, ul_debug("associate %s with stream", filename
));
275 log
= get_log_by_name(stream
, filename
);
277 return log
; /* already defined */
279 log
= get_log_by_name(stream
== &ctl
->out
? &ctl
->in
: &ctl
->out
, filename
);
281 /* create a new log */
282 log
= xcalloc(1, sizeof(*log
));
283 log
->filename
= xstrdup(filename
);
284 log
->format
= format
;
287 /* add log to the stream */
288 stream
->logs
= xrealloc(stream
->logs
,
289 (stream
->nlogs
+ 1) * sizeof(log
));
290 stream
->logs
[stream
->nlogs
] = log
;
293 /* remember where to write info about signals */
294 if (format
== SCRIPT_FMT_TIMING_MULTI
) {
304 static void log_close(struct script_control
*ctl
,
305 struct script_log
*log
,
309 if (!log
->initialized
)
312 DBG(MISC
, ul_debug("closing %s", log
->filename
));
314 switch (log
->format
) {
317 char buf
[FORMAT_TIMESTAMP_MAX
];
318 time_t tvec
= script_time((time_t *)NULL
);
320 strtime_iso(&tvec
, ISO_TIMESTAMP
, buf
, sizeof(buf
));
322 fprintf(log
->fp
, _("\nScript done on %s [<%s>]\n"), buf
, msg
);
324 fprintf(log
->fp
, _("\nScript done on %s [COMMAND_EXIT_CODE=\"%d\"]\n"), buf
, status
);
327 case SCRIPT_FMT_TIMING_MULTI
:
329 struct timeval now
, delta
;
331 gettime_monotonic(&now
);
332 timersub(&now
, &log
->starttime
, &delta
);
334 log_info(ctl
, "DURATION", "%ld.%06ld",
335 (long)delta
.tv_sec
, (long)delta
.tv_usec
);
336 log_info(ctl
, "EXIT_CODE", "%d", status
);
339 case SCRIPT_FMT_TIMING_SIMPLE
:
343 if (close_stream(log
->fp
) != 0)
344 err(EXIT_FAILURE
, "write failed: %s", log
->filename
);
347 log
->initialized
= 0;
350 static void log_start(struct script_control
*ctl
,
351 struct script_log
*log
)
353 if (log
->initialized
)
356 DBG(MISC
, ul_debug("opening %s", log
->filename
));
358 assert(log
->fp
== NULL
);
361 log
->fp
= fopen(log
->filename
,
362 ctl
->append
&& log
->format
== SCRIPT_FMT_RAW
?
366 restore_tty(ctl
, TCSANOW
);
367 warn(_("cannot open %s"), log
->filename
);
371 /* write header, etc. */
372 switch (log
->format
) {
375 char buf
[FORMAT_TIMESTAMP_MAX
];
376 time_t tvec
= script_time((time_t *)NULL
);
378 strtime_iso(&tvec
, ISO_TIMESTAMP
, buf
, sizeof(buf
));
379 fprintf(log
->fp
, _("Script started on %s ["), buf
);
382 init_terminal_info(ctl
);
385 fprintf(log
->fp
, "TERM=\"%s\" ", ctl
->ttytype
);
387 fprintf(log
->fp
, "TTY=\"%s\" ", ctl
->ttyname
);
389 fprintf(log
->fp
, "COLUMNS=\"%d\" LINES=\"%d\"", ctl
->ttycols
, ctl
->ttylines
);
391 fprintf(log
->fp
, _("<not executed on terminal>"));
393 fputs("]\n", log
->fp
);
396 case SCRIPT_FMT_TIMING_SIMPLE
:
397 case SCRIPT_FMT_TIMING_MULTI
:
398 gettime_monotonic(&log
->oldtime
);
399 gettime_monotonic(&log
->starttime
);
403 log
->initialized
= 1;
406 static void start_logging(struct script_control
*ctl
)
410 /* start all output logs */
411 for (i
= 0; i
< ctl
->out
.nlogs
; i
++)
412 log_start(ctl
, ctl
->out
.logs
[i
]);
414 /* start all input logs */
415 for (i
= 0; i
< ctl
->in
.nlogs
; i
++)
416 log_start(ctl
, ctl
->in
.logs
[i
]);
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
)
427 DBG(IO
, ul_debug(" writining %s", log
->filename
));
429 switch (log
->format
) {
431 if (fwrite_all(obuf
, 1, bytes
, log
->fp
)) {
432 warn(_("cannot write %s"), log
->filename
);
436 case SCRIPT_FMT_TIMING_SIMPLE
:
438 struct timeval now
, delta
;
441 DBG(IO
, ul_debug(" writing timing info"));
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
);
448 bytes
= sz
> 0 ? sz
: 0;
451 case SCRIPT_FMT_TIMING_MULTI
:
453 struct timeval now
, delta
;
456 DBG(IO
, ul_debug(" writing multi-stream timing info"));
458 gettime_monotonic(&now
);
459 timersub(&now
, &log
->oldtime
, &delta
);
460 sz
= fprintf(log
->fp
, "%c %ld.%06ld %zd\n",
462 (long)delta
.tv_sec
, (long)delta
.tv_usec
, bytes
);
464 bytes
= sz
> 0 ? sz
: 0;
476 static uint64_t log_stream_activity(
477 struct script_control
*ctl
,
478 struct script_stream
*stream
,
479 char *buf
, size_t bytes
)
484 for (i
= 0; i
< stream
->nlogs
; i
++)
485 outsz
+= log_write(ctl
, stream
, stream
->logs
[i
], buf
, bytes
);
490 static uint64_t log_signal(struct script_control
*ctl
, int signum
, char *msgfmt
, ...)
492 struct script_log
*log
;
493 struct timeval now
, delta
;
494 char msg
[BUFSIZ
] = {0};
504 assert(log
->format
== SCRIPT_FMT_TIMING_MULTI
);
505 DBG(IO
, ul_debug(" writing signal to multi-stream timing"));
507 gettime_monotonic(&now
);
508 timersub(&now
, &log
->oldtime
, &delta
);
512 va_start(ap
, msgfmt
);
513 rc
= vsnprintf(msg
, sizeof(msg
), msgfmt
, ap
);
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
);
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
));
529 return sz
> 0 ? sz
: 0;
532 static uint64_t log_info(struct script_control
*ctl
, const char *name
, const char *msgfmt
, ...)
534 struct script_log
*log
;
535 char msg
[BUFSIZ
] = {0};
545 assert(log
->format
== SCRIPT_FMT_TIMING_MULTI
);
546 DBG(IO
, ul_debug(" writing info to multi-stream log"));
550 va_start(ap
, msgfmt
);
551 rc
= vsnprintf(msg
, sizeof(msg
), msgfmt
, ap
);
558 sz
= fprintf(log
->fp
, "H %f %s %s\n", 0.0, name
, msg
);
560 sz
= fprintf(log
->fp
, "H %f %s\n", 0.0, name
);
562 return sz
> 0 ? sz
: 0;
565 static void die_if_link(struct script_control
*ctl
, const char *filename
)
571 if (lstat(filename
, &s
) == 0 && (S_ISLNK(s
.st_mode
) || s
.st_nlink
> 1))
573 _("output file `%s' is a link\n"
574 "Use --force if you really want to use it.\n"
575 "Program not started."), filename
);
578 static void restore_tty(struct script_control
*ctl
, int mode
)
586 tcsetattr(STDIN_FILENO
, mode
, &rtt
);
589 static void enable_rawmode_tty(struct script_control
*ctl
)
598 rtt
.c_lflag
&= ~ECHO
;
599 tcsetattr(STDIN_FILENO
, TCSANOW
, &rtt
);
602 static void __attribute__((__noreturn__
)) done_log(struct script_control
*ctl
, const char *msg
)
607 DBG(MISC
, ul_debug("done!"));
609 restore_tty(ctl
, TCSADRAIN
);
611 if (WIFSIGNALED(ctl
->childstatus
))
612 status
= WTERMSIG(ctl
->childstatus
) + 0x80;
614 status
= WEXITSTATUS(ctl
->childstatus
);
617 DBG(MISC
, ul_debug(" status=%d", status
));
619 /* close all output logs */
620 for (i
= 0; i
< ctl
->out
.nlogs
; i
++)
621 log_close(ctl
, ctl
->out
.logs
[i
], msg
, status
);
623 /* close all input logs */
624 for (i
= 0; i
< ctl
->in
.nlogs
; i
++)
625 log_close(ctl
, ctl
->in
.logs
[i
], msg
, status
);
628 printf(_("Script done.\n"));
630 #ifdef HAVE_LIBUTEMPTER
631 if (ctl
->master
>= 0)
632 utempter_remove_record(ctl
->master
);
634 kill(ctl
->child
, SIGTERM
); /* make sure we don't create orphans */
636 exit(ctl
->rc_wanted
? status
: EXIT_SUCCESS
);
639 static void __attribute__((__noreturn__
)) done(struct script_control
*ctl
)
644 static void __attribute__((__noreturn__
)) fail(struct script_control
*ctl
)
646 DBG(MISC
, ul_debug("fail!"));
651 static void wait_for_child(struct script_control
*ctl
, int wait
)
655 int options
= wait
? 0 : WNOHANG
;
657 DBG(MISC
, ul_debug("waiting for child"));
659 while ((pid
= wait3(&status
, options
, NULL
)) > 0)
660 if (pid
== ctl
->child
)
661 ctl
->childstatus
= status
;
664 /* data from master to stdout */
665 static void write_output(struct script_control
*ctl
, char *obuf
,
668 DBG(IO
, ul_debug(" writing to output"));
670 if (write_all(STDOUT_FILENO
, obuf
, bytes
)) {
671 DBG(IO
, ul_debug(" writing output *failed*"));
672 warn(_("write failed"));
677 static int write_to_shell(struct script_control
*ctl
,
678 char *buf
, size_t bufsz
)
680 return write_all(ctl
->master
, buf
, bufsz
);
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:
690 * echo "date" | script
692 * Unfortunately, the child (usually shell) can ignore stdin at all, so we
693 * don't wait forever to avoid dead locks...
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
700 static void write_eof_to_shell(struct script_control
*ctl
)
702 unsigned int tries
= 0;
703 struct pollfd fds
[] = {
704 { .fd
= ctl
->slave
, .events
= POLLIN
}
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"));
715 DBG(IO
, ul_debug(" slave is empty now"));
717 DBG(IO
, ul_debug(" sending EOF to master"));
718 write_to_shell(ctl
, &c
, sizeof(char));
721 static void handle_io(struct script_control
*ctl
, int fd
, int *eof
)
725 DBG(IO
, ul_debug("%d FD active", fd
));
728 /* read from active FD */
729 bytes
= read(fd
, buf
, sizeof(buf
));
731 if (errno
== EAGAIN
|| errno
== EINTR
)
741 /* from stdin (user) to command */
742 if (fd
== STDIN_FILENO
) {
743 DBG(IO
, ul_debug(" stdin --> master %zd bytes", bytes
));
745 if (write_to_shell(ctl
, buf
, bytes
)) {
746 warn(_("write failed"));
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
);
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
);
761 /* check output limit */
762 if (ctl
->maxsz
!= 0 && ctl
->outsz
>= ctl
->maxsz
) {
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"));
770 static void handle_signal(struct script_control
*ctl
, int fd
)
772 struct signalfd_siginfo info
;
775 DBG(SIGNAL
, ul_debug("signal FD %d active", fd
));
777 bytes
= read(fd
, &info
, sizeof(info
));
778 if (bytes
!= sizeof(info
)) {
779 if (bytes
< 0 && (errno
== EAGAIN
|| errno
== EINTR
))
784 switch (info
.ssi_signo
) {
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;
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
);
803 DBG(SIGNAL
, ul_debug(" get signal SIGWINCH"));
805 ioctl(STDIN_FILENO
, TIOCGWINSZ
, (char *)&ctl
->win
);
806 ioctl(ctl
->slave
, TIOCSWINSZ
, (char *)&ctl
->win
);
807 log_signal(ctl
, info
.ssi_signo
,
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
);
827 DBG(SIGNAL
, ul_debug("signal handle on FD %d done", fd
));
830 static void do_io(struct script_control
*ctl
)
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
}
849 DBG(POLL
, ul_debug("calling poll()"));
851 /* wait for input or signal */
852 ret
= poll(pfd
, ARRAY_SIZE(pfd
), ctl
->poll_timeout
);
854 DBG(POLL
, ul_debug("poll() rc=%d", ret
));
859 warn(_("poll failed"));
863 DBG(POLL
, ul_debug("setting die=1"));
868 for (i
= 0; i
< ARRAY_SIZE(pfd
); i
++) {
869 if (pfd
[i
].revents
== 0)
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" : "???",
877 pfd
[i
].revents
& POLLIN
? "POLLIN" : "",
878 pfd
[i
].revents
& POLLHUP
? "POLLHUP" : "",
879 pfd
[i
].revents
& POLLERR
? "POLLERR" : ""));
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"));
892 if (i
== POLLFD_STDIN
) {
893 write_eof_to_shell(ctl
);
894 DBG(POLL
, ul_debug(" ignore STDIN"));
899 handle_signal(ctl
, pfd
[i
].fd
);
905 DBG(POLL
, ul_debug("poll() done"));
908 wait_for_child(ctl
, 1);
913 static void getslave(struct script_control
*ctl
)
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
);
923 tcsetattr(ctl
->slave
, TCSANOW
, &ctl
->attrs
);
924 ioctl(ctl
->slave
, TIOCSWINSZ
, (char *)&ctl
->win
);
928 ioctl(ctl
->slave
, TIOCSCTTY
, 0);
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
)
938 /* close things irrelevant for this process */
942 dup2(ctl
->slave
, STDIN_FILENO
);
943 dup2(ctl
->slave
, STDOUT_FILENO
);
944 dup2(ctl
->slave
, STDERR_FILENO
);
949 shname
= strrchr(ctl
->shell
, '/');
955 sigprocmask(SIG_SETMASK
, &ctl
->sigorg
, NULL
);
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.
963 * Let's restore the default behavior.
965 signal(SIGTERM
, SIG_DFL
);
967 if (access(ctl
->shell
, X_OK
) == 0) {
969 execl(ctl
->shell
, shname
, "-c", ctl
->command
, NULL
);
971 execl(ctl
->shell
, shname
, "-i", NULL
);
974 execlp(shname
, "-c", ctl
->command
, NULL
);
976 execlp(shname
, "-i", NULL
);
978 warn(_("failed to execute %s"), ctl
->shell
);
983 static void getmaster(struct script_control
*ctl
)
985 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
988 ctl
->isterm
= isatty(STDIN_FILENO
);
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
);
996 rc
= openpty(&ctl
->master
, &ctl
->slave
, NULL
, NULL
, NULL
);
999 warn(_("openpty failed"));
1003 char *pty
, *bank
, *cp
;
1005 ctl
->isterm
= isatty(STDIN_FILENO
);
1007 pty
= &ctl
->line
[strlen("/dev/ptyp")];
1008 for (bank
= "pqrs"; *bank
; bank
++) {
1009 ctl
->line
[strlen("/dev/pty")] = *bank
;
1011 if (access(ctl
->line
, F_OK
) != 0)
1013 for (cp
= "0123456789abcdef"; *cp
; cp
++) {
1015 ctl
->master
= open(ctl
->line
, O_RDWR
| O_CLOEXEC
);
1016 if (ctl
->master
>= 0) {
1017 char *tp
= &ctl
->line
[strlen("/dev/")];
1020 /* verify slave side is usable */
1022 ok
= access(ctl
->line
, R_OK
| W_OK
) == 0;
1026 tcgetattr(STDIN_FILENO
, &ctl
->attrs
);
1027 ioctl(STDIN_FILENO
, TIOCGWINSZ
, (char *)&ctl
->win
);
1037 warn(_("out of pty's"));
1039 #endif /* not HAVE_LIBUTIL */
1041 DBG(IO
, ul_debug("master fd: %d", ctl
->master
));
1044 int main(int argc
, char **argv
)
1046 struct script_control ctl
= {
1047 #if !HAVE_LIBUTIL || !HAVE_PTY_H
1048 .line
= "/dev/ptyXX",
1053 .out
= { .ident
= 'O' },
1054 .in
= { .ident
= 'I' },
1059 const char *outfile
= NULL
, *infile
= NULL
;
1060 const char *timingfile
= NULL
;
1062 enum { FORCE_OPTION
= CHAR_MAX
+ 1 };
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'},
1082 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
1086 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
1087 setlocale(LC_ALL
, "");
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.
1095 setlocale(LC_NUMERIC
, "C");
1096 bindtextdomain(PACKAGE
, LOCALEDIR
);
1097 textdomain(PACKAGE
);
1098 close_stdout_atexit();
1100 script_init_debug();
1102 while ((ch
= getopt_long(argc
, argv
, "aB:c:efI:O:o:qm:T:t::Vh", longopts
, NULL
)) != -1) {
1104 err_exclusive_options(ch
, longopts
, excl
, excl_st
);
1111 ctl
.command
= optarg
;
1123 log_associate(&ctl
, &ctl
.in
, optarg
, SCRIPT_FMT_RAW
);
1124 log_associate(&ctl
, &ctl
.out
, optarg
, SCRIPT_FMT_RAW
);
1125 infile
= outfile
= optarg
;
1128 log_associate(&ctl
, &ctl
.in
, optarg
, SCRIPT_FMT_RAW
);
1132 log_associate(&ctl
, &ctl
.out
, optarg
, SCRIPT_FMT_RAW
);
1136 ctl
.maxsz
= strtosize_or_err(optarg
, _("failed to parse output limit size"));
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
;
1147 errx(EXIT_FAILURE
, _("unssuported logging format: '%s'"), optarg
);
1150 if (optarg
&& *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";
1159 timingfile
= optarg
;
1162 print_version(EXIT_SUCCESS
);
1166 errtryhelp(EXIT_FAILURE
);
1172 /* default if no --log-* specified */
1173 if (!outfile
&& !infile
) {
1177 die_if_link(&ctl
, DEFAULT_TYPESCRIPT_FILENAME
);
1178 outfile
= DEFAULT_TYPESCRIPT_FILENAME
;
1181 /* associate stdout with typescript file */
1182 log_associate(&ctl
, &ctl
.out
, outfile
, SCRIPT_FMT_RAW
);
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. */
1190 format
= infile
|| (outfile
&& infile
) ?
1191 SCRIPT_FMT_TIMING_MULTI
:
1192 SCRIPT_FMT_TIMING_SIMPLE
;
1194 else if (format
== SCRIPT_FMT_TIMING_SIMPLE
&& outfile
&& infile
)
1195 errx(EXIT_FAILURE
, _("log multiple streams is mutually "
1196 "exclusive with 'classic' format"));
1198 log_associate(&ctl
, &ctl
.out
, timingfile
, format
);
1200 log_associate(&ctl
, &ctl
.in
, timingfile
, format
);
1203 ctl
.shell
= getenv("SHELL");
1204 if (ctl
.shell
== NULL
)
1205 ctl
.shell
= _PATH_BSHELL
;
1210 printf(_("Script started"));
1212 printf(_(", output log file is '%s'"), outfile
);
1214 printf(_(", input log file is '%s'"), infile
);
1216 printf(_(", timing file is '%s'"), timingfile
);
1219 enable_rawmode_tty(&ctl
);
1221 #ifdef HAVE_LIBUTEMPTER
1222 utempter_add_record(ctl
.master
, NULL
);
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
);
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
);
1236 if ((ctl
.sigfd
= signalfd(-1, &ctl
.sigset
, SFD_CLOEXEC
)) < 0)
1237 err(EXIT_FAILURE
, _("cannot set signal handler"));
1239 DBG(SIGNAL
, ul_debug("signal fd=%d", ctl
.sigfd
));
1244 switch (ctl
.child
) {
1245 case -1: /* error */
1246 warn(_("fork failed"));
1252 default: /* parent */
1253 start_logging(&ctl
);
1255 if (timingfile
&& format
== SCRIPT_FMT_TIMING_MULTI
) {
1256 char buf
[FORMAT_TIMESTAMP_MAX
];
1257 time_t tvec
= script_time((time_t *)NULL
);
1259 strtime_iso(&tvec
, ISO_TIMESTAMP
, buf
, sizeof(buf
));
1260 log_info(&ctl
, "START_TIME", buf
);
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
);
1269 log_info(&ctl
, "SHELL", ctl
.shell
);
1270 log_info(&ctl
, "TIMING_LOG", timingfile
);
1272 log_info(&ctl
, "OUTPUT_LOG", outfile
);
1274 log_info(&ctl
, "INPUT_LOG", infile
);
1280 /* should not happen, all used functions are non-return */
1281 return EXIT_FAILURE
;