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