back to topotato report
topotato coverage report
Current view: top level - lib - libfrr.c (source / functions) Hit Total Coverage
Test: test_bgp_aggregate_address_route_map.py::BGPAggregateAddressRouteMap Lines: 315 621 50.7 %
Date: 2023-02-24 18:36:44 Functions: 26 35 74.3 %

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

Generated by: LCOV version v1.16-topotato