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.
428 lines
10 KiB
Go
428 lines
10 KiB
Go
package dbus
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
byteType = reflect.TypeOf(byte(0))
|
|
boolType = reflect.TypeOf(false)
|
|
uint8Type = reflect.TypeOf(uint8(0))
|
|
int16Type = reflect.TypeOf(int16(0))
|
|
uint16Type = reflect.TypeOf(uint16(0))
|
|
intType = reflect.TypeOf(int(0))
|
|
uintType = reflect.TypeOf(uint(0))
|
|
int32Type = reflect.TypeOf(int32(0))
|
|
uint32Type = reflect.TypeOf(uint32(0))
|
|
int64Type = reflect.TypeOf(int64(0))
|
|
uint64Type = reflect.TypeOf(uint64(0))
|
|
float64Type = reflect.TypeOf(float64(0))
|
|
stringType = reflect.TypeOf("")
|
|
signatureType = reflect.TypeOf(Signature{""})
|
|
objectPathType = reflect.TypeOf(ObjectPath(""))
|
|
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
|
interfacesType = reflect.TypeOf([]interface{}{})
|
|
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
|
unixFDType = reflect.TypeOf(UnixFD(0))
|
|
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
|
)
|
|
|
|
// An InvalidTypeError signals that a value which cannot be represented in the
|
|
// D-Bus wire format was passed to a function.
|
|
type InvalidTypeError struct {
|
|
Type reflect.Type
|
|
}
|
|
|
|
func (e InvalidTypeError) Error() string {
|
|
return "dbus: invalid type " + e.Type.String()
|
|
}
|
|
|
|
// Store copies the values contained in src to dest, which must be a slice of
|
|
// pointers. It converts slices of interfaces from src to corresponding structs
|
|
// in dest. An error is returned if the lengths of src and dest or the types of
|
|
// their elements don't match.
|
|
func Store(src []interface{}, dest ...interface{}) error {
|
|
if len(src) != len(dest) {
|
|
return errors.New("dbus.Store: length mismatch")
|
|
}
|
|
|
|
for i := range src {
|
|
if err := storeInterfaces(src[i], dest[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func storeInterfaces(src, dest interface{}) error {
|
|
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
|
|
}
|
|
|
|
func store(dest, src reflect.Value) error {
|
|
if dest.Kind() == reflect.Ptr {
|
|
return store(dest.Elem(), src)
|
|
}
|
|
switch src.Kind() {
|
|
case reflect.Slice:
|
|
return storeSlice(dest, src)
|
|
case reflect.Map:
|
|
return storeMap(dest, src)
|
|
default:
|
|
return storeBase(dest, src)
|
|
}
|
|
}
|
|
|
|
func storeBase(dest, src reflect.Value) error {
|
|
return setDest(dest, src)
|
|
}
|
|
|
|
func setDest(dest, src reflect.Value) error {
|
|
if !isVariant(src.Type()) && isVariant(dest.Type()) {
|
|
//special conversion for dbus.Variant
|
|
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
|
|
return nil
|
|
}
|
|
if isVariant(src.Type()) && !isVariant(dest.Type()) {
|
|
src = getVariantValue(src)
|
|
}
|
|
if !src.Type().ConvertibleTo(dest.Type()) {
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: cannot convert %s to %s",
|
|
src.Type(), dest.Type())
|
|
}
|
|
dest.Set(src.Convert(dest.Type()))
|
|
return nil
|
|
}
|
|
|
|
func kindsAreCompatible(dest, src reflect.Type) bool {
|
|
switch {
|
|
case isVariant(dest):
|
|
return true
|
|
case dest.Kind() == reflect.Interface:
|
|
return true
|
|
default:
|
|
return dest.Kind() == src.Kind()
|
|
}
|
|
}
|
|
|
|
func isConvertibleTo(dest, src reflect.Type) bool {
|
|
switch {
|
|
case isVariant(dest):
|
|
return true
|
|
case dest.Kind() == reflect.Interface:
|
|
return true
|
|
case dest.Kind() == reflect.Slice:
|
|
return src.Kind() == reflect.Slice &&
|
|
isConvertibleTo(dest.Elem(), src.Elem())
|
|
case dest.Kind() == reflect.Struct:
|
|
return src == interfacesType
|
|
default:
|
|
return src.ConvertibleTo(dest)
|
|
}
|
|
}
|
|
|
|
func storeMap(dest, src reflect.Value) error {
|
|
switch {
|
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"map: cannot store a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
case isVariant(dest.Type()):
|
|
return storeMapIntoVariant(dest, src)
|
|
case dest.Kind() == reflect.Interface:
|
|
return storeMapIntoInterface(dest, src)
|
|
case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
|
|
isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
|
return storeMapIntoMap(dest, src)
|
|
default:
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"map: cannot convert a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
}
|
|
}
|
|
|
|
func storeMapIntoVariant(dest, src reflect.Value) error {
|
|
dv := reflect.MakeMap(src.Type())
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeMapIntoInterface(dest, src reflect.Value) error {
|
|
var dv reflect.Value
|
|
if isVariant(src.Type().Elem()) {
|
|
//Convert variants to interface{} recursively when converting
|
|
//to interface{}
|
|
dv = reflect.MakeMap(
|
|
reflect.MapOf(src.Type().Key(), interfaceType))
|
|
} else {
|
|
dv = reflect.MakeMap(src.Type())
|
|
}
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeMapIntoMap(dest, src reflect.Value) error {
|
|
if dest.IsNil() {
|
|
dest.Set(reflect.MakeMap(dest.Type()))
|
|
}
|
|
keys := src.MapKeys()
|
|
for _, key := range keys {
|
|
dkey := key.Convert(dest.Type().Key())
|
|
dval := reflect.New(dest.Type().Elem()).Elem()
|
|
err := store(dval, getVariantValue(src.MapIndex(key)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dest.SetMapIndex(dkey, dval)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func storeSlice(dest, src reflect.Value) error {
|
|
switch {
|
|
case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
|
|
//The decoder always decodes structs as slices of interface{}
|
|
return storeStruct(dest, src)
|
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"slice: cannot store a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
case isVariant(dest.Type()):
|
|
return storeSliceIntoVariant(dest, src)
|
|
case dest.Kind() == reflect.Interface:
|
|
return storeSliceIntoInterface(dest, src)
|
|
case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
|
return storeSliceIntoSlice(dest, src)
|
|
default:
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"slice: cannot convert a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
}
|
|
}
|
|
|
|
func storeStruct(dest, src reflect.Value) error {
|
|
if isVariant(dest.Type()) {
|
|
return storeBase(dest, src)
|
|
}
|
|
dval := make([]interface{}, 0, dest.NumField())
|
|
dtype := dest.Type()
|
|
for i := 0; i < dest.NumField(); i++ {
|
|
field := dest.Field(i)
|
|
ftype := dtype.Field(i)
|
|
if ftype.PkgPath != "" {
|
|
continue
|
|
}
|
|
if ftype.Tag.Get("dbus") == "-" {
|
|
continue
|
|
}
|
|
dval = append(dval, field.Addr().Interface())
|
|
}
|
|
if src.Len() != len(dval) {
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"destination struct does not have "+
|
|
"enough fields need: %d have: %d",
|
|
src.Len(), len(dval))
|
|
}
|
|
return Store(src.Interface().([]interface{}), dval...)
|
|
}
|
|
|
|
func storeSliceIntoVariant(dest, src reflect.Value) error {
|
|
dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeSliceIntoInterface(dest, src reflect.Value) error {
|
|
var dv reflect.Value
|
|
if isVariant(src.Type().Elem()) {
|
|
//Convert variants to interface{} recursively when converting
|
|
//to interface{}
|
|
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
|
|
src.Len(), src.Cap())
|
|
} else {
|
|
dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
|
}
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeSliceIntoSlice(dest, src reflect.Value) error {
|
|
if dest.IsNil() || dest.Len() < src.Len() {
|
|
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
|
|
}
|
|
if dest.Len() != src.Len() {
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"slices are different lengths "+
|
|
"need: %d have: %d",
|
|
src.Len(), dest.Len())
|
|
}
|
|
for i := 0; i < src.Len(); i++ {
|
|
err := store(dest.Index(i), getVariantValue(src.Index(i)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getVariantValue(in reflect.Value) reflect.Value {
|
|
if isVariant(in.Type()) {
|
|
return reflect.ValueOf(in.Interface().(Variant).Value())
|
|
}
|
|
return in
|
|
}
|
|
|
|
func isVariant(t reflect.Type) bool {
|
|
return t == variantType
|
|
}
|
|
|
|
// An ObjectPath is an object path as defined by the D-Bus spec.
|
|
type ObjectPath string
|
|
|
|
// IsValid returns whether the object path is valid.
|
|
func (o ObjectPath) IsValid() bool {
|
|
s := string(o)
|
|
if len(s) == 0 {
|
|
return false
|
|
}
|
|
if s[0] != '/' {
|
|
return false
|
|
}
|
|
if s[len(s)-1] == '/' && len(s) != 1 {
|
|
return false
|
|
}
|
|
// probably not used, but technically possible
|
|
if s == "/" {
|
|
return true
|
|
}
|
|
split := strings.Split(s[1:], "/")
|
|
for _, v := range split {
|
|
if len(v) == 0 {
|
|
return false
|
|
}
|
|
for _, c := range v {
|
|
if !isMemberChar(c) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
|
|
// documentation for more information about Unix file descriptor passsing.
|
|
type UnixFD int32
|
|
|
|
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
|
|
type UnixFDIndex uint32
|
|
|
|
// alignment returns the alignment of values of type t.
|
|
func alignment(t reflect.Type) int {
|
|
switch t {
|
|
case variantType:
|
|
return 1
|
|
case objectPathType:
|
|
return 4
|
|
case signatureType:
|
|
return 1
|
|
case interfacesType:
|
|
return 4
|
|
}
|
|
switch t.Kind() {
|
|
case reflect.Uint8:
|
|
return 1
|
|
case reflect.Uint16, reflect.Int16:
|
|
return 2
|
|
case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
|
return 4
|
|
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
|
return 8
|
|
case reflect.Ptr:
|
|
return alignment(t.Elem())
|
|
}
|
|
return 1
|
|
}
|
|
|
|
// isKeyType returns whether t is a valid type for a D-Bus dict.
|
|
func isKeyType(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
|
reflect.String, reflect.Uint, reflect.Int:
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isValidInterface returns whether s is a valid name for an interface.
|
|
func isValidInterface(s string) bool {
|
|
if len(s) == 0 || len(s) > 255 || s[0] == '.' {
|
|
return false
|
|
}
|
|
elem := strings.Split(s, ".")
|
|
if len(elem) < 2 {
|
|
return false
|
|
}
|
|
for _, v := range elem {
|
|
if len(v) == 0 {
|
|
return false
|
|
}
|
|
if v[0] >= '0' && v[0] <= '9' {
|
|
return false
|
|
}
|
|
for _, c := range v {
|
|
if !isMemberChar(c) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// isValidMember returns whether s is a valid name for a member.
|
|
func isValidMember(s string) bool {
|
|
if len(s) == 0 || len(s) > 255 {
|
|
return false
|
|
}
|
|
i := strings.Index(s, ".")
|
|
if i != -1 {
|
|
return false
|
|
}
|
|
if s[0] >= '0' && s[0] <= '9' {
|
|
return false
|
|
}
|
|
for _, c := range s {
|
|
if !isMemberChar(c) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func isMemberChar(c rune) bool {
|
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
|
(c >= 'a' && c <= 'z') || c == '_'
|
|
}
|