about summary refs log tree commit homepage
diff options
context:
space:
mode:
-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]