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