diff --git a/configure.ac b/configure.ac index c04349f35..2cf16b46a 100644 --- a/configure.ac +++ b/configure.ac @@ -915,6 +915,7 @@ int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) AC_DEFINE([USE_BTMP]) AC_DEFINE([LINUX_OOM_ADJUST], [1], [Adjust Linux out-of-memory killer]) + AC_DEFINE([SYSTEMD_NOTIFY], [1], [Have sshd notify systemd on start/reload]) inet6_default_4in6=yes case `uname -r` in 1.*|2.0.*) diff --git a/openbsd-compat/port-linux.c b/openbsd-compat/port-linux.c index 0457e28d0..df7290246 100644 --- a/openbsd-compat/port-linux.c +++ b/openbsd-compat/port-linux.c @@ -21,16 +21,23 @@ #include "includes.h" -#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) +#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) || \ + defined(SYSTEMD_NOTIFY) +#include +#include + #include +#include #include #include #include #include +#include #include "log.h" #include "xmalloc.h" #include "port-linux.h" +#include "misc.h" #ifdef WITH_SELINUX #include @@ -310,4 +317,90 @@ oom_adjust_restore(void) return; } #endif /* LINUX_OOM_ADJUST */ -#endif /* WITH_SELINUX || LINUX_OOM_ADJUST */ + +#ifdef SYSTEMD_NOTIFY + +static void ssh_systemd_notify(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) __attribute__((__nonnull__ (1))); + +static void +ssh_systemd_notify(const char *fmt, ...) +{ + char *s = NULL; + const char *path; + struct stat sb; + struct sockaddr_un addr; + int fd = -1; + va_list ap; + + if ((path = getenv("NOTIFY_SOCKET")) == NULL || strlen(path) == 0) + return; + + va_start(ap, fmt); + xvasprintf(&s, fmt, ap); + va_end(ap); + + /* Only AF_UNIX is supported, with path or abstract sockets */ + if (path[0] != '/' && path[0] != '@') { + error_f("socket \"%s\" is not compatible with AF_UNIX", path); + goto out; + } + + if (path[0] == '/' && stat(path, &sb) != 0) { + error_f("socket \"%s\" stat: %s", path, strerror(errno)); + goto out; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + if (strlcpy(addr.sun_path, path, + sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) { + error_f("socket path \"%s\" too long", path); + goto out; + } + /* Support for abstract socket */ + if (addr.sun_path[0] == '@') + addr.sun_path[0] = 0; + if ((fd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) { + error_f("socket \"%s\": %s", path, strerror(errno)); + goto out; + } + if (connect(fd, &addr, sizeof(addr)) != 0) { + error_f("socket \"%s\" connect: %s", path, strerror(errno)); + goto out; + } + if (write(fd, s, strlen(s)) != (ssize_t)strlen(s)) { + error_f("socket \"%s\" write: %s", path, strerror(errno)); + goto out; + } + debug_f("socket \"%s\" notified %s", path, s); + out: + if (fd != -1) + close(fd); + free(s); +} + +void +ssh_systemd_notify_ready(void) +{ + ssh_systemd_notify("READY=1"); +} + +void +ssh_systemd_notify_reload(void) +{ + struct timespec now; + + monotime_ts(&now); + if (now.tv_sec < 0 || now.tv_nsec < 0) { + error_f("monotime returned negative value"); + ssh_systemd_notify("RELOADING=1"); + } else { + ssh_systemd_notify("RELOADING=1\nMONOTONIC_USEC=%llu", + ((uint64_t)now.tv_sec * 1000000ULL) + + ((uint64_t)now.tv_nsec / 1000ULL)); + } +} +#endif /* SYSTEMD_NOTIFY */ + +#endif /* WITH_SELINUX || LINUX_OOM_ADJUST || SYSTEMD_NOTIFY */ diff --git a/openbsd-compat/port-linux.h b/openbsd-compat/port-linux.h index 3c22a854d..14064f87d 100644 --- a/openbsd-compat/port-linux.h +++ b/openbsd-compat/port-linux.h @@ -30,4 +30,9 @@ void oom_adjust_restore(void); void oom_adjust_setup(void); #endif +#ifdef SYSTEMD_NOTIFY +void ssh_systemd_notify_ready(void); +void ssh_systemd_notify_reload(void); +#endif + #endif /* ! _PORT_LINUX_H */ diff --git a/platform.c b/platform.c index 4fe8744ee..9cf818153 100644 --- a/platform.c +++ b/platform.c @@ -44,6 +44,14 @@ platform_pre_listen(void) #endif } +void +platform_post_listen(void) +{ +#ifdef SYSTEMD_NOTIFY + ssh_systemd_notify_ready(); +#endif +} + void platform_pre_fork(void) { @@ -55,6 +63,9 @@ platform_pre_fork(void) void platform_pre_restart(void) { +#ifdef SYSTEMD_NOTIFY + ssh_systemd_notify_reload(); +#endif #ifdef LINUX_OOM_ADJUST oom_adjust_restore(); #endif diff --git a/platform.h b/platform.h index 7fef8c983..5dec23276 100644 --- a/platform.h +++ b/platform.h @@ -21,6 +21,7 @@ void platform_pre_listen(void); void platform_pre_fork(void); void platform_pre_restart(void); +void platform_post_listen(void); void platform_post_fork_parent(pid_t child_pid); void platform_post_fork_child(void); int platform_privileged_uidswap(void); diff --git a/sshd.c b/sshd.c index b4f2b9742..865331b46 100644 --- a/sshd.c +++ b/sshd.c @@ -2077,6 +2077,8 @@ main(int ac, char **av) ssh_signal(SIGTERM, sigterm_handler); ssh_signal(SIGQUIT, sigterm_handler); + platform_post_listen(); + /* * Write out the pid file after the sigterm handler * is setup and the listen sockets are bound