...

Source file src/github.com/godbus/dbus/v5/export.go

Documentation: github.com/godbus/dbus/v5

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

View as plain text