]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sd-daemon.c
manager: notify plymouth about progress if it is running
[thirdparty/systemd.git] / src / sd-daemon.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
abbbea81
LP
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
8c47c732
LP
27#ifndef _GNU_SOURCE
28#define _GNU_SOURCE
29#endif
30
7c394faa
LP
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34#include <sys/un.h>
8640e111 35#include <sys/fcntl.h>
7c394faa 36#include <netinet/in.h>
abbbea81
LP
37#include <stdlib.h>
38#include <errno.h>
abbbea81 39#include <unistd.h>
7c394faa 40#include <string.h>
8c47c732
LP
41#include <stdarg.h>
42#include <stdio.h>
abbbea81
LP
43
44#include "sd-daemon.h"
45
46int sd_listen_fds(int unset_environment) {
47
8c47c732 48#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
abbbea81
LP
49 return 0;
50#else
8640e111 51 int r, fd;
abbbea81
LP
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
8640e111
LP
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
abbbea81
LP
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}
7c394faa
LP
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));
fd8bccfb 144 if (stat(path, &st_path) < 0) {
7c394faa
LP
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
88ce42f6 160static int sd_is_socket_internal(int fd, int type, int listening) {
7c394faa
LP
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
dde770cf 196 if (!accepting != !listening)
7c394faa
LP
197 return 0;
198 }
199
200 return 1;
201}
202
88ce42f6
LP
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;
7c394faa
LP
241 socklen_t l;
242 int r;
243
88ce42f6
LP
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)
7c394faa
LP
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
88ce42f6 256 if (l < sizeof(sa_family_t))
7c394faa
LP
257 return -EINVAL;
258
259 if (sockaddr.sa.sa_family != AF_INET &&
260 sockaddr.sa.sa_family != AF_INET6)
261 return 0;
262
88ce42f6
LP
263 if (family > 0)
264 if (sockaddr.sa.sa_family != family)
265 return 0;
266
7c394faa
LP
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) {
88ce42f6 285 union sockaddr_union sockaddr;
7c394faa
LP
286 socklen_t l;
287 int r;
288
88ce42f6 289 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
7c394faa
LP
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
7c394faa
LP
312 if (path[0])
313 /* Normal path socket */
cd250a39
LP
314 return
315 (l >= sizeof(sa_family_t) + length + 1) &&
316 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
7c394faa
LP
317 else
318 /* Abstract namespace socket */
cd250a39
LP
319 return
320 (l == sizeof(sa_family_t) + length) &&
321 memcmp(path, sockaddr.un.sun_path, length) == 0;
7c394faa
LP
322 }
323
324 return 1;
325}
8c47c732
LP
326
327int sd_notify(int unset_environment, const char *state) {
c593cfe1 328#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
8c47c732
LP
329 return 0;
330#else
331 int fd = -1, r;
332 struct msghdr msghdr;
333 struct iovec iovec;
334 union sockaddr_union sockaddr;
8c47c732
LP
335 const char *e;
336
337 if (!state) {
338 r = -EINVAL;
339 goto finish;
340 }
341
08bfb810
LP
342 if (!(e = getenv("NOTIFY_SOCKET")))
343 return 0;
8c47c732
LP
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
8c47c732
LP
367 memset(&msghdr, 0, sizeof(msghdr));
368 msghdr.msg_name = &sockaddr;
a013bd94
LP
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
8c47c732
LP
374 msghdr.msg_iov = &iovec;
375 msghdr.msg_iovlen = 1;
8c47c732
LP
376
377 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
378 r = -errno;
379 goto finish;
380 }
381
08bfb810 382 r = 1;
8c47c732
LP
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}
40473a70
LP
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
77d5f105 427 if (lstat("/sys/fs/cgroup", &a) < 0)
40473a70
LP
428 return 0;
429
77d5f105 430 if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
40473a70
LP
431 return 0;
432
433 return a.st_dev != b.st_dev;
434#endif
435}