435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
322 lines
6.9 KiB
Go
322 lines
6.9 KiB
Go
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)
|
|
|
|
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{
|
|
closeChan: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func isDefaultSignalHandler(handler SignalHandler) bool {
|
|
_, ok := handler.(*defaultSignalHandler)
|
|
return ok
|
|
}
|
|
|
|
type defaultSignalHandler struct {
|
|
sync.RWMutex
|
|
closed bool
|
|
signals []chan<- *Signal
|
|
closeChan chan struct{}
|
|
}
|
|
|
|
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
|
sh.RLock()
|
|
defer sh.RUnlock()
|
|
if sh.closed {
|
|
return
|
|
}
|
|
for _, ch := range sh.signals {
|
|
select {
|
|
case ch <- signal:
|
|
case <-sh.closeChan:
|
|
return
|
|
default:
|
|
go func() {
|
|
select {
|
|
case ch <- signal:
|
|
case <-sh.closeChan:
|
|
return
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (sh *defaultSignalHandler) Init() error {
|
|
sh.Lock()
|
|
sh.signals = make([]chan<- *Signal, 0)
|
|
sh.closeChan = make(chan struct{})
|
|
sh.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (sh *defaultSignalHandler) Terminate() {
|
|
sh.Lock()
|
|
if !sh.closed {
|
|
close(sh.closeChan)
|
|
}
|
|
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]
|
|
}
|
|
}
|
|
}
|