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