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