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