Class: Nl::Exchange

Inherits:
Object
  • Object
show all
Defined in:
nl/lib/nl/exchange.rb

Overview

State machine for one Netlink request/reply exchange.

Dump exchanges are known to be multipart from the request and remain open through NLMSG_DONE. Some Linux Generic Netlink dump handlers construct replies with genlmsg_iput(), which leaves NLM_F_MULTI unset despite that multipart lifetime. Dump mode therefore accepts data with or without the flag; reply mode still uses it to detect a multipart do response.

Some operations also return multipart data for a do request, although YNL cannot declare such a reply. Reply mode handles this kernel behavior by retaining the first data message, draining the remainder, and completing on NLMSG_DONE instead of ACK.

Defined Under Namespace

Classes: Complete, Failure, Item

Constant Summary collapse

COMPLETE =
Complete.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mode:) ⇒ Exchange

Returns a new instance of Exchange.

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'nl/lib/nl/exchange.rb', line 29

def initialize(mode:)
  raise ArgumentError, "unknown exchange mode: #{mode.inspect}" unless MODES.include?(mode)

  @mode = mode
  @reply = nil
  # Receive states:
  # - :dump accepts and emits every data message, then completes on DONE.
  # - :no_reply rejects data and completes on ACK.
  # - :initial waits for the first data message of a reply operation.
  # - :single retains that message and completes once ACK is received.
  # - :multi retains the first message, drains the rest, and
  #   completes on DONE. Every data message must carry NLM_F_MULTI.
  @state = mode == :reply ? :initial : mode
  @acked = false
  @cancelled = false
  @complete = false
  @result = nil
  @mutex = Mutex.new
end

Instance Attribute Details

#mode ⇒ Object (readonly)

Returns the value of attribute mode.



26
27
28
# File 'nl/lib/nl/exchange.rb', line 26

def mode
  @mode
end

Instance Method Details

#accept(frame) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'nl/lib/nl/exchange.rb', line 49

def accept(frame)
  @mutex.synchronize do
    return if @complete

    case frame
    when Raw::UnknownFrame
      nil
    when Raw::ErrorFrame
      @cancelled ? complete(nil) : fail_with(SystemCallError.new(frame.errno))
    when Raw::DoneFrame
      if frame.errno && !@cancelled
        fail_with(SystemCallError.new(frame.errno))
      else
        complete(@reply)
      end
    when Raw::AckFrame
      accept_ack
    when Raw::DataFrame
      accept_reply(frame)
    else
      raise ArgumentError, "unexpected exchange input: #{frame.inspect}"
    end
  end
end

#cancel ⇒ Object



74
75
76
77
# File 'nl/lib/nl/exchange.rb', line 74

def cancel
  @mutex.synchronize { @cancelled = true unless @complete }
  nil
end

#cancelled? ⇒ Boolean

Returns:

  • (Boolean)


79
# File 'nl/lib/nl/exchange.rb', line 79

def cancelled? = @mutex.synchronize { @cancelled }

#complete? ⇒ Boolean

Returns:

  • (Boolean)


80
# File 'nl/lib/nl/exchange.rb', line 80

def complete? = @mutex.synchronize { @complete }

#result ⇒ Object



81
# File 'nl/lib/nl/exchange.rb', line 81

def result = @mutex.synchronize { @result }