1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
8
9
10 /*
11 * Copyright (c) 1980 Regents of the University of California.
12 * All rights reserved. The Berkeley software License Agreement
13 * specifies the terms and conditions for redistribution.
14 */
15
16 /* Portions Copyright(c) 1988, Sun Microsystems, Inc. */
17 /* All Rights Reserved. */
18
19 /*
20 * script: Produce a record of a terminal session.
21 */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <signal.h>
26 #include <fcntl.h>
27 #include <locale.h>
28 #include <time.h>
29 #include <sys/stropts.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/termios.h>
33 #include <sys/file.h>
34 #include <errno.h>
35
36 int grantpt();
37 int unlockpt();
38 char *ptsname();
39 void doinput() __NORETURN;
40 void dooutput();
41 void doshell();
42 void fixtty();
43 void fail();
44 void done() __NORETURN;
45 void getmanager();
46 void getsubsid();
47
48 char *shell;
49 FILE *fscript;
50 int manager; /* file descriptor for manager pseudo-tty */
51 int subsid; /* file descriptor for subsidiary pseudo-tty */
52 int child;
53 int subchild;
54 char *fname = "typescript";
55 void sigwinch();
56 void finish();
57
58 struct termios b;
59 struct winsize size;
60 int lb;
61 int l;
62 char *mptname = "/dev/ptmx"; /* manager pseudo-tty device */
63
64 int aflg;
65
66 int
main(int argc,char * argv[])67 main(int argc, char *argv[])
68 {
69 uid_t ruidt;
70 gid_t gidt;
71
72 (void) setlocale(LC_ALL, "");
73 #if !defined(TEXT_DOMAIN)
74 #define TEXT_DOMAIN "SYS_TEST"
75 #endif
76 (void) textdomain(TEXT_DOMAIN);
77
78 shell = getenv("SHELL");
79 if (shell == NULL)
80 shell = "/bin/sh";
81 argc--, argv++;
82 while (argc > 0 && argv[0][0] == '-') {
83 switch (argv[0][1]) {
84
85 case 'a':
86 aflg++;
87 break;
88
89 default:
90 fprintf(stderr,
91 gettext("usage: script [ -a ] [ typescript ]\n"));
92 exit(1);
93 }
94 argc--, argv++;
95 }
96 if (argc > 0)
97 fname = argv[0];
98 ruidt = getuid();
99 gidt = getgid();
100 if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
101 perror(fname);
102 fail();
103 }
104 setbuf(fscript, NULL);
105 chown(fname, ruidt, gidt);
106 getmanager();
107 printf(gettext("Script started, file is %s\n"), fname);
108 fixtty();
109
110 (void) signal(SIGCHLD, finish);
111 child = fork();
112 if (child < 0) {
113 perror("fork");
114 fail();
115 }
116 if (child == 0) {
117 subchild = child = fork();
118 if (child < 0) {
119 perror("fork");
120 fail();
121 }
122 if (child)
123 dooutput();
124 else
125 doshell();
126 }
127 doinput();
128 /* NOTREACHED */
129 return (0);
130 }
131
132 void
doinput()133 doinput()
134 {
135 char ibuf[BUFSIZ];
136 int cc;
137
138 (void) fclose(fscript);
139 sigset(SIGWINCH, sigwinch);
140
141 while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
142 if (cc == -1) {
143 if (errno == EINTR) { /* SIGWINCH probably */
144 continue;
145 } else {
146 break;
147 }
148 }
149 (void) write(manager, ibuf, cc);
150 }
151 done();
152 }
153
154 void
sigwinch()155 sigwinch()
156 {
157 struct winsize ws;
158
159 if (ioctl(0, TIOCGWINSZ, &ws) == 0)
160 (void) ioctl(manager, TIOCSWINSZ, &ws);
161 }
162
163 #include <sys/wait.h>
164
165 void
finish()166 finish()
167 {
168 int status;
169 register int pid;
170 register int die = 0;
171
172 while ((pid = wait(&status)) > 0)
173 if (pid == child)
174 die = 1;
175
176 if (die)
177 done();
178 }
179
180 void
dooutput()181 dooutput()
182 {
183 time_t tvec;
184 char obuf[BUFSIZ];
185 char tbuf[BUFSIZ];
186 int cc;
187
188 (void) close(0);
189 tvec = time((time_t *)0);
190 strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
191 fprintf(fscript, gettext("Script started on %s\n"), tbuf);
192 for (;;) {
193 cc = read(manager, obuf, sizeof (obuf));
194 if (cc <= 0)
195 break;
196 (void) write(1, obuf, cc);
197 (void) fwrite(obuf, 1, cc, fscript);
198 }
199 done();
200 }
201
202 void
doshell()203 doshell()
204 {
205
206 setpgrp(); /* relinquish control terminal */
207 getsubsid();
208 (void) close(manager);
209 (void) fclose(fscript);
210 (void) dup2(subsid, 0);
211 (void) dup2(subsid, 1);
212 (void) dup2(subsid, 2);
213 (void) close(subsid);
214 execl(shell, shell, "-i", (char *)0);
215 perror(shell);
216 fail();
217 }
218
219 void
fixtty()220 fixtty()
221 {
222 struct termios sbuf;
223
224 sbuf = b;
225 sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
226 sbuf.c_oflag &= ~OPOST;
227 sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
228 sbuf.c_cc[VMIN] = 1;
229 sbuf.c_cc[VTIME] = 0;
230 (void) ioctl(0, TCSETSF, (char *)&sbuf);
231 }
232
233 void
fail()234 fail()
235 {
236
237 (void) kill(0, SIGTERM);
238 done();
239 }
240
241 void
done()242 done()
243 {
244 time_t tvec;
245 char tbuf[BUFSIZ];
246
247 if (subchild) {
248 tvec = time((time_t *)0);
249 strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
250 fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
251 (void) fclose(fscript);
252 (void) close(manager);
253 } else {
254 (void) ioctl(0, TCSETSW, (char *)&b);
255 printf(gettext("Script done, file is %s\n"), fname);
256 }
257 exit(0);
258 }
259
260 void
getmanager()261 getmanager()
262 {
263 struct stat stb;
264
265 if ((manager = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
266 (void) ioctl(0, TCGETS, (char *)&b);
267 (void) ioctl(0, TIOCGWINSZ, (char *)&size);
268 return;
269 } else { /* out of pseudo-tty's */
270 perror(mptname);
271 fprintf(stderr, gettext("Out of pseudo-tty's\n"));
272 fail();
273 }
274 }
275
276 void
getsubsid()277 getsubsid()
278 {
279 char *subsidname; /* name of subsidiary pseudo-tty */
280
281 grantpt(manager); /* change permissions of subsidiary */
282 unlockpt(manager); /* unlock subsidiary */
283 subsidname = ptsname(manager); /* get name of subsidiary */
284 subsid = open(subsidname, O_RDWR); /* open subsidiary */
285 if (subsid < 0) { /* error opening subsidiary */
286 perror(subsidname);
287 fail();
288 }
289 ioctl(subsid, I_PUSH, "ptem"); /* push pt hw emulation module */
290 ioctl(subsid, I_PUSH, "ldterm"); /* push line discipline */
291
292 (void) ioctl(subsid, TCSETSF, (char *)&b);
293 (void) ioctl(subsid, TIOCSWINSZ, (char *)&size);
294 }
295