]> git.proxmox.com Git - systemd.git/blame - src/libsystemd/sd-daemon/sd-daemon.c
Imported Upstream version 220
[systemd.git] / src / libsystemd / sd-daemon / sd-daemon.c
CommitLineData
663996b3
MS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
60f067b4
JS
4 This file is part of systemd.
5
663996b3
MS
6 Copyright 2010 Lennart Poettering
7
60f067b4
JS
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.
663996b3 12
60f067b4
JS
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.
17
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***/
663996b3 21
663996b3
MS
22#include <sys/stat.h>
23#include <sys/socket.h>
24#include <sys/un.h>
663996b3
MS
25#include <netinet/in.h>
26#include <stdlib.h>
27#include <errno.h>
28#include <unistd.h>
29#include <string.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stddef.h>
33#include <limits.h>
60f067b4 34#include <mqueue.h>
663996b3 35
60f067b4
JS
36#include "util.h"
37#include "path-util.h"
5eef597e 38#include "socket-util.h"
663996b3
MS
39#include "sd-daemon.h"
40
60f067b4 41_public_ int sd_listen_fds(int unset_environment) {
663996b3 42 const char *e;
60f067b4
JS
43 unsigned n;
44 int r, fd;
45 pid_t pid;
663996b3
MS
46
47 e = getenv("LISTEN_PID");
48 if (!e) {
49 r = 0;
50 goto finish;
51 }
52
60f067b4
JS
53 r = parse_pid(e, &pid);
54 if (r < 0)
663996b3 55 goto finish;
663996b3
MS
56
57 /* Is this for us? */
60f067b4 58 if (getpid() != pid) {
663996b3
MS
59 r = 0;
60 goto finish;
61 }
62
63 e = getenv("LISTEN_FDS");
64 if (!e) {
65 r = 0;
66 goto finish;
67 }
68
60f067b4
JS
69 r = safe_atou(e, &n);
70 if (r < 0)
663996b3 71 goto finish;
663996b3 72
60f067b4
JS
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)
663996b3 76 goto finish;
663996b3
MS
77 }
78
60f067b4 79 r = (int) n;
663996b3
MS
80
81finish:
82 if (unset_environment) {
83 unsetenv("LISTEN_PID");
84 unsetenv("LISTEN_FDS");
85 }
86
87 return r;
663996b3
MS
88}
89
60f067b4 90_public_ int sd_is_fifo(int fd, const char *path) {
663996b3
MS
91 struct stat st_fd;
92
60f067b4 93 assert_return(fd >= 0, -EINVAL);
663996b3
MS
94
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
104 if (stat(path, &st_path) < 0) {
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
60f067b4 120_public_ int sd_is_special(int fd, const char *path) {
663996b3
MS
121 struct stat st_fd;
122
60f067b4 123 assert_return(fd >= 0, -EINVAL);
663996b3
MS
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
155static int sd_is_socket_internal(int fd, int type, int listening) {
156 struct stat st_fd;
157
60f067b4
JS
158 assert_return(fd >= 0, -EINVAL);
159 assert_return(type >= 0, -EINVAL);
663996b3
MS
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
191 if (!accepting != !listening)
192 return 0;
193 }
194
195 return 1;
196}
197
60f067b4 198_public_ int sd_is_socket(int fd, int family, int type, int listening) {
663996b3
MS
199 int r;
200
60f067b4
JS
201 assert_return(fd >= 0, -EINVAL);
202 assert_return(family >= 0, -EINVAL);
663996b3
MS
203
204 r = sd_is_socket_internal(fd, type, listening);
205 if (r <= 0)
206 return r;
207
208 if (family > 0) {
209 union sockaddr_union sockaddr = {};
210 socklen_t l = sizeof(sockaddr);
211
212 if (getsockname(fd, &sockaddr.sa, &l) < 0)
213 return -errno;
214
215 if (l < sizeof(sa_family_t))
216 return -EINVAL;
217
218 return sockaddr.sa.sa_family == family;
219 }
220
221 return 1;
222}
223
60f067b4 224_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
663996b3
MS
225 union sockaddr_union sockaddr = {};
226 socklen_t l = sizeof(sockaddr);
227 int r;
228
60f067b4
JS
229 assert_return(fd >= 0, -EINVAL);
230 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
663996b3
MS
231
232 r = sd_is_socket_internal(fd, type, listening);
233 if (r <= 0)
234 return r;
235
236 if (getsockname(fd, &sockaddr.sa, &l) < 0)
237 return -errno;
238
239 if (l < sizeof(sa_family_t))
240 return -EINVAL;
241
242 if (sockaddr.sa.sa_family != AF_INET &&
243 sockaddr.sa.sa_family != AF_INET6)
244 return 0;
245
60f067b4 246 if (family != 0)
663996b3
MS
247 if (sockaddr.sa.sa_family != family)
248 return 0;
249
250 if (port > 0) {
251 if (sockaddr.sa.sa_family == AF_INET) {
252 if (l < sizeof(struct sockaddr_in))
253 return -EINVAL;
254
5eef597e 255 return htons(port) == sockaddr.in.sin_port;
663996b3
MS
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
60f067b4 267_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
663996b3
MS
268 union sockaddr_union sockaddr = {};
269 socklen_t l = sizeof(sockaddr);
270 int r;
271
60f067b4
JS
272 assert_return(fd >= 0, -EINVAL);
273
663996b3
MS
274 r = sd_is_socket_internal(fd, type, listening);
275 if (r <= 0)
276 return r;
277
278 if (getsockname(fd, &sockaddr.sa, &l) < 0)
279 return -errno;
280
281 if (l < sizeof(sa_family_t))
282 return -EINVAL;
283
284 if (sockaddr.sa.sa_family != AF_UNIX)
285 return 0;
286
287 if (path) {
288 if (length == 0)
289 length = strlen(path);
290
291 if (length == 0)
292 /* Unnamed socket */
293 return l == offsetof(struct sockaddr_un, sun_path);
294
295 if (path[0])
296 /* Normal path socket */
297 return
298 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
299 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
300 else
301 /* Abstract namespace socket */
302 return
303 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
304 memcmp(path, sockaddr.un.sun_path, length) == 0;
305 }
306
307 return 1;
308}
309
60f067b4 310_public_ int sd_is_mq(int fd, const char *path) {
663996b3
MS
311 struct mq_attr attr;
312
60f067b4 313 assert_return(fd >= 0, -EINVAL);
663996b3
MS
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
60f067b4 322 assert_return(path_is_absolute(path), -EINVAL);
663996b3
MS
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;
663996b3
MS
339}
340
e735f4d4
MP
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 };
e735f4d4
MP
353 _cleanup_close_ int fd = -1;
354 struct cmsghdr *cmsg = NULL;
355 const char *e;
e3bff60a 356 bool have_pid;
60f067b4 357 int r;
663996b3
MS
358
359 if (!state) {
360 r = -EINVAL;
361 goto finish;
362 }
363
e735f4d4
MP
364 if (n_fds > 0 && !fds) {
365 r = -EINVAL;
366 goto finish;
367 }
368
663996b3
MS
369 e = getenv("NOTIFY_SOCKET");
370 if (!e)
371 return 0;
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
379 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
380 if (fd < 0) {
381 r = -errno;
382 goto finish;
383 }
384
e735f4d4 385 iovec.iov_len = strlen(state);
663996b3 386
e735f4d4 387 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
663996b3
MS
388 if (sockaddr.un.sun_path[0] == '@')
389 sockaddr.un.sun_path[0] = 0;
390
663996b3 391 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
663996b3
MS
392 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
393 msghdr.msg_namelen = sizeof(struct sockaddr_un);
394
e3bff60a 395 have_pid = pid != 0 && pid != getpid();
e735f4d4 396
e3bff60a
MP
397 if (n_fds > 0 || have_pid) {
398 msghdr.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds) +
399 CMSG_SPACE(sizeof(struct ucred) * have_pid);
400 msghdr.msg_control = alloca(msghdr.msg_controllen);
663996b3 401
e3bff60a
MP
402 cmsg = CMSG_FIRSTHDR(&msghdr);
403 if (n_fds > 0) {
404 cmsg->cmsg_level = SOL_SOCKET;
405 cmsg->cmsg_type = SCM_RIGHTS;
406 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
e735f4d4 407
e3bff60a 408 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
60f067b4 409
e3bff60a
MP
410 if (have_pid)
411 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
412 }
e735f4d4 413
e3bff60a
MP
414 if (have_pid) {
415 struct ucred *ucred;
60f067b4 416
e3bff60a
MP
417 cmsg->cmsg_level = SOL_SOCKET;
418 cmsg->cmsg_type = SCM_CREDENTIALS;
419 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
60f067b4 420
e3bff60a
MP
421 ucred = (struct ucred*) CMSG_DATA(cmsg);
422 ucred->pid = pid;
423 ucred->uid = getuid();
424 ucred->gid = getgid();
425 }
60f067b4
JS
426 }
427
428 /* First try with fake ucred data, as requested */
429 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
430 r = 1;
663996b3
MS
431 goto finish;
432 }
433
e735f4d4 434 /* If that failed, try with our own ucred instead */
e3bff60a
MP
435 if (have_pid) {
436 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
437 if (msghdr.msg_controllen == 0)
e735f4d4 438 msghdr.msg_control = NULL;
60f067b4
JS
439
440 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
441 r = 1;
442 goto finish;
443 }
444 }
445
446 r = -errno;
663996b3
MS
447
448finish:
449 if (unset_environment)
450 unsetenv("NOTIFY_SOCKET");
451
663996b3 452 return r;
663996b3
MS
453}
454
e735f4d4
MP
455_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
456 return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
457}
458
60f067b4 459_public_ int sd_notify(int unset_environment, const char *state) {
e735f4d4 460 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
60f067b4
JS
461}
462
463_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
464 _cleanup_free_ char *p = NULL;
663996b3
MS
465 int r;
466
60f067b4
JS
467 if (format) {
468 va_list ap;
663996b3 469
60f067b4
JS
470 va_start(ap, format);
471 r = vasprintf(&p, format, ap);
472 va_end(ap);
663996b3 473
60f067b4
JS
474 if (r < 0 || !p)
475 return -ENOMEM;
476 }
663996b3 477
60f067b4 478 return sd_pid_notify(pid, unset_environment, p);
663996b3
MS
479}
480
60f067b4
JS
481_public_ int sd_notifyf(int unset_environment, const char *format, ...) {
482 _cleanup_free_ char *p = NULL;
483 int r;
484
485 if (format) {
486 va_list ap;
487
488 va_start(ap, format);
489 r = vasprintf(&p, format, ap);
490 va_end(ap);
491
492 if (r < 0 || !p)
493 return -ENOMEM;
494 }
495
496 return sd_pid_notify(0, unset_environment, p);
497}
498
499_public_ int sd_booted(void) {
663996b3
MS
500 struct stat st;
501
502 /* We test whether the runtime unit file directory has been
503 * created. This takes place in mount-setup.c, so is
504 * guaranteed to happen very early during boot. */
505
506 if (lstat("/run/systemd/system/", &st) < 0)
507 return 0;
508
509 return !!S_ISDIR(st.st_mode);
60f067b4
JS
510}
511
512_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
5eef597e 513 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
60f067b4 514 uint64_t u;
5eef597e 515 int r = 0;
60f067b4 516
5eef597e
MP
517 s = getenv("WATCHDOG_USEC");
518 if (!s)
60f067b4 519 goto finish;
60f067b4 520
5eef597e 521 r = safe_atou64(s, &u);
60f067b4
JS
522 if (r < 0)
523 goto finish;
5eef597e 524 if (u <= 0) {
60f067b4
JS
525 r = -EINVAL;
526 goto finish;
527 }
528
5eef597e
MP
529 p = getenv("WATCHDOG_PID");
530 if (p) {
531 pid_t pid;
532
533 r = parse_pid(p, &pid);
534 if (r < 0)
535 goto finish;
536
537 /* Is this for us? */
538 if (getpid() != pid) {
539 r = 0;
540 goto finish;
541 }
60f067b4
JS
542 }
543
544 if (usec)
545 *usec = u;
546
547 r = 1;
548
549finish:
5eef597e 550 if (unset_environment && s)
60f067b4 551 unsetenv("WATCHDOG_USEC");
5eef597e
MP
552 if (unset_environment && p)
553 unsetenv("WATCHDOG_PID");
60f067b4
JS
554
555 return r;
663996b3 556}