raindrops 0.16.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,46 +1,24 @@
1
1
  #include <ruby.h>
2
2
  #include <stdarg.h>
3
- #ifdef HAVE_RUBY_ST_H
4
- # include <ruby/st.h>
5
- #else
6
- # include <st.h>
7
- #endif
3
+ #include <ruby/st.h>
8
4
  #include "my_fileno.h"
9
5
  #ifdef __linux__
10
6
 
11
- /* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
12
- #ifndef RSTRING_LEN
13
- # define RSTRING_LEN(s) (RSTRING(s)->len)
14
- #endif
15
-
16
- /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
17
- #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && \
18
- !defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
19
- # include <rubysig.h>
20
- # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
21
- typedef void rb_unblock_function_t(void *);
22
- typedef VALUE rb_blocking_function_t(void *);
23
- static VALUE
24
- rb_thread_blocking_region(
25
- rb_blocking_function_t *func, void *data1,
26
- rb_unblock_function_t *ubf, void *data2)
27
- {
28
- VALUE rv;
29
-
30
- TRAP_BEG;
31
- rv = func(data1);
32
- TRAP_END;
33
-
34
- return rv;
35
- }
36
- #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
37
-
38
7
  #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
8
+ /* Ruby 1.9.3 and 2.0.0 */
39
9
  VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
10
+ # define rd_fd_region(fn,data,fd) \
11
+ rb_thread_io_blocking_region((fn),(data),(fd))
12
+ #elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \
13
+ defined(HAVE_RUBY_THREAD_H) && HAVE_RUBY_THREAD_H
14
+ /* in case Ruby 2.0+ ever drops rb_thread_io_blocking_region: */
15
+ # include <ruby/thread.h>
16
+ # define COMPAT_FN (void *(*)(void *))
17
+ # define rd_fd_region(fn,data,fd) \
18
+ rb_thread_call_without_gvl(COMPAT_FN(fn),(data),RUBY_UBF_IO,NULL)
40
19
  #else
41
- # define rb_thread_io_blocking_region(fn,data,fd) \
42
- rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,0)
43
- #endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
20
+ # error Ruby <= 1.8 not supported
21
+ #endif
44
22
 
45
23
  #include <assert.h>
46
24
  #include <errno.h>
@@ -214,6 +192,10 @@ static const char *addr_any(sa_family_t family)
214
192
  return ipv6;
215
193
  }
216
194
 
195
+ #ifdef __GNUC__
196
+ static void bug_warn_nogvl(const char *, ...)
197
+ __attribute__((format(printf,1,2)));
198
+ #endif
217
199
  static void bug_warn_nogvl(const char *fmt, ...)
218
200
  {
219
201
  va_list ap;
@@ -223,86 +205,76 @@ static void bug_warn_nogvl(const char *fmt, ...)
223
205
  va_end(ap);
224
206
 
225
207
  fprintf(stderr, "Please report how you produced this at "\
226
- "raindrops-public@bogomips.org\n");
208
+ "raindrops-public@yhbt.net\n");
227
209
  fflush(stderr);
228
210
  }
229
211
 
230
212
  static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
231
213
  {
232
- char *key, *port, *old_key;
214
+ char *host, *key, *port, *old_key;
233
215
  size_t alloca_len;
234
216
  struct listen_stats *stats;
235
- socklen_t keylen;
217
+ socklen_t hostlen;
236
218
  socklen_t portlen = (socklen_t)sizeof("65535");
237
- union any_addr sa;
238
- socklen_t len = sizeof(struct sockaddr_storage);
239
- int rc;
240
- int flags = NI_NUMERICHOST | NI_NUMERICSERV;
219
+ int n;
220
+ const void *src = r->id.idiag_src;
241
221
 
242
- switch ((sa.ss.ss_family = r->idiag_family)) {
222
+ switch (r->idiag_family) {
243
223
  case AF_INET: {
244
- sa.in.sin_port = r->id.idiag_sport;
245
- sa.in.sin_addr.s_addr = r->id.idiag_src[0];
246
- keylen = INET_ADDRSTRLEN;
247
- alloca_len = keylen + 1 + portlen;
248
- key = alloca(alloca_len);
249
- key[keylen] = 0; /* will be ':' later */
250
- port = key + keylen + 1;
251
- rc = getnameinfo(&sa.sa, len,
252
- key, keylen, port, portlen, flags);
224
+ hostlen = INET_ADDRSTRLEN;
225
+ alloca_len = hostlen + portlen;
226
+ host = key = alloca(alloca_len);
253
227
  break;
254
228
  }
255
229
  case AF_INET6: {
256
- sa.in6.sin6_port = r->id.idiag_sport;
257
- memcpy(&sa.in6.sin6_addr, &r->id.idiag_src, sizeof(__be32[4]));
258
- keylen = INET6_ADDRSTRLEN;
259
- /* [ ] */
260
- alloca_len = 1 + keylen + 1 + 1 + portlen;
230
+ hostlen = INET6_ADDRSTRLEN;
231
+ alloca_len = 1 + hostlen + 1 + portlen;
261
232
  key = alloca(alloca_len);
262
- *key = '[';
263
- key[1 + keylen + 1] = 0; /* will be ':' later */
264
- port = 1 + key + keylen + 1 + 1;
265
- rc = getnameinfo(&sa.sa, len,
266
- key + 1, keylen, port, portlen, flags);
233
+ host = key + 1;
267
234
  break;
268
235
  }
269
236
  default:
270
237
  assert(0 && "unsupported address family, could that be IPv7?!");
271
238
  }
272
- if (rc != 0) {
273
- bug_warn_nogvl("BUG: getnameinfo: %s\n", gai_strerror(rc));
274
- *key = 0;
239
+ if (!inet_ntop(r->idiag_family, src, host, hostlen)) {
240
+ bug_warn_nogvl("BUG: inet_ntop: %s\n", strerror(errno));
241
+ *key = '\0';
242
+ *host = '\0';
275
243
  }
276
-
277
- keylen = (socklen_t)strlen(key);
278
- portlen = (socklen_t)strlen(port);
279
-
280
- switch (sa.ss.ss_family) {
244
+ hostlen = (socklen_t)strlen(host);
245
+ switch (r->idiag_family) {
281
246
  case AF_INET:
282
- key[keylen] = ':';
283
- memmove(key + keylen + 1, port, portlen + 1);
247
+ host[hostlen] = ':';
248
+ port = host + hostlen + 1;
284
249
  break;
285
250
  case AF_INET6:
286
- key[keylen] = ']';
287
- key[keylen + 1] = ':';
288
- memmove(key + keylen + 2, port, portlen + 1);
289
- keylen++;
251
+ key[0] = '[';
252
+ host[hostlen] = ']';
253
+ host[hostlen + 1] = ':';
254
+ port = host + hostlen + 2;
290
255
  break;
291
256
  default:
292
257
  assert(0 && "unsupported address family, could that be IPv7?!");
293
258
  }
294
259
 
260
+ n = snprintf(port, portlen, "%u", ntohs(r->id.idiag_sport));
261
+ if (n <= 0) {
262
+ bug_warn_nogvl("BUG: snprintf port: %d\n", n);
263
+ *key = '\0';
264
+ }
265
+
295
266
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
296
267
  return stats;
297
268
 
298
269
  old_key = key;
299
270
 
300
271
  if (r->idiag_state == TCP_ESTABLISHED) {
301
- int n = snprintf(key, alloca_len, "%s:%u",
302
- addr_any(sa.ss.ss_family),
272
+ n = snprintf(key, alloca_len, "%s:%u",
273
+ addr_any(r->idiag_family),
303
274
  ntohs(r->id.idiag_sport));
304
275
  if (n <= 0) {
305
276
  bug_warn_nogvl("BUG: snprintf: %d\n", n);
277
+ *key = '\0';
306
278
  }
307
279
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
308
280
  return stats;
@@ -315,8 +287,9 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
315
287
  memcpy(key, old_key, n + 1);
316
288
  }
317
289
  } else {
318
- key = xmalloc(keylen + 1 + portlen + 1);
319
- memcpy(key, old_key, keylen + 1 + portlen + 1);
290
+ size_t old_len = strlen(old_key) + 1;
291
+ key = xmalloc(old_len);
292
+ memcpy(key, old_key, old_len);
320
293
  }
321
294
  stats = xcalloc(1, sizeof(struct listen_stats));
322
295
  st_insert(table, (st_data_t)key, (st_data_t)stats);
@@ -613,7 +586,7 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
613
586
  gen_bytecode(&args->iov[2], &query_addr);
614
587
 
615
588
  memset(&args->stats, 0, sizeof(struct listen_stats));
616
- nl_errcheck(rb_thread_io_blocking_region(diag, args, args->fd));
589
+ nl_errcheck(rd_fd_region(diag, args, args->fd));
617
590
 
618
591
  return rb_listen_stats(&args->stats);
619
592
  }
@@ -690,7 +663,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
690
663
  "addr must be an array of strings, a string, or nil");
691
664
  }
692
665
 
693
- nl_errcheck(rb_thread_io_blocking_region(diag, &args, args.fd));
666
+ nl_errcheck(rd_fd_region(diag, &args, args.fd));
694
667
 
695
668
  st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
696
669
  st_free_table(args.table);
@@ -705,11 +678,12 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
705
678
 
706
679
  void Init_raindrops_linux_inet_diag(void)
707
680
  {
708
- VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
681
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
709
682
  VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
683
+ VALUE Socket;
710
684
 
711
685
  rb_require("socket");
712
- cIDSock = rb_const_get(rb_cObject, rb_intern("Socket"));
686
+ Socket = rb_const_get(rb_cObject, rb_intern("Socket"));
713
687
  id_new = rb_intern("new");
714
688
 
715
689
  /*
@@ -718,10 +692,11 @@ void Init_raindrops_linux_inet_diag(void)
718
692
  * This is a subclass of +Socket+ specifically for talking
719
693
  * to the inet_diag facility of Netlink.
720
694
  */
721
- cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", cIDSock);
695
+ cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", Socket);
722
696
  rb_define_singleton_method(cIDSock, "new", ids_s_new, 0);
723
697
 
724
698
  cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));
699
+ rb_gc_register_mark_object(cListenStats); /* pin */
725
700
 
726
701
  rb_define_module_function(mLinux, "tcp_listener_stats",
727
702
  tcp_listener_stats, -1);
@@ -4,6 +4,7 @@
4
4
  #include <assert.h>
5
5
  #include <errno.h>
6
6
  #include <stddef.h>
7
+ #include <string.h>
7
8
  #include "raindrops_atomic.h"
8
9
 
9
10
  #ifndef SIZET2NUM
@@ -34,11 +35,19 @@ struct raindrops {
34
35
  size_t size;
35
36
  size_t capa;
36
37
  pid_t pid;
38
+ VALUE io;
37
39
  struct raindrop *drops;
38
40
  };
39
41
 
40
42
  /* called by GC */
41
- static void gcfree(void *ptr)
43
+ static void rd_mark(void *ptr)
44
+ {
45
+ struct raindrops *r = ptr;
46
+ rb_gc_mark(r->io);
47
+ }
48
+
49
+ /* called by GC */
50
+ static void rd_free(void *ptr)
42
51
  {
43
52
  struct raindrops *r = ptr;
44
53
 
@@ -51,11 +60,24 @@ static void gcfree(void *ptr)
51
60
  xfree(ptr);
52
61
  }
53
62
 
63
+ static size_t rd_memsize(const void *ptr)
64
+ {
65
+ const struct raindrops *r = ptr;
66
+
67
+ return r->drops == MAP_FAILED ? 0 : raindrop_size * r->capa;
68
+ }
69
+
70
+ static const rb_data_type_t rd_type = {
71
+ "raindrops",
72
+ { rd_mark, rd_free, rd_memsize, /* reserved */ },
73
+ /* parent, data, [ flags ] */
74
+ };
75
+
54
76
  /* automatically called at creation (before initialize) */
55
77
  static VALUE alloc(VALUE klass)
56
78
  {
57
79
  struct raindrops *r;
58
- VALUE rv = Data_Make_Struct(klass, struct raindrops, NULL, gcfree, r);
80
+ VALUE rv = TypedData_Make_Struct(klass, struct raindrops, &rd_type, r);
59
81
 
60
82
  r->drops = MAP_FAILED;
61
83
  return rv;
@@ -65,7 +87,7 @@ static struct raindrops *get(VALUE self)
65
87
  {
66
88
  struct raindrops *r;
67
89
 
68
- Data_Get_Struct(self, struct raindrops, r);
90
+ TypedData_Get_Struct(self, struct raindrops, &rd_type, r);
69
91
 
70
92
  if (r->drops == MAP_FAILED)
71
93
  rb_raise(rb_eStandardError, "invalid or freed Raindrops");
@@ -74,16 +96,10 @@ static struct raindrops *get(VALUE self)
74
96
  }
75
97
 
76
98
  /*
77
- * call-seq:
78
- * Raindrops.new(size) -> raindrops object
79
- *
80
- * Initializes a Raindrops object to hold +size+ counters. +size+ is
81
- * only a hint and the actual number of counters the object has is
82
- * dependent on the CPU model, number of cores, and page size of
83
- * the machine. The actual size of the object will always be equal
84
- * or greater than the specified +size+.
99
+ * This is the _actual_ implementation of #initialize - the Ruby wrapper
100
+ * handles keyword-argument handling then calls this method.
85
101
  */
86
- static VALUE init(VALUE self, VALUE size)
102
+ static VALUE init_cimpl(VALUE self, VALUE size, VALUE io, VALUE zero)
87
103
  {
88
104
  struct raindrops *r = DATA_PTR(self);
89
105
  int tries = 1;
@@ -100,11 +116,23 @@ static VALUE init(VALUE self, VALUE size)
100
116
  r->capa = tmp / raindrop_size;
101
117
  assert(PAGE_ALIGN(raindrop_size * r->capa) == tmp && "not aligned");
102
118
 
119
+ r->io = io;
120
+
103
121
  retry:
104
- r->drops = mmap(NULL, tmp,
105
- PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
122
+ if (RTEST(r->io)) {
123
+ int fd = NUM2INT(rb_funcall(r->io, rb_intern("fileno"), 0));
124
+ rb_funcall(r->io, rb_intern("truncate"), 1, SIZET2NUM(tmp));
125
+ r->drops = mmap(NULL, tmp,
126
+ PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
127
+ } else {
128
+ r->drops = mmap(NULL, tmp,
129
+ PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED,
130
+ -1, 0);
131
+ }
106
132
  if (r->drops == MAP_FAILED) {
107
- if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
133
+ int err = errno;
134
+
135
+ if ((err == EAGAIN || err == ENOMEM) && tries-- > 0) {
108
136
  rb_gc();
109
137
  goto retry;
110
138
  }
@@ -112,6 +140,9 @@ retry:
112
140
  }
113
141
  r->pid = getpid();
114
142
 
143
+ if (RTEST(zero))
144
+ memset(r->drops, 0, tmp);
145
+
115
146
  return self;
116
147
  }
117
148
 
@@ -140,7 +171,9 @@ static void resize(struct raindrops *r, size_t new_rd_size)
140
171
 
141
172
  rv = mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
142
173
  if (rv == MAP_FAILED) {
143
- if (errno == EAGAIN || errno == ENOMEM) {
174
+ int err = errno;
175
+
176
+ if (err == EAGAIN || err == ENOMEM) {
144
177
  rb_gc();
145
178
  rv = mremap(old_address, old_size, new_size, 0);
146
179
  }
@@ -200,14 +233,16 @@ static VALUE capa(VALUE self)
200
233
  * call-seq:
201
234
  * rd.dup -> rd_copy
202
235
  *
203
- * Duplicates and snapshots the current state of a Raindrops object.
236
+ * Duplicates and snapshots the current state of a Raindrops object. Even
237
+ * if the given Raindrops object is backed by a file, the copy will be backed
238
+ * by independent, anonymously mapped memory.
204
239
  */
205
240
  static VALUE init_copy(VALUE dest, VALUE source)
206
241
  {
207
242
  struct raindrops *dst = DATA_PTR(dest);
208
243
  struct raindrops *src = get(source);
209
244
 
210
- init(dest, SIZET2NUM(src->size));
245
+ init_cimpl(dest, SIZET2NUM(src->size), Qnil, Qfalse);
211
246
  memcpy(dst->drops, src->drops, raindrop_size * src->size);
212
247
 
213
248
  return dest;
@@ -323,7 +358,9 @@ static VALUE aref(VALUE self, VALUE index)
323
358
 
324
359
  #ifdef __linux__
325
360
  void Init_raindrops_linux_inet_diag(void);
326
- void Init_raindrops_linux_tcp_info(void);
361
+ #endif
362
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
363
+ void Init_raindrops_tcp_info(void);
327
364
  #endif
328
365
 
329
366
  #ifndef _SC_NPROCESSORS_CONF
@@ -356,6 +393,20 @@ static VALUE evaporate_bang(VALUE self)
356
393
  return Qnil;
357
394
  }
358
395
 
396
+ /*
397
+ * call-seq:
398
+ * to_io -> IO
399
+ *
400
+ * Returns the IO object backing the memory for this raindrop, if
401
+ * one was specified when constructing this Raindrop. If this
402
+ * Raindrop is backed by anonymous memory, this method returns nil.
403
+ */
404
+ static VALUE to_io(VALUE self)
405
+ {
406
+ struct raindrops *r = get(self);
407
+ return r->io;
408
+ }
409
+
359
410
  void Init_raindrops_ext(void)
360
411
  {
361
412
  VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
@@ -414,7 +465,7 @@ void Init_raindrops_ext(void)
414
465
 
415
466
  rb_define_alloc_func(cRaindrops, alloc);
416
467
 
417
- rb_define_method(cRaindrops, "initialize", init, 1);
468
+ rb_define_private_method(cRaindrops, "initialize_cimpl", init_cimpl, 3);
418
469
  rb_define_method(cRaindrops, "incr", incr, -1);
419
470
  rb_define_method(cRaindrops, "decr", decr, -1);
420
471
  rb_define_method(cRaindrops, "to_ary", to_ary, 0);
@@ -425,9 +476,12 @@ void Init_raindrops_ext(void)
425
476
  rb_define_method(cRaindrops, "capa", capa, 0);
426
477
  rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
427
478
  rb_define_method(cRaindrops, "evaporate!", evaporate_bang, 0);
479
+ rb_define_method(cRaindrops, "to_io", to_io, 0);
428
480
 
429
481
  #ifdef __linux__
430
482
  Init_raindrops_linux_inet_diag();
431
- Init_raindrops_linux_tcp_info();
483
+ #endif
484
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
485
+ Init_raindrops_tcp_info();
432
486
  #endif
433
487
  }
@@ -0,0 +1,245 @@
1
+ #include <ruby.h>
2
+ #include <sys/socket.h>
3
+ #include <netinet/in.h>
4
+ #if defined(HAVE_LINUX_TCP_H)
5
+ # include <linux/tcp.h>
6
+ #else
7
+ # if defined(HAVE_NETINET_TCP_H)
8
+ # include <netinet/tcp.h>
9
+ # endif
10
+ # if defined(HAVE_NETINET_TCP_FSM_H)
11
+ # include <netinet/tcp_fsm.h>
12
+ # endif
13
+ #endif
14
+
15
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
16
+ #include "my_fileno.h"
17
+
18
+ CFUNC_tcp_info_tcpi_state
19
+ CFUNC_tcp_info_tcpi_ca_state
20
+ CFUNC_tcp_info_tcpi_retransmits
21
+ CFUNC_tcp_info_tcpi_probes
22
+ CFUNC_tcp_info_tcpi_backoff
23
+ CFUNC_tcp_info_tcpi_options
24
+ CFUNC_tcp_info_tcpi_snd_wscale
25
+ CFUNC_tcp_info_tcpi_rcv_wscale
26
+ CFUNC_tcp_info_tcpi_rto
27
+ CFUNC_tcp_info_tcpi_ato
28
+ CFUNC_tcp_info_tcpi_snd_mss
29
+ CFUNC_tcp_info_tcpi_rcv_mss
30
+ CFUNC_tcp_info_tcpi_unacked
31
+ CFUNC_tcp_info_tcpi_sacked
32
+ CFUNC_tcp_info_tcpi_lost
33
+ CFUNC_tcp_info_tcpi_retrans
34
+ CFUNC_tcp_info_tcpi_fackets
35
+ CFUNC_tcp_info_tcpi_last_data_sent
36
+ CFUNC_tcp_info_tcpi_last_ack_sent
37
+ CFUNC_tcp_info_tcpi_last_data_recv
38
+ CFUNC_tcp_info_tcpi_last_ack_recv
39
+ CFUNC_tcp_info_tcpi_pmtu
40
+ CFUNC_tcp_info_tcpi_rcv_ssthresh
41
+ CFUNC_tcp_info_tcpi_rtt
42
+ CFUNC_tcp_info_tcpi_rttvar
43
+ CFUNC_tcp_info_tcpi_snd_ssthresh
44
+ CFUNC_tcp_info_tcpi_snd_cwnd
45
+ CFUNC_tcp_info_tcpi_advmss
46
+ CFUNC_tcp_info_tcpi_reordering
47
+ CFUNC_tcp_info_tcpi_rcv_rtt
48
+ CFUNC_tcp_info_tcpi_rcv_space
49
+ CFUNC_tcp_info_tcpi_total_retrans
50
+
51
+ static size_t tcpi_memsize(const void *ptr)
52
+ {
53
+ return sizeof(struct tcp_info);
54
+ }
55
+
56
+ static const rb_data_type_t tcpi_type = {
57
+ "tcp_info",
58
+ { NULL, RUBY_TYPED_DEFAULT_FREE, tcpi_memsize, /* reserved */ },
59
+ /* parent, data, [ flags ] */
60
+ };
61
+
62
+ static VALUE alloc(VALUE klass)
63
+ {
64
+ struct tcp_info *info;
65
+
66
+ return TypedData_Make_Struct(klass, struct tcp_info, &tcpi_type, info);
67
+ }
68
+
69
+ /*
70
+ * call-seq:
71
+ *
72
+ * Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object
73
+ *
74
+ * Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7)
75
+ * manpage and /usr/include/linux/tcp.h for more details.
76
+ */
77
+ static VALUE init(VALUE self, VALUE io)
78
+ {
79
+ int fd = my_fileno(io);
80
+ struct tcp_info *info = DATA_PTR(self);
81
+ socklen_t len = (socklen_t)sizeof(struct tcp_info);
82
+ int rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, info, &len);
83
+
84
+ if (rc != 0)
85
+ rb_sys_fail("getsockopt");
86
+
87
+ return self;
88
+ }
89
+
90
+ void Init_raindrops_tcp_info(void)
91
+ {
92
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
93
+ VALUE cTCP_Info;
94
+
95
+ /*
96
+ * Document-class: Raindrops::TCP_Info
97
+ *
98
+ * This is used to wrap "struct tcp_info" as described in tcp(7)
99
+ * and /usr/include/linux/tcp.h. The following readers methods
100
+ * are defined corresponding to the "tcpi_" fields in the
101
+ * tcp_info struct.
102
+ *
103
+ * As of raindrops 0.18.0+, this is supported on FreeBSD and OpenBSD
104
+ * systems as well as Linux, although not all fields exist or
105
+ * match the documentation, below.
106
+ *
107
+ * In particular, the +last_data_recv+ field is useful for measuring
108
+ * the amount of time a client spent in the listen queue before
109
+ * +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the
110
+ * listen socket (it is on by default in Unicorn).
111
+ *
112
+ * - state
113
+ * - ca_state
114
+ * - retransmits
115
+ * - probes
116
+ * - backoff
117
+ * - options
118
+ * - snd_wscale
119
+ * - rcv_wscale
120
+ * - rto
121
+ * - ato
122
+ * - snd_mss
123
+ * - rcv_mss
124
+ * - unacked
125
+ * - sacked
126
+ * - lost
127
+ * - retrans
128
+ * - fackets
129
+ * - last_data_sent
130
+ * - last_ack_sent
131
+ * - last_data_recv
132
+ * - last_ack_recv
133
+ * - pmtu
134
+ * - rcv_ssthresh
135
+ * - rtt
136
+ * - rttvar
137
+ * - snd_ssthresh
138
+ * - snd_cwnd
139
+ * - advmss
140
+ * - reordering
141
+ * - rcv_rtt
142
+ * - rcv_space
143
+ * - total_retrans
144
+ *
145
+ * https://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html
146
+ */
147
+ cTCP_Info = rb_define_class_under(cRaindrops, "TCP_Info", rb_cObject);
148
+ rb_define_alloc_func(cTCP_Info, alloc);
149
+ rb_define_private_method(cTCP_Info, "initialize", init, 1);
150
+
151
+ /*
152
+ * Document-method: Raindrops::TCP_Info#get!
153
+ *
154
+ * call-seq:
155
+ *
156
+ * info = Raindrops::TCP_Info.new(tcp_socket)
157
+ * info.get!(tcp_socket)
158
+ *
159
+ * Update an existing TCP_Info objects with the latest stats
160
+ * from the given socket. This even allows sharing TCP_Info
161
+ * objects between different sockets to avoid garbage.
162
+ */
163
+ rb_define_method(cTCP_Info, "get!", init, 1);
164
+
165
+ DEFINE_METHOD_tcp_info_tcpi_state;
166
+ DEFINE_METHOD_tcp_info_tcpi_ca_state;
167
+ DEFINE_METHOD_tcp_info_tcpi_retransmits;
168
+ DEFINE_METHOD_tcp_info_tcpi_probes;
169
+ DEFINE_METHOD_tcp_info_tcpi_backoff;
170
+ DEFINE_METHOD_tcp_info_tcpi_options;
171
+ DEFINE_METHOD_tcp_info_tcpi_snd_wscale;
172
+ DEFINE_METHOD_tcp_info_tcpi_rcv_wscale;
173
+ DEFINE_METHOD_tcp_info_tcpi_rto;
174
+ DEFINE_METHOD_tcp_info_tcpi_ato;
175
+ DEFINE_METHOD_tcp_info_tcpi_snd_mss;
176
+ DEFINE_METHOD_tcp_info_tcpi_rcv_mss;
177
+ DEFINE_METHOD_tcp_info_tcpi_unacked;
178
+ DEFINE_METHOD_tcp_info_tcpi_sacked;
179
+ DEFINE_METHOD_tcp_info_tcpi_lost;
180
+ DEFINE_METHOD_tcp_info_tcpi_retrans;
181
+ DEFINE_METHOD_tcp_info_tcpi_fackets;
182
+ DEFINE_METHOD_tcp_info_tcpi_last_data_sent;
183
+ DEFINE_METHOD_tcp_info_tcpi_last_ack_sent;
184
+ DEFINE_METHOD_tcp_info_tcpi_last_data_recv;
185
+ DEFINE_METHOD_tcp_info_tcpi_last_ack_recv;
186
+ DEFINE_METHOD_tcp_info_tcpi_pmtu;
187
+ DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh;
188
+ DEFINE_METHOD_tcp_info_tcpi_rtt;
189
+ DEFINE_METHOD_tcp_info_tcpi_rttvar;
190
+ DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh;
191
+ DEFINE_METHOD_tcp_info_tcpi_snd_cwnd;
192
+ DEFINE_METHOD_tcp_info_tcpi_advmss;
193
+ DEFINE_METHOD_tcp_info_tcpi_reordering;
194
+ DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
195
+ DEFINE_METHOD_tcp_info_tcpi_rcv_space;
196
+ DEFINE_METHOD_tcp_info_tcpi_total_retrans;
197
+
198
+ #ifdef RAINDROPS_TCP_STATES_ALL_KNOWN
199
+
200
+ /*
201
+ * Document-const: Raindrops::TCP
202
+ *
203
+ * This is a frozen hash storing the numeric values
204
+ * maps platform-independent symbol keys to
205
+ * platform-dependent numeric values. These states
206
+ * are all valid values for the Raindrops::TCP_Info#state field.
207
+ *
208
+ * The platform-independent names of the keys in this hash are:
209
+ *
210
+ * - :ESTABLISHED
211
+ * - :SYN_SENT
212
+ * - :SYN_RECV
213
+ * - :FIN_WAIT1
214
+ * - :FIN_WAIT2
215
+ * - :TIME_WAIT
216
+ * - :CLOSE
217
+ * - :CLOSE_WAIT
218
+ * - :LAST_ACK
219
+ * - :LISTEN
220
+ * - :CLOSING
221
+ *
222
+ * This is only supported on platforms where TCP_Info is supported,
223
+ * currently FreeBSD, OpenBSD, and Linux-based systems.
224
+ */
225
+ {
226
+ #define TCPSET(n,v) rb_hash_aset(tcp, ID2SYM(rb_intern(#n)), INT2NUM(v))
227
+ VALUE tcp = rb_hash_new();
228
+ TCPSET(ESTABLISHED, RAINDROPS_TCP_ESTABLISHED);
229
+ TCPSET(SYN_SENT, RAINDROPS_TCP_SYN_SENT);
230
+ TCPSET(SYN_RECV, RAINDROPS_TCP_SYN_RECV);
231
+ TCPSET(FIN_WAIT1, RAINDROPS_TCP_FIN_WAIT1);
232
+ TCPSET(FIN_WAIT2, RAINDROPS_TCP_FIN_WAIT2);
233
+ TCPSET(TIME_WAIT, RAINDROPS_TCP_TIME_WAIT);
234
+ TCPSET(CLOSE, RAINDROPS_TCP_CLOSE);
235
+ TCPSET(CLOSE_WAIT, RAINDROPS_TCP_CLOSE_WAIT);
236
+ TCPSET(LAST_ACK, RAINDROPS_TCP_LAST_ACK);
237
+ TCPSET(LISTEN, RAINDROPS_TCP_LISTEN);
238
+ TCPSET(CLOSING, RAINDROPS_TCP_CLOSING);
239
+ #undef TCPSET
240
+ OBJ_FREEZE(tcp);
241
+ rb_define_const(cRaindrops, "TCP", tcp);
242
+ }
243
+ #endif
244
+ }
245
+ #endif /* HAVE_STRUCT_TCP_INFO */