From a354329f724d6ce913d2ccffb2be8f3327a67faa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Jan 2015 00:26:25 +0100 Subject: core: add new logic for services to store file descriptors in PID 1 With this change it is possible to send file descriptors to PID 1, via sd_pid_notify_with_fds() which PID 1 will store individually for each service, and pass via the usual fd passing logic on next invocation. This is useful for enable daemon reload schemes where daemons serialize their state to /run, push their fds into PID 1 and terminate, restoring their state on next start from the data in /run and passed in from PID 1. The fds are kept by PID 1 as long as no POLLHUP or POLLERR is seen on them, and the service they belong to are either not dead or failed, or have a job queued. --- src/libsystemd/libsystemd.sym.m4 | 5 ++ src/libsystemd/sd-daemon/sd-daemon.c | 94 ++++++++++++++++++++++++------------ 2 files changed, 67 insertions(+), 32 deletions(-) (limited to 'src/libsystemd') diff --git a/src/libsystemd/libsystemd.sym.m4 b/src/libsystemd/libsystemd.sym.m4 index 80a61baab8..19a49f45da 100644 --- a/src/libsystemd/libsystemd.sym.m4 +++ b/src/libsystemd/libsystemd.sym.m4 @@ -158,6 +158,11 @@ global: sd_session_get_desktop; } LIBSYSTEMD_216; +LIBSYSTEMD_219 { +global: + sd_pid_notify_with_fds; +} LIBSYSTEMD_217; + m4_ifdef(`ENABLE_KDBUS', LIBSYSTEMD_FUTURE { global: diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 1f2a53393f..028c2a7a5b 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -340,16 +340,28 @@ _public_ int sd_is_mq(int fd, const char *path) { return 1; } -_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { - union sockaddr_union sockaddr = {}; - _cleanup_close_ int fd = -1; - struct msghdr msghdr = {}; - struct iovec iovec = {}; - const char *e; +_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) { + union sockaddr_union sockaddr = { + .sa.sa_family = AF_UNIX, + }; + struct iovec iovec = { + .iov_base = (char*) state, + }; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &sockaddr, + }; union { struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control = {}; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int) * n_fds)]; + } control; + _cleanup_close_ int fd = -1; + struct cmsghdr *cmsg = NULL; + const char *e; + size_t controllen_without_ucred = 0; + bool try_without_ucred = false; int r; if (!state) { @@ -357,6 +369,11 @@ _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) goto finish; } + if (n_fds > 0 && !fds) { + r = -EINVAL; + goto finish; + } + e = getenv("NOTIFY_SOCKET"); if (!e) return 0; @@ -373,42 +390,50 @@ _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) goto finish; } - sockaddr.sa.sa_family = AF_UNIX; - strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); + iovec.iov_len = strlen(state); + strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); if (sockaddr.un.sun_path[0] == '@') sockaddr.un.sun_path[0] = 0; - iovec.iov_base = (char*) state; - iovec.iov_len = strlen(state); - - msghdr.msg_name = &sockaddr; msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + 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; + if (n_fds > 0) { + msghdr.msg_control = &control; + msghdr.msg_controllen = CMSG_LEN(sizeof(int) * n_fds); + + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); + + memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); + } if (pid != 0 && pid != getpid()) { - struct cmsghdr *cmsg; - struct ucred ucred = {}; + struct ucred *ucred; + + try_without_ucred = true; + controllen_without_ucred = msghdr.msg_controllen; msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); + msghdr.msg_controllen += CMSG_LEN(sizeof(struct ucred)); + + if (cmsg) + cmsg = CMSG_NXTHDR(&msghdr, cmsg); + else + cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg = CMSG_FIRSTHDR(&msghdr); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - ucred.pid = pid; - ucred.uid = getuid(); - ucred.gid = getgid(); - - memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred)); - msghdr.msg_controllen = cmsg->cmsg_len; + ucred = (struct ucred*) CMSG_DATA(cmsg); + ucred->pid = pid; + ucred->uid = getuid(); + ucred->gid = getgid(); } /* First try with fake ucred data, as requested */ @@ -417,10 +442,11 @@ _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) goto finish; } - /* If that failed, try with our own instead */ - if (msghdr.msg_control) { - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; + /* If that failed, try with our own ucred instead */ + if (try_without_ucred) { + if (controllen_without_ucred <= 0) + msghdr.msg_control = NULL; + msghdr.msg_controllen = controllen_without_ucred; if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { r = 1; @@ -437,8 +463,12 @@ finish: return r; } +_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { + return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); +} + _public_ int sd_notify(int unset_environment, const char *state) { - return sd_pid_notify(0, unset_environment, state); + return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0); } _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) { -- cgit v1.2.3-54-g00ecf