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