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 "socket-util.h"
41 #include "sd-daemon.h"
43 _public_ int sd_listen_fds(int unset_environment) {
49 e = getenv("LISTEN_PID");
55 r = parse_pid(e, &pid);
60 if (getpid() != pid) {
65 e = getenv("LISTEN_FDS");
75 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) n; fd ++) {
76 r = fd_cloexec(fd, true);
84 if (unset_environment) {
85 unsetenv("LISTEN_PID");
86 unsetenv("LISTEN_FDS");
92 _public_ int sd_is_fifo(int fd, const char *path) {
95 assert_return(fd >= 0, -EINVAL);
97 if (fstat(fd, &st_fd) < 0)
100 if (!S_ISFIFO(st_fd.st_mode))
106 if (stat(path, &st_path) < 0) {
108 if (errno == ENOENT || errno == ENOTDIR)
115 st_path.st_dev == st_fd.st_dev &&
116 st_path.st_ino == st_fd.st_ino;
122 _public_ int sd_is_special(int fd, const char *path) {
125 assert_return(fd >= 0, -EINVAL);
127 if (fstat(fd, &st_fd) < 0)
130 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
136 if (stat(path, &st_path) < 0) {
138 if (errno == ENOENT || errno == ENOTDIR)
144 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
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;
157 static int sd_is_socket_internal(int fd, int type, int listening) {
160 assert_return(fd >= 0, -EINVAL);
161 assert_return(type >= 0, -EINVAL);
163 if (fstat(fd, &st_fd) < 0)
166 if (!S_ISSOCK(st_fd.st_mode))
171 socklen_t l = sizeof(other_type);
173 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
176 if (l != sizeof(other_type))
179 if (other_type != type)
183 if (listening >= 0) {
185 socklen_t l = sizeof(accepting);
187 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
190 if (l != sizeof(accepting))
193 if (!accepting != !listening)
200 _public_ int sd_is_socket(int fd, int family, int type, int listening) {
203 assert_return(fd >= 0, -EINVAL);
204 assert_return(family >= 0, -EINVAL);
206 r = sd_is_socket_internal(fd, type, listening);
211 union sockaddr_union sockaddr = {};
212 socklen_t l = sizeof(sockaddr);
214 if (getsockname(fd, &sockaddr.sa, &l) < 0)
217 if (l < sizeof(sa_family_t))
220 return sockaddr.sa.sa_family == family;
226 _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
227 union sockaddr_union sockaddr = {};
228 socklen_t l = sizeof(sockaddr);
231 assert_return(fd >= 0, -EINVAL);
232 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
234 r = sd_is_socket_internal(fd, type, listening);
238 if (getsockname(fd, &sockaddr.sa, &l) < 0)
241 if (l < sizeof(sa_family_t))
244 if (sockaddr.sa.sa_family != AF_INET &&
245 sockaddr.sa.sa_family != AF_INET6)
249 if (sockaddr.sa.sa_family != family)
253 if (sockaddr.sa.sa_family == AF_INET) {
254 if (l < sizeof(struct sockaddr_in))
257 return htons(port) == sockaddr.in.sin_port;
259 if (l < sizeof(struct sockaddr_in6))
262 return htons(port) == sockaddr.in6.sin6_port;
269 _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
270 union sockaddr_union sockaddr = {};
271 socklen_t l = sizeof(sockaddr);
274 assert_return(fd >= 0, -EINVAL);
276 r = sd_is_socket_internal(fd, type, listening);
280 if (getsockname(fd, &sockaddr.sa, &l) < 0)
283 if (l < sizeof(sa_family_t))
286 if (sockaddr.sa.sa_family != AF_UNIX)
291 length = strlen(path);
295 return l == offsetof(struct sockaddr_un, sun_path);
298 /* Normal path socket */
300 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
301 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
303 /* Abstract namespace socket */
305 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
306 memcmp(path, sockaddr.un.sun_path, length) == 0;
312 _public_ int sd_is_mq(int fd, const char *path) {
315 assert_return(fd >= 0, -EINVAL);
317 if (mq_getattr(fd, &attr) < 0)
321 char fpath[PATH_MAX];
324 assert_return(path_is_absolute(path), -EINVAL);
326 if (fstat(fd, &a) < 0)
329 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
330 fpath[sizeof(fpath)-1] = 0;
332 if (stat(fpath, &b) < 0)
335 if (a.st_dev != b.st_dev ||
336 a.st_ino != b.st_ino)
343 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
344 union sockaddr_union sockaddr = {};
345 _cleanup_close_ int fd = -1;
346 struct msghdr msghdr = {};
347 struct iovec iovec = {};
350 struct cmsghdr cmsghdr;
351 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
360 e = getenv("NOTIFY_SOCKET");
364 /* Must be an abstract socket, or an absolute path */
365 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
370 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
376 sockaddr.sa.sa_family = AF_UNIX;
377 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
379 if (sockaddr.un.sun_path[0] == '@')
380 sockaddr.un.sun_path[0] = 0;
382 iovec.iov_base = (char*) state;
383 iovec.iov_len = strlen(state);
385 msghdr.msg_name = &sockaddr;
386 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
388 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
389 msghdr.msg_namelen = sizeof(struct sockaddr_un);
391 msghdr.msg_iov = &iovec;
392 msghdr.msg_iovlen = 1;
394 if (pid != 0 && pid != getpid()) {
395 struct cmsghdr *cmsg;
396 struct ucred ucred = {};
398 msghdr.msg_control = &control;
399 msghdr.msg_controllen = sizeof(control);
401 cmsg = CMSG_FIRSTHDR(&msghdr);
402 cmsg->cmsg_level = SOL_SOCKET;
403 cmsg->cmsg_type = SCM_CREDENTIALS;
404 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
407 ucred.uid = getuid();
408 ucred.gid = getgid();
410 memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred));
411 msghdr.msg_controllen = cmsg->cmsg_len;
414 /* First try with fake ucred data, as requested */
415 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
420 /* If that failed, try with our own instead */
421 if (msghdr.msg_control) {
422 msghdr.msg_control = NULL;
423 msghdr.msg_controllen = 0;
425 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
434 if (unset_environment)
435 unsetenv("NOTIFY_SOCKET");
440 _public_ int sd_notify(int unset_environment, const char *state) {
441 return sd_pid_notify(0, unset_environment, state);
444 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
445 _cleanup_free_ char *p = NULL;
451 va_start(ap, format);
452 r = vasprintf(&p, format, ap);
459 return sd_pid_notify(pid, unset_environment, p);
462 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
463 _cleanup_free_ char *p = NULL;
469 va_start(ap, format);
470 r = vasprintf(&p, format, ap);
477 return sd_pid_notify(0, unset_environment, p);
480 _public_ int sd_booted(void) {
483 /* We test whether the runtime unit file directory has been
484 * created. This takes place in mount-setup.c, so is
485 * guaranteed to happen very early during boot. */
487 if (lstat("/run/systemd/system/", &st) < 0)
490 return !!S_ISDIR(st.st_mode);
493 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
494 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
498 s = getenv("WATCHDOG_USEC");
502 r = safe_atou64(s, &u);
510 p = getenv("WATCHDOG_PID");
514 r = parse_pid(p, &pid);
518 /* Is this for us? */
519 if (getpid() != pid) {
531 if (unset_environment && s)
532 unsetenv("WATCHDOG_USEC");
533 if (unset_environment && p)
534 unsetenv("WATCHDOG_PID");