blast: Upper-case metasyntactic variables in the usage message.
[fwd] / fw.c
CommitLineData
e82f7154 1/* -*-c-*-
2 *
b69a615b 3 * $Id$
e82f7154 4 *
5 * Port forwarding thingy
6 *
61e3dbdf 7 * (c) 1999 Straylight/Edgeware
e82f7154 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
e82f7154 29/*----- Header files ------------------------------------------------------*/
30
31#include "config.h"
32
372a98e2 33#include <assert.h>
e82f7154 34#include <ctype.h>
35#include <errno.h>
61e3dbdf 36#include <signal.h>
37#include <stdarg.h>
e82f7154 38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
61e3dbdf 41#include <time.h>
e82f7154 42
43#include <unistd.h>
44#include <syslog.h>
45
fc170a33 46#include <grp.h>
47#include <pwd.h>
48
2d9ec601 49#include <mLib/bres.h>
61e3dbdf 50#include <mLib/dstr.h>
e82f7154 51#include <mLib/mdwopt.h>
52#include <mLib/quis.h>
53#include <mLib/report.h>
54#include <mLib/sel.h>
61e3dbdf 55#include <mLib/sig.h>
e82f7154 56#include <mLib/sub.h>
57
e82f7154 58#include "conf.h"
61e3dbdf 59#include "endpt.h"
60#include "exec.h"
61#include "fattr.h"
8c0a939e 62#include "file.h"
afd7451e 63#include "fw.h"
ee599f55 64#include "privconn.h"
e82f7154 65#include "scan.h"
8c0a939e 66#include "socket.h"
61e3dbdf 67#include "source.h"
e82f7154 68
afd7451e 69/*----- Global variables --------------------------------------------------*/
e82f7154 70
71sel_state *sel; /* Multiplexor for nonblocking I/O */
61e3dbdf 72
73/*----- Static variables --------------------------------------------------*/
74
372a98e2 75typedef struct conffile {
76 struct conffile *next;
77 char *name;
78} conffile;
79
61e3dbdf 80static unsigned flags = 0; /* Global state flags */
81static unsigned active = 0; /* Number of active things */
372a98e2 82static conffile *conffiles = 0; /* List of configuration files */
61e3dbdf 83
84#define FW_SYSLOG 1u
85#define FW_QUIET 2u
86#define FW_SET 4u
e82f7154 87
8c0a939e 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
99static source_ops *sources[] =
100 { &xsource_ops, &fsource_ops, &ssource_ops, 0 };
101static target_ops *targets[] =
102 { &xtarget_ops, &ftarget_ops, &starget_ops, 0 };
103
104void 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);
ee599f55 197 if (t->ops->confirm)
198 t->ops->confirm(t);
8c0a939e 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 -----------------------------------------*/
e82f7154 263
61e3dbdf 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
275void 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);
17be1d6b 288 d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
61e3dbdf 289 va_start(ap, fmt);
2f8a65a1 290 dstr_vputf(&d, fmt, &ap);
61e3dbdf 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
311void fw_inc(void) { flags |= FW_SET; active++; }
312void fw_dec(void) { if (active) active--; }
313
314/* --- @fw_exit@ --- *
315 *
316 * Arguments: ---
317 *
318 * Returns: ---
319 *
320 * Use: Exits when appropriate.
321 */
322
323static 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
339static void fw_tidy(int n, void *p)
340{
372a98e2 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
362static 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
385static 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 }
8c0a939e 405 parse(&sc);
372a98e2 406 fw_log(-1, "... reload completed OK");
61e3dbdf 407}
408
8c0a939e 409/*----- Startup and options parsing ---------------------------------------*/
410
e82f7154 411/* --- Standard GNU help options --- */
412
413static void version(FILE *fp)
414{
2d9ec601 415 pquis(fp, "$ version " VERSION "\n");
e82f7154 416}
417
418static void usage(FILE *fp)
419{
ad4499e3 420 pquis(fp, "Usage: $ [-dql] [-p PIDFILE] [-f FILE] [CONFIG-STMTS...]\n");
e82f7154 421}
422
423static void help(FILE *fp)
424{
425 version(fp);
426 fputc('\n', fp);
427 usage(fp);
2d9ec601 428 pquis(fp, "\n\
61e3dbdf 429An excessively full-featured port-forwarder, which subsumes large chunks\n\
ad4499e3 430of the functionality of inetd, netcat, and normal cat.\n\
e82f7154 431\n\
432Configuration may be supplied in one or more configuration files, or on\n\
433the command line (or both). If no `-f' option is present, and no\n\
434configuration is given on the command line, the standard input stream is\n\
435read.\n\
436\n\
437Configuration is free-form. Comments begin with a `#' character and\n\
afd7451e 438continue to the end of the line. Each command line argument is considered\n\
61e3dbdf 439to be a separate line.\n\
afd7451e 440\n\
2d9ec601 441The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\
442For a summary of the various options, run `$ --options'.\n\
ad4499e3 443\n\
444Options available are:\n\
445\n\
446Help 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\
451Configuration 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\
455Other 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\
2d9ec601 463");
464}
465
466/* --- Other helpful options --- */
467
468static void grammar(FILE *fp)
469{
470 version(fp);
471 pquis(fp, "\n\
472Grammar summary\n\
473\n\
474Basic syntax\n\
1c2054c7 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\
2d9ec601 480\n\
481Option syntax\n\
1c2054c7 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\
2d9ec601 487\n\
488File source and target\n\
1c2054c7 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\
b69a615b 494 NAME-SPEC ::= [[`:']`name'[`:']] FILE-NAME\n\
1c2054c7 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\
2d9ec601 499\n\
500Exec source and target\n\
1c2054c7 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\
2d9ec601 508\n\
509Socket source and target\n\
1c2054c7 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\
2d9ec601 514\n\
1c2054c7 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\
2d9ec601 519\n\
1c2054c7 520 UNIX-SOURCE-ADDR ::= FILE-NAME\n\
521 UNIX-TARGET-ADDR ::= FILE-NAME\n\
2d9ec601 522");
523}
524
525static void options(FILE *fp)
526{
527 version(fp);
528 pquis(fp, "\n\
529Options summary\n\
530\n\
531File attributes (`fattr')\n\
1c2054c7 532 prefix.FATTR.MODE [=] MODE\n\
533 prefix.FATTR.OWNER [=] USER\n\
534 prefix.FATTR.GROUP [=] GROUP\n\
2d9ec601 535\n\
536File options\n\
537 file.create [=] yes|no\n\
538 file.open [=] no|truncate|append\n\
539 file.fattr.*\n\
540\n\
541Exec options\n\
542 exec.logging [=] yes|no\n\
1c2054c7 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\
2d9ec601 548 exec.env.clear\n\
1c2054c7 549 exec.env.unset VAR\n\
550 exec.env.[set] VAR [=] VALUE\n\
2d9ec601 551\n\
552Socket options\n\
1c2054c7 553 socket.conn [=] NUMBER|unlimited|one-shot\n\
554 socket.listen [=] NUMBER\n\
2d9ec601 555 socket.logging [=] yes|no\n\
8c0a939e 556\n\
1c2054c7 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\
ee599f55 561 socket.inet.dest.priv-port [=] yes|no\n\
8c0a939e 562\n\
2d9ec601 563 socket.unix.fattr.*\n\
564");
e82f7154 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
577int main(int argc, char *argv[])
578{
579 unsigned f = 0;
580 sel_state sst;
372a98e2 581 sig s_term, s_quit, s_int, s_hup;
61e3dbdf 582 scanner sc;
fc170a33 583 uid_t drop = -1;
584 gid_t dropg = -1;
4166ea7c 585 const char *pidfile = 0;
372a98e2 586 conffile *cf, **cff = &conffiles;
e82f7154 587
372a98e2 588#define f_bogus 1u
a8b9c5eb 589#define f_file 2u
590#define f_syslog 4u
591#define f_fork 8u
e82f7154 592
593 /* --- Initialize things --- */
594
595 ego(argv[0]);
596 sel = &sst;
597 sel_init(sel);
598 sub_init();
61e3dbdf 599 sig_init(sel);
e82f7154 600 bres_init(sel);
2d9ec601 601 bres_exec(0);
61e3dbdf 602 exec_init();
603 fattr_init(&fattr_global);
604 scan_create(&sc);
605
61e3dbdf 606 atexit(fw_exit);
e82f7154 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
2d9ec601 619 /* --- Other help options --- */
620
1e8a64e8 621 { "grammar", 0, 0, 'G' },
622 { "options", 0, 0, 'O' },
2d9ec601 623
e82f7154 624 /* --- Other useful arguments --- */
625
626 { "file", OPTF_ARGREQ, 0, 'f' },
61e3dbdf 627 { "fork", 0, 0, 'd' },
628 { "daemon", 0, 0, 'd' },
4166ea7c 629 { "pidfile", OPTF_ARGREQ, 0, 'p' },
17be1d6b 630 { "syslog", 0, 0, 'l' },
631 { "log", 0, 0, 'l' },
61e3dbdf 632 { "quiet", 0, 0, 'q' },
fc170a33 633 { "setuid", OPTF_ARGREQ, 0, 's' },
634 { "uid", OPTF_ARGREQ, 0, 's' },
635 { "setgid", OPTF_ARGREQ, 0, 'g' },
636 { "gid", OPTF_ARGREQ, 0, 'g' },
e82f7154 637
638 /* --- Magic terminator --- */
639
640 { 0, 0, 0, 0 }
641 };
4166ea7c 642 int i = mdwopt(argc, argv, "+hvu" "GO" "f:dp:ls:g:", opts, 0, 0, 0);
e82f7154 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;
fc170a33 659 case 'G':
2d9ec601 660 grammar(stdout);
661 exit(0);
662 break;
fc170a33 663 case 'O':
2d9ec601 664 options(stdout);
665 exit(0);
666 break;
61e3dbdf 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));
372a98e2 674 cf = CREATE(conffile);
675 cf->name = optarg;
676 *cff = cf;
677 cff = &cf->next;
61e3dbdf 678 scan_add(&sc, scan_file(fp, optarg, 0));
679 }
e82f7154 680 f |= f_file;
e82f7154 681 break;
61e3dbdf 682 case 'd':
e82f7154 683 f |= f_fork;
684 break;
4166ea7c 685 case 'p':
686 pidfile = optarg;
687 break;
17be1d6b 688 case 'l':
689 f |= f_syslog;
690 break;
61e3dbdf 691 case 'q':
692 flags |= FW_QUIET;
693 break;
fc170a33 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;
e82f7154 720 default:
721 f |= f_bogus;
722 break;
723 }
724 }
725
726 if (f & f_bogus) {
727 usage(stderr);
728 exit(1);
729 }
372a98e2 730 *cff = 0;
e82f7154 731
732 /* --- Deal with the remaining arguments --- */
733
61e3dbdf 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);
e82f7154 744 }
745
61e3dbdf 746 /* --- Parse the configuration now gathered --- */
e82f7154 747
8c0a939e 748 parse(&sc);
e82f7154 749
372a98e2 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
fc170a33 766 /* --- Drop privileges --- */
767
ee599f55 768 if (drop != (uid_t)-1)
769 privconn_split(sel);
fc170a33 770#ifdef HAVE_SETGROUPS
00e3c0f1 771 if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) ||
772 (drop != (uid_t)-1 && setuid(drop)))
f9d40245 773 die(1, "couldn't drop privileges: %s", strerror(errno));
fc170a33 774#else
00e3c0f1 775 if ((dropg != (gid_t)-1 && setgid(dropg)) ||
776 (drop != (uid_t)-1 && setuid(drop)))
fc170a33 777 die(1, "couldn't drop privileges: %s", strerror(errno));
f9d40245 778#endif
fc170a33 779
e82f7154 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);
afd7451e 792 chdir("/");
e82f7154 793 setsid();
794
795 kid = fork();
796 if (kid != 0)
797 _exit(0);
17be1d6b 798 }
4166ea7c 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 }
afd7451e 811
17be1d6b 812 if (f & f_syslog) {
afd7451e 813 flags |= FW_SYSLOG;
814 openlog(QUIS, 0, LOG_DAEMON);
e82f7154 815 }
816
817 /* --- Let rip --- */
818
61e3dbdf 819 if (!(flags & FW_SET))
820 moan("nothing to do!");
821 signal(SIGPIPE, SIG_IGN);
f9d40245 822
823 {
824 int selerr = 0;
825 while (active) {
826 if (!sel_select(sel))
827 selerr = 0;
372a98e2 828 else if (errno != EINTR && errno != EAGAIN) {
f9d40245 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
e82f7154 839 return (0);
840}
841
842/*----- That's all, folks -------------------------------------------------*/