1 // SPDX-License-Identifier: MIT-0
2 /*
3 * The code below is imported from:
4 * https://www.freedesktop.org/software/systemd/man/devel/sd_notify.html#Standalone%20Implementations
5 */
6
7 #define _GNU_SOURCE 1
8 #include <string.h>
9 #include <errno.h>
10 #include <inttypes.h>
11 #include <signal.h>
12 #include <stdbool.h>
13 #include <stddef.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <time.h>
19 #include <unistd.h>
20
21 #include "sd_notify.h"
22
23 #define _cleanup_(f) __attribute__((cleanup(f)))
24
closep(int * fd)25 static void closep(int *fd)
26 {
27 if (!fd || *fd < 0)
28 return;
29
30 close(*fd);
31 *fd = -1;
32 }
33
notify(const char * message)34 static int notify(const char *message)
35 {
36 union sockaddr_union {
37 struct sockaddr sa;
38 struct sockaddr_un sun;
39 } socket_addr = {
40 .sun.sun_family = AF_UNIX,
41 };
42
43 ssize_t written = 0;
44 size_t path_length, message_length = 0;
45 _cleanup_(closep) int fd = -1;
46 const char *socket_path = NULL;
47
48 /* Verify the argument first */
49 if (!message)
50 return -EINVAL;
51
52 message_length = strlen(message);
53 if (message_length == 0)
54 return -EINVAL;
55
56 /* If the variable is not set, the protocol is a noop */
57 socket_path = getenv("NOTIFY_SOCKET");
58 if (!socket_path)
59 return 0; /* Not set? Nothing to do */
60
61 /* Only AF_UNIX is supported, with path or abstract sockets */
62 if (socket_path[0] != '/' && socket_path[0] != '@')
63 return -EAFNOSUPPORT;
64
65 path_length = strlen(socket_path);
66 /* Ensure there is room for NULL byte */
67 if (path_length >= sizeof(socket_addr.sun.sun_path))
68 return -E2BIG;
69
70 memcpy(socket_addr.sun.sun_path, socket_path, path_length);
71
72 /* Support for abstract socket */
73 if (socket_addr.sun.sun_path[0] == '@')
74 socket_addr.sun.sun_path[0] = 0;
75
76 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
77 if (fd < 0)
78 return -errno;
79
80 if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0)
81 return -errno;
82
83 written = write(fd, message, message_length);
84 if (written != (ssize_t) message_length)
85 return written < 0 ? -errno : -EPROTO;
86
87 return 1; /* Notified! */
88 }
89
sd_notify_ready(void)90 int sd_notify_ready(void)
91 {
92 return notify("READY=1");
93 }
94