10Sstevel@tonic-gate /*
2*9258SRitwik.Ghoshal@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
70Sstevel@tonic-gate /* All Rights Reserved */
80Sstevel@tonic-gate
90Sstevel@tonic-gate
100Sstevel@tonic-gate /*
110Sstevel@tonic-gate * Copyright (c) 1980 Regents of the University of California.
120Sstevel@tonic-gate * All rights reserved. The Berkeley software License Agreement
130Sstevel@tonic-gate * specifies the terms and conditions for redistribution.
140Sstevel@tonic-gate */
150Sstevel@tonic-gate
160Sstevel@tonic-gate /* Portions Copyright(c) 1988, Sun Microsystems, Inc. */
170Sstevel@tonic-gate /* All Rights Reserved. */
180Sstevel@tonic-gate
190Sstevel@tonic-gate /*
20334Sdp * script: Produce a record of a terminal session.
210Sstevel@tonic-gate */
220Sstevel@tonic-gate #include <stdio.h>
23334Sdp #include <stdlib.h>
24334Sdp #include <unistd.h>
250Sstevel@tonic-gate #include <signal.h>
260Sstevel@tonic-gate #include <fcntl.h>
270Sstevel@tonic-gate #include <locale.h>
280Sstevel@tonic-gate #include <time.h>
290Sstevel@tonic-gate #include <sys/stropts.h>
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/stat.h>
320Sstevel@tonic-gate #include <sys/termios.h>
330Sstevel@tonic-gate #include <sys/file.h>
340Sstevel@tonic-gate #include <errno.h>
350Sstevel@tonic-gate
360Sstevel@tonic-gate int grantpt();
370Sstevel@tonic-gate int unlockpt();
380Sstevel@tonic-gate char *ptsname();
39334Sdp void doinput() __NORETURN;
40334Sdp void dooutput();
41334Sdp void doshell();
42334Sdp void fixtty();
43334Sdp void fail();
44334Sdp void done() __NORETURN;
45334Sdp void getmaster();
46334Sdp void getslave();
470Sstevel@tonic-gate
480Sstevel@tonic-gate char *shell;
490Sstevel@tonic-gate FILE *fscript;
500Sstevel@tonic-gate int master; /* file descriptor for master pseudo-tty */
510Sstevel@tonic-gate int slave; /* file descriptor for slave pseudo-tty */
520Sstevel@tonic-gate int child;
530Sstevel@tonic-gate int subchild;
540Sstevel@tonic-gate char *fname = "typescript";
550Sstevel@tonic-gate void sigwinch();
560Sstevel@tonic-gate void finish();
570Sstevel@tonic-gate
580Sstevel@tonic-gate struct termios b;
590Sstevel@tonic-gate struct winsize size;
600Sstevel@tonic-gate int lb;
610Sstevel@tonic-gate int l;
620Sstevel@tonic-gate char *mptname = "/dev/ptmx"; /* master pseudo-tty device */
630Sstevel@tonic-gate
640Sstevel@tonic-gate int aflg;
650Sstevel@tonic-gate
66334Sdp int
main(int argc,char * argv[])67334Sdp main(int argc, char *argv[])
680Sstevel@tonic-gate {
690Sstevel@tonic-gate uid_t ruidt;
700Sstevel@tonic-gate gid_t gidt;
710Sstevel@tonic-gate
720Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
730Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
740Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
750Sstevel@tonic-gate #endif
760Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
770Sstevel@tonic-gate
780Sstevel@tonic-gate shell = getenv("SHELL");
79334Sdp if (shell == NULL)
800Sstevel@tonic-gate shell = "/bin/sh";
810Sstevel@tonic-gate argc--, argv++;
820Sstevel@tonic-gate while (argc > 0 && argv[0][0] == '-') {
830Sstevel@tonic-gate switch (argv[0][1]) {
840Sstevel@tonic-gate
850Sstevel@tonic-gate case 'a':
860Sstevel@tonic-gate aflg++;
870Sstevel@tonic-gate break;
880Sstevel@tonic-gate
890Sstevel@tonic-gate default:
900Sstevel@tonic-gate fprintf(stderr,
910Sstevel@tonic-gate gettext("usage: script [ -a ] [ typescript ]\n"));
920Sstevel@tonic-gate exit(1);
930Sstevel@tonic-gate }
940Sstevel@tonic-gate argc--, argv++;
950Sstevel@tonic-gate }
960Sstevel@tonic-gate if (argc > 0)
970Sstevel@tonic-gate fname = argv[0];
980Sstevel@tonic-gate ruidt = getuid();
990Sstevel@tonic-gate gidt = getgid();
1000Sstevel@tonic-gate if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
1010Sstevel@tonic-gate perror(fname);
1020Sstevel@tonic-gate fail();
1030Sstevel@tonic-gate }
104*9258SRitwik.Ghoshal@Sun.COM setbuf(fscript, NULL);
1050Sstevel@tonic-gate chown(fname, ruidt, gidt);
1060Sstevel@tonic-gate getmaster();
1070Sstevel@tonic-gate printf(gettext("Script started, file is %s\n"), fname);
1080Sstevel@tonic-gate fixtty();
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate (void) signal(SIGCHLD, finish);
1110Sstevel@tonic-gate child = fork();
1120Sstevel@tonic-gate if (child < 0) {
1130Sstevel@tonic-gate perror("fork");
1140Sstevel@tonic-gate fail();
1150Sstevel@tonic-gate }
1160Sstevel@tonic-gate if (child == 0) {
1170Sstevel@tonic-gate subchild = child = fork();
1180Sstevel@tonic-gate if (child < 0) {
1190Sstevel@tonic-gate perror("fork");
1200Sstevel@tonic-gate fail();
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate if (child)
1230Sstevel@tonic-gate dooutput();
1240Sstevel@tonic-gate else
1250Sstevel@tonic-gate doshell();
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate doinput();
128334Sdp /* NOTREACHED */
129334Sdp return (0);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate
132334Sdp void
doinput()1330Sstevel@tonic-gate doinput()
1340Sstevel@tonic-gate {
1350Sstevel@tonic-gate char ibuf[BUFSIZ];
1360Sstevel@tonic-gate int cc;
1370Sstevel@tonic-gate
1380Sstevel@tonic-gate (void) fclose(fscript);
1390Sstevel@tonic-gate sigset(SIGWINCH, sigwinch);
1400Sstevel@tonic-gate
1410Sstevel@tonic-gate while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
1420Sstevel@tonic-gate if (cc == -1) {
1430Sstevel@tonic-gate if (errno == EINTR) { /* SIGWINCH probably */
1440Sstevel@tonic-gate continue;
1450Sstevel@tonic-gate } else {
1460Sstevel@tonic-gate break;
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate }
1490Sstevel@tonic-gate (void) write(master, ibuf, cc);
1500Sstevel@tonic-gate }
1510Sstevel@tonic-gate done();
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate
1540Sstevel@tonic-gate void
sigwinch()1550Sstevel@tonic-gate sigwinch()
1560Sstevel@tonic-gate {
1570Sstevel@tonic-gate struct winsize ws;
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate if (ioctl(0, TIOCGWINSZ, &ws) == 0)
1600Sstevel@tonic-gate (void) ioctl(master, TIOCSWINSZ, &ws);
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate #include <sys/wait.h>
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate void
finish()1660Sstevel@tonic-gate finish()
1670Sstevel@tonic-gate {
1680Sstevel@tonic-gate int status;
1690Sstevel@tonic-gate register int pid;
1700Sstevel@tonic-gate register int die = 0;
1710Sstevel@tonic-gate
1720Sstevel@tonic-gate while ((pid = wait(&status)) > 0)
1730Sstevel@tonic-gate if (pid == child)
1740Sstevel@tonic-gate die = 1;
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate if (die)
1770Sstevel@tonic-gate done();
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate
180334Sdp void
dooutput()1810Sstevel@tonic-gate dooutput()
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate time_t tvec;
1840Sstevel@tonic-gate char obuf[BUFSIZ];
1850Sstevel@tonic-gate char tbuf[BUFSIZ];
1860Sstevel@tonic-gate int cc;
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate (void) close(0);
1890Sstevel@tonic-gate tvec = time((time_t *)0);
1900Sstevel@tonic-gate strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
1910Sstevel@tonic-gate fprintf(fscript, gettext("Script started on %s\n"), tbuf);
1920Sstevel@tonic-gate for (;;) {
1930Sstevel@tonic-gate cc = read(master, obuf, sizeof (obuf));
1940Sstevel@tonic-gate if (cc <= 0)
1950Sstevel@tonic-gate break;
1960Sstevel@tonic-gate (void) write(1, obuf, cc);
1970Sstevel@tonic-gate (void) fwrite(obuf, 1, cc, fscript);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate done();
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate
202334Sdp void
doshell()2030Sstevel@tonic-gate doshell()
2040Sstevel@tonic-gate {
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate setpgrp(); /* relinquish control terminal */
2070Sstevel@tonic-gate getslave();
2080Sstevel@tonic-gate (void) close(master);
2090Sstevel@tonic-gate (void) fclose(fscript);
2100Sstevel@tonic-gate (void) dup2(slave, 0);
2110Sstevel@tonic-gate (void) dup2(slave, 1);
2120Sstevel@tonic-gate (void) dup2(slave, 2);
2130Sstevel@tonic-gate (void) close(slave);
2140Sstevel@tonic-gate execl(shell, shell, "-i", (char *)0);
2150Sstevel@tonic-gate perror(shell);
2160Sstevel@tonic-gate fail();
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate
219334Sdp void
fixtty()2200Sstevel@tonic-gate fixtty()
2210Sstevel@tonic-gate {
2220Sstevel@tonic-gate struct termios sbuf;
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate sbuf = b;
2250Sstevel@tonic-gate sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
2260Sstevel@tonic-gate sbuf.c_oflag &= ~OPOST;
2270Sstevel@tonic-gate sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
2280Sstevel@tonic-gate sbuf.c_cc[VMIN] = 1;
2290Sstevel@tonic-gate sbuf.c_cc[VTIME] = 0;
2300Sstevel@tonic-gate (void) ioctl(0, TCSETSF, (char *)&sbuf);
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate
233334Sdp void
fail()2340Sstevel@tonic-gate fail()
2350Sstevel@tonic-gate {
2360Sstevel@tonic-gate
2370Sstevel@tonic-gate (void) kill(0, SIGTERM);
2380Sstevel@tonic-gate done();
2390Sstevel@tonic-gate }
2400Sstevel@tonic-gate
241334Sdp void
done()2420Sstevel@tonic-gate done()
2430Sstevel@tonic-gate {
2440Sstevel@tonic-gate time_t tvec;
2450Sstevel@tonic-gate char tbuf[BUFSIZ];
2460Sstevel@tonic-gate
2470Sstevel@tonic-gate if (subchild) {
2480Sstevel@tonic-gate tvec = time((time_t *)0);
2490Sstevel@tonic-gate strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
2500Sstevel@tonic-gate fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
2510Sstevel@tonic-gate (void) fclose(fscript);
2520Sstevel@tonic-gate (void) close(master);
2530Sstevel@tonic-gate } else {
2540Sstevel@tonic-gate (void) ioctl(0, TCSETSW, (char *)&b);
2550Sstevel@tonic-gate printf(gettext("Script done, file is %s\n"), fname);
2560Sstevel@tonic-gate }
2570Sstevel@tonic-gate exit(0);
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate
260334Sdp void
getmaster()2610Sstevel@tonic-gate getmaster()
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate struct stat stb;
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
2660Sstevel@tonic-gate (void) ioctl(0, TCGETS, (char *)&b);
2670Sstevel@tonic-gate (void) ioctl(0, TIOCGWINSZ, (char *)&size);
2680Sstevel@tonic-gate return;
2690Sstevel@tonic-gate } else { /* out of pseudo-tty's */
2700Sstevel@tonic-gate perror(mptname);
2710Sstevel@tonic-gate fprintf(stderr, gettext("Out of pseudo-tty's\n"));
2720Sstevel@tonic-gate fail();
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate }
2750Sstevel@tonic-gate
276334Sdp void
getslave()2770Sstevel@tonic-gate getslave()
2780Sstevel@tonic-gate {
2790Sstevel@tonic-gate char *slavename; /* name of slave pseudo-tty */
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate grantpt(master); /* change permissions of slave */
2820Sstevel@tonic-gate unlockpt(master); /* unlock slave */
2830Sstevel@tonic-gate slavename = ptsname(master); /* get name of slave */
2840Sstevel@tonic-gate slave = open(slavename, O_RDWR); /* open slave */
2850Sstevel@tonic-gate if (slave < 0) { /* error on opening slave */
2860Sstevel@tonic-gate perror(slavename);
2870Sstevel@tonic-gate fail();
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate ioctl(slave, I_PUSH, "ptem"); /* push pt hw emulation module */
2900Sstevel@tonic-gate ioctl(slave, I_PUSH, "ldterm"); /* push line discipline */
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate (void) ioctl(slave, TCSETSF, (char *)&b);
2930Sstevel@tonic-gate (void) ioctl(slave, TIOCSWINSZ, (char *)&size);
2940Sstevel@tonic-gate }
295