From 247898d5d27bc3bba83c56d139ecc78b23ae42b3 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 19 Apr 2021 10:33:18 +0200 Subject: 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/systemd.c | 173 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 65 deletions(-) (limited to 'lib/systemd.c') 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; + +/* 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; -/* - * Wrapper this silliness if we - * don't have systemd +/* 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; - - ret = sd_watchdog_enabled(0, &usec); - - /* - * If return is 0 -> we don't want watchdog - * if return is < 0, some sort of failure occurred - */ - if (ret < 0) - return 0; - - /* - * 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; - - if (ret == 0 && !the_process) { - watchdog = getenv("WATCHDOG_USEC"); - if (!watchdog) - return 0; - - usec = atol(watchdog); - } + if (!notify_socket) + return; + + sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) + return; + + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path)); - return (usec / 1000000) / 3; -#else - return 0; -#endif + /* linux abstract unix socket namespace */ + if (sun.sun_path[0] == '@') + sun.sun_path[0] = '\0'; + + /* nothing we can do if this errors out... */ + sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun, + sizeof(sun)); + + 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"); +} -- cgit v1.2.3