]> git.proxmox.com Git - mirror_frr.git/blame - lib/libfrr.c
tools: remove watchfrr -A/-a/-z from init scripts
[mirror_frr.git] / lib / libfrr.c
CommitLineData
4f04a76b
DL
1/*
2 * libfrr overall management functions
3 *
4 * Copyright (C) 2016 David Lamparter for NetDEF, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
896014f4
DL
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4f04a76b
DL
19 */
20
21#include <zebra.h>
22
f43fbf83
DL
23#include <sys/types.h>
24#include <sys/wait.h>
25
4f04a76b
DL
26#include "libfrr.h"
27#include "getopt.h"
beaa5470 28#include "privs.h"
4f04a76b
DL
29#include "vty.h"
30#include "command.h"
31#include "version.h"
857b5446 32#include "memory_vty.h"
eb05883f 33#include "zclient.h"
cf7466ac 34#include "log_int.h"
30771d65 35#include "module.h"
f43fbf83 36#include "network.h"
eb05883f 37
d62a17ae 38DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
a5b38c5b 39
eb05883f
DL
40const char frr_sysconfdir[] = SYSCONFDIR;
41const char frr_vtydir[] = DAEMON_VTY_DIR;
80b4df3b 42const char frr_moduledir[] = MODULE_PATH;
eb05883f 43
4f138a3e
DL
44char frr_protoname[256] = "NONE";
45char frr_protonameinst[256] = "NONE";
b85120bc 46
eb05883f
DL
47char config_default[256];
48static char pidfile_default[256];
49static char vtypath_default[256];
4f04a76b
DL
50
51static char comb_optstr[256];
52static struct option comb_lo[64];
53static struct option *comb_next_lo = &comb_lo[0];
54static char comb_helpstr[4096];
55
56struct optspec {
57 const char *optstr;
58 const char *helpstr;
59 const struct option *longopts;
60};
61
62static void opt_extend(const struct optspec *os)
63{
64 const struct option *lo;
65
66 strcat(comb_optstr, os->optstr);
67 strcat(comb_helpstr, os->helpstr);
68 for (lo = os->longopts; lo->name; lo++)
69 memcpy(comb_next_lo++, lo, sizeof(*lo));
70}
71
72
80b4df3b
MW
73#define OPTION_VTYSOCK 1000
74#define OPTION_MODULEDIR 1002
4f04a76b
DL
75
76static const struct option lo_always[] = {
d62a17ae 77 {"help", no_argument, NULL, 'h'},
78 {"version", no_argument, NULL, 'v'},
79 {"daemon", no_argument, NULL, 'd'},
80 {"module", no_argument, NULL, 'M'},
81 {"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
82 {"moduledir", required_argument, NULL, OPTION_MODULEDIR},
83 {NULL}};
4f04a76b 84static const struct optspec os_always = {
30771d65 85 "hvdM:",
4f04a76b
DL
86 " -h, --help Display this help and exit\n"
87 " -v, --version Print program version\n"
eb05883f 88 " -d, --daemon Runs in daemon mode\n"
30771d65 89 " -M, --module Load specified module\n"
80b4df3b
MW
90 " --vty_socket Override vty socket path\n"
91 " --moduledir Override modules directory\n",
d62a17ae 92 lo_always};
4f04a76b
DL
93
94
eb05883f 95static const struct option lo_cfg_pid_dry[] = {
d62a17ae 96 {"pid_file", required_argument, NULL, 'i'},
97 {"config_file", required_argument, NULL, 'f'},
98 {"dryrun", no_argument, NULL, 'C'},
cff2b211 99 {"terminal", no_argument, NULL, 't'},
d62a17ae 100 {NULL}};
eb05883f 101static const struct optspec os_cfg_pid_dry = {
cff2b211 102 "f:i:Ct",
eb05883f
DL
103 " -f, --config_file Set configuration file name\n"
104 " -i, --pid_file Set process identifier file name\n"
cff2b211
DL
105 " -C, --dryrun Check configuration for validity and exit\n"
106 " -t, --terminal Open terminal session on stdio\n"
107 " -d -t Daemonize after terminal session ends\n",
d62a17ae 108 lo_cfg_pid_dry};
eb05883f
DL
109
110
111static const struct option lo_zclient[] = {
d62a17ae 112 {"socket", required_argument, NULL, 'z'},
113 {NULL}};
eb05883f 114static const struct optspec os_zclient = {
d62a17ae 115 "z:", " -z, --socket Set path of zebra socket\n", lo_zclient};
eb05883f
DL
116
117
4f04a76b 118static const struct option lo_vty[] = {
d62a17ae 119 {"vty_addr", required_argument, NULL, 'A'},
120 {"vty_port", required_argument, NULL, 'P'},
121 {NULL}};
4f04a76b
DL
122static const struct optspec os_vty = {
123 "A:P:",
124 " -A, --vty_addr Set vty's bind address\n"
125 " -P, --vty_port Set vty's port number\n",
d62a17ae 126 lo_vty};
4f04a76b
DL
127
128
d62a17ae 129static const struct option lo_user[] = {{"user", required_argument, NULL, 'u'},
130 {"group", required_argument, NULL, 'g'},
131 {NULL}};
132static const struct optspec os_user = {"u:g:",
133 " -u, --user User to run as\n"
134 " -g, --group Group to run as\n",
135 lo_user};
4f04a76b
DL
136
137
138static struct frr_daemon_info *di = NULL;
139
140void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
141{
142 di = daemon;
143
144 /* basename(), opencoded. */
145 char *p = strrchr(argv[0], '/');
146 di->progname = p ? p + 1 : argv[0];
147
148 umask(0027);
149
150 opt_extend(&os_always);
eb05883f
DL
151 if (!(di->flags & FRR_NO_CFG_PID_DRY))
152 opt_extend(&os_cfg_pid_dry);
4f04a76b
DL
153 if (!(di->flags & FRR_NO_PRIVSEP))
154 opt_extend(&os_user);
eb05883f
DL
155 if (!(di->flags & FRR_NO_ZCLIENT))
156 opt_extend(&os_zclient);
4f04a76b
DL
157 if (!(di->flags & FRR_NO_TCPVTY))
158 opt_extend(&os_vty);
eb05883f
DL
159
160 snprintf(config_default, sizeof(config_default), "%s/%s.conf",
d62a17ae 161 frr_sysconfdir, di->name);
eb05883f 162 snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
d62a17ae 163 frr_vtydir, di->name);
b85120bc
DL
164
165 strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
166 strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
4f04a76b
DL
167}
168
169void frr_opt_add(const char *optstr, const struct option *longopts,
d62a17ae 170 const char *helpstr)
4f04a76b 171{
d62a17ae 172 const struct optspec main_opts = {optstr, helpstr, longopts};
4f04a76b
DL
173 opt_extend(&main_opts);
174}
175
176void frr_help_exit(int status)
177{
178 FILE *target = status ? stderr : stdout;
179
180 if (status != 0)
181 fprintf(stderr, "Invalid options.\n\n");
182
183 if (di->printhelp)
184 di->printhelp(target);
185 else
186 fprintf(target, "Usage: %s [OPTION...]\n\n%s%s%s\n\n%s",
d62a17ae 187 di->progname, di->proghelp, di->copyright ? "\n\n" : "",
188 di->copyright ? di->copyright : "", comb_helpstr);
4f04a76b
DL
189 fprintf(target, "\nReport bugs to %s\n", FRR_BUG_ADDRESS);
190 exit(status);
191}
192
30771d65
DL
193struct option_chain {
194 struct option_chain *next;
195 const char *arg;
196};
80b4df3b 197
30771d65 198static struct option_chain *modules = NULL, **modnext = &modules;
4f04a76b
DL
199static int errors = 0;
200
201static int frr_opt(int opt)
202{
203 static int vty_port_set = 0;
204 static int vty_addr_set = 0;
30771d65 205 struct option_chain *oc;
4f04a76b
DL
206 char *err;
207
208 switch (opt) {
209 case 'h':
210 frr_help_exit(0);
211 break;
212 case 'v':
213 print_version(di->progname);
214 exit(0);
215 break;
eb05883f
DL
216 case 'd':
217 di->daemon_mode = 1;
218 break;
30771d65
DL
219 case 'M':
220 oc = XMALLOC(MTYPE_TMP, sizeof(*oc));
221 oc->arg = optarg;
222 oc->next = NULL;
223 *modnext = oc;
224 modnext = &oc->next;
225 break;
eb05883f
DL
226 case 'i':
227 if (di->flags & FRR_NO_CFG_PID_DRY)
228 return 1;
229 di->pid_file = optarg;
230 break;
231 case 'f':
232 if (di->flags & FRR_NO_CFG_PID_DRY)
233 return 1;
234 di->config_file = optarg;
235 break;
236 case 'C':
237 if (di->flags & FRR_NO_CFG_PID_DRY)
238 return 1;
239 di->dryrun = 1;
240 break;
cff2b211
DL
241 case 't':
242 if (di->flags & FRR_NO_CFG_PID_DRY)
243 return 1;
244 di->terminal = 1;
245 break;
eb05883f
DL
246 case 'z':
247 if (di->flags & FRR_NO_ZCLIENT)
248 return 1;
249 zclient_serv_path_set(optarg);
250 break;
4f04a76b
DL
251 case 'A':
252 if (di->flags & FRR_NO_TCPVTY)
253 return 1;
254 if (vty_addr_set) {
d62a17ae 255 fprintf(stderr,
256 "-A option specified more than once!\n");
4f04a76b
DL
257 errors++;
258 break;
259 }
260 vty_addr_set = 1;
261 di->vty_addr = optarg;
262 break;
263 case 'P':
264 if (di->flags & FRR_NO_TCPVTY)
265 return 1;
266 if (vty_port_set) {
d62a17ae 267 fprintf(stderr,
268 "-P option specified more than once!\n");
4f04a76b
DL
269 errors++;
270 break;
271 }
272 vty_port_set = 1;
273 di->vty_port = strtoul(optarg, &err, 0);
274 if (*err || !*optarg) {
d62a17ae 275 fprintf(stderr,
276 "invalid port number \"%s\" for -P option\n",
277 optarg);
4f04a76b
DL
278 errors++;
279 break;
280 }
281 break;
282 case OPTION_VTYSOCK:
283 if (di->vty_sock_path) {
d62a17ae 284 fprintf(stderr,
285 "--vty_socket option specified more than once!\n");
4f04a76b
DL
286 errors++;
287 break;
288 }
289 di->vty_sock_path = optarg;
290 break;
80b4df3b
MW
291 case OPTION_MODULEDIR:
292 if (di->module_path) {
d62a17ae 293 fprintf(stderr,
294 "----moduledir option specified more than once!\n");
80b4df3b
MW
295 errors++;
296 break;
297 }
298 di->module_path = optarg;
299 break;
4f04a76b
DL
300 case 'u':
301 if (di->flags & FRR_NO_PRIVSEP)
302 return 1;
303 di->privs->user = optarg;
304 break;
305 case 'g':
306 if (di->flags & FRR_NO_PRIVSEP)
307 return 1;
308 di->privs->group = optarg;
309 break;
310 default:
311 return 1;
312 }
313 return 0;
314}
315
d62a17ae 316int frr_getopt(int argc, char *const argv[], int *longindex)
4f04a76b
DL
317{
318 int opt;
319 int lidx;
320
321 comb_next_lo->name = NULL;
322
323 do {
324 opt = getopt_long(argc, argv, comb_optstr, comb_lo, &lidx);
325 if (frr_opt(opt))
326 break;
327 } while (opt != -1);
328
329 if (opt == -1 && errors)
330 frr_help_exit(1);
331 if (longindex)
332 *longindex = lidx;
333 return opt;
334}
335
beaa5470
DL
336static void frr_mkdir(const char *path, bool strip)
337{
338 char buf[256];
339 mode_t prev;
340 int ret;
341 struct zprivs_ids_t ids;
342
343 if (strip) {
344 char *slash = strrchr(path, '/');
345 size_t plen;
346 if (!slash)
347 return;
348 plen = slash - path;
349 if (plen > sizeof(buf) - 1)
350 return;
351 memcpy(buf, path, plen);
352 buf[plen] = '\0';
353 path = buf;
354 }
355
356 /* o+rx (..5) is needed for the frrvty group to work properly;
357 * without it, users in the frrvty group can't access the vty sockets.
358 */
359 prev = umask(0022);
360 ret = mkdir(path, 0755);
361 umask(prev);
362
363 if (ret != 0) {
364 /* if EEXIST, return without touching the permissions,
365 * so user-set custom permissions are left in place
366 */
367 if (errno == EEXIST)
368 return;
369
370 zlog_warn("failed to mkdir \"%s\": %s", path, strerror(errno));
371 return;
372 }
373
374 zprivs_get_ids(&ids);
375 if (chown(path, ids.uid_normal, ids.gid_normal))
376 zlog_warn("failed to chown \"%s\": %s", path, strerror(errno));
377}
378
a5b38c5b 379static struct thread_master *master;
4f04a76b
DL
380struct thread_master *frr_init(void)
381{
30771d65
DL
382 struct option_chain *oc;
383 struct frrmod_runtime *module;
384 char moderr[256];
80b4df3b
MW
385 const char *dir;
386 dir = di->module_path ? di->module_path : frr_moduledir;
4f04a76b
DL
387
388 srandom(time(NULL));
389
b85120bc 390 if (di->instance)
d62a17ae 391 snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
392 di->logname, di->instance);
b85120bc 393
37a1f2fb
DL
394 zprivs_preinit(di->privs);
395
d62a17ae 396 openzlog(di->progname, di->logname, di->instance,
397 LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
4f04a76b 398#if defined(HAVE_CUMULUS)
d62a17ae 399 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4f04a76b
DL
400#endif
401
beaa5470
DL
402 if (!di->pid_file || !di->vty_path)
403 frr_mkdir(frr_vtydir, false);
404 if (di->pid_file)
405 frr_mkdir(di->pid_file, true);
406 if (di->vty_path)
407 frr_mkdir(di->vty_path, true);
408
30771d65
DL
409 frrmod_init(di->module);
410 while (modules) {
411 modules = (oc = modules)->next;
80b4df3b 412 module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr));
30771d65
DL
413 if (!module) {
414 fprintf(stderr, "%s\n", moderr);
415 exit(1);
416 }
417 XFREE(MTYPE_TMP, oc);
418 }
419
4f04a76b
DL
420 zprivs_init(di->privs);
421
972a411c 422 master = thread_master_create(NULL);
4f04a76b
DL
423 signal_init(master, di->n_signals, di->signals);
424
857b5446
DL
425 if (di->flags & FRR_LIMITED_CLI)
426 cmd_init(-1);
427 else
428 cmd_init(1);
429 vty_init(master);
430 memory_init();
431
4f04a76b
DL
432 return master;
433}
434
154b9e8f
DL
435static int rcvd_signal = 0;
436
437static void rcv_signal(int signum)
438{
439 rcvd_signal = signum;
440 /* poll() is interrupted by the signal; handled below */
441}
442
f43fbf83
DL
443static void frr_daemon_wait(int fd)
444{
445 struct pollfd pfd[1];
446 int ret;
447 pid_t exitpid;
448 int exitstat;
154b9e8f
DL
449 sigset_t sigs, prevsigs;
450
451 sigemptyset(&sigs);
452 sigaddset(&sigs, SIGTSTP);
453 sigaddset(&sigs, SIGQUIT);
454 sigaddset(&sigs, SIGINT);
455 sigprocmask(SIG_BLOCK, &sigs, &prevsigs);
456
457 struct sigaction sa = {
458 .sa_handler = rcv_signal, .sa_flags = SA_RESETHAND,
459 };
460 sigemptyset(&sa.sa_mask);
461 sigaction(SIGTSTP, &sa, NULL);
462 sigaction(SIGQUIT, &sa, NULL);
463 sigaction(SIGINT, &sa, NULL);
f43fbf83
DL
464
465 do {
154b9e8f
DL
466 char buf[1];
467 ssize_t nrecv;
468
f43fbf83
DL
469 pfd[0].fd = fd;
470 pfd[0].events = POLLIN;
471
154b9e8f
DL
472 rcvd_signal = 0;
473
474#if defined(HAVE_PPOLL)
475 ret = ppoll(pfd, 1, NULL, &prevsigs);
476#elif defined(HAVE_POLLTS)
477 ret = pollts(pfd, 1, NULL, &prevsigs);
478#else
479 /* racy -- only used on FreeBSD 9 */
480 sigset_t tmpsigs;
481 sigprocmask(SIG_SETMASK, &prevsigs, &tmpsigs);
f43fbf83 482 ret = poll(pfd, 1, -1);
154b9e8f
DL
483 sigprocmask(SIG_SETMASK, &tmpsigs, NULL);
484#endif
f43fbf83
DL
485 if (ret < 0 && errno != EINTR && errno != EAGAIN) {
486 perror("poll()");
487 exit(1);
488 }
154b9e8f
DL
489 switch (rcvd_signal) {
490 case SIGTSTP:
491 send(fd, "S", 1, 0);
492 do {
493 nrecv = recv(fd, buf, sizeof(buf), 0);
494 } while (nrecv == -1
495 && (errno == EINTR || errno == EAGAIN));
496
497 raise(SIGTSTP);
498 sigaction(SIGTSTP, &sa, NULL);
499 send(fd, "R", 1, 0);
500 break;
501 case SIGINT:
502 send(fd, "I", 1, 0);
503 break;
504 case SIGQUIT:
505 send(fd, "Q", 1, 0);
506 break;
507 }
f43fbf83
DL
508 } while (ret <= 0);
509
510 exitpid = waitpid(-1, &exitstat, WNOHANG);
511 if (exitpid == 0)
512 /* child successfully went to main loop & closed socket */
513 exit(0);
514
515 /* child failed one way or another ... */
516 if (WIFEXITED(exitstat))
517 fprintf(stderr, "%s failed to start, exited %d\n", di->name,
518 WEXITSTATUS(exitstat));
519 else if (WIFSIGNALED(exitstat))
520 fprintf(stderr, "%s crashed in startup, signal %d\n", di->name,
521 WTERMSIG(exitstat));
522 else
523 fprintf(stderr, "%s failed to start, unknown problem\n",
524 di->name);
525 exit(1);
526}
527
528static int daemon_ctl_sock = -1;
529
530static void frr_daemonize(void)
531{
532 int fds[2];
533 pid_t pid;
534
535 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
536 perror("socketpair() for daemon control");
537 exit(1);
538 }
539 set_cloexec(fds[0]);
540 set_cloexec(fds[1]);
541
542 pid = fork();
543 if (pid < 0) {
544 perror("fork()");
545 exit(1);
546 }
547 if (pid == 0) {
548 /* child */
549 close(fds[0]);
550 if (setsid() < 0) {
551 perror("setsid()");
552 exit(1);
553 }
554
555 daemon_ctl_sock = fds[1];
556 return;
557 }
558
559 close(fds[1]);
560 frr_daemon_wait(fds[0]);
561}
562
eb05883f 563void frr_config_fork(void)
4f04a76b 564{
a5b38c5b
DL
565 hook_call(frr_late_init, master);
566
eb05883f 567 if (di->instance) {
d62a17ae 568 snprintf(config_default, sizeof(config_default),
569 "%s/%s-%d.conf", frr_sysconfdir, di->name,
570 di->instance);
571 snprintf(pidfile_default, sizeof(pidfile_default),
572 "%s/%s-%d.pid", frr_vtydir, di->name, di->instance);
eb05883f
DL
573 }
574
575 vty_read_config(di->config_file, config_default);
576
577 /* Don't start execution if we are in dry-run mode */
578 if (di->dryrun)
579 exit(0);
580
154b9e8f 581 if (di->daemon_mode || di->terminal)
f43fbf83 582 frr_daemonize();
eb05883f
DL
583
584 if (!di->pid_file)
585 di->pid_file = pidfile_default;
d62a17ae 586 pid_output(di->pid_file);
eb05883f
DL
587}
588
589void frr_vty_serv(void)
590{
d62a17ae 591 /* allow explicit override of vty_path in the future
eb05883f
DL
592 * (not currently set anywhere) */
593 if (!di->vty_path) {
594 const char *dir;
595 dir = di->vty_sock_path ? di->vty_sock_path : frr_vtydir;
596
597 if (di->instance)
598 snprintf(vtypath_default, sizeof(vtypath_default),
d62a17ae 599 "%s/%s-%d.vty", dir, di->name, di->instance);
eb05883f
DL
600 else
601 snprintf(vtypath_default, sizeof(vtypath_default),
d62a17ae 602 "%s/%s.vty", dir, di->name);
eb05883f
DL
603
604 di->vty_path = vtypath_default;
605 }
606
607 vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
4f04a76b
DL
608}
609
154b9e8f 610static void frr_terminal_close(int isexit)
cff2b211 611{
154b9e8f
DL
612 if (daemon_ctl_sock != -1) {
613 close(daemon_ctl_sock);
614 daemon_ctl_sock = -1;
615 }
616
617 if (!di->daemon_mode || isexit) {
cff2b211 618 printf("\n%s exiting\n", di->name);
154b9e8f
DL
619 if (!isexit)
620 raise(SIGINT);
621 return;
cff2b211
DL
622 } else {
623 printf("\n%s daemonizing\n", di->name);
624 fflush(stdout);
625 }
626
627 int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
628 dup2(nullfd, 0);
629 dup2(nullfd, 1);
630 dup2(nullfd, 2);
631 close(nullfd);
154b9e8f 632}
cff2b211 633
154b9e8f
DL
634static struct thread *daemon_ctl_thread = NULL;
635
636static int frr_daemon_ctl(struct thread *t)
637{
638 char buf[1];
639 ssize_t nr;
640
641 nr = recv(daemon_ctl_sock, buf, sizeof(buf), 0);
642 if (nr < 0 && (errno == EINTR || errno == EAGAIN))
643 goto out;
644 if (nr <= 0)
645 return 0;
646
647 switch (buf[0]) {
648 case 'S': /* SIGTSTP */
649 vty_stdio_suspend();
650 send(daemon_ctl_sock, "s", 1, 0);
651 break;
652 case 'R': /* SIGTCNT [implicit] */
653 vty_stdio_resume();
654 break;
655 case 'I': /* SIGINT */
656 di->daemon_mode = false;
657 raise(SIGINT);
658 break;
659 case 'Q': /* SIGQUIT */
660 di->daemon_mode = true;
661 vty_stdio_close();
662 break;
cff2b211 663 }
154b9e8f
DL
664
665out:
666 thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock,
667 &daemon_ctl_thread);
668 return 0;
cff2b211
DL
669}
670
16077f2f
DL
671void frr_run(struct thread_master *master)
672{
673 char instanceinfo[64] = "";
674
675 frr_vty_serv();
676
677 if (di->instance)
678 snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
d62a17ae 679 di->instance);
680
681 zlog_notice("%s %s starting: %svty@%d%s", di->name, FRR_VERSION,
682 instanceinfo, di->vty_port, di->startinfo);
16077f2f 683
cff2b211
DL
684 if (di->terminal) {
685 vty_stdio(frr_terminal_close);
154b9e8f
DL
686 if (daemon_ctl_sock != -1) {
687 set_nonblocking(daemon_ctl_sock);
688 thread_add_read(master, frr_daemon_ctl, NULL,
689 daemon_ctl_sock, &daemon_ctl_thread);
690 }
cff2b211 691 } else if (daemon_ctl_sock != -1) {
f43fbf83
DL
692 close(daemon_ctl_sock);
693 daemon_ctl_sock = -1;
694 }
695
d34cb7f0
DL
696 /* end fixed stderr startup logging */
697 zlog_startup_stderr = false;
698
16077f2f
DL
699 struct thread thread;
700 while (thread_fetch(master, &thread))
701 thread_call(&thread);
702}