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