1 /*
2  * Flush: A little program that tricks another program into line buffering
3  * its output.
4  *
5  * By Michael Sandrof
6  *
7  * Copyright (c) 1990 Michael Sandrof.
8  * Copyright (c) 1991, 1992 Troy Rollo.
9  * Copyright (c) 1992-2000 Matthew R. Green.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "irc.h"
37 IRCII_RCSID("@(#)$Id: ircflush.c,v 1.30 2000/02/26 05:00:47 mrg Exp $");
38 
39 #include <sys/wait.h>
40 
41 #ifndef __linux__
42 # if defined(__SVR4) || defined(HAVE_TERMIOS_H)
43 #  include <termios.h>
44 # else
45 #  include <sgtty.h>	/* SVR4 => sgtty = yuk */
46 # endif /* SOLARIS */
47 #endif /* __linux__ */
48 
49 
50 #define BUFFER_SIZE 1024
51 
52 /* descriptors of the tty and pty */
53 	int	master,
54 		slave;
55 	pid_t	pid;
56 
57 RETSIGTYPE death _((void));
58 static	void	setup_master_slave _((void));
59 
60 RETSIGTYPE
death()61 death()
62 {
63 	close(0);
64 	close(master);
65 	kill(pid, SIGKILL);
66 	wait(0);
67 	exit(0);
68 }
69 
70 /*
71  * setup_master_slave: this searches for an open tty/pty pair, opening the
72  * pty as the master device and the tty as the slace device
73  */
74 static void
setup_master_slave()75 setup_master_slave()
76 {
77 	char	line[11];
78 	char	linec;
79 	int	linen;
80 
81 	for (linec = 'p'; linec <= 's'; linec++)
82 	{
83 		sprintf(line, "/dev/pty%c0", linec);
84 		if (access(line, 0) != 0)
85 			break;
86 		for (linen = 0; linen < 16; linen++)
87 		{
88 			sprintf(line, "/dev/pty%c%1x", linec, linen);
89 			if ((master = open(line, O_RDWR)) >= 0)
90 			{
91 				sprintf(line, "/dev/tty%c%1x", linec, linen);
92 				if (access(line, R_OK | W_OK) == 0)
93 				{
94 					if ((slave = open(line, O_RDWR)) >= 0)
95 						return;
96 				}
97 				close(master);
98 			}
99 		}
100 	}
101 	fprintf(stderr, "flush: Can't find a pty\n");
102 	exit(0);
103 }
104 
105 /*
106  * What's the deal here?  Well, it's like this.  First we find an open
107  * tty/pty pair.  Then we fork three processes.  The first reads from stdin
108  * and sends the info to the master device.  The next process reads from the
109  * master device and sends stuff to stdout.  The last processes is the rest
110  * of the command line arguments exec'd.  By doing all this, the exec'd
111  * process is fooled into flushing each line of output as it occurs.
112  */
113 int
main(argc,argv)114 main(argc, argv)
115 	int	argc;
116 	char	**argv;
117 {
118 	char	lbuf[BUFFER_SIZE];
119 	size_t	cnt;
120 	fd_set	rd;
121 
122 	if (argc < 2)
123 	{
124 	    fprintf(stderr, "Usage: %s [program] [arguments to program]\n", argv[0]);
125 	    exit(1);
126 	}
127 	pid = open("/dev/tty", O_RDWR);
128 #ifdef HAVE_SETSID
129 	setsid();
130 #else
131 	ioctl(pid, TIOCNOTTY, 0);
132 #endif /* HAVE_SETSID */
133 	setup_master_slave();
134 	switch (pid = fork())
135 	{
136 	case -1:
137 		fprintf(stderr, "flush: Unable to fork process!\n");
138 		exit(1);
139 	case 0:
140 		dup2(slave, 0);
141 		dup2(slave, 1);
142 		dup2(slave, 2);
143 		close(master);
144 		setuid(getuid());
145 		setgid(getgid());
146 		execvp(argv[1], &(argv[1]));
147 		fprintf(stderr, "flush: Error exec'ing process!\n");
148 		exit(1);
149 		break;
150 	default:
151 		(void) MY_SIGNAL(SIGCHLD, death, 0);
152 		close(slave);
153 		while (1)
154 		{
155 			FD_ZERO(&rd);
156 			FD_SET(master, &rd);
157 			FD_SET(0, &rd);
158 			switch (select(NFDBITS, &rd, 0, 0, 0))
159 			{
160 			case -1:
161 			case 0:
162 				break;
163 			default:
164 				if (FD_ISSET(0, &rd))
165 				{
166 				    if ((cnt = read(0, lbuf, BUFFER_SIZE)) > 0)
167 					write(master, lbuf, cnt);
168 				    else
169 					death();
170 				}
171 				if (FD_ISSET(master, &rd))
172 				{
173 					if ((cnt = read(master, lbuf,
174 							BUFFER_SIZE)) > 0)
175 						write(1, lbuf, cnt);
176 					else
177 						death();
178 				}
179 			}
180 		}
181 		break;
182 	}
183 	return 0;
184 }
185