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