1package dbus
2
3import (
4	"errors"
5	"io"
6	"os"
7	"reflect"
8	"strings"
9	"sync"
10)
11
12const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
13
14var (
15	systemBus     *Conn
16	systemBusLck  sync.Mutex
17	sessionBus    *Conn
18	sessionBusLck sync.Mutex
19)
20
21// ErrClosed is the error returned by calls on a closed connection.
22var ErrClosed = errors.New("dbus: connection closed by user")
23
24// Conn represents a connection to a message bus (usually, the system or
25// session bus).
26//
27// Connections are either shared or private. Shared connections
28// are shared between calls to the functions that return them. As a result,
29// the methods Close, Auth and Hello must not be called on them.
30//
31// Multiple goroutines may invoke methods on a connection simultaneously.
32type Conn struct {
33	transport
34
35	busObj BusObject
36	unixFD bool
37	uuid   string
38
39	names    []string
40	namesLck sync.RWMutex
41
42	serialLck  sync.Mutex
43	nextSerial uint32
44	serialUsed map[uint32]bool
45
46	calls    map[uint32]*Call
47	callsLck sync.RWMutex
48
49	handlers    map[ObjectPath]map[string]exportWithMapping
50	handlersLck sync.RWMutex
51
52	out    chan *Message
53	closed bool
54	outLck sync.RWMutex
55
56	signals    []chan<- *Signal
57	signalsLck sync.Mutex
58
59	eavesdropped    chan<- *Message
60	eavesdroppedLck sync.Mutex
61}
62
63// SessionBus returns a shared connection to the session bus, connecting to it
64// if not already done.
65func SessionBus() (conn *Conn, err error) {
66	sessionBusLck.Lock()
67	defer sessionBusLck.Unlock()
68	if sessionBus != nil {
69		return sessionBus, nil
70	}
71	defer func() {
72		if conn != nil {
73			sessionBus = conn
74		}
75	}()
76	conn, err = SessionBusPrivate()
77	if err != nil {
78		return
79	}
80	if err = conn.Auth(nil); err != nil {
81		conn.Close()
82		conn = nil
83		return
84	}
85	if err = conn.Hello(); err != nil {
86		conn.Close()
87		conn = nil
88	}
89	return
90}
91
92// SessionBusPrivate returns a new private connection to the session bus.
93func SessionBusPrivate() (*Conn, error) {
94	address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
95	if address != "" && address != "autolaunch:" {
96		return Dial(address)
97	}
98
99	return sessionBusPlatform()
100}
101
102// SystemBus returns a shared connection to the system bus, connecting to it if
103// not already done.
104func SystemBus() (conn *Conn, err error) {
105	systemBusLck.Lock()
106	defer systemBusLck.Unlock()
107	if systemBus != nil {
108		return systemBus, nil
109	}
110	defer func() {
111		if conn != nil {
112			systemBus = conn
113		}
114	}()
115	conn, err = SystemBusPrivate()
116	if err != nil {
117		return
118	}
119	if err = conn.Auth(nil); err != nil {
120		conn.Close()
121		conn = nil
122		return
123	}
124	if err = conn.Hello(); err != nil {
125		conn.Close()
126		conn = nil
127	}
128	return
129}
130
131// SystemBusPrivate returns a new private connection to the system bus.
132func SystemBusPrivate() (*Conn, error) {
133	address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
134	if address != "" {
135		return Dial(address)
136	}
137	return Dial(defaultSystemBusAddress)
138}
139
140// Dial establishes a new private connection to the message bus specified by address.
141func Dial(address string) (*Conn, error) {
142	tr, err := getTransport(address)
143	if err != nil {
144		return nil, err
145	}
146	return newConn(tr)
147}
148
149// NewConn creates a new private *Conn from an already established connection.
150func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
151	return newConn(genericTransport{conn})
152}
153
154// newConn creates a new *Conn from a transport.
155func newConn(tr transport) (*Conn, error) {
156	conn := new(Conn)
157	conn.transport = tr
158	conn.calls = make(map[uint32]*Call)
159	conn.out = make(chan *Message, 10)
160	conn.handlers = make(map[ObjectPath]map[string]exportWithMapping)
161	conn.nextSerial = 1
162	conn.serialUsed = map[uint32]bool{0: true}
163	conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
164	return conn, nil
165}
166
167// BusObject returns the object owned by the bus daemon which handles
168// administrative requests.
169func (conn *Conn) BusObject() BusObject {
170	return conn.busObj
171}
172
173// Close closes the connection. Any blocked operations will return with errors
174// and the channels passed to Eavesdrop and Signal are closed. This method must
175// not be called on shared connections.
176func (conn *Conn) Close() error {
177	conn.outLck.Lock()
178	if conn.closed {
179		// inWorker calls Close on read error, the read error may
180		// be caused by another caller calling Close to shutdown the
181		// dbus connection, a double-close scenario we prevent here.
182		conn.outLck.Unlock()
183		return nil
184	}
185	close(conn.out)
186	conn.closed = true
187	conn.outLck.Unlock()
188	conn.signalsLck.Lock()
189	for _, ch := range conn.signals {
190		close(ch)
191	}
192	conn.signalsLck.Unlock()
193	conn.eavesdroppedLck.Lock()
194	if conn.eavesdropped != nil {
195		close(conn.eavesdropped)
196	}
197	conn.eavesdroppedLck.Unlock()
198	return conn.transport.Close()
199}
200
201// Eavesdrop causes conn to send all incoming messages to the given channel
202// without further processing. Method replies, errors and signals will not be
203// sent to the appropiate channels and method calls will not be handled. If nil
204// is passed, the normal behaviour is restored.
205//
206// The caller has to make sure that ch is sufficiently buffered;
207// if a message arrives when a write to ch is not possible, the message is
208// discarded.
209func (conn *Conn) Eavesdrop(ch chan<- *Message) {
210	conn.eavesdroppedLck.Lock()
211	conn.eavesdropped = ch
212	conn.eavesdroppedLck.Unlock()
213}
214
215// getSerial returns an unused serial.
216func (conn *Conn) getSerial() uint32 {
217	conn.serialLck.Lock()
218	defer conn.serialLck.Unlock()
219	n := conn.nextSerial
220	for conn.serialUsed[n] {
221		n++
222	}
223	conn.serialUsed[n] = true
224	conn.nextSerial = n + 1
225	return n
226}
227
228// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
229// called after authentication, but before sending any other messages to the
230// bus. Hello must not be called for shared connections.
231func (conn *Conn) Hello() error {
232	var s string
233	err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
234	if err != nil {
235		return err
236	}
237	conn.namesLck.Lock()
238	conn.names = make([]string, 1)
239	conn.names[0] = s
240	conn.namesLck.Unlock()
241	return nil
242}
243
244// inWorker runs in an own goroutine, reading incoming messages from the
245// transport and dispatching them appropiately.
246func (conn *Conn) inWorker() {
247	for {
248		msg, err := conn.ReadMessage()
249		if err == nil {
250			conn.eavesdroppedLck.Lock()
251			if conn.eavesdropped != nil {
252				select {
253				case conn.eavesdropped <- msg:
254				default:
255				}
256				conn.eavesdroppedLck.Unlock()
257				continue
258			}
259			conn.eavesdroppedLck.Unlock()
260			dest, _ := msg.Headers[FieldDestination].value.(string)
261			found := false
262			if dest == "" {
263				found = true
264			} else {
265				conn.namesLck.RLock()
266				if len(conn.names) == 0 {
267					found = true
268				}
269				for _, v := range conn.names {
270					if dest == v {
271						found = true
272						break
273					}
274				}
275				conn.namesLck.RUnlock()
276			}
277			if !found {
278				// Eavesdropped a message, but no channel for it is registered.
279				// Ignore it.
280				continue
281			}
282			switch msg.Type {
283			case TypeMethodReply, TypeError:
284				serial := msg.Headers[FieldReplySerial].value.(uint32)
285				conn.callsLck.Lock()
286				if c, ok := conn.calls[serial]; ok {
287					if msg.Type == TypeError {
288						name, _ := msg.Headers[FieldErrorName].value.(string)
289						c.Err = Error{name, msg.Body}
290					} else {
291						c.Body = msg.Body
292					}
293					c.Done <- c
294					conn.serialLck.Lock()
295					delete(conn.serialUsed, serial)
296					conn.serialLck.Unlock()
297					delete(conn.calls, serial)
298				}
299				conn.callsLck.Unlock()
300			case TypeSignal:
301				iface := msg.Headers[FieldInterface].value.(string)
302				member := msg.Headers[FieldMember].value.(string)
303				// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
304				// sender is optional for signals.
305				sender, _ := msg.Headers[FieldSender].value.(string)
306				if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
307					if member == "NameLost" {
308						// If we lost the name on the bus, remove it from our
309						// tracking list.
310						name, ok := msg.Body[0].(string)
311						if !ok {
312							panic("Unable to read the lost name")
313						}
314						conn.namesLck.Lock()
315						for i, v := range conn.names {
316							if v == name {
317								conn.names = append(conn.names[:i],
318									conn.names[i+1:]...)
319							}
320						}
321						conn.namesLck.Unlock()
322					} else if member == "NameAcquired" {
323						// If we acquired the name on the bus, add it to our
324						// tracking list.
325						name, ok := msg.Body[0].(string)
326						if !ok {
327							panic("Unable to read the acquired name")
328						}
329						conn.namesLck.Lock()
330						conn.names = append(conn.names, name)
331						conn.namesLck.Unlock()
332					}
333				}
334				signal := &Signal{
335					Sender: sender,
336					Path:   msg.Headers[FieldPath].value.(ObjectPath),
337					Name:   iface + "." + member,
338					Body:   msg.Body,
339				}
340				conn.signalsLck.Lock()
341				for _, ch := range conn.signals {
342					ch <- signal
343				}
344				conn.signalsLck.Unlock()
345			case TypeMethodCall:
346				go conn.handleCall(msg)
347			}
348		} else if _, ok := err.(InvalidMessageError); !ok {
349			// Some read error occured (usually EOF); we can't really do
350			// anything but to shut down all stuff and returns errors to all
351			// pending replies.
352			conn.Close()
353			conn.callsLck.RLock()
354			for _, v := range conn.calls {
355				v.Err = err
356				v.Done <- v
357			}
358			conn.callsLck.RUnlock()
359			return
360		}
361		// invalid messages are ignored
362	}
363}
364
365// Names returns the list of all names that are currently owned by this
366// connection. The slice is always at least one element long, the first element
367// being the unique name of the connection.
368func (conn *Conn) Names() []string {
369	conn.namesLck.RLock()
370	// copy the slice so it can't be modified
371	s := make([]string, len(conn.names))
372	copy(s, conn.names)
373	conn.namesLck.RUnlock()
374	return s
375}
376
377// Object returns the object identified by the given destination name and path.
378func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
379	return &Object{conn, dest, path}
380}
381
382// outWorker runs in an own goroutine, encoding and sending messages that are
383// sent to conn.out.
384func (conn *Conn) outWorker() {
385	for msg := range conn.out {
386		err := conn.SendMessage(msg)
387		conn.callsLck.RLock()
388		if err != nil {
389			if c := conn.calls[msg.serial]; c != nil {
390				c.Err = err
391				c.Done <- c
392			}
393			conn.serialLck.Lock()
394			delete(conn.serialUsed, msg.serial)
395			conn.serialLck.Unlock()
396		} else if msg.Type != TypeMethodCall {
397			conn.serialLck.Lock()
398			delete(conn.serialUsed, msg.serial)
399			conn.serialLck.Unlock()
400		}
401		conn.callsLck.RUnlock()
402	}
403}
404
405// Send sends the given message to the message bus. You usually don't need to
406// use this; use the higher-level equivalents (Call / Go, Emit and Export)
407// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
408// call is returned and the same value is sent to ch (which must be buffered)
409// once the call is complete. Otherwise, ch is ignored and a Call structure is
410// returned of which only the Err member is valid.
411func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
412	var call *Call
413
414	msg.serial = conn.getSerial()
415	if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
416		if ch == nil {
417			ch = make(chan *Call, 5)
418		} else if cap(ch) == 0 {
419			panic("dbus: unbuffered channel passed to (*Conn).Send")
420		}
421		call = new(Call)
422		call.Destination, _ = msg.Headers[FieldDestination].value.(string)
423		call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
424		iface, _ := msg.Headers[FieldInterface].value.(string)
425		member, _ := msg.Headers[FieldMember].value.(string)
426		call.Method = iface + "." + member
427		call.Args = msg.Body
428		call.Done = ch
429		conn.callsLck.Lock()
430		conn.calls[msg.serial] = call
431		conn.callsLck.Unlock()
432		conn.outLck.RLock()
433		if conn.closed {
434			call.Err = ErrClosed
435			call.Done <- call
436		} else {
437			conn.out <- msg
438		}
439		conn.outLck.RUnlock()
440	} else {
441		conn.outLck.RLock()
442		if conn.closed {
443			call = &Call{Err: ErrClosed}
444		} else {
445			conn.out <- msg
446			call = &Call{Err: nil}
447		}
448		conn.outLck.RUnlock()
449	}
450	return call
451}
452
453// sendError creates an error message corresponding to the parameters and sends
454// it to conn.out.
455func (conn *Conn) sendError(e Error, dest string, serial uint32) {
456	msg := new(Message)
457	msg.Type = TypeError
458	msg.serial = conn.getSerial()
459	msg.Headers = make(map[HeaderField]Variant)
460	if dest != "" {
461		msg.Headers[FieldDestination] = MakeVariant(dest)
462	}
463	msg.Headers[FieldErrorName] = MakeVariant(e.Name)
464	msg.Headers[FieldReplySerial] = MakeVariant(serial)
465	msg.Body = e.Body
466	if len(e.Body) > 0 {
467		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
468	}
469	conn.outLck.RLock()
470	if !conn.closed {
471		conn.out <- msg
472	}
473	conn.outLck.RUnlock()
474}
475
476// sendReply creates a method reply message corresponding to the parameters and
477// sends it to conn.out.
478func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
479	msg := new(Message)
480	msg.Type = TypeMethodReply
481	msg.serial = conn.getSerial()
482	msg.Headers = make(map[HeaderField]Variant)
483	if dest != "" {
484		msg.Headers[FieldDestination] = MakeVariant(dest)
485	}
486	msg.Headers[FieldReplySerial] = MakeVariant(serial)
487	msg.Body = values
488	if len(values) > 0 {
489		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
490	}
491	conn.outLck.RLock()
492	if !conn.closed {
493		conn.out <- msg
494	}
495	conn.outLck.RUnlock()
496}
497
498// Signal registers the given channel to be passed all received signal messages.
499// The caller has to make sure that ch is sufficiently buffered; if a message
500// arrives when a write to c is not possible, it is discarded.
501//
502// Multiple of these channels can be registered at the same time. Passing a
503// channel that already is registered will remove it from the list of the
504// registered channels.
505//
506// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
507// channel for eavesdropped messages, this channel receives all signals, and
508// none of the channels passed to Signal will receive any signals.
509func (conn *Conn) Signal(ch chan<- *Signal) {
510	conn.signalsLck.Lock()
511	conn.signals = append(conn.signals, ch)
512	conn.signalsLck.Unlock()
513}
514
515// SupportsUnixFDs returns whether the underlying transport supports passing of
516// unix file descriptors. If this is false, method calls containing unix file
517// descriptors will return an error and emitted signals containing them will
518// not be sent.
519func (conn *Conn) SupportsUnixFDs() bool {
520	return conn.unixFD
521}
522
523// Error represents a D-Bus message of type Error.
524type Error struct {
525	Name string
526	Body []interface{}
527}
528
529func NewError(name string, body []interface{}) *Error {
530	return &Error{name, body}
531}
532
533func (e Error) Error() string {
534	if len(e.Body) >= 1 {
535		s, ok := e.Body[0].(string)
536		if ok {
537			return s
538		}
539	}
540	return e.Name
541}
542
543// Signal represents a D-Bus message of type Signal. The name member is given in
544// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
545type Signal struct {
546	Sender string
547	Path   ObjectPath
548	Name   string
549	Body   []interface{}
550}
551
552// transport is a D-Bus transport.
553type transport interface {
554	// Read and Write raw data (for example, for the authentication protocol).
555	io.ReadWriteCloser
556
557	// Send the initial null byte used for the EXTERNAL mechanism.
558	SendNullByte() error
559
560	// Returns whether this transport supports passing Unix FDs.
561	SupportsUnixFDs() bool
562
563	// Signal the transport that Unix FD passing is enabled for this connection.
564	EnableUnixFDs()
565
566	// Read / send a message, handling things like Unix FDs.
567	ReadMessage() (*Message, error)
568	SendMessage(*Message) error
569}
570
571var (
572	transports = make(map[string]func(string) (transport, error))
573)
574
575func getTransport(address string) (transport, error) {
576	var err error
577	var t transport
578
579	addresses := strings.Split(address, ";")
580	for _, v := range addresses {
581		i := strings.IndexRune(v, ':')
582		if i == -1 {
583			err = errors.New("dbus: invalid bus address (no transport)")
584			continue
585		}
586		f := transports[v[:i]]
587		if f == nil {
588			err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
589			continue
590		}
591		t, err = f(v[i+1:])
592		if err == nil {
593			return t, nil
594		}
595	}
596	return nil, err
597}
598
599// dereferenceAll returns a slice that, assuming that vs is a slice of pointers
600// of arbitrary types, containes the values that are obtained from dereferencing
601// all elements in vs.
602func dereferenceAll(vs []interface{}) []interface{} {
603	for i := range vs {
604		v := reflect.ValueOf(vs[i])
605		v = v.Elem()
606		vs[i] = v.Interface()
607	}
608	return vs
609}
610
611// getKey gets a key from a the list of keys. Returns "" on error / not found...
612func getKey(s, key string) string {
613	i := strings.Index(s, key)
614	if i == -1 {
615		return ""
616	}
617	if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' {
618		return ""
619	}
620	j := strings.Index(s, ",")
621	if j == -1 {
622		j = len(s)
623	}
624	return s[i+len(key)+1 : j]
625}
626