~vtuson/scopecreator/twitter-template

« back to all changes in this revision

Viewing changes to src/go/src/launchpad.net/go-dbus/v1/dbus.go

  • Committer: Victor Palau
  • Date: 2015-03-11 14:24:42 UTC
  • Revision ID: vtuson@gmail.com-20150311142442-f2pxp111c8ynv232
public release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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").
 
4
package dbus
 
5
 
 
6
import (
 
7
        "errors"
 
8
        "fmt"
 
9
        "io"
 
10
        "log"
 
11
        "net"
 
12
        "os"
 
13
        "strings"
 
14
        "sync"
 
15
        "sync/atomic"
 
16
)
 
17
 
 
18
type StandardBus int
 
19
 
 
20
const (
 
21
        SessionBus StandardBus = iota
 
22
        SystemBus
 
23
)
 
24
 
 
25
const (
 
26
        BUS_DAEMON_NAME  = "org.freedesktop.DBus"
 
27
        BUS_DAEMON_PATH  = ObjectPath("/org/freedesktop/DBus")
 
28
        BUS_DAEMON_IFACE = "org.freedesktop.DBus"
 
29
)
 
30
 
 
31
type MessageFilter struct {
 
32
        filter func(*Message) *Message
 
33
}
 
34
 
 
35
// Connection represents a connection to a message bus.
 
36
type Connection struct {
 
37
        // The unique name of this connection on the message bus.
 
38
        UniqueName string
 
39
        conn       net.Conn
 
40
        busProxy   BusDaemon
 
41
        lastSerial uint32
 
42
 
 
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
 
48
 
 
49
        nameInfoMutex sync.Mutex
 
50
        nameInfo      map[string]*nameInfo
 
51
}
 
52
 
 
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 {
 
57
        bus         *Connection
 
58
        destination string
 
59
        path        ObjectPath
 
60
}
 
61
 
 
62
func (o *ObjectProxy) ObjectPath() ObjectPath {
 
63
        return o.path
 
64
}
 
65
 
 
66
// Call the given method on the remote object.
 
67
//
 
68
// On success, the reply message will be returned, whose arguments can
 
69
// be unpacked with its Args() method.
 
70
//
 
71
// On failure (both network failures and D-Bus level errors), an error
 
72
// will be returned.
 
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 {
 
76
                return nil, err
 
77
        }
 
78
        reply, err := o.bus.SendWithReply(msg)
 
79
        if err != nil {
 
80
                return nil, err
 
81
        }
 
82
        if reply.Type == TypeError {
 
83
                return nil, reply.AsError()
 
84
        }
 
85
        return reply, nil
 
86
}
 
87
 
 
88
func (o *ObjectProxy) WatchSignal(iface, member string) (*SignalWatch, error) {
 
89
        return o.bus.WatchSignal(&MatchRule{
 
90
                Type:      TypeSignal,
 
91
                Sender:    o.destination,
 
92
                Path:      o.path,
 
93
                Interface: iface,
 
94
                Member:    member})
 
95
}
 
96
 
 
97
// Connect returns a connection to the message bus identified by busType.
 
98
func Connect(busType StandardBus) (*Connection, error) {
 
99
        var address string
 
100
 
 
101
        switch busType {
 
102
        case SessionBus:
 
103
                address = os.Getenv("DBUS_SESSION_BUS_ADDRESS")
 
104
 
 
105
        case SystemBus:
 
106
                if address = os.Getenv("DBUS_SYSTEM_BUS_ADDRESS"); len(address) == 0 {
 
107
                        address = "unix:path=/var/run/dbus/system_bus_socket"
 
108
                }
 
109
 
 
110
        default:
 
111
                return nil, errors.New("Unknown bus")
 
112
        }
 
113
 
 
114
        trans, err := newTransport(address)
 
115
        if err != nil {
 
116
                return nil, err
 
117
        }
 
118
        bus := new(Connection)
 
119
        if bus.conn, err = trans.Dial(); err != nil {
 
120
                return nil, err
 
121
        }
 
122
 
 
123
        if err = authenticate(bus.conn, nil); err != nil {
 
124
                bus.conn.Close()
 
125
                return nil, err
 
126
        }
 
127
 
 
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)
 
134
 
 
135
        go bus.receiveLoop()
 
136
        if bus.UniqueName, err = bus.busProxy.Hello(); err != nil {
 
137
                bus.Close()
 
138
                return nil, err
 
139
        }
 
140
 
 
141
        return bus, nil
 
142
}
 
143
 
 
144
func (p *Connection) Authenticate() error {
 
145
        log.Println("dbus.Connection.Authenticate() is deprecated.  This call can be removed")
 
146
        return nil
 
147
}
 
148
 
 
149
func (p *Connection) receiveLoop() {
 
150
        for {
 
151
                msg, err := readMessage(p.conn)
 
152
                if err != nil {
 
153
                        if err != io.EOF {
 
154
                                log.Println("Failed to read message:", err)
 
155
                        }
 
156
                        break
 
157
                }
 
158
                if err = p.dispatchMessage(msg); err != nil {
 
159
                        log.Println("Error dispatching message:", err)
 
160
                        break
 
161
                }
 
162
        }
 
163
}
 
164
 
 
165
func (p *Connection) handlerForPath(objpath ObjectPath) (chan<- *Message, bool) {
 
166
        p.handlerMutex.Lock()
 
167
        defer p.handlerMutex.Unlock()
 
168
 
 
169
        path := string(objpath)
 
170
        idx := strings.LastIndex(path, "/") + 1
 
171
 
 
172
        for {
 
173
                h, ok := p.objectPathHandlers[ObjectPath(path)]
 
174
                if ok {
 
175
                        return h, true
 
176
                }
 
177
                if idx < 1 {
 
178
                        return nil, false
 
179
                }
 
180
                idx = strings.LastIndex(path[:idx], "/")
 
181
                path = path[:idx+1] + "*"
 
182
        }
 
183
}
 
184
 
 
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)
 
190
                if msg == nil {
 
191
                        return nil
 
192
                }
 
193
        }
 
194
 
 
195
        switch msg.Type {
 
196
        case TypeMethodCall:
 
197
                switch {
 
198
                case msg.Interface == "org.freedesktop.DBus.Peer" && msg.Member == "Ping":
 
199
                        reply := NewMethodReturnMessage(msg)
 
200
                        if err := p.Send(reply); err != nil {
 
201
                                return err
 
202
                        }
 
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 {
 
208
                                return err
 
209
                        }
 
210
                        if err := p.Send(reply); err != nil {
 
211
                                return err
 
212
                        }
 
213
                default:
 
214
                        handler, ok := p.handlerForPath(msg.Path)
 
215
                        if ok {
 
216
                                handler <- msg
 
217
                        } else {
 
218
                                reply := NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object path "+string(msg.Path))
 
219
                                if err := p.Send(reply); err != nil {
 
220
                                        return err
 
221
                                }
 
222
                        }
 
223
                }
 
224
        case TypeMethodReturn, TypeError:
 
225
                p.handlerMutex.Lock()
 
226
                rs := msg.replySerial
 
227
                replyChan, ok := p.methodCallReplies[rs]
 
228
                if ok {
 
229
                        delete(p.methodCallReplies, rs)
 
230
                }
 
231
                p.handlerMutex.Unlock()
 
232
                if ok {
 
233
                        replyChan <- msg
 
234
                }
 
235
        case TypeSignal:
 
236
                p.handlerMutex.Lock()
 
237
                watches := p.signalMatchRules.FindMatches(msg)
 
238
                p.handlerMutex.Unlock()
 
239
                for _, watch := range watches {
 
240
                        watch.C <- msg
 
241
                }
 
242
        }
 
243
        return nil
 
244
}
 
245
 
 
246
func (p *Connection) Close() error {
 
247
        return p.conn.Close()
 
248
}
 
249
 
 
250
func (p *Connection) nextSerial() uint32 {
 
251
        return atomic.AddUint32(&p.lastSerial, 1)
 
252
}
 
253
 
 
254
func (p *Connection) Send(msg *Message) error {
 
255
        msg.setSerial(p.nextSerial())
 
256
        if _, err := msg.WriteTo(p.conn); err != nil {
 
257
                return err
 
258
        }
 
259
        return nil
 
260
}
 
261
 
 
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")
 
266
        }
 
267
        serial := p.nextSerial()
 
268
        msg.setSerial(serial)
 
269
 
 
270
        replyChan := make(chan *Message, 1)
 
271
        p.handlerMutex.Lock()
 
272
        p.methodCallReplies[serial] = replyChan
 
273
        p.handlerMutex.Unlock()
 
274
 
 
275
        if _, err := msg.WriteTo(p.conn); err != nil {
 
276
                p.handlerMutex.Lock()
 
277
                delete(p.methodCallReplies, serial)
 
278
                p.handlerMutex.Unlock()
 
279
                return nil, err
 
280
        }
 
281
 
 
282
        reply := <-replyChan
 
283
        return reply, nil
 
284
}
 
285
 
 
286
func (p *Connection) RegisterMessageFilter(filter func(*Message) *Message) *MessageFilter {
 
287
        msgFilter := &MessageFilter{filter}
 
288
        p.messageFilters = append(p.messageFilters, msgFilter)
 
289
        return msgFilter
 
290
}
 
291
 
 
292
func (p *Connection) UnregisterMessageFilter(filter *MessageFilter) {
 
293
        for i, other := range p.messageFilters {
 
294
                if other == filter {
 
295
                        p.messageFilters = append(p.messageFilters[:i], p.messageFilters[i+1:]...)
 
296
                        return
 
297
                }
 
298
        }
 
299
        panic("Message filter not registered to this bus")
 
300
}
 
301
 
 
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))
 
307
        }
 
308
        p.objectPathHandlers[path] = handler
 
309
}
 
310
 
 
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))
 
316
        }
 
317
        delete(p.objectPathHandlers, path)
 
318
}
 
319
 
 
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}
 
324
}