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