ruby-dbus 0.19.0 → 0.21.0

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
2
  SHA256:
3
- metadata.gz: 7261c5ad0fb4e067da97b31298e6a79023e54743f69563b22c9a751101df2f85
4
- data.tar.gz: 38075d862b0407889bf0a0e25eff450d18c8e225012a028545700fc1be15c342
3
+ metadata.gz: 922468208faa7a9a13f3c40c0b7ffd22a5a9a6b00de2e30e05e6ae18c84662d5
4
+ data.tar.gz: 9386581c654bad7f970763c1c67f8984e95654d6f49673a1c3452c2cc59ced0f
5
5
  SHA512:
6
- metadata.gz: 700dc07af8d28f7a7537be292dd6313268032f169102c3581d2f5c7a13ba8b9b7c864b23d3b8bdb66e3760a147a7edf7d835098f450625ffade7f748e101f987
7
- data.tar.gz: 2637cb7cad666a415c6e4a83a158b116507e5d6db3e70a53f5384d1a9240f191e6e2b540950076ae76397b7581c891e71b9f59e84346647394fe0bd7833e49c1
6
+ metadata.gz: 67e6e4058de8f6e9e0eddc404f53b5219487d711c165dd607ec1138c18f089de560f6467bf6960a28dc0c29b6df2a4a5eee4156e2c2fab68c1dada5ce9383f91
7
+ data.tar.gz: 364d914924b85f11354fdf8ed3f44f446091422014a92c5c7f0ecc3175cc28236d6a6b0cfec233321657a35554eb24be8692ab83c41a65eb9a75a7eef5dd7a5b
data/NEWS.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.21.0 - 2023-04-08
6
+
7
+ Features:
8
+ * Respect env RUBY_DBUS_ENDIANNESS=B (or =l) for outgoing messages.
9
+
10
+ Bug fixes:
11
+ * Reduce socket buffer allocations ([#129][]).
12
+ * Message#marshall speedup: don't marshall the body twice.
13
+
14
+ [#129]: https://github.com/mvidner/ruby-dbus/pull/129
15
+
16
+ ## Ruby D-Bus 0.20.0 - 2023-03-21
17
+
18
+ Features:
19
+ * For EXTERNAL authentication, try also without the user id, to work with
20
+ containers ([#126][]).
21
+ * Thread safety, as long as the non-main threads only send signals.
22
+
23
+ [#126]: https://github.com/mvidner/ruby-dbus/issues/126
24
+
5
25
  ## Ruby D-Bus 0.19.0 - 2023-01-18
6
26
 
7
27
  API:
@@ -73,7 +93,7 @@ API:
73
93
  when declaring properties ([#117][]).
74
94
 
75
95
  [#115]: https://github.com/mvidner/ruby-dbus/issues/115
76
- [#117]: https://github.com/mvidner/ruby-dbus/pulls/117
96
+ [#117]: https://github.com/mvidner/ruby-dbus/pull/117
77
97
 
78
98
  ## Ruby D-Bus 0.18.0.beta7 - 2022-05-29
79
99
 
@@ -441,7 +461,7 @@ Bug fixes:
441
461
  * Handle more ways which tell us that a bus connection has died.
442
462
 
443
463
  [#3]: https://github.com/mvidner/ruby-dbus/issue/3
444
- [bsc#617350]: https://bugzilla.novell.com/show_bug.cgi?id=617350
464
+ [bsc#617350]: https://bugzilla.suse.com/show_bug.cgi?id=617350
445
465
 
446
466
  ## Ruby D-Bus 0.3.0 - 2010-03-28
447
467
 
@@ -509,8 +529,8 @@ Bug fixes:
509
529
  * Fixed an endless sleep in DBus::Main.run ([bsc#537401][]).
510
530
  * Added details to PacketMarshaller exceptions ([bsc#538050][]).
511
531
 
512
- [bsc#537401]: https://bugzilla.novell.com/show_bug.cgi?id=537401
513
- [bsc#538050]: https://bugzilla.novell.com/show_bug.cgi?id=538050
532
+ [bsc#537401]: https://bugzilla.suse.com/show_bug.cgi?id=537401
533
+ [bsc#538050]: https://bugzilla.suse.com/show_bug.cgi?id=538050
514
534
 
515
535
  ## Ruby D-Bus "I'm not dead" 0.2.9 - 2009-08-26
516
536
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.19.0
1
+ 0.21.0
@@ -6,7 +6,9 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
6
6
 
7
7
  require "dbus"
8
8
 
9
- bus = DBus::SystemBus.instance
9
+ busname = ARGV.fetch(0, "system")
10
+ bus = busname == "session" ? DBus::SessionBus.instance : DBus::SystemBus.instance
11
+
10
12
  driver_svc = bus["org.freedesktop.DBus"]
11
13
  # p driver_svc
12
14
  driver_obj = driver_svc["/"]
@@ -15,4 +17,4 @@ driver_ifc = driver_obj["org.freedesktop.DBus"]
15
17
  # p driver_ifc
16
18
 
17
19
  bus_id = driver_ifc.GetId
18
- puts "The system bus id is #{bus_id}"
20
+ puts "The #{busname} bus id is #{bus_id}"
data/lib/dbus/auth.rb CHANGED
@@ -12,261 +12,348 @@ require "rbconfig"
12
12
 
13
13
  module DBus
14
14
  # Exception raised when authentication fails somehow.
15
- class AuthenticationFailed < Exception
15
+ class AuthenticationFailed < StandardError
16
16
  end
17
17
 
18
- # = General class for authentication.
19
- class Authenticator
20
- # Returns the name of the authenticator.
21
- def name
22
- self.class.to_s.upcase.sub(/.*::/, "")
23
- end
24
- end
18
+ # The Authentication Protocol.
19
+ # https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
20
+ #
21
+ # @api private
22
+ module Authentication
23
+ # Base class of authentication mechanisms
24
+ class Mechanism
25
+ # @!method call(challenge)
26
+ # @abstract
27
+ # Replies to server *challenge*, or sends an initial response if the challenge is `nil`.
28
+ # @param challenge [String,nil]
29
+ # @return [Array(Symbol,String)] pair [action, response], where
30
+ # - [:MechContinue, response] caller should send "DATA response" and go to :WaitingForData
31
+ # - [:MechOk, response] caller should send "DATA response" and go to :WaitingForOk
32
+ # - [:MechError, message] caller should send "ERROR message" and go to :WaitingForData
25
33
 
26
- # = Anonymous authentication class
27
- class Anonymous < Authenticator
28
- def authenticate
29
- "527562792044427573" # Hex encoded version of "Ruby DBus"
34
+ # Uppercase mechanism name, as sent to the server
35
+ # @return [String]
36
+ def name
37
+ self.class.to_s.upcase.sub(/.*::/, "")
38
+ end
30
39
  end
31
- end
32
40
 
33
- # = External authentication class
34
- #
35
- # Class for 'external' type authentication.
36
- class External < Authenticator
37
- # Performs the authentication.
38
- def authenticate
39
- # Take the user id (eg integer 1000) make a string out of it "1000", take
40
- # each character and determin hex value "1" => 0x31, "0" => 0x30. You
41
- # obtain for "1000" => 31303030 This is what the server is expecting.
42
- # Why? I dunno. How did I come to that conclusion? by looking at rbus
43
- # code. I have no idea how he found that out.
44
- Process.uid.to_s.split(//).map { |d| d.ord.to_s(16) }.join
41
+ # Anonymous authentication class.
42
+ # https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-anonymous
43
+ class Anonymous < Mechanism
44
+ def call(_challenge)
45
+ [:MechOk, "Ruby DBus"]
46
+ end
45
47
  end
46
- end
47
48
 
48
- # = Authentication class using SHA1 crypto algorithm
49
- #
50
- # Class for 'CookieSHA1' type authentication.
51
- # Implements the AUTH DBUS_COOKIE_SHA1 mechanism.
52
- class DBusCookieSHA1 < Authenticator
53
- # the autenticate method (called in stage one of authentification)
54
- def authenticate
55
- require "etc"
56
- # number of retries we have for auth
57
- @retries = 1
58
- hex_encode(Etc.getlogin).to_s # server expects it to be binary
49
+ # Class for 'external' type authentication.
50
+ # https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-external
51
+ class External < Mechanism
52
+ # Performs the authentication.
53
+ def call(_challenge)
54
+ [:MechOk, Process.uid.to_s]
55
+ end
59
56
  end
60
57
 
61
- # returns the modules name
62
- def name
63
- "DBUS_COOKIE_SHA1"
58
+ # A variant of EXTERNAL that doesn't say our UID.
59
+ # Seen busctl do this and it worked across a container boundary.
60
+ class ExternalWithoutUid < External
61
+ def name
62
+ "EXTERNAL"
63
+ end
64
+
65
+ def call(_challenge)
66
+ [:MechContinue, nil]
67
+ end
64
68
  end
65
69
 
66
- # handles the interesting crypto stuff, check the rbus-project for more info: http://rbus.rubyforge.org/
67
- def data(hexdata)
68
- require "digest/sha1"
69
- data = hex_decode(hexdata)
70
- # name of cookie file, id of cookie in file, servers random challenge
71
- context, id, s_challenge = data.split(" ")
72
- # Random client challenge
73
- c_challenge = 1.upto(s_challenge.bytesize / 2).map { rand(255).to_s }.join
74
- # Search cookie file for id
75
- path = File.join(ENV["HOME"], ".dbus-keyrings", context)
76
- DBus.logger.debug "path: #{path.inspect}"
77
- File.foreach(path) do |line|
78
- if line.start_with?(id)
79
- # Right line of file, read cookie
80
- cookie = line.split(" ")[2].chomp
81
- DBus.logger.debug "cookie: #{cookie.inspect}"
82
- # Concatenate and encrypt
83
- to_encrypt = [s_challenge, c_challenge, cookie].join(":")
84
- sha = Digest::SHA1.hexdigest(to_encrypt)
85
- # the almighty tcp server wants everything hex encoded
86
- hex_response = hex_encode("#{c_challenge} #{sha}")
87
- # Return response
88
- response = [:AuthOk, hex_response]
89
- return response
70
+ # Implements the AUTH DBUS_COOKIE_SHA1 mechanism.
71
+ # https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha
72
+ class DBusCookieSHA1 < Mechanism
73
+ # returns the modules name
74
+ def name
75
+ "DBUS_COOKIE_SHA1"
76
+ end
77
+
78
+ # First we are called with nil and we reply with our username.
79
+ # Then we prove that we can read that user's cookie file.
80
+ def call(challenge)
81
+ if challenge.nil?
82
+ require "etc"
83
+ # number of retries we have for auth
84
+ @retries = 1
85
+ return [:MechContinue, Etc.getlogin]
86
+ end
87
+
88
+ require "digest/sha1"
89
+ # name of cookie file, id of cookie in file, servers random challenge
90
+ context, id, s_challenge = challenge.split(" ")
91
+ # Random client challenge
92
+ c_challenge = 1.upto(s_challenge.bytesize / 2).map { rand(255).to_s }.join
93
+ # Search cookie file for id
94
+ path = File.join(ENV["HOME"], ".dbus-keyrings", context)
95
+ DBus.logger.debug "path: #{path.inspect}"
96
+ File.foreach(path) do |line|
97
+ if line.start_with?(id)
98
+ # Right line of file, read cookie
99
+ cookie = line.split(" ")[2].chomp
100
+ DBus.logger.debug "cookie: #{cookie.inspect}"
101
+ # Concatenate and encrypt
102
+ to_encrypt = [s_challenge, c_challenge, cookie].join(":")
103
+ sha = Digest::SHA1.hexdigest(to_encrypt)
104
+ # Return response
105
+ response = [:MechOk, "#{c_challenge} #{sha}"]
106
+ return response
107
+ end
90
108
  end
109
+ return if @retries <= 0
110
+
111
+ # a little rescue magic
112
+ puts "ERROR: Could not auth, will now exit."
113
+ puts "ERROR: Unable to locate cookie, retry in 1 second."
114
+ @retries -= 1
115
+ sleep 1
116
+ call(challenge)
91
117
  end
92
- return if @retries <= 0
93
-
94
- # a little rescue magic
95
- puts "ERROR: Could not auth, will now exit."
96
- puts "ERROR: Unable to locate cookie, retry in 1 second."
97
- @retries -= 1
98
- sleep 1
99
- data(hexdata)
100
118
  end
101
119
 
102
- # encode plain to hex
103
- def hex_encode(plain)
104
- return nil if plain.nil?
120
+ # Declare client state transitions, for ease of code reading.
121
+ # It is just a pair.
122
+ NextState = Struct.new(:state, :command_words)
105
123
 
106
- plain.to_s.unpack1("H*")
107
- end
124
+ # Authenticates the connection before messages can be exchanged.
125
+ class Client
126
+ # @return [Boolean] have we negotiated Unix file descriptor passing
127
+ # NOTE: not implemented yet in upper layers
128
+ attr_reader :unix_fd
108
129
 
109
- # decode hex to plain
110
- def hex_decode(encoded)
111
- encoded.scan(/[[:xdigit:]]{2}/).map { |h| h.hex.chr }.join
112
- end
113
- end
130
+ # @return [String]
131
+ attr_reader :address_uuid
114
132
 
115
- # Note: this following stuff is tested with External authenticator only!
133
+ # Create a new authentication client.
134
+ # @param mechs [Array<Mechanism,Class>,nil] custom list of auth Mechanism objects or classes
135
+ def initialize(socket, mechs = nil)
136
+ @unix_fd = false
137
+ @address_uuid = nil
116
138
 
117
- # = Authentication client class.
118
- #
119
- # Class tha performs the actional authentication.
120
- class Client
121
- # Create a new authentication client.
122
- def initialize(socket)
123
- @socket = socket
124
- @state = nil
125
- @auth_list = [External, DBusCookieSHA1, Anonymous]
126
- end
139
+ @socket = socket
140
+ @state = nil
141
+ @auth_list = mechs || [
142
+ External,
143
+ DBusCookieSHA1,
144
+ ExternalWithoutUid,
145
+ Anonymous
146
+ ]
147
+ end
148
+
149
+ # Start the authentication process.
150
+ # @return [void]
151
+ # @raise [AuthenticationFailed]
152
+ def authenticate
153
+ DBus.logger.debug "Authenticating"
154
+ send_nul_byte
127
155
 
128
- # Start the authentication process.
129
- def authenticate
130
- if RbConfig::CONFIG["target_os"] =~ /freebsd/
131
- @socket.sendmsg(0.chr, 0, nil, [:SOCKET, :SCM_CREDS, ""])
132
- else
133
- @socket.write(0.chr)
156
+ use_next_mechanism
157
+
158
+ @state, command = next_state_via_mechanism.to_a
159
+ send(command)
160
+
161
+ loop do
162
+ DBus.logger.debug "auth STATE: #{@state}"
163
+ words = next_msg
164
+
165
+ @state, command = next_state(words).to_a
166
+ break if [:TerminatedOk, :TerminatedError].include? @state
167
+
168
+ send(command)
169
+ end
170
+
171
+ raise AuthenticationFailed, command.first if @state == :TerminatedError
172
+
173
+ send("BEGIN")
134
174
  end
135
- next_authenticator
136
- @state = :Starting
137
- while @state != :Authenticated
138
- r = next_state
139
- return r if !r
175
+
176
+ ##########
177
+
178
+ private
179
+
180
+ ##########
181
+
182
+ # The authentication protocol requires a nul byte
183
+ # that may carry credentials.
184
+ # @return [void]
185
+ def send_nul_byte
186
+ if RbConfig::CONFIG["target_os"] =~ /freebsd/
187
+ @socket.sendmsg(0.chr, 0, nil, [:SOCKET, :SCM_CREDS, ""])
188
+ else
189
+ @socket.write(0.chr)
190
+ end
140
191
  end
141
- true
142
- end
143
192
 
144
- ##########
193
+ # encode plain to hex
194
+ # @param plain [String,nil]
195
+ # @return [String,nil]
196
+ def hex_encode(plain)
197
+ return nil if plain.nil?
145
198
 
146
- private
199
+ plain.unpack1("H*")
200
+ end
147
201
 
148
- ##########
202
+ # decode hex to plain
203
+ # @param encoded [String,nil]
204
+ # @return [String,nil]
205
+ def hex_decode(encoded)
206
+ return nil if encoded.nil?
149
207
 
150
- # Send an authentication method _meth_ with arguments _args_ to the
151
- # server.
152
- def send(meth, *args)
153
- o = ([meth] + args).join(" ")
154
- @socket.write("#{o}\r\n")
155
- end
208
+ [encoded].pack("H*")
209
+ end
156
210
 
157
- # Try authentication using the next authenticator.
158
- def next_authenticator
159
- raise AuthenticationFailed if @auth_list.empty?
160
-
161
- @authenticator = @auth_list.shift.new
162
- auth_msg = ["AUTH", @authenticator.name, @authenticator.authenticate]
163
- DBus.logger.debug "auth_msg: #{auth_msg.inspect}"
164
- send(auth_msg)
165
- rescue AuthenticationFailed
166
- @socket.close
167
- raise
168
- end
211
+ # Send a string to the socket; good place for test mocks.
212
+ def write_line(str)
213
+ DBus.logger.debug "auth_write: #{str.inspect}"
214
+ @socket.write(str)
215
+ end
169
216
 
170
- # Read data (a buffer) from the bus until CR LF is encountered.
171
- # Return the buffer without the CR LF characters.
172
- def next_msg
173
- data = ""
174
- crlf = "\r\n"
175
- left = 1024 # 1024 byte, no idea if it's ever getting bigger
176
- while left.positive?
177
- buf = @socket.read(left > 1 ? 1 : left)
178
- break if buf.nil?
179
-
180
- left -= buf.bytesize
181
- data += buf
182
- break if data.include? crlf # crlf means line finished, the TCP socket keeps on listening, so we break
217
+ # Send *words* to the server as a single CRLF terminated string.
218
+ # @param words [Array<String>,String]
219
+ def send(words)
220
+ joined = Array(words).compact.join(" ")
221
+ write_line("#{joined}\r\n")
183
222
  end
184
- readline = data.chomp.split(" ")
185
- DBus.logger.debug "readline: #{readline.inspect}"
186
- readline
187
- end
188
223
 
189
- # # Read data (a buffer) from the bus until CR LF is encountered.
190
- # # Return the buffer without the CR LF characters.
191
- # def next_msg
192
- # @socket.readline.chomp.split(" ")
193
- # end
194
-
195
- # Try to reach the next state based on the current state.
196
- def next_state
197
- msg = next_msg
198
- if @state == :Starting
199
- DBus.logger.debug ":Starting msg: #{msg[0].inspect}"
200
- case msg[0]
201
- when "OK"
202
- @state = :WaitingForOk
203
- when "CONTINUE"
204
- @state = :WaitingForData
205
- when "REJECTED" # needed by tcp, unix-path/abstract doesn't get here
206
- @state = :WaitingForData
207
- end
224
+ # Try authentication using the next mechanism.
225
+ # @raise [AuthenticationFailed] if there are no more left
226
+ # @return [void]
227
+ def use_next_mechanism
228
+ raise AuthenticationFailed, "Authentication mechanisms exhausted" if @auth_list.empty?
229
+
230
+ @mechanism = @auth_list.shift
231
+ @mechanism = @mechanism.new if @mechanism.is_a? Class
232
+ rescue AuthenticationFailed
233
+ # TODO: make this caller's responsibility
234
+ @socket.close
235
+ raise
208
236
  end
209
- DBus.logger.debug "state: #{@state}"
210
- case @state
211
- when :WaitingForData
212
- DBus.logger.debug ":WaitingForData msg: #{msg[0].inspect}"
213
- case msg[0]
214
- when "DATA"
215
- chall = msg[1]
216
- resp, chall = @authenticator.data(chall)
217
- DBus.logger.debug ":WaitingForData/DATA resp: #{resp.inspect}"
218
- case resp
219
- when :AuthContinue
220
- send("DATA", chall)
221
- @state = :WaitingForData
222
- when :AuthOk
223
- send("DATA", chall)
224
- @state = :WaitingForOk
225
- when :AuthError
226
- send("ERROR")
227
- @state = :WaitingForData
228
- end
229
- when "REJECTED"
230
- next_authenticator
231
- @state = :WaitingForData
232
- when "ERROR"
233
- send("CANCEL")
234
- @state = :WaitingForReject
235
- when "OK"
236
- send("BEGIN")
237
- @state = :Authenticated
238
- else
239
- send("ERROR")
240
- @state = :WaitingForData
237
+
238
+ # Read data (a buffer) from the bus until CR LF is encountered.
239
+ # Return the buffer without the CR LF characters.
240
+ # @return [Array<String>] received words
241
+ def next_msg
242
+ read_line.chomp.split(" ")
243
+ end
244
+
245
+ # Read a line from the socket; good place for test mocks.
246
+ # @return [String] CRLF (\r\n) terminated
247
+ def read_line
248
+ # TODO: probably can simply call @socket.readline
249
+ data = ""
250
+ crlf = "\r\n"
251
+ left = 1024 # 1024 byte, no idea if it's ever getting bigger
252
+ while left.positive?
253
+ buf = @socket.read(left > 1 ? 1 : left)
254
+ break if buf.nil?
255
+
256
+ left -= buf.bytesize
257
+ data += buf
258
+ break if data.include? crlf # crlf means line finished, the TCP socket keeps on listening, so we break
241
259
  end
242
- when :WaitingForOk
243
- DBus.logger.debug ":WaitingForOk msg: #{msg[0].inspect}"
244
- case msg[0]
245
- when "OK"
246
- send("BEGIN")
247
- @state = :Authenticated
248
- when "REJECT"
249
- next_authenticator
250
- @state = :WaitingForData
251
- when "DATA", "ERROR"
252
- send("CANCEL")
253
- @state = :WaitingForReject
260
+ DBus.logger.debug "auth_read: #{data.inspect}"
261
+ data
262
+ end
263
+
264
+ # # Read data (a buffer) from the bus until CR LF is encountered.
265
+ # # Return the buffer without the CR LF characters.
266
+ # def next_msg
267
+ # @socket.readline.chomp.split(" ")
268
+ # end
269
+
270
+ # @param hex_challenge [String,nil] (nil when the server said "DATA\r\n")
271
+ # @param use_data [Boolean] say DATA instead of AUTH
272
+ # @return [NextState]
273
+ def next_state_via_mechanism(hex_challenge = nil, use_data: false)
274
+ challenge = hex_decode(hex_challenge)
275
+
276
+ action, response = @mechanism.call(challenge)
277
+ DBus.logger.debug "auth mechanism action: #{action.inspect}"
278
+
279
+ command = use_data ? ["DATA"] : ["AUTH", @mechanism.name]
280
+
281
+ case action
282
+ when :MechError
283
+ NextState.new(:WaitingForData, ["ERROR", response])
284
+ when :MechContinue
285
+ NextState.new(:WaitingForData, command + [hex_encode(response)])
286
+ when :MechOk
287
+ NextState.new(:WaitingForOk, command + [hex_encode(response)])
254
288
  else
255
- send("ERROR")
256
- @state = :WaitingForOk
289
+ raise AuthenticationFailed, "internal error, unknown action #{action.inspect} " \
290
+ "from our mechanism #{@mechanism.inspect}"
257
291
  end
258
- when :WaitingForReject
259
- DBus.logger.debug ":WaitingForReject msg: #{msg[0].inspect}"
260
- case msg[0]
261
- when "REJECT"
262
- next_authenticator
263
- @state = :WaitingForOk
292
+ end
293
+
294
+ # Try to reach the next state based on the current state.
295
+ # @param received_words [Array<String>]
296
+ # @return [NextState]
297
+ def next_state(received_words)
298
+ msg = received_words
299
+
300
+ case @state
301
+ when :WaitingForData
302
+ case msg[0]
303
+ when "DATA"
304
+ next_state_via_mechanism(msg[1], use_data: true)
305
+ when "REJECTED"
306
+ use_next_mechanism
307
+ next_state_via_mechanism
308
+ when "ERROR"
309
+ NextState.new(:WaitingForReject, ["CANCEL"])
310
+ when "OK"
311
+ @address_uuid = msg[1]
312
+ # NextState.new(:TerminatedOk, [])
313
+ NextState.new(:WaitingForAgreeUnixFD, ["NEGOTIATE_UNIX_FD"])
314
+ else
315
+ NextState.new(:WaitingForData, ["ERROR"])
316
+ end
317
+ when :WaitingForOk
318
+ case msg[0]
319
+ when "OK"
320
+ @address_uuid = msg[1]
321
+ # NextState.new(:TerminatedOk, [])
322
+ NextState.new(:WaitingForAgreeUnixFD, ["NEGOTIATE_UNIX_FD"])
323
+ when "REJECTED"
324
+ use_next_mechanism
325
+ next_state_via_mechanism
326
+ when "DATA", "ERROR"
327
+ NextState.new(:WaitingForReject, ["CANCEL"])
328
+ else
329
+ # we don't understand server's response but still wait for a successful auth completion
330
+ NextState.new(:WaitingForOk, ["ERROR"])
331
+ end
332
+ when :WaitingForReject
333
+ case msg[0]
334
+ when "REJECTED"
335
+ use_next_mechanism
336
+ next_state_via_mechanism
337
+ else
338
+ # TODO: spec says to close socket, clarify
339
+ NextState.new(:TerminatedError, ["Unknown server reply #{msg[0].inspect} when expecting REJECTED"])
340
+ end
341
+ when :WaitingForAgreeUnixFD
342
+ case msg[0]
343
+ when "AGREE_UNIX_FD"
344
+ @unix_fd = true
345
+ NextState.new(:TerminatedOk, [])
346
+ when "ERROR"
347
+ @unix_fd = false
348
+ NextState.new(:TerminatedOk, [])
349
+ else
350
+ # TODO: spec says to close socket, clarify
351
+ NextState.new(:TerminatedError, ["Unknown server reply #{msg[0].inspect} to NEGOTIATE_UNIX_FD"])
352
+ end
264
353
  else
265
- @socket.close
266
- return false
354
+ raise "Internal error: unhandled state #{@state.inspect}"
267
355
  end
268
356
  end
269
- true
270
357
  end
271
358
  end
272
359
  end
data/lib/dbus/message.rb CHANGED
@@ -10,6 +10,8 @@
10
10
  # License, version 2.1 as published by the Free Software Foundation.
11
11
  # See the file "COPYING" for the exact licensing terms.
12
12
 
13
+ require_relative "raw_message"
14
+
13
15
  # = D-Bus main module
14
16
  #
15
17
  # Module containing all the D-Bus modules and classes.
@@ -144,6 +146,10 @@ module DBus
144
146
  @params << [type, val]
145
147
  end
146
148
 
149
+ # "l" or "B"
150
+ ENDIANNESS_CHAR = ENV.fetch("RUBY_DBUS_ENDIANNESS", HOST_END)
151
+ ENDIANNESS = RawMessage.endianness(ENDIANNESS_CHAR)
152
+
147
153
  # FIXME: what are these? a message element constant enumeration?
148
154
  # See method below, in a message, you have and array of optional parameters
149
155
  # that come with an index, to determine their meaning. The values are in
@@ -165,14 +171,14 @@ module DBus
165
171
  raise InvalidDestinationName
166
172
  end
167
173
 
168
- params = PacketMarshaller.new
169
- @params.each do |param|
170
- params.append(param[0], param[1])
174
+ params_marshaller = PacketMarshaller.new(endianness: ENDIANNESS)
175
+ @params.each do |type, value|
176
+ params_marshaller.append(type, value)
171
177
  end
172
- @body_length = params.packet.bytesize
178
+ @body_length = params_marshaller.packet.bytesize
173
179
 
174
- marshaller = PacketMarshaller.new
175
- marshaller.append(Type::BYTE, HOST_END.ord)
180
+ marshaller = PacketMarshaller.new(endianness: ENDIANNESS)
181
+ marshaller.append(Type::BYTE, ENDIANNESS_CHAR.ord)
176
182
  marshaller.append(Type::BYTE, @message_type)
177
183
  marshaller.append(Type::BYTE, @flags)
178
184
  marshaller.append(Type::BYTE, @protocol)
@@ -191,10 +197,8 @@ module DBus
191
197
  marshaller.append("a(yv)", headers)
192
198
 
193
199
  marshaller.align(8)
194
- @params.each do |param|
195
- marshaller.append(param[0], param[1])
196
- end
197
- marshaller.packet
200
+
201
+ marshaller.packet + params_marshaller.packet
198
202
  end
199
203
 
200
204
  # Unmarshall a packet contained in the buffer _buf_ and set the
@@ -18,10 +18,17 @@ module DBus
18
18
  # The socket that is used to connect with the bus.
19
19
  attr_reader :socket
20
20
 
21
+ # The buffer size for messages.
22
+ MSG_BUF_SIZE = 4096
23
+
21
24
  def initialize(address)
25
+ DBus.logger.debug "MessageQueue: #{address}"
22
26
  @address = address
23
27
  @buffer = ""
28
+ # Reduce allocations by using a single buffer for our socket
29
+ @read_buffer = String.new(capacity: MSG_BUF_SIZE)
24
30
  @is_tcp = false
31
+ @mutex = Mutex.new
25
32
  connect
26
33
  end
27
34
 
@@ -32,23 +39,28 @@ module DBus
32
39
  # @raise EOFError
33
40
  # @todo failure modes
34
41
  def pop(blocking: true)
35
- buffer_from_socket_nonblock
36
- message = message_from_buffer_nonblock
37
- if blocking
38
- # we can block
39
- while message.nil?
40
- r, _d, _d = IO.select([@socket])
41
- if r && r[0] == @socket
42
- buffer_from_socket_nonblock
43
- message = message_from_buffer_nonblock
42
+ # FIXME: this is not enough, the R/W test deadlocks on shared connections
43
+ @mutex.synchronize do
44
+ buffer_from_socket_nonblock
45
+ message = message_from_buffer_nonblock
46
+ if blocking
47
+ # we can block
48
+ while message.nil?
49
+ r, _d, _d = IO.select([@socket])
50
+ if r && r[0] == @socket
51
+ buffer_from_socket_nonblock
52
+ message = message_from_buffer_nonblock
53
+ end
44
54
  end
45
55
  end
56
+ message
46
57
  end
47
- message
48
58
  end
49
59
 
50
60
  def push(message)
51
- @socket.write(message.marshall)
61
+ @mutex.synchronize do
62
+ @socket.write(message.marshall)
63
+ end
52
64
  end
53
65
  alias << push
54
66
 
@@ -129,7 +141,7 @@ module DBus
129
141
 
130
142
  # Initialize the connection to the bus.
131
143
  def init_connection
132
- client = Client.new(@socket)
144
+ client = Authentication::Client.new(@socket)
133
145
  client.authenticate
134
146
  end
135
147
 
@@ -150,15 +162,12 @@ module DBus
150
162
  ret
151
163
  end
152
164
 
153
- # The buffer size for messages.
154
- MSG_BUF_SIZE = 4096
155
-
156
165
  # Fill (append) the buffer from data that might be available on the
157
166
  # socket.
158
167
  # @return [void]
159
168
  # @raise EOFError
160
169
  def buffer_from_socket_nonblock
161
- @buffer += @socket.read_nonblock(MSG_BUF_SIZE)
170
+ @buffer += @socket.read_nonblock(MSG_BUF_SIZE, @read_buffer)
162
171
  rescue EOFError
163
172
  raise # the caller expects it
164
173
  rescue Errno::EAGAIN
data/lib/dbus.rb CHANGED
@@ -10,6 +10,21 @@
10
10
  # License, version 2.1 as published by the Free Software Foundation.
11
11
  # See the file "COPYING" for the exact licensing terms.
12
12
 
13
+ module DBus
14
+ # Byte signifying big endianness.
15
+ BIG_END = "B"
16
+ # Byte signifying little endianness.
17
+ LIL_END = "l"
18
+
19
+ # Byte signifying the host's endianness.
20
+ HOST_END = if [0x01020304].pack("L").unpack1("V") == 0x01020304
21
+ LIL_END
22
+ else
23
+ BIG_END
24
+ end
25
+ end
26
+ # ^ That's because dbus/message needs HOST_END early
27
+
13
28
  require_relative "dbus/api_options"
14
29
  require_relative "dbus/auth"
15
30
  require_relative "dbus/bus"
@@ -41,18 +56,6 @@ module DBus
41
56
  # Default socket name for the system bus.
42
57
  SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"
43
58
 
44
- # Byte signifying big endianness.
45
- BIG_END = "B"
46
- # Byte signifying little endianness.
47
- LIL_END = "l"
48
-
49
- # Byte signifying the host's endianness.
50
- HOST_END = if [0x01020304].pack("L").unpack1("V") == 0x01020304
51
- LIL_END
52
- else
53
- BIG_END
54
- end
55
-
56
59
  # Comparing symbols is faster than strings
57
60
  # @return [:little,:big]
58
61
  HOST_ENDIANNESS = RawMessage.endianness(HOST_END)
data/spec/auth_spec.rb ADDED
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+
7
+ describe DBus::Authentication::Client do
8
+ let(:socket) { instance_double("Socket") }
9
+ let(:subject) { described_class.new(socket) }
10
+
11
+ before(:each) do
12
+ allow(Process).to receive(:uid).and_return(999)
13
+ allow(subject).to receive(:send_nul_byte)
14
+ end
15
+
16
+ describe "#next_state" do
17
+ it "raises when I forget to handle a state" do
18
+ subject.instance_variable_set(:@state, :Denmark)
19
+ expect { subject.__send__(:next_state, []) }.to raise_error(RuntimeError, /unhandled state :Denmark/)
20
+ end
21
+ end
22
+
23
+ def expect_protocol(pairs)
24
+ pairs.each do |we_say, server_says|
25
+ expect(subject).to receive(:write_line).with(we_say)
26
+ next if server_says.nil?
27
+
28
+ expect(subject).to receive(:read_line).and_return(server_says)
29
+ end
30
+ end
31
+
32
+ context "with ANONYMOUS" do
33
+ let(:subject) { described_class.new(socket, [DBus::Authentication::Anonymous]) }
34
+
35
+ it "authentication passes" do
36
+ expect_protocol [
37
+ ["AUTH ANONYMOUS 527562792044427573\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
38
+ ["NEGOTIATE_UNIX_FD\r\n", "ERROR not for anonymous\r\n"],
39
+ ["BEGIN\r\n"]
40
+ ]
41
+
42
+ expect { subject.authenticate }.to_not raise_error
43
+ end
44
+ end
45
+
46
+ context "with EXTERNAL" do
47
+ let(:subject) { described_class.new(socket, [DBus::Authentication::External]) }
48
+
49
+ it "authentication passes, and address_uuid is set" do
50
+ expect_protocol [
51
+ ["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
52
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
53
+ ["BEGIN\r\n"]
54
+ ]
55
+
56
+ expect { subject.authenticate }.to_not raise_error
57
+ expect(subject.address_uuid).to eq "ffffffffffffffffffffffffffffffff"
58
+ end
59
+
60
+ context "when the server says superfluous things before an OK" do
61
+ it "authentication passes" do
62
+ expect_protocol [
63
+ ["AUTH EXTERNAL 393939\r\n", "WOULD_YOU_LIKE_SOME_TEA\r\n"],
64
+ ["ERROR\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
65
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
66
+ ["BEGIN\r\n"]
67
+ ]
68
+
69
+ expect { subject.authenticate }.to_not raise_error
70
+ end
71
+ end
72
+
73
+ context "when the server messes up NEGOTIATE_UNIX_FD" do
74
+ it "authentication fails orderly" do
75
+ expect_protocol [
76
+ ["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
77
+ ["NEGOTIATE_UNIX_FD\r\n", "I_DONT_NEGOTIATE_WITH_TENORISTS\r\n"]
78
+ ]
79
+
80
+ allow(socket).to receive(:close) # want to get rid of this
81
+ # TODO: quote the server error message?
82
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /Unknown server reply/)
83
+ end
84
+ end
85
+
86
+ context "when the server replies with ERROR" do
87
+ it "authentication fails orderly" do
88
+ expect_protocol [
89
+ ["AUTH EXTERNAL 393939\r\n", "ERROR something failed\r\n"],
90
+ ["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
91
+ ]
92
+
93
+ allow(socket).to receive(:close) # want to get rid of this
94
+ # TODO: quote the server error message?
95
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
96
+ end
97
+ end
98
+ end
99
+
100
+ context "with EXTERNAL without uid" do
101
+ let(:subject) do
102
+ described_class.new(socket, [DBus::Authentication::External, DBus::Authentication::ExternalWithoutUid])
103
+ end
104
+
105
+ it "authentication passes" do
106
+ expect_protocol [
107
+ ["AUTH EXTERNAL 393939\r\n", "REJECTED EXTERNAL\r\n"],
108
+ # this succeeds when we connect to a privileged container,
109
+ # where outside-non-root becomes inside-root
110
+ ["AUTH EXTERNAL\r\n", "DATA\r\n"],
111
+ ["DATA\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
112
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
113
+ ["BEGIN\r\n"]
114
+ ]
115
+
116
+ expect { subject.authenticate }.to_not raise_error
117
+ end
118
+ end
119
+
120
+ context "with a rejected mechanism and then EXTERNAL" do
121
+ let(:rejected_mechanism) do
122
+ double("Mechanism", name: "WIMP", call: [:MechContinue, "I expect to be rejected"])
123
+ end
124
+
125
+ let(:subject) { described_class.new(socket, [rejected_mechanism, DBus::Authentication::External]) }
126
+
127
+ it "authentication eventually passes" do
128
+ expect_protocol [
129
+ [/^AUTH WIMP .*\r\n/, "REJECTED EXTERNAL\r\n"],
130
+ ["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
131
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
132
+ ["BEGIN\r\n"]
133
+ ]
134
+
135
+ expect { subject.authenticate }.to_not raise_error
136
+ end
137
+ end
138
+
139
+ context "with a DATA-using mechanism" do
140
+ let(:mechanism) do
141
+ double("Mechanism", name: "CHALLENGE_ME", call: [:MechContinue, "1"])
142
+ end
143
+
144
+ # try it twice to test calling #use_next_mechanism
145
+ let(:subject) { described_class.new(socket, [mechanism, mechanism]) }
146
+
147
+ it "authentication fails orderly when the server says ERROR" do
148
+ expect_protocol [
149
+ ["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
150
+ ["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"],
151
+ ["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
152
+ ["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
153
+ ]
154
+
155
+ allow(socket).to receive(:close) # want to get rid of this
156
+ # TODO: quote the server error message?
157
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
158
+ end
159
+
160
+ it "authentication fails orderly when the server says ERROR and then changes its mind" do
161
+ expect_protocol [
162
+ ["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
163
+ ["CANCEL\r\n", "I_CHANGED_MY_MIND please come back\r\n"]
164
+ ]
165
+
166
+ allow(socket).to receive(:close) # want to get rid of this
167
+ # TODO: quote the server error message?
168
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /Unknown.*MIND.*REJECTED/)
169
+ end
170
+
171
+ it "authentication passes when the server says superfluous things before DATA" do
172
+ expect_protocol [
173
+ ["AUTH CHALLENGE_ME 31\r\n", "WOULD_YOU_LIKE_SOME_TEA\r\n"],
174
+ ["ERROR\r\n", "DATA\r\n"],
175
+ ["DATA 31\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
176
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
177
+ ["BEGIN\r\n"]
178
+ ]
179
+
180
+ expect { subject.authenticate }.to_not raise_error
181
+ end
182
+
183
+ it "authentication passes when the server decides not to need the DATA" do
184
+ expect_protocol [
185
+ ["AUTH CHALLENGE_ME 31\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
186
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
187
+ ["BEGIN\r\n"]
188
+ ]
189
+
190
+ expect { subject.authenticate }.to_not raise_error
191
+ end
192
+ end
193
+
194
+ context "with a mechanism returning :MechError" do
195
+ let(:fallible_mechanism) do
196
+ double(name: "FALLIBLE", call: [:MechError, "not my best day"])
197
+ end
198
+
199
+ let(:subject) { described_class.new(socket, [fallible_mechanism]) }
200
+
201
+ it "authentication fails orderly" do
202
+ expect_protocol [
203
+ ["ERROR not my best day\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
204
+ ]
205
+
206
+ allow(socket).to receive(:close) # want to get rid of thise
207
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
208
+ end
209
+ end
210
+
211
+ context "with a badly implemented mechanism" do
212
+ let(:buggy_mechanism) do
213
+ double(name: "buggy", call: [:smurf, nil])
214
+ end
215
+
216
+ let(:subject) { described_class.new(socket, [buggy_mechanism]) }
217
+
218
+ it "authentication fails before protoxol is exchanged" do
219
+ expect(subject).to_not receive(:write_line)
220
+ expect(subject).to_not receive(:read_line)
221
+
222
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /smurf/)
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+
7
+ describe DBus::ProxyObjectInterface do
8
+ # TODO: tag tests that need a service, eg "needs-service"
9
+ # TODO: maybe remove this and rely on a packaged tool
10
+ around(:each) do |example|
11
+ with_private_bus do
12
+ with_service_by_activation(&example)
13
+ end
14
+ end
15
+
16
+ let(:bus) { DBus::ASessionBus.new }
17
+
18
+ context "when calling org.ruby.service" do
19
+ let(:svc) { bus["org.ruby.service"] }
20
+
21
+ # This is white box testing, knowing the implementation
22
+ # A better way would be structuring it according to the D-Bus Spec
23
+ # Or testing the service side doing the right thing? (What if our bugs cancel out)
24
+ describe "#define_method_from_descriptor" do
25
+ it "can call a method with multiple OUT arguments" do
26
+ obj = svc["/org/ruby/MyInstance"]
27
+ ifc = obj["org.ruby.SampleInterface"]
28
+
29
+ even, odd = ifc.EvenOdd([3, 1, 4, 1, 5, 9, 2, 6])
30
+ expect(even).to eq [4, 2, 6]
31
+ expect(odd).to eq [3, 1, 1, 5, 9]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+
7
+ # Pedantic full coverage test.
8
+ # The happy paths are covered via calling classes
9
+ describe DBus::RawMessage do
10
+ describe ".endianness" do
11
+ it "returns :little for 'l'" do
12
+ expect(described_class.endianness("l")).to eq :little
13
+ end
14
+
15
+ it "returns :big for 'B'" do
16
+ expect(described_class.endianness("B")).to eq :big
17
+ end
18
+
19
+ it "raises for other strings" do
20
+ expect { described_class.endianness("m") }
21
+ .to raise_error(DBus::InvalidPacketException, /Incorrect endianness/)
22
+ end
23
+ end
24
+
25
+ describe "#align" do
26
+ it "raises for values other than 1 2 4 8" do
27
+ subject = described_class.new("l")
28
+ expect { subject.align(3) }.to raise_error(ArgumentError)
29
+ expect { subject.align(16) }.to raise_error(ArgumentError)
30
+ end
31
+ end
32
+ end
@@ -102,6 +102,12 @@ class Test < DBus::Object
102
102
  [coords]
103
103
  end
104
104
 
105
+ # Two OUT arguments
106
+ dbus_method :EvenOdd, "in numbers:ai, out even:ai, out odd:ai" do |numbers|
107
+ even, odd = numbers.partition(&:even?)
108
+ [even, odd]
109
+ end
110
+
105
111
  # Properties:
106
112
  # ReadMe:string, returns "READ ME" at first, then what WriteMe received
107
113
  # WriteMe:string
data/spec/spec_helper.rb CHANGED
@@ -31,7 +31,7 @@ if coverage
31
31
  c.single_report_path = "coverage/lcov.info"
32
32
  end
33
33
 
34
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
34
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new [
35
35
  SimpleCov::Formatter::HTMLFormatter,
36
36
  SimpleCov::Formatter::LcovFormatter
37
37
  ]
@@ -5,28 +5,71 @@
5
5
  require_relative "spec_helper"
6
6
  require "dbus"
7
7
 
8
- describe "ThreadSafetyTest" do
9
- it "tests thread competition" do
10
- print "Thread competition: "
11
- jobs = []
12
- 5.times do
13
- jobs << Thread.new do
14
- Thread.current.abort_on_exception = true
8
+ class TestSignalRace < DBus::Object
9
+ dbus_interface "org.ruby.ServerTest" do
10
+ dbus_signal :signal_without_arguments
11
+ end
12
+ end
13
+
14
+ # Run *count* threads all doing *body*, wait for their finish
15
+ def race_threads(count, &body)
16
+ jobs = count.times.map do |j|
17
+ Thread.new do
18
+ Thread.current.abort_on_exception = true
19
+
20
+ body.call(j)
21
+ end
22
+ end
23
+ jobs.each(&:join)
24
+ end
25
+
26
+ # Repeat *count* times: { random sleep, *body* }, printing progress
27
+ def repeat_with_jitter(count, &body)
28
+ count.times do |i|
29
+ sleep 0.1 * rand
30
+ print "#{i} "
31
+ $stdout.flush
15
32
 
33
+ body.call
34
+ end
35
+ end
36
+
37
+ describe "thread safety" do
38
+ context "R/W: when the threads call methods with return values" do
39
+ it "it works with separate bus connections" do
40
+ race_threads(5) do |_j|
16
41
  # use separate connections to avoid races
17
42
  bus = DBus::ASessionBus.new
18
43
  svc = bus.service("org.ruby.service")
19
44
  obj = svc.object("/org/ruby/MyInstance")
20
45
  obj.default_iface = "org.ruby.SampleInterface"
21
46
 
22
- 10.times do |i|
23
- print "#{i} "
24
- $stdout.flush
47
+ repeat_with_jitter(10) do
25
48
  expect(obj.the_answer[0]).to eq(42)
26
- sleep 0.1 * rand
27
49
  end
28
50
  end
51
+ puts
52
+ end
53
+ end
54
+
55
+ context "W/O: when the threads only send signals" do
56
+ it "it works with a shared separate bus connection" do
57
+ race_threads(5) do |j|
58
+ # shared connection
59
+ bus = DBus::SessionBus.instance
60
+ # hackish: we do not actually request the name
61
+ svc = DBus::Service.new("org.ruby.server-test#{j}", bus)
62
+
63
+ obj = TestSignalRace.new "/org/ruby/Foo"
64
+ svc.export obj
65
+
66
+ repeat_with_jitter(10) do
67
+ obj.signal_without_arguments
68
+ end
69
+
70
+ svc.unexport(obj)
71
+ end
72
+ puts
29
73
  end
30
- jobs.each(&:join)
31
74
  end
32
75
  end
@@ -7,6 +7,20 @@
7
7
  <!-- Our well-known bus type, don't change this -->
8
8
  <type>session</type>
9
9
 
10
+ <!-- Authentication:
11
+ This was useful during refactoring, but meanwhile RSpec mocking has
12
+ replaced it. -->
13
+ <!-- Explicitly list all known authentication mechanisms,
14
+ their order is not important.
15
+ By default the daemon allows all but this lets me disable some. -->
16
+ <auth>EXTERNAL</auth>
17
+ <auth>DBUS_COOKIE_SHA1</auth>
18
+ <auth>ANONYMOUS</auth>
19
+ <!-- Insecure, other users could call us and exploit debug APIs/bugs -->
20
+ <!--
21
+ <allow_anonymous/>
22
+ -->
23
+
10
24
  <listen>unix:tmpdir=/tmp</listen>
11
25
  <listen>tcp:host=127.0.0.1</listen>
12
26
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-dbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruby DBus Team
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-18 00:00:00.000000000 Z
11
+ date: 2023-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rexml
@@ -165,6 +165,7 @@ files:
165
165
  - lib/dbus/xml.rb
166
166
  - ruby-dbus.gemspec
167
167
  - spec/async_spec.rb
168
+ - spec/auth_spec.rb
168
169
  - spec/binding_spec.rb
169
170
  - spec/bus_and_xml_backend_spec.rb
170
171
  - spec/bus_driver_spec.rb
@@ -186,7 +187,9 @@ files:
186
187
  - spec/packet_marshaller_spec.rb
187
188
  - spec/packet_unmarshaller_spec.rb
188
189
  - spec/property_spec.rb
190
+ - spec/proxy_object_interface_spec.rb
189
191
  - spec/proxy_object_spec.rb
192
+ - spec/raw_message_spec.rb
190
193
  - spec/server_robustness_spec.rb
191
194
  - spec/server_spec.rb
192
195
  - spec/service_newapi.rb
@@ -208,7 +211,7 @@ homepage: https://github.com/mvidner/ruby-dbus
208
211
  licenses:
209
212
  - LGPL-2.1
210
213
  metadata: {}
211
- post_install_message:
214
+ post_install_message:
212
215
  rdoc_options: []
213
216
  require_paths:
214
217
  - lib
@@ -223,9 +226,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
226
  - !ruby/object:Gem::Version
224
227
  version: '0'
225
228
  requirements: []
226
- rubyforge_project:
227
- rubygems_version: 2.7.6.3
228
- signing_key:
229
+ rubygems_version: 3.3.0.dev
230
+ signing_key:
229
231
  specification_version: 4
230
232
  summary: Ruby module for interaction with D-Bus
231
233
  test_files: []