/* The following compilation lines were used with the associated OS's. sco: cc -o rtty.sco rtty.c -lsocket sun: cc -o rtty.solaris1 rtty.c isc: cc -o rtty.isc rtty.c -linet -lcposix aix: cc -DAIX -o rtty.aix rtty.c */ /******************************************************************** * NAME * rtty - Connect a tty to a remote TCP port. * * SYNOPSIS * rtty [-dhw] tty host port * * DESCRIPTION * rtty attaches the master side of a named pseudo tty * to a TCP session. * * This is most often useful to allow a TCP terminal server * port to appear as a local tty on some host computer. * * -[0-9] Wait for data to be written to the slave side of * the pseudo tty before opening the connection; * open the connection and sleep the given number * seconds [0-9] before writing the data to the port. * * -d Daemonize. A detached child process is spawned * to perform the program function, ignoring all * signals. * * -h Hold the tty open so "stty" settings are not * disrupted and EOF's are not sent when the tty * is locally closed. * * -q Quit after one session is complete. Normally the * program loops to handle multiple sessions. * * -s Suppress the copyright notice. * * -w Wait for data to be written to the tty port before * executing the rsh command. * * -x Output debugging information. Specifying "x" * twice produces even more output. * * tty The name of a local pseudo tty (eg ttyq0) to be * used for the connection. * * host A hostname or IP address. * * port Decimal TCP port number. * * EXAMPLES * * To associate "ttypf" with the dedicated printer device * connected to (DigiBoard NC/Con-16) node ncx, port 4. * * rtty -dh ttypf ncx 2104 * * To associate "ttysf" with a dial-in/dial-out (type mio) * modem attached to (DigiBoard NC/Con-16) node nccon port 13. * * rtty -1d ttysf nccon 2113 */ static char *copyright[] = { " ", "@(#)Copyright 1992, Digi International, All Rights Reserved.", "@(#)An unlimited use and distribution license is granted for use with,", "@(#)and only with, DigiBoard terminal servers and other network prod ucts.", " ", 0 } ; #if !defined(lint) static char version[] = "@(#)$Id: rtty.c,v 1.4 1992/11/01 14:46:52 gene Exp $" ; #endif #include #include #include #include #include #include #include #include #if defined(AIX) #include #endif extern char *strstr() ; extern char *strcat() ; extern char *strcpy() ; extern int optind ; extern char *optarg ; extern void exit() ; char pty[50] ; /* Name of master tty */ char tty[50] ; /* Name of slave tty */ char hbuf[512] ; /* Buffer from pty to host */ char mbuf[512] ; /* Buffer from host to pty */ int daemonize ; /* Run as a daemon */ int holdopen ; /* Hold slave tty open */ int datadelay ; /* Wait for data */ int single ; /* Process single request only */ int debug ; /* Debug level */ int silent ; /* Suppress copyright notice */ #define dprint(d, f) { if (debug >= d) (void) fprintf(stderr, f) ; } #define dprint1(d, f, x) { if (debug >= d) (void) fprintf(stderr, f, x) ; } /* * Catch interrupts, and exit. */ void quit() { exit(0) ; } /* * Main program. Do the deed right here. */ main(argc, argv) int argc ; char **argv ; { int ac ; char **av ; int c ; int w ; int i ; char *p ; int hfd ; int mfd ; int sfd ; int nfd ; int port ; int mcount ; int hcount ; int child ; unsigned delay ; struct hostent *hp ; struct sockaddr_in sin ; fd_set fdread ; fd_set fdwrite ; fd_set fdnull ; /* * Unpack parameters. */ datadelay = -1 ; while ((c = getopt(argc, argv, "0123456789dhqswx")) != -1) { switch(c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': datadelay = c - '0' ; break ; case 'd': daemonize++ ; break ; case 'h': holdopen++ ; break ; case 'q': single++ ; break ; case 's': silent++ ; break ; case 'x': debug++ ; break ; default: goto usage ; } } av = argv + optind ; ac = argc - optind ; if (!silent) { for (i = 0 ; p = copyright[i] ; i++) (void) fprintf(stderr, "%s\n", p + 4) ; } if (ac != 3) goto usage ; /* * From given (slave) ttyname, form full master/slave tty names. */ p = strstr(av[0], "tty") ; if (p == 0) goto usage ; (void) strcpy(pty, "/dev/") ; (void) strcat(pty, "p") ; (void) strcat(pty, p+1) ; /* * Look up the host name in the database. */ hp = gethostbyname(av[1]) ; if (hp == 0) { dprint1(0, "Unknown host: %s\n", av[1]) ; goto usage ; } /* * Get the requested port. */ if (sscanf(av[2], "%d%c", &port, &c) != 1) { dprint1(0, "Bad port: %s\n", av[2]) ; goto usage ; } /* * Daemonize. * * We spawn a child to do the dirtywork, then let the * parent exit. Any file descriptors associated with * a terminal are redirected to /dev/null. */ if (daemonize) { while ((child = fork()) == -1) sleep(1) ; if (child) return(0) ; dprint(1, "Daemonized\n") ; (void) close(0) ; (void) open("/dev/null", O_RDWR) ; if (isatty(2)) { if (isatty(1)) { dprint(1, "Closing stdout and stderr\n") ; (void) close(2) ; (void) dup(0) ; } else { dprint(1, "Redirecting stderr to stdout\n") ; (void) close(2) ; (void) dup(1) ; } } (void) close(1) ; (void) dup(0) ; for (i = 3 ; i < 10 ; i++) (void) close(i) ; (void) setpgrp(0, 0) ; } /* * Setup to catch signals. */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) (void) signal(SIGHUP, quit) ; if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void) signal(SIGINT, quit) ; if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) (void) signal(SIGQUIT, quit) ; if (signal(SIGTERM, SIG_IGN) != SIG_IGN) (void) signal(SIGTERM, quit) ; /* * Make a TCP connection to the requested socket. */ mfd = sfd = hfd = -1 ; for (;;) { delay = 0 ; /* * Open the pseudo tty. */ if (mfd == -1) { /* * Open the master side of the pty. */ dprint(1, "Opening master tty\n") ; mfd = open(pty, O_RDWR) ; if (mfd == -1) { dprint(0, "Cannot open: ") ; perror(pty) ; delay = 60 ; goto again ; } /* * If requested, open the slave side. */ if (holdopen) { dprint(1, "Opening slave tty\n") ; (void) strcpy(tty, "/dev/") ; (void) strcat(tty, p) ; sfd = open(tty, O_RDWR) ; if (sfd == -1) { dprint(0, "Cannot open: ") ; perror(tty) ; (void) close(mfd) ; delay = 60 ; goto again ; } } } /* * When requested, wait for data to be written to the * pty slave side before attempting to connect to * the remote TCP host. */ mcount = 0 ; hcount = 0 ; if (datadelay >= 0) { dprint(1, "Waiting for input data\n") ; hcount = read(mfd, hbuf, sizeof(hbuf)) ; dprint1(2, "Received %2d byte(s) from PTY\n", hcount) ; if (hcount <= 0) goto again ; } /* * Open a TCP connection to the remote. */ dprint(1, "Opening TCP connection\n") ; hfd = socket(AF_INET, SOCK_STREAM, 0) ; if (hfd < 0) { perror("Cannot obtain a socket") ; return(1) ; } bzero((char *)&sin, sizeof(sin)) ; bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length) ; sin.sin_family = hp->h_addrtype ; sin.sin_port = htons(port) ; if (connect(hfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("Cannot connect to remote") ; if (datadelay < 0) delay = 60 ; goto again ; } /* * Pause a bit for the connection to be completed. * * This gives modems on the other end a chance to think * before the ATDT stuff starts rolling. */ FD_ZERO(&fdnull) ; if (datadelay > 0) { (void) sleep((unsigned) datadelay) ; } /* * Loop to send and receive data. */ dprint(1, "Ready to transfer data\n") ; for (;;) { /* * Wait for read/write ready. */ FD_ZERO(&fdread) ; FD_ZERO(&fdwrite) ; if (mcount) FD_SET(mfd, &fdwrite) ; else FD_SET(hfd, &fdread) ; if (hcount) FD_SET(hfd, &fdwrite) ; else FD_SET(mfd, &fdread) ; nfd = select(16, &fdread, &fdwrite, &fdnull, (struct timeval *)0) ; if (nfd <= 0) { perror("select") ; goto again ; } /* * Read data from host, destined for the pty. */ if (FD_ISSET(hfd, &fdread)) { mcount = read(hfd, mbuf, sizeof(mbuf)) ; dprint1(2, "Received %2d byte(s) from host\n", mcount) ; if (mcount <= 0) break ; } /* * Read data from the pty, destined for the host. */ if (FD_ISSET(mfd, &fdread)) { hcount = read(mfd, hbuf, sizeof(hbuf)) ; dprint1(2, "Received %2d byte(s) from PTY\n", hcount) ; if (hcount <= 0) break ; } /* * Write data to the TCP host. */ if (FD_ISSET(hfd, &fdwrite)) { dprint1(2, "Sending %2d byte(s) to host\n", hcount) ; w = write(hfd, hbuf, hcount) ; if (w <= 0) break ; hcount = 0 ; } /* * Write data to the pseudo tty. */ if (FD_ISSET(mfd, &fdwrite)) { dprint1(2, "Sending %2d byte(s) to PTY\n", mcount) ; w = write(mfd, mbuf, mcount) ; if (w <= 0) break ; mcount = 0 ; } } /* * Close the TCP connection, and if we are not holding * open the pty connection ourselves, close that also. */ again: if (hfd >= 0) { dprint(1, "Closing host\n") ; (void) close(hfd) ; hfd = -1 ; } if (!holdopen && mfd >= 0) { dprint(1, "Closing pty\n") ; (void) close(mfd) ; (void) close(sfd) ; mfd = sfd = -1 ; } /* * In single connection mode, exit now. */ if (single) break ; if (delay) { dprint1(1, "Delaying %d seconds\n", delay) ; (void) sleep(delay) ; } } dprint(1, "Exit after one session\n") ; return(0) ; usage: (void) fprintf(stderr, "usage: %s [-0123456789dhqswx] tty nodename port\n", argv[0]) ; return(2) ; }