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