raindrops 0.1.0
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.
- data/.document +7 -0
- data/.gitignore +13 -0
- data/COPYING +165 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +172 -0
- data/LICENSE +16 -0
- data/README +117 -0
- data/Rakefile +156 -0
- data/TODO +2 -0
- data/examples/linux-tcp-listener-stats.rb +28 -0
- data/ext/raindrops/extconf.rb +11 -0
- data/ext/raindrops/linux_inet_diag.c +342 -0
- data/ext/raindrops/raindrops.c +192 -0
- data/lib/raindrops.rb +35 -0
- data/lib/raindrops/linux.rb +55 -0
- data/lib/raindrops/middleware.rb +75 -0
- data/lib/raindrops/struct.rb +47 -0
- data/raindrops.gemspec +38 -0
- data/setup.rb +1586 -0
- data/test/test_linux.rb +228 -0
- data/test/test_linux_middleware.rb +59 -0
- data/test/test_middleware.rb +111 -0
- data/test/test_raindrops.rb +95 -0
- data/test/test_raindrops_gc.rb +13 -0
- data/test/test_struct.rb +54 -0
- metadata +103 -0
data/Rakefile
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# most tasks are in the GNUmakefile which offers better parallelism
|
4
|
+
|
5
|
+
def tags
|
6
|
+
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
7
|
+
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
8
|
+
if %r{\Av[\d\.]+\z} =~ tag
|
9
|
+
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
10
|
+
header = header.split(/\n/)
|
11
|
+
tagger = header.grep(/\Atagger /).first
|
12
|
+
body ||= "initial"
|
13
|
+
{
|
14
|
+
:time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
|
15
|
+
:tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
|
16
|
+
:tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
|
17
|
+
:id => `git rev-parse refs/tags/#{tag}`.chomp!,
|
18
|
+
:tag => tag,
|
19
|
+
:subject => subject,
|
20
|
+
:body => body,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end.compact.sort { |a,b| b[:time] <=> a[:time] }
|
24
|
+
end
|
25
|
+
|
26
|
+
cgit_url = "http://git.bogomips.org/cgit/raindrops.git"
|
27
|
+
git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/raindrops.git'
|
28
|
+
web_url = "http://rainbows.bogomips.org/"
|
29
|
+
|
30
|
+
desc 'prints news as an Atom feed'
|
31
|
+
task :news_atom do
|
32
|
+
require 'nokogiri'
|
33
|
+
new_tags = tags[0,10]
|
34
|
+
puts(Nokogiri::XML::Builder.new do
|
35
|
+
feed :xmlns => "http://www.w3.org/2005/Atom" do
|
36
|
+
id! "#{web_url}NEWS.atom.xml"
|
37
|
+
title "Raindrops news"
|
38
|
+
subtitle "real-time stats for Rack servers"
|
39
|
+
link! :rel => "alternate", :type => "text/html",
|
40
|
+
:href => "#{web_url}NEWS.html"
|
41
|
+
updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
|
42
|
+
new_tags.each do |tag|
|
43
|
+
entry do
|
44
|
+
title tag[:subject]
|
45
|
+
updated tag[:time]
|
46
|
+
published tag[:time]
|
47
|
+
author {
|
48
|
+
name tag[:tagger_name]
|
49
|
+
email tag[:tagger_email]
|
50
|
+
}
|
51
|
+
url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
|
52
|
+
link! :rel => "alternate", :type => "text/html", :href =>url
|
53
|
+
id! url
|
54
|
+
message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
|
55
|
+
content({:type =>:text}, message_only)
|
56
|
+
content(:type =>:xhtml) { pre tag[:body] }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end.to_xml)
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'prints RDoc-formatted news'
|
64
|
+
task :news_rdoc do
|
65
|
+
tags.each do |tag|
|
66
|
+
time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
|
67
|
+
puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
|
68
|
+
puts ""
|
69
|
+
|
70
|
+
body = tag[:body]
|
71
|
+
puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
|
72
|
+
puts ""
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "print release changelog for Rubyforge"
|
77
|
+
task :release_changes do
|
78
|
+
version = ENV['VERSION'] or abort "VERSION= needed"
|
79
|
+
version = "v#{version}"
|
80
|
+
vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
|
81
|
+
prev = vtags[vtags.index(version) - 1]
|
82
|
+
if prev
|
83
|
+
system('git', 'diff', '--stat', prev, version) or abort $?
|
84
|
+
puts ""
|
85
|
+
system('git', 'log', "#{prev}..#{version}") or abort $?
|
86
|
+
else
|
87
|
+
system('git', 'log', version) or abort $?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "print release notes for Rubyforge"
|
92
|
+
task :release_notes do
|
93
|
+
spec = Gem::Specification.load('raindrops.gemspec')
|
94
|
+
puts spec.description.strip
|
95
|
+
puts ""
|
96
|
+
puts "* #{spec.homepage}"
|
97
|
+
puts "* #{spec.email}"
|
98
|
+
puts "* #{git_url}"
|
99
|
+
|
100
|
+
_, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
101
|
+
print "\nChanges:\n\n"
|
102
|
+
puts body
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "read news article from STDIN and post to rubyforge"
|
106
|
+
task :publish_news do
|
107
|
+
require 'rubyforge'
|
108
|
+
IO.select([STDIN], nil, nil, 1) or abort "E: news must be read from stdin"
|
109
|
+
msg = STDIN.readlines
|
110
|
+
subject = msg.shift
|
111
|
+
blank = msg.shift
|
112
|
+
blank == "\n" or abort "no newline after subject!"
|
113
|
+
subject.strip!
|
114
|
+
body = msg.join("").strip!
|
115
|
+
|
116
|
+
rf = RubyForge.new.configure
|
117
|
+
rf.login
|
118
|
+
rf.post_news('rainbows', subject, body)
|
119
|
+
end
|
120
|
+
|
121
|
+
desc "post to RAA"
|
122
|
+
task :raa_update do
|
123
|
+
require 'net/http'
|
124
|
+
require 'net/netrc'
|
125
|
+
rc = Net::Netrc.locate('raindrops-raa') or abort "~/.netrc not found"
|
126
|
+
password = rc.password
|
127
|
+
|
128
|
+
s = Gem::Specification.load('raindrops.gemspec')
|
129
|
+
desc = [ s.description.strip ]
|
130
|
+
desc << ""
|
131
|
+
desc << "* #{s.email}"
|
132
|
+
desc << "* #{git_url}"
|
133
|
+
desc << "* #{cgit_url}"
|
134
|
+
desc = desc.join("\n")
|
135
|
+
uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml')
|
136
|
+
form = {
|
137
|
+
:name => s.name,
|
138
|
+
:short_description => s.summary,
|
139
|
+
:version => s.version.to_s,
|
140
|
+
:status => 'experimental',
|
141
|
+
:owner => s.authors.first,
|
142
|
+
:email => s.email,
|
143
|
+
:category_major => 'Library',
|
144
|
+
:category_minor => 'Rack',
|
145
|
+
:url => s.homepage,
|
146
|
+
:download => 'http://rubyforge.org/frs/?group_id=8977',
|
147
|
+
:license => 'LGPL', # LGPLv3, actually, but RAA is ancient...
|
148
|
+
:description_style => 'Plain',
|
149
|
+
:description => desc,
|
150
|
+
:pass => password,
|
151
|
+
:submit => 'Update',
|
152
|
+
}
|
153
|
+
res = Net::HTTP.post_form(uri, form)
|
154
|
+
p res
|
155
|
+
puts res.body
|
156
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# this is used to show or watch the number of active and queued
|
4
|
+
# connections on any listener socket from the command line
|
5
|
+
|
6
|
+
require 'raindrops'
|
7
|
+
require 'optparse'
|
8
|
+
usage = "Usage: #$0 [--loop] ADDR..."
|
9
|
+
ARGV.size > 0 or abort usage
|
10
|
+
delay = false
|
11
|
+
|
12
|
+
# "normal" exits when driven on the command-line
|
13
|
+
trap(:INT) { exit 130 }
|
14
|
+
trap(:PIPE) { exit 0 }
|
15
|
+
|
16
|
+
opts = OptionParser.new('', 24, ' ') do |opts|
|
17
|
+
opts.banner = usage
|
18
|
+
opts.on('-d', '--delay=delay') { |nr| delay = nr.to_i }
|
19
|
+
opts.parse! ARGV
|
20
|
+
end
|
21
|
+
|
22
|
+
fmt = "% 19s % 10u % 10u\n"
|
23
|
+
printf fmt.tr('u','s'), *%w(address active queued)
|
24
|
+
|
25
|
+
begin
|
26
|
+
stats = Raindrops::Linux.tcp_listener_stats(ARGV)
|
27
|
+
stats.each { |addr,stats| printf fmt, addr, stats.active, stats.queued }
|
28
|
+
end while delay && sleep(delay)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
# FIXME: test for GCC __sync_XXX builtins here, somehow...
|
4
|
+
have_func('mmap', 'sys/mman.h') or abort 'mmap() not found'
|
5
|
+
have_func('munmap', 'sys/mman.h') or abort 'munmap() not found'
|
6
|
+
|
7
|
+
have_func("rb_struct_alloc_noinit")
|
8
|
+
have_func('rb_thread_blocking_region')
|
9
|
+
|
10
|
+
dir_config('raindrops')
|
11
|
+
create_makefile('raindrops_ext')
|
@@ -0,0 +1,342 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
/* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
|
4
|
+
#ifndef RSTRING_PTR
|
5
|
+
# define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
6
|
+
#endif
|
7
|
+
#ifndef RSTRING_LEN
|
8
|
+
# define RSTRING_LEN(s) (RSTRING(s)->len)
|
9
|
+
#endif
|
10
|
+
#ifndef RSTRUCT_PTR
|
11
|
+
# define RSTRUCT_PTR(s) (RSTRUCT(s)->ptr)
|
12
|
+
#endif
|
13
|
+
#ifndef RSTRUCT_LEN
|
14
|
+
# define RSTRUCT_LEN(s) (RSTRUCT(s)->len)
|
15
|
+
#endif
|
16
|
+
|
17
|
+
#ifndef HAVE_RB_STRUCT_ALLOC_NOINIT
|
18
|
+
static ID id_new;
|
19
|
+
static VALUE rb_struct_alloc_noinit(VALUE class)
|
20
|
+
{
|
21
|
+
return rb_funcall(class, id_new, 0, 0);
|
22
|
+
}
|
23
|
+
#endif /* !defined(HAVE_RB_STRUCT_ALLOC_NOINIT) */
|
24
|
+
|
25
|
+
/* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
|
26
|
+
#ifndef HAVE_RB_THREAD_BLOCKING_REGION
|
27
|
+
# include <rubysig.h>
|
28
|
+
# define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
|
29
|
+
typedef void rb_unblock_function_t(void *);
|
30
|
+
typedef VALUE rb_blocking_function_t(void *);
|
31
|
+
static VALUE
|
32
|
+
rb_thread_blocking_region(
|
33
|
+
rb_blocking_function_t *func, void *data1,
|
34
|
+
rb_unblock_function_t *ubf, void *data2)
|
35
|
+
{
|
36
|
+
VALUE rv;
|
37
|
+
|
38
|
+
TRAP_BEG;
|
39
|
+
rv = func(data1);
|
40
|
+
TRAP_END;
|
41
|
+
|
42
|
+
return rv;
|
43
|
+
}
|
44
|
+
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
45
|
+
|
46
|
+
#include <assert.h>
|
47
|
+
#include <errno.h>
|
48
|
+
#include <sys/socket.h>
|
49
|
+
#include <sys/types.h>
|
50
|
+
#include <unistd.h>
|
51
|
+
#include <string.h>
|
52
|
+
#include <asm/types.h>
|
53
|
+
#include <netinet/in.h>
|
54
|
+
#include <arpa/inet.h>
|
55
|
+
#include <netinet/tcp.h>
|
56
|
+
#include <linux/netlink.h>
|
57
|
+
#include <linux/rtnetlink.h>
|
58
|
+
#include <linux/inet_diag.h>
|
59
|
+
|
60
|
+
static size_t page_size;
|
61
|
+
static unsigned g_seq;
|
62
|
+
static VALUE cListenStats;
|
63
|
+
|
64
|
+
struct my_addr {
|
65
|
+
in_addr_t addr;
|
66
|
+
uint16_t port;
|
67
|
+
};
|
68
|
+
|
69
|
+
struct listen_stats {
|
70
|
+
long active;
|
71
|
+
long queued;
|
72
|
+
};
|
73
|
+
|
74
|
+
#define OPLEN (sizeof(struct inet_diag_bc_op) + \
|
75
|
+
sizeof(struct inet_diag_hostcond) + \
|
76
|
+
sizeof(in_addr_t))
|
77
|
+
|
78
|
+
struct nogvl_args {
|
79
|
+
struct iovec iov[3]; /* last iov holds inet_diag bytecode */
|
80
|
+
struct my_addr addrs;
|
81
|
+
struct listen_stats stats;
|
82
|
+
};
|
83
|
+
|
84
|
+
/* creates a Ruby ListenStats Struct based on our internal listen_stats */
|
85
|
+
static VALUE rb_listen_stats(struct listen_stats *stats)
|
86
|
+
{
|
87
|
+
VALUE rv = rb_struct_alloc_noinit(cListenStats);
|
88
|
+
VALUE *ptr = RSTRUCT_PTR(rv);
|
89
|
+
|
90
|
+
ptr[0] = LONG2NUM(stats->active);
|
91
|
+
ptr[1] = LONG2NUM(stats->queued);
|
92
|
+
|
93
|
+
return rv;
|
94
|
+
}
|
95
|
+
|
96
|
+
/*
|
97
|
+
* converts a base 10 string representing a port number into
|
98
|
+
* an unsigned 16 bit integer. Raises ArgumentError on failure
|
99
|
+
*/
|
100
|
+
static uint16_t my_inet_port(const char *port)
|
101
|
+
{
|
102
|
+
char *err;
|
103
|
+
unsigned long tmp = strtoul(port, &err, 10);
|
104
|
+
|
105
|
+
if (*err != 0 || tmp > 0xffff)
|
106
|
+
rb_raise(rb_eArgError, "port not parsable: `%s'\n", port);
|
107
|
+
|
108
|
+
return (uint16_t)tmp;
|
109
|
+
}
|
110
|
+
|
111
|
+
/* inner loop of inet_diag, called for every socket returned by netlink */
|
112
|
+
static inline void r_acc(struct nogvl_args *args, struct inet_diag_msg *r)
|
113
|
+
{
|
114
|
+
/*
|
115
|
+
* inode == 0 means the connection is still in the listen queue
|
116
|
+
* and has not yet been accept()-ed by the server. The
|
117
|
+
* inet_diag bytecode cannot filter this for us.
|
118
|
+
*/
|
119
|
+
if (r->idiag_inode == 0)
|
120
|
+
return;
|
121
|
+
if (r->idiag_state == TCP_ESTABLISHED)
|
122
|
+
args->stats.active++;
|
123
|
+
else /* if (r->idiag_state == TCP_LISTEN) */
|
124
|
+
args->stats.queued = r->idiag_rqueue;
|
125
|
+
/*
|
126
|
+
* we wont get anything else because of the idiag_states filter
|
127
|
+
*/
|
128
|
+
}
|
129
|
+
|
130
|
+
static const char err_socket[] = "socket";
|
131
|
+
static const char err_sendmsg[] = "sendmsg";
|
132
|
+
static const char err_recvmsg[] = "recvmsg";
|
133
|
+
static const char err_nlmsg[] = "nlmsg";
|
134
|
+
|
135
|
+
/* does the inet_diag stuff with netlink(), this is called w/o GVL */
|
136
|
+
static VALUE diag(void *ptr)
|
137
|
+
{
|
138
|
+
struct nogvl_args *args = ptr;
|
139
|
+
struct sockaddr_nl nladdr;
|
140
|
+
struct rtattr rta;
|
141
|
+
struct {
|
142
|
+
struct nlmsghdr nlh;
|
143
|
+
struct inet_diag_req r;
|
144
|
+
} req;
|
145
|
+
struct msghdr msg;
|
146
|
+
const char *err = NULL;
|
147
|
+
unsigned seq = __sync_add_and_fetch(&g_seq, 1);
|
148
|
+
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
|
149
|
+
|
150
|
+
if (fd < 0)
|
151
|
+
return (VALUE)err_socket;
|
152
|
+
|
153
|
+
memset(&args->stats, 0, sizeof(struct listen_stats));
|
154
|
+
|
155
|
+
memset(&nladdr, 0, sizeof(nladdr));
|
156
|
+
nladdr.nl_family = AF_NETLINK;
|
157
|
+
|
158
|
+
memset(&req, 0, sizeof(req));
|
159
|
+
req.nlh.nlmsg_len = sizeof(req) + RTA_LENGTH(args->iov[2].iov_len);
|
160
|
+
req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
|
161
|
+
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
|
162
|
+
req.nlh.nlmsg_pid = getpid();
|
163
|
+
req.nlh.nlmsg_seq = seq;
|
164
|
+
req.r.idiag_family = AF_INET;
|
165
|
+
req.r.idiag_states = (1<<TCP_ESTABLISHED) | (1<<TCP_LISTEN);
|
166
|
+
rta.rta_type = INET_DIAG_REQ_BYTECODE;
|
167
|
+
rta.rta_len = RTA_LENGTH(args->iov[2].iov_len);
|
168
|
+
|
169
|
+
args->iov[0].iov_base = &req;
|
170
|
+
args->iov[0].iov_len = sizeof(req);
|
171
|
+
args->iov[1].iov_base = &rta;
|
172
|
+
args->iov[1].iov_len = sizeof(rta);
|
173
|
+
|
174
|
+
memset(&msg, 0, sizeof(msg));
|
175
|
+
msg.msg_name = (void *)&nladdr;
|
176
|
+
msg.msg_namelen = sizeof(nladdr);
|
177
|
+
msg.msg_iov = args->iov;
|
178
|
+
msg.msg_iovlen = 3;
|
179
|
+
|
180
|
+
if (sendmsg(fd, &msg, 0) < 0) {
|
181
|
+
err = err_sendmsg;
|
182
|
+
goto out;
|
183
|
+
}
|
184
|
+
|
185
|
+
/* reuse buffer that was allocated for bytecode */
|
186
|
+
args->iov[0].iov_len = page_size;
|
187
|
+
args->iov[0].iov_base = args->iov[2].iov_base;
|
188
|
+
|
189
|
+
while (1) {
|
190
|
+
ssize_t readed;
|
191
|
+
struct nlmsghdr *h = (struct nlmsghdr *)args->iov[0].iov_base;
|
192
|
+
|
193
|
+
memset(&msg, 0, sizeof(msg));
|
194
|
+
msg.msg_name = (void *)&nladdr;
|
195
|
+
msg.msg_namelen = sizeof(nladdr);
|
196
|
+
msg.msg_iov = args->iov;
|
197
|
+
msg.msg_iovlen = 1;
|
198
|
+
|
199
|
+
readed = recvmsg(fd, &msg, 0);
|
200
|
+
if (readed < 0) {
|
201
|
+
if (errno == EINTR)
|
202
|
+
continue;
|
203
|
+
err = err_recvmsg;
|
204
|
+
goto out;
|
205
|
+
}
|
206
|
+
if (readed == 0)
|
207
|
+
goto out;
|
208
|
+
|
209
|
+
for ( ; NLMSG_OK(h, readed); h = NLMSG_NEXT(h, readed)) {
|
210
|
+
if (h->nlmsg_seq != seq)
|
211
|
+
continue;
|
212
|
+
if (h->nlmsg_type == NLMSG_DONE)
|
213
|
+
goto out;
|
214
|
+
if (h->nlmsg_type == NLMSG_ERROR) {
|
215
|
+
err = err_nlmsg;
|
216
|
+
goto out;
|
217
|
+
}
|
218
|
+
r_acc(args, NLMSG_DATA(h));
|
219
|
+
}
|
220
|
+
}
|
221
|
+
out:
|
222
|
+
{
|
223
|
+
int save_errno = errno;
|
224
|
+
close(fd);
|
225
|
+
errno = save_errno;
|
226
|
+
}
|
227
|
+
return (VALUE)err;
|
228
|
+
}
|
229
|
+
|
230
|
+
/* populates inet my_addr struct by parsing +addr+ */
|
231
|
+
static void parse_addr(struct my_addr *inet, VALUE addr)
|
232
|
+
{
|
233
|
+
char *host_port, *colon;
|
234
|
+
|
235
|
+
if (TYPE(addr) != T_STRING)
|
236
|
+
rb_raise(rb_eArgError, "addrs must be an Array of Strings");
|
237
|
+
|
238
|
+
host_port = RSTRING_PTR(addr);
|
239
|
+
colon = memchr(host_port, ':', RSTRING_LEN(addr));
|
240
|
+
if (!colon)
|
241
|
+
rb_raise(rb_eArgError, "port not found in: `%s'", host_port);
|
242
|
+
|
243
|
+
*colon = 0;
|
244
|
+
inet->addr = inet_addr(host_port);
|
245
|
+
*colon = ':';
|
246
|
+
inet->port = htons(my_inet_port(colon + 1));
|
247
|
+
}
|
248
|
+
|
249
|
+
/* generates inet_diag bytecode to match a single addr */
|
250
|
+
static void gen_bytecode(struct iovec *iov, struct my_addr *inet)
|
251
|
+
{
|
252
|
+
struct inet_diag_bc_op *op;
|
253
|
+
struct inet_diag_hostcond *cond;
|
254
|
+
|
255
|
+
/* iov_len was already set and base allocated in a parent function */
|
256
|
+
assert(iov->iov_len == OPLEN && iov->iov_base && "iov invalid");
|
257
|
+
op = iov->iov_base;
|
258
|
+
op->code = INET_DIAG_BC_S_COND;
|
259
|
+
op->yes = OPLEN;
|
260
|
+
op->no = sizeof(struct inet_diag_bc_op) + OPLEN;
|
261
|
+
|
262
|
+
cond = (struct inet_diag_hostcond *)(op + 1);
|
263
|
+
cond->family = AF_INET;
|
264
|
+
cond->port = ntohs(inet->port);
|
265
|
+
cond->prefix_len = inet->addr == 0 ? 0 : sizeof(in_addr_t) * CHAR_BIT;
|
266
|
+
*cond->addr = inet->addr;
|
267
|
+
}
|
268
|
+
|
269
|
+
static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
|
270
|
+
{
|
271
|
+
const char *err;
|
272
|
+
VALUE verr;
|
273
|
+
|
274
|
+
parse_addr(&args->addrs, addr);
|
275
|
+
gen_bytecode(&args->iov[2], &args->addrs);
|
276
|
+
|
277
|
+
verr = rb_thread_blocking_region(diag, args, RUBY_UBF_IO, 0);
|
278
|
+
err = (const char *)verr;
|
279
|
+
if (err) {
|
280
|
+
if (err == err_nlmsg)
|
281
|
+
rb_raise(rb_eRuntimeError, "NLMSG_ERROR");
|
282
|
+
else
|
283
|
+
rb_sys_fail(err);
|
284
|
+
}
|
285
|
+
|
286
|
+
return rb_listen_stats(&args->stats);
|
287
|
+
}
|
288
|
+
|
289
|
+
/*
|
290
|
+
* call-seq:
|
291
|
+
* addrs = %w(0.0.0.0:80 127.0.0.1:8080)
|
292
|
+
* Raindrops::Linux.tcp_listener_stats(addrs) => hash
|
293
|
+
*
|
294
|
+
* Takes an array of strings representing listen addresses to filter for.
|
295
|
+
* Returns a hash with given addresses as keys and ListenStats
|
296
|
+
* objects as the values.
|
297
|
+
*/
|
298
|
+
static VALUE tcp_listener_stats(VALUE obj, VALUE addrs)
|
299
|
+
{
|
300
|
+
VALUE *ary;
|
301
|
+
long i;
|
302
|
+
VALUE rv;
|
303
|
+
struct nogvl_args args;
|
304
|
+
|
305
|
+
/*
|
306
|
+
* allocating page_size instead of OP_LEN since we'll reuse the
|
307
|
+
* buffer for recvmsg() later, we already checked for
|
308
|
+
* OPLEN <= page_size at initialization
|
309
|
+
*/
|
310
|
+
args.iov[2].iov_len = OPLEN;
|
311
|
+
args.iov[2].iov_base = alloca(page_size);
|
312
|
+
|
313
|
+
if (TYPE(addrs) != T_ARRAY)
|
314
|
+
rb_raise(rb_eArgError, "addrs must be an Array or String");
|
315
|
+
|
316
|
+
rv = rb_hash_new();
|
317
|
+
ary = RARRAY_PTR(addrs);
|
318
|
+
for (i = RARRAY_LEN(addrs); --i >= 0; ary++)
|
319
|
+
rb_hash_aset(rv, *ary, tcp_stats(&args, *ary));
|
320
|
+
|
321
|
+
return rv;
|
322
|
+
}
|
323
|
+
|
324
|
+
void Init_raindrops_linux_inet_diag(void)
|
325
|
+
{
|
326
|
+
VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
|
327
|
+
VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
|
328
|
+
|
329
|
+
cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));
|
330
|
+
|
331
|
+
rb_define_module_function(mLinux, "tcp_listener_stats",
|
332
|
+
tcp_listener_stats, 1);
|
333
|
+
|
334
|
+
#ifndef HAVE_RB_STRUCT_ALLOC_NOINIT
|
335
|
+
id_new = rb_intern("new");
|
336
|
+
#endif
|
337
|
+
rb_require("raindrops/linux");
|
338
|
+
|
339
|
+
page_size = getpagesize();
|
340
|
+
|
341
|
+
assert(OPLEN <= page_size && "bytecode OPLEN is no <= PAGE_SIZE");
|
342
|
+
}
|