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