1package dbus
2
3import (
4	"context"
5	"errors"
6	"io"
7	"os"
8	"reflect"
9	"strings"
10	"sync"
11)
12
13var (
14	systemBus     *Conn
15	systemBusLck  sync.Mutex
16	sessionBus    *Conn
17	sessionBusLck sync.Mutex
18)
19
20// ErrClosed is the error returned by calls on a closed connection.
21var ErrClosed = errors.New("dbus: connection closed by user")
22
23// Conn represents a connection to a message bus (usually, the system or
24// session bus).
25//
26// Connections are either shared or private. Shared connections
27// are shared between calls to the functions that return them. As a result,
28// the methods Close, Auth and Hello must not be called on them.
29//
30// Multiple goroutines may invoke methods on a connection simultaneously.
31type Conn struct {
32	transport
33
34	busObj BusObject
35	unixFD bool
36	uuid   string
37
38	handler       Handler
39	signalHandler SignalHandler
40	serialGen     SerialGenerator
41
42	names      *nameTracker
43	calls      *callTracker
44	outHandler *outputHandler
45
46	eavesdropped    chan<- *Message
47	eavesdroppedLck sync.Mutex
48}
49
50// SessionBus returns a shared connection to the session bus, connecting to it
51// if not already done.
52func SessionBus() (conn *Conn, err error) {
53	sessionBusLck.Lock()
54	defer sessionBusLck.Unlock()
55	if sessionBus != nil {
56		return sessionBus, nil
57	}
58	defer func() {
59		if conn != nil {
60			sessionBus = conn
61		}
62	}()
63	conn, err = SessionBusPrivate()
64	if err != nil {
65		return
66	}
67	if err = conn.Auth(nil); err != nil {
68		conn.Close()
69		conn = nil
70		return
71	}
72	if err = conn.Hello(); err != nil {
73		conn.Close()
74		conn = nil
75	}
76	return
77}
78
79func getSessionBusAddress() (string, error) {
80	if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
81		return address, nil
82
83	} else if address := tryDiscoverDbusSessionBusAddress(); address != "" {
84		os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
85		return address, nil
86	}
87	return getSessionBusPlatformAddress()
88}
89
90// SessionBusPrivate returns a new private connection to the session bus.
91func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
92	address, err := getSessionBusAddress()
93	if err != nil {
94		return nil, err
95	}
96
97	return Dial(address, opts...)
98}
99
100// SessionBusPrivate returns a new private connection to the session bus.
101//
102// Deprecated: use SessionBusPrivate with options instead.
103func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
104	return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
105}
106
107// SystemBus returns a shared connection to the system bus, connecting to it if
108// not already done.
109func SystemBus() (conn *Conn, err error) {
110	systemBusLck.Lock()
111	defer systemBusLck.Unlock()
112	if systemBus != nil {
113		return systemBus, nil
114	}
115	defer func() {
116		if conn != nil {
117			systemBus = conn
118		}
119	}()
120	conn, err = SystemBusPrivate()
121	if err != nil {
122		return
123	}
124	if err = conn.Auth(nil); err != nil {
125		conn.Close()
126		conn = nil
127		return
128	}
129	if err = conn.Hello(); err != nil {
130		conn.Close()
131		conn = nil
132	}
133	return
134}
135
136// SystemBusPrivate returns a new private connection to the system bus.
137// Note: this connection is not ready to use. One must perform Auth and Hello
138// on the connection before it is useable.
139func SystemBusPrivate(opts ...ConnOption) (*Conn, error) {
140	return Dial(getSystemBusPlatformAddress(), opts...)
141}
142
143// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers.
144//
145// Deprecated: use SystemBusPrivate with options instead.
146func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
147	return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
148}
149
150// Dial establishes a new private connection to the message bus specified by address.
151func Dial(address string, opts ...ConnOption) (*Conn, error) {
152	tr, err := getTransport(address)
153	if err != nil {
154		return nil, err
155	}
156	return newConn(tr, opts...)
157}
158
159// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers.
160//
161// Deprecated: use Dial with options instead.
162func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
163	return Dial(address, WithSignalHandler(signalHandler))
164}
165
166// ConnOption is a connection option.
167type ConnOption func(conn *Conn) error
168
169// WithHandler overrides the default handler.
170func WithHandler(handler Handler) ConnOption {
171	return func(conn *Conn) error {
172		conn.handler = handler
173		return nil
174	}
175}
176
177// WithSignalHandler overrides the default signal handler.
178func WithSignalHandler(handler SignalHandler) ConnOption {
179	return func(conn *Conn) error {
180		conn.signalHandler = handler
181		return nil
182	}
183}
184
185// WithSerialGenerator overrides the default signals generator.
186func WithSerialGenerator(gen SerialGenerator) ConnOption {
187	return func(conn *Conn) error {
188		conn.serialGen = gen
189		return nil
190	}
191}
192
193// NewConn creates a new private *Conn from an already established connection.
194func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) {
195	return newConn(genericTransport{conn}, opts...)
196}
197
198// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers.
199//
200// Deprecated: use NewConn with options instead.
201func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) {
202	return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler))
203}
204
205// newConn creates a new *Conn from a transport.
206func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
207	conn := new(Conn)
208	conn.transport = tr
209	for _, opt := range opts {
210		if err := opt(conn); err != nil {
211			return nil, err
212		}
213	}
214	conn.calls = newCallTracker()
215	if conn.handler == nil {
216		conn.handler = NewDefaultHandler()
217	}
218	if conn.signalHandler == nil {
219		conn.signalHandler = NewDefaultSignalHandler()
220	}
221	if conn.serialGen == nil {
222		conn.serialGen = newSerialGenerator()
223	}
224	conn.outHandler = &outputHandler{conn: conn}
225	conn.names = newNameTracker()
226	conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
227	return conn, nil
228}
229
230// BusObject returns the object owned by the bus daemon which handles
231// administrative requests.
232func (conn *Conn) BusObject() BusObject {
233	return conn.busObj
234}
235
236// Close closes the connection. Any blocked operations will return with errors
237// and the channels passed to Eavesdrop and Signal are closed. This method must
238// not be called on shared connections.
239func (conn *Conn) Close() error {
240	conn.outHandler.close()
241	if term, ok := conn.signalHandler.(Terminator); ok {
242		term.Terminate()
243	}
244
245	if term, ok := conn.handler.(Terminator); ok {
246		term.Terminate()
247	}
248
249	conn.eavesdroppedLck.Lock()
250	if conn.eavesdropped != nil {
251		close(conn.eavesdropped)
252	}
253	conn.eavesdroppedLck.Unlock()
254
255	return conn.transport.Close()
256}
257
258// Eavesdrop causes conn to send all incoming messages to the given channel
259// without further processing. Method replies, errors and signals will not be
260// sent to the appropiate channels and method calls will not be handled. If nil
261// is passed, the normal behaviour is restored.
262//
263// The caller has to make sure that ch is sufficiently buffered;
264// if a message arrives when a write to ch is not possible, the message is
265// discarded.
266func (conn *Conn) Eavesdrop(ch chan<- *Message) {
267	conn.eavesdroppedLck.Lock()
268	conn.eavesdropped = ch
269	conn.eavesdroppedLck.Unlock()
270}
271
272// getSerial returns an unused serial.
273func (conn *Conn) getSerial() uint32 {
274	return conn.serialGen.GetSerial()
275}
276
277// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
278// called after authentication, but before sending any other messages to the
279// bus. Hello must not be called for shared connections.
280func (conn *Conn) Hello() error {
281	var s string
282	err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
283	if err != nil {
284		return err
285	}
286	conn.names.acquireUniqueConnectionName(s)
287	return nil
288}
289
290// inWorker runs in an own goroutine, reading incoming messages from the
291// transport and dispatching them appropiately.
292func (conn *Conn) inWorker() {
293	for {
294		msg, err := conn.ReadMessage()
295		if err != nil {
296			if _, ok := err.(InvalidMessageError); !ok {
297				// Some read error occured (usually EOF); we can't really do
298				// anything but to shut down all stuff and returns errors to all
299				// pending replies.
300				conn.Close()
301				conn.calls.finalizeAllWithError(err)
302				return
303			}
304			// invalid messages are ignored
305			continue
306		}
307		conn.eavesdroppedLck.Lock()
308		if conn.eavesdropped != nil {
309			select {
310			case conn.eavesdropped <- msg:
311			default:
312			}
313			conn.eavesdroppedLck.Unlock()
314			continue
315		}
316		conn.eavesdroppedLck.Unlock()
317		dest, _ := msg.Headers[FieldDestination].value.(string)
318		found := dest == "" ||
319			!conn.names.uniqueNameIsKnown() ||
320			conn.names.isKnownName(dest)
321		if !found {
322			// Eavesdropped a message, but no channel for it is registered.
323			// Ignore it.
324			continue
325		}
326		switch msg.Type {
327		case TypeError:
328			conn.serialGen.RetireSerial(conn.calls.handleDBusError(msg))
329		case TypeMethodReply:
330			conn.serialGen.RetireSerial(conn.calls.handleReply(msg))
331		case TypeSignal:
332			conn.handleSignal(msg)
333		case TypeMethodCall:
334			go conn.handleCall(msg)
335		}
336
337	}
338}
339
340func (conn *Conn) handleSignal(msg *Message) {
341	iface := msg.Headers[FieldInterface].value.(string)
342	member := msg.Headers[FieldMember].value.(string)
343	// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
344	// sender is optional for signals.
345	sender, _ := msg.Headers[FieldSender].value.(string)
346	if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
347		if member == "NameLost" {
348			// If we lost the name on the bus, remove it from our
349			// tracking list.
350			name, ok := msg.Body[0].(string)
351			if !ok {
352				panic("Unable to read the lost name")
353			}
354			conn.names.loseName(name)
355		} else if member == "NameAcquired" {
356			// If we acquired the name on the bus, add it to our
357			// tracking list.
358			name, ok := msg.Body[0].(string)
359			if !ok {
360				panic("Unable to read the acquired name")
361			}
362			conn.names.acquireName(name)
363		}
364	}
365	signal := &Signal{
366		Sender: sender,
367		Path:   msg.Headers[FieldPath].value.(ObjectPath),
368		Name:   iface + "." + member,
369		Body:   msg.Body,
370	}
371	conn.signalHandler.DeliverSignal(iface, member, signal)
372}
373
374// Names returns the list of all names that are currently owned by this
375// connection. The slice is always at least one element long, the first element
376// being the unique name of the connection.
377func (conn *Conn) Names() []string {
378	return conn.names.listKnownNames()
379}
380
381// Object returns the object identified by the given destination name and path.
382func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
383	return &Object{conn, dest, path}
384}
385
386func (conn *Conn) sendMessage(msg *Message) {
387	conn.sendMessageAndIfClosed(msg, func() {})
388}
389
390func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
391	err := conn.outHandler.sendAndIfClosed(msg, ifClosed)
392	conn.calls.handleSendError(msg, err)
393	if err != nil {
394		conn.serialGen.RetireSerial(msg.serial)
395	} else if msg.Type != TypeMethodCall {
396		conn.serialGen.RetireSerial(msg.serial)
397	}
398}
399
400// Send sends the given message to the message bus. You usually don't need to
401// use this; use the higher-level equivalents (Call / Go, Emit and Export)
402// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
403// call is returned and the same value is sent to ch (which must be buffered)
404// once the call is complete. Otherwise, ch is ignored and a Call structure is
405// returned of which only the Err member is valid.
406func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
407	return conn.send(context.Background(), msg, ch)
408}
409
410// SendWithContext acts like Send but takes a context
411func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call {
412	return conn.send(ctx, msg, ch)
413}
414
415func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
416	if ctx == nil {
417		panic("nil context")
418	}
419
420	var call *Call
421	ctx, canceler := context.WithCancel(ctx)
422	msg.serial = conn.getSerial()
423	if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
424		if ch == nil {
425			ch = make(chan *Call, 5)
426		} else if cap(ch) == 0 {
427			panic("dbus: unbuffered channel passed to (*Conn).Send")
428		}
429		call = new(Call)
430		call.Destination, _ = msg.Headers[FieldDestination].value.(string)
431		call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
432		iface, _ := msg.Headers[FieldInterface].value.(string)
433		member, _ := msg.Headers[FieldMember].value.(string)
434		call.Method = iface + "." + member
435		call.Args = msg.Body
436		call.Done = ch
437		call.ctx = ctx
438		call.ctxCanceler = canceler
439		conn.calls.track(msg.serial, call)
440		go func() {
441			<-ctx.Done()
442			conn.calls.handleSendError(msg, ctx.Err())
443		}()
444		conn.sendMessageAndIfClosed(msg, func() {
445			conn.calls.handleSendError(msg, ErrClosed)
446			canceler()
447		})
448	} else {
449		canceler()
450		call = &Call{Err: nil}
451		conn.sendMessageAndIfClosed(msg, func() {
452			call = &Call{Err: ErrClosed}
453		})
454	}
455	return call
456}
457
458// sendError creates an error message corresponding to the parameters and sends
459// it to conn.out.
460func (conn *Conn) sendError(err error, dest string, serial uint32) {
461	var e *Error
462	switch em := err.(type) {
463	case Error:
464		e = &em
465	case *Error:
466		e = em
467	case DBusError:
468		name, body := em.DBusError()
469		e = NewError(name, body)
470	default:
471		e = MakeFailedError(err)
472	}
473	msg := new(Message)
474	msg.Type = TypeError
475	msg.serial = conn.getSerial()
476	msg.Headers = make(map[HeaderField]Variant)
477	if dest != "" {
478		msg.Headers[FieldDestination] = MakeVariant(dest)
479	}
480	msg.Headers[FieldErrorName] = MakeVariant(e.Name)
481	msg.Headers[FieldReplySerial] = MakeVariant(serial)
482	msg.Body = e.Body
483	if len(e.Body) > 0 {
484		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
485	}
486	conn.sendMessage(msg)
487}
488
489// sendReply creates a method reply message corresponding to the parameters and
490// sends it to conn.out.
491func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
492	msg := new(Message)
493	msg.Type = TypeMethodReply
494	msg.serial = conn.getSerial()
495	msg.Headers = make(map[HeaderField]Variant)
496	if dest != "" {
497		msg.Headers[FieldDestination] = MakeVariant(dest)
498	}
499	msg.Headers[FieldReplySerial] = MakeVariant(serial)
500	msg.Body = values
501	if len(values) > 0 {
502		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
503	}
504	conn.sendMessage(msg)
505}
506
507// AddMatchSignal registers the given match rule to receive broadcast
508// signals based on their contents.
509func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
510	options = append([]MatchOption{withMatchType("signal")}, options...)
511	return conn.busObj.Call(
512		"org.freedesktop.DBus.AddMatch", 0,
513		formatMatchOptions(options),
514	).Store()
515}
516
517// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
518func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
519	options = append([]MatchOption{withMatchType("signal")}, options...)
520	return conn.busObj.Call(
521		"org.freedesktop.DBus.RemoveMatch", 0,
522		formatMatchOptions(options),
523	).Store()
524}
525
526// Signal registers the given channel to be passed all received signal messages.
527//
528// Multiple of these channels can be registered at the same time.
529//
530// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
531// channel for eavesdropped messages, this channel receives all signals, and
532// none of the channels passed to Signal will receive any signals.
533//
534// Panics if the signal handler is not a `SignalRegistrar`.
535func (conn *Conn) Signal(ch chan<- *Signal) {
536	handler, ok := conn.signalHandler.(SignalRegistrar)
537	if !ok {
538		panic("cannot use this method with a non SignalRegistrar handler")
539	}
540	handler.AddSignal(ch)
541}
542
543// RemoveSignal removes the given channel from the list of the registered channels.
544//
545// Panics if the signal handler is not a `SignalRegistrar`.
546func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
547	handler, ok := conn.signalHandler.(SignalRegistrar)
548	if !ok {
549		panic("cannot use this method with a non SignalRegistrar handler")
550	}
551	handler.RemoveSignal(ch)
552}
553
554// SupportsUnixFDs returns whether the underlying transport supports passing of
555// unix file descriptors. If this is false, method calls containing unix file
556// descriptors will return an error and emitted signals containing them will
557// not be sent.
558func (conn *Conn) SupportsUnixFDs() bool {
559	return conn.unixFD
560}
561
562// Error represents a D-Bus message of type Error.
563type Error struct {
564	Name string
565	Body []interface{}
566}
567
568func NewError(name string, body []interface{}) *Error {
569	return &Error{name, body}
570}
571
572func (e Error) Error() string {
573	if len(e.Body) >= 1 {
574		s, ok := e.Body[0].(string)
575		if ok {
576			return s
577		}
578	}
579	return e.Name
580}
581
582// Signal represents a D-Bus message of type Signal. The name member is given in
583// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
584type Signal struct {
585	Sender string
586	Path   ObjectPath
587	Name   string
588	Body   []interface{}
589}
590
591// transport is a D-Bus transport.
592type transport interface {
593	// Read and Write raw data (for example, for the authentication protocol).
594	io.ReadWriteCloser
595
596	// Send the initial null byte used for the EXTERNAL mechanism.
597	SendNullByte() error
598
599	// Returns whether this transport supports passing Unix FDs.
600	SupportsUnixFDs() bool
601
602	// Signal the transport that Unix FD passing is enabled for this connection.
603	EnableUnixFDs()
604
605	// Read / send a message, handling things like Unix FDs.
606	ReadMessage() (*Message, error)
607	SendMessage(*Message) error
608}
609
610var (
611	transports = make(map[string]func(string) (transport, error))
612)
613
614func getTransport(address string) (transport, error) {
615	var err error
616	var t transport
617
618	addresses := strings.Split(address, ";")
619	for _, v := range addresses {
620		i := strings.IndexRune(v, ':')
621		if i == -1 {
622			err = errors.New("dbus: invalid bus address (no transport)")
623			continue
624		}
625		f := transports[v[:i]]
626		if f == nil {
627			err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
628			continue
629		}
630		t, err = f(v[i+1:])
631		if err == nil {
632			return t, nil
633		}
634	}
635	return nil, err
636}
637
638// dereferenceAll returns a slice that, assuming that vs is a slice of pointers
639// of arbitrary types, containes the values that are obtained from dereferencing
640// all elements in vs.
641func dereferenceAll(vs []interface{}) []interface{} {
642	for i := range vs {
643		v := reflect.ValueOf(vs[i])
644		v = v.Elem()
645		vs[i] = v.Interface()
646	}
647	return vs
648}
649
650// getKey gets a key from a the list of keys. Returns "" on error / not found...
651func getKey(s, key string) string {
652	for _, keyEqualsValue := range strings.Split(s, ",") {
653		keyValue := strings.SplitN(keyEqualsValue, "=", 2)
654		if len(keyValue) == 2 && keyValue[0] == key {
655			return keyValue[1]
656		}
657	}
658	return ""
659}
660
661type outputHandler struct {
662	conn    *Conn
663	sendLck sync.Mutex
664	closed  struct {
665		isClosed bool
666		lck      sync.RWMutex
667	}
668}
669
670func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error {
671	h.closed.lck.RLock()
672	defer h.closed.lck.RUnlock()
673	if h.closed.isClosed {
674		ifClosed()
675		return nil
676	}
677	h.sendLck.Lock()
678	defer h.sendLck.Unlock()
679	return h.conn.SendMessage(msg)
680}
681
682func (h *outputHandler) close() {
683	h.closed.lck.Lock()
684	defer h.closed.lck.Unlock()
685	h.closed.isClosed = true
686}
687
688type serialGenerator struct {
689	lck        sync.Mutex
690	nextSerial uint32
691	serialUsed map[uint32]bool
692}
693
694func newSerialGenerator() *serialGenerator {
695	return &serialGenerator{
696		serialUsed: map[uint32]bool{0: true},
697		nextSerial: 1,
698	}
699}
700
701func (gen *serialGenerator) GetSerial() uint32 {
702	gen.lck.Lock()
703	defer gen.lck.Unlock()
704	n := gen.nextSerial
705	for gen.serialUsed[n] {
706		n++
707	}
708	gen.serialUsed[n] = true
709	gen.nextSerial = n + 1
710	return n
711}
712
713func (gen *serialGenerator) RetireSerial(serial uint32) {
714	gen.lck.Lock()
715	defer gen.lck.Unlock()
716	delete(gen.serialUsed, serial)
717}
718
719type nameTracker struct {
720	lck    sync.RWMutex
721	unique string
722	names  map[string]struct{}
723}
724
725func newNameTracker() *nameTracker {
726	return &nameTracker{names: map[string]struct{}{}}
727}
728func (tracker *nameTracker) acquireUniqueConnectionName(name string) {
729	tracker.lck.Lock()
730	defer tracker.lck.Unlock()
731	tracker.unique = name
732}
733func (tracker *nameTracker) acquireName(name string) {
734	tracker.lck.Lock()
735	defer tracker.lck.Unlock()
736	tracker.names[name] = struct{}{}
737}
738func (tracker *nameTracker) loseName(name string) {
739	tracker.lck.Lock()
740	defer tracker.lck.Unlock()
741	delete(tracker.names, name)
742}
743
744func (tracker *nameTracker) uniqueNameIsKnown() bool {
745	tracker.lck.RLock()
746	defer tracker.lck.RUnlock()
747	return tracker.unique != ""
748}
749func (tracker *nameTracker) isKnownName(name string) bool {
750	tracker.lck.RLock()
751	defer tracker.lck.RUnlock()
752	_, ok := tracker.names[name]
753	return ok || name == tracker.unique
754}
755func (tracker *nameTracker) listKnownNames() []string {
756	tracker.lck.RLock()
757	defer tracker.lck.RUnlock()
758	out := make([]string, 0, len(tracker.names)+1)
759	out = append(out, tracker.unique)
760	for k := range tracker.names {
761		out = append(out, k)
762	}
763	return out
764}
765
766type callTracker struct {
767	calls map[uint32]*Call
768	lck   sync.RWMutex
769}
770
771func newCallTracker() *callTracker {
772	return &callTracker{calls: map[uint32]*Call{}}
773}
774
775func (tracker *callTracker) track(sn uint32, call *Call) {
776	tracker.lck.Lock()
777	tracker.calls[sn] = call
778	tracker.lck.Unlock()
779}
780
781func (tracker *callTracker) handleReply(msg *Message) uint32 {
782	serial := msg.Headers[FieldReplySerial].value.(uint32)
783	tracker.lck.RLock()
784	_, ok := tracker.calls[serial]
785	tracker.lck.RUnlock()
786	if ok {
787		tracker.finalizeWithBody(serial, msg.Body)
788	}
789	return serial
790}
791
792func (tracker *callTracker) handleDBusError(msg *Message) uint32 {
793	serial := msg.Headers[FieldReplySerial].value.(uint32)
794	tracker.lck.RLock()
795	_, ok := tracker.calls[serial]
796	tracker.lck.RUnlock()
797	if ok {
798		name, _ := msg.Headers[FieldErrorName].value.(string)
799		tracker.finalizeWithError(serial, Error{name, msg.Body})
800	}
801	return serial
802}
803
804func (tracker *callTracker) handleSendError(msg *Message, err error) {
805	if err == nil {
806		return
807	}
808	tracker.lck.RLock()
809	_, ok := tracker.calls[msg.serial]
810	tracker.lck.RUnlock()
811	if ok {
812		tracker.finalizeWithError(msg.serial, err)
813	}
814}
815
816// finalize was the only func that did not strobe Done
817func (tracker *callTracker) finalize(sn uint32) {
818	tracker.lck.Lock()
819	defer tracker.lck.Unlock()
820	c, ok := tracker.calls[sn]
821	if ok {
822		delete(tracker.calls, sn)
823		c.ContextCancel()
824	}
825	return
826}
827
828func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) {
829	tracker.lck.Lock()
830	c, ok := tracker.calls[sn]
831	if ok {
832		delete(tracker.calls, sn)
833	}
834	tracker.lck.Unlock()
835	if ok {
836		c.Body = body
837		c.done()
838	}
839	return
840}
841
842func (tracker *callTracker) finalizeWithError(sn uint32, err error) {
843	tracker.lck.Lock()
844	c, ok := tracker.calls[sn]
845	if ok {
846		delete(tracker.calls, sn)
847	}
848	tracker.lck.Unlock()
849	if ok {
850		c.Err = err
851		c.done()
852	}
853	return
854}
855
856func (tracker *callTracker) finalizeAllWithError(err error) {
857	tracker.lck.Lock()
858	closedCalls := make([]*Call, 0, len(tracker.calls))
859	for sn := range tracker.calls {
860		closedCalls = append(closedCalls, tracker.calls[sn])
861	}
862	tracker.calls = map[uint32]*Call{}
863	tracker.lck.Unlock()
864	for _, call := range closedCalls {
865		call.Err = err
866		call.done()
867	}
868}
869