]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sd-daemon.c
update TODO
[thirdparty/systemd.git] / src / sd-daemon.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
abbbea81
LP
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
8c47c732
LP
27#ifndef _GNU_SOURCE
28#define _GNU_SOURCE
29#endif
30
7c394faa
LP
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34#include <sys/un.h>
8640e111 35#include <sys/fcntl.h>
7c394faa 36#include <netinet/in.h>
abbbea81
LP
37#include <stdlib.h>
38#include <errno.h>
abbbea81 39#include <unistd.h>
7c394faa 40#include <string.h>
8c47c732
LP
41#include <stdarg.h>
42#include <stdio.h>
0e098b15 43#include <stddef.h>
916abb21
LP
44#include <limits.h>
45
46#if defined(__linux__)
47#include <mqueue.h>
48#endif
abbbea81
LP
49
50#include "sd-daemon.h"
51
b136daf5
KS
52#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
53#define _sd_hidden_ __attribute__ ((visibility("hidden")))
54#else
55#define _sd_hidden_
56#endif
57
58_sd_hidden_ int sd_listen_fds(int unset_environment) {
abbbea81 59
8c47c732 60#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
abbbea81
LP
61 return 0;
62#else
8640e111 63 int r, fd;
abbbea81
LP
64 const char *e;
65 char *p = NULL;
66 unsigned long l;
67
68 if (!(e = getenv("LISTEN_PID"))) {
69 r = 0;
70 goto finish;
71 }
72
73 errno = 0;
74 l = strtoul(e, &p, 10);
75
76 if (errno != 0) {
77 r = -errno;
78 goto finish;
79 }
80
81 if (!p || *p || l <= 0) {
82 r = -EINVAL;
83 goto finish;
84 }
85
86 /* Is this for us? */
87 if (getpid() != (pid_t) l) {
88 r = 0;
89 goto finish;
90 }
91
92 if (!(e = getenv("LISTEN_FDS"))) {
93 r = 0;
94 goto finish;
95 }
96
97 errno = 0;
98 l = strtoul(e, &p, 10);
99
100 if (errno != 0) {
101 r = -errno;
102 goto finish;
103 }
104
105 if (!p || *p) {
106 r = -EINVAL;
107 goto finish;
108 }
109
8640e111
LP
110 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
111 int flags;
112
113 if ((flags = fcntl(fd, F_GETFD)) < 0) {
114 r = -errno;
115 goto finish;
116 }
117
118 if (flags & FD_CLOEXEC)
119 continue;
120
121 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
122 r = -errno;
123 goto finish;
124 }
125 }
126
abbbea81
LP
127 r = (int) l;
128
129finish:
130 if (unset_environment) {
131 unsetenv("LISTEN_PID");
132 unsetenv("LISTEN_FDS");
133 }
134
135 return r;
136#endif
137}
7c394faa 138
b136daf5 139_sd_hidden_ int sd_is_fifo(int fd, const char *path) {
7c394faa
LP
140 struct stat st_fd;
141
142 if (fd < 0)
143 return -EINVAL;
144
145 memset(&st_fd, 0, sizeof(st_fd));
146 if (fstat(fd, &st_fd) < 0)
147 return -errno;
148
149 if (!S_ISFIFO(st_fd.st_mode))
150 return 0;
151
152 if (path) {
153 struct stat st_path;
154
155 memset(&st_path, 0, sizeof(st_path));
fd8bccfb 156 if (stat(path, &st_path) < 0) {
7c394faa
LP
157
158 if (errno == ENOENT || errno == ENOTDIR)
159 return 0;
160
161 return -errno;
162 }
163
164 return
165 st_path.st_dev == st_fd.st_dev &&
166 st_path.st_ino == st_fd.st_ino;
167 }
168
169 return 1;
170}
171
e6a3081a 172static int sd_is_socket_internal(int fd, int type, int listening) {
7c394faa
LP
173 struct stat st_fd;
174
175 if (fd < 0 || type < 0)
176 return -EINVAL;
177
178 if (fstat(fd, &st_fd) < 0)
179 return -errno;
180
181 if (!S_ISSOCK(st_fd.st_mode))
182 return 0;
183
184 if (type != 0) {
185 int other_type = 0;
186 socklen_t l = sizeof(other_type);
187
188 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
189 return -errno;
190
191 if (l != sizeof(other_type))
192 return -EINVAL;
193
194 if (other_type != type)
195 return 0;
196 }
197
198 if (listening >= 0) {
199 int accepting = 0;
200 socklen_t l = sizeof(accepting);
201
202 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
203 return -errno;
204
205 if (l != sizeof(accepting))
206 return -EINVAL;
207
dde770cf 208 if (!accepting != !listening)
7c394faa
LP
209 return 0;
210 }
211
212 return 1;
213}
214
88ce42f6
LP
215union sockaddr_union {
216 struct sockaddr sa;
217 struct sockaddr_in in4;
218 struct sockaddr_in6 in6;
219 struct sockaddr_un un;
220 struct sockaddr_storage storage;
221};
222
b136daf5 223_sd_hidden_ int sd_is_socket(int fd, int family, int type, int listening) {
88ce42f6
LP
224 int r;
225
226 if (family < 0)
227 return -EINVAL;
228
229 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
230 return r;
231
232 if (family > 0) {
233 union sockaddr_union sockaddr;
234 socklen_t l;
235
236 memset(&sockaddr, 0, sizeof(sockaddr));
237 l = sizeof(sockaddr);
238
239 if (getsockname(fd, &sockaddr.sa, &l) < 0)
240 return -errno;
241
b7f42664 242 if (l < sizeof(sa_family_t))
88ce42f6
LP
243 return -EINVAL;
244
245 return sockaddr.sa.sa_family == family;
246 }
247
248 return 1;
249}
250
b136daf5 251_sd_hidden_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
88ce42f6 252 union sockaddr_union sockaddr;
7c394faa
LP
253 socklen_t l;
254 int r;
255
88ce42f6
LP
256 if (family != 0 && family != AF_INET && family != AF_INET6)
257 return -EINVAL;
258
259 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
7c394faa
LP
260 return r;
261
262 memset(&sockaddr, 0, sizeof(sockaddr));
263 l = sizeof(sockaddr);
264
265 if (getsockname(fd, &sockaddr.sa, &l) < 0)
266 return -errno;
267
b7f42664 268 if (l < sizeof(sa_family_t))
7c394faa
LP
269 return -EINVAL;
270
271 if (sockaddr.sa.sa_family != AF_INET &&
272 sockaddr.sa.sa_family != AF_INET6)
273 return 0;
274
88ce42f6
LP
275 if (family > 0)
276 if (sockaddr.sa.sa_family != family)
277 return 0;
278
7c394faa
LP
279 if (port > 0) {
280 if (sockaddr.sa.sa_family == AF_INET) {
281 if (l < sizeof(struct sockaddr_in))
282 return -EINVAL;
283
284 return htons(port) == sockaddr.in4.sin_port;
285 } else {
286 if (l < sizeof(struct sockaddr_in6))
287 return -EINVAL;
288
289 return htons(port) == sockaddr.in6.sin6_port;
290 }
291 }
292
293 return 1;
294}
295
b136daf5 296_sd_hidden_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
88ce42f6 297 union sockaddr_union sockaddr;
7c394faa
LP
298 socklen_t l;
299 int r;
300
88ce42f6 301 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
7c394faa
LP
302 return r;
303
304 memset(&sockaddr, 0, sizeof(sockaddr));
305 l = sizeof(sockaddr);
306
307 if (getsockname(fd, &sockaddr.sa, &l) < 0)
308 return -errno;
309
b7f42664 310 if (l < sizeof(sa_family_t))
7c394faa
LP
311 return -EINVAL;
312
313 if (sockaddr.sa.sa_family != AF_UNIX)
314 return 0;
315
316 if (path) {
317 if (length <= 0)
318 length = strlen(path);
319
320 if (length <= 0)
321 /* Unnamed socket */
0e098b15 322 return l == offsetof(struct sockaddr_un, sun_path);
7c394faa 323
7c394faa
LP
324 if (path[0])
325 /* Normal path socket */
cd250a39 326 return
0e098b15 327 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
cd250a39 328 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
7c394faa
LP
329 else
330 /* Abstract namespace socket */
cd250a39 331 return
0e098b15 332 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
cd250a39 333 memcmp(path, sockaddr.un.sun_path, length) == 0;
7c394faa
LP
334 }
335
336 return 1;
337}
8c47c732 338
b136daf5 339_sd_hidden_ int sd_is_mq(int fd, const char *path) {
916abb21
LP
340#if !defined(__linux__)
341 return 0;
342#else
343 struct mq_attr attr;
344
345 if (fd < 0)
346 return -EINVAL;
347
348 if (mq_getattr(fd, &attr) < 0)
349 return -errno;
350
351 if (path) {
352 char fpath[PATH_MAX];
353 struct stat a, b;
354
355 if (path[0] != '/')
356 return -EINVAL;
357
358 if (fstat(fd, &a) < 0)
359 return -errno;
360
361 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
362 fpath[sizeof(fpath)-1] = 0;
363
364 if (stat(fpath, &b) < 0)
365 return -errno;
366
367 if (a.st_dev != b.st_dev ||
368 a.st_ino != b.st_ino)
369 return 0;
370 }
371
372 return 1;
373#endif
374}
375
b136daf5 376_sd_hidden_ int sd_notify(int unset_environment, const char *state) {
c593cfe1 377#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
8c47c732
LP
378 return 0;
379#else
380 int fd = -1, r;
381 struct msghdr msghdr;
382 struct iovec iovec;
383 union sockaddr_union sockaddr;
8c47c732
LP
384 const char *e;
385
386 if (!state) {
387 r = -EINVAL;
388 goto finish;
389 }
390
08bfb810
LP
391 if (!(e = getenv("NOTIFY_SOCKET")))
392 return 0;
8c47c732
LP
393
394 /* Must be an abstract socket, or an absolute path */
395 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
396 r = -EINVAL;
397 goto finish;
398 }
399
400 if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
401 r = -errno;
402 goto finish;
403 }
404
405 memset(&sockaddr, 0, sizeof(sockaddr));
406 sockaddr.sa.sa_family = AF_UNIX;
407 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
408
409 if (sockaddr.un.sun_path[0] == '@')
410 sockaddr.un.sun_path[0] = 0;
411
412 memset(&iovec, 0, sizeof(iovec));
413 iovec.iov_base = (char*) state;
414 iovec.iov_len = strlen(state);
415
8c47c732
LP
416 memset(&msghdr, 0, sizeof(msghdr));
417 msghdr.msg_name = &sockaddr;
0e098b15 418 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
a013bd94
LP
419
420 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
421 msghdr.msg_namelen = sizeof(struct sockaddr_un);
422
8c47c732
LP
423 msghdr.msg_iov = &iovec;
424 msghdr.msg_iovlen = 1;
8c47c732
LP
425
426 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
427 r = -errno;
428 goto finish;
429 }
430
08bfb810 431 r = 1;
8c47c732
LP
432
433finish:
434 if (unset_environment)
435 unsetenv("NOTIFY_SOCKET");
436
437 if (fd >= 0)
438 close(fd);
439
440 return r;
441#endif
442}
443
b136daf5 444_sd_hidden_ int sd_notifyf(int unset_environment, const char *format, ...) {
8c47c732
LP
445#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
446 return 0;
447#else
448 va_list ap;
449 char *p = NULL;
450 int r;
451
452 va_start(ap, format);
453 r = vasprintf(&p, format, ap);
454 va_end(ap);
455
456 if (r < 0 || !p)
457 return -ENOMEM;
458
459 r = sd_notify(unset_environment, p);
460 free(p);
461
462 return r;
463#endif
464}
40473a70 465
b136daf5 466_sd_hidden_ int sd_booted(void) {
40473a70
LP
467#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
468 return 0;
469#else
470
471 struct stat a, b;
472
473 /* We simply test whether the systemd cgroup hierarchy is
474 * mounted */
475
77d5f105 476 if (lstat("/sys/fs/cgroup", &a) < 0)
40473a70
LP
477 return 0;
478
77d5f105 479 if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
40473a70
LP
480 return 0;
481
482 return a.st_dev != b.st_dev;
483#endif
484}