]> git.proxmox.com Git - mirror_frr.git/blob - lib/libfrr.c
Merge pull request #3077 from rgirada/ospf_2980_fix
[mirror_frr.git] / lib / libfrr.c
1 /*
2 * libfrr overall management functions
3 *
4 * Copyright (C) 2016 David Lamparter for NetDEF, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22 #include <sys/un.h>
23
24 #include <sys/types.h>
25 #include <sys/wait.h>
26
27 #include "libfrr.h"
28 #include "getopt.h"
29 #include "privs.h"
30 #include "vty.h"
31 #include "command.h"
32 #include "version.h"
33 #include "memory_vty.h"
34 #include "zclient.h"
35 #include "log_int.h"
36 #include "module.h"
37 #include "network.h"
38 #include "lib_errors.h"
39
40 DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
41 DEFINE_KOOH(frr_early_fini, (), ())
42 DEFINE_KOOH(frr_fini, (), ())
43
44 const char frr_sysconfdir[] = SYSCONFDIR;
45 const char frr_vtydir[] = DAEMON_VTY_DIR;
46 const char frr_moduledir[] = MODULE_PATH;
47
48 char frr_protoname[256] = "NONE";
49 char frr_protonameinst[256] = "NONE";
50
51 char config_default[512];
52 char frr_zclientpath[256];
53 static char pidfile_default[512];
54 static char vtypath_default[256];
55
56 bool debug_memstats_at_exit = 0;
57 static bool nodetach_term, nodetach_daemon;
58
59 static char comb_optstr[256];
60 static struct option comb_lo[64];
61 static struct option *comb_next_lo = &comb_lo[0];
62 static char comb_helpstr[4096];
63
64 struct optspec {
65 const char *optstr;
66 const char *helpstr;
67 const struct option *longopts;
68 };
69
70 static void opt_extend(const struct optspec *os)
71 {
72 const struct option *lo;
73
74 strcat(comb_optstr, os->optstr);
75 strcat(comb_helpstr, os->helpstr);
76 for (lo = os->longopts; lo->name; lo++)
77 memcpy(comb_next_lo++, lo, sizeof(*lo));
78 }
79
80
81 #define OPTION_VTYSOCK 1000
82 #define OPTION_MODULEDIR 1002
83 #define OPTION_LOG 1003
84 #define OPTION_LOGLEVEL 1004
85
86 static const struct option lo_always[] = {
87 {"help", no_argument, NULL, 'h'},
88 {"version", no_argument, NULL, 'v'},
89 {"daemon", no_argument, NULL, 'd'},
90 {"module", no_argument, NULL, 'M'},
91 {"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
92 {"moduledir", required_argument, NULL, OPTION_MODULEDIR},
93 {"log", required_argument, NULL, OPTION_LOG},
94 {"log-level", required_argument, NULL, OPTION_LOGLEVEL},
95 {NULL}};
96 static const struct optspec os_always = {
97 "hvdM:",
98 " -h, --help Display this help and exit\n"
99 " -v, --version Print program version\n"
100 " -d, --daemon Runs in daemon mode\n"
101 " -M, --module Load specified module\n"
102 " --vty_socket Override vty socket path\n"
103 " --moduledir Override modules directory\n"
104 " --log Set Logging to stdout, syslog, or file:<name>\n"
105 " --log-level Set Logging Level to use, debug, info, warn, etc\n",
106 lo_always};
107
108
109 static const struct option lo_cfg_pid_dry[] = {
110 {"pid_file", required_argument, NULL, 'i'},
111 {"config_file", required_argument, NULL, 'f'},
112 {"pathspace", required_argument, NULL, 'N'},
113 {"dryrun", no_argument, NULL, 'C'},
114 {"terminal", no_argument, NULL, 't'},
115 {NULL}};
116 static const struct optspec os_cfg_pid_dry = {
117 "f:i:CtN:",
118 " -f, --config_file Set configuration file name\n"
119 " -i, --pid_file Set process identifier file name\n"
120 " -N, --pathspace Insert prefix into config & socket paths\n"
121 " -C, --dryrun Check configuration for validity and exit\n"
122 " -t, --terminal Open terminal session on stdio\n"
123 " -d -t Daemonize after terminal session ends\n",
124 lo_cfg_pid_dry};
125
126
127 static const struct option lo_zclient[] = {
128 {"socket", required_argument, NULL, 'z'},
129 {NULL}};
130 static const struct optspec os_zclient = {
131 "z:", " -z, --socket Set path of zebra socket\n", lo_zclient};
132
133
134 static const struct option lo_vty[] = {
135 {"vty_addr", required_argument, NULL, 'A'},
136 {"vty_port", required_argument, NULL, 'P'},
137 {NULL}};
138 static const struct optspec os_vty = {
139 "A:P:",
140 " -A, --vty_addr Set vty's bind address\n"
141 " -P, --vty_port Set vty's port number\n",
142 lo_vty};
143
144
145 static const struct option lo_user[] = {{"user", required_argument, NULL, 'u'},
146 {"group", required_argument, NULL, 'g'},
147 {NULL}};
148 static const struct optspec os_user = {"u:g:",
149 " -u, --user User to run as\n"
150 " -g, --group Group to run as\n",
151 lo_user};
152
153
154 bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
155 const char *path)
156 {
157 memset(sa, 0, sizeof(*sa));
158
159 if (!path)
160 path = ZEBRA_SERV_PATH;
161
162 if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) {
163 /* note: this functionality is disabled at bottom */
164 int af;
165 int port = ZEBRA_PORT;
166 char *err = NULL;
167 struct sockaddr_in *sin = NULL;
168 struct sockaddr_in6 *sin6 = NULL;
169
170 path += strlen(ZAPI_TCP_PATHNAME);
171
172 switch (path[0]) {
173 case '4':
174 path++;
175 af = AF_INET;
176 break;
177 case '6':
178 path++;
179 /* fallthrough */
180 default:
181 af = AF_INET6;
182 break;
183 }
184
185 switch (path[0]) {
186 case '\0':
187 break;
188 case ':':
189 path++;
190 port = strtoul(path, &err, 10);
191 if (*err || !*path)
192 return false;
193 break;
194 default:
195 return false;
196 }
197
198 sa->ss_family = af;
199 switch (af) {
200 case AF_INET:
201 sin = (struct sockaddr_in *)sa;
202 sin->sin_port = htons(port);
203 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
204 *sa_len = sizeof(struct sockaddr_in);
205 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
206 sin->sin_len = *sa_len;
207 #endif
208 break;
209 case AF_INET6:
210 sin6 = (struct sockaddr_in6 *)sa;
211 sin6->sin6_port = htons(port);
212 inet_pton(AF_INET6, "::1", &sin6->sin6_addr);
213 *sa_len = sizeof(struct sockaddr_in6);
214 #ifdef SIN6_LEN
215 sin6->sin6_len = *sa_len;
216 #endif
217 break;
218 }
219
220 #if 1
221 /* force-disable this path, because tcp-zebra is a
222 * SECURITY ISSUE. there are no checks at all against
223 * untrusted users on the local system connecting on TCP
224 * and injecting bogus routing data into the entire routing
225 * domain.
226 *
227 * The functionality is only left here because it may be
228 * useful during development, in order to be able to get
229 * tcpdump or wireshark watching ZAPI as TCP. If you want
230 * to do that, flip the #if 1 above to #if 0. */
231 memset(sa, 0, sizeof(*sa));
232 return false;
233 #endif
234 } else {
235 /* "sun" is a #define on solaris */
236 struct sockaddr_un *suna = (struct sockaddr_un *)sa;
237
238 suna->sun_family = AF_UNIX;
239 strlcpy(suna->sun_path, path, sizeof(suna->sun_path));
240 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
241 *sa_len = suna->sun_len = SUN_LEN(suna);
242 #else
243 *sa_len = sizeof(suna->sun_family) + strlen(suna->sun_path);
244 #endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
245 #if 0
246 /* this is left here for future reference; Linux abstract
247 * socket namespace support can be enabled by replacing
248 * above #if 0 with #ifdef GNU_LINUX.
249 *
250 * THIS IS A SECURITY ISSUE, the abstract socket namespace
251 * does not have user/group permission control on sockets.
252 * we'd need to implement SCM_CREDENTIALS support first to
253 * check that only proper users can connect to abstract
254 * sockets. (same problem as tcp-zebra, except there is a
255 * fix with SCM_CREDENTIALS. tcp-zebra has no such fix.)
256 */
257 if (suna->sun_path[0] == '@')
258 suna->sun_path[0] = '\0';
259 #endif
260 }
261 return true;
262 }
263
264 static struct frr_daemon_info *di = NULL;
265
266 void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
267 {
268 di = daemon;
269
270 /* basename(), opencoded. */
271 char *p = strrchr(argv[0], '/');
272 di->progname = p ? p + 1 : argv[0];
273
274 umask(0027);
275
276 opt_extend(&os_always);
277 if (!(di->flags & FRR_NO_CFG_PID_DRY))
278 opt_extend(&os_cfg_pid_dry);
279 if (!(di->flags & FRR_NO_PRIVSEP))
280 opt_extend(&os_user);
281 if (!(di->flags & FRR_NO_ZCLIENT))
282 opt_extend(&os_zclient);
283 if (!(di->flags & FRR_NO_TCPVTY))
284 opt_extend(&os_vty);
285 if (di->flags & FRR_DETACH_LATER)
286 nodetach_daemon = true;
287
288 snprintf(config_default, sizeof(config_default), "%s/%s.conf",
289 frr_sysconfdir, di->name);
290 snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
291 frr_vtydir, di->name);
292
293 strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
294 strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
295
296 strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath));
297 }
298
299 void frr_opt_add(const char *optstr, const struct option *longopts,
300 const char *helpstr)
301 {
302 const struct optspec main_opts = {optstr, helpstr, longopts};
303 opt_extend(&main_opts);
304 }
305
306 void frr_help_exit(int status)
307 {
308 FILE *target = status ? stderr : stdout;
309
310 if (status != 0)
311 fprintf(stderr, "Invalid options.\n\n");
312
313 if (di->printhelp)
314 di->printhelp(target);
315 else
316 fprintf(target, "Usage: %s [OPTION...]\n\n%s%s%s\n\n%s",
317 di->progname, di->proghelp, di->copyright ? "\n\n" : "",
318 di->copyright ? di->copyright : "", comb_helpstr);
319 fprintf(target, "\nReport bugs to %s\n", FRR_BUG_ADDRESS);
320 exit(status);
321 }
322
323 struct option_chain {
324 struct option_chain *next;
325 const char *arg;
326 };
327
328 static struct option_chain *modules = NULL, **modnext = &modules;
329 static int errors = 0;
330
331 static int frr_opt(int opt)
332 {
333 static int vty_port_set = 0;
334 static int vty_addr_set = 0;
335 struct option_chain *oc;
336 char *err;
337
338 switch (opt) {
339 case 'h':
340 frr_help_exit(0);
341 break;
342 case 'v':
343 print_version(di->progname);
344 exit(0);
345 break;
346 case 'd':
347 di->daemon_mode = 1;
348 break;
349 case 'M':
350 oc = XMALLOC(MTYPE_TMP, sizeof(*oc));
351 oc->arg = optarg;
352 oc->next = NULL;
353 *modnext = oc;
354 modnext = &oc->next;
355 break;
356 case 'i':
357 if (di->flags & FRR_NO_CFG_PID_DRY)
358 return 1;
359 di->pid_file = optarg;
360 break;
361 case 'f':
362 if (di->flags & FRR_NO_CFG_PID_DRY)
363 return 1;
364 di->config_file = optarg;
365 break;
366 case 'N':
367 if (di->flags & FRR_NO_CFG_PID_DRY)
368 return 1;
369 if (di->pathspace) {
370 fprintf(stderr,
371 "-N/--pathspace option specified more than once!\n");
372 errors++;
373 break;
374 }
375 if (strchr(optarg, '/') || strchr(optarg, '.')) {
376 fprintf(stderr,
377 "slashes or dots are not permitted in the --pathspace option.\n");
378 errors++;
379 break;
380 }
381 di->pathspace = optarg;
382 break;
383 case 'C':
384 if (di->flags & FRR_NO_CFG_PID_DRY)
385 return 1;
386 di->dryrun = 1;
387 break;
388 case 't':
389 if (di->flags & FRR_NO_CFG_PID_DRY)
390 return 1;
391 di->terminal = 1;
392 break;
393 case 'z':
394 if (di->flags & FRR_NO_ZCLIENT)
395 return 1;
396 strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath));
397 break;
398 case 'A':
399 if (di->flags & FRR_NO_TCPVTY)
400 return 1;
401 if (vty_addr_set) {
402 fprintf(stderr,
403 "-A option specified more than once!\n");
404 errors++;
405 break;
406 }
407 vty_addr_set = 1;
408 di->vty_addr = optarg;
409 break;
410 case 'P':
411 if (di->flags & FRR_NO_TCPVTY)
412 return 1;
413 if (vty_port_set) {
414 fprintf(stderr,
415 "-P option specified more than once!\n");
416 errors++;
417 break;
418 }
419 vty_port_set = 1;
420 di->vty_port = strtoul(optarg, &err, 0);
421 if (*err || !*optarg) {
422 fprintf(stderr,
423 "invalid port number \"%s\" for -P option\n",
424 optarg);
425 errors++;
426 break;
427 }
428 break;
429 case OPTION_VTYSOCK:
430 if (di->vty_sock_path) {
431 fprintf(stderr,
432 "--vty_socket option specified more than once!\n");
433 errors++;
434 break;
435 }
436 di->vty_sock_path = optarg;
437 break;
438 case OPTION_MODULEDIR:
439 if (di->module_path) {
440 fprintf(stderr,
441 "----moduledir option specified more than once!\n");
442 errors++;
443 break;
444 }
445 di->module_path = optarg;
446 break;
447 case 'u':
448 if (di->flags & FRR_NO_PRIVSEP)
449 return 1;
450 di->privs->user = optarg;
451 break;
452 case 'g':
453 if (di->flags & FRR_NO_PRIVSEP)
454 return 1;
455 di->privs->group = optarg;
456 break;
457 case OPTION_LOG:
458 di->early_logging = optarg;
459 break;
460 case OPTION_LOGLEVEL:
461 di->early_loglevel = optarg;
462 break;
463 default:
464 return 1;
465 }
466 return 0;
467 }
468
469 int frr_getopt(int argc, char *const argv[], int *longindex)
470 {
471 int opt;
472 int lidx;
473
474 comb_next_lo->name = NULL;
475
476 do {
477 opt = getopt_long(argc, argv, comb_optstr, comb_lo, &lidx);
478 if (frr_opt(opt))
479 break;
480 } while (opt != -1);
481
482 if (opt == -1 && errors)
483 frr_help_exit(1);
484 if (longindex)
485 *longindex = lidx;
486 return opt;
487 }
488
489 static void frr_mkdir(const char *path, bool strip)
490 {
491 char buf[256];
492 mode_t prev;
493 int ret;
494 struct zprivs_ids_t ids;
495
496 if (strip) {
497 char *slash = strrchr(path, '/');
498 size_t plen;
499 if (!slash)
500 return;
501 plen = slash - path;
502 if (plen > sizeof(buf) - 1)
503 return;
504 memcpy(buf, path, plen);
505 buf[plen] = '\0';
506 path = buf;
507 }
508
509 /* o+rx (..5) is needed for the frrvty group to work properly;
510 * without it, users in the frrvty group can't access the vty sockets.
511 */
512 prev = umask(0022);
513 ret = mkdir(path, 0755);
514 umask(prev);
515
516 if (ret != 0) {
517 /* if EEXIST, return without touching the permissions,
518 * so user-set custom permissions are left in place
519 */
520 if (errno == EEXIST)
521 return;
522
523 flog_err(EC_LIB_SYSTEM_CALL, "failed to mkdir \"%s\": %s", path,
524 strerror(errno));
525 return;
526 }
527
528 zprivs_get_ids(&ids);
529 if (chown(path, ids.uid_normal, ids.gid_normal))
530 flog_err(EC_LIB_SYSTEM_CALL, "failed to chown \"%s\": %s", path,
531 strerror(errno));
532 }
533
534 static struct thread_master *master;
535 struct thread_master *frr_init(void)
536 {
537 struct option_chain *oc;
538 struct frrmod_runtime *module;
539 char moderr[256];
540 char p_instance[16] = "", p_pathspace[256] = "";
541 const char *dir;
542 dir = di->module_path ? di->module_path : frr_moduledir;
543
544 srandom(time(NULL));
545
546 if (di->instance) {
547 snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
548 di->logname, di->instance);
549 snprintf(p_instance, sizeof(p_instance), "-%d", di->instance);
550 }
551 if (di->pathspace)
552 snprintf(p_pathspace, sizeof(p_pathspace), "%s/",
553 di->pathspace);
554
555 snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf",
556 frr_sysconfdir, p_pathspace, di->name, p_instance);
557 snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid",
558 frr_vtydir, p_pathspace, di->name, p_instance);
559
560 zprivs_preinit(di->privs);
561
562 openzlog(di->progname, di->logname, di->instance,
563 LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
564
565 command_setup_early_logging(di->early_logging, di->early_loglevel);
566
567 if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len,
568 frr_zclientpath)) {
569 fprintf(stderr, "Invalid zserv socket path: %s\n",
570 frr_zclientpath);
571 exit(1);
572 }
573
574 /* don't mkdir these as root... */
575 if (!(di->flags & FRR_NO_PRIVSEP)) {
576 if (!di->pid_file || !di->vty_path)
577 frr_mkdir(frr_vtydir, false);
578 if (di->pid_file)
579 frr_mkdir(di->pid_file, true);
580 if (di->vty_path)
581 frr_mkdir(di->vty_path, true);
582 }
583
584 frrmod_init(di->module);
585 while (modules) {
586 modules = (oc = modules)->next;
587 module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr));
588 if (!module) {
589 fprintf(stderr, "%s\n", moderr);
590 exit(1);
591 }
592 XFREE(MTYPE_TMP, oc);
593 }
594
595 zprivs_init(di->privs);
596
597 master = thread_master_create(NULL);
598 signal_init(master, di->n_signals, di->signals);
599
600 if (di->flags & FRR_LIMITED_CLI)
601 cmd_init(-1);
602 else
603 cmd_init(1);
604 vty_init(master);
605 memory_init();
606
607 log_ref_init();
608 lib_error_init();
609
610 return master;
611 }
612
613 static int rcvd_signal = 0;
614
615 static void rcv_signal(int signum)
616 {
617 rcvd_signal = signum;
618 /* poll() is interrupted by the signal; handled below */
619 }
620
621 static void frr_daemon_wait(int fd)
622 {
623 struct pollfd pfd[1];
624 int ret;
625 pid_t exitpid;
626 int exitstat;
627 sigset_t sigs, prevsigs;
628
629 sigemptyset(&sigs);
630 sigaddset(&sigs, SIGTSTP);
631 sigaddset(&sigs, SIGQUIT);
632 sigaddset(&sigs, SIGINT);
633 sigprocmask(SIG_BLOCK, &sigs, &prevsigs);
634
635 struct sigaction sa = {
636 .sa_handler = rcv_signal, .sa_flags = SA_RESETHAND,
637 };
638 sigemptyset(&sa.sa_mask);
639 sigaction(SIGTSTP, &sa, NULL);
640 sigaction(SIGQUIT, &sa, NULL);
641 sigaction(SIGINT, &sa, NULL);
642
643 do {
644 char buf[1];
645 ssize_t nrecv;
646
647 pfd[0].fd = fd;
648 pfd[0].events = POLLIN;
649
650 rcvd_signal = 0;
651
652 #if defined(HAVE_PPOLL)
653 ret = ppoll(pfd, 1, NULL, &prevsigs);
654 #elif defined(HAVE_POLLTS)
655 ret = pollts(pfd, 1, NULL, &prevsigs);
656 #else
657 /* racy -- only used on FreeBSD 9 */
658 sigset_t tmpsigs;
659 sigprocmask(SIG_SETMASK, &prevsigs, &tmpsigs);
660 ret = poll(pfd, 1, -1);
661 sigprocmask(SIG_SETMASK, &tmpsigs, NULL);
662 #endif
663 if (ret < 0 && errno != EINTR && errno != EAGAIN) {
664 perror("poll()");
665 exit(1);
666 }
667 switch (rcvd_signal) {
668 case SIGTSTP:
669 send(fd, "S", 1, 0);
670 do {
671 nrecv = recv(fd, buf, sizeof(buf), 0);
672 } while (nrecv == -1
673 && (errno == EINTR || errno == EAGAIN));
674
675 raise(SIGTSTP);
676 sigaction(SIGTSTP, &sa, NULL);
677 send(fd, "R", 1, 0);
678 break;
679 case SIGINT:
680 send(fd, "I", 1, 0);
681 break;
682 case SIGQUIT:
683 send(fd, "Q", 1, 0);
684 break;
685 }
686 } while (ret <= 0);
687
688 exitpid = waitpid(-1, &exitstat, WNOHANG);
689 if (exitpid == 0)
690 /* child successfully went to main loop & closed socket */
691 exit(0);
692
693 /* child failed one way or another ... */
694 if (WIFEXITED(exitstat) && WEXITSTATUS(exitstat) == 0)
695 /* can happen in --terminal case if exit is fast enough */
696 (void)0;
697 else if (WIFEXITED(exitstat))
698 fprintf(stderr, "%s failed to start, exited %d\n", di->name,
699 WEXITSTATUS(exitstat));
700 else if (WIFSIGNALED(exitstat))
701 fprintf(stderr, "%s crashed in startup, signal %d\n", di->name,
702 WTERMSIG(exitstat));
703 else
704 fprintf(stderr, "%s failed to start, unknown problem\n",
705 di->name);
706 exit(1);
707 }
708
709 static int daemon_ctl_sock = -1;
710
711 static void frr_daemonize(void)
712 {
713 int fds[2];
714 pid_t pid;
715
716 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
717 perror("socketpair() for daemon control");
718 exit(1);
719 }
720 set_cloexec(fds[0]);
721 set_cloexec(fds[1]);
722
723 pid = fork();
724 if (pid < 0) {
725 perror("fork()");
726 exit(1);
727 }
728 if (pid == 0) {
729 /* child */
730 close(fds[0]);
731 if (setsid() < 0) {
732 perror("setsid()");
733 exit(1);
734 }
735
736 daemon_ctl_sock = fds[1];
737 return;
738 }
739
740 close(fds[1]);
741 frr_daemon_wait(fds[0]);
742 }
743
744 /*
745 * Why is this a thread?
746 *
747 * The read in of config for integrated config happens *after*
748 * thread execution starts( because it is passed in via a vtysh -b -n )
749 * While if you are not using integrated config we want the ability
750 * to read the config in after thread execution starts, so that
751 * we can match this behavior.
752 */
753 static int frr_config_read_in(struct thread *t)
754 {
755 if (!vty_read_config(di->config_file, config_default) &&
756 di->backup_config_file) {
757 char *orig = XSTRDUP(MTYPE_TMP, host_config_get());
758
759 zlog_info("Attempting to read backup config file: %s specified",
760 di->backup_config_file);
761 vty_read_config(di->backup_config_file, config_default);
762
763 host_config_set(orig);
764 XFREE(MTYPE_TMP, orig);
765 }
766 return 0;
767 }
768
769 void frr_config_fork(void)
770 {
771 hook_call(frr_late_init, master);
772
773 if (!(di->flags & FRR_NO_CFG_PID_DRY)) {
774 /* Don't start execution if we are in dry-run mode */
775 if (di->dryrun) {
776 frr_config_read_in(NULL);
777 exit(0);
778 }
779
780 thread_add_event(master, frr_config_read_in, NULL, 0,
781 &di->read_in);
782 }
783
784 if (di->daemon_mode || di->terminal)
785 frr_daemonize();
786
787 if (!di->pid_file)
788 di->pid_file = pidfile_default;
789 pid_output(di->pid_file);
790 }
791
792 static void frr_vty_serv(void)
793 {
794 /* allow explicit override of vty_path in the future
795 * (not currently set anywhere) */
796 if (!di->vty_path) {
797 const char *dir;
798 char defvtydir[256];
799
800 snprintf(defvtydir, sizeof(defvtydir), "%s%s%s", frr_vtydir,
801 di->pathspace ? "/" : "",
802 di->pathspace ? di->pathspace : "");
803
804 dir = di->vty_sock_path ? di->vty_sock_path : defvtydir;
805
806 if (di->instance)
807 snprintf(vtypath_default, sizeof(vtypath_default),
808 "%s/%s-%d.vty", dir, di->name, di->instance);
809 else
810 snprintf(vtypath_default, sizeof(vtypath_default),
811 "%s/%s.vty", dir, di->name);
812
813 di->vty_path = vtypath_default;
814 }
815
816 vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
817 }
818
819 static void frr_check_detach(void)
820 {
821 if (nodetach_term || nodetach_daemon)
822 return;
823
824 if (daemon_ctl_sock != -1)
825 close(daemon_ctl_sock);
826 daemon_ctl_sock = -1;
827 }
828
829 static void frr_terminal_close(int isexit)
830 {
831 int nullfd;
832
833 nodetach_term = false;
834 frr_check_detach();
835
836 if (!di->daemon_mode || isexit) {
837 printf("\n%s exiting\n", di->name);
838 if (!isexit)
839 raise(SIGINT);
840 return;
841 } else {
842 printf("\n%s daemonizing\n", di->name);
843 fflush(stdout);
844 }
845
846 nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
847 if (nullfd == -1) {
848 flog_err_sys(EC_LIB_SYSTEM_CALL,
849 "%s: failed to open /dev/null: %s", __func__,
850 safe_strerror(errno));
851 } else {
852 dup2(nullfd, 0);
853 dup2(nullfd, 1);
854 dup2(nullfd, 2);
855 close(nullfd);
856 }
857 }
858
859 static struct thread *daemon_ctl_thread = NULL;
860
861 static int frr_daemon_ctl(struct thread *t)
862 {
863 char buf[1];
864 ssize_t nr;
865
866 nr = recv(daemon_ctl_sock, buf, sizeof(buf), 0);
867 if (nr < 0 && (errno == EINTR || errno == EAGAIN))
868 goto out;
869 if (nr <= 0)
870 return 0;
871
872 switch (buf[0]) {
873 case 'S': /* SIGTSTP */
874 vty_stdio_suspend();
875 if (send(daemon_ctl_sock, "s", 1, 0) < 0)
876 zlog_err("%s send(\"s\") error (SIGTSTP propagation)",
877 (di && di->name ? di->name : ""));
878 break;
879 case 'R': /* SIGTCNT [implicit] */
880 vty_stdio_resume();
881 break;
882 case 'I': /* SIGINT */
883 di->daemon_mode = false;
884 raise(SIGINT);
885 break;
886 case 'Q': /* SIGQUIT */
887 di->daemon_mode = true;
888 vty_stdio_close();
889 break;
890 }
891
892 out:
893 thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock,
894 &daemon_ctl_thread);
895 return 0;
896 }
897
898 void frr_detach(void)
899 {
900 nodetach_daemon = false;
901 frr_check_detach();
902 }
903
904 void frr_run(struct thread_master *master)
905 {
906 char instanceinfo[64] = "";
907
908 frr_vty_serv();
909
910 if (di->instance)
911 snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
912 di->instance);
913
914 zlog_notice("%s %s starting: %svty@%d%s", di->name, FRR_VERSION,
915 instanceinfo, di->vty_port, di->startinfo);
916
917 if (di->terminal) {
918 nodetach_term = true;
919
920 vty_stdio(frr_terminal_close);
921 if (daemon_ctl_sock != -1) {
922 set_nonblocking(daemon_ctl_sock);
923 thread_add_read(master, frr_daemon_ctl, NULL,
924 daemon_ctl_sock, &daemon_ctl_thread);
925 }
926 } else if (di->daemon_mode) {
927 int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
928 if (nullfd == -1) {
929 flog_err_sys(EC_LIB_SYSTEM_CALL,
930 "%s: failed to open /dev/null: %s",
931 __func__, safe_strerror(errno));
932 } else {
933 dup2(nullfd, 0);
934 dup2(nullfd, 1);
935 dup2(nullfd, 2);
936 close(nullfd);
937 }
938
939 frr_check_detach();
940 }
941
942 /* end fixed stderr startup logging */
943 zlog_startup_stderr = false;
944
945 struct thread thread;
946 while (thread_fetch(master, &thread))
947 thread_call(&thread);
948 }
949
950 void frr_early_fini(void)
951 {
952 hook_call(frr_early_fini);
953 }
954
955 void frr_fini(void)
956 {
957 FILE *fp;
958 char filename[128];
959 int have_leftovers;
960
961 hook_call(frr_fini);
962
963 /* memory_init -> nothing needed */
964 vty_terminate();
965 cmd_terminate();
966 log_ref_fini();
967 zprivs_terminate(di->privs);
968 /* signal_init -> nothing needed */
969 thread_master_free(master);
970 master = NULL;
971 closezlog();
972 /* frrmod_init -> nothing needed / hooks */
973
974 if (!debug_memstats_at_exit)
975 return;
976
977 have_leftovers = log_memstats(stderr, di->name);
978
979 /* in case we decide at runtime that we want exit-memstats for
980 * a daemon, but it has no stderr because it's daemonized
981 * (only do this if we actually have something to print though)
982 */
983 if (!have_leftovers)
984 return;
985
986 snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu",
987 di->name, (unsigned long long)getpid(),
988 (unsigned long long)time(NULL));
989
990 fp = fopen(filename, "w");
991 if (fp) {
992 log_memstats(fp, di->name);
993 fclose(fp);
994 }
995 }
996
997 #ifdef INTERP
998 static const char interp[]
999 __attribute__((section(".interp"), used)) = INTERP;
1000 #endif
1001 /*
1002 * executable entry point for libfrr.so
1003 *
1004 * note that libc initialization is skipped for this so the set of functions
1005 * that can be called is rather limited
1006 */
1007 extern void _libfrr_version(void)
1008 __attribute__((visibility("hidden"), noreturn));
1009 void _libfrr_version(void)
1010 {
1011 const char banner[] =
1012 FRR_FULL_NAME " " FRR_VERSION ".\n"
1013 FRR_COPYRIGHT GIT_INFO "\n"
1014 "configured with:\n " FRR_CONFIG_ARGS "\n";
1015 write(1, banner, sizeof(banner) - 1);
1016 _exit(0);
1017 }