export.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. ErrMsgUnknownInterface = Error{
  22. "org.freedesktop.DBus.Error.UnknownInterface",
  23. []interface{}{"Object does not implement the interface"},
  24. }
  25. )
  26. func MakeFailedError(err error) *Error {
  27. return &Error{
  28. "org.freedesktop.DBus.Error.Failed",
  29. []interface{}{err.Error()},
  30. }
  31. }
  32. // Sender is a type which can be used in exported methods to receive the message
  33. // sender.
  34. type Sender string
  35. func computeMethodName(name string, mapping map[string]string) string {
  36. newname, ok := mapping[name]
  37. if ok {
  38. name = newname
  39. }
  40. return name
  41. }
  42. func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
  43. if in == nil {
  44. return nil
  45. }
  46. methods := make(map[string]reflect.Value)
  47. val := reflect.ValueOf(in)
  48. typ := val.Type()
  49. for i := 0; i < typ.NumMethod(); i++ {
  50. methtype := typ.Method(i)
  51. method := val.Method(i)
  52. t := method.Type()
  53. // only track valid methods must return *Error as last arg
  54. // and must be exported
  55. if t.NumOut() == 0 ||
  56. t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
  57. methtype.PkgPath != "" {
  58. continue
  59. }
  60. // map names while building table
  61. methods[computeMethodName(methtype.Name, mapping)] = method
  62. }
  63. return methods
  64. }
  65. func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
  66. pointers := make([]interface{}, m.NumArguments())
  67. decode := make([]interface{}, 0, len(body))
  68. for i := 0; i < m.NumArguments(); i++ {
  69. tp := reflect.TypeOf(m.ArgumentValue(i))
  70. val := reflect.New(tp)
  71. pointers[i] = val.Interface()
  72. if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
  73. val.Elem().SetString(sender)
  74. } else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
  75. val.Elem().Set(reflect.ValueOf(*msg))
  76. } else {
  77. decode = append(decode, pointers[i])
  78. }
  79. }
  80. if len(decode) != len(body) {
  81. return nil, ErrMsgInvalidArg
  82. }
  83. if err := Store(body, decode...); err != nil {
  84. return nil, ErrMsgInvalidArg
  85. }
  86. return pointers, nil
  87. }
  88. func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
  89. if decoder, ok := m.(ArgumentDecoder); ok {
  90. return decoder.DecodeArguments(conn, sender, msg, msg.Body)
  91. }
  92. return standardMethodArgumentDecode(m, sender, msg, msg.Body)
  93. }
  94. // handleCall handles the given method call (i.e. looks if it's one of the
  95. // pre-implemented ones and searches for a corresponding handler if not).
  96. func (conn *Conn) handleCall(msg *Message) {
  97. name := msg.Headers[FieldMember].value.(string)
  98. path := msg.Headers[FieldPath].value.(ObjectPath)
  99. ifaceName, _ := msg.Headers[FieldInterface].value.(string)
  100. sender, hasSender := msg.Headers[FieldSender].value.(string)
  101. serial := msg.serial
  102. if ifaceName == "org.freedesktop.DBus.Peer" {
  103. switch name {
  104. case "Ping":
  105. conn.sendReply(sender, serial)
  106. case "GetMachineId":
  107. conn.sendReply(sender, serial, conn.uuid)
  108. default:
  109. conn.sendError(ErrMsgUnknownMethod, sender, serial)
  110. }
  111. return
  112. }
  113. if len(name) == 0 {
  114. conn.sendError(ErrMsgUnknownMethod, sender, serial)
  115. }
  116. object, ok := conn.handler.LookupObject(path)
  117. if !ok {
  118. conn.sendError(ErrMsgNoObject, sender, serial)
  119. return
  120. }
  121. iface, exists := object.LookupInterface(ifaceName)
  122. if !exists {
  123. conn.sendError(ErrMsgUnknownInterface, sender, serial)
  124. return
  125. }
  126. m, exists := iface.LookupMethod(name)
  127. if !exists {
  128. conn.sendError(ErrMsgUnknownMethod, sender, serial)
  129. return
  130. }
  131. args, err := conn.decodeArguments(m, sender, msg)
  132. if err != nil {
  133. conn.sendError(err, sender, serial)
  134. return
  135. }
  136. ret, err := m.Call(args...)
  137. if err != nil {
  138. conn.sendError(err, sender, serial)
  139. return
  140. }
  141. if msg.Flags&FlagNoReplyExpected == 0 {
  142. reply := new(Message)
  143. reply.Type = TypeMethodReply
  144. reply.serial = conn.getSerial()
  145. reply.Headers = make(map[HeaderField]Variant)
  146. if hasSender {
  147. reply.Headers[FieldDestination] = msg.Headers[FieldSender]
  148. }
  149. reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
  150. reply.Body = make([]interface{}, len(ret))
  151. for i := 0; i < len(ret); i++ {
  152. reply.Body[i] = ret[i]
  153. }
  154. reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
  155. conn.sendMessage(reply)
  156. }
  157. }
  158. // Emit emits the given signal on the message bus. The name parameter must be
  159. // formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
  160. func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
  161. if !path.IsValid() {
  162. return errors.New("dbus: invalid object path")
  163. }
  164. i := strings.LastIndex(name, ".")
  165. if i == -1 {
  166. return errors.New("dbus: invalid method name")
  167. }
  168. iface := name[:i]
  169. member := name[i+1:]
  170. if !isValidMember(member) {
  171. return errors.New("dbus: invalid method name")
  172. }
  173. if !isValidInterface(iface) {
  174. return errors.New("dbus: invalid interface name")
  175. }
  176. msg := new(Message)
  177. msg.Type = TypeSignal
  178. msg.serial = conn.getSerial()
  179. msg.Headers = make(map[HeaderField]Variant)
  180. msg.Headers[FieldInterface] = MakeVariant(iface)
  181. msg.Headers[FieldMember] = MakeVariant(member)
  182. msg.Headers[FieldPath] = MakeVariant(path)
  183. msg.Body = values
  184. if len(values) > 0 {
  185. msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
  186. }
  187. var closed bool
  188. conn.sendMessageAndIfClosed(msg, func() {
  189. closed = true
  190. })
  191. if closed {
  192. return ErrClosed
  193. }
  194. return nil
  195. }
  196. // Export registers the given value to be exported as an object on the
  197. // message bus.
  198. //
  199. // If a method call on the given path and interface is received, an exported
  200. // method with the same name is called with v as the receiver if the
  201. // parameters match and the last return value is of type *Error. If this
  202. // *Error is not nil, it is sent back to the caller as an error.
  203. // Otherwise, a method reply is sent with the other return values as its body.
  204. //
  205. // Any parameters with the special type Sender are set to the sender of the
  206. // dbus message when the method is called. Parameters of this type do not
  207. // contribute to the dbus signature of the method (i.e. the method is exposed
  208. // as if the parameters of type Sender were not there).
  209. //
  210. // Similarly, any parameters with the type Message are set to the raw message
  211. // received on the bus. Again, parameters of this type do not contribute to the
  212. // dbus signature of the method.
  213. //
  214. // Every method call is executed in a new goroutine, so the method may be called
  215. // in multiple goroutines at once.
  216. //
  217. // Method calls on the interface org.freedesktop.DBus.Peer will be automatically
  218. // handled for every object.
  219. //
  220. // Passing nil as the first parameter will cause conn to cease handling calls on
  221. // the given combination of path and interface.
  222. //
  223. // Export returns an error if path is not a valid path name.
  224. func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
  225. return conn.ExportWithMap(v, nil, path, iface)
  226. }
  227. // ExportWithMap works exactly like Export but provides the ability to remap
  228. // method names (e.g. export a lower-case method).
  229. //
  230. // The keys in the map are the real method names (exported on the struct), and
  231. // the values are the method names to be exported on DBus.
  232. func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
  233. return conn.export(getMethods(v, mapping), path, iface, false)
  234. }
  235. // ExportSubtree works exactly like Export but registers the given value for
  236. // an entire subtree rather under the root path provided.
  237. //
  238. // In order to make this useful, one parameter in each of the value's exported
  239. // methods should be a Message, in which case it will contain the raw message
  240. // (allowing one to get access to the path that caused the method to be called).
  241. //
  242. // Note that more specific export paths take precedence over less specific. For
  243. // example, a method call using the ObjectPath /foo/bar/baz will call a method
  244. // exported on /foo/bar before a method exported on /foo.
  245. func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
  246. return conn.ExportSubtreeWithMap(v, nil, path, iface)
  247. }
  248. // ExportSubtreeWithMap works exactly like ExportSubtree but provides the
  249. // ability to remap method names (e.g. export a lower-case method).
  250. //
  251. // The keys in the map are the real method names (exported on the struct), and
  252. // the values are the method names to be exported on DBus.
  253. func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
  254. return conn.export(getMethods(v, mapping), path, iface, true)
  255. }
  256. // ExportMethodTable like Export registers the given methods as an object
  257. // on the message bus. Unlike Export the it uses a method table to define
  258. // the object instead of a native go object.
  259. //
  260. // The method table is a map from method name to function closure
  261. // representing the method. This allows an object exported on the bus to not
  262. // necessarily be a native go object. It can be useful for generating exposed
  263. // methods on the fly.
  264. //
  265. // Any non-function objects in the method table are ignored.
  266. func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
  267. return conn.exportMethodTable(methods, path, iface, false)
  268. }
  269. // Like ExportSubtree, but with the same caveats as ExportMethodTable.
  270. func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
  271. return conn.exportMethodTable(methods, path, iface, true)
  272. }
  273. func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
  274. out := make(map[string]reflect.Value)
  275. for name, method := range methods {
  276. rval := reflect.ValueOf(method)
  277. if rval.Kind() != reflect.Func {
  278. continue
  279. }
  280. t := rval.Type()
  281. // only track valid methods must return *Error as last arg
  282. if t.NumOut() == 0 ||
  283. t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
  284. continue
  285. }
  286. out[name] = rval
  287. }
  288. return conn.export(out, path, iface, includeSubtree)
  289. }
  290. func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
  291. if h.PathExists(path) {
  292. obj := h.objects[path]
  293. obj.DeleteInterface(iface)
  294. if len(obj.interfaces) == 0 {
  295. h.DeleteObject(path)
  296. }
  297. }
  298. return nil
  299. }
  300. // exportWithMap is the worker function for all exports/registrations.
  301. func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
  302. h, ok := conn.handler.(*defaultHandler)
  303. if !ok {
  304. return fmt.Errorf(
  305. `dbus: export only allowed on the default hander handler have %T"`,
  306. conn.handler)
  307. }
  308. if !path.IsValid() {
  309. return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
  310. }
  311. // Remove a previous export if the interface is nil
  312. if methods == nil {
  313. return conn.unexport(h, path, iface)
  314. }
  315. // If this is the first handler for this path, make a new map to hold all
  316. // handlers for this path.
  317. if !h.PathExists(path) {
  318. h.AddObject(path, newExportedObject())
  319. }
  320. exportedMethods := make(map[string]Method)
  321. for name, method := range methods {
  322. exportedMethods[name] = exportedMethod{method}
  323. }
  324. // Finally, save this handler
  325. obj := h.objects[path]
  326. obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
  327. return nil
  328. }
  329. // ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
  330. func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
  331. var r uint32
  332. err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
  333. if err != nil {
  334. return 0, err
  335. }
  336. return ReleaseNameReply(r), nil
  337. }
  338. // RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
  339. func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
  340. var r uint32
  341. err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
  342. if err != nil {
  343. return 0, err
  344. }
  345. return RequestNameReply(r), nil
  346. }
  347. // ReleaseNameReply is the reply to a ReleaseName call.
  348. type ReleaseNameReply uint32
  349. const (
  350. ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
  351. ReleaseNameReplyNonExistent
  352. ReleaseNameReplyNotOwner
  353. )
  354. // RequestNameFlags represents the possible flags for a RequestName call.
  355. type RequestNameFlags uint32
  356. const (
  357. NameFlagAllowReplacement RequestNameFlags = 1 << iota
  358. NameFlagReplaceExisting
  359. NameFlagDoNotQueue
  360. )
  361. // RequestNameReply is the reply to a RequestName call.
  362. type RequestNameReply uint32
  363. const (
  364. RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
  365. RequestNameReplyInQueue
  366. RequestNameReplyExists
  367. RequestNameReplyAlreadyOwner
  368. )