1
// Package dbus provides a client interface to the D-Bus IPC system.
2
// It can be used to talk to system services (via the "system bus") or
3
// services within the user's session (via the "session bus").
21
SessionBus StandardBus = iota
26
BUS_DAEMON_NAME = "org.freedesktop.DBus"
27
BUS_DAEMON_PATH = ObjectPath("/org/freedesktop/DBus")
28
BUS_DAEMON_IFACE = "org.freedesktop.DBus"
31
type MessageFilter struct {
32
filter func(*Message) *Message
35
// Connection represents a connection to a message bus.
36
type Connection struct {
37
// The unique name of this connection on the message bus.
43
handlerMutex sync.Mutex // covers the next three
44
messageFilters []*MessageFilter
45
methodCallReplies map[uint32]chan<- *Message
46
objectPathHandlers map[ObjectPath]chan<- *Message
47
signalMatchRules signalWatchSet
49
nameInfoMutex sync.Mutex
50
nameInfo map[string]*nameInfo
53
// ObjectProxy represents a remote object on the bus. It can be used
54
// to simplify constructing method calls, and acts as a basis for
55
// D-Bus interface client stubs.
56
type ObjectProxy struct {
62
func (o *ObjectProxy) ObjectPath() ObjectPath {
66
// Call the given method on the remote object.
68
// On success, the reply message will be returned, whose arguments can
69
// be unpacked with its Args() method.
71
// On failure (both network failures and D-Bus level errors), an error
73
func (o *ObjectProxy) Call(iface, method string, args ...interface{}) (*Message, error) {
74
msg := NewMethodCallMessage(o.destination, o.path, iface, method)
75
if err := msg.AppendArgs(args...); err != nil {
78
reply, err := o.bus.SendWithReply(msg)
82
if reply.Type == TypeError {
83
return nil, reply.AsError()
88
func (o *ObjectProxy) WatchSignal(iface, member string) (*SignalWatch, error) {
89
return o.bus.WatchSignal(&MatchRule{
91
Sender: o.destination,
97
// Connect returns a connection to the message bus identified by busType.
98
func Connect(busType StandardBus) (*Connection, error) {
103
address = os.Getenv("DBUS_SESSION_BUS_ADDRESS")
106
if address = os.Getenv("DBUS_SYSTEM_BUS_ADDRESS"); len(address) == 0 {
107
address = "unix:path=/var/run/dbus/system_bus_socket"
111
return nil, errors.New("Unknown bus")
114
trans, err := newTransport(address)
118
bus := new(Connection)
119
if bus.conn, err = trans.Dial(); err != nil {
123
if err = authenticate(bus.conn, nil); err != nil {
128
bus.busProxy = BusDaemon{bus.Object(BUS_DAEMON_NAME, BUS_DAEMON_PATH)}
129
bus.messageFilters = []*MessageFilter{}
130
bus.methodCallReplies = make(map[uint32]chan<- *Message)
131
bus.objectPathHandlers = make(map[ObjectPath]chan<- *Message)
132
bus.signalMatchRules = make(signalWatchSet)
133
bus.nameInfo = make(map[string]*nameInfo)
136
if bus.UniqueName, err = bus.busProxy.Hello(); err != nil {
144
func (p *Connection) Authenticate() error {
145
log.Println("dbus.Connection.Authenticate() is deprecated. This call can be removed")
149
func (p *Connection) receiveLoop() {
151
msg, err := readMessage(p.conn)
154
log.Println("Failed to read message:", err)
158
if err = p.dispatchMessage(msg); err != nil {
159
log.Println("Error dispatching message:", err)
165
func (p *Connection) handlerForPath(objpath ObjectPath) (chan<- *Message, bool) {
166
p.handlerMutex.Lock()
167
defer p.handlerMutex.Unlock()
169
path := string(objpath)
170
idx := strings.LastIndex(path, "/") + 1
173
h, ok := p.objectPathHandlers[ObjectPath(path)]
180
idx = strings.LastIndex(path[:idx], "/")
181
path = path[:idx+1] + "*"
185
func (p *Connection) dispatchMessage(msg *Message) error {
186
// Run the message through the registered filters, stopping
187
// processing if a filter returns nil.
188
for _, filter := range p.messageFilters {
189
msg := filter.filter(msg)
198
case msg.Interface == "org.freedesktop.DBus.Peer" && msg.Member == "Ping":
199
reply := NewMethodReturnMessage(msg)
200
if err := p.Send(reply); err != nil {
203
case msg.Interface == "org.freedesktop.DBus.Peer" && msg.Member == "GetMachineId":
204
// Should be returning the UUID found in /var/lib/dbus/machine-id
205
fmt.Println("XXX: handle GetMachineId")
206
reply := NewMethodReturnMessage(msg)
207
if err := reply.AppendArgs("machine-id"); err != nil {
210
if err := p.Send(reply); err != nil {
214
handler, ok := p.handlerForPath(msg.Path)
218
reply := NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object path "+string(msg.Path))
219
if err := p.Send(reply); err != nil {
224
case TypeMethodReturn, TypeError:
225
p.handlerMutex.Lock()
226
rs := msg.replySerial
227
replyChan, ok := p.methodCallReplies[rs]
229
delete(p.methodCallReplies, rs)
231
p.handlerMutex.Unlock()
236
p.handlerMutex.Lock()
237
watches := p.signalMatchRules.FindMatches(msg)
238
p.handlerMutex.Unlock()
239
for _, watch := range watches {
246
func (p *Connection) Close() error {
247
return p.conn.Close()
250
func (p *Connection) nextSerial() uint32 {
251
return atomic.AddUint32(&p.lastSerial, 1)
254
func (p *Connection) Send(msg *Message) error {
255
msg.setSerial(p.nextSerial())
256
if _, err := msg.WriteTo(p.conn); err != nil {
262
func (p *Connection) SendWithReply(msg *Message) (*Message, error) {
263
// XXX: also check for "no reply" flag.
264
if msg.Type != TypeMethodCall {
265
panic("Only method calls have replies")
267
serial := p.nextSerial()
268
msg.setSerial(serial)
270
replyChan := make(chan *Message, 1)
271
p.handlerMutex.Lock()
272
p.methodCallReplies[serial] = replyChan
273
p.handlerMutex.Unlock()
275
if _, err := msg.WriteTo(p.conn); err != nil {
276
p.handlerMutex.Lock()
277
delete(p.methodCallReplies, serial)
278
p.handlerMutex.Unlock()
286
func (p *Connection) RegisterMessageFilter(filter func(*Message) *Message) *MessageFilter {
287
msgFilter := &MessageFilter{filter}
288
p.messageFilters = append(p.messageFilters, msgFilter)
292
func (p *Connection) UnregisterMessageFilter(filter *MessageFilter) {
293
for i, other := range p.messageFilters {
295
p.messageFilters = append(p.messageFilters[:i], p.messageFilters[i+1:]...)
299
panic("Message filter not registered to this bus")
302
func (p *Connection) RegisterObjectPath(path ObjectPath, handler chan<- *Message) {
303
p.handlerMutex.Lock()
304
defer p.handlerMutex.Unlock()
305
if _, ok := p.objectPathHandlers[path]; ok {
306
panic("A handler has already been registered for " + string(path))
308
p.objectPathHandlers[path] = handler
311
func (p *Connection) UnregisterObjectPath(path ObjectPath) {
312
p.handlerMutex.Lock()
313
defer p.handlerMutex.Unlock()
314
if _, ok := p.objectPathHandlers[path]; !ok {
315
panic("No handler registered for " + string(path))
317
delete(p.objectPathHandlers, path)
320
// Object returns a proxy for the object identified by the given
321
// destination address and path
322
func (p *Connection) Object(dest string, path ObjectPath) *ObjectProxy {
323
return &ObjectProxy{p, dest, path}