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