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>
45 #include "sd-daemon.h"
47 int sd_listen_fds(int unset_environment) {
53 if (!(e = getenv("LISTEN_PID"))) {
59 l = strtoul(e, &p, 10);
66 if (!p || *p || l <= 0) {
72 if (getpid() != (pid_t)l) {
77 if (!(e = getenv("LISTEN_FDS"))) {
83 l = strtoul(e, &p, 10);
95 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)l; fd++) {
98 if ((flags = fcntl(fd, F_GETFD)) < 0) {
103 if (flags & FD_CLOEXEC)
106 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
115 if (unset_environment) {
116 unsetenv("LISTEN_PID");
117 unsetenv("LISTEN_FDS");
123 int sd_is_fifo(int fd, const char *path) {
129 memset(&st_fd, 0, sizeof(st_fd));
130 if (fstat(fd, &st_fd) < 0)
133 if (!S_ISFIFO(st_fd.st_mode))
139 memset(&st_path, 0, sizeof(st_path));
140 if (stat(path, &st_path) < 0) {
141 if (errno == ENOENT || errno == ENOTDIR)
147 return st_path.st_dev == st_fd.st_dev &&
148 st_path.st_ino == st_fd.st_ino;
154 static int sd_is_socket_internal(int fd, int type, int listening) {
157 if (fd < 0 || type < 0)
160 if (fstat(fd, &st_fd) < 0)
163 if (!S_ISSOCK(st_fd.st_mode))
168 socklen_t l = sizeof(other_type);
170 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
173 if (l != sizeof(other_type))
176 if (other_type != type)
180 if (listening >= 0) {
182 socklen_t l = sizeof(accepting);
184 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
187 if (l != sizeof(accepting))
190 if (!accepting != !listening)
197 union sockaddr_union {
199 struct sockaddr_in in4;
200 struct sockaddr_in6 in6;
201 struct sockaddr_un un;
202 struct sockaddr_storage storage;
205 int sd_is_socket(int fd, int family, int type, int listening) {
211 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
215 union sockaddr_union sockaddr;
218 memset(&sockaddr, 0, sizeof(sockaddr));
219 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 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
234 union sockaddr_union sockaddr;
238 if (family != 0 && family != AF_INET && family != AF_INET6)
241 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
244 memset(&sockaddr, 0, sizeof(sockaddr));
245 l = sizeof(sockaddr);
247 if (getsockname(fd, &sockaddr.sa, &l) < 0)
250 if (l < sizeof(sa_family_t))
253 if (sockaddr.sa.sa_family != AF_INET &&
254 sockaddr.sa.sa_family != AF_INET6)
258 if (sockaddr.sa.sa_family != family)
262 if (sockaddr.sa.sa_family == AF_INET) {
263 if (l < sizeof(struct sockaddr_in))
266 return htons(port) == sockaddr.in4.sin_port;
268 if (l < sizeof(struct sockaddr_in6))
271 return htons(port) == sockaddr.in6.sin6_port;
278 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
279 union sockaddr_union sockaddr;
283 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
286 memset(&sockaddr, 0, sizeof(sockaddr));
287 l = sizeof(sockaddr);
289 if (getsockname(fd, &sockaddr.sa, &l) < 0)
292 if (l < sizeof(sa_family_t))
295 if (sockaddr.sa.sa_family != AF_UNIX)
300 length = strlen(path);
304 return l == offsetof(struct sockaddr_un, sun_path);
307 /* Normal path socket */
308 return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
309 memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
311 /* Abstract namespace socket */
312 return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
313 memcmp(path, sockaddr.un.sun_path, length) == 0;
319 int sd_notify(int unset_environment, const char *state) {
320 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
324 struct msghdr msghdr;
326 union sockaddr_union sockaddr;
334 if (!(e = getenv("NOTIFY_SOCKET")))
337 /* Must be an abstract socket, or an absolute path */
338 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
343 if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
348 memset(&sockaddr, 0, sizeof(sockaddr));
349 sockaddr.sa.sa_family = AF_UNIX;
350 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
352 if (sockaddr.un.sun_path[0] == '@')
353 sockaddr.un.sun_path[0] = 0;
355 memset(&iovec, 0, sizeof(iovec));
356 iovec.iov_base = (char *)state;
357 iovec.iov_len = strlen(state);
359 memset(&msghdr, 0, sizeof(msghdr));
360 msghdr.msg_name = &sockaddr;
361 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
363 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
364 msghdr.msg_namelen = sizeof(struct sockaddr_un);
366 msghdr.msg_iov = &iovec;
367 msghdr.msg_iovlen = 1;
369 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
377 if (unset_environment)
378 unsetenv("NOTIFY_SOCKET");
387 int sd_notifyf(int unset_environment, const char *format, ...) {
388 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
395 va_start(ap, format);
396 r = vasprintf(&p, format, ap);
402 r = sd_notify(unset_environment, p);
409 int sd_booted(void) {
410 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
416 /* We simply test whether the systemd cgroup hierarchy is mounted */
418 if (lstat("/sys/fs/cgroup", &a) < 0)
421 if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
424 return a.st_dev != b.st_dev;