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