back to topotato report
topotato coverage report
Current view: top level - lib - libfrr.c (source / functions) Hit Total Coverage
Test: test_bgp_ecmp_enhe.py::BGP_Unnumbered_ECMP Lines: 317 634 50.0 %
Date: 2023-11-16 17:19:14 Functions: 26 72 36.1 %

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

Generated by: LCOV version v1.16-topotato