X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsd-daemon.c;h=3bb258d3418570fe6c5ac8540c47c089a77b703e;hp=ad2bfa82e72f4e76e9d46edb069a35c14459b9ec;hb=a013bd94cc0137c20236f3db2fa156ada9373e59;hpb=dde770cfc6a287772a936be58b73adf9a6c334f5 diff --git a/src/sd-daemon.c b/src/sd-daemon.c index ad2bfa82e..3bb258d34 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -24,24 +24,31 @@ SOFTWARE. ***/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include #include +#include #include #include #include #include #include +#include +#include #include "sd-daemon.h" int sd_listen_fds(int unset_environment) { -#ifdef DISABLE_SYSTEMD +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else - int r; + int r, fd; const char *e; char *p = NULL; unsigned long l; @@ -88,6 +95,23 @@ int sd_listen_fds(int unset_environment) { goto finish; } + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) < 0) { + r = -errno; + goto finish; + } + + if (flags & FD_CLOEXEC) + continue; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { + r = -errno; + goto finish; + } + } + r = (int) l; finish: @@ -117,7 +141,7 @@ int sd_is_fifo(int fd, const char *path) { struct stat st_path; memset(&st_path, 0, sizeof(st_path)); - if (fstat(fd, &st_path) < 0) { + if (stat(path, &st_path) < 0) { if (errno == ENOENT || errno == ENOTDIR) return 0; @@ -133,7 +157,7 @@ int sd_is_fifo(int fd, const char *path) { return 1; } -int sd_is_socket(int fd, int type, int listening) { +static int sd_is_socket_internal(int fd, int type, int listening) { struct stat st_fd; if (fd < 0 || type < 0) @@ -176,18 +200,51 @@ int sd_is_socket(int fd, int type, int listening) { return 1; } -int sd_is_socket_inet(int fd, int type, int listening, uint16_t port) { - union { - struct sockaddr sa; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_storage storage; - } sockaddr; +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_storage storage; +}; + +int sd_is_socket(int fd, int family, int type, int listening) { + int r; + + if (family < 0) + return -EINVAL; + + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + return r; + + if (family > 0) { + union sockaddr_union sockaddr; + socklen_t l; + + memset(&sockaddr, 0, sizeof(sockaddr)); + l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + return sockaddr.sa.sa_family == family; + } + + return 1; +} + +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { + union sockaddr_union sockaddr; socklen_t l; int r; - if ((r = sd_is_socket(fd, type, listening)) <= 0) + if (family != 0 && family != AF_INET && family != AF_INET6) + return -EINVAL; + + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) return r; memset(&sockaddr, 0, sizeof(sockaddr)); @@ -196,13 +253,17 @@ int sd_is_socket_inet(int fd, int type, int listening, uint16_t port) { if (getsockname(fd, &sockaddr.sa, &l) < 0) return -errno; - if (l < sizeof(struct sockaddr)) + if (l < sizeof(sa_family_t)) return -EINVAL; if (sockaddr.sa.sa_family != AF_INET && sockaddr.sa.sa_family != AF_INET6) return 0; + if (family > 0) + if (sockaddr.sa.sa_family != family) + return 0; + if (port > 0) { if (sockaddr.sa.sa_family == AF_INET) { if (l < sizeof(struct sockaddr_in)) @@ -221,17 +282,11 @@ int sd_is_socket_inet(int fd, int type, int listening, uint16_t port) { } int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { - union { - struct sockaddr sa; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_storage storage; - } sockaddr; + union sockaddr_union sockaddr; socklen_t l; int r; - if ((r = sd_is_socket(fd, type, listening)) <= 0) + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) return r; memset(&sockaddr, 0, sizeof(sockaddr)); @@ -268,3 +323,130 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t return 1; } + +int sd_notify(int unset_environment, const char *state) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + int fd = -1, r; + struct msghdr msghdr; + struct iovec iovec; + union sockaddr_union sockaddr; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + const char *e; + + if (!state) { + r = -EINVAL; + goto finish; + } + + if (!(e = getenv("NOTIFY_SOCKET"))) + return 0; + + /* Must be an abstract socket, or an absolute path */ + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { + r = -EINVAL; + goto finish; + } + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + goto finish; + } + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sa.sa_family = AF_UNIX; + strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); + + if (sockaddr.un.sun_path[0] == '@') + sockaddr.un.sun_path[0] = 0; + + memset(&iovec, 0, sizeof(iovec)); + iovec.iov_base = (char*) state; + iovec.iov_len = strlen(state); + + memset(&control, 0, sizeof(control)); + control.cmsghdr.cmsg_level = SOL_SOCKET; + control.cmsghdr.cmsg_type = SCM_CREDENTIALS; + control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_name = &sockaddr; + msghdr.msg_namelen = sizeof(sa_family_t) + strlen(e); + + if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) + msghdr.msg_namelen = sizeof(struct sockaddr_un); + + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = control.cmsghdr.cmsg_len; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { + r = -errno; + goto finish; + } + + r = 1; + +finish: + if (unset_environment) + unsetenv("NOTIFY_SOCKET"); + + if (fd >= 0) + close(fd); + + return r; +#endif +} + +int sd_notifyf(int unset_environment, const char *format, ...) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + va_list ap; + char *p = NULL; + int r; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + + r = sd_notify(unset_environment, p); + free(p); + + return r; +#endif +} + +int sd_booted(void) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + + struct stat a, b; + + /* We simply test whether the systemd cgroup hierarchy is + * mounted */ + + if (lstat("/cgroup", &a) < 0) + return 0; + + if (lstat("/cgroup/systemd", &b) < 0) + return 0; + + return a.st_dev != b.st_dev; +#endif +}