chiark / gitweb /
journal: fix memory leak
[elogind.git] / src / 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 || 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) {
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         memset(&st_fd, 0, sizeof(st_fd));
156         if (fstat(fd, &st_fd) < 0)
157                 return -errno;
158
159         if (!S_ISFIFO(st_fd.st_mode))
160                 return 0;
161
162         if (path) {
163                 struct stat st_path;
164
165                 memset(&st_path, 0, sizeof(st_path));
166                 if (stat(path, &st_path) < 0) {
167
168                         if (errno == ENOENT || errno == ENOTDIR)
169                                 return 0;
170
171                         return -errno;
172                 }
173
174                 return
175                         st_path.st_dev == st_fd.st_dev &&
176                         st_path.st_ino == st_fd.st_ino;
177         }
178
179         return 1;
180 }
181
182 _sd_export_ int sd_is_special(int fd, const char *path) {
183         struct stat st_fd;
184
185         if (fd < 0)
186                 return -EINVAL;
187
188         if (fstat(fd, &st_fd) < 0)
189                 return -errno;
190
191         if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
192                 return 0;
193
194         if (path) {
195                 struct stat st_path;
196
197                 if (stat(path, &st_path) < 0) {
198
199                         if (errno == ENOENT || errno == ENOTDIR)
200                                 return 0;
201
202                         return -errno;
203                 }
204
205                 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
206                         return
207                                 st_path.st_dev == st_fd.st_dev &&
208                                 st_path.st_ino == st_fd.st_ino;
209                 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
210                         return st_path.st_rdev == st_fd.st_rdev;
211                 else
212                         return 0;
213         }
214
215         return 1;
216 }
217
218 static int sd_is_socket_internal(int fd, int type, int listening) {
219         struct stat st_fd;
220
221         if (fd < 0 || type < 0)
222                 return -EINVAL;
223
224         if (fstat(fd, &st_fd) < 0)
225                 return -errno;
226
227         if (!S_ISSOCK(st_fd.st_mode))
228                 return 0;
229
230         if (type != 0) {
231                 int other_type = 0;
232                 socklen_t l = sizeof(other_type);
233
234                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
235                         return -errno;
236
237                 if (l != sizeof(other_type))
238                         return -EINVAL;
239
240                 if (other_type != type)
241                         return 0;
242         }
243
244         if (listening >= 0) {
245                 int accepting = 0;
246                 socklen_t l = sizeof(accepting);
247
248                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
249                         return -errno;
250
251                 if (l != sizeof(accepting))
252                         return -EINVAL;
253
254                 if (!accepting != !listening)
255                         return 0;
256         }
257
258         return 1;
259 }
260
261 union sockaddr_union {
262         struct sockaddr sa;
263         struct sockaddr_in in4;
264         struct sockaddr_in6 in6;
265         struct sockaddr_un un;
266         struct sockaddr_storage storage;
267 };
268
269 _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
270         int r;
271
272         if (family < 0)
273                 return -EINVAL;
274
275         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
276                 return r;
277
278         if (family > 0) {
279                 union sockaddr_union sockaddr;
280                 socklen_t l;
281
282                 memset(&sockaddr, 0, sizeof(sockaddr));
283                 l = sizeof(sockaddr);
284
285                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
286                         return -errno;
287
288                 if (l < sizeof(sa_family_t))
289                         return -EINVAL;
290
291                 return sockaddr.sa.sa_family == family;
292         }
293
294         return 1;
295 }
296
297 _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
298         union sockaddr_union sockaddr;
299         socklen_t l;
300         int r;
301
302         if (family != 0 && family != AF_INET && family != AF_INET6)
303                 return -EINVAL;
304
305         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
306                 return r;
307
308         memset(&sockaddr, 0, sizeof(sockaddr));
309         l = sizeof(sockaddr);
310
311         if (getsockname(fd, &sockaddr.sa, &l) < 0)
312                 return -errno;
313
314         if (l < sizeof(sa_family_t))
315                 return -EINVAL;
316
317         if (sockaddr.sa.sa_family != AF_INET &&
318             sockaddr.sa.sa_family != AF_INET6)
319                 return 0;
320
321         if (family > 0)
322                 if (sockaddr.sa.sa_family != family)
323                         return 0;
324
325         if (port > 0) {
326                 if (sockaddr.sa.sa_family == AF_INET) {
327                         if (l < sizeof(struct sockaddr_in))
328                                 return -EINVAL;
329
330                         return htons(port) == sockaddr.in4.sin_port;
331                 } else {
332                         if (l < sizeof(struct sockaddr_in6))
333                                 return -EINVAL;
334
335                         return htons(port) == sockaddr.in6.sin6_port;
336                 }
337         }
338
339         return 1;
340 }
341
342 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
343         union sockaddr_union sockaddr;
344         socklen_t l;
345         int r;
346
347         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
348                 return r;
349
350         memset(&sockaddr, 0, sizeof(sockaddr));
351         l = sizeof(sockaddr);
352
353         if (getsockname(fd, &sockaddr.sa, &l) < 0)
354                 return -errno;
355
356         if (l < sizeof(sa_family_t))
357                 return -EINVAL;
358
359         if (sockaddr.sa.sa_family != AF_UNIX)
360                 return 0;
361
362         if (path) {
363                 if (length <= 0)
364                         length = strlen(path);
365
366                 if (length <= 0)
367                         /* Unnamed socket */
368                         return l == offsetof(struct sockaddr_un, sun_path);
369
370                 if (path[0])
371                         /* Normal path socket */
372                         return
373                                 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
374                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
375                 else
376                         /* Abstract namespace socket */
377                         return
378                                 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
379                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
380         }
381
382         return 1;
383 }
384
385 _sd_export_ int sd_is_mq(int fd, const char *path) {
386 #if !defined(__linux__)
387         return 0;
388 #else
389         struct mq_attr attr;
390
391         if (fd < 0)
392                 return -EINVAL;
393
394         if (mq_getattr(fd, &attr) < 0)
395                 return -errno;
396
397         if (path) {
398                 char fpath[PATH_MAX];
399                 struct stat a, b;
400
401                 if (path[0] != '/')
402                         return -EINVAL;
403
404                 if (fstat(fd, &a) < 0)
405                         return -errno;
406
407                 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
408                 fpath[sizeof(fpath)-1] = 0;
409
410                 if (stat(fpath, &b) < 0)
411                         return -errno;
412
413                 if (a.st_dev != b.st_dev ||
414                     a.st_ino != b.st_ino)
415                         return 0;
416         }
417
418         return 1;
419 #endif
420 }
421
422 _sd_export_ int sd_notify(int unset_environment, const char *state) {
423 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
424         return 0;
425 #else
426         int fd = -1, r;
427         struct msghdr msghdr;
428         struct iovec iovec;
429         union sockaddr_union sockaddr;
430         const char *e;
431
432         if (!state) {
433                 r = -EINVAL;
434                 goto finish;
435         }
436
437         if (!(e = getenv("NOTIFY_SOCKET")))
438                 return 0;
439
440         /* Must be an abstract socket, or an absolute path */
441         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
442                 r = -EINVAL;
443                 goto finish;
444         }
445
446         if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
447                 r = -errno;
448                 goto finish;
449         }
450
451         memset(&sockaddr, 0, sizeof(sockaddr));
452         sockaddr.sa.sa_family = AF_UNIX;
453         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
454
455         if (sockaddr.un.sun_path[0] == '@')
456                 sockaddr.un.sun_path[0] = 0;
457
458         memset(&iovec, 0, sizeof(iovec));
459         iovec.iov_base = (char*) state;
460         iovec.iov_len = strlen(state);
461
462         memset(&msghdr, 0, sizeof(msghdr));
463         msghdr.msg_name = &sockaddr;
464         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
465
466         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
467                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
468
469         msghdr.msg_iov = &iovec;
470         msghdr.msg_iovlen = 1;
471
472         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
473                 r = -errno;
474                 goto finish;
475         }
476
477         r = 1;
478
479 finish:
480         if (unset_environment)
481                 unsetenv("NOTIFY_SOCKET");
482
483         if (fd >= 0)
484                 close(fd);
485
486         return r;
487 #endif
488 }
489
490 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
491 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
492         return 0;
493 #else
494         va_list ap;
495         char *p = NULL;
496         int r;
497
498         va_start(ap, format);
499         r = vasprintf(&p, format, ap);
500         va_end(ap);
501
502         if (r < 0 || !p)
503                 return -ENOMEM;
504
505         r = sd_notify(unset_environment, p);
506         free(p);
507
508         return r;
509 #endif
510 }
511
512 _sd_export_ int sd_booted(void) {
513 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
514         return 0;
515 #else
516
517         struct stat a, b;
518
519         /* We simply test whether the systemd cgroup hierarchy is
520          * mounted */
521
522         if (lstat("/sys/fs/cgroup", &a) < 0)
523                 return 0;
524
525         if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
526                 return 0;
527
528         return a.st_dev != b.st_dev;
529 #endif
530 }