about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-02-22 19:24:54 -0800
committerEric Wong <normalperson@yhbt.net>2011-02-23 13:14:27 -0800
commitdb192979f096d0153ad14e530e1e2e193289c7e0 (patch)
tree356c4a8bf64f30dd6c4886adc92f253bd4bf831d
parentebc2093847705c382b4d83ed5120e44b9afad3c0 (diff)
downloadraindrops-db192979f096d0153ad14e530e1e2e193289c7e0.tar.gz
This returns a Raindrops::TCP_Info object
that wraps a tcp_info struct.
-rw-r--r--ext/raindrops/linux_tcp_info.c114
-rw-r--r--ext/raindrops/my_fileno.h36
-rw-r--r--ext/raindrops/raindrops.c2
-rw-r--r--test/test_linux_tcp_info.rb41
4 files changed, 193 insertions, 0 deletions
diff --git a/ext/raindrops/linux_tcp_info.c b/ext/raindrops/linux_tcp_info.c
new file mode 100644
index 0000000..8ca3585
--- /dev/null
+++ b/ext/raindrops/linux_tcp_info.c
@@ -0,0 +1,114 @@
+#include <ruby.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef TCP_INFO
+#include "my_fileno.h"
+
+#define TCPI_ATTR_READER(x) \
+static VALUE tcp_info_##x(VALUE self) \
+{ \
+        struct tcp_info *info = DATA_PTR(self); \
+        return UINT2NUM((uint32_t)info->tcpi_##x); \
+}
+
+TCPI_ATTR_READER(state)
+TCPI_ATTR_READER(ca_state)
+TCPI_ATTR_READER(retransmits)
+TCPI_ATTR_READER(probes)
+TCPI_ATTR_READER(backoff)
+TCPI_ATTR_READER(options)
+TCPI_ATTR_READER(snd_wscale)
+TCPI_ATTR_READER(rcv_wscale)
+TCPI_ATTR_READER(rto)
+TCPI_ATTR_READER(ato)
+TCPI_ATTR_READER(snd_mss)
+TCPI_ATTR_READER(rcv_mss)
+TCPI_ATTR_READER(unacked)
+TCPI_ATTR_READER(sacked)
+TCPI_ATTR_READER(lost)
+TCPI_ATTR_READER(retrans)
+TCPI_ATTR_READER(fackets)
+TCPI_ATTR_READER(last_data_sent)
+TCPI_ATTR_READER(last_ack_sent)
+TCPI_ATTR_READER(last_data_recv)
+TCPI_ATTR_READER(last_ack_recv)
+TCPI_ATTR_READER(pmtu)
+TCPI_ATTR_READER(rcv_ssthresh)
+TCPI_ATTR_READER(rtt)
+TCPI_ATTR_READER(rttvar)
+TCPI_ATTR_READER(snd_ssthresh)
+TCPI_ATTR_READER(snd_cwnd)
+TCPI_ATTR_READER(advmss)
+TCPI_ATTR_READER(reordering)
+TCPI_ATTR_READER(rcv_rtt)
+TCPI_ATTR_READER(rcv_space)
+TCPI_ATTR_READER(total_retrans)
+
+static VALUE alloc(VALUE klass)
+{
+        struct tcp_info *info = xmalloc(sizeof(struct tcp_info));
+
+        /* Data_Make_Struct has an extra memset 0 which is so wasteful */
+        return Data_Wrap_Struct(klass, NULL, -1, info);
+}
+
+static VALUE init(VALUE self, VALUE io)
+{
+        int fd = my_fileno(io);
+        struct tcp_info *info = DATA_PTR(self);
+        socklen_t len = (socklen_t)sizeof(struct tcp_info);
+        int rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, info, &len);
+
+        if (rc != 0)
+                rb_sys_fail("getsockopt");
+
+        return self;
+}
+
+void Init_raindrops_linux_tcp_info(void)
+{
+        VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
+        VALUE cTCP_Info;
+
+        cTCP_Info = rb_define_class_under(cRaindrops, "TCP_Info", rb_cObject);
+        rb_define_alloc_func(cTCP_Info, alloc);
+        rb_define_private_method(cTCP_Info, "initialize", init, 1);
+
+#define TCPI_DEFINE_METHOD(x) \
+        rb_define_method(cTCP_Info, #x, tcp_info_##x, 0)
+
+        TCPI_DEFINE_METHOD(state);
+        TCPI_DEFINE_METHOD(ca_state);
+        TCPI_DEFINE_METHOD(retransmits);
+        TCPI_DEFINE_METHOD(probes);
+        TCPI_DEFINE_METHOD(backoff);
+        TCPI_DEFINE_METHOD(options);
+        TCPI_DEFINE_METHOD(snd_wscale);
+        TCPI_DEFINE_METHOD(rcv_wscale);
+        TCPI_DEFINE_METHOD(rto);
+        TCPI_DEFINE_METHOD(ato);
+        TCPI_DEFINE_METHOD(snd_mss);
+        TCPI_DEFINE_METHOD(rcv_mss);
+        TCPI_DEFINE_METHOD(unacked);
+        TCPI_DEFINE_METHOD(sacked);
+        TCPI_DEFINE_METHOD(lost);
+        TCPI_DEFINE_METHOD(retrans);
+        TCPI_DEFINE_METHOD(fackets);
+        TCPI_DEFINE_METHOD(last_data_sent);
+        TCPI_DEFINE_METHOD(last_ack_sent);
+        TCPI_DEFINE_METHOD(last_data_recv);
+        TCPI_DEFINE_METHOD(last_ack_recv);
+        TCPI_DEFINE_METHOD(pmtu);
+        TCPI_DEFINE_METHOD(rcv_ssthresh);
+        TCPI_DEFINE_METHOD(rtt);
+        TCPI_DEFINE_METHOD(rttvar);
+        TCPI_DEFINE_METHOD(snd_ssthresh);
+        TCPI_DEFINE_METHOD(snd_cwnd);
+        TCPI_DEFINE_METHOD(advmss);
+        TCPI_DEFINE_METHOD(reordering);
+        TCPI_DEFINE_METHOD(rcv_rtt);
+        TCPI_DEFINE_METHOD(rcv_space);
+        TCPI_DEFINE_METHOD(total_retrans);
+}
+#endif /* TCP_INFO */
diff --git a/ext/raindrops/my_fileno.h b/ext/raindrops/my_fileno.h
new file mode 100644
index 0000000..bdf1a5f
--- /dev/null
+++ b/ext/raindrops/my_fileno.h
@@ -0,0 +1,36 @@
+#include <ruby.h>
+#ifdef HAVE_RUBY_IO_H
+#  include <ruby/io.h>
+#else
+#  include <stdio.h>
+#  include <rubyio.h>
+#endif
+
+#if ! HAVE_RB_IO_T
+#  define rb_io_t OpenFile
+#endif
+
+#ifdef GetReadFile
+#  define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
+#else
+#  if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
+#    define FPTR_TO_FD(fptr) fileno(fptr->f)
+#  else
+#    define FPTR_TO_FD(fptr) fptr->fd
+#  endif
+#endif
+
+static int my_fileno(VALUE io)
+{
+        rb_io_t *fptr;
+        int fd;
+
+        if (TYPE(io) != T_FILE)
+                io = rb_convert_type(io, T_FILE, "IO", "to_io");
+        GetOpenFile(io, fptr);
+        fd = FPTR_TO_FD(fptr);
+
+        if (fd < 0)
+                rb_raise(rb_eIOError, "closed stream");
+        return fd;
+}
diff --git a/ext/raindrops/raindrops.c b/ext/raindrops/raindrops.c
index e43dc2c..599f39d 100644
--- a/ext/raindrops/raindrops.c
+++ b/ext/raindrops/raindrops.c
@@ -172,6 +172,7 @@ static VALUE aref(VALUE self, VALUE index)
 
 #ifdef __linux__
 void Init_raindrops_linux_inet_diag(void);
+void Init_raindrops_linux_tcp_info(void);
 #endif
 
 #ifndef _SC_NPROCESSORS_ONLN
@@ -215,5 +216,6 @@ void Init_raindrops_ext(void)
 
 #ifdef __linux__
         Init_raindrops_linux_inet_diag();
+        Init_raindrops_linux_tcp_info();
 #endif
 }
diff --git a/test/test_linux_tcp_info.rb b/test/test_linux_tcp_info.rb
new file mode 100644
index 0000000..bfa7eb3
--- /dev/null
+++ b/test/test_linux_tcp_info.rb
@@ -0,0 +1,41 @@
+# -*- encoding: binary -*-
+require 'test/unit'
+require 'tempfile'
+require 'raindrops'
+require 'socket'
+require 'pp'
+$stderr.sync = $stdout.sync = true
+class TestLinuxTCP_Info < Test::Unit::TestCase
+
+  TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
+
+  def test_tcp_server
+    s = TCPServer.new(TEST_ADDR, 0)
+    rv = Raindrops::TCP_Info.new s
+    c = TCPSocket.new TEST_ADDR, s.addr[1]
+    tmp = Raindrops::TCP_Info.new s
+    assert_equal 1, tmp.unacked
+    assert_equal 0, rv.unacked
+    a = s.accept
+    tmp = Raindrops::TCP_Info.new s
+    assert_equal 0, tmp.unacked
+    ensure
+      c.close if c
+      a.close if a
+      s.close
+  end
+
+  def test_accessors
+    s = TCPServer.new TEST_ADDR, 0
+    tmp = Raindrops::TCP_Info.new s
+    tcp_info_methods = tmp.methods - Object.new.methods
+    assert tcp_info_methods.size >= 32
+    tcp_info_methods.each do |m|
+      val = tmp.__send__ m
+      assert_kind_of Integer, val
+      assert val >= 0
+    end
+    ensure
+      s.close
+  end
+end