10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * Copyright 2005 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 #pragma ident "%Z%%M% %I% %E% SMI" 200Sstevel@tonic-gate 210Sstevel@tonic-gate /* 22*334Sdp * script: Produce a record of a terminal session. 230Sstevel@tonic-gate */ 240Sstevel@tonic-gate #include <stdio.h> 25*334Sdp #include <stdlib.h> 26*334Sdp #include <unistd.h> 270Sstevel@tonic-gate #include <signal.h> 280Sstevel@tonic-gate #include <fcntl.h> 290Sstevel@tonic-gate #include <locale.h> 300Sstevel@tonic-gate #include <time.h> 310Sstevel@tonic-gate #include <sys/stropts.h> 320Sstevel@tonic-gate #include <sys/types.h> 330Sstevel@tonic-gate #include <sys/stat.h> 340Sstevel@tonic-gate #include <sys/termios.h> 350Sstevel@tonic-gate #include <sys/file.h> 360Sstevel@tonic-gate #include <errno.h> 370Sstevel@tonic-gate 380Sstevel@tonic-gate int grantpt(); 390Sstevel@tonic-gate int unlockpt(); 400Sstevel@tonic-gate char *ptsname(); 41*334Sdp void doinput() __NORETURN; 42*334Sdp void dooutput(); 43*334Sdp void doshell(); 44*334Sdp void fixtty(); 45*334Sdp void fail(); 46*334Sdp void done() __NORETURN; 47*334Sdp void getmaster(); 48*334Sdp void getslave(); 490Sstevel@tonic-gate 500Sstevel@tonic-gate char *shell; 510Sstevel@tonic-gate FILE *fscript; 520Sstevel@tonic-gate int master; /* file descriptor for master pseudo-tty */ 530Sstevel@tonic-gate int slave; /* file descriptor for slave pseudo-tty */ 540Sstevel@tonic-gate int child; 550Sstevel@tonic-gate int subchild; 560Sstevel@tonic-gate char *fname = "typescript"; 570Sstevel@tonic-gate void sigwinch(); 580Sstevel@tonic-gate void finish(); 590Sstevel@tonic-gate 600Sstevel@tonic-gate struct termios b; 610Sstevel@tonic-gate struct winsize size; 620Sstevel@tonic-gate int lb; 630Sstevel@tonic-gate int l; 640Sstevel@tonic-gate char *mptname = "/dev/ptmx"; /* master pseudo-tty device */ 650Sstevel@tonic-gate 660Sstevel@tonic-gate int aflg; 670Sstevel@tonic-gate 68*334Sdp int 69*334Sdp main(int argc, char *argv[]) 700Sstevel@tonic-gate { 710Sstevel@tonic-gate uid_t ruidt; 720Sstevel@tonic-gate gid_t gidt; 730Sstevel@tonic-gate 740Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 750Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) 760Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" 770Sstevel@tonic-gate #endif 780Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 790Sstevel@tonic-gate 800Sstevel@tonic-gate shell = getenv("SHELL"); 81*334Sdp if (shell == NULL) 820Sstevel@tonic-gate shell = "/bin/sh"; 830Sstevel@tonic-gate argc--, argv++; 840Sstevel@tonic-gate while (argc > 0 && argv[0][0] == '-') { 850Sstevel@tonic-gate switch (argv[0][1]) { 860Sstevel@tonic-gate 870Sstevel@tonic-gate case 'a': 880Sstevel@tonic-gate aflg++; 890Sstevel@tonic-gate break; 900Sstevel@tonic-gate 910Sstevel@tonic-gate default: 920Sstevel@tonic-gate fprintf(stderr, 930Sstevel@tonic-gate gettext("usage: script [ -a ] [ typescript ]\n")); 940Sstevel@tonic-gate exit(1); 950Sstevel@tonic-gate } 960Sstevel@tonic-gate argc--, argv++; 970Sstevel@tonic-gate } 980Sstevel@tonic-gate if (argc > 0) 990Sstevel@tonic-gate fname = argv[0]; 1000Sstevel@tonic-gate ruidt = getuid(); 1010Sstevel@tonic-gate gidt = getgid(); 1020Sstevel@tonic-gate if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) { 1030Sstevel@tonic-gate perror(fname); 1040Sstevel@tonic-gate fail(); 1050Sstevel@tonic-gate } 1060Sstevel@tonic-gate chown(fname, ruidt, gidt); 1070Sstevel@tonic-gate getmaster(); 1080Sstevel@tonic-gate printf(gettext("Script started, file is %s\n"), fname); 1090Sstevel@tonic-gate fixtty(); 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate (void) signal(SIGCHLD, finish); 1120Sstevel@tonic-gate child = fork(); 1130Sstevel@tonic-gate if (child < 0) { 1140Sstevel@tonic-gate perror("fork"); 1150Sstevel@tonic-gate fail(); 1160Sstevel@tonic-gate } 1170Sstevel@tonic-gate if (child == 0) { 1180Sstevel@tonic-gate subchild = child = fork(); 1190Sstevel@tonic-gate if (child < 0) { 1200Sstevel@tonic-gate perror("fork"); 1210Sstevel@tonic-gate fail(); 1220Sstevel@tonic-gate } 1230Sstevel@tonic-gate if (child) 1240Sstevel@tonic-gate dooutput(); 1250Sstevel@tonic-gate else 1260Sstevel@tonic-gate doshell(); 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate doinput(); 129*334Sdp /* NOTREACHED */ 130*334Sdp return (0); 1310Sstevel@tonic-gate } 1320Sstevel@tonic-gate 133*334Sdp void 1340Sstevel@tonic-gate doinput() 1350Sstevel@tonic-gate { 1360Sstevel@tonic-gate char ibuf[BUFSIZ]; 1370Sstevel@tonic-gate int cc; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate (void) fclose(fscript); 1400Sstevel@tonic-gate sigset(SIGWINCH, sigwinch); 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate while ((cc = read(0, ibuf, BUFSIZ)) != 0) { 1430Sstevel@tonic-gate if (cc == -1) { 1440Sstevel@tonic-gate if (errno == EINTR) { /* SIGWINCH probably */ 1450Sstevel@tonic-gate continue; 1460Sstevel@tonic-gate } else { 1470Sstevel@tonic-gate break; 1480Sstevel@tonic-gate } 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate (void) write(master, ibuf, cc); 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate done(); 1530Sstevel@tonic-gate } 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate void 1560Sstevel@tonic-gate sigwinch() 1570Sstevel@tonic-gate { 1580Sstevel@tonic-gate struct winsize ws; 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate if (ioctl(0, TIOCGWINSZ, &ws) == 0) 1610Sstevel@tonic-gate (void) ioctl(master, TIOCSWINSZ, &ws); 1620Sstevel@tonic-gate } 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate #include <sys/wait.h> 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate void 1670Sstevel@tonic-gate finish() 1680Sstevel@tonic-gate { 1690Sstevel@tonic-gate int status; 1700Sstevel@tonic-gate register int pid; 1710Sstevel@tonic-gate register int die = 0; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate while ((pid = wait(&status)) > 0) 1740Sstevel@tonic-gate if (pid == child) 1750Sstevel@tonic-gate die = 1; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate if (die) 1780Sstevel@tonic-gate done(); 1790Sstevel@tonic-gate } 1800Sstevel@tonic-gate 181*334Sdp void 1820Sstevel@tonic-gate dooutput() 1830Sstevel@tonic-gate { 1840Sstevel@tonic-gate time_t tvec; 1850Sstevel@tonic-gate char obuf[BUFSIZ]; 1860Sstevel@tonic-gate char tbuf[BUFSIZ]; 1870Sstevel@tonic-gate int cc; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate (void) close(0); 1900Sstevel@tonic-gate tvec = time((time_t *)0); 1910Sstevel@tonic-gate strftime(tbuf, BUFSIZ, "%c", localtime(&tvec)); 1920Sstevel@tonic-gate fprintf(fscript, gettext("Script started on %s\n"), tbuf); 1930Sstevel@tonic-gate for (;;) { 1940Sstevel@tonic-gate cc = read(master, obuf, sizeof (obuf)); 1950Sstevel@tonic-gate if (cc <= 0) 1960Sstevel@tonic-gate break; 1970Sstevel@tonic-gate (void) write(1, obuf, cc); 1980Sstevel@tonic-gate (void) fwrite(obuf, 1, cc, fscript); 1990Sstevel@tonic-gate } 2000Sstevel@tonic-gate done(); 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate 203*334Sdp void 2040Sstevel@tonic-gate doshell() 2050Sstevel@tonic-gate { 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate setpgrp(); /* relinquish control terminal */ 2080Sstevel@tonic-gate getslave(); 2090Sstevel@tonic-gate (void) close(master); 2100Sstevel@tonic-gate (void) fclose(fscript); 2110Sstevel@tonic-gate (void) dup2(slave, 0); 2120Sstevel@tonic-gate (void) dup2(slave, 1); 2130Sstevel@tonic-gate (void) dup2(slave, 2); 2140Sstevel@tonic-gate (void) close(slave); 2150Sstevel@tonic-gate execl(shell, shell, "-i", (char *)0); 2160Sstevel@tonic-gate perror(shell); 2170Sstevel@tonic-gate fail(); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate 220*334Sdp void 2210Sstevel@tonic-gate fixtty() 2220Sstevel@tonic-gate { 2230Sstevel@tonic-gate struct termios sbuf; 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate sbuf = b; 2260Sstevel@tonic-gate sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON); 2270Sstevel@tonic-gate sbuf.c_oflag &= ~OPOST; 2280Sstevel@tonic-gate sbuf.c_lflag &= ~(ICANON|ISIG|ECHO); 2290Sstevel@tonic-gate sbuf.c_cc[VMIN] = 1; 2300Sstevel@tonic-gate sbuf.c_cc[VTIME] = 0; 2310Sstevel@tonic-gate (void) ioctl(0, TCSETSF, (char *)&sbuf); 2320Sstevel@tonic-gate } 2330Sstevel@tonic-gate 234*334Sdp void 2350Sstevel@tonic-gate fail() 2360Sstevel@tonic-gate { 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate (void) kill(0, SIGTERM); 2390Sstevel@tonic-gate done(); 2400Sstevel@tonic-gate } 2410Sstevel@tonic-gate 242*334Sdp void 2430Sstevel@tonic-gate done() 2440Sstevel@tonic-gate { 2450Sstevel@tonic-gate time_t tvec; 2460Sstevel@tonic-gate char tbuf[BUFSIZ]; 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate if (subchild) { 2490Sstevel@tonic-gate tvec = time((time_t *)0); 2500Sstevel@tonic-gate strftime(tbuf, BUFSIZ, "%c", localtime(&tvec)); 2510Sstevel@tonic-gate fprintf(fscript, gettext("\nscript done on %s\n"), tbuf); 2520Sstevel@tonic-gate (void) fclose(fscript); 2530Sstevel@tonic-gate (void) close(master); 2540Sstevel@tonic-gate } else { 2550Sstevel@tonic-gate (void) ioctl(0, TCSETSW, (char *)&b); 2560Sstevel@tonic-gate printf(gettext("Script done, file is %s\n"), fname); 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate exit(0); 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate 261*334Sdp void 2620Sstevel@tonic-gate getmaster() 2630Sstevel@tonic-gate { 2640Sstevel@tonic-gate struct stat stb; 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */ 2670Sstevel@tonic-gate (void) ioctl(0, TCGETS, (char *)&b); 2680Sstevel@tonic-gate (void) ioctl(0, TIOCGWINSZ, (char *)&size); 2690Sstevel@tonic-gate return; 2700Sstevel@tonic-gate } else { /* out of pseudo-tty's */ 2710Sstevel@tonic-gate perror(mptname); 2720Sstevel@tonic-gate fprintf(stderr, gettext("Out of pseudo-tty's\n")); 2730Sstevel@tonic-gate fail(); 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate 277*334Sdp void 2780Sstevel@tonic-gate getslave() 2790Sstevel@tonic-gate { 2800Sstevel@tonic-gate char *slavename; /* name of slave pseudo-tty */ 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate grantpt(master); /* change permissions of slave */ 2830Sstevel@tonic-gate unlockpt(master); /* unlock slave */ 2840Sstevel@tonic-gate slavename = ptsname(master); /* get name of slave */ 2850Sstevel@tonic-gate slave = open(slavename, O_RDWR); /* open slave */ 2860Sstevel@tonic-gate if (slave < 0) { /* error on opening slave */ 2870Sstevel@tonic-gate perror(slavename); 2880Sstevel@tonic-gate fail(); 2890Sstevel@tonic-gate } 2900Sstevel@tonic-gate ioctl(slave, I_PUSH, "ptem"); /* push pt hw emulation module */ 2910Sstevel@tonic-gate ioctl(slave, I_PUSH, "ldterm"); /* push line discipline */ 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate (void) ioctl(slave, TCSETSF, (char *)&b); 2940Sstevel@tonic-gate (void) ioctl(slave, TIOCSWINSZ, (char *)&size); 2950Sstevel@tonic-gate } 296