/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Copied and adapted from systemd source code. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include "sd-socket.h" #define _cleanup_(f) __attribute__((cleanup(f))) int sd_is_socket(int fd, int type, int listening) { struct stat st_fd; assert(fd >= 0); assert(type >= 0); if (fstat(fd, &st_fd) < 0) return -errno; if (!S_ISSOCK(st_fd.st_mode)) return 0; if (type != 0) { int other_type = 0; socklen_t l = sizeof(other_type); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) return -errno; if (l != sizeof(other_type)) return -EINVAL; if (other_type != type) return 0; } if (listening >= 0) { int accepting = 0; socklen_t l = sizeof(accepting); if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) return -errno; if (l != sizeof(accepting)) return -EINVAL; if (!accepting != !listening) return 0; } return 1; } /* SPDX-License-Identifier: MIT-0 */ /* Implement the systemd notify protocol without external dependencies. * Supports both readiness notification on startup and on reloading, * according to the protocol defined at: * https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html * This protocol is guaranteed to be stable as per: * https://systemd.io/PORTABILITY_AND_STABILITY/ */ static void closep(int *fd) { if (!fd || *fd < 0) return; close(*fd); *fd = -1; } static int sd_notify(const char *message) { union sockaddr_union { struct sockaddr sa; struct sockaddr_un sun; } socket_addr = { .sun.sun_family = AF_UNIX, }; size_t path_length, message_length; _cleanup_(closep) int fd = -1; const char *socket_path; /* Verify the argument first */ if (!message) return -EINVAL; message_length = strlen(message); if (message_length == 0) return -EINVAL; /* If the variable is not set, the protocol is a noop */ socket_path = getenv("NOTIFY_SOCKET"); if (!socket_path) return 0; /* Not set? Nothing to do */ /* Only AF_UNIX is supported, with path or abstract sockets */ if (socket_path[0] != '/' && socket_path[0] != '@') return -EAFNOSUPPORT; path_length = strlen(socket_path); /* Ensure there is room for NUL byte */ if (path_length >= sizeof(socket_addr.sun.sun_path)) return -E2BIG; memcpy(socket_addr.sun.sun_path, socket_path, path_length); /* Support for abstract socket */ if (socket_addr.sun.sun_path[0] == '@') socket_addr.sun.sun_path[0] = 0; fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0) return -errno; ssize_t written = write(fd, message, message_length); if (written != (ssize_t) message_length) return written < 0 ? -errno : -EPROTO; return 1; /* Notified! */ } int sd_notify_ready(void) { return sd_notify("READY=1"); } int sd_notify_stopping(void) { return sd_notify("STOPPING=1"); }