123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- package dbus
- import (
- "bytes"
- "reflect"
- "strings"
- "sync"
- )
- func newIntrospectIntf(h *defaultHandler) *exportedIntf {
- methods := make(map[string]Method)
- methods["Introspect"] = exportedMethod{
- reflect.ValueOf(func(msg Message) (string, *Error) {
- path := msg.Headers[FieldPath].value.(ObjectPath)
- return h.introspectPath(path), nil
- }),
- }
- return newExportedIntf(methods, true)
- }
- //NewDefaultHandler returns an instance of the default
- //call handler. This is useful if you want to implement only
- //one of the two handlers but not both.
- //
- // Deprecated: this is the default value, don't use it, it will be unexported.
- func NewDefaultHandler() *defaultHandler {
- h := &defaultHandler{
- objects: make(map[ObjectPath]*exportedObj),
- defaultIntf: make(map[string]*exportedIntf),
- }
- h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
- return h
- }
- type defaultHandler struct {
- sync.RWMutex
- objects map[ObjectPath]*exportedObj
- defaultIntf map[string]*exportedIntf
- }
- func (h *defaultHandler) PathExists(path ObjectPath) bool {
- _, ok := h.objects[path]
- return ok
- }
- func (h *defaultHandler) introspectPath(path ObjectPath) string {
- subpath := make(map[string]struct{})
- var xml bytes.Buffer
- xml.WriteString("<node>")
- for obj := range h.objects {
- p := string(path)
- if p != "/" {
- p += "/"
- }
- if strings.HasPrefix(string(obj), p) {
- node_name := strings.Split(string(obj[len(p):]), "/")[0]
- subpath[node_name] = struct{}{}
- }
- }
- for s := range subpath {
- xml.WriteString("\n\t<node name=\"" + s + "\"/>")
- }
- xml.WriteString("\n</node>")
- return xml.String()
- }
- func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
- h.RLock()
- defer h.RUnlock()
- object, ok := h.objects[path]
- if ok {
- return object, ok
- }
- // If an object wasn't found for this exact path,
- // look for a matching subtree registration
- subtreeObject := newExportedObject()
- path = path[:strings.LastIndex(string(path), "/")]
- for len(path) > 0 {
- object, ok = h.objects[path]
- if ok {
- for name, iface := range object.interfaces {
- // Only include this handler if it registered for the subtree
- if iface.isFallbackInterface() {
- subtreeObject.interfaces[name] = iface
- }
- }
- break
- }
- path = path[:strings.LastIndex(string(path), "/")]
- }
- for name, intf := range h.defaultIntf {
- if _, exists := subtreeObject.interfaces[name]; exists {
- continue
- }
- subtreeObject.interfaces[name] = intf
- }
- return subtreeObject, true
- }
- func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
- h.Lock()
- h.objects[path] = object
- h.Unlock()
- }
- func (h *defaultHandler) DeleteObject(path ObjectPath) {
- h.Lock()
- delete(h.objects, path)
- h.Unlock()
- }
- type exportedMethod struct {
- reflect.Value
- }
- func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
- t := m.Type()
- params := make([]reflect.Value, len(args))
- for i := 0; i < len(args); i++ {
- params[i] = reflect.ValueOf(args[i]).Elem()
- }
- ret := m.Value.Call(params)
- var err error
- nilErr := false // The reflection will find almost-nils, let's only pass back clean ones!
- if t.NumOut() > 0 {
- if e, ok := ret[t.NumOut()-1].Interface().(*Error); ok { // godbus *Error
- nilErr = ret[t.NumOut()-1].IsNil()
- ret = ret[:t.NumOut()-1]
- err = e
- } else if ret[t.NumOut()-1].Type().Implements(errType) { // Go error
- i := ret[t.NumOut()-1].Interface()
- if i == nil {
- nilErr = ret[t.NumOut()-1].IsNil()
- } else {
- err = i.(error)
- }
- ret = ret[:t.NumOut()-1]
- }
- }
- out := make([]interface{}, len(ret))
- for i, val := range ret {
- out[i] = val.Interface()
- }
- if nilErr || err == nil {
- //concrete type to interface nil is a special case
- return out, nil
- }
- return out, err
- }
- func (m exportedMethod) NumArguments() int {
- return m.Value.Type().NumIn()
- }
- func (m exportedMethod) ArgumentValue(i int) interface{} {
- return reflect.Zero(m.Type().In(i)).Interface()
- }
- func (m exportedMethod) NumReturns() int {
- return m.Value.Type().NumOut()
- }
- func (m exportedMethod) ReturnValue(i int) interface{} {
- return reflect.Zero(m.Type().Out(i)).Interface()
- }
- func newExportedObject() *exportedObj {
- return &exportedObj{
- interfaces: make(map[string]*exportedIntf),
- }
- }
- type exportedObj struct {
- mu sync.RWMutex
- interfaces map[string]*exportedIntf
- }
- func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
- if name == "" {
- return obj, true
- }
- obj.mu.RLock()
- defer obj.mu.RUnlock()
- intf, exists := obj.interfaces[name]
- return intf, exists
- }
- func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
- obj.mu.Lock()
- defer obj.mu.Unlock()
- obj.interfaces[name] = iface
- }
- func (obj *exportedObj) DeleteInterface(name string) {
- obj.mu.Lock()
- defer obj.mu.Unlock()
- delete(obj.interfaces, name)
- }
- func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
- obj.mu.RLock()
- defer obj.mu.RUnlock()
- for _, intf := range obj.interfaces {
- method, exists := intf.LookupMethod(name)
- if exists {
- return method, exists
- }
- }
- return nil, false
- }
- func (obj *exportedObj) isFallbackInterface() bool {
- return false
- }
- func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
- return &exportedIntf{
- methods: methods,
- includeSubtree: includeSubtree,
- }
- }
- type exportedIntf struct {
- methods map[string]Method
- // Whether or not this export is for the entire subtree
- includeSubtree bool
- }
- func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
- out, exists := obj.methods[name]
- return out, exists
- }
- func (obj *exportedIntf) isFallbackInterface() bool {
- return obj.includeSubtree
- }
- //NewDefaultSignalHandler returns an instance of the default
- //signal handler. This is useful if you want to implement only
- //one of the two handlers but not both.
- //
- // Deprecated: this is the default value, don't use it, it will be unexported.
- func NewDefaultSignalHandler() *defaultSignalHandler {
- return &defaultSignalHandler{}
- }
- type defaultSignalHandler struct {
- mu sync.RWMutex
- closed bool
- signals []*signalChannelData
- }
- func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
- sh.mu.RLock()
- defer sh.mu.RUnlock()
- if sh.closed {
- return
- }
- for _, scd := range sh.signals {
- scd.deliver(signal)
- }
- }
- func (sh *defaultSignalHandler) Terminate() {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.closed {
- return
- }
- for _, scd := range sh.signals {
- scd.close()
- close(scd.ch)
- }
- sh.closed = true
- sh.signals = nil
- }
- func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.closed {
- return
- }
- sh.signals = append(sh.signals, &signalChannelData{
- ch: ch,
- done: make(chan struct{}),
- })
- }
- func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.closed {
- return
- }
- for i := len(sh.signals) - 1; i >= 0; i-- {
- if ch == sh.signals[i].ch {
- sh.signals[i].close()
- copy(sh.signals[i:], sh.signals[i+1:])
- sh.signals[len(sh.signals)-1] = nil
- sh.signals = sh.signals[:len(sh.signals)-1]
- }
- }
- }
- type signalChannelData struct {
- wg sync.WaitGroup
- ch chan<- *Signal
- done chan struct{}
- }
- func (scd *signalChannelData) deliver(signal *Signal) {
- select {
- case scd.ch <- signal:
- case <-scd.done:
- return
- default:
- scd.wg.Add(1)
- go scd.deferredDeliver(signal)
- }
- }
- func (scd *signalChannelData) deferredDeliver(signal *Signal) {
- select {
- case scd.ch <- signal:
- case <-scd.done:
- }
- scd.wg.Done()
- }
- func (scd *signalChannelData) close() {
- close(scd.done)
- scd.wg.Wait() // wait until all spawned goroutines return
- }
|