|
@@ -45,6 +45,7 @@ type Conn struct {
|
|
|
serialGen SerialGenerator
|
|
|
inInt Interceptor
|
|
|
outInt Interceptor
|
|
|
+ auth []Auth
|
|
|
|
|
|
names *nameTracker
|
|
|
calls *callTracker
|
|
@@ -59,7 +60,8 @@ type Conn struct {
|
|
|
func SessionBus() (conn *Conn, err error) {
|
|
|
sessionBusLck.Lock()
|
|
|
defer sessionBusLck.Unlock()
|
|
|
- if sessionBus != nil {
|
|
|
+ if sessionBus != nil &&
|
|
|
+ sessionBus.Connected() {
|
|
|
return sessionBus, nil
|
|
|
}
|
|
|
defer func() {
|
|
@@ -67,19 +69,7 @@ func SessionBus() (conn *Conn, err error) {
|
|
|
sessionBus = conn
|
|
|
}
|
|
|
}()
|
|
|
- conn, err = SessionBusPrivate()
|
|
|
- if err != nil {
|
|
|
- return
|
|
|
- }
|
|
|
- if err = conn.Auth(nil); err != nil {
|
|
|
- conn.Close()
|
|
|
- conn = nil
|
|
|
- return
|
|
|
- }
|
|
|
- if err = conn.Hello(); err != nil {
|
|
|
- conn.Close()
|
|
|
- conn = nil
|
|
|
- }
|
|
|
+ conn, err = ConnectSessionBus()
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -116,7 +106,8 @@ func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Co
|
|
|
func SystemBus() (conn *Conn, err error) {
|
|
|
systemBusLck.Lock()
|
|
|
defer systemBusLck.Unlock()
|
|
|
- if systemBus != nil {
|
|
|
+ if systemBus != nil &&
|
|
|
+ systemBus.Connected() {
|
|
|
return systemBus, nil
|
|
|
}
|
|
|
defer func() {
|
|
@@ -124,20 +115,42 @@ func SystemBus() (conn *Conn, err error) {
|
|
|
systemBus = conn
|
|
|
}
|
|
|
}()
|
|
|
- conn, err = SystemBusPrivate()
|
|
|
+ conn, err = ConnectSystemBus()
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// ConnectSessionBus connects to the session bus.
|
|
|
+func ConnectSessionBus(opts ...ConnOption) (*Conn, error) {
|
|
|
+ address, err := getSessionBusAddress()
|
|
|
if err != nil {
|
|
|
- return
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- if err = conn.Auth(nil); err != nil {
|
|
|
- conn.Close()
|
|
|
- conn = nil
|
|
|
- return
|
|
|
+ return Connect(address, opts...)
|
|
|
+}
|
|
|
+
|
|
|
+// ConnectSystemBus connects to the system bus.
|
|
|
+func ConnectSystemBus(opts ...ConnOption) (*Conn, error) {
|
|
|
+ return Connect(getSystemBusPlatformAddress(), opts...)
|
|
|
+}
|
|
|
+
|
|
|
+// Connect connects to the given address.
|
|
|
+//
|
|
|
+// Returned connection is ready to use and doesn't require calling
|
|
|
+// Auth and Hello methods to make it usable.
|
|
|
+func Connect(address string, opts ...ConnOption) (*Conn, error) {
|
|
|
+ conn, err := Dial(address, opts...)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err = conn.Auth(conn.auth); err != nil {
|
|
|
+ _ = conn.Close()
|
|
|
+ return nil, err
|
|
|
}
|
|
|
if err = conn.Hello(); err != nil {
|
|
|
- conn.Close()
|
|
|
- conn = nil
|
|
|
+ _ = conn.Close()
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- return
|
|
|
+ return conn, nil
|
|
|
}
|
|
|
|
|
|
// SystemBusPrivate returns a new private connection to the system bus.
|
|
@@ -197,6 +210,14 @@ func WithSerialGenerator(gen SerialGenerator) ConnOption {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// WithAuth sets authentication methods for the auth conversation.
|
|
|
+func WithAuth(methods ...Auth) ConnOption {
|
|
|
+ return func(conn *Conn) error {
|
|
|
+ conn.auth = methods
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// Interceptor intercepts incoming and outgoing messages.
|
|
|
type Interceptor func(msg *Message)
|
|
|
|
|
@@ -309,6 +330,11 @@ func (conn *Conn) Context() context.Context {
|
|
|
return conn.ctx
|
|
|
}
|
|
|
|
|
|
+// Connected returns whether conn is connected
|
|
|
+func (conn *Conn) Connected() bool {
|
|
|
+ return conn.ctx.Err() == nil
|
|
|
+}
|
|
|
+
|
|
|
// Eavesdrop causes conn to send all incoming messages to the given channel
|
|
|
// without further processing. Method replies, errors and signals will not be
|
|
|
// sent to the appropriate channels and method calls will not be handled. If nil
|
|
@@ -342,8 +368,9 @@ func (conn *Conn) Hello() error {
|
|
|
}
|
|
|
|
|
|
// inWorker runs in an own goroutine, reading incoming messages from the
|
|
|
-// transport and dispatching them appropiately.
|
|
|
+// transport and dispatching them appropriately.
|
|
|
func (conn *Conn) inWorker() {
|
|
|
+ sequenceGen := newSequenceGenerator()
|
|
|
for {
|
|
|
msg, err := conn.ReadMessage()
|
|
|
if err != nil {
|
|
@@ -352,7 +379,7 @@ func (conn *Conn) inWorker() {
|
|
|
// anything but to shut down all stuff and returns errors to all
|
|
|
// pending replies.
|
|
|
conn.Close()
|
|
|
- conn.calls.finalizeAllWithError(err)
|
|
|
+ conn.calls.finalizeAllWithError(sequenceGen, err)
|
|
|
return
|
|
|
}
|
|
|
// invalid messages are ignored
|
|
@@ -381,13 +408,14 @@ func (conn *Conn) inWorker() {
|
|
|
if conn.inInt != nil {
|
|
|
conn.inInt(msg)
|
|
|
}
|
|
|
+ sequence := sequenceGen.next()
|
|
|
switch msg.Type {
|
|
|
case TypeError:
|
|
|
- conn.serialGen.RetireSerial(conn.calls.handleDBusError(msg))
|
|
|
+ conn.serialGen.RetireSerial(conn.calls.handleDBusError(sequence, msg))
|
|
|
case TypeMethodReply:
|
|
|
- conn.serialGen.RetireSerial(conn.calls.handleReply(msg))
|
|
|
+ conn.serialGen.RetireSerial(conn.calls.handleReply(sequence, msg))
|
|
|
case TypeSignal:
|
|
|
- conn.handleSignal(msg)
|
|
|
+ conn.handleSignal(sequence, msg)
|
|
|
case TypeMethodCall:
|
|
|
go conn.handleCall(msg)
|
|
|
}
|
|
@@ -395,7 +423,7 @@ func (conn *Conn) inWorker() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (conn *Conn) handleSignal(msg *Message) {
|
|
|
+func (conn *Conn) handleSignal(sequence Sequence, msg *Message) {
|
|
|
iface := msg.Headers[FieldInterface].value.(string)
|
|
|
member := msg.Headers[FieldMember].value.(string)
|
|
|
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
|
@@ -421,10 +449,11 @@ func (conn *Conn) handleSignal(msg *Message) {
|
|
|
}
|
|
|
}
|
|
|
signal := &Signal{
|
|
|
- Sender: sender,
|
|
|
- Path: msg.Headers[FieldPath].value.(ObjectPath),
|
|
|
- Name: iface + "." + member,
|
|
|
- Body: msg.Body,
|
|
|
+ Sender: sender,
|
|
|
+ Path: msg.Headers[FieldPath].value.(ObjectPath),
|
|
|
+ Name: iface + "." + member,
|
|
|
+ Body: msg.Body,
|
|
|
+ Sequence: sequence,
|
|
|
}
|
|
|
conn.signalHandler.DeliverSignal(iface, member, signal)
|
|
|
}
|
|
@@ -442,6 +471,9 @@ func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
|
|
|
}
|
|
|
|
|
|
func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
|
|
|
+ if msg.serial == 0 {
|
|
|
+ msg.serial = conn.getSerial()
|
|
|
+ }
|
|
|
if conn.outInt != nil {
|
|
|
conn.outInt(msg)
|
|
|
}
|
|
@@ -473,16 +505,16 @@ func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
|
|
|
if ctx == nil {
|
|
|
panic("nil context")
|
|
|
}
|
|
|
+ if ch == nil {
|
|
|
+ ch = make(chan *Call, 1)
|
|
|
+ } else if cap(ch) == 0 {
|
|
|
+ panic("dbus: unbuffered channel passed to (*Conn).Send")
|
|
|
+ }
|
|
|
|
|
|
var call *Call
|
|
|
ctx, canceler := context.WithCancel(ctx)
|
|
|
msg.serial = conn.getSerial()
|
|
|
if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
|
|
|
- if ch == nil {
|
|
|
- ch = make(chan *Call, 5)
|
|
|
- } else if cap(ch) == 0 {
|
|
|
- panic("dbus: unbuffered channel passed to (*Conn).Send")
|
|
|
- }
|
|
|
call = new(Call)
|
|
|
call.Destination, _ = msg.Headers[FieldDestination].value.(string)
|
|
|
call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
|
|
@@ -504,7 +536,8 @@ func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
|
|
|
})
|
|
|
} else {
|
|
|
canceler()
|
|
|
- call = &Call{Err: nil}
|
|
|
+ call = &Call{Err: nil, Done: ch}
|
|
|
+ ch <- call
|
|
|
conn.sendMessageAndIfClosed(msg, func() {
|
|
|
call = &Call{Err: ErrClosed}
|
|
|
})
|
|
@@ -529,7 +562,6 @@ func (conn *Conn) sendError(err error, dest string, serial uint32) {
|
|
|
}
|
|
|
msg := new(Message)
|
|
|
msg.Type = TypeError
|
|
|
- msg.serial = conn.getSerial()
|
|
|
msg.Headers = make(map[HeaderField]Variant)
|
|
|
if dest != "" {
|
|
|
msg.Headers[FieldDestination] = MakeVariant(dest)
|
|
@@ -548,7 +580,6 @@ func (conn *Conn) sendError(err error, dest string, serial uint32) {
|
|
|
func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
|
|
msg := new(Message)
|
|
|
msg.Type = TypeMethodReply
|
|
|
- msg.serial = conn.getSerial()
|
|
|
msg.Headers = make(map[HeaderField]Variant)
|
|
|
if dest != "" {
|
|
|
msg.Headers[FieldDestination] = MakeVariant(dest)
|
|
@@ -564,8 +595,14 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
|
|
// AddMatchSignal registers the given match rule to receive broadcast
|
|
|
// signals based on their contents.
|
|
|
func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
|
|
|
+ return conn.AddMatchSignalContext(context.Background(), options...)
|
|
|
+}
|
|
|
+
|
|
|
+// AddMatchSignalContext acts like AddMatchSignal but takes a context.
|
|
|
+func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error {
|
|
|
options = append([]MatchOption{withMatchType("signal")}, options...)
|
|
|
- return conn.busObj.Call(
|
|
|
+ return conn.busObj.CallWithContext(
|
|
|
+ ctx,
|
|
|
"org.freedesktop.DBus.AddMatch", 0,
|
|
|
formatMatchOptions(options),
|
|
|
).Store()
|
|
@@ -573,8 +610,14 @@ func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
|
|
|
|
|
|
// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
|
|
|
func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
|
|
|
+ return conn.RemoveMatchSignalContext(context.Background(), options...)
|
|
|
+}
|
|
|
+
|
|
|
+// RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context.
|
|
|
+func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error {
|
|
|
options = append([]MatchOption{withMatchType("signal")}, options...)
|
|
|
- return conn.busObj.Call(
|
|
|
+ return conn.busObj.CallWithContext(
|
|
|
+ ctx,
|
|
|
"org.freedesktop.DBus.RemoveMatch", 0,
|
|
|
formatMatchOptions(options),
|
|
|
).Store()
|
|
@@ -639,10 +682,11 @@ func (e Error) Error() string {
|
|
|
// Signal represents a D-Bus message of type Signal. The name member is given in
|
|
|
// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
|
|
|
type Signal struct {
|
|
|
- Sender string
|
|
|
- Path ObjectPath
|
|
|
- Name string
|
|
|
- Body []interface{}
|
|
|
+ Sender string
|
|
|
+ Path ObjectPath
|
|
|
+ Name string
|
|
|
+ Body []interface{}
|
|
|
+ Sequence Sequence
|
|
|
}
|
|
|
|
|
|
// transport is a D-Bus transport.
|
|
@@ -825,25 +869,25 @@ func (tracker *callTracker) track(sn uint32, call *Call) {
|
|
|
tracker.lck.Unlock()
|
|
|
}
|
|
|
|
|
|
-func (tracker *callTracker) handleReply(msg *Message) uint32 {
|
|
|
+func (tracker *callTracker) handleReply(sequence Sequence, msg *Message) uint32 {
|
|
|
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
|
|
tracker.lck.RLock()
|
|
|
_, ok := tracker.calls[serial]
|
|
|
tracker.lck.RUnlock()
|
|
|
if ok {
|
|
|
- tracker.finalizeWithBody(serial, msg.Body)
|
|
|
+ tracker.finalizeWithBody(serial, sequence, msg.Body)
|
|
|
}
|
|
|
return serial
|
|
|
}
|
|
|
|
|
|
-func (tracker *callTracker) handleDBusError(msg *Message) uint32 {
|
|
|
+func (tracker *callTracker) handleDBusError(sequence Sequence, msg *Message) uint32 {
|
|
|
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
|
|
tracker.lck.RLock()
|
|
|
_, ok := tracker.calls[serial]
|
|
|
tracker.lck.RUnlock()
|
|
|
if ok {
|
|
|
name, _ := msg.Headers[FieldErrorName].value.(string)
|
|
|
- tracker.finalizeWithError(serial, Error{name, msg.Body})
|
|
|
+ tracker.finalizeWithError(serial, sequence, Error{name, msg.Body})
|
|
|
}
|
|
|
return serial
|
|
|
}
|
|
@@ -856,7 +900,7 @@ func (tracker *callTracker) handleSendError(msg *Message, err error) {
|
|
|
_, ok := tracker.calls[msg.serial]
|
|
|
tracker.lck.RUnlock()
|
|
|
if ok {
|
|
|
- tracker.finalizeWithError(msg.serial, err)
|
|
|
+ tracker.finalizeWithError(msg.serial, NoSequence, err)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -871,7 +915,7 @@ func (tracker *callTracker) finalize(sn uint32) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) {
|
|
|
+func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []interface{}) {
|
|
|
tracker.lck.Lock()
|
|
|
c, ok := tracker.calls[sn]
|
|
|
if ok {
|
|
@@ -880,11 +924,12 @@ func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) {
|
|
|
tracker.lck.Unlock()
|
|
|
if ok {
|
|
|
c.Body = body
|
|
|
+ c.ResponseSequence = sequence
|
|
|
c.done()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (tracker *callTracker) finalizeWithError(sn uint32, err error) {
|
|
|
+func (tracker *callTracker) finalizeWithError(sn uint32, sequence Sequence, err error) {
|
|
|
tracker.lck.Lock()
|
|
|
c, ok := tracker.calls[sn]
|
|
|
if ok {
|
|
@@ -893,11 +938,12 @@ func (tracker *callTracker) finalizeWithError(sn uint32, err error) {
|
|
|
tracker.lck.Unlock()
|
|
|
if ok {
|
|
|
c.Err = err
|
|
|
+ c.ResponseSequence = sequence
|
|
|
c.done()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (tracker *callTracker) finalizeAllWithError(err error) {
|
|
|
+func (tracker *callTracker) finalizeAllWithError(sequenceGen *sequenceGenerator, err error) {
|
|
|
tracker.lck.Lock()
|
|
|
closedCalls := make([]*Call, 0, len(tracker.calls))
|
|
|
for sn := range tracker.calls {
|
|
@@ -907,6 +953,7 @@ func (tracker *callTracker) finalizeAllWithError(err error) {
|
|
|
tracker.lck.Unlock()
|
|
|
for _, call := range closedCalls {
|
|
|
call.Err = err
|
|
|
+ call.ResponseSequence = sequenceGen.next()
|
|
|
call.done()
|
|
|
}
|
|
|
}
|