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) } 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 { interfaces map[string]*exportedIntf } func (obj *exportedObj) LookupInterface(name string) (Interface, bool) { if name == "" { return obj, true } intf, exists := obj.interfaces[name] return intf, exists } func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) { obj.interfaces[name] = iface } func (obj *exportedObj) DeleteInterface(name string) { delete(obj.interfaces, name) } func (obj *exportedObj) LookupMethod(name string) (Method, bool) { 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 } func newDefaultSignalHandler() *defaultSignalHandler { return &defaultSignalHandler{} } func isDefaultSignalHandler(handler SignalHandler) bool { _, ok := handler.(*defaultSignalHandler) return ok } type defaultSignalHandler struct { sync.RWMutex closed bool signals []chan<- *Signal } func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) { sh.RLock() defer sh.RUnlock() if sh.closed { return } for _, ch := range sh.signals { ch <- signal } } func (sh *defaultSignalHandler) Init() error { sh.Lock() sh.signals = make([]chan<- *Signal, 0) sh.Unlock() return nil } func (sh *defaultSignalHandler) Terminate() { sh.Lock() sh.closed = true for _, ch := range sh.signals { close(ch) } sh.signals = nil sh.Unlock() } func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) { sh.Lock() defer sh.Unlock() if sh.closed { return } sh.signals = append(sh.signals, ch) } func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) { sh.Lock() defer sh.Unlock() if sh.closed { return } for i := len(sh.signals) - 1; i >= 0; i-- { if ch == sh.signals[i] { copy(sh.signals[i:], sh.signals[i+1:]) sh.signals[len(sh.signals)-1] = nil sh.signals = sh.signals[:len(sh.signals)-1] } } }