diff options
author | Eric Wong <e@80x24.org> | 2017-03-18 01:32:19 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2017-03-18 01:32:19 +0000 |
commit | 0b8ecbc71cd6aba009e5e3a99a8e0e8f140e2e5f (patch) | |
tree | 0ff0289ddd6f6de1e0fa22f8cfcc39087f5fcf72 /ext/raindrops | |
parent | 33a2540fb12cec9052f9b92810f2a9aa5b395911 (diff) | |
parent | bf2fb0a16091201a9b2798ebdea54e03c1c3e61b (diff) | |
download | raindrops-0b8ecbc71cd6aba009e5e3a99a8e0e8f140e2e5f.tar.gz |
* origin/freebsd: define Raindrops::TCP hash for TCP states tcp_info: support this struct under FreeBSD
Diffstat (limited to 'ext/raindrops')
-rw-r--r-- | ext/raindrops/extconf.rb | 105 | ||||
-rw-r--r-- | ext/raindrops/linux_tcp_info.c | 196 | ||||
-rw-r--r-- | ext/raindrops/raindrops.c | 8 | ||||
-rw-r--r-- | ext/raindrops/tcp_info.c | 216 |
4 files changed, 326 insertions, 199 deletions
diff --git a/ext/raindrops/extconf.rb b/ext/raindrops/extconf.rb index 79d212c..86c7d78 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,110 @@ have_func('munmap', 'sys/mman.h') or abort 'munmap() not found' $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 + tcp_state_map = { + ESTABLISHED: %w(TCP_ESTABLISHED TCPS_ESTABLISHED), + SYN_SENT: %w(TCP_SYN_SENT TCPS_SYN_SENT), + SYN_RECV: %w(TCP_SYN_RECV TCPS_SYN_RECEIVED), + FIN_WAIT1: %w(TCP_FIN_WAIT1 TCPS_FIN_WAIT_1), + FIN_WAIT2: %w(TCP_FIN_WAIT2 TCPS_FIN_WAIT_2), + TIME_WAIT: %w(TCP_TIME_WAIT TCPS_TIME_WAIT), + CLOSE: %w(TCP_CLOSE TCPS_CLOSED), + CLOSE_WAIT: %w(TCP_CLOSE_WAIT TCPS_CLOSE_WAIT), + LAST_ACK: %w(TCP_LAST_ACK TCPS_LAST_ACK), + LISTEN: %w(TCP_LISTEN TCPS_LISTEN), + CLOSING: %w(TCP_CLOSING TCPS_CLOSING), + } + nstate = 0 + tcp_state_map.each do |state, try| + try.each do |os_name| + have_const(os_name, headers) or next + tcp_state_map[state] = os_name + nstate += 1 + end + end + if nstate == tcp_state_map.size + $defs << '-DRAINDROPS_TCP_STATES_ALL_KNOWN=1' + tcp_state_map.each do |state, name| + $defs << "-DRAINDROPS_TCP_#{state}=#{name}" + end + end +end + have_func("getpagesize", "unistd.h") have_func('rb_thread_blocking_region') have_func('rb_thread_io_blocking_region') @@ -53,4 +155,5 @@ Users of Debian-based distros may run: 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/linux_tcp_info.c b/ext/raindrops/linux_tcp_info.c deleted file mode 100644 index 83001a5..0000000 --- a/ext/raindrops/linux_tcp_info.c +++ /dev/null @@ -1,196 +0,0 @@ -#if defined(__linux__) && defined(HAVE_LINUX_TCP_H) -#include <ruby.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <linux/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 size_t tcpi_memsize(const void *ptr) -{ - return sizeof(struct tcp_info); -} - -static const rb_data_type_t tcpi_type = { - "tcp_info", - { NULL, RUBY_TYPED_DEFAULT_FREE, tcpi_memsize, /* reserved */ }, - /* parent, data, [ flags ] */ -}; - -static VALUE alloc(VALUE klass) -{ - struct tcp_info *info; - - return TypedData_Make_Struct(klass, struct tcp_info, &tcpi_type, info); -} - -/* - * call-seq: - * - * Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object - * - * Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7) - * manpage and /usr/include/linux/tcp.h for more details. - */ -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_define_class("Raindrops", rb_cObject); - VALUE cTCP_Info; - - /* - * Document-class: Raindrops::TCP_Info - * - * This is used to wrap "struct tcp_info" as described in tcp(7) - * and /usr/include/linux/tcp.h. The following readers methods - * are defined corresponding to the "tcpi_" fields in the - * tcp_info struct. - * - * In particular, the +last_data_recv+ field is useful for measuring - * the amount of time a client spent in the listen queue before - * +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the - * listen socket (it is on by default in Unicorn). - * - * - state - * - ca_state - * - retransmits - * - probes - * - backoff - * - options - * - snd_wscale - * - rcv_wscale - * - rto - * - ato - * - snd_mss - * - rcv_mss - * - unacked - * - sacked - * - lost - * - retrans - * - fackets - * - last_data_sent - * - last_ack_sent - * - last_data_recv - * - last_ack_recv - * - pmtu - * - rcv_ssthresh - * - rtt - * - rttvar - * - snd_ssthresh - * - snd_cwnd - * - advmss - * - reordering - * - rcv_rtt - * - rcv_space - * - total_retrans - * - * https://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html - */ - 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); - - /* - * Document-method: Raindrops::TCP_Info#get! - * - * call-seq: - * - * info = Raindrops::TCP_Info.new(tcp_socket) - * info.get!(tcp_socket) - * - * Update an existing TCP_Info objects with the latest stats - * from the given socket. This even allows sharing TCP_Info - * objects between different sockets to avoid garbage. - */ - 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); -} -#endif /* TCP_INFO */ -#endif /* defined(__linux__) && defined(HAVE_LINUX_TCP_H) */ diff --git a/ext/raindrops/raindrops.c b/ext/raindrops/raindrops.c index 9090839..837084c 100644 --- a/ext/raindrops/raindrops.c +++ b/ext/raindrops/raindrops.c @@ -340,7 +340,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 @@ -445,6 +447,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/tcp_info.c b/ext/raindrops/tcp_info.c new file mode 100644 index 0000000..3e241a1 --- /dev/null +++ b/ext/raindrops/tcp_info.c @@ -0,0 +1,216 @@ +#include <ruby.h> +#include "extconf.h" +#include <sys/socket.h> +#include <netinet/in.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 + +#ifdef HAVE_TYPE_STRUCT_TCP_INFO +#include "my_fileno.h" + +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) +{ + return sizeof(struct tcp_info); +} + +static const rb_data_type_t tcpi_type = { + "tcp_info", + { NULL, RUBY_TYPED_DEFAULT_FREE, tcpi_memsize, /* reserved */ }, + /* parent, data, [ flags ] */ +}; + +static VALUE alloc(VALUE klass) +{ + struct tcp_info *info; + + return TypedData_Make_Struct(klass, struct tcp_info, &tcpi_type, info); +} + +/* + * call-seq: + * + * Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object + * + * Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7) + * manpage and /usr/include/linux/tcp.h for more details. + */ +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_tcp_info(void) +{ + VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject); + VALUE cTCP_Info; + + /* + * Document-class: Raindrops::TCP_Info + * + * This is used to wrap "struct tcp_info" as described in tcp(7) + * and /usr/include/linux/tcp.h. The following readers methods + * are defined corresponding to the "tcpi_" fields in the + * tcp_info struct. + * + * In particular, the +last_data_recv+ field is useful for measuring + * the amount of time a client spent in the listen queue before + * +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the + * listen socket (it is on by default in Unicorn). + * + * - state + * - ca_state + * - retransmits + * - probes + * - backoff + * - options + * - snd_wscale + * - rcv_wscale + * - rto + * - ato + * - snd_mss + * - rcv_mss + * - unacked + * - sacked + * - lost + * - retrans + * - fackets + * - last_data_sent + * - last_ack_sent + * - last_data_recv + * - last_ack_recv + * - pmtu + * - rcv_ssthresh + * - rtt + * - rttvar + * - snd_ssthresh + * - snd_cwnd + * - advmss + * - reordering + * - rcv_rtt + * - rcv_space + * - total_retrans + * + * https://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html + */ + 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); + + /* + * Document-method: Raindrops::TCP_Info#get! + * + * call-seq: + * + * info = Raindrops::TCP_Info.new(tcp_socket) + * info.get!(tcp_socket) + * + * Update an existing TCP_Info objects with the latest stats + * from the given socket. This even allows sharing TCP_Info + * objects between different sockets to avoid garbage. + */ + rb_define_method(cTCP_Info, "get!", init, 1); + + 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; + +#ifdef RAINDROPS_TCP_STATES_ALL_KNOWN + { +#define TCPSET(n,v) rb_hash_aset(tcp, ID2SYM(rb_intern(#n)), INT2NUM(v)) + VALUE tcp = rb_hash_new(); + TCPSET(ESTABLISHED, RAINDROPS_TCP_ESTABLISHED); + TCPSET(SYN_SENT, RAINDROPS_TCP_SYN_SENT); + TCPSET(SYN_RECV, RAINDROPS_TCP_SYN_RECV); + TCPSET(FIN_WAIT1, RAINDROPS_TCP_FIN_WAIT1); + TCPSET(FIN_WAIT2, RAINDROPS_TCP_FIN_WAIT2); + TCPSET(TIME_WAIT, RAINDROPS_TCP_TIME_WAIT); + TCPSET(CLOSE, RAINDROPS_TCP_CLOSE); + TCPSET(CLOSE_WAIT, RAINDROPS_TCP_CLOSE_WAIT); + TCPSET(LAST_ACK, RAINDROPS_TCP_LAST_ACK); + TCPSET(LISTEN, RAINDROPS_TCP_LISTEN); + TCPSET(CLOSING, RAINDROPS_TCP_CLOSING); +#undef TCPSET + OBJ_FREEZE(tcp); + rb_define_const(cRaindrops, "TCP", tcp); + } +#endif +} +#endif /* HAVE_STRUCT_TCP_INFO */ |