nl 0.3.0 → 0.4.2

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Rakefile +7 -0
  4. data/lib/nl/async/dispatcher.rb +16 -16
  5. data/lib/nl/async/driver.rb +0 -2
  6. data/lib/nl/async/mailbox.rb +0 -2
  7. data/lib/nl/async/operation.rb +0 -2
  8. data/lib/nl/async.rb +0 -2
  9. data/lib/nl/attribute_set.rb +173 -0
  10. data/lib/nl/bitfield32.rb +86 -0
  11. data/lib/nl/blocking_transport.rb +12 -12
  12. data/lib/nl/connection.rb +7 -9
  13. data/lib/nl/datagram.rb +7 -9
  14. data/lib/nl/datatypes.rb +454 -0
  15. data/lib/nl/decoder.rb +4 -0
  16. data/lib/nl/endian.rb +9 -0
  17. data/lib/nl/exchange.rb +40 -18
  18. data/lib/nl/family.rb +50 -55
  19. data/lib/nl/genl/client.rb +105 -0
  20. data/lib/nl/genl/protocol.rb +59 -0
  21. data/lib/nl/genl/wire.rb +95 -0
  22. data/lib/nl/genl.rb +60 -62
  23. data/lib/nl/notification.rb +3 -6
  24. data/lib/nl/notification_router.rb +30 -23
  25. data/lib/nl/raw/client.rb +83 -0
  26. data/lib/nl/raw/protocol.rb +112 -0
  27. data/lib/nl/raw/wire.rb +147 -0
  28. data/lib/nl/raw.rb +104 -0
  29. data/lib/nl/sequence_allocator.rb +0 -2
  30. data/lib/nl/socket.rb +21 -5
  31. data/lib/nl/structured_payload.rb +61 -0
  32. data/lib/nl/sub_message.rb +94 -0
  33. data/lib/nl/version.rb +1 -1
  34. data/lib/nl.rb +5 -6
  35. data/sig/generated/nl/async/dispatcher.rbs +53 -0
  36. data/sig/generated/nl/async/driver.rbs +21 -0
  37. data/sig/generated/nl/async/mailbox.rbs +32 -0
  38. data/sig/generated/nl/async/operation.rbs +123 -0
  39. data/sig/generated/nl/async.rbs +9 -0
  40. data/sig/generated/nl/attribute_set.rbs +50 -0
  41. data/sig/generated/nl/bitfield32.rbs +41 -0
  42. data/sig/generated/nl/blocking_transport.rbs +33 -0
  43. data/sig/generated/nl/connection.rbs +26 -0
  44. data/sig/generated/nl/datagram.rbs +11 -0
  45. data/sig/generated/nl/datatypes.rbs +181 -0
  46. data/sig/generated/nl/decoder.rbs +38 -0
  47. data/sig/generated/nl/encoder.rbs +29 -0
  48. data/sig/generated/nl/endian.rbs +29 -0
  49. data/sig/generated/nl/error.rbs +27 -0
  50. data/sig/generated/nl/exchange.rbs +75 -0
  51. data/sig/generated/nl/family.rbs +89 -0
  52. data/sig/generated/nl/genl/client.rbs +74 -0
  53. data/sig/generated/nl/genl/protocol.rbs +37 -0
  54. data/sig/generated/nl/genl/wire.rbs +113 -0
  55. data/sig/generated/nl/genl.rbs +50 -0
  56. data/sig/generated/nl/notification.rbs +53 -0
  57. data/sig/generated/nl/notification_router.rbs +30 -0
  58. data/sig/generated/nl/raw/client.rbs +49 -0
  59. data/sig/generated/nl/raw/protocol.rbs +122 -0
  60. data/sig/generated/nl/raw/wire.rbs +151 -0
  61. data/sig/generated/nl/raw.rbs +70 -0
  62. data/sig/generated/nl/sequence_allocator.rbs +15 -0
  63. data/sig/generated/nl/socket.rbs +59 -0
  64. data/sig/generated/nl/structured_payload.rbs +25 -0
  65. data/sig/generated/nl/sub_message.rbs +74 -0
  66. data/sig/generated/nl/version.rbs +5 -0
  67. data/sig/generated/nl.rbs +2 -0
  68. metadata +48 -7
  69. data/lib/nl/core.rb +0 -94
  70. data/lib/nl/genl/connection.rb +0 -89
  71. data/lib/nl/protocols/genl.rb +0 -74
  72. data/lib/nl/protocols/raw.rb +0 -460
data/lib/nl/family.rb CHANGED
@@ -1,5 +1,3 @@
1
- #--
2
- # rbs_inline: enabled
3
1
  require_relative 'connection'
4
2
  require_relative 'notification'
5
3
 
@@ -9,14 +7,14 @@ module Nl
9
7
  #
10
8
  # interface _Connection
11
9
  # def exchange: (
12
- # Protocols::Raw protocol,
10
+ # Raw::Endpoint endpoint,
13
11
  # Symbol kind,
14
12
  # Class request_class,
15
13
  # Class reply_class,
16
14
  # Hash[Symbol, untyped] args
17
15
  # ) ?{ (untyped) -> void } -> untyped
18
16
  # def exchange_async: (
19
- # Protocols::Raw protocol,
17
+ # Raw::Endpoint endpoint,
20
18
  # Symbol kind,
21
19
  # Class request_class,
22
20
  # Class reply_class,
@@ -24,97 +22,93 @@ module Nl
24
22
  # ?stream_capacity: Integer?
25
23
  # ) -> (Async::Future[untyped] | Async::Stream[untyped])
26
24
  # def async_capable?: () -> bool
27
- # def register_notifications: (Protocols::Raw, Hash[Integer, Class]) -> NotificationChannel
25
+ # def register_notifications: (Raw::Endpoint, Hash[Integer, Class]) -> NotificationChannel
28
26
  # def add_memberships: (Array[Integer]) -> nil
29
27
  # def drop_memberships: (Array[Integer]) -> nil
30
- # def receive_notification: (Protocols::Raw, ?timeout: Numeric?) -> untyped
28
+ # def receive_notification: (Raw::Endpoint, ?timeout: Numeric?) -> untyped
31
29
  # def close: () -> nil
32
30
  # end
31
+ #
32
+ # interface _FamilyClass[out F]
33
+ # def new: (_Connection, endpoint: Raw::Endpoint) -> F
34
+ # end
33
35
 
34
36
  class Family
35
- DEFAULT_NOTIFICATION_CAPACITY = Connection::DEFAULT_NOTIFICATION_CAPACITY
37
+ DEFAULT_NOTIFICATION_CAPACITY = Connection::DEFAULT_NOTIFICATION_CAPACITY #: Integer
36
38
 
39
+ # Socket-owning family.
40
+ #
41
+ # {Family} instances that owns sockets are extended with this module.
37
42
  module Session
38
- def close #: nil
43
+ # Closes the Netlink socket owned by this {Family}.
44
+ # @rbs () -> void
45
+ def close
39
46
  @connection.close
40
47
  end
41
48
  end
42
49
 
43
- #--
44
- # @rbs connection: _Connection
45
- # @rbs protocol: Protocol
46
- # @rbs return: instance
47
- def initialize(connection, protocol: self.class::PROTOCOL)
48
- @protocol = protocol
50
+ # @rbs (_Connection connection, endpoint: Raw::Endpoint) -> instance
51
+ def initialize(connection, endpoint:)
52
+ @endpoint = endpoint
49
53
  @connection = connection
50
- @connection.register_notifications(@protocol, notification_classes)
54
+ @connection.register_notifications(@endpoint, notification_classes)
51
55
  @notification_stream = NotificationStream.new do |timeout|
52
- @connection.receive_notification(@protocol, timeout:)
53
- end
54
- end
55
-
56
- #--
57
- # @rbs (?executor: executor?, ?notification_capacity: Integer?) -> (Session & instance)
58
- # | [R] (?executor: executor?, ?notification_capacity: Integer?) { (instance) -> R } -> R
59
- def self.open(executor: nil, notification_capacity: DEFAULT_NOTIFICATION_CAPACITY)
60
- session = build_session(executor:, notification_capacity:)
61
- return session unless block_given?
62
-
63
- begin
64
- yield session
65
- ensure
66
- session.close
67
- end
68
- end
69
-
70
- class << self
71
- # @rbs (?executor: executor?, notification_capacity: Integer) -> (Session & instance)
72
- private def build_session(executor: nil, notification_capacity:)
73
- protocol = self::PROTOCOL
74
- connection = Connection.new(protocol:, executor:, notification_capacity:)
75
- new(connection).extend(Session)
76
- rescue Exception
77
- connection&.close
78
- raise
56
+ @connection.receive_notification(@endpoint, timeout:)
79
57
  end
80
58
  end
81
59
 
82
60
  private def exchange_message(kind, request_class, reply_class, args, &block)
83
- @connection.exchange(@protocol, kind, request_class, reply_class, args, &block)
61
+ @connection.exchange(@endpoint, kind, request_class, reply_class, args, &block)
84
62
  end
85
63
 
86
- def async_capable? #: bool
64
+ # Returns if this family supports asynchronous operations.
65
+ #
66
+ # @rbs () -> bool
67
+ def async_capable?
87
68
  @connection.async_capable?
88
69
  end
89
70
 
90
- # Adds multicast memberships to the existing family socket. Membership is
91
- # additive and remains active until explicitly removed or the owner closes.
71
+ # Adds multicast memberships to the netlink socket.
72
+ #
73
+ # Membership is additive and remains active until explicitly removed or the owner closes.
74
+ #
75
+ # @param [Array<Symbol>] groups
76
+ # @return [Family] self
77
+ # @rbs (Symbol *groups) -> self
92
78
  def subscribe(*groups)
93
79
  @connection.add_memberships(multicast_group_ids(groups))
94
80
  self
95
81
  end
96
82
 
83
+ # Removes multicast memberships from the netlink socket.
84
+ #
85
+ # @param [Array<Symbol>] groups
86
+ # @return [Family] self
87
+ # @rbs (Symbol *groups) -> self
97
88
  def unsubscribe(*groups)
98
89
  @connection.drop_memberships(multicast_group_ids(groups))
99
90
  self
100
91
  end
101
92
 
93
+ # Receives the next unsolicited message.
94
+ #
95
+ # @rbs (?timeout: Integer?) -> Message
102
96
  def receive_notification(timeout: nil)
103
97
  @notification_stream.next(timeout:)
104
98
  end
105
99
 
100
+ # Receives unsolicited messages.
101
+ #
102
+ # @rbs () { (Message) -> void } -> void
106
103
  def each_notification(&block)
107
104
  return @notification_stream.each unless block
108
105
 
109
106
  @notification_stream.each(&block)
110
107
  end
111
108
 
112
- # Builds a generated asynchronous-operation facade with a narrowly scoped
113
- # callback, so the facade does not need access to Family's private API.
114
- #--
115
- # @rbs operations_class: Class
116
- # @rbs stream_capacity: Integer?
117
- # @rbs return: untyped
109
+ # Builds a asynchronous-operation facade.
110
+ #
111
+ # @rbs (Class operations_class, ?stream_capacity: Integer?) -> untyped
118
112
  private def build_async_facade(operations_class, stream_capacity: nil)
119
113
  unless async_capable?
120
114
  raise Async::UnavailableError, 'async operations require an executor'
@@ -126,7 +120,7 @@ module Nl
126
120
  end
127
121
 
128
122
  private def exchange_message_async(kind, request_class, reply_class, args, stream_capacity: nil)
129
- @connection.exchange_async(@protocol, kind, request_class, reply_class, args, stream_capacity:)
123
+ @connection.exchange_async(@endpoint, kind, request_class, reply_class, args, stream_capacity:)
130
124
  end
131
125
 
132
126
  private def notification_classes
@@ -145,10 +139,11 @@ module Nl
145
139
  names.map do |name|
146
140
  key = name.to_sym
147
141
  group = multicast_groups.fetch(key) do
148
- raise UnknownMulticastGroupError, "unknown multicast group #{name.inspect} for #{@protocol.name}"
142
+ raise UnknownMulticastGroupError, "unknown multicast group #{name.inspect} for #{@endpoint.name}"
149
143
  end
150
- @protocol.multicast_group_id(group.name, group.id)
144
+ @endpoint.multicast_group_id(group.name, group.id)
151
145
  end
152
146
  end
153
147
  end
148
+
154
149
  end
@@ -0,0 +1,105 @@
1
+ # Generic Netlink client handling
2
+ #-
3
+
4
+ require_relative '../connection'
5
+ require_relative 'wire'
6
+
7
+ module Nl
8
+ module Genl
9
+ # Dynamically resolved information for a generic Netlink family.
10
+ #
11
+ # @!attribute [r] id
12
+ # @return [Integer] the assigned family ID
13
+ # @!attribute [r] multicast_groups
14
+ # @return [Hash<String, Integer>] multicast group names mapped to their IDs
15
+ FamilyInfo = Data.define(
16
+ :id, #: Integer
17
+ :multicast_groups, #: Hash[String, Integer]
18
+ )
19
+
20
+ # Owns one generic Netlink connection shared by compatible families.
21
+ class Client
22
+ # Opens a generic Netlink client.
23
+ #
24
+ # @overload open(resolver:, executor: nil, notification_capacity: Nl::Connection::DEFAULT_NOTIFICATION_CAPACITY)
25
+ # The caller is responsible for closing the client.
26
+ # @param [#call] resolver a callable that resolves a family name to {FamilyInfo}
27
+ # @param [:thread, :fiber, nil] executor the asynchronous executor, or `nil` for blocking operation
28
+ # @param [Integer] notification_capacity the maximum number of queued notifications
29
+ # @return [Client] the opened client
30
+ # @overload open(resolver:, executor: nil, notification_capacity: Nl::Connection::DEFAULT_NOTIFICATION_CAPACITY, &block)
31
+ # The client is automatically closed after the block returns.
32
+ # @param [#call] resolver a callable that resolves a family name to {FamilyInfo}
33
+ # @param [:thread, :fiber, nil] executor the asynchronous executor, or `nil` for blocking operation
34
+ # @param [Integer] notification_capacity the maximum number of queued notifications
35
+ # @yieldparam [Client] client the opened client
36
+ # @return [Object] the value returned from the block
37
+ # @rbs (resolver: ^(instance, ::String) -> FamilyInfo, ?executor: executor?, ?notification_capacity: Integer?) -> instance
38
+ # | [R] (resolver: ^(instance, ::String) -> FamilyInfo, ?executor: executor?, ?notification_capacity: Integer?) { (instance) -> R } -> R
39
+ def self.open(resolver:, executor: nil, notification_capacity: Nl::Connection::DEFAULT_NOTIFICATION_CAPACITY)
40
+ client = new(resolver:, executor:, notification_capacity:)
41
+ return client unless block_given?
42
+
43
+ begin
44
+ yield client
45
+ ensure
46
+ client.close
47
+ end
48
+ end
49
+
50
+ # @param [#call] resolver a callable that resolves a family name to {FamilyInfo}
51
+ # @param [:thread, :fiber, nil] executor the asynchronous executor, or `nil` for blocking operation
52
+ # @param [Integer] notification_capacity the maximum number of queued notifications
53
+ # @rbs (resolver: ^(instance, ::String) -> FamilyInfo, ?executor: executor?, ?notification_capacity: Integer?) -> void
54
+ def initialize(resolver:, executor: nil, notification_capacity: Nl::Connection::DEFAULT_NOTIFICATION_CAPACITY)
55
+ @resolver = resolver
56
+ @family_cache = {}
57
+ @family_cache_mutex = Mutex.new
58
+ @connection = Nl::Connection.new(
59
+ protocol: Protocol.new,
60
+ executor:,
61
+ notification_capacity:,
62
+ )
63
+ end
64
+
65
+ # Builds a family backed by this client's connection.
66
+ #
67
+ # @param [Class<Family>] family_class a generic Netlink family class
68
+ # @return [Family] an instance of `family_class`
69
+ # @raise [TypeError] if +family_class+ does not inherit from {Family}
70
+ # @rbs [F < Family] (_FamilyClass[F] family_class) -> F
71
+ def family(family_class)
72
+ unless family_class <= Family
73
+ raise TypeError, "family class must inherit from #{Family}"
74
+ end
75
+
76
+ info = family_info(family_class::NAME)
77
+ family_class.new(
78
+ @connection,
79
+ endpoint: Endpoint.new(family_class, info),
80
+ )
81
+ end
82
+
83
+ # Closes the underlying Netlink connection.
84
+ #
85
+ # @return [void]
86
+ # @rbs () -> void
87
+ def close
88
+ @connection.close
89
+ end
90
+
91
+ # Resolves and caches information for a family by name.
92
+ #
93
+ # @param [String] name the generic Netlink family name
94
+ # @return [FamilyInfo] the resolved family information
95
+ # @rbs (String name) -> FamilyInfo
96
+ private def family_info(name)
97
+ cached_info = @family_cache_mutex.synchronize { @family_cache[name] }
98
+ return cached_info if cached_info
99
+
100
+ info = @resolver.call(self, name)
101
+ @family_cache_mutex.synchronize { @family_cache[name] ||= info }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'wire'
2
+ require_relative '../raw'
3
+
4
+ module Nl
5
+ module Genl
6
+ # A generated Generic Netlink family bound to nlctrl-provided information.
7
+ class Endpoint < Raw::Endpoint
8
+ attr_reader :info
9
+
10
+ def initialize(definition, info)
11
+ super(definition)
12
+ @info = info
13
+ end
14
+
15
+ def family_id = @info.id
16
+ def version = @definition.version
17
+ def frame_type(_message_class) = family_id
18
+
19
+ def multicast_group_id(name, _value)
20
+ @info.multicast_groups.fetch(name) do
21
+ raise UnresolvedMulticastGroupError,
22
+ "Generic Netlink multicast group #{name.inspect} was not resolved"
23
+ end
24
+ end
25
+ end
26
+
27
+ # Socket-wide Generic Netlink wire behavior.
28
+ class Protocol < Raw::Protocol
29
+ def initialize
30
+ super(Raw::NETLINK_GENERIC)
31
+ end
32
+
33
+ def encode_message(encoder, endpoint, request, seq:, pid:)
34
+ message = request.message
35
+ header = Raw::NlMsgHdr.new(0, request.type, request.flags, seq, pid)
36
+ encoder.measure(Endian::Host::U16) do
37
+ header.encode(encoder)
38
+ Nl::Genl::GenlMsgHdr.new(message.class::TYPE, endpoint.version, 0).encode(encoder)
39
+ message.encode(encoder)
40
+ end
41
+ end
42
+
43
+ def notification_channel_key(endpoint) = endpoint.family_id
44
+ def notification_route_keys(endpoint, _classes) = [endpoint.family_id]
45
+ def notification_frame_key(header) = header.type
46
+
47
+ def notification_frame?(endpoint, header, _payload)
48
+ header.type == endpoint.family_id
49
+ end
50
+
51
+ def notification_class(endpoint, header, payload, classes)
52
+ return unless notification_frame?(endpoint, header, payload)
53
+
54
+ command = Nl::Genl::GenlMsgHdr.decode(Decoder.new(payload)).cmd
55
+ classes[command]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,95 @@
1
+ # Generic Netlink wire definitions
2
+ #-
3
+
4
+ require_relative '../raw/wire'
5
+ require_relative '../endian'
6
+
7
+ module Nl
8
+ module Genl
9
+ # Constants from <linux/genetlink.h>
10
+ module Constants
11
+ GENL_NAMSIZ = 16
12
+ GENL_MIN_ID = Raw::NLMSG_MIN_TYPE
13
+ GENL_MAX_ID = 1023
14
+
15
+ GENL_HDRLEN = 4
16
+
17
+ GENL_ID_GENERATE = 0
18
+ GENL_ID_CTRL = Raw::NLMSG_MIN_TYPE
19
+ GENL_ID_VFS_DQUOT = Raw::NLMSG_MIN_TYPE + 1
20
+ GENL_ID_PMCRAID = Raw::NLMSG_MIN_TYPE + 2
21
+
22
+ CTRL_CMD_UNSPEC = 0
23
+ CTRL_CMD_NEWFAMILY = 1
24
+ CTRL_CMD_DELFAMILY = 2
25
+ CTRL_CMD_GETFAMILY = 3
26
+ CTRL_CMD_NEWOPS = 4
27
+ CTRL_CMD_DELOPS = 5
28
+ CTRL_CMD_GETOPS = 6
29
+ CTRL_CMD_NEWMCAST_GRP = 7
30
+ CTRL_CMD_DELMCAST_GRP = 8
31
+ CTRL_CMD_GETMCAST_GRP = 9
32
+
33
+ CTRL_ATTR_UNSPEC = 0
34
+ CTRL_ATTR_FAMILY_ID = 1
35
+ CTRL_ATTR_FAMILY_NAME = 2
36
+ CTRL_ATTR_VERSION = 3
37
+ CTRL_ATTR_HDRSIZE = 4
38
+ CTRL_ATTR_MAXATTR = 5
39
+ CTRL_ATTR_OPS = 6
40
+ CTRL_ATTR_MCAST_GROUPS = 7
41
+
42
+ CTRL_ATTR_OP_UNSPEC = 0
43
+ CTRL_ATTR_OP_ID = 1
44
+ CTRL_ATTR_OP_FLAGS = 2
45
+
46
+ CTRL_ATTR_MCAST_GRP_UNSPEC = 0
47
+ CTRL_ATTR_MCAST_GRP_NAME = 1
48
+ CTRL_ATTR_MCAST_GRP_ID = 2
49
+ end
50
+ include Constants
51
+
52
+ # Header prepended to a Generic Netlink payload after the Netlink header.
53
+ #
54
+ # This corresponds to Linux's +struct genlmsghdr+.
55
+ #
56
+ # @!attribute [rw] cmd
57
+ # @return [Integer] family-specific command identifier
58
+ # @!attribute [rw] version
59
+ # @return [Integer] family-specific protocol version
60
+ # @!attribute [rw] reserved
61
+ # @return [Integer] reserved field, which must be zero
62
+ GenlMsgHdr = Struct.new(
63
+ :cmd, #: Integer
64
+ :version, #: Integer
65
+ :reserved, #: Integer
66
+ )
67
+
68
+ class GenlMsgHdr
69
+ FORMAT = Ractor.make_shareable([
70
+ Endian::Host::U8,
71
+ Endian::Host::U8,
72
+ Endian::Host::U16,
73
+ ])
74
+ private_constant :FORMAT
75
+
76
+ # Decodes a header from the decoder's current position.
77
+ #
78
+ # @param [Decoder] decoder the source decoder
79
+ # @return [GenlMsgHdr] the decoded header
80
+ # @rbs (Decoder decoder) -> instance
81
+ def self.decode(decoder)
82
+ new(*decoder.get_values(FORMAT))
83
+ end
84
+
85
+ # Encodes this header at the encoder's current position.
86
+ #
87
+ # @param [Encoder] encoder the destination encoder
88
+ # @return [void]
89
+ # @rbs (Encoder encoder) -> void
90
+ def encode(encoder)
91
+ encoder.put_values(FORMAT, to_a)
92
+ end
93
+ end
94
+ end
95
+ end
data/lib/nl/genl.rb CHANGED
@@ -1,74 +1,72 @@
1
- # Generic Netlink wire definitions
2
- #--
3
- # rbs_inline: enabled
4
-
5
- require_relative 'core'
6
- require_relative 'endian'
1
+ require_relative 'genl/wire'
2
+ require_relative 'family'
3
+ require_relative 'raw'
4
+ require_relative 'genl/protocol'
7
5
 
8
6
  module Nl
7
+ # Generic Netlink families.
9
8
  module Genl
10
- module Constants
11
- # From include/uapi/linux/genetlink.
12
- GENL_NAMSIZ = 16
13
- GENL_MIN_ID = Core::NLMSG_MIN_TYPE
14
- GENL_MAX_ID = 1023
15
-
16
- GENL_HDRLEN = 4
17
-
18
- GENL_ID_GENERATE = 0
19
- GENL_ID_CTRL = Core::NLMSG_MIN_TYPE
20
- GENL_ID_VFS_DQUOT = Core::NLMSG_MIN_TYPE + 1
21
- GENL_ID_PMCRAID = Core::NLMSG_MIN_TYPE + 2
22
-
23
- CTRL_CMD_UNSPEC = 0
24
- CTRL_CMD_NEWFAMILY = 1
25
- CTRL_CMD_DELFAMILY = 2
26
- CTRL_CMD_GETFAMILY = 3
27
- CTRL_CMD_NEWOPS = 4
28
- CTRL_CMD_DELOPS = 5
29
- CTRL_CMD_GETOPS = 6
30
- CTRL_CMD_NEWMCAST_GRP = 7
31
- CTRL_CMD_DELMCAST_GRP = 8
32
- CTRL_CMD_GETMCAST_GRP = 9
33
-
34
- CTRL_ATTR_UNSPEC = 0
35
- CTRL_ATTR_FAMILY_ID = 1
36
- CTRL_ATTR_FAMILY_NAME = 2
37
- CTRL_ATTR_VERSION = 3
38
- CTRL_ATTR_HDRSIZE = 4
39
- CTRL_ATTR_MAXATTR = 5
40
- CTRL_ATTR_OPS = 6
41
- CTRL_ATTR_MCAST_GROUPS = 7
9
+ # Base class for Generic Netlink families.
10
+ class Family < Nl::Family
11
+ # Returns the family-specific protocol version.
12
+ #
13
+ # @return [Integer] the protocol version
14
+ # @rbs () -> Integer
15
+ def self.version = self::VERSION
42
16
 
43
- CTRL_ATTR_OP_UNSPEC = 0
44
- CTRL_ATTR_OP_ID = 1
45
- CTRL_ATTR_OP_FLAGS = 2
17
+ # Opens a session for this Generic Netlink family.
18
+ #
19
+ # @overload open(resolver:, executor: nil, notification_capacity: DEFAULT_NOTIFICATION_CAPACITY)
20
+ # The caller is responsible for closing the session.
21
+ # @param [#call] resolver a callable that resolves a family name to {FamilyInfo}
22
+ # @param [:thread, :fiber, nil] executor the asynchronous executor, or `nil` for blocking operation
23
+ # @param [Integer] notification_capacity the maximum number of queued notifications
24
+ # @return [Family] the opened family session
25
+ # @overload open(resolver:, executor: nil, notification_capacity: DEFAULT_NOTIFICATION_CAPACITY, &block)
26
+ # The session is automatically closed after the block returns.
27
+ # @param [#call] resolver a callable that resolves a family name to {FamilyInfo}
28
+ # @param [:thread, :fiber, nil] executor the asynchronous executor, or `nil` for blocking operation
29
+ # @param [Integer] notification_capacity the maximum number of queued notifications
30
+ # @yieldparam [Family] session the opened family session
31
+ # @return [Object] the value returned from the block
32
+ # @rbs (resolver: ^(Client, ::String) -> FamilyInfo, ?executor: executor?, ?notification_capacity: Integer?) -> (Nl::Family::Session & instance)
33
+ # | [R] (resolver: ^(Client, ::String) -> FamilyInfo, ?executor: executor?, ?notification_capacity: Integer?) { (instance) -> R } -> R
34
+ def self.open(resolver:, executor: nil, notification_capacity: DEFAULT_NOTIFICATION_CAPACITY)
35
+ begin
36
+ owner = Client.new(resolver:, executor:, notification_capacity:)
37
+ session = owner.family(self).extend(Nl::Family::Session)
38
+ rescue Exception
39
+ owner&.close
40
+ raise
41
+ end
42
+ return session unless block_given?
46
43
 
47
- CTRL_ATTR_MCAST_GRP_UNSPEC = 0
48
- CTRL_ATTR_MCAST_GRP_NAME = 1
49
- CTRL_ATTR_MCAST_GRP_ID = 2
50
- end
51
- include Constants
52
-
53
- GenlMsgHdr = Struct.new(:cmd, :version, :reserved)
54
- # Generic Netlink message header
55
- class GenlMsgHdr
56
- FORMAT = Ractor.make_shareable([
57
- Endian::Host::U8,
58
- Endian::Host::U8,
59
- Endian::Host::U16,
60
- ])
61
- private_constant :FORMAT
62
-
63
- def self.decode(decoder)
64
- new(*decoder.get_values(FORMAT))
44
+ begin
45
+ yield session
46
+ ensure
47
+ session.close
48
+ end
65
49
  end
50
+ end
66
51
 
67
- def encode(encoder)
68
- encoder.put_values(FORMAT, to_a)
52
+ # Base class for Generic Netlink messages.
53
+ #
54
+ # A Generic Netlink message payload begins with a {GenlMsgHdr}, followed by
55
+ # the optional fixed header and attributes handled by {Raw::Message}.
56
+ class Message < Raw::Message
57
+ # Decodes a Generic Netlink message payload.
58
+ #
59
+ # @param [Decoder] decoder the source decoder
60
+ # @param [Integer] type the family ID from the Netlink message header
61
+ # @return [Message] the decoded message
62
+ # @raise [RuntimeError] if the command does not match the message class's type
63
+ # @rbs (Decoder decoder, type: Integer) -> instance
64
+ def self.decode(decoder, type:)
65
+ genlhdr = GenlMsgHdr.decode(decoder)
66
+ super(decoder, type: genlhdr.cmd)
69
67
  end
70
68
  end
71
69
  end
72
70
  end
73
71
 
74
- require_relative 'genl/connection'
72
+ require_relative 'genl/client'
@@ -1,5 +1,3 @@
1
- # rbs_inline: enabled
2
-
3
1
  require_relative 'error'
4
2
 
5
3
  module Nl
@@ -7,9 +5,6 @@ module Nl
7
5
  # `id` is the fixed ID when the specification provides one.
8
6
  McastGroup = Data.define(:name, :id)
9
7
 
10
- # An unsolicited message whose wire type is not present in the loaded spec.
11
- UnknownNotification = Data.define(:header, :payload)
12
-
13
8
  # Thread-safe queue shared by a family's blocking and asynchronous facades.
14
9
  class NotificationChannel
15
10
  def initialize(capacity:)
@@ -96,7 +91,7 @@ module Nl
96
91
 
97
92
  # An unbounded-in-time, single-family view of unsolicited messages.
98
93
  class NotificationStream
99
- include Enumerable
94
+ include Enumerable #[untyped]
100
95
 
101
96
  def initialize(&receive)
102
97
  @receive = receive
@@ -106,6 +101,8 @@ module Nl
106
101
  @receive.call(timeout)
107
102
  end
108
103
 
104
+ # @rbs () -> Enumerator[untyped, void]
105
+ # | () { (untyped) -> void } -> void
109
106
  def each
110
107
  return enum_for(__method__) unless block_given?
111
108