module Raindrops::Linux - raindrops-0.19.0 Documentation

# module Raindrops::Linux

For reporting TCP ListenStats, users of older Linux kernels need to ensure that the the “inet_diag” and “tcp_diag” kernel modules are loaded as they do not autoload correctly. The inet_diag facilities of Raindrops is useful for periodic snapshot reporting of listen queue sizes.

Instead of snapshotting, Raindrops::Aggregate::LastDataRecv may be used to aggregate statistics from `all` accepted sockets as they arrive based on the `last_data_recv` field in Raindrops::TCP_Info

### Constants

PROC_NET_UNIX_ARGS

  The standard proc path for active UNIX domain sockets, feel free to call String#replace on this if your /proc is mounted in a non-standard location for whatever reason

### Public Class Methods

Raindrops::Linux.tcp_listener_stats([addrs[, sock]]) → hash click to toggle source

If specified, `addr` may be a string or array of strings representing listen addresses to filter for. Returns a hash with given addresses as keys and ListenStats objects as the values or a hash of all addresses.

    addrs = %w(0.0.0.0:80 127.0.0.1:8080)

If `addr` is nil or not specified, all (IPv4) addresses are returned. If `sock` is specified, it should be a Raindrops::InetDiagSock object.

    static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
    {
            VALUE rv = rb_hash_new();
            struct nogvl_args args;
            VALUE addrs, sock;

            rb_scan_args(argc, argv, "02", &addrs, &sock);

            /*
             * allocating page_size instead of OP_LEN since we'll reuse the
             * buffer for recvmsg() later, we already checked for
             * OPLEN <= page_size at initialization
             */
            args.iov[2].iov_len = OPLEN;
            args.iov[2].iov_base = alloca(page_size);
            args.table = NULL;
            if (NIL_P(sock))
                    sock = rb_funcall(cIDSock, id_new, 0);
            args.fd = my_fileno(sock);

            switch (TYPE(addrs)) {
            case T_STRING:
                    rb_hash_aset(rv, addrs, tcp_stats(&args, addrs));
                    return rv;
            case T_ARRAY: {
                    long i;
                    long len = RARRAY_LEN(addrs);

                    if (len == 1) {
                            VALUE cur = rb_ary_entry(addrs, 0);

                            rb_hash_aset(rv, cur, tcp_stats(&args, cur));
                            return rv;
                    }
                    for (i = 0; i < len; i++) {
                            union any_addr check;
                            VALUE cur = rb_ary_entry(addrs, i);

                            parse_addr(&check, cur);
                            rb_hash_aset(rv, cur, Qtrue /* placeholder */);
                    }
                    /* fall through */
            }
            case T_NIL:
                    args.table = st_init_strtable();
                    gen_bytecode_all(&args.iov[2]);
                    break;
            default:
                    rb_raise(rb_eArgError,
                             "addr must be an array of strings, a string, or nil");
            }

            nl_errcheck(rd_fd_region(diag, &args, args.fd));

            st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
            st_free_table(args.table);

            if (RHASH_SIZE(rv) > 1)
                    rb_hash_foreach(rv, drop_placeholders, Qfalse);

            /* let GC deal with corner cases */
            if (argc < 2) rb_io_close(sock);
            return rv;
    }

unix_listener_stats(paths = nil) click to toggle source

Get ListenStats from an array of `paths`

Socket state mapping from integer => symbol, based on socket_state enum from include/linux/net.h in the Linux kernel:

      typedef enum {
              SS_FREE = 0,                 not allocated
              SS_UNCONNECTED,              unconnected to any socket
              SS_CONNECTING,            /* in process of connecting     */
              SS_CONNECTED,             /* connected to socket          */
              SS_DISCONNECTING          /* in process of disconnecting  */
      } socket_state;
    SS_CONNECTING maps to ListenStats#queued
    SS_CONNECTED maps to ListenStats#active

This method may be significantly slower than its ::tcp_listener_stats counterpart due to the latter being able to use inet_diag via netlink. This parses /proc/net/unix as there is no other (known) way to expose Unix domain socket statistics over netlink.

    # File lib/raindrops/linux.rb, line 37
    def unix_listener_stats(paths = nil)
      rv = Hash.new { |h,k| h[k.freeze] = Raindrops::ListenStats.new(0, 0) }
      if nil == paths
        paths = [ '[^\n]+' ]
      else
        paths = paths.map do |path|
          path = path.dup
          path.force_encoding(Encoding::BINARY) if defined?(Encoding)
          if File.symlink?(path)
            link = path
            path = File.readlink(link)
            path.force_encoding(Encoding::BINARY) if defined?(Encoding)
            rv[link] = rv[path] # vivify ListenerStats
          else
            rv[path] # vivify ListenerStats
          end
          Regexp.escape(path)
        end
      end
      paths = /^\w+: \d+ \d+ (\d+) \d+ (\d+)\s+\d+ (#{paths.join('|')})$/n

      # no point in pread since we can't stat for size on this file
      File.read(*PROC_NET_UNIX_ARGS).scan(paths) do |s|
        path = s[-1]
        case s[0]
        when "00000000" # client sockets
          case s[1].to_i
          when 2 then rv[path].queued += 1
          when 3 then rv[path].active += 1
          end
        else
          # listeners, vivify empty stats
          rv[path]
        end
      end

      rv
    end

### Private Instance Methods

unix_listener_stats(paths = nil) click to toggle source

Get ListenStats from an array of `paths`

Socket state mapping from integer => symbol, based on socket_state enum from include/linux/net.h in the Linux kernel:

      typedef enum {
              SS_FREE = 0,                 not allocated
              SS_UNCONNECTED,              unconnected to any socket
              SS_CONNECTING,            /* in process of connecting     */
              SS_CONNECTED,             /* connected to socket          */
              SS_DISCONNECTING          /* in process of disconnecting  */
      } socket_state;
    SS_CONNECTING maps to ListenStats#queued
    SS_CONNECTED maps to ListenStats#active

This method may be significantly slower than its ::tcp_listener_stats counterpart due to the latter being able to use inet_diag via netlink. This parses /proc/net/unix as there is no other (known) way to expose Unix domain socket statistics over netlink.

    # File lib/raindrops/linux.rb, line 37
    def unix_listener_stats(paths = nil)
      rv = Hash.new { |h,k| h[k.freeze] = Raindrops::ListenStats.new(0, 0) }
      if nil == paths
        paths = [ '[^\n]+' ]
      else
        paths = paths.map do |path|
          path = path.dup
          path.force_encoding(Encoding::BINARY) if defined?(Encoding)
          if File.symlink?(path)
            link = path
            path = File.readlink(link)
            path.force_encoding(Encoding::BINARY) if defined?(Encoding)
            rv[link] = rv[path] # vivify ListenerStats
          else
            rv[path] # vivify ListenerStats
          end
          Regexp.escape(path)
        end
      end
      paths = /^\w+: \d+ \d+ (\d+) \d+ (\d+)\s+\d+ (#{paths.join('|')})$/n

      # no point in pread since we can't stat for size on this file
      File.read(*PROC_NET_UNIX_ARGS).scan(paths) do |s|
        path = s[-1]
        case s[0]
        when "00000000" # client sockets
          case s[1].to_i
          when 2 then rv[path].queued += 1
          when 3 then rv[path].active += 1
          end
        else
          # listeners, vivify empty stats
          rv[path]
        end
      end

      rv
    end