export.go 14 KB

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