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