chiark / gitweb /
41f4b588dfe9a0c36197b7bf60c1e5b8e5b8ac8d
[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__) && !defined(SD_DAEMON_DISABLE_MQ)
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 = sizeof(sockaddr);
283
284                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
285                         return -errno;
286
287                 if (l < sizeof(sa_family_t))
288                         return -EINVAL;
289
290                 return sockaddr.sa.sa_family == family;
291         }
292
293         return 1;
294 }
295
296 _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
297         union sockaddr_union sockaddr = {};
298         socklen_t l = sizeof(sockaddr);
299         int r;
300
301         if (family != 0 && family != AF_INET && family != AF_INET6)
302                 return -EINVAL;
303
304         r = sd_is_socket_internal(fd, type, listening);
305         if (r <= 0)
306                 return r;
307
308         if (getsockname(fd, &sockaddr.sa, &l) < 0)
309                 return -errno;
310
311         if (l < sizeof(sa_family_t))
312                 return -EINVAL;
313
314         if (sockaddr.sa.sa_family != AF_INET &&
315             sockaddr.sa.sa_family != AF_INET6)
316                 return 0;
317
318         if (family > 0)
319                 if (sockaddr.sa.sa_family != family)
320                         return 0;
321
322         if (port > 0) {
323                 if (sockaddr.sa.sa_family == AF_INET) {
324                         if (l < sizeof(struct sockaddr_in))
325                                 return -EINVAL;
326
327                         return htons(port) == sockaddr.in4.sin_port;
328                 } else {
329                         if (l < sizeof(struct sockaddr_in6))
330                                 return -EINVAL;
331
332                         return htons(port) == sockaddr.in6.sin6_port;
333                 }
334         }
335
336         return 1;
337 }
338
339 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
340         union sockaddr_union sockaddr = {};
341         socklen_t l = sizeof(sockaddr);
342         int r;
343
344         r = sd_is_socket_internal(fd, type, listening);
345         if (r <= 0)
346                 return r;
347
348         if (getsockname(fd, &sockaddr.sa, &l) < 0)
349                 return -errno;
350
351         if (l < sizeof(sa_family_t))
352                 return -EINVAL;
353
354         if (sockaddr.sa.sa_family != AF_UNIX)
355                 return 0;
356
357         if (path) {
358                 if (length == 0)
359                         length = strlen(path);
360
361                 if (length == 0)
362                         /* Unnamed socket */
363                         return l == offsetof(struct sockaddr_un, sun_path);
364
365                 if (path[0])
366                         /* Normal path socket */
367                         return
368                                 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
369                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
370                 else
371                         /* Abstract namespace socket */
372                         return
373                                 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
374                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
375         }
376
377         return 1;
378 }
379
380 _sd_export_ int sd_is_mq(int fd, const char *path) {
381 #if !defined(__linux__) || defined(SD_DAEMON_DISABLE_MQ)
382         return 0;
383 #else
384         struct mq_attr attr;
385
386         if (fd < 0)
387                 return -EINVAL;
388
389         if (mq_getattr(fd, &attr) < 0)
390                 return -errno;
391
392         if (path) {
393                 char fpath[PATH_MAX];
394                 struct stat a, b;
395
396                 if (path[0] != '/')
397                         return -EINVAL;
398
399                 if (fstat(fd, &a) < 0)
400                         return -errno;
401
402                 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
403                 fpath[sizeof(fpath)-1] = 0;
404
405                 if (stat(fpath, &b) < 0)
406                         return -errno;
407
408                 if (a.st_dev != b.st_dev ||
409                     a.st_ino != b.st_ino)
410                         return 0;
411         }
412
413         return 1;
414 #endif
415 }
416
417 _sd_export_ int sd_notify(int unset_environment, const char *state) {
418 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
419         return 0;
420 #else
421         int fd = -1, r;
422         struct msghdr msghdr;
423         struct iovec iovec;
424         union sockaddr_union sockaddr;
425         const char *e;
426
427         if (!state) {
428                 r = -EINVAL;
429                 goto finish;
430         }
431
432         e = getenv("NOTIFY_SOCKET");
433         if (!e)
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         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
443         if (fd < 0) {
444                 r = -errno;
445                 goto finish;
446         }
447
448         memset(&sockaddr, 0, sizeof(sockaddr));
449         sockaddr.sa.sa_family = AF_UNIX;
450         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
451
452         if (sockaddr.un.sun_path[0] == '@')
453                 sockaddr.un.sun_path[0] = 0;
454
455         memset(&iovec, 0, sizeof(iovec));
456         iovec.iov_base = (char*) state;
457         iovec.iov_len = strlen(state);
458
459         memset(&msghdr, 0, sizeof(msghdr));
460         msghdr.msg_name = &sockaddr;
461         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
462
463         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
464                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
465
466         msghdr.msg_iov = &iovec;
467         msghdr.msg_iovlen = 1;
468
469         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
470                 r = -errno;
471                 goto finish;
472         }
473
474         r = 1;
475
476 finish:
477         if (unset_environment)
478                 unsetenv("NOTIFY_SOCKET");
479
480         if (fd >= 0)
481                 close(fd);
482
483         return r;
484 #endif
485 }
486
487 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
488 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
489         return 0;
490 #else
491         va_list ap;
492         char *p = NULL;
493         int r;
494
495         va_start(ap, format);
496         r = vasprintf(&p, format, ap);
497         va_end(ap);
498
499         if (r < 0 || !p)
500                 return -ENOMEM;
501
502         r = sd_notify(unset_environment, p);
503         free(p);
504
505         return r;
506 #endif
507 }
508
509 _sd_export_ int sd_booted(void) {
510 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
511         return 0;
512 #else
513         struct stat st;
514
515         /* We test whether the runtime unit file directory has been
516          * created. This takes place in mount-setup.c, so is
517          * guaranteed to happen very early during boot. */
518
519         if (lstat("/run/systemd/system/", &st) < 0)
520                 return 0;
521
522         return !!S_ISDIR(st.st_mode);
523 #endif
524 }