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