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-2021 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("@(#)$eterna: ircflush.c,v 1.49 2021/02/24 21:47:46 mrg Exp $");
38
39 #include <sys/wait.h>
40
41 #include <termios.h>
42
43 #define BUFFER_SIZE 1024
44
45 /* descriptors of the tty and pty */
46 static int master,
47 slave;
48 static pid_t pid;
49
50 int main(int, char *[], char *[]);
51 static void death(int);
52 static void setup_master_slave(void);
53
54 static void
death(int signo)55 death(int signo)
56 {
57 close(0);
58 close(master);
59 kill(pid, SIGKILL);
60 wait(0);
61 exit(0);
62 }
63
64 /*
65 * setup_master_slave: this searches for an open tty/pty pair, opening the
66 * pty as the master device and the tty as the slace device
67 */
68 static void
setup_master_slave(void)69 setup_master_slave(void)
70 {
71 char line[24];
72 char linec;
73 int linen;
74
75 for (linec = 'p'; linec <= 's'; linec++)
76 {
77 snprintf(line, sizeof line, "/dev/pty%c0", linec);
78 if (access(line, 0) != 0)
79 break;
80 for (linen = 0; linen < 16; linen++)
81 {
82 snprintf(line, sizeof line, "/dev/pty%c%1x", linec, linen);
83 if ((master = open(line, O_RDWR)) >= 0)
84 {
85 snprintf(line, sizeof line, "/dev/tty%c%1x", linec, linen);
86 if (access(line, R_OK | W_OK) == 0)
87 {
88 if ((slave = open(line, O_RDWR)) >= 0)
89 return;
90 }
91 close(master);
92 }
93 }
94 }
95 fprintf(stderr, "flush: Can't find a pty\n");
96 exit(0);
97 }
98
99 /*
100 * What's the deal here? Well, it's like this. First we find an open
101 * tty/pty pair. Then we fork three processes. The first reads from stdin
102 * and sends the info to the master device. The next process reads from the
103 * master device and sends stuff to stdout. The last processes is the rest
104 * of the command line arguments exec'd. By doing all this, the exec'd
105 * process is fooled into flushing each line of output as it occurs.
106 */
107 int
main(int argc,char * argv[],char * envp[])108 main(int argc, char *argv[], char *envp[])
109 {
110 char lbuf[BUFFER_SIZE];
111 size_t cnt;
112 fd_set rd;
113
114 if (argc < 2)
115 {
116 fprintf(stderr, "Usage: %s [program] [arguments to program]\n", argv[0]);
117 exit(1);
118 }
119 pid = open("/dev/tty", O_RDWR);
120 #ifdef HAVE_SETSID
121 setsid();
122 #else
123 ioctl(pid, TIOCNOTTY, 0);
124 #endif /* HAVE_SETSID */
125 setup_master_slave();
126 switch (pid = fork())
127 {
128 case -1:
129 fprintf(stderr, "flush: Unable to fork process!\n");
130 exit(1);
131 case 0:
132 dup2(slave, 0);
133 dup2(slave, 1);
134 dup2(slave, 2);
135 close(master);
136 (void)setuid(getuid());
137 (void)setgid(getgid());
138 execvp(argv[1], &(argv[1]));
139 fprintf(stderr, "flush: Error exec'ing process!\n");
140 exit(1);
141 break;
142 default:
143 (void) MY_SIGNAL(SIGCHLD, death, 0);
144 close(slave);
145 while (1)
146 {
147 FD_ZERO(&rd);
148 FD_SET(master, &rd);
149 FD_SET(0, &rd);
150 switch (select(NFDBITS, &rd, 0, 0, 0))
151 {
152 case -1:
153 case 0:
154 break;
155 default:
156 if (FD_ISSET(0, &rd))
157 {
158 if ((cnt = read(0, lbuf, sizeof lbuf)) > 0)
159 {
160 if (write(master, lbuf, cnt) != cnt)
161 death(0);
162 }
163 else
164 death(0);
165 }
166 if (FD_ISSET(master, &rd))
167 {
168 if ((cnt = read(master, lbuf,
169 sizeof lbuf)) > 0)
170 {
171 if (write(1, lbuf, cnt) != cnt)
172 death(0);
173 }
174 else
175 death(0);
176 }
177 }
178 }
179 break;
180 }
181 return 0;
182 }
183