object.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package dbus
  2. import (
  3. "context"
  4. "errors"
  5. "strings"
  6. )
  7. // BusObject is the interface of a remote object on which methods can be
  8. // invoked.
  9. type BusObject interface {
  10. Call(method string, flags Flags, args ...interface{}) *Call
  11. CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call
  12. Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
  13. GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call
  14. AddMatchSignal(iface, member string, options ...MatchOption) *Call
  15. RemoveMatchSignal(iface, member string, options ...MatchOption) *Call
  16. GetProperty(p string) (Variant, error)
  17. Destination() string
  18. Path() ObjectPath
  19. }
  20. // Object represents a remote object on which methods can be invoked.
  21. type Object struct {
  22. conn *Conn
  23. dest string
  24. path ObjectPath
  25. }
  26. // Call calls a method with (*Object).Go and waits for its reply.
  27. func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
  28. return <-o.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done
  29. }
  30. // CallWithContext acts like Call but takes a context
  31. func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call {
  32. return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done
  33. }
  34. // MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers.
  35. // For full list of available options consult
  36. // https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
  37. type MatchOption struct {
  38. key string
  39. value string
  40. }
  41. // WithMatchOption creates match option with given key and value
  42. func WithMatchOption(key, value string) MatchOption {
  43. return MatchOption{key, value}
  44. }
  45. // WithMatchObjectPath creates match option that filters events based on given path
  46. func WithMatchObjectPath(path ObjectPath) MatchOption {
  47. return MatchOption{"path", string(path)}
  48. }
  49. func formatMatchOptions(options []MatchOption) string {
  50. items := make([]string, 0, len(options))
  51. for _, option := range options {
  52. items = append(items, option.key+"='"+option.value+"'")
  53. }
  54. return strings.Join(items, ",")
  55. }
  56. // AddMatchSignal subscribes BusObject to signals from specified interface,
  57. // method (member). Additional filter rules can be added via WithMatch* option constructors.
  58. // Note: To filter events by object path you have to specify this path via an option.
  59. func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call {
  60. base := []MatchOption{
  61. {"type", "signal"},
  62. {"interface", iface},
  63. {"member", member},
  64. }
  65. options = append(base, options...)
  66. return o.conn.BusObject().Call(
  67. "org.freedesktop.DBus.AddMatch",
  68. 0,
  69. formatMatchOptions(options),
  70. )
  71. }
  72. // RemoveMatchSignal unsubscribes BusObject from signals from specified interface,
  73. // method (member). Additional filter rules can be added via WithMatch* option constructors
  74. func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call {
  75. base := []MatchOption{
  76. {"type", "signal"},
  77. {"interface", iface},
  78. {"member", member},
  79. }
  80. options = append(base, options...)
  81. return o.conn.BusObject().Call(
  82. "org.freedesktop.DBus.RemoveMatch",
  83. 0,
  84. formatMatchOptions(options),
  85. )
  86. }
  87. // Go calls a method with the given arguments asynchronously. It returns a
  88. // Call structure representing this method call. The passed channel will
  89. // return the same value once the call is done. If ch is nil, a new channel
  90. // will be allocated. Otherwise, ch has to be buffered or Go will panic.
  91. //
  92. // If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
  93. // is returned with any error in Err and a closed channel in Done containing
  94. // the returned Call as it's one entry.
  95. //
  96. // If the method parameter contains a dot ('.'), the part before the last dot
  97. // specifies the interface on which the method is called.
  98. func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
  99. return o.createCall(context.Background(), method, flags, ch, args...)
  100. }
  101. // GoWithContext acts like Go but takes a context
  102. func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
  103. return o.createCall(ctx, method, flags, ch, args...)
  104. }
  105. func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
  106. if ctx == nil {
  107. panic("nil context")
  108. }
  109. iface := ""
  110. i := strings.LastIndex(method, ".")
  111. if i != -1 {
  112. iface = method[:i]
  113. }
  114. method = method[i+1:]
  115. msg := new(Message)
  116. msg.Type = TypeMethodCall
  117. msg.serial = o.conn.getSerial()
  118. msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
  119. msg.Headers = make(map[HeaderField]Variant)
  120. msg.Headers[FieldPath] = MakeVariant(o.path)
  121. msg.Headers[FieldDestination] = MakeVariant(o.dest)
  122. msg.Headers[FieldMember] = MakeVariant(method)
  123. if iface != "" {
  124. msg.Headers[FieldInterface] = MakeVariant(iface)
  125. }
  126. msg.Body = args
  127. if len(args) > 0 {
  128. msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
  129. }
  130. if msg.Flags&FlagNoReplyExpected == 0 {
  131. if ch == nil {
  132. ch = make(chan *Call, 10)
  133. } else if cap(ch) == 0 {
  134. panic("dbus: unbuffered channel passed to (*Object).Go")
  135. }
  136. ctx, cancel := context.WithCancel(ctx)
  137. call := &Call{
  138. Destination: o.dest,
  139. Path: o.path,
  140. Method: method,
  141. Args: args,
  142. Done: ch,
  143. ctxCanceler: cancel,
  144. ctx: ctx,
  145. }
  146. o.conn.calls.track(msg.serial, call)
  147. o.conn.sendMessageAndIfClosed(msg, func() {
  148. o.conn.calls.handleSendError(msg, ErrClosed)
  149. cancel()
  150. })
  151. go func() {
  152. <-ctx.Done()
  153. o.conn.calls.handleSendError(msg, ctx.Err())
  154. }()
  155. return call
  156. }
  157. done := make(chan *Call, 1)
  158. call := &Call{
  159. Err: nil,
  160. Done: done,
  161. }
  162. defer func() {
  163. call.Done <- call
  164. close(done)
  165. }()
  166. o.conn.sendMessageAndIfClosed(msg, func() {
  167. call.Err = ErrClosed
  168. })
  169. return call
  170. }
  171. // GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
  172. // object. The property name must be given in interface.member notation.
  173. func (o *Object) GetProperty(p string) (Variant, error) {
  174. idx := strings.LastIndex(p, ".")
  175. if idx == -1 || idx+1 == len(p) {
  176. return Variant{}, errors.New("dbus: invalid property " + p)
  177. }
  178. iface := p[:idx]
  179. prop := p[idx+1:]
  180. result := Variant{}
  181. err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
  182. if err != nil {
  183. return Variant{}, err
  184. }
  185. return result, nil
  186. }
  187. // Destination returns the destination that calls on (o *Object) are sent to.
  188. func (o *Object) Destination() string {
  189. return o.dest
  190. }
  191. // Path returns the path that calls on (o *Object") are sent to.
  192. func (o *Object) Path() ObjectPath {
  193. return o.path
  194. }