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