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