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