]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-daemon/sd-daemon.c
systemctl: make "systemctl is-system-running" return "offline" if we are not booted...
[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/stat.h>
23#include <sys/socket.h>
24#include <sys/un.h>
25#include <netinet/in.h>
abbbea81
LP
26#include <stdlib.h>
27#include <errno.h>
abbbea81 28#include <unistd.h>
7c394faa 29#include <string.h>
8c47c732
LP
30#include <stdarg.h>
31#include <stdio.h>
0e098b15 32#include <stddef.h>
916abb21 33#include <limits.h>
0ebee881 34#include <mqueue.h>
abbbea81 35
29804cc1 36#include "util.h"
be8f4e9e 37#include "path-util.h"
3cb46740 38#include "socket-util.h"
abbbea81
LP
39#include "sd-daemon.h"
40
0ebee881 41_public_ int sd_listen_fds(int unset_environment) {
abbbea81 42 const char *e;
be8f4e9e
LP
43 unsigned n;
44 int r, fd;
45 pid_t pid;
abbbea81 46
50425d16
MS
47 e = getenv("LISTEN_PID");
48 if (!e) {
abbbea81
LP
49 r = 0;
50 goto finish;
51 }
52
be8f4e9e
LP
53 r = parse_pid(e, &pid);
54 if (r < 0)
abbbea81 55 goto finish;
abbbea81
LP
56
57 /* Is this for us? */
be8f4e9e 58 if (getpid() != pid) {
abbbea81
LP
59 r = 0;
60 goto finish;
61 }
62
50425d16
MS
63 e = getenv("LISTEN_FDS");
64 if (!e) {
abbbea81
LP
65 r = 0;
66 goto finish;
67 }
68
be8f4e9e
LP
69 r = safe_atou(e, &n);
70 if (r < 0)
abbbea81 71 goto finish;
8640e111 72
be8f4e9e
LP
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)
8640e111 76 goto finish;
8640e111
LP
77 }
78
be8f4e9e 79 r = (int) n;
abbbea81
LP
80
81finish:
82 if (unset_environment) {
83 unsetenv("LISTEN_PID");
84 unsetenv("LISTEN_FDS");
85 }
86
87 return r;
abbbea81 88}
7c394faa 89
0ebee881 90_public_ int sd_is_fifo(int fd, const char *path) {
7c394faa
LP
91 struct stat st_fd;
92
e6803801 93 assert_return(fd >= 0, -EBADF);
7c394faa 94
7c394faa
LP
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
fd8bccfb 104 if (stat(path, &st_path) < 0) {
7c394faa
LP
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
0ebee881 120_public_ int sd_is_special(int fd, const char *path) {
4160ec67
WD
121 struct stat st_fd;
122
e6803801 123 assert_return(fd >= 0, -EBADF);
4160ec67
WD
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
e6a3081a 155static int sd_is_socket_internal(int fd, int type, int listening) {
7c394faa
LP
156 struct stat st_fd;
157
e6803801 158 assert_return(fd >= 0, -EBADF);
be8f4e9e 159 assert_return(type >= 0, -EINVAL);
7c394faa
LP
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
dde770cf 191 if (!accepting != !listening)
7c394faa
LP
192 return 0;
193 }
194
195 return 1;
196}
197
0ebee881 198_public_ int sd_is_socket(int fd, int family, int type, int listening) {
88ce42f6
LP
199 int r;
200
e6803801 201 assert_return(fd >= 0, -EBADF);
be8f4e9e 202 assert_return(family >= 0, -EINVAL);
88ce42f6 203
50425d16
MS
204 r = sd_is_socket_internal(fd, type, listening);
205 if (r <= 0)
88ce42f6
LP
206 return r;
207
208 if (family > 0) {
1c633045
ZJS
209 union sockaddr_union sockaddr = {};
210 socklen_t l = sizeof(sockaddr);
88ce42f6
LP
211
212 if (getsockname(fd, &sockaddr.sa, &l) < 0)
213 return -errno;
214
b7f42664 215 if (l < sizeof(sa_family_t))
88ce42f6
LP
216 return -EINVAL;
217
218 return sockaddr.sa.sa_family == family;
219 }
220
221 return 1;
222}
223
0ebee881 224_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
1c633045
ZJS
225 union sockaddr_union sockaddr = {};
226 socklen_t l = sizeof(sockaddr);
7c394faa
LP
227 int r;
228
e6803801 229 assert_return(fd >= 0, -EBADF);
be8f4e9e 230 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
88ce42f6 231
50425d16
MS
232 r = sd_is_socket_internal(fd, type, listening);
233 if (r <= 0)
7c394faa
LP
234 return r;
235
7c394faa
LP
236 if (getsockname(fd, &sockaddr.sa, &l) < 0)
237 return -errno;
238
b7f42664 239 if (l < sizeof(sa_family_t))
7c394faa
LP
240 return -EINVAL;
241
242 if (sockaddr.sa.sa_family != AF_INET &&
243 sockaddr.sa.sa_family != AF_INET6)
244 return 0;
245
be8f4e9e 246 if (family != 0)
88ce42f6
LP
247 if (sockaddr.sa.sa_family != family)
248 return 0;
249
7c394faa
LP
250 if (port > 0) {
251 if (sockaddr.sa.sa_family == AF_INET) {
252 if (l < sizeof(struct sockaddr_in))
253 return -EINVAL;
254
3cb46740 255 return htons(port) == sockaddr.in.sin_port;
7c394faa
LP
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
0ebee881 267_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
1c633045
ZJS
268 union sockaddr_union sockaddr = {};
269 socklen_t l = sizeof(sockaddr);
7c394faa
LP
270 int r;
271
e6803801 272 assert_return(fd >= 0, -EBADF);
be8f4e9e 273
50425d16
MS
274 r = sd_is_socket_internal(fd, type, listening);
275 if (r <= 0)
7c394faa
LP
276 return r;
277
7c394faa
LP
278 if (getsockname(fd, &sockaddr.sa, &l) < 0)
279 return -errno;
280
b7f42664 281 if (l < sizeof(sa_family_t))
7c394faa
LP
282 return -EINVAL;
283
284 if (sockaddr.sa.sa_family != AF_UNIX)
285 return 0;
286
287 if (path) {
d1d7caee 288 if (length == 0)
7c394faa
LP
289 length = strlen(path);
290
d1d7caee 291 if (length == 0)
7c394faa 292 /* Unnamed socket */
0e098b15 293 return l == offsetof(struct sockaddr_un, sun_path);
7c394faa 294
7c394faa
LP
295 if (path[0])
296 /* Normal path socket */
cd250a39 297 return
0e098b15 298 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
cd250a39 299 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
7c394faa
LP
300 else
301 /* Abstract namespace socket */
cd250a39 302 return
0e098b15 303 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
cd250a39 304 memcmp(path, sockaddr.un.sun_path, length) == 0;
7c394faa
LP
305 }
306
307 return 1;
308}
8c47c732 309
0ebee881 310_public_ int sd_is_mq(int fd, const char *path) {
916abb21
LP
311 struct mq_attr attr;
312
e6803801 313 assert_return(fd >= 0, -EBADF);
916abb21
LP
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
be8f4e9e 322 assert_return(path_is_absolute(path), -EINVAL);
916abb21
LP
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;
916abb21
LP
339}
340
a354329f
LP
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 };
a354329f
LP
353 _cleanup_close_ int fd = -1;
354 struct cmsghdr *cmsg = NULL;
355 const char *e;
64144440 356 bool have_pid;
be8f4e9e 357 int r;
8c47c732
LP
358
359 if (!state) {
360 r = -EINVAL;
361 goto finish;
362 }
363
a354329f
LP
364 if (n_fds > 0 && !fds) {
365 r = -EINVAL;
366 goto finish;
367 }
368
50425d16
MS
369 e = getenv("NOTIFY_SOCKET");
370 if (!e)
08bfb810 371 return 0;
8c47c732
LP
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
50425d16
MS
379 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
380 if (fd < 0) {
8c47c732
LP
381 r = -errno;
382 goto finish;
383 }
384
a354329f 385 iovec.iov_len = strlen(state);
8c47c732 386
a354329f 387 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
8c47c732
LP
388 if (sockaddr.un.sun_path[0] == '@')
389 sockaddr.un.sun_path[0] = 0;
390
0e098b15 391 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
a013bd94
LP
392 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
393 msghdr.msg_namelen = sizeof(struct sockaddr_un);
394
64144440 395 have_pid = pid != 0 && pid != getpid();
d4a144fa 396
64144440 397 if (n_fds > 0 || have_pid) {
a5bd3c32
MW
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;
64144440 401 msghdr.msg_control = alloca(msghdr.msg_controllen);
a354329f
LP
402
403 cmsg = CMSG_FIRSTHDR(&msghdr);
64144440
ZJS
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);
a354329f 408
64144440 409 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
be8f4e9e 410
64144440
ZJS
411 if (have_pid)
412 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
413 }
a354329f 414
64144440
ZJS
415 if (have_pid) {
416 struct ucred *ucred;
be8f4e9e 417
64144440
ZJS
418 cmsg->cmsg_level = SOL_SOCKET;
419 cmsg->cmsg_type = SCM_CREDENTIALS;
420 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
be8f4e9e 421
64144440
ZJS
422 ucred = (struct ucred*) CMSG_DATA(cmsg);
423 ucred->pid = pid;
424 ucred->uid = getuid();
425 ucred->gid = getgid();
426 }
be8f4e9e
LP
427 }
428
429 /* First try with fake ucred data, as requested */
430 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
431 r = 1;
8c47c732
LP
432 goto finish;
433 }
434
a354329f 435 /* If that failed, try with our own ucred instead */
64144440
ZJS
436 if (have_pid) {
437 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
438 if (msghdr.msg_controllen == 0)
a354329f 439 msghdr.msg_control = NULL;
be8f4e9e
LP
440
441 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
442 r = 1;
443 goto finish;
444 }
445 }
446
447 r = -errno;
8c47c732
LP
448
449finish:
450 if (unset_environment)
451 unsetenv("NOTIFY_SOCKET");
452
8c47c732 453 return r;
8c47c732
LP
454}
455
a354329f
LP
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
be8f4e9e 460_public_ int sd_notify(int unset_environment, const char *state) {
a354329f 461 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
be8f4e9e
LP
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
0ebee881 482_public_ int sd_notifyf(int unset_environment, const char *format, ...) {
be8f4e9e 483 _cleanup_free_ char *p = NULL;
8c47c732
LP
484 int r;
485
be8f4e9e
LP
486 if (format) {
487 va_list ap;
8c47c732 488
be8f4e9e
LP
489 va_start(ap, format);
490 r = vasprintf(&p, format, ap);
491 va_end(ap);
8c47c732 492
be8f4e9e
LP
493 if (r < 0 || !p)
494 return -ENOMEM;
495 }
8c47c732 496
be8f4e9e 497 return sd_pid_notify(0, unset_environment, p);
8c47c732 498}
40473a70 499
0ebee881 500_public_ int sd_booted(void) {
66e41181 501 struct stat st;
40473a70 502
66e41181
LP
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. */
40473a70 506
66e41181 507 if (lstat("/run/systemd/system/", &st) < 0)
40473a70
LP
508 return 0;
509
66e41181 510 return !!S_ISDIR(st.st_mode);
40473a70 511}
09812eb7 512
0ebee881 513_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
a9becdd6 514 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
be8f4e9e 515 uint64_t u;
a9becdd6 516 int r = 0;
09812eb7 517
a9becdd6
ZJS
518 s = getenv("WATCHDOG_USEC");
519 if (!s)
09812eb7 520 goto finish;
09812eb7 521
a9becdd6 522 r = safe_atou64(s, &u);
be8f4e9e 523 if (r < 0)
09812eb7 524 goto finish;
a9becdd6 525 if (u <= 0) {
09812eb7
LP
526 r = -EINVAL;
527 goto finish;
528 }
529
a9becdd6
ZJS
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 }
09812eb7
LP
543 }
544
545 if (usec)
be8f4e9e 546 *usec = u;
09812eb7
LP
547
548 r = 1;
549
550finish:
a9becdd6 551 if (unset_environment && s)
09812eb7 552 unsetenv("WATCHDOG_USEC");
a9becdd6
ZJS
553 if (unset_environment && p)
554 unsetenv("WATCHDOG_PID");
09812eb7
LP
555
556 return r;
09812eb7 557}