1 /*
2  * Copyright (C) 2003  Sam Horrocks
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 /*
21  * Speedy Frontend Program
22  */
23 
24 #include "speedy.h"
25 
26 /*
27  * FILE DESCRIPTOR CODE
28  */
29 
30 #define FD_UNKNOWN	0
31 #define FD_CLOSE	1
32 #define FD_BLOCK	2
33 #define FD_NOBLOCK	3
34 
35 typedef struct _fdinfo {
36     int  flags;
37     char state;
38 } fdinfo_t;
39 
40 static int fdinfo_size;
41 static fdinfo_t *fdinfo;
42 
fd_change(int fd,int state)43 static void fd_change(int fd, int state) {
44     if (state != fdinfo[fd].state && fdinfo[fd].state != FD_CLOSE) {
45 	if (state == FD_CLOSE) {
46 	    /* SGI's /dev/tty goes crazy unless we turn on blocking I/O. */
47 	    if (fdinfo[fd].state == FD_NOBLOCK && fd <= 2)
48 		fd_change(fd, FD_BLOCK);
49 	    close(fd);
50 	    fdinfo[fd].state = FD_CLOSE;
51 	} else {
52 	    int flags;
53 
54 	    fdinfo[fd].state = state;
55 	    if (state == FD_BLOCK) {
56 		flags = fdinfo[fd].flags & ~O_NONBLOCK;
57 	    } else {
58 		flags = fdinfo[fd].flags | O_NONBLOCK;
59 	    }
60 	    if (fcntl(fd, F_SETFL, flags) == -1 && errno == EBADF) {
61 		fdinfo[fd].state = FD_CLOSE;
62 	    }
63 	}
64     }
65 }
66 
fd_init(int fd,int flags,int state)67 static void fd_init(int fd, int flags, int state) {
68     if (fd >= fdinfo_size) {
69 #ifdef SPEEDY_EFENCE
70 	fdinfo_size = fd + 1;
71 #else
72 	fdinfo_size = fd + 10;
73 #endif
74 	speedy_renew(fdinfo, fdinfo_size, fdinfo_t);
75     }
76     fdinfo[fd].flags = flags;
77     fdinfo[fd].state = FD_UNKNOWN;
78     fd_change(fd, state);
79 }
80 
81 #define fd_closed(fd) (fdinfo[fd].state == FD_CLOSE)
82 #define fd_open(fd) (!fd_closed(fd))
83 
84 /*
85  * END OF FILE DESCRIPTOR SECTION
86  */
87 
killme(int sig)88 static void killme(int sig) {
89     sigset_t sigs;
90     signal(sig, SIG_DFL);
91     sigemptyset(&sigs);
92     sigaddset(&sigs, sig);
93     sigprocmask(SIG_UNBLOCK, &sigs, NULL);
94     kill(speedy_util_getpid(), sig);
95     speedy_util_exit(sig+128, 0);
96 }
97 
98 static int sig_pipes[2];
99 static volatile int got_sig;
catch_sig(int sig)100 static void catch_sig(int sig) {
101     got_sig++;
102     write(sig_pipes[1], "", 1);
103 }
104 
105 /*
106  * When profiling, only call speedy_opt_init once
107  */
108 #ifdef SPEEDY_PROFILING
109 static int did_opt_init, profile_runs;
110 #define DO_OPT_INIT if (!did_opt_init++) speedy_opt_init
111 #else
112 #define DO_OPT_INIT speedy_opt_init
113 #endif
114 
115 #ifdef FILL_STDIN_BUF
116 #define STDIN_INITIAL_STATE FD_BLOCK
117 #else
118 #define STDIN_INITIAL_STATE FD_NOBLOCK
119 #endif
120 
121 #define CB_IN	(cb[0])
122 #define CB_OUT	(cb[1])
123 #define CB_ERR	(cb[2])
124 
125 extern char **environ;
126 
127 static CopyBuf cb[NUMFDS];
128 static int got_stdout, stop_sock_reads, read_stopped[NUMFDS];
129 
130 /* See if done copying through this buf */
my_copydone(const CopyBuf * b)131 static int my_copydone(const CopyBuf *b) {
132     return speedy_cb_copydone(b) &&
133 	(got_stdout || b != &CB_OUT || speedy_cb_eof(b));
134 }
135 
136 /* See if we can read into this buf */
my_canread(const CopyBuf * b)137 static int my_canread(const CopyBuf *b) {
138     return
139 	!((b) != &CB_IN && stop_sock_reads && read_stopped[(b)-&CB_IN]) &&
140 	(speedy_cb_canread(b) ||
141 	    (!got_stdout && b == &CB_OUT && !speedy_cb_eof(b)));
142 }
143 
144 /* Try to close this copy buf */
try_close(const CopyBuf * b)145 static void try_close(const CopyBuf *b) {
146     if (my_copydone(b)) {
147 	fd_change(b->rdfd, FD_CLOSE);
148 	fd_change(b->wrfd, FD_CLOSE);
149     }
150 }
151 
doit(const char * const * argv,int * exit_on_sig,int * exit_val)152 static void doit(const char * const *argv, int *exit_on_sig, int *exit_val)
153 {
154     PollInfo pi;
155     SpeedyBuf ibuf;
156     int backend_exited = 0, am_child = 0, in_is_tty;
157     int socks[NUMFDS];
158     register int i;
159     slotnum_t fslotnum;
160 
161     got_stdout = stop_sock_reads = 0;
162 
163     /* Initialize file descriptors */
164     fd_init(0, O_RDONLY, STDIN_INITIAL_STATE);
165     for (i = 1; i <= 2; ++i)
166 	fd_init(i, O_WRONLY, FD_NOBLOCK);
167 
168     /* Is stdin a tty? */
169     in_is_tty = fd_open(0) ? isatty(0) : 0;
170 
171     /* Initialize options */
172     DO_OPT_INIT(argv, (const char * const *)environ);
173 
174 #   ifdef IAMSUID
175 	if (speedy_util_geteuid() == 0) {
176 	    int new_uid;
177 
178 	    /* Set group-id */
179 	    if (speedy_script_getstat()->st_mode & S_ISGID) {
180 		if (setegid(speedy_script_getstat()->st_gid) == -1)
181 		    speedy_util_die("setegid");
182 	    }
183 
184 	    /* Must set euid to something - either the script owner
185 	     * or the real-uid
186 	     */
187 	    if (speedy_script_getstat()->st_mode & S_ISUID) {
188 		new_uid = speedy_script_getstat()->st_uid;
189 	    } else {
190 		new_uid = speedy_util_getuid();
191 	    }
192 	    if (speedy_util_seteuid(new_uid) == -1)
193 		speedy_util_die("seteuid");
194 	}
195 #   endif
196 
197     /* Create buffer with env/argv data to send */
198     speedy_frontend_mkenv(
199 	(const char * const *)environ, speedy_opt_script_argv(), 0, &ibuf, 0
200     );
201 
202     /* Allocate buffers for copying below: */
203     /*	fd0 -> cb[0] -> s	*/
204     speedy_cb_init(
205 	&CB_IN,
206 	max(OPTVAL_BUFSIZPOST, ibuf.alloced),
207 	0,
208 	-1,
209 	&ibuf
210     );
211 
212     /* Read as much as possible from stdin, then make it non-blocking */
213 #ifdef FILL_STDIN_BUF
214     if (fd_open(0)) {
215 	speedy_cb_read(&CB_IN);
216 	fd_change(0, FD_NOBLOCK);
217     }
218 #endif
219 
220     /* Connect up with a backend */
221     if (!speedy_frontend_connect(socks, &fslotnum))
222 	DIE_QUIET("Cannot spawn backend process");
223 
224     /* Get ready to catch sig as soon as we send over environment to be */
225     pipe(sig_pipes);
226     signal(SIGUSR1, catch_sig);
227 
228     /* Non-blocking I/O on sockets */
229     for (i = 0; i < NUMFDS; ++i)
230 	fd_init(socks[i], O_RDWR, FD_NOBLOCK);
231 
232     /* Allocate buffers for copying below: */
233     /*	fd0 -> cb[0] -> s	*/
234     /*	s   -> cb[1] -> fd1	*/
235     /*	e   -> cb[2] -> fd2	*/
236     speedy_cb_setfd(&CB_IN, 0, socks[0]);
237     speedy_cb_init(
238 	&CB_OUT,
239 	OPTVAL_BUFSIZGET,
240 	socks[1],
241 	1,
242 	NULL
243     );
244     speedy_cb_init(
245 	&CB_ERR,
246 	512,
247 	socks[2],
248 	2,
249 	NULL
250     );
251 
252     /* Disable i/o on any fd's that are not open */
253     if (fd_closed(0))
254 	speedy_cb_seteof(&CB_IN);
255     for (i = 1; i < NUMFDS; ++i) {
256 	if (fd_closed(i))
257 	    speedy_cb_set_write_err(cb+i, EBADF);
258     }
259 
260     /* Poll/select may not wakeup on intial eof, so set for initial read here.
261      * (this is tested in initial_eof test #1)
262      */
263     {
264 	int m = socks[0];
265 	for (i = 1; i < NUMFDS; ++i)
266 	    m = max(m, socks[i]);
267 	speedy_poll_init(&pi, m);
268     }
269     speedy_poll_reset(&pi);
270     if (!in_is_tty && my_canread(&CB_IN))
271 	speedy_poll_set(&pi, 0, SPEEDY_POLLIN);
272     for (i = 1; i < NUMFDS; ++i) {
273 	if (my_canread(cb+i))
274 	    speedy_poll_set(&pi, cb[i].rdfd, SPEEDY_POLLIN);
275     }
276 
277     /* Try to write our env/argv without dropping into select */
278     if (speedy_cb_canwrite(&CB_IN))
279 	speedy_poll_set(&pi, CB_IN.wrfd, SPEEDY_POLLOUT);
280 
281     /* Turn off sigpipes so when we epipe on our sockets we don't die */
282     /* The backend should sigpipe anyways, and we'll get that status */
283     signal(SIGPIPE, SIG_IGN);
284 #ifdef SIGTTIN
285     /* We don't want to get a stop signal */
286     signal(SIGTTIN, SIG_IGN);
287 #endif
288 
289     /* Try to close copy bufs if possible now */
290     for (i = 0; i < NUMFDS; ++i)
291 	try_close(cb+i);
292 
293     /* We seem to lose our non-blocking on 0 if frontend_connect
294      * forks a backend.  Bug somewhere...
295      */
296     if (fd_open(0))
297 	fcntl(0, F_SETFL, O_RDONLY|O_NONBLOCK);
298 
299     /* Copy streams */
300     while (1) {
301 	/* Do reads/writes */
302         int close_stdout_delayed = 0;
303 	for (i = 0; i < NUMFDS; ++i) {
304 	    register CopyBuf *b = cb + i;
305 	    int do_read  = my_canread(b) &&
306 		           speedy_poll_isset(&pi, b->rdfd, SPEEDY_POLLIN);
307 	    int do_write = speedy_cb_canwrite(b) &&
308 			   speedy_poll_isset(&pi, b->wrfd, SPEEDY_POLLOUT);
309 
310 	    while (do_read || do_write) {
311 		if (do_read) {
312 		    int data_read, sz = speedy_cb_data_len(b);
313 
314 		    speedy_cb_read(b);
315 		    data_read = speedy_cb_data_len(b) > sz;
316 
317 		    if (!data_read)
318 			read_stopped[i] = 1;
319 
320 		    if (!got_stdout && b == &CB_OUT && data_read) {
321 			got_stdout = 1;
322 			speedy_frontend_proto2(socks[2], speedy_cb_shift(b));
323 		    }
324 		    if (speedy_cb_canwrite(b) &&
325 			(speedy_cb_eof(b) || data_read))
326 		    {
327 			do_write = 1;
328 		    }
329 		    do_read = 0;
330 		}
331 
332 		/* Attempt write now if we did a read.  Slightly more efficient
333 		 * and on SGI if we are run with >/dev/null,  select won't
334 		 * initially wakeup (this is tested in initial_eof test #2)
335 		 */
336 		if (do_write) {
337 		    int sz = speedy_cb_data_len(b);
338 		    speedy_cb_write(b);
339 		    if (my_canread(b) && speedy_cb_data_len(b) < sz &&
340 			!(i == 0 && in_is_tty))
341 		    {
342 			do_read = 1;
343 		    }
344 		    do_write = 0;
345 		}
346 
347 		/* Try to close files now, so we can wake up the backend
348 		 * and do more I/O before dropping into select
349 		 */
350 		if (!do_read && !do_write) {
351                     if (i == 1)
352                         /* delay closing STDOUT until all the other fds are closed */
353                         close_stdout_delayed = 1;
354                     else
355 		        try_close(b);
356                 }
357             }
358         }
359         if (close_stdout_delayed)
360             try_close(cb+1);
361 
362 	/* All done with reads/writes after backend exited */
363 	if (backend_exited) {
364 	    if (am_child) {
365 		if (my_copydone(&CB_OUT) && my_copydone(&CB_ERR))
366 		    break;
367 	    }
368 	    else if (!speedy_cb_canwrite(&CB_OUT) &&
369 	             !speedy_cb_canwrite(&CB_ERR))
370 	    {
371 		/* If eof, all done */
372 		if (my_copydone(&CB_OUT) && my_copydone(&CB_ERR))
373 		    break;
374 
375 		/* See if all reads on sockets have stopped */
376 		for (i = 1; i < NUMFDS; ++i) {
377 		    if (!read_stopped[i])
378 			break;
379 		}
380 
381 		/* If all have stopped */
382 		if (i == NUMFDS) {
383 		    /* Continue copying in the background until eof */
384 		    if (fork())
385 			break;
386 		    am_child = 1;
387 		    stop_sock_reads = 0;
388 		}
389 	    }
390 	}
391 	/* See if the backend exited */
392 	else if (got_sig || speedy_poll_isset(&pi, sig_pipes[0], SPEEDY_POLLIN))
393 	{
394 	    char c;
395 
396 	    /* See if backend exited and if so, get status */
397 	    speedy_file_set_state(FS_CORRUPT);
398 	    backend_exited =
399 		speedy_frontend_collect_status(fslotnum, exit_on_sig, exit_val);
400 	    speedy_file_set_state(FS_OPEN);
401 
402 	    /* Exit & EOF? If so, no need to continue */
403 	    if (backend_exited && my_copydone(&CB_OUT) && my_copydone(&CB_ERR))
404 		break;
405 
406 	    /* Ack signal */
407 	    got_sig = 0;
408 	    read(sig_pipes[0], &c, 1);
409 
410 	    if (backend_exited) {
411 		/* Don't need sig any more */
412 		signal(SIGUSR1, SIG_IGN);
413 
414 		/* Continue until no more data from socket */
415 		stop_sock_reads = 1;
416 		for (i = 0; i < NUMFDS; ++i)
417 		    read_stopped[i] = 0;
418 		speedy_poll_reset(&pi);
419 		for (i = 1; i < NUMFDS; ++i) {
420 		    speedy_poll_set(&pi, cb[i].rdfd, SPEEDY_POLLIN);
421 		    speedy_poll_set(&pi, cb[i].wrfd, SPEEDY_POLLOUT);
422 		}
423 		continue;
424 	    }
425 	}
426 
427 	/* Reset events */
428 	speedy_poll_reset(&pi);
429 
430 	/* Do select on signal pipe */
431 	if (!backend_exited)
432 	    speedy_poll_set(&pi, sig_pipes[0], SPEEDY_POLLIN);
433 
434 	/* Set read/write events */
435 	for (i = 0; i < NUMFDS; ++i) {
436 	    CopyBuf *b = cb + i;
437 	    if (my_canread(b))
438 		speedy_poll_set(&pi, b->rdfd, SPEEDY_POLLIN);
439 	    if (speedy_cb_canwrite(b))
440 		speedy_poll_set(&pi, b->wrfd, SPEEDY_POLLOUT);
441 	}
442 
443 	/* Poll... */
444 	i = speedy_poll_wait(&pi, 5000);
445 	if (i < 1) {
446 	    if (i == -1 && errno != EINTR)
447 		speedy_util_exit(1, 0);
448 	    /* Want to check whether backend is still alive */
449 	    if (!backend_exited)
450 		catch_sig(0);
451 	    speedy_poll_reset(&pi);
452 	}
453     }
454 
455     /* SGI's /dev/tty goes crazy unless we turn on blocking I/O. */
456     for (i = 0; i < NUMFDS; ++i)
457 	fd_change(i, FD_BLOCK);
458 
459     if (am_child)
460 	speedy_util_exit(0, 0);
461 
462 #ifdef SPEEDY_PROFILING
463     /* Slightly faster to skip these, since we're exiting anyways. */
464     if (profile_runs) {
465 	for (i = 0; i < NUMFDS; ++i)
466 	    fd_change(socks[i], FD_CLOSE);
467 	speedy_poll_free(&pi);
468 	speedy_cb_free(&CB_IN);
469 	speedy_cb_free(&CB_OUT);
470 	speedy_cb_free(&CB_ERR);
471 	signal(SIGUSR1, SIG_DFL);
472 	signal(SIGPIPE, SIG_DFL);
473 	close(pipes[0]);
474 	close(pipes[1]);
475     }
476 #endif
477 }
478 
main(int argc,char ** argv,char ** _junk)479 int main(int argc, char **argv, char **_junk) {
480     int exit_on_sig, exit_val;
481 
482     speedy_util_unlimit_core();
483 
484 #ifdef SPEEDY_PROFILING
485     char *runs = getenv("SPEEDY_PROFILE_RUNS");
486     profile_runs = runs ? atoi(runs) : 1;
487     while (profile_runs--)
488 #endif
489 	doit((const char * const *)argv, &exit_on_sig, &exit_val);
490 
491     /* If signal, try to kill ourself with the same sig the backend died on */
492     if (exit_on_sig)
493 	killme(exit_val);
494 
495     return exit_val;
496 }
497 
498 /*
499  * Glue Functions
500  */
501 
speedy_abort(const char * s)502 void speedy_abort(const char *s) {
503     write(2, s, strlen(s));
504     speedy_util_exit(1, 0);
505 }
506 
507 #ifdef SPEEDY_EFENCE
508 
509 void *efence_malloc (size_t size);
510 void efence_free (void *ptr);
511 void *efence_realloc (void *ptr, size_t size);
512 
malloc(size_t size)513 void * malloc (size_t size) {
514     return efence_malloc(size);
515 }
516 
free(void * ptr)517 void free (void *ptr) {
518     efence_free(ptr);
519 }
520 
realloc(void * ptr,size_t size)521 void * realloc (void *ptr, size_t size) {
522     return efence_realloc(ptr, size);
523 }
524 
525 #endif
526