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("") 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") } xml.WriteString("\n") 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) err := ret[t.NumOut()-1].Interface().(*Error) ret = ret[:t.NumOut()-1] out := make([]interface{}, len(ret)) for i, val := range ret { out[i] = val.Interface() } if 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 }