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