Commit | Line | Data |
---|---|---|
e82f7154 | 1 | /* -*-c-*- |
e82f7154 | 2 | * |
3 | * Port forwarding thingy | |
4 | * | |
61e3dbdf | 5 | * (c) 1999 Straylight/Edgeware |
e82f7154 | 6 | */ |
7 | ||
206212ca | 8 | /*----- Licensing notice --------------------------------------------------* |
e82f7154 | 9 | * |
9155ea97 | 10 | * This file is part of the `fwd' port forwarder. |
e82f7154 | 11 | * |
9155ea97 | 12 | * `fwd' is free software; you can redistribute it and/or modify |
e82f7154 | 13 | * it under the terms of the GNU General Public License as published by |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
206212ca | 16 | * |
9155ea97 | 17 | * `fwd' is distributed in the hope that it will be useful, |
e82f7154 | 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
206212ca | 21 | * |
e82f7154 | 22 | * You should have received a copy of the GNU General Public License |
9155ea97 | 23 | * along with `fwd'; if not, write to the Free Software Foundation, |
e82f7154 | 24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
25 | */ | |
26 | ||
9155ea97 | 27 | #include "fwd.h" |
e82f7154 | 28 | |
afd7451e | 29 | /*----- Global variables --------------------------------------------------*/ |
e82f7154 | 30 | |
31 | sel_state *sel; /* Multiplexor for nonblocking I/O */ | |
45f51d2f | 32 | unsigned flags = 0; /* Global state flags */ |
61e3dbdf | 33 | |
34 | /*----- Static variables --------------------------------------------------*/ | |
35 | ||
372a98e2 | 36 | typedef struct conffile { |
37 | struct conffile *next; | |
38 | char *name; | |
39 | } conffile; | |
40 | ||
61e3dbdf | 41 | static unsigned active = 0; /* Number of active things */ |
372a98e2 | 42 | static conffile *conffiles = 0; /* List of configuration files */ |
61e3dbdf | 43 | |
8c0a939e | 44 | /*----- Configuration parsing ---------------------------------------------*/ |
45 | ||
46 | /* --- @parse@ --- * | |
47 | * | |
48 | * Arguments: @scanner *sc@ = pointer to scanner definition | |
49 | * | |
50 | * Returns: --- | |
51 | * | |
52 | * Use: Parses a configuration file from the scanner. | |
53 | */ | |
54 | ||
55 | static source_ops *sources[] = | |
56 | { &xsource_ops, &fsource_ops, &ssource_ops, 0 }; | |
57 | static target_ops *targets[] = | |
58 | { &xtarget_ops, &ftarget_ops, &starget_ops, 0 }; | |
59 | ||
60 | void parse(scanner *sc) | |
61 | { | |
62 | token(sc); | |
63 | ||
64 | for (;;) { | |
65 | if (sc->t == CTOK_EOF) | |
66 | break; | |
67 | if (sc->t != CTOK_WORD) | |
68 | error(sc, "parse error, keyword expected"); | |
69 | ||
70 | /* --- Handle a forwarding request --- */ | |
71 | ||
72 | if (strcmp(sc->d.buf, "forward") == 0 || | |
9155ea97 | 73 | strcmp(sc->d.buf, "fwd") == 0 || |
206212ca | 74 | strcmp(sc->d.buf, "from") == 0) { |
8c0a939e | 75 | source *s; |
76 | target *t; | |
77 | ||
78 | token(sc); | |
79 | ||
80 | /* --- Read a source description --- */ | |
81 | ||
82 | { | |
83 | source_ops **sops; | |
84 | ||
85 | /* --- Try to find a source type which understands --- */ | |
86 | ||
87 | s = 0; | |
88 | for (sops = sources; *sops; sops++) { | |
89 | if ((s = (*sops)->read(sc)) != 0) | |
90 | goto found_source; | |
91 | } | |
92 | error(sc, "unknown source name `%s'", sc->d.buf); | |
93 | ||
94 | /* --- Read any source-specific options --- */ | |
95 | ||
96 | found_source: | |
97 | if (sc->t == '{') { | |
98 | token(sc); | |
99 | while (sc->t == CTOK_WORD) { | |
100 | if (!s->ops->option || !s->ops->option(s, sc)) { | |
101 | error(sc, "unknown %s source option `%s'", | |
102 | s->ops->name, sc->d.buf); | |
103 | } | |
104 | if (sc->t == ';') | |
105 | token(sc); | |
106 | } | |
107 | if (sc->t != '}') | |
108 | error(sc, "parse error, missing `}'"); | |
109 | token(sc); | |
110 | } | |
111 | } | |
112 | ||
113 | /* --- Read a destination description --- */ | |
114 | ||
115 | if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "to") == 0 || | |
116 | strcmp(sc->d.buf, "->") == 0)) | |
117 | token(sc); | |
118 | ||
119 | { | |
120 | target_ops **tops; | |
121 | ||
122 | /* --- Try to find a target which understands --- */ | |
123 | ||
124 | t = 0; | |
125 | for (tops = targets; *tops; tops++) { | |
126 | if ((t = (*tops)->read(sc)) != 0) | |
127 | goto found_target; | |
128 | } | |
129 | error(sc, "unknown target name `%s'", sc->d.buf); | |
130 | ||
131 | /* --- Read any target-specific options --- */ | |
132 | ||
133 | found_target: | |
134 | if (sc->t == '{') { | |
135 | token(sc); | |
136 | while (sc->t == CTOK_WORD) { | |
137 | if (!t->ops->option || !t->ops->option(t, sc)) { | |
138 | error(sc, "unknown %s target option `%s'", | |
139 | t->ops->name, sc->d.buf); | |
140 | } | |
141 | if (sc->t == ';') | |
142 | token(sc); | |
143 | } | |
144 | if (sc->t != '}') | |
145 | error(sc, "parse error, `}' expected"); | |
146 | token(sc); | |
147 | } | |
148 | } | |
149 | ||
150 | /* --- Combine the source and target --- */ | |
151 | ||
152 | s->ops->attach(s, sc, t); | |
ee599f55 | 153 | if (t->ops->confirm) |
154 | t->ops->confirm(t); | |
57ceb980 | 155 | source_dec(s); target_dec(t); |
8c0a939e | 156 | } |
157 | ||
158 | /* --- Include configuration from a file --- * | |
159 | * | |
160 | * Slightly tricky. Scan the optional semicolon from the including | |
161 | * stream, not the included one. | |
162 | */ | |
163 | ||
164 | else if (strcmp(sc->d.buf, "include") == 0) { | |
165 | FILE *fp; | |
166 | dstr d = DSTR_INIT; | |
167 | ||
168 | token(sc); | |
169 | conf_name(sc, '/', &d); | |
170 | if ((fp = fopen(d.buf, "r")) == 0) | |
171 | error(sc, "can't include `%s': %s", d.buf, strerror(errno)); | |
172 | if (sc->t == ';') | |
173 | token(sc); | |
174 | pushback(sc); | |
175 | scan_push(sc, scan_file(fp, d.buf, 0)); | |
176 | token(sc); | |
177 | dstr_destroy(&d); | |
178 | continue; /* Don't parse a trailing `;' */ | |
179 | } | |
180 | ||
181 | /* --- Other configuration is handled elsewhere --- */ | |
182 | ||
183 | else { | |
184 | ||
185 | /* --- First try among the sources --- */ | |
186 | ||
187 | { | |
188 | source_ops **sops; | |
189 | ||
190 | for (sops = sources; *sops; sops++) { | |
191 | if ((*sops)->option && (*sops)->option(0, sc)) | |
192 | goto found_option; | |
193 | } | |
194 | } | |
195 | ||
196 | /* --- Then try among the targets --- */ | |
197 | ||
198 | { | |
199 | target_ops **tops; | |
200 | ||
201 | for (tops = targets; *tops; tops++) { | |
202 | if ((*tops)->option && (*tops)->option(0, sc)) | |
203 | goto found_option; | |
204 | } | |
205 | } | |
206 | ||
207 | /* --- Nobody wants the option --- */ | |
208 | ||
209 | error(sc, "unknown global option or prefix `%s'", sc->d.buf); | |
210 | ||
211 | found_option:; | |
212 | } | |
213 | ||
214 | if (sc->t == ';') | |
215 | token(sc); | |
216 | } | |
217 | } | |
218 | ||
219 | /*----- General utility functions -----------------------------------------*/ | |
e82f7154 | 220 | |
61e3dbdf | 221 | /* --- @fw_log@ --- * |
222 | * | |
223 | * Arguments: @time_t t@ = when the connection occurred or (@-1@) | |
224 | * @const char *fmt@ = format string to fill in | |
225 | * @...@ = other arguments | |
226 | * | |
227 | * Returns: --- | |
228 | * | |
229 | * Use: Logs a connection. | |
230 | */ | |
231 | ||
232 | void fw_log(time_t t, const char *fmt, ...) | |
233 | { | |
234 | struct tm *tm; | |
235 | dstr d = DSTR_INIT; | |
236 | va_list ap; | |
237 | ||
238 | if (flags & FW_QUIET) | |
239 | return; | |
240 | ||
d935f68b | 241 | if (t == NOW) |
61e3dbdf | 242 | t = time(0); |
243 | tm = localtime(&t); | |
244 | DENSURE(&d, 64); | |
367d81af | 245 | d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S %z ", tm); |
61e3dbdf | 246 | va_start(ap, fmt); |
2f8a65a1 | 247 | dstr_vputf(&d, fmt, &ap); |
61e3dbdf | 248 | va_end(ap); |
249 | if (flags & FW_SYSLOG) | |
250 | syslog(LOG_NOTICE, "%s", d.buf); | |
251 | else { | |
252 | DPUTC(&d, '\n'); | |
253 | dstr_write(&d, stderr); | |
254 | } | |
255 | DDESTROY(&d); | |
256 | } | |
257 | ||
258 | /* --- @fw_inc@, @fw_dec@ --- * | |
259 | * | |
260 | * Arguments: --- | |
261 | * | |
262 | * Returns: --- | |
263 | * | |
9155ea97 | 264 | * Use: Increments or decrements the active thing count. `fwd' won't |
61e3dbdf | 265 | * quit while there are active things. |
266 | */ | |
267 | ||
268 | void fw_inc(void) { flags |= FW_SET; active++; } | |
269 | void fw_dec(void) { if (active) active--; } | |
270 | ||
271 | /* --- @fw_exit@ --- * | |
272 | * | |
273 | * Arguments: --- | |
274 | * | |
275 | * Returns: --- | |
276 | * | |
277 | * Use: Exits when appropriate. | |
278 | */ | |
279 | ||
280 | static void fw_exit(void) | |
281 | { | |
282 | endpt_killall(); | |
283 | source_killall(); | |
284 | } | |
285 | ||
286 | /* --- @fw_tidy@ --- * | |
287 | * | |
288 | * Arguments: @int n@ = signal number | |
289 | * @void *p@ = an uninteresting argument | |
290 | * | |
291 | * Returns: --- | |
292 | * | |
293 | * Use: Handles various signals and causes a clean tidy-up. | |
294 | */ | |
295 | ||
296 | static void fw_tidy(int n, void *p) | |
297 | { | |
372a98e2 | 298 | const char *sn = 0; |
299 | switch (n) { | |
300 | case SIGTERM: sn = "SIGTERM"; break; | |
301 | case SIGINT: sn = "SIGINT"; break; | |
302 | default: abort(); | |
303 | } | |
304 | ||
d935f68b | 305 | fw_log(NOW, "closing down gracefully on %s", sn); |
372a98e2 | 306 | source_killall(); |
307 | } | |
308 | ||
309 | /* --- @fw_die@ --- * | |
310 | * | |
311 | * Arguments: @int n@ = signal number | |
312 | * @void *p@ = an uninteresting argument | |
313 | * | |
314 | * Returns: --- | |
315 | * | |
316 | * Use: Handles various signals and causes an abrupt shutdown. | |
317 | */ | |
318 | ||
319 | static void fw_die(int n, void *p) | |
320 | { | |
321 | const char *sn = 0; | |
322 | switch (n) { | |
323 | case SIGQUIT: sn = "SIGQUIT"; break; | |
324 | default: abort(); | |
325 | } | |
326 | ||
d935f68b | 327 | fw_log(NOW, "closing down abruptly on %s", sn); |
372a98e2 | 328 | source_killall(); |
329 | endpt_killall(); | |
330 | } | |
331 | ||
332 | /* --- @fw_reload@ --- * | |
333 | * | |
334 | * Arguments: @int n@ = a signal number | |
335 | * @void *p@ = an uninteresting argument | |
336 | * | |
337 | * Returns: --- | |
338 | * | |
339 | * Use: Handles a hangup signal by re-reading configuration files. | |
340 | */ | |
341 | ||
342 | static void fw_reload(int n, void *p) | |
343 | { | |
344 | FILE *fp; | |
345 | scanner sc; | |
346 | conffile *cf; | |
347 | ||
348 | assert(n == SIGHUP); | |
349 | if (!conffiles) { | |
d935f68b | 350 | fw_log(NOW, "no configuration files to reload: ignoring SIGHUP"); |
372a98e2 | 351 | return; |
352 | } | |
d935f68b | 353 | fw_log(NOW, "reloading configuration files..."); |
372a98e2 | 354 | source_killall(); |
355 | scan_create(&sc); | |
356 | for (cf = conffiles; cf; cf = cf->next) { | |
357 | if ((fp = fopen(cf->name, "r")) == 0) | |
d935f68b | 358 | fw_log(NOW, "error loading `%s': %s", cf->name, strerror(errno)); |
372a98e2 | 359 | else |
360 | scan_add(&sc, scan_file(fp, cf->name, 0)); | |
361 | } | |
8c0a939e | 362 | parse(&sc); |
d935f68b | 363 | fw_log(NOW, "... reload completed OK"); |
61e3dbdf | 364 | } |
365 | ||
8c0a939e | 366 | /*----- Startup and options parsing ---------------------------------------*/ |
367 | ||
e82f7154 | 368 | /* --- Standard GNU help options --- */ |
369 | ||
370 | static void version(FILE *fp) | |
371 | { | |
2d9ec601 | 372 | pquis(fp, "$ version " VERSION "\n"); |
e82f7154 | 373 | } |
374 | ||
375 | static void usage(FILE *fp) | |
376 | { | |
ad4499e3 | 377 | pquis(fp, "Usage: $ [-dql] [-p PIDFILE] [-f FILE] [CONFIG-STMTS...]\n"); |
e82f7154 | 378 | } |
379 | ||
380 | static void help(FILE *fp) | |
381 | { | |
382 | version(fp); | |
383 | fputc('\n', fp); | |
384 | usage(fp); | |
2d9ec601 | 385 | pquis(fp, "\n\ |
61e3dbdf | 386 | An excessively full-featured port-forwarder, which subsumes large chunks\n\ |
ad4499e3 | 387 | of the functionality of inetd, netcat, and normal cat.\n\ |
e82f7154 | 388 | \n\ |
389 | Configuration may be supplied in one or more configuration files, or on\n\ | |
390 | the command line (or both). If no `-f' option is present, and no\n\ | |
391 | configuration is given on the command line, the standard input stream is\n\ | |
392 | read.\n\ | |
393 | \n\ | |
394 | Configuration is free-form. Comments begin with a `#' character and\n\ | |
afd7451e | 395 | continue to the end of the line. Each command line argument is considered\n\ |
61e3dbdf | 396 | to be a separate line.\n\ |
afd7451e | 397 | \n\ |
2d9ec601 | 398 | The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\ |
399 | For a summary of the various options, run `$ --options'.\n\ | |
ad4499e3 | 400 | \n\ |
401 | Options available are:\n\ | |
402 | \n\ | |
403 | Help options:\n\ | |
404 | -h, --help Display this help message.\n\ | |
405 | -v, --version Display the program's version number.\n\ | |
406 | -u, --usage Display a terse usage summary.\n\ | |
407 | \n\ | |
408 | Configuration summary:\n\ | |
409 | -G, --grammar Show a summary of the configuration language.\n\ | |
410 | -O, --options Show a summary of the source and target options.\n\ | |
411 | \n\ | |
412 | Other options:\n\ | |
413 | -f, --file=FILE Read configuration from a file.\n\ | |
414 | -q, --quiet Don't emit any logging information.\n\ | |
415 | -d, --daemon Fork into background after initializing.\n\ | |
416 | -p, --pidfile=FILE Write process-id to the named FILE.\n\ | |
417 | -l, --syslog Send log output to the system logger.\n\ | |
418 | -s, --setuid=USER Change uid to USER after initializing sources.\n\ | |
419 | -g, --setgid=GRP Change gid to GRP after initializing sources.\n\ | |
2d9ec601 | 420 | "); |
421 | } | |
422 | ||
423 | /* --- Other helpful options --- */ | |
424 | ||
425 | static void grammar(FILE *fp) | |
426 | { | |
427 | version(fp); | |
8cf7c7c2 MW |
428 | fputs("\nGrammar summary\n\n", fp); |
429 | fputs(grammar_text, fp); | |
2d9ec601 | 430 | } |
431 | ||
432 | static void options(FILE *fp) | |
433 | { | |
434 | version(fp); | |
8cf7c7c2 MW |
435 | fputs("\nOption summary\n\n", fp); |
436 | fputs(option_text, fp); | |
e82f7154 | 437 | } |
438 | ||
439 | /* --- @main@ --- * | |
440 | * | |
441 | * Arguments: @int argc@ = number of command line arguments | |
442 | * @char *argv[]@ = vector of argument strings | |
443 | * | |
444 | * Returns: --- | |
445 | * | |
446 | * Use: Simple port-forwarding server. | |
447 | */ | |
448 | ||
449 | int main(int argc, char *argv[]) | |
450 | { | |
451 | unsigned f = 0; | |
452 | sel_state sst; | |
372a98e2 | 453 | sig s_term, s_quit, s_int, s_hup; |
61e3dbdf | 454 | scanner sc; |
fc170a33 | 455 | uid_t drop = -1; |
456 | gid_t dropg = -1; | |
4166ea7c | 457 | const char *pidfile = 0; |
372a98e2 | 458 | conffile *cf, **cff = &conffiles; |
e82f7154 | 459 | |
372a98e2 | 460 | #define f_bogus 1u |
a8b9c5eb | 461 | #define f_file 2u |
462 | #define f_syslog 4u | |
463 | #define f_fork 8u | |
e82f7154 | 464 | |
465 | /* --- Initialize things --- */ | |
466 | ||
467 | ego(argv[0]); | |
468 | sel = &sst; | |
469 | sel_init(sel); | |
470 | sub_init(); | |
61e3dbdf | 471 | sig_init(sel); |
e82f7154 | 472 | bres_init(sel); |
2d9ec601 | 473 | bres_exec(0); |
61e3dbdf | 474 | exec_init(); |
475 | fattr_init(&fattr_global); | |
476 | scan_create(&sc); | |
477 | ||
61e3dbdf | 478 | atexit(fw_exit); |
e82f7154 | 479 | |
480 | /* --- Parse command line options --- */ | |
481 | ||
482 | for (;;) { | |
483 | static struct option opts[] = { | |
484 | ||
485 | /* --- Standard GNU help options --- */ | |
486 | ||
096c89c3 | 487 | { "help", 0, 0, 'h' }, |
e82f7154 | 488 | { "version", 0, 0, 'v' }, |
489 | { "usage", 0, 0, 'u' }, | |
490 | ||
2d9ec601 | 491 | /* --- Other help options --- */ |
492 | ||
1e8a64e8 | 493 | { "grammar", 0, 0, 'G' }, |
494 | { "options", 0, 0, 'O' }, | |
2d9ec601 | 495 | |
e82f7154 | 496 | /* --- Other useful arguments --- */ |
497 | ||
498 | { "file", OPTF_ARGREQ, 0, 'f' }, | |
61e3dbdf | 499 | { "fork", 0, 0, 'd' }, |
500 | { "daemon", 0, 0, 'd' }, | |
4166ea7c | 501 | { "pidfile", OPTF_ARGREQ, 0, 'p' }, |
17be1d6b | 502 | { "syslog", 0, 0, 'l' }, |
503 | { "log", 0, 0, 'l' }, | |
61e3dbdf | 504 | { "quiet", 0, 0, 'q' }, |
fc170a33 | 505 | { "setuid", OPTF_ARGREQ, 0, 's' }, |
506 | { "uid", OPTF_ARGREQ, 0, 's' }, | |
507 | { "setgid", OPTF_ARGREQ, 0, 'g' }, | |
508 | { "gid", OPTF_ARGREQ, 0, 'g' }, | |
e82f7154 | 509 | |
510 | /* --- Magic terminator --- */ | |
511 | ||
512 | { 0, 0, 0, 0 } | |
513 | }; | |
c1e028be | 514 | int i = mdwopt(argc, argv, "+hvu" "GO" "f:dp:lqs:g:", opts, 0, 0, 0); |
e82f7154 | 515 | |
516 | if (i < 0) | |
517 | break; | |
518 | switch (i) { | |
519 | case 'h': | |
520 | help(stdout); | |
521 | exit(0); | |
522 | break; | |
523 | case 'v': | |
524 | version(stdout); | |
525 | exit(0); | |
526 | break; | |
527 | case 'u': | |
528 | usage(stdout); | |
529 | exit(0); | |
530 | break; | |
fc170a33 | 531 | case 'G': |
2d9ec601 | 532 | grammar(stdout); |
533 | exit(0); | |
534 | break; | |
fc170a33 | 535 | case 'O': |
2d9ec601 | 536 | options(stdout); |
537 | exit(0); | |
538 | break; | |
61e3dbdf | 539 | case 'f': |
540 | if (strcmp(optarg, "-") == 0) | |
541 | scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE)); | |
542 | else { | |
543 | FILE *fp; | |
544 | if ((fp = fopen(optarg, "r")) == 0) | |
545 | die(1, "couldn't open file `%s': %s", optarg, strerror(errno)); | |
372a98e2 | 546 | cf = CREATE(conffile); |
547 | cf->name = optarg; | |
548 | *cff = cf; | |
549 | cff = &cf->next; | |
61e3dbdf | 550 | scan_add(&sc, scan_file(fp, optarg, 0)); |
551 | } | |
e82f7154 | 552 | f |= f_file; |
e82f7154 | 553 | break; |
61e3dbdf | 554 | case 'd': |
e82f7154 | 555 | f |= f_fork; |
556 | break; | |
4166ea7c | 557 | case 'p': |
558 | pidfile = optarg; | |
559 | break; | |
17be1d6b | 560 | case 'l': |
561 | f |= f_syslog; | |
562 | break; | |
61e3dbdf | 563 | case 'q': |
564 | flags |= FW_QUIET; | |
565 | break; | |
fc170a33 | 566 | case 's': |
206212ca | 567 | if (isdigit((unsigned char )optarg[0])) { |
fc170a33 | 568 | char *q; |
569 | drop = strtol(optarg, &q, 0); | |
570 | if (*q) | |
571 | die(1, "bad uid `%s'", optarg); | |
572 | } else { | |
573 | struct passwd *pw = getpwnam(optarg); | |
574 | if (!pw) | |
575 | die(1, "unknown user `%s'", optarg); | |
576 | drop = pw->pw_uid; | |
577 | } | |
578 | break; | |
579 | case 'g': | |
206212ca | 580 | if (isdigit((unsigned char )optarg[0])) { |
fc170a33 | 581 | char *q; |
582 | dropg = strtol(optarg, &q, 0); | |
583 | if (*q) | |
584 | die(1, "bad gid `%s'", optarg); | |
585 | } else { | |
586 | struct group *gr = getgrnam(optarg); | |
587 | if (!gr) | |
588 | die(1, "unknown group `%s'", optarg); | |
589 | dropg = gr->gr_gid; | |
590 | } | |
591 | break; | |
e82f7154 | 592 | default: |
593 | f |= f_bogus; | |
594 | break; | |
595 | } | |
596 | } | |
597 | ||
598 | if (f & f_bogus) { | |
599 | usage(stderr); | |
600 | exit(1); | |
601 | } | |
372a98e2 | 602 | *cff = 0; |
e82f7154 | 603 | |
604 | /* --- Deal with the remaining arguments --- */ | |
605 | ||
61e3dbdf | 606 | if (optind < argc) |
607 | scan_add(&sc, scan_argv(argv + optind)); | |
608 | else if (f & f_file) | |
609 | /* Cool */; | |
610 | else if (!isatty(STDIN_FILENO)) | |
611 | scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE)); | |
612 | else { | |
613 | moan("no configuration given and stdin is a terminal."); | |
614 | moan("type `%s --help' for usage information.", QUIS); | |
615 | exit(1); | |
e82f7154 | 616 | } |
617 | ||
61e3dbdf | 618 | /* --- Parse the configuration now gathered --- */ |
e82f7154 | 619 | |
8c0a939e | 620 | parse(&sc); |
e82f7154 | 621 | |
372a98e2 | 622 | /* --- Set up some signal handlers --- * |
623 | * | |
624 | * Don't enable @SIGINT@ if the caller already disabled it. | |
625 | */ | |
626 | ||
627 | { | |
628 | struct sigaction sa; | |
629 | ||
630 | sig_add(&s_term, SIGTERM, fw_tidy, 0); | |
631 | sig_add(&s_quit, SIGQUIT, fw_die, 0); | |
632 | sigaction(SIGINT, 0, &sa); | |
633 | if (sa.sa_handler != SIG_IGN) | |
634 | sig_add(&s_int, SIGINT, fw_tidy, 0); | |
635 | sig_add(&s_hup, SIGHUP, fw_reload, 0); | |
636 | } | |
637 | ||
e82f7154 | 638 | /* --- Fork into the background --- */ |
639 | ||
640 | if (f & f_fork) { | |
641 | pid_t kid; | |
642 | ||
643 | kid = fork(); | |
644 | if (kid == -1) | |
645 | die(1, "couldn't fork: %s", strerror(errno)); | |
646 | if (kid != 0) | |
647 | _exit(0); | |
648 | ||
649 | close(0); close(1); close(2); | |
f8f2bc04 MW |
650 | if (chdir("/")) |
651 | die(1, "couldn't change to root directory: %s", strerror(errno)); | |
e82f7154 | 652 | setsid(); |
653 | ||
654 | kid = fork(); | |
655 | if (kid != 0) | |
656 | _exit(0); | |
17be1d6b | 657 | } |
4166ea7c | 658 | if (pidfile) { |
659 | FILE *fp = fopen(pidfile, "w"); | |
660 | if (!fp) { | |
661 | die(EXIT_FAILURE, "couldn't create pidfile `%s': %s", | |
662 | pidfile, strerror(errno)); | |
663 | } | |
664 | fprintf(fp, "%lu\n", (unsigned long)getpid()); | |
665 | if (fflush(fp) || ferror(fp) || fclose(fp)) { | |
666 | die(EXIT_FAILURE, "couldn't write pidfile `%s': %s", | |
667 | pidfile, strerror(errno)); | |
668 | } | |
669 | } | |
afd7451e | 670 | |
17be1d6b | 671 | if (f & f_syslog) { |
afd7451e | 672 | flags |= FW_SYSLOG; |
673 | openlog(QUIS, 0, LOG_DAEMON); | |
e82f7154 | 674 | } |
675 | ||
8938f77b MW |
676 | /* --- Drop privileges --- */ |
677 | ||
678 | if (drop != (uid_t)-1) | |
679 | privconn_split(sel); | |
680 | #ifdef HAVE_SETGROUPS | |
681 | if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) || | |
682 | (drop != (uid_t)-1 && setuid(drop))) | |
683 | die(1, "couldn't drop privileges: %s", strerror(errno)); | |
684 | #else | |
685 | if ((dropg != (gid_t)-1 && setgid(dropg)) || | |
686 | (drop != (uid_t)-1 && setuid(drop))) | |
687 | die(1, "couldn't drop privileges: %s", strerror(errno)); | |
688 | #endif | |
689 | ||
e82f7154 | 690 | /* --- Let rip --- */ |
691 | ||
61e3dbdf | 692 | if (!(flags & FW_SET)) |
693 | moan("nothing to do!"); | |
694 | signal(SIGPIPE, SIG_IGN); | |
f9d40245 | 695 | |
696 | { | |
697 | int selerr = 0; | |
698 | while (active) { | |
699 | if (!sel_select(sel)) | |
206212ca | 700 | selerr = 0; |
372a98e2 | 701 | else if (errno != EINTR && errno != EAGAIN) { |
d935f68b | 702 | fw_log(NOW, "error from select: %s", strerror(errno)); |
f9d40245 | 703 | selerr++; |
704 | if (selerr > 8) { | |
d935f68b | 705 | fw_log(NOW, "too many consecutive select errors: bailing out"); |
f9d40245 | 706 | exit(EXIT_FAILURE); |
707 | } | |
708 | } | |
709 | } | |
710 | } | |
206212ca | 711 | |
e82f7154 | 712 | return (0); |
713 | } | |
714 | ||
715 | /*----- That's all, folks -------------------------------------------------*/ |