]> git.ipfire.org Git - thirdparty/util-linux.git/blob - term-utils/script.c
misc: cosmetics, remove argument from usage(FILE*)
[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
75 #include "debug.h"
76
77 static UL_DEBUG_DEFINE_MASK(script);
78 UL_DEBUG_DEFINE_MASKNAMES(script) = UL_DEBUG_EMPTY_MASKNAMES;
79
80 #define SCRIPT_DEBUG_INIT (1 << 1)
81 #define SCRIPT_DEBUG_POLL (1 << 2)
82 #define SCRIPT_DEBUG_SIGNAL (1 << 3)
83 #define SCRIPT_DEBUG_IO (1 << 4)
84 #define SCRIPT_DEBUG_MISC (1 << 5)
85 #define SCRIPT_DEBUG_ALL 0xFFFF
86
87 #define DBG(m, x) __UL_DBG(script, SCRIPT_DEBUG_, m, x)
88 #define ON_DBG(m, x) __UL_DBG_CALL(script, SCRIPT_DEBUG_, m, x)
89
90 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
91 # include <pty.h>
92 #endif
93
94 #ifdef HAVE_LIBUTEMPTER
95 # include <utempter.h>
96 #endif
97
98 #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
99
100 struct script_control {
101 char *shell; /* shell to be executed */
102 char *command; /* command to be executed */
103 char *fname; /* output file path */
104 FILE *typescriptfp; /* output file pointer */
105 char *tname; /* timing file path */
106 FILE *timingfp; /* timing file pointer */
107 struct timeval oldtime; /* previous write or command start time */
108 int master; /* pseudoterminal master file descriptor */
109 int slave; /* pseudoterminal slave file descriptor */
110 int poll_timeout; /* poll() timeout, used in end of execution */
111 pid_t child; /* child pid */
112 int childstatus; /* child process exit value */
113 struct termios attrs; /* slave terminal runtime attributes */
114 struct winsize win; /* terminal window size */
115 #if !HAVE_LIBUTIL || !HAVE_PTY_H
116 char *line; /* terminal line */
117 #endif
118 unsigned int
119 append:1, /* append output */
120 rc_wanted:1, /* return child exit value */
121 flush:1, /* flush after each write */
122 quiet:1, /* suppress most output */
123 timing:1, /* include timing file */
124 force:1, /* write output to links */
125 isterm:1, /* is child process running as terminal */
126 die:1; /* terminate program */
127
128 sigset_t sigset; /* catch SIGCHLD and SIGWINCH with signalfd() */
129 sigset_t sigorg; /* original signal mask */
130 int sigfd; /* file descriptor for signalfd() */
131 };
132
133 static void script_init_debug(void)
134 {
135 __UL_INIT_DEBUG(script, SCRIPT_DEBUG_, 0, SCRIPT_DEBUG);
136 }
137
138 /*
139 * For tests we want to be able to control time output
140 */
141 #ifdef TEST_SCRIPT
142 static inline time_t script_time(time_t *t)
143 {
144 const char *str = getenv("SCRIPT_TEST_SECOND_SINCE_EPOCH");
145 int64_t sec;
146
147 if (!str || sscanf(str, "%"SCNi64, &sec) != 1)
148 return time(t);
149 if (t)
150 *t = (time_t)sec;
151 return (time_t)sec;
152 }
153 #else /* !TEST_SCRIPT */
154 # define script_time(x) time(x)
155 #endif
156
157 static void __attribute__((__noreturn__)) usage(void)
158 {
159 FILE *out = stdout;
160 fputs(USAGE_HEADER, out);
161 fprintf(out, _(" %s [options] [file]\n"), program_invocation_short_name);
162
163 fputs(USAGE_SEPARATOR, out);
164 fputs(_("Make a typescript of a terminal session.\n"), out);
165
166 fputs(USAGE_OPTIONS, out);
167 fputs(_(" -a, --append append the output\n"
168 " -c, --command <command> run command rather than interactive shell\n"
169 " -e, --return return exit code of the child process\n"
170 " -f, --flush run flush after each write\n"
171 " --force use output file even when it is a link\n"
172 " -q, --quiet be quiet\n"
173 " -t, --timing[=<file>] output timing data to stderr (or to FILE)\n"
174 " -V, --version output version information and exit\n"
175 " -h, --help display this help and exit\n\n"), out);
176
177 fprintf(out, USAGE_MAN_TAIL("script(1)"));
178 exit(EXIT_SUCCESS);
179 }
180
181 static void die_if_link(const struct script_control *ctl)
182 {
183 struct stat s;
184
185 if (ctl->force)
186 return;
187 if (lstat(ctl->fname, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1))
188 errx(EXIT_FAILURE,
189 _("output file `%s' is a link\n"
190 "Use --force if you really want to use it.\n"
191 "Program not started."), ctl->fname);
192 }
193
194 static void __attribute__((__noreturn__)) done(struct script_control *ctl)
195 {
196 DBG(MISC, ul_debug("done!"));
197
198 if (ctl->isterm)
199 tcsetattr(STDIN_FILENO, TCSADRAIN, &ctl->attrs);
200 if (!ctl->quiet && ctl->typescriptfp)
201 printf(_("Script done, file is %s\n"), ctl->fname);
202 #ifdef HAVE_LIBUTEMPTER
203 if (ctl->master >= 0)
204 utempter_remove_record(ctl->master);
205 #endif
206 kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
207
208 if (ctl->timingfp && close_stream(ctl->timingfp) != 0)
209 err(EXIT_FAILURE, "write failed: %s", ctl->tname);
210 if (ctl->typescriptfp && close_stream(ctl->typescriptfp) != 0)
211 err(EXIT_FAILURE, "write failed: %s", ctl->fname);
212
213 if (ctl->rc_wanted) {
214 if (WIFSIGNALED(ctl->childstatus))
215 exit(WTERMSIG(ctl->childstatus) + 0x80);
216 else
217 exit(WEXITSTATUS(ctl->childstatus));
218 }
219 exit(EXIT_SUCCESS);
220 }
221
222 static void __attribute__((__noreturn__)) fail(struct script_control *ctl)
223 {
224 DBG(MISC, ul_debug("fail!"));
225 kill(0, SIGTERM);
226 done(ctl);
227 }
228
229 static void wait_for_child(struct script_control *ctl, int wait)
230 {
231 int status;
232 pid_t pid;
233 int options = wait ? 0 : WNOHANG;
234
235 DBG(MISC, ul_debug("waiting for child"));
236
237 while ((pid = wait3(&status, options, NULL)) > 0)
238 if (pid == ctl->child)
239 ctl->childstatus = status;
240 }
241
242 static void write_output(struct script_control *ctl, char *obuf,
243 ssize_t bytes)
244 {
245 DBG(IO, ul_debug(" writing output"));
246
247 if (ctl->timing && ctl->timingfp) {
248 struct timeval now, delta;
249
250 DBG(IO, ul_debug(" writing timing info"));
251
252 gettime_monotonic(&now);
253 timersub(&now, &ctl->oldtime, &delta);
254 fprintf(ctl->timingfp, "%ld.%06ld %zd\n",
255 (long)delta.tv_sec, (long)delta.tv_usec, bytes);
256 if (ctl->flush)
257 fflush(ctl->timingfp);
258 ctl->oldtime = now;
259 }
260
261 DBG(IO, ul_debug(" writing to script file"));
262
263 if (fwrite_all(obuf, 1, bytes, ctl->typescriptfp)) {
264 warn(_("cannot write script file"));
265 fail(ctl);
266 }
267 if (ctl->flush)
268 fflush(ctl->typescriptfp);
269
270 DBG(IO, ul_debug(" writing to output"));
271
272 if (write_all(STDOUT_FILENO, obuf, bytes)) {
273 DBG(IO, ul_debug(" writing output *failed*"));
274 warn(_("write failed"));
275 fail(ctl);
276 }
277
278 DBG(IO, ul_debug(" writing output *done*"));
279 }
280
281 static int write_to_shell(struct script_control *ctl,
282 char *buf, size_t bufsz)
283 {
284 return write_all(ctl->master, buf, bufsz);
285 }
286
287 /*
288 * The script(1) is usually faster than shell, so it's a good idea to wait until
289 * the previous message has been already read by shell from slave before we
290 * write to master. This is necessary especially for EOF situation when we can
291 * send EOF to master before shell is fully initialized, to workaround this
292 * problem we wait until slave is empty. For example:
293 *
294 * echo "date" | script
295 *
296 * Unfortunately, the child (usually shell) can ignore stdin at all, so we
297 * don't wait forever to avoid dead locks...
298 *
299 * Note that script is primarily designed for interactive sessions as it
300 * maintains master+slave tty stuff within the session. Use pipe to write to
301 * script(1) and assume non-interactive (tee-like) behavior is NOT well
302 * supported.
303 */
304 static void write_eof_to_shell(struct script_control *ctl)
305 {
306 unsigned int tries = 0;
307 struct pollfd fds[] = {
308 { .fd = ctl->slave, .events = POLLIN }
309 };
310 char c = DEF_EOF;
311
312 DBG(IO, ul_debug(" waiting for empty slave"));
313 while (poll(fds, 1, 10) == 1 && tries < 8) {
314 DBG(IO, ul_debug(" slave is not empty"));
315 xusleep(250000);
316 tries++;
317 }
318 if (tries < 8)
319 DBG(IO, ul_debug(" slave is empty now"));
320
321 DBG(IO, ul_debug(" sending EOF to master"));
322 write_to_shell(ctl, &c, sizeof(char));
323 }
324
325 static void handle_io(struct script_control *ctl, int fd, int *eof)
326 {
327 char buf[BUFSIZ];
328 ssize_t bytes;
329
330 DBG(IO, ul_debug("%d FD active", fd));
331 *eof = 0;
332
333 /* read from active FD */
334 bytes = read(fd, buf, sizeof(buf));
335 if (bytes < 0) {
336 if (errno == EAGAIN || errno == EINTR)
337 return;
338 fail(ctl);
339 }
340
341 if (bytes == 0) {
342 *eof = 1;
343 return;
344 }
345
346 /* from stdin (user) to command */
347 if (fd == STDIN_FILENO) {
348 DBG(IO, ul_debug(" stdin --> master %zd bytes", bytes));
349
350 if (write_to_shell(ctl, buf, bytes)) {
351 warn(_("write failed"));
352 fail(ctl);
353 }
354 /* without sync write_output() will write both input &
355 * shell output that looks like double echoing */
356 fdatasync(ctl->master);
357
358 /* from command (master) to stdout */
359 } else if (fd == ctl->master) {
360 DBG(IO, ul_debug(" master --> stdout %zd bytes", bytes));
361 write_output(ctl, buf, bytes);
362 }
363 }
364
365 static void handle_signal(struct script_control *ctl, int fd)
366 {
367 struct signalfd_siginfo info;
368 ssize_t bytes;
369
370 DBG(SIGNAL, ul_debug("signal FD %d active", fd));
371
372 bytes = read(fd, &info, sizeof(info));
373 if (bytes != sizeof(info)) {
374 if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
375 return;
376 fail(ctl);
377 }
378
379 switch (info.ssi_signo) {
380 case SIGCHLD:
381 DBG(SIGNAL, ul_debug(" get signal SIGCHLD"));
382 wait_for_child(ctl, 0);
383 ctl->poll_timeout = 10;
384 return;
385 case SIGWINCH:
386 DBG(SIGNAL, ul_debug(" get signal SIGWINCH"));
387 if (ctl->isterm) {
388 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
389 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
390 }
391 break;
392 case SIGTERM:
393 /* fallthrough */
394 case SIGINT:
395 /* fallthrough */
396 case SIGQUIT:
397 DBG(SIGNAL, ul_debug(" get signal SIG{TERM,INT,QUIT}"));
398 fprintf(stderr, _("\nSession terminated.\n"));
399 /* Child termination is going to generate SIGCHILD (see above) */
400 kill(ctl->child, SIGTERM);
401 return;
402 default:
403 abort();
404 }
405 }
406
407 static void do_io(struct script_control *ctl)
408 {
409 int ret, ignore_stdin = 0, eof = 0;
410 time_t tvec = script_time((time_t *)NULL);
411 char buf[128];
412 enum {
413 POLLFD_SIGNAL = 0,
414 POLLFD_MASTER,
415 POLLFD_STDIN /* optional; keep it last, see ignore_stdin */
416
417 };
418 struct pollfd pfd[] = {
419 [POLLFD_SIGNAL] = { .fd = ctl->sigfd, .events = POLLIN | POLLERR | POLLHUP },
420 [POLLFD_MASTER] = { .fd = ctl->master, .events = POLLIN | POLLERR | POLLHUP },
421 [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP }
422 };
423
424
425 if ((ctl->typescriptfp =
426 fopen(ctl->fname, ctl->append ? "a" UL_CLOEXECSTR : "w" UL_CLOEXECSTR)) == NULL) {
427 warn(_("cannot open %s"), ctl->fname);
428 fail(ctl);
429 }
430 if (ctl->timing) {
431 if (!ctl->tname) {
432 if (!(ctl->timingfp = fopen("/dev/stderr", "w" UL_CLOEXECSTR)))
433 err(EXIT_FAILURE, _("cannot open %s"), "/dev/stderr");
434 } else if (!(ctl->timingfp = fopen(ctl->tname, "w" UL_CLOEXECSTR)))
435 err(EXIT_FAILURE, _("cannot open %s"), ctl->tname);
436 }
437
438
439 if (ctl->typescriptfp) {
440 strtime_iso(&tvec, ISO_8601_DATE | ISO_8601_TIME |
441 ISO_8601_TIMEZONE | ISO_8601_SPACE,
442 buf, sizeof(buf));
443 fprintf(ctl->typescriptfp, _("Script started on %s\n"), buf);
444 }
445 gettime_monotonic(&ctl->oldtime);
446
447 while (!ctl->die) {
448 size_t i;
449 int errsv;
450
451 DBG(POLL, ul_debug("calling poll()"));
452
453 /* wait for input or signal */
454 ret = poll(pfd, ARRAY_SIZE(pfd) - ignore_stdin, ctl->poll_timeout);
455 errsv = errno;
456 DBG(POLL, ul_debug("poll() rc=%d", ret));
457
458 if (ret < 0) {
459 if (errsv == EAGAIN)
460 continue;
461 warn(_("poll failed"));
462 fail(ctl);
463 }
464 if (ret == 0) {
465 DBG(POLL, ul_debug("setting die=1"));
466 ctl->die = 1;
467 break;
468 }
469
470 for (i = 0; i < ARRAY_SIZE(pfd) - ignore_stdin; i++) {
471 if (pfd[i].revents == 0)
472 continue;
473
474 DBG(POLL, ul_debug(" active pfd[%s].fd=%d %s %s %s",
475 i == POLLFD_STDIN ? "stdin" :
476 i == POLLFD_MASTER ? "master" :
477 i == POLLFD_SIGNAL ? "signal" : "???",
478 pfd[i].fd,
479 pfd[i].revents & POLLIN ? "POLLIN" : "",
480 pfd[i].revents & POLLHUP ? "POLLHUP" : "",
481 pfd[i].revents & POLLERR ? "POLLERR" : ""));
482 switch (i) {
483 case POLLFD_STDIN:
484 case POLLFD_MASTER:
485 /* data */
486 if (pfd[i].revents & POLLIN)
487 handle_io(ctl, pfd[i].fd, &eof);
488 /* EOF maybe detected by two ways:
489 * A) poll() return POLLHUP event after close()
490 * B) read() returns 0 (no data) */
491 if ((pfd[i].revents & POLLHUP) || eof) {
492 DBG(POLL, ul_debug(" ignore FD"));
493 pfd[i].fd = -1;
494 /* according to man poll() set FD to -1 can't be used to ignore
495 * STDIN, so let's remove the FD from pool at all */
496 if (i == POLLFD_STDIN) {
497 ignore_stdin = 1;
498 write_eof_to_shell(ctl);
499 DBG(POLL, ul_debug(" ignore STDIN"));
500 }
501 }
502 continue;
503 case POLLFD_SIGNAL:
504 handle_signal(ctl, pfd[i].fd);
505 break;
506 }
507 }
508 }
509
510 DBG(POLL, ul_debug("poll() done"));
511
512 if (!ctl->die)
513 wait_for_child(ctl, 1);
514
515 if (ctl->typescriptfp) {
516 tvec = script_time((time_t *)NULL);
517 strtime_iso(&tvec, ISO_8601_DATE | ISO_8601_TIME |
518 ISO_8601_TIMEZONE | ISO_8601_SPACE,
519 buf, sizeof(buf));
520 fprintf(ctl->typescriptfp, _("\nScript done on %s\n"), buf);
521 }
522 done(ctl);
523 }
524
525 static void getslave(struct script_control *ctl)
526 {
527 #ifndef HAVE_LIBUTIL
528 ctl->line[strlen("/dev/")] = 't';
529 ctl->slave = open(ctl->line, O_RDWR | O_CLOEXEC);
530 if (ctl->slave < 0) {
531 warn(_("cannot open %s"), ctl->line);
532 fail(ctl);
533 }
534 if (ctl->isterm) {
535 tcsetattr(ctl->slave, TCSANOW, &ctl->attrs);
536 ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
537 }
538 #endif
539 setsid();
540 ioctl(ctl->slave, TIOCSCTTY, 0);
541 }
542
543 /* don't use DBG() stuff here otherwise it will be in the typescript file */
544 static void __attribute__((__noreturn__)) do_shell(struct script_control *ctl)
545 {
546 char *shname;
547
548 getslave(ctl);
549
550 /* close things irrelevant for this process */
551 close(ctl->master);
552 close(ctl->sigfd);
553
554 dup2(ctl->slave, STDIN_FILENO);
555 dup2(ctl->slave, STDOUT_FILENO);
556 dup2(ctl->slave, STDERR_FILENO);
557 close(ctl->slave);
558
559 ctl->master = -1;
560
561 shname = strrchr(ctl->shell, '/');
562 if (shname)
563 shname++;
564 else
565 shname = ctl->shell;
566
567 sigprocmask(SIG_SETMASK, &ctl->sigorg, NULL);
568
569 /*
570 * When invoked from within /etc/csh.login, script spawns a csh shell
571 * that spawns programs that cannot be killed with a SIGTERM. This is
572 * because csh has a documented behavior wherein it disables all
573 * signals when processing the /etc/csh.* files.
574 *
575 * Let's restore the default behavior.
576 */
577 signal(SIGTERM, SIG_DFL);
578
579 if (access(ctl->shell, X_OK) == 0) {
580 if (ctl->command)
581 execl(ctl->shell, shname, "-c", ctl->command, NULL);
582 else
583 execl(ctl->shell, shname, "-i", NULL);
584 } else {
585 if (ctl->command)
586 execlp(shname, "-c", ctl->command, NULL);
587 else
588 execlp(shname, "-i", NULL);
589 }
590 warn(_("failed to execute %s"), ctl->shell);
591 fail(ctl);
592 }
593
594 static void fixtty(struct script_control *ctl)
595 {
596 struct termios rtt;
597
598 if (!ctl->isterm)
599 return;
600
601 rtt = ctl->attrs;
602 cfmakeraw(&rtt);
603 rtt.c_lflag &= ~ECHO;
604 tcsetattr(STDIN_FILENO, TCSANOW, &rtt);
605 }
606
607 static void getmaster(struct script_control *ctl)
608 {
609 #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
610 int rc;
611
612 ctl->isterm = isatty(STDIN_FILENO);
613
614 if (ctl->isterm) {
615 if (tcgetattr(STDIN_FILENO, &ctl->attrs) != 0)
616 err(EXIT_FAILURE, _("failed to get terminal attributes"));
617 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
618 rc = openpty(&ctl->master, &ctl->slave, NULL, &ctl->attrs, &ctl->win);
619 } else
620 rc = openpty(&ctl->master, &ctl->slave, NULL, NULL, NULL);
621
622 if (rc < 0) {
623 warn(_("openpty failed"));
624 fail(ctl);
625 }
626 #else
627 char *pty, *bank, *cp;
628
629 ctl->isterm = isatty(STDIN_FILENO);
630
631 pty = &ctl->line[strlen("/dev/ptyp")];
632 for (bank = "pqrs"; *bank; bank++) {
633 ctl->line[strlen("/dev/pty")] = *bank;
634 *pty = '0';
635 if (access(ctl->line, F_OK) != 0)
636 break;
637 for (cp = "0123456789abcdef"; *cp; cp++) {
638 *pty = *cp;
639 ctl->master = open(ctl->line, O_RDWR | O_CLOEXEC);
640 if (ctl->master >= 0) {
641 char *tp = &ctl->line[strlen("/dev/")];
642 int ok;
643
644 /* verify slave side is usable */
645 *tp = 't';
646 ok = access(ctl->line, R_OK | W_OK) == 0;
647 *tp = 'p';
648 if (ok) {
649 if (ctl->isterm) {
650 tcgetattr(STDIN_FILENO, &ctl->attrs);
651 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
652 }
653 return;
654 }
655 close(ctl->master);
656 ctl->master = -1;
657 }
658 }
659 }
660 ctl->master = -1;
661 warn(_("out of pty's"));
662 fail(ctl);
663 #endif /* not HAVE_LIBUTIL */
664
665 DBG(IO, ul_debug("master fd: %d", ctl->master));
666 }
667
668 int main(int argc, char **argv)
669 {
670 struct script_control ctl = {
671 #if !HAVE_LIBUTIL || !HAVE_PTY_H
672 .line = "/dev/ptyXX",
673 #endif
674 .master = -1,
675 .poll_timeout = -1,
676 0
677 };
678 int ch;
679
680 enum { FORCE_OPTION = CHAR_MAX + 1 };
681
682 static const struct option longopts[] = {
683 {"append", no_argument, NULL, 'a'},
684 {"command", required_argument, NULL, 'c'},
685 {"return", no_argument, NULL, 'e'},
686 {"flush", no_argument, NULL, 'f'},
687 {"force", no_argument, NULL, FORCE_OPTION,},
688 {"quiet", no_argument, NULL, 'q'},
689 {"timing", optional_argument, NULL, 't'},
690 {"version", no_argument, NULL, 'V'},
691 {"help", no_argument, NULL, 'h'},
692 {NULL, 0, NULL, 0}
693 };
694
695 setlocale(LC_ALL, "");
696 /*
697 * script -t prints time delays as floating point numbers. The example
698 * program (scriptreplay) that we provide to handle this timing output
699 * is a perl script, and does not handle numbers in locale format (not
700 * even when "use locale;" is added). So, since these numbers are not
701 * for human consumption, it seems easiest to set LC_NUMERIC here.
702 */
703 setlocale(LC_NUMERIC, "C");
704 bindtextdomain(PACKAGE, LOCALEDIR);
705 textdomain(PACKAGE);
706 atexit(close_stdout);
707
708 script_init_debug();
709
710 while ((ch = getopt_long(argc, argv, "ac:efqt::Vh", longopts, NULL)) != -1)
711 switch (ch) {
712 case 'a':
713 ctl.append = 1;
714 break;
715 case 'c':
716 ctl.command = optarg;
717 break;
718 case 'e':
719 ctl.rc_wanted = 1;
720 break;
721 case 'f':
722 ctl.flush = 1;
723 break;
724 case FORCE_OPTION:
725 ctl.force = 1;
726 break;
727 case 'q':
728 ctl.quiet = 1;
729 break;
730 case 't':
731 if (optarg)
732 ctl.tname = optarg;
733 ctl.timing = 1;
734 break;
735 case 'V':
736 printf(UTIL_LINUX_VERSION);
737 exit(EXIT_SUCCESS);
738 break;
739 case 'h':
740 usage();
741 break;
742 default:
743 errtryhelp(EXIT_FAILURE);
744 }
745 argc -= optind;
746 argv += optind;
747
748 if (argc > 0)
749 ctl.fname = argv[0];
750 else {
751 ctl.fname = DEFAULT_TYPESCRIPT_FILENAME;
752 die_if_link(&ctl);
753 }
754
755 ctl.shell = getenv("SHELL");
756 if (ctl.shell == NULL)
757 ctl.shell = _PATH_BSHELL;
758
759 getmaster(&ctl);
760 if (!ctl.quiet)
761 printf(_("Script started, file is %s\n"), ctl.fname);
762 fixtty(&ctl);
763
764 #ifdef HAVE_LIBUTEMPTER
765 utempter_add_record(ctl.master, NULL);
766 #endif
767 /* setup signal handler */
768 sigemptyset(&ctl.sigset);
769 sigaddset(&ctl.sigset, SIGCHLD);
770 sigaddset(&ctl.sigset, SIGWINCH);
771 sigaddset(&ctl.sigset, SIGTERM);
772 sigaddset(&ctl.sigset, SIGINT);
773 sigaddset(&ctl.sigset, SIGQUIT);
774
775 /* block signals used for signalfd() to prevent the signals being
776 * handled according to their default dispositions */
777 sigprocmask(SIG_BLOCK, &ctl.sigset, &ctl.sigorg);
778
779 if ((ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC)) < 0)
780 err(EXIT_FAILURE, _("cannot set signal handler"));
781
782 DBG(SIGNAL, ul_debug("signal fd=%d", ctl.sigfd));
783
784 fflush(stdout);
785 ctl.child = fork();
786
787 switch (ctl.child) {
788 case -1: /* error */
789 warn(_("fork failed"));
790 fail(&ctl);
791 break;
792 case 0: /* child */
793 do_shell(&ctl);
794 break;
795 default: /* parent */
796 do_io(&ctl);
797 break;
798 }
799
800 /* should not happen, all used functions are non-return */
801 return EXIT_FAILURE;
802 }