raindrops RubyGem user+dev discussion/patches/pulls/bugs/help
 help / color / Atom feed
From: Eric Wong <e@80x24.org>
To: raindrops-public@bogomips.org
Subject: [PATCH 1/2] tcp_info: support this struct under FreeBSD
Date: Thu, 16 Mar 2017 03:16:51 +0000
Message-ID: <20170316031652.17433-2-e@80x24.org> (raw)
In-Reply-To: <20170316031652.17433-1-e@80x24.org>

Of course these fields are not portable between Linux and FreeBSD,
but they should remain ABI-compatible for future versions of each OS.

Tested on FreeBSD 10.3-RELEASE i386

TCP state names will be another problem...
---
 ext/raindrops/extconf.rb                          |  78 ++++++++++-
 ext/raindrops/raindrops.c                         |   8 +-
 ext/raindrops/{linux_tcp_info.c => tcp_info.c}    | 159 +++++++++++-----------
 test/{test_linux_tcp_info.rb => test_tcp_info.rb} |  37 +++--
 4 files changed, 189 insertions(+), 93 deletions(-)
 rename ext/raindrops/{linux_tcp_info.c => tcp_info.c} (51%)
 rename test/{test_linux_tcp_info.rb => test_tcp_info.rb} (66%)

diff --git a/ext/raindrops/extconf.rb b/ext/raindrops/extconf.rb
index 79d212c..5273b74 100644
--- a/ext/raindrops/extconf.rb
+++ b/ext/raindrops/extconf.rb
@@ -1,4 +1,5 @@
 require 'mkmf'
+require 'shellwords'
 
 dir_config('atomic_ops')
 have_func('mmap', 'sys/mman.h') or abort 'mmap() not found'
@@ -6,9 +7,83 @@
 
 $CPPFLAGS += " -D_GNU_SOURCE "
 have_func('mremap', 'sys/mman.h')
-have_header('linux/tcp.h')
+headers = %w(sys/types.h netdb.h string.h sys/socket.h netinet/in.h)
+if have_header('linux/tcp.h')
+  headers << 'linux/tcp.h'
+else
+  %w(netinet/tcp.h netinet/tcp_fsm.h).each { |h|
+    have_header(h, headers) and headers << h
+  }
+end
 
 $CPPFLAGS += " -D_BSD_SOURCE "
+
+if have_type("struct tcp_info", headers)
+  %w(
+    tcpi_state
+    tcpi_ca_state
+    tcpi_retransmits
+    tcpi_probes
+    tcpi_backoff
+    tcpi_options
+    tcpi_snd_wscale
+    tcpi_rcv_wscale
+    tcpi_rto
+    tcpi_ato
+    tcpi_snd_mss
+    tcpi_rcv_mss
+    tcpi_unacked
+    tcpi_sacked
+    tcpi_lost
+    tcpi_retrans
+    tcpi_fackets
+    tcpi_last_data_sent
+    tcpi_last_ack_sent
+    tcpi_last_data_recv
+    tcpi_last_ack_recv
+    tcpi_pmtu
+    tcpi_rcv_ssthresh
+    tcpi_rtt
+    tcpi_rttvar
+    tcpi_snd_ssthresh
+    tcpi_snd_cwnd
+    tcpi_advmss
+    tcpi_reordering
+    tcpi_rcv_rtt
+    tcpi_rcv_space
+    tcpi_total_retrans
+    tcpi_snd_wnd
+    tcpi_snd_bwnd
+    tcpi_snd_nxt
+    tcpi_rcv_nxt
+    tcpi_toe_tid
+    tcpi_snd_rexmitpack
+    tcpi_rcv_ooopack
+    tcpi_snd_zerowin
+  ).each do |field|
+    cfunc = "tcp_info_#{field}"
+    if have_struct_member('struct tcp_info', field, headers)
+      func_body = <<EOF
+static VALUE #{cfunc}(VALUE self)
+{
+	struct tcp_info *info = DATA_PTR(self);
+	return UINT2NUM((uint32_t)info->#{field});
+}
+EOF
+      func_body.delete!("\n")
+      $defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}"
+    else
+      func_body = "static inline void #{cfunc}(void) {}"
+      $defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}"
+      cfunc = 'rb_f_notimplement'.freeze
+    end
+    rbmethod = %Q("#{field.sub(/\Atcpi_/, ''.freeze)}")
+    $defs << "-DDEFINE_METHOD_tcp_info_#{field}=" \
+	     "#{Shellwords.shellescape(
+                %Q[rb_define_method(cTCP_Info,#{rbmethod},#{cfunc},0)])}"
+  end
+end
+
 have_func("getpagesize", "unistd.h")
 have_func('rb_thread_blocking_region')
 have_func('rb_thread_io_blocking_region')
@@ -53,4 +128,5 @@
 
   apt-get install libatomic-ops-dev
 SRC
+create_header # generate extconf.h to avoid excessively long command-line
 create_makefile('raindrops_ext')
diff --git a/ext/raindrops/raindrops.c b/ext/raindrops/raindrops.c
index 390b8b8..0ea3e32 100644
--- a/ext/raindrops/raindrops.c
+++ b/ext/raindrops/raindrops.c
@@ -336,7 +336,9 @@ static VALUE aref(VALUE self, VALUE index)
 
 #ifdef __linux__
 void Init_raindrops_linux_inet_diag(void);
-void Init_raindrops_linux_tcp_info(void);
+#endif
+#ifdef HAVE_TYPE_STRUCT_TCP_INFO
+void Init_raindrops_tcp_info(void);
 #endif
 
 #ifndef _SC_NPROCESSORS_CONF
@@ -441,6 +443,8 @@ void Init_raindrops_ext(void)
 
 #ifdef __linux__
 	Init_raindrops_linux_inet_diag();
-	Init_raindrops_linux_tcp_info();
+#endif
+#ifdef HAVE_TYPE_STRUCT_TCP_INFO
+	Init_raindrops_tcp_info();
 #endif
 }
diff --git a/ext/raindrops/linux_tcp_info.c b/ext/raindrops/tcp_info.c
similarity index 51%
rename from ext/raindrops/linux_tcp_info.c
rename to ext/raindrops/tcp_info.c
index 83001a5..dc615f7 100644
--- a/ext/raindrops/linux_tcp_info.c
+++ b/ext/raindrops/tcp_info.c
@@ -1,50 +1,53 @@
-#if defined(__linux__) && defined(HAVE_LINUX_TCP_H)
 #include <ruby.h>
+#include "extconf.h"
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <linux/tcp.h>
-#ifdef TCP_INFO
-#include "my_fileno.h"
+#if defined(HAVE_LINUX_TCP_H)
+#  include <linux/tcp.h>
+#else
+#  if defined(HAVE_NETINET_TCP_H)
+#    include <netinet/tcp.h>
+#  endif
+#  if defined(HAVE_NETINET_TCP_FSM_H)
+#    include <netinet/tcp_fsm.h>
+#  endif
+#endif
 
-#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); \
-}
+#ifdef HAVE_TYPE_STRUCT_TCP_INFO
+#include "my_fileno.h"
 
-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)
+CFUNC_tcp_info_tcpi_state
+CFUNC_tcp_info_tcpi_ca_state
+CFUNC_tcp_info_tcpi_retransmits
+CFUNC_tcp_info_tcpi_probes
+CFUNC_tcp_info_tcpi_backoff
+CFUNC_tcp_info_tcpi_options
+CFUNC_tcp_info_tcpi_snd_wscale
+CFUNC_tcp_info_tcpi_rcv_wscale
+CFUNC_tcp_info_tcpi_rto
+CFUNC_tcp_info_tcpi_ato
+CFUNC_tcp_info_tcpi_snd_mss
+CFUNC_tcp_info_tcpi_rcv_mss
+CFUNC_tcp_info_tcpi_unacked
+CFUNC_tcp_info_tcpi_sacked
+CFUNC_tcp_info_tcpi_lost
+CFUNC_tcp_info_tcpi_retrans
+CFUNC_tcp_info_tcpi_fackets
+CFUNC_tcp_info_tcpi_last_data_sent
+CFUNC_tcp_info_tcpi_last_ack_sent
+CFUNC_tcp_info_tcpi_last_data_recv
+CFUNC_tcp_info_tcpi_last_ack_recv
+CFUNC_tcp_info_tcpi_pmtu
+CFUNC_tcp_info_tcpi_rcv_ssthresh
+CFUNC_tcp_info_tcpi_rtt
+CFUNC_tcp_info_tcpi_rttvar
+CFUNC_tcp_info_tcpi_snd_ssthresh
+CFUNC_tcp_info_tcpi_snd_cwnd
+CFUNC_tcp_info_tcpi_advmss
+CFUNC_tcp_info_tcpi_reordering
+CFUNC_tcp_info_tcpi_rcv_rtt
+CFUNC_tcp_info_tcpi_rcv_space
+CFUNC_tcp_info_tcpi_total_retrans
 
 static size_t tcpi_memsize(const void *ptr)
 {
@@ -85,7 +88,7 @@ static VALUE init(VALUE self, VALUE io)
 	return self;
 }
 
-void Init_raindrops_linux_tcp_info(void)
+void Init_raindrops_tcp_info(void)
 {
 	VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
 	VALUE cTCP_Info;
@@ -156,41 +159,37 @@ void Init_raindrops_linux_tcp_info(void)
 	 */
 	rb_define_method(cTCP_Info, "get!", 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);
+	DEFINE_METHOD_tcp_info_tcpi_state;
+	DEFINE_METHOD_tcp_info_tcpi_ca_state;
+	DEFINE_METHOD_tcp_info_tcpi_retransmits;
+	DEFINE_METHOD_tcp_info_tcpi_probes;
+	DEFINE_METHOD_tcp_info_tcpi_backoff;
+	DEFINE_METHOD_tcp_info_tcpi_options;
+	DEFINE_METHOD_tcp_info_tcpi_snd_wscale;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_wscale;
+	DEFINE_METHOD_tcp_info_tcpi_rto;
+	DEFINE_METHOD_tcp_info_tcpi_ato;
+	DEFINE_METHOD_tcp_info_tcpi_snd_mss;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_mss;
+	DEFINE_METHOD_tcp_info_tcpi_unacked;
+	DEFINE_METHOD_tcp_info_tcpi_sacked;
+	DEFINE_METHOD_tcp_info_tcpi_lost;
+	DEFINE_METHOD_tcp_info_tcpi_retrans;
+	DEFINE_METHOD_tcp_info_tcpi_fackets;
+	DEFINE_METHOD_tcp_info_tcpi_last_data_sent;
+	DEFINE_METHOD_tcp_info_tcpi_last_ack_sent;
+	DEFINE_METHOD_tcp_info_tcpi_last_data_recv;
+	DEFINE_METHOD_tcp_info_tcpi_last_ack_recv;
+	DEFINE_METHOD_tcp_info_tcpi_pmtu;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh;
+	DEFINE_METHOD_tcp_info_tcpi_rtt;
+	DEFINE_METHOD_tcp_info_tcpi_rttvar;
+	DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh;
+	DEFINE_METHOD_tcp_info_tcpi_snd_cwnd;
+	DEFINE_METHOD_tcp_info_tcpi_advmss;
+	DEFINE_METHOD_tcp_info_tcpi_reordering;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_space;
+	DEFINE_METHOD_tcp_info_tcpi_total_retrans;
 }
-#endif /* TCP_INFO */
-#endif /* defined(__linux__) && defined(HAVE_LINUX_TCP_H) */
+#endif /* HAVE_STRUCT_TCP_INFO */
diff --git a/test/test_linux_tcp_info.rb b/test/test_tcp_info.rb
similarity index 66%
rename from test/test_linux_tcp_info.rb
rename to test/test_tcp_info.rb
index c947211..15df087 100644
--- a/test/test_linux_tcp_info.rb
+++ b/test/test_tcp_info.rb
@@ -5,15 +5,15 @@
 require 'socket'
 require 'pp'
 $stderr.sync = $stdout.sync = true
-class TestLinuxTCP_Info < Test::Unit::TestCase
+class TestTCP_Info < Test::Unit::TestCase
 
   TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
 
   # Linux kernel commit 5ee3afba88f5a79d0bff07ddd87af45919259f91
   TCP_INFO_useful_listenq = `uname -r`.strip >= '2.6.24'
 
-
-  def test_tcp_server
+  def test_tcp_server_unacked
+    return if RUBY_PLATFORM !~ /linux/ # unacked not implemented on others...
     s = TCPServer.new(TEST_ADDR, 0)
     rv = Raindrops::TCP_Info.new s
     c = TCPSocket.new TEST_ADDR, s.addr[1]
@@ -29,10 +29,8 @@ def test_tcp_server
     tmp.get!(s)
     assert_equal before, tmp.object_id
 
-    ensure
-      c.close if c
-      a.close if a
-      s.close
+  ensure
+    [ c, a, s ].compact.each(&:close)
   end
 
   def test_accessors
@@ -42,12 +40,14 @@ def test_accessors
     assert tcp_info_methods.size >= 32
     tcp_info_methods.each do |m|
       next if m.to_sym == :get!
+      next if ! tmp.respond_to?(m)
       val = tmp.__send__ m
       assert_kind_of Integer, val
       assert val >= 0
     end
-    ensure
-      s.close
+    assert tmp.respond_to?(:state), 'every OS knows about TCP state, right?'
+  ensure
+    s.close
   end
 
   def test_tcp_server_delayed
@@ -65,4 +65,21 @@ def test_tcp_server_delayed
       a.close if a
       s.close
   end
-end if RUBY_PLATFORM =~ /linux/
+
+  def test_tcp_server_state_closed
+    s = TCPServer.new(TEST_ADDR, 0)
+    c = TCPSocket.new(TEST_ADDR, s.addr[1])
+    i = Raindrops::TCP_Info.allocate
+    a = s.accept
+    i.get!(a)
+    state = i.state
+    c = c.close
+    sleep(0.01) # wait for kernel to update state
+    i.get!(a)
+    assert_not_equal state, i.state
+  ensure
+    s.close if s
+    c.close if c
+    a.close if a
+  end
+end if defined? Raindrops::TCP_Info
-- 
EW


  reply index

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-03-16  3:16 [PATCH 0/2] support TCP_INFO " Eric Wong
2017-03-16  3:16 ` Eric Wong [this message]
2017-03-16  3:16 ` [PATCH 2/2] define Raindrops::TCP hash for TCP states Eric Wong
2017-03-21  2:55 ` [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on Eric Wong
2017-03-21  7:50   ` Simon Eskildsen
2017-03-21  8:19     ` Eric Wong
2017-03-21 18:48   ` Jeremy Evans
2017-03-21 19:12     ` Eric Wong

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://bogomips.org/raindrops/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170316031652.17433-2-e@80x24.org \
    --to=e@80x24.org \
    --cc=raindrops-public@bogomips.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

raindrops RubyGem user+dev discussion/patches/pulls/bugs/help

Archives are clonable:
	git clone --mirror https://bogomips.org/raindrops-public
	git clone --mirror http://ou63pmih66umazou.onion/raindrops-public

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.lang.ruby.raindrops
	nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.raindrops

 note: .onion URLs require Tor: https://www.torproject.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox