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