/* * svr_tty.c -- Remote port driver (UNIX version). * (C) Copyright Stallion Technologies, 1995-1999. * * Compilation instructions: * * SCO: cc -O -DSCO32 -o svr_tty svr_tty.c -lsocket -lpt * LINUX/BSDI/FreeBSD: cc -O -o svr_tty svr_tty.c * SVR4/UnixWare: cc -O -DSVR4 -o svr_tty svr_tty.c -lsocket * Olivetti SVR4: cc -O -DSVR4 -o svr_tty svr_tty.c -lsocket -lnsl * Solaris (x86/Sparc): cc -O -DSVR4 -o svr_tty svr_tty.c -lsocket -lnsl * SunOS 4: cc -O -DSUNOS4 -o svr_tty svr_tty.c * HP/UX version 10: cc -o svr_tty svr_tty.c -lV3 * Interactive: cc -O -DINTER -o svr_tty svr_tty.c -linet * AIX: cc -O -DAIX -o svr_tty svr_tty.c */ /*****************************************************************************/ #define VERSION "6.1.0" static char *rcsid = "$Revision: 1.30 $ $Date: 1999/06/07 23:50:50 $"; /*****************************************************************************/ /* * This code fundamentally supports 2 different types of pseudo ttys. * The standard old BSD style, and the newer STREAMS style. Either * can be specified on the build line, but will always be defaulted * to some appropriate setting if left out. Most systems default to * BSD style, the only exception is SVR4. */ #if defined(SVR4) && !defined(BSDPTY) #define STREAMPTY #endif #if !defined(STREAMPTY) && !defined(BSDPTY) #define BSDPTY #endif /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef AIX #include #endif #if defined(INTER) #include #include #include #include #endif #ifdef SVR4 #include #include #include #endif #ifdef SCO32 #ifdef STREAMPTY #include #include #include #endif #ifndef TIOCPKT #undef FIONBIO #include #endif #endif #ifdef hpux #include #endif /*****************************************************************************/ /* * Make sure we don't break any OS's */ #ifndef O_RDWR #define O_RDWR 002 #endif #ifndef O_NDELAY #define O_NDELAY 004 #endif #ifdef SVR4 #define TIOCPKT_FLUSHWRITE 0x02 #define TIOCPKT_NOSTOP 0x10 #define TIOCPKT_DOSTOP 0x20 #endif #ifdef STREAMPTY #define MaxPTSUnit 255 #endif /* * Just in case this isn't defined on some system. */ #ifndef __LINE__ #define __LINE__ 0 #endif /*****************************************************************************/ #define DefaultConfig "/etc/svr_tty.conf" #define BufferSize 1024 typedef struct config Config, *ConfPtr; struct config { ConfPtr next; char master_tty[64]; char link_tty[64]; ushort pts_unit; /* SVR4 only */ struct in_addr ipaddr; ushort tcpport; short status; short flags; long timeout; int pty_fd; int pts_fd; /* SVR4 only */ int socket_fd; char pty_buffer[BufferSize]; int pty_count; int pty_offset; char socket_buffer[BufferSize]; int socket_count; int socket_offset; }; /* * Status values */ #define INITIAL 0 #define ACTIVE 1 #define PTY_OK 10 #define PTY_BAD 11 #define SOCK_OK 20 #define SOCK_BAD 21 /* * Flag values */ #define CONFIGURED 0x1 #define MADELINK 0x2 #define PTSOPEN 0x4 /* * Coniguration types, mutually exclusive. */ #define CONFIG_OLD 1 #define CONFIG_NEW 2 #define CONFIG_ARG 3 /* * Global variables... */ char *whoami; ConfPtr myConf; ConfPtr ConfHead; int AnyTimeouts; int MaxFD = 0; int SocketTimeout = 0; int SocketWaitTime = 0; int SocketLastTime; int Keepalives = 0; int NetworkOnDemand = 0; int NetworkDrop = 0; #ifndef SVR4 /* * Pty device name suffixes, for BSD style pseudo ttys. */ char PtySuffix1[] = "pqrs"; char PtySuffix2[] = "0123456789abcdef"; #endif /* * Debugging support. */ int Debugging = 0; FILE *DebugFp = stderr; char *DebugFileName = NULL; int ConfigType = CONFIG_OLD; #define DebugMsg Debugging && DebugMessage #define DebugTm Debugging && DebugTime /*****************************************************************************/ /* * Forward declarations. */ void usage(); void pktread(); /*****************************************************************************/ int main(argc, argv) int argc; char **argv; { char *file; int n, daemonize; daemonize = 1; whoami = argv[0]; ConfHead = NULL; while ((argc > 1) && (argv[1][0] == '-')) { switch (argv[1][1]) { case '?': case 'h': usage(0); case 'D': DebugFileName = argv[2]; argc--; argv++; /* fall thru */ case 'd': Debugging = 1; break; case 'o': ConfigType = CONFIG_OLD; break; case 'n': ConfigType = CONFIG_NEW; break; case 'B': daemonize = 1; break; case 'F': daemonize = 0; break; case 'f': NetworkOnDemand = 1; break; case 'm': NetworkDrop = 1; break; case 'p': ConfigType = CONFIG_ARG; if (ParseConfig(0, argv[2]) < 0) { fprintf(stderr, "%s: Invalid port configuration\n", whoami); usage(1); } break; case 'v': printf("Version: %s %s\n", VERSION, rcsid); exit(0); case 'k': Keepalives = 1; break; case 't': n = sscanf(argv[2], "%d,%d", &SocketTimeout, &SocketWaitTime); if ((n != 1) && (n != 2)) { fprintf(stderr, "%s: Invalid timeout specification\n", whoami); usage(1); } if (SocketTimeout < 0 || SocketWaitTime < 0) { fprintf(stderr, "%s: Invalid time specification\n", whoami); usage(1); } argc--; argv++; break; default: fprintf(stderr, "%s: Invalid option -- '%c'\n", whoami, argv[1][1]); usage(1); } argc--; argv++; } if (DebugFileName != NULL) DebugOpen(DebugFileName); if (ConfigType != CONFIG_ARG) { if (argc > 2) { fprintf(stderr, "%s: Invalid number of arguments\n", whoami); usage(1); } #if defined(SCO32) && defined(STREAMPTY) if (ConfigType == CONFIG_OLD) { fprintf(stderr, "%s: old configuration not supported with STREAMS pty\n", whoami); exit(1); } #endif file = (argc > 1) ? argv[1] : DefaultConfig; DebugMsg(__LINE__, 0, "Loading Configuration File %s\n", file); LoadConfigFile(file); } if (daemonize) make_daemon(); DebugMsg(__LINE__, 0, "Starting Communicate\n"); Communicate(); DebugMsg(__LINE__, 0, "All Done, exit!\n"); exit(0); } /*****************************************************************************/ void usage(int status) { fprintf(stderr, "Usage: %s [-?hvdofmnkBF] ", whoami); fprintf(stderr, "[-t poll-time[,wait-time]] [-D debug-file]\n"); fprintf(stderr, " [-p \"link server port\"]"); fprintf(stderr, " [config-file]\n\n"); fprintf(stderr, " -?h this help\n"); fprintf(stderr, " -d generate debug trace to stderr\n"); fprintf(stderr, " -D generate debug trace into debug-file\n"); fprintf(stderr, " -v output version information\n"); fprintf(stderr, " -o use old config file format (default)\n"); fprintf(stderr, " -n use new config file format\n"); fprintf(stderr, " -f network connection on first pty data\n"); fprintf(stderr, " -m drop network connection on pty close\n"); fprintf(stderr, " (Not supported on all systems)\n"); fprintf(stderr, " -k use TCP keepalives\n"); fprintf(stderr, " -B run as daemon (default)\n"); fprintf(stderr, " -F run in foreground, not daemon\n"); fprintf(stderr, " -t check for network disconnection\n"); fprintf(stderr, " poll-time = how often to check\n"); fprintf(stderr, " wait-time = how long to wait\n"); fprintf(stderr, " -p specify port configuration as:\n"); fprintf(stderr, " link = device name to link\n"); fprintf(stderr, " server = server name or IP address\n"); fprintf(stderr, " port = TCP port number\n"); exit(status); } /*****************************************************************************/ /* * Become a daemon. */ int make_daemon() { int fd; int status; int frominit; signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); #ifdef SIGTTOU signal(SIGTTOU, SIG_IGN); #endif #ifdef SIGTTIN signal(SIGTTIN, SIG_IGN); #endif #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif /* * Don't fork if parent is init so we can use a respawn entry * in inittab. There is a race here (parent might exit before * we start giving ppid of 1), but this is no bother. */ frominit = (getppid() == 1); if (!frominit) { switch (fork()) { case -1: DebugMsg(__LINE__, 0, "Can't fork! errno=%d\n", errno); exit(1); case 0: break; default: exit(0); } } umask(0); chdir("/"); /* * Get rid of controlling tty and make sure we don't inherit one. * We use every available method -- looks like overkill, but it * is necessary when dealing with lots of UNIX variants. */ #if defined(SVR4) || !defined(TIOCNOTTY) #ifdef SVR4 setsid(); #else setpgrp(); #endif /* * Fork again so we are not session/pgrp leader and can't * acquire a controlling tty. The parent waits if we were * invoked from init (for respawn). */ switch (fork()) { case -1: DebugMsg(__LINE__, 0, "ERROR: can't do second fork! errno=%d\n", errno); exit(1); case 0: break; default: if (frominit) wait(&status); exit(0); } #else fd = open ("/dev/tty", O_RDWR); if (fd >= 0) { ioctl (fd, TIOCNOTTY, 0); close(fd); } #endif } /*****************************************************************************/ int noctty_open(name, mode) char *name; int mode; { #ifndef O_NOCTTY return(open(name, mode)); #else return(open(name, mode | O_NOCTTY)); #endif } /*****************************************************************************/ int DebugOpen(file) char *file; { FILE *fp; if ((fp = fopen(file, "w")) == (FILE *) NULL) { fprintf(stderr, "%s: failed to open debug file %s, errno=%d\n", whoami, file, errno); exit(2); } DebugFp = fp; return(0); } /*****************************************************************************/ int DebugTime() { long t; char *s, *ctime(); t = time(0); s = ctime(&t); s[19] = 0; fprintf(DebugFp, "%s :: ", s+11); } /*****************************************************************************/ int Message(line, msg) int line; char *msg; { fprintf(DebugFp, "%s(%d): ", whoami, line); DebugTm(); fprintf(DebugFp, "%s\n", msg); fflush(DebugFp); } /*****************************************************************************/ int DebugMessage(line, c, msg, a0, a1, a2, a3) int line; struct config *c; char *msg, *a0, *a1, *a2, *a3; { fprintf(DebugFp, "%s(%d): ", whoami, line); DebugTime(); if (c) { fprintf(DebugFp, "Port=%s", (c->link_tty[0] && (c->link_tty[0] != '-')) ? c->link_tty : c->master_tty); #ifdef STREAMPTY fprintf(DebugFp, "(%d)", c->pts_unit); #endif fprintf(DebugFp, " IP=%s:%d - ", inet_ntoa(c->ipaddr), ntohs(c->tcpport)); } fprintf(DebugFp, msg, a0, a1, a2, a3); fflush(DebugFp); } /*****************************************************************************/ /* * Shut everything down, then die. */ void Shutdown(sig) int sig; { ConfPtr c, tmpc; DebugMsg(__LINE__, 0, "Shutdown(sig=%d) started\n", sig); for (c = ConfHead; c; ) { DebugMsg(__LINE__, c, "Closing\n"); close(c->pts_fd); close(c->pty_fd); close(c->socket_fd); if (c->flags & MADELINK) { char ltty[256]; ltty[0] = 0; if (c->link_tty[0] != '/') strcpy(ltty, "/dev/"); strcat(ltty, c->link_tty); DebugMsg(__LINE__, c, "Unlink %s\n", ltty); unlink(ltty); } tmpc = c; c = c->next; free(tmpc); } DebugMsg(__LINE__, 0, "Shutdown() complete.\n"); exit(0); } /*****************************************************************************/ void ReConfigure(sig) int sig; { DebugMsg(__LINE__, 0, "Reconfigure(%d) called.\n", sig); /* * Reload the previous configuration file. */ if (ConfigType != CONFIG_ARG) LoadConfigFile(NULL); } /*****************************************************************************/ int ParseConfig(int line_number, char *line) { struct in_addr ipaddr; struct hostent *hp; ConfPtr c; char mtty[256], ip[256], port[256], dummy[256]; int tcpport, pts_unit; if (sscanf(line, "%s%s%s%s", mtty, ip, port, dummy) != 3) { Message(line_number, "bad format line in config file"); return(-1); } if (ConfigType == CONFIG_OLD) { #ifdef STREAMPTY if (strncmp(mtty, "pts/", 4) != 0) { Message(line_number, "bad pts master"); return(-1); } pts_unit = atoi(&mtty[4]); if (((pts_unit == 0) && strcmp("0", &mtty[4]) != 0) || (pts_unit > MaxPTSUnit)) { Message(line_number, "bad pts unit"); return(-1); } #else if (strncmp(mtty, "pty", 3) != 0) { Message(line_number, "bad pty master"); return(-1); } #endif } hp = gethostbyname(ip); if (hp) { ipaddr = *(struct in_addr *) hp->h_addr; } else { ipaddr.s_addr = inet_addr(ip); if (ipaddr.s_addr == -1) { Message(line_number, "bad ip address"); return(-1); } } tcpport = atoi(port); if ((tcpport <= 0) || (tcpport >= 0x10000)) { Message(line_number, "bad TCP port number"); return(-1); } for (c = ConfHead; c; c = c->next) { if (ConfigType == CONFIG_OLD) { if (strcmp(c->master_tty, mtty) == 0) break; } else { if (strcmp(c->link_tty, mtty) == 0) break; } } if (c == NULL) { c = (ConfPtr) calloc(sizeof(Config), 1); if (c == NULL) { DebugMsg(__LINE__, 0, "ERROR: calloc(size=%d) failed, errno=%d\n", errno); exit(2); } c->next = ConfHead; ConfHead = c; if (ConfigType == CONFIG_OLD) strcpy(c->master_tty, mtty); else strcpy(c->link_tty, mtty); c->pts_unit = pts_unit; AnyTimeouts++; c->status = INITIAL; c->flags = 0; c->timeout = 0; c->pty_fd = -1; c->pts_fd = -1; c->socket_fd = -1; } if ((c->status == ACTIVE) || (c->status == SOCK_OK)) { if ((c->ipaddr.s_addr != ipaddr.s_addr) || (c->tcpport != htons(tcpport))) LostConnection(c); } c->ipaddr = ipaddr; c->tcpport = htons(tcpport); c->flags |= CONFIGURED; return(0); } /*****************************************************************************/ /* * Process the configuration file. Create a list of structures * one for each port entry. This is the old configuration method. */ int LoadConfigFile(filename) char *filename; { static char *conf_file; ConfPtr c, tail = 0; FILE *f; char line[256]; int line_number = 0; if (filename) conf_file = filename; for (c = ConfHead, tail = 0; c; tail = c, c = c->next) c->flags &= ~CONFIGURED; DebugMsg(__LINE__, 0, "LoadConfigFile(%s) started\n", conf_file); f = fopen(conf_file, "r"); if (f == NULL) { DebugMsg(__LINE__, 0, "ERROR: failed to open %s, errno=%d\n", conf_file, errno); goto done; } while (fgets(line, sizeof(line), f)) { line_number++; if (line[0] == 0 || line[0] == '\n' || line[0] == '#') continue; ParseConfig(line_number, line); } fclose(f); endhostent(); done: for (c = ConfHead, tail = 0; c; c = (tail ? tail->next : ConfHead)) { if (c->flags & CONFIGURED) { tail = c; continue; } /* * Shutdown this pty and its connection. */ DebugMsg(__LINE__, c, "Shutting down\n"); close(c->pts_fd); close(c->pty_fd); close(c->socket_fd); if (tail == 0) ConfHead = c->next; else tail->next = c->next; if (c->flags & MADELINK) { line[0] = 0; if (c->link_tty[0] != '/') strcpy(line, "/dev/"); strcat(line, c->link_tty); DebugMsg(__LINE__, c, "Unlink %s\n", line); unlink(line); c->flags &= ~MADELINK; } free(c); } DebugMsg(__LINE__, 0, "LoadConfigFile(%s) complete.\n", conf_file); } /*****************************************************************************/ int OpenPty(c) ConfPtr c; { struct stat status; long t = time(0); int on = 1, target, i, fd; char mtty[256], ltty[256], stty[256]; DebugMsg(__LINE__, c, "OpenPty()\n"); #ifdef STREAMPTY if (ConfigType == CONFIG_OLD) { int fdlist[MaxPTSUnit+1]; /* * Keep opening the clone device until the proper unit * number is assigned. */ for (i = 0; (i <= MaxPTSUnit); i++) { fdlist[i] = noctty_open("/dev/ptmx", O_RDWR); if (fdlist[i] < 0) { DebugMsg(__LINE__, c, "ERROR: open(%s) for %s failed, errno=%d\n", "/dev/ptmx", c->master_tty, errno); break; } if (fstat(fdlist[i], &status) < 0) { DebugMsg(__LINE__, c, "ERROR: stat(%s) for %s failed, errno=%d\n", "/dev/ptmx", c->master_tty, errno); break; } if (c->pts_unit == minor(status.st_rdev)) goto found; } while (i >= 0) close(fdlist[i--]); goto pty_bad; found: c->pty_fd = fdlist[i--]; while (i >= 0) close(fdlist[i--]); } else { /* * New config type, open the clone devices and use next * assigned pseudo tty. */ fd = noctty_open("/dev/ptmx", O_RDWR | O_NDELAY); if (fd < 0) { DebugMsg(__LINE__, c, "ERROR: open(%s) failed, errno=%d\n", "/dev/ptmx", errno); goto pty_bad; } if (fstat(fd, &status) < 0) { DebugMsg(__LINE__, c, "ERROR: stat(%s) failed, errno=%d\n", "/dev/ptmx", errno); close(fd); goto pty_bad; } c->pts_unit = minor(status.st_rdev); c->pty_fd = fd; sprintf(mtty, "/dev/ptm/%d", c->pts_unit); strcpy(c->master_tty, mtty); DebugMsg(__LINE__, c, "Pty master %s\n", mtty); } #else if (ConfigType == CONFIG_OLD) { strcpy(mtty, "/dev/"); strcat(mtty, c->master_tty); DebugMsg(__LINE__, c, "Pty master %s\n", mtty); c->pty_fd = noctty_open(mtty, O_RDWR | O_NDELAY); } else { int j; /* * Try opening pty's until we get one... */ for (i = 0; (i < sizeof(PtySuffix1) - 1); i++) { for (j = 0; (j < sizeof(PtySuffix2) - 1); j++) { sprintf(mtty, "/dev/pty%c%c", PtySuffix1[i], PtySuffix2[j]); fd = noctty_open(mtty, O_RDWR | O_NDELAY); if (fd >= 0) goto got_pty; } } got_pty: if (fd < 0) { DebugMsg(__LINE__, c, "ERROR: no available ptys\n"); goto pty_bad; } strcpy(c->master_tty, mtty); DebugMsg(__LINE__, c, "Pty master %s\n", mtty); c->pty_fd = fd; } #endif if (c->pty_fd < 0) { DebugMsg(__LINE__, c, "ERROR: open(%s) failed, errno=%d\n", mtty, errno); pty_bad: c->timeout = t + 5; AnyTimeouts++; c->status = PTY_BAD; } else { #if defined(SVR4) if (OpenPts(c) < 0) goto pty_bad; sprintf(stty, "/dev/pts/%d", c->pts_unit); DebugMsg(__LINE__, c, "Pty slave %s\n", stty); #elif defined(STREAMPTY) if (OpenPts(c) < 0) goto pty_bad; sprintf(stty, "/dev/pts%03d", c->pts_unit); DebugMsg(__LINE__, c, "Pty slave %s\n", stty); #else /* * SVR3 BSD pty devices. */ ioctl(c->pty_fd, TIOCPKT, &on); strcpy(stty, mtty); #ifndef hpux stty[5] = 't'; #else /* * HP/UX has multiple pty naming conventions! */ if (strncmp(stty, "/dev/ptym/", 10) != 0) { stty[5] = 't'; } else { /* * Convert e.g. "/dev/ptym/ptya0" to "/dev/pty/ttya0". */ char *p = &stty[8]; for (p = &stty[8] ; *p = *(p + 1) ; p++) /* doodle */ ; stty[9] = 't'; } #endif DebugMsg(__LINE__, c, "Pty slave %s\n", stty); c->pts_fd = noctty_open(stty, O_RDWR | O_NDELAY); if (c->pts_fd < 0) { DebugMsg(__LINE__, c, "ERROR: open(%s) failed, errno=%d\n", stty, errno); close(c->pty_fd); c->pty_fd = -1; goto pty_bad; } #endif /* SVR4 */ c->status = PTY_OK; if (c->pty_fd > MaxFD) MaxFD = c->pty_fd; /* * If this is a new style config then there may be * a device name to link up. */ if (c->link_tty[0] && (c->link_tty[0] != '-')) { ltty[0] = 0; if (c->link_tty[0] != '/') strcpy(ltty, "/dev/"); strcat(ltty, c->link_tty); DebugMsg(__LINE__, c, "Pty link %s\n", ltty); if (stat(ltty, &status) >= 0) { if ((ConfigType == CONFIG_ARG) && !Debugging) { fprintf(stderr, "svr_tty: link device %s exists - not removing\n", ltty); } else { DebugMsg(__LINE__, c, "ERROR: link device %s exists\n", ltty); } } else { if (link(stty, ltty) < 0) { DebugMsg(__LINE__, c, "ERROR: link() " "from %s to %s failed, " "errno=%d\n", stty, ltty, errno); } else { c->flags |= MADELINK; } } } } } /*****************************************************************************/ #if defined(STREAMPTY) && !defined(AIX) int OpenPts(c) ConfPtr c; { char *pts, *ptsname(); DebugMsg(__LINE__, c, "Openpts()...\n"); /* * SVR4 stream ptm/pts devices. */ if (grantpt(c->pty_fd) < 0) { ptmfail: close(c->pty_fd); c->pty_fd = -1; DebugMsg(__LINE__, c, "ERROR: pty operation failed, errno=%d\n", errno); return(-1); } if (unlockpt(c->pty_fd) < 0) goto ptmfail; if ((pts = ptsname(c->pty_fd)) == NULL) goto ptmfail; if ((c->pts_fd = noctty_open(pts, O_RDWR | O_NDELAY)) < 0) goto ptmfail; if (ioctl(c->pts_fd, I_PUSH, "ptem") < 0) { ptsfail: close(c->pts_fd); c->pts_fd = -1; goto ptmfail; } if (ioctl(c->pts_fd, I_PUSH, "ldterm") < 0) goto ptsfail; #ifdef SVR4 if (ioctl(c->pts_fd, I_PUSH, "ttcompat") < 0) goto ptsfail; #endif #if 0 /* * This doesn't appear to be required for any OS, but it * does cause problems with the standard interface script * for printers on SVR4 operating systems. * * eg., Solaris, Unixware and Olivetti. */ if (ioctl(c->pty_fd, I_PUSH, "pckt") < 0) goto ptsfail; #endif c->flags |= PTSOPEN; return(0); } #endif /* STREAMPTY && !AIX */ /*****************************************************************************/ int OpenSocket(c) ConfPtr c; { struct sockaddr_in sin; struct linger l; long t = time(0); int optval; DebugMsg(__LINE__, c, "OpenSocket()\n"); sin.sin_family = AF_INET; sin.sin_addr = c->ipaddr; sin.sin_port = c->tcpport; c->socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (c->socket_fd < 0) { /* * Can't continue without sockets. */ DebugMsg(__LINE__, c, "ERROR: socket() failed, errno=%d\n", errno); exit(2); } l.l_onoff = 1; l.l_linger = 60; if (setsockopt(c->socket_fd, SOL_SOCKET, SO_LINGER, (char *) &l, sizeof(l)) < 0) { DebugMsg(__LINE__, c, "ERROR: setsocketopt(SO_LINGER) failed, errno=%d\n", errno); exit(2); } #ifdef SO_KEEPALIVE if (Keepalives) { optval = 1; if (setsockopt(c->socket_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &optval, sizeof(optval)) < 0) { DebugMsg(__LINE__, c, "ERROR: setsockopt(SO_KEEPALIVE) failed, errno=%d\n", errno); exit(2); } } #endif if (connect(c->socket_fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { close(c->socket_fd); c->socket_fd = -1; DebugMsg(__LINE__, c, "ERROR: connect() failed, errno=%d\n", errno); c->timeout = t + 5; AnyTimeouts++; c->status = SOCK_BAD; } else { c->status = SOCK_OK; if (c->socket_fd > MaxFD) MaxFD = c->socket_fd; } } /*****************************************************************************/ int CheckSocket() { ConfPtr c; struct sockaddr_in sin; struct timeval tmo; fd_set infd, outfd, exfd; int newfd, n; char ch; for (c = ConfHead; c; c = c->next) { if (c->status != ACTIVE) continue; sin.sin_family = AF_INET; sin.sin_addr = c->ipaddr; sin.sin_port = c->tcpport; newfd = socket(AF_INET, SOCK_STREAM, 0); if (newfd < 0) { DebugMsg(__LINE__, c, "ERROR: re-socket() failed, errno=%d\n", errno); continue; } if (connect(newfd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { LostConnection(c); close(newfd); continue; } /* * This is a total kludge! * We do a short select to see if the socket * connection is really there. If yes, then the * connection was recently broken... */ FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&exfd); FD_SET(newfd, &infd); /* * Allow the select wait time to be bumped up for long latency * WAN links. Otherwise we may reset the connection when it * is still actually ok. */ if (SocketWaitTime > 0) { tmo.tv_sec = SocketWaitTime; tmo.tv_usec = 0; } else { tmo.tv_sec = 0; tmo.tv_usec = 50000; } n = select((newfd + 1), &infd, &outfd, &exfd, &tmo); if (n < 0) { if (errno != EINTR) DebugMsg(__LINE__, c, "ERROR: select() failed, errno=%d\n", errno); } if (FD_ISSET(newfd, &infd)) { n = read(newfd, &ch, sizeof(ch)); if (n <= 0) { close(newfd); continue; } } close(newfd); LostConnection(c); } } /*****************************************************************************/ int CloseConnection(c) ConfPtr c; { long t = time(0); DebugMsg(__LINE__, c, "CloseConnection!\n"); close(c->socket_fd); c->socket_fd = -1; if (! NetworkOnDemand) { c->timeout = t + 5; AnyTimeouts++; c->status = SOCK_BAD; } } /*****************************************************************************/ int LostConnection(c) ConfPtr c; { long t = time(0); DebugMsg(__LINE__, c, "LostConnection!\n"); c->timeout = t + 5; AnyTimeouts++; close(c->socket_fd); c->socket_fd = -1; c->status = SOCK_BAD; } /*****************************************************************************/ int CheckStatus() { ConfPtr c; long t; DebugMsg(__LINE__, 0, "CheckStatus(%d) started.\n", AnyTimeouts); t = time(0); for (c = ConfHead; c; c = c->next) { if (c->status == INITIAL) { /* * Attempt to open the PTY. */ AnyTimeouts--; OpenPty(c); } else if (c->status == PTY_BAD) { /* * Wait for timeout to expire before retrying. */ if (c->timeout <= t) { AnyTimeouts--; OpenPty(c); } } if (c->status == PTY_OK) { /* * Attempt to open the network connection. */ if (NetworkOnDemand) c->status = SOCK_OK; else OpenSocket(c); } else if (c->status == SOCK_BAD) { /* * Wait for timeout to expire before retrying. */ if (c->timeout <= t) { AnyTimeouts--; OpenSocket(c); } } if (c->status == SOCK_OK) c->status = ACTIVE; } DebugMsg(__LINE__, 0, "CheckStatus(%d) completed.\n", AnyTimeouts); } /*****************************************************************************/ int Communicate() { ConfPtr c; fd_set infd, outfd, exfd; int n; char ch; struct timeval tmo; #ifdef SUNOS4 struct sigvec sv; memset(&sv, 0, sizeof(sv)); sv.sv_handler = ReConfigure; sigvec(SIGHUP, &sv, 0); sv.sv_handler = Shutdown; sigvec(SIGTERM, &sv, 0); sigvec(SIGINT, &sv, 0); sigvec(SIGQUIT, &sv, 0); #elif defined(linux) || defined(bsdi) || defined(__FreeBSD__) struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = ReConfigure; sa.sa_flags = SA_RESTART; sigaction(SIGHUP, &sa, NULL); sa.sa_handler = Shutdown; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); #else sigset(SIGHUP, ReConfigure); sigset(SIGTERM, Shutdown); sigset(SIGINT, Shutdown); sigset(SIGQUIT, Shutdown); #endif for (;;) { while (ConfHead == NULL) { DebugMsg(__LINE__, 0, "Communicate has nothing to do.\n"); pause(); } if (AnyTimeouts) CheckStatus(); if ((SocketTimeout > 0) && (time(0) >= (SocketLastTime + SocketTimeout))) { CheckSocket(); SocketLastTime = time(0); } FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&exfd); for (c = ConfHead; c; c = c->next) { if (c->status != ACTIVE) continue; /* * We need to fix up the offsets here otherwise we * may not make it into the select */ if (c->pty_count == 0) c->pty_offset = 0; if (c->socket_count == 0) c->socket_offset = 0; /* * Don't select the pty for input if there is data * waiting to be output on the socket. */ if (c->pty_count > 0) { if (c->socket_fd >= 0) FD_SET(c->socket_fd, &outfd); } else { FD_SET(c->pty_fd, &infd); } /* * Always try to select the socket for input even * if there is data to output to the pty because * we need to know if the network connection has * gone away. */ if (c->socket_count > 0) FD_SET(c->pty_fd, &outfd); if (c->socket_count + c->socket_offset < BufferSize) { if (c->socket_fd >= 0) FD_SET(c->socket_fd, &infd); } #if defined(STREAMPTY) || defined(AIX) FD_SET(c->pty_fd, &exfd); #endif } tmo.tv_sec = 3; tmo.tv_usec = 0; n = select(MaxFD+1, &infd, &outfd, &exfd, &tmo); if (n <= 0) { if (n < 0 && errno != EINTR) DebugMsg(__LINE__, 0, "ERROR: select() failed, errno=%d\n", errno); continue; } for (c = ConfHead; c; c = c->next) { if (c->status != ACTIVE) continue; #ifndef STREAMPTY #define PKControl(ch) \ ((ch) & (TIOCPKT_FLUSHWRITE | TIOCPKT_NOSTOP | TIOCPKT_DOSTOP)) if (FD_ISSET(c->pty_fd, &exfd)) { DebugMsg(__LINE__, c, "Exception occurred on pty\n"); #if defined(SCO32) || defined(linux) FD_CLR(c->pty_fd, &infd); #endif n = read(c->pty_fd, &ch, 1); if (n == 1 && PKControl(ch)) { send(c->socket_fd, &ch, 1, MSG_OOB); if (ch & TIOCPKT_FLUSHWRITE) { c->pty_count = 0; FD_CLR(c->pty_fd, &infd); /* * Should also flush the * outgoing socket data. */ } } } #endif /* STREAMPTY */ if (FD_ISSET(c->pty_fd, &infd)) { #ifdef STREAMPTY pktread(c); #else c->pty_count = read(c->pty_fd, c->pty_buffer, BufferSize); if (c->pty_count < 0) { DebugMsg(__LINE__, c, "Pty read error=%d\n", errno); } else if (c->pty_count == 0) { if (NetworkDrop) { DebugMsg(__LINE__, c, "Close of pty\n"); CloseConnection(c); } } else { if (NetworkOnDemand && (c->socket_fd < 0)) { OpenSocket(c); c->status = ACTIVE; } /* * Normal data should be * preceded with a NULL byte. */ if (c->pty_buffer[0] == 0) { c->pty_count--; c->pty_offset++; } else if (PKControl(c->pty_buffer[0])) { send(c->socket_fd, c->pty_buffer, 1, MSG_OOB); c->pty_count = 0; } else { DebugMsg(__LINE__, c, "Read %02x\n", c->pty_buffer[0]); c->pty_count = 0; } } #endif /* !STREAMPTY */ } if ((c->socket_fd >= 0) && FD_ISSET(c->socket_fd, &infd)) { n = read(c->socket_fd, c->socket_buffer + c->socket_count + c->socket_offset, BufferSize - c->socket_count - c->socket_offset); if (n <= 0) { LostConnection(c); continue; } else { c->socket_count += n; } } if (FD_ISSET(c->pty_fd, &outfd)) { n = write(c->pty_fd, c->socket_buffer + c->socket_offset, c->socket_count); if (n > 0) { c->socket_count -= n; c->socket_offset += n; } } if ((c->socket_fd >= 0) && FD_ISSET(c->socket_fd, &outfd)) { n = write(c->socket_fd, c->pty_buffer + c->pty_offset, c->pty_count); if (n > 0) { c->pty_count -= n; c->pty_offset += n; } } } } } /*****************************************************************************/ #ifdef STREAMPTY /* * Read from a System V pty in "packet mode." In this mode, we get * all streams messages sent down the slave side. The come up with * the streams message type in the control part of the message we read. * Because we never know whether what we read will be a streams message * or just some normal data until after we have read it, we have to use * getmsg() for all reads. * * To be almost plug-compatible with read, we return 0 only when we * get a zero length data message. This indicates that last user * has closed the slave side of the pty. In all other cases where * we got a message, but no user data, we return -1 and EWOULDBLOCK * so that the caller will ignore it. */ void pktread(c) ConfPtr c; { struct strbuf ctlbuf; struct strbuf databuf; u_char ctlstuff; int flags = 0; int ret; char cntl; memset(&ctlbuf, 0, sizeof (ctlbuf)); memset(&databuf, 0, sizeof (databuf)); ctlbuf.maxlen = sizeof(ctlstuff); ctlbuf.buf = (char *) &ctlstuff; databuf.maxlen = BufferSize; databuf.buf = c->pty_buffer; if ((ret = getmsg(c->pty_fd, &ctlbuf, &databuf, &flags)) == -1) return; /* * Check for the pty closing. We also keep track of the * very first data that arrives on the port, so that we * can close the pts stream - otherwise we don't get a * close notification. */ if (databuf.len == 0) { if (NetworkDrop) { DebugMsg(__LINE__, c, "Close of pty\n"); CloseConnection(c); OpenPts(c); return; } } else if (databuf.len > 0) { /* * If checking for pty closes then we need to close the * pts fd now, so that we get the close notification. */ if (NetworkDrop && (c->flags & PTSOPEN)) { c->flags &= ~PTSOPEN; close(c->pts_fd); c->pts_fd = -1; } if (NetworkOnDemand && (c->socket_fd < 0)) { OpenSocket(c); c->status = ACTIVE; } } if (ctlbuf.len <= 0 || ctlstuff == M_DATA) { if (databuf.len > 0) c->pty_count = databuf.len; return; } if (ctlstuff == M_FLUSH && (databuf.buf[0] & FLUSHW)) { cntl = TIOCPKT_FLUSHWRITE; send(c->socket_fd, &cntl, 1, MSG_OOB); } return; } #endif /* STREAMPTY */ /*****************************************************************************/