|
|
|
@ -22,67 +22,60 @@ var (
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// exportWithMapping represents an exported struct along with a method name
|
|
|
|
|
// mapping to allow for exporting lower-case methods, etc.
|
|
|
|
|
type exportWithMapping struct {
|
|
|
|
|
export interface{}
|
|
|
|
|
|
|
|
|
|
// Method name mapping; key -> struct method, value -> dbus method.
|
|
|
|
|
mapping map[string]string
|
|
|
|
|
// exportedObj represents an exported object. It stores a precomputed
|
|
|
|
|
// method table that represents the methods exported on the bus.
|
|
|
|
|
type exportedObj struct {
|
|
|
|
|
methods map[string]reflect.Value
|
|
|
|
|
|
|
|
|
|
// Whether or not this export is for the entire subtree
|
|
|
|
|
includeSubtree bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (obj exportedObj) Method(name string) (reflect.Value, bool) {
|
|
|
|
|
out, exists := obj.methods[name]
|
|
|
|
|
return out, exists
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sender is a type which can be used in exported methods to receive the message
|
|
|
|
|
// sender.
|
|
|
|
|
type Sender string
|
|
|
|
|
|
|
|
|
|
func exportedMethod(export exportWithMapping, name string) reflect.Value {
|
|
|
|
|
if export.export == nil {
|
|
|
|
|
return reflect.Value{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If a mapping was included in the export, check the map to see if we
|
|
|
|
|
// should be looking for a different method in the export.
|
|
|
|
|
if export.mapping != nil {
|
|
|
|
|
for key, value := range export.mapping {
|
|
|
|
|
if value == name {
|
|
|
|
|
name = key
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Catch the case where a method is aliased but the client is calling
|
|
|
|
|
// the original, e.g. the "Foo" method was exported mapped to
|
|
|
|
|
// "foo," and dbus client called the original "Foo."
|
|
|
|
|
if key == name {
|
|
|
|
|
return reflect.Value{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func computeMethodName(name string, mapping map[string]string) string {
|
|
|
|
|
newname, ok := mapping[name]
|
|
|
|
|
if ok {
|
|
|
|
|
name = newname
|
|
|
|
|
}
|
|
|
|
|
return name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value := reflect.ValueOf(export.export)
|
|
|
|
|
m := value.MethodByName(name)
|
|
|
|
|
|
|
|
|
|
// Catch the case of attempting to call an unexported method
|
|
|
|
|
method, ok := value.Type().MethodByName(name)
|
|
|
|
|
|
|
|
|
|
if !m.IsValid() || !ok || method.PkgPath != "" {
|
|
|
|
|
return reflect.Value{}
|
|
|
|
|
func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
|
|
|
|
|
if in == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
t := m.Type()
|
|
|
|
|
if t.NumOut() == 0 ||
|
|
|
|
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
|
|
|
|
|
|
|
|
|
|
return reflect.Value{}
|
|
|
|
|
methods := make(map[string]reflect.Value)
|
|
|
|
|
val := reflect.ValueOf(in)
|
|
|
|
|
typ := val.Type()
|
|
|
|
|
for i := 0; i < typ.NumMethod(); i++ {
|
|
|
|
|
methtype := typ.Method(i)
|
|
|
|
|
method := val.Method(i)
|
|
|
|
|
t := method.Type()
|
|
|
|
|
// only track valid methods must return *Error as last arg
|
|
|
|
|
// and must be exported
|
|
|
|
|
if t.NumOut() == 0 ||
|
|
|
|
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) ||
|
|
|
|
|
methtype.PkgPath != "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// map names while building table
|
|
|
|
|
methods[computeMethodName(methtype.Name, mapping)] = method
|
|
|
|
|
}
|
|
|
|
|
return m
|
|
|
|
|
return methods
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// searchHandlers will look through all registered handlers looking for one
|
|
|
|
|
// to handle the given path. If a verbatim one isn't found, it will check for
|
|
|
|
|
// a subtree registration for the path as well.
|
|
|
|
|
func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) {
|
|
|
|
|
func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportedObj, bool) {
|
|
|
|
|
conn.handlersLck.RLock()
|
|
|
|
|
defer conn.handlersLck.RUnlock()
|
|
|
|
|
|
|
|
|
@ -93,10 +86,10 @@ func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping,
|
|
|
|
|
|
|
|
|
|
// If handlers weren't found for this exact path, look for a matching subtree
|
|
|
|
|
// registration
|
|
|
|
|
handlers = make(map[string]exportWithMapping)
|
|
|
|
|
handlers = make(map[string]exportedObj)
|
|
|
|
|
path = path[:strings.LastIndex(string(path), "/")]
|
|
|
|
|
for len(path) > 0 {
|
|
|
|
|
var subtreeHandlers map[string]exportWithMapping
|
|
|
|
|
var subtreeHandlers map[string]exportedObj
|
|
|
|
|
subtreeHandlers, ok = conn.handlers[path]
|
|
|
|
|
if ok {
|
|
|
|
|
for iface, handler := range subtreeHandlers {
|
|
|
|
@ -146,19 +139,20 @@ func (conn *Conn) handleCall(msg *Message) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var m reflect.Value
|
|
|
|
|
var exists bool
|
|
|
|
|
if hasIface {
|
|
|
|
|
iface := handlers[ifaceName]
|
|
|
|
|
m = exportedMethod(iface, name)
|
|
|
|
|
m, exists = iface.Method(name)
|
|
|
|
|
} else {
|
|
|
|
|
for _, v := range handlers {
|
|
|
|
|
m = exportedMethod(v, name)
|
|
|
|
|
if m.IsValid() {
|
|
|
|
|
m, exists = v.Method(name)
|
|
|
|
|
if exists {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !m.IsValid() {
|
|
|
|
|
if !exists {
|
|
|
|
|
conn.sendError(errmsgUnknownMethod, sender, serial)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -303,7 +297,7 @@ func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
|
|
|
|
// The keys in the map are the real method names (exported on the struct), and
|
|
|
|
|
// the values are the method names to be exported on DBus.
|
|
|
|
|
func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
|
|
|
|
return conn.exportWithMap(v, mapping, path, iface, false)
|
|
|
|
|
return conn.export(getMethods(v, mapping), path, iface, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSubtree works exactly like Export but registers the given value for
|
|
|
|
@ -326,11 +320,48 @@ func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) er
|
|
|
|
|
// The keys in the map are the real method names (exported on the struct), and
|
|
|
|
|
// the values are the method names to be exported on DBus.
|
|
|
|
|
func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
|
|
|
|
return conn.exportWithMap(v, mapping, path, iface, true)
|
|
|
|
|
return conn.export(getMethods(v, mapping), path, iface, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportMethodTable like Export registers the given methods as an object
|
|
|
|
|
// on the message bus. Unlike Export the it uses a method table to define
|
|
|
|
|
// the object instead of a native go object.
|
|
|
|
|
//
|
|
|
|
|
// The method table is a map from method name to function closure
|
|
|
|
|
// representing the method. This allows an object exported on the bus to not
|
|
|
|
|
// necessarily be a native go object. It can be useful for generating exposed
|
|
|
|
|
// methods on the fly.
|
|
|
|
|
//
|
|
|
|
|
// Any non-function objects in the method table are ignored.
|
|
|
|
|
func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
|
|
|
|
return conn.exportMethodTable(methods, path, iface, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Like ExportSubtree, but with the same caveats as ExportMethodTable.
|
|
|
|
|
func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
|
|
|
|
return conn.exportMethodTable(methods, path, iface, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
|
|
|
|
|
out := make(map[string]reflect.Value)
|
|
|
|
|
for name, method := range methods {
|
|
|
|
|
rval := reflect.ValueOf(method)
|
|
|
|
|
if rval.Kind() != reflect.Func {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
t := rval.Type()
|
|
|
|
|
// only track valid methods must return *Error as last arg
|
|
|
|
|
if t.NumOut() == 0 ||
|
|
|
|
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
out[name] = rval
|
|
|
|
|
}
|
|
|
|
|
return conn.export(out, path, iface, includeSubtree)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// exportWithMap is the worker function for all exports/registrations.
|
|
|
|
|
func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error {
|
|
|
|
|
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
|
|
|
|
|
if !path.IsValid() {
|
|
|
|
|
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
|
|
|
|
}
|
|
|
|
@ -339,7 +370,7 @@ func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path O
|
|
|
|
|
defer conn.handlersLck.Unlock()
|
|
|
|
|
|
|
|
|
|
// Remove a previous export if the interface is nil
|
|
|
|
|
if v == nil {
|
|
|
|
|
if methods == nil {
|
|
|
|
|
if _, ok := conn.handlers[path]; ok {
|
|
|
|
|
delete(conn.handlers[path], iface)
|
|
|
|
|
if len(conn.handlers[path]) == 0 {
|
|
|
|
@ -353,11 +384,14 @@ func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path O
|
|
|
|
|
// If this is the first handler for this path, make a new map to hold all
|
|
|
|
|
// handlers for this path.
|
|
|
|
|
if _, ok := conn.handlers[path]; !ok {
|
|
|
|
|
conn.handlers[path] = make(map[string]exportWithMapping)
|
|
|
|
|
conn.handlers[path] = make(map[string]exportedObj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally, save this handler
|
|
|
|
|
conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree}
|
|
|
|
|
conn.handlers[path][iface] = exportedObj{
|
|
|
|
|
methods: methods,
|
|
|
|
|
includeSubtree: includeSubtree,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|