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