xref: /illumos-gate/usr/src/cmd/script/script.c (revision 1fa2a664)
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