/* * Flush: A little program that tricks another program into line buffering * its output. * * By Michael Sandrof * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2003 Matthew R. Green. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * * $Id: ircflush.c,v 1.5 2003/01/08 20:00:54 f Exp $ */ #include "irc.h" #include <sys/wait.h> #ifndef __linux__ # if defined(__SVR4) || defined(HAVE_TERMIOS_H) # include <termios.h> # else # include <sgtty.h> /* SVR4 => sgtty = yuk */ # endif /* SOLARIS */ #endif /* __linux__ */ #define BUFFER_SIZE 1024 /* descriptors of the tty and pty */ int master, slave; pid_t pid; RETSIGTYPE death _((void)); static void setup_master_slave _((void)); RETSIGTYPE death() { close(0); close(master); kill(pid, SIGKILL); wait(0); exit(0); } /* * setup_master_slave: this searches for an open tty/pty pair, opening the * pty as the master device and the tty as the slace device */ static void setup_master_slave() { char line[11]; char linec; int linen; for (linec = 'p'; linec <= 's'; linec++) { snprintf(line, sizeof line, "/dev/pty%c0", linec); if (access(line, 0) != 0) break; for (linen = 0; linen < 16; linen++) { snprintf(line, sizeof line, "/dev/pty%c%1x", linec, linen); if ((master = open(line, O_RDWR)) >= 0) { snprintf(line, sizeof line, "/dev/tty%c%1x", linec, linen); if (access(line, R_OK | W_OK) == 0) { if ((slave = open(line, O_RDWR)) >= 0) return; } close(master); } } } fprintf(stderr, "flush: Can't find a pty\n"); exit(0); } /* * What's the deal here? Well, it's like this. First we find an open * tty/pty pair. Then we fork three processes. The first reads from stdin * and sends the info to the master device. The next process reads from the * master device and sends stuff to stdout. The last processes is the rest * of the command line arguments exec'd. By doing all this, the exec'd * process is fooled into flushing each line of output as it occurs. */ int main(argc, argv) int argc; char **argv; { char buffer[BUFFER_SIZE]; int cnt; fd_set rd; if (argc < 2) { fprintf(stderr, "Usage: %s [program] [arguments to program]\n", argv[0]); exit(1); } pid = open("/dev/tty", O_RDWR); #ifdef HAVE_SETSID setsid(); #else ioctl(pid, TIOCNOTTY, 0); #endif /* HAVE_SETSID */ setup_master_slave(); switch (pid = fork()) { case -1: fprintf(stderr, "flush: Unable to fork process!\n"); exit(1); case 0: dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); close(master); setuid(getuid()); setgid(getgid()); execvp(argv[1], &(argv[1])); fprintf(stderr, "flush: Error exec'ing process!\n"); exit(1); break; default: (void) MY_SIGNAL(SIGCHLD, death, 0); close(slave); while (1) { FD_ZERO(&rd); FD_SET(master, &rd); FD_SET(0, &rd); switch (select(NFDBITS, &rd, 0, 0, 0)) { case -1: case 0: break; default: if (FD_ISSET(0, &rd)) { if ((cnt = read(0, buffer,BUFFER_SIZE)) > 0) write(master, buffer, cnt); else death(); } if (FD_ISSET(master, &rd)) { if ((cnt = read(master, buffer, BUFFER_SIZE)) > 0) write(1, buffer, cnt); else death(); } } } break; } return 0; }