README: update with extra disclaimer
[raindrops.git] / lib / raindrops / linux.rb
bloba76192c28d885d62ef6e34c0a42cfb86d06e87a4
1 # -*- encoding: binary -*-
2 # frozen_string_literal: false
4 # For reporting TCP ListenStats, users of older \Linux kernels need to ensure
5 # that the the "inet_diag" and "tcp_diag" kernel modules are loaded as they do
6 # not autoload correctly.  The inet_diag facilities of \Raindrops is useful
7 # for periodic snapshot reporting of listen queue sizes.
9 # Instead of snapshotting, Raindrops::Aggregate::LastDataRecv may be used
10 # to aggregate statistics from +all+ accepted sockets as they arrive
11 # based on the +last_data_recv+ field in Raindrops::TCP_Info
13 module Raindrops::Linux
15   # The standard proc path for active UNIX domain sockets, feel free to call
16   # String#replace on this if your /proc is mounted in a non-standard location
17   # for whatever reason
18   PROC_NET_UNIX_ARGS = [ '/proc/net/unix', { encoding: "binary" }]
20   # Get ListenStats from an array of +paths+
21   #
22   # Socket state mapping from integer => symbol, based on socket_state
23   # enum from include/linux/net.h in the \Linux kernel:
24   #     typedef enum {
25   #             SS_FREE = 0,              /* not allocated                */
26   #             SS_UNCONNECTED,           /* unconnected to any socket    */
27   #             SS_CONNECTING,            /* in process of connecting     */
28   #             SS_CONNECTED,             /* connected to socket          */
29   #             SS_DISCONNECTING          /* in process of disconnecting  */
30   #     } socket_state;
31   # * SS_CONNECTING maps to ListenStats#queued
32   # * SS_CONNECTED maps to ListenStats#active
33   #
34   # This method may be significantly slower than its tcp_listener_stats
35   # counterpart due to the latter being able to use inet_diag via netlink.
36   # This parses /proc/net/unix as there is no other (known) way
37   # to expose Unix domain socket statistics over netlink.
38   def unix_listener_stats(paths = nil)
39     rv = Hash.new { |h,k| h[k.freeze] = Raindrops::ListenStats.new(0, 0) }
40     if nil == paths
41       paths = [ '[^\n]+' ]
42     else
43       paths = paths.map do |path|
44         path = path.dup
45         path.force_encoding(Encoding::BINARY)
46         if File.symlink?(path)
47           link = path
48           path = File.readlink(link)
49           path.force_encoding(Encoding::BINARY)
50           rv[link] = rv[path] # vivify ListenerStats
51         else
52           rv[path] # vivify ListenerStats
53         end
54         Regexp.escape(path)
55       end
56     end
57     paths = /^\w+: \d+ \d+ (\d+) \d+ (\d+)\s+\d+ (#{paths.join('|')})$/n
59     # no point in pread since we can't stat for size on this file
60     File.read(PROC_NET_UNIX_ARGS[0], encoding: 'binary').scan(paths) do |s|
61       path = s[-1]
62       case s[0]
63       when "00000000" # client sockets
64         case s[1].to_i
65         when 2 then rv[path].queued += 1
66         when 3 then rv[path].active += 1
67         end
68       else
69         # listeners, vivify empty stats
70         rv[path]
71       end
72     end
74     rv
75   end
76   module_function :unix_listener_stats
78 end # Raindrops::Linux