i3
sd-daemon.c
Go to the documentation of this file.
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 Copyright 2010 Lennart Poettering
5
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation files
8 (the "Software"), to deal in the Software without restriction,
9 including without limitation the rights to use, copy, modify, merge,
10 publish, distribute, sublicense, and/or sell copies of the Software,
11 and to permit persons to whom the Software is furnished to do so,
12 subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25***/
26
27#ifndef _GNU_SOURCE
28#define _GNU_SOURCE
29#endif
30
31#include "sd-daemon.h"
32
33#include <errno.h>
34#include <netinet/in.h>
35#include <stdarg.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/fcntl.h>
41#include <sys/socket.h>
42#include <sys/stat.h>
43#include <sys/types.h>
44#include <sys/un.h>
45#include <unistd.h>
46
47int sd_listen_fds(int unset_environment) {
48 int r, fd;
49 const char *e;
50 char *p = NULL;
51 unsigned long l;
52
53 if (!(e = getenv("LISTEN_PID"))) {
54 r = 0;
55 goto finish;
56 }
57
58 errno = 0;
59 l = strtoul(e, &p, 10);
60
61 if (errno != 0) {
62 r = -errno;
63 goto finish;
64 }
65
66 if (!p || *p || l <= 0) {
67 r = -EINVAL;
68 goto finish;
69 }
70
71 /* Is this for us? */
72 if (getpid() != (pid_t)l) {
73 r = 0;
74 goto finish;
75 }
76
77 if (!(e = getenv("LISTEN_FDS"))) {
78 r = 0;
79 goto finish;
80 }
81
82 errno = 0;
83 l = strtoul(e, &p, 10);
84
85 if (errno != 0) {
86 r = -errno;
87 goto finish;
88 }
89
90 if (!p || *p) {
91 r = -EINVAL;
92 goto finish;
93 }
94
95 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)l; fd++) {
96 int flags;
97
98 if ((flags = fcntl(fd, F_GETFD)) < 0) {
99 r = -errno;
100 goto finish;
101 }
102
103 if (flags & FD_CLOEXEC)
104 continue;
105
106 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
107 r = -errno;
108 goto finish;
109 }
110 }
111
112 r = (int)l;
113
114finish:
115 if (unset_environment) {
116 unsetenv("LISTEN_PID");
117 unsetenv("LISTEN_FDS");
118 }
119
120 return r;
121}
122
123int sd_is_fifo(int fd, const char *path) {
124 struct stat st_fd;
125
126 if (fd < 0)
127 return -EINVAL;
128
129 memset(&st_fd, 0, sizeof(st_fd));
130 if (fstat(fd, &st_fd) < 0)
131 return -errno;
132
133 if (!S_ISFIFO(st_fd.st_mode))
134 return 0;
135
136 if (path) {
137 struct stat st_path;
138
139 memset(&st_path, 0, sizeof(st_path));
140 if (stat(path, &st_path) < 0) {
141 if (errno == ENOENT || errno == ENOTDIR)
142 return 0;
143
144 return -errno;
145 }
146
147 return st_path.st_dev == st_fd.st_dev &&
148 st_path.st_ino == st_fd.st_ino;
149 }
150
151 return 1;
152}
153
154static int sd_is_socket_internal(int fd, int type, int listening) {
155 struct stat st_fd;
156
157 if (fd < 0 || type < 0)
158 return -EINVAL;
159
160 if (fstat(fd, &st_fd) < 0)
161 return -errno;
162
163 if (!S_ISSOCK(st_fd.st_mode))
164 return 0;
165
166 if (type != 0) {
167 int other_type = 0;
168 socklen_t l = sizeof(other_type);
169
170 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
171 return -errno;
172
173 if (l != sizeof(other_type))
174 return -EINVAL;
175
176 if (other_type != type)
177 return 0;
178 }
179
180 if (listening >= 0) {
181 int accepting = 0;
182 socklen_t l = sizeof(accepting);
183
184 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
185 return -errno;
186
187 if (l != sizeof(accepting))
188 return -EINVAL;
189
190 if (!accepting != !listening)
191 return 0;
192 }
193
194 return 1;
195}
196
198 struct sockaddr sa;
199 struct sockaddr_in in4;
200 struct sockaddr_in6 in6;
201 struct sockaddr_un un;
202 struct sockaddr_storage storage;
203};
204
205int sd_is_socket(int fd, int family, int type, int listening) {
206 int r;
207
208 if (family < 0)
209 return -EINVAL;
210
211 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
212 return r;
213
214 if (family > 0) {
215 union sockaddr_union sockaddr;
216 socklen_t l;
217
218 memset(&sockaddr, 0, sizeof(sockaddr));
219 l = sizeof(sockaddr);
220
221 if (getsockname(fd, &sockaddr.sa, &l) < 0)
222 return -errno;
223
224 if (l < sizeof(sa_family_t))
225 return -EINVAL;
226
227 return sockaddr.sa.sa_family == family;
228 }
229
230 return 1;
231}
232
233int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
234 union sockaddr_union sockaddr;
235 socklen_t l;
236 int r;
237
238 if (family != 0 && family != AF_INET && family != AF_INET6)
239 return -EINVAL;
240
241 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
242 return r;
243
244 memset(&sockaddr, 0, sizeof(sockaddr));
245 l = sizeof(sockaddr);
246
247 if (getsockname(fd, &sockaddr.sa, &l) < 0)
248 return -errno;
249
250 if (l < sizeof(sa_family_t))
251 return -EINVAL;
252
253 if (sockaddr.sa.sa_family != AF_INET &&
254 sockaddr.sa.sa_family != AF_INET6)
255 return 0;
256
257 if (family > 0)
258 if (sockaddr.sa.sa_family != family)
259 return 0;
260
261 if (port > 0) {
262 if (sockaddr.sa.sa_family == AF_INET) {
263 if (l < sizeof(struct sockaddr_in))
264 return -EINVAL;
265
266 return htons(port) == sockaddr.in4.sin_port;
267 } else {
268 if (l < sizeof(struct sockaddr_in6))
269 return -EINVAL;
270
271 return htons(port) == sockaddr.in6.sin6_port;
272 }
273 }
274
275 return 1;
276}
277
278int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
279 union sockaddr_union sockaddr;
280 socklen_t l;
281 int r;
282
283 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
284 return r;
285
286 memset(&sockaddr, 0, sizeof(sockaddr));
287 l = sizeof(sockaddr);
288
289 if (getsockname(fd, &sockaddr.sa, &l) < 0)
290 return -errno;
291
292 if (l < sizeof(sa_family_t))
293 return -EINVAL;
294
295 if (sockaddr.sa.sa_family != AF_UNIX)
296 return 0;
297
298 if (path) {
299 if (length <= 0)
300 length = strlen(path);
301
302 if (length <= 0)
303 /* Unnamed socket */
304 return l == offsetof(struct sockaddr_un, sun_path);
305
306 if (path[0])
307 /* Normal path socket */
308 return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
309 memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
310 else
311 /* Abstract namespace socket */
312 return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
313 memcmp(path, sockaddr.un.sun_path, length) == 0;
314 }
315
316 return 1;
317}
318
319int sd_notify(int unset_environment, const char *state) {
320#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
321 return 0;
322#else
323 int fd = -1, r;
324 struct msghdr msghdr;
325 struct iovec iovec;
326 union sockaddr_union sockaddr;
327 const char *e;
328
329 if (!state) {
330 r = -EINVAL;
331 goto finish;
332 }
333
334 if (!(e = getenv("NOTIFY_SOCKET")))
335 return 0;
336
337 /* Must be an abstract socket, or an absolute path */
338 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
339 r = -EINVAL;
340 goto finish;
341 }
342
343 if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
344 r = -EINVAL;
345 goto finish;
346 }
347
348 if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
349 r = -errno;
350 goto finish;
351 }
352
353 memset(&sockaddr, 0, sizeof(sockaddr));
354 sockaddr.sa.sa_family = AF_UNIX;
355 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path) - 1);
356
357 if (sockaddr.un.sun_path[0] == '@')
358 sockaddr.un.sun_path[0] = 0;
359
360 memset(&iovec, 0, sizeof(iovec));
361 iovec.iov_base = (char *)state;
362 iovec.iov_len = strlen(state);
363
364 memset(&msghdr, 0, sizeof(msghdr));
365 msghdr.msg_name = &sockaddr;
366 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
367
368 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
369 msghdr.msg_namelen = sizeof(struct sockaddr_un);
370
371 msghdr.msg_iov = &iovec;
372 msghdr.msg_iovlen = 1;
373
374 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
375 r = -errno;
376 goto finish;
377 }
378
379 r = 1;
380
381finish:
382 if (unset_environment)
383 unsetenv("NOTIFY_SOCKET");
384
385 if (fd >= 0)
386 close(fd);
387
388 return r;
389#endif
390}
391
392int sd_notifyf(int unset_environment, const char *format, ...) {
393#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
394 return 0;
395#else
396 va_list ap;
397 char *p = NULL;
398 int r;
399
400 va_start(ap, format);
401 r = vasprintf(&p, format, ap);
402 va_end(ap);
403
404 if (r < 0 || !p)
405 return -ENOMEM;
406
407 r = sd_notify(unset_environment, p);
408 free(p);
409
410 return r;
411#endif
412}
413
414int sd_booted(void) {
415#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
416 return 0;
417#else
418
419 struct stat a, b;
420
421 /* We simply test whether the systemd cgroup hierarchy is mounted */
422
423 if (lstat("/sys/fs/cgroup", &a) < 0)
424 return 0;
425
426 if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
427 return 0;
428
429 return a.st_dev != b.st_dev;
430#endif
431}
static cmdp_state state
static int sd_is_socket_internal(int fd, int type, int listening)
Definition sd-daemon.c:154
int sd_booted(void)
Definition sd-daemon.c:414
int sd_notify(int unset_environment, const char *state)
Definition sd-daemon.c:319
int sd_is_fifo(int fd, const char *path)
Definition sd-daemon.c:123
int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port)
Definition sd-daemon.c:233
int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length)
Definition sd-daemon.c:278
int sd_notifyf(int unset_environment, const char *format,...)
Definition sd-daemon.c:392
int sd_listen_fds(int unset_environment)
Definition sd-daemon.c:47
int sd_is_socket(int fd, int family, int type, int listening)
Definition sd-daemon.c:205
#define SD_LISTEN_FDS_START
Definition sd-daemon.h:102
struct sockaddr_in in4
Definition sd-daemon.c:199
struct sockaddr_un un
Definition sd-daemon.c:201
struct sockaddr_storage storage
Definition sd-daemon.c:202
struct sockaddr_in6 in6
Definition sd-daemon.c:200
struct sockaddr sa
Definition sd-daemon.c:198