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/>.
22 #include <sys/types.h>
24 #include <sys/socket.h>
27 #include <netinet/in.h>
39 #include "path-util.h"
40 #include "sd-daemon.h"
42 _public_ int sd_listen_fds(int unset_environment) {
48 e = getenv("LISTEN_PID");
54 r = parse_pid(e, &pid);
59 if (getpid() != pid) {
64 e = getenv("LISTEN_FDS");
74 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) n; fd ++) {
75 r = fd_cloexec(fd, true);
83 if (unset_environment) {
84 unsetenv("LISTEN_PID");
85 unsetenv("LISTEN_FDS");
91 _public_ int sd_is_fifo(int fd, const char *path) {
94 assert_return(fd >= 0, -EINVAL);
96 if (fstat(fd, &st_fd) < 0)
99 if (!S_ISFIFO(st_fd.st_mode))
105 if (stat(path, &st_path) < 0) {
107 if (errno == ENOENT || errno == ENOTDIR)
114 st_path.st_dev == st_fd.st_dev &&
115 st_path.st_ino == st_fd.st_ino;
121 _public_ int sd_is_special(int fd, const char *path) {
124 assert_return(fd >= 0, -EINVAL);
126 if (fstat(fd, &st_fd) < 0)
129 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
135 if (stat(path, &st_path) < 0) {
137 if (errno == ENOENT || errno == ENOTDIR)
143 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
145 st_path.st_dev == st_fd.st_dev &&
146 st_path.st_ino == st_fd.st_ino;
147 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
148 return st_path.st_rdev == st_fd.st_rdev;
156 static int sd_is_socket_internal(int fd, int type, int listening) {
159 assert_return(fd >= 0, -EINVAL);
160 assert_return(type >= 0, -EINVAL);
162 if (fstat(fd, &st_fd) < 0)
165 if (!S_ISSOCK(st_fd.st_mode))
170 socklen_t l = sizeof(other_type);
172 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
175 if (l != sizeof(other_type))
178 if (other_type != type)
182 if (listening >= 0) {
184 socklen_t l = sizeof(accepting);
186 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
189 if (l != sizeof(accepting))
192 if (!accepting != !listening)
199 union sockaddr_union {
201 struct sockaddr_in in4;
202 struct sockaddr_in6 in6;
203 struct sockaddr_un un;
204 struct sockaddr_storage storage;
207 _public_ int sd_is_socket(int fd, int family, int type, int listening) {
210 assert_return(fd >= 0, -EINVAL);
211 assert_return(family >= 0, -EINVAL);
213 r = sd_is_socket_internal(fd, type, listening);
218 union sockaddr_union sockaddr = {};
219 socklen_t l = sizeof(sockaddr);
221 if (getsockname(fd, &sockaddr.sa, &l) < 0)
224 if (l < sizeof(sa_family_t))
227 return sockaddr.sa.sa_family == family;
233 _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
234 union sockaddr_union sockaddr = {};
235 socklen_t l = sizeof(sockaddr);
238 assert_return(fd >= 0, -EINVAL);
239 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
241 r = sd_is_socket_internal(fd, type, listening);
245 if (getsockname(fd, &sockaddr.sa, &l) < 0)
248 if (l < sizeof(sa_family_t))
251 if (sockaddr.sa.sa_family != AF_INET &&
252 sockaddr.sa.sa_family != AF_INET6)
256 if (sockaddr.sa.sa_family != family)
260 if (sockaddr.sa.sa_family == AF_INET) {
261 if (l < sizeof(struct sockaddr_in))
264 return htons(port) == sockaddr.in4.sin_port;
266 if (l < sizeof(struct sockaddr_in6))
269 return htons(port) == sockaddr.in6.sin6_port;
276 _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
277 union sockaddr_union sockaddr = {};
278 socklen_t l = sizeof(sockaddr);
281 assert_return(fd >= 0, -EINVAL);
283 r = sd_is_socket_internal(fd, type, listening);
287 if (getsockname(fd, &sockaddr.sa, &l) < 0)
290 if (l < sizeof(sa_family_t))
293 if (sockaddr.sa.sa_family != AF_UNIX)
298 length = strlen(path);
302 return l == offsetof(struct sockaddr_un, sun_path);
305 /* Normal path socket */
307 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
308 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
310 /* Abstract namespace socket */
312 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
313 memcmp(path, sockaddr.un.sun_path, length) == 0;
319 _public_ int sd_is_mq(int fd, const char *path) {
322 assert_return(fd >= 0, -EINVAL);
324 if (mq_getattr(fd, &attr) < 0)
328 char fpath[PATH_MAX];
331 assert_return(path_is_absolute(path), -EINVAL);
333 if (fstat(fd, &a) < 0)
336 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
337 fpath[sizeof(fpath)-1] = 0;
339 if (stat(fpath, &b) < 0)
342 if (a.st_dev != b.st_dev ||
343 a.st_ino != b.st_ino)
350 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
351 union sockaddr_union sockaddr = {};
352 _cleanup_close_ int fd = -1;
353 struct msghdr msghdr = {};
354 struct iovec iovec = {};
357 struct cmsghdr cmsghdr;
358 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
367 e = getenv("NOTIFY_SOCKET");
371 /* Must be an abstract socket, or an absolute path */
372 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
377 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
383 sockaddr.sa.sa_family = AF_UNIX;
384 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
386 if (sockaddr.un.sun_path[0] == '@')
387 sockaddr.un.sun_path[0] = 0;
389 iovec.iov_base = (char*) state;
390 iovec.iov_len = strlen(state);
392 msghdr.msg_name = &sockaddr;
393 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
395 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
396 msghdr.msg_namelen = sizeof(struct sockaddr_un);
398 msghdr.msg_iov = &iovec;
399 msghdr.msg_iovlen = 1;
401 if (pid != 0 && pid != getpid()) {
402 struct cmsghdr *cmsg;
403 struct ucred ucred = {};
405 msghdr.msg_control = &control;
406 msghdr.msg_controllen = sizeof(control);
408 cmsg = CMSG_FIRSTHDR(&msghdr);
409 cmsg->cmsg_level = SOL_SOCKET;
410 cmsg->cmsg_type = SCM_CREDENTIALS;
411 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
414 ucred.uid = getuid();
415 ucred.gid = getgid();
417 memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred));
418 msghdr.msg_controllen = cmsg->cmsg_len;
421 /* First try with fake ucred data, as requested */
422 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
427 /* If that failed, try with our own instead */
428 if (msghdr.msg_control) {
429 msghdr.msg_control = NULL;
430 msghdr.msg_controllen = 0;
432 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
441 if (unset_environment)
442 unsetenv("NOTIFY_SOCKET");
447 _public_ int sd_notify(int unset_environment, const char *state) {
448 return sd_pid_notify(0, unset_environment, state);
451 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
452 _cleanup_free_ char *p = NULL;
458 va_start(ap, format);
459 r = vasprintf(&p, format, ap);
466 return sd_pid_notify(pid, unset_environment, p);
469 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
470 _cleanup_free_ char *p = NULL;
476 va_start(ap, format);
477 r = vasprintf(&p, format, ap);
484 return sd_pid_notify(0, unset_environment, p);
487 _public_ int sd_booted(void) {
490 /* We test whether the runtime unit file directory has been
491 * created. This takes place in mount-setup.c, so is
492 * guaranteed to happen very early during boot. */
494 if (lstat("/run/systemd/system/", &st) < 0)
497 return !!S_ISDIR(st.st_mode);
500 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
506 e = getenv("WATCHDOG_PID");
512 r = parse_pid(e, &pid);
516 /* Is this for us? */
517 if (getpid() != pid) {
522 e = getenv("WATCHDOG_USEC");
528 r = safe_atou64(e, &u);
542 if (unset_environment) {
543 unsetenv("WATCHDOG_PID");
544 unsetenv("WATCHDOG_USEC");