]> git.ipfire.org Git - thirdparty/bird.git/blob - sysdep/unix/main.c
Main: Improve BIRD help messages
[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_get_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, uint 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 c->latency_limit = UNIX_DEFAULT_LATENCY_LIMIT;
172 c->watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING;
173
174 #ifdef PATH_IPROUTE_DIR
175 read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
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
180 }
181
182 int
183 sysdep_commit(struct config *new, struct config *old UNUSED)
184 {
185 log_switch(debug_flag, &new->logfiles, new->syslog_name);
186 return 0;
187 }
188
189 static int
190 unix_read_config(struct config **cp, char *name)
191 {
192 struct config *conf = config_alloc(name);
193 int ret;
194
195 *cp = conf;
196 conf->file_fd = open(name, O_RDONLY);
197 if (conf->file_fd < 0)
198 return 0;
199 cf_read_hook = cf_read;
200 ret = config_parse(conf);
201 close(conf->file_fd);
202 return ret;
203 }
204
205 static struct config *
206 read_config(void)
207 {
208 struct config *conf;
209
210 if (!unix_read_config(&conf, config_name))
211 {
212 if (conf->err_msg)
213 die("%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
214 else
215 die("Unable to open configuration file %s: %m", config_name);
216 }
217
218 return conf;
219 }
220
221 void
222 async_config(void)
223 {
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)
230 log(L_ERR "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
231 else
232 log(L_ERR "Unable to open configuration file %s: %m", config_name);
233 config_free(conf);
234 }
235 else
236 config_commit(conf, RECONFIG_HARD, 0);
237 }
238
239 static struct config *
240 cmd_read_config(char *name)
241 {
242 struct config *conf;
243
244 if (!name)
245 name = config_name;
246
247 cli_msg(-2, "Reading configuration from %s", name);
248 if (!unix_read_config(&conf, name))
249 {
250 if (conf->err_msg)
251 cli_msg(8002, "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
252 else
253 cli_msg(8002, "%s: %m", name);
254 config_free(conf);
255 conf = NULL;
256 }
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)
276 {
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);
318 }
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);
343 }
344
345 /*
346 * Command-Line Interface
347 */
348
349 static sock *cli_sk;
350 static char *path_control_socket = PATH_CONTROL_SOCKET;
351
352
353 static void
354 cli_write(cli *c)
355 {
356 sock *s = c->priv;
357
358 while (c->tx_pos)
359 {
360 struct cli_out *o = c->tx_pos;
361
362 int len = o->wpos - o->outpos;
363 s->tbuf = o->outpos;
364 o->outpos = o->wpos;
365
366 if (sk_send(s, len) <= 0)
367 return;
368
369 c->tx_pos = o->next;
370 }
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);
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
422 cli_rx(sock *s, int size UNUSED)
423 {
424 cli_kick(s->data);
425 return 0;
426 }
427
428 static void
429 cli_err(sock *s, int err)
430 {
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 }
438 cli_free(s->data);
439 }
440
441 static int
442 cli_connect(sock *s, int size UNUSED)
443 {
444 cli *c;
445
446 if (config->cli_debug)
447 log(L_INFO "CLI connect");
448 s->rx_hook = cli_rx;
449 s->tx_hook = cli_tx;
450 s->err_hook = cli_err;
451 s->data = c = cli_new(s);
452 s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
453 s->fast_rx = 1;
454 c->rx_pos = c->rx_buf;
455 c->rx_aux = NULL;
456 rmove(s, c->pool);
457 return 1;
458 }
459
460 static void
461 cli_init_unix(uid_t use_uid, gid_t use_gid)
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;
469 s->rbsize = 1024;
470 s->fast_rx = 1;
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);
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");
484 }
485
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
538 /*
539 * Shutdown
540 */
541
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
552 void
553 async_shutdown(void)
554 {
555 DBG("Shutting down...\n");
556 order_shutdown();
557 }
558
559 void
560 sysdep_shutdown_done(void)
561 {
562 unlink_pid_file();
563 unlink(path_control_socket);
564 log_msg(L_FATAL "Shutdown completed");
565 exit(0);
566 }
567
568 /*
569 * Signals
570 */
571
572 static void
573 handle_sighup(int sig UNUSED)
574 {
575 DBG("Caught SIGHUP...\n");
576 async_config_flag = 1;
577 }
578
579 static void
580 handle_sigusr(int sig UNUSED)
581 {
582 DBG("Caught SIGUSR...\n");
583 async_dump_flag = 1;
584 }
585
586 static void
587 handle_sigterm(int sig UNUSED)
588 {
589 DBG("Caught SIGTERM...\n");
590 async_shutdown_flag = 1;
591 }
592
593 void watchdog_sigalrm(int sig UNUSED);
594
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);
607 sa.sa_handler = handle_sigterm;
608 sa.sa_flags = SA_RESTART;
609 sigaction(SIGTERM, &sa, NULL);
610 sa.sa_handler = watchdog_sigalrm;
611 sa.sa_flags = 0;
612 sigaction(SIGALRM, &sa, NULL);
613 signal(SIGPIPE, SIG_IGN);
614 }
615
616 /*
617 * Parsing of command-line arguments
618 */
619
620 static char *opt_list = "c:dD:ps:P:u:g:flRh";
621 static int parse_and_exit;
622 char *bird_name;
623 static char *use_user;
624 static char *use_group;
625 static int run_in_foreground = 0;
626
627 static void
628 display_usage(void)
629 {
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);
665 }
666
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 }
680
681 static inline uid_t
682 get_uid(const char *s)
683 {
684 struct passwd *pw;
685 char *endptr;
686 long int rv;
687
688 if (!s)
689 return 0;
690
691 errno = 0;
692 rv = strtol(s, &endptr, 10);
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;
709 long int rv;
710
711 if (!s)
712 return 0;
713
714 errno = 0;
715 rv = strtol(s, &endptr, 10);
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
727 static void
728 parse_args(int argc, char **argv)
729 {
730 int config_changed = 0;
731 int socket_changed = 0;
732 int c;
733
734 bird_name = get_bird_name(argv[0], "bird");
735 if (argc == 2)
736 {
737 if (!strcmp(argv[1], "--version"))
738 display_version();
739 if (!strcmp(argv[1], "--help"))
740 display_help();
741 }
742 while ((c = getopt(argc, argv, opt_list)) >= 0)
743 switch (c)
744 {
745 case 'c':
746 config_name = optarg;
747 config_changed = 1;
748 break;
749 case 'd':
750 debug_flag |= 1;
751 break;
752 case 'D':
753 log_init_debug(optarg);
754 debug_flag |= 2;
755 break;
756 case 'p':
757 parse_and_exit = 1;
758 break;
759 case 's':
760 path_control_socket = optarg;
761 socket_changed = 1;
762 break;
763 case 'P':
764 pid_file = optarg;
765 break;
766 case 'u':
767 use_user = optarg;
768 break;
769 case 'g':
770 use_group = optarg;
771 break;
772 case 'f':
773 run_in_foreground = 1;
774 break;
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;
781 case 'R':
782 graceful_restart_recovery();
783 break;
784 case 'h':
785 display_help();
786 break;
787 default:
788 fputc('\n', stderr);
789 display_usage();
790 exit(1);
791 }
792 if (optind < argc)
793 {
794 display_usage();
795 exit(1);
796 }
797 }
798
799 /*
800 * Hic Est main()
801 */
802
803 int
804 main(int argc, char **argv)
805 {
806 #ifdef HAVE_LIBDMALLOC
807 if (!getenv("DMALLOC_OPTIONS"))
808 dmalloc_debug(0x2f03d00);
809 #endif
810
811 parse_args(argc, argv);
812 if (debug_flag == 1)
813 log_init_debug("");
814 log_switch(debug_flag, NULL, NULL);
815
816 resource_init();
817 olock_init();
818 io_init();
819 rt_init();
820 if_init();
821 roa_init();
822 config_init();
823
824 uid_t use_uid = get_uid(use_user);
825 gid_t use_gid = get_gid(use_group);
826
827 if (!parse_and_exit)
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);
838
839 if (!parse_and_exit)
840 open_pid_file();
841
842 protos_build();
843 proto_build(&proto_unix_kernel);
844 proto_build(&proto_unix_iface);
845
846 struct config *conf = read_config();
847
848 if (parse_and_exit)
849 exit(0);
850
851 if (!(debug_flag||run_in_foreground))
852 {
853 pid_t pid = fork();
854 if (pid < 0)
855 die("fork: %m");
856 if (pid)
857 return 0;
858 setsid();
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);
864 }
865
866 main_thread_init();
867
868 write_pid_file();
869
870 signal_init();
871
872 config_commit(conf, RECONFIG_HARD, 0);
873
874 graceful_restart_init();
875
876 #ifdef LOCAL_DEBUG
877 async_dump_flag = 1;
878 #endif
879
880 log(L_INFO "Started");
881 DBG("Entering I/O loop.\n");
882
883 io_loop();
884 bug("I/O loop died");
885 }