Line data Source code
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 "lib/version.h"
33 : #include "lib_vty.h"
34 : #include "log_vty.h"
35 : #include "zclient.h"
36 : #include "module.h"
37 : #include "network.h"
38 : #include "lib_errors.h"
39 : #include "db.h"
40 : #include "northbound_cli.h"
41 : #include "northbound_db.h"
42 : #include "debug.h"
43 : #include "frrcu.h"
44 : #include "frr_pthread.h"
45 : #include "defaults.h"
46 : #include "frrscript.h"
47 : #include "systemd.h"
48 :
49 4 : DEFINE_HOOK(frr_early_init, (struct thread_master * tm), (tm));
50 2 : DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
51 2 : DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm));
52 2 : DEFINE_HOOK(frr_config_post, (struct thread_master * tm), (tm));
53 1 : DEFINE_KOOH(frr_early_fini, (), ());
54 4 : DEFINE_KOOH(frr_fini, (), ());
55 :
56 : const char frr_sysconfdir[] = SYSCONFDIR;
57 : char frr_vtydir[256];
58 : #ifdef HAVE_SQLITE3
59 : const char frr_dbdir[] = DAEMON_DB_DIR;
60 : #endif
61 : const char frr_moduledir[] = MODULE_PATH;
62 : const char frr_scriptdir[] = SCRIPT_PATH;
63 :
64 : char frr_protoname[256] = "NONE";
65 : char frr_protonameinst[256] = "NONE";
66 :
67 : char config_default[512];
68 : char frr_zclientpath[256];
69 : static char pidfile_default[1024];
70 : #ifdef HAVE_SQLITE3
71 : static char dbfile_default[512];
72 : #endif
73 : static char vtypath_default[512];
74 :
75 : /* cleared in frr_preinit(), then re-set after daemonizing */
76 : bool frr_is_after_fork = true;
77 : bool debug_memstats_at_exit = false;
78 : static bool nodetach_term, nodetach_daemon;
79 : static uint64_t startup_fds;
80 :
81 : static char comb_optstr[256];
82 : static struct option comb_lo[64];
83 : static struct option *comb_next_lo = &comb_lo[0];
84 : static char comb_helpstr[4096];
85 :
86 : struct optspec {
87 : const char *optstr;
88 : const char *helpstr;
89 : const struct option *longopts;
90 : };
91 :
92 15 : static void opt_extend(const struct optspec *os)
93 : {
94 15 : const struct option *lo;
95 :
96 15 : strlcat(comb_optstr, os->optstr, sizeof(comb_optstr));
97 15 : strlcat(comb_helpstr, os->helpstr, sizeof(comb_helpstr));
98 72 : for (lo = os->longopts; lo->name; lo++)
99 57 : memcpy(comb_next_lo++, lo, sizeof(*lo));
100 15 : }
101 :
102 :
103 : #define OPTION_VTYSOCK 1000
104 : #define OPTION_MODULEDIR 1002
105 : #define OPTION_LOG 1003
106 : #define OPTION_LOGLEVEL 1004
107 : #define OPTION_TCLI 1005
108 : #define OPTION_DB_FILE 1006
109 : #define OPTION_LOGGING 1007
110 : #define OPTION_LIMIT_FDS 1008
111 : #define OPTION_SCRIPTDIR 1009
112 :
113 : static const struct option lo_always[] = {
114 : {"help", no_argument, NULL, 'h'},
115 : {"version", no_argument, NULL, 'v'},
116 : {"daemon", no_argument, NULL, 'd'},
117 : {"module", no_argument, NULL, 'M'},
118 : {"profile", required_argument, NULL, 'F'},
119 : {"pathspace", required_argument, NULL, 'N'},
120 : {"vrfdefaultname", required_argument, NULL, 'o'},
121 : {"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
122 : {"moduledir", required_argument, NULL, OPTION_MODULEDIR},
123 : {"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR},
124 : {"log", required_argument, NULL, OPTION_LOG},
125 : {"log-level", required_argument, NULL, OPTION_LOGLEVEL},
126 : {"command-log-always", no_argument, NULL, OPTION_LOGGING},
127 : {"limit-fds", required_argument, NULL, OPTION_LIMIT_FDS},
128 : {NULL}};
129 : static const struct optspec os_always = {
130 : "hvdM:F:N:o:",
131 : " -h, --help Display this help and exit\n"
132 : " -v, --version Print program version\n"
133 : " -d, --daemon Runs in daemon mode\n"
134 : " -M, --module Load specified module\n"
135 : " -F, --profile Use specified configuration profile\n"
136 : " -N, --pathspace Insert prefix into config & socket paths\n"
137 : " -o, --vrfdefaultname Set default VRF name.\n"
138 : " --vty_socket Override vty socket path\n"
139 : " --moduledir Override modules directory\n"
140 : " --scriptdir Override scripts directory\n"
141 : " --log Set Logging to stdout, syslog, or file:<name>\n"
142 : " --log-level Set Logging Level to use, debug, info, warn, etc\n"
143 : " --limit-fds Limit number of fds supported\n",
144 : lo_always};
145 :
146 :
147 : static const struct option lo_cfg[] = {
148 : {"config_file", required_argument, NULL, 'f'},
149 : {"dryrun", no_argument, NULL, 'C'},
150 : {NULL}};
151 : static const struct optspec os_cfg = {
152 : "f:C",
153 : " -f, --config_file Set configuration file name\n"
154 : " -C, --dryrun Check configuration for validity and exit\n",
155 : lo_cfg};
156 :
157 :
158 : static const struct option lo_fullcli[] = {
159 : {"terminal", no_argument, NULL, 't'},
160 : {"tcli", no_argument, NULL, OPTION_TCLI},
161 : #ifdef HAVE_SQLITE3
162 : {"db_file", required_argument, NULL, OPTION_DB_FILE},
163 : #endif
164 : {NULL}};
165 : static const struct optspec os_fullcli = {
166 : "t",
167 : " --tcli Use transaction-based CLI\n"
168 : " -t, --terminal Open terminal session on stdio\n"
169 : " -d -t Daemonize after terminal session ends\n",
170 : lo_fullcli};
171 :
172 :
173 : static const struct option lo_pid[] = {
174 : {"pid_file", required_argument, NULL, 'i'},
175 : {NULL}};
176 : static const struct optspec os_pid = {
177 : "i:",
178 : " -i, --pid_file Set process identifier file name\n",
179 : lo_pid};
180 :
181 :
182 : static const struct option lo_zclient[] = {
183 : {"socket", required_argument, NULL, 'z'},
184 : {NULL}};
185 : static const struct optspec os_zclient = {
186 : "z:", " -z, --socket Set path of zebra socket\n", lo_zclient};
187 :
188 :
189 : static const struct option lo_vty[] = {
190 : {"vty_addr", required_argument, NULL, 'A'},
191 : {"vty_port", required_argument, NULL, 'P'},
192 : {NULL}};
193 : static const struct optspec os_vty = {
194 : "A:P:",
195 : " -A, --vty_addr Set vty's bind address\n"
196 : " -P, --vty_port Set vty's port number\n",
197 : lo_vty};
198 :
199 :
200 : static const struct option lo_user[] = {{"user", required_argument, NULL, 'u'},
201 : {"group", required_argument, NULL, 'g'},
202 : {NULL}};
203 : static const struct optspec os_user = {"u:g:",
204 : " -u, --user User to run as\n"
205 : " -g, --group Group to run as\n",
206 : lo_user};
207 :
208 3 : bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
209 : const char *path)
210 : {
211 3 : memset(sa, 0, sizeof(*sa));
212 :
213 3 : if (!path)
214 1 : path = frr_zclientpath;
215 :
216 3 : if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) {
217 : /* note: this functionality is disabled at bottom */
218 0 : int af;
219 0 : int port = ZEBRA_PORT;
220 0 : char *err = NULL;
221 0 : struct sockaddr_in *sin = NULL;
222 0 : struct sockaddr_in6 *sin6 = NULL;
223 :
224 0 : path += strlen(ZAPI_TCP_PATHNAME);
225 :
226 0 : switch (path[0]) {
227 0 : case '4':
228 0 : path++;
229 0 : af = AF_INET;
230 0 : break;
231 0 : case '6':
232 0 : path++;
233 : /* fallthrough */
234 : default:
235 : af = AF_INET6;
236 : break;
237 : }
238 :
239 0 : switch (path[0]) {
240 : case '\0':
241 : break;
242 0 : case ':':
243 0 : path++;
244 0 : port = strtoul(path, &err, 10);
245 0 : if (*err || !*path)
246 : return false;
247 : break;
248 : default:
249 : return false;
250 : }
251 :
252 0 : sa->ss_family = af;
253 0 : switch (af) {
254 0 : case AF_INET:
255 0 : sin = (struct sockaddr_in *)sa;
256 0 : sin->sin_port = htons(port);
257 0 : sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
258 0 : *sa_len = sizeof(struct sockaddr_in);
259 : #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
260 : sin->sin_len = *sa_len;
261 : #endif
262 0 : break;
263 0 : case AF_INET6:
264 0 : sin6 = (struct sockaddr_in6 *)sa;
265 0 : sin6->sin6_port = htons(port);
266 0 : inet_pton(AF_INET6, "::1", &sin6->sin6_addr);
267 0 : *sa_len = sizeof(struct sockaddr_in6);
268 : #ifdef SIN6_LEN
269 : sin6->sin6_len = *sa_len;
270 : #endif
271 0 : break;
272 : }
273 :
274 : #if 1
275 : /* force-disable this path, because tcp-zebra is a
276 : * SECURITY ISSUE. there are no checks at all against
277 : * untrusted users on the local system connecting on TCP
278 : * and injecting bogus routing data into the entire routing
279 : * domain.
280 : *
281 : * The functionality is only left here because it may be
282 : * useful during development, in order to be able to get
283 : * tcpdump or wireshark watching ZAPI as TCP. If you want
284 : * to do that, flip the #if 1 above to #if 0. */
285 0 : memset(sa, 0, sizeof(*sa));
286 0 : return false;
287 : #endif
288 : } else {
289 : /* "sun" is a #define on solaris */
290 3 : struct sockaddr_un *suna = (struct sockaddr_un *)sa;
291 :
292 3 : suna->sun_family = AF_UNIX;
293 3 : strlcpy(suna->sun_path, path, sizeof(suna->sun_path));
294 : #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
295 : *sa_len = suna->sun_len = SUN_LEN(suna);
296 : #else
297 3 : *sa_len = sizeof(suna->sun_family) + strlen(suna->sun_path);
298 : #endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
299 : #if 0
300 : /* this is left here for future reference; Linux abstract
301 : * socket namespace support can be enabled by replacing
302 : * above #if 0 with #ifdef GNU_LINUX.
303 : *
304 : * THIS IS A SECURITY ISSUE, the abstract socket namespace
305 : * does not have user/group permission control on sockets.
306 : * we'd need to implement SCM_CREDENTIALS support first to
307 : * check that only proper users can connect to abstract
308 : * sockets. (same problem as tcp-zebra, except there is a
309 : * fix with SCM_CREDENTIALS. tcp-zebra has no such fix.)
310 : */
311 : if (suna->sun_path[0] == '@')
312 : suna->sun_path[0] = '\0';
313 : #endif
314 : }
315 3 : return true;
316 : }
317 :
318 : static struct frr_daemon_info *di = NULL;
319 :
320 3 : void frr_init_vtydir(void)
321 : {
322 3 : snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", "");
323 3 : }
324 :
325 2 : void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
326 : {
327 2 : di = daemon;
328 2 : frr_is_after_fork = false;
329 :
330 : /* basename(), opencoded. */
331 2 : char *p = strrchr(argv[0], '/');
332 2 : di->progname = p ? p + 1 : argv[0];
333 :
334 2 : umask(0027);
335 :
336 2 : log_args_init(daemon->early_logging);
337 :
338 2 : opt_extend(&os_always);
339 2 : if (!(di->flags & FRR_NO_SPLIT_CONFIG))
340 2 : opt_extend(&os_cfg);
341 2 : if (!(di->flags & FRR_LIMITED_CLI))
342 2 : opt_extend(&os_fullcli);
343 2 : if (!(di->flags & FRR_NO_PID))
344 2 : opt_extend(&os_pid);
345 2 : if (!(di->flags & FRR_NO_PRIVSEP))
346 2 : opt_extend(&os_user);
347 2 : if (!(di->flags & FRR_NO_ZCLIENT))
348 1 : opt_extend(&os_zclient);
349 2 : if (!(di->flags & FRR_NO_TCPVTY))
350 2 : opt_extend(&os_vty);
351 2 : if (di->flags & FRR_DETACH_LATER)
352 0 : nodetach_daemon = true;
353 :
354 2 : frr_init_vtydir();
355 2 : snprintf(config_default, sizeof(config_default), "%s/%s.conf",
356 2 : frr_sysconfdir, di->name);
357 2 : snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
358 2 : frr_vtydir, di->name);
359 2 : snprintf(frr_zclientpath, sizeof(frr_zclientpath),
360 : ZEBRA_SERV_PATH, "", "");
361 : #ifdef HAVE_SQLITE3
362 : snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db",
363 : frr_dbdir, di->name);
364 : #endif
365 :
366 2 : strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
367 2 : strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
368 :
369 2 : di->cli_mode = FRR_CLI_CLASSIC;
370 :
371 : /* we may be starting with extra FDs open for whatever purpose,
372 : * e.g. logging, some module, etc. Recording them here allows later
373 : * checking whether an fd is valid for such extension purposes,
374 : * without this we could end up e.g. logging to a BGP session fd.
375 : */
376 2 : startup_fds = 0;
377 130 : for (int i = 0; i < 64; i++) {
378 128 : struct stat st;
379 :
380 128 : if (fstat(i, &st))
381 120 : continue;
382 8 : if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode))
383 0 : continue;
384 :
385 8 : startup_fds |= UINT64_C(0x1) << (uint64_t)i;
386 : }
387 :
388 : /* note this doesn't do anything, it just grabs state, so doing it
389 : * early in _preinit is perfect.
390 : */
391 2 : systemd_init_env();
392 2 : }
393 :
394 0 : bool frr_is_startup_fd(int fd)
395 : {
396 0 : return !!(startup_fds & (UINT64_C(0x1) << (uint64_t)fd));
397 : }
398 :
399 2 : void frr_opt_add(const char *optstr, const struct option *longopts,
400 : const char *helpstr)
401 : {
402 2 : const struct optspec main_opts = {optstr, helpstr, longopts};
403 2 : opt_extend(&main_opts);
404 2 : }
405 :
406 0 : void frr_help_exit(int status)
407 : {
408 0 : FILE *target = status ? stderr : stdout;
409 :
410 0 : if (status != 0)
411 0 : fprintf(stderr, "Invalid options.\n\n");
412 :
413 0 : if (di->printhelp)
414 0 : di->printhelp(target);
415 : else
416 0 : fprintf(target, "Usage: %s [OPTION...]\n\n%s%s%s\n\n%s",
417 : di->progname, di->proghelp, di->copyright ? "\n\n" : "",
418 0 : di->copyright ? di->copyright : "", comb_helpstr);
419 0 : fprintf(target, "\nReport bugs to %s\n", FRR_BUG_ADDRESS);
420 0 : exit(status);
421 : }
422 :
423 : struct option_chain {
424 : struct option_chain *next;
425 : const char *arg;
426 : };
427 :
428 : static struct option_chain *modules = NULL, **modnext = &modules;
429 : static int errors = 0;
430 :
431 15 : static int frr_opt(int opt)
432 : {
433 15 : static int vty_port_set = 0;
434 15 : static int vty_addr_set = 0;
435 15 : struct option_chain *oc;
436 15 : struct log_arg *log_arg;
437 15 : size_t arg_len;
438 15 : char *err;
439 :
440 15 : switch (opt) {
441 0 : case 'h':
442 0 : frr_help_exit(0);
443 0 : case 'v':
444 0 : print_version(di->progname);
445 0 : exit(0);
446 2 : break;
447 2 : case 'd':
448 2 : di->daemon_mode = true;
449 2 : break;
450 0 : case 'M':
451 0 : oc = XMALLOC(MTYPE_TMP, sizeof(*oc));
452 0 : oc->arg = optarg;
453 0 : oc->next = NULL;
454 0 : *modnext = oc;
455 0 : modnext = &oc->next;
456 0 : break;
457 0 : case 'F':
458 0 : if (!frr_defaults_profile_valid(optarg)) {
459 0 : const char **p;
460 0 : FILE *ofd = stderr;
461 :
462 0 : if (!strcmp(optarg, "help"))
463 0 : ofd = stdout;
464 : else
465 0 : fprintf(stderr,
466 : "The \"%s\" configuration profile is not valid for this FRR version.\n",
467 : optarg);
468 :
469 0 : fprintf(ofd, "Available profiles are:\n");
470 0 : for (p = frr_defaults_profiles; *p; p++)
471 0 : fprintf(ofd, "%s%s\n",
472 0 : strcmp(*p, DFLT_NAME) ? " " : " * ",
473 : *p);
474 :
475 0 : if (ofd == stdout)
476 0 : exit(0);
477 0 : fprintf(ofd, "\n");
478 0 : errors++;
479 0 : break;
480 : }
481 0 : frr_defaults_profile_set(optarg);
482 0 : break;
483 2 : case 'i':
484 2 : if (di->flags & FRR_NO_PID)
485 : return 1;
486 2 : di->pid_file = optarg;
487 2 : break;
488 1 : case 'f':
489 1 : if (di->flags & FRR_NO_SPLIT_CONFIG)
490 : return 1;
491 1 : di->config_file = optarg;
492 1 : break;
493 0 : case 'N':
494 0 : if (di->pathspace) {
495 0 : fprintf(stderr,
496 : "-N/--pathspace option specified more than once!\n");
497 0 : errors++;
498 0 : break;
499 : }
500 0 : if (di->zpathspace)
501 0 : fprintf(stderr,
502 : "-N option overridden by -z for zebra named socket path\n");
503 :
504 0 : if (strchr(optarg, '/') || strchr(optarg, '.')) {
505 0 : fprintf(stderr,
506 : "slashes or dots are not permitted in the --pathspace option.\n");
507 0 : errors++;
508 0 : break;
509 : }
510 0 : di->pathspace = optarg;
511 :
512 0 : if (!di->zpathspace)
513 0 : snprintf(frr_zclientpath, sizeof(frr_zclientpath),
514 : ZEBRA_SERV_PATH, "/", di->pathspace);
515 0 : snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/",
516 0 : di->pathspace);
517 0 : snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
518 0 : frr_vtydir, di->name);
519 0 : break;
520 0 : case 'o':
521 0 : vrf_set_default_name(optarg);
522 0 : break;
523 : #ifdef HAVE_SQLITE3
524 : case OPTION_DB_FILE:
525 : if (di->flags & FRR_NO_PID)
526 : return 1;
527 : di->db_file = optarg;
528 : break;
529 : #endif
530 0 : case 'C':
531 0 : if (di->flags & FRR_NO_SPLIT_CONFIG)
532 : return 1;
533 0 : di->dryrun = true;
534 0 : break;
535 0 : case 't':
536 0 : if (di->flags & FRR_LIMITED_CLI)
537 : return 1;
538 0 : di->terminal = true;
539 0 : break;
540 0 : case 'z':
541 0 : di->zpathspace = true;
542 0 : if (di->pathspace)
543 0 : fprintf(stderr,
544 : "-z option overrides -N option for zebra named socket path\n");
545 0 : if (di->flags & FRR_NO_ZCLIENT)
546 : return 1;
547 0 : strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath));
548 0 : break;
549 0 : case 'A':
550 0 : if (di->flags & FRR_NO_TCPVTY)
551 : return 1;
552 0 : if (vty_addr_set) {
553 0 : fprintf(stderr,
554 : "-A option specified more than once!\n");
555 0 : errors++;
556 0 : break;
557 : }
558 0 : vty_addr_set = 1;
559 0 : di->vty_addr = optarg;
560 0 : break;
561 0 : case 'P':
562 0 : if (di->flags & FRR_NO_TCPVTY)
563 : return 1;
564 0 : if (vty_port_set) {
565 0 : fprintf(stderr,
566 : "-P option specified more than once!\n");
567 0 : errors++;
568 0 : break;
569 : }
570 0 : vty_port_set = 1;
571 0 : di->vty_port = strtoul(optarg, &err, 0);
572 0 : if (*err || !*optarg) {
573 0 : fprintf(stderr,
574 : "invalid port number \"%s\" for -P option\n",
575 : optarg);
576 0 : errors++;
577 0 : break;
578 : }
579 : break;
580 2 : case OPTION_VTYSOCK:
581 2 : if (di->vty_sock_path) {
582 0 : fprintf(stderr,
583 : "--vty_socket option specified more than once!\n");
584 0 : errors++;
585 0 : break;
586 : }
587 2 : di->vty_sock_path = optarg;
588 2 : break;
589 0 : case OPTION_MODULEDIR:
590 0 : if (di->module_path) {
591 0 : fprintf(stderr,
592 : "----moduledir option specified more than once!\n");
593 0 : errors++;
594 0 : break;
595 : }
596 0 : di->module_path = optarg;
597 0 : break;
598 0 : case OPTION_SCRIPTDIR:
599 0 : if (di->script_path) {
600 0 : fprintf(stderr, "--scriptdir option specified more than once!\n");
601 0 : errors++;
602 0 : break;
603 : }
604 0 : di->script_path = optarg;
605 0 : break;
606 0 : case OPTION_TCLI:
607 0 : di->cli_mode = FRR_CLI_TRANSACTIONAL;
608 0 : break;
609 0 : case 'u':
610 0 : if (di->flags & FRR_NO_PRIVSEP)
611 : return 1;
612 0 : di->privs->user = optarg;
613 0 : break;
614 0 : case 'g':
615 0 : if (di->flags & FRR_NO_PRIVSEP)
616 : return 1;
617 0 : di->privs->group = optarg;
618 0 : break;
619 4 : case OPTION_LOG:
620 4 : arg_len = strlen(optarg) + 1;
621 4 : log_arg = XCALLOC(MTYPE_TMP, sizeof(*log_arg) + arg_len);
622 4 : memcpy(log_arg->target, optarg, arg_len);
623 4 : log_args_add_tail(di->early_logging, log_arg);
624 : break;
625 2 : case OPTION_LOGLEVEL:
626 2 : di->early_loglevel = optarg;
627 2 : break;
628 0 : case OPTION_LOGGING:
629 0 : di->log_always = true;
630 0 : break;
631 0 : case OPTION_LIMIT_FDS:
632 0 : di->limit_fds = strtoul(optarg, &err, 0);
633 0 : break;
634 : default:
635 : return 1;
636 : }
637 : return 0;
638 : }
639 :
640 2 : int frr_getopt(int argc, char *const argv[], int *longindex)
641 : {
642 2 : int opt;
643 2 : int lidx;
644 :
645 2 : comb_next_lo->name = NULL;
646 :
647 15 : do {
648 15 : opt = getopt_long(argc, argv, comb_optstr, comb_lo, &lidx);
649 15 : if (frr_opt(opt))
650 : break;
651 13 : } while (opt != -1);
652 :
653 2 : if (opt == -1 && errors)
654 0 : frr_help_exit(1);
655 2 : if (longindex)
656 0 : *longindex = lidx;
657 2 : return opt;
658 : }
659 :
660 4 : static void frr_mkdir(const char *path, bool strip)
661 : {
662 4 : char buf[256];
663 4 : mode_t prev;
664 4 : int ret;
665 4 : struct zprivs_ids_t ids;
666 :
667 4 : if (strip) {
668 2 : char *slash = strrchr(path, '/');
669 2 : size_t plen;
670 2 : if (!slash)
671 3 : return;
672 2 : plen = slash - path;
673 2 : if (plen > sizeof(buf) - 1)
674 : return;
675 2 : memcpy(buf, path, plen);
676 2 : buf[plen] = '\0';
677 2 : path = buf;
678 : }
679 :
680 : /* o+rx (..5) is needed for the frrvty group to work properly;
681 : * without it, users in the frrvty group can't access the vty sockets.
682 : */
683 4 : prev = umask(0022);
684 4 : ret = mkdir(path, 0755);
685 4 : umask(prev);
686 :
687 4 : if (ret != 0) {
688 : /* if EEXIST, return without touching the permissions,
689 : * so user-set custom permissions are left in place
690 : */
691 3 : if (errno == EEXIST)
692 : return;
693 :
694 0 : flog_err(EC_LIB_SYSTEM_CALL, "failed to mkdir \"%s\": %s", path,
695 : strerror(errno));
696 0 : return;
697 : }
698 :
699 1 : zprivs_get_ids(&ids);
700 1 : if (chown(path, ids.uid_normal, ids.gid_normal))
701 1 : flog_err(EC_LIB_SYSTEM_CALL, "failed to chown \"%s\": %s", path,
702 : strerror(errno));
703 : }
704 :
705 0 : static void _err_print(const void *cookie, const char *errstr)
706 : {
707 0 : const char *prefix = (const char *)cookie;
708 :
709 0 : fprintf(stderr, "%s: %s\n", prefix, errstr);
710 0 : }
711 :
712 : static struct thread_master *master;
713 2 : struct thread_master *frr_init(void)
714 : {
715 2 : struct option_chain *oc;
716 2 : struct log_arg *log_arg;
717 2 : struct frrmod_runtime *module;
718 2 : struct zprivs_ids_t ids;
719 2 : char p_instance[16] = "", p_pathspace[256] = "";
720 2 : const char *dir;
721 :
722 2 : dir = di->module_path ? di->module_path : frr_moduledir;
723 :
724 2 : srandom(time(NULL));
725 2 : frr_defaults_apply();
726 :
727 2 : if (di->instance) {
728 0 : snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
729 : di->logname, di->instance);
730 0 : snprintf(p_instance, sizeof(p_instance), "-%d", di->instance);
731 : }
732 2 : if (di->pathspace)
733 0 : snprintf(p_pathspace, sizeof(p_pathspace), "%s/",
734 : di->pathspace);
735 :
736 2 : snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf",
737 2 : frr_sysconfdir, p_pathspace, di->name, p_instance);
738 2 : snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid",
739 2 : frr_vtydir, di->name, p_instance);
740 : #ifdef HAVE_SQLITE3
741 : snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db",
742 : frr_dbdir, p_pathspace, di->name, p_instance);
743 : #endif
744 :
745 2 : zprivs_preinit(di->privs);
746 2 : zprivs_get_ids(&ids);
747 :
748 2 : zlog_init(di->progname, di->logname, di->instance,
749 : ids.uid_normal, ids.gid_normal);
750 :
751 8 : while ((log_arg = log_args_pop(di->early_logging))) {
752 4 : command_setup_early_logging(log_arg->target,
753 : di->early_loglevel);
754 6 : XFREE(MTYPE_TMP, log_arg);
755 : }
756 :
757 2 : if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len,
758 : frr_zclientpath)) {
759 0 : fprintf(stderr, "Invalid zserv socket path: %s\n",
760 : frr_zclientpath);
761 0 : exit(1);
762 : }
763 :
764 : /* don't mkdir these as root... */
765 2 : if (!(di->flags & FRR_NO_PRIVSEP)) {
766 2 : if (!di->pid_file || !di->vty_path)
767 2 : frr_mkdir(frr_vtydir, false);
768 2 : if (di->pid_file)
769 2 : frr_mkdir(di->pid_file, true);
770 2 : if (di->vty_path)
771 0 : frr_mkdir(di->vty_path, true);
772 : }
773 :
774 2 : frrmod_init(di->module);
775 2 : while (modules) {
776 0 : modules = (oc = modules)->next;
777 0 : module = frrmod_load(oc->arg, dir, _err_print, __func__);
778 0 : if (!module)
779 0 : exit(1);
780 2 : XFREE(MTYPE_TMP, oc);
781 : }
782 :
783 2 : zprivs_init(di->privs);
784 :
785 2 : master = thread_master_create(NULL);
786 2 : signal_init(master, di->n_signals, di->signals);
787 2 : hook_call(frr_early_init, master);
788 :
789 : #ifdef HAVE_SQLITE3
790 : if (!di->db_file)
791 : di->db_file = dbfile_default;
792 : db_init("%s", di->db_file);
793 : #endif
794 :
795 2 : if (di->flags & FRR_LIMITED_CLI)
796 0 : cmd_init(-1);
797 : else
798 2 : cmd_init(1);
799 :
800 2 : vty_init(master, di->log_always);
801 2 : lib_cmd_init();
802 :
803 2 : frr_pthread_init();
804 : #ifdef HAVE_SCRIPTING
805 : frrscript_init(di->script_path ? di->script_path : frr_scriptdir);
806 : #endif
807 :
808 2 : log_ref_init();
809 2 : log_ref_vty_init();
810 2 : lib_error_init();
811 :
812 2 : nb_init(master, di->yang_modules, di->n_yang_modules, true);
813 2 : if (nb_db_init() != NB_OK)
814 0 : flog_warn(EC_LIB_NB_DATABASE,
815 : "%s: failed to initialize northbound database",
816 : __func__);
817 :
818 2 : debug_init_cli();
819 :
820 2 : return master;
821 : }
822 :
823 0 : const char *frr_get_progname(void)
824 : {
825 0 : return di ? di->progname : NULL;
826 : }
827 :
828 65 : enum frr_cli_mode frr_get_cli_mode(void)
829 : {
830 65 : return di ? di->cli_mode : FRR_CLI_CLASSIC;
831 : }
832 :
833 6 : uint32_t frr_get_fd_limit(void)
834 : {
835 6 : return di ? di->limit_fds : 0;
836 : }
837 :
838 : static int rcvd_signal = 0;
839 :
840 0 : static void rcv_signal(int signum)
841 : {
842 0 : rcvd_signal = signum;
843 : /* poll() is interrupted by the signal; handled below */
844 0 : }
845 :
846 2 : static void frr_daemon_wait(int fd)
847 : {
848 2 : struct pollfd pfd[1];
849 2 : int ret;
850 2 : pid_t exitpid;
851 2 : int exitstat;
852 2 : sigset_t sigs, prevsigs;
853 :
854 2 : sigemptyset(&sigs);
855 2 : sigaddset(&sigs, SIGTSTP);
856 2 : sigaddset(&sigs, SIGQUIT);
857 2 : sigaddset(&sigs, SIGINT);
858 2 : sigprocmask(SIG_BLOCK, &sigs, &prevsigs);
859 :
860 2 : struct sigaction sa = {
861 : .sa_handler = rcv_signal, .sa_flags = SA_RESETHAND,
862 : };
863 2 : sigemptyset(&sa.sa_mask);
864 2 : sigaction(SIGTSTP, &sa, NULL);
865 2 : sigaction(SIGQUIT, &sa, NULL);
866 2 : sigaction(SIGINT, &sa, NULL);
867 :
868 2 : do {
869 2 : char buf[1];
870 2 : ssize_t nrecv;
871 :
872 2 : pfd[0].fd = fd;
873 2 : pfd[0].events = POLLIN;
874 :
875 2 : rcvd_signal = 0;
876 :
877 : #if defined(HAVE_PPOLL)
878 2 : ret = ppoll(pfd, 1, NULL, &prevsigs);
879 : #elif defined(HAVE_POLLTS)
880 : ret = pollts(pfd, 1, NULL, &prevsigs);
881 : #else
882 : /* racy -- only used on FreeBSD 9 */
883 : sigset_t tmpsigs;
884 : sigprocmask(SIG_SETMASK, &prevsigs, &tmpsigs);
885 : ret = poll(pfd, 1, -1);
886 : sigprocmask(SIG_SETMASK, &tmpsigs, NULL);
887 : #endif
888 2 : if (ret < 0 && errno != EINTR && errno != EAGAIN) {
889 0 : perror("poll()");
890 0 : exit(1);
891 : }
892 2 : switch (rcvd_signal) {
893 0 : case SIGTSTP:
894 0 : send(fd, "S", 1, 0);
895 0 : do {
896 0 : nrecv = recv(fd, buf, sizeof(buf), 0);
897 : } while (nrecv == -1
898 0 : && (errno == EINTR || errno == EAGAIN));
899 :
900 0 : raise(SIGTSTP);
901 0 : sigaction(SIGTSTP, &sa, NULL);
902 0 : send(fd, "R", 1, 0);
903 0 : break;
904 0 : case SIGINT:
905 0 : send(fd, "I", 1, 0);
906 0 : break;
907 0 : case SIGQUIT:
908 0 : send(fd, "Q", 1, 0);
909 0 : break;
910 : }
911 2 : } while (ret <= 0);
912 :
913 2 : exitpid = waitpid(-1, &exitstat, WNOHANG);
914 2 : if (exitpid == 0)
915 : /* child successfully went to main loop & closed socket */
916 2 : exit(0);
917 :
918 : /* child failed one way or another ... */
919 0 : if (WIFEXITED(exitstat) && WEXITSTATUS(exitstat) == 0)
920 : /* can happen in --terminal case if exit is fast enough */
921 : (void)0;
922 0 : else if (WIFEXITED(exitstat))
923 0 : fprintf(stderr, "%s failed to start, exited %d\n", di->name,
924 0 : WEXITSTATUS(exitstat));
925 0 : else if (WIFSIGNALED(exitstat))
926 0 : fprintf(stderr, "%s crashed in startup, signal %d\n", di->name,
927 : WTERMSIG(exitstat));
928 : else
929 0 : fprintf(stderr, "%s failed to start, unknown problem\n",
930 0 : di->name);
931 0 : exit(1);
932 : }
933 :
934 : static int daemon_ctl_sock = -1;
935 :
936 2 : static void frr_daemonize(void)
937 : {
938 2 : int fds[2];
939 2 : pid_t pid;
940 :
941 2 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
942 0 : perror("socketpair() for daemon control");
943 0 : exit(1);
944 : }
945 2 : set_cloexec(fds[0]);
946 2 : set_cloexec(fds[1]);
947 :
948 2 : pid = fork();
949 4 : if (pid < 0) {
950 0 : perror("fork()");
951 0 : exit(1);
952 : }
953 4 : if (pid == 0) {
954 : /* child */
955 2 : close(fds[0]);
956 2 : if (setsid() < 0) {
957 0 : perror("setsid()");
958 0 : exit(1);
959 : }
960 :
961 2 : daemon_ctl_sock = fds[1];
962 2 : return;
963 : }
964 :
965 2 : close(fds[1]);
966 2 : nb_terminate();
967 2 : yang_terminate();
968 2 : frr_daemon_wait(fds[0]);
969 : }
970 :
971 : /*
972 : * Why is this a thread?
973 : *
974 : * The read in of config for integrated config happens *after*
975 : * thread execution starts( because it is passed in via a vtysh -b -n )
976 : * While if you are not using integrated config we want the ability
977 : * to read the config in after thread execution starts, so that
978 : * we can match this behavior.
979 : */
980 2 : static void frr_config_read_in(struct thread *t)
981 : {
982 2 : hook_call(frr_config_pre, master);
983 :
984 2 : if (!vty_read_config(vty_shared_candidate_config, di->config_file,
985 : config_default)
986 1 : && di->backup_config_file) {
987 0 : char *orig = XSTRDUP(MTYPE_TMP, host_config_get());
988 :
989 0 : zlog_info("Attempting to read backup config file: %s specified",
990 : di->backup_config_file);
991 0 : vty_read_config(vty_shared_candidate_config,
992 0 : di->backup_config_file, config_default);
993 :
994 0 : host_config_set(orig);
995 0 : XFREE(MTYPE_TMP, orig);
996 : }
997 :
998 : /*
999 : * Automatically commit the candidate configuration after
1000 : * reading the configuration file.
1001 : */
1002 2 : if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
1003 0 : struct nb_context context = {};
1004 0 : char errmsg[BUFSIZ] = {0};
1005 0 : int ret;
1006 :
1007 0 : context.client = NB_CLIENT_CLI;
1008 0 : ret = nb_candidate_commit(&context, vty_shared_candidate_config,
1009 : true, "Read configuration file", NULL,
1010 : errmsg, sizeof(errmsg));
1011 0 : if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
1012 0 : zlog_err(
1013 : "%s: failed to read configuration file: %s (%s)",
1014 : __func__, nb_err_name(ret), errmsg);
1015 : }
1016 :
1017 2 : hook_call(frr_config_post, master);
1018 2 : }
1019 :
1020 2 : void frr_config_fork(void)
1021 : {
1022 2 : hook_call(frr_late_init, master);
1023 :
1024 2 : if (!(di->flags & FRR_NO_SPLIT_CONFIG)) {
1025 : /* Don't start execution if we are in dry-run mode */
1026 2 : if (di->dryrun) {
1027 0 : frr_config_read_in(NULL);
1028 0 : exit(0);
1029 : }
1030 :
1031 2 : thread_add_event(master, frr_config_read_in, NULL, 0,
1032 : &di->read_in);
1033 : }
1034 :
1035 2 : if (di->daemon_mode || di->terminal)
1036 2 : frr_daemonize();
1037 :
1038 2 : frr_is_after_fork = true;
1039 :
1040 2 : if (!di->pid_file)
1041 0 : di->pid_file = pidfile_default;
1042 2 : pid_output(di->pid_file);
1043 2 : zlog_tls_buffer_init();
1044 2 : }
1045 :
1046 2 : static void frr_vty_serv(void)
1047 : {
1048 : /* allow explicit override of vty_path in the future
1049 : * (not currently set anywhere) */
1050 2 : if (!di->vty_path) {
1051 2 : const char *dir;
1052 2 : char defvtydir[256];
1053 :
1054 2 : snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir);
1055 :
1056 2 : dir = di->vty_sock_path ? di->vty_sock_path : defvtydir;
1057 :
1058 2 : if (di->instance)
1059 0 : snprintf(vtypath_default, sizeof(vtypath_default),
1060 : "%s/%s-%d.vty", dir, di->name, di->instance);
1061 : else
1062 2 : snprintf(vtypath_default, sizeof(vtypath_default),
1063 : "%s/%s.vty", dir, di->name);
1064 :
1065 2 : di->vty_path = vtypath_default;
1066 : }
1067 :
1068 2 : vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
1069 2 : }
1070 :
1071 2 : static void frr_check_detach(void)
1072 : {
1073 2 : if (nodetach_term || nodetach_daemon)
1074 : return;
1075 :
1076 2 : if (daemon_ctl_sock != -1)
1077 2 : close(daemon_ctl_sock);
1078 2 : daemon_ctl_sock = -1;
1079 : }
1080 :
1081 0 : static void frr_terminal_close(int isexit)
1082 : {
1083 0 : int nullfd;
1084 :
1085 0 : nodetach_term = false;
1086 0 : frr_check_detach();
1087 :
1088 0 : if (!di->daemon_mode || isexit) {
1089 0 : printf("\n%s exiting\n", di->name);
1090 0 : if (!isexit)
1091 0 : raise(SIGINT);
1092 0 : return;
1093 : } else {
1094 0 : printf("\n%s daemonizing\n", di->name);
1095 0 : fflush(stdout);
1096 : }
1097 :
1098 0 : nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
1099 0 : if (nullfd == -1) {
1100 0 : flog_err_sys(EC_LIB_SYSTEM_CALL,
1101 : "%s: failed to open /dev/null: %s", __func__,
1102 : safe_strerror(errno));
1103 : } else {
1104 0 : dup2(nullfd, 0);
1105 0 : dup2(nullfd, 1);
1106 0 : dup2(nullfd, 2);
1107 0 : close(nullfd);
1108 : }
1109 : }
1110 :
1111 : static struct thread *daemon_ctl_thread = NULL;
1112 :
1113 0 : static void frr_daemon_ctl(struct thread *t)
1114 : {
1115 0 : char buf[1];
1116 0 : ssize_t nr;
1117 :
1118 0 : nr = recv(daemon_ctl_sock, buf, sizeof(buf), 0);
1119 0 : if (nr < 0 && (errno == EINTR || errno == EAGAIN))
1120 0 : goto out;
1121 0 : if (nr <= 0)
1122 0 : return;
1123 :
1124 0 : switch (buf[0]) {
1125 0 : case 'S': /* SIGTSTP */
1126 0 : vty_stdio_suspend();
1127 0 : if (send(daemon_ctl_sock, "s", 1, 0) < 0)
1128 0 : zlog_err("%s send(\"s\") error (SIGTSTP propagation)",
1129 : (di && di->name ? di->name : ""));
1130 : break;
1131 0 : case 'R': /* SIGTCNT [implicit] */
1132 0 : vty_stdio_resume();
1133 0 : break;
1134 0 : case 'I': /* SIGINT */
1135 0 : di->daemon_mode = false;
1136 0 : raise(SIGINT);
1137 0 : break;
1138 0 : case 'Q': /* SIGQUIT */
1139 0 : di->daemon_mode = true;
1140 0 : vty_stdio_close();
1141 0 : break;
1142 : }
1143 :
1144 0 : out:
1145 0 : thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock,
1146 : &daemon_ctl_thread);
1147 : }
1148 :
1149 0 : void frr_detach(void)
1150 : {
1151 0 : nodetach_daemon = false;
1152 0 : frr_check_detach();
1153 0 : }
1154 :
1155 2 : void frr_run(struct thread_master *master)
1156 : {
1157 2 : char instanceinfo[64] = "";
1158 :
1159 2 : frr_vty_serv();
1160 :
1161 2 : if (di->instance)
1162 0 : snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
1163 : di->instance);
1164 :
1165 2 : zlog_notice("%s %s starting: %svty@%d%s", di->name, FRR_VERSION,
1166 : instanceinfo, di->vty_port, di->startinfo);
1167 :
1168 2 : if (di->terminal) {
1169 0 : nodetach_term = true;
1170 :
1171 0 : vty_stdio(frr_terminal_close);
1172 0 : if (daemon_ctl_sock != -1) {
1173 0 : set_nonblocking(daemon_ctl_sock);
1174 0 : thread_add_read(master, frr_daemon_ctl, NULL,
1175 : daemon_ctl_sock, &daemon_ctl_thread);
1176 : }
1177 2 : } else if (di->daemon_mode) {
1178 2 : int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
1179 2 : if (nullfd == -1) {
1180 0 : flog_err_sys(EC_LIB_SYSTEM_CALL,
1181 : "%s: failed to open /dev/null: %s",
1182 : __func__, safe_strerror(errno));
1183 : } else {
1184 2 : dup2(nullfd, 0);
1185 2 : dup2(nullfd, 1);
1186 2 : dup2(nullfd, 2);
1187 2 : close(nullfd);
1188 : }
1189 :
1190 2 : frr_check_detach();
1191 : }
1192 :
1193 : /* end fixed stderr startup logging */
1194 2 : zlog_startup_end();
1195 :
1196 2 : struct thread thread;
1197 199 : while (thread_fetch(master, &thread))
1198 198 : thread_call(&thread);
1199 0 : }
1200 :
1201 1 : void frr_early_fini(void)
1202 : {
1203 1 : hook_call(frr_early_fini);
1204 1 : }
1205 :
1206 2 : void frr_fini(void)
1207 : {
1208 2 : FILE *fp;
1209 2 : char filename[128];
1210 2 : int have_leftovers;
1211 :
1212 2 : hook_call(frr_fini);
1213 :
1214 2 : vty_terminate();
1215 2 : cmd_terminate();
1216 2 : nb_terminate();
1217 2 : yang_terminate();
1218 : #ifdef HAVE_SQLITE3
1219 : db_close();
1220 : #endif
1221 2 : log_ref_fini();
1222 :
1223 : #ifdef HAVE_SCRIPTING
1224 : frrscript_fini();
1225 : #endif
1226 2 : frr_pthread_finish();
1227 2 : zprivs_terminate(di->privs);
1228 : /* signal_init -> nothing needed */
1229 2 : thread_master_free(master);
1230 2 : master = NULL;
1231 2 : zlog_tls_buffer_fini();
1232 2 : zlog_fini();
1233 : /* frrmod_init -> nothing needed / hooks */
1234 2 : rcu_shutdown();
1235 :
1236 2 : if (!debug_memstats_at_exit)
1237 0 : return;
1238 :
1239 2 : have_leftovers = log_memstats(stderr, di->name);
1240 :
1241 : /* in case we decide at runtime that we want exit-memstats for
1242 : * a daemon, but it has no stderr because it's daemonized
1243 : * (only do this if we actually have something to print though)
1244 : */
1245 2 : if (!have_leftovers)
1246 : return;
1247 :
1248 6 : snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu",
1249 2 : di->name, (unsigned long long)getpid(),
1250 2 : (unsigned long long)time(NULL));
1251 :
1252 2 : fp = fopen(filename, "w");
1253 2 : if (fp) {
1254 2 : log_memstats(fp, di->name);
1255 2 : fclose(fp);
1256 : }
1257 : }
1258 :
1259 : #ifdef INTERP
1260 : static const char interp[]
1261 : __attribute__((section(".interp"), used)) = INTERP;
1262 : #endif
1263 : /*
1264 : * executable entry point for libfrr.so
1265 : *
1266 : * note that libc initialization is skipped for this so the set of functions
1267 : * that can be called is rather limited
1268 : */
1269 : extern void _libfrr_version(void)
1270 : __attribute__((visibility("hidden"), noreturn));
1271 0 : void _libfrr_version(void)
1272 : {
1273 0 : const char banner[] =
1274 : FRR_FULL_NAME " " FRR_VERSION ".\n"
1275 : FRR_COPYRIGHT GIT_INFO "\n"
1276 : "configured with:\n " FRR_CONFIG_ARGS "\n";
1277 0 : write(1, banner, sizeof(banner) - 1);
1278 0 : _exit(0);
1279 : }
|