]> git.sur5r.net Git - i3/i3/blob - src/sd-daemon.c
format **/*.c with clang-format-3.5
[i3/i3] / 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
45 #include "sd-daemon.h"
46
47 int sd_listen_fds(int unset_environment) {
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
117 finish:
118     if (unset_environment) {
119         unsetenv("LISTEN_PID");
120         unsetenv("LISTEN_FDS");
121     }
122
123     return r;
124 #endif
125 }
126
127 int 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             if (errno == ENOENT || errno == ENOTDIR)
146                 return 0;
147
148             return -errno;
149         }
150
151         return st_path.st_dev == st_fd.st_dev &&
152                st_path.st_ino == st_fd.st_ino;
153     }
154
155     return 1;
156 }
157
158 static int sd_is_socket_internal(int fd, int type, int listening) {
159     struct stat st_fd;
160
161     if (fd < 0 || type < 0)
162         return -EINVAL;
163
164     if (fstat(fd, &st_fd) < 0)
165         return -errno;
166
167     if (!S_ISSOCK(st_fd.st_mode))
168         return 0;
169
170     if (type != 0) {
171         int other_type = 0;
172         socklen_t l = sizeof(other_type);
173
174         if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
175             return -errno;
176
177         if (l != sizeof(other_type))
178             return -EINVAL;
179
180         if (other_type != type)
181             return 0;
182     }
183
184     if (listening >= 0) {
185         int accepting = 0;
186         socklen_t l = sizeof(accepting);
187
188         if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
189             return -errno;
190
191         if (l != sizeof(accepting))
192             return -EINVAL;
193
194         if (!accepting != !listening)
195             return 0;
196     }
197
198     return 1;
199 }
200
201 union sockaddr_union {
202     struct sockaddr sa;
203     struct sockaddr_in in4;
204     struct sockaddr_in6 in6;
205     struct sockaddr_un un;
206     struct sockaddr_storage storage;
207 };
208
209 int sd_is_socket(int fd, int family, int type, int listening) {
210     int r;
211
212     if (family < 0)
213         return -EINVAL;
214
215     if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
216         return r;
217
218     if (family > 0) {
219         union sockaddr_union sockaddr;
220         socklen_t l;
221
222         memset(&sockaddr, 0, sizeof(sockaddr));
223         l = sizeof(sockaddr);
224
225         if (getsockname(fd, &sockaddr.sa, &l) < 0)
226             return -errno;
227
228         if (l < sizeof(sa_family_t))
229             return -EINVAL;
230
231         return sockaddr.sa.sa_family == family;
232     }
233
234     return 1;
235 }
236
237 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
238     union sockaddr_union sockaddr;
239     socklen_t l;
240     int r;
241
242     if (family != 0 && family != AF_INET && family != AF_INET6)
243         return -EINVAL;
244
245     if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
246         return r;
247
248     memset(&sockaddr, 0, sizeof(sockaddr));
249     l = sizeof(sockaddr);
250
251     if (getsockname(fd, &sockaddr.sa, &l) < 0)
252         return -errno;
253
254     if (l < sizeof(sa_family_t))
255         return -EINVAL;
256
257     if (sockaddr.sa.sa_family != AF_INET &&
258         sockaddr.sa.sa_family != AF_INET6)
259         return 0;
260
261     if (family > 0)
262         if (sockaddr.sa.sa_family != family)
263             return 0;
264
265     if (port > 0) {
266         if (sockaddr.sa.sa_family == AF_INET) {
267             if (l < sizeof(struct sockaddr_in))
268                 return -EINVAL;
269
270             return htons(port) == sockaddr.in4.sin_port;
271         } else {
272             if (l < sizeof(struct sockaddr_in6))
273                 return -EINVAL;
274
275             return htons(port) == sockaddr.in6.sin6_port;
276         }
277     }
278
279     return 1;
280 }
281
282 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
283     union sockaddr_union sockaddr;
284     socklen_t l;
285     int r;
286
287     if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
288         return r;
289
290     memset(&sockaddr, 0, sizeof(sockaddr));
291     l = sizeof(sockaddr);
292
293     if (getsockname(fd, &sockaddr.sa, &l) < 0)
294         return -errno;
295
296     if (l < sizeof(sa_family_t))
297         return -EINVAL;
298
299     if (sockaddr.sa.sa_family != AF_UNIX)
300         return 0;
301
302     if (path) {
303         if (length <= 0)
304             length = strlen(path);
305
306         if (length <= 0)
307             /* Unnamed socket */
308             return l == offsetof(struct sockaddr_un, sun_path);
309
310         if (path[0])
311             /* Normal path socket */
312             return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
313                    memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
314         else
315             /* Abstract namespace socket */
316             return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
317                    memcmp(path, sockaddr.un.sun_path, length) == 0;
318     }
319
320     return 1;
321 }
322
323 int sd_notify(int unset_environment, const char *state) {
324 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
325     return 0;
326 #else
327     int fd = -1, r;
328     struct msghdr msghdr;
329     struct iovec iovec;
330     union sockaddr_union sockaddr;
331     const char *e;
332
333     if (!state) {
334         r = -EINVAL;
335         goto finish;
336     }
337
338     if (!(e = getenv("NOTIFY_SOCKET")))
339         return 0;
340
341     /* Must be an abstract socket, or an absolute path */
342     if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
343         r = -EINVAL;
344         goto finish;
345     }
346
347     if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
348         r = -errno;
349         goto finish;
350     }
351
352     memset(&sockaddr, 0, sizeof(sockaddr));
353     sockaddr.sa.sa_family = AF_UNIX;
354     strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
355
356     if (sockaddr.un.sun_path[0] == '@')
357         sockaddr.un.sun_path[0] = 0;
358
359     memset(&iovec, 0, sizeof(iovec));
360     iovec.iov_base = (char *)state;
361     iovec.iov_len = strlen(state);
362
363     memset(&msghdr, 0, sizeof(msghdr));
364     msghdr.msg_name = &sockaddr;
365     msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
366
367     if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
368         msghdr.msg_namelen = sizeof(struct sockaddr_un);
369
370     msghdr.msg_iov = &iovec;
371     msghdr.msg_iovlen = 1;
372
373     if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
374         r = -errno;
375         goto finish;
376     }
377
378     r = 1;
379
380 finish:
381     if (unset_environment)
382         unsetenv("NOTIFY_SOCKET");
383
384     if (fd >= 0)
385         close(fd);
386
387     return r;
388 #endif
389 }
390
391 int sd_notifyf(int unset_environment, const char *format, ...) {
392 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
393     return 0;
394 #else
395     va_list ap;
396     char *p = NULL;
397     int r;
398
399     va_start(ap, format);
400     r = vasprintf(&p, format, ap);
401     va_end(ap);
402
403     if (r < 0 || !p)
404         return -ENOMEM;
405
406     r = sd_notify(unset_environment, p);
407     free(p);
408
409     return r;
410 #endif
411 }
412
413 int sd_booted(void) {
414 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
415     return 0;
416 #else
417
418     struct stat a, b;
419
420     /* We simply test whether the systemd cgroup hierarchy is
421          * mounted */
422
423     if (lstat("/sys/fs/cgroup", &a) < 0)
424         return 0;
425
426     if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
427         return 0;
428
429     return a.st_dev != b.st_dev;
430 #endif
431 }