summaryrefslogtreecommitdiffstats
path: root/bin/hpuifilter.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/hpuifilter.c')
-rw-r--r--bin/hpuifilter.c662
1 files changed, 485 insertions, 177 deletions
diff --git a/bin/hpuifilter.c b/bin/hpuifilter.c
index 4747299..a13c223 100644
--- a/bin/hpuifilter.c
+++ b/bin/hpuifilter.c
@@ -1,5 +1,5 @@
/*
- * $Id: hpuifilter.c,v 1.21 2005/06/14 20:20:43 heas Exp $
+ * $Id: hpuifilter.c,v 1.30 2005/09/29 16:58:16 heas Exp $
*
* Copyright (C) 1997-2004 by Terrapin Communications, Inc.
* All rights reserved.
@@ -21,53 +21,117 @@
* point of hpfilter is to filter all the bloody vt100 (curses) escape codes
* that the HP procurve switches belch out, which are a real bitch to handle
* in hlogin.
+ *
+ * Modified openpty() from NetBSD:
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
-#define DFLT_TO 60 /* default timeout */
#include "config.h"
#include "version.h"
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
#include <stdio.h>
#include <limits.h>
#include <fcntl.h>
+#include <poll.h>
+#if HAVE_PTY_H
+# include <pty.h>
+#endif
+#include <regex.h>
#include <signal.h>
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_PTMX && HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <sys/time.h>
-#include <regex.h>
-
#include <termios.h>
+#if HAVE_UTIL_H
+# include <util.h>
+#endif
#define BUFSZ (LINE_MAX * 2)
-char *progname;
-int debug = 0;
+char **environ,
+ *progname;
+int child,
+ debug,
+ drain,
+ timeo = 5; /* default timeout */
int filter __P((char *, int));
+RETSIGTYPE reapchild __P((void));
+#if !HAVE_OPENPTY
+int openpty(int *, int *, char *, struct termios *,
+ struct winsize *);
+#endif
+RETSIGTYPE sighdlr __P((int));
+#if !HAVE_UNSETENV
+int unsetenv __P((const char *));
+#endif
void usage __P((void));
void vers __P((void));
-RETSIGTYPE reapchild __P((void));
int
-main(int argc, char **argv)
+main(int argc, char **argv, char **ev)
{
extern char *optarg;
extern int optind;
char ch,
hbuf[BUFSZ], /* hlogin buffer */
- *hbufp,
+ ptyname[FILENAME_MAX + 1],
tbuf[BUFSZ], /* telnet/ssh buffer */
*tbufp;
int bytes, /* bytes read/written */
- child,
- r[2], /* recv pipe */
- s[2]; /* send pipe */
+ devnull,
+ rval = EX_OK,
+ ptym, /* master pty */
+ ptys; /* slave pty */
ssize_t hlen = 0, /* len of hbuf */
tlen = 0; /* len of tbuf */
- struct timeval to = { DFLT_TO, 0 };
- fd_set rfds, /* select() */
- wfds;
+ struct pollfd pfds[3];
struct termios tios;
+ environ = ev;
+
/* get just the basename() of our exec() name and strip a .* off the end */
if ((progname = strrchr(argv[0], '/')) != NULL)
progname += 1;
@@ -76,11 +140,16 @@ main(int argc, char **argv)
if (strrchr(progname, '.') != NULL)
*(strrchr(progname, '.')) = '\0';
- while ((ch = getopt(argc, argv, "dhv")) != -1 )
+ while ((ch = getopt(argc, argv, "dhvt:")) != -1 )
switch (ch) {
case 'd':
debug++;
break;
+ case 't':
+ timeo = atoi(optarg);
+ if (timeo < 1)
+ timeo = 1;
+ break;
case 'v':
vers();
return(EX_OK);
@@ -95,26 +164,40 @@ main(int argc, char **argv)
return(EX_USAGE);
}
- /* reap our children */
- signal(SIGCHLD, (void *) reapchild);
- signal(SIGHUP, (void *) reapchild);
- signal(SIGINT, (void *) reapchild);
- signal(SIGTERM, (void *) reapchild);
+ unsetenv("DISPLAY");
- /* create 2 pipes for send/recv and then fork and exec telnet/ssh */
+ /* allocate pty for telnet/ssh, then fork and exec */
for (child = 3; child < 10; child++)
close(child);
- if (pipe(s) || pipe(r)) {
- fprintf(stderr, "%s: pipe() failed: %s\n", progname,
+ if (openpty(&ptym, &ptys, ptyname, NULL, NULL)) {
+ fprintf(stderr, "%s: could not allocate pty: %s\n", progname,
strerror(errno));
return(EX_TEMPFAIL);
}
+ /* make the pty raw */
+ if (tcgetattr(ptys, &tios)) {
+ fprintf(stderr, "%s: tcgetattr() failed: %s\n", progname,
+ strerror(errno));
+ return(EX_OSERR);
+ }
+ tios.c_lflag &= ~ECHO;
+ tios.c_lflag &= ~ICANON;
+#ifdef VMIN
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+#endif
+ if (tcsetattr(ptys, TCSANOW, &tios)) {
+ fprintf(stderr, "%s: tcsetattr() failed: %s\n", progname,
+ strerror(errno));
+ return(EX_OSERR);
+ }
- /* if a tty, make it raw as the hp echos _everything_, including
+ /*
+ * if a tty, make it raw as the hp echos _everything_, including
* passwords.
*/
- if (isatty(0)) {
- if (tcgetattr(0, &tios)) {
+ if (isatty(fileno(stdin))) {
+ if (tcgetattr(fileno(stdin), &tios)) {
fprintf(stderr, "%s: tcgetattr() failed: %s\n", progname,
strerror(errno));
return(EX_OSERR);
@@ -125,164 +208,249 @@ main(int argc, char **argv)
tios.c_cc[VMIN] = 1;
tios.c_cc[VTIME] = 0;
#endif
- if (tcsetattr(0, TCSANOW, &tios)) {
+ if (tcsetattr(fileno(stdin), TCSANOW, &tios)) {
fprintf(stderr, "%s: tcsetattr() failed: %s\n", progname,
strerror(errno));
return(EX_OSERR);
}
}
+ /* zero the buffers */
+ memset(hbuf, 0, BUFSZ);
+ memset(tbuf, 0, BUFSZ);
+
+ /* reap our children, must be set-up *after* openpty() */
+ signal(SIGCHLD, (void *) reapchild);
+
if ((child = fork()) == -1) {
- fprintf(stderr, "%s: fork() failed: %s\n", progname,
- strerror(errno));
+ fprintf(stderr, "%s: fork() failed: %s\n", progname, strerror(errno));
return(EX_TEMPFAIL);
}
- /* zero the buffers */
- bzero(hbuf, BUFSZ);
- bzero(tbuf, BUFSZ);
-
if (child == 0) {
- /* close the parent's side of the pipes; we write r[1], read s[0] */
- close(s[1]);
- close(r[0]);
+ signal(SIGCHLD, SIG_DFL);
+ /* close the master pty & std* inherited from the parent */
+ close(ptym);
+ setsid();
+ if (ptys != 0)
+ close(0);
+ if (ptys != 1)
+ close(1);
+ if (ptys != 2)
+ close(2);
+#ifdef TIOCSCTTY
+ if (ioctl(ptys, TIOCSCTTY, NULL) == -1) {
+ snprintf(ptyname, FILENAME_MAX, "%s: could not set controlling "
+ "tty: %s\n", progname, strerror(errno));
+ write(ptys, ptyname, strlen(ptyname));
+ return(EX_OSERR);
+ }
+#endif
+
/* close stdin/out/err and attach them to the pipes */
- if (dup2(s[0], 0) == -1 || dup2(r[1], 1) == -1 || dup2(r[1], 2) == -1) {
- fprintf(stderr, "%s: dup2() failed: %s\n", progname,
- strerror(errno));
+ if (dup2(ptys, 0) == -1 || dup2(ptys, 1) == -1 || dup2(ptys, 2) == -1) {
+ snprintf(ptyname, FILENAME_MAX, "%s: dup2() failed: %s\n", progname,
+ strerror(errno));
+ write(ptys, ptyname, strlen(ptyname));
return(EX_OSERR);
}
- close(s[0]);
- close(r[1]);
+ if (ptys > 2)
+ close(ptys);
+
/* exec telnet/ssh */
- if (execvp(argv[optind], argv + optind)) {
- fprintf(stderr, "%s: execlp() failed: %s\n", progname,
+ execvp(argv[optind], argv + optind);
+ snprintf(ptyname, FILENAME_MAX, "%s: execvp() failed: %s\n", progname,
+ strerror(errno));
+ write(ptys, ptyname, strlen(ptyname));
+ return(EX_TEMPFAIL);
+ /*NOTREACHED*/
+ }
+
+ /* parent */
+ if (debug)
+ fprintf(stderr, "child %d\n", child);
+
+ signal(SIGHUP, (void *) sighdlr);
+
+ /* close the slave pty */
+ close(ptys);
+
+ devnull = open("/dev/null", O_RDWR);
+
+ /* make FDs non-blocking */
+ if (fcntl(ptym, F_SETFL, O_NONBLOCK) ||
+ fcntl(fileno(stdin), F_SETFL, O_NONBLOCK) ||
+ fcntl(fileno(stdout), F_SETFL, O_NONBLOCK)) {
+ fprintf(stderr, "%s: fcntl(NONBLOCK) failed: %s\n", progname,
strerror(errno));
- return(EX_TEMPFAIL);
+ exit(EX_OSERR);
+ }
+
+ /* loop to read on stdin and ptym */
+#define POLLEXP (POLLERR | POLLHUP | POLLNVAL)
+ pfds[0].fd = fileno(stdin);
+ pfds[0].events = POLLIN | POLLEXP;
+ pfds[1].fd = fileno(stdout);
+ pfds[2].fd = ptym;
+ pfds[2].events = POLLIN | POLLEXP;
+
+ while (1) {
+ bytes = poll(pfds, 3, (timeo * 1000));
+ if (bytes == 0) {
+ if (drain)
+ break;
+ /* timeout */
+ continue;
}
- /* not reached */
- } else {
- /* parent */
- if (debug)
- fprintf(stderr, "child %d\n", child);
-
- /* close the child's side of the pipes; we write s[1], read r[0] */
- close(s[0]);
- close(r[1]);
-
- /* make FDs non-blocking */
- if (fcntl(s[1], F_SETFL, O_NONBLOCK) ||
- fcntl(r[0], F_SETFL, O_NONBLOCK) ||
- fcntl(0, F_SETFL, O_NONBLOCK) ||
- fcntl(1, F_SETFL, O_NONBLOCK)) {
- fprintf(stderr, "%s: fcntl(NONBLOCK) failed: %s\n", progname,
- strerror(errno));
- exit(EX_OSERR);
+ if (bytes == -1) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ break;
+ default:
+ rval = EX_IOERR;
+ break;
+ }
+ continue;
}
- /* loop to read on stdin and r[0] */
- FD_ZERO(&rfds); FD_ZERO(&wfds);
- hbufp = hbuf; tbufp = tbuf;
+ /*
+ * write buffers first
+ * write hbuf (stdin) -> ptym
+ */
+ if ((pfds[2].revents & POLLOUT) && hlen) {
+ if ((bytes = write(pfds[2].fd, hbuf, hlen)) < 0 &&
+ errno != EINTR && errno != EAGAIN) {
+ fprintf(stderr, "%s: write() failed: %s\n", progname,
+ strerror(errno));
+ hbuf[0] = '\0';
+ hlen = 0;
+ drain = 1;
+ pfds[2].events &= ~POLLOUT;
- while (1) {
- FD_SET(0, &rfds); FD_SET(r[0], &rfds);
- /* if we have stuff in our buffer(s), we select on writes too */
- FD_ZERO(&wfds);
- if (hlen) {
- FD_SET(s[1], &wfds);
- }
- if (tlen) {
- FD_SET(1, &wfds);
+ break;
+ } else if (bytes > 0) {
+ strcpy(hbuf, hbuf + bytes);
+ hlen -= bytes;
+ if (hlen < 1)
+ pfds[2].events &= ~POLLOUT;
}
+ } else if (pfds[2].revents & POLLEXP) {
+ hbuf[0] = '\0';
+ hlen = 0;
+ pfds[2].events &= POLLIN;
+ break;
+ }
- switch (select(r[1], &rfds, &wfds, NULL, &to)) {
- case 0:
- /* timeout */
- /* XXX what do i do here? */
+ /* write tbuf -> stdout */
+ if ((pfds[1].revents & POLLOUT) && tlen) {
+ /*
+ * if there is an escape char that didnt get filter()'d,
+ * we need to write only up to that point and wait for
+ * the bits that complete the escape sequence. if at least
+ * two bytes follow it, write it anyway as filter() didnt
+ * match it.
+ */
+ bytes = tlen;
+ if ((tbufp = index(tbuf, 0x1b)) != NULL)
+ if (tlen - (tbufp - tbuf) < 2)
+ bytes = tbufp - tbuf;
+
+ if ((bytes = write(pfds[1].fd, tbuf, bytes)) < 0 &&
+ errno != EINTR && errno != EAGAIN) {
+ fprintf(stderr, "%s: write() failed: %s\n", progname,
+ strerror(errno));
break;
- case -1:
- switch (errno) {
- case EINTR: /* interrupted syscall */
+ tbuf[0] = '\0';
+ tlen = 0;
+ drain = 1;
+ pfds[1].events = 0;
+ } else if (bytes > 0) {
+ strcpy(tbuf, tbuf + bytes);
+ tlen -= bytes;
+ if (tlen < 1)
+ pfds[1].events &= ~POLLOUT;
+ }
+ } else if (pfds[1].revents & POLLEXP) {
+ break;
+ tbuf[0] = '\0';
+ tlen = 0;
+ pfds[1].fd = devnull;
+ pfds[1].events = 0;
+ }
+
+ /* read stdin -> hbuf */
+ if (pfds[0].revents & POLLIN) {
+ if (BUFSZ - hlen > 1) {
+ bytes = read(pfds[0].fd, hbuf + hlen, (BUFSZ - 1) - hlen);
+ if (bytes > 0) {
+ hlen += bytes;
+ hbuf[hlen] = '\0';
+ pfds[2].events |= POLLOUT;
+ } else if (bytes == 0 && errno != EAGAIN && errno != EINTR) {
break;
- default:
- exit(EX_IOERR);
- }
- break;
- default:
- /* check exceptions first */
-
- /* which FD is ready? write our buffers asap. */
- /* write hbuf (stdin) -> s[1] */
- if (FD_ISSET(s[1], &wfds) && hlen) {
- if ((hlen = write(s[1], hbuf, hlen)) < 0) {
- fprintf(stderr, "%s: write() failed: %s\n", progname,
- strerror(errno));
- close(s[1]);
- } else
- strcpy(hbuf, hbuf + hlen);
-
- hlen = strlen(hbuf);
- }
- /* write tbuf -> stdout */
- if (FD_ISSET(1, &wfds) && tlen) {
- /* if there is an escape char that didnt get filter()'d,
- * we need to only write up to that point and wait for
- * the bits that complete the escape sequence
- */
- if ((tbufp = index(tbuf, 0x1b)) != NULL)
- tlen = tbufp - tbuf;
-
- if ((tlen = write(1, tbuf, tlen)) < 0) {
- fprintf(stderr, "%s: write() failed: %s\n", progname,
- strerror(errno));
- close(1);
- } else
- strcpy(tbuf, tbuf + tlen);
-
- tlen = strlen(tbuf);
+ /* EOF or read error */
+ drain = 1;
+ pfds[0].fd = devnull;
+ pfds[0].events = 0;
}
- if (FD_ISSET(0, &rfds)) {
- /* read stdin into hbuf */
- if (BUFSZ - hlen > 1) {
- hlen += read(0, hbuf + hlen, (BUFSZ - 1) - hlen);
- if (hlen > 0) {
- hbuf[hlen] = '\0';
- } else if (hlen == 0 || errno != EAGAIN)
- /* EOF or read error */
- close(0);
-
- hlen = strlen(hbuf);
- }
- } else if (FD_ISSET(r[0], &rfds)) {
- /* read telnet/ssh into tbuf, then filter */
- if (BUFSZ - tlen > 1) {
- tlen += read(r[0], tbuf + tlen, (BUFSZ - 1) - tlen);
- if (tlen > 0) {
- tbuf[tlen] = '\0';
- tlen = filter(tbuf, tlen);
- } else if (tlen == 0 || errno != EAGAIN)
- /* EOF or read error */
- close(r[0]);
-
- tlen = strlen(tbuf);
- }
- }
-
- break;
}
+ } else if (pfds[0].revents & POLLEXP) {
+ break;
+ drain = 1;
+ pfds[0].fd = devnull;
+ pfds[0].events = 0;
}
- /* close */
- close(0);
- close(1);
- close(s[1]);
- close(r[0]);
+ /* read telnet/ssh -> tbuf, then filter */
+ if (pfds[2].revents & POLLIN) {
+ if (BUFSZ - tlen > 1) {
+ bytes = read(pfds[2].fd, tbuf + tlen, (BUFSZ - 1) - tlen);
+ if (bytes > 0) {
+ tlen += bytes;
+ tbuf[tlen] = '\0';
+ tlen = filter(tbuf, tlen);
+ if (tlen > 0)
+ pfds[1].events |= POLLOUT;
+ } else if (bytes == 0 && errno != EAGAIN && errno != EINTR) {
+ /* EOF or read error */
+ break;
+ drain = 1;
+ pfds[2].fd = devnull;
+ pfds[2].events = 0;
+ }
+ }
+ } else if (pfds[2].revents & POLLEXP) {
+ break;
+ drain = 1;
+ pfds[2].fd = devnull;
+ pfds[2].events = 0;
+ }
+ }
+ /* try to flush buffers */
+ if (hlen) {
+ (void) write(pfds[2].fd, hbuf, hlen);
+ hlen = 0;
+ }
+ if (tlen) {
+ (void) write(pfds[1].fd, tbuf, tlen);
+ tlen = 0;
+ }
+ if ((bytes = read(pfds[2].fd, tbuf, (BUFSZ - 1))) > 0) {
+ tbuf[bytes] = '\0';
+ tlen = filter(tbuf, bytes);
+ (void) write(pfds[1].fd, tbuf, tlen);
}
+ tcdrain(pfds[1].fd);
+ if ((hlen = read(pfds[0].fd, hbuf, (BUFSZ - 1))) > 0) {
+ (void) write(pfds[2].fd, hbuf, hlen);
+ }
+ tcdrain(pfds[2].fd);
- if (! kill(child, SIGQUIT))
+ if (child && ! kill(child, SIGINT))
reapchild();
- return(EX_OK);
+ return(rval);
}
int
@@ -291,7 +459,7 @@ filter(buf, len)
int len;
{
static regmatch_t pmatch[1];
-#define N_REG 11 /* number of regexes in reg[][] */
+#define N_REG 13 /* number of regexes in reg[][] */
static regex_t preg[N_REG];
static char reg[N_REG][50] = { /* vt100/220 escape codes */
"\e7\e\\[1;24r\e8", /* ds */
@@ -307,8 +475,11 @@ filter(buf, len)
"\e\\[\\?7l", /* RA */
"\e\\[\\?25h", /* ve */
"\e\\[\\?25l", /* vi */
+ "\e\\[K", /* ce */
- "\eE", /* replace w/ CR */
+ /* replace these with CR */
+ "\e\\[0m", /* me */
+ "\eE",
};
char ebuf[256];
size_t nmatch = 1;
@@ -319,7 +490,7 @@ filter(buf, len)
if (index(buf, 0x1b) == 0 || len == 0)
return(len);
- for (x = 0; x < N_REG - 1; x++) {
+ for (x = 0; x < N_REG - 2; x++) {
if (! init) {
if ((err = regcomp(&preg[x], reg[x], REG_EXTENDED))) {
regerror(err, &preg[x], ebuf, 256);
@@ -342,27 +513,28 @@ filter(buf, len)
/* replace \eE w/ CR NL */
if (! init++) {
- if ((err = regcomp(&preg[N_REG - 1], reg[N_REG - 1], REG_EXTENDED))) {
- regerror(err, &preg[N_REG - 1], ebuf, 256);
- fprintf(stderr, "%s: regex compile failed: %s\n", progname,
- ebuf);
- abort();
- }
+ for (x = N_REG - 2; x < N_REG; x++)
+ if ((err = regcomp(&preg[x], reg[x], REG_EXTENDED))) {
+ regerror(err, &preg[x], ebuf, 256);
+ fprintf(stderr, "%s: regex compile failed: %s\n", progname,
+ ebuf);
+ abort();
+ }
}
- while (1)
- if ((err = regexec(&preg[N_REG - 1], buf, nmatch, pmatch, 0))) {
+ for (x = N_REG - 2; x < N_REG; x++) {
+ if ((err = regexec(&preg[x], buf, nmatch, pmatch, 0))) {
if (err != REG_NOMATCH) {
- regerror(err, &preg[N_REG - 1], ebuf, 256);
+ regerror(err, &preg[x], ebuf, 256);
fprintf(stderr, "%s: regexec failed: %s\n", progname, ebuf);
abort();
- } else
- break;
+ }
} else {
- *(buf + pmatch[0].rm_so) = '\n';
- strcpy(buf + pmatch[0].rm_so + 1, buf + pmatch[0].rm_eo);
- x = 0;
+ *(buf + pmatch[0].rm_so) = '\r';
+ *(buf + pmatch[0].rm_so + 1) = '\n';
+ strcpy(buf + pmatch[0].rm_so + 2, buf + pmatch[0].rm_eo);
+ x = N_REG - 2;
}
-
+ }
return(strlen(buf));
}
@@ -376,19 +548,54 @@ reapchild(void)
while ((pid = wait3(&status, WNOHANG, 0)) > 0)
if (debug)
fprintf(stderr, "reap child %d\n", pid);
+ if (pid == child)
+ child = 0;
- /*exit(1);*/
-return;
+ return;
+}
+
+RETSIGTYPE
+sighdlr(int sig)
+{
+ if (debug)
+ fprintf(stderr, "GOT SIGNAL %d\n", sig);
+ drain = 1;
+ return;
+}
- /* not reached */
+#if !HAVE_UNSETENV
+int
+unsetenv(const char *name)
+{
+ char **victim,
+ **end;
+ int len;
+ if (environ == NULL)
+ return(0);
+ len = strlen(name);
+ victim = environ;
+ while (*victim != NULL) {
+ if (strncmp(name, *victim, len) == 0 && victim[0][len] == '=')
+ break;
+ victim++;
+ }
+ if (*victim == NULL)
+ return(0);
+ end = victim + 1;
+ while (*end != NULL)
+ end++;
+ end--;
+ *victim = *end;
+ *end = NULL;
+ return(0);
}
+#endif
void
usage(void)
{
- fprintf(stderr,
-"usage: %s [-hv] <telnet|ssh> [<ssh options>] <hostname> [<telnet_port>]\n",
- progname);
+ fprintf(stderr, "usage: %s [-hv] [-t timeout] <telnet|ssh> [<ssh options>]"
+ " <hostname> [<telnet_port>]\n", progname);
return;
}
@@ -398,3 +605,104 @@ vers(void)
fprintf(stderr, "%s: %s version %s\n", progname, package, version);
return;
}
+
+
+#if !HAVE_OPENPTY
+#include <grp.h>
+#define TTY_LETTERS "pqrstuvwxyzPQRST"
+#define TTY_OLD_SUFFIX "0123456789abcdef"
+#define TTY_NEW_SUFFIX "ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *term,
+ struct winsize *winp)
+{
+ static char line[] = "/dev/XtyXX";
+ const char *cp1, *cp2, *cp, *linep;
+ int master, slave;
+ gid_t ttygid;
+ mode_t mode;
+ struct group *gr;
+
+#if HAVE_PTMX
+ if ((master =
+#if HAVE_PTMX_BSD
+ open("/dev/ptmx_bsd", O_RDWR))
+#else
+ open("/dev/ptmx", O_RDWR))
+#endif
+ != -1) {
+ linep = ptsname(master);
+ grantpt(master);
+ unlockpt(master);
+ if ((slave = open(linep, O_RDWR)) < 0) {
+ slave = errno;
+ (void) close(master);
+ errno = slave;
+ return(-1);
+ }
+#if HAVE_PTMX_OSF
+ {
+ char buf[10240];
+ if (ioctl (slave, I_LOOK, buf) != 0)
+ if (ioctl (slave, I_PUSH, "ldterm")) {
+ close(slave);
+ close(master);
+ return(-1);
+ }
+ }
+#elif HAVE_STROPTS_H
+ ioctl(slave, I_PUSH, "ptem");
+ ioctl(slave, I_PUSH, "ldterm");
+ ioctl(slave, I_PUSH, "ttcompat");
+#endif
+ goto gotit;
+ }
+ if (errno != ENOENT)
+ return(-1);
+#endif
+
+ if ((gr = getgrnam("tty")) != NULL) {
+ ttygid = gr->gr_gid;
+ mode = S_IRUSR|S_IWUSR|S_IWGRP;
+ } else {
+ ttygid = getgid();
+ mode = S_IRUSR|S_IWUSR;
+ }
+
+ for (cp1 = TTY_LETTERS; *cp1; cp1++) {
+ line[8] = *cp1;
+ for (cp = cp2 = TTY_OLD_SUFFIX TTY_NEW_SUFFIX; *cp2; cp2++) {
+ line[5] = 'p';
+ line[9] = *cp2;
+ if ((master = open(line, O_RDWR, 0)) == -1) {
+ if (errno != ENOENT)
+ continue; /* busy */
+ if (cp2 - cp + 1 < sizeof(TTY_OLD_SUFFIX))
+ return -1; /* out of ptys */
+ else
+ break; /* out of ptys in this group */
+ }
+ line[5] = 't';
+ linep = line;
+ if (chown(line, getuid(), ttygid) == 0 &&
+ chmod(line, mode) == 0 &&
+ (slave = open(line, O_RDWR, 0)) != -1) {
+gotit:
+ *amaster = master;
+ *aslave = slave;
+ if (name)
+ (void)strcpy(name, linep);
+ if (term)
+ (void)tcsetattr(slave, TCSAFLUSH, term);
+ if (winp)
+ (void)ioctl(slave, TIOCSWINSZ, winp);
+ return 0;
+ }
+ (void)close(master);
+ }
+ }
+ errno = ENOENT; /* out of ptys */
+ return -1;
+}
+#endif