basic/linux: update kernel headers from v6.14-rc1
[systemd.io.git] / man / notify-selfcontained-example.c
blob3498d508430d93a30cccb4780313b4619a3eceb4
1 /* SPDX-License-Identifier: MIT-0 */
3 /* Implement the systemd notify protocol without external dependencies.
4 * Supports both readiness notification on startup and on reloading,
5 * according to the protocol defined at:
6 * https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
7 * This protocol is guaranteed to be stable as per:
8 * https://systemd.io/PORTABILITY_AND_STABILITY/ */
10 #define _GNU_SOURCE 1
11 #include <errno.h>
12 #include <inttypes.h>
13 #include <signal.h>
14 #include <stdbool.h>
15 #include <stddef.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 #include <time.h>
22 #include <unistd.h>
24 #define _cleanup_(f) __attribute__((cleanup(f)))
26 static void closep(int *fd) {
27 if (!fd || *fd < 0)
28 return;
30 close(*fd);
31 *fd = -1;
34 static int notify(const char *message) {
35 union sockaddr_union {
36 struct sockaddr sa;
37 struct sockaddr_un sun;
38 } socket_addr = {
39 .sun.sun_family = AF_UNIX,
41 size_t path_length, message_length;
42 _cleanup_(closep) int fd = -1;
43 const char *socket_path;
45 /* Verify the argument first */
46 if (!message)
47 return -EINVAL;
49 message_length = strlen(message);
50 if (message_length == 0)
51 return -EINVAL;
53 /* If the variable is not set, the protocol is a noop */
54 socket_path = getenv("NOTIFY_SOCKET");
55 if (!socket_path)
56 return 0; /* Not set? Nothing to do */
58 /* Only AF_UNIX is supported, with path or abstract sockets */
59 if (socket_path[0] != '/' && socket_path[0] != '@')
60 return -EAFNOSUPPORT;
62 path_length = strlen(socket_path);
63 /* Ensure there is room for NUL byte */
64 if (path_length >= sizeof(socket_addr.sun.sun_path))
65 return -E2BIG;
67 memcpy(socket_addr.sun.sun_path, socket_path, path_length);
69 /* Support for abstract socket */
70 if (socket_addr.sun.sun_path[0] == '@')
71 socket_addr.sun.sun_path[0] = 0;
73 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
74 if (fd < 0)
75 return -errno;
77 if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0)
78 return -errno;
80 ssize_t written = write(fd, message, message_length);
81 if (written != (ssize_t) message_length)
82 return written < 0 ? -errno : -EPROTO;
84 return 1; /* Notified! */
87 static int notify_ready(void) {
88 return notify("READY=1");
91 static int notify_reloading(void) {
92 /* A buffer with length sufficient to format the maximum UINT64 value. */
93 char reload_message[sizeof("RELOADING=1\nMONOTONIC_USEC=18446744073709551615")];
94 struct timespec ts;
95 uint64_t now;
97 /* Notify systemd that we are reloading, including a CLOCK_MONOTONIC timestamp in usec
98 * so that the program is compatible with a Type=notify-reload service. */
100 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
101 return -errno;
103 if (ts.tv_sec < 0 || ts.tv_nsec < 0 ||
104 (uint64_t) ts.tv_sec > (UINT64_MAX - (ts.tv_nsec / 1000ULL)) / 1000000ULL)
105 return -EINVAL;
107 now = (uint64_t) ts.tv_sec * 1000000ULL + (uint64_t) ts.tv_nsec / 1000ULL;
109 if (snprintf(reload_message, sizeof(reload_message), "RELOADING=1\nMONOTONIC_USEC=%" PRIu64, now) < 0)
110 return -EINVAL;
112 return notify(reload_message);
115 static int notify_stopping(void) {
116 return notify("STOPPING=1");
119 static volatile sig_atomic_t reloading = 0;
120 static volatile sig_atomic_t terminating = 0;
122 static void signal_handler(int sig) {
123 if (sig == SIGHUP)
124 reloading = 1;
125 else if (sig == SIGINT || sig == SIGTERM)
126 terminating = 1;
129 int main(int argc, char **argv) {
130 struct sigaction sa = {
131 .sa_handler = signal_handler,
132 .sa_flags = SA_RESTART,
134 int r;
136 /* Setup signal handlers */
137 sigemptyset(&sa.sa_mask);
138 sigaction(SIGHUP, &sa, NULL);
139 sigaction(SIGINT, &sa, NULL);
140 sigaction(SIGTERM, &sa, NULL);
142 /* Do more service initialization work here … */
144 /* Now that all the preparations steps are done, signal readiness */
146 r = notify_ready();
147 if (r < 0) {
148 fprintf(stderr, "Failed to notify readiness to $NOTIFY_SOCKET: %s\n", strerror(-r));
149 return EXIT_FAILURE;
152 while (!terminating) {
153 if (reloading) {
154 reloading = false;
156 /* As a separate but related feature, we can also notify the manager
157 * when reloading configuration. This allows accurate state-tracking,
158 * and also automated hook-in of 'systemctl reload' without having to
159 * specify manually an ExecReload= line in the unit file. */
161 r = notify_reloading();
162 if (r < 0) {
163 fprintf(stderr, "Failed to notify reloading to $NOTIFY_SOCKET: %s\n", strerror(-r));
164 return EXIT_FAILURE;
167 /* Do some reconfiguration work here … */
169 r = notify_ready();
170 if (r < 0) {
171 fprintf(stderr, "Failed to notify readiness to $NOTIFY_SOCKET: %s\n", strerror(-r));
172 return EXIT_FAILURE;
176 /* Do some daemon work here … */
177 sleep(5);
180 r = notify_stopping();
181 if (r < 0) {
182 fprintf(stderr, "Failed to report termination to $NOTIFY_SOCKET: %s\n", strerror(-r));
183 return EXIT_FAILURE;
186 /* Do some shutdown work here … */
188 return EXIT_SUCCESS;