Provide and use fallbacks for POSIX functions extended by linux/BSD.
authorDavin McCall <davmac@davmac.org>
Wed, 26 Apr 2017 09:35:29 +0000 (10:35 +0100)
committerDavin McCall <davmac@davmac.org>
Wed, 26 Apr 2017 09:35:29 +0000 (10:35 +0100)
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 [new file with mode: 0644]
src/dinit.cc
src/service.cc

diff --git a/src/dinit-socket.h b/src/dinit-socket.h
new file mode 100644 (file)
index 0000000..1e3b3e9
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef _DINIT_SOCKET_H_INCLUDED
+#define _DINIT_SOCKET_H_INCLUDED
+
+#include <sys/socket.h>
+#include <fcntl.h>
+
+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
index 53289a08dac13aca10029101f40df97fa2de5277..0ae09ae391d538c7b42a696ccf2a379ec7727cd3 100644 (file)
@@ -17,6 +17,7 @@
 #include "service.h"
 #include "control.h"
 #include "dinit-log.h"
+#include "dinit-socket.h"
 
 #ifdef __linux__
 #include <sys/klog.h>
@@ -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);
index d23c3fce6c44af2a74170b71c5df3e85c301972a..c15c80d1b0d5ff33decf90818d84e456c22f4443 100644 (file)
@@ -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<const char *> &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;
         }