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