1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include <netinet/in.h>
31 #include <sys/socket.h>
36 #include "path-util.h"
37 #include "socket-util.h"
41 #include "sd-daemon.h"
43 /// UNNEEDED by elogind
45 static void unsetenv_all(bool unset_environment) {
47 if (!unset_environment)
50 unsetenv("LISTEN_PID");
51 unsetenv("LISTEN_FDS");
52 unsetenv("LISTEN_FDNAMES");
55 _public_ int sd_listen_fds(int unset_environment) {
60 e = getenv("LISTEN_PID");
66 r = parse_pid(e, &pid);
71 if (getpid() != pid) {
76 e = getenv("LISTEN_FDS");
86 assert_cc(SD_LISTEN_FDS_START < INT_MAX);
87 if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
92 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
93 r = fd_cloexec(fd, true);
101 unsetenv_all(unset_environment);
105 _public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
106 _cleanup_strv_free_ char **l = NULL;
108 int n_names = 0, n_fds;
113 return sd_listen_fds(unset_environment);
115 e = getenv("LISTEN_FDNAMES");
117 n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
119 unsetenv_all(unset_environment);
127 n_fds = sd_listen_fds(unset_environment);
132 if (n_names != n_fds)
135 r = strv_extend_n(&l, "unknown", n_fds);
146 _public_ int sd_is_fifo(int fd, const char *path) {
149 assert_return(fd >= 0, -EBADF);
151 if (fstat(fd, &st_fd) < 0)
154 if (!S_ISFIFO(st_fd.st_mode))
160 if (stat(path, &st_path) < 0) {
162 if (errno == ENOENT || errno == ENOTDIR)
169 st_path.st_dev == st_fd.st_dev &&
170 st_path.st_ino == st_fd.st_ino;
176 _public_ int sd_is_special(int fd, const char *path) {
179 assert_return(fd >= 0, -EBADF);
181 if (fstat(fd, &st_fd) < 0)
184 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
190 if (stat(path, &st_path) < 0) {
192 if (errno == ENOENT || errno == ENOTDIR)
198 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
200 st_path.st_dev == st_fd.st_dev &&
201 st_path.st_ino == st_fd.st_ino;
202 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
203 return st_path.st_rdev == st_fd.st_rdev;
212 static int sd_is_socket_internal(int fd, int type, int listening) {
215 assert_return(fd >= 0, -EBADF);
216 assert_return(type >= 0, -EINVAL);
218 if (fstat(fd, &st_fd) < 0)
221 if (!S_ISSOCK(st_fd.st_mode))
226 socklen_t l = sizeof(other_type);
228 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
231 if (l != sizeof(other_type))
234 if (other_type != type)
238 if (listening >= 0) {
240 socklen_t l = sizeof(accepting);
242 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
245 if (l != sizeof(accepting))
248 if (!accepting != !listening)
255 _public_ int sd_is_socket(int fd, int family, int type, int listening) {
258 assert_return(fd >= 0, -EBADF);
259 assert_return(family >= 0, -EINVAL);
261 r = sd_is_socket_internal(fd, type, listening);
266 union sockaddr_union sockaddr = {};
267 socklen_t l = sizeof(sockaddr);
269 if (getsockname(fd, &sockaddr.sa, &l) < 0)
272 if (l < sizeof(sa_family_t))
275 return sockaddr.sa.sa_family == family;
281 /// UNNEEDED by elogind
283 _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
284 union sockaddr_union sockaddr = {};
285 socklen_t l = sizeof(sockaddr);
288 assert_return(fd >= 0, -EBADF);
289 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
291 r = sd_is_socket_internal(fd, type, listening);
295 if (getsockname(fd, &sockaddr.sa, &l) < 0)
298 if (l < sizeof(sa_family_t))
301 if (sockaddr.sa.sa_family != AF_INET &&
302 sockaddr.sa.sa_family != AF_INET6)
306 if (sockaddr.sa.sa_family != family)
310 if (sockaddr.sa.sa_family == AF_INET) {
311 if (l < sizeof(struct sockaddr_in))
314 return htons(port) == sockaddr.in.sin_port;
316 if (l < sizeof(struct sockaddr_in6))
319 return htons(port) == sockaddr.in6.sin6_port;
326 _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
327 union sockaddr_union sockaddr = {};
328 socklen_t l = sizeof(sockaddr);
331 assert_return(fd >= 0, -EBADF);
333 r = sd_is_socket_internal(fd, type, listening);
337 if (getsockname(fd, &sockaddr.sa, &l) < 0)
340 if (l < sizeof(sa_family_t))
343 if (sockaddr.sa.sa_family != AF_UNIX)
348 length = strlen(path);
352 return l == offsetof(struct sockaddr_un, sun_path);
355 /* Normal path socket */
357 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
358 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
360 /* Abstract namespace socket */
362 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
363 memcmp(path, sockaddr.un.sun_path, length) == 0;
369 _public_ int sd_is_mq(int fd, const char *path) {
372 /* Check that the fd is valid */
373 assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
375 if (mq_getattr(fd, &attr) < 0) {
377 /* A non-mq fd (or an invalid one, but we ruled that out above) */
383 char fpath[PATH_MAX];
386 assert_return(path_is_absolute(path), -EINVAL);
388 if (fstat(fd, &a) < 0)
391 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
392 fpath[sizeof(fpath)-1] = 0;
394 if (stat(fpath, &b) < 0)
397 if (a.st_dev != b.st_dev ||
398 a.st_ino != b.st_ino)
406 _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
407 union sockaddr_union sockaddr = {
408 .sa.sa_family = AF_UNIX,
410 struct iovec iovec = {
411 .iov_base = (char*) state,
413 struct msghdr msghdr = {
416 .msg_name = &sockaddr,
418 _cleanup_close_ int fd = -1;
419 struct cmsghdr *cmsg = NULL;
429 if (n_fds > 0 && !fds) {
434 e = getenv("NOTIFY_SOCKET");
438 /* Must be an abstract socket, or an absolute path */
439 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
444 if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
449 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
455 iovec.iov_len = strlen(state);
457 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
458 if (sockaddr.un.sun_path[0] == '@')
459 sockaddr.un.sun_path[0] = 0;
461 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
462 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
463 msghdr.msg_namelen = sizeof(struct sockaddr_un);
465 have_pid = pid != 0 && pid != getpid();
467 if (n_fds > 0 || have_pid) {
468 /* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */
469 msghdr.msg_controllen =
470 (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
471 (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0);
473 msghdr.msg_control = alloca0(msghdr.msg_controllen);
475 cmsg = CMSG_FIRSTHDR(&msghdr);
477 cmsg->cmsg_level = SOL_SOCKET;
478 cmsg->cmsg_type = SCM_RIGHTS;
479 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
481 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
484 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
490 cmsg->cmsg_level = SOL_SOCKET;
491 cmsg->cmsg_type = SCM_CREDENTIALS;
492 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
494 ucred = (struct ucred*) CMSG_DATA(cmsg);
496 ucred->uid = getuid();
497 ucred->gid = getgid();
501 /* First try with fake ucred data, as requested */
502 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
507 /* If that failed, try with our own ucred instead */
509 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
510 if (msghdr.msg_controllen == 0)
511 msghdr.msg_control = NULL;
513 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
522 if (unset_environment)
523 unsetenv("NOTIFY_SOCKET");
528 /// UNNEEDED by elogind
530 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
531 return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
535 _public_ int sd_notify(int unset_environment, const char *state) {
536 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
539 /// UNNEEDED by elogind
541 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
542 _cleanup_free_ char *p = NULL;
548 va_start(ap, format);
549 r = vasprintf(&p, format, ap);
556 return sd_pid_notify(pid, unset_environment, p);
559 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
560 _cleanup_free_ char *p = NULL;
566 va_start(ap, format);
567 r = vasprintf(&p, format, ap);
574 return sd_pid_notify(0, unset_environment, p);
577 _public_ int sd_booted(void) {
578 /* We test whether the runtime unit file directory has been
579 * created. This takes place in mount-setup.c, so is
580 * guaranteed to happen very early during boot. */
582 return laccess("/run/systemd/system/", F_OK) >= 0;
586 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
587 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
591 s = getenv("WATCHDOG_USEC");
595 r = safe_atou64(s, &u);
598 if (u <= 0 || u >= USEC_INFINITY) {
603 p = getenv("WATCHDOG_PID");
607 r = parse_pid(p, &pid);
611 /* Is this for us? */
612 if (getpid() != pid) {
624 if (unset_environment && s)
625 unsetenv("WATCHDOG_USEC");
626 if (unset_environment && p)
627 unsetenv("WATCHDOG_PID");