59bc18f7cbcf8eb40f57dfb8acaec46da57daa70
[fwd] / fw.c
1 /* -*-c-*-
2 *
3 * $Id: fw.c,v 1.17 2004/04/08 01:36:25 mdw Exp $
4 *
5 * Port forwarding thingy
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the `fw' port forwarder.
13 *
14 * `fw' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `fw' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with `fw'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "config.h"
32
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42
43 #include <unistd.h>
44 #include <syslog.h>
45
46 #include <grp.h>
47 #include <pwd.h>
48
49 #include <mLib/bres.h>
50 #include <mLib/dstr.h>
51 #include <mLib/mdwopt.h>
52 #include <mLib/quis.h>
53 #include <mLib/report.h>
54 #include <mLib/sel.h>
55 #include <mLib/sig.h>
56 #include <mLib/sub.h>
57
58 #include "conf.h"
59 #include "endpt.h"
60 #include "exec.h"
61 #include "fattr.h"
62 #include "file.h"
63 #include "fw.h"
64 #include "privconn.h"
65 #include "scan.h"
66 #include "socket.h"
67 #include "source.h"
68
69 /*----- Global variables --------------------------------------------------*/
70
71 sel_state *sel; /* Multiplexor for nonblocking I/O */
72
73 /*----- Static variables --------------------------------------------------*/
74
75 typedef struct conffile {
76 struct conffile *next;
77 char *name;
78 } conffile;
79
80 static unsigned flags = 0; /* Global state flags */
81 static unsigned active = 0; /* Number of active things */
82 static conffile *conffiles = 0; /* List of configuration files */
83
84 #define FW_SYSLOG 1u
85 #define FW_QUIET 2u
86 #define FW_SET 4u
87
88 /*----- Configuration parsing ---------------------------------------------*/
89
90 /* --- @parse@ --- *
91 *
92 * Arguments: @scanner *sc@ = pointer to scanner definition
93 *
94 * Returns: ---
95 *
96 * Use: Parses a configuration file from the scanner.
97 */
98
99 static source_ops *sources[] =
100 { &xsource_ops, &fsource_ops, &ssource_ops, 0 };
101 static target_ops *targets[] =
102 { &xtarget_ops, &ftarget_ops, &starget_ops, 0 };
103
104 void parse(scanner *sc)
105 {
106 token(sc);
107
108 for (;;) {
109 if (sc->t == CTOK_EOF)
110 break;
111 if (sc->t != CTOK_WORD)
112 error(sc, "parse error, keyword expected");
113
114 /* --- Handle a forwarding request --- */
115
116 if (strcmp(sc->d.buf, "forward") == 0 ||
117 strcmp(sc->d.buf, "fw") == 0 ||
118 strcmp(sc->d.buf, "from") == 0) {
119 source *s;
120 target *t;
121
122 token(sc);
123
124 /* --- Read a source description --- */
125
126 {
127 source_ops **sops;
128
129 /* --- Try to find a source type which understands --- */
130
131 s = 0;
132 for (sops = sources; *sops; sops++) {
133 if ((s = (*sops)->read(sc)) != 0)
134 goto found_source;
135 }
136 error(sc, "unknown source name `%s'", sc->d.buf);
137
138 /* --- Read any source-specific options --- */
139
140 found_source:
141 if (sc->t == '{') {
142 token(sc);
143 while (sc->t == CTOK_WORD) {
144 if (!s->ops->option || !s->ops->option(s, sc)) {
145 error(sc, "unknown %s source option `%s'",
146 s->ops->name, sc->d.buf);
147 }
148 if (sc->t == ';')
149 token(sc);
150 }
151 if (sc->t != '}')
152 error(sc, "parse error, missing `}'");
153 token(sc);
154 }
155 }
156
157 /* --- Read a destination description --- */
158
159 if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "to") == 0 ||
160 strcmp(sc->d.buf, "->") == 0))
161 token(sc);
162
163 {
164 target_ops **tops;
165
166 /* --- Try to find a target which understands --- */
167
168 t = 0;
169 for (tops = targets; *tops; tops++) {
170 if ((t = (*tops)->read(sc)) != 0)
171 goto found_target;
172 }
173 error(sc, "unknown target name `%s'", sc->d.buf);
174
175 /* --- Read any target-specific options --- */
176
177 found_target:
178 if (sc->t == '{') {
179 token(sc);
180 while (sc->t == CTOK_WORD) {
181 if (!t->ops->option || !t->ops->option(t, sc)) {
182 error(sc, "unknown %s target option `%s'",
183 t->ops->name, sc->d.buf);
184 }
185 if (sc->t == ';')
186 token(sc);
187 }
188 if (sc->t != '}')
189 error(sc, "parse error, `}' expected");
190 token(sc);
191 }
192 }
193
194 /* --- Combine the source and target --- */
195
196 s->ops->attach(s, sc, t);
197 if (t->ops->confirm)
198 t->ops->confirm(t);
199 }
200
201 /* --- Include configuration from a file --- *
202 *
203 * Slightly tricky. Scan the optional semicolon from the including
204 * stream, not the included one.
205 */
206
207 else if (strcmp(sc->d.buf, "include") == 0) {
208 FILE *fp;
209 dstr d = DSTR_INIT;
210
211 token(sc);
212 conf_name(sc, '/', &d);
213 if ((fp = fopen(d.buf, "r")) == 0)
214 error(sc, "can't include `%s': %s", d.buf, strerror(errno));
215 if (sc->t == ';')
216 token(sc);
217 pushback(sc);
218 scan_push(sc, scan_file(fp, d.buf, 0));
219 token(sc);
220 dstr_destroy(&d);
221 continue; /* Don't parse a trailing `;' */
222 }
223
224 /* --- Other configuration is handled elsewhere --- */
225
226 else {
227
228 /* --- First try among the sources --- */
229
230 {
231 source_ops **sops;
232
233 for (sops = sources; *sops; sops++) {
234 if ((*sops)->option && (*sops)->option(0, sc))
235 goto found_option;
236 }
237 }
238
239 /* --- Then try among the targets --- */
240
241 {
242 target_ops **tops;
243
244 for (tops = targets; *tops; tops++) {
245 if ((*tops)->option && (*tops)->option(0, sc))
246 goto found_option;
247 }
248 }
249
250 /* --- Nobody wants the option --- */
251
252 error(sc, "unknown global option or prefix `%s'", sc->d.buf);
253
254 found_option:;
255 }
256
257 if (sc->t == ';')
258 token(sc);
259 }
260 }
261
262 /*----- General utility functions -----------------------------------------*/
263
264 /* --- @fw_log@ --- *
265 *
266 * Arguments: @time_t t@ = when the connection occurred or (@-1@)
267 * @const char *fmt@ = format string to fill in
268 * @...@ = other arguments
269 *
270 * Returns: ---
271 *
272 * Use: Logs a connection.
273 */
274
275 void fw_log(time_t t, const char *fmt, ...)
276 {
277 struct tm *tm;
278 dstr d = DSTR_INIT;
279 va_list ap;
280
281 if (flags & FW_QUIET)
282 return;
283
284 if (t == -1)
285 t = time(0);
286 tm = localtime(&t);
287 DENSURE(&d, 64);
288 d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
289 va_start(ap, fmt);
290 dstr_vputf(&d, fmt, &ap);
291 va_end(ap);
292 if (flags & FW_SYSLOG)
293 syslog(LOG_NOTICE, "%s", d.buf);
294 else {
295 DPUTC(&d, '\n');
296 dstr_write(&d, stderr);
297 }
298 DDESTROY(&d);
299 }
300
301 /* --- @fw_inc@, @fw_dec@ --- *
302 *
303 * Arguments: ---
304 *
305 * Returns: ---
306 *
307 * Use: Increments or decrements the active thing count. `fw' won't
308 * quit while there are active things.
309 */
310
311 void fw_inc(void) { flags |= FW_SET; active++; }
312 void fw_dec(void) { if (active) active--; }
313
314 /* --- @fw_exit@ --- *
315 *
316 * Arguments: ---
317 *
318 * Returns: ---
319 *
320 * Use: Exits when appropriate.
321 */
322
323 static void fw_exit(void)
324 {
325 endpt_killall();
326 source_killall();
327 }
328
329 /* --- @fw_tidy@ --- *
330 *
331 * Arguments: @int n@ = signal number
332 * @void *p@ = an uninteresting argument
333 *
334 * Returns: ---
335 *
336 * Use: Handles various signals and causes a clean tidy-up.
337 */
338
339 static void fw_tidy(int n, void *p)
340 {
341 const char *sn = 0;
342 switch (n) {
343 case SIGTERM: sn = "SIGTERM"; break;
344 case SIGINT: sn = "SIGINT"; break;
345 default: abort();
346 }
347
348 fw_log(-1, "closing down gracefully on %s", sn);
349 source_killall();
350 }
351
352 /* --- @fw_die@ --- *
353 *
354 * Arguments: @int n@ = signal number
355 * @void *p@ = an uninteresting argument
356 *
357 * Returns: ---
358 *
359 * Use: Handles various signals and causes an abrupt shutdown.
360 */
361
362 static void fw_die(int n, void *p)
363 {
364 const char *sn = 0;
365 switch (n) {
366 case SIGQUIT: sn = "SIGQUIT"; break;
367 default: abort();
368 }
369
370 fw_log(-1, "closing down abruptly on %s", sn);
371 source_killall();
372 endpt_killall();
373 }
374
375 /* --- @fw_reload@ --- *
376 *
377 * Arguments: @int n@ = a signal number
378 * @void *p@ = an uninteresting argument
379 *
380 * Returns: ---
381 *
382 * Use: Handles a hangup signal by re-reading configuration files.
383 */
384
385 static void fw_reload(int n, void *p)
386 {
387 FILE *fp;
388 scanner sc;
389 conffile *cf;
390
391 assert(n == SIGHUP);
392 if (!conffiles) {
393 fw_log(-1, "no configuration files to reload: ignoring SIGHUP");
394 return;
395 }
396 fw_log(-1, "reloading configuration files...");
397 source_killall();
398 scan_create(&sc);
399 for (cf = conffiles; cf; cf = cf->next) {
400 if ((fp = fopen(cf->name, "r")) == 0)
401 fw_log(-1, "error loading `%s': %s", cf->name, strerror(errno));
402 else
403 scan_add(&sc, scan_file(fp, cf->name, 0));
404 }
405 parse(&sc);
406 fw_log(-1, "... reload completed OK");
407 }
408
409 /*----- Startup and options parsing ---------------------------------------*/
410
411 /* --- Standard GNU help options --- */
412
413 static void version(FILE *fp)
414 {
415 pquis(fp, "$ version " VERSION "\n");
416 }
417
418 static void usage(FILE *fp)
419 {
420 pquis(fp, "Usage: $ [-dql] [-f file] [config statements...]\n");
421 }
422
423 static void help(FILE *fp)
424 {
425 version(fp);
426 fputc('\n', fp);
427 usage(fp);
428 pquis(fp, "\n\
429 An excessively full-featured port-forwarder, which subsumes large chunks\n\
430 of the functionality of inetd, netcat, and normal cat. Options available\n\
431 are:\n\
432 \n\
433 -h, --help Display this help message.\n\
434 -v, --version Display the program's version number.\n\
435 -u, --usage Display a terse usage summary.\n\
436 \n\
437 -G, --grammar Show a summary of the configuration language.\n\
438 -O, --options Show a summary of the source and target options.\n\
439 \n\
440 -f, --file=FILE Read configuration from a file.\n\
441 -q, --quiet Don't emit any logging information.\n\
442 -d, --daemon Fork into background after initializing.\n\
443 -l, --syslog Send log output to the system logger.\n\
444 -s, --setuid=USER Change uid to USER after initializing sources.\n\
445 -g, --setgid=GRP Change gid to GRP after initializing sources.\n\
446 \n\
447 Configuration may be supplied in one or more configuration files, or on\n\
448 the command line (or both). If no `-f' option is present, and no\n\
449 configuration is given on the command line, the standard input stream is\n\
450 read.\n\
451 \n\
452 Configuration is free-form. Comments begin with a `#' character and\n\
453 continue to the end of the line. Each command line argument is considered\n\
454 to be a separate line.\n\
455 \n\
456 The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\
457 For a summary of the various options, run `$ --options'.\n\
458 ");
459 }
460
461 /* --- Other helpful options --- */
462
463 static void grammar(FILE *fp)
464 {
465 version(fp);
466 pquis(fp, "\n\
467 Grammar summary\n\
468 \n\
469 Basic syntax\n\
470 FILE ::= EMPTY | FILE STMT [`;']\n\
471 STMT ::= OPTION-STMT | FW-STMT\n\
472 FW-STMT ::= `fw' SOURCE OPTIONS [`to'|`->'] TARGET OPTIONS\n\
473 OPTIONS ::= `{' OPTION-SEQ `}'\n\
474 OPTION-SEQ ::= EMPTY | OPTION-STMT [`;'] OPTION-SEQ\n\
475 \n\
476 Option syntax\n\
477 OPTION-STMT ::= Q-OPTION\n\
478 Q-OPTION ::= OPTION\n\
479 | PREFIX `.' Q-OPTION\n\
480 | PREFIX `{' OPTION-SEQ `}'\n\
481 PREFIX ::= WORD\n\
482 \n\
483 File source and target\n\
484 SOURCE ::= FILE\n\
485 TARGET ::= FILE\n\
486 FILE ::= `file' [`.'] FSPEC [`,' FSPEC]\n\
487 FSPEC ::= FD-SPEC | NAME-SPEC | NULL-SPEC\n\
488 FD-SPEC ::= [[`:']`fd'[`:']] NUMBER|`stdin'|`stdout'\n\
489 NAME-SPEC ::= [[`:']`file'[`:']] FILE-NAME\n\
490 FILE-NAME ::= PATH-SEQ | [ PATH-SEQ ]\n\
491 PATH-SEQ ::= PATH-ELT | PATH-SEQ PATH-ELT\n\
492 PATH-ELT ::= `/' | WORD\n\
493 NULL-SPEC ::= [`:']`null'[`:']\n\
494 \n\
495 Exec source and target\n\
496 SOURCE ::= EXEC\n\
497 TARGET ::= EXEC\n\
498 EXEC ::= `exec' [`.'] CMD-SPEC\n\
499 CMD-SPEC ::= SHELL-CMD | [PROG-NAME] `[' ARGV0 ARG-SEQ `]'\n\
500 ARG-SEQ ::= WORD | ARG-SEQ WORD\n\
501 SHELL-CMD ::= WORD\n\
502 ARGV0 ::= WORD\n\
503 \n\
504 Socket source and target\n\
505 SOURCE ::= SOCKET-SOURCE\n\
506 TARGET ::= SOCKET-TARGET\n\
507 SOCKET-SOURCE ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] SOURCE-ADDR\n\
508 SOCKET-TARGET ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] TARGET-ADDR\n\
509 \n\
510 INET-SOURCE-ADDR ::= [`port'] PORT\n\
511 INET-TARGET-ADDR ::= ADDRESS [`:'] PORT\n\
512 ADDRESS ::= ADDR-ELT | ADDRESS ADDR-ELT\n\
513 ADDR-ELT ::= `.' | WORD\n\
514 \n\
515 UNIX-SOURCE-ADDR ::= FILE-NAME\n\
516 UNIX-TARGET-ADDR ::= FILE-NAME\n\
517 ");
518 }
519
520 static void options(FILE *fp)
521 {
522 version(fp);
523 pquis(fp, "\n\
524 Options summary\n\
525 \n\
526 File attributes (`fattr')\n\
527 prefix.FATTR.MODE [=] MODE\n\
528 prefix.FATTR.OWNER [=] USER\n\
529 prefix.FATTR.GROUP [=] GROUP\n\
530 \n\
531 File options\n\
532 file.create [=] yes|no\n\
533 file.open [=] no|truncate|append\n\
534 file.fattr.*\n\
535 \n\
536 Exec options\n\
537 exec.logging [=] yes|no\n\
538 exec.dir [=] FILE-NAME\n\
539 exec.root [=] FILE-NAME\n\
540 exec.user [=] USER\n\
541 exec.group [=] GROUP\n\
542 exec.rlimit.LIMIT[.hard|.soft] [=] VALUE\n\
543 exec.env.clear\n\
544 exec.env.unset VAR\n\
545 exec.env.[set] VAR [=] VALUE\n\
546 \n\
547 Socket options\n\
548 socket.conn [=] NUMBER|unlimited|one-shot\n\
549 socket.listen [=] NUMBER\n\
550 socket.logging [=] yes|no\n\
551 \n\
552 socket.inet.source.[allow|deny] [host] ADDR [/ ADDR]\n\
553 socket.inet.source.[allow|deny] priv-port\n\
554 socket.inet.source.addr [=] any|ADDR\n\
555 socket.inet.dest.addr [=] any|ADDR\n\
556 socket.inet.dest.priv-port [=] yes|no\n\
557 \n\
558 socket.unix.fattr.*\n\
559 ");
560 }
561
562 /* --- @main@ --- *
563 *
564 * Arguments: @int argc@ = number of command line arguments
565 * @char *argv[]@ = vector of argument strings
566 *
567 * Returns: ---
568 *
569 * Use: Simple port-forwarding server.
570 */
571
572 int main(int argc, char *argv[])
573 {
574 unsigned f = 0;
575 sel_state sst;
576 sig s_term, s_quit, s_int, s_hup;
577 scanner sc;
578 uid_t drop = -1;
579 gid_t dropg = -1;
580 conffile *cf, **cff = &conffiles;
581
582 #define f_bogus 1u
583 #define f_file 2u
584 #define f_syslog 4u
585 #define f_fork 8u
586
587 /* --- Initialize things --- */
588
589 ego(argv[0]);
590 sel = &sst;
591 sel_init(sel);
592 sub_init();
593 sig_init(sel);
594 bres_init(sel);
595 bres_exec(0);
596 exec_init();
597 fattr_init(&fattr_global);
598 scan_create(&sc);
599
600 atexit(fw_exit);
601
602 /* --- Parse command line options --- */
603
604 for (;;) {
605 static struct option opts[] = {
606
607 /* --- Standard GNU help options --- */
608
609 { "help", 0, 0, 'h' },
610 { "version", 0, 0, 'v' },
611 { "usage", 0, 0, 'u' },
612
613 /* --- Other help options --- */
614
615 { "grammar", 0, 0, 'G' },
616 { "options", 0, 0, 'O' },
617
618 /* --- Other useful arguments --- */
619
620 { "file", OPTF_ARGREQ, 0, 'f' },
621 { "fork", 0, 0, 'd' },
622 { "daemon", 0, 0, 'd' },
623 { "syslog", 0, 0, 'l' },
624 { "log", 0, 0, 'l' },
625 { "quiet", 0, 0, 'q' },
626 { "setuid", OPTF_ARGREQ, 0, 's' },
627 { "uid", OPTF_ARGREQ, 0, 's' },
628 { "setgid", OPTF_ARGREQ, 0, 'g' },
629 { "gid", OPTF_ARGREQ, 0, 'g' },
630
631 /* --- Magic terminator --- */
632
633 { 0, 0, 0, 0 }
634 };
635 int i = mdwopt(argc, argv, "+hvu GO f:dls:g:", opts, 0, 0, 0);
636
637 if (i < 0)
638 break;
639 switch (i) {
640 case 'h':
641 help(stdout);
642 exit(0);
643 break;
644 case 'v':
645 version(stdout);
646 exit(0);
647 break;
648 case 'u':
649 usage(stdout);
650 exit(0);
651 break;
652 case 'G':
653 grammar(stdout);
654 exit(0);
655 break;
656 case 'O':
657 options(stdout);
658 exit(0);
659 break;
660 case 'f':
661 if (strcmp(optarg, "-") == 0)
662 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
663 else {
664 FILE *fp;
665 if ((fp = fopen(optarg, "r")) == 0)
666 die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
667 cf = CREATE(conffile);
668 cf->name = optarg;
669 *cff = cf;
670 cff = &cf->next;
671 scan_add(&sc, scan_file(fp, optarg, 0));
672 }
673 f |= f_file;
674 break;
675 case 'd':
676 f |= f_fork;
677 break;
678 case 'l':
679 f |= f_syslog;
680 break;
681 case 'q':
682 flags |= FW_QUIET;
683 break;
684 case 's':
685 if (isdigit((unsigned char )optarg[0])) {
686 char *q;
687 drop = strtol(optarg, &q, 0);
688 if (*q)
689 die(1, "bad uid `%s'", optarg);
690 } else {
691 struct passwd *pw = getpwnam(optarg);
692 if (!pw)
693 die(1, "unknown user `%s'", optarg);
694 drop = pw->pw_uid;
695 }
696 break;
697 case 'g':
698 if (isdigit((unsigned char )optarg[0])) {
699 char *q;
700 dropg = strtol(optarg, &q, 0);
701 if (*q)
702 die(1, "bad gid `%s'", optarg);
703 } else {
704 struct group *gr = getgrnam(optarg);
705 if (!gr)
706 die(1, "unknown group `%s'", optarg);
707 dropg = gr->gr_gid;
708 }
709 break;
710 default:
711 f |= f_bogus;
712 break;
713 }
714 }
715
716 if (f & f_bogus) {
717 usage(stderr);
718 exit(1);
719 }
720 *cff = 0;
721
722 /* --- Deal with the remaining arguments --- */
723
724 if (optind < argc)
725 scan_add(&sc, scan_argv(argv + optind));
726 else if (f & f_file)
727 /* Cool */;
728 else if (!isatty(STDIN_FILENO))
729 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
730 else {
731 moan("no configuration given and stdin is a terminal.");
732 moan("type `%s --help' for usage information.", QUIS);
733 exit(1);
734 }
735
736 /* --- Parse the configuration now gathered --- */
737
738 parse(&sc);
739
740 /* --- Set up some signal handlers --- *
741 *
742 * Don't enable @SIGINT@ if the caller already disabled it.
743 */
744
745 {
746 struct sigaction sa;
747
748 sig_add(&s_term, SIGTERM, fw_tidy, 0);
749 sig_add(&s_quit, SIGQUIT, fw_die, 0);
750 sigaction(SIGINT, 0, &sa);
751 if (sa.sa_handler != SIG_IGN)
752 sig_add(&s_int, SIGINT, fw_tidy, 0);
753 sig_add(&s_hup, SIGHUP, fw_reload, 0);
754 }
755
756 /* --- Drop privileges --- */
757
758 if (drop != (uid_t)-1)
759 privconn_split(sel);
760 #ifdef HAVE_SETGROUPS
761 if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) ||
762 (drop != (uid_t)-1 && setuid(drop)))
763 die(1, "couldn't drop privileges: %s", strerror(errno));
764 #else
765 if ((dropg != (gid_t)-1 && setgid(dropg)) ||
766 (drop != (uid_t)-1 && setuid(drop)))
767 die(1, "couldn't drop privileges: %s", strerror(errno));
768 #endif
769
770 /* --- Fork into the background --- */
771
772 if (f & f_fork) {
773 pid_t kid;
774
775 kid = fork();
776 if (kid == -1)
777 die(1, "couldn't fork: %s", strerror(errno));
778 if (kid != 0)
779 _exit(0);
780
781 close(0); close(1); close(2);
782 chdir("/");
783 setsid();
784
785 kid = fork();
786 if (kid != 0)
787 _exit(0);
788 }
789
790 if (f & f_syslog) {
791 flags |= FW_SYSLOG;
792 openlog(QUIS, 0, LOG_DAEMON);
793 }
794
795 /* --- Let rip --- */
796
797 if (!(flags & FW_SET))
798 moan("nothing to do!");
799 signal(SIGPIPE, SIG_IGN);
800
801 {
802 int selerr = 0;
803 while (active) {
804 if (!sel_select(sel))
805 selerr = 0;
806 else if (errno != EINTR && errno != EAGAIN) {
807 fw_log(-1, "error from select: %s", strerror(errno));
808 selerr++;
809 if (selerr > 8) {
810 fw_log(-1, "too many consecutive select errors: bailing out");
811 exit(EXIT_FAILURE);
812 }
813 }
814 }
815 }
816
817 return (0);
818 }
819
820 /*----- That's all, folks -------------------------------------------------*/