1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 Copyright 2010 Lennart Poettering
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation files
8 (the "Software"), to deal in the Software without restriction,
9 including without limitation the rights to use, copy, modify, merge,
10 publish, distribute, sublicense, and/or sell copies of the Software,
11 and to permit persons to whom the Software is furnished to do so,
12 subject to the following conditions:
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <sys/fcntl.h>
36 #include <netinet/in.h>
46 #if defined(__linux__)
50 #include "sd-daemon.h"
53 #ifdef SD_EXPORT_SYMBOLS
55 #define _sd_export_ __attribute__ ((visibility("default")))
57 /* Don't export the symbols */
58 #define _sd_export_ __attribute__ ((visibility("hidden")))
64 _sd_export_ int sd_listen_fds(int unset_environment) {
66 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
74 if (!(e = getenv("LISTEN_PID"))) {
80 l = strtoul(e, &p, 10);
87 if (!p || *p || l <= 0) {
93 if (getpid() != (pid_t) l) {
98 if (!(e = getenv("LISTEN_FDS"))) {
104 l = strtoul(e, &p, 10);
116 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
119 if ((flags = fcntl(fd, F_GETFD)) < 0) {
124 if (flags & FD_CLOEXEC)
127 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
136 if (unset_environment) {
137 unsetenv("LISTEN_PID");
138 unsetenv("LISTEN_FDS");
145 _sd_export_ int sd_is_fifo(int fd, const char *path) {
151 memset(&st_fd, 0, sizeof(st_fd));
152 if (fstat(fd, &st_fd) < 0)
155 if (!S_ISFIFO(st_fd.st_mode))
161 memset(&st_path, 0, sizeof(st_path));
162 if (stat(path, &st_path) < 0) {
164 if (errno == ENOENT || errno == ENOTDIR)
171 st_path.st_dev == st_fd.st_dev &&
172 st_path.st_ino == st_fd.st_ino;
178 _sd_export_ int sd_is_special(int fd, const char *path) {
184 if (fstat(fd, &st_fd) < 0)
187 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
193 if (stat(path, &st_path) < 0) {
195 if (errno == ENOENT || errno == ENOTDIR)
201 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
203 st_path.st_dev == st_fd.st_dev &&
204 st_path.st_ino == st_fd.st_ino;
205 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
206 return st_path.st_rdev == st_fd.st_rdev;
214 static int sd_is_socket_internal(int fd, int type, int listening) {
217 if (fd < 0 || type < 0)
220 if (fstat(fd, &st_fd) < 0)
223 if (!S_ISSOCK(st_fd.st_mode))
228 socklen_t l = sizeof(other_type);
230 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
233 if (l != sizeof(other_type))
236 if (other_type != type)
240 if (listening >= 0) {
242 socklen_t l = sizeof(accepting);
244 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
247 if (l != sizeof(accepting))
250 if (!accepting != !listening)
257 union sockaddr_union {
259 struct sockaddr_in in4;
260 struct sockaddr_in6 in6;
261 struct sockaddr_un un;
262 struct sockaddr_storage storage;
265 _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
271 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
275 union sockaddr_union sockaddr;
278 memset(&sockaddr, 0, sizeof(sockaddr));
279 l = sizeof(sockaddr);
281 if (getsockname(fd, &sockaddr.sa, &l) < 0)
284 if (l < sizeof(sa_family_t))
287 return sockaddr.sa.sa_family == family;
293 _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
294 union sockaddr_union sockaddr;
298 if (family != 0 && family != AF_INET && family != AF_INET6)
301 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
304 memset(&sockaddr, 0, sizeof(sockaddr));
305 l = sizeof(sockaddr);
307 if (getsockname(fd, &sockaddr.sa, &l) < 0)
310 if (l < sizeof(sa_family_t))
313 if (sockaddr.sa.sa_family != AF_INET &&
314 sockaddr.sa.sa_family != AF_INET6)
318 if (sockaddr.sa.sa_family != family)
322 if (sockaddr.sa.sa_family == AF_INET) {
323 if (l < sizeof(struct sockaddr_in))
326 return htons(port) == sockaddr.in4.sin_port;
328 if (l < sizeof(struct sockaddr_in6))
331 return htons(port) == sockaddr.in6.sin6_port;
338 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
339 union sockaddr_union sockaddr;
343 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
346 memset(&sockaddr, 0, sizeof(sockaddr));
347 l = sizeof(sockaddr);
349 if (getsockname(fd, &sockaddr.sa, &l) < 0)
352 if (l < sizeof(sa_family_t))
355 if (sockaddr.sa.sa_family != AF_UNIX)
360 length = strlen(path);
364 return l == offsetof(struct sockaddr_un, sun_path);
367 /* Normal path socket */
369 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
370 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
372 /* Abstract namespace socket */
374 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
375 memcmp(path, sockaddr.un.sun_path, length) == 0;
381 _sd_export_ int sd_is_mq(int fd, const char *path) {
382 #if !defined(__linux__)
390 if (mq_getattr(fd, &attr) < 0)
394 char fpath[PATH_MAX];
400 if (fstat(fd, &a) < 0)
403 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
404 fpath[sizeof(fpath)-1] = 0;
406 if (stat(fpath, &b) < 0)
409 if (a.st_dev != b.st_dev ||
410 a.st_ino != b.st_ino)
418 _sd_export_ int sd_notify(int unset_environment, const char *state) {
419 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
423 struct msghdr msghdr;
425 union sockaddr_union sockaddr;
433 if (!(e = getenv("NOTIFY_SOCKET")))
436 /* Must be an abstract socket, or an absolute path */
437 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
442 if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
447 memset(&sockaddr, 0, sizeof(sockaddr));
448 sockaddr.sa.sa_family = AF_UNIX;
449 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
451 if (sockaddr.un.sun_path[0] == '@')
452 sockaddr.un.sun_path[0] = 0;
454 memset(&iovec, 0, sizeof(iovec));
455 iovec.iov_base = (char*) state;
456 iovec.iov_len = strlen(state);
458 memset(&msghdr, 0, sizeof(msghdr));
459 msghdr.msg_name = &sockaddr;
460 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 msghdr.msg_iov = &iovec;
466 msghdr.msg_iovlen = 1;
468 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
476 if (unset_environment)
477 unsetenv("NOTIFY_SOCKET");
486 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
487 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
494 va_start(ap, format);
495 r = vasprintf(&p, format, ap);
501 r = sd_notify(unset_environment, p);
508 _sd_export_ int sd_booted(void) {
509 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
515 /* We simply test whether the systemd cgroup hierarchy is
518 if (lstat("/sys/fs/cgroup", &a) < 0)
521 if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
524 return a.st_dev != b.st_dev;