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