chiark / gitweb /
journalctl: rename --new-id to --new-id128 in order not to introduce yet another...
[elogind.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 }