|  |  |  | @ -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 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |