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