+static void
+charshunt(ifd, ofd, record_file)
+ int ifd, ofd;
+ char *record_file;
+{
+ int n, nfds;
+ fd_set ready, writey;
+ u_char *ibufp, *obufp;
+ int nibuf, nobuf;
+ int flags;
+ int pty_readable, stdin_readable;
+ struct timeval lasttime;
+ FILE *recordf = NULL;
+ int ilevel, olevel, max_level;
+ struct timeval levelt, tout, *top;
+
+ /*
+ * Reset signal handlers.
+ */
+ signal(SIGHUP, SIG_IGN); /* Hangup */
+ signal(SIGINT, SIG_DFL); /* Interrupt */
+ signal(SIGTERM, SIG_DFL); /* Terminate */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+#ifdef SIGBUS
+ signal(SIGBUS, SIG_DFL);
+#endif
+#ifdef SIGEMT
+ signal(SIGEMT, SIG_DFL);
+#endif
+#ifdef SIGPOLL
+ signal(SIGPOLL, SIG_DFL);
+#endif
+#ifdef SIGPROF
+ signal(SIGPROF, SIG_DFL);
+#endif
+#ifdef SIGSYS
+ signal(SIGSYS, SIG_DFL);
+#endif
+#ifdef SIGTRAP
+ signal(SIGTRAP, SIG_DFL);
+#endif
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, SIG_DFL);
+#endif
+#ifdef SIGXCPU
+ signal(SIGXCPU, SIG_DFL);
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_DFL);
+#endif
+
+ /*
+ * Open the record file if required.
+ */
+ if (record_file != NULL) {
+ recordf = fopen(record_file, "a");
+ if (recordf == NULL)
+ error("Couldn't create record file %s: %m", record_file);
+ }
+
+ /* set all the fds to non-blocking mode */
+ flags = fcntl(pty_master, F_GETFL);
+ if (flags == -1
+ || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set pty master to nonblock: %m");
+ flags = fcntl(ifd, F_GETFL);
+ if (flags == -1
+ || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
+ if (ofd != ifd) {
+ flags = fcntl(ofd, F_GETFL);
+ if (flags == -1
+ || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set stdout to nonblock: %m");
+ }
+
+ nibuf = nobuf = 0;
+ ibufp = obufp = NULL;
+ pty_readable = stdin_readable = 1;
+
+ ilevel = olevel = 0;
+ gettimeofday(&levelt, NULL);
+ if (max_data_rate) {
+ max_level = max_data_rate / 10;
+ if (max_level < 100)
+ max_level = 100;
+ } else
+ max_level = sizeof(inpacket_buf) + 1;
+
+ nfds = (ofd > pty_master? ofd: pty_master) + 1;
+ if (recordf != NULL) {
+ gettimeofday(&lasttime, NULL);
+ putc(7, recordf); /* put start marker */
+ putc(lasttime.tv_sec >> 24, recordf);
+ putc(lasttime.tv_sec >> 16, recordf);
+ putc(lasttime.tv_sec >> 8, recordf);
+ putc(lasttime.tv_sec, recordf);
+ lasttime.tv_usec = 0;
+ }
+
+ while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
+ top = 0;
+ tout.tv_sec = 0;
+ tout.tv_usec = 10000;
+ FD_ZERO(&ready);
+ FD_ZERO(&writey);
+ if (nibuf != 0) {
+ if (ilevel >= max_level)
+ top = &tout;
+ else
+ FD_SET(pty_master, &writey);
+ } else if (stdin_readable)
+ FD_SET(ifd, &ready);
+ if (nobuf != 0) {
+ if (olevel >= max_level)
+ top = &tout;
+ else
+ FD_SET(ofd, &writey);
+ } else if (pty_readable)
+ FD_SET(pty_master, &ready);
+ if (select(nfds, &ready, &writey, NULL, top) < 0) {
+ if (errno != EINTR)
+ fatal("select");
+ continue;
+ }
+ if (max_data_rate) {
+ double dt;
+ int nbt;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ dt = (now.tv_sec - levelt.tv_sec
+ + (now.tv_usec - levelt.tv_usec) / 1e6);
+ nbt = (int)(dt * max_data_rate);
+ ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
+ olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
+ levelt = now;
+ } else
+ ilevel = olevel = 0;
+ if (FD_ISSET(ifd, &ready)) {
+ ibufp = inpacket_buf;
+ nibuf = read(ifd, ibufp, sizeof(inpacket_buf));
+ if (nibuf < 0 && errno == EIO)
+ nibuf = 0;
+ if (nibuf < 0) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading standard input: %m");
+ break;
+ }
+ nibuf = 0;
+ } else if (nibuf == 0) {
+ /* end of file from stdin */
+ stdin_readable = 0;
+ /* do a 0-length write, hopefully this will generate
+ an EOF (hangup) on the slave side. */
+ write(pty_master, inpacket_buf, 0);
+ if (recordf)
+ if (!record_write(recordf, 4, NULL, 0, &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(pty_master, &writey);
+ if (recordf)
+ if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (FD_ISSET(pty_master, &ready)) {
+ obufp = outpacket_buf;
+ nobuf = read(pty_master, obufp, sizeof(outpacket_buf));
+ if (nobuf < 0 && errno == EIO)
+ nobuf = 0;
+ if (nobuf < 0) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading pseudo-tty master: %m");
+ break;
+ }
+ nobuf = 0;
+ } else if (nobuf == 0) {
+ /* end of file from the pty - slave side has closed */
+ pty_readable = 0;
+ stdin_readable = 0; /* pty is not writable now */
+ nibuf = 0;
+ close(ofd);
+ if (recordf)
+ if (!record_write(recordf, 3, NULL, 0, &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(ofd, &writey);
+ if (recordf)
+ if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (FD_ISSET(ofd, &writey)) {
+ n = nobuf;
+ if (olevel + n > max_level)
+ n = max_level - olevel;
+ n = write(ofd, obufp, n);
+ if (n < 0) {
+ if (errno != EIO) {
+ error("Error writing standard output: %m");
+ break;
+ }
+ pty_readable = 0;
+ nobuf = 0;
+ } else {
+ obufp += n;
+ nobuf -= n;
+ olevel += n;
+ }
+ }
+ if (FD_ISSET(pty_master, &writey)) {
+ n = nibuf;
+ if (ilevel + n > max_level)
+ n = max_level - ilevel;
+ n = write(pty_master, ibufp, n);
+ if (n < 0) {
+ if (errno != EIO) {
+ error("Error writing pseudo-tty master: %m");
+ break;
+ }
+ stdin_readable = 0;
+ nibuf = 0;
+ } else {
+ ibufp += n;
+ nibuf -= n;
+ ilevel += n;
+ }
+ }
+ }
+ exit(0);
+}
+
+static int
+record_write(f, code, buf, nb, tp)
+ FILE *f;
+ int code;
+ u_char *buf;
+ int nb;
+ struct timeval *tp;