chiark / gitweb /
update TODO (add section for things to fix before 209)
[elogind.git] / src / libsystemd-daemon / 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 <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__) && !defined(SD_DAEMON_DISABLE_MQ)
47 #  include <mqueue.h>
48 #endif
49
50 #include "util.h"
51 #include "sd-daemon.h"
52
53 #if (__GNUC__ >= 4)
54 #  ifdef SD_EXPORT_SYMBOLS
55 /* Export symbols */
56 #    define _sd_export_ __attribute__ ((visibility("default")))
57 #  else
58 /* Don't export the symbols */
59 #    define _sd_export_ __attribute__ ((visibility("hidden")))
60 #  endif
61 #else
62 #  define _sd_export_
63 #endif
64
65 _sd_export_ int sd_listen_fds(int unset_environment) {
66
67 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
68         return 0;
69 #else
70         int r, fd;
71         const char *e;
72         char *p = NULL;
73         unsigned long l;
74
75         e = getenv("LISTEN_PID");
76         if (!e) {
77                 r = 0;
78                 goto finish;
79         }
80
81         errno = 0;
82         l = strtoul(e, &p, 10);
83
84         if (errno > 0) {
85                 r = -errno;
86                 goto finish;
87         }
88
89         if (!p || p == e || *p || l <= 0) {
90                 r = -EINVAL;
91                 goto finish;
92         }
93
94         /* Is this for us? */
95         if (getpid() != (pid_t) l) {
96                 r = 0;
97                 goto finish;
98         }
99
100         e = getenv("LISTEN_FDS");
101         if (!e) {
102                 r = 0;
103                 goto finish;
104         }
105
106         errno = 0;
107         l = strtoul(e, &p, 10);
108
109         if (errno > 0) {
110                 r = -errno;
111                 goto finish;
112         }
113
114         if (!p || p == e || *p) {
115                 r = -EINVAL;
116                 goto finish;
117         }
118
119         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
120                 int flags;
121
122                 flags = fcntl(fd, F_GETFD);
123                 if (flags < 0) {
124                         r = -errno;
125                         goto finish;
126                 }
127
128                 if (flags & FD_CLOEXEC)
129                         continue;
130
131                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
132                         r = -errno;
133                         goto finish;
134                 }
135         }
136
137         r = (int) l;
138
139 finish:
140         if (unset_environment) {
141                 unsetenv("LISTEN_PID");
142                 unsetenv("LISTEN_FDS");
143         }
144
145         return r;
146 #endif
147 }
148
149 _sd_export_ int sd_is_fifo(int fd, const char *path) {
150         struct stat st_fd;
151
152         if (fd < 0)
153                 return -EINVAL;
154
155         if (fstat(fd, &st_fd) < 0)
156                 return -errno;
157
158         if (!S_ISFIFO(st_fd.st_mode))
159                 return 0;
160
161         if (path) {
162                 struct stat st_path;
163
164                 if (stat(path, &st_path) < 0) {
165
166                         if (errno == ENOENT || errno == ENOTDIR)
167                                 return 0;
168
169                         return -errno;
170                 }
171
172                 return
173                         st_path.st_dev == st_fd.st_dev &&
174                         st_path.st_ino == st_fd.st_ino;
175         }
176
177         return 1;
178 }
179
180 _sd_export_ int sd_is_special(int fd, const char *path) {
181         struct stat st_fd;
182
183         if (fd < 0)
184                 return -EINVAL;
185
186         if (fstat(fd, &st_fd) < 0)
187                 return -errno;
188
189         if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
190                 return 0;
191
192         if (path) {
193                 struct stat st_path;
194
195                 if (stat(path, &st_path) < 0) {
196
197                         if (errno == ENOENT || errno == ENOTDIR)
198                                 return 0;
199
200                         return -errno;
201                 }
202
203                 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
204                         return
205                                 st_path.st_dev == st_fd.st_dev &&
206                                 st_path.st_ino == st_fd.st_ino;
207                 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
208                         return st_path.st_rdev == st_fd.st_rdev;
209                 else
210                         return 0;
211         }
212
213         return 1;
214 }
215
216 static int sd_is_socket_internal(int fd, int type, int listening) {
217         struct stat st_fd;
218
219         if (fd < 0 || type < 0)
220                 return -EINVAL;
221
222         if (fstat(fd, &st_fd) < 0)
223                 return -errno;
224
225         if (!S_ISSOCK(st_fd.st_mode))
226                 return 0;
227
228         if (type != 0) {
229                 int other_type = 0;
230                 socklen_t l = sizeof(other_type);
231
232                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
233                         return -errno;
234
235                 if (l != sizeof(other_type))
236                         return -EINVAL;
237
238                 if (other_type != type)
239                         return 0;
240         }
241
242         if (listening >= 0) {
243                 int accepting = 0;
244                 socklen_t l = sizeof(accepting);
245
246                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
247                         return -errno;
248
249                 if (l != sizeof(accepting))
250                         return -EINVAL;
251
252                 if (!accepting != !listening)
253                         return 0;
254         }
255
256         return 1;
257 }
258
259 union sockaddr_union {
260         struct sockaddr sa;
261         struct sockaddr_in in4;
262         struct sockaddr_in6 in6;
263         struct sockaddr_un un;
264         struct sockaddr_storage storage;
265 };
266
267 _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
268         int r;
269
270         if (family < 0)
271                 return -EINVAL;
272
273         r = sd_is_socket_internal(fd, type, listening);
274         if (r <= 0)
275                 return r;
276
277         if (family > 0) {
278                 union sockaddr_union sockaddr = {};
279                 socklen_t 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 = sizeof(sockaddr);
296         int r;
297
298         if (family != 0 && family != AF_INET && family != AF_INET6)
299                 return -EINVAL;
300
301         r = sd_is_socket_internal(fd, type, listening);
302         if (r <= 0)
303                 return r;
304
305         if (getsockname(fd, &sockaddr.sa, &l) < 0)
306                 return -errno;
307
308         if (l < sizeof(sa_family_t))
309                 return -EINVAL;
310
311         if (sockaddr.sa.sa_family != AF_INET &&
312             sockaddr.sa.sa_family != AF_INET6)
313                 return 0;
314
315         if (family > 0)
316                 if (sockaddr.sa.sa_family != family)
317                         return 0;
318
319         if (port > 0) {
320                 if (sockaddr.sa.sa_family == AF_INET) {
321                         if (l < sizeof(struct sockaddr_in))
322                                 return -EINVAL;
323
324                         return htons(port) == sockaddr.in4.sin_port;
325                 } else {
326                         if (l < sizeof(struct sockaddr_in6))
327                                 return -EINVAL;
328
329                         return htons(port) == sockaddr.in6.sin6_port;
330                 }
331         }
332
333         return 1;
334 }
335
336 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
337         union sockaddr_union sockaddr = {};
338         socklen_t l = sizeof(sockaddr);
339         int r;
340
341         r = sd_is_socket_internal(fd, type, listening);
342         if (r <= 0)
343                 return r;
344
345         if (getsockname(fd, &sockaddr.sa, &l) < 0)
346                 return -errno;
347
348         if (l < sizeof(sa_family_t))
349                 return -EINVAL;
350
351         if (sockaddr.sa.sa_family != AF_UNIX)
352                 return 0;
353
354         if (path) {
355                 if (length == 0)
356                         length = strlen(path);
357
358                 if (length == 0)
359                         /* Unnamed socket */
360                         return l == offsetof(struct sockaddr_un, sun_path);
361
362                 if (path[0])
363                         /* Normal path socket */
364                         return
365                                 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
366                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
367                 else
368                         /* Abstract namespace socket */
369                         return
370                                 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
371                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
372         }
373
374         return 1;
375 }
376
377 _sd_export_ int sd_is_mq(int fd, const char *path) {
378 #if !defined(__linux__) || defined(SD_DAEMON_DISABLE_MQ)
379         return 0;
380 #else
381         struct mq_attr attr;
382
383         if (fd < 0)
384                 return -EINVAL;
385
386         if (mq_getattr(fd, &attr) < 0)
387                 return -errno;
388
389         if (path) {
390                 char fpath[PATH_MAX];
391                 struct stat a, b;
392
393                 if (path[0] != '/')
394                         return -EINVAL;
395
396                 if (fstat(fd, &a) < 0)
397                         return -errno;
398
399                 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
400                 fpath[sizeof(fpath)-1] = 0;
401
402                 if (stat(fpath, &b) < 0)
403                         return -errno;
404
405                 if (a.st_dev != b.st_dev ||
406                     a.st_ino != b.st_ino)
407                         return 0;
408         }
409
410         return 1;
411 #endif
412 }
413
414 _sd_export_ int sd_notify(int unset_environment, const char *state) {
415 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
416         return 0;
417 #else
418         int fd = -1, r;
419         struct msghdr msghdr;
420         struct iovec iovec;
421         union sockaddr_union sockaddr;
422         const char *e;
423
424         if (!state) {
425                 r = -EINVAL;
426                 goto finish;
427         }
428
429         e = getenv("NOTIFY_SOCKET");
430         if (!e)
431                 return 0;
432
433         /* Must be an abstract socket, or an absolute path */
434         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
435                 r = -EINVAL;
436                 goto finish;
437         }
438
439         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
440         if (fd < 0) {
441                 r = -errno;
442                 goto finish;
443         }
444
445         memzero(&sockaddr, sizeof(sockaddr));
446         sockaddr.sa.sa_family = AF_UNIX;
447         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
448
449         if (sockaddr.un.sun_path[0] == '@')
450                 sockaddr.un.sun_path[0] = 0;
451
452         memzero(&iovec, sizeof(iovec));
453         iovec.iov_base = (char*) state;
454         iovec.iov_len = strlen(state);
455
456         memzero(&msghdr, sizeof(msghdr));
457         msghdr.msg_name = &sockaddr;
458         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
459
460         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
461                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
462
463         msghdr.msg_iov = &iovec;
464         msghdr.msg_iovlen = 1;
465
466         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
467                 r = -errno;
468                 goto finish;
469         }
470
471         r = 1;
472
473 finish:
474         if (unset_environment)
475                 unsetenv("NOTIFY_SOCKET");
476
477         if (fd >= 0)
478                 close(fd);
479
480         return r;
481 #endif
482 }
483
484 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
485 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
486         return 0;
487 #else
488         va_list ap;
489         char *p = NULL;
490         int r;
491
492         va_start(ap, format);
493         r = vasprintf(&p, format, ap);
494         va_end(ap);
495
496         if (r < 0 || !p)
497                 return -ENOMEM;
498
499         r = sd_notify(unset_environment, p);
500         free(p);
501
502         return r;
503 #endif
504 }
505
506 _sd_export_ int sd_booted(void) {
507 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
508         return 0;
509 #else
510         struct stat st;
511
512         /* We test whether the runtime unit file directory has been
513          * created. This takes place in mount-setup.c, so is
514          * guaranteed to happen very early during boot. */
515
516         if (lstat("/run/systemd/system/", &st) < 0)
517                 return 0;
518
519         return !!S_ISDIR(st.st_mode);
520 #endif
521 }
522
523 _sd_export_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
524
525 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
526         return 0;
527 #else
528         unsigned long long ll;
529         unsigned long l;
530         const char *e;
531         char *p = NULL;
532         int r;
533
534         e = getenv("WATCHDOG_PID");
535         if (!e) {
536                 r = 0;
537                 goto finish;
538         }
539
540         errno = 0;
541         l = strtoul(e, &p, 10);
542         if (errno > 0) {
543                 r = -errno;
544                 goto finish;
545         }
546         if (!p || p == e || *p || l <= 0) {
547                 r = -EINVAL;
548                 goto finish;
549         }
550
551         /* Is this for us? */
552         if (getpid() != (pid_t) l) {
553                 r = 0;
554                 goto finish;
555         }
556
557         e = getenv("WATCHDOG_USEC");
558         if (!e) {
559                 r = -EINVAL;
560                 goto finish;
561         }
562
563         errno = 0;
564         ll = strtoull(e, &p, 10);
565         if (errno > 0) {
566                 r = -errno;
567                 goto finish;
568         }
569         if (!p || p == e || *p || l <= 0) {
570                 r = -EINVAL;
571                 goto finish;
572         }
573
574         if (usec)
575                 *usec = ll;
576
577         r = 1;
578
579 finish:
580         if (unset_environment) {
581                 unsetenv("WATCHDOG_PID");
582                 unsetenv("WATCHDOG_USEC");
583         }
584
585         return r;
586 #endif
587 }