raindrops 0.13.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +1 -2
- data/.gitattributes +4 -0
- data/.gitignore +1 -1
- data/.olddoc.yml +13 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -2
- data/LICENSE +3 -3
- data/README +28 -34
- data/TODO +2 -0
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/examples/linux-listener-stats.rb +1 -2
- data/examples/watcher_demo.ru +1 -1
- data/examples/yahns.conf.rb +30 -0
- data/examples/zbatery.conf.rb +4 -1
- data/ext/raindrops/extconf.rb +107 -2
- data/ext/raindrops/linux_inet_diag.c +94 -101
- data/ext/raindrops/raindrops.c +28 -7
- data/ext/raindrops/tcp_info.c +245 -0
- data/lib/raindrops.rb +1 -1
- data/lib/raindrops/aggregate.rb +1 -1
- data/lib/raindrops/aggregate/last_data_recv.rb +1 -5
- data/lib/raindrops/aggregate/pmq.rb +23 -17
- data/lib/raindrops/linux.rb +5 -6
- data/lib/raindrops/middleware.rb +4 -6
- data/lib/raindrops/middleware/proxy.rb +2 -2
- data/lib/raindrops/watcher.rb +13 -13
- data/pkg.mk +26 -50
- data/raindrops.gemspec +14 -21
- data/test/ipv6_enabled.rb +4 -4
- data/test/test_aggregate_pmq.rb +1 -1
- data/test/test_inet_diag_socket.rb +1 -1
- data/test/test_last_data_recv_unicorn.rb +1 -1
- data/test/test_linux.rb +10 -2
- data/test/test_linux_all_tcp_listen_stats_leak.rb +2 -2
- data/test/test_linux_ipv6.rb +8 -0
- data/test/test_raindrops.rb +1 -1
- data/test/{test_linux_tcp_info.rb → test_tcp_info.rb} +34 -14
- data/test/test_watcher.rb +15 -10
- metadata +59 -171
- data/.wrongdoc.yml +0 -6
- data/Rakefile +0 -28
- data/ext/raindrops/linux_tcp_info.c +0 -173
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1e1444bc6e35e67993745b3627e848537490fd3b02a67e12be9721b031e22e70
|
4
|
+
data.tar.gz: 9ed2dff40418e89a71ecf18cfb74b72d28d9570ceef314ee1c81bdff19ed832a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ccb15ce6a96c1619d53d6090851ef8152ebd673b2db8aed9c6d4bb42757de2d9d051888d3f105d6d3461683eb33575d343e7d31a1ddb91a8f9914bb4e6abcb81
|
7
|
+
data.tar.gz: ce265e39bf3217f3b62931ead0fff78e7e118785466bc0eecff1858f9d808982c12b14f2645ecfffd96c8cb8e0a1a72ac03c0d6ad220c84950dfd2fe05d7f258
|
data/.document
CHANGED
data/.gitattributes
ADDED
data/.gitignore
CHANGED
data/.olddoc.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
cgit_url: https://yhbt.net/raindrops.git/
|
3
|
+
rdoc_url: https://yhbt.net/raindrops/
|
4
|
+
public_email: raindrops-public@yhbt.net
|
5
|
+
ml_url:
|
6
|
+
- https://yhbt.net/raindrops-public/
|
7
|
+
- http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/raindrops-public
|
8
|
+
nntp_url:
|
9
|
+
- nntp://news.public-inbox.org/inbox.comp.lang.ruby.raindrops
|
10
|
+
- nntp://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/inbox.comp.lang.ruby.raindrops
|
11
|
+
source_code:
|
12
|
+
- git clone https://yhbt.net/raindrops.git
|
13
|
+
- torsocks git clone http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/raindrops.git
|
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
data/LICENSE
CHANGED
@@ -3,8 +3,8 @@ revision control for names and email addresses of all of them.
|
|
3
3
|
|
4
4
|
You can redistribute it and/or modify it under the terms of the GNU
|
5
5
|
Lesser General Public License (LGPL) as published by the Free Software
|
6
|
-
Foundation, version {2.1}[
|
7
|
-
later. Currently version {3}[
|
6
|
+
Foundation, version {2.1}[https://www.gnu.org/licenses/lgpl-2.1.txt] or
|
7
|
+
later. Currently version {3}[https://www.gnu.org/licenses/lgpl-3.0.txt],
|
8
8
|
is preferred (see link:COPYING).
|
9
9
|
|
10
10
|
raindrops is distributed in the hope that it will be useful, but WITHOUT
|
@@ -13,4 +13,4 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
13
13
|
License for more details.
|
14
14
|
|
15
15
|
You should have received a copy of the GNU Lesser General Public License
|
16
|
-
along with the raindrops; if not, see <
|
16
|
+
along with the raindrops; if not, see <https://www.gnu.org/licenses/>
|
data/README
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
= raindrops - real-time stats for preforking Rack servers
|
2
2
|
|
3
|
-
|
4
|
-
servers. It is designed for preforking servers such as
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
multiple processes.
|
3
|
+
raindrops is a real-time stats toolkit to show statistics for Rack HTTP
|
4
|
+
servers. It is designed for preforking servers such as unicorn, but
|
5
|
+
should support any Rack HTTP server on platforms supporting POSIX shared
|
6
|
+
memory. It may also be used as a generic scoreboard for sharing atomic
|
7
|
+
counters across multiple processes.
|
9
8
|
|
10
9
|
== Features
|
11
10
|
|
@@ -38,12 +37,15 @@ and "tcp_diag" kernel modules are loaded as they do not autoload correctly
|
|
38
37
|
|
39
38
|
== Install
|
40
39
|
|
41
|
-
We recommend GCC 4+ (or compatible) to support the
|
42
|
-
{
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
We recommend GCC 4+ (or compatible) to support the __sync builtins
|
41
|
+
(__sync_{add,sub}_and_fetch()):
|
42
|
+
|
43
|
+
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
44
|
+
|
45
|
+
For non-GCC 4+ users, we also support compilation with the libatomic_ops
|
46
|
+
package starting with Raindrops 0.4.0:
|
47
|
+
|
48
|
+
https://github.com/ivmai/libatomic_ops
|
47
49
|
|
48
50
|
If you're using a packaged Ruby distribution, make sure you have a C
|
49
51
|
compiler and the matching Ruby development libraries and headers.
|
@@ -52,30 +54,25 @@ If you use RubyGems:
|
|
52
54
|
|
53
55
|
gem install raindrops
|
54
56
|
|
55
|
-
Otherwise grab the latest tarball from:
|
56
|
-
|
57
|
-
http://raindrops.bogomips.org/files/
|
58
|
-
|
59
|
-
Unpack it, and run "ruby setup.rb"
|
60
|
-
|
61
57
|
== Usage
|
62
58
|
|
63
59
|
See Raindrops::Middleware and Raindrops::LastDataRecv documentation for
|
64
60
|
use Rack servers. The entire library is fully-documented and we are
|
65
|
-
responsive on the mailing list
|
66
|
-
|
61
|
+
responsive on the publically archived mailing list
|
62
|
+
(mailto:raindrops-public@yhbt.net) if
|
63
|
+
you have any questions or comments.
|
67
64
|
|
68
65
|
== Development
|
69
66
|
|
70
67
|
You can get the latest source via git from the following locations:
|
71
68
|
|
72
|
-
git://
|
69
|
+
git://yhbt.net/raindrops.git
|
73
70
|
git://repo.or.cz/raindrops.git (mirror)
|
74
71
|
|
75
72
|
You may browse the code from the web and download the latest snapshot
|
76
73
|
tarballs here:
|
77
74
|
|
78
|
-
*
|
75
|
+
* https://yhbt.net/raindrops.git
|
79
76
|
* http://repo.or.cz/w/raindrops.git (gitweb)
|
80
77
|
|
81
78
|
Inline patches (from "git format-patch") to the mailing list are
|
@@ -87,22 +84,19 @@ git itself. See the Documentation/SubmittingPatches document
|
|
87
84
|
distributed with git on on patch submission guidelines to follow. Just
|
88
85
|
don't email the git mailing list or maintainer with raindrops patches.
|
89
86
|
|
90
|
-
raindrops is
|
91
|
-
allow for a transition to future versions of the LGPL, contributors are
|
92
|
-
required to sign-off changes allowing allowing the project leader to
|
93
|
-
relicense raindrops under newer versions of the LGPL (which should be
|
94
|
-
similar in spirit to the existing LGPL).
|
87
|
+
raindrops is licensed under the LGPL-2.1+
|
95
88
|
|
96
89
|
== Contact
|
97
90
|
|
98
91
|
All feedback (bug reports, user/development discussion, patches, pull
|
99
|
-
requests) go to the mailing list:
|
100
|
-
|
101
|
-
The mailing list is mirrored to Gmane, all information about the
|
102
|
-
group is here:
|
92
|
+
requests) go to the publically archived mailing list:
|
93
|
+
mailto:raindrops-public@yhbt.net
|
103
94
|
|
104
|
-
|
95
|
+
Mailing list archives are available over HTTPS and NNTP:
|
105
96
|
|
106
|
-
|
97
|
+
* https://yhbt.net/raindrops-public/
|
98
|
+
* http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/raindrops-public/
|
99
|
+
* nntp://news.public-inbox.org/inbox.comp.lang.ruby.raindrops
|
107
100
|
|
108
|
-
|
101
|
+
Since archives are public, scrub sensitive information and
|
102
|
+
use anonymity tools such as Tor or Mixmaster if you deem necessary.
|
data/TODO
CHANGED
data/archive/.gitignore
ADDED
@@ -15,7 +15,6 @@
|
|
15
15
|
usage = "Usage: #$0 [-d DELAY] [-t QUEUED_THRESHOLD] ADDR..."
|
16
16
|
ARGV.size > 0 or abort usage
|
17
17
|
delay = false
|
18
|
-
all = false
|
19
18
|
queued_thresh = -1
|
20
19
|
# "normal" exits when driven on the command-line
|
21
20
|
trap(:INT) { exit 130 }
|
@@ -25,7 +24,7 @@
|
|
25
24
|
opts.banner = usage
|
26
25
|
opts.on('-d', '--delay=DELAY', Float) { |n| delay = n }
|
27
26
|
opts.on('-t', '--queued-threshold=INT', Integer) { |n| queued_thresh = n }
|
28
|
-
opts.on('-a', '--all') {
|
27
|
+
opts.on('-a', '--all') { } # noop
|
29
28
|
opts.parse! ARGV
|
30
29
|
end
|
31
30
|
|
data/examples/watcher_demo.ru
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Inlined rack app using yahns server (git clone git://yhbt.net/yahns.git)
|
2
|
+
# Usage: yahns -c /path/to/this/file.conf.rb
|
3
|
+
# There is no separate config.ru file for this example,
|
4
|
+
# but rack_app may also be a string pointing to the path of a
|
5
|
+
# config.ru file
|
6
|
+
|
7
|
+
require 'rack'
|
8
|
+
rack_app = Rack::Builder.new do
|
9
|
+
use Rack::Head
|
10
|
+
addr = %w(0.0.0.0:9418 0.0.0.0:443 [::]:443 0.0.0.0:80 [::]:80
|
11
|
+
127.0.0.1:6081 127.0.0.1:280 0.0.0.0:119 [::]:119)
|
12
|
+
use Raindrops::Middleware, listeners: addr
|
13
|
+
run Raindrops::Watcher.new(listeners: addr)
|
14
|
+
end.to_app
|
15
|
+
# rack_app = '/path/to/config.ru' # a more standard config
|
16
|
+
|
17
|
+
app(:rack, rack_app) do
|
18
|
+
# I keep IPv4 and IPv6 on separate sockets to avoid ugly
|
19
|
+
# IPv4-mapped-IPv6 addresses:
|
20
|
+
listen 8080
|
21
|
+
listen '[::]:8080', ipv6only: true
|
22
|
+
client_max_body_size 0 # no POST or any uploads
|
23
|
+
client_timeout 5
|
24
|
+
output_buffering false # needed for /tail/ endpoint to avoid ENOSPC
|
25
|
+
queue { worker_threads 30 }
|
26
|
+
end
|
27
|
+
|
28
|
+
# logging is optional, but recommended for diagnosing problems
|
29
|
+
# stderr_path '/var/log/yahns/stderr-raindrops.log'
|
30
|
+
# stdout_path '/var/log/yahns/stdout-raindrops.log'
|
data/examples/zbatery.conf.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Used for running Raindrops::Watcher, which requires a multi-threaded
|
2
2
|
# Rack server capable of streaming a response. Threads must be used,
|
3
|
-
# so
|
3
|
+
# so any multi-threaded Rack server may be used.
|
4
|
+
# zbatery was recommended in the past, but it is abandoned
|
5
|
+
# <http://zbatery.bogomip.org/>.
|
6
|
+
# yahns may work as an alternative (see yahns.conf.rb in this dir)
|
4
7
|
Rainbows! do
|
5
8
|
use :ThreadSpawn
|
6
9
|
end
|
data/ext/raindrops/extconf.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mkmf'
|
2
|
+
require 'shellwords'
|
2
3
|
|
3
4
|
dir_config('atomic_ops')
|
4
5
|
have_func('mmap', 'sys/mman.h') or abort 'mmap() not found'
|
@@ -6,9 +7,112 @@
|
|
6
7
|
|
7
8
|
$CPPFLAGS += " -D_GNU_SOURCE "
|
8
9
|
have_func('mremap', 'sys/mman.h')
|
10
|
+
headers = %w(sys/types.h netdb.h string.h sys/socket.h netinet/in.h)
|
11
|
+
if have_header('linux/tcp.h')
|
12
|
+
headers << 'linux/tcp.h'
|
13
|
+
else
|
14
|
+
%w(netinet/tcp.h netinet/tcp_fsm.h).each { |h|
|
15
|
+
have_header(h, headers) and headers << h
|
16
|
+
}
|
17
|
+
end
|
9
18
|
|
10
19
|
$CPPFLAGS += " -D_BSD_SOURCE "
|
20
|
+
|
21
|
+
if have_type("struct tcp_info", headers)
|
22
|
+
%w(
|
23
|
+
tcpi_state
|
24
|
+
tcpi_ca_state
|
25
|
+
tcpi_retransmits
|
26
|
+
tcpi_probes
|
27
|
+
tcpi_backoff
|
28
|
+
tcpi_options
|
29
|
+
tcpi_snd_wscale
|
30
|
+
tcpi_rcv_wscale
|
31
|
+
tcpi_rto
|
32
|
+
tcpi_ato
|
33
|
+
tcpi_snd_mss
|
34
|
+
tcpi_rcv_mss
|
35
|
+
tcpi_unacked
|
36
|
+
tcpi_sacked
|
37
|
+
tcpi_lost
|
38
|
+
tcpi_retrans
|
39
|
+
tcpi_fackets
|
40
|
+
tcpi_last_data_sent
|
41
|
+
tcpi_last_ack_sent
|
42
|
+
tcpi_last_data_recv
|
43
|
+
tcpi_last_ack_recv
|
44
|
+
tcpi_pmtu
|
45
|
+
tcpi_rcv_ssthresh
|
46
|
+
tcpi_rtt
|
47
|
+
tcpi_rttvar
|
48
|
+
tcpi_snd_ssthresh
|
49
|
+
tcpi_snd_cwnd
|
50
|
+
tcpi_advmss
|
51
|
+
tcpi_reordering
|
52
|
+
tcpi_rcv_rtt
|
53
|
+
tcpi_rcv_space
|
54
|
+
tcpi_total_retrans
|
55
|
+
tcpi_snd_wnd
|
56
|
+
tcpi_snd_bwnd
|
57
|
+
tcpi_snd_nxt
|
58
|
+
tcpi_rcv_nxt
|
59
|
+
tcpi_toe_tid
|
60
|
+
tcpi_snd_rexmitpack
|
61
|
+
tcpi_rcv_ooopack
|
62
|
+
tcpi_snd_zerowin
|
63
|
+
).each do |field|
|
64
|
+
cfunc = "tcp_info_#{field}"
|
65
|
+
if have_struct_member('struct tcp_info', field, headers)
|
66
|
+
func_body = <<EOF
|
67
|
+
static VALUE #{cfunc}(VALUE self)
|
68
|
+
{
|
69
|
+
struct tcp_info *info = DATA_PTR(self);
|
70
|
+
return UINT2NUM((uint32_t)info->#{field});
|
71
|
+
}
|
72
|
+
EOF
|
73
|
+
func_body.delete!("\n")
|
74
|
+
$defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}"
|
75
|
+
else
|
76
|
+
func_body = "static inline void #{cfunc}(void) {}"
|
77
|
+
$defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}"
|
78
|
+
cfunc = 'rb_f_notimplement'.freeze
|
79
|
+
end
|
80
|
+
rbmethod = %Q("#{field.sub(/\Atcpi_/, ''.freeze)}")
|
81
|
+
$defs << "-DDEFINE_METHOD_tcp_info_#{field}=" \
|
82
|
+
"#{Shellwords.shellescape(
|
83
|
+
%Q[rb_define_method(cTCP_Info,#{rbmethod},#{cfunc},0)])}"
|
84
|
+
end
|
85
|
+
tcp_state_map = {
|
86
|
+
ESTABLISHED: %w(TCP_ESTABLISHED TCPS_ESTABLISHED),
|
87
|
+
SYN_SENT: %w(TCP_SYN_SENT TCPS_SYN_SENT),
|
88
|
+
SYN_RECV: %w(TCP_SYN_RECV TCPS_SYN_RECEIVED),
|
89
|
+
FIN_WAIT1: %w(TCP_FIN_WAIT1 TCPS_FIN_WAIT_1),
|
90
|
+
FIN_WAIT2: %w(TCP_FIN_WAIT2 TCPS_FIN_WAIT_2),
|
91
|
+
TIME_WAIT: %w(TCP_TIME_WAIT TCPS_TIME_WAIT),
|
92
|
+
CLOSE: %w(TCP_CLOSE TCPS_CLOSED),
|
93
|
+
CLOSE_WAIT: %w(TCP_CLOSE_WAIT TCPS_CLOSE_WAIT),
|
94
|
+
LAST_ACK: %w(TCP_LAST_ACK TCPS_LAST_ACK),
|
95
|
+
LISTEN: %w(TCP_LISTEN TCPS_LISTEN),
|
96
|
+
CLOSING: %w(TCP_CLOSING TCPS_CLOSING),
|
97
|
+
}
|
98
|
+
nstate = 0
|
99
|
+
tcp_state_map.each do |state, try|
|
100
|
+
try.each do |os_name|
|
101
|
+
have_const(os_name, headers) or next
|
102
|
+
tcp_state_map[state] = os_name
|
103
|
+
nstate += 1
|
104
|
+
end
|
105
|
+
end
|
106
|
+
if nstate == tcp_state_map.size
|
107
|
+
$defs << '-DRAINDROPS_TCP_STATES_ALL_KNOWN=1'
|
108
|
+
tcp_state_map.each do |state, name|
|
109
|
+
$defs << "-DRAINDROPS_TCP_#{state}=#{name}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
11
114
|
have_func("getpagesize", "unistd.h")
|
115
|
+
have_func('rb_thread_call_without_gvl')
|
12
116
|
have_func('rb_thread_blocking_region')
|
13
117
|
have_func('rb_thread_io_blocking_region')
|
14
118
|
|
@@ -39,17 +143,18 @@
|
|
39
143
|
$defs.push(format("-DHAVE_GCC_ATOMIC_BUILTINS"))
|
40
144
|
true
|
41
145
|
else
|
42
|
-
|
146
|
+
$CFLAGS = prev_cflags
|
43
147
|
false
|
44
148
|
end
|
45
149
|
end
|
46
150
|
end or have_header('atomic_ops.h') or abort <<-SRC
|
47
151
|
|
48
152
|
libatomic_ops is required if GCC 4+ is not used.
|
49
|
-
See
|
153
|
+
See https://github.com/ivmai/libatomic_ops
|
50
154
|
|
51
155
|
Users of Debian-based distros may run:
|
52
156
|
|
53
157
|
apt-get install libatomic-ops-dev
|
54
158
|
SRC
|
159
|
+
create_header # generate extconf.h to avoid excessively long command-line
|
55
160
|
create_makefile('raindrops_ext')
|
@@ -1,45 +1,24 @@
|
|
1
1
|
#include <ruby.h>
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#else
|
5
|
-
# include <st.h>
|
6
|
-
#endif
|
2
|
+
#include <stdarg.h>
|
3
|
+
#include <ruby/st.h>
|
7
4
|
#include "my_fileno.h"
|
8
5
|
#ifdef __linux__
|
9
6
|
|
10
|
-
/* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
|
11
|
-
#ifndef RSTRING_LEN
|
12
|
-
# define RSTRING_LEN(s) (RSTRING(s)->len)
|
13
|
-
#endif
|
14
|
-
|
15
|
-
/* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
|
16
|
-
#if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && \
|
17
|
-
!defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
|
18
|
-
# include <rubysig.h>
|
19
|
-
# define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
|
20
|
-
typedef void rb_unblock_function_t(void *);
|
21
|
-
typedef VALUE rb_blocking_function_t(void *);
|
22
|
-
static VALUE
|
23
|
-
rb_thread_blocking_region(
|
24
|
-
rb_blocking_function_t *func, void *data1,
|
25
|
-
rb_unblock_function_t *ubf, void *data2)
|
26
|
-
{
|
27
|
-
VALUE rv;
|
28
|
-
|
29
|
-
TRAP_BEG;
|
30
|
-
rv = func(data1);
|
31
|
-
TRAP_END;
|
32
|
-
|
33
|
-
return rv;
|
34
|
-
}
|
35
|
-
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
36
|
-
|
37
7
|
#ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
|
8
|
+
/* Ruby 1.9.3 and 2.0.0 */
|
38
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)
|
39
19
|
#else
|
40
|
-
#
|
41
|
-
|
42
|
-
#endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
|
20
|
+
# error Ruby <= 1.8 not supported
|
21
|
+
#endif
|
43
22
|
|
44
23
|
#include <assert.h>
|
45
24
|
#include <errno.h>
|
@@ -213,91 +192,89 @@ static const char *addr_any(sa_family_t family)
|
|
213
192
|
return ipv6;
|
214
193
|
}
|
215
194
|
|
216
|
-
|
195
|
+
#ifdef __GNUC__
|
196
|
+
static void bug_warn_nogvl(const char *, ...)
|
197
|
+
__attribute__((format(printf,1,2)));
|
198
|
+
#endif
|
199
|
+
static void bug_warn_nogvl(const char *fmt, ...)
|
217
200
|
{
|
201
|
+
va_list ap;
|
202
|
+
|
203
|
+
va_start(ap, fmt);
|
204
|
+
vfprintf(stderr, fmt, ap);
|
205
|
+
va_end(ap);
|
206
|
+
|
218
207
|
fprintf(stderr, "Please report how you produced this at "\
|
219
|
-
"raindrops@
|
208
|
+
"raindrops-public@yhbt.net\n");
|
220
209
|
fflush(stderr);
|
221
210
|
}
|
222
211
|
|
223
212
|
static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
|
224
213
|
{
|
225
|
-
char *key, *port, *old_key;
|
214
|
+
char *host, *key, *port, *old_key;
|
226
215
|
size_t alloca_len;
|
227
216
|
struct listen_stats *stats;
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
switch ((sa.ss.ss_family = r->idiag_family)) {
|
217
|
+
socklen_t hostlen;
|
218
|
+
socklen_t portlen = (socklen_t)sizeof("65535");
|
219
|
+
int n;
|
220
|
+
const void *src = r->id.idiag_src;
|
221
|
+
|
222
|
+
switch (r->idiag_family) {
|
236
223
|
case AF_INET: {
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
alloca_len = keylen + 1 + portlen;
|
241
|
-
key = alloca(alloca_len);
|
242
|
-
key[keylen] = 0; /* will be ':' later */
|
243
|
-
port = key + keylen + 1;
|
244
|
-
rc = getnameinfo(&sa.sa, len,
|
245
|
-
key, keylen, port, portlen, flags);
|
224
|
+
hostlen = INET_ADDRSTRLEN;
|
225
|
+
alloca_len = hostlen + portlen;
|
226
|
+
host = key = alloca(alloca_len);
|
246
227
|
break;
|
247
228
|
}
|
248
229
|
case AF_INET6: {
|
249
|
-
|
250
|
-
|
251
|
-
keylen = INET6_ADDRSTRLEN;
|
252
|
-
/* [ ] */
|
253
|
-
alloca_len = 1 + keylen + 1 + 1 + portlen;
|
230
|
+
hostlen = INET6_ADDRSTRLEN;
|
231
|
+
alloca_len = 1 + hostlen + 1 + portlen;
|
254
232
|
key = alloca(alloca_len);
|
255
|
-
|
256
|
-
key[1 + keylen + 1] = 0; /* will be ':' later */
|
257
|
-
port = 1 + key + keylen + 1 + 1;
|
258
|
-
rc = getnameinfo(&sa.sa, len,
|
259
|
-
key + 1, keylen, port, portlen, flags);
|
233
|
+
host = key + 1;
|
260
234
|
break;
|
261
235
|
}
|
262
236
|
default:
|
263
237
|
assert(0 && "unsupported address family, could that be IPv7?!");
|
264
238
|
}
|
265
|
-
if (
|
266
|
-
|
267
|
-
|
268
|
-
*
|
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';
|
269
243
|
}
|
270
|
-
|
271
|
-
|
272
|
-
portlen = strlen(port);
|
273
|
-
|
274
|
-
switch (sa.ss.ss_family) {
|
244
|
+
hostlen = (socklen_t)strlen(host);
|
245
|
+
switch (r->idiag_family) {
|
275
246
|
case AF_INET:
|
276
|
-
|
277
|
-
|
247
|
+
host[hostlen] = ':';
|
248
|
+
port = host + hostlen + 1;
|
278
249
|
break;
|
279
250
|
case AF_INET6:
|
280
|
-
key[
|
281
|
-
|
282
|
-
|
283
|
-
|
251
|
+
key[0] = '[';
|
252
|
+
host[hostlen] = ']';
|
253
|
+
host[hostlen + 1] = ':';
|
254
|
+
port = host + hostlen + 2;
|
284
255
|
break;
|
285
256
|
default:
|
286
257
|
assert(0 && "unsupported address family, could that be IPv7?!");
|
287
258
|
}
|
288
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
|
+
|
289
266
|
if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
|
290
267
|
return stats;
|
291
268
|
|
292
269
|
old_key = key;
|
293
270
|
|
294
271
|
if (r->idiag_state == TCP_ESTABLISHED) {
|
295
|
-
|
296
|
-
addr_any(
|
272
|
+
n = snprintf(key, alloca_len, "%s:%u",
|
273
|
+
addr_any(r->idiag_family),
|
297
274
|
ntohs(r->id.idiag_sport));
|
298
275
|
if (n <= 0) {
|
299
|
-
|
300
|
-
|
276
|
+
bug_warn_nogvl("BUG: snprintf: %d\n", n);
|
277
|
+
*key = '\0';
|
301
278
|
}
|
302
279
|
if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
|
303
280
|
return stats;
|
@@ -310,8 +287,9 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
|
|
310
287
|
memcpy(key, old_key, n + 1);
|
311
288
|
}
|
312
289
|
} else {
|
313
|
-
|
314
|
-
|
290
|
+
size_t old_len = strlen(old_key) + 1;
|
291
|
+
key = xmalloc(old_len);
|
292
|
+
memcpy(key, old_key, old_len);
|
315
293
|
}
|
316
294
|
stats = xcalloc(1, sizeof(struct listen_stats));
|
317
295
|
st_insert(table, (st_data_t)key, (st_data_t)stats);
|
@@ -391,8 +369,8 @@ static void prep_diag_args(
|
|
391
369
|
|
392
370
|
nladdr->nl_family = AF_NETLINK;
|
393
371
|
|
394
|
-
req->nlh.nlmsg_len = sizeof(struct diag_req) +
|
395
|
-
RTA_LENGTH(args->iov[2].iov_len);
|
372
|
+
req->nlh.nlmsg_len = (unsigned int)(sizeof(struct diag_req) +
|
373
|
+
RTA_LENGTH(args->iov[2].iov_len));
|
396
374
|
req->nlh.nlmsg_type = TCPDIAG_GETSOCK;
|
397
375
|
req->nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
|
398
376
|
req->nlh.nlmsg_pid = getpid();
|
@@ -465,12 +443,12 @@ static VALUE diag(void *ptr)
|
|
465
443
|
}
|
466
444
|
}
|
467
445
|
out:
|
468
|
-
|
446
|
+
/* prepare to raise, free memory before reacquiring GVL */
|
447
|
+
if (err && args->table) {
|
469
448
|
int save_errno = errno;
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
}
|
449
|
+
|
450
|
+
st_foreach(args->table, st_free_data, 0);
|
451
|
+
st_free_table(args->table);
|
474
452
|
errno = save_errno;
|
475
453
|
}
|
476
454
|
return (VALUE)err;
|
@@ -584,6 +562,10 @@ static void gen_bytecode(struct iovec *iov, union any_addr *inet)
|
|
584
562
|
}
|
585
563
|
}
|
586
564
|
|
565
|
+
/*
|
566
|
+
* n.b. we may safely raise here because an error will cause diag()
|
567
|
+
* to free args->table
|
568
|
+
*/
|
587
569
|
static void nl_errcheck(VALUE r)
|
588
570
|
{
|
589
571
|
const char *err = (const char *)r;
|
@@ -604,11 +586,18 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
|
|
604
586
|
gen_bytecode(&args->iov[2], &query_addr);
|
605
587
|
|
606
588
|
memset(&args->stats, 0, sizeof(struct listen_stats));
|
607
|
-
nl_errcheck(
|
589
|
+
nl_errcheck(rd_fd_region(diag, args, args->fd));
|
608
590
|
|
609
591
|
return rb_listen_stats(&args->stats);
|
610
592
|
}
|
611
593
|
|
594
|
+
static int drop_placeholders(st_data_t k, st_data_t v, st_data_t ign)
|
595
|
+
{
|
596
|
+
if ((VALUE)v == Qtrue)
|
597
|
+
return ST_DELETE;
|
598
|
+
return ST_CONTINUE;
|
599
|
+
}
|
600
|
+
|
612
601
|
/*
|
613
602
|
* call-seq:
|
614
603
|
* Raindrops::Linux.tcp_listener_stats([addrs[, sock]]) => hash
|
@@ -649,10 +638,9 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
649
638
|
case T_ARRAY: {
|
650
639
|
long i;
|
651
640
|
long len = RARRAY_LEN(addrs);
|
652
|
-
VALUE cur;
|
653
641
|
|
654
642
|
if (len == 1) {
|
655
|
-
cur = rb_ary_entry(addrs, 0);
|
643
|
+
VALUE cur = rb_ary_entry(addrs, 0);
|
656
644
|
|
657
645
|
rb_hash_aset(rv, cur, tcp_stats(&args, cur));
|
658
646
|
return rv;
|
@@ -662,7 +650,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
662
650
|
VALUE cur = rb_ary_entry(addrs, i);
|
663
651
|
|
664
652
|
parse_addr(&check, cur);
|
665
|
-
rb_hash_aset(rv, cur, Qtrue);
|
653
|
+
rb_hash_aset(rv, cur, Qtrue /* placeholder */);
|
666
654
|
}
|
667
655
|
/* fall through */
|
668
656
|
}
|
@@ -675,11 +663,14 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
675
663
|
"addr must be an array of strings, a string, or nil");
|
676
664
|
}
|
677
665
|
|
678
|
-
nl_errcheck(
|
666
|
+
nl_errcheck(rd_fd_region(diag, &args, args.fd));
|
679
667
|
|
680
668
|
st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
|
681
669
|
st_free_table(args.table);
|
682
670
|
|
671
|
+
if (RHASH_SIZE(rv) > 1)
|
672
|
+
rb_hash_foreach(rv, drop_placeholders, Qfalse);
|
673
|
+
|
683
674
|
/* let GC deal with corner cases */
|
684
675
|
if (argc < 2) rb_io_close(sock);
|
685
676
|
return rv;
|
@@ -687,11 +678,12 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
687
678
|
|
688
679
|
void Init_raindrops_linux_inet_diag(void)
|
689
680
|
{
|
690
|
-
VALUE cRaindrops =
|
681
|
+
VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
|
691
682
|
VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
|
683
|
+
VALUE Socket;
|
692
684
|
|
693
685
|
rb_require("socket");
|
694
|
-
|
686
|
+
Socket = rb_const_get(rb_cObject, rb_intern("Socket"));
|
695
687
|
id_new = rb_intern("new");
|
696
688
|
|
697
689
|
/*
|
@@ -700,10 +692,11 @@ void Init_raindrops_linux_inet_diag(void)
|
|
700
692
|
* This is a subclass of +Socket+ specifically for talking
|
701
693
|
* to the inet_diag facility of Netlink.
|
702
694
|
*/
|
703
|
-
cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket",
|
695
|
+
cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", Socket);
|
704
696
|
rb_define_singleton_method(cIDSock, "new", ids_s_new, 0);
|
705
697
|
|
706
698
|
cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));
|
699
|
+
rb_gc_register_mark_object(cListenStats); /* pin */
|
707
700
|
|
708
701
|
rb_define_module_function(mLinux, "tcp_listener_stats",
|
709
702
|
tcp_listener_stats, -1);
|