diff options
| author | David Lamparter <equinox@diac24.net> | 2021-04-19 10:33:18 +0200 | 
|---|---|---|
| committer | David Lamparter <equinox@opensourcerouting.org> | 2021-06-29 17:57:04 +0200 | 
| commit | 247898d5d27bc3bba83c56d139ecc78b23ae42b3 (patch) | |
| tree | 34cba409000a1e8f8df2e2386b5805896a677a66 | |
| parent | 32694c41bb298075c19b2cd50525bee3a336ccec (diff) | |
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 <equinox@diac24.net>
| -rw-r--r-- | lib/libfrr.c | 6 | ||||
| -rw-r--r-- | lib/systemd.c | 173 | ||||
| -rw-r--r-- | lib/systemd.h | 10 | ||||
| -rw-r--r-- | watchfrr/watchfrr.c | 10 | 
4 files changed, 123 insertions, 76 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 <zebra.h> +#include <sys/un.h>  #include "thread.h"  #include "systemd.h" +#include "lib_errors.h" -#if defined HAVE_SYSTEMD -#include <systemd/sd-daemon.h> -#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"); +} 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;  }  | 
