chiark / gitweb /
480db3bbb9dab9eb85a8497abe1a32d97c459afe
[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         if (!(e = getenv("LISTEN_PID"))) {
79                 r = 0;
80                 goto finish;
81         }
82
83         errno = 0;
84         l = strtoul(e, &p, 10);
85
86         if (errno != 0) {
87                 r = -errno;
88                 goto finish;
89         }
90
91         if (!p || p == e || *p || l <= 0) {
92                 r = -EINVAL;
93                 goto finish;
94         }
95
96         /* Is this for us? */
97         if (getpid() != (pid_t) l) {
98                 r = 0;
99                 goto finish;
100         }
101
102         if (!(e = getenv("LISTEN_FDS"))) {
103                 r = 0;
104                 goto finish;
105         }
106
107         errno = 0;
108         l = strtoul(e, &p, 10);
109
110         if (errno != 0) {
111                 r = -errno;
112                 goto finish;
113         }
114
115         if (!p || p == e || *p) {
116                 r = -EINVAL;
117                 goto finish;
118         }
119
120         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
121                 int flags;
122
123                 if ((flags = fcntl(fd, F_GETFD)) < 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         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
274                 return r;
275
276         if (family > 0) {
277                 union sockaddr_union sockaddr;
278                 socklen_t l;
279
280                 memset(&sockaddr, 0, sizeof(sockaddr));
281                 l = sizeof(sockaddr);
282
283                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
284                         return -errno;
285
286                 if (l < sizeof(sa_family_t))
287                         return -EINVAL;
288
289                 return sockaddr.sa.sa_family == family;
290         }
291
292         return 1;
293 }
294
295 _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
296         union sockaddr_union sockaddr;
297         socklen_t l;
298         int r;
299
300         if (family != 0 && family != AF_INET && family != AF_INET6)
301                 return -EINVAL;
302
303         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
304                 return r;
305
306         memset(&sockaddr, 0, sizeof(sockaddr));
307         l = sizeof(sockaddr);
308
309         if (getsockname(fd, &sockaddr.sa, &l) < 0)
310                 return -errno;
311
312         if (l < sizeof(sa_family_t))
313                 return -EINVAL;
314
315         if (sockaddr.sa.sa_family != AF_INET &&
316             sockaddr.sa.sa_family != AF_INET6)
317                 return 0;
318
319         if (family > 0)
320                 if (sockaddr.sa.sa_family != family)
321                         return 0;
322
323         if (port > 0) {
324                 if (sockaddr.sa.sa_family == AF_INET) {
325                         if (l < sizeof(struct sockaddr_in))
326                                 return -EINVAL;
327
328                         return htons(port) == sockaddr.in4.sin_port;
329                 } else {
330                         if (l < sizeof(struct sockaddr_in6))
331                                 return -EINVAL;
332
333                         return htons(port) == sockaddr.in6.sin6_port;
334                 }
335         }
336
337         return 1;
338 }
339
340 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
341         union sockaddr_union sockaddr;
342         socklen_t l;
343         int r;
344
345         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
346                 return r;
347
348         memset(&sockaddr, 0, sizeof(sockaddr));
349         l = sizeof(sockaddr);
350
351         if (getsockname(fd, &sockaddr.sa, &l) < 0)
352                 return -errno;
353
354         if (l < sizeof(sa_family_t))
355                 return -EINVAL;
356
357         if (sockaddr.sa.sa_family != AF_UNIX)
358                 return 0;
359
360         if (path) {
361                 if (length <= 0)
362                         length = strlen(path);
363
364                 if (length <= 0)
365                         /* Unnamed socket */
366                         return l == offsetof(struct sockaddr_un, sun_path);
367
368                 if (path[0])
369                         /* Normal path socket */
370                         return
371                                 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
372                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
373                 else
374                         /* Abstract namespace socket */
375                         return
376                                 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
377                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
378         }
379
380         return 1;
381 }
382
383 _sd_export_ int sd_is_mq(int fd, const char *path) {
384 #if !defined(__linux__)
385         return 0;
386 #else
387         struct mq_attr attr;
388
389         if (fd < 0)
390                 return -EINVAL;
391
392         if (mq_getattr(fd, &attr) < 0)
393                 return -errno;
394
395         if (path) {
396                 char fpath[PATH_MAX];
397                 struct stat a, b;
398
399                 if (path[0] != '/')
400                         return -EINVAL;
401
402                 if (fstat(fd, &a) < 0)
403                         return -errno;
404
405                 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
406                 fpath[sizeof(fpath)-1] = 0;
407
408                 if (stat(fpath, &b) < 0)
409                         return -errno;
410
411                 if (a.st_dev != b.st_dev ||
412                     a.st_ino != b.st_ino)
413                         return 0;
414         }
415
416         return 1;
417 #endif
418 }
419
420 _sd_export_ int sd_notify(int unset_environment, const char *state) {
421 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
422         return 0;
423 #else
424         int fd = -1, r;
425         struct msghdr msghdr;
426         struct iovec iovec;
427         union sockaddr_union sockaddr;
428         const char *e;
429
430         if (!state) {
431                 r = -EINVAL;
432                 goto finish;
433         }
434
435         if (!(e = getenv("NOTIFY_SOCKET")))
436                 return 0;
437
438         /* Must be an abstract socket, or an absolute path */
439         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
440                 r = -EINVAL;
441                 goto finish;
442         }
443
444         if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
445                 r = -errno;
446                 goto finish;
447         }
448
449         memset(&sockaddr, 0, sizeof(sockaddr));
450         sockaddr.sa.sa_family = AF_UNIX;
451         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
452
453         if (sockaddr.un.sun_path[0] == '@')
454                 sockaddr.un.sun_path[0] = 0;
455
456         memset(&iovec, 0, sizeof(iovec));
457         iovec.iov_base = (char*) state;
458         iovec.iov_len = strlen(state);
459
460         memset(&msghdr, 0, sizeof(msghdr));
461         msghdr.msg_name = &sockaddr;
462         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
463
464         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
465                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
466
467         msghdr.msg_iov = &iovec;
468         msghdr.msg_iovlen = 1;
469
470         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
471                 r = -errno;
472                 goto finish;
473         }
474
475         r = 1;
476
477 finish:
478         if (unset_environment)
479                 unsetenv("NOTIFY_SOCKET");
480
481         if (fd >= 0)
482                 close(fd);
483
484         return r;
485 #endif
486 }
487
488 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
489 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
490         return 0;
491 #else
492         va_list ap;
493         char *p = NULL;
494         int r;
495
496         va_start(ap, format);
497         r = vasprintf(&p, format, ap);
498         va_end(ap);
499
500         if (r < 0 || !p)
501                 return -ENOMEM;
502
503         r = sd_notify(unset_environment, p);
504         free(p);
505
506         return r;
507 #endif
508 }
509
510 _sd_export_ int sd_booted(void) {
511 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
512         return 0;
513 #else
514
515         struct stat a, b;
516
517         /* We simply test whether the systemd cgroup hierarchy is
518          * mounted */
519
520         if (lstat("/sys/fs/cgroup", &a) < 0)
521                 return 0;
522
523         if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
524                 return 0;
525
526         return a.st_dev != b.st_dev;
527 #endif
528 }