@ -22,67 +22,60 @@ var (
}
}
)
)
// exportWithMapping represents an exported struct along with a method name
// exportedObj represents an exported object. It stores a precomputed
// mapping to allow for exporting lower-case methods, etc.
// method table that represents the methods exported on the bus.
type exportWithMapping struct {
type exportedObj struct {
export interface { }
methods map [ string ] reflect . Value
// Method name mapping; key -> struct method, value -> dbus method.
mapping map [ string ] string
// Whether or not this export is for the entire subtree
// Whether or not this export is for the entire subtree
includeSubtree bool
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 is a type which can be used in exported methods to receive the message
// sender.
// sender.
type Sender string
type Sender string
func exportedMethod ( export exportWithMapping , name string ) reflect . Value {
func computeMethodName ( name string , mapping map [ string ] string ) string {
if export . export == nil {
newname , ok := mapping [ name ]
return reflect . Value { }
if ok {
}
name = newname
// 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 { }
}
}
}
return name
}
}
value := reflect . ValueOf ( export . export )
func getMethods ( in interface { } , mapping map [ string ] string ) map [ string ] reflect . Value {
m := value . MethodByName ( name )
if in == nil {
return nil
// 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 { }
}
}
t := m . Type ( )
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 ||
if t . NumOut ( ) == 0 ||
t . Out ( t . NumOut ( ) - 1 ) != reflect . TypeOf ( & errmsgInvalidArg ) {
t . Out ( t . NumOut ( ) - 1 ) != reflect . TypeOf ( & errmsgInvalidArg ) ||
methtype . PkgPath != "" {
return reflect . Value { }
continue
}
}
return m
// map names while building table
methods [ computeMethodName ( methtype . Name , mapping ) ] = method
}
return methods
}
}
// searchHandlers will look through all registered handlers looking for one
// 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
// to handle the given path. If a verbatim one isn't found, it will check for
// a subtree registration for the path as well.
// a subtree registration for the path as well.
func ( conn * Conn ) searchHandlers ( path ObjectPath ) ( map [ string ] export WithMapping , bool ) {
func ( conn * Conn ) searchHandlers ( path ObjectPath ) ( map [ string ] export edObj , bool ) {
conn . handlersLck . RLock ( )
conn . handlersLck . RLock ( )
defer conn . handlersLck . RUnlock ( )
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
// If handlers weren't found for this exact path, look for a matching subtree
// registration
// registration
handlers = make ( map [ string ] export WithMapping )
handlers = make ( map [ string ] export edObj )
path = path [ : strings . LastIndex ( string ( path ) , "/" ) ]
path = path [ : strings . LastIndex ( string ( path ) , "/" ) ]
for len ( path ) > 0 {
for len ( path ) > 0 {
var subtreeHandlers map [ string ] export WithMapping
var subtreeHandlers map [ string ] export edObj
subtreeHandlers , ok = conn . handlers [ path ]
subtreeHandlers , ok = conn . handlers [ path ]
if ok {
if ok {
for iface , handler := range subtreeHandlers {
for iface , handler := range subtreeHandlers {
@ -146,19 +139,20 @@ func (conn *Conn) handleCall(msg *Message) {
}
}
var m reflect . Value
var m reflect . Value
var exists bool
if hasIface {
if hasIface {
iface := handlers [ ifaceName ]
iface := handlers [ ifaceName ]
m = exportedMethod ( iface , name )
m , exists = iface . Method ( name )
} else {
} else {
for _ , v := range handlers {
for _ , v := range handlers {
m = exported Method( v , name )
m , exists = v . Method( name )
if m. IsValid ( ) {
if exists {
break
break
}
}
}
}
}
}
if ! m. IsValid ( ) {
if ! exists {
conn . sendError ( errmsgUnknownMethod , sender , serial )
conn . sendError ( errmsgUnknownMethod , sender , serial )
return
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 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.
// 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 {
func ( conn * Conn ) ExportWithMap ( v interface { } , mapping map [ string ] string , path ObjectPath , iface string ) error {
return conn . export WithMap( 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
// 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 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.
// 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 {
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.
// 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 ( ) {
if ! path . IsValid ( ) {
return fmt . Errorf ( ` dbus: Invalid path name: "%s" ` , path )
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 ( )
defer conn . handlersLck . Unlock ( )
// Remove a previous export if the interface is nil
// Remove a previous export if the interface is nil
if v == nil {
if methods == nil {
if _ , ok := conn . handlers [ path ] ; ok {
if _ , ok := conn . handlers [ path ] ; ok {
delete ( conn . handlers [ path ] , iface )
delete ( conn . handlers [ path ] , iface )
if len ( conn . handlers [ path ] ) == 0 {
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
// If this is the first handler for this path, make a new map to hold all
// handlers for this path.
// handlers for this path.
if _ , ok := conn . handlers [ path ] ; ! ok {
if _ , ok := conn . handlers [ path ] ; ! ok {
conn . handlers [ path ] = make ( map [ string ] export WithMapping )
conn . handlers [ path ] = make ( map [ string ] export edObj )
}
}
// Finally, save this handler
// 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
return nil
}
}