1 /* SPDX-License-Identifier: MIT-0 */
2
3 /*
4 * Implement the systemd notify protocol without external dependencies.
5 * Supports both readiness notification on startup and on reloading,
6 * according to the protocol defined at:
7 * https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
8 * This protocol is guaranteed to be stable as per:
9 * https://systemd.io/PORTABILITY_AND_STABILITY/
10 *
11 * Differences from the upstream copy:
12 * - Rename/rework as a drop-in replacement for systemd/sd-daemon.h
13 * - Only take the subset Xen cares about
14 * - Respect -Wdeclaration-after-statement
15 */
16
17 #ifndef XEN_SD_NOTIFY
18 #define XEN_SD_NOTIFY
19
20 #include <errno.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <unistd.h>
26
xen_sd_closep(int * fd)27 static inline void xen_sd_closep(int *fd) {
28 if (!fd || *fd < 0)
29 return;
30
31 close(*fd);
32 *fd = -1;
33 }
34
xen_sd_notify(const char * message)35 static inline int xen_sd_notify(const char *message) {
36 union sockaddr_union {
37 struct sockaddr sa;
38 struct sockaddr_un sun;
39 } socket_addr = {
40 .sun.sun_family = AF_UNIX,
41 };
42 size_t path_length, message_length;
43 ssize_t written;
44 const char *socket_path;
45 int __attribute__((cleanup(xen_sd_closep))) fd = -1;
46
47 /* Verify the argument first */
48 if (!message)
49 return -EINVAL;
50
51 message_length = strlen(message);
52 if (message_length == 0)
53 return -EINVAL;
54
55 /* If the variable is not set, the protocol is a noop */
56 socket_path = getenv("NOTIFY_SOCKET");
57 if (!socket_path)
58 return 0; /* Not set? Nothing to do */
59
60 /* Only AF_UNIX is supported, with path or abstract sockets */
61 if (socket_path[0] != '/' && socket_path[0] != '@')
62 return -EAFNOSUPPORT;
63
64 path_length = strlen(socket_path);
65 /* Ensure there is room for NUL byte */
66 if (path_length >= sizeof(socket_addr.sun.sun_path))
67 return -E2BIG;
68
69 memcpy(socket_addr.sun.sun_path, socket_path, path_length);
70
71 /* Support for abstract socket */
72 if (socket_addr.sun.sun_path[0] == '@')
73 socket_addr.sun.sun_path[0] = 0;
74
75 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
76 if (fd < 0)
77 return -errno;
78
79 if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0)
80 return -errno;
81
82 written = write(fd, message, message_length);
83 if (written != (ssize_t) message_length)
84 return written < 0 ? -errno : -EPROTO;
85
86 return 1; /* Notified! */
87 }
88
sd_notify(int unset_environment,const char * message)89 static inline int sd_notify(int unset_environment, const char *message) {
90 int r = xen_sd_notify(message);
91
92 if (unset_environment)
93 unsetenv("NOTIFY_SOCKET");
94
95 return r;
96 }
97
98 #endif /* XEN_SD_NOTIFY */
99