about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-11-16 18:37:37 -0800
committerEric Wong <normalperson@yhbt.net>2011-11-17 17:58:06 -0800
commitbe3672501ecde716dae723e887d4a9e4d731240c (patch)
tree07c6fc18258a4adecf51deb7e2136d81892cac08
parent48dc3c5a1943801311567e72a8e69fcb0cd8cf8d (diff)
downloadkgio-be3672501ecde716dae723e887d4a9e4d731240c.tar.gz
All IO objects created by Kgio will have FD_CLOEXEC descriptor
flag set on it when run under Ruby 2.0.0dev.  This matches the
upcoming behavior of Ruby 2.0.0dev for IO objects in the core
and standard library.  This change does not affect users on Ruby
1.9.3 and earlier.

accept()-ed sockets in kgio have _always_ had FD_CLOEXEC
set by default.
-rw-r--r--ext/kgio/connect.c37
-rw-r--r--ext/kgio/extconf.rb2
-rw-r--r--ext/kgio/tryopen.c6
-rw-r--r--test/test_tcp_connect.rb11
-rw-r--r--test/test_tryopen.rb4
-rw-r--r--test/test_unix_connect.rb8
6 files changed, 59 insertions, 9 deletions
diff --git a/ext/kgio/connect.c b/ext/kgio/connect.c
index dd2f409..ff5e639 100644
--- a/ext/kgio/connect.c
+++ b/ext/kgio/connect.c
@@ -9,16 +9,30 @@ static void close_fail(int fd, const char *msg)
         rb_sys_fail(msg);
 }
 
-#ifdef SOCK_NONBLOCK
-#  define MY_SOCK_STREAM (SOCK_STREAM|SOCK_NONBLOCK)
+static int MY_SOCK_STREAM =
+#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
+#  ifdef HAVE_RB_FD_FIX_CLOEXEC
+  (SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC)
+#  else
+  (SOCK_STREAM|SOCK_NONBLOCK)
+#  endif
 #else
-#  define MY_SOCK_STREAM SOCK_STREAM
+  SOCK_STREAM
 #endif /* ! SOCK_NONBLOCK */
+;
+
+/* do not set close-on-exec by default on Ruby <2.0.0 */
+#ifndef HAVE_RB_FD_FIX_CLOEXEC
+#  define rb_fd_fix_cloexec(fd) for (;0;)
+#endif /* HAVE_RB_FD_FIX_CLOEXEC */
 
 static VALUE
 my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen)
 {
-        int fd = socket(domain, MY_SOCK_STREAM, 0);
+        int fd;
+
+retry:
+        fd = socket(domain, MY_SOCK_STREAM, 0);
 
         if (fd == -1) {
                 switch (errno) {
@@ -30,15 +44,22 @@ my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen)
                         errno = 0;
                         rb_gc();
                         fd = socket(domain, MY_SOCK_STREAM, 0);
+                        break;
+                case EINVAL:
+                        if (MY_SOCK_STREAM != SOCK_STREAM) {
+                                MY_SOCK_STREAM = SOCK_STREAM;
+                                goto retry;
+                        }
                 }
                 if (fd == -1)
                         rb_sys_fail("socket");
         }
 
-#ifndef SOCK_NONBLOCK
-        if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK) == -1)
-                close_fail(fd, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)");
-#endif /* SOCK_NONBLOCK */
+        if (MY_SOCK_STREAM == SOCK_STREAM) {
+                if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK) == -1)
+                        close_fail(fd, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)");
+                rb_fd_fix_cloexec(fd);
+        }
 
         if (connect(fd, addr, addrlen) == -1) {
                 if (errno == EINPROGRESS) {
diff --git a/ext/kgio/extconf.rb b/ext/kgio/extconf.rb
index a76454d..fb680f7 100644
--- a/ext/kgio/extconf.rb
+++ b/ext/kgio/extconf.rb
@@ -42,6 +42,8 @@ have_type("struct RObject") and check_sizeof("struct RObject")
 check_sizeof("int")
 have_func('rb_io_ascii8bit_binmode')
 have_func('rb_update_max_fd')
+have_func('rb_fd_fix_cloexec')
+have_func('rb_cloexec_open')
 have_func('rb_thread_blocking_region')
 have_func('rb_thread_io_blocking_region')
 have_func('rb_str_set_len')
diff --git a/ext/kgio/tryopen.c b/ext/kgio/tryopen.c
index 16f47fb..85bbbc2 100644
--- a/ext/kgio/tryopen.c
+++ b/ext/kgio/tryopen.c
@@ -25,11 +25,15 @@ struct open_args {
         mode_t mode;
 };
 
+#ifndef HAVE_RB_CLOEXEC_OPEN
+#  define rb_cloexec_open(p,f,m) open((p),(f),(m))
+#endif
+
 static VALUE nogvl_open(void *ptr)
 {
         struct open_args *o = ptr;
 
-        return (VALUE)open(o->pathname, o->flags, o->mode);
+        return (VALUE)rb_cloexec_open(o->pathname, o->flags, o->mode);
 }
 
 #ifndef HAVE_RB_THREAD_BLOCKING_REGION
diff --git a/test/test_tcp_connect.rb b/test/test_tcp_connect.rb
index 9756407..2dbe541 100644
--- a/test/test_tcp_connect.rb
+++ b/test/test_tcp_connect.rb
@@ -31,10 +31,17 @@ class TestKgioTcpConnect < Test::Unit::TestCase
     ready = IO.select(nil, [ sock ])
     assert_equal sock, ready[1][0]
     assert_equal nil, sock.kgio_write("HELLO")
+
+    sock.respond_to?(:close_on_exec?) and
+      assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?)
   end
 
   def test_start
     sock = Kgio::Socket.start(@addr)
+
+    sock.respond_to?(:close_on_exec?) and
+      assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?)
+
     assert_kind_of Kgio::Socket, sock
     ready = IO.select(nil, [ sock ])
     assert_equal sock, ready[1][0]
@@ -50,6 +57,10 @@ class TestKgioTcpConnect < Test::Unit::TestCase
 
   def test_tcp_socket_new
     sock = Kgio::TCPSocket.new(@host, @port)
+
+    sock.respond_to?(:close_on_exec?) and
+      assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?)
+
     assert_instance_of Kgio::TCPSocket, sock
     ready = IO.select(nil, [ sock ])
     assert_equal sock, ready[1][0]
diff --git a/test/test_tryopen.rb b/test/test_tryopen.rb
index 380026d..5a8efb2 100644
--- a/test/test_tryopen.rb
+++ b/test/test_tryopen.rb
@@ -7,6 +7,10 @@ class TestTryopen < Test::Unit::TestCase
 
   def test_tryopen_success
     tmp = Kgio::File.tryopen(__FILE__)
+
+    tmp.respond_to?(:close_on_exec?) and
+      assert_equal(RUBY_VERSION.to_f >= 2.0, tmp.close_on_exec?)
+
     assert_kind_of File, tmp
     assert_equal File.read(__FILE__), tmp.read
     assert_equal __FILE__, tmp.path
diff --git a/test/test_unix_connect.rb b/test/test_unix_connect.rb
index f99a877..b85f1f6 100644
--- a/test/test_unix_connect.rb
+++ b/test/test_unix_connect.rb
@@ -34,6 +34,10 @@ class TestKgioUnixConnect < Test::Unit::TestCase
 
   def test_unix_socket_new
     sock = Kgio::UNIXSocket.new(@path)
+
+    sock.respond_to?(:close_on_exec?) and
+      assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?)
+
     assert_instance_of Kgio::UNIXSocket, sock
     ready = IO.select(nil, [ sock ])
     assert_equal sock, ready[1][0]
@@ -42,6 +46,10 @@ class TestKgioUnixConnect < Test::Unit::TestCase
 
   def test_new
     sock = Kgio::Socket.new(@addr)
+
+    sock.respond_to?(:close_on_exec?) and
+      assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?)
+
     assert_instance_of Kgio::Socket, sock
     ready = IO.select(nil, [ sock ])
     assert_equal sock, ready[1][0]