chiark / gitweb /
don't make up buffer sizes, use standard LINE_MAX instead
[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
45 #include "sd-daemon.h"
46
47 int 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
118 finish:
119         if (unset_environment) {
120                 unsetenv("LISTEN_PID");
121                 unsetenv("LISTEN_FDS");
122         }
123
124         return r;
125 #endif
126 }
127
128 int 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
161 static 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
204 union 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
212 int 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
240 int 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
285 int 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
328 int 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
385 finish:
386         if (unset_environment)
387                 unsetenv("NOTIFY_SOCKET");
388
389         if (fd >= 0)
390                 close(fd);
391
392         return r;
393 #endif
394 }
395
396 int 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
418 int 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 }