Annotation of embedaddon/bird/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/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");
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: static void
575: handle_sighup(int sig UNUSED)
576: {
577: DBG("Caught SIGHUP...\n");
578: async_config_flag = 1;
579: }
580:
581: static void
582: handle_sigusr(int sig UNUSED)
583: {
584: DBG("Caught SIGUSR...\n");
585: async_dump_flag = 1;
586: }
587:
588: static void
589: handle_sigterm(int sig UNUSED)
590: {
591: DBG("Caught SIGTERM...\n");
592: async_shutdown_flag = 1;
593: }
594:
595: void watchdog_sigalrm(int sig UNUSED);
596:
597: static void
598: signal_init(void)
599: {
600: struct sigaction sa;
601:
602: bzero(&sa, sizeof(sa));
603: sa.sa_handler = handle_sigusr;
604: sa.sa_flags = SA_RESTART;
605: sigaction(SIGUSR1, &sa, NULL);
606: sa.sa_handler = handle_sighup;
607: sa.sa_flags = SA_RESTART;
608: sigaction(SIGHUP, &sa, NULL);
609: sa.sa_handler = handle_sigterm;
610: sa.sa_flags = SA_RESTART;
611: sigaction(SIGTERM, &sa, NULL);
612: sa.sa_handler = watchdog_sigalrm;
613: sa.sa_flags = 0;
614: sigaction(SIGALRM, &sa, NULL);
615: signal(SIGPIPE, SIG_IGN);
616: }
617:
618: /*
619: * Parsing of command-line arguments
620: */
621:
622: static char *opt_list = "c:dD:ps:P:u:g:flRh";
623: static int parse_and_exit;
624: char *bird_name;
625: static char *use_user;
626: static char *use_group;
627: static int run_in_foreground = 0;
628:
629: static void
630: display_usage(void)
631: {
632: fprintf(stderr, "Usage: %s [--version] [--help] [-c <config-file>] [OPTIONS]\n", bird_name);
633: }
634:
635: static void
636: display_help(void)
637: {
638: display_usage();
639:
640: fprintf(stderr,
641: "\n"
642: "Options: \n"
643: " -c <config-file> Use given configuration file instead\n"
644: " of prefix/etc/bird.conf\n"
645: " -d Enable debug messages and run bird in foreground\n"
646: " -D <debug-file> Log debug messages to given file instead of stderr\n"
647: " -f Run bird in foreground\n"
648: " -g <group> Use given group ID\n"
649: " -h, --help Display this information\n"
650: " -l Look for a configuration file and a communication socket\n"
651: " file in the current working directory\n"
652: " -p Test configuration file and exit without start\n"
653: " -P <pid-file> Create a PID file with given filename\n"
654: " -R Apply graceful restart recovery after start\n"
655: " -s <control-socket> Use given filename for a control socket\n"
656: " -u <user> Drop privileges and use given user ID\n"
657: " --version Display version of BIRD\n");
658:
659: exit(0);
660: }
661:
662: static void
663: display_version(void)
664: {
665: fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
666: exit(0);
667: }
668:
669: static inline char *
670: get_bird_name(char *s, char *def)
671: {
672: char *t;
673: if (!s)
674: return def;
675: t = strrchr(s, '/');
676: if (!t)
677: return s;
678: if (!t[1])
679: return def;
680: return t+1;
681: }
682:
683: static inline uid_t
684: get_uid(const char *s)
685: {
686: struct passwd *pw;
687: char *endptr;
688: long int rv;
689:
690: if (!s)
691: return 0;
692:
693: errno = 0;
694: rv = strtol(s, &endptr, 10);
695:
696: if (!errno && !*endptr)
697: return rv;
698:
699: pw = getpwnam(s);
700: if (!pw)
701: die("Cannot find user '%s'", s);
702:
703: return pw->pw_uid;
704: }
705:
706: static inline gid_t
707: get_gid(const char *s)
708: {
709: struct group *gr;
710: char *endptr;
711: long int rv;
712:
713: if (!s)
714: return 0;
715:
716: errno = 0;
717: rv = strtol(s, &endptr, 10);
718:
719: if (!errno && !*endptr)
720: return rv;
721:
722: gr = getgrnam(s);
723: if (!gr)
724: die("Cannot find group '%s'", s);
725:
726: return gr->gr_gid;
727: }
728:
729: static void
730: parse_args(int argc, char **argv)
731: {
732: int config_changed = 0;
733: int socket_changed = 0;
734: int c;
735:
736: bird_name = get_bird_name(argv[0], "bird");
737: if (argc == 2)
738: {
739: if (!strcmp(argv[1], "--version"))
740: display_version();
741: if (!strcmp(argv[1], "--help"))
742: display_help();
743: }
744: while ((c = getopt(argc, argv, opt_list)) >= 0)
745: switch (c)
746: {
747: case 'c':
748: config_name = optarg;
749: config_changed = 1;
750: break;
751: case 'd':
752: debug_flag |= 1;
753: break;
754: case 'D':
755: log_init_debug(optarg);
756: debug_flag |= 2;
757: break;
758: case 'p':
759: parse_and_exit = 1;
760: break;
761: case 's':
762: path_control_socket = optarg;
763: socket_changed = 1;
764: break;
765: case 'P':
766: pid_file = optarg;
767: break;
768: case 'u':
769: use_user = optarg;
770: break;
771: case 'g':
772: use_group = optarg;
773: break;
774: case 'f':
775: run_in_foreground = 1;
776: break;
777: case 'l':
778: if (!config_changed)
779: config_name = xbasename(config_name);
780: if (!socket_changed)
781: path_control_socket = xbasename(path_control_socket);
782: break;
783: case 'R':
784: graceful_restart_recovery();
785: break;
786: case 'h':
787: display_help();
788: break;
789: default:
790: fputc('\n', stderr);
791: display_usage();
792: exit(1);
793: }
794: if (optind < argc)
795: {
796: display_usage();
797: exit(1);
798: }
799: }
800:
801: /*
802: * Hic Est main()
803: */
804:
805: int
806: main(int argc, char **argv)
807: {
808: #ifdef HAVE_LIBDMALLOC
809: if (!getenv("DMALLOC_OPTIONS"))
810: dmalloc_debug(0x2f03d00);
811: #endif
812:
813: parse_args(argc, argv);
814: if (debug_flag == 1)
815: log_init_debug("");
816: log_switch(debug_flag, NULL, NULL);
817:
818: resource_init();
819: olock_init();
820: io_init();
821: rt_init();
822: if_init();
823: roa_init();
824: config_init();
825:
826: uid_t use_uid = get_uid(use_user);
827: gid_t use_gid = get_gid(use_group);
828:
829: if (!parse_and_exit)
830: {
831: test_old_bird(path_control_socket);
832: cli_init_unix(use_uid, use_gid);
833: }
834:
835: if (use_gid)
836: drop_gid(use_gid);
837:
838: if (use_uid)
839: drop_uid(use_uid);
840:
841: if (!parse_and_exit)
842: open_pid_file();
843:
844: protos_build();
845: proto_build(&proto_unix_kernel);
846: proto_build(&proto_unix_iface);
847:
848: struct config *conf = read_config();
849:
850: if (parse_and_exit)
851: exit(0);
852:
853: if (!(debug_flag||run_in_foreground))
854: {
855: pid_t pid = fork();
856: if (pid < 0)
857: die("fork: %m");
858: if (pid)
859: return 0;
860: setsid();
861: close(0);
862: if (open("/dev/null", O_RDWR) < 0)
863: die("Cannot open /dev/null: %m");
864: dup2(0, 1);
865: dup2(0, 2);
866: }
867:
868: main_thread_init();
869:
870: write_pid_file();
871:
872: signal_init();
873:
874: config_commit(conf, RECONFIG_HARD, 0);
875:
876: graceful_restart_init();
877:
878: #ifdef LOCAL_DEBUG
879: async_dump_flag = 1;
880: #endif
881:
882: log(L_INFO "Started");
883: DBG("Entering I/O loop.\n");
884:
885: io_loop();
886: bug("I/O loop died");
887: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>