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