Provide a `--pidfile' option in `fw'.
[fwd] / fw.c
1 /* -*-c-*-
2 *
3 * $Id$
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 ::= [[`:']`name'[`:']] 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 const char *pidfile = 0;
581 conffile *cf, **cff = &conffiles;
582
583 #define f_bogus 1u
584 #define f_file 2u
585 #define f_syslog 4u
586 #define f_fork 8u
587
588 /* --- Initialize things --- */
589
590 ego(argv[0]);
591 sel = &sst;
592 sel_init(sel);
593 sub_init();
594 sig_init(sel);
595 bres_init(sel);
596 bres_exec(0);
597 exec_init();
598 fattr_init(&fattr_global);
599 scan_create(&sc);
600
601 atexit(fw_exit);
602
603 /* --- Parse command line options --- */
604
605 for (;;) {
606 static struct option opts[] = {
607
608 /* --- Standard GNU help options --- */
609
610 { "help", 0, 0, 'h' },
611 { "version", 0, 0, 'v' },
612 { "usage", 0, 0, 'u' },
613
614 /* --- Other help options --- */
615
616 { "grammar", 0, 0, 'G' },
617 { "options", 0, 0, 'O' },
618
619 /* --- Other useful arguments --- */
620
621 { "file", OPTF_ARGREQ, 0, 'f' },
622 { "fork", 0, 0, 'd' },
623 { "daemon", 0, 0, 'd' },
624 { "pidfile", OPTF_ARGREQ, 0, 'p' },
625 { "syslog", 0, 0, 'l' },
626 { "log", 0, 0, 'l' },
627 { "quiet", 0, 0, 'q' },
628 { "setuid", OPTF_ARGREQ, 0, 's' },
629 { "uid", OPTF_ARGREQ, 0, 's' },
630 { "setgid", OPTF_ARGREQ, 0, 'g' },
631 { "gid", OPTF_ARGREQ, 0, 'g' },
632
633 /* --- Magic terminator --- */
634
635 { 0, 0, 0, 0 }
636 };
637 int i = mdwopt(argc, argv, "+hvu" "GO" "f:dp:ls:g:", opts, 0, 0, 0);
638
639 if (i < 0)
640 break;
641 switch (i) {
642 case 'h':
643 help(stdout);
644 exit(0);
645 break;
646 case 'v':
647 version(stdout);
648 exit(0);
649 break;
650 case 'u':
651 usage(stdout);
652 exit(0);
653 break;
654 case 'G':
655 grammar(stdout);
656 exit(0);
657 break;
658 case 'O':
659 options(stdout);
660 exit(0);
661 break;
662 case 'f':
663 if (strcmp(optarg, "-") == 0)
664 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
665 else {
666 FILE *fp;
667 if ((fp = fopen(optarg, "r")) == 0)
668 die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
669 cf = CREATE(conffile);
670 cf->name = optarg;
671 *cff = cf;
672 cff = &cf->next;
673 scan_add(&sc, scan_file(fp, optarg, 0));
674 }
675 f |= f_file;
676 break;
677 case 'd':
678 f |= f_fork;
679 break;
680 case 'p':
681 pidfile = optarg;
682 break;
683 case 'l':
684 f |= f_syslog;
685 break;
686 case 'q':
687 flags |= FW_QUIET;
688 break;
689 case 's':
690 if (isdigit((unsigned char )optarg[0])) {
691 char *q;
692 drop = strtol(optarg, &q, 0);
693 if (*q)
694 die(1, "bad uid `%s'", optarg);
695 } else {
696 struct passwd *pw = getpwnam(optarg);
697 if (!pw)
698 die(1, "unknown user `%s'", optarg);
699 drop = pw->pw_uid;
700 }
701 break;
702 case 'g':
703 if (isdigit((unsigned char )optarg[0])) {
704 char *q;
705 dropg = strtol(optarg, &q, 0);
706 if (*q)
707 die(1, "bad gid `%s'", optarg);
708 } else {
709 struct group *gr = getgrnam(optarg);
710 if (!gr)
711 die(1, "unknown group `%s'", optarg);
712 dropg = gr->gr_gid;
713 }
714 break;
715 default:
716 f |= f_bogus;
717 break;
718 }
719 }
720
721 if (f & f_bogus) {
722 usage(stderr);
723 exit(1);
724 }
725 *cff = 0;
726
727 /* --- Deal with the remaining arguments --- */
728
729 if (optind < argc)
730 scan_add(&sc, scan_argv(argv + optind));
731 else if (f & f_file)
732 /* Cool */;
733 else if (!isatty(STDIN_FILENO))
734 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
735 else {
736 moan("no configuration given and stdin is a terminal.");
737 moan("type `%s --help' for usage information.", QUIS);
738 exit(1);
739 }
740
741 /* --- Parse the configuration now gathered --- */
742
743 parse(&sc);
744
745 /* --- Set up some signal handlers --- *
746 *
747 * Don't enable @SIGINT@ if the caller already disabled it.
748 */
749
750 {
751 struct sigaction sa;
752
753 sig_add(&s_term, SIGTERM, fw_tidy, 0);
754 sig_add(&s_quit, SIGQUIT, fw_die, 0);
755 sigaction(SIGINT, 0, &sa);
756 if (sa.sa_handler != SIG_IGN)
757 sig_add(&s_int, SIGINT, fw_tidy, 0);
758 sig_add(&s_hup, SIGHUP, fw_reload, 0);
759 }
760
761 /* --- Drop privileges --- */
762
763 if (drop != (uid_t)-1)
764 privconn_split(sel);
765 #ifdef HAVE_SETGROUPS
766 if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) ||
767 (drop != (uid_t)-1 && setuid(drop)))
768 die(1, "couldn't drop privileges: %s", strerror(errno));
769 #else
770 if ((dropg != (gid_t)-1 && setgid(dropg)) ||
771 (drop != (uid_t)-1 && setuid(drop)))
772 die(1, "couldn't drop privileges: %s", strerror(errno));
773 #endif
774
775 /* --- Fork into the background --- */
776
777 if (f & f_fork) {
778 pid_t kid;
779
780 kid = fork();
781 if (kid == -1)
782 die(1, "couldn't fork: %s", strerror(errno));
783 if (kid != 0)
784 _exit(0);
785
786 close(0); close(1); close(2);
787 chdir("/");
788 setsid();
789
790 kid = fork();
791 if (kid != 0)
792 _exit(0);
793 }
794 if (pidfile) {
795 FILE *fp = fopen(pidfile, "w");
796 if (!fp) {
797 die(EXIT_FAILURE, "couldn't create pidfile `%s': %s",
798 pidfile, strerror(errno));
799 }
800 fprintf(fp, "%lu\n", (unsigned long)getpid());
801 if (fflush(fp) || ferror(fp) || fclose(fp)) {
802 die(EXIT_FAILURE, "couldn't write pidfile `%s': %s",
803 pidfile, strerror(errno));
804 }
805 }
806
807 if (f & f_syslog) {
808 flags |= FW_SYSLOG;
809 openlog(QUIS, 0, LOG_DAEMON);
810 }
811
812 /* --- Let rip --- */
813
814 if (!(flags & FW_SET))
815 moan("nothing to do!");
816 signal(SIGPIPE, SIG_IGN);
817
818 {
819 int selerr = 0;
820 while (active) {
821 if (!sel_select(sel))
822 selerr = 0;
823 else if (errno != EINTR && errno != EAGAIN) {
824 fw_log(-1, "error from select: %s", strerror(errno));
825 selerr++;
826 if (selerr > 8) {
827 fw_log(-1, "too many consecutive select errors: bailing out");
828 exit(EXIT_FAILURE);
829 }
830 }
831 }
832 }
833
834 return (0);
835 }
836
837 /*----- That's all, folks -------------------------------------------------*/