/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ptydrv -- pseudo tty driver * * Copyright (c) 1994, 1995 by Gunther Schadow * */ #include "ptydrv.h" #include #include #include #include #include #include #include #include #include #include /* * Define last resort defaults. * These should have been supplied by the Makefile -- do not edit here! */ #ifndef LOG_IDENT # define LOG_IDENT "ptydrv" #endif #ifndef LOG_FACILITY # define LOG_FACILITY LOG_USER #endif #ifndef LOG_FILE # define LOG_FILE "/tmp/ptydrv.log" #endif #ifndef MASTER # define MASTER "/dev/ptyp0" #endif #ifndef SLAVE # define SLAVE "/dev/ttyp0" #endif #ifndef DEST_AF # define DEST_AF AF_FILE #endif #ifndef DEST_ADDRESS # define DEST_ADDRESS "/dev/null" #endif #ifndef TRANSCRIPT_FILE # define TRANSCRIPT_FILE "/tmp/ptydrv.trans" #endif /* * End of last resort defaults. */ const char *program; const char *source_address = MASTER"%"SLAVE; const char *dest_address = DEST_ADDRESS; int transcript_fd = FAIL; void usage() { fprintf(stderr, "usage: %s " "[-h] [-t] [-s source] [[-f|i|p|u] dest]\n", program); exit(1); } int main(int argc, char *argv[]) { int dlevel = LOG_WARNING; int dest_af = DEST_AF; /* address family */ int mfd; /* master descriptor */ bool dont_fork = FALSE; #ifdef USE_FSYSLOG const char *logfile = LOG_FILE; #endif /* * Parse command line arguments */ { char c; ((char *)program) = argv[0]; while ( ( c = getopt(argc, argv, "f:hi:l:p:s:tu:x:y") ) != FAIL ) { switch(c) { case 's': source_address = optarg; break; case 'f': dest_af = AF_FILE; ((char *)dest_address) = optarg; break; case 'i': dest_af = AF_INET; ((char *)dest_address) = optarg; break; case 'p': dest_af = AF_PIPE; ((char *)dest_address) = optarg; break; case 'u': dest_af = AF_UNIX; ((char *)dest_address) = optarg; break; case 't': transcript_fd = open(TRANSCRIPT_FILE, O_WRONLY | O_CREAT | O_APPEND, 0666); if(transcript_fd != FAIL) { fprintf(stderr, "writing transcript to " TRANSCRIPT_FILE "(%d)\n", transcript_fd); } else { perror("open " TRANSCRIPT_FILE); exit(1); } break; #ifdef DEBUG case 'y': dont_fork = TRUE; break; case 'x': dlevel = atoi(optarg); if (dlevel > LOG_DEBUG) dlevel = LOG_DEBUG; if (dlevel < LOG_WARNING) dlevel = LOG_WARNING; break; #endif #ifdef USE_FSYSLOG case 'l': logfile = optarg; break; #endif default: usage(program); } } } DBG(syslog(LOG_DEBUG, "bind %s", source_address)); mfd = bind_pty(source_address); if(mfd < 0) { syslog(LOG_ERR, "bind_pty %s: %m", source_address); /* * If we cannot open the master, we have to give up. */ exit(1); } if(!dont_fork) { /* * Fork and disassociate from terminal */ int i = fork(); if(i < 0) { fprintf(stderr, "%s:fork: %s", program, strerror(errno)); exit(1); } if(i > 0) exit(0); /* parent exits here */ for(i = 0; i < 3; i++) close(i); if(setsid() == FAIL) { syslog(LOG_ERR,"setsid: %m"); exit(1); } } /* * Open syslog */ openlog(LOG_IDENT, LOG_NDELAY | LOG_PID, LOG_FACILITY); setlogmask(LOG_UPTO(dlevel)); { /* * Log start of process */ char afopt; switch(dest_af) { case AF_FILE: afopt = 'f'; break; case AF_PIPE: afopt = 'p'; break; case AF_UNIX: afopt = 'u'; break; case AF_INET: afopt = 'i'; break; } syslog(LOG_INFO, "%s -s %s -%c %s", program, source_address, afopt, dest_address); } /* * Open pty device and wait for data to be available for reading. * When data is available, connect the destination and forward data * back and forth between pty and destination. * When end of file is detected, shutdown the connection and and * loop. */ do { int cfd; /* client descriptor (same as master) */ int dfd; /* destination (dest) descriptor */ /* * Accept connections to pty slave at pty master */ if((cfd = accept_pty(mfd)) == FAIL) goto fail_before_open_dest; /* * Connect to the destination */ switch(dest_af) { case AF_FILE: dfd = connect_file(dest_address); break; case AF_PIPE: dfd = connect_pipe(dest_address); break; case AF_UNIX: dfd = connect_unix(dest_address); break; case AF_INET: dfd = connect_inet(dest_address); break; } if( dfd == FAIL ) { /* * If we could not connect the destination, there might still * be a chance in a subsequent try. On HP we can forward the * error to the client process by TIOCTRAP on any request and * return EAGAIN error. On traditional BSD, we can only eat up * incoming data. */ fwddata(cfd, FAIL); goto fail_before_open_dest; } /* * Forward data from master to destination and vice versa */ switch(dest_af) { case AF_FILE: if(fwddata(cfd, dfd) == FAIL) goto fail; break; case AF_PIPE: if(fwddata(cfd, dfd) == FAIL) goto fail; break; case AF_UNIX: case AF_INET: if(duplex(cfd, dfd) == FAIL) goto fail; } DBG(syslog(LOG_DEBUG, "transaction successful")); /* * Shutdown destination */ fail: DBG(syslog(LOG_DEBUG, "closing destination")); switch(dest_af) { case AF_FILE: shutdown_file(dfd); break; case AF_PIPE: shutdown_pipe(dfd); break; case AF_UNIX: shutdown_unix(dfd); break; case AF_INET: shutdown_inet(dfd); break; } fail_before_open_dest: #ifdef hpux_DISCARD /* * Discard the queue of requests */ if(shutdown(mfd) == FAIL) { syslog(LOG_ERR, "shutdown source: %m"); exit(1); } DBG(syslog(LOG_DEBUG, "bind %s", source_address)); mfd = bind(source_address); if(mfd < 0) { syslog(LOG_ERR, "bind %s: %m", source_address); /* * If we cannot open the master, we have to give up. */ exit(1); } #endif } while(1); }