emulate SOCK_CLOEXEC and SOCK_NONBLOCK for old (pre-2.6.27) kernels
authorRich Felker <dalias@aerifal.cx>
Sat, 29 Sep 2012 21:36:27 +0000 (17:36 -0400)
committerRich Felker <dalias@aerifal.cx>
Sat, 29 Sep 2012 21:36:27 +0000 (17:36 -0400)
also update syslog to use SOCK_CLOEXEC rather than separate fcntl
step, to make it safe in multithreaded programs that run external
programs.

emulation is not atomic; it could be made atomic by holding a lock on
forking during the operation, but this seems like overkill. my goal is
not to achieve perfect behavior on old kernels (which have plenty of
other imperfect behavior already) but to avoid catastrophic breakage
in (1) syslog, which would give no output on old kernels with the
change to use SOCK_CLOEXEC, and (2) programs built on a new kernel
where configure scripts detected a working SOCK_CLOEXEC, which later
get run on older kernels (they may otherwise fail to work completely).

src/misc/syslog.c
src/network/socket.c

index a4f36dee9582111bbf3425251a554e2c4a01bf0c..8de34f8db0c923af46ede6680cda07c7ea28df86 100644 (file)
@@ -51,8 +51,7 @@ static void __openlog(const char *ident, int opt, int facility)
 
        if (!(opt & LOG_NDELAY) || log_fd>=0) return;
 
-       log_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
-       fcntl(log_fd, F_SETFD, FD_CLOEXEC);
+       log_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
 }
 
 void openlog(const char *ident, int opt, int facility)
index 4f1e86dbf16f9d0e124137eb3623baa5057af281..ba8d45b16403d991ffe2a6e3fa02d9440bc7b529 100644 (file)
@@ -1,7 +1,20 @@
 #include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
 #include "syscall.h"
 
 int socket(int domain, int type, int protocol)
 {
-       return socketcall(socket, domain, type, protocol, 0, 0, 0);
+       int s = socketcall(socket, domain, type, protocol, 0, 0, 0);
+       if (s<0 && errno==EINVAL && (type&(SOCK_CLOEXEC|SOCK_NONBLOCK))) {
+               s = socketcall(socket, domain,
+                       type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK),
+                       protocol, 0, 0, 0);
+               if (s < 0) return s;
+               if (type & SOCK_CLOEXEC)
+                       fcntl(s, F_SETFD, FD_CLOEXEC);
+               if (type & SOCK_NONBLOCK)
+                       fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
+       }
+       return s;
 }