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
50 #include <sys/ioctl.h>
62 #include "closestream.h"
66 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
70 #ifdef HAVE_LIBUTEMPTER
71 # include <utempter.h>
74 #define DEFAULT_OUTPUT "typescript"
84 void dooutput(FILE *timingfd
);
100 #if !HAVE_LIBUTIL || !HAVE_PTY_H
101 char line
[] = "/dev/ptyXX";
115 die_if_link(char *fn
) {
120 if (lstat(fn
, &s
) == 0 && (S_ISLNK(s
.st_mode
) || s
.st_nlink
> 1))
122 _("output file `%s' is a link\n"
123 "Use --force if you really want to use it.\n"
124 "Program not started."), fn
);
127 static void __attribute__((__noreturn__
))
130 fputs(_("\nUsage:\n"), out
);
132 _(" %s [options] [file]\n"), program_invocation_short_name
);
134 fputs(_("\nOptions:\n"), out
);
135 fputs(_(" -a, --append append the output\n"
136 " -c, --command <command> run command rather than interactive shell\n"
137 " -e, --return return exit code of the child process\n"
138 " -f, --flush run flush after each write\n"
139 " --force use output file even when it is a link\n"
140 " -q, --quiet be quiet\n"
141 " -t, --timing[=<file>] output timing data to stderr (or to FILE)\n"
142 " -V, --version output version information and exit\n"
143 " -h, --help display this help and exit\n\n"), out
);
145 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
149 * script -t prints time delays as floating point numbers
150 * The example program (scriptreplay) that we provide to handle this
151 * timing output is a perl script, and does not handle numbers in
152 * locale format (not even when "use locale;" is added).
153 * So, since these numbers are not for human consumption, it seems
154 * easiest to set LC_NUMERIC here.
158 main(int argc
, char **argv
) {
159 sigset_t block_mask
, unblock_mask
;
163 FILE *timingfd
= stderr
;
165 enum { FORCE_OPTION
= CHAR_MAX
+ 1 };
167 static const struct option longopts
[] = {
168 { "append", no_argument
, NULL
, 'a' },
169 { "command", required_argument
, NULL
, 'c' },
170 { "return", no_argument
, NULL
, 'e' },
171 { "flush", no_argument
, NULL
, 'f' },
172 { "force", no_argument
, NULL
, FORCE_OPTION
, },
173 { "quiet", no_argument
, NULL
, 'q' },
174 { "timing", optional_argument
, NULL
, 't' },
175 { "version", no_argument
, NULL
, 'V' },
176 { "help", no_argument
, NULL
, 'h' },
180 setlocale(LC_ALL
, "");
181 setlocale(LC_NUMERIC
, "C"); /* see comment above */
182 bindtextdomain(PACKAGE
, LOCALEDIR
);
184 atexit(close_stdout
);
186 while ((ch
= getopt_long(argc
, argv
, "ac:efqt::Vh", longopts
, NULL
)) != -1)
208 if ((timingfd
= fopen(optarg
, "w")) == NULL
)
209 err(EXIT_FAILURE
, _("cannot open timing file %s"), optarg
);
213 printf(_("%s from %s\n"), program_invocation_short_name
,
230 fname
= DEFAULT_OUTPUT
;
233 if ((fscript
= fopen(fname
, aflg
? "a" : "w")) == NULL
) {
234 warn(_("open failed: %s"), fname
);
238 shell
= getenv("SHELL");
240 shell
= _PATH_BSHELL
;
244 printf(_("Script started, file is %s\n"), fname
);
247 #ifdef HAVE_LIBUTEMPTER
248 utempter_add_record(master
, NULL
);
250 /* setup SIGCHLD handler */
251 sigemptyset(&sa
.sa_mask
);
253 sa
.sa_handler
= finish
;
254 sigaction(SIGCHLD
, &sa
, NULL
);
256 /* init mask for SIGCHLD */
257 sigprocmask(SIG_SETMASK
, NULL
, &block_mask
);
258 sigaddset(&block_mask
, SIGCHLD
);
260 sigprocmask(SIG_SETMASK
, &block_mask
, &unblock_mask
);
262 sigprocmask(SIG_SETMASK
, &unblock_mask
, NULL
);
265 warn(_("fork failed"));
270 sigprocmask(SIG_SETMASK
, &block_mask
, NULL
);
271 subchild
= child
= fork();
272 sigprocmask(SIG_SETMASK
, &unblock_mask
, NULL
);
275 warn(_("fork failed"));
283 sa
.sa_handler
= resize
;
284 sigaction(SIGWINCH
, &sa
, NULL
);
288 if (close_stream(timingfd
) != 0)
289 errx(EXIT_FAILURE
, _("write error"));
298 if (close_stream(fscript
) != 0)
299 errx(EXIT_FAILURE
, _("write error"));
302 if ((cc
= read(STDIN_FILENO
, ibuf
, BUFSIZ
)) > 0) {
303 ssize_t wrt
= write(master
, ibuf
, cc
);
305 warn (_("write failed"));
309 else if (cc
< 0 && errno
== EINTR
&& resized
)
318 #include <sys/wait.h>
321 finish(int dummy
__attribute__ ((__unused__
))) {
325 while ((pid
= wait3(&status
, WNOHANG
, 0)) > 0)
327 childstatus
= status
;
333 resize(int dummy
__attribute__ ((__unused__
))) {
335 /* transmit window change information to the child */
336 ioctl(STDIN_FILENO
, TIOCGWINSZ
, (char *)&win
);
337 ioctl(slave
, TIOCSWINSZ
, (char *)&win
);
341 * Stop extremely silly gcc complaint on %c:
342 * warning: `%c' yields only last 2 digits of year in some locales
345 my_strftime(char *buf
, size_t len
, const char *fmt
, const struct tm
*tm
) {
346 strftime(buf
, len
, fmt
, tm
);
350 dooutput(FILE *timingfd
) {
355 double oldtime
=time(NULL
), newtime
;
364 tvec
= time((time_t *)NULL
);
365 my_strftime(obuf
, sizeof obuf
, "%c\n", localtime(&tvec
));
366 fprintf(fscript
, _("Script started on %s"), obuf
);
369 if (die
&& flgs
== 0) {
370 /* ..child is dead, but it doesn't mean that there is
371 * nothing in buffers.
373 flgs
= fcntl(master
, F_GETFL
, 0);
374 if (fcntl(master
, F_SETFL
, (flgs
| O_NONBLOCK
)) == -1)
378 gettimeofday(&tv
, NULL
);
381 cc
= read(master
, obuf
, sizeof (obuf
));
383 if (die
&& errno
== EINTR
&& cc
<= 0)
384 /* read() has been interrupted by SIGCHLD, try it again
391 newtime
= tv
.tv_sec
+ (double) tv
.tv_usec
/ 1000000;
392 fprintf(timingfd
, "%f %zd\n", newtime
- oldtime
, cc
);
395 wrt
= write(STDOUT_FILENO
, obuf
, cc
);
397 warn (_("write failed"));
400 fwrt
= fwrite(obuf
, 1, cc
, fscript
);
402 warn (_("cannot write script file"));
410 fcntl(master
, F_SETFL
, flgs
);
411 if (close_stream(timingfd
) != 0)
412 errx(EXIT_FAILURE
, _("write error"));
423 t
= open(_PATH_DEV_TTY
, O_RDWR
);
425 ioctl(t
, TIOCNOTTY
, (char *)0);
432 if (close_stream(fscript
) != 0)
433 errx(EXIT_FAILURE
, _("write error"));
434 dup2(slave
, STDIN_FILENO
);
435 dup2(slave
, STDOUT_FILENO
);
436 dup2(slave
, STDERR_FILENO
);
441 shname
= strrchr(shell
, '/');
448 * When invoked from within /etc/csh.login, script spawns a csh shell
449 * that spawns programs that cannot be killed with a SIGTERM. This is
450 * because csh has a documented behaviour wherein it disables all
451 * signals when processing the /etc/csh.* files.
453 * Let's restore the default behavior.
455 signal(SIGTERM
, SIG_DFL
);
458 execl(shell
, shname
, "-c", cflg
, NULL
);
460 execl(shell
, shname
, "-i", NULL
);
462 warn(_("failed to execute %s"), shell
);
472 rtt
.c_lflag
&= ~ECHO
;
473 tcsetattr(STDIN_FILENO
, TCSANOW
, &rtt
);
490 tvec
= time((time_t *)NULL
);
491 my_strftime(buf
, sizeof buf
, "%c\n", localtime(&tvec
));
492 fprintf(fscript
, _("\nScript done on %s"), buf
);
494 if (close_stream(fscript
) != 0)
495 errx(EXIT_FAILURE
, _("write error"));
500 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &tt
);
502 printf(_("Script done, file is %s\n"), fname
);
503 #ifdef HAVE_LIBUTEMPTER
505 utempter_remove_record(master
);
510 if (WIFSIGNALED(childstatus
))
511 exit(WTERMSIG(childstatus
) + 0x80);
513 exit(WEXITSTATUS(childstatus
));
520 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
521 tcgetattr(STDIN_FILENO
, &tt
);
522 ioctl(STDIN_FILENO
, TIOCGWINSZ
, (char *)&win
);
523 if (openpty(&master
, &slave
, NULL
, &tt
, &win
) < 0) {
524 warn(_("openpty failed"));
528 char *pty
, *bank
, *cp
;
531 pty
= &line
[strlen("/dev/ptyp")];
532 for (bank
= "pqrs"; *bank
; bank
++) {
533 line
[strlen("/dev/pty")] = *bank
;
535 if (stat(line
, &stb
) < 0)
537 for (cp
= "0123456789abcdef"; *cp
; cp
++) {
539 master
= open(line
, O_RDWR
);
541 char *tp
= &line
[strlen("/dev/")];
544 /* verify slave side is usable */
546 ok
= access(line
, R_OK
|W_OK
) == 0;
549 tcgetattr(STDIN_FILENO
, &tt
);
550 ioctl(STDIN_FILENO
, TIOCGWINSZ
,
560 warn(_("out of pty's"));
562 #endif /* not HAVE_LIBUTIL */
568 line
[strlen("/dev/")] = 't';
569 slave
= open(line
, O_RDWR
);
571 warn(_("open failed: %s"), line
);
574 tcsetattr(slave
, TCSANOW
, &tt
);
575 ioctl(slave
, TIOCSWINSZ
, (char *)&win
);
578 ioctl(slave
, TIOCSCTTY
, 0);