blast: Upper-case metasyntactic variables in the usage message.
[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] [-p PIDFILE] [-f FILE] [CONFIG-STMTS...]\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.\n\
431 \n\
432 Configuration may be supplied in one or more configuration files, or on\n\
433 the command line (or both). If no `-f' option is present, and no\n\
434 configuration is given on the command line, the standard input stream is\n\
435 read.\n\
436 \n\
437 Configuration is free-form. Comments begin with a `#' character and\n\
438 continue to the end of the line. Each command line argument is considered\n\
439 to be a separate line.\n\
440 \n\
441 The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\
442 For a summary of the various options, run `$ --options'.\n\
443 \n\
444 Options available are:\n\
445 \n\
446 Help options:\n\
447 -h, --help Display this help message.\n\
448 -v, --version Display the program's version number.\n\
449 -u, --usage Display a terse usage summary.\n\
450 \n\
451 Configuration summary:\n\
452 -G, --grammar Show a summary of the configuration language.\n\
453 -O, --options Show a summary of the source and target options.\n\
454 \n\
455 Other options:\n\
456 -f, --file=FILE Read configuration from a file.\n\
457 -q, --quiet Don't emit any logging information.\n\
458 -d, --daemon Fork into background after initializing.\n\
459 -p, --pidfile=FILE Write process-id to the named FILE.\n\
460 -l, --syslog Send log output to the system logger.\n\
461 -s, --setuid=USER Change uid to USER after initializing sources.\n\
462 -g, --setgid=GRP Change gid to GRP after initializing sources.\n\
463 ");
464 }
465
466 /* --- Other helpful options --- */
467
468 static void grammar(FILE *fp)
469 {
470 version(fp);
471 pquis(fp, "\n\
472 Grammar summary\n\
473 \n\
474 Basic syntax\n\
475 FILE ::= EMPTY | FILE STMT [`;']\n\
476 STMT ::= OPTION-STMT | FW-STMT\n\
477 FW-STMT ::= `fw' SOURCE OPTIONS [`to'|`->'] TARGET OPTIONS\n\
478 OPTIONS ::= `{' OPTION-SEQ `}'\n\
479 OPTION-SEQ ::= EMPTY | OPTION-STMT [`;'] OPTION-SEQ\n\
480 \n\
481 Option syntax\n\
482 OPTION-STMT ::= Q-OPTION\n\
483 Q-OPTION ::= OPTION\n\
484 | PREFIX `.' Q-OPTION\n\
485 | PREFIX `{' OPTION-SEQ `}'\n\
486 PREFIX ::= WORD\n\
487 \n\
488 File source and target\n\
489 SOURCE ::= FILE\n\
490 TARGET ::= FILE\n\
491 FILE ::= `file' [`.'] FSPEC [`,' FSPEC]\n\
492 FSPEC ::= FD-SPEC | NAME-SPEC | NULL-SPEC\n\
493 FD-SPEC ::= [[`:']`fd'[`:']] NUMBER|`stdin'|`stdout'\n\
494 NAME-SPEC ::= [[`:']`name'[`:']] FILE-NAME\n\
495 FILE-NAME ::= PATH-SEQ | [ PATH-SEQ ]\n\
496 PATH-SEQ ::= PATH-ELT | PATH-SEQ PATH-ELT\n\
497 PATH-ELT ::= `/' | WORD\n\
498 NULL-SPEC ::= [`:']`null'[`:']\n\
499 \n\
500 Exec source and target\n\
501 SOURCE ::= EXEC\n\
502 TARGET ::= EXEC\n\
503 EXEC ::= `exec' [`.'] CMD-SPEC\n\
504 CMD-SPEC ::= SHELL-CMD | [PROG-NAME] `[' ARGV0 ARG-SEQ `]'\n\
505 ARG-SEQ ::= WORD | ARG-SEQ WORD\n\
506 SHELL-CMD ::= WORD\n\
507 ARGV0 ::= WORD\n\
508 \n\
509 Socket source and target\n\
510 SOURCE ::= SOCKET-SOURCE\n\
511 TARGET ::= SOCKET-TARGET\n\
512 SOCKET-SOURCE ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] SOURCE-ADDR\n\
513 SOCKET-TARGET ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] TARGET-ADDR\n\
514 \n\
515 INET-SOURCE-ADDR ::= [`port'] PORT\n\
516 INET-TARGET-ADDR ::= ADDRESS [`:'] PORT\n\
517 ADDRESS ::= ADDR-ELT | ADDRESS ADDR-ELT\n\
518 ADDR-ELT ::= `.' | WORD\n\
519 \n\
520 UNIX-SOURCE-ADDR ::= FILE-NAME\n\
521 UNIX-TARGET-ADDR ::= FILE-NAME\n\
522 ");
523 }
524
525 static void options(FILE *fp)
526 {
527 version(fp);
528 pquis(fp, "\n\
529 Options summary\n\
530 \n\
531 File attributes (`fattr')\n\
532 prefix.FATTR.MODE [=] MODE\n\
533 prefix.FATTR.OWNER [=] USER\n\
534 prefix.FATTR.GROUP [=] GROUP\n\
535 \n\
536 File options\n\
537 file.create [=] yes|no\n\
538 file.open [=] no|truncate|append\n\
539 file.fattr.*\n\
540 \n\
541 Exec options\n\
542 exec.logging [=] yes|no\n\
543 exec.dir [=] FILE-NAME\n\
544 exec.root [=] FILE-NAME\n\
545 exec.user [=] USER\n\
546 exec.group [=] GROUP\n\
547 exec.rlimit.LIMIT[.hard|.soft] [=] VALUE\n\
548 exec.env.clear\n\
549 exec.env.unset VAR\n\
550 exec.env.[set] VAR [=] VALUE\n\
551 \n\
552 Socket options\n\
553 socket.conn [=] NUMBER|unlimited|one-shot\n\
554 socket.listen [=] NUMBER\n\
555 socket.logging [=] yes|no\n\
556 \n\
557 socket.inet.source.[allow|deny] [host] ADDR [/ ADDR]\n\
558 socket.inet.source.[allow|deny] priv-port\n\
559 socket.inet.source.addr [=] any|ADDR\n\
560 socket.inet.dest.addr [=] any|ADDR\n\
561 socket.inet.dest.priv-port [=] yes|no\n\
562 \n\
563 socket.unix.fattr.*\n\
564 ");
565 }
566
567 /* --- @main@ --- *
568 *
569 * Arguments: @int argc@ = number of command line arguments
570 * @char *argv[]@ = vector of argument strings
571 *
572 * Returns: ---
573 *
574 * Use: Simple port-forwarding server.
575 */
576
577 int main(int argc, char *argv[])
578 {
579 unsigned f = 0;
580 sel_state sst;
581 sig s_term, s_quit, s_int, s_hup;
582 scanner sc;
583 uid_t drop = -1;
584 gid_t dropg = -1;
585 const char *pidfile = 0;
586 conffile *cf, **cff = &conffiles;
587
588 #define f_bogus 1u
589 #define f_file 2u
590 #define f_syslog 4u
591 #define f_fork 8u
592
593 /* --- Initialize things --- */
594
595 ego(argv[0]);
596 sel = &sst;
597 sel_init(sel);
598 sub_init();
599 sig_init(sel);
600 bres_init(sel);
601 bres_exec(0);
602 exec_init();
603 fattr_init(&fattr_global);
604 scan_create(&sc);
605
606 atexit(fw_exit);
607
608 /* --- Parse command line options --- */
609
610 for (;;) {
611 static struct option opts[] = {
612
613 /* --- Standard GNU help options --- */
614
615 { "help", 0, 0, 'h' },
616 { "version", 0, 0, 'v' },
617 { "usage", 0, 0, 'u' },
618
619 /* --- Other help options --- */
620
621 { "grammar", 0, 0, 'G' },
622 { "options", 0, 0, 'O' },
623
624 /* --- Other useful arguments --- */
625
626 { "file", OPTF_ARGREQ, 0, 'f' },
627 { "fork", 0, 0, 'd' },
628 { "daemon", 0, 0, 'd' },
629 { "pidfile", OPTF_ARGREQ, 0, 'p' },
630 { "syslog", 0, 0, 'l' },
631 { "log", 0, 0, 'l' },
632 { "quiet", 0, 0, 'q' },
633 { "setuid", OPTF_ARGREQ, 0, 's' },
634 { "uid", OPTF_ARGREQ, 0, 's' },
635 { "setgid", OPTF_ARGREQ, 0, 'g' },
636 { "gid", OPTF_ARGREQ, 0, 'g' },
637
638 /* --- Magic terminator --- */
639
640 { 0, 0, 0, 0 }
641 };
642 int i = mdwopt(argc, argv, "+hvu" "GO" "f:dp:ls:g:", opts, 0, 0, 0);
643
644 if (i < 0)
645 break;
646 switch (i) {
647 case 'h':
648 help(stdout);
649 exit(0);
650 break;
651 case 'v':
652 version(stdout);
653 exit(0);
654 break;
655 case 'u':
656 usage(stdout);
657 exit(0);
658 break;
659 case 'G':
660 grammar(stdout);
661 exit(0);
662 break;
663 case 'O':
664 options(stdout);
665 exit(0);
666 break;
667 case 'f':
668 if (strcmp(optarg, "-") == 0)
669 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
670 else {
671 FILE *fp;
672 if ((fp = fopen(optarg, "r")) == 0)
673 die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
674 cf = CREATE(conffile);
675 cf->name = optarg;
676 *cff = cf;
677 cff = &cf->next;
678 scan_add(&sc, scan_file(fp, optarg, 0));
679 }
680 f |= f_file;
681 break;
682 case 'd':
683 f |= f_fork;
684 break;
685 case 'p':
686 pidfile = optarg;
687 break;
688 case 'l':
689 f |= f_syslog;
690 break;
691 case 'q':
692 flags |= FW_QUIET;
693 break;
694 case 's':
695 if (isdigit((unsigned char )optarg[0])) {
696 char *q;
697 drop = strtol(optarg, &q, 0);
698 if (*q)
699 die(1, "bad uid `%s'", optarg);
700 } else {
701 struct passwd *pw = getpwnam(optarg);
702 if (!pw)
703 die(1, "unknown user `%s'", optarg);
704 drop = pw->pw_uid;
705 }
706 break;
707 case 'g':
708 if (isdigit((unsigned char )optarg[0])) {
709 char *q;
710 dropg = strtol(optarg, &q, 0);
711 if (*q)
712 die(1, "bad gid `%s'", optarg);
713 } else {
714 struct group *gr = getgrnam(optarg);
715 if (!gr)
716 die(1, "unknown group `%s'", optarg);
717 dropg = gr->gr_gid;
718 }
719 break;
720 default:
721 f |= f_bogus;
722 break;
723 }
724 }
725
726 if (f & f_bogus) {
727 usage(stderr);
728 exit(1);
729 }
730 *cff = 0;
731
732 /* --- Deal with the remaining arguments --- */
733
734 if (optind < argc)
735 scan_add(&sc, scan_argv(argv + optind));
736 else if (f & f_file)
737 /* Cool */;
738 else if (!isatty(STDIN_FILENO))
739 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
740 else {
741 moan("no configuration given and stdin is a terminal.");
742 moan("type `%s --help' for usage information.", QUIS);
743 exit(1);
744 }
745
746 /* --- Parse the configuration now gathered --- */
747
748 parse(&sc);
749
750 /* --- Set up some signal handlers --- *
751 *
752 * Don't enable @SIGINT@ if the caller already disabled it.
753 */
754
755 {
756 struct sigaction sa;
757
758 sig_add(&s_term, SIGTERM, fw_tidy, 0);
759 sig_add(&s_quit, SIGQUIT, fw_die, 0);
760 sigaction(SIGINT, 0, &sa);
761 if (sa.sa_handler != SIG_IGN)
762 sig_add(&s_int, SIGINT, fw_tidy, 0);
763 sig_add(&s_hup, SIGHUP, fw_reload, 0);
764 }
765
766 /* --- Drop privileges --- */
767
768 if (drop != (uid_t)-1)
769 privconn_split(sel);
770 #ifdef HAVE_SETGROUPS
771 if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) ||
772 (drop != (uid_t)-1 && setuid(drop)))
773 die(1, "couldn't drop privileges: %s", strerror(errno));
774 #else
775 if ((dropg != (gid_t)-1 && setgid(dropg)) ||
776 (drop != (uid_t)-1 && setuid(drop)))
777 die(1, "couldn't drop privileges: %s", strerror(errno));
778 #endif
779
780 /* --- Fork into the background --- */
781
782 if (f & f_fork) {
783 pid_t kid;
784
785 kid = fork();
786 if (kid == -1)
787 die(1, "couldn't fork: %s", strerror(errno));
788 if (kid != 0)
789 _exit(0);
790
791 close(0); close(1); close(2);
792 chdir("/");
793 setsid();
794
795 kid = fork();
796 if (kid != 0)
797 _exit(0);
798 }
799 if (pidfile) {
800 FILE *fp = fopen(pidfile, "w");
801 if (!fp) {
802 die(EXIT_FAILURE, "couldn't create pidfile `%s': %s",
803 pidfile, strerror(errno));
804 }
805 fprintf(fp, "%lu\n", (unsigned long)getpid());
806 if (fflush(fp) || ferror(fp) || fclose(fp)) {
807 die(EXIT_FAILURE, "couldn't write pidfile `%s': %s",
808 pidfile, strerror(errno));
809 }
810 }
811
812 if (f & f_syslog) {
813 flags |= FW_SYSLOG;
814 openlog(QUIS, 0, LOG_DAEMON);
815 }
816
817 /* --- Let rip --- */
818
819 if (!(flags & FW_SET))
820 moan("nothing to do!");
821 signal(SIGPIPE, SIG_IGN);
822
823 {
824 int selerr = 0;
825 while (active) {
826 if (!sel_select(sel))
827 selerr = 0;
828 else if (errno != EINTR && errno != EAGAIN) {
829 fw_log(-1, "error from select: %s", strerror(errno));
830 selerr++;
831 if (selerr > 8) {
832 fw_log(-1, "too many consecutive select errors: bailing out");
833 exit(EXIT_FAILURE);
834 }
835 }
836 }
837 }
838
839 return (0);
840 }
841
842 /*----- That's all, folks -------------------------------------------------*/