/*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1