]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-daemon/sd-daemon.c
Merge pull request #1215 from maciejaszek/fds_pass_fixes
[thirdparty/systemd.git] / src / libsystemd / sd-daemon / sd-daemon.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <netinet/in.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stddef.h>
33 #include <limits.h>
34 #include <mqueue.h>
35
36 #include "util.h"
37 #include "path-util.h"
38 #include "socket-util.h"
39 #include "sd-daemon.h"
40
41 _public_ int sd_listen_fds(int unset_environment) {
42 const char *e;
43 unsigned n;
44 int r, fd;
45 pid_t pid;
46
47 e = getenv("LISTEN_PID");
48 if (!e) {
49 r = 0;
50 goto finish;
51 }
52
53 r = parse_pid(e, &pid);
54 if (r < 0)
55 goto finish;
56
57 /* Is this for us? */
58 if (getpid() != pid) {
59 r = 0;
60 goto finish;
61 }
62
63 e = getenv("LISTEN_FDS");
64 if (!e) {
65 r = 0;
66 goto finish;
67 }
68
69 r = safe_atou(e, &n);
70 if (r < 0)
71 goto finish;
72
73 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) n; fd ++) {
74 r = fd_cloexec(fd, true);
75 if (r < 0)
76 goto finish;
77 }
78
79 r = (int) n;
80
81 finish:
82 if (unset_environment) {
83 unsetenv("LISTEN_PID");
84 unsetenv("LISTEN_FDS");
85 }
86
87 return r;
88 }
89
90 _public_ int sd_is_fifo(int fd, const char *path) {
91 struct stat st_fd;
92
93 assert_return(fd >= 0, -EBADF);
94
95 if (fstat(fd, &st_fd) < 0)
96 return -errno;
97
98 if (!S_ISFIFO(st_fd.st_mode))
99 return 0;
100
101 if (path) {
102 struct stat st_path;
103
104 if (stat(path, &st_path) < 0) {
105
106 if (errno == ENOENT || errno == ENOTDIR)
107 return 0;
108
109 return -errno;
110 }
111
112 return
113 st_path.st_dev == st_fd.st_dev &&
114 st_path.st_ino == st_fd.st_ino;
115 }
116
117 return 1;
118 }
119
120 _public_ int sd_is_special(int fd, const char *path) {
121 struct stat st_fd;
122
123 assert_return(fd >= 0, -EBADF);
124
125 if (fstat(fd, &st_fd) < 0)
126 return -errno;
127
128 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
129 return 0;
130
131 if (path) {
132 struct stat st_path;
133
134 if (stat(path, &st_path) < 0) {
135
136 if (errno == ENOENT || errno == ENOTDIR)
137 return 0;
138
139 return -errno;
140 }
141
142 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
143 return
144 st_path.st_dev == st_fd.st_dev &&
145 st_path.st_ino == st_fd.st_ino;
146 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
147 return st_path.st_rdev == st_fd.st_rdev;
148 else
149 return 0;
150 }
151
152 return 1;
153 }
154
155 static int sd_is_socket_internal(int fd, int type, int listening) {
156 struct stat st_fd;
157
158 assert_return(fd >= 0, -EBADF);
159 assert_return(type >= 0, -EINVAL);
160
161 if (fstat(fd, &st_fd) < 0)
162 return -errno;
163
164 if (!S_ISSOCK(st_fd.st_mode))
165 return 0;
166
167 if (type != 0) {
168 int other_type = 0;
169 socklen_t l = sizeof(other_type);
170
171 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
172 return -errno;
173
174 if (l != sizeof(other_type))
175 return -EINVAL;
176
177 if (other_type != type)
178 return 0;
179 }
180
181 if (listening >= 0) {
182 int accepting = 0;
183 socklen_t l = sizeof(accepting);
184
185 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
186 return -errno;
187
188 if (l != sizeof(accepting))
189 return -EINVAL;
190
191 if (!accepting != !listening)
192 return 0;
193 }
194
195 return 1;
196 }
197
198 _public_ int sd_is_socket(int fd, int family, int type, int listening) {
199 int r;
200
201 assert_return(fd >= 0, -EBADF);
202 assert_return(family >= 0, -EINVAL);
203
204 r = sd_is_socket_internal(fd, type, listening);
205 if (r <= 0)
206 return r;
207
208 if (family > 0) {
209 union sockaddr_union sockaddr = {};
210 socklen_t l = sizeof(sockaddr);
211
212 if (getsockname(fd, &sockaddr.sa, &l) < 0)
213 return -errno;
214
215 if (l < sizeof(sa_family_t))
216 return -EINVAL;
217
218 return sockaddr.sa.sa_family == family;
219 }
220
221 return 1;
222 }
223
224 _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
225 union sockaddr_union sockaddr = {};
226 socklen_t l = sizeof(sockaddr);
227 int r;
228
229 assert_return(fd >= 0, -EBADF);
230 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
231
232 r = sd_is_socket_internal(fd, type, listening);
233 if (r <= 0)
234 return r;
235
236 if (getsockname(fd, &sockaddr.sa, &l) < 0)
237 return -errno;
238
239 if (l < sizeof(sa_family_t))
240 return -EINVAL;
241
242 if (sockaddr.sa.sa_family != AF_INET &&
243 sockaddr.sa.sa_family != AF_INET6)
244 return 0;
245
246 if (family != 0)
247 if (sockaddr.sa.sa_family != family)
248 return 0;
249
250 if (port > 0) {
251 if (sockaddr.sa.sa_family == AF_INET) {
252 if (l < sizeof(struct sockaddr_in))
253 return -EINVAL;
254
255 return htons(port) == sockaddr.in.sin_port;
256 } else {
257 if (l < sizeof(struct sockaddr_in6))
258 return -EINVAL;
259
260 return htons(port) == sockaddr.in6.sin6_port;
261 }
262 }
263
264 return 1;
265 }
266
267 _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
268 union sockaddr_union sockaddr = {};
269 socklen_t l = sizeof(sockaddr);
270 int r;
271
272 assert_return(fd >= 0, -EBADF);
273
274 r = sd_is_socket_internal(fd, type, listening);
275 if (r <= 0)
276 return r;
277
278 if (getsockname(fd, &sockaddr.sa, &l) < 0)
279 return -errno;
280
281 if (l < sizeof(sa_family_t))
282 return -EINVAL;
283
284 if (sockaddr.sa.sa_family != AF_UNIX)
285 return 0;
286
287 if (path) {
288 if (length == 0)
289 length = strlen(path);
290
291 if (length == 0)
292 /* Unnamed socket */
293 return l == offsetof(struct sockaddr_un, sun_path);
294
295 if (path[0])
296 /* Normal path socket */
297 return
298 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
299 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
300 else
301 /* Abstract namespace socket */
302 return
303 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
304 memcmp(path, sockaddr.un.sun_path, length) == 0;
305 }
306
307 return 1;
308 }
309
310 _public_ int sd_is_mq(int fd, const char *path) {
311 struct mq_attr attr;
312
313 assert_return(fd >= 0, -EBADF);
314
315 if (mq_getattr(fd, &attr) < 0)
316 return -errno;
317
318 if (path) {
319 char fpath[PATH_MAX];
320 struct stat a, b;
321
322 assert_return(path_is_absolute(path), -EINVAL);
323
324 if (fstat(fd, &a) < 0)
325 return -errno;
326
327 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
328 fpath[sizeof(fpath)-1] = 0;
329
330 if (stat(fpath, &b) < 0)
331 return -errno;
332
333 if (a.st_dev != b.st_dev ||
334 a.st_ino != b.st_ino)
335 return 0;
336 }
337
338 return 1;
339 }
340
341 _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
342 union sockaddr_union sockaddr = {
343 .sa.sa_family = AF_UNIX,
344 };
345 struct iovec iovec = {
346 .iov_base = (char*) state,
347 };
348 struct msghdr msghdr = {
349 .msg_iov = &iovec,
350 .msg_iovlen = 1,
351 .msg_name = &sockaddr,
352 };
353 _cleanup_close_ int fd = -1;
354 struct cmsghdr *cmsg = NULL;
355 const char *e;
356 bool have_pid;
357 int r;
358
359 if (!state) {
360 r = -EINVAL;
361 goto finish;
362 }
363
364 if (n_fds > 0 && !fds) {
365 r = -EINVAL;
366 goto finish;
367 }
368
369 e = getenv("NOTIFY_SOCKET");
370 if (!e)
371 return 0;
372
373 /* Must be an abstract socket, or an absolute path */
374 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
375 r = -EINVAL;
376 goto finish;
377 }
378
379 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
380 if (fd < 0) {
381 r = -errno;
382 goto finish;
383 }
384
385 iovec.iov_len = strlen(state);
386
387 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
388 if (sockaddr.un.sun_path[0] == '@')
389 sockaddr.un.sun_path[0] = 0;
390
391 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
392 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
393 msghdr.msg_namelen = sizeof(struct sockaddr_un);
394
395 have_pid = pid != 0 && pid != getpid();
396
397 if (n_fds > 0 || have_pid) {
398 /* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */
399 msghdr.msg_controllen = (n_fds ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
400 CMSG_SPACE(sizeof(struct ucred)) * have_pid;
401 msghdr.msg_control = alloca(msghdr.msg_controllen);
402
403 cmsg = CMSG_FIRSTHDR(&msghdr);
404 if (n_fds > 0) {
405 cmsg->cmsg_level = SOL_SOCKET;
406 cmsg->cmsg_type = SCM_RIGHTS;
407 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
408
409 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
410
411 if (have_pid)
412 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
413 }
414
415 if (have_pid) {
416 struct ucred *ucred;
417
418 cmsg->cmsg_level = SOL_SOCKET;
419 cmsg->cmsg_type = SCM_CREDENTIALS;
420 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
421
422 ucred = (struct ucred*) CMSG_DATA(cmsg);
423 ucred->pid = pid;
424 ucred->uid = getuid();
425 ucred->gid = getgid();
426 }
427 }
428
429 /* First try with fake ucred data, as requested */
430 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
431 r = 1;
432 goto finish;
433 }
434
435 /* If that failed, try with our own ucred instead */
436 if (have_pid) {
437 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
438 if (msghdr.msg_controllen == 0)
439 msghdr.msg_control = NULL;
440
441 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
442 r = 1;
443 goto finish;
444 }
445 }
446
447 r = -errno;
448
449 finish:
450 if (unset_environment)
451 unsetenv("NOTIFY_SOCKET");
452
453 return r;
454 }
455
456 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
457 return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
458 }
459
460 _public_ int sd_notify(int unset_environment, const char *state) {
461 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
462 }
463
464 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
465 _cleanup_free_ char *p = NULL;
466 int r;
467
468 if (format) {
469 va_list ap;
470
471 va_start(ap, format);
472 r = vasprintf(&p, format, ap);
473 va_end(ap);
474
475 if (r < 0 || !p)
476 return -ENOMEM;
477 }
478
479 return sd_pid_notify(pid, unset_environment, p);
480 }
481
482 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
483 _cleanup_free_ char *p = NULL;
484 int r;
485
486 if (format) {
487 va_list ap;
488
489 va_start(ap, format);
490 r = vasprintf(&p, format, ap);
491 va_end(ap);
492
493 if (r < 0 || !p)
494 return -ENOMEM;
495 }
496
497 return sd_pid_notify(0, unset_environment, p);
498 }
499
500 _public_ int sd_booted(void) {
501 struct stat st;
502
503 /* We test whether the runtime unit file directory has been
504 * created. This takes place in mount-setup.c, so is
505 * guaranteed to happen very early during boot. */
506
507 if (lstat("/run/systemd/system/", &st) < 0)
508 return 0;
509
510 return !!S_ISDIR(st.st_mode);
511 }
512
513 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
514 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
515 uint64_t u;
516 int r = 0;
517
518 s = getenv("WATCHDOG_USEC");
519 if (!s)
520 goto finish;
521
522 r = safe_atou64(s, &u);
523 if (r < 0)
524 goto finish;
525 if (u <= 0) {
526 r = -EINVAL;
527 goto finish;
528 }
529
530 p = getenv("WATCHDOG_PID");
531 if (p) {
532 pid_t pid;
533
534 r = parse_pid(p, &pid);
535 if (r < 0)
536 goto finish;
537
538 /* Is this for us? */
539 if (getpid() != pid) {
540 r = 0;
541 goto finish;
542 }
543 }
544
545 if (usec)
546 *usec = u;
547
548 r = 1;
549
550 finish:
551 if (unset_environment && s)
552 unsetenv("WATCHDOG_USEC");
553 if (unset_environment && p)
554 unsetenv("WATCHDOG_PID");
555
556 return r;
557 }