export.go 14 KB

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