]>
Commit | Line | Data |
---|---|---|
25697773 MM |
1 | /* |
2 | * BIRD Internet Routing Daemon -- Unix Entry Point | |
3 | * | |
50fe90ed | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
25697773 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9a220cab | 9 | #undef LOCAL_DEBUG |
6b9fa320 | 10 | |
1bc26957 OZ |
11 | #define _GNU_SOURCE 1 |
12 | ||
25697773 | 13 | #include <stdio.h> |
7a2105be | 14 | #include <stdlib.h> |
70591fa0 MM |
15 | #include <fcntl.h> |
16 | #include <unistd.h> | |
b1a1faba | 17 | #include <signal.h> |
1bc26957 OZ |
18 | #include <pwd.h> |
19 | #include <grp.h> | |
b1b19433 | 20 | #include <sys/stat.h> |
48ec367a | 21 | #include <libgen.h> |
25697773 MM |
22 | |
23 | #include "nest/bird.h" | |
24 | #include "lib/lists.h" | |
25 | #include "lib/resource.h" | |
b5d9ee5c | 26 | #include "lib/socket.h" |
14dea0ed | 27 | #include "lib/event.h" |
221135d6 | 28 | #include "lib/string.h" |
25697773 | 29 | #include "nest/route.h" |
b5d9ee5c | 30 | #include "nest/protocol.h" |
8a48ecb8 | 31 | #include "nest/iface.h" |
7d3aab1c | 32 | #include "nest/cli.h" |
f545d387 | 33 | #include "nest/locks.h" |
70591fa0 | 34 | #include "conf/conf.h" |
b9d70dc8 | 35 | #include "filter/filter.h" |
b5d9ee5c MM |
36 | |
37 | #include "unix.h" | |
7e7790c6 | 38 | #include "krt.h" |
b5d9ee5c MM |
39 | |
40 | /* | |
41 | * Debugging | |
42 | */ | |
43 | ||
f9eb8f7e MM |
44 | #ifdef DEBUGGING |
45 | static int debug_flag = 1; | |
46 | #else | |
47 | static int debug_flag = 0; | |
48 | #endif | |
49 | ||
4c9dd1e4 MM |
50 | void |
51 | async_dump(void) | |
b5d9ee5c | 52 | { |
4c9dd1e4 | 53 | debug("INTERNAL STATE DUMP\n\n"); |
b5d9ee5c | 54 | |
5bc512aa | 55 | rdump(&root_pool); |
b5d9ee5c MM |
56 | sk_dump_all(); |
57 | tm_dump_all(); | |
8a48ecb8 | 58 | if_dump_all(); |
869c6959 | 59 | neigh_dump_all(); |
b5d9ee5c MM |
60 | rta_dump_all(); |
61 | rt_dump_all(); | |
86b00230 | 62 | protos_dump_all(); |
b5d9ee5c MM |
63 | |
64 | debug("\n"); | |
65 | } | |
66 | ||
1bc26957 OZ |
67 | /* |
68 | * Dropping privileges | |
69 | */ | |
70 | ||
71 | #ifdef CONFIG_RESTRICTED_PRIVILEGES | |
72 | #include "lib/syspriv.h" | |
73 | #else | |
74 | ||
75 | static inline void | |
76 | drop_uid(uid_t uid) | |
77 | { | |
78 | die("Cannot change user on this platform"); | |
79 | } | |
80 | ||
81 | #endif | |
82 | ||
83 | static inline void | |
84 | drop_gid(gid_t gid) | |
85 | { | |
86 | if (setgid(gid) < 0) | |
87 | die("setgid: %m"); | |
88 | } | |
89 | ||
b5d9ee5c | 90 | /* |
70591fa0 | 91 | * Reading the Configuration |
b5d9ee5c MM |
92 | */ |
93 | ||
acc93efd OZ |
94 | #ifdef PATH_IPROUTE_DIR |
95 | ||
96 | static inline void | |
97 | add_num_const(char *name, int val) | |
98 | { | |
9b9a7143 | 99 | struct symbol *s = cf_get_symbol(name); |
1103b32e OZ |
100 | s->class = SYM_CONSTANT | T_INT; |
101 | s->def = cfg_allocz(sizeof(struct f_val)); | |
102 | SYM_TYPE(s) = T_INT; | |
103 | SYM_VAL(s).i = val; | |
acc93efd OZ |
104 | } |
105 | ||
106 | /* the code of read_iproute_table() is based on | |
107 | rtnl_tab_initialize() from iproute2 package */ | |
108 | static void | |
109 | read_iproute_table(char *file, char *prefix, int max) | |
110 | { | |
111 | char buf[512], namebuf[512]; | |
112 | char *name; | |
113 | int val; | |
114 | FILE *fp; | |
115 | ||
116 | strcpy(namebuf, prefix); | |
117 | name = namebuf + strlen(prefix); | |
118 | ||
119 | fp = fopen(file, "r"); | |
120 | if (!fp) | |
121 | return; | |
122 | ||
123 | while (fgets(buf, sizeof(buf), fp)) | |
124 | { | |
125 | char *p = buf; | |
126 | ||
127 | while (*p == ' ' || *p == '\t') | |
128 | p++; | |
129 | ||
130 | if (*p == '#' || *p == '\n' || *p == 0) | |
131 | continue; | |
132 | ||
133 | if (sscanf(p, "0x%x %s\n", &val, name) != 2 && | |
134 | sscanf(p, "0x%x %s #", &val, name) != 2 && | |
135 | sscanf(p, "%d %s\n", &val, name) != 2 && | |
136 | sscanf(p, "%d %s #", &val, name) != 2) | |
137 | continue; | |
138 | ||
139 | if (val < 0 || val > max) | |
140 | continue; | |
141 | ||
142 | for(p = name; *p; p++) | |
143 | if ((*p < 'a' || *p > 'z') && (*p < '0' || *p > '9') && (*p != '_')) | |
144 | *p = '_'; | |
145 | ||
146 | add_num_const(namebuf, val); | |
147 | } | |
148 | ||
149 | fclose(fp); | |
150 | } | |
151 | ||
152 | #endif // PATH_IPROUTE_DIR | |
153 | ||
154 | ||
ab188fb7 | 155 | static char *config_name = PATH_CONFIG_FILE; |
70591fa0 MM |
156 | |
157 | static int | |
ae80a2de | 158 | cf_read(byte *dest, uint len, int fd) |
70591fa0 | 159 | { |
48ec367a | 160 | int l = read(fd, dest, len); |
70591fa0 MM |
161 | if (l < 0) |
162 | cf_error("Read error"); | |
163 | return l; | |
164 | } | |
165 | ||
a0c37b45 MM |
166 | void |
167 | sysdep_preconfig(struct config *c) | |
168 | { | |
169 | init_list(&c->logfiles); | |
acc93efd | 170 | |
8bcb5fb1 OZ |
171 | c->latency_limit = UNIX_DEFAULT_LATENCY_LIMIT; |
172 | c->watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING; | |
173 | ||
acc93efd | 174 | #ifdef PATH_IPROUTE_DIR |
72aed1a0 | 175 | read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256); |
acc93efd OZ |
176 | read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256); |
177 | read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256); | |
178 | read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256); | |
179 | #endif | |
a0c37b45 MM |
180 | } |
181 | ||
50fe90ed | 182 | int |
6578a604 | 183 | sysdep_commit(struct config *new, struct config *old UNUSED) |
a0c37b45 | 184 | { |
44d4ab7a | 185 | log_switch(debug_flag, &new->logfiles, new->syslog_name); |
50fe90ed | 186 | return 0; |
a0c37b45 MM |
187 | } |
188 | ||
50fe90ed MM |
189 | static int |
190 | unix_read_config(struct config **cp, char *name) | |
70591fa0 | 191 | { |
50fe90ed | 192 | struct config *conf = config_alloc(name); |
3c3271d9 | 193 | int ret; |
31b3e1bb | 194 | |
50fe90ed | 195 | *cp = conf; |
48ec367a OF |
196 | conf->file_fd = open(name, O_RDONLY); |
197 | if (conf->file_fd < 0) | |
50fe90ed | 198 | return 0; |
70591fa0 | 199 | cf_read_hook = cf_read; |
3c3271d9 | 200 | ret = config_parse(conf); |
48ec367a | 201 | close(conf->file_fd); |
3c3271d9 | 202 | return ret; |
50fe90ed MM |
203 | } |
204 | ||
0e175f9f | 205 | static struct config * |
50fe90ed MM |
206 | read_config(void) |
207 | { | |
208 | struct config *conf; | |
209 | ||
210 | if (!unix_read_config(&conf, config_name)) | |
211 | { | |
212 | if (conf->err_msg) | |
48ec367a | 213 | die("%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg); |
50fe90ed MM |
214 | else |
215 | die("Unable to open configuration file %s: %m", config_name); | |
216 | } | |
0e175f9f OZ |
217 | |
218 | return conf; | |
70591fa0 | 219 | } |
31b3e1bb | 220 | |
4c9dd1e4 MM |
221 | void |
222 | async_config(void) | |
223 | { | |
50fe90ed MM |
224 | struct config *conf; |
225 | ||
226 | log(L_INFO "Reconfiguration requested by SIGHUP"); | |
227 | if (!unix_read_config(&conf, config_name)) | |
228 | { | |
229 | if (conf->err_msg) | |
48ec367a | 230 | log(L_ERR "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg); |
50fe90ed MM |
231 | else |
232 | log(L_ERR "Unable to open configuration file %s: %m", config_name); | |
233 | config_free(conf); | |
234 | } | |
235 | else | |
a92cf57d | 236 | config_commit(conf, RECONFIG_HARD, 0); |
50fe90ed MM |
237 | } |
238 | ||
a92cf57d OZ |
239 | static struct config * |
240 | cmd_read_config(char *name) | |
50fe90ed MM |
241 | { |
242 | struct config *conf; | |
243 | ||
244 | if (!name) | |
245 | name = config_name; | |
a92cf57d | 246 | |
50fe90ed MM |
247 | cli_msg(-2, "Reading configuration from %s", name); |
248 | if (!unix_read_config(&conf, name)) | |
249 | { | |
250 | if (conf->err_msg) | |
ed7c4b0c | 251 | cli_msg(8002, "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg); |
50fe90ed MM |
252 | else |
253 | cli_msg(8002, "%s: %m", name); | |
254 | config_free(conf); | |
a92cf57d | 255 | conf = NULL; |
50fe90ed | 256 | } |
a92cf57d OZ |
257 | |
258 | return conf; | |
259 | } | |
260 | ||
261 | void | |
262 | cmd_check_config(char *name) | |
263 | { | |
264 | struct config *conf = cmd_read_config(name); | |
265 | if (!conf) | |
266 | return; | |
267 | ||
268 | cli_msg(20, "Configuration OK"); | |
269 | config_free(conf); | |
270 | } | |
271 | ||
272 | static void | |
273 | cmd_reconfig_msg(int r) | |
274 | { | |
275 | switch (r) | |
50fe90ed | 276 | { |
a92cf57d OZ |
277 | case CONF_DONE: cli_msg( 3, "Reconfigured"); break; |
278 | case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break; | |
279 | case CONF_QUEUED: cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break; | |
280 | case CONF_UNQUEUED: cli_msg(17, "Reconfiguration already in progress, removing queued config"); break; | |
281 | case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break; | |
282 | case CONF_SHUTDOWN: cli_msg( 6, "Reconfiguration ignored, shutting down"); break; | |
283 | case CONF_NOTHING: cli_msg(19, "Nothing to do"); break; | |
284 | default: break; | |
285 | } | |
286 | } | |
287 | ||
288 | /* Hack for scheduled undo notification */ | |
289 | cli *cmd_reconfig_stored_cli; | |
290 | ||
291 | void | |
292 | cmd_reconfig_undo_notify(void) | |
293 | { | |
294 | if (cmd_reconfig_stored_cli) | |
295 | { | |
296 | cli *c = cmd_reconfig_stored_cli; | |
297 | cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo"); | |
298 | cli_write_trigger(c); | |
299 | } | |
300 | } | |
301 | ||
302 | void | |
303 | cmd_reconfig(char *name, int type, int timeout) | |
304 | { | |
305 | if (cli_access_restricted()) | |
306 | return; | |
307 | ||
308 | struct config *conf = cmd_read_config(name); | |
309 | if (!conf) | |
310 | return; | |
311 | ||
312 | int r = config_commit(conf, type, timeout); | |
313 | ||
314 | if ((r >= 0) && (timeout > 0)) | |
315 | { | |
316 | cmd_reconfig_stored_cli = this_cli; | |
317 | cli_msg(-22, "Undo scheduled in %d s", timeout); | |
50fe90ed | 318 | } |
a92cf57d OZ |
319 | |
320 | cmd_reconfig_msg(r); | |
321 | } | |
322 | ||
323 | void | |
324 | cmd_reconfig_confirm(void) | |
325 | { | |
326 | if (cli_access_restricted()) | |
327 | return; | |
328 | ||
329 | int r = config_confirm(); | |
330 | cmd_reconfig_msg(r); | |
331 | } | |
332 | ||
333 | void | |
334 | cmd_reconfig_undo(void) | |
335 | { | |
336 | if (cli_access_restricted()) | |
337 | return; | |
338 | ||
339 | cli_msg(-21, "Undo requested"); | |
340 | ||
341 | int r = config_undo(); | |
342 | cmd_reconfig_msg(r); | |
4c9dd1e4 MM |
343 | } |
344 | ||
7d3aab1c MM |
345 | /* |
346 | * Command-Line Interface | |
347 | */ | |
348 | ||
349 | static sock *cli_sk; | |
dc82daaa | 350 | static char *path_control_socket = PATH_CONTROL_SOCKET; |
7d3aab1c | 351 | |
6baef17e OZ |
352 | |
353 | static void | |
7d3aab1c MM |
354 | cli_write(cli *c) |
355 | { | |
356 | sock *s = c->priv; | |
357 | ||
6baef17e | 358 | while (c->tx_pos) |
7d3aab1c MM |
359 | { |
360 | struct cli_out *o = c->tx_pos; | |
6baef17e OZ |
361 | |
362 | int len = o->wpos - o->outpos; | |
7d3aab1c | 363 | s->tbuf = o->outpos; |
6baef17e OZ |
364 | o->outpos = o->wpos; |
365 | ||
366 | if (sk_send(s, len) <= 0) | |
367 | return; | |
368 | ||
369 | c->tx_pos = o->next; | |
7d3aab1c | 370 | } |
6baef17e OZ |
371 | |
372 | /* Everything is written */ | |
373 | s->tbuf = NULL; | |
374 | cli_written(c); | |
375 | } | |
376 | ||
377 | void | |
378 | cli_write_trigger(cli *c) | |
379 | { | |
380 | sock *s = c->priv; | |
381 | ||
382 | if (s->tbuf == NULL) | |
383 | cli_write(c); | |
384 | } | |
385 | ||
386 | static void | |
387 | cli_tx(sock *s) | |
388 | { | |
389 | cli_write(s->data); | |
7d3aab1c MM |
390 | } |
391 | ||
392 | int | |
393 | cli_get_command(cli *c) | |
394 | { | |
395 | sock *s = c->priv; | |
396 | byte *t = c->rx_aux ? : s->rbuf; | |
397 | byte *tend = s->rpos; | |
398 | byte *d = c->rx_pos; | |
399 | byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2; | |
400 | ||
401 | while (t < tend) | |
402 | { | |
403 | if (*t == '\r') | |
404 | t++; | |
405 | else if (*t == '\n') | |
406 | { | |
407 | t++; | |
408 | c->rx_pos = c->rx_buf; | |
409 | c->rx_aux = t; | |
410 | *d = 0; | |
411 | return (d < dend) ? 1 : -1; | |
412 | } | |
413 | else if (d < dend) | |
414 | *d++ = *t++; | |
415 | } | |
416 | c->rx_aux = s->rpos = s->rbuf; | |
417 | c->rx_pos = d; | |
418 | return 0; | |
419 | } | |
420 | ||
421 | static int | |
6578a604 | 422 | cli_rx(sock *s, int size UNUSED) |
7d3aab1c | 423 | { |
7d3aab1c MM |
424 | cli_kick(s->data); |
425 | return 0; | |
426 | } | |
427 | ||
7d3aab1c MM |
428 | static void |
429 | cli_err(sock *s, int err) | |
430 | { | |
4761efdb MM |
431 | if (config->cli_debug) |
432 | { | |
433 | if (err) | |
434 | log(L_INFO "CLI connection dropped: %s", strerror(err)); | |
435 | else | |
436 | log(L_INFO "CLI connection closed"); | |
437 | } | |
7d3aab1c MM |
438 | cli_free(s->data); |
439 | } | |
440 | ||
441 | static int | |
6578a604 | 442 | cli_connect(sock *s, int size UNUSED) |
7d3aab1c MM |
443 | { |
444 | cli *c; | |
445 | ||
4761efdb MM |
446 | if (config->cli_debug) |
447 | log(L_INFO "CLI connect"); | |
7d3aab1c MM |
448 | s->rx_hook = cli_rx; |
449 | s->tx_hook = cli_tx; | |
450 | s->err_hook = cli_err; | |
7d3aab1c | 451 | s->data = c = cli_new(s); |
dc82daaa | 452 | s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */ |
9e7b3ebd | 453 | s->fast_rx = 1; |
7d3aab1c MM |
454 | c->rx_pos = c->rx_buf; |
455 | c->rx_aux = NULL; | |
ea0ac8f6 | 456 | rmove(s, c->pool); |
7d3aab1c MM |
457 | return 1; |
458 | } | |
459 | ||
460 | static void | |
e8b89a61 | 461 | cli_init_unix(uid_t use_uid, gid_t use_gid) |
7d3aab1c MM |
462 | { |
463 | sock *s; | |
464 | ||
465 | cli_init(); | |
466 | s = cli_sk = sk_new(cli_pool); | |
467 | s->type = SK_UNIX_PASSIVE; | |
468 | s->rx_hook = cli_connect; | |
e1ddd993 | 469 | s->rbsize = 1024; |
9e7b3ebd | 470 | s->fast_rx = 1; |
05476c4d OZ |
471 | |
472 | /* Return value intentionally ignored */ | |
473 | unlink(path_control_socket); | |
474 | ||
475 | if (sk_open_unix(s, path_control_socket) < 0) | |
476 | die("Cannot create control socket %s: %m", path_control_socket); | |
e8b89a61 OZ |
477 | |
478 | if (use_uid || use_gid) | |
479 | if (chown(path_control_socket, use_uid, use_gid) < 0) | |
480 | die("chown: %m"); | |
481 | ||
482 | if (chmod(path_control_socket, 0660) < 0) | |
483 | die("chmod: %m"); | |
7d3aab1c MM |
484 | } |
485 | ||
e7c23802 OZ |
486 | /* |
487 | * PID file | |
488 | */ | |
489 | ||
490 | static char *pid_file; | |
491 | static int pid_fd; | |
492 | ||
493 | static inline void | |
494 | open_pid_file(void) | |
495 | { | |
496 | if (!pid_file) | |
497 | return; | |
498 | ||
499 | pid_fd = open(pid_file, O_WRONLY|O_CREAT, 0664); | |
500 | if (pid_fd < 0) | |
501 | die("Cannot create PID file %s: %m", pid_file); | |
502 | } | |
503 | ||
504 | static inline void | |
505 | write_pid_file(void) | |
506 | { | |
507 | int pl, rv; | |
508 | char ps[24]; | |
509 | ||
510 | if (!pid_file) | |
511 | return; | |
512 | ||
513 | /* We don't use PID file for uniqueness, so no need for locking */ | |
514 | ||
515 | pl = bsnprintf(ps, sizeof(ps), "%ld\n", (long) getpid()); | |
516 | if (pl < 0) | |
517 | bug("PID buffer too small"); | |
518 | ||
519 | rv = ftruncate(pid_fd, 0); | |
520 | if (rv < 0) | |
521 | die("fruncate: %m"); | |
522 | ||
523 | rv = write(pid_fd, ps, pl); | |
524 | if(rv < 0) | |
525 | die("write: %m"); | |
526 | ||
527 | close(pid_fd); | |
528 | } | |
529 | ||
530 | static inline void | |
531 | unlink_pid_file(void) | |
532 | { | |
533 | if (pid_file) | |
534 | unlink(pid_file); | |
535 | } | |
536 | ||
537 | ||
f4aabcee MM |
538 | /* |
539 | * Shutdown | |
540 | */ | |
541 | ||
e0a45fb4 OZ |
542 | void |
543 | cmd_shutdown(void) | |
544 | { | |
545 | if (cli_access_restricted()) | |
546 | return; | |
547 | ||
548 | cli_msg(7, "Shutdown requested"); | |
549 | order_shutdown(); | |
550 | } | |
551 | ||
f4aabcee MM |
552 | void |
553 | async_shutdown(void) | |
554 | { | |
6b9fa320 | 555 | DBG("Shutting down...\n"); |
bf8558bc | 556 | order_shutdown(); |
f4aabcee MM |
557 | } |
558 | ||
559 | void | |
bf8558bc | 560 | sysdep_shutdown_done(void) |
f4aabcee | 561 | { |
e7c23802 | 562 | unlink_pid_file(); |
d3f36e59 | 563 | unlink(path_control_socket); |
76b53a4e | 564 | log_msg(L_FATAL "Shutdown completed"); |
653b4015 | 565 | exit(0); |
f4aabcee MM |
566 | } |
567 | ||
4c9dd1e4 MM |
568 | /* |
569 | * Signals | |
570 | */ | |
571 | ||
572 | static void | |
6578a604 | 573 | handle_sighup(int sig UNUSED) |
4c9dd1e4 | 574 | { |
6b9fa320 | 575 | DBG("Caught SIGHUP...\n"); |
4c9dd1e4 MM |
576 | async_config_flag = 1; |
577 | } | |
578 | ||
579 | static void | |
6578a604 | 580 | handle_sigusr(int sig UNUSED) |
4c9dd1e4 | 581 | { |
6b9fa320 | 582 | DBG("Caught SIGUSR...\n"); |
4c9dd1e4 MM |
583 | async_dump_flag = 1; |
584 | } | |
585 | ||
f4aabcee | 586 | static void |
6578a604 | 587 | handle_sigterm(int sig UNUSED) |
f4aabcee | 588 | { |
6b9fa320 | 589 | DBG("Caught SIGTERM...\n"); |
f4aabcee MM |
590 | async_shutdown_flag = 1; |
591 | } | |
592 | ||
8bcb5fb1 OZ |
593 | void watchdog_sigalrm(int sig UNUSED); |
594 | ||
4c9dd1e4 MM |
595 | static void |
596 | signal_init(void) | |
597 | { | |
598 | struct sigaction sa; | |
599 | ||
600 | bzero(&sa, sizeof(sa)); | |
601 | sa.sa_handler = handle_sigusr; | |
602 | sa.sa_flags = SA_RESTART; | |
603 | sigaction(SIGUSR1, &sa, NULL); | |
604 | sa.sa_handler = handle_sighup; | |
605 | sa.sa_flags = SA_RESTART; | |
606 | sigaction(SIGHUP, &sa, NULL); | |
f4aabcee MM |
607 | sa.sa_handler = handle_sigterm; |
608 | sa.sa_flags = SA_RESTART; | |
609 | sigaction(SIGTERM, &sa, NULL); | |
8bcb5fb1 OZ |
610 | sa.sa_handler = watchdog_sigalrm; |
611 | sa.sa_flags = 0; | |
612 | sigaction(SIGALRM, &sa, NULL); | |
4c9dd1e4 MM |
613 | signal(SIGPIPE, SIG_IGN); |
614 | } | |
615 | ||
616 | /* | |
617 | * Parsing of command-line arguments | |
618 | */ | |
619 | ||
292f7858 | 620 | static char *opt_list = "c:dD:ps:P:u:g:flRh"; |
44d4ab7a OZ |
621 | static int parse_and_exit; |
622 | char *bird_name; | |
1bc26957 OZ |
623 | static char *use_user; |
624 | static char *use_group; | |
1cd198cf | 625 | static int run_in_foreground = 0; |
0bcba21e | 626 | |
4c9dd1e4 | 627 | static void |
292f7858 | 628 | display_usage(void) |
4c9dd1e4 | 629 | { |
292f7858 PT |
630 | fprintf(stderr, "Usage: %s [--version] [--help] [-c <config-file>] [OPTIONS]\n", bird_name); |
631 | } | |
632 | ||
633 | static void | |
634 | display_help(void) | |
635 | { | |
636 | display_usage(); | |
637 | ||
638 | fprintf(stderr, | |
639 | "\n" | |
640 | "Options: \n" | |
641 | " -c <config-file> Use given configuration file instead\n" | |
642 | " of prefix/etc/bird.conf\n" | |
643 | " -d Enable debug messages and run bird in foreground\n" | |
644 | " -D <debug-file> Log debug messages to given file instead of stderr\n" | |
645 | " -f Run bird in foreground\n" | |
646 | " -g <group> Use given group ID\n" | |
647 | " -h, --help Display this information\n" | |
648 | " -l Look for a configuration file and a communication socket\n" | |
649 | " file in the current working directory\n" | |
650 | " -p Test configuration file and exit without start\n" | |
651 | " -P <pid-file> Create a PID file with given filename\n" | |
652 | " -R Apply graceful restart recovery after start\n" | |
653 | " -s <control-socket> Use given filename for a control socket\n" | |
654 | " -u <user> Drop privileges and use given user ID\n" | |
655 | " --version Display version of BIRD\n"); | |
656 | ||
657 | exit(0); | |
658 | } | |
659 | ||
660 | static void | |
661 | display_version(void) | |
662 | { | |
663 | fprintf(stderr, "BIRD version " BIRD_VERSION "\n"); | |
664 | exit(0); | |
4c9dd1e4 MM |
665 | } |
666 | ||
44d4ab7a OZ |
667 | static inline char * |
668 | get_bird_name(char *s, char *def) | |
669 | { | |
670 | char *t; | |
671 | if (!s) | |
672 | return def; | |
673 | t = strrchr(s, '/'); | |
674 | if (!t) | |
675 | return s; | |
676 | if (!t[1]) | |
677 | return def; | |
678 | return t+1; | |
679 | } | |
a4644ed6 | 680 | |
1bc26957 OZ |
681 | static inline uid_t |
682 | get_uid(const char *s) | |
683 | { | |
684 | struct passwd *pw; | |
685 | char *endptr; | |
e8b89a61 OZ |
686 | long int rv; |
687 | ||
688 | if (!s) | |
689 | return 0; | |
690 | ||
1bc26957 | 691 | errno = 0; |
e8b89a61 | 692 | rv = strtol(s, &endptr, 10); |
1bc26957 OZ |
693 | |
694 | if (!errno && !*endptr) | |
695 | return rv; | |
696 | ||
697 | pw = getpwnam(s); | |
698 | if (!pw) | |
699 | die("Cannot find user '%s'", s); | |
700 | ||
701 | return pw->pw_uid; | |
702 | } | |
703 | ||
704 | static inline gid_t | |
705 | get_gid(const char *s) | |
706 | { | |
707 | struct group *gr; | |
708 | char *endptr; | |
e8b89a61 OZ |
709 | long int rv; |
710 | ||
711 | if (!s) | |
712 | return 0; | |
f2ae2bad | 713 | |
1bc26957 | 714 | errno = 0; |
e8b89a61 | 715 | rv = strtol(s, &endptr, 10); |
1bc26957 OZ |
716 | |
717 | if (!errno && !*endptr) | |
718 | return rv; | |
719 | ||
720 | gr = getgrnam(s); | |
721 | if (!gr) | |
722 | die("Cannot find group '%s'", s); | |
723 | ||
724 | return gr->gr_gid; | |
725 | } | |
726 | ||
4c9dd1e4 MM |
727 | static void |
728 | parse_args(int argc, char **argv) | |
729 | { | |
f2ae2bad OZ |
730 | int config_changed = 0; |
731 | int socket_changed = 0; | |
4c9dd1e4 MM |
732 | int c; |
733 | ||
44d4ab7a | 734 | bird_name = get_bird_name(argv[0], "bird"); |
e67af428 MM |
735 | if (argc == 2) |
736 | { | |
737 | if (!strcmp(argv[1], "--version")) | |
292f7858 | 738 | display_version(); |
e67af428 | 739 | if (!strcmp(argv[1], "--help")) |
292f7858 | 740 | display_help(); |
e67af428 | 741 | } |
4c9dd1e4 MM |
742 | while ((c = getopt(argc, argv, opt_list)) >= 0) |
743 | switch (c) | |
744 | { | |
745 | case 'c': | |
746 | config_name = optarg; | |
f2ae2bad | 747 | config_changed = 1; |
4c9dd1e4 MM |
748 | break; |
749 | case 'd': | |
a0c37b45 MM |
750 | debug_flag |= 1; |
751 | break; | |
752 | case 'D': | |
4c9dd1e4 | 753 | log_init_debug(optarg); |
a0c37b45 | 754 | debug_flag |= 2; |
4c9dd1e4 | 755 | break; |
a4644ed6 OZ |
756 | case 'p': |
757 | parse_and_exit = 1; | |
758 | break; | |
dc82daaa MM |
759 | case 's': |
760 | path_control_socket = optarg; | |
f2ae2bad | 761 | socket_changed = 1; |
dc82daaa | 762 | break; |
e7c23802 OZ |
763 | case 'P': |
764 | pid_file = optarg; | |
765 | break; | |
1bc26957 OZ |
766 | case 'u': |
767 | use_user = optarg; | |
768 | break; | |
769 | case 'g': | |
770 | use_group = optarg; | |
771 | break; | |
1cd198cf OF |
772 | case 'f': |
773 | run_in_foreground = 1; | |
774 | break; | |
f2ae2bad OZ |
775 | case 'l': |
776 | if (!config_changed) | |
777 | config_name = xbasename(config_name); | |
778 | if (!socket_changed) | |
779 | path_control_socket = xbasename(path_control_socket); | |
780 | break; | |
0c791f87 OZ |
781 | case 'R': |
782 | graceful_restart_recovery(); | |
783 | break; | |
292f7858 PT |
784 | case 'h': |
785 | display_help(); | |
786 | break; | |
4c9dd1e4 | 787 | default: |
292f7858 PT |
788 | fputc('\n', stderr); |
789 | display_usage(); | |
790 | exit(1); | |
4c9dd1e4 MM |
791 | } |
792 | if (optind < argc) | |
292f7858 PT |
793 | { |
794 | display_usage(); | |
795 | exit(1); | |
796 | } | |
4c9dd1e4 MM |
797 | } |
798 | ||
fd50083d MM |
799 | /* |
800 | * Hic Est main() | |
801 | */ | |
25697773 MM |
802 | |
803 | int | |
4c9dd1e4 | 804 | main(int argc, char **argv) |
25697773 | 805 | { |
7a2105be MM |
806 | #ifdef HAVE_LIBDMALLOC |
807 | if (!getenv("DMALLOC_OPTIONS")) | |
808 | dmalloc_debug(0x2f03d00); | |
809 | #endif | |
810 | ||
4c9dd1e4 | 811 | parse_args(argc, argv); |
a0c37b45 MM |
812 | if (debug_flag == 1) |
813 | log_init_debug(""); | |
44d4ab7a | 814 | log_switch(debug_flag, NULL, NULL); |
4c9dd1e4 | 815 | |
c74c0e3c | 816 | resource_init(); |
f545d387 | 817 | olock_init(); |
b5d9ee5c | 818 | io_init(); |
2326b001 | 819 | rt_init(); |
8a48ecb8 | 820 | if_init(); |
af582c48 | 821 | roa_init(); |
a92cf57d | 822 | config_init(); |
c74c0e3c | 823 | |
e8b89a61 OZ |
824 | uid_t use_uid = get_uid(use_user); |
825 | gid_t use_gid = get_gid(use_group); | |
826 | ||
97e46d28 | 827 | if (!parse_and_exit) |
e8b89a61 OZ |
828 | { |
829 | test_old_bird(path_control_socket); | |
830 | cli_init_unix(use_uid, use_gid); | |
831 | } | |
832 | ||
833 | if (use_gid) | |
834 | drop_gid(use_gid); | |
835 | ||
836 | if (use_uid) | |
837 | drop_uid(use_uid); | |
97e46d28 | 838 | |
e7c23802 OZ |
839 | if (!parse_and_exit) |
840 | open_pid_file(); | |
841 | ||
0432c017 | 842 | protos_build(); |
3991d84e MM |
843 | proto_build(&proto_unix_kernel); |
844 | proto_build(&proto_unix_iface); | |
c74c0e3c | 845 | |
0e175f9f | 846 | struct config *conf = read_config(); |
8a48ecb8 | 847 | |
a4644ed6 OZ |
848 | if (parse_and_exit) |
849 | exit(0); | |
850 | ||
1cd198cf | 851 | if (!(debug_flag||run_in_foreground)) |
0bcba21e MM |
852 | { |
853 | pid_t pid = fork(); | |
854 | if (pid < 0) | |
855 | die("fork: %m"); | |
4524331a | 856 | if (pid) |
0bcba21e MM |
857 | return 0; |
858 | setsid(); | |
8411a37e MM |
859 | close(0); |
860 | if (open("/dev/null", O_RDWR) < 0) | |
861 | die("Cannot open /dev/null: %m"); | |
862 | dup2(0, 1); | |
863 | dup2(0, 2); | |
0bcba21e MM |
864 | } |
865 | ||
4e398e34 OZ |
866 | main_thread_init(); |
867 | ||
e7c23802 OZ |
868 | write_pid_file(); |
869 | ||
b5d9ee5c MM |
870 | signal_init(); |
871 | ||
0e175f9f OZ |
872 | config_commit(conf, RECONFIG_HARD, 0); |
873 | ||
0c791f87 OZ |
874 | graceful_restart_init(); |
875 | ||
9a220cab MM |
876 | #ifdef LOCAL_DEBUG |
877 | async_dump_flag = 1; | |
878 | #endif | |
8a48ecb8 | 879 | |
76b53a4e | 880 | log(L_INFO "Started"); |
6b9fa320 | 881 | DBG("Entering I/O loop.\n"); |
25697773 | 882 | |
b5d9ee5c | 883 | io_loop(); |
08c69a77 | 884 | bug("I/O loop died"); |
25697773 | 885 | } |