]> git.ipfire.org Git - thirdparty/bird.git/blob - sysdep/unix/main.c
Unix: Remove removed option from help
[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
40 #include "unix.h"
41 #include "krt.h"
42
43 /*
44 * Debugging
45 */
46
47 void
48 async_dump(void)
49 {
50 debug("INTERNAL STATE DUMP\n\n");
51
52 rdump(&root_pool);
53 sk_dump_all();
54 // XXXX tm_dump_all();
55 if_dump_all();
56 neigh_dump_all();
57 rta_dump_all();
58 rt_dump_all();
59 protos_dump_all();
60
61 debug("\n");
62 }
63
64 /*
65 * Dropping privileges
66 */
67
68 #ifdef CONFIG_RESTRICTED_PRIVILEGES
69 #include CONFIG_INCLUDE_SYSPRIV_H
70 #else
71
72 static inline void
73 drop_uid(uid_t uid UNUSED)
74 {
75 die("Cannot change user on this platform");
76 }
77
78 #endif
79
80 static inline void
81 drop_gid(gid_t gid)
82 {
83 if (setgid(gid) < 0)
84 die("setgid: %m");
85 }
86
87 /*
88 * Reading the Configuration
89 */
90
91 #ifdef PATH_IPROUTE_DIR
92
93 static inline void
94 add_num_const(char *name, int val)
95 {
96 struct symbol *s = cf_get_symbol(name);
97 s->class = SYM_CONSTANT | T_INT;
98 s->def = cfg_allocz(sizeof(struct f_val));
99 SYM_TYPE(s) = T_INT;
100 SYM_VAL(s).i = val;
101 }
102
103 /* the code of read_iproute_table() is based on
104 rtnl_tab_initialize() from iproute2 package */
105 static void
106 read_iproute_table(char *file, char *prefix, int max)
107 {
108 char buf[512], namebuf[512];
109 char *name;
110 int val;
111 FILE *fp;
112
113 strcpy(namebuf, prefix);
114 name = namebuf + strlen(prefix);
115
116 fp = fopen(file, "r");
117 if (!fp)
118 return;
119
120 while (fgets(buf, sizeof(buf), fp))
121 {
122 char *p = buf;
123
124 while (*p == ' ' || *p == '\t')
125 p++;
126
127 if (*p == '#' || *p == '\n' || *p == 0)
128 continue;
129
130 if (sscanf(p, "0x%x %s\n", &val, name) != 2 &&
131 sscanf(p, "0x%x %s #", &val, name) != 2 &&
132 sscanf(p, "%d %s\n", &val, name) != 2 &&
133 sscanf(p, "%d %s #", &val, name) != 2)
134 continue;
135
136 if (val < 0 || val > max)
137 continue;
138
139 for(p = name; *p; p++)
140 if ((*p < 'a' || *p > 'z') && (*p < '0' || *p > '9') && (*p != '_'))
141 *p = '_';
142
143 add_num_const(namebuf, val);
144 }
145
146 fclose(fp);
147 }
148
149 #endif // PATH_IPROUTE_DIR
150
151
152 static char *config_name = PATH_CONFIG_FILE;
153
154 static int
155 cf_read(byte *dest, uint len, int fd)
156 {
157 int l = read(fd, dest, len);
158 if (l < 0)
159 cf_error("Read error");
160 return l;
161 }
162
163 void
164 sysdep_preconfig(struct config *c)
165 {
166 init_list(&c->logfiles);
167
168 c->latency_limit = UNIX_DEFAULT_LATENCY_LIMIT;
169 c->watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING;
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(0, &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:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, 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:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, 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:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, 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, uint 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, uint 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, uint 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 s->fast_rx = 1;
451 c->rx_pos = c->rx_buf;
452 c->rx_aux = NULL;
453 rmove(s, c->pool);
454 return 1;
455 }
456
457 static void
458 cli_init_unix(uid_t use_uid, gid_t use_gid)
459 {
460 sock *s;
461
462 cli_init();
463 s = cli_sk = sk_new(cli_pool);
464 s->type = SK_UNIX_PASSIVE;
465 s->rx_hook = cli_connect;
466 s->rbsize = 1024;
467 s->fast_rx = 1;
468
469 /* Return value intentionally ignored */
470 unlink(path_control_socket);
471
472 if (sk_open_unix(s, path_control_socket) < 0)
473 die("Cannot create control socket %s: %m", path_control_socket);
474
475 if (use_uid || use_gid)
476 if (chown(path_control_socket, use_uid, use_gid) < 0)
477 die("chown: %m");
478
479 if (chmod(path_control_socket, 0660) < 0)
480 die("chmod: %m");
481 }
482
483 /*
484 * PID file
485 */
486
487 static char *pid_file;
488 static int pid_fd;
489
490 static inline void
491 open_pid_file(void)
492 {
493 if (!pid_file)
494 return;
495
496 pid_fd = open(pid_file, O_WRONLY|O_CREAT, 0664);
497 if (pid_fd < 0)
498 die("Cannot create PID file %s: %m", pid_file);
499 }
500
501 static inline void
502 write_pid_file(void)
503 {
504 int pl, rv;
505 char ps[24];
506
507 if (!pid_file)
508 return;
509
510 /* We don't use PID file for uniqueness, so no need for locking */
511
512 pl = bsnprintf(ps, sizeof(ps), "%ld\n", (long) getpid());
513 if (pl < 0)
514 bug("PID buffer too small");
515
516 rv = ftruncate(pid_fd, 0);
517 if (rv < 0)
518 die("fruncate: %m");
519
520 rv = write(pid_fd, ps, pl);
521 if(rv < 0)
522 die("write: %m");
523
524 close(pid_fd);
525 }
526
527 static inline void
528 unlink_pid_file(void)
529 {
530 if (pid_file)
531 unlink(pid_file);
532 }
533
534
535 /*
536 * Shutdown
537 */
538
539 void
540 cmd_shutdown(void)
541 {
542 if (cli_access_restricted())
543 return;
544
545 cli_msg(7, "Shutdown requested");
546 order_shutdown();
547 }
548
549 void
550 async_shutdown(void)
551 {
552 DBG("Shutting down...\n");
553 order_shutdown();
554 }
555
556 void
557 sysdep_shutdown_done(void)
558 {
559 unlink_pid_file();
560 unlink(path_control_socket);
561 log_msg(L_FATAL "Shutdown completed");
562 exit(0);
563 }
564
565 /*
566 * Signals
567 */
568
569 volatile int async_config_flag;
570 volatile int async_dump_flag;
571 volatile int async_shutdown_flag;
572
573 static void
574 handle_sighup(int sig UNUSED)
575 {
576 DBG("Caught SIGHUP...\n");
577 async_config_flag = 1;
578 }
579
580 static void
581 handle_sigusr(int sig UNUSED)
582 {
583 DBG("Caught SIGUSR...\n");
584 async_dump_flag = 1;
585 }
586
587 static void
588 handle_sigterm(int sig UNUSED)
589 {
590 DBG("Caught SIGTERM...\n");
591 async_shutdown_flag = 1;
592 }
593
594 void watchdog_sigalrm(int sig UNUSED);
595
596 static void
597 signal_init(void)
598 {
599 struct sigaction sa;
600
601 bzero(&sa, sizeof(sa));
602 sa.sa_handler = handle_sigusr;
603 sa.sa_flags = SA_RESTART;
604 sigaction(SIGUSR1, &sa, NULL);
605 sa.sa_handler = handle_sighup;
606 sa.sa_flags = SA_RESTART;
607 sigaction(SIGHUP, &sa, NULL);
608 sa.sa_handler = handle_sigterm;
609 sa.sa_flags = SA_RESTART;
610 sigaction(SIGTERM, &sa, NULL);
611 sa.sa_handler = watchdog_sigalrm;
612 sa.sa_flags = 0;
613 sigaction(SIGALRM, &sa, NULL);
614 signal(SIGPIPE, SIG_IGN);
615 }
616
617 /*
618 * Parsing of command-line arguments
619 */
620
621 static char *opt_list = "bc:dD:ps:P:u:g:flRh";
622 static int parse_and_exit;
623 char *bird_name;
624 static char *use_user;
625 static char *use_group;
626 static int run_in_foreground = 0;
627
628 static void
629 display_usage(void)
630 {
631 fprintf(stderr, "Usage: %s [--version] [--help] [-c <config-file>] [OPTIONS]\n", bird_name);
632 }
633
634 static void
635 display_help(void)
636 {
637 display_usage();
638
639 fprintf(stderr,
640 "\n"
641 "Options: \n"
642 " -c <config-file> Use given configuration file instead of\n"
643 " " PATH_CONFIG_FILE "\n"
644 " -d Enable debug messages and run bird in foreground\n"
645 " -D <debug-file> Log debug messages to given file instead of stderr\n"
646 " -f Run bird in foreground\n"
647 " -g <group> Use given group ID\n"
648 " -h, --help Display this information\n"
649 " -l Look for a configuration file and a control socket\n"
650 " in the current working directory\n"
651 " -p Test configuration file and exit without start\n"
652 " -P <pid-file> Create a PID file with given filename\n"
653 " -R Apply graceful restart recovery after start\n"
654 " -s <control-socket> Use given filename for a control socket\n"
655 " -u <user> Drop privileges and use given user ID\n"
656 " --version Display version of BIRD\n");
657
658 exit(0);
659 }
660
661 static void
662 display_version(void)
663 {
664 fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
665 exit(0);
666 }
667
668 static inline char *
669 get_bird_name(char *s, char *def)
670 {
671 char *t;
672 if (!s)
673 return def;
674 t = strrchr(s, '/');
675 if (!t)
676 return s;
677 if (!t[1])
678 return def;
679 return t+1;
680 }
681
682 static inline uid_t
683 get_uid(const char *s)
684 {
685 struct passwd *pw;
686 char *endptr;
687 long int rv;
688
689 if (!s)
690 return 0;
691
692 errno = 0;
693 rv = strtol(s, &endptr, 10);
694
695 if (!errno && !*endptr)
696 return rv;
697
698 pw = getpwnam(s);
699 if (!pw)
700 die("Cannot find user '%s'", s);
701
702 return pw->pw_uid;
703 }
704
705 static inline gid_t
706 get_gid(const char *s)
707 {
708 struct group *gr;
709 char *endptr;
710 long int rv;
711
712 if (!s)
713 return 0;
714
715 errno = 0;
716 rv = strtol(s, &endptr, 10);
717
718 if (!errno && !*endptr)
719 return rv;
720
721 gr = getgrnam(s);
722 if (!gr)
723 die("Cannot find group '%s'", s);
724
725 return gr->gr_gid;
726 }
727
728 static void
729 parse_args(int argc, char **argv)
730 {
731 int config_changed = 0;
732 int socket_changed = 0;
733 int c;
734
735 bird_name = get_bird_name(argv[0], "bird");
736 if (argc == 2)
737 {
738 if (!strcmp(argv[1], "--version"))
739 display_version();
740 if (!strcmp(argv[1], "--help"))
741 display_help();
742 }
743 while ((c = getopt(argc, argv, opt_list)) >= 0)
744 switch (c)
745 {
746 case 'c':
747 config_name = optarg;
748 config_changed = 1;
749 break;
750 case 'd':
751 log_init_debug("");
752 run_in_foreground = 1;
753 break;
754 case 'D':
755 log_init_debug(optarg);
756 break;
757 case 'p':
758 parse_and_exit = 1;
759 break;
760 case 's':
761 path_control_socket = optarg;
762 socket_changed = 1;
763 break;
764 case 'P':
765 pid_file = optarg;
766 break;
767 case 'u':
768 use_user = optarg;
769 break;
770 case 'g':
771 use_group = optarg;
772 break;
773 case 'f':
774 run_in_foreground = 1;
775 break;
776 case 'l':
777 if (!config_changed)
778 config_name = xbasename(config_name);
779 if (!socket_changed)
780 path_control_socket = xbasename(path_control_socket);
781 break;
782 case 'R':
783 graceful_restart_recovery();
784 break;
785 case 'h':
786 display_help();
787 break;
788 default:
789 fputc('\n', stderr);
790 display_usage();
791 exit(1);
792 }
793 if (optind < argc)
794 {
795 display_usage();
796 exit(1);
797 }
798 }
799
800 /*
801 * Hic Est main()
802 */
803
804 int
805 main(int argc, char **argv)
806 {
807 #ifdef HAVE_LIBDMALLOC
808 if (!getenv("DMALLOC_OPTIONS"))
809 dmalloc_debug(0x2f03d00);
810 #endif
811
812 parse_args(argc, argv);
813 log_switch(1, NULL, NULL);
814
815 net_init();
816 resource_init();
817 timer_init();
818 olock_init();
819 io_init();
820 rt_init();
821 if_init();
822 // roa_init();
823 config_init();
824
825 uid_t use_uid = get_uid(use_user);
826 gid_t use_gid = get_gid(use_group);
827
828 if (!parse_and_exit)
829 {
830 test_old_bird(path_control_socket);
831 cli_init_unix(use_uid, use_gid);
832 }
833
834 if (use_gid)
835 drop_gid(use_gid);
836
837 if (use_uid)
838 drop_uid(use_uid);
839
840 if (!parse_and_exit)
841 open_pid_file();
842
843 protos_build();
844 proto_build(&proto_unix_kernel);
845 proto_build(&proto_unix_iface);
846
847 struct config *conf = read_config();
848
849 if (parse_and_exit)
850 exit(0);
851
852 if (!run_in_foreground)
853 {
854 pid_t pid = fork();
855 if (pid < 0)
856 die("fork: %m");
857 if (pid)
858 return 0;
859 setsid();
860 close(0);
861 if (open("/dev/null", O_RDWR) < 0)
862 die("Cannot open /dev/null: %m");
863 dup2(0, 1);
864 dup2(0, 2);
865 }
866
867 main_thread_init();
868
869 write_pid_file();
870
871 signal_init();
872
873 config_commit(conf, RECONFIG_HARD, 0);
874
875 graceful_restart_init();
876
877 #ifdef LOCAL_DEBUG
878 async_dump_flag = 1;
879 #endif
880
881 log(L_INFO "Started");
882 DBG("Entering I/O loop.\n");
883
884 io_loop();
885 bug("I/O loop died");
886 }