diff --git a/vendor.conf b/vendor.conf index 2804e21a16a..0909ba96d82 100644 --- a/vendor.conf +++ b/vendor.conf @@ -11,9 +11,9 @@ github.com/sirupsen/logrus 1.0.2 github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2 github.com/vishvananda/netlink 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270 # systemd integration. -github.com/coreos/go-systemd v14 -github.com/coreos/pkg v3 -github.com/godbus/dbus v3 +github.com/coreos/go-systemd v19 +github.com/coreos/pkg v4 +github.com/godbus/dbus v5.0.1 github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 # Command-line interface. github.com/cyphar/filepath-securejoin v0.2.2 diff --git a/vendor/github.com/coreos/go-systemd/NOTICE b/vendor/github.com/coreos/go-systemd/NOTICE new file mode 100644 index 00000000000..23a0ada2fbb --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2018 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-systemd/README.md b/vendor/github.com/coreos/go-systemd/README.md index cb87a11245c..cde3a8f9bf1 100644 --- a/vendor/github.com/coreos/go-systemd/README.md +++ b/vendor/github.com/coreos/go-systemd/README.md @@ -2,13 +2,17 @@ [![Build Status](https://travis-ci.org/coreos/go-systemd.png?branch=master)](https://travis-ci.org/coreos/go-systemd) [![godoc](https://godoc.org/github.com/coreos/go-systemd?status.svg)](http://godoc.org/github.com/coreos/go-systemd) +![minimum golang 1.10](https://img.shields.io/badge/golang-1.10%2B-orange.svg) + Go bindings to systemd. The project has several packages: - `activation` - for writing and using socket activation from Go +- `daemon` - for notifying systemd of service status changes - `dbus` - for starting/stopping/inspecting running services and units - `journal` - for writing to systemd's logging service, journald - `sdjournal` - for reading from journald by wrapping its C API +- `login1` - for integration with the systemd logind API - `machine1` - for registering machines/containers with systemd - `unit` - for (de)serialization and comparison of unit files @@ -18,10 +22,9 @@ An example HTTP server using socket activation can be quickly set up by followin https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver -## Journal +## systemd Service Notification -Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry. -The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available. +The `daemon` package is an implementation of the [sd_notify protocol](https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description). It can be used to inform systemd of service start-up completion, watchdog events, and other status changes. ## D-Bus @@ -45,6 +48,20 @@ Create `/etc/dbus-1/system-local.conf` that looks like this: ``` +## Journal + +### Writing to the Journal + +Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry. + +### Reading from the Journal + +The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available. + +## logind + +The `login1` package provides functions to integrate with the [systemd logind API](http://www.freedesktop.org/wiki/Software/systemd/logind/). + ## machined The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/). diff --git a/vendor/github.com/coreos/go-systemd/activation/files.go b/vendor/github.com/coreos/go-systemd/activation/files.go index c8e85fcd588..29dd18defad 100644 --- a/vendor/github.com/coreos/go-systemd/activation/files.go +++ b/vendor/github.com/coreos/go-systemd/activation/files.go @@ -18,18 +18,26 @@ package activation import ( "os" "strconv" + "strings" "syscall" ) -// based on: https://gist.github.com/alberts/4640792 const ( + // listenFdsStart corresponds to `SD_LISTEN_FDS_START`. listenFdsStart = 3 ) +// Files returns a slice containing a `os.File` object for each +// file descriptor passed to this process via systemd fd-passing protocol. +// +// The order of the file descriptors is preserved in the returned slice. +// `unsetEnv` is typically set to `true` in order to avoid clashes in +// fd usage and to avoid leaking environment flags to child processes. func Files(unsetEnv bool) []*os.File { if unsetEnv { defer os.Unsetenv("LISTEN_PID") defer os.Unsetenv("LISTEN_FDS") + defer os.Unsetenv("LISTEN_FDNAMES") } pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) @@ -42,10 +50,17 @@ func Files(unsetEnv bool) []*os.File { return nil } + names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") + files := make([]*os.File, 0, nfds) for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { syscall.CloseOnExec(fd) - files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))) + name := "LISTEN_FD_" + strconv.Itoa(fd) + offset := fd - listenFdsStart + if offset < len(names) && len(names[offset]) > 0 { + name = names[offset] + } + files = append(files, os.NewFile(uintptr(fd), name)) } return files diff --git a/vendor/github.com/coreos/go-systemd/activation/listeners.go b/vendor/github.com/coreos/go-systemd/activation/listeners.go index fd5dfc709c7..bb5cc2311e2 100644 --- a/vendor/github.com/coreos/go-systemd/activation/listeners.go +++ b/vendor/github.com/coreos/go-systemd/activation/listeners.go @@ -25,13 +25,33 @@ import ( // The order of the file descriptors is preserved in the returned slice. // Nil values are used to fill any gaps. For example if systemd were to return file descriptors // corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} -func Listeners(unsetEnv bool) ([]net.Listener, error) { - files := Files(unsetEnv) +func Listeners() ([]net.Listener, error) { + files := Files(true) listeners := make([]net.Listener, len(files)) for i, f := range files { if pc, err := net.FileListener(f); err == nil { listeners[i] = pc + f.Close() + } + } + return listeners, nil +} + +// ListenersWithNames maps a listener name to a set of net.Listener instances. +func ListenersWithNames() (map[string][]net.Listener, error) { + files := Files(true) + listeners := map[string][]net.Listener{} + + for _, f := range files { + if pc, err := net.FileListener(f); err == nil { + current, ok := listeners[f.Name()] + if !ok { + listeners[f.Name()] = []net.Listener{pc} + } else { + listeners[f.Name()] = append(current, pc) + } + f.Close() } } return listeners, nil @@ -40,8 +60,8 @@ func Listeners(unsetEnv bool) ([]net.Listener, error) { // TLSListeners returns a slice containing a net.listener for each matching TCP socket type // passed to this process. // It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. -func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) { - listeners, err := Listeners(unsetEnv) +func TLSListeners(tlsConfig *tls.Config) ([]net.Listener, error) { + listeners, err := Listeners() if listeners == nil || err != nil { return nil, err @@ -58,3 +78,26 @@ func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) return listeners, err } + +// TLSListenersWithNames maps a listener name to a net.Listener with +// the associated TLS configuration. +func TLSListenersWithNames(tlsConfig *tls.Config) (map[string][]net.Listener, error) { + listeners, err := ListenersWithNames() + + if listeners == nil || err != nil { + return nil, err + } + + if tlsConfig != nil && err == nil { + for _, ll := range listeners { + // Activate TLS only for TCP sockets + for i, l := range ll { + if l.Addr().Network() == "tcp" { + ll[i] = tls.NewListener(l, tlsConfig) + } + } + } + } + + return listeners, err +} diff --git a/vendor/github.com/coreos/go-systemd/activation/packetconns.go b/vendor/github.com/coreos/go-systemd/activation/packetconns.go index 48b2ca029df..a97206785a4 100644 --- a/vendor/github.com/coreos/go-systemd/activation/packetconns.go +++ b/vendor/github.com/coreos/go-systemd/activation/packetconns.go @@ -24,13 +24,14 @@ import ( // The order of the file descriptors is preserved in the returned slice. // Nil values are used to fill any gaps. For example if systemd were to return file descriptors // corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} -func PacketConns(unsetEnv bool) ([]net.PacketConn, error) { - files := Files(unsetEnv) +func PacketConns() ([]net.PacketConn, error) { + files := Files(true) conns := make([]net.PacketConn, len(files)) for i, f := range files { if pc, err := net.FilePacketConn(f); err == nil { conns[i] = pc + f.Close() } } return conns, nil diff --git a/vendor/github.com/coreos/go-systemd/dbus/dbus.go b/vendor/github.com/coreos/go-systemd/dbus/dbus.go index c1694fb522e..f652582e658 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/dbus.go +++ b/vendor/github.com/coreos/go-systemd/dbus/dbus.go @@ -16,6 +16,7 @@ package dbus import ( + "encoding/hex" "fmt" "os" "strconv" @@ -60,6 +61,27 @@ func PathBusEscape(path string) string { return string(n) } +// pathBusUnescape is the inverse of PathBusEscape. +func pathBusUnescape(path string) string { + if path == "_" { + return "" + } + n := []byte{} + for i := 0; i < len(path); i++ { + c := path[i] + if c == '_' && i+2 < len(path) { + res, err := hex.DecodeString(path[i+1 : i+3]) + if err == nil { + n = append(n, res...) + } + i += 2 + } else { + n = append(n, c) + } + } + return string(n) +} + // Conn is a connection to systemd's dbus endpoint. type Conn struct { // sysconn/sysobj are only used to call dbus methods @@ -74,13 +96,18 @@ type Conn struct { jobs map[dbus.ObjectPath]chan<- string sync.Mutex } - subscriber struct { + subStateSubscriber struct { updateCh chan<- *SubStateUpdate errCh chan<- error sync.Mutex ignore map[dbus.ObjectPath]int64 cleanIgnore int64 } + propertiesSubscriber struct { + updateCh chan<- *PropertiesUpdate + errCh chan<- error + sync.Mutex + } } // New establishes a connection to any available bus and authenticates. @@ -116,7 +143,7 @@ func NewUserConnection() (*Conn, error) { func NewSystemdConnection() (*Conn, error) { return NewConnection(func() (*dbus.Conn, error) { // We skip Hello when talking directly to systemd. - return dbusAuthConnection(func() (*dbus.Conn, error) { + return dbusAuthConnection(func(opts ...dbus.ConnOption) (*dbus.Conn, error) { return dbus.Dial("unix:path=/run/systemd/private") }) }) @@ -152,7 +179,7 @@ func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) { sigobj: systemdObject(sigconn), } - c.subscriber.ignore = make(map[dbus.ObjectPath]int64) + c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64) c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string) // Setup the listeners on jobs so that we can get completions @@ -174,7 +201,7 @@ func (c *Conn) GetManagerProperty(prop string) (string, error) { return variant.String(), nil } -func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { +func dbusAuthConnection(createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { conn, err := createBus() if err != nil { return nil, err @@ -194,7 +221,7 @@ func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error return conn, nil } -func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { +func dbusAuthHelloConnection(createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { conn, err := dbusAuthConnection(createBus) if err != nil { return nil, err diff --git a/vendor/github.com/coreos/go-systemd/dbus/methods.go b/vendor/github.com/coreos/go-systemd/dbus/methods.go index ab17f7cc75a..5f2790acff9 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/methods.go +++ b/vendor/github.com/coreos/go-systemd/dbus/methods.go @@ -1,4 +1,4 @@ -// Copyright 2015 CoreOS, Inc. +// Copyright 2015, 2018 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package dbus import ( "errors" + "fmt" "path" "strconv" @@ -116,13 +117,13 @@ func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) } -// ReloadOrRestart attempts a reload if the unit supports it and use a restart +// ReloadOrRestartUnit attempts a reload if the unit supports it and use a restart // otherwise. func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) { return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) } -// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try" +// ReloadOrTryRestartUnit attempts a reload if the unit supports it and use a "Try" // flavored restart otherwise. func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) { return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) @@ -148,14 +149,27 @@ func (c *Conn) ResetFailedUnit(name string) error { return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store() } -// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface -func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) { +// SystemState returns the systemd state. Equivalent to `systemctl is-system-running`. +func (c *Conn) SystemState() (*Property, error) { + var err error + var prop dbus.Variant + + obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") + err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: "SystemState", Value: prop}, nil +} + +// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface +func (c *Conn) getProperties(path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) { var err error var props map[string]dbus.Variant - path := unitPath(unit) if !path.IsValid() { - return nil, errors.New("invalid unit name: " + unit) + return nil, fmt.Errorf("invalid unit name: %v", path) } obj := c.sysconn.Object("org.freedesktop.systemd1", path) @@ -172,9 +186,15 @@ func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]inte return out, nil } -// GetUnitProperties takes the unit name and returns all of its dbus object properties. +// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties. func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { - return c.getProperties(unit, "org.freedesktop.systemd1.Unit") + path := unitPath(unit) + return c.getProperties(path, "org.freedesktop.systemd1.Unit") +} + +// GetUnitPathProperties takes the (escaped) unit path and returns all of its dbus object properties. +func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) { + return c.getProperties(path, "org.freedesktop.systemd1.Unit") } func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) { @@ -208,7 +228,8 @@ func (c *Conn) GetServiceProperty(service string, propertyName string) (*Propert // Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope // return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { - return c.getProperties(unit, "org.freedesktop.systemd1."+unitType) + path := unitPath(unit) + return c.getProperties(path, "org.freedesktop.systemd1."+unitType) } // SetUnitProperties() may be used to modify certain unit properties at runtime. @@ -270,6 +291,8 @@ func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) { // ListUnits returns an array with all currently loaded units. Note that // units may be known by multiple names at the same time, and hence there might // be more unit names loaded than actual units behind them. +// Also note that a unit is only loaded if it is active and/or enabled. +// Units that are both disabled and inactive will thus not be returned. func (c *Conn) ListUnits() ([]UnitStatus, error) { return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store) } @@ -292,6 +315,7 @@ func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitSt // names and returns an UnitStatus array. Comparing to ListUnitsByPatterns // method, this method returns statuses even for inactive or non-existing // units. Input array should contain exact unit names, but not patterns. +// Note: Requires systemd v230 or higher func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) { return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store) } @@ -563,3 +587,8 @@ func (c *Conn) Reload() error { func unitPath(name string) dbus.ObjectPath { return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name)) } + +// unitName returns the unescaped base element of the supplied escaped path +func unitName(dpath dbus.ObjectPath) string { + return pathBusUnescape(path.Base(string(dpath))) +} diff --git a/vendor/github.com/coreos/go-systemd/dbus/set.go b/vendor/github.com/coreos/go-systemd/dbus/set.go index f92e6fbed1e..17c5d485657 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/set.go +++ b/vendor/github.com/coreos/go-systemd/dbus/set.go @@ -36,7 +36,7 @@ func (s *set) Length() int { } func (s *set) Values() (values []string) { - for val, _ := range s.data { + for val := range s.data { values = append(values, val) } return diff --git a/vendor/github.com/coreos/go-systemd/dbus/subscription.go b/vendor/github.com/coreos/go-systemd/dbus/subscription.go index 996451445c0..f6d7a08a106 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/subscription.go +++ b/vendor/github.com/coreos/go-systemd/dbus/subscription.go @@ -16,6 +16,7 @@ package dbus import ( "errors" + "log" "time" "github.com/godbus/dbus" @@ -36,22 +37,12 @@ func (c *Conn) Subscribe() error { c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'") - err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() - if err != nil { - return err - } - - return nil + return c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() } // Unsubscribe this connection from systemd dbus events. func (c *Conn) Unsubscribe() error { - err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() - if err != nil { - return err - } - - return nil + return c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() } func (c *Conn) dispatch() { @@ -70,7 +61,8 @@ func (c *Conn) dispatch() { c.jobComplete(signal) } - if c.subscriber.updateCh == nil { + if c.subStateSubscriber.updateCh == nil && + c.propertiesSubscriber.updateCh == nil { continue } @@ -84,6 +76,12 @@ func (c *Conn) dispatch() { case "org.freedesktop.DBus.Properties.PropertiesChanged": if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" { unitPath = signal.Path + + if len(signal.Body) >= 2 { + if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok { + c.sendPropertiesUpdate(unitPath, changed) + } + } } } @@ -96,7 +94,7 @@ func (c *Conn) dispatch() { }() } -// Returns two unbuffered channels which will receive all changed units every +// SubscribeUnits returns two unbuffered channels which will receive all changed units every // interval. Deleted units are sent as nil. func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) { return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil) @@ -169,42 +167,80 @@ type SubStateUpdate struct { // is full, it attempts to write an error to errCh; if errCh is full, the error // passes silently. func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) { - c.subscriber.Lock() - defer c.subscriber.Unlock() - c.subscriber.updateCh = updateCh - c.subscriber.errCh = errCh + if c == nil { + msg := "nil receiver" + select { + case errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } + + c.subStateSubscriber.Lock() + defer c.subStateSubscriber.Unlock() + c.subStateSubscriber.updateCh = updateCh + c.subStateSubscriber.errCh = errCh } -func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) { - c.subscriber.Lock() - defer c.subscriber.Unlock() +func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) { + c.subStateSubscriber.Lock() + defer c.subStateSubscriber.Unlock() + + if c.subStateSubscriber.updateCh == nil { + return + } - if c.shouldIgnore(path) { + isIgnored := c.shouldIgnore(unitPath) + defer c.cleanIgnore() + if isIgnored { return } - info, err := c.GetUnitProperties(string(path)) + info, err := c.GetUnitPathProperties(unitPath) if err != nil { select { - case c.subscriber.errCh <- err: + case c.subStateSubscriber.errCh <- err: default: + log.Printf("full error channel while reporting: %s\n", err) } + return } + defer c.updateIgnore(unitPath, info) - name := info["Id"].(string) - substate := info["SubState"].(string) + name, ok := info["Id"].(string) + if !ok { + msg := "failed to cast info.Id" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", err) + } + return + } + substate, ok := info["SubState"].(string) + if !ok { + msg := "failed to cast info.SubState" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } update := &SubStateUpdate{name, substate} select { - case c.subscriber.updateCh <- update: + case c.subStateSubscriber.updateCh <- update: default: + msg := "update channel is full" select { - case c.subscriber.errCh <- errors.New("update channel full!"): + case c.subStateSubscriber.errCh <- errors.New(msg): default: + log.Printf("full error channel while reporting: %s\n", msg) } + return } - - c.updateIgnore(path, info) } // The ignore functions work around a wart in the systemd dbus interface. @@ -222,29 +258,76 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) { // the properties). func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool { - t, ok := c.subscriber.ignore[path] + t, ok := c.subStateSubscriber.ignore[path] return ok && t >= time.Now().UnixNano() } func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) { - c.cleanIgnore() + loadState, ok := info["LoadState"].(string) + if !ok { + return + } // unit is unloaded - it will trigger bad systemd dbus behavior - if info["LoadState"].(string) == "not-found" { - c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval + if loadState == "not-found" { + c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval } } // without this, ignore would grow unboundedly over time func (c *Conn) cleanIgnore() { now := time.Now().UnixNano() - if c.subscriber.cleanIgnore < now { - c.subscriber.cleanIgnore = now + cleanIgnoreInterval + if c.subStateSubscriber.cleanIgnore < now { + c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval - for p, t := range c.subscriber.ignore { + for p, t := range c.subStateSubscriber.ignore { if t < now { - delete(c.subscriber.ignore, p) + delete(c.subStateSubscriber.ignore, p) } } } } + +// PropertiesUpdate holds a map of a unit's changed properties +type PropertiesUpdate struct { + UnitName string + Changed map[string]dbus.Variant +} + +// SetPropertiesSubscriber writes to updateCh when any unit's properties +// change. Every property change reported by systemd will be sent; that is, no +// transitions will be "missed" (as they might be with SetSubStateSubscriber). +// However, state changes will only be written to the channel with non-blocking +// writes. If updateCh is full, it attempts to write an error to errCh; if +// errCh is full, the error passes silently. +func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) { + c.propertiesSubscriber.Lock() + defer c.propertiesSubscriber.Unlock() + c.propertiesSubscriber.updateCh = updateCh + c.propertiesSubscriber.errCh = errCh +} + +// we don't need to worry about shouldIgnore() here because +// sendPropertiesUpdate doesn't call GetProperties() +func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) { + c.propertiesSubscriber.Lock() + defer c.propertiesSubscriber.Unlock() + + if c.propertiesSubscriber.updateCh == nil { + return + } + + update := &PropertiesUpdate{unitName(unitPath), changedProps} + + select { + case c.propertiesSubscriber.updateCh <- update: + default: + msg := "update channel is full" + select { + case c.propertiesSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } +} diff --git a/vendor/github.com/coreos/go-systemd/util/util_cgo.go b/vendor/github.com/coreos/go-systemd/util/util_cgo.go index 22c0d6099df..6269bc73236 100644 --- a/vendor/github.com/coreos/go-systemd/util/util_cgo.go +++ b/vendor/github.com/coreos/go-systemd/util/util_cgo.go @@ -122,11 +122,12 @@ func runningFromSystemService() (ret bool, err error) { errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid) serrno := syscall.Errno(-errno) // when we're running from a unit file, sd_pid_get_owner_uid returns - // ENOENT (systemd <220) or ENXIO (systemd >=220) + // ENOENT (systemd <220), ENXIO (systemd 220-223), or ENODATA + // (systemd >=234) switch { case errno >= 0: ret = false - case serrno == syscall.ENOENT, serrno == syscall.ENXIO: + case serrno == syscall.ENOENT, serrno == syscall.ENXIO, serrno == syscall.ENODATA: // Since the implementation of sessions in systemd relies on // the `pam_systemd` module, using the sd_pid_get_owner_uid // heuristic alone can result in false positives if that module diff --git a/vendor/github.com/godbus/dbus/README.markdown b/vendor/github.com/godbus/dbus/README.markdown index 0a6e7e5bfb0..fd29648752d 100644 --- a/vendor/github.com/godbus/dbus/README.markdown +++ b/vendor/github.com/godbus/dbus/README.markdown @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/godbus/dbus.svg?branch=master)](https://travis-ci.org/godbus/dbus) + dbus ---- @@ -12,7 +14,7 @@ D-Bus message bus system. ### Installation -This packages requires Go 1.1. If you installed it and set up your GOPATH, just run: +This packages requires Go 1.7. If you installed it and set up your GOPATH, just run: ``` go get github.com/godbus/dbus @@ -29,6 +31,7 @@ gives a short overview over the basic usage. #### Projects using godbus - [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. +- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API. Please note that the API is considered unstable for now and may change without further notice. diff --git a/vendor/github.com/godbus/dbus/auth.go b/vendor/github.com/godbus/dbus/auth.go index 98017b693ee..b0dcb54e6b5 100644 --- a/vendor/github.com/godbus/dbus/auth.go +++ b/vendor/github.com/godbus/dbus/auth.go @@ -116,7 +116,6 @@ func (conn *Conn) Auth(methods []Auth) error { return err } go conn.inWorker() - go conn.outWorker() return nil } } diff --git a/vendor/github.com/godbus/dbus/auth_anonymous.go b/vendor/github.com/godbus/dbus/auth_anonymous.go new file mode 100644 index 00000000000..75f3ad34d7a --- /dev/null +++ b/vendor/github.com/godbus/dbus/auth_anonymous.go @@ -0,0 +1,16 @@ +package dbus + +// AuthAnonymous returns an Auth that uses the ANONYMOUS mechanism. +func AuthAnonymous() Auth { + return &authAnonymous{} +} + +type authAnonymous struct{} + +func (a *authAnonymous) FirstData() (name, resp []byte, status AuthStatus) { + return []byte("ANONYMOUS"), nil, AuthOk +} + +func (a *authAnonymous) HandleData(data []byte) (resp []byte, status AuthStatus) { + return nil, AuthError +} diff --git a/vendor/github.com/godbus/dbus/call.go b/vendor/github.com/godbus/dbus/call.go index ba6e73f607a..2cb189012e7 100644 --- a/vendor/github.com/godbus/dbus/call.go +++ b/vendor/github.com/godbus/dbus/call.go @@ -1,9 +1,12 @@ package dbus import ( + "context" "errors" ) +var errSignature = errors.New("dbus: mismatched signature") + // Call represents a pending or completed method call. type Call struct { Destination string @@ -20,9 +23,25 @@ type Call struct { // Holds the response once the call is done. Body []interface{} + + // tracks context and canceler + ctx context.Context + ctxCanceler context.CancelFunc } -var errSignature = errors.New("dbus: mismatched signature") +func (c *Call) Context() context.Context { + if c.ctx == nil { + return context.Background() + } + + return c.ctx +} + +func (c *Call) ContextCancel() { + if c.ctxCanceler != nil { + c.ctxCanceler() + } +} // Store stores the body of the reply into the provided pointers. It returns // an error if the signatures of the body and retvalues don't match, or if @@ -34,3 +53,8 @@ func (c *Call) Store(retvalues ...interface{}) error { return Store(c.Body, retvalues...) } + +func (c *Call) done() { + c.Done <- c + c.ContextCancel() +} diff --git a/vendor/github.com/godbus/dbus/conn.go b/vendor/github.com/godbus/dbus/conn.go index a4f5394010a..b38920baf5b 100644 --- a/vendor/github.com/godbus/dbus/conn.go +++ b/vendor/github.com/godbus/dbus/conn.go @@ -1,6 +1,7 @@ package dbus import ( + "context" "errors" "io" "os" @@ -9,8 +10,6 @@ import ( "sync" ) -const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" - var ( systemBus *Conn systemBusLck sync.Mutex @@ -36,25 +35,13 @@ type Conn struct { unixFD bool uuid string - names []string - namesLck sync.RWMutex - - serialLck sync.Mutex - nextSerial uint32 - serialUsed map[uint32]bool - - calls map[uint32]*Call - callsLck sync.RWMutex + handler Handler + signalHandler SignalHandler + serialGen SerialGenerator - handlers map[ObjectPath]map[string]exportWithMapping - handlersLck sync.RWMutex - - out chan *Message - closed bool - outLck sync.RWMutex - - signals []chan<- *Signal - signalsLck sync.Mutex + names *nameTracker + calls *callTracker + outHandler *outputHandler eavesdropped chan<- *Message eavesdroppedLck sync.Mutex @@ -89,14 +76,32 @@ func SessionBus() (conn *Conn, err error) { return } +func getSessionBusAddress() (string, error) { + if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" { + return address, nil + + } else if address := tryDiscoverDbusSessionBusAddress(); address != "" { + os.Setenv("DBUS_SESSION_BUS_ADDRESS", address) + return address, nil + } + return getSessionBusPlatformAddress() +} + // SessionBusPrivate returns a new private connection to the session bus. -func SessionBusPrivate() (*Conn, error) { - address := os.Getenv("DBUS_SESSION_BUS_ADDRESS") - if address != "" && address != "autolaunch:" { - return Dial(address) +func SessionBusPrivate(opts ...ConnOption) (*Conn, error) { + address, err := getSessionBusAddress() + if err != nil { + return nil, err } - return sessionBusPlatform() + return Dial(address, opts...) +} + +// SessionBusPrivate returns a new private connection to the session bus. +// +// Deprecated: use SessionBusPrivate with options instead. +func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { + return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler)) } // SystemBus returns a shared connection to the system bus, connecting to it if @@ -129,37 +134,93 @@ func SystemBus() (conn *Conn, err error) { } // SystemBusPrivate returns a new private connection to the system bus. -func SystemBusPrivate() (*Conn, error) { - address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") - if address != "" { - return Dial(address) - } - return Dial(defaultSystemBusAddress) +func SystemBusPrivate(opts ...ConnOption) (*Conn, error) { + return Dial(getSystemBusPlatformAddress(), opts...) +} + +// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers. +// +// Deprecated: use SystemBusPrivate with options instead. +func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { + return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler)) } // Dial establishes a new private connection to the message bus specified by address. -func Dial(address string) (*Conn, error) { +func Dial(address string, opts ...ConnOption) (*Conn, error) { tr, err := getTransport(address) if err != nil { return nil, err } - return newConn(tr) + return newConn(tr, opts...) +} + +// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers. +// +// Deprecated: use Dial with options instead. +func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) { + return Dial(address, WithSignalHandler(signalHandler)) +} + +// ConnOption is a connection option. +type ConnOption func(conn *Conn) error + +// WithHandler overrides the default handler. +func WithHandler(handler Handler) ConnOption { + return func(conn *Conn) error { + conn.handler = handler + return nil + } +} + +// WithSignalHandler overrides the default signal handler. +func WithSignalHandler(handler SignalHandler) ConnOption { + return func(conn *Conn) error { + conn.signalHandler = handler + return nil + } +} + +// WithSerialGenerator overrides the default signals generator. +func WithSerialGenerator(gen SerialGenerator) ConnOption { + return func(conn *Conn) error { + conn.serialGen = gen + return nil + } } // NewConn creates a new private *Conn from an already established connection. -func NewConn(conn io.ReadWriteCloser) (*Conn, error) { - return newConn(genericTransport{conn}) +func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) { + return newConn(genericTransport{conn}, opts...) +} + +// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers. +// +// Deprecated: use NewConn with options instead. +func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) { + return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler)) } // newConn creates a new *Conn from a transport. -func newConn(tr transport) (*Conn, error) { +func newConn(tr transport, opts ...ConnOption) (*Conn, error) { conn := new(Conn) conn.transport = tr - conn.calls = make(map[uint32]*Call) - conn.out = make(chan *Message, 10) - conn.handlers = make(map[ObjectPath]map[string]exportWithMapping) - conn.nextSerial = 1 - conn.serialUsed = map[uint32]bool{0: true} + for _, opt := range opts { + if err := opt(conn); err != nil { + return nil, err + } + } + conn.calls = newCallTracker() + if conn.handler == nil { + conn.handler = NewDefaultHandler() + } + if conn.signalHandler == nil { + conn.signalHandler = NewDefaultSignalHandler() + } + if conn.serialGen == nil { + conn.serialGen = newSerialGenerator() + } + conn.outHandler = &outputHandler{conn: conn} + conn.names = newNameTracker() conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") return conn, nil } @@ -174,27 +235,21 @@ func (conn *Conn) BusObject() BusObject { // and the channels passed to Eavesdrop and Signal are closed. This method must // not be called on shared connections. func (conn *Conn) Close() error { - conn.outLck.Lock() - if conn.closed { - // inWorker calls Close on read error, the read error may - // be caused by another caller calling Close to shutdown the - // dbus connection, a double-close scenario we prevent here. - conn.outLck.Unlock() - return nil + conn.outHandler.close() + if term, ok := conn.signalHandler.(Terminator); ok { + term.Terminate() } - close(conn.out) - conn.closed = true - conn.outLck.Unlock() - conn.signalsLck.Lock() - for _, ch := range conn.signals { - close(ch) + + if term, ok := conn.handler.(Terminator); ok { + term.Terminate() } - conn.signalsLck.Unlock() + conn.eavesdroppedLck.Lock() if conn.eavesdropped != nil { close(conn.eavesdropped) } conn.eavesdroppedLck.Unlock() + return conn.transport.Close() } @@ -212,17 +267,9 @@ func (conn *Conn) Eavesdrop(ch chan<- *Message) { conn.eavesdroppedLck.Unlock() } -// getSerial returns an unused serial. +// GetSerial returns an unused serial. func (conn *Conn) getSerial() uint32 { - conn.serialLck.Lock() - defer conn.serialLck.Unlock() - n := conn.nextSerial - for conn.serialUsed[n] { - n++ - } - conn.serialUsed[n] = true - conn.nextSerial = n + 1 - return n + return conn.serialGen.GetSerial() } // Hello sends the initial org.freedesktop.DBus.Hello call. This method must be @@ -234,10 +281,7 @@ func (conn *Conn) Hello() error { if err != nil { return err } - conn.namesLck.Lock() - conn.names = make([]string, 1) - conn.names[0] = s - conn.namesLck.Unlock() + conn.names.acquireUniqueConnectionName(s) return nil } @@ -246,132 +290,90 @@ func (conn *Conn) Hello() error { func (conn *Conn) inWorker() { for { msg, err := conn.ReadMessage() - if err == nil { - conn.eavesdroppedLck.Lock() - if conn.eavesdropped != nil { - select { - case conn.eavesdropped <- msg: - default: - } - conn.eavesdroppedLck.Unlock() - continue - } - conn.eavesdroppedLck.Unlock() - dest, _ := msg.Headers[FieldDestination].value.(string) - found := false - if dest == "" { - found = true - } else { - conn.namesLck.RLock() - if len(conn.names) == 0 { - found = true - } - for _, v := range conn.names { - if dest == v { - found = true - break - } - } - conn.namesLck.RUnlock() + if err != nil { + if _, ok := err.(InvalidMessageError); !ok { + // Some read error occured (usually EOF); we can't really do + // anything but to shut down all stuff and returns errors to all + // pending replies. + conn.Close() + conn.calls.finalizeAllWithError(err) + return } - if !found { - // Eavesdropped a message, but no channel for it is registered. - // Ignore it. - continue + // invalid messages are ignored + continue + } + conn.eavesdroppedLck.Lock() + if conn.eavesdropped != nil { + select { + case conn.eavesdropped <- msg: + default: } - switch msg.Type { - case TypeMethodReply, TypeError: - serial := msg.Headers[FieldReplySerial].value.(uint32) - conn.callsLck.Lock() - if c, ok := conn.calls[serial]; ok { - if msg.Type == TypeError { - name, _ := msg.Headers[FieldErrorName].value.(string) - c.Err = Error{name, msg.Body} - } else { - c.Body = msg.Body - } - c.Done <- c - conn.serialLck.Lock() - delete(conn.serialUsed, serial) - conn.serialLck.Unlock() - delete(conn.calls, serial) - } - conn.callsLck.Unlock() - case TypeSignal: - iface := msg.Headers[FieldInterface].value.(string) - member := msg.Headers[FieldMember].value.(string) - // as per http://dbus.freedesktop.org/doc/dbus-specification.html , - // sender is optional for signals. - sender, _ := msg.Headers[FieldSender].value.(string) - if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" { - if member == "NameLost" { - // If we lost the name on the bus, remove it from our - // tracking list. - name, ok := msg.Body[0].(string) - if !ok { - panic("Unable to read the lost name") - } - conn.namesLck.Lock() - for i, v := range conn.names { - if v == name { - conn.names = append(conn.names[:i], - conn.names[i+1:]...) - } - } - conn.namesLck.Unlock() - } else if member == "NameAcquired" { - // If we acquired the name on the bus, add it to our - // tracking list. - name, ok := msg.Body[0].(string) - if !ok { - panic("Unable to read the acquired name") - } - conn.namesLck.Lock() - conn.names = append(conn.names, name) - conn.namesLck.Unlock() - } - } - signal := &Signal{ - Sender: sender, - Path: msg.Headers[FieldPath].value.(ObjectPath), - Name: iface + "." + member, - Body: msg.Body, - } - conn.signalsLck.Lock() - for _, ch := range conn.signals { - ch <- signal - } - conn.signalsLck.Unlock() - case TypeMethodCall: - go conn.handleCall(msg) + conn.eavesdroppedLck.Unlock() + continue + } + conn.eavesdroppedLck.Unlock() + dest, _ := msg.Headers[FieldDestination].value.(string) + found := dest == "" || + !conn.names.uniqueNameIsKnown() || + conn.names.isKnownName(dest) + if !found { + // Eavesdropped a message, but no channel for it is registered. + // Ignore it. + continue + } + switch msg.Type { + case TypeError: + conn.serialGen.RetireSerial(conn.calls.handleDBusError(msg)) + case TypeMethodReply: + conn.serialGen.RetireSerial(conn.calls.handleReply(msg)) + case TypeSignal: + conn.handleSignal(msg) + case TypeMethodCall: + go conn.handleCall(msg) + } + + } +} + +func (conn *Conn) handleSignal(msg *Message) { + iface := msg.Headers[FieldInterface].value.(string) + member := msg.Headers[FieldMember].value.(string) + // as per http://dbus.freedesktop.org/doc/dbus-specification.html , + // sender is optional for signals. + sender, _ := msg.Headers[FieldSender].value.(string) + if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" { + if member == "NameLost" { + // If we lost the name on the bus, remove it from our + // tracking list. + name, ok := msg.Body[0].(string) + if !ok { + panic("Unable to read the lost name") } - } else if _, ok := err.(InvalidMessageError); !ok { - // Some read error occured (usually EOF); we can't really do - // anything but to shut down all stuff and returns errors to all - // pending replies. - conn.Close() - conn.callsLck.RLock() - for _, v := range conn.calls { - v.Err = err - v.Done <- v + conn.names.loseName(name) + } else if member == "NameAcquired" { + // If we acquired the name on the bus, add it to our + // tracking list. + name, ok := msg.Body[0].(string) + if !ok { + panic("Unable to read the acquired name") } - conn.callsLck.RUnlock() - return + conn.names.acquireName(name) } - // invalid messages are ignored } + signal := &Signal{ + Sender: sender, + Path: msg.Headers[FieldPath].value.(ObjectPath), + Name: iface + "." + member, + Body: msg.Body, + } + conn.signalHandler.DeliverSignal(iface, member, signal) } // Names returns the list of all names that are currently owned by this // connection. The slice is always at least one element long, the first element // being the unique name of the connection. func (conn *Conn) Names() []string { - conn.namesLck.RLock() - // copy the slice so it can't be modified - s := make([]string, len(conn.names)) - copy(s, conn.names) - conn.namesLck.RUnlock() - return s + return conn.names.listKnownNames() } // Object returns the object identified by the given destination name and path. @@ -381,24 +383,17 @@ func (conn *Conn) Object(dest string, path ObjectPath) BusObject { // outWorker runs in an own goroutine, encoding and sending messages that are // sent to conn.out. -func (conn *Conn) outWorker() { - for msg := range conn.out { - err := conn.SendMessage(msg) - conn.callsLck.RLock() - if err != nil { - if c := conn.calls[msg.serial]; c != nil { - c.Err = err - c.Done <- c - } - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } else if msg.Type != TypeMethodCall { - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } - conn.callsLck.RUnlock() +func (conn *Conn) sendMessage(msg *Message) { + conn.sendMessageAndIfClosed(msg, func() {}) +} + +func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { + err := conn.outHandler.sendAndIfClosed(msg, ifClosed) + conn.calls.handleSendError(msg, err) + if err != nil { + conn.serialGen.RetireSerial(msg.serial) + } else if msg.Type != TypeMethodCall { + conn.serialGen.RetireSerial(msg.serial) } } @@ -409,8 +404,21 @@ func (conn *Conn) outWorker() { // once the call is complete. Otherwise, ch is ignored and a Call structure is // returned of which only the Err member is valid. func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { - var call *Call + return conn.send(context.Background(), msg, ch) +} + +// SendWithContext acts like Send but takes a context +func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call { + return conn.send(ctx, msg, ch) +} + +func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call { + if ctx == nil { + panic("nil context") + } + var call *Call + ctx, canceler := context.WithCancel(ctx) msg.serial = conn.getSerial() if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 { if ch == nil { @@ -426,33 +434,42 @@ func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { call.Method = iface + "." + member call.Args = msg.Body call.Done = ch - conn.callsLck.Lock() - conn.calls[msg.serial] = call - conn.callsLck.Unlock() - conn.outLck.RLock() - if conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - conn.out <- msg - } - conn.outLck.RUnlock() + call.ctx = ctx + call.ctxCanceler = canceler + conn.calls.track(msg.serial, call) + go func() { + <-ctx.Done() + conn.calls.handleSendError(msg, ctx.Err()) + }() + conn.sendMessageAndIfClosed(msg, func() { + conn.calls.handleSendError(msg, ErrClosed) + canceler() + }) } else { - conn.outLck.RLock() - if conn.closed { + canceler() + call = &Call{Err: nil} + conn.sendMessageAndIfClosed(msg, func() { call = &Call{Err: ErrClosed} - } else { - conn.out <- msg - call = &Call{Err: nil} - } - conn.outLck.RUnlock() + }) } return call } // sendError creates an error message corresponding to the parameters and sends // it to conn.out. -func (conn *Conn) sendError(e Error, dest string, serial uint32) { +func (conn *Conn) sendError(err error, dest string, serial uint32) { + var e *Error + switch em := err.(type) { + case Error: + e = &em + case *Error: + e = em + case DBusError: + name, body := em.DBusError() + e = NewError(name, body) + default: + e = MakeFailedError(err) + } msg := new(Message) msg.Type = TypeError msg.serial = conn.getSerial() @@ -466,11 +483,7 @@ func (conn *Conn) sendError(e Error, dest string, serial uint32) { if len(e.Body) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg - } - conn.outLck.RUnlock() + conn.sendMessage(msg) } // sendReply creates a method reply message corresponding to the parameters and @@ -488,28 +501,33 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg + conn.sendMessage(msg) +} + +func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) { + if !isDefaultSignalHandler(conn.signalHandler) { + return } - conn.outLck.RUnlock() + handler := conn.signalHandler.(*defaultSignalHandler) + fn(handler, ch) } // Signal registers the given channel to be passed all received signal messages. // The caller has to make sure that ch is sufficiently buffered; if a message // arrives when a write to c is not possible, it is discarded. // -// Multiple of these channels can be registered at the same time. Passing a -// channel that already is registered will remove it from the list of the -// registered channels. +// Multiple of these channels can be registered at the same time. // // These channels are "overwritten" by Eavesdrop; i.e., if there currently is a // channel for eavesdropped messages, this channel receives all signals, and // none of the channels passed to Signal will receive any signals. func (conn *Conn) Signal(ch chan<- *Signal) { - conn.signalsLck.Lock() - conn.signals = append(conn.signals, ch) - conn.signalsLck.Unlock() + conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch) +} + +// RemoveSignal removes the given channel from the list of the registered channels. +func (conn *Conn) RemoveSignal(ch chan<- *Signal) { + conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch) } // SupportsUnixFDs returns whether the underlying transport supports passing of @@ -610,16 +628,220 @@ func dereferenceAll(vs []interface{}) []interface{} { // getKey gets a key from a the list of keys. Returns "" on error / not found... func getKey(s, key string) string { - i := strings.Index(s, key) - if i == -1 { - return "" + for _, keyEqualsValue := range strings.Split(s, ",") { + keyValue := strings.SplitN(keyEqualsValue, "=", 2) + if len(keyValue) == 2 && keyValue[0] == key { + return keyValue[1] + } + } + return "" +} + +type outputHandler struct { + conn *Conn + sendLck sync.Mutex + closed struct { + isClosed bool + lck sync.RWMutex + } +} + +func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error { + h.closed.lck.RLock() + defer h.closed.lck.RUnlock() + if h.closed.isClosed { + ifClosed() + return nil + } + h.sendLck.Lock() + defer h.sendLck.Unlock() + return h.conn.SendMessage(msg) +} + +func (h *outputHandler) close() { + h.closed.lck.Lock() + defer h.closed.lck.Unlock() + h.closed.isClosed = true +} + +type serialGenerator struct { + lck sync.Mutex + nextSerial uint32 + serialUsed map[uint32]bool +} + +func newSerialGenerator() *serialGenerator { + return &serialGenerator{ + serialUsed: map[uint32]bool{0: true}, + nextSerial: 1, + } +} + +func (gen *serialGenerator) GetSerial() uint32 { + gen.lck.Lock() + defer gen.lck.Unlock() + n := gen.nextSerial + for gen.serialUsed[n] { + n++ + } + gen.serialUsed[n] = true + gen.nextSerial = n + 1 + return n +} + +func (gen *serialGenerator) RetireSerial(serial uint32) { + gen.lck.Lock() + defer gen.lck.Unlock() + delete(gen.serialUsed, serial) +} + +type nameTracker struct { + lck sync.RWMutex + unique string + names map[string]struct{} +} + +func newNameTracker() *nameTracker { + return &nameTracker{names: map[string]struct{}{}} +} +func (tracker *nameTracker) acquireUniqueConnectionName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + tracker.unique = name +} +func (tracker *nameTracker) acquireName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + tracker.names[name] = struct{}{} +} +func (tracker *nameTracker) loseName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + delete(tracker.names, name) +} + +func (tracker *nameTracker) uniqueNameIsKnown() bool { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + return tracker.unique != "" +} +func (tracker *nameTracker) isKnownName(name string) bool { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + _, ok := tracker.names[name] + return ok || name == tracker.unique +} +func (tracker *nameTracker) listKnownNames() []string { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + out := make([]string, 0, len(tracker.names)+1) + out = append(out, tracker.unique) + for k := range tracker.names { + out = append(out, k) + } + return out +} + +type callTracker struct { + calls map[uint32]*Call + lck sync.RWMutex +} + +func newCallTracker() *callTracker { + return &callTracker{calls: map[uint32]*Call{}} +} + +func (tracker *callTracker) track(sn uint32, call *Call) { + tracker.lck.Lock() + tracker.calls[sn] = call + tracker.lck.Unlock() +} + +func (tracker *callTracker) handleReply(msg *Message) uint32 { + serial := msg.Headers[FieldReplySerial].value.(uint32) + tracker.lck.RLock() + _, ok := tracker.calls[serial] + tracker.lck.RUnlock() + if ok { + tracker.finalizeWithBody(serial, msg.Body) + } + return serial +} + +func (tracker *callTracker) handleDBusError(msg *Message) uint32 { + serial := msg.Headers[FieldReplySerial].value.(uint32) + tracker.lck.RLock() + _, ok := tracker.calls[serial] + tracker.lck.RUnlock() + if ok { + name, _ := msg.Headers[FieldErrorName].value.(string) + tracker.finalizeWithError(serial, Error{name, msg.Body}) + } + return serial +} + +func (tracker *callTracker) handleSendError(msg *Message, err error) { + if err == nil { + return + } + tracker.lck.RLock() + _, ok := tracker.calls[msg.serial] + tracker.lck.RUnlock() + if ok { + tracker.finalizeWithError(msg.serial, err) + } +} + +// finalize was the only func that did not strobe Done +func (tracker *callTracker) finalize(sn uint32) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + c.ContextCancel() + } + return +} + +func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) { + tracker.lck.Lock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + } + tracker.lck.Unlock() + if ok { + c.Body = body + c.done() + } + return +} + +func (tracker *callTracker) finalizeWithError(sn uint32, err error) { + tracker.lck.Lock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + } + tracker.lck.Unlock() + if ok { + c.Err = err + c.done() } - if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' { - return "" + return +} + +func (tracker *callTracker) finalizeAllWithError(err error) { + tracker.lck.Lock() + closedCalls := make([]*Call, 0, len(tracker.calls)) + for sn := range tracker.calls { + closedCalls = append(closedCalls, tracker.calls[sn]) } - j := strings.Index(s, ",") - if j == -1 { - j = len(s) + tracker.calls = map[uint32]*Call{} + tracker.lck.Unlock() + for _, call := range closedCalls { + call.Err = err + call.done() } - return s[i+len(key)+1 : j] } diff --git a/vendor/github.com/godbus/dbus/conn_darwin.go b/vendor/github.com/godbus/dbus/conn_darwin.go index b67bb1b81da..6e2e4020216 100644 --- a/vendor/github.com/godbus/dbus/conn_darwin.go +++ b/vendor/github.com/godbus/dbus/conn_darwin.go @@ -2,20 +2,36 @@ package dbus import ( "errors" + "fmt" + "os" "os/exec" ) -func sessionBusPlatform() (*Conn, error) { +const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket" + +func getSessionBusPlatformAddress() (string, error) { cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET") b, err := cmd.CombinedOutput() if err != nil { - return nil, err + return "", err } if len(b) == 0 { - return nil, errors.New("dbus: couldn't determine address of session bus") + return "", errors.New("dbus: couldn't determine address of session bus") + } + + return "unix:path=" + string(b[:len(b)-1]), nil +} + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET") + if address != "" { + return fmt.Sprintf("unix:path=%s", address) } + return defaultSystemBusAddress +} - return Dial("unix:path=" + string(b[:len(b)-1])) +func tryDiscoverDbusSessionBusAddress() string { + return "" } diff --git a/vendor/github.com/godbus/dbus/conn_other.go b/vendor/github.com/godbus/dbus/conn_other.go index f74b8758d44..289044a44d4 100644 --- a/vendor/github.com/godbus/dbus/conn_other.go +++ b/vendor/github.com/godbus/dbus/conn_other.go @@ -5,23 +5,87 @@ package dbus import ( "bytes" "errors" + "fmt" + "io/ioutil" + "os" "os/exec" + "os/user" + "path" + "strings" ) -func sessionBusPlatform() (*Conn, error) { +func getSessionBusPlatformAddress() (string, error) { cmd := exec.Command("dbus-launch") b, err := cmd.CombinedOutput() if err != nil { - return nil, err + return "", err } i := bytes.IndexByte(b, '=') j := bytes.IndexByte(b, '\n') if i == -1 || j == -1 { - return nil, errors.New("dbus: couldn't determine address of session bus") + return "", errors.New("dbus: couldn't determine address of session bus") } - return Dial(string(b[i+1 : j])) + env, addr := string(b[0:i]), string(b[i+1:j]) + os.Setenv(env, addr) + + return addr, nil +} + +// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session +// and return the value of its DBUS_SESSION_BUS_ADDRESS. +// It tries different techniques employed by different operating systems, +// returning the first valid address it finds, or an empty string. +// +// * /run/user//bus if this exists, it *is* the bus socket. present on +// Ubuntu 18.04 +// * /run/user//dbus-session: if this exists, it can be parsed for the bus +// address. present on Ubuntu 16.04 +// +// See https://dbus.freedesktop.org/doc/dbus-launch.1.html +func tryDiscoverDbusSessionBusAddress() string { + if runtimeDirectory, err := getRuntimeDirectory(); err == nil { + + if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) { + // if /run/user//bus exists, that file itself + // *is* the unix socket, so return its path + return fmt.Sprintf("unix:path=%s", runUserBusFile) + } + if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) { + // if /run/user//dbus-session exists, it's a + // text file // containing the address of the socket, e.g.: + // DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG + + if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil { + fileContent := string(f) + + prefix := "DBUS_SESSION_BUS_ADDRESS=" + + if strings.HasPrefix(fileContent, prefix) { + address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r") + return address + } + } + } + } + return "" +} + +func getRuntimeDirectory() (string, error) { + if currentUser, err := user.Current(); err != nil { + return "", err + } else { + return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil + } +} + +func fileExists(filename string) bool { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + return true + } else { + return false + } } diff --git a/vendor/github.com/godbus/dbus/conn_unix.go b/vendor/github.com/godbus/dbus/conn_unix.go new file mode 100644 index 00000000000..4cba8ae8ec3 --- /dev/null +++ b/vendor/github.com/godbus/dbus/conn_unix.go @@ -0,0 +1,18 @@ +//+build !windows,!solaris,!darwin + +package dbus + +import ( + "os" + "fmt" +) + +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return fmt.Sprintf("unix:path=%s", address) + } + return defaultSystemBusAddress +} \ No newline at end of file diff --git a/vendor/github.com/godbus/dbus/conn_windows.go b/vendor/github.com/godbus/dbus/conn_windows.go new file mode 100644 index 00000000000..4291e4519cc --- /dev/null +++ b/vendor/github.com/godbus/dbus/conn_windows.go @@ -0,0 +1,15 @@ +//+build windows + +package dbus + +import "os" + +const defaultSystemBusAddress = "tcp:host=127.0.0.1,port=12434" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return address + } + return defaultSystemBusAddress +} diff --git a/vendor/github.com/godbus/dbus/dbus.go b/vendor/github.com/godbus/dbus/dbus.go index 2ce68735cdf..c6d0d3ce0e3 100644 --- a/vendor/github.com/godbus/dbus/dbus.go +++ b/vendor/github.com/godbus/dbus/dbus.go @@ -2,6 +2,7 @@ package dbus import ( "errors" + "fmt" "reflect" "strings" ) @@ -12,6 +13,8 @@ var ( 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)) @@ -22,6 +25,7 @@ var ( 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)) ) @@ -46,86 +50,251 @@ func Store(src []interface{}, dest ...interface{}) error { } for i := range src { - if err := store(src[i], dest[i]); err != nil { + if err := storeInterfaces(src[i], dest[i]); err != nil { return err } } return nil } -func store(src, dest interface{}) error { - if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) { - reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src)) +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 - } else if hasStruct(dest) { - rv := reflect.ValueOf(dest).Elem() - switch rv.Kind() { - case reflect.Struct: - vs, ok := src.([]interface{}) - if !ok { - return errors.New("dbus.Store: type mismatch") - } - t := rv.Type() - ndest := make([]interface{}, 0, rv.NumField()) - for i := 0; i < rv.NumField(); i++ { - field := t.Field(i) - if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { - ndest = append(ndest, rv.Field(i).Addr().Interface()) - } - } - if len(vs) != len(ndest) { - return errors.New("dbus.Store: type mismatch") - } - err := Store(vs, ndest...) - if err != nil { - return errors.New("dbus.Store: type mismatch") - } - case reflect.Slice: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Slice { - return errors.New("dbus.Store: type mismatch") - } - rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len())) - for i := 0; i < sv.Len(); i++ { - if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil { - return err - } - } - case reflect.Map: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Map { - return errors.New("dbus.Store: type mismatch") - } - keys := sv.MapKeys() - rv.Set(reflect.MakeMap(sv.Type())) - for _, key := range keys { - v := reflect.New(sv.Type().Elem()) - if err := store(v, sv.MapIndex(key).Interface()); err != nil { - return err - } - rv.SetMapIndex(key, v.Elem()) - } - default: - return errors.New("dbus.Store: type mismatch") + } + 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 } - return nil + 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 { - return errors.New("dbus.Store: type mismatch") + dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) + } + err := store(dv, src) + if err != nil { + return err } + return storeBase(dest, dv) } -func hasStruct(v interface{}) bool { - t := reflect.TypeOf(v) - for { - switch t.Kind() { - case reflect.Struct: - return true - case reflect.Slice, reflect.Ptr, reflect.Map: - t = t.Elem() - default: - return false +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. @@ -177,15 +346,15 @@ func alignment(t reflect.Type) int { return 4 case signatureType: return 1 - case interfacesType: // sometimes used for structs - return 8 + case interfacesType: + return 4 } switch t.Kind() { case reflect.Uint8: return 1 case reflect.Uint16, reflect.Int16: return 2 - case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: + 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 @@ -200,7 +369,7 @@ 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.String, reflect.Uint, reflect.Int: return true } diff --git a/vendor/github.com/godbus/dbus/decoder.go b/vendor/github.com/godbus/dbus/decoder.go index ef50dcab98d..5c27d3b51f6 100644 --- a/vendor/github.com/godbus/dbus/decoder.go +++ b/vendor/github.com/godbus/dbus/decoder.go @@ -191,7 +191,14 @@ func (dec *decoder) decode(s string, depth int) interface{} { length := dec.decode("u", depth).(uint32) v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length)) // Even for empty arrays, the correct padding must be included - dec.align(alignment(typeFor(s[1:]))) + align := alignment(typeFor(s[1:])) + if len(s) > 1 && s[1] == '(' { + //Special case for arrays of structs + //structs decode as a slice of interface{} values + //but the dbus alignment does not match this + align = 8 + } + dec.align(align) spos := dec.pos for dec.pos < spos+int(length) { ev := dec.decode(s[1:], depth+1) diff --git a/vendor/github.com/godbus/dbus/default_handler.go b/vendor/github.com/godbus/dbus/default_handler.go new file mode 100644 index 00000000000..81dbcc7e42a --- /dev/null +++ b/vendor/github.com/godbus/dbus/default_handler.go @@ -0,0 +1,321 @@ +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{ + 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] + } + } +} diff --git a/vendor/github.com/godbus/dbus/doc.go b/vendor/github.com/godbus/dbus/doc.go index deff554a381..895036a8c11 100644 --- a/vendor/github.com/godbus/dbus/doc.go +++ b/vendor/github.com/godbus/dbus/doc.go @@ -19,6 +19,8 @@ respective D-Bus equivalents: bool | BOOLEAN int16 | INT16 uint16 | UINT16 + int | INT32 + uint | UINT32 int32 | INT32 uint32 | UINT32 int64 | INT64 @@ -28,6 +30,7 @@ respective D-Bus equivalents: ObjectPath | OBJECT_PATH Signature | SIGNATURE Variant | VARIANT + interface{} | VARIANT UnixFDIndex | UNIX_FD Slices and arrays encode as ARRAYs of their element type. @@ -41,6 +44,9 @@ be skipped. Pointers encode as the value they're pointed to. +Types convertible to one of the base types above will be mapped as the +base type. + Trying to encode any other type or a slice, map or struct containing an unsupported type will result in an InvalidTypeError. diff --git a/vendor/github.com/godbus/dbus/encoder.go b/vendor/github.com/godbus/dbus/encoder.go index 9f0a9e89eab..8bb717761cd 100644 --- a/vendor/github.com/godbus/dbus/encoder.go +++ b/vendor/github.com/godbus/dbus/encoder.go @@ -96,10 +96,10 @@ func (enc *encoder) encode(v reflect.Value, depth int) { case reflect.Uint16: enc.binwrite(uint16(v.Uint())) enc.pos += 2 - case reflect.Int32: + case reflect.Int, reflect.Int32: enc.binwrite(int32(v.Int())) enc.pos += 4 - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: enc.binwrite(uint32(v.Uint())) enc.pos += 4 case reflect.Int64: @@ -202,6 +202,8 @@ func (enc *encoder) encode(v reflect.Value, depth int) { panic(err) } enc.pos += length + case reflect.Interface: + enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth) default: panic(InvalidTypeError{v.Type()}) } diff --git a/vendor/github.com/godbus/dbus/export.go b/vendor/github.com/godbus/dbus/export.go index c6440a7416a..95d0e29582d 100644 --- a/vendor/github.com/godbus/dbus/export.go +++ b/vendor/github.com/godbus/dbus/export.go @@ -8,111 +8,100 @@ import ( ) var ( - errmsgInvalidArg = Error{ + ErrMsgInvalidArg = Error{ "org.freedesktop.DBus.Error.InvalidArgs", []interface{}{"Invalid type / number of args"}, } - errmsgNoObject = Error{ + ErrMsgNoObject = Error{ "org.freedesktop.DBus.Error.NoSuchObject", []interface{}{"No such object"}, } - errmsgUnknownMethod = Error{ + ErrMsgUnknownMethod = Error{ "org.freedesktop.DBus.Error.UnknownMethod", []interface{}{"Unknown / invalid method"}, } + ErrMsgUnknownInterface = Error{ + "org.freedesktop.DBus.Error.UnknownInterface", + []interface{}{"Object does not implement the interface"}, + } ) -// exportWithMapping represents an exported struct along with a method name -// mapping to allow for exporting lower-case methods, etc. -type exportWithMapping struct { - export interface{} - - // Method name mapping; key -> struct method, value -> dbus method. - mapping map[string]string - - // Whether or not this export is for the entire subtree - includeSubtree bool +func MakeFailedError(err error) *Error { + return &Error{ + "org.freedesktop.DBus.Error.Failed", + []interface{}{err.Error()}, + } } // Sender is a type which can be used in exported methods to receive the message // sender. type Sender string -func exportedMethod(export exportWithMapping, name string) reflect.Value { - if export.export == nil { - return reflect.Value{} - } - - // If a mapping was included in the export, check the map to see if we - // should be looking for a different method in the export. - if export.mapping != nil { - for key, value := range export.mapping { - if value == name { - name = key - break - } - - // Catch the case where a method is aliased but the client is calling - // the original, e.g. the "Foo" method was exported mapped to - // "foo," and dbus client called the original "Foo." - if key == name { - return reflect.Value{} - } - } +func computeMethodName(name string, mapping map[string]string) string { + newname, ok := mapping[name] + if ok { + name = newname } + return name +} - value := reflect.ValueOf(export.export) - m := value.MethodByName(name) - - // Catch the case of attempting to call an unexported method - method, ok := value.Type().MethodByName(name) - - if !m.IsValid() || !ok || method.PkgPath != "" { - return reflect.Value{} +func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value { + if in == nil { + return nil } - t := m.Type() - if t.NumOut() == 0 || - t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) { - - return reflect.Value{} + methods := make(map[string]reflect.Value) + val := reflect.ValueOf(in) + typ := val.Type() + for i := 0; i < typ.NumMethod(); i++ { + methtype := typ.Method(i) + method := val.Method(i) + t := method.Type() + // only track valid methods must return *Error as last arg + // and must be exported + if t.NumOut() == 0 || + t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) || + methtype.PkgPath != "" { + continue + } + // map names while building table + methods[computeMethodName(methtype.Name, mapping)] = method } - return m + return methods } -// searchHandlers will look through all registered handlers looking for one -// to handle the given path. If a verbatim one isn't found, it will check for -// a subtree registration for the path as well. -func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) { - conn.handlersLck.RLock() - defer conn.handlersLck.RUnlock() +func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) { + pointers := make([]interface{}, m.NumArguments()) + decode := make([]interface{}, 0, len(body)) - handlers, ok := conn.handlers[path] - if ok { - return handlers, ok - } - - // If handlers weren't found for this exact path, look for a matching subtree - // registration - handlers = make(map[string]exportWithMapping) - path = path[:strings.LastIndex(string(path), "/")] - for len(path) > 0 { - var subtreeHandlers map[string]exportWithMapping - subtreeHandlers, ok = conn.handlers[path] - if ok { - for iface, handler := range subtreeHandlers { - // Only include this handler if it registered for the subtree - if handler.includeSubtree { - handlers[iface] = handler - } - } - - break + for i := 0; i < m.NumArguments(); i++ { + tp := reflect.TypeOf(m.ArgumentValue(i)) + val := reflect.New(tp) + pointers[i] = val.Interface() + if tp == reflect.TypeOf((*Sender)(nil)).Elem() { + val.Elem().SetString(sender) + } else if tp == reflect.TypeOf((*Message)(nil)).Elem() { + val.Elem().Set(reflect.ValueOf(*msg)) + } else { + decode = append(decode, pointers[i]) } + } + + if len(decode) != len(body) { + return nil, ErrMsgInvalidArg + } - path = path[:strings.LastIndex(string(path), "/")] + if err := Store(body, decode...); err != nil { + return nil, ErrMsgInvalidArg } - return handlers, ok + return pointers, nil +} + +func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) { + if decoder, ok := m.(ArgumentDecoder); ok { + return decoder.DecodeArguments(conn, sender, msg, msg.Body) + } + return standardMethodArgumentDecode(m, sender, msg, msg.Body) } // handleCall handles the given method call (i.e. looks if it's one of the @@ -120,7 +109,7 @@ func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, func (conn *Conn) handleCall(msg *Message) { name := msg.Headers[FieldMember].value.(string) path := msg.Headers[FieldPath].value.(ObjectPath) - ifaceName, hasIface := msg.Headers[FieldInterface].value.(string) + ifaceName, _ := msg.Headers[FieldInterface].value.(string) sender, hasSender := msg.Headers[FieldSender].value.(string) serial := msg.serial if ifaceName == "org.freedesktop.DBus.Peer" { @@ -130,76 +119,40 @@ func (conn *Conn) handleCall(msg *Message) { case "GetMachineId": conn.sendReply(sender, serial, conn.uuid) default: - conn.sendError(errmsgUnknownMethod, sender, serial) + conn.sendError(ErrMsgUnknownMethod, sender, serial) } return } if len(name) == 0 { - conn.sendError(errmsgUnknownMethod, sender, serial) + conn.sendError(ErrMsgUnknownMethod, sender, serial) } - // Find the exported handler (if any) for this path - handlers, ok := conn.searchHandlers(path) + object, ok := conn.handler.LookupObject(path) if !ok { - conn.sendError(errmsgNoObject, sender, serial) + conn.sendError(ErrMsgNoObject, sender, serial) return } - var m reflect.Value - if hasIface { - iface := handlers[ifaceName] - m = exportedMethod(iface, name) - } else { - for _, v := range handlers { - m = exportedMethod(v, name) - if m.IsValid() { - break - } - } - } - - if !m.IsValid() { - conn.sendError(errmsgUnknownMethod, sender, serial) + iface, exists := object.LookupInterface(ifaceName) + if !exists { + conn.sendError(ErrMsgUnknownInterface, sender, serial) return } - t := m.Type() - vs := msg.Body - pointers := make([]interface{}, t.NumIn()) - decode := make([]interface{}, 0, len(vs)) - for i := 0; i < t.NumIn(); i++ { - tp := t.In(i) - val := reflect.New(tp) - pointers[i] = val.Interface() - if tp == reflect.TypeOf((*Sender)(nil)).Elem() { - val.Elem().SetString(sender) - } else if tp == reflect.TypeOf((*Message)(nil)).Elem() { - val.Elem().Set(reflect.ValueOf(*msg)) - } else { - decode = append(decode, pointers[i]) - } - } - - if len(decode) != len(vs) { - conn.sendError(errmsgInvalidArg, sender, serial) + m, exists := iface.LookupMethod(name) + if !exists { + conn.sendError(ErrMsgUnknownMethod, sender, serial) return } - - if err := Store(vs, decode...); err != nil { - conn.sendError(errmsgInvalidArg, sender, serial) + args, err := conn.decodeArguments(m, sender, msg) + if err != nil { + conn.sendError(err, sender, serial) return } - // Extract parameters - params := make([]reflect.Value, len(pointers)) - for i := 0; i < len(pointers); i++ { - params[i] = reflect.ValueOf(pointers[i]).Elem() - } - - // Call method - ret := m.Call(params) - if em := ret[t.NumOut()-1].Interface().(*Error); em != nil { - conn.sendError(*em, sender, serial) + ret, err := m.Call(args...) + if err != nil { + conn.sendError(err, sender, serial) return } @@ -212,18 +165,13 @@ func (conn *Conn) handleCall(msg *Message) { reply.Headers[FieldDestination] = msg.Headers[FieldSender] } reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) - reply.Body = make([]interface{}, len(ret)-1) - for i := 0; i < len(ret)-1; i++ { - reply.Body[i] = ret[i].Interface() - } - if len(ret) != 1 { - reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) - } - conn.outLck.RLock() - if !conn.closed { - conn.out <- reply + reply.Body = make([]interface{}, len(ret)) + for i := 0; i < len(ret); i++ { + reply.Body[i] = ret[i] } - conn.outLck.RUnlock() + reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) + + conn.sendMessage(reply) } } @@ -256,12 +204,14 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.outLck.RLock() - defer conn.outLck.RUnlock() - if conn.closed { + + var closed bool + conn.sendMessageAndIfClosed(msg, func() { + closed = true + }) + if closed { return ErrClosed } - conn.out <- msg return nil } @@ -303,7 +253,7 @@ func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error { // The keys in the map are the real method names (exported on the struct), and // the values are the method names to be exported on DBus. func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error { - return conn.exportWithMap(v, mapping, path, iface, false) + return conn.export(getMethods(v, mapping), path, iface, false) } // ExportSubtree works exactly like Export but registers the given value for @@ -326,38 +276,89 @@ func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) er // The keys in the map are the real method names (exported on the struct), and // the values are the method names to be exported on DBus. func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error { - return conn.exportWithMap(v, mapping, path, iface, true) + return conn.export(getMethods(v, mapping), path, iface, true) +} + +// ExportMethodTable like Export registers the given methods as an object +// on the message bus. Unlike Export the it uses a method table to define +// the object instead of a native go object. +// +// The method table is a map from method name to function closure +// representing the method. This allows an object exported on the bus to not +// necessarily be a native go object. It can be useful for generating exposed +// methods on the fly. +// +// Any non-function objects in the method table are ignored. +func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error { + return conn.exportMethodTable(methods, path, iface, false) +} + +// Like ExportSubtree, but with the same caveats as ExportMethodTable. +func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error { + return conn.exportMethodTable(methods, path, iface, true) +} + +func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error { + out := make(map[string]reflect.Value) + for name, method := range methods { + rval := reflect.ValueOf(method) + if rval.Kind() != reflect.Func { + continue + } + t := rval.Type() + // only track valid methods must return *Error as last arg + if t.NumOut() == 0 || + t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) { + continue + } + out[name] = rval + } + return conn.export(out, path, iface, includeSubtree) +} + +func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error { + if h.PathExists(path) { + obj := h.objects[path] + obj.DeleteInterface(iface) + if len(obj.interfaces) == 0 { + h.DeleteObject(path) + } + } + return nil } // exportWithMap is the worker function for all exports/registrations. -func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error { +func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error { + h, ok := conn.handler.(*defaultHandler) + if !ok { + return fmt.Errorf( + `dbus: export only allowed on the default hander handler have %T"`, + conn.handler) + } + if !path.IsValid() { return fmt.Errorf(`dbus: Invalid path name: "%s"`, path) } - conn.handlersLck.Lock() - defer conn.handlersLck.Unlock() - // Remove a previous export if the interface is nil - if v == nil { - if _, ok := conn.handlers[path]; ok { - delete(conn.handlers[path], iface) - if len(conn.handlers[path]) == 0 { - delete(conn.handlers, path) - } - } - - return nil + if methods == nil { + return conn.unexport(h, path, iface) } // If this is the first handler for this path, make a new map to hold all // handlers for this path. - if _, ok := conn.handlers[path]; !ok { - conn.handlers[path] = make(map[string]exportWithMapping) + if !h.PathExists(path) { + h.AddObject(path, newExportedObject()) + } + + exportedMethods := make(map[string]Method) + for name, method := range methods { + exportedMethods[name] = exportedMethod{method} } // Finally, save this handler - conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree} + obj := h.objects[path] + obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree)) return nil } diff --git a/vendor/github.com/godbus/dbus/message.go b/vendor/github.com/godbus/dbus/message.go index 075d6e38bae..6a925367ebe 100644 --- a/vendor/github.com/godbus/dbus/message.go +++ b/vendor/github.com/godbus/dbus/message.go @@ -22,6 +22,13 @@ const ( // FlagNoAutoStart signals that the message bus should not automatically // start an application when handling this message. FlagNoAutoStart + // FlagAllowInteractiveAuthorization may be set on a method call + // message to inform the receiving side that the caller is prepared + // to wait for interactive authorization, which might take a + // considerable time to complete. For instance, if this flag is set, + // it would be appropriate to query the user for passwords or + // confirmation via Polkit or a similar framework. + FlagAllowInteractiveAuthorization ) // Type represents the possible types of a D-Bus message. @@ -248,7 +255,7 @@ func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { // IsValid checks whether msg is a valid message and returns an // InvalidMessageError if it is not. func (msg *Message) IsValid() error { - if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected) != 0 { + if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 { return InvalidMessageError("invalid flags") } if msg.Type == 0 || msg.Type >= typeMax { diff --git a/vendor/github.com/godbus/dbus/object.go b/vendor/github.com/godbus/dbus/object.go index 7ef45da4c88..f27ffe144a8 100644 --- a/vendor/github.com/godbus/dbus/object.go +++ b/vendor/github.com/godbus/dbus/object.go @@ -1,6 +1,7 @@ package dbus import ( + "context" "errors" "strings" ) @@ -9,7 +10,11 @@ import ( // invoked. type BusObject interface { Call(method string, flags Flags, args ...interface{}) *Call + CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call + GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call + AddMatchSignal(iface, member string, options ...MatchOption) *Call + RemoveMatchSignal(iface, member string, options ...MatchOption) *Call GetProperty(p string) (Variant, error) Destination() string Path() ObjectPath @@ -24,7 +29,74 @@ type Object struct { // Call calls a method with (*Object).Go and waits for its reply. func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { - return <-o.Go(method, flags, make(chan *Call, 1), args...).Done + return <-o.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done +} + +// CallWithContext acts like Call but takes a context +func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call { + return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done +} + +// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers. +// For full list of available options consult +// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules +type MatchOption struct { + key string + value string +} + +// WithMatchOption creates match option with given key and value +func WithMatchOption(key, value string) MatchOption { + return MatchOption{key, value} +} + +// WithMatchObjectPath creates match option that filters events based on given path +func WithMatchObjectPath(path ObjectPath) MatchOption { + return MatchOption{"path", string(path)} +} + +func formatMatchOptions(options []MatchOption) string { + items := make([]string, 0, len(options)) + for _, option := range options { + items = append(items, option.key+"='"+option.value+"'") + } + + return strings.Join(items, ",") +} + +// AddMatchSignal subscribes BusObject to signals from specified interface, +// method (member). Additional filter rules can be added via WithMatch* option constructors. +// Note: To filter events by object path you have to specify this path via an option. +func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + {"type", "signal"}, + {"interface", iface}, + {"member", member}, + } + + options = append(base, options...) + return o.conn.BusObject().Call( + "org.freedesktop.DBus.AddMatch", + 0, + formatMatchOptions(options), + ) +} + +// RemoveMatchSignal unsubscribes BusObject from signals from specified interface, +// method (member). Additional filter rules can be added via WithMatch* option constructors +func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + {"type", "signal"}, + {"interface", iface}, + {"member", member}, + } + + options = append(base, options...) + return o.conn.BusObject().Call( + "org.freedesktop.DBus.RemoveMatch", + 0, + formatMatchOptions(options), + ) } // Go calls a method with the given arguments asynchronously. It returns a @@ -33,11 +105,24 @@ func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { // will be allocated. Otherwise, ch has to be buffered or Go will panic. // // If the flags include FlagNoReplyExpected, ch is ignored and a Call structure -// is returned of which only the Err member is valid. +// is returned with any error in Err and a closed channel in Done containing +// the returned Call as it's one entry. // // If the method parameter contains a dot ('.'), the part before the last dot // specifies the interface on which the method is called. func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + return o.createCall(context.Background(), method, flags, ch, args...) +} + +// GoWithContext acts like Go but takes a context +func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + return o.createCall(ctx, method, flags, ch, args...) +} + +func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + if ctx == nil { + panic("nil context") + } iface := "" i := strings.LastIndex(method, ".") if i != -1 { @@ -65,33 +150,41 @@ func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface } else if cap(ch) == 0 { panic("dbus: unbuffered channel passed to (*Object).Go") } + ctx, cancel := context.WithCancel(ctx) call := &Call{ Destination: o.dest, Path: o.path, Method: method, Args: args, Done: ch, + ctxCanceler: cancel, + ctx: ctx, } - o.conn.callsLck.Lock() - o.conn.calls[msg.serial] = call - o.conn.callsLck.Unlock() - o.conn.outLck.RLock() - if o.conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - o.conn.out <- msg - } - o.conn.outLck.RUnlock() + o.conn.calls.track(msg.serial, call) + o.conn.sendMessageAndIfClosed(msg, func() { + o.conn.calls.handleSendError(msg, ErrClosed) + cancel() + }) + go func() { + <-ctx.Done() + o.conn.calls.handleSendError(msg, ctx.Err()) + }() + return call } - o.conn.outLck.RLock() - defer o.conn.outLck.RUnlock() - if o.conn.closed { - return &Call{Err: ErrClosed} + done := make(chan *Call, 1) + call := &Call{ + Err: nil, + Done: done, } - o.conn.out <- msg - return &Call{Err: nil} + defer func() { + call.Done <- call + close(done) + }() + o.conn.sendMessageAndIfClosed(msg, func() { + call.Err = ErrClosed + }) + return call } // GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given @@ -115,12 +208,12 @@ func (o *Object) GetProperty(p string) (Variant, error) { return result, nil } -// Destination returns the destination that calls on o are sent to. +// Destination returns the destination that calls on (o *Object) are sent to. func (o *Object) Destination() string { return o.dest } -// Path returns the path that calls on o are sent to. +// Path returns the path that calls on (o *Object") are sent to. func (o *Object) Path() ObjectPath { return o.path } diff --git a/vendor/github.com/godbus/dbus/server_interfaces.go b/vendor/github.com/godbus/dbus/server_interfaces.go new file mode 100644 index 00000000000..01166f0bd2a --- /dev/null +++ b/vendor/github.com/godbus/dbus/server_interfaces.go @@ -0,0 +1,99 @@ +package dbus + +// Terminator allows a handler to implement a shutdown mechanism that +// is called when the connection terminates. +type Terminator interface { + Terminate() +} + +// Handler is the representation of a D-Bus Application. +// +// The Handler must have a way to lookup objects given +// an ObjectPath. The returned object must implement the +// ServerObject interface. +type Handler interface { + LookupObject(path ObjectPath) (ServerObject, bool) +} + +// ServerObject is the representation of an D-Bus Object. +// +// Objects are registered at a path for a given Handler. +// The Objects implement D-Bus interfaces. The semantics +// of Interface lookup is up to the implementation of +// the ServerObject. The ServerObject implementation may +// choose to implement empty string as a valid interface +// represeting all methods or not per the D-Bus specification. +type ServerObject interface { + LookupInterface(name string) (Interface, bool) +} + +// An Interface is the representation of a D-Bus Interface. +// +// Interfaces are a grouping of methods implemented by the Objects. +// Interfaces are responsible for routing method calls. +type Interface interface { + LookupMethod(name string) (Method, bool) +} + +// A Method represents the exposed methods on D-Bus. +type Method interface { + // Call requires that all arguments are decoded before being passed to it. + Call(args ...interface{}) ([]interface{}, error) + NumArguments() int + NumReturns() int + // ArgumentValue returns a representative value for the argument at position + // it should be of the proper type. reflect.Zero would be a good mechanism + // to use for this Value. + ArgumentValue(position int) interface{} + // ReturnValue returns a representative value for the return at position + // it should be of the proper type. reflect.Zero would be a good mechanism + // to use for this Value. + ReturnValue(position int) interface{} +} + +// An Argument Decoder can decode arguments using the non-standard mechanism +// +// If a method implements this interface then the non-standard +// decoder will be used. +// +// Method arguments must be decoded from the message. +// The mechanism for doing this will vary based on the +// implementation of the method. A normal approach is provided +// as part of this library, but may be replaced with +// any other decoding scheme. +type ArgumentDecoder interface { + // To decode the arguments of a method the sender and message are + // provided incase the semantics of the implementer provides access + // to these as part of the method invocation. + DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error) +} + +// A SignalHandler is responsible for delivering a signal. +// +// Signal delivery may be changed from the default channel +// based approach by Handlers implementing the SignalHandler +// interface. +type SignalHandler interface { + DeliverSignal(iface, name string, signal *Signal) +} + +// A DBusError is used to convert a generic object to a D-Bus error. +// +// Any custom error mechanism may implement this interface to provide +// a custom encoding of the error on D-Bus. By default if a normal +// error is returned, it will be encoded as the generic +// "org.freedesktop.DBus.Error.Failed" error. By implementing this +// interface as well a custom encoding may be provided. +type DBusError interface { + DBusError() (string, []interface{}) +} + +// SerialGenerator is responsible for serials generation. +// +// Different approaches for the serial generation can be used, +// maintaining a map guarded with a mutex (the standard way) or +// simply increment an atomic counter. +type SerialGenerator interface { + GetSerial() uint32 + RetireSerial(serial uint32) +} diff --git a/vendor/github.com/godbus/dbus/sig.go b/vendor/github.com/godbus/dbus/sig.go index f45b53ce1b2..c1b809202cd 100644 --- a/vendor/github.com/godbus/dbus/sig.go +++ b/vendor/github.com/godbus/dbus/sig.go @@ -57,12 +57,12 @@ func getSignature(t reflect.Type) string { return "n" case reflect.Uint16: return "q" - case reflect.Int32: + case reflect.Int, reflect.Int32: if t == unixFDType { return "h" } return "i" - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: if t == unixFDIndexType { return "h" } @@ -101,6 +101,8 @@ func getSignature(t reflect.Type) string { panic(InvalidTypeError{t}) } return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" + case reflect.Interface: + return "v" } panic(InvalidTypeError{t}) } @@ -162,7 +164,7 @@ func (e SignatureError) Error() string { return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) } -// Try to read a single type from this string. If it was successfull, err is nil +// Try to read a single type from this string. If it was successful, err is nil // and rem is the remaining unparsed part. Otherwise, err is a non-nil // SignatureError and rem is "". depth is the current recursion depth which may // not be greater than 64 and should be given as 0 on the first call. diff --git a/vendor/github.com/godbus/dbus/transport_generic.go b/vendor/github.com/godbus/dbus/transport_generic.go index 46f8f49d699..718a1ff0219 100644 --- a/vendor/github.com/godbus/dbus/transport_generic.go +++ b/vendor/github.com/godbus/dbus/transport_generic.go @@ -4,8 +4,23 @@ import ( "encoding/binary" "errors" "io" + "unsafe" ) +var nativeEndian binary.ByteOrder + +func detectEndianness() binary.ByteOrder { + var x uint32 = 0x01020304 + if *(*byte)(unsafe.Pointer(&x)) == 0x01 { + return binary.BigEndian + } + return binary.LittleEndian +} + +func init() { + nativeEndian = detectEndianness() +} + type genericTransport struct { io.ReadWriteCloser } @@ -31,5 +46,5 @@ func (t genericTransport) SendMessage(msg *Message) error { return errors.New("dbus: unix fd passing not enabled") } } - return msg.EncodeTo(t, binary.LittleEndian) + return msg.EncodeTo(t, nativeEndian) } diff --git a/vendor/github.com/godbus/dbus/transport_nonce_tcp.go b/vendor/github.com/godbus/dbus/transport_nonce_tcp.go new file mode 100644 index 00000000000..697739efafb --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_nonce_tcp.go @@ -0,0 +1,39 @@ +//+build !windows + +package dbus + +import ( + "errors" + "io/ioutil" + "net" +) + +func init() { + transports["nonce-tcp"] = newNonceTcpTransport +} + +func newNonceTcpTransport(keys string) (transport, error) { + host := getKey(keys, "host") + port := getKey(keys, "port") + noncefile := getKey(keys, "noncefile") + if host == "" || port == "" || noncefile == "" { + return nil, errors.New("dbus: unsupported address (must set host, port and noncefile)") + } + protocol, err := tcpFamily(keys) + if err != nil { + return nil, err + } + socket, err := net.Dial(protocol, net.JoinHostPort(host, port)) + if err != nil { + return nil, err + } + b, err := ioutil.ReadFile(noncefile) + if err != nil { + return nil, err + } + _, err = socket.Write(b) + if err != nil { + return nil, err + } + return NewConn(socket) +} diff --git a/vendor/github.com/godbus/dbus/transport_tcp.go b/vendor/github.com/godbus/dbus/transport_tcp.go new file mode 100644 index 00000000000..dd1c8e59c5b --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_tcp.go @@ -0,0 +1,43 @@ +//+build !windows + +package dbus + +import ( + "errors" + "net" +) + +func init() { + transports["tcp"] = newTcpTransport +} + +func tcpFamily(keys string) (string, error) { + switch getKey(keys, "family") { + case "": + return "tcp", nil + case "ipv4": + return "tcp4", nil + case "ipv6": + return "tcp6", nil + default: + return "", errors.New("dbus: invalid tcp family (must be ipv4 or ipv6)") + } +} + +func newTcpTransport(keys string) (transport, error) { + host := getKey(keys, "host") + port := getKey(keys, "port") + if host == "" || port == "" { + return nil, errors.New("dbus: unsupported address (must set host and port)") + } + + protocol, err := tcpFamily(keys) + if err != nil { + return nil, err + } + socket, err := net.Dial(protocol, net.JoinHostPort(host, port)) + if err != nil { + return nil, err + } + return NewConn(socket) +} diff --git a/vendor/github.com/godbus/dbus/transport_unix.go b/vendor/github.com/godbus/dbus/transport_unix.go index 3fafeabb15b..f000c6b5d45 100644 --- a/vendor/github.com/godbus/dbus/transport_unix.go +++ b/vendor/github.com/godbus/dbus/transport_unix.go @@ -1,4 +1,4 @@ -//+build !windows +//+build !windows,!solaris package dbus @@ -31,6 +31,7 @@ func (o *oobReader) Read(b []byte) (n int, err error) { type unixTransport struct { *net.UnixConn + rdr *oobReader hasUnixFDs bool } @@ -79,10 +80,15 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // To be sure that all bytes of out-of-band data are read, we use a special // reader that uses ReadUnix on the underlying connection instead of Read // and gathers the out-of-band data in a buffer. - rd := &oobReader{conn: t.UnixConn} + if t.rdr == nil { + t.rdr = &oobReader{conn: t.UnixConn} + } else { + t.rdr.oob = nil + } + // read the first 16 bytes (the part of the header that has a constant size), // from which we can figure out the length of the rest of the message - if _, err := io.ReadFull(rd, csheader[:]); err != nil { + if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil { return nil, err } switch csheader[0] { @@ -104,7 +110,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // decode headers and look for unix fds headerdata := make([]byte, hlen+4) copy(headerdata, csheader[12:]) - if _, err := io.ReadFull(t, headerdata[4:]); err != nil { + if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil { return nil, err } dec := newDecoder(bytes.NewBuffer(headerdata), order) @@ -122,7 +128,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { all := make([]byte, 16+hlen+blen) copy(all, csheader[:]) copy(all[16:], headerdata[4:]) - if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil { + if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil { return nil, err } if unixfds != 0 { @@ -130,7 +136,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { return nil, errors.New("dbus: got unix fds on unsupported transport") } // read the fds from the OOB data - scms, err := syscall.ParseSocketControlMessage(rd.oob) + scms, err := syscall.ParseSocketControlMessage(t.rdr.oob) if err != nil { return nil, err } @@ -148,11 +154,23 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // substitute the values in the message body (which are indices for the // array receiver via OOB) with the actual values for i, v := range msg.Body { - if j, ok := v.(UnixFDIndex); ok { + switch v.(type) { + case UnixFDIndex: + j := v.(UnixFDIndex) if uint32(j) >= unixfds { return nil, InvalidMessageError("invalid index for unix fd") } msg.Body[i] = UnixFD(fds[j]) + case []UnixFDIndex: + idxArray := v.([]UnixFDIndex) + fdArray := make([]UnixFD, len(idxArray)) + for k, j := range idxArray { + if uint32(j) >= unixfds { + return nil, InvalidMessageError("invalid index for unix fd") + } + fdArray[k] = UnixFD(fds[j]) + } + msg.Body[i] = fdArray } } return msg, nil @@ -175,7 +193,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds))) oob := syscall.UnixRights(fds...) buf := new(bytes.Buffer) - msg.EncodeTo(buf, binary.LittleEndian) + msg.EncodeTo(buf, nativeEndian) n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil) if err != nil { return err @@ -184,7 +202,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { return io.ErrShortWrite } } else { - if err := msg.EncodeTo(t, binary.LittleEndian); err != nil { + if err := msg.EncodeTo(t, nativeEndian); err != nil { return nil } } diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go b/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go new file mode 100644 index 00000000000..0fc5b92739b --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go @@ -0,0 +1,91 @@ +// The UnixCredentials system call is currently only implemented on Linux +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// https://golang.org/s/go1.4-syscall +// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys + +// Local implementation of the UnixCredentials system call for FreeBSD + +package dbus + +/* +const int sizeofPtr = sizeof(void*); +#define _WANT_UCRED +#include +*/ +import "C" + +import ( + "io" + "os" + "syscall" + "unsafe" +) + +// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go +// https://golang.org/src/syscall/ztypes_freebsd_amd64.go +type Ucred struct { + Pid int32 + Uid uint32 + Gid uint32 +} + +// http://golang.org/src/pkg/syscall/types_linux.go +// https://golang.org/src/syscall/types_freebsd.go +// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h +const ( + SizeofUcred = C.sizeof_struct_ucred +) + +// http://golang.org/src/pkg/syscall/sockcmsg_unix.go +func cmsgAlignOf(salen int) int { + salign := C.sizeofPtr + + return (salen + salign - 1) & ^(salign - 1) +} + +// http://golang.org/src/pkg/syscall/sockcmsg_unix.go +func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer { + return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr))) +} + +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// UnixCredentials encodes credentials into a socket control message +// for sending to another process. This can be used for +// authentication. +func UnixCredentials(ucred *Ucred) []byte { + b := make([]byte, syscall.CmsgSpace(SizeofUcred)) + h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) + h.Level = syscall.SOL_SOCKET + h.Type = syscall.SCM_CREDS + h.SetLen(syscall.CmsgLen(SizeofUcred)) + *((*Ucred)(cmsgData(h))) = *ucred + return b +} + +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// ParseUnixCredentials decodes a socket control message that contains +// credentials in a Ucred structure. To receive such a message, the +// SO_PASSCRED option must be enabled on the socket. +func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) { + if m.Header.Level != syscall.SOL_SOCKET { + return nil, syscall.EINVAL + } + if m.Header.Type != syscall.SCM_CREDS { + return nil, syscall.EINVAL + } + ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0])) + return &ucred, nil +} + +func (t *unixTransport) SendNullByte() error { + ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())} + b := UnixCredentials(ucred) + _, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil) + if err != nil { + return err + } + if oobn != len(b) { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go b/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go new file mode 100644 index 00000000000..af7bafdf953 --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go @@ -0,0 +1,14 @@ +package dbus + +import "io" + +func (t *unixTransport) SendNullByte() error { + n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil) + if err != nil { + return err + } + if n != 1 { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/variant.go b/vendor/github.com/godbus/dbus/variant.go index b7b13ae90dd..0ca123b0128 100644 --- a/vendor/github.com/godbus/dbus/variant.go +++ b/vendor/github.com/godbus/dbus/variant.go @@ -17,7 +17,12 @@ type Variant struct { // MakeVariant converts the given value to a Variant. It panics if v cannot be // represented as a D-Bus type. func MakeVariant(v interface{}) Variant { - return Variant{SignatureOf(v), v} + return MakeVariantWithSignature(v, SignatureOf(v)) +} + +// MakeVariantWithSignature converts the given value to a Variant. +func MakeVariantWithSignature(v interface{}, s Signature) Variant { + return Variant{s, v} } // ParseVariant parses the given string as a variant as described at