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) {
61 e = getenv("LISTEN_PID");
67 r = parse_pid(e, &pid);
72 if (getpid() != pid) {
77 e = getenv("LISTEN_FDS");
87 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) n; fd ++) {
88 r = fd_cloexec(fd, true);
96 unsetenv_all(unset_environment);
100 _public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
101 _cleanup_strv_free_ char **l = NULL;
103 int n_names = 0, n_fds;
108 return sd_listen_fds(unset_environment);
110 e = getenv("LISTEN_FDNAMES");
112 n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
114 unsetenv_all(unset_environment);
122 n_fds = sd_listen_fds(unset_environment);
127 if (n_names != n_fds)
130 r = strv_extend_n(&l, "unknown", n_fds);
141 _public_ int sd_is_fifo(int fd, const char *path) {
144 assert_return(fd >= 0, -EBADF);
146 if (fstat(fd, &st_fd) < 0)
149 if (!S_ISFIFO(st_fd.st_mode))
155 if (stat(path, &st_path) < 0) {
157 if (errno == ENOENT || errno == ENOTDIR)
164 st_path.st_dev == st_fd.st_dev &&
165 st_path.st_ino == st_fd.st_ino;
171 _public_ int sd_is_special(int fd, const char *path) {
174 assert_return(fd >= 0, -EBADF);
176 if (fstat(fd, &st_fd) < 0)
179 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
185 if (stat(path, &st_path) < 0) {
187 if (errno == ENOENT || errno == ENOTDIR)
193 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
195 st_path.st_dev == st_fd.st_dev &&
196 st_path.st_ino == st_fd.st_ino;
197 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
198 return st_path.st_rdev == st_fd.st_rdev;
207 static int sd_is_socket_internal(int fd, int type, int listening) {
210 assert_return(fd >= 0, -EBADF);
211 assert_return(type >= 0, -EINVAL);
213 if (fstat(fd, &st_fd) < 0)
216 if (!S_ISSOCK(st_fd.st_mode))
221 socklen_t l = sizeof(other_type);
223 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
226 if (l != sizeof(other_type))
229 if (other_type != type)
233 if (listening >= 0) {
235 socklen_t l = sizeof(accepting);
237 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
240 if (l != sizeof(accepting))
243 if (!accepting != !listening)
250 _public_ int sd_is_socket(int fd, int family, int type, int listening) {
253 assert_return(fd >= 0, -EBADF);
254 assert_return(family >= 0, -EINVAL);
256 r = sd_is_socket_internal(fd, type, listening);
261 union sockaddr_union sockaddr = {};
262 socklen_t l = sizeof(sockaddr);
264 if (getsockname(fd, &sockaddr.sa, &l) < 0)
267 if (l < sizeof(sa_family_t))
270 return sockaddr.sa.sa_family == family;
276 /// UNNEEDED by elogind
278 _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
279 union sockaddr_union sockaddr = {};
280 socklen_t l = sizeof(sockaddr);
283 assert_return(fd >= 0, -EBADF);
284 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
286 r = sd_is_socket_internal(fd, type, listening);
290 if (getsockname(fd, &sockaddr.sa, &l) < 0)
293 if (l < sizeof(sa_family_t))
296 if (sockaddr.sa.sa_family != AF_INET &&
297 sockaddr.sa.sa_family != AF_INET6)
301 if (sockaddr.sa.sa_family != family)
305 if (sockaddr.sa.sa_family == AF_INET) {
306 if (l < sizeof(struct sockaddr_in))
309 return htons(port) == sockaddr.in.sin_port;
311 if (l < sizeof(struct sockaddr_in6))
314 return htons(port) == sockaddr.in6.sin6_port;
321 _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
322 union sockaddr_union sockaddr = {};
323 socklen_t l = sizeof(sockaddr);
326 assert_return(fd >= 0, -EBADF);
328 r = sd_is_socket_internal(fd, type, listening);
332 if (getsockname(fd, &sockaddr.sa, &l) < 0)
335 if (l < sizeof(sa_family_t))
338 if (sockaddr.sa.sa_family != AF_UNIX)
343 length = strlen(path);
347 return l == offsetof(struct sockaddr_un, sun_path);
350 /* Normal path socket */
352 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
353 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
355 /* Abstract namespace socket */
357 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
358 memcmp(path, sockaddr.un.sun_path, length) == 0;
364 _public_ int sd_is_mq(int fd, const char *path) {
367 /* Check that the fd is valid */
368 assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
370 if (mq_getattr(fd, &attr) < 0) {
372 /* A non-mq fd (or an invalid one, but we ruled that out above) */
378 char fpath[PATH_MAX];
381 assert_return(path_is_absolute(path), -EINVAL);
383 if (fstat(fd, &a) < 0)
386 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
387 fpath[sizeof(fpath)-1] = 0;
389 if (stat(fpath, &b) < 0)
392 if (a.st_dev != b.st_dev ||
393 a.st_ino != b.st_ino)
401 _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
402 union sockaddr_union sockaddr = {
403 .sa.sa_family = AF_UNIX,
405 struct iovec iovec = {
406 .iov_base = (char*) state,
408 struct msghdr msghdr = {
411 .msg_name = &sockaddr,
413 _cleanup_close_ int fd = -1;
414 struct cmsghdr *cmsg = NULL;
424 if (n_fds > 0 && !fds) {
429 e = getenv("NOTIFY_SOCKET");
433 /* Must be an abstract socket, or an absolute path */
434 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
439 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
445 iovec.iov_len = strlen(state);
447 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
448 if (sockaddr.un.sun_path[0] == '@')
449 sockaddr.un.sun_path[0] = 0;
451 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
452 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
453 msghdr.msg_namelen = sizeof(struct sockaddr_un);
455 have_pid = pid != 0 && pid != getpid();
457 if (n_fds > 0 || have_pid) {
458 /* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */
459 msghdr.msg_controllen = (n_fds ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
460 CMSG_SPACE(sizeof(struct ucred)) * have_pid;
461 msghdr.msg_control = alloca(msghdr.msg_controllen);
463 cmsg = CMSG_FIRSTHDR(&msghdr);
465 cmsg->cmsg_level = SOL_SOCKET;
466 cmsg->cmsg_type = SCM_RIGHTS;
467 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
469 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
472 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
478 cmsg->cmsg_level = SOL_SOCKET;
479 cmsg->cmsg_type = SCM_CREDENTIALS;
480 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
482 ucred = (struct ucred*) CMSG_DATA(cmsg);
484 ucred->uid = getuid();
485 ucred->gid = getgid();
489 /* First try with fake ucred data, as requested */
490 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
495 /* If that failed, try with our own ucred instead */
497 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
498 if (msghdr.msg_controllen == 0)
499 msghdr.msg_control = NULL;
501 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
510 if (unset_environment)
511 unsetenv("NOTIFY_SOCKET");
516 /// UNNEEDED by elogind
518 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
519 return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
523 _public_ int sd_notify(int unset_environment, const char *state) {
524 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
527 /// UNNEEDED by elogind
529 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
530 _cleanup_free_ char *p = NULL;
536 va_start(ap, format);
537 r = vasprintf(&p, format, ap);
544 return sd_pid_notify(pid, unset_environment, p);
547 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
548 _cleanup_free_ char *p = NULL;
554 va_start(ap, format);
555 r = vasprintf(&p, format, ap);
562 return sd_pid_notify(0, unset_environment, p);
565 _public_ int sd_booted(void) {
566 /* We test whether the runtime unit file directory has been
567 * created. This takes place in mount-setup.c, so is
568 * guaranteed to happen very early during boot. */
570 return laccess("/run/systemd/system/", F_OK) >= 0;
574 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
575 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
579 s = getenv("WATCHDOG_USEC");
583 r = safe_atou64(s, &u);
591 p = getenv("WATCHDOG_PID");
595 r = parse_pid(p, &pid);
599 /* Is this for us? */
600 if (getpid() != pid) {
612 if (unset_environment && s)
613 unsetenv("WATCHDOG_USEC");
614 if (unset_environment && p)
615 unsetenv("WATCHDOG_PID");