xref: /original-bsd/usr.bin/script/script.c (revision 2ce9ec30)
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 are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #ifndef lint
14 char copyright[] =
15 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
16  All rights reserved.\n";
17 #endif /* not lint */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#)script.c	5.5 (Berkeley) 06/03/88";
21 #endif /* not lint */
22 
23 /*
24  * script
25  */
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <sys/time.h>
30 #include <sys/file.h>
31 #include <stdio.h>
32 #include <signal.h>
33 
34 char	*shell;
35 FILE	*fscript;
36 int	master;
37 int	slave;
38 int	child;
39 int	subchild;
40 char	*fname;
41 
42 struct	sgttyb b;
43 struct	tchars tc;
44 struct	ltchars lc;
45 struct	winsize win;
46 int	lb;
47 int	l;
48 char	*line = "/dev/ptyXX";
49 int	aflg;
50 
51 main(argc, argv)
52 	int argc;
53 	char *argv[];
54 {
55 	extern char *optarg;
56 	extern int optind;
57 	int ch;
58 	int finish();
59 	char *getenv();
60 
61 	while ((ch = getopt(argc, argv, "a")) != EOF)
62 		switch((char)ch) {
63 		case 'a':
64 			aflg++;
65 			break;
66 		case '?':
67 		default:
68 			fprintf(stderr, "usage: script [-a] [file]\n");
69 			exit(1);
70 		}
71 	argc -= optind;
72 	argv += optind;
73 
74 	if (argc > 0)
75 		fname = argv[0];
76 	else
77 		fname = "typescript";
78 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
79 		perror(fname);
80 		fail();
81 	}
82 
83 	shell = getenv("SHELL");
84 	if (shell == NULL)
85 		shell = "/bin/sh";
86 
87 	getmaster();
88 	printf("Script started, file is %s\n", fname);
89 	fixtty();
90 
91 	(void) signal(SIGCHLD, finish);
92 	child = fork();
93 	if (child < 0) {
94 		perror("fork");
95 		fail();
96 	}
97 	if (child == 0) {
98 		subchild = child = fork();
99 		if (child < 0) {
100 			perror("fork");
101 			fail();
102 		}
103 		if (child)
104 			dooutput();
105 		else
106 			doshell();
107 	}
108 	doinput();
109 }
110 
111 doinput()
112 {
113 	register int cc;
114 	char ibuf[BUFSIZ];
115 
116 	(void) fclose(fscript);
117 	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
118 		(void) write(master, ibuf, cc);
119 	done();
120 }
121 
122 #include <sys/wait.h>
123 
124 finish()
125 {
126 	union wait status;
127 	register int pid;
128 	register int die = 0;
129 
130 	while ((pid = wait3(&status, WNOHANG, 0)) > 0)
131 		if (pid == child)
132 			die = 1;
133 
134 	if (die)
135 		done();
136 }
137 
138 dooutput()
139 {
140 	register int cc;
141 	time_t tvec, time();
142 	char obuf[BUFSIZ], *ctime();
143 
144 	(void) close(0);
145 	tvec = time((time_t *)NULL);
146 	fprintf(fscript, "Script started on %s", ctime(&tvec));
147 	for (;;) {
148 		cc = read(master, obuf, sizeof (obuf));
149 		if (cc <= 0)
150 			break;
151 		(void) write(1, obuf, cc);
152 		(void) fwrite(obuf, 1, cc, fscript);
153 	}
154 	done();
155 }
156 
157 doshell()
158 {
159 	int t;
160 
161 	t = open("/dev/tty", O_RDWR);
162 	if (t >= 0) {
163 		(void) ioctl(t, TIOCNOTTY, (char *)0);
164 		(void) close(t);
165 	}
166 	getslave();
167 	(void) close(master);
168 	(void) fclose(fscript);
169 	(void) dup2(slave, 0);
170 	(void) dup2(slave, 1);
171 	(void) dup2(slave, 2);
172 	(void) close(slave);
173 	execl(shell, "sh", "-i", 0);
174 	perror(shell);
175 	fail();
176 }
177 
178 fixtty()
179 {
180 	struct sgttyb sbuf;
181 
182 	sbuf = b;
183 	sbuf.sg_flags |= RAW;
184 	sbuf.sg_flags &= ~ECHO;
185 	(void) ioctl(0, TIOCSETP, (char *)&sbuf);
186 }
187 
188 fail()
189 {
190 
191 	(void) kill(0, SIGTERM);
192 	done();
193 }
194 
195 done()
196 {
197 	time_t tvec, time();
198 	char *ctime();
199 
200 	if (subchild) {
201 		tvec = time((time_t *)NULL);
202 		fprintf(fscript,"\nscript done on %s", ctime(&tvec));
203 		(void) fclose(fscript);
204 		(void) close(master);
205 	} else {
206 		(void) ioctl(0, TIOCSETP, (char *)&b);
207 		printf("Script done, file is %s\n", fname);
208 	}
209 	exit(0);
210 }
211 
212 getmaster()
213 {
214 	char *pty, *bank, *cp;
215 	struct stat stb;
216 
217 	pty = &line[strlen("/dev/ptyp")];
218 	for (bank = "pqrs"; *bank; bank++) {
219 		line[strlen("/dev/pty")] = *bank;
220 		*pty = '0';
221 		if (stat(line, &stb) < 0)
222 			break;
223 		for (cp = "0123456789abcdef"; *cp; cp++) {
224 			*pty = *cp;
225 			master = open(line, O_RDWR);
226 			if (master >= 0) {
227 				char *tp = &line[strlen("/dev/")];
228 				int ok;
229 
230 				/* verify slave side is usable */
231 				*tp = 't';
232 				ok = access(line, R_OK|W_OK) == 0;
233 				*tp = 'p';
234 				if (ok) {
235 				    (void) ioctl(0, TIOCGETP, (char *)&b);
236 				    (void) ioctl(0, TIOCGETC, (char *)&tc);
237 				    (void) ioctl(0, TIOCGETD, (char *)&l);
238 				    (void) ioctl(0, TIOCGLTC, (char *)&lc);
239 				    (void) ioctl(0, TIOCLGET, (char *)&lb);
240 				    (void) ioctl(0, TIOCGWINSZ, (char *)&win);
241 					return;
242 				}
243 				(void) close(master);
244 			}
245 		}
246 	}
247 	fprintf(stderr, "Out of pty's\n");
248 	fail();
249 }
250 
251 getslave()
252 {
253 
254 	line[strlen("/dev/")] = 't';
255 	slave = open(line, O_RDWR);
256 	if (slave < 0) {
257 		perror(line);
258 		fail();
259 	}
260 	(void) ioctl(slave, TIOCSETP, (char *)&b);
261 	(void) ioctl(slave, TIOCSETC, (char *)&tc);
262 	(void) ioctl(slave, TIOCSLTC, (char *)&lc);
263 	(void) ioctl(slave, TIOCLSET, (char *)&lb);
264 	(void) ioctl(slave, TIOCSETD, (char *)&l);
265 	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
266 }
267