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, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /* 1999-02-22 Arkadiusz Mi�kiewicz <misiek@misiek.eu.org>
35  * - added Native Language Support
36  */
37 
38 /* 2000-12-27 Satoru Takabayashi <satoru@namazu.org>
39  * - modify `script' to create `ttyrec'.
40  */
41 
42 /*
43  * script
44  */
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <termios.h>
48 #include <sys/ioctl.h>
49 #include <sys/time.h>
50 #include <sys/file.h>
51 #include <sys/signal.h>
52 #include <stdio.h>
53 #include <time.h>
54 #include <unistd.h>
55 #include <string.h>
56 #include <stdlib.h>
57 
58 #if defined(SVR4)
59 #include <fcntl.h>
60 #include <stropts.h>
61 #endif /* SVR4 */
62 
63 #include <sys/time.h>
64 #include "ttyrec.h"
65 #include "io.h"
66 
67 #define HAVE_inet_aton
68 #define HAVE_scsi_h
69 #define HAVE_kd_h
70 
71 #define _(FOO) FOO
72 
73 #ifdef HAVE_openpty
74 #include <libutil.h>
75 #endif
76 
77 #if defined(SVR4) && !defined(CDEL)
78 #if defined(_POSIX_VDISABLE)
79 #define CDEL _POSIX_VDISABLE
80 #elif defined(CDISABLE)
81 #define CDEL CDISABLE
82 #else /* not _POSIX_VISIBLE && not CDISABLE */
83 #define CDEL 255
84 #endif /* not _POSIX_VISIBLE && not CDISABLE */
85 #endif /* SVR4 && ! CDEL */
86 
87 void done(void);
88 void fail(void);
89 void fixtty(void);
90 void getmaster(void);
91 void getslave(void);
92 void doinput(void);
93 void dooutput(void);
94 void doshell(const char*);
95 
96 char	*shell;
97 FILE	*fscript;
98 int	master;
99 int	slave;
100 int	child;
101 int	subchild;
102 char	*fname;
103 
104 struct	termios tt;
105 struct	winsize win;
106 int	lb;
107 int	l;
108 #if !defined(SVR4)
109 #ifndef HAVE_openpty
110 char	line[] = "/dev/ptyXX";
111 #endif
112 #endif /* !SVR4 */
113 int	aflg;
114 int	uflg;
115 
116 int
main(argc,argv)117 main(argc, argv)
118 	int argc;
119 	char *argv[];
120 {
121 	extern int optind;
122 	int ch;
123 	void finish();
124 	char *getenv();
125 	char *command = NULL;
126 
127 	while ((ch = getopt(argc, argv, "aue:h?")) != EOF)
128 		switch((char)ch) {
129 		case 'a':
130 			aflg++;
131 			break;
132 		case 'u':
133 		        uflg++;
134 			break;
135 		case 'e':
136 			command = strdup(optarg);
137 			break;
138 		case 'h':
139 		case '?':
140 		default:
141 			fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [file]\n"));
142 			exit(1);
143 		}
144 	argc -= optind;
145 	argv += optind;
146 
147 	if (argc > 0)
148 		fname = argv[0];
149 	else
150 		fname = "ttyrecord";
151 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
152 		perror(fname);
153 		fail();
154 	}
155 	setbuf(fscript, NULL);
156 
157 	shell = getenv("SHELL");
158 	if (shell == NULL)
159 		shell = "/bin/sh";
160 
161 	getmaster();
162 	fixtty();
163 
164 	(void) signal(SIGCHLD, finish);
165 	child = fork();
166 	if (child < 0) {
167 		perror("fork");
168 		fail();
169 	}
170 	if (child == 0) {
171 		subchild = child = fork();
172 		if (child < 0) {
173 			perror("fork");
174 			fail();
175 		}
176 		if (child)
177 			dooutput();
178 		else
179 			doshell(command);
180 	}
181 	doinput();
182 
183 	return 0;
184 }
185 
186 void
doinput()187 doinput()
188 {
189 	register int cc;
190 	char ibuf[BUFSIZ];
191 
192 	(void) fclose(fscript);
193 #ifdef HAVE_openpty
194 	(void) close(slave);
195 #endif
196 	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
197 		(void) write(master, ibuf, cc);
198 	done();
199 }
200 
201 #include <sys/wait.h>
202 
203 void
finish()204 finish()
205 {
206 	int status;
207 	register int pid;
208 	register int die = 0;
209 
210 	while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
211 		if (pid == child)
212 			die = 1;
213 
214 	if (die)
215 		done();
216 }
217 
218 struct linebuf {
219     char str[BUFSIZ + 1]; /* + 1 for an additional NULL character.*/
220     int len;
221 };
222 
223 
224 void
check_line(const char * line)225 check_line (const char *line)
226 {
227     static int uuencode_mode = 0;
228     static FILE *uudecode;
229 
230     if (uuencode_mode == 1) {
231 	fprintf(uudecode, "%s", line);
232 	if (strcmp(line, "end\n") == 0) {
233 	    pclose(uudecode);
234 	    uuencode_mode = 0;
235 	}
236     } else {
237 	int dummy; char dummy2[BUFSIZ];
238 	if (sscanf(line, "begin %o %s", &dummy, dummy2) == 2) {
239 	    /*
240 	     * uuencode line found!
241 	     */
242 	    uudecode = popen("uudecode", "w");
243 	    fprintf(uudecode, "%s", line);
244 	    uuencode_mode = 1;
245 	}
246     }
247 }
248 
249 void
check_output(const char * str,int len)250 check_output(const char *str, int len)
251 {
252     static struct linebuf lbuf = {"", 0};
253     int i;
254 
255     for (i = 0; i < len; i++) {
256 	if (lbuf.len < BUFSIZ) {
257 	    lbuf.str[lbuf.len] = str[i];
258 	    if (lbuf.str[lbuf.len] == '\r') {
259 		lbuf.str[lbuf.len] = '\n';
260 	    }
261 	    lbuf.len++;
262 	    if (lbuf.str[lbuf.len - 1] == '\n') {
263 		if (lbuf.len > 1) { /* skip a blank line. */
264 		    lbuf.str[lbuf.len] = '\0';
265 		    check_line(lbuf.str);
266 		}
267 		lbuf.len = 0;
268 	    }
269 	} else {/* buffer overflow */
270 	    lbuf.len = 0;
271 	}
272     }
273 }
274 
275 void
dooutput()276 dooutput()
277 {
278 	int cc;
279 	char obuf[BUFSIZ];
280 
281 	setbuf(stdout, NULL);
282 	(void) close(0);
283 #ifdef HAVE_openpty
284 	(void) close(slave);
285 #endif
286 	for (;;) {
287 		Header h;
288 
289 		cc = read(master, obuf, BUFSIZ);
290 		if (cc <= 0)
291 			break;
292 		if (uflg)
293 		    check_output(obuf, cc);
294 		h.len = cc;
295 		gettimeofday(&h.tv, NULL);
296 		(void) write(1, obuf, cc);
297 		(void) write_header(fscript, &h);
298 		(void) fwrite(obuf, 1, cc, fscript);
299 	}
300 	done();
301 }
302 
303 void
doshell(const char * command)304 doshell(const char* command)
305 {
306 	/***
307 	int t;
308 
309 	t = open(_PATH_TTY, O_RDWR);
310 	if (t >= 0) {
311 		(void) ioctl(t, TIOCNOTTY, (char *)0);
312 		(void) close(t);
313 	}
314 	***/
315 	getslave();
316 	(void) close(master);
317 	(void) fclose(fscript);
318 	(void) dup2(slave, 0);
319 	(void) dup2(slave, 1);
320 	(void) dup2(slave, 2);
321 	(void) close(slave);
322 
323 	if (!command) {
324 		execl(shell, strrchr(shell, '/') + 1, "-i", 0);
325 	} else {
326 		execl(shell, strrchr(shell, '/') + 1, "-c", command, 0);
327 	}
328 	perror(shell);
329 	fail();
330 }
331 
332 void
fixtty()333 fixtty()
334 {
335 	struct termios rtt;
336 
337 	rtt = tt;
338 #if defined(SVR4)
339 	rtt.c_iflag = 0;
340 	rtt.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL);
341 	rtt.c_oflag = OPOST;
342 	rtt.c_cc[VINTR] = CDEL;
343 	rtt.c_cc[VQUIT] = CDEL;
344 	rtt.c_cc[VERASE] = CDEL;
345 	rtt.c_cc[VKILL] = CDEL;
346 	rtt.c_cc[VEOF] = 1;
347 	rtt.c_cc[VEOL] = 0;
348 #else /* !SVR4 */
349 	cfmakeraw(&rtt);
350 	rtt.c_lflag &= ~ECHO;
351 #endif /* !SVR4 */
352 	(void) tcsetattr(0, TCSAFLUSH, &rtt);
353 }
354 
355 void
fail()356 fail()
357 {
358 
359 	(void) kill(0, SIGTERM);
360 	done();
361 }
362 
363 void
done()364 done()
365 {
366 	if (subchild) {
367 		(void) fclose(fscript);
368 		(void) close(master);
369 	} else {
370 		(void) tcsetattr(0, TCSAFLUSH, &tt);
371 	}
372 	exit(0);
373 }
374 
375 void
getmaster()376 getmaster()
377 {
378 #if defined(SVR4)
379 	(void) tcgetattr(0, &tt);
380 	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
381 	if ((master = open("/dev/ptmx", O_RDWR)) < 0) {
382 		perror("open(\"/dev/ptmx\", O_RDWR)");
383 		fail();
384 	}
385 #else /* !SVR4 */
386 #ifdef HAVE_openpty
387 	(void) tcgetattr(0, &tt);
388 	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
389 	if (openpty(&master, &slave, NULL, &tt, &win) < 0) {
390 		fprintf(stderr, _("openpty failed\n"));
391 		fail();
392 	}
393 #else
394 #ifdef HAVE_getpt
395 	if ((master = getpt()) < 0) {
396 		perror("getpt()");
397 		fail();
398 	}
399 #else
400 	char *pty, *bank, *cp;
401 	struct stat stb;
402 
403 	pty = &line[strlen("/dev/ptyp")];
404 	for (bank = "pqrs"; *bank; bank++) {
405 		line[strlen("/dev/pty")] = *bank;
406 		*pty = '0';
407 		if (stat(line, &stb) < 0)
408 			break;
409 		for (cp = "0123456789abcdef"; *cp; cp++) {
410 			*pty = *cp;
411 			master = open(line, O_RDWR);
412 			if (master >= 0) {
413 				char *tp = &line[strlen("/dev/")];
414 				int ok;
415 
416 				/* verify slave side is usable */
417 				*tp = 't';
418 				ok = access(line, R_OK|W_OK) == 0;
419 				*tp = 'p';
420 				if (ok) {
421 					(void) tcgetattr(0, &tt);
422 				    	(void) ioctl(0, TIOCGWINSZ,
423 						(char *)&win);
424 					return;
425 				}
426 				(void) close(master);
427 			}
428 		}
429 	}
430 	fprintf(stderr, _("Out of pty's\n"));
431 	fail();
432 #endif /* not HAVE_getpt */
433 #endif /* not HAVE_openpty */
434 #endif /* !SVR4 */
435 }
436 
437 void
getslave()438 getslave()
439 {
440 #if defined(SVR4)
441 	(void) setsid();
442 	grantpt( master);
443 	unlockpt(master);
444 	if ((slave = open((const char *)ptsname(master), O_RDWR)) < 0) {
445 		perror("open(fd, O_RDWR)");
446 		fail();
447 	}
448 	if (isastream(slave)) {
449 		if (ioctl(slave, I_PUSH, "ptem") < 0) {
450 			perror("ioctl(fd, I_PUSH, ptem)");
451 			fail();
452 		}
453 		if (ioctl(slave, I_PUSH, "ldterm") < 0) {
454 			perror("ioctl(fd, I_PUSH, ldterm)");
455 			fail();
456 		}
457 #ifndef _HPUX_SOURCE
458 		if (ioctl(slave, I_PUSH, "ttcompat") < 0) {
459 			perror("ioctl(fd, I_PUSH, ttcompat)");
460 			fail();
461 		}
462 #endif
463 		(void) ioctl(0, TIOCGWINSZ, (char *)&win);
464 	}
465 #else /* !SVR4 */
466 #ifndef HAVE_openpty
467 	line[strlen("/dev/")] = 't';
468 	slave = open(line, O_RDWR);
469 	if (slave < 0) {
470 		perror(line);
471 		fail();
472 	}
473 	(void) tcsetattr(slave, TCSAFLUSH, &tt);
474 	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
475 #endif
476 	(void) setsid();
477 	(void) ioctl(slave, TIOCSCTTY, 0);
478 #endif /* SVR4 */
479 }
480