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