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