From 4118a92ca4937f5e42f1a113c79e0df3a74f46d8 Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Wed, 26 Apr 2017 10:35:29 +0100 Subject: [PATCH] Provide and use fallbacks for POSIX functions extended by linux/BSD. accept4 isn't standard; Linux and OpenBSD have extensions to socket and socketpair which allow opening in nonblocking/close-on-exec modes. Implement adaptor functions which also work when these extensions aren't present. --- src/dinit-socket.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/dinit.cc | 7 +++-- src/service.cc | 5 ++-- 3 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 src/dinit-socket.h diff --git a/src/dinit-socket.h b/src/dinit-socket.h new file mode 100644 index 0000000..1e3b3e9 --- /dev/null +++ b/src/dinit-socket.h @@ -0,0 +1,72 @@ +#ifndef _DINIT_SOCKET_H_INCLUDED +#define _DINIT_SOCKET_H_INCLUDED + +#include +#include + +namespace { +#if !defined(SOCK_NONBLOCK) && !defined(SOCK_CLOEXEC) + // make our own accept4 on systems that don't have it: + constexpr int SOCK_NONBLOCK = 1; + constexpr int SOCK_CLOEXEC = 2; + inline int dinit_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) + { + int fd = accept(sockfd, addr, addrlen); + if (fd == -1) { + return -1; + } + + if (flags & SOCK_CLOEXEC) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (flags & SOCK_NONBLOCK) fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; + } + + inline int dinit_socket(int domain, int type, int protocol, int flags) + { + int fd = socket(domain, type, protocol); + if (fd == -1) { + return -1; + } + + if (flags & SOCK_CLOEXEC) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (flags & SOCK_NONBLOCK) fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; + } + + inline int dinit_socketpair(int domain, int type, int protocol, int socket_vector[2], int flags) + { + int r = socketpair(domain, type, protocol, socket_vector); + if (r == -1) { + return -1; + } + + if (flags & SOCK_CLOEXEC) { + fcntl(socket_vector[0], F_SETFD, FD_CLOEXEC); + fcntl(socket_vector[1], F_SETFD, FD_CLOEXEC); + } + if (flags & SOCK_NONBLOCK) { + fcntl(socket_vector[0], F_SETFL, O_NONBLOCK); + fcntl(socket_vector[1], F_SETFL, O_NONBLOCK); + } + return 0; + } + +#else + inline int dinit_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) + { + return accept4(sockfd, addr, addrlen, flags); + } + + inline int dinit_socket(int domain, int type, int protocol, int flags) + { + return socket(domain, type | flags, protocol); + } + + inline int dinit_socketpair(int domain, int type, int protocol, int socket_vector[2], int flags) + { + return socketpair(domain, type | flags, protocol, socket_vector); + } +#endif +} + +#endif diff --git a/src/dinit.cc b/src/dinit.cc index 53289a0..0ae09ae 100644 --- a/src/dinit.cc +++ b/src/dinit.cc @@ -17,6 +17,7 @@ #include "service.h" #include "control.h" #include "dinit-log.h" +#include "dinit-socket.h" #ifdef __linux__ #include @@ -410,7 +411,7 @@ static void control_socket_cb(EventLoop_t *loop, int sockfd) // it once it falls below the maximum. // Accept a connection - int newfd = accept4(sockfd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC); + int newfd = dinit_accept4(sockfd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC); if (newfd != -1) { try { @@ -447,7 +448,7 @@ void open_control_socket(bool report_ro_failure) noexcept // OpenBSD and Linux both allow combining NONBLOCK/CLOEXEC flags with socket type, however // it's not actually POSIX. (TODO). - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + int sockfd = dinit_socket(AF_UNIX, SOCK_STREAM, 0, SOCK_NONBLOCK | SOCK_CLOEXEC); if (sockfd == -1) { log(LogLevel::ERROR, "Error creating control socket: ", strerror(errno)); free(name); @@ -520,7 +521,7 @@ void setup_external_log() noexcept name->sun_family = AF_UNIX; memcpy(name->sun_path, saddrname, saddrname_len + 1); - int sockfd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + int sockfd = dinit_socket(AF_UNIX, SOCK_DGRAM, 0, SOCK_NONBLOCK | SOCK_CLOEXEC); if (sockfd == -1) { log(LogLevel::ERROR, "Error creating log socket: ", strerror(errno)); free(name); diff --git a/src/service.cc b/src/service.cc index d23c3fc..c15c80d 100644 --- a/src/service.cc +++ b/src/service.cc @@ -16,6 +16,7 @@ #include "service.h" #include "dinit-log.h" +#include "dinit-socket.h" /* * service.cc - Service management. @@ -530,7 +531,7 @@ bool ServiceRecord::open_socket() noexcept name->sun_family = AF_UNIX; strcpy(name->sun_path, saddrname); - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + int sockfd = dinit_socket(AF_UNIX, SOCK_STREAM, 0, SOCK_NONBLOCK | SOCK_CLOEXEC); if (sockfd == -1) { log(LogLevel::ERROR, service_name, ": Error creating activation socket: ", strerror(errno)); free(name); @@ -743,7 +744,7 @@ bool ServiceRecord::start_ps_process(const std::vector &cmd, bool int control_socket[2] = {-1, -1}; if (onstart_flags.pass_cs_fd) { - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, /* protocol */ 0, control_socket)) { + if (dinit_socketpair(AF_UNIX, SOCK_STREAM, /* protocol */ 0, control_socket, SOCK_NONBLOCK)) { log(LogLevel::ERROR, service_name, ": can't create control socket: ", strerror(errno)); goto out_p; } -- 2.25.1