~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to term-utils/script.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
41
/*
 
42
 * script
 
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
 
 
62
#include "nls.h"
 
63
#include "c.h"
 
64
 
 
65
#if HAVE_LIBUTIL && HAVE_PTY_H
 
66
#include <pty.h>
 
67
#endif
 
68
 
 
69
#ifdef HAVE_LIBUTEMPTER
 
70
#include <utempter.h>
 
71
#endif
 
72
 
 
73
#define DEFAULT_OUTPUT "typescript"
 
74
 
 
75
void finish(int);
 
76
void done(void);
 
77
void fail(void);
 
78
void resize(int);
 
79
void fixtty(void);
 
80
void getmaster(void);
 
81
void getslave(void);
 
82
void doinput(void);
 
83
void dooutput(FILE *timingfd);
 
84
void doshell(void);
 
85
 
 
86
char    *shell;
 
87
FILE    *fscript;
 
88
int     master = -1;
 
89
int     slave;
 
90
pid_t   child;
 
91
pid_t   subchild;
 
92
int     childstatus;
 
93
char    *fname;
 
94
 
 
95
struct  termios tt;
 
96
struct  winsize win;
 
97
int     lb;
 
98
int     l;
 
99
#if !HAVE_LIBUTIL || !HAVE_PTY_H
 
100
char    line[] = "/dev/ptyXX";
 
101
#endif
 
102
int     aflg = 0;
 
103
char    *cflg = NULL;
 
104
int     eflg = 0;
 
105
int     fflg = 0;
 
106
int     qflg = 0;
 
107
int     tflg = 0;
 
108
int     forceflg = 0;
 
109
 
 
110
int die;
 
111
int resized;
 
112
 
 
113
static void
 
114
die_if_link(char *fn) {
 
115
        struct stat s;
 
116
 
 
117
        if (forceflg)
 
118
                return;
 
119
        if (lstat(fn, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1))
 
120
                errx(EXIT_FAILURE,
 
121
                     _("output file `%s' is a link\n"
 
122
                       "Use --force if you really want to use it.\n"
 
123
                       "Program not started."), fn);
 
124
}
 
125
 
 
126
static void __attribute__((__noreturn__))
 
127
usage(FILE *out)
 
128
{
 
129
        fputs(_("\nUsage:\n"), out);
 
130
        fprintf(out,
 
131
              _(" %s [options] [file]\n"), program_invocation_short_name);
 
132
 
 
133
        fputs(_("\nOptions:\n"), out);
 
134
        fputs(_(" -a, --append            append the output\n"
 
135
                " -c, --command <command> run command rather than interactive shell\n"
 
136
                " -r, --return            return exit code of the child process\n"
 
137
                " -f, --flush             run flush after each write\n"
 
138
                "     --force             use output file even when it is a link\n"
 
139
                " -q, --quiet             be quiet\n"
 
140
                " -t, --timing[=<file>]   output timing data to stderr (or to FILE)\n"
 
141
                " -V, --version           output version information and exit\n"
 
142
                " -h, --help              display this help and exit\n\n"), out);
 
143
 
 
144
        exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
 
145
}
 
146
 
 
147
/*
 
148
 * script -t prints time delays as floating point numbers
 
149
 * The example program (scriptreplay) that we provide to handle this
 
150
 * timing output is a perl script, and does not handle numbers in
 
151
 * locale format (not even when "use locale;" is added).
 
152
 * So, since these numbers are not for human consumption, it seems
 
153
 * easiest to set LC_NUMERIC here.
 
154
 */
 
155
 
 
156
int
 
157
main(int argc, char **argv) {
 
158
        sigset_t block_mask, unblock_mask;
 
159
        struct sigaction sa;
 
160
        extern int optind;
 
161
        int ch;
 
162
        FILE *timingfd = stderr;
 
163
 
 
164
        enum { FORCE_OPTION = CHAR_MAX + 1 };
 
165
 
 
166
        static const struct option longopts[] = {
 
167
                { "append",     no_argument,       NULL, 'a' },
 
168
                { "command",    required_argument, NULL, 'c' },
 
169
                { "return",     no_argument,       NULL, 'e' },
 
170
                { "flush",      no_argument,       NULL, 'f' },
 
171
                { "force",      no_argument,       NULL, FORCE_OPTION, },
 
172
                { "quiet",      no_argument,       NULL, 'q' },
 
173
                { "timing",     optional_argument, NULL, 't' },
 
174
                { "version",    no_argument,       NULL, 'V' },
 
175
                { "help",       no_argument,       NULL, 'h' },
 
176
                { NULL,         0, NULL, 0 }
 
177
        };
 
178
 
 
179
        setlocale(LC_ALL, "");
 
180
        setlocale(LC_NUMERIC, "C");     /* see comment above */
 
181
        bindtextdomain(PACKAGE, LOCALEDIR);
 
182
        textdomain(PACKAGE);
 
183
 
 
184
        while ((ch = getopt_long(argc, argv, "ac:efqt::Vh", longopts, NULL)) != -1)
 
185
                switch(ch) {
 
186
                case 'a':
 
187
                        aflg = 1;
 
188
                        break;
 
189
                case 'c':
 
190
                        cflg = optarg;
 
191
                        break;
 
192
                case 'e':
 
193
                        eflg = 1;
 
194
                        break;
 
195
                case 'f':
 
196
                        fflg = 1;
 
197
                        break;
 
198
                case FORCE_OPTION:
 
199
                        forceflg = 1;
 
200
                        break;
 
201
                case 'q':
 
202
                        qflg = 1;
 
203
                        break;
 
204
                case 't':
 
205
                        if (optarg)
 
206
                                if ((timingfd = fopen(optarg, "w")) == NULL)
 
207
                                        err(EXIT_FAILURE, _("cannot open timing file %s"), optarg);
 
208
                        tflg = 1;
 
209
                        break;
 
210
                case 'V':
 
211
                        printf(_("%s from %s\n"), program_invocation_short_name,
 
212
                                                  PACKAGE_STRING);
 
213
                        exit(EXIT_SUCCESS);
 
214
                        break;
 
215
                case 'h':
 
216
                        usage(stdout);
 
217
                        break;
 
218
                case '?':
 
219
                default:
 
220
                        usage(stderr);
 
221
                }
 
222
        argc -= optind;
 
223
        argv += optind;
 
224
 
 
225
        if (argc > 0)
 
226
                fname = argv[0];
 
227
        else {
 
228
                fname = DEFAULT_OUTPUT;
 
229
                die_if_link(fname);
 
230
        }
 
231
        if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
 
232
                warn(_("open failed: %s"), fname);
 
233
                fail();
 
234
        }
 
235
 
 
236
        shell = getenv("SHELL");
 
237
        if (shell == NULL)
 
238
                shell = _PATH_BSHELL;
 
239
 
 
240
        getmaster();
 
241
        if (!qflg)
 
242
                printf(_("Script started, file is %s\n"), fname);
 
243
        fixtty();
 
244
 
 
245
#ifdef HAVE_LIBUTEMPTER
 
246
        utempter_add_record(master, NULL);
 
247
#endif
 
248
        /* setup SIGCHLD handler */
 
249
        sigemptyset(&sa.sa_mask);
 
250
        sa.sa_flags = 0;
 
251
        sa.sa_handler = finish;
 
252
        sigaction(SIGCHLD, &sa, NULL);
 
253
 
 
254
        /* init mask for SIGCHLD */
 
255
        sigprocmask(SIG_SETMASK, NULL, &block_mask);
 
256
        sigaddset(&block_mask, SIGCHLD);
 
257
 
 
258
        sigprocmask(SIG_SETMASK, &block_mask, &unblock_mask);
 
259
        child = fork();
 
260
        sigprocmask(SIG_SETMASK, &unblock_mask, NULL);
 
261
 
 
262
        if (child < 0) {
 
263
                warn(_("fork failed"));
 
264
                fail();
 
265
        }
 
266
        if (child == 0) {
 
267
 
 
268
                sigprocmask(SIG_SETMASK, &block_mask, NULL);
 
269
                subchild = child = fork();
 
270
                sigprocmask(SIG_SETMASK, &unblock_mask, NULL);
 
271
 
 
272
                if (child < 0) {
 
273
                        warn(_("fork failed"));
 
274
                        fail();
 
275
                }
 
276
                if (child)
 
277
                        dooutput(timingfd);
 
278
                else
 
279
                        doshell();
 
280
        } else {
 
281
                sa.sa_handler = resize;
 
282
                sigaction(SIGWINCH, &sa, NULL);
 
283
        }
 
284
        doinput();
 
285
 
 
286
        fclose(timingfd);
 
287
        return EXIT_SUCCESS;
 
288
}
 
289
 
 
290
void
 
291
doinput() {
 
292
        ssize_t cc;
 
293
        char ibuf[BUFSIZ];
 
294
 
 
295
        fclose(fscript);
 
296
 
 
297
        while (die == 0) {
 
298
                if ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) {
 
299
                        ssize_t wrt = write(master, ibuf, cc);
 
300
                        if (wrt < 0) {
 
301
                                warn (_("write failed"));
 
302
                                fail();
 
303
                        }
 
304
                }
 
305
                else if (cc < 0 && errno == EINTR && resized)
 
306
                        resized = 0;
 
307
                else
 
308
                        break;
 
309
        }
 
310
 
 
311
        done();
 
312
}
 
313
 
 
314
#include <sys/wait.h>
 
315
 
 
316
void
 
317
finish(int dummy __attribute__ ((__unused__))) {
 
318
        int status;
 
319
        pid_t pid;
 
320
 
 
321
        while ((pid = wait3(&status, WNOHANG, 0)) > 0)
 
322
                if (pid == child) {
 
323
                        childstatus = status;
 
324
                        die = 1;
 
325
                }
 
326
}
 
327
 
 
328
void
 
329
resize(int dummy __attribute__ ((__unused__))) {
 
330
        resized = 1;
 
331
        /* transmit window change information to the child */
 
332
        ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&win);
 
333
        ioctl(slave, TIOCSWINSZ, (char *)&win);
 
334
}
 
335
 
 
336
/*
 
337
 * Stop extremely silly gcc complaint on %c:
 
338
 *  warning: `%c' yields only last 2 digits of year in some locales
 
339
 */
 
340
static void
 
341
my_strftime(char *buf, size_t len, const char *fmt, const struct tm *tm) {
 
342
        strftime(buf, len, fmt, tm);
 
343
}
 
344
 
 
345
void
 
346
dooutput(FILE *timingfd) {
 
347
        ssize_t cc;
 
348
        time_t tvec;
 
349
        char obuf[BUFSIZ];
 
350
        struct timeval tv;
 
351
        double oldtime=time(NULL), newtime;
 
352
        int flgs = 0;
 
353
        ssize_t wrt;
 
354
        ssize_t fwrt;
 
355
 
 
356
        close(STDIN_FILENO);
 
357
#ifdef HAVE_LIBUTIL
 
358
        close(slave);
 
359
#endif
 
360
        tvec = time((time_t *)NULL);
 
361
        my_strftime(obuf, sizeof obuf, "%c\n", localtime(&tvec));
 
362
        fprintf(fscript, _("Script started on %s"), obuf);
 
363
 
 
364
        do {
 
365
                if (die && flgs == 0) {
 
366
                        /* ..child is dead, but it doesn't mean that there is
 
367
                         * nothing in buffers.
 
368
                         */
 
369
                        flgs = fcntl(master, F_GETFL, 0);
 
370
                        if (fcntl(master, F_SETFL, (flgs | O_NONBLOCK)) == -1)
 
371
                                break;
 
372
                }
 
373
                if (tflg)
 
374
                        gettimeofday(&tv, NULL);
 
375
 
 
376
                errno = 0;
 
377
                cc = read(master, obuf, sizeof (obuf));
 
378
 
 
379
                if (die && errno == EINTR && cc <= 0)
 
380
                        /* read() has been interrupted by SIGCHLD, try it again
 
381
                         * with O_NONBLOCK
 
382
                         */
 
383
                        continue;
 
384
                if (cc <= 0)
 
385
                        break;
 
386
                if (tflg) {
 
387
                        newtime = tv.tv_sec + (double) tv.tv_usec / 1000000;
 
388
                        fprintf(timingfd, "%f %zd\n", newtime - oldtime, cc);
 
389
                        oldtime = newtime;
 
390
                }
 
391
                wrt = write(STDOUT_FILENO, obuf, cc);
 
392
                if (wrt < 0) {
 
393
                        warn (_("write failed"));
 
394
                        fail();
 
395
                }
 
396
                fwrt = fwrite(obuf, 1, cc, fscript);
 
397
                if (fwrt < cc) {
 
398
                        warn (_("cannot write script file"));
 
399
                        fail();
 
400
                }
 
401
                if (fflg)
 
402
                        fflush(fscript);
 
403
        } while(1);
 
404
 
 
405
        if (flgs)
 
406
                fcntl(master, F_SETFL, flgs);
 
407
        done();
 
408
}
 
409
 
 
410
void
 
411
doshell() {
 
412
        char *shname;
 
413
 
 
414
#if 0
 
415
        int t;
 
416
 
 
417
        t = open(_PATH_DEV_TTY, O_RDWR);
 
418
        if (t >= 0) {
 
419
                ioctl(t, TIOCNOTTY, (char *)0);
 
420
                close(t);
 
421
        }
 
422
#endif
 
423
 
 
424
        getslave();
 
425
        close(master);
 
426
        fclose(fscript);
 
427
        dup2(slave, STDIN_FILENO);
 
428
        dup2(slave, STDOUT_FILENO);
 
429
        dup2(slave, STDERR_FILENO);
 
430
        close(slave);
 
431
 
 
432
        master = -1;
 
433
 
 
434
        shname = strrchr(shell, '/');
 
435
        if (shname)
 
436
                shname++;
 
437
        else
 
438
                shname = shell;
 
439
 
 
440
        if (cflg)
 
441
                execl(shell, shname, "-c", cflg, NULL);
 
442
        else
 
443
                execl(shell, shname, "-i", NULL);
 
444
 
 
445
        warn(_("failed to execute %s"), shell);
 
446
        fail();
 
447
}
 
448
 
 
449
void
 
450
fixtty() {
 
451
        struct termios rtt;
 
452
 
 
453
        rtt = tt;
 
454
        cfmakeraw(&rtt);
 
455
        rtt.c_lflag &= ~ECHO;
 
456
        tcsetattr(STDIN_FILENO, TCSANOW, &rtt);
 
457
}
 
458
 
 
459
void
 
460
fail() {
 
461
 
 
462
        kill(0, SIGTERM);
 
463
        done();
 
464
}
 
465
 
 
466
void
 
467
done() {
 
468
        time_t tvec;
 
469
 
 
470
        if (subchild) {
 
471
                if (!qflg) {
 
472
                        char buf[BUFSIZ];
 
473
                        tvec = time((time_t *)NULL);
 
474
                        my_strftime(buf, sizeof buf, "%c\n", localtime(&tvec));
 
475
                        fprintf(fscript, _("\nScript done on %s"), buf);
 
476
                }
 
477
                fclose(fscript);
 
478
                close(master);
 
479
 
 
480
                master = -1;
 
481
        } else {
 
482
                tcsetattr(STDIN_FILENO, TCSADRAIN, &tt);
 
483
                if (!qflg)
 
484
                        printf(_("Script done, file is %s\n"), fname);
 
485
#ifdef HAVE_LIBUTEMPTER
 
486
                if (master >= 0)
 
487
                        utempter_remove_record(master);
 
488
#endif
 
489
        }
 
490
 
 
491
        if(eflg) {
 
492
                if (WIFSIGNALED(childstatus))
 
493
                        exit(WTERMSIG(childstatus) + 0x80);
 
494
                else
 
495
                        exit(WEXITSTATUS(childstatus));
 
496
        }
 
497
        exit(EXIT_SUCCESS);
 
498
}
 
499
 
 
500
void
 
501
getmaster() {
 
502
#if HAVE_LIBUTIL && HAVE_PTY_H
 
503
        tcgetattr(STDIN_FILENO, &tt);
 
504
        ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&win);
 
505
        if (openpty(&master, &slave, NULL, &tt, &win) < 0) {
 
506
                warn(_("openpty failed"));
 
507
                fail();
 
508
        }
 
509
#else
 
510
        char *pty, *bank, *cp;
 
511
        struct stat stb;
 
512
 
 
513
        pty = &line[strlen("/dev/ptyp")];
 
514
        for (bank = "pqrs"; *bank; bank++) {
 
515
                line[strlen("/dev/pty")] = *bank;
 
516
                *pty = '0';
 
517
                if (stat(line, &stb) < 0)
 
518
                        break;
 
519
                for (cp = "0123456789abcdef"; *cp; cp++) {
 
520
                        *pty = *cp;
 
521
                        master = open(line, O_RDWR);
 
522
                        if (master >= 0) {
 
523
                                char *tp = &line[strlen("/dev/")];
 
524
                                int ok;
 
525
 
 
526
                                /* verify slave side is usable */
 
527
                                *tp = 't';
 
528
                                ok = access(line, R_OK|W_OK) == 0;
 
529
                                *tp = 'p';
 
530
                                if (ok) {
 
531
                                        tcgetattr(STDIN_FILENO, &tt);
 
532
                                        ioctl(STDIN_FILENO, TIOCGWINSZ,
 
533
                                                (char *)&win);
 
534
                                        return;
 
535
                                }
 
536
                                close(master);
 
537
                                master = -1;
 
538
                        }
 
539
                }
 
540
        }
 
541
        master = -1;
 
542
        warn(_("out of pty's"));
 
543
        fail();
 
544
#endif /* not HAVE_LIBUTIL */
 
545
}
 
546
 
 
547
void
 
548
getslave() {
 
549
#ifndef HAVE_LIBUTIL
 
550
        line[strlen("/dev/")] = 't';
 
551
        slave = open(line, O_RDWR);
 
552
        if (slave < 0) {
 
553
                warn(_("open failed: %s"), line);
 
554
                fail();
 
555
        }
 
556
        tcsetattr(slave, TCSANOW, &tt);
 
557
        ioctl(slave, TIOCSWINSZ, (char *)&win);
 
558
#endif
 
559
        setsid();
 
560
        ioctl(slave, TIOCSCTTY, 0);
 
561
}