]> git.ipfire.org Git - thirdparty/bird.git/blob - sysdep/unix/main.c
IPv4/IPv6 integrated socket code.
[thirdparty/bird.git] / sysdep / unix / main.c
1 /*
2 * BIRD Internet Routing Daemon -- Unix Entry Point
3 *
4 * (c) 1998--2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9 #undef LOCAL_DEBUG
10
11 #define _GNU_SOURCE 1
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <grp.h>
20 #include <sys/stat.h>
21 #include <libgen.h>
22
23 #include "nest/bird.h"
24 #include "lib/lists.h"
25 #include "lib/resource.h"
26 #include "lib/socket.h"
27 #include "lib/event.h"
28 #include "lib/string.h"
29 #include "nest/route.h"
30 #include "nest/protocol.h"
31 #include "nest/iface.h"
32 #include "nest/cli.h"
33 #include "nest/locks.h"
34 #include "conf/conf.h"
35 #include "filter/filter.h"
36
37 #include "unix.h"
38 #include "krt.h"
39
40 /*
41 * Debugging
42 */
43
44 #ifdef DEBUGGING
45 static int debug_flag = 1;
46 #else
47 static int debug_flag = 0;
48 #endif
49
50 void
51 async_dump(void)
52 {
53 debug("INTERNAL STATE DUMP\n\n");
54
55 rdump(&root_pool);
56 sk_dump_all();
57 tm_dump_all();
58 if_dump_all();
59 neigh_dump_all();
60 rta_dump_all();
61 rt_dump_all();
62 protos_dump_all();
63
64 debug("\n");
65 }
66
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
90 /*
91 * Reading the Configuration
92 */
93
94 #ifdef PATH_IPROUTE_DIR
95
96 static inline void
97 add_num_const(char *name, int val)
98 {
99 struct symbol *s = cf_find_symbol(name);
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;
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
155 static char *config_name = PATH_CONFIG_FILE;
156
157 static int
158 cf_read(byte *dest, unsigned int len, int fd)
159 {
160 int l = read(fd, dest, len);
161 if (l < 0)
162 cf_error("Read error");
163 return l;
164 }
165
166 void
167 sysdep_preconfig(struct config *c)
168 {
169 init_list(&c->logfiles);
170
171 #ifdef PATH_IPROUTE_DIR
172 read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
173 read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256);
174 read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256);
175 read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256);
176 #endif
177 }
178
179 int
180 sysdep_commit(struct config *new, struct config *old UNUSED)
181 {
182 log_switch(debug_flag, &new->logfiles, new->syslog_name);
183 return 0;
184 }
185
186 static int
187 unix_read_config(struct config **cp, char *name)
188 {
189 struct config *conf = config_alloc(name);
190 int ret;
191
192 *cp = conf;
193 conf->file_fd = open(name, O_RDONLY);
194 if (conf->file_fd < 0)
195 return 0;
196 cf_read_hook = cf_read;
197 ret = config_parse(conf);
198 close(conf->file_fd);
199 return ret;
200 }
201
202 static struct config *
203 read_config(void)
204 {
205 struct config *conf;
206
207 if (!unix_read_config(&conf, config_name))
208 {
209 if (conf->err_msg)
210 die("%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
211 else
212 die("Unable to open configuration file %s: %m", config_name);
213 }
214
215 return conf;
216 }
217
218 void
219 async_config(void)
220 {
221 struct config *conf;
222
223 log(L_INFO "Reconfiguration requested by SIGHUP");
224 if (!unix_read_config(&conf, config_name))
225 {
226 if (conf->err_msg)
227 log(L_ERR "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
228 else
229 log(L_ERR "Unable to open configuration file %s: %m", config_name);
230 config_free(conf);
231 }
232 else
233 config_commit(conf, RECONFIG_HARD, 0);
234 }
235
236 static struct config *
237 cmd_read_config(char *name)
238 {
239 struct config *conf;
240
241 if (!name)
242 name = config_name;
243
244 cli_msg(-2, "Reading configuration from %s", name);
245 if (!unix_read_config(&conf, name))
246 {
247 if (conf->err_msg)
248 cli_msg(8002, "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
249 else
250 cli_msg(8002, "%s: %m", name);
251 config_free(conf);
252 conf = NULL;
253 }
254
255 return conf;
256 }
257
258 void
259 cmd_check_config(char *name)
260 {
261 struct config *conf = cmd_read_config(name);
262 if (!conf)
263 return;
264
265 cli_msg(20, "Configuration OK");
266 config_free(conf);
267 }
268
269 static void
270 cmd_reconfig_msg(int r)
271 {
272 switch (r)
273 {
274 case CONF_DONE: cli_msg( 3, "Reconfigured"); break;
275 case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break;
276 case CONF_QUEUED: cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break;
277 case CONF_UNQUEUED: cli_msg(17, "Reconfiguration already in progress, removing queued config"); break;
278 case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break;
279 case CONF_SHUTDOWN: cli_msg( 6, "Reconfiguration ignored, shutting down"); break;
280 case CONF_NOTHING: cli_msg(19, "Nothing to do"); break;
281 default: break;
282 }
283 }
284
285 /* Hack for scheduled undo notification */
286 cli *cmd_reconfig_stored_cli;
287
288 void
289 cmd_reconfig_undo_notify(void)
290 {
291 if (cmd_reconfig_stored_cli)
292 {
293 cli *c = cmd_reconfig_stored_cli;
294 cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo");
295 cli_write_trigger(c);
296 }
297 }
298
299 void
300 cmd_reconfig(char *name, int type, int timeout)
301 {
302 if (cli_access_restricted())
303 return;
304
305 struct config *conf = cmd_read_config(name);
306 if (!conf)
307 return;
308
309 int r = config_commit(conf, type, timeout);
310
311 if ((r >= 0) && (timeout > 0))
312 {
313 cmd_reconfig_stored_cli = this_cli;
314 cli_msg(-22, "Undo scheduled in %d s", timeout);
315 }
316
317 cmd_reconfig_msg(r);
318 }
319
320 void
321 cmd_reconfig_confirm(void)
322 {
323 if (cli_access_restricted())
324 return;
325
326 int r = config_confirm();
327 cmd_reconfig_msg(r);
328 }
329
330 void
331 cmd_reconfig_undo(void)
332 {
333 if (cli_access_restricted())
334 return;
335
336 cli_msg(-21, "Undo requested");
337
338 int r = config_undo();
339 cmd_reconfig_msg(r);
340 }
341
342 /*
343 * Command-Line Interface
344 */
345
346 static sock *cli_sk;
347 static char *path_control_socket = PATH_CONTROL_SOCKET;
348
349
350 static void
351 cli_write(cli *c)
352 {
353 sock *s = c->priv;
354
355 while (c->tx_pos)
356 {
357 struct cli_out *o = c->tx_pos;
358
359 int len = o->wpos - o->outpos;
360 s->tbuf = o->outpos;
361 o->outpos = o->wpos;
362
363 if (sk_send(s, len) <= 0)
364 return;
365
366 c->tx_pos = o->next;
367 }
368
369 /* Everything is written */
370 s->tbuf = NULL;
371 cli_written(c);
372 }
373
374 void
375 cli_write_trigger(cli *c)
376 {
377 sock *s = c->priv;
378
379 if (s->tbuf == NULL)
380 cli_write(c);
381 }
382
383 static void
384 cli_tx(sock *s)
385 {
386 cli_write(s->data);
387 }
388
389 int
390 cli_get_command(cli *c)
391 {
392 sock *s = c->priv;
393 byte *t = c->rx_aux ? : s->rbuf;
394 byte *tend = s->rpos;
395 byte *d = c->rx_pos;
396 byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
397
398 while (t < tend)
399 {
400 if (*t == '\r')
401 t++;
402 else if (*t == '\n')
403 {
404 t++;
405 c->rx_pos = c->rx_buf;
406 c->rx_aux = t;
407 *d = 0;
408 return (d < dend) ? 1 : -1;
409 }
410 else if (d < dend)
411 *d++ = *t++;
412 }
413 c->rx_aux = s->rpos = s->rbuf;
414 c->rx_pos = d;
415 return 0;
416 }
417
418 static int
419 cli_rx(sock *s, int size UNUSED)
420 {
421 cli_kick(s->data);
422 return 0;
423 }
424
425 static void
426 cli_err(sock *s, int err)
427 {
428 if (config->cli_debug)
429 {
430 if (err)
431 log(L_INFO "CLI connection dropped: %s", strerror(err));
432 else
433 log(L_INFO "CLI connection closed");
434 }
435 cli_free(s->data);
436 }
437
438 static int
439 cli_connect(sock *s, int size UNUSED)
440 {
441 cli *c;
442
443 if (config->cli_debug)
444 log(L_INFO "CLI connect");
445 s->rx_hook = cli_rx;
446 s->tx_hook = cli_tx;
447 s->err_hook = cli_err;
448 s->data = c = cli_new(s);
449 s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
450 c->rx_pos = c->rx_buf;
451 c->rx_aux = NULL;
452 rmove(s, c->pool);
453 return 1;
454 }
455
456 static void
457 cli_init_unix(uid_t use_uid, gid_t use_gid)
458 {
459 sock *s;
460
461 cli_init();
462 s = cli_sk = sk_new(cli_pool);
463 s->type = SK_UNIX_PASSIVE;
464 s->rx_hook = cli_connect;
465 s->rbsize = 1024;
466
467 /* Return value intentionally ignored */
468 unlink(path_control_socket);
469
470 if (sk_open_unix(s, path_control_socket) < 0)
471 die("Cannot create control socket %s: %m", path_control_socket);
472
473 if (use_uid || use_gid)
474 if (chown(path_control_socket, use_uid, use_gid) < 0)
475 die("chown: %m");
476
477 if (chmod(path_control_socket, 0660) < 0)
478 die("chmod: %m");
479 }
480
481 /*
482 * PID file
483 */
484
485 static char *pid_file;
486 static int pid_fd;
487
488 static inline void
489 open_pid_file(void)
490 {
491 if (!pid_file)
492 return;
493
494 pid_fd = open(pid_file, O_WRONLY|O_CREAT, 0664);
495 if (pid_fd < 0)
496 die("Cannot create PID file %s: %m", pid_file);
497 }
498
499 static inline void
500 write_pid_file(void)
501 {
502 int pl, rv;
503 char ps[24];
504
505 if (!pid_file)
506 return;
507
508 /* We don't use PID file for uniqueness, so no need for locking */
509
510 pl = bsnprintf(ps, sizeof(ps), "%ld\n", (long) getpid());
511 if (pl < 0)
512 bug("PID buffer too small");
513
514 rv = ftruncate(pid_fd, 0);
515 if (rv < 0)
516 die("fruncate: %m");
517
518 rv = write(pid_fd, ps, pl);
519 if(rv < 0)
520 die("write: %m");
521
522 close(pid_fd);
523 }
524
525 static inline void
526 unlink_pid_file(void)
527 {
528 if (pid_file)
529 unlink(pid_file);
530 }
531
532
533 /*
534 * Shutdown
535 */
536
537 void
538 cmd_shutdown(void)
539 {
540 if (cli_access_restricted())
541 return;
542
543 cli_msg(7, "Shutdown requested");
544 order_shutdown();
545 }
546
547 void
548 async_shutdown(void)
549 {
550 DBG("Shutting down...\n");
551 order_shutdown();
552 }
553
554 void
555 sysdep_shutdown_done(void)
556 {
557 unlink_pid_file();
558 unlink(path_control_socket);
559 log_msg(L_FATAL "Shutdown completed");
560 exit(0);
561 }
562
563 /*
564 * Signals
565 */
566
567 static void
568 handle_sighup(int sig UNUSED)
569 {
570 DBG("Caught SIGHUP...\n");
571 async_config_flag = 1;
572 }
573
574 static void
575 handle_sigusr(int sig UNUSED)
576 {
577 DBG("Caught SIGUSR...\n");
578 async_dump_flag = 1;
579 }
580
581 static void
582 handle_sigterm(int sig UNUSED)
583 {
584 DBG("Caught SIGTERM...\n");
585 async_shutdown_flag = 1;
586 }
587
588 static void
589 signal_init(void)
590 {
591 struct sigaction sa;
592
593 bzero(&sa, sizeof(sa));
594 sa.sa_handler = handle_sigusr;
595 sa.sa_flags = SA_RESTART;
596 sigaction(SIGUSR1, &sa, NULL);
597 sa.sa_handler = handle_sighup;
598 sa.sa_flags = SA_RESTART;
599 sigaction(SIGHUP, &sa, NULL);
600 sa.sa_handler = handle_sigterm;
601 sa.sa_flags = SA_RESTART;
602 sigaction(SIGTERM, &sa, NULL);
603 signal(SIGPIPE, SIG_IGN);
604 }
605
606 /*
607 * Parsing of command-line arguments
608 */
609
610 static char *opt_list = "c:dD:ps:P:u:g:fR";
611 static int parse_and_exit;
612 char *bird_name;
613 static char *use_user;
614 static char *use_group;
615 static int run_in_foreground = 0;
616
617 static void
618 usage(void)
619 {
620 fprintf(stderr, "Usage: %s [-c <config-file>] [-d] [-D <debug-file>] [-p] [-s <control-socket>] [-P <pid-file>] [-u <user>] [-g <group>] [-f] [-R]\n", bird_name);
621 exit(1);
622 }
623
624 static inline char *
625 get_bird_name(char *s, char *def)
626 {
627 char *t;
628 if (!s)
629 return def;
630 t = strrchr(s, '/');
631 if (!t)
632 return s;
633 if (!t[1])
634 return def;
635 return t+1;
636 }
637
638 static inline uid_t
639 get_uid(const char *s)
640 {
641 struct passwd *pw;
642 char *endptr;
643 long int rv;
644
645 if (!s)
646 return 0;
647
648 errno = 0;
649 rv = strtol(s, &endptr, 10);
650
651 if (!errno && !*endptr)
652 return rv;
653
654 pw = getpwnam(s);
655 if (!pw)
656 die("Cannot find user '%s'", s);
657
658 return pw->pw_uid;
659 }
660
661 static inline gid_t
662 get_gid(const char *s)
663 {
664 struct group *gr;
665 char *endptr;
666 long int rv;
667
668 if (!s)
669 return 0;
670
671 errno = 0;
672 rv = strtol(s, &endptr, 10);
673
674 if (!errno && !*endptr)
675 return rv;
676
677 gr = getgrnam(s);
678 if (!gr)
679 die("Cannot find group '%s'", s);
680
681 return gr->gr_gid;
682 }
683
684 static void
685 parse_args(int argc, char **argv)
686 {
687 int c;
688
689 bird_name = get_bird_name(argv[0], "bird");
690 if (argc == 2)
691 {
692 if (!strcmp(argv[1], "--version"))
693 {
694 fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
695 exit(0);
696 }
697 if (!strcmp(argv[1], "--help"))
698 usage();
699 }
700 while ((c = getopt(argc, argv, opt_list)) >= 0)
701 switch (c)
702 {
703 case 'c':
704 config_name = optarg;
705 break;
706 case 'd':
707 debug_flag |= 1;
708 break;
709 case 'D':
710 log_init_debug(optarg);
711 debug_flag |= 2;
712 break;
713 case 'p':
714 parse_and_exit = 1;
715 break;
716 case 's':
717 path_control_socket = optarg;
718 break;
719 case 'P':
720 pid_file = optarg;
721 break;
722 case 'u':
723 use_user = optarg;
724 break;
725 case 'g':
726 use_group = optarg;
727 break;
728 case 'f':
729 run_in_foreground = 1;
730 break;
731 case 'R':
732 graceful_restart_recovery();
733 break;
734 default:
735 usage();
736 }
737 if (optind < argc)
738 usage();
739 }
740
741 /*
742 * Hic Est main()
743 */
744
745 int
746 main(int argc, char **argv)
747 {
748 #ifdef HAVE_LIBDMALLOC
749 if (!getenv("DMALLOC_OPTIONS"))
750 dmalloc_debug(0x2f03d00);
751 #endif
752
753 parse_args(argc, argv);
754 if (debug_flag == 1)
755 log_init_debug("");
756 log_switch(debug_flag, NULL, NULL);
757
758 resource_init();
759 olock_init();
760 io_init();
761 rt_init();
762 if_init();
763 roa_init();
764 config_init();
765
766 uid_t use_uid = get_uid(use_user);
767 gid_t use_gid = get_gid(use_group);
768
769 if (!parse_and_exit)
770 {
771 test_old_bird(path_control_socket);
772 cli_init_unix(use_uid, use_gid);
773 }
774
775 if (use_gid)
776 drop_gid(use_gid);
777
778 if (use_uid)
779 drop_uid(use_uid);
780
781 if (!parse_and_exit)
782 open_pid_file();
783
784 protos_build();
785 proto_build(&proto_unix_kernel);
786 proto_build(&proto_unix_iface);
787
788 struct config *conf = read_config();
789
790 if (parse_and_exit)
791 exit(0);
792
793 if (!(debug_flag||run_in_foreground))
794 {
795 pid_t pid = fork();
796 if (pid < 0)
797 die("fork: %m");
798 if (pid)
799 return 0;
800 setsid();
801 close(0);
802 if (open("/dev/null", O_RDWR) < 0)
803 die("Cannot open /dev/null: %m");
804 dup2(0, 1);
805 dup2(0, 2);
806 }
807
808 main_thread_init();
809
810 write_pid_file();
811
812 signal_init();
813
814 config_commit(conf, RECONFIG_HARD, 0);
815
816 graceful_restart_init();
817
818 #ifdef LOCAL_DEBUG
819 async_dump_flag = 1;
820 #endif
821
822 log(L_INFO "Started");
823 DBG("Entering I/O loop.\n");
824
825 io_loop();
826 bug("I/O loop died");
827 }