From 247898d5d27bc3bba83c56d139ecc78b23ae42b3 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 19 Apr 2021 10:33:18 +0200 Subject: [PATCH] lib, watchfrr: remove `HAVE_SYSTEMD`, use own code This replaces the external libsystemd dependency with... pretty much the same amount of built-in code. But with one fewer dependency and build switch needed. Also check `JOURNAL_STREAM` for future logging integration. Signed-off-by: David Lamparter --- lib/libfrr.c | 6 ++ lib/systemd.c | 165 ++++++++++++++++++++++++++++---------------- lib/systemd.h | 10 +-- watchfrr/watchfrr.c | 10 +-- 4 files changed, 119 insertions(+), 72 deletions(-) diff --git a/lib/libfrr.c b/lib/libfrr.c index 0817182f7a..97dab74d9b 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -44,6 +44,7 @@ #include "frr_pthread.h" #include "defaults.h" #include "frrscript.h" +#include "systemd.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)); DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm)); @@ -363,6 +364,11 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) startup_fds |= UINT64_C(0x1) << (uint64_t)i; } + + /* note this doesn't do anything, it just grabs state, so doing it + * early in _preinit is perfect. + */ + systemd_init_env(); } bool frr_is_startup_fd(int fd) diff --git a/lib/systemd.c b/lib/systemd.c index c5cc3aa447..7944c2493b 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -20,68 +20,56 @@ */ #include +#include #include "thread.h" #include "systemd.h" +#include "lib_errors.h" -#if defined HAVE_SYSTEMD -#include -#endif +/* these are cleared from env so they don't "leak" into things we fork(), + * particularly for watchfrr starting individual daemons + * + * watchdog_pid is currently not used since watchfrr starts forking. + * (TODO: handle that better, somehow?) + */ +static pid_t watchdog_pid = -1; +static intmax_t watchdog_msec; -/* - * Wrapper this silliness if we - * don't have systemd +/* not used yet, but can trigger auto-switch to journald logging */ +bool sd_stdout_is_journal; +bool sd_stderr_is_journal; + +static char *notify_socket; + +/* talk to whatever entity claims to be systemd ;) + * + * refer to sd_notify docs for messages systemd accepts over this socket. + * This function should be functionally equivalent to sd_notify(). */ static void systemd_send_information(const char *info) { -#if defined HAVE_SYSTEMD - sd_notify(0, info); -#else - return; -#endif -} + int sock; + struct sockaddr_un sun; -/* - * A return of 0 means that we are not watchdoged - */ -static int systemd_get_watchdog_time(int the_process) -{ -#if defined HAVE_SYSTEMD - uint64_t usec; - char *watchdog = NULL; - int ret; + if (!notify_socket) + return; - ret = sd_watchdog_enabled(0, &usec); + sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) + return; - /* - * If return is 0 -> we don't want watchdog - * if return is < 0, some sort of failure occurred - */ - if (ret < 0) - return 0; + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path)); - /* - * systemd can return that this process - * is not the expected sender of the watchdog timer - * If we set the_process = 0 then we expect to - * be able to send the watchdog to systemd - * irrelevant of the pid of this process. - */ - if (ret == 0 && the_process) - return 0; + /* linux abstract unix socket namespace */ + if (sun.sun_path[0] == '@') + sun.sun_path[0] = '\0'; - if (ret == 0 && !the_process) { - watchdog = getenv("WATCHDOG_USEC"); - if (!watchdog) - return 0; + /* nothing we can do if this errors out... */ + sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun, + sizeof(sun)); - usec = atol(watchdog); - } - - return (usec / 1000000) / 3; -#else - return 0; -#endif + close(sock); } void systemd_send_stopping(void) @@ -90,34 +78,27 @@ void systemd_send_stopping(void) systemd_send_information("STOPPING=1"); } -/* - * How many seconds should we wait between watchdog sends - */ -static int wsecs = 0; static struct thread_master *systemd_master = NULL; static int systemd_send_watchdog(struct thread *t) { systemd_send_information("WATCHDOG=1"); - thread_add_timer(systemd_master, systemd_send_watchdog, NULL, wsecs, - NULL); - + assert(watchdog_msec > 0); + thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL, + watchdog_msec, NULL); return 1; } -void systemd_send_started(struct thread_master *m, int the_process) +void systemd_send_started(struct thread_master *m) { assert(m != NULL); - wsecs = systemd_get_watchdog_time(the_process); systemd_master = m; systemd_send_information("READY=1"); - if (wsecs != 0) { - systemd_send_information("WATCHDOG=1"); - thread_add_timer(m, systemd_send_watchdog, m, wsecs, NULL); - } + if (watchdog_msec > 0) + systemd_send_watchdog(NULL); } void systemd_send_status(const char *status) @@ -127,3 +108,65 @@ void systemd_send_status(const char *status) snprintf(buffer, sizeof(buffer), "STATUS=%s", status); systemd_send_information(buffer); } + +static intmax_t getenv_int(const char *varname, intmax_t dflt) +{ + char *val, *err; + intmax_t intval; + + val = getenv(varname); + if (!val) + return dflt; + + intval = strtoimax(val, &err, 0); + if (*err || !*val) + return dflt; + return intval; +} + +void systemd_init_env(void) +{ + char *tmp; + uintmax_t dev, ino; + int len; + struct stat st; + + notify_socket = getenv("NOTIFY_SOCKET"); + + /* no point in setting up watchdog w/o notify socket */ + if (notify_socket) { + intmax_t watchdog_usec; + + watchdog_pid = getenv_int("WATCHDOG_PID", -1); + if (watchdog_pid <= 0) + watchdog_pid = -1; + + /* note this is the deadline, hence the divide by 3 */ + watchdog_usec = getenv_int("WATCHDOG_USEC", 0); + if (watchdog_usec >= 3000) + watchdog_msec = watchdog_usec / 3000; + else { + if (watchdog_usec != 0) + flog_err( + EC_LIB_UNAVAILABLE, + "systemd expects a %jd microsecond watchdog timer, but FRR only supports millisecond resolution!", + watchdog_usec); + watchdog_msec = 0; + } + } + + tmp = getenv("JOURNAL_STREAM"); + if (tmp && sscanf(tmp, "%ju:%ju%n", &dev, &ino, &len) == 2 + && (size_t)len == strlen(tmp)) { + if (fstat(1, &st) == 0 && st.st_dev == (dev_t)dev + && st.st_ino == (ino_t)ino) + sd_stdout_is_journal = true; + if (fstat(2, &st) == 0 && st.st_dev == (dev_t)dev + && st.st_ino == (ino_t)ino) + sd_stderr_is_journal = true; + } + + /* these should *not* be passed to any other process we start */ + unsetenv("WATCHDOG_PID"); + unsetenv("WATCHDOG_USEC"); +} diff --git a/lib/systemd.h b/lib/systemd.h index d9885c5d9c..1933f4f688 100644 --- a/lib/systemd.h +++ b/lib/systemd.h @@ -28,9 +28,6 @@ extern "C" { * * Design point is that if systemd is not being used on this system * then these functions becomes a no-op. - * - * To turn on systemd compilation, use --enable-systemd on - * configure run. */ void systemd_send_stopping(void); @@ -39,13 +36,18 @@ void systemd_send_stopping(void); * the_process - Should we send watchdog if we are not the requested * process? */ -void systemd_send_started(struct thread_master *master, int the_process); +void systemd_send_started(struct thread_master *master); /* * status - A status string to send to systemd */ void systemd_send_status(const char *status); +/* + * grab startup state from env vars + */ +void systemd_init_env(void); + #ifdef __cplusplus } #endif diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index faf1777d7f..d0b4be81d4 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -469,12 +469,10 @@ static int run_job(struct restart_info *restart, const char *cmdtype, return -1; } -#if defined HAVE_SYSTEMD char buffer[512]; snprintf(buffer, sizeof(buffer), "restarting %s", restart->name); systemd_send_status(buffer); -#endif /* Note: time_elapsed test must come before the force test, since we need @@ -506,9 +504,8 @@ static int run_job(struct restart_info *restart, const char *cmdtype, restart->pid = 0; } -#if defined HAVE_SYSTEMD systemd_send_status("FRR Operational"); -#endif + /* Calculate the new restart interval. */ if (update_interval) { if (delay.tv_sec > 2 * gs.max_restart_interval) @@ -718,10 +715,9 @@ static void daemon_send_ready(int exitcode) fp = fopen(started, "w"); if (fp) fclose(fp); -#if defined HAVE_SYSTEMD - systemd_send_started(master, 0); + + systemd_send_started(master); systemd_send_status("FRR Operational"); -#endif sent = 1; }