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