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