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