]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-daemon/sd-daemon.c
cgroup: memory limits on / are not supported
[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
be8f4e9e
LP
343_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
344 union sockaddr_union sockaddr = {};
345 _cleanup_close_ int fd = -1;
346 struct msghdr msghdr = {};
347 struct iovec iovec = {};
8c47c732 348 const char *e;
be8f4e9e
LP
349 union {
350 struct cmsghdr cmsghdr;
351 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
352 } control = {};
353 int r;
8c47c732
LP
354
355 if (!state) {
356 r = -EINVAL;
357 goto finish;
358 }
359
50425d16
MS
360 e = getenv("NOTIFY_SOCKET");
361 if (!e)
08bfb810 362 return 0;
8c47c732
LP
363
364 /* Must be an abstract socket, or an absolute path */
365 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
366 r = -EINVAL;
367 goto finish;
368 }
369
50425d16
MS
370 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
371 if (fd < 0) {
8c47c732
LP
372 r = -errno;
373 goto finish;
374 }
375
8c47c732
LP
376 sockaddr.sa.sa_family = AF_UNIX;
377 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
378
379 if (sockaddr.un.sun_path[0] == '@')
380 sockaddr.un.sun_path[0] = 0;
381
8c47c732
LP
382 iovec.iov_base = (char*) state;
383 iovec.iov_len = strlen(state);
384
8c47c732 385 msghdr.msg_name = &sockaddr;
0e098b15 386 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
a013bd94
LP
387
388 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
389 msghdr.msg_namelen = sizeof(struct sockaddr_un);
390
8c47c732
LP
391 msghdr.msg_iov = &iovec;
392 msghdr.msg_iovlen = 1;
8c47c732 393
be8f4e9e
LP
394 if (pid != 0 && pid != getpid()) {
395 struct cmsghdr *cmsg;
396 struct ucred ucred = {};
397
398 msghdr.msg_control = &control;
399 msghdr.msg_controllen = sizeof(control);
400
401 cmsg = CMSG_FIRSTHDR(&msghdr);
402 cmsg->cmsg_level = SOL_SOCKET;
403 cmsg->cmsg_type = SCM_CREDENTIALS;
404 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
405
406 ucred.pid = pid;
407 ucred.uid = getuid();
408 ucred.gid = getgid();
409
410 memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred));
411 msghdr.msg_controllen = cmsg->cmsg_len;
412 }
413
414 /* First try with fake ucred data, as requested */
415 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
416 r = 1;
8c47c732
LP
417 goto finish;
418 }
419
be8f4e9e
LP
420 /* If that failed, try with our own instead */
421 if (msghdr.msg_control) {
422 msghdr.msg_control = NULL;
423 msghdr.msg_controllen = 0;
424
425 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
426 r = 1;
427 goto finish;
428 }
429 }
430
431 r = -errno;
8c47c732
LP
432
433finish:
434 if (unset_environment)
435 unsetenv("NOTIFY_SOCKET");
436
8c47c732 437 return r;
8c47c732
LP
438}
439
be8f4e9e
LP
440_public_ int sd_notify(int unset_environment, const char *state) {
441 return sd_pid_notify(0, unset_environment, state);
442}
443
444_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
445 _cleanup_free_ char *p = NULL;
446 int r;
447
448 if (format) {
449 va_list ap;
450
451 va_start(ap, format);
452 r = vasprintf(&p, format, ap);
453 va_end(ap);
454
455 if (r < 0 || !p)
456 return -ENOMEM;
457 }
458
459 return sd_pid_notify(pid, unset_environment, p);
460}
461
0ebee881 462_public_ int sd_notifyf(int unset_environment, const char *format, ...) {
be8f4e9e 463 _cleanup_free_ char *p = NULL;
8c47c732
LP
464 int r;
465
be8f4e9e
LP
466 if (format) {
467 va_list ap;
8c47c732 468
be8f4e9e
LP
469 va_start(ap, format);
470 r = vasprintf(&p, format, ap);
471 va_end(ap);
8c47c732 472
be8f4e9e
LP
473 if (r < 0 || !p)
474 return -ENOMEM;
475 }
8c47c732 476
be8f4e9e 477 return sd_pid_notify(0, unset_environment, p);
8c47c732 478}
40473a70 479
0ebee881 480_public_ int sd_booted(void) {
66e41181 481 struct stat st;
40473a70 482
66e41181
LP
483 /* We test whether the runtime unit file directory has been
484 * created. This takes place in mount-setup.c, so is
485 * guaranteed to happen very early during boot. */
40473a70 486
66e41181 487 if (lstat("/run/systemd/system/", &st) < 0)
40473a70
LP
488 return 0;
489
66e41181 490 return !!S_ISDIR(st.st_mode);
40473a70 491}
09812eb7 492
0ebee881 493_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
a9becdd6 494 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
be8f4e9e 495 uint64_t u;
a9becdd6 496 int r = 0;
09812eb7 497
a9becdd6
ZJS
498 s = getenv("WATCHDOG_USEC");
499 if (!s)
09812eb7 500 goto finish;
09812eb7 501
a9becdd6 502 r = safe_atou64(s, &u);
be8f4e9e 503 if (r < 0)
09812eb7 504 goto finish;
a9becdd6 505 if (u <= 0) {
09812eb7
LP
506 r = -EINVAL;
507 goto finish;
508 }
509
a9becdd6
ZJS
510 p = getenv("WATCHDOG_PID");
511 if (p) {
512 pid_t pid;
513
514 r = parse_pid(p, &pid);
515 if (r < 0)
516 goto finish;
517
518 /* Is this for us? */
519 if (getpid() != pid) {
520 r = 0;
521 goto finish;
522 }
09812eb7
LP
523 }
524
525 if (usec)
be8f4e9e 526 *usec = u;
09812eb7
LP
527
528 r = 1;
529
530finish:
a9becdd6 531 if (unset_environment && s)
09812eb7 532 unsetenv("WATCHDOG_USEC");
a9becdd6
ZJS
533 if (unset_environment && p)
534 unsetenv("WATCHDOG_PID");
09812eb7
LP
535
536 return r;
09812eb7 537}