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