export.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. package dbus
  2. import (
  3. "errors"
  4. "fmt"
  5. "reflect"
  6. "strings"
  7. )
  8. var (
  9. errmsgInvalidArg = Error{
  10. "org.freedesktop.DBus.Error.InvalidArgs",
  11. []interface{}{"Invalid type / number of args"},
  12. }
  13. errmsgNoObject = Error{
  14. "org.freedesktop.DBus.Error.NoSuchObject",
  15. []interface{}{"No such object"},
  16. }
  17. errmsgUnknownMethod = Error{
  18. "org.freedesktop.DBus.Error.UnknownMethod",
  19. []interface{}{"Unknown / invalid method"},
  20. }
  21. )
  22. // exportWithMapping represents an exported struct along with a method name
  23. // mapping to allow for exporting lower-case methods, etc.
  24. type exportWithMapping struct {
  25. export interface{}
  26. // Method name mapping; key -> struct method, value -> dbus method.
  27. mapping map[string]string
  28. // Whether or not this export is for the entire subtree
  29. includeSubtree bool
  30. }
  31. // Sender is a type which can be used in exported methods to receive the message
  32. // sender.
  33. type Sender string
  34. func exportedMethod(export exportWithMapping, name string) reflect.Value {
  35. if export.export == nil {
  36. return reflect.Value{}
  37. }
  38. // If a mapping was included in the export, check the map to see if we
  39. // should be looking for a different method in the export.
  40. if export.mapping != nil {
  41. for key, value := range export.mapping {
  42. if value == name {
  43. name = key
  44. break
  45. }
  46. // Catch the case where a method is aliased but the client is calling
  47. // the original, e.g. the "Foo" method was exported mapped to
  48. // "foo," and dbus client called the original "Foo."
  49. if key == name {
  50. return reflect.Value{}
  51. }
  52. }
  53. }
  54. value := reflect.ValueOf(export.export)
  55. m := value.MethodByName(name)
  56. // Catch the case of attempting to call an unexported method
  57. method, ok := value.Type().MethodByName(name)
  58. if !m.IsValid() || !ok || method.PkgPath != "" {
  59. return reflect.Value{}
  60. }
  61. t := m.Type()
  62. if t.NumOut() == 0 ||
  63. t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
  64. return reflect.Value{}
  65. }
  66. return m
  67. }
  68. // searchHandlers will look through all registered handlers looking for one
  69. // to handle the given path. If a verbatim one isn't found, it will check for
  70. // a subtree registration for the path as well.
  71. func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) {
  72. conn.handlersLck.RLock()
  73. defer conn.handlersLck.RUnlock()
  74. handlers, ok := conn.handlers[path]
  75. if ok {
  76. return handlers, ok
  77. }
  78. // If handlers weren't found for this exact path, look for a matching subtree
  79. // registration
  80. handlers = make(map[string]exportWithMapping)
  81. path = path[:strings.LastIndex(string(path), "/")]
  82. for len(path) > 0 {
  83. var subtreeHandlers map[string]exportWithMapping
  84. subtreeHandlers, ok = conn.handlers[path]
  85. if ok {
  86. for iface, handler := range subtreeHandlers {
  87. // Only include this handler if it registered for the subtree
  88. if handler.includeSubtree {
  89. handlers[iface] = handler
  90. }
  91. }
  92. break
  93. }
  94. path = path[:strings.LastIndex(string(path), "/")]
  95. }
  96. return handlers, ok
  97. }
  98. // handleCall handles the given method call (i.e. looks if it's one of the
  99. // pre-implemented ones and searches for a corresponding handler if not).
  100. func (conn *Conn) handleCall(msg *Message) {
  101. name := msg.Headers[FieldMember].value.(string)
  102. path := msg.Headers[FieldPath].value.(ObjectPath)
  103. ifaceName, hasIface := msg.Headers[FieldInterface].value.(string)
  104. sender, hasSender := msg.Headers[FieldSender].value.(string)
  105. serial := msg.serial
  106. if ifaceName == "org.freedesktop.DBus.Peer" {
  107. switch name {
  108. case "Ping":
  109. conn.sendReply(sender, serial)
  110. case "GetMachineId":
  111. conn.sendReply(sender, serial, conn.uuid)
  112. default:
  113. conn.sendError(errmsgUnknownMethod, sender, serial)
  114. }
  115. return
  116. }
  117. if len(name) == 0 {
  118. conn.sendError(errmsgUnknownMethod, sender, serial)
  119. }
  120. // Find the exported handler (if any) for this path
  121. handlers, ok := conn.searchHandlers(path)
  122. if !ok {
  123. conn.sendError(errmsgNoObject, sender, serial)
  124. return
  125. }
  126. var m reflect.Value
  127. if hasIface {
  128. iface := handlers[ifaceName]
  129. m = exportedMethod(iface, name)
  130. } else {
  131. for _, v := range handlers {
  132. m = exportedMethod(v, name)
  133. if m.IsValid() {
  134. break
  135. }
  136. }
  137. }
  138. if !m.IsValid() {
  139. conn.sendError(errmsgUnknownMethod, sender, serial)
  140. return
  141. }
  142. t := m.Type()
  143. vs := msg.Body
  144. pointers := make([]interface{}, t.NumIn())
  145. decode := make([]interface{}, 0, len(vs))
  146. for i := 0; i < t.NumIn(); i++ {
  147. tp := t.In(i)
  148. val := reflect.New(tp)
  149. pointers[i] = val.Interface()
  150. if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
  151. val.Elem().SetString(sender)
  152. } else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
  153. val.Elem().Set(reflect.ValueOf(*msg))
  154. } else {
  155. decode = append(decode, pointers[i])
  156. }
  157. }
  158. if len(decode) != len(vs) {
  159. conn.sendError(errmsgInvalidArg, sender, serial)
  160. return
  161. }
  162. if err := Store(vs, decode...); err != nil {
  163. conn.sendError(errmsgInvalidArg, sender, serial)
  164. return
  165. }
  166. // Extract parameters
  167. params := make([]reflect.Value, len(pointers))
  168. for i := 0; i < len(pointers); i++ {
  169. params[i] = reflect.ValueOf(pointers[i]).Elem()
  170. }
  171. // Call method
  172. ret := m.Call(params)
  173. if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
  174. conn.sendError(*em, sender, serial)
  175. return
  176. }
  177. if msg.Flags&FlagNoReplyExpected == 0 {
  178. reply := new(Message)
  179. reply.Type = TypeMethodReply
  180. reply.serial = conn.getSerial()
  181. reply.Headers = make(map[HeaderField]Variant)
  182. if hasSender {
  183. reply.Headers[FieldDestination] = msg.Headers[FieldSender]
  184. }
  185. reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
  186. reply.Body = make([]interface{}, len(ret)-1)
  187. for i := 0; i < len(ret)-1; i++ {
  188. reply.Body[i] = ret[i].Interface()
  189. }
  190. if len(ret) != 1 {
  191. reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
  192. }
  193. conn.outLck.RLock()
  194. if !conn.closed {
  195. conn.out <- reply
  196. }
  197. conn.outLck.RUnlock()
  198. }
  199. }
  200. // Emit emits the given signal on the message bus. The name parameter must be
  201. // formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
  202. func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
  203. if !path.IsValid() {
  204. return errors.New("dbus: invalid object path")
  205. }
  206. i := strings.LastIndex(name, ".")
  207. if i == -1 {
  208. return errors.New("dbus: invalid method name")
  209. }
  210. iface := name[:i]
  211. member := name[i+1:]
  212. if !isValidMember(member) {
  213. return errors.New("dbus: invalid method name")
  214. }
  215. if !isValidInterface(iface) {
  216. return errors.New("dbus: invalid interface name")
  217. }
  218. msg := new(Message)
  219. msg.Type = TypeSignal
  220. msg.serial = conn.getSerial()
  221. msg.Headers = make(map[HeaderField]Variant)
  222. msg.Headers[FieldInterface] = MakeVariant(iface)
  223. msg.Headers[FieldMember] = MakeVariant(member)
  224. msg.Headers[FieldPath] = MakeVariant(path)
  225. msg.Body = values
  226. if len(values) > 0 {
  227. msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
  228. }
  229. conn.outLck.RLock()
  230. defer conn.outLck.RUnlock()
  231. if conn.closed {
  232. return ErrClosed
  233. }
  234. conn.out <- msg
  235. return nil
  236. }
  237. // Export registers the given value to be exported as an object on the
  238. // message bus.
  239. //
  240. // If a method call on the given path and interface is received, an exported
  241. // method with the same name is called with v as the receiver if the
  242. // parameters match and the last return value is of type *Error. If this
  243. // *Error is not nil, it is sent back to the caller as an error.
  244. // Otherwise, a method reply is sent with the other return values as its body.
  245. //
  246. // Any parameters with the special type Sender are set to the sender of the
  247. // dbus message when the method is called. Parameters of this type do not
  248. // contribute to the dbus signature of the method (i.e. the method is exposed
  249. // as if the parameters of type Sender were not there).
  250. //
  251. // Similarly, any parameters with the type Message are set to the raw message
  252. // received on the bus. Again, parameters of this type do not contribute to the
  253. // dbus signature of the method.
  254. //
  255. // Every method call is executed in a new goroutine, so the method may be called
  256. // in multiple goroutines at once.
  257. //
  258. // Method calls on the interface org.freedesktop.DBus.Peer will be automatically
  259. // handled for every object.
  260. //
  261. // Passing nil as the first parameter will cause conn to cease handling calls on
  262. // the given combination of path and interface.
  263. //
  264. // Export returns an error if path is not a valid path name.
  265. func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
  266. return conn.ExportWithMap(v, nil, path, iface)
  267. }
  268. // ExportWithMap works exactly like Export but provides the ability to remap
  269. // method names (e.g. export a lower-case method).
  270. //
  271. // The keys in the map are the real method names (exported on the struct), and
  272. // the values are the method names to be exported on DBus.
  273. func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
  274. return conn.exportWithMap(v, mapping, path, iface, false)
  275. }
  276. // ExportSubtree works exactly like Export but registers the given value for
  277. // an entire subtree rather under the root path provided.
  278. //
  279. // In order to make this useful, one parameter in each of the value's exported
  280. // methods should be a Message, in which case it will contain the raw message
  281. // (allowing one to get access to the path that caused the method to be called).
  282. //
  283. // Note that more specific export paths take precedence over less specific. For
  284. // example, a method call using the ObjectPath /foo/bar/baz will call a method
  285. // exported on /foo/bar before a method exported on /foo.
  286. func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
  287. return conn.ExportSubtreeWithMap(v, nil, path, iface)
  288. }
  289. // ExportSubtreeWithMap works exactly like ExportSubtree but provides the
  290. // ability to remap method names (e.g. export a lower-case method).
  291. //
  292. // The keys in the map are the real method names (exported on the struct), and
  293. // the values are the method names to be exported on DBus.
  294. func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
  295. return conn.exportWithMap(v, mapping, path, iface, true)
  296. }
  297. // exportWithMap is the worker function for all exports/registrations.
  298. func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error {
  299. if !path.IsValid() {
  300. return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
  301. }
  302. conn.handlersLck.Lock()
  303. defer conn.handlersLck.Unlock()
  304. // Remove a previous export if the interface is nil
  305. if v == nil {
  306. if _, ok := conn.handlers[path]; ok {
  307. delete(conn.handlers[path], iface)
  308. if len(conn.handlers[path]) == 0 {
  309. delete(conn.handlers, path)
  310. }
  311. }
  312. return nil
  313. }
  314. // If this is the first handler for this path, make a new map to hold all
  315. // handlers for this path.
  316. if _, ok := conn.handlers[path]; !ok {
  317. conn.handlers[path] = make(map[string]exportWithMapping)
  318. }
  319. // Finally, save this handler
  320. conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree}
  321. return nil
  322. }
  323. // ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
  324. func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
  325. var r uint32
  326. err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
  327. if err != nil {
  328. return 0, err
  329. }
  330. return ReleaseNameReply(r), nil
  331. }
  332. // RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
  333. func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
  334. var r uint32
  335. err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
  336. if err != nil {
  337. return 0, err
  338. }
  339. return RequestNameReply(r), nil
  340. }
  341. // ReleaseNameReply is the reply to a ReleaseName call.
  342. type ReleaseNameReply uint32
  343. const (
  344. ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
  345. ReleaseNameReplyNonExistent
  346. ReleaseNameReplyNotOwner
  347. )
  348. // RequestNameFlags represents the possible flags for a RequestName call.
  349. type RequestNameFlags uint32
  350. const (
  351. NameFlagAllowReplacement RequestNameFlags = 1 << iota
  352. NameFlagReplaceExisting
  353. NameFlagDoNotQueue
  354. )
  355. // RequestNameReply is the reply to a RequestName call.
  356. type RequestNameReply uint32
  357. const (
  358. RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
  359. RequestNameReplyInQueue
  360. RequestNameReplyExists
  361. RequestNameReplyAlreadyOwner
  362. )