raindrops 0.17.0 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b07afc95a2e2b6d5f116be36ceab5bfe45b58bef
4
- data.tar.gz: 5c8e436802a87b95de68fda59b5e0947d58a4ad5
2
+ SHA256:
3
+ metadata.gz: 0c5318918bfd5808ecc840404c67ed59f80019c6a705ef434b10ae92da78ba06
4
+ data.tar.gz: 1f25061c601aab07e3eb041cc9c2fd005438a1527994e0696b5eafc8dc148650
5
5
  SHA512:
6
- metadata.gz: fd1415d86ff4e2a641eec851906498b551318d0a66cdc52d8cfafe5d46826ede496c0e44b9b6a768ea95db0537f03b5387ce4fda09a8e97af2a8f9ab7ce741d0
7
- data.tar.gz: f0960929656a511b43df2340bf07cbd072cd2fabbe8858c35112450bc790c6cfa406114329b93dd954a6d7c9e6f3ebee75a703bd6cf29f8198b0342fb9c292fd
6
+ metadata.gz: df8be08761d8444a8599da3a5885efc806df935820753f5b000f27ab408171438b0f6ff4650810b5096e648904f5993bbc2a1303b9571df213201919b4b7f565
7
+ data.tar.gz: 250b7236881f06fdd684b22f4844cacb7a0d2ca43703220fe3f0f4ebc49e51faf2015864ef6776edc0d66c7dfd2762e08145548a5dd06b55c2193e989f21a3fd
data/.document CHANGED
@@ -4,4 +4,4 @@ NEWS
4
4
  lib
5
5
  ext/raindrops/raindrops.c
6
6
  ext/raindrops/linux_inet_diag.c
7
- ext/raindrops/linux_tcp_info.c
7
+ ext/raindrops/tcp_info.c
data/.olddoc.yml CHANGED
@@ -1,15 +1,13 @@
1
1
  ---
2
- cgit_url: https://bogomips.org/raindrops.git
3
- git_url: git://bogomips.org/raindrops.git
4
- rdoc_url: https://bogomips.org/raindrops/
5
- public_email: raindrops-public@bogomips.org
6
- private_email: raindrops@bogomips.org
2
+ cgit_url: https://yhbt.net/raindrops.git/
3
+ rdoc_url: https://yhbt.net/raindrops/
4
+ public_email: raindrops-public@yhbt.net
7
5
  ml_url:
8
- - https://bogomips.org/raindrops-public/
6
+ - https://yhbt.net/raindrops-public/
9
7
  - http://ou63pmih66umazou.onion/raindrops-public
10
8
  nntp_url:
11
9
  - nntp://news.public-inbox.org/inbox.comp.lang.ruby.raindrops
12
10
  - nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.raindrops
13
11
  source_code:
14
- - git clone git://bogomips.org/raindrops.git
15
- - git clone https://bogomips.org/raindrops.git
12
+ - git clone https://yhbt.net/raindrops.git
13
+ - torsocks git clone http://ou63pmih66umazou.onion/raindrops.git
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.17.0
4
+ DEF_VER=v0.19.1
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -1,4 +1,4 @@
1
1
  all::
2
- RSYNC_DEST := bogomips.org:/srv/bogomips/raindrops
2
+ RSYNC_DEST := yhbt.net:/srv/yhbt/raindrops
3
3
  rfpackage := raindrops
4
4
  include pkg.mk
data/README CHANGED
@@ -58,20 +58,21 @@ If you use RubyGems:
58
58
 
59
59
  See Raindrops::Middleware and Raindrops::LastDataRecv documentation for
60
60
  use Rack servers. The entire library is fully-documented and we are
61
- responsive on the mailing list (mailto:raindrops-public@bogomips.org) if
61
+ responsive on the publically archived mailing list
62
+ (mailto:raindrops-public@yhbt.net) if
62
63
  you have any questions or comments.
63
64
 
64
65
  == Development
65
66
 
66
67
  You can get the latest source via git from the following locations:
67
68
 
68
- git://bogomips.org/raindrops.git
69
+ git://yhbt.net/raindrops.git
69
70
  git://repo.or.cz/raindrops.git (mirror)
70
71
 
71
72
  You may browse the code from the web and download the latest snapshot
72
73
  tarballs here:
73
74
 
74
- * https://bogomips.org/raindrops.git
75
+ * https://yhbt.net/raindrops.git
75
76
  * http://repo.or.cz/w/raindrops.git (gitweb)
76
77
 
77
78
  Inline patches (from "git format-patch") to the mailing list are
@@ -88,9 +89,14 @@ raindrops is licensed under the LGPL-2.1+
88
89
  == Contact
89
90
 
90
91
  All feedback (bug reports, user/development discussion, patches, pull
91
- requests) go to the mailing list: mailto:raindrops-public@bogomips.org
92
+ requests) go to the publically archived mailing list:
93
+ mailto:raindrops-public@yhbt.net
92
94
 
93
95
  Mailing list archives are available over HTTPS and NNTP:
94
96
 
95
- * https://bogomips.org/raindrops-public/
97
+ * https://yhbt.net/raindrops-public/
98
+ * http://ou63pmih66umazou.onion/raindrops-public/
96
99
  * nntp://news.public-inbox.org/inbox.comp.lang.ruby.raindrops
100
+
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
@@ -1,2 +1,3 @@
1
+ * fix IPv6 inet_diag reporting for Raindrops::Middleware
1
2
  * pure Ruby version for non-forking servers (patches welcome!)
2
3
  * unix_diag and udp_diag support
@@ -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') { all = true }
27
+ opts.on('-a', '--all') { } # noop
29
28
  opts.parse! ARGV
30
29
  end
31
30
 
@@ -1,5 +1,5 @@
1
1
  # This is a snippet of the config that powers
2
- # https://raindrops-demo.bogomips.org/
2
+ # https://yhbt.net/raindrops-demo/
3
3
  # This may be used with the packaged zbatery.conf.rb
4
4
  #
5
5
  # zbatery -c zbatery.conf.ru watcher_demo.ru -E none
@@ -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,10 +7,112 @@
6
7
 
7
8
  $CPPFLAGS += " -D_GNU_SOURCE "
8
9
  have_func('mremap', 'sys/mman.h')
9
- have_header('linux/tcp.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
10
18
 
11
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
+
12
114
  have_func("getpagesize", "unistd.h")
115
+ have_func('rb_thread_call_without_gvl')
13
116
  have_func('rb_thread_blocking_region')
14
117
  have_func('rb_thread_io_blocking_region')
15
118
 
@@ -40,7 +143,7 @@
40
143
  $defs.push(format("-DHAVE_GCC_ATOMIC_BUILTINS"))
41
144
  true
42
145
  else
43
- prev_cflags = $CFLAGS
146
+ $CFLAGS = prev_cflags
44
147
  false
45
148
  end
46
149
  end
@@ -53,4 +156,5 @@
53
156
 
54
157
  apt-get install libatomic-ops-dev
55
158
  SRC
159
+ create_header # generate extconf.h to avoid excessively long command-line
56
160
  create_makefile('raindrops_ext')
@@ -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>
@@ -227,86 +205,76 @@ static void bug_warn_nogvl(const char *fmt, ...)
227
205
  va_end(ap);
228
206
 
229
207
  fprintf(stderr, "Please report how you produced this at "\
230
- "raindrops-public@bogomips.org\n");
208
+ "raindrops-public@yhbt.net\n");
231
209
  fflush(stderr);
232
210
  }
233
211
 
234
212
  static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
235
213
  {
236
- char *key, *port, *old_key;
214
+ char *host, *key, *port, *old_key;
237
215
  size_t alloca_len;
238
216
  struct listen_stats *stats;
239
- socklen_t keylen;
217
+ socklen_t hostlen;
240
218
  socklen_t portlen = (socklen_t)sizeof("65535");
241
- union any_addr sa;
242
- socklen_t len = sizeof(struct sockaddr_storage);
243
- int rc;
244
- int flags = NI_NUMERICHOST | NI_NUMERICSERV;
219
+ int n;
220
+ const void *src = r->id.idiag_src;
245
221
 
246
- switch ((sa.ss.ss_family = r->idiag_family)) {
222
+ switch (r->idiag_family) {
247
223
  case AF_INET: {
248
- sa.in.sin_port = r->id.idiag_sport;
249
- sa.in.sin_addr.s_addr = r->id.idiag_src[0];
250
- keylen = INET_ADDRSTRLEN;
251
- alloca_len = keylen + 1 + portlen;
252
- key = alloca(alloca_len);
253
- key[keylen] = 0; /* will be ':' later */
254
- port = key + keylen + 1;
255
- rc = getnameinfo(&sa.sa, len,
256
- key, keylen, port, portlen, flags);
224
+ hostlen = INET_ADDRSTRLEN;
225
+ alloca_len = hostlen + portlen;
226
+ host = key = alloca(alloca_len);
257
227
  break;
258
228
  }
259
229
  case AF_INET6: {
260
- sa.in6.sin6_port = r->id.idiag_sport;
261
- memcpy(&sa.in6.sin6_addr, &r->id.idiag_src, sizeof(__be32[4]));
262
- keylen = INET6_ADDRSTRLEN;
263
- /* [ ] */
264
- alloca_len = 1 + keylen + 1 + 1 + portlen;
230
+ hostlen = INET6_ADDRSTRLEN;
231
+ alloca_len = 1 + hostlen + 1 + portlen;
265
232
  key = alloca(alloca_len);
266
- *key = '[';
267
- key[1 + keylen + 1] = 0; /* will be ':' later */
268
- port = 1 + key + keylen + 1 + 1;
269
- rc = getnameinfo(&sa.sa, len,
270
- key + 1, keylen, port, portlen, flags);
233
+ host = key + 1;
271
234
  break;
272
235
  }
273
236
  default:
274
237
  assert(0 && "unsupported address family, could that be IPv7?!");
275
238
  }
276
- if (rc != 0) {
277
- bug_warn_nogvl("BUG: getnameinfo: %s\n", gai_strerror(rc));
278
- *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';
279
243
  }
280
-
281
- keylen = (socklen_t)strlen(key);
282
- portlen = (socklen_t)strlen(port);
283
-
284
- switch (sa.ss.ss_family) {
244
+ hostlen = (socklen_t)strlen(host);
245
+ switch (r->idiag_family) {
285
246
  case AF_INET:
286
- key[keylen] = ':';
287
- memmove(key + keylen + 1, port, portlen + 1);
247
+ host[hostlen] = ':';
248
+ port = host + hostlen + 1;
288
249
  break;
289
250
  case AF_INET6:
290
- key[keylen] = ']';
291
- key[keylen + 1] = ':';
292
- memmove(key + keylen + 2, port, portlen + 1);
293
- keylen++;
251
+ key[0] = '[';
252
+ host[hostlen] = ']';
253
+ host[hostlen + 1] = ':';
254
+ port = host + hostlen + 2;
294
255
  break;
295
256
  default:
296
257
  assert(0 && "unsupported address family, could that be IPv7?!");
297
258
  }
298
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
+
299
266
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
300
267
  return stats;
301
268
 
302
269
  old_key = key;
303
270
 
304
271
  if (r->idiag_state == TCP_ESTABLISHED) {
305
- int n = snprintf(key, alloca_len, "%s:%u",
306
- addr_any(sa.ss.ss_family),
272
+ n = snprintf(key, alloca_len, "%s:%u",
273
+ addr_any(r->idiag_family),
307
274
  ntohs(r->id.idiag_sport));
308
275
  if (n <= 0) {
309
276
  bug_warn_nogvl("BUG: snprintf: %d\n", n);
277
+ *key = '\0';
310
278
  }
311
279
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
312
280
  return stats;
@@ -319,8 +287,9 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
319
287
  memcpy(key, old_key, n + 1);
320
288
  }
321
289
  } else {
322
- key = xmalloc(keylen + 1 + portlen + 1);
323
- 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);
324
293
  }
325
294
  stats = xcalloc(1, sizeof(struct listen_stats));
326
295
  st_insert(table, (st_data_t)key, (st_data_t)stats);
@@ -617,7 +586,7 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
617
586
  gen_bytecode(&args->iov[2], &query_addr);
618
587
 
619
588
  memset(&args->stats, 0, sizeof(struct listen_stats));
620
- nl_errcheck(rb_thread_io_blocking_region(diag, args, args->fd));
589
+ nl_errcheck(rd_fd_region(diag, args, args->fd));
621
590
 
622
591
  return rb_listen_stats(&args->stats);
623
592
  }
@@ -694,7 +663,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
694
663
  "addr must be an array of strings, a string, or nil");
695
664
  }
696
665
 
697
- nl_errcheck(rb_thread_io_blocking_region(diag, &args, args.fd));
666
+ nl_errcheck(rd_fd_region(diag, &args, args.fd));
698
667
 
699
668
  st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
700
669
  st_free_table(args.table);
@@ -709,11 +678,12 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
709
678
 
710
679
  void Init_raindrops_linux_inet_diag(void)
711
680
  {
712
- VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
681
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
713
682
  VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
683
+ VALUE Socket;
714
684
 
715
685
  rb_require("socket");
716
- cIDSock = rb_const_get(rb_cObject, rb_intern("Socket"));
686
+ Socket = rb_const_get(rb_cObject, rb_intern("Socket"));
717
687
  id_new = rb_intern("new");
718
688
 
719
689
  /*
@@ -722,7 +692,7 @@ void Init_raindrops_linux_inet_diag(void)
722
692
  * This is a subclass of +Socket+ specifically for talking
723
693
  * to the inet_diag facility of Netlink.
724
694
  */
725
- cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", cIDSock);
695
+ cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", Socket);
726
696
  rb_define_singleton_method(cIDSock, "new", ids_s_new, 0);
727
697
 
728
698
  cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));