]> git.proxmox.com Git - mirror_frr.git/blame - lib/libfrr.c
lib: daemonize more intelligently
[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'},
98 {NULL}};
eb05883f
DL
99static const struct optspec os_cfg_pid_dry = {
100 "f:i:C",
101 " -f, --config_file Set configuration file name\n"
102 " -i, --pid_file Set process identifier file name\n"
103 " -C, --dryrun Check configuration for validity and exit\n",
d62a17ae 104 lo_cfg_pid_dry};
eb05883f
DL
105
106
107static const struct option lo_zclient[] = {
d62a17ae 108 {"socket", required_argument, NULL, 'z'},
109 {NULL}};
eb05883f 110static const struct optspec os_zclient = {
d62a17ae 111 "z:", " -z, --socket Set path of zebra socket\n", lo_zclient};
eb05883f
DL
112
113
4f04a76b 114static const struct option lo_vty[] = {
d62a17ae 115 {"vty_addr", required_argument, NULL, 'A'},
116 {"vty_port", required_argument, NULL, 'P'},
117 {NULL}};
4f04a76b
DL
118static const struct optspec os_vty = {
119 "A:P:",
120 " -A, --vty_addr Set vty's bind address\n"
121 " -P, --vty_port Set vty's port number\n",
d62a17ae 122 lo_vty};
4f04a76b
DL
123
124
d62a17ae 125static const struct option lo_user[] = {{"user", required_argument, NULL, 'u'},
126 {"group", required_argument, NULL, 'g'},
127 {NULL}};
128static const struct optspec os_user = {"u:g:",
129 " -u, --user User to run as\n"
130 " -g, --group Group to run as\n",
131 lo_user};
4f04a76b
DL
132
133
134static struct frr_daemon_info *di = NULL;
135
136void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
137{
138 di = daemon;
139
140 /* basename(), opencoded. */
141 char *p = strrchr(argv[0], '/');
142 di->progname = p ? p + 1 : argv[0];
143
144 umask(0027);
145
146 opt_extend(&os_always);
eb05883f
DL
147 if (!(di->flags & FRR_NO_CFG_PID_DRY))
148 opt_extend(&os_cfg_pid_dry);
4f04a76b
DL
149 if (!(di->flags & FRR_NO_PRIVSEP))
150 opt_extend(&os_user);
eb05883f
DL
151 if (!(di->flags & FRR_NO_ZCLIENT))
152 opt_extend(&os_zclient);
4f04a76b
DL
153 if (!(di->flags & FRR_NO_TCPVTY))
154 opt_extend(&os_vty);
eb05883f
DL
155
156 snprintf(config_default, sizeof(config_default), "%s/%s.conf",
d62a17ae 157 frr_sysconfdir, di->name);
eb05883f 158 snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
d62a17ae 159 frr_vtydir, di->name);
b85120bc
DL
160
161 strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
162 strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
4f04a76b
DL
163}
164
165void frr_opt_add(const char *optstr, const struct option *longopts,
d62a17ae 166 const char *helpstr)
4f04a76b 167{
d62a17ae 168 const struct optspec main_opts = {optstr, helpstr, longopts};
4f04a76b
DL
169 opt_extend(&main_opts);
170}
171
172void frr_help_exit(int status)
173{
174 FILE *target = status ? stderr : stdout;
175
176 if (status != 0)
177 fprintf(stderr, "Invalid options.\n\n");
178
179 if (di->printhelp)
180 di->printhelp(target);
181 else
182 fprintf(target, "Usage: %s [OPTION...]\n\n%s%s%s\n\n%s",
d62a17ae 183 di->progname, di->proghelp, di->copyright ? "\n\n" : "",
184 di->copyright ? di->copyright : "", comb_helpstr);
4f04a76b
DL
185 fprintf(target, "\nReport bugs to %s\n", FRR_BUG_ADDRESS);
186 exit(status);
187}
188
30771d65
DL
189struct option_chain {
190 struct option_chain *next;
191 const char *arg;
192};
80b4df3b 193
30771d65 194static struct option_chain *modules = NULL, **modnext = &modules;
4f04a76b
DL
195static int errors = 0;
196
197static int frr_opt(int opt)
198{
199 static int vty_port_set = 0;
200 static int vty_addr_set = 0;
30771d65 201 struct option_chain *oc;
4f04a76b
DL
202 char *err;
203
204 switch (opt) {
205 case 'h':
206 frr_help_exit(0);
207 break;
208 case 'v':
209 print_version(di->progname);
210 exit(0);
211 break;
eb05883f
DL
212 case 'd':
213 di->daemon_mode = 1;
214 break;
30771d65
DL
215 case 'M':
216 oc = XMALLOC(MTYPE_TMP, sizeof(*oc));
217 oc->arg = optarg;
218 oc->next = NULL;
219 *modnext = oc;
220 modnext = &oc->next;
221 break;
eb05883f
DL
222 case 'i':
223 if (di->flags & FRR_NO_CFG_PID_DRY)
224 return 1;
225 di->pid_file = optarg;
226 break;
227 case 'f':
228 if (di->flags & FRR_NO_CFG_PID_DRY)
229 return 1;
230 di->config_file = optarg;
231 break;
232 case 'C':
233 if (di->flags & FRR_NO_CFG_PID_DRY)
234 return 1;
235 di->dryrun = 1;
236 break;
237 case 'z':
238 if (di->flags & FRR_NO_ZCLIENT)
239 return 1;
240 zclient_serv_path_set(optarg);
241 break;
4f04a76b
DL
242 case 'A':
243 if (di->flags & FRR_NO_TCPVTY)
244 return 1;
245 if (vty_addr_set) {
d62a17ae 246 fprintf(stderr,
247 "-A option specified more than once!\n");
4f04a76b
DL
248 errors++;
249 break;
250 }
251 vty_addr_set = 1;
252 di->vty_addr = optarg;
253 break;
254 case 'P':
255 if (di->flags & FRR_NO_TCPVTY)
256 return 1;
257 if (vty_port_set) {
d62a17ae 258 fprintf(stderr,
259 "-P option specified more than once!\n");
4f04a76b
DL
260 errors++;
261 break;
262 }
263 vty_port_set = 1;
264 di->vty_port = strtoul(optarg, &err, 0);
265 if (*err || !*optarg) {
d62a17ae 266 fprintf(stderr,
267 "invalid port number \"%s\" for -P option\n",
268 optarg);
4f04a76b
DL
269 errors++;
270 break;
271 }
272 break;
273 case OPTION_VTYSOCK:
274 if (di->vty_sock_path) {
d62a17ae 275 fprintf(stderr,
276 "--vty_socket option specified more than once!\n");
4f04a76b
DL
277 errors++;
278 break;
279 }
280 di->vty_sock_path = optarg;
281 break;
80b4df3b
MW
282 case OPTION_MODULEDIR:
283 if (di->module_path) {
d62a17ae 284 fprintf(stderr,
285 "----moduledir option specified more than once!\n");
80b4df3b
MW
286 errors++;
287 break;
288 }
289 di->module_path = optarg;
290 break;
4f04a76b
DL
291 case 'u':
292 if (di->flags & FRR_NO_PRIVSEP)
293 return 1;
294 di->privs->user = optarg;
295 break;
296 case 'g':
297 if (di->flags & FRR_NO_PRIVSEP)
298 return 1;
299 di->privs->group = optarg;
300 break;
301 default:
302 return 1;
303 }
304 return 0;
305}
306
d62a17ae 307int frr_getopt(int argc, char *const argv[], int *longindex)
4f04a76b
DL
308{
309 int opt;
310 int lidx;
311
312 comb_next_lo->name = NULL;
313
314 do {
315 opt = getopt_long(argc, argv, comb_optstr, comb_lo, &lidx);
316 if (frr_opt(opt))
317 break;
318 } while (opt != -1);
319
320 if (opt == -1 && errors)
321 frr_help_exit(1);
322 if (longindex)
323 *longindex = lidx;
324 return opt;
325}
326
a5b38c5b 327static struct thread_master *master;
4f04a76b
DL
328struct thread_master *frr_init(void)
329{
30771d65
DL
330 struct option_chain *oc;
331 struct frrmod_runtime *module;
332 char moderr[256];
80b4df3b
MW
333 const char *dir;
334 dir = di->module_path ? di->module_path : frr_moduledir;
4f04a76b
DL
335
336 srandom(time(NULL));
337
b85120bc 338 if (di->instance)
d62a17ae 339 snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
340 di->logname, di->instance);
b85120bc 341
d62a17ae 342 openzlog(di->progname, di->logname, di->instance,
343 LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
4f04a76b 344#if defined(HAVE_CUMULUS)
d62a17ae 345 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
4f04a76b
DL
346#endif
347
30771d65
DL
348 frrmod_init(di->module);
349 while (modules) {
350 modules = (oc = modules)->next;
80b4df3b 351 module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr));
30771d65
DL
352 if (!module) {
353 fprintf(stderr, "%s\n", moderr);
354 exit(1);
355 }
356 XFREE(MTYPE_TMP, oc);
357 }
358
4f04a76b
DL
359 zprivs_init(di->privs);
360
972a411c 361 master = thread_master_create(NULL);
4f04a76b
DL
362 signal_init(master, di->n_signals, di->signals);
363
857b5446
DL
364 if (di->flags & FRR_LIMITED_CLI)
365 cmd_init(-1);
366 else
367 cmd_init(1);
368 vty_init(master);
369 memory_init();
370
4f04a76b
DL
371 return master;
372}
373
f43fbf83
DL
374static void frr_daemon_wait(int fd)
375{
376 struct pollfd pfd[1];
377 int ret;
378 pid_t exitpid;
379 int exitstat;
380
381 do {
382 pfd[0].fd = fd;
383 pfd[0].events = POLLIN;
384
385 ret = poll(pfd, 1, -1);
386 if (ret < 0 && errno != EINTR && errno != EAGAIN) {
387 perror("poll()");
388 exit(1);
389 }
390 } while (ret <= 0);
391
392 exitpid = waitpid(-1, &exitstat, WNOHANG);
393 if (exitpid == 0)
394 /* child successfully went to main loop & closed socket */
395 exit(0);
396
397 /* child failed one way or another ... */
398 if (WIFEXITED(exitstat))
399 fprintf(stderr, "%s failed to start, exited %d\n", di->name,
400 WEXITSTATUS(exitstat));
401 else if (WIFSIGNALED(exitstat))
402 fprintf(stderr, "%s crashed in startup, signal %d\n", di->name,
403 WTERMSIG(exitstat));
404 else
405 fprintf(stderr, "%s failed to start, unknown problem\n",
406 di->name);
407 exit(1);
408}
409
410static int daemon_ctl_sock = -1;
411
412static void frr_daemonize(void)
413{
414 int fds[2];
415 pid_t pid;
416
417 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
418 perror("socketpair() for daemon control");
419 exit(1);
420 }
421 set_cloexec(fds[0]);
422 set_cloexec(fds[1]);
423
424 pid = fork();
425 if (pid < 0) {
426 perror("fork()");
427 exit(1);
428 }
429 if (pid == 0) {
430 /* child */
431 close(fds[0]);
432 if (setsid() < 0) {
433 perror("setsid()");
434 exit(1);
435 }
436
437 daemon_ctl_sock = fds[1];
438 return;
439 }
440
441 close(fds[1]);
442 frr_daemon_wait(fds[0]);
443}
444
eb05883f 445void frr_config_fork(void)
4f04a76b 446{
a5b38c5b
DL
447 hook_call(frr_late_init, master);
448
eb05883f 449 if (di->instance) {
d62a17ae 450 snprintf(config_default, sizeof(config_default),
451 "%s/%s-%d.conf", frr_sysconfdir, di->name,
452 di->instance);
453 snprintf(pidfile_default, sizeof(pidfile_default),
454 "%s/%s-%d.pid", frr_vtydir, di->name, di->instance);
eb05883f
DL
455 }
456
457 vty_read_config(di->config_file, config_default);
458
459 /* Don't start execution if we are in dry-run mode */
460 if (di->dryrun)
461 exit(0);
462
f43fbf83
DL
463 if (di->daemon_mode)
464 frr_daemonize();
eb05883f
DL
465
466 if (!di->pid_file)
467 di->pid_file = pidfile_default;
d62a17ae 468 pid_output(di->pid_file);
eb05883f
DL
469}
470
471void frr_vty_serv(void)
472{
d62a17ae 473 /* allow explicit override of vty_path in the future
eb05883f
DL
474 * (not currently set anywhere) */
475 if (!di->vty_path) {
476 const char *dir;
477 dir = di->vty_sock_path ? di->vty_sock_path : frr_vtydir;
478
479 if (di->instance)
480 snprintf(vtypath_default, sizeof(vtypath_default),
d62a17ae 481 "%s/%s-%d.vty", dir, di->name, di->instance);
eb05883f
DL
482 else
483 snprintf(vtypath_default, sizeof(vtypath_default),
d62a17ae 484 "%s/%s.vty", dir, di->name);
eb05883f
DL
485
486 di->vty_path = vtypath_default;
487 }
488
489 vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
4f04a76b
DL
490}
491
16077f2f
DL
492void frr_run(struct thread_master *master)
493{
494 char instanceinfo[64] = "";
495
496 frr_vty_serv();
497
498 if (di->instance)
499 snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
d62a17ae 500 di->instance);
501
502 zlog_notice("%s %s starting: %svty@%d%s", di->name, FRR_VERSION,
503 instanceinfo, di->vty_port, di->startinfo);
16077f2f 504
f43fbf83
DL
505 if (daemon_ctl_sock != -1) {
506 close(daemon_ctl_sock);
507 daemon_ctl_sock = -1;
508 }
509
16077f2f
DL
510 struct thread thread;
511 while (thread_fetch(master, &thread))
512 thread_call(&thread);
513}