em-ruby-dbus 0.11.0
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.
- checksums.yaml +7 -0
- data/COPYING +504 -0
- data/NEWS +253 -0
- data/README.md +93 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/doc/Reference.md +207 -0
- data/doc/Tutorial.md +480 -0
- data/doc/ex-calling-methods.body.rb +8 -0
- data/doc/ex-calling-methods.rb +3 -0
- data/doc/ex-properties.body.rb +9 -0
- data/doc/ex-properties.rb +3 -0
- data/doc/ex-setup.rb +7 -0
- data/doc/ex-signal.body.rb +20 -0
- data/doc/ex-signal.rb +3 -0
- data/doc/example-helper.rb +6 -0
- data/em-ruby-dbus.gemspec +20 -0
- data/examples/gdbus/gdbus +255 -0
- data/examples/gdbus/gdbus.glade +184 -0
- data/examples/gdbus/launch.sh +4 -0
- data/examples/no-introspect/nm-test.rb +21 -0
- data/examples/no-introspect/tracker-test.rb +16 -0
- data/examples/rhythmbox/playpause.rb +25 -0
- data/examples/service/call_service.rb +25 -0
- data/examples/service/service_newapi.rb +51 -0
- data/examples/simple/call_introspect.rb +34 -0
- data/examples/simple/properties.rb +19 -0
- data/examples/utils/listnames.rb +11 -0
- data/examples/utils/notify.rb +19 -0
- data/lib/dbus.rb +82 -0
- data/lib/dbus/auth.rb +269 -0
- data/lib/dbus/bus.rb +739 -0
- data/lib/dbus/core_ext/array/extract_options.rb +31 -0
- data/lib/dbus/core_ext/class/attribute.rb +129 -0
- data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/dbus/core_ext/module/remove_method.rb +14 -0
- data/lib/dbus/error.rb +46 -0
- data/lib/dbus/export.rb +128 -0
- data/lib/dbus/introspect.rb +219 -0
- data/lib/dbus/logger.rb +31 -0
- data/lib/dbus/loop-em.rb +19 -0
- data/lib/dbus/marshall.rb +434 -0
- data/lib/dbus/matchrule.rb +101 -0
- data/lib/dbus/message.rb +276 -0
- data/lib/dbus/message_queue.rb +166 -0
- data/lib/dbus/proxy_object.rb +149 -0
- data/lib/dbus/proxy_object_factory.rb +41 -0
- data/lib/dbus/proxy_object_interface.rb +128 -0
- data/lib/dbus/type.rb +193 -0
- data/lib/dbus/xml.rb +161 -0
- data/test/async_spec.rb +47 -0
- data/test/binding_spec.rb +74 -0
- data/test/bus_and_xml_backend_spec.rb +39 -0
- data/test/bus_driver_spec.rb +20 -0
- data/test/bus_spec.rb +20 -0
- data/test/byte_array_spec.rb +38 -0
- data/test/err_msg_spec.rb +42 -0
- data/test/introspect_xml_parser_spec.rb +26 -0
- data/test/introspection_spec.rb +32 -0
- data/test/main_loop_spec.rb +82 -0
- data/test/property_spec.rb +53 -0
- data/test/server_robustness_spec.rb +66 -0
- data/test/server_spec.rb +53 -0
- data/test/service_newapi.rb +217 -0
- data/test/session_bus_spec_manual.rb +15 -0
- data/test/signal_spec.rb +90 -0
- data/test/spec_helper.rb +33 -0
- data/test/thread_safety_spec.rb +31 -0
- data/test/tools/dbus-launch-simple +35 -0
- data/test/tools/dbus-limited-session.conf +28 -0
- data/test/tools/test_env +13 -0
- data/test/tools/test_server +39 -0
- data/test/type_spec.rb +19 -0
- data/test/value_spec.rb +81 -0
- data/test/variant_spec.rb +66 -0
- metadata +145 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
# This file is part of the ruby-dbus project
|
2
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
3
|
+
#
|
4
|
+
# This library is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
6
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
7
|
+
# See the file "COPYING" for the exact licensing terms.
|
8
|
+
|
9
|
+
module DBus
|
10
|
+
# Exception raised when an erroneous match rule type is encountered.
|
11
|
+
class MatchRuleException < Exception
|
12
|
+
end
|
13
|
+
|
14
|
+
# = D-Bus match rule class
|
15
|
+
#
|
16
|
+
# FIXME
|
17
|
+
class MatchRule
|
18
|
+
# The list of possible match filters. TODO argN, argNpath
|
19
|
+
FILTERS = [:sender, :interface, :member, :path, :destination, :type]
|
20
|
+
# The sender filter.
|
21
|
+
attr_accessor :sender
|
22
|
+
# The interface filter.
|
23
|
+
attr_accessor :interface
|
24
|
+
# The member filter.
|
25
|
+
attr_accessor :member
|
26
|
+
# The path filter.
|
27
|
+
attr_accessor :path
|
28
|
+
# The destination filter.
|
29
|
+
attr_accessor :destination
|
30
|
+
# The type type that is matched.
|
31
|
+
attr_reader :type
|
32
|
+
|
33
|
+
# Create a new match rule
|
34
|
+
def initialize
|
35
|
+
@sender = @interface = @member = @path = @destination = @type = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set the message types to filter to type _t_.
|
39
|
+
# Possible message types are: signal, method_call, method_return, and error.
|
40
|
+
def type=(t)
|
41
|
+
if not ['signal', 'method_call', 'method_return', 'error'].member?(t)
|
42
|
+
raise MatchRuleException, t
|
43
|
+
end
|
44
|
+
@type = t
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a match rule string version of the object.
|
48
|
+
# E.g.: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',path='/bar/foo',destination=':452345.34',arg2='bar'"
|
49
|
+
def to_s
|
50
|
+
FILTERS.select do |sym|
|
51
|
+
not method(sym).call.nil?
|
52
|
+
end.collect do |sym|
|
53
|
+
"#{sym.to_s}='#{method(sym).call}'"
|
54
|
+
end.join(",")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parses a match rule string _s_ and sets the filters on the object.
|
58
|
+
def from_s(str)
|
59
|
+
str.split(",").each do |eq|
|
60
|
+
if eq =~ /^(.*)='([^']*)'$/
|
61
|
+
# "
|
62
|
+
name = $1
|
63
|
+
val = $2
|
64
|
+
if FILTERS.member?(name.to_sym)
|
65
|
+
method(name + "=").call(val)
|
66
|
+
else
|
67
|
+
raise MatchRuleException, name
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sets the match rule to filter for the given _signal_ and the
|
75
|
+
# given interface _intf_.
|
76
|
+
def from_signal(intf, signal)
|
77
|
+
signal = signal.name unless signal.is_a?(String)
|
78
|
+
self.type = "signal"
|
79
|
+
self.interface = intf.name
|
80
|
+
self.member = signal
|
81
|
+
self.path = intf.object.path
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Determines whether a message _msg_ matches the match rule.
|
86
|
+
def match(msg)
|
87
|
+
if @type
|
88
|
+
if {Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
|
89
|
+
Message::METHOD_RETURN => "method_return",
|
90
|
+
Message::ERROR => "error"}[msg.message_type] != @type
|
91
|
+
return false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return false if @interface and @interface != msg.interface
|
95
|
+
return false if @member and @member != msg.member
|
96
|
+
return false if @path and @path != msg.path
|
97
|
+
# FIXME sender and destination are ignored
|
98
|
+
true
|
99
|
+
end
|
100
|
+
end # class MatchRule
|
101
|
+
end # module D-Bus
|
data/lib/dbus/message.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
# dbus.rb - Module containing the low-level D-Bus implementation
|
2
|
+
#
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
9
|
+
# See the file "COPYING" for the exact licensing terms.
|
10
|
+
|
11
|
+
# = D-Bus main module
|
12
|
+
#
|
13
|
+
# Module containing all the D-Bus modules and classes.
|
14
|
+
module DBus
|
15
|
+
# = InvalidDestinationName class
|
16
|
+
# Thrown when you try to send a message to /org/freedesktop/DBus/Local, that
|
17
|
+
# is reserved.
|
18
|
+
class InvalidDestinationName < Exception
|
19
|
+
end
|
20
|
+
|
21
|
+
# = D-Bus message class
|
22
|
+
#
|
23
|
+
# Class that holds any type of message that travels over the bus.
|
24
|
+
class Message
|
25
|
+
# The serial number of the message.
|
26
|
+
@@serial = 1
|
27
|
+
# Mutex that protects updates on the serial number.
|
28
|
+
@@serial_mutex = Mutex.new
|
29
|
+
# Type of a message (by specification).
|
30
|
+
MESSAGE_SIGNATURE = "yyyyuua(yv)"
|
31
|
+
|
32
|
+
# FIXME: following message type constants should be under Message::Type IMO
|
33
|
+
# well, yeah sure
|
34
|
+
#
|
35
|
+
# Invalid message type.
|
36
|
+
INVALID = 0
|
37
|
+
# Method call message type.
|
38
|
+
METHOD_CALL = 1
|
39
|
+
# Method call return value message type.
|
40
|
+
METHOD_RETURN = 2
|
41
|
+
# Error message type.
|
42
|
+
ERROR = 3
|
43
|
+
# Signal message type.
|
44
|
+
SIGNAL = 4
|
45
|
+
|
46
|
+
# Message flag signyfing that no reply is expected.
|
47
|
+
NO_REPLY_EXPECTED = 0x1
|
48
|
+
# Message flag signifying that no automatic start is required/must be
|
49
|
+
# performed.
|
50
|
+
NO_AUTO_START = 0x2
|
51
|
+
|
52
|
+
# The type of the message.
|
53
|
+
attr_reader :message_type
|
54
|
+
# The path of the object instance the message must be sent to/is sent from.
|
55
|
+
attr_accessor :path
|
56
|
+
# The interface of the object that must be used/was used.
|
57
|
+
attr_accessor :interface
|
58
|
+
# The interface member (method/signal name) of the object that must be
|
59
|
+
# used/was used.
|
60
|
+
attr_accessor :member
|
61
|
+
# The name of the error (in case of an error message type).
|
62
|
+
attr_accessor :error_name
|
63
|
+
# The destination connection of the object that must be used/was used.
|
64
|
+
attr_accessor :destination
|
65
|
+
# The sender of the message.
|
66
|
+
attr_accessor :sender
|
67
|
+
# The signature of the message contents.
|
68
|
+
attr_accessor :signature
|
69
|
+
# The serial number of the message this message is a reply for.
|
70
|
+
attr_accessor :reply_serial
|
71
|
+
# The protocol.
|
72
|
+
attr_reader :protocol
|
73
|
+
# The serial of the message.
|
74
|
+
attr_reader :serial
|
75
|
+
# The parameters of the message.
|
76
|
+
attr_reader :params
|
77
|
+
|
78
|
+
# Create a message with message type _mtype_ with default values and a
|
79
|
+
# unique serial number.
|
80
|
+
def initialize(mtype = INVALID)
|
81
|
+
@message_type = mtype
|
82
|
+
|
83
|
+
@flags = 0
|
84
|
+
@protocol = 1
|
85
|
+
@body_length = 0
|
86
|
+
@signature = String.new
|
87
|
+
@@serial_mutex.synchronize do
|
88
|
+
@serial = @@serial
|
89
|
+
@@serial += 1
|
90
|
+
end
|
91
|
+
@params = Array.new
|
92
|
+
@destination = nil
|
93
|
+
@interface = nil
|
94
|
+
@error_name = nil
|
95
|
+
@member = nil
|
96
|
+
@path = nil
|
97
|
+
@reply_serial = nil
|
98
|
+
|
99
|
+
if mtype == METHOD_RETURN
|
100
|
+
@flags = NO_REPLY_EXPECTED
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
"#{message_type} sender=#{sender} -> dest=#{destination} serial=#{serial} reply_serial=#{reply_serial} path=#{path}; interface=#{interface}; member=#{member} error_name=#{error_name}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create a regular reply to a message _m_.
|
109
|
+
def self.method_return(m)
|
110
|
+
MethodReturnMessage.new.reply_to(m)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create an error reply to a message _m_.
|
114
|
+
def self.error(m, error_name, description=nil)
|
115
|
+
ErrorMessage.new(error_name, description).reply_to(m)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Mark this message as a reply to a another message _m_, taking
|
119
|
+
# the serial number of _m_ as reply serial and the sender of _m_ as
|
120
|
+
# destination.
|
121
|
+
def reply_to(m)
|
122
|
+
@reply_serial = m.serial
|
123
|
+
@destination = m.sender
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# Add a parameter _val_ of type _type_ to the message.
|
128
|
+
def add_param(type, val)
|
129
|
+
type = type.chr if type.kind_of?(Fixnum)
|
130
|
+
@signature += type.to_s
|
131
|
+
@params << [type, val]
|
132
|
+
end
|
133
|
+
|
134
|
+
# FIXME: what are these? a message element constant enumeration?
|
135
|
+
# See method below, in a message, you have and array of optional parameters
|
136
|
+
# that come with an index, to determine their meaning. The values are in
|
137
|
+
# spec, more a definition than an enumeration.
|
138
|
+
|
139
|
+
PATH = 1
|
140
|
+
INTERFACE = 2
|
141
|
+
MEMBER = 3
|
142
|
+
ERROR_NAME = 4
|
143
|
+
REPLY_SERIAL = 5
|
144
|
+
DESTINATION = 6
|
145
|
+
SENDER = 7
|
146
|
+
SIGNATURE = 8
|
147
|
+
|
148
|
+
# Marshall the message with its current set parameters and return
|
149
|
+
# it in a packet form.
|
150
|
+
def marshall
|
151
|
+
if @path == "/org/freedesktop/DBus/Local"
|
152
|
+
raise InvalidDestinationName
|
153
|
+
end
|
154
|
+
|
155
|
+
params = PacketMarshaller.new
|
156
|
+
@params.each do |param|
|
157
|
+
params.append(param[0], param[1])
|
158
|
+
end
|
159
|
+
@body_length = params.packet.bytesize
|
160
|
+
|
161
|
+
marshaller = PacketMarshaller.new
|
162
|
+
marshaller.append(Type::BYTE, HOST_END)
|
163
|
+
marshaller.append(Type::BYTE, @message_type)
|
164
|
+
marshaller.append(Type::BYTE, @flags)
|
165
|
+
marshaller.append(Type::BYTE, @protocol)
|
166
|
+
marshaller.append(Type::UINT32, @body_length)
|
167
|
+
marshaller.append(Type::UINT32, @serial)
|
168
|
+
|
169
|
+
headers = []
|
170
|
+
headers << [PATH, ["o", @path]] if @path
|
171
|
+
headers << [INTERFACE, ["s", @interface]] if @interface
|
172
|
+
headers << [MEMBER, ["s", @member]] if @member
|
173
|
+
headers << [ERROR_NAME, ["s", @error_name]] if @error_name
|
174
|
+
headers << [REPLY_SERIAL, ["u", @reply_serial]] if @reply_serial
|
175
|
+
headers << [DESTINATION, ["s", @destination]] if @destination
|
176
|
+
# SENDER is not sent, the message bus fills it in instead
|
177
|
+
headers << [SIGNATURE, ["g", @signature]] if @signature != ""
|
178
|
+
marshaller.append("a(yv)", headers)
|
179
|
+
|
180
|
+
marshaller.align(8)
|
181
|
+
@params.each do |param|
|
182
|
+
marshaller.append(param[0], param[1])
|
183
|
+
end
|
184
|
+
marshaller.packet
|
185
|
+
end
|
186
|
+
|
187
|
+
# Unmarshall a packet contained in the buffer _buf_ and set the
|
188
|
+
# parameters of the message object according the data found in the
|
189
|
+
# buffer.
|
190
|
+
# Return the detected message and the index pointer of the buffer where
|
191
|
+
# the message data ended.
|
192
|
+
def unmarshall_buffer(buf)
|
193
|
+
buf = buf.dup
|
194
|
+
if buf[0] == ?l
|
195
|
+
endianness = LIL_END
|
196
|
+
else
|
197
|
+
endianness = BIG_END
|
198
|
+
end
|
199
|
+
pu = PacketUnmarshaller.new(buf, endianness)
|
200
|
+
mdata = pu.unmarshall(MESSAGE_SIGNATURE)
|
201
|
+
_, @message_type, @flags, @protocol, @body_length, @serial,
|
202
|
+
headers = mdata
|
203
|
+
|
204
|
+
headers.each do |struct|
|
205
|
+
case struct[0]
|
206
|
+
when PATH
|
207
|
+
@path = struct[1]
|
208
|
+
when INTERFACE
|
209
|
+
@interface = struct[1]
|
210
|
+
when MEMBER
|
211
|
+
@member = struct[1]
|
212
|
+
when ERROR_NAME
|
213
|
+
@error_name = struct[1]
|
214
|
+
when REPLY_SERIAL
|
215
|
+
@reply_serial = struct[1]
|
216
|
+
when DESTINATION
|
217
|
+
@destination = struct[1]
|
218
|
+
when SENDER
|
219
|
+
@sender = struct[1]
|
220
|
+
when SIGNATURE
|
221
|
+
@signature = struct[1]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
pu.align(8)
|
225
|
+
if @body_length > 0 and @signature
|
226
|
+
@params = pu.unmarshall(@signature, @body_length)
|
227
|
+
end
|
228
|
+
[self, pu.idx]
|
229
|
+
end # def unmarshall_buf
|
230
|
+
|
231
|
+
# Unmarshall the data of a message found in the buffer _buf_ using
|
232
|
+
# Message#unmarshall_buf.
|
233
|
+
# Return the message.
|
234
|
+
def unmarshall(buf)
|
235
|
+
ret, _ = unmarshall_buffer(buf)
|
236
|
+
ret
|
237
|
+
end
|
238
|
+
|
239
|
+
# Make a new exception from ex, mark it as being caused by this message
|
240
|
+
# @api private
|
241
|
+
def annotate_exception(ex)
|
242
|
+
new_ex = ex.exception("#{ex}; caused by #{self}")
|
243
|
+
new_ex.set_backtrace(ex.backtrace)
|
244
|
+
new_ex
|
245
|
+
end
|
246
|
+
end # class Message
|
247
|
+
|
248
|
+
class MethodReturnMessage < Message
|
249
|
+
def initialize
|
250
|
+
super(METHOD_RETURN)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class ErrorMessage < Message
|
255
|
+
def initialize(error_name, description=nil)
|
256
|
+
super(ERROR)
|
257
|
+
@error_name = error_name
|
258
|
+
unless description.nil?
|
259
|
+
add_param(Type::STRING, description)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.from_exception(ex)
|
264
|
+
name = if ex.is_a? DBus::Error
|
265
|
+
ex.name
|
266
|
+
else
|
267
|
+
"org.freedesktop.DBus.Error.Failed"
|
268
|
+
# ex.class.to_s # RuntimeError is not a valid name, has no dot
|
269
|
+
end
|
270
|
+
description = ex.message
|
271
|
+
msg = self.new(name, description)
|
272
|
+
msg.add_param(DBus.type("as"), ex.backtrace)
|
273
|
+
msg
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end # module DBus
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# This file is part of the ruby-dbus project
|
2
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
3
|
+
# Copyright (C) 2009-2014 Martin Vidner
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
8
|
+
# See the file "COPYING" for the exact licensing terms.
|
9
|
+
|
10
|
+
require "fcntl"
|
11
|
+
require "socket"
|
12
|
+
|
13
|
+
module DBus
|
14
|
+
class MessageQueue
|
15
|
+
# The socket that is used to connect with the bus.
|
16
|
+
attr_reader :socket
|
17
|
+
|
18
|
+
def initialize(address)
|
19
|
+
@address = address
|
20
|
+
@buffer = ""
|
21
|
+
@is_tcp = false
|
22
|
+
connect
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO failure modes
|
26
|
+
#
|
27
|
+
# If _non_block_ is true, return nil instead of waiting
|
28
|
+
# EOFError may be raised
|
29
|
+
def pop(non_block = false)
|
30
|
+
buffer_from_socket_nonblock
|
31
|
+
message = message_from_buffer_nonblock
|
32
|
+
unless non_block
|
33
|
+
# we can block
|
34
|
+
while message.nil?
|
35
|
+
r, d, d = IO.select([@socket])
|
36
|
+
if r and r[0] == @socket
|
37
|
+
buffer_from_socket_nonblock
|
38
|
+
message = message_from_buffer_nonblock
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
message
|
43
|
+
end
|
44
|
+
|
45
|
+
def push(message)
|
46
|
+
@socket.write(message.marshall)
|
47
|
+
end
|
48
|
+
alias :<< :push
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Connect to the bus and initialize the connection.
|
53
|
+
def connect
|
54
|
+
addresses = @address.split ";"
|
55
|
+
# connect to first one that succeeds
|
56
|
+
worked = addresses.find do |a|
|
57
|
+
transport, keyvaluestring = a.split ":"
|
58
|
+
kv_list = keyvaluestring.split ","
|
59
|
+
kv_hash = Hash.new
|
60
|
+
kv_list.each do |kv|
|
61
|
+
key, escaped_value = kv.split "="
|
62
|
+
value = escaped_value.gsub(/%(..)/) {|m| [$1].pack "H2" }
|
63
|
+
kv_hash[key] = value
|
64
|
+
end
|
65
|
+
case transport
|
66
|
+
when "unix"
|
67
|
+
connect_to_unix kv_hash
|
68
|
+
when "tcp"
|
69
|
+
connect_to_tcp kv_hash
|
70
|
+
when "launchd"
|
71
|
+
connect_to_launchd kv_hash
|
72
|
+
else
|
73
|
+
# ignore, report?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
worked
|
77
|
+
# returns the address that worked or nil.
|
78
|
+
# how to report failure?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Connect to a bus over tcp and initialize the connection.
|
82
|
+
def connect_to_tcp(params)
|
83
|
+
#check if the path is sufficient
|
84
|
+
if params.key?("host") and params.key?("port")
|
85
|
+
begin
|
86
|
+
#initialize the tcp socket
|
87
|
+
@socket = TCPSocket.new(params["host"],params["port"].to_i)
|
88
|
+
@socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
89
|
+
init_connection
|
90
|
+
@is_tcp = true
|
91
|
+
rescue Exception => e
|
92
|
+
puts "Oops:", e
|
93
|
+
puts "Error: Could not establish connection to: #{@path}, will now exit."
|
94
|
+
exit(1) #a little harsh
|
95
|
+
end
|
96
|
+
else
|
97
|
+
#Danger, Will Robinson: the specified "path" is not usable
|
98
|
+
puts "Error: supplied path: #{@path}, unusable! sorry."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Connect to an abstract unix bus and initialize the connection.
|
103
|
+
def connect_to_unix(params)
|
104
|
+
@socket = Socket.new(Socket::Constants::PF_UNIX,Socket::Constants::SOCK_STREAM, 0)
|
105
|
+
@socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
106
|
+
if ! params['abstract'].nil?
|
107
|
+
if HOST_END == LIL_END
|
108
|
+
sockaddr = "\1\0\0#{params['abstract']}"
|
109
|
+
else
|
110
|
+
sockaddr = "\0\1\0#{params['abstract']}"
|
111
|
+
end
|
112
|
+
elsif ! params['path'].nil?
|
113
|
+
sockaddr = Socket.pack_sockaddr_un(params['path'])
|
114
|
+
end
|
115
|
+
@socket.connect(sockaddr)
|
116
|
+
init_connection
|
117
|
+
end
|
118
|
+
|
119
|
+
def connect_to_launchd(params)
|
120
|
+
socket_var = params['env']
|
121
|
+
socket = `launchctl getenv #{socket_var}`.chomp
|
122
|
+
connect_to_unix 'path' => socket
|
123
|
+
end
|
124
|
+
|
125
|
+
# Initialize the connection to the bus.
|
126
|
+
def init_connection
|
127
|
+
@client = Client.new(@socket)
|
128
|
+
@client.authenticate
|
129
|
+
end
|
130
|
+
|
131
|
+
public # FIXME: fix Main loop instead
|
132
|
+
|
133
|
+
# Get and remove one message from the buffer.
|
134
|
+
# Return the message or nil.
|
135
|
+
def message_from_buffer_nonblock
|
136
|
+
return nil if @buffer.empty?
|
137
|
+
ret = nil
|
138
|
+
begin
|
139
|
+
ret, size = Message.new.unmarshall_buffer(@buffer)
|
140
|
+
@buffer.slice!(0, size)
|
141
|
+
rescue IncompleteBufferException
|
142
|
+
# fall through, let ret be null
|
143
|
+
end
|
144
|
+
ret
|
145
|
+
end
|
146
|
+
|
147
|
+
# The buffer size for messages.
|
148
|
+
MSG_BUF_SIZE = 4096
|
149
|
+
|
150
|
+
# Fill (append) the buffer from data that might be available on the
|
151
|
+
# socket.
|
152
|
+
# EOFError may be raised
|
153
|
+
def buffer_from_socket_nonblock
|
154
|
+
@buffer += @socket.read_nonblock(MSG_BUF_SIZE)
|
155
|
+
rescue EOFError
|
156
|
+
raise # the caller expects it
|
157
|
+
rescue Errno::EAGAIN
|
158
|
+
# fine, would block
|
159
|
+
rescue Exception => e
|
160
|
+
puts "Oops:", e
|
161
|
+
raise if @is_tcp # why?
|
162
|
+
puts "WARNING: read_nonblock failed, falling back to .recv"
|
163
|
+
@buffer += @socket.recv(MSG_BUF_SIZE)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|