summaryrefslogtreecommitdiffstats
path: root/bin/hpuifilter.c
diff options
context:
space:
mode:
authorTar Committer <tar@ocjtech.us>2004-01-12 03:17:26 +0000
committerTar Committer <tar@ocjtech.us>2004-01-12 03:17:26 +0000
commit989312339ea2e16579803a48700628c5469e327a (patch)
tree967ee77195819d70f2dc675444e92340bb98e0d6 /bin/hpuifilter.c
parentff168ecfe045c690c24d5bbc5a3062bf9d64120c (diff)
downloadrancid-989312339ea2e16579803a48700628c5469e327a.tar.gz
rancid-989312339ea2e16579803a48700628c5469e327a.tar.xz
rancid-989312339ea2e16579803a48700628c5469e327a.zip
Imported from rancid-2.3.rc1.tar.gz.rancid-2.3.rc1
Diffstat (limited to 'bin/hpuifilter.c')
-rw-r--r--bin/hpuifilter.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/bin/hpuifilter.c b/bin/hpuifilter.c
new file mode 100644
index 0000000..8f7005a
--- /dev/null
+++ b/bin/hpuifilter.c
@@ -0,0 +1,400 @@
+/*
+ * $Id: hpuifilter.c,v 1.17 2004/01/11 03:49:13 heas Exp $
+ *
+ * Copyright (C) 1997-2004 by Terrapin Communications, Inc.
+ * All rights reserved.
+ *
+ * This software may be freely copied, modified and redistributed
+ * without fee for non-commerical purposes provided that this license
+ * remains intact and unmodified with any RANCID distribution.
+ *
+ * There is no warranty or other guarantee of fitness of this software.
+ * It is provided solely "as is". The author(s) disclaim(s) all
+ * responsibility and liability with respect to this software's usage
+ * or its effect upon hardware, computer systems, other software, or
+ * anything else.
+ *
+ * Except where noted otherwise, rancid was written by and is maintained by
+ * Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz.
+ *
+ * Run telnet or ssh to connect to device specified on the command line. The
+ * 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.
+ */
+
+#define DFLT_TO 60 /* default timeout */
+
+#include <config.h>
+#include <version.h>
+
+#include <stdio.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <regex.h>
+
+#include <termios.h>
+
+char *progname;
+int debug = 0;
+
+int filter __P((char *, int));
+void usage __P((void));
+void vers __P((void));
+RETSIGTYPE reapchild __P((void));
+
+int
+main(int argc, char **argv)
+{
+ extern char *optarg;
+ extern int optind;
+ char ch,
+ hbuf[LINE_MAX * 2], /* hlogin buffer */
+ *hbufp,
+ tbuf[LINE_MAX * 2], /* telnet buffer */
+ *tbufp;
+ int bytes, /* bytes read/written */
+ child,
+ r[2], /* recv pipe */
+ s[2]; /* send pipe */
+ ssize_t hlen = 0, /* len of hbuf */
+ tlen = 0; /* len of tbuf */
+ struct timeval to = { DFLT_TO, 0 };
+ fd_set rfds, /* select() */
+ wfds;
+ struct termios tios;
+
+ /* get just the basename() of our exec() name and strip a .* off the end */
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname += 1;
+ else
+ progname = argv[0];
+ if (strrchr(progname, '.') != NULL)
+ *(strrchr(progname, '.')) = '\0';
+
+ while ((ch = getopt(argc, argv, "dhv")) != -1 )
+ switch (ch) {
+ case 'd':
+ debug++;
+ break;
+ case 'v':
+ vers();
+ return(EX_OK);
+ case 'h':
+ default:
+ usage();
+ return(EX_USAGE);
+ }
+
+ if (argc - optind < 2) {
+ usage();
+ return(EX_USAGE);
+ }
+
+ /* reap our children */
+ signal(SIGCHLD, (void *) reapchild);
+ signal(SIGHUP, (void *) reapchild);
+ signal(SIGINT, (void *) reapchild);
+ signal(SIGTERM, (void *) reapchild);
+
+ /* create 2 pipes for send/recv and then fork and exec telnet */
+ for (child = 3; child < 10; child++)
+ close(child);
+ if (pipe(s) || pipe(r)) {
+ fprintf(stderr, "%s: pipe() failed: %s\n", progname,
+ strerror(errno));
+ return(EX_TEMPFAIL);
+ }
+
+ /* if a tty, make it raw as the hp echos _everything_, including
+ * passwords.
+ */
+ if (isatty(0)) {
+ if (tcgetattr(0, &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(0, TCSANOW, &tios)) {
+ fprintf(stderr, "%s: tcsetattr() failed: %s\n", progname,
+ strerror(errno));
+ return(EX_OSERR);
+ }
+ }
+
+ if ((child = fork()) == -1) {
+ fprintf(stderr, "%s: fork() failed: %s\n", progname,
+ strerror(errno));
+ return(EX_TEMPFAIL);
+ }
+
+ /* zero the buffers */
+ bzero(hbuf, LINE_MAX * 2);
+ bzero(tbuf, LINE_MAX * 2);
+
+ if (child == 0) {
+ /* close the parent's side of the pipes; we write r[1], read s[0] */
+ close(s[1]);
+ close(r[0]);
+ /* 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));
+ return(EX_OSERR);
+ }
+ close(s[0]);
+ close(r[1]);
+ /* exec telnet */
+ if (execvp(argv[optind], argv + optind)) {
+ fprintf(stderr, "%s: execlp() failed: %s\n", progname,
+ strerror(errno));
+ return(EX_TEMPFAIL);
+ }
+ /* 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);
+ }
+
+ /* loop to read on stdin and r[0] */
+ FD_ZERO(&rfds); FD_ZERO(&wfds);
+ hbufp = hbuf; tbufp = tbuf;
+
+ 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);
+ }
+
+ switch (select(r[1], &rfds, &wfds, NULL, &to)) {
+ case 0:
+ /* timeout */
+ /* HEAS: what do i do here? */
+ break;
+ case -1:
+ switch (errno) {
+ case EINTR: /* interrupted syscall */
+ 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);
+ }
+ if (FD_ISSET(0, &rfds)) {
+ /* read stdin into hbuf */
+ if (LINE_MAX * 2 - hlen > 1) {
+ hlen += read(0, hbuf + hlen,
+ (LINE_MAX * 2 - 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 into tbuf, then filter */
+ if (LINE_MAX * 2 - tlen > 1) {
+ tlen += read(r[0], tbuf + tlen,
+ (LINE_MAX * 2 - 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;
+ }
+ }
+ /* close */
+ close(0);
+ close(1);
+ close(s[1]);
+ close(r[0]);
+
+ }
+
+ if (! kill(child, SIGQUIT))
+ reapchild();
+
+ return(EX_OK);
+}
+
+int
+filter(buf, len)
+ char *buf;
+ int len;
+{
+ static regmatch_t pmatch[1];
+#define N_REG 11 /* 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 */
+ "\e8", /* fs */
+
+ "\e\\[2J",
+ "\e\\[2K", /* kE */
+
+ "\e\\[[0-9]+;[0-9]+r", /* cs */
+ "\e\\[[0-9]+;[0-9]+H", /* cm */
+
+ "\e\\[\\?6l",
+ "\e\\[\\?7l", /* RA */
+ "\e\\[\\?25h", /* ve */
+ "\e\\[\\?25l", /* vi */
+
+ "\eE", /* replace w/ CR */
+ };
+ char ebuf[256];
+ size_t nmatch = 1;
+ int err,
+ x;
+ static int init = 0;
+
+ if (index(buf, 0x1b) == 0 || len == 0)
+ return(len);
+
+ for (x = 0; x < N_REG - 1; x++) {
+ if (! init) {
+ 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();
+ }
+ }
+ if ((err = regexec(&preg[x], buf, nmatch, pmatch, 0))) {
+ if (err != REG_NOMATCH) {
+ regerror(err, &preg[x], ebuf, 256);
+ fprintf(stderr, "%s: regexec failed: %s\n", progname, ebuf);
+ abort();
+ }
+ } else {
+ strcpy(buf + pmatch[0].rm_so, buf + pmatch[0].rm_eo);
+ x = 0;
+ }
+ }
+
+ /* 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();
+ }
+ }
+ while (1)
+ if ((err = regexec(&preg[N_REG - 1], buf, nmatch, pmatch, 0))) {
+ if (err != REG_NOMATCH) {
+ regerror(err, &preg[N_REG - 1], 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;
+ }
+
+ return(strlen(buf));
+}
+
+RETSIGTYPE
+reapchild(void)
+{
+ int status;
+ pid_t pid;
+
+ /* HEAS: this needs to deal with/without wait3 via HAVE_WAIT3 */
+ while ((pid = wait3(&status, WNOHANG, 0)) > 0)
+ if (debug)
+ fprintf(stderr, "reap child %d\n", pid);
+
+ /*exit(1);*/
+return;
+
+ /* not reached */
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+"usage: %s [-hv] <telnet|ssh> [<ssh options>] <hostname> [<telnet_port>]\n",
+ progname);
+ return;
+}
+
+void
+vers(void)
+{
+ fprintf(stderr, "%s: %s version %s\n", progname, package, version);
+ return;
+}