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