github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobuffalo/flect v1.0.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
- github.com/godbus/dbus/v5 v5.1.0 // indirect
+ github.com/godbus/dbus/v5 v5.2.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
-github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
+github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
--- /dev/null
+# See https://cirrus-ci.org/guide/FreeBSD/
+freebsd_instance:
+ image_family: freebsd-14-2
+
+task:
+ name: Test on FreeBSD
+ install_script: pkg install -y go122 dbus
+ test_script: |
+ /usr/local/etc/rc.d/dbus onestart && \
+ eval `dbus-launch --sh-syntax` && \
+ go122 test -v ./...
--- /dev/null
+# For documentation, see https://golangci-lint.run/usage/configuration/
+
+linters:
+ enable:
+ - gofumpt
+ - unconvert
+ - unparam
### Installation
-This packages requires Go 1.12 or later. It can be installed by running the command below:
+This packages requires Go 1.20 or later. It can be installed by running the command below:
```
go get github.com/godbus/dbus/v5
### Usage
The complete package documentation and some simple examples are available at
-[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the
+[pkg.go.dev](https://pkg.go.dev/github.com/godbus/dbus/v5). Also, the
[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
gives a short overview over the basic usage.
- [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd".
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players.
+- [rpic](https://github.com/stephenhu/rpic) lightweight web app and RESTful API for managing a Raspberry Pi
Please note that the API is considered unstable for now and may change without
further notice.
--- /dev/null
+# Security Policy
+
+## Supported Versions
+
+Security updates are applied only to the latest release.
+
+## Reporting a Vulnerability
+
+If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
+
+Please disclose it at [security advisory](https://github.com/godbus/dbus/security/advisories/new).
+
+This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base.
func (conn *Conn) Auth(methods []Auth) error {
if methods == nil {
uid := strconv.Itoa(os.Geteuid())
- methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
+ methods = getDefaultAuthMethods(uid)
}
in := bufio.NewReader(conn.transport)
err := conn.transport.SendNullByte()
}
switch status {
case AuthOk:
- err, ok = conn.tryAuth(m, waitingForOk, in)
+ ok, err = conn.tryAuth(m, waitingForOk, in)
case AuthContinue:
- err, ok = conn.tryAuth(m, waitingForData, in)
+ ok, err = conn.tryAuth(m, waitingForData, in)
default:
panic("dbus: invalid authentication status")
}
}
// tryAuth tries to authenticate with m as the mechanism, using state as the
-// initial authState and in for reading input. It returns (nil, true) on
-// success, (nil, false) on a REJECTED and (someErr, false) if some other
+// initial authState and in for reading input. It returns (true, nil) on
+// success, (false, nil) on a REJECTED and (false, someErr) if some other
// error occurred.
-func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
+func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (bool, error) {
for {
s, err := authReadLine(in)
if err != nil {
- return err, false
+ return false, err
}
switch {
case state == waitingForData && string(s[0]) == "DATA":
if len(s) != 2 {
err = authWriteLine(conn.transport, []byte("ERROR"))
if err != nil {
- return err, false
+ return false, err
}
continue
}
if len(data) != 0 {
err = authWriteLine(conn.transport, []byte("DATA"), data)
if err != nil {
- return err, false
+ return false, err
}
}
if status == AuthOk {
case AuthError:
err = authWriteLine(conn.transport, []byte("ERROR"))
if err != nil {
- return err, false
+ return false, err
}
}
case state == waitingForData && string(s[0]) == "REJECTED":
- return nil, false
+ return false, nil
case state == waitingForData && string(s[0]) == "ERROR":
err = authWriteLine(conn.transport, []byte("CANCEL"))
if err != nil {
- return err, false
+ return false, err
}
state = waitingForReject
case state == waitingForData && string(s[0]) == "OK":
if len(s) != 2 {
err = authWriteLine(conn.transport, []byte("CANCEL"))
if err != nil {
- return err, false
+ return false, err
}
state = waitingForReject
} else {
conn.uuid = string(s[1])
- return nil, true
+ return true, nil
}
case state == waitingForData:
err = authWriteLine(conn.transport, []byte("ERROR"))
if err != nil {
- return err, false
+ return false, err
}
case state == waitingForOk && string(s[0]) == "OK":
if len(s) != 2 {
err = authWriteLine(conn.transport, []byte("CANCEL"))
if err != nil {
- return err, false
+ return false, err
}
state = waitingForReject
} else {
conn.uuid = string(s[1])
- return nil, true
+ return true, nil
}
case state == waitingForOk && string(s[0]) == "DATA":
err = authWriteLine(conn.transport, []byte("DATA"))
if err != nil {
- return err, false
+ return false, nil
}
case state == waitingForOk && string(s[0]) == "REJECTED":
- return nil, false
+ return false, nil
case state == waitingForOk && string(s[0]) == "ERROR":
err = authWriteLine(conn.transport, []byte("CANCEL"))
if err != nil {
- return err, false
+ return false, err
}
state = waitingForReject
case state == waitingForOk:
err = authWriteLine(conn.transport, []byte("ERROR"))
if err != nil {
- return err, false
+ return false, err
}
case state == waitingForReject && string(s[0]) == "REJECTED":
- return nil, false
+ return false, nil
case state == waitingForReject:
- return errors.New("dbus: authentication protocol error"), false
+ return false, errors.New("dbus: authentication protocol error")
default:
panic("dbus: invalid auth state")
}
--- /dev/null
+//go:build !windows
+// +build !windows
+
+package dbus
+
+func getDefaultAuthMethods(user string) []Auth {
+ return []Auth{AuthExternal(user)}
+}
--- /dev/null
+package dbus
+
+func getDefaultAuthMethods(user string) []Auth {
+ return []Auth{AuthCookieSha1(user, getHomeDir())}
+}
+++ /dev/null
-package dbus
-
-import (
- "bufio"
- "bytes"
- "crypto/rand"
- "crypto/sha1"
- "encoding/hex"
- "os"
-)
-
-// AuthCookieSha1 returns an Auth that authenticates as the given user with the
-// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home
-// directory of the user.
-func AuthCookieSha1(user, home string) Auth {
- return authCookieSha1{user, home}
-}
-
-type authCookieSha1 struct {
- user, home string
-}
-
-func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) {
- b := make([]byte, 2*len(a.user))
- hex.Encode(b, []byte(a.user))
- return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue
-}
-
-func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
- challenge := make([]byte, len(data)/2)
- _, err := hex.Decode(challenge, data)
- if err != nil {
- return nil, AuthError
- }
- b := bytes.Split(challenge, []byte{' '})
- if len(b) != 3 {
- return nil, AuthError
- }
- context := b[0]
- id := b[1]
- svchallenge := b[2]
- cookie := a.getCookie(context, id)
- if cookie == nil {
- return nil, AuthError
- }
- clchallenge := a.generateChallenge()
- if clchallenge == nil {
- return nil, AuthError
- }
- hash := sha1.New()
- hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'}))
- hexhash := make([]byte, 2*hash.Size())
- hex.Encode(hexhash, hash.Sum(nil))
- data = append(clchallenge, ' ')
- data = append(data, hexhash...)
- resp := make([]byte, 2*len(data))
- hex.Encode(resp, data)
- return resp, AuthOk
-}
-
-// getCookie searches for the cookie identified by id in context and returns
-// the cookie content or nil. (Since HandleData can't return a specific error,
-// but only whether an error occurred, this function also doesn't bother to
-// return an error.)
-func (a authCookieSha1) getCookie(context, id []byte) []byte {
- file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
- if err != nil {
- return nil
- }
- defer file.Close()
- rd := bufio.NewReader(file)
- for {
- line, err := rd.ReadBytes('\n')
- if err != nil {
- return nil
- }
- line = line[:len(line)-1]
- b := bytes.Split(line, []byte{' '})
- if len(b) != 3 {
- return nil
- }
- if bytes.Equal(b[0], id) {
- return b[2]
- }
- }
-}
-
-// generateChallenge returns a random, hex-encoded challenge, or nil on error
-// (see above).
-func (a authCookieSha1) generateChallenge() []byte {
- b := make([]byte, 16)
- n, err := rand.Read(b)
- if err != nil {
- return nil
- }
- if n != 16 {
- return nil
- }
- enc := make([]byte, 32)
- hex.Encode(enc, b)
- return enc
-}
--- /dev/null
+package dbus
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/rand"
+ "crypto/sha1"
+ "encoding/hex"
+ "os"
+ "os/user"
+)
+
+// AuthCookieSha1 returns an Auth that authenticates as the given user with the
+// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home
+// directory of the user.
+func AuthCookieSha1(user, home string) Auth {
+ return authCookieSha1{user, home}
+}
+
+type authCookieSha1 struct {
+ user, home string
+}
+
+func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) {
+ b := make([]byte, 2*len(a.user))
+ hex.Encode(b, []byte(a.user))
+ return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue
+}
+
+func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
+ challenge := make([]byte, len(data)/2)
+ _, err := hex.Decode(challenge, data)
+ if err != nil {
+ return nil, AuthError
+ }
+ b := bytes.Split(challenge, []byte{' '})
+ if len(b) != 3 {
+ return nil, AuthError
+ }
+ context := b[0]
+ id := b[1]
+ svchallenge := b[2]
+ cookie := a.getCookie(context, id)
+ if cookie == nil {
+ return nil, AuthError
+ }
+ clchallenge := a.generateChallenge()
+ if clchallenge == nil {
+ return nil, AuthError
+ }
+ hash := sha1.New()
+ hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'}))
+ hexhash := make([]byte, 2*hash.Size())
+ hex.Encode(hexhash, hash.Sum(nil))
+ data = append(clchallenge, ' ')
+ data = append(data, hexhash...)
+ resp := make([]byte, 2*len(data))
+ hex.Encode(resp, data)
+ return resp, AuthOk
+}
+
+// getCookie searches for the cookie identified by id in context and returns
+// the cookie content or nil. (Since HandleData can't return a specific error,
+// but only whether an error occurred, this function also doesn't bother to
+// return an error.)
+func (a authCookieSha1) getCookie(context, id []byte) []byte {
+ file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
+ if err != nil {
+ return nil
+ }
+ defer file.Close()
+ rd := bufio.NewReader(file)
+ for {
+ line, err := rd.ReadBytes('\n')
+ if err != nil {
+ return nil
+ }
+ line = line[:len(line)-1]
+ b := bytes.Split(line, []byte{' '})
+ if len(b) != 3 {
+ return nil
+ }
+ if bytes.Equal(b[0], id) {
+ return b[2]
+ }
+ }
+}
+
+// generateChallenge returns a random, hex-encoded challenge, or nil on error
+// (see above).
+func (a authCookieSha1) generateChallenge() []byte {
+ b := make([]byte, 16)
+ n, err := rand.Read(b)
+ if err != nil {
+ return nil
+ }
+ if n != 16 {
+ return nil
+ }
+ enc := make([]byte, 32)
+ hex.Encode(enc, b)
+ return enc
+}
+
+// Get returns the home directory of the current user, which is usually the
+// value of HOME environment variable. In case it is not set or empty, os/user
+// package is used.
+//
+// If linking statically with cgo enabled against glibc, make sure the
+// osusergo build tag is used.
+//
+// If needing to do nss lookups, do not disable cgo or set osusergo.
+func getHomeDir() string {
+ homeDir := os.Getenv("HOME")
+ if homeDir != "" {
+ return homeDir
+ }
+ if u, err := user.Current(); err == nil {
+ return u.HomeDir
+ }
+ return "/"
+}
import (
"context"
- "errors"
)
-var errSignature = errors.New("dbus: mismatched signature")
-
// Call represents a pending or completed method call.
type Call struct {
Destination string
Path ObjectPath
Method string
- Args []interface{}
+ Args []any
// Strobes when the call is complete.
Done chan *Call
Err error
// Holds the response once the call is done.
- Body []interface{}
+ Body []any
// ResponseSequence stores the sequence number of the DBus message containing
// the call response (or error). This can be compared to the sequence number
// 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
// the error status is not nil.
-func (c *Call) Store(retvalues ...interface{}) error {
+func (c *Call) Store(retvalues ...any) error {
if c.Err != nil {
return c.Err
}
import (
"context"
"errors"
+ "fmt"
"io"
"os"
"strings"
func getSessionBusAddress(autolaunch bool) (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 Dial(address, opts...)
}
-// SessionBusPrivate returns a new private connection to the session bus. If
+// SessionBusPrivateNoAutoStartup returns a new private connection to the session bus. If
// the session bus is not already open, do not attempt to launch it.
func SessionBusPrivateNoAutoStartup(opts ...ConnOption) (*Conn, error) {
address, err := getSessionBusAddress(false)
return Dial(address, opts...)
}
-// SessionBusPrivate returns a new private connection to the session bus.
+// SessionBusPrivateHandler returns a new private connection to the session bus.
//
// Deprecated: use SessionBusPrivate with options instead.
func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
return &Object{conn, dest, path}
}
-func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
+func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) error {
if msg.serial == 0 {
msg.serial = conn.getSerial()
}
} else if msg.Type != TypeMethodCall {
conn.serialGen.RetireSerial(msg.serial)
}
+ return err
+}
+
+func isEncodingError(err error) bool {
+ switch err.(type) {
+ case FormatError:
+ return true
+ case InvalidMessageError:
+ return true
+ }
+ return false
}
func (conn *Conn) handleSendError(msg *Message, err error) {
if msg.Type == TypeMethodCall {
conn.calls.handleSendError(msg, err)
} else if msg.Type == TypeMethodReply {
- if _, ok := err.(FormatError); ok {
- conn.sendError(err, msg.Headers[FieldDestination].value.(string), msg.Headers[FieldReplySerial].value.(uint32))
+ if isEncodingError(err) {
+ // Make sure that the caller gets some kind of error response if
+ // the application code tried to respond, but the resulting message
+ // was malformed in the end
+ returnedErr := fmt.Errorf("destination tried to respond with invalid message (%w)", err)
+ conn.sendError(returnedErr, msg.Headers[FieldDestination].value.(string), msg.Headers[FieldReplySerial].value.(uint32))
}
}
conn.serialGen.RetireSerial(msg.serial)
<-ctx.Done()
conn.calls.handleSendError(msg, ctx.Err())
}()
- conn.sendMessageAndIfClosed(msg, func() {
+ // error is handled in handleSendError
+ _ = conn.sendMessageAndIfClosed(msg, func() {
conn.calls.handleSendError(msg, ErrClosed)
canceler()
})
canceler()
call = &Call{Err: nil, Done: ch}
ch <- call
- conn.sendMessageAndIfClosed(msg, func() {
+ // error is handled in handleSendError
+ _ = conn.sendMessageAndIfClosed(msg, func() {
call = &Call{Err: ErrClosed}
})
}
if len(e.Body) > 0 {
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
}
- conn.sendMessageAndIfClosed(msg, nil)
+ // not much we can do to handle a possible error here
+ _ = conn.sendMessageAndIfClosed(msg, nil)
}
// sendReply creates a method reply message corresponding to the parameters and
// sends it to conn.out.
-func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
+func (conn *Conn) sendReply(dest string, serial uint32, values ...any) {
msg := new(Message)
msg.Type = TypeMethodReply
msg.Headers = make(map[HeaderField]Variant)
if len(values) > 0 {
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
}
- conn.sendMessageAndIfClosed(msg, nil)
+ // not much we can do to handle a possible error here
+ _ = conn.sendMessageAndIfClosed(msg, nil)
}
// AddMatchSignal registers the given match rule to receive broadcast
// AddMatchSignalContext acts like AddMatchSignal but takes a context.
func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error {
- options = append([]MatchOption{withMatchType("signal")}, options...)
+ options = append([]MatchOption{withMatchTypeSignal()}, options...)
return conn.busObj.CallWithContext(
ctx,
"org.freedesktop.DBus.AddMatch", 0,
// RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context.
func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error {
- options = append([]MatchOption{withMatchType("signal")}, options...)
+ options = append([]MatchOption{withMatchTypeSignal()}, options...)
return conn.busObj.CallWithContext(
ctx,
"org.freedesktop.DBus.RemoveMatch", 0,
// Error represents a D-Bus message of type Error.
type Error struct {
Name string
- Body []interface{}
+ Body []any
}
-func NewError(name string, body []interface{}) *Error {
+func NewError(name string, body []any) *Error {
return &Error{name, body}
}
Sender string
Path ObjectPath
Name string
- Body []interface{}
+ Body []any
Sequence Sequence
}
SendMessage(*Message) error
}
-var (
- transports = make(map[string]func(string) (transport, error))
-)
+var transports = make(map[string]func(string) (transport, error))
func getTransport(address string) (transport, error) {
var err error
// getKey gets a key from a the list of keys. Returns "" on error / not found...
func getKey(s, key string) string {
- for _, keyEqualsValue := range strings.Split(s, ",") {
- keyValue := strings.SplitN(keyEqualsValue, "=", 2)
- if len(keyValue) == 2 && keyValue[0] == key {
- val, err := UnescapeBusAddressValue(keyValue[1])
+ keyEq := key + "="
+ for _, kv := range strings.Split(s, ",") {
+ if v, ok := strings.CutPrefix(kv, keyEq); ok {
+ val, err := UnescapeBusAddressValue(v)
if err != nil {
// No way to return an error.
return ""
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()
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()
}
}
-// 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()
- }
-}
-
-func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []interface{}) {
+func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []any) {
tracker.lck.Lock()
c, ok := tracker.calls[sn]
if ok {
func getSessionBusPlatformAddress() (string, error) {
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
b, err := cmd.CombinedOutput()
-
if err != nil {
return "", err
}
+//go:build !darwin
// +build !darwin
package dbus
"bytes"
"errors"
"fmt"
- "io/ioutil"
"os"
"os/exec"
"os/user"
func getSessionBusPlatformAddress() (string, error) {
cmd := execCommand("dbus-launch")
b, err := cmd.CombinedOutput()
-
if err != nil {
return "", err
}
// It tries different techniques employed by different operating systems,
// returning the first valid address it finds, or an empty string.
//
-// * /run/user/<uid>/bus if this exists, it *is* the bus socket. present on
-// Ubuntu 18.04
-// * /run/user/<uid>/dbus-session: if this exists, it can be parsed for the bus
-// address. present on Ubuntu 16.04
+// - /run/user/<uid>/bus if this exists, it *is* the bus socket. present on
+// Ubuntu 18.04
+// - /run/user/<uid>/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 {
// 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
+ if f, err := os.ReadFile(runUserSessionDbusFile); err == nil {
+ if addr, ok := strings.CutPrefix(string(f), "DBUS_SESSION_BUS_ADDRESS="); ok {
+ return strings.TrimRight(addr, "\n\r")
}
}
}
-//+build !windows,!solaris,!darwin
+//go:build !windows && !solaris && !darwin
+// +build !windows,!solaris,!darwin
package dbus
import (
+ "net"
"os"
)
}
return defaultSystemBusAddress
}
+
+// DialUnix establishes a new private connection to the message bus specified by UnixConn.
+func DialUnix(conn *net.UnixConn, opts ...ConnOption) (*Conn, error) {
+ tr := newUnixTransportFromConn(conn)
+ return newConn(tr, opts...)
+}
+
+func ConnectUnix(uconn *net.UnixConn, opts ...ConnOption) (*Conn, error) {
+ conn, err := DialUnix(uconn, opts...)
+ if err != nil {
+ return nil, err
+ }
+ if err = conn.Auth(conn.auth); err != nil {
+ _ = conn.Close()
+ return nil, err
+ }
+ if err = conn.Hello(); err != nil {
+ _ = conn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
-//+build windows
-
package dbus
import "os"
var (
byteType = reflect.TypeOf(byte(0))
boolType = reflect.TypeOf(false)
- uint8Type = reflect.TypeOf(uint8(0))
int16Type = reflect.TypeOf(int16(0))
uint16Type = reflect.TypeOf(uint16(0))
- intType = reflect.TypeOf(int(0))
- uintType = reflect.TypeOf(uint(0))
int32Type = reflect.TypeOf(int32(0))
uint32Type = reflect.TypeOf(uint32(0))
int64Type = reflect.TypeOf(int64(0))
signatureType = reflect.TypeOf(Signature{""})
objectPathType = reflect.TypeOf(ObjectPath(""))
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
- interfacesType = reflect.TypeOf([]interface{}{})
- interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
+ interfacesType = reflect.TypeOf([]any{})
+ interfaceType = reflect.TypeOf((*any)(nil)).Elem()
unixFDType = reflect.TypeOf(UnixFD(0))
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
errType = reflect.TypeOf((*error)(nil)).Elem()
// pointers. It converts slices of interfaces from src to corresponding structs
// in dest. An error is returned if the lengths of src and dest or the types of
// their elements don't match.
-func Store(src []interface{}, dest ...interface{}) error {
+func Store(src []any, dest ...any) error {
if len(src) != len(dest) {
return errors.New("dbus.Store: length mismatch")
}
return nil
}
-func storeInterfaces(src, dest interface{}) error {
+func storeInterfaces(src, dest any) error {
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
}
func setDest(dest, src reflect.Value) error {
if !isVariant(src.Type()) && isVariant(dest.Type()) {
- //special conversion for dbus.Variant
+ // special conversion for dbus.Variant
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
return nil
}
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{}
+ // Convert variants to interface{} recursively when converting
+ // to interface{}
dv = reflect.MakeMap(
reflect.MapOf(src.Type().Key(), interfaceType))
} else {
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{}
+ // The decoder always decodes structs as slices of interface{}
return storeStruct(dest, src)
case !kindsAreCompatible(dest.Type(), src.Type()):
return fmt.Errorf(
if isVariant(dest.Type()) {
return storeBase(dest, src)
}
- dval := make([]interface{}, 0, dest.NumField())
+ dval := make([]any, 0, dest.NumField())
dtype := dest.Type()
for i := 0; i < dest.NumField(); i++ {
field := dest.Field(i)
"enough fields need: %d have: %d",
src.Len(), len(dval))
}
- return Store(src.Interface().([]interface{}), dval...)
+ return Store(src.Interface().([]any), dval...)
}
func storeSliceIntoVariant(dest, src reflect.Value) error {
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{}
+ // Convert variants to interface{} recursively when converting
+ // to interface{}
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
src.Len(), src.Cap())
} else {
}
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
-// documentation for more information about Unix file descriptor passsing.
+// documentation for more information about Unix file descriptor passing.
type UnixFD int32
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
"encoding/binary"
"io"
"reflect"
+ "unsafe"
)
type decoder struct {
order binary.ByteOrder
pos int
fds []int
+
+ // The following fields are used to reduce memory allocs.
+ conv *stringConverter
+ buf []byte
+ d float64
+ y [1]byte
}
// newDecoder returns a new decoder that reads values from in. The input is
dec.in = in
dec.order = order
dec.fds = fds
+ dec.conv = newStringConverter(stringConverterBufferSize)
return dec
}
+// Reset resets the decoder to be reading from in.
+func (dec *decoder) Reset(in io.Reader, order binary.ByteOrder, fds []int) {
+ dec.in = in
+ dec.order = order
+ dec.pos = 0
+ dec.fds = fds
+
+ if dec.conv == nil {
+ dec.conv = newStringConverter(stringConverterBufferSize)
+ }
+}
+
// align aligns the input to the given boundary and panics on error.
func (dec *decoder) align(n int) {
if dec.pos%n != 0 {
newpos := (dec.pos + n - 1) & ^(n - 1)
- empty := make([]byte, newpos-dec.pos)
- if _, err := io.ReadFull(dec.in, empty); err != nil {
- panic(err)
- }
+ dec.read2buf(newpos - dec.pos)
dec.pos = newpos
}
}
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
-func (dec *decoder) binread(v interface{}) {
+func (dec *decoder) binread(v any) {
if err := binary.Read(dec.in, dec.order, v); err != nil {
panic(err)
}
}
-func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
+func (dec *decoder) Decode(sig Signature) (vs []any, err error) {
defer func() {
var ok bool
v := recover()
}
}
}()
- vs = make([]interface{}, 0)
+ vs = make([]any, 0)
s := sig.str
for s != "" {
err, rem := validSingle(s, &depthCounter{})
return vs, nil
}
-func (dec *decoder) decode(s string, depth int) interface{} {
+// read2buf reads exactly n bytes from the reader dec.in into the buffer dec.buf
+// to reduce memory allocs.
+// The buffer grows automatically.
+func (dec *decoder) read2buf(n int) {
+ if cap(dec.buf) < n {
+ dec.buf = make([]byte, n)
+ } else {
+ dec.buf = dec.buf[:n]
+ }
+ if _, err := io.ReadFull(dec.in, dec.buf); err != nil {
+ panic(err)
+ }
+}
+
+// decodeU decodes uint32 obtained from the reader dec.in.
+// The goal is to reduce memory allocs.
+func (dec *decoder) decodeU() uint32 {
+ dec.align(4)
+ dec.read2buf(4)
+ dec.pos += 4
+ return dec.order.Uint32(dec.buf)
+}
+
+func (dec *decoder) decode(s string, depth int) any {
dec.align(alignment(typeFor(s)))
switch s[0] {
case 'y':
- var b [1]byte
- if _, err := dec.in.Read(b[:]); err != nil {
+ if _, err := dec.in.Read(dec.y[:]); err != nil {
panic(err)
}
dec.pos++
- return b[0]
+ return dec.y[0]
case 'b':
- i := dec.decode("u", depth).(uint32)
- switch {
- case i == 0:
+ switch dec.decodeU() {
+ case 0:
return false
- case i == 1:
+ case 1:
return true
default:
panic(FormatError("invalid value for boolean"))
}
case 'n':
- var i int16
- dec.binread(&i)
+ dec.read2buf(2)
dec.pos += 2
- return i
+ return int16(dec.order.Uint16(dec.buf))
case 'i':
- var i int32
- dec.binread(&i)
+ dec.read2buf(4)
dec.pos += 4
- return i
+ return int32(dec.order.Uint32(dec.buf))
case 'x':
- var i int64
- dec.binread(&i)
+ dec.read2buf(8)
dec.pos += 8
- return i
+ return int64(dec.order.Uint64(dec.buf))
case 'q':
- var i uint16
- dec.binread(&i)
+ dec.read2buf(2)
dec.pos += 2
- return i
+ return dec.order.Uint16(dec.buf)
case 'u':
- var i uint32
- dec.binread(&i)
- dec.pos += 4
- return i
+ return dec.decodeU()
case 't':
- var i uint64
- dec.binread(&i)
+ dec.read2buf(8)
dec.pos += 8
- return i
+ return dec.order.Uint64(dec.buf)
case 'd':
- var f float64
- dec.binread(&f)
+ dec.binread(&dec.d)
dec.pos += 8
- return f
+ return dec.d
case 's':
- length := dec.decode("u", depth).(uint32)
- b := make([]byte, int(length)+1)
- if _, err := io.ReadFull(dec.in, b); err != nil {
- panic(err)
- }
- dec.pos += int(length) + 1
- return string(b[:len(b)-1])
+ length := dec.decodeU()
+ p := int(length) + 1
+ dec.read2buf(p)
+ dec.pos += p
+ return dec.conv.String(dec.buf[:len(dec.buf)-1])
case 'o':
return ObjectPath(dec.decode("s", depth).(string))
case 'g':
length := dec.decode("y", depth).(byte)
- b := make([]byte, int(length)+1)
- if _, err := io.ReadFull(dec.in, b); err != nil {
- panic(err)
- }
- dec.pos += int(length) + 1
- sig, err := ParseSignature(string(b[:len(b)-1]))
+ p := int(length) + 1
+ dec.read2buf(p)
+ dec.pos += p
+ sig, err := ParseSignature(
+ dec.conv.String(dec.buf[:len(dec.buf)-1]),
+ )
if err != nil {
panic(err)
}
variant.value = dec.decode(sig.str, depth+1)
return variant
case 'h':
- idx := dec.decode("u", depth).(uint32)
+ idx := dec.decodeU()
if int(idx) < len(dec.fds) {
return UnixFD(dec.fds[idx])
}
if depth >= 63 {
panic(FormatError("input exceeds container depth limit"))
}
- length := dec.decode("u", depth).(uint32)
+ length := dec.decodeU()
// Even for empty maps, the correct padding must be included
dec.align(8)
spos := dec.pos
panic(FormatError("input exceeds container depth limit"))
}
sig := s[1:]
- length := dec.decode("u", depth).(uint32)
+ length := dec.decodeU()
// capacity can be determined only for fixed-size element types
var capacity int
if s := sigByteSize(sig); s != 0 {
// Even for empty arrays, the correct padding must be included
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
+ // 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)
panic(FormatError("input exceeds container depth limit"))
}
dec.align(8)
- v := make([]interface{}, 0)
+ v := make([]any, 0)
s = s[1 : len(s)-1]
for s != "" {
err, rem := validSingle(s, &depthCounter{})
func (e FormatError) Error() string {
return "dbus: wire format error: " + string(e)
}
+
+// stringConverterBufferSize defines the recommended buffer size of 4KB.
+// It showed good results in a benchmark when decoding 35KB message,
+// see https://github.com/marselester/systemd#testing.
+const stringConverterBufferSize = 4096
+
+func newStringConverter(capacity int) *stringConverter {
+ return &stringConverter{
+ buf: make([]byte, 0, capacity),
+ offset: 0,
+ }
+}
+
+// stringConverter converts bytes to strings with less allocs.
+// The idea is to accumulate bytes in a buffer with specified capacity
+// and create strings with unsafe package using bytes from a buffer.
+// For example, 10 "fizz" strings written to a 40-byte buffer
+// will result in 1 alloc instead of 10.
+//
+// Once a buffer is filled, a new one is created with the same capacity.
+// Old buffers will be eventually GC-ed
+// with no side effects to the returned strings.
+type stringConverter struct {
+ // buf is a temporary buffer where decoded strings are batched.
+ buf []byte
+ // offset is a buffer position where the last string was written.
+ offset int
+}
+
+// String converts bytes to a string.
+func (c *stringConverter) String(b []byte) string {
+ n := len(b)
+ if n == 0 {
+ return ""
+ }
+ // Must allocate because a string doesn't fit into the buffer.
+ if n > cap(c.buf) {
+ return string(b)
+ }
+
+ if len(c.buf)+n > cap(c.buf) {
+ c.buf = make([]byte, 0, cap(c.buf))
+ c.offset = 0
+ }
+ c.buf = append(c.buf, b...)
+
+ b = c.buf[c.offset:]
+ s := toString(b)
+ c.offset += n
+ return s
+}
+
+// toString converts a byte slice to a string without allocating.
+func toString(b []byte) string {
+ return unsafe.String(&b[0], len(b))
+}
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.
+// 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 {
if p != "/" {
p += "/"
}
- if strings.HasPrefix(string(obj), p) {
- node_name := strings.Split(string(obj[len(p):]), "/")[0]
- subpath[node_name] = struct{}{}
+ if after, ok := strings.CutPrefix(string(obj), p); ok {
+ name, _, _ := strings.Cut(after, "/")
+ subpath[name] = struct{}{}
}
}
for s := range subpath {
reflect.Value
}
-func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
+func (m exportedMethod) Call(args ...any) ([]any, error) {
t := m.Type()
params := make([]reflect.Value, len(args))
ret = ret[:t.NumOut()-1]
}
}
- out := make([]interface{}, len(ret))
+ out := make([]any, len(ret))
for i, val := range ret {
out[i] = val.Interface()
}
if nilErr || err == nil {
- //concrete type to interface nil is a special case
+ // concrete type to interface nil is a special case
return out, nil
}
return out, err
return m.Value.Type().NumIn()
}
-func (m exportedMethod) ArgumentValue(i int) interface{} {
+func (m exportedMethod) ArgumentValue(i int) any {
return reflect.Zero(m.Type().In(i)).Interface()
}
return m.Value.Type().NumOut()
}
-func (m exportedMethod) ReturnValue(i int) interface{} {
+func (m exportedMethod) ReturnValue(i int) any {
return reflect.Zero(m.Type().Out(i)).Interface()
}
return nil, false
}
-func (obj *exportedObj) isFallbackInterface() bool {
- return false
-}
-
func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
return &exportedIntf{
methods: methods,
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.
+// 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 {
arrange D-Bus methods calls to be directly translated to method calls on a Go
value.
-Conversion Rules
+# Conversion Rules
For outgoing messages, Go types are automatically converted to the
corresponding D-Bus types. See the official specification at
information on the D-Bus type system. The following types are directly encoded
as their respective D-Bus equivalents:
- Go type | D-Bus type
- ------------+-----------
- byte | BYTE
- bool | BOOLEAN
- int16 | INT16
- uint16 | UINT16
- int | INT32
- uint | UINT32
- int32 | INT32
- uint32 | UINT32
- int64 | INT64
- uint64 | UINT64
- float64 | DOUBLE
- string | STRING
- ObjectPath | OBJECT_PATH
- Signature | SIGNATURE
- Variant | VARIANT
- interface{} | VARIANT
- UnixFDIndex | UNIX_FD
+ Go type | D-Bus type
+ ------------+-----------
+ byte | BYTE
+ bool | BOOLEAN
+ int16 | INT16
+ uint16 | UINT16
+ int | INT32
+ uint | UINT32
+ int32 | INT32
+ uint32 | UINT32
+ int64 | INT64
+ uint64 | UINT64
+ float64 | DOUBLE
+ string | STRING
+ ObjectPath | OBJECT_PATH
+ Signature | SIGNATURE
+ Variant | VARIANT
+ interface{} | VARIANT
+ UnixFDIndex | UNIX_FD
Slices and arrays encode as ARRAYs of their element type.
containing the struct fields in the correct order. The Store function can be
used to convert such values to Go structs.
-Unix FD passing
+# Unix FD passing
Handling Unix file descriptors deserves special mention. To use them, you should
first check that they are supported on a connection by calling SupportsUnixFDs.
UnixFD values being substituted by the correct indices. Similarly, the indices
of incoming messages are automatically resolved. It shouldn't be necessary to use
UnixFDIndex.
-
*/
package dbus
}
// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
-func (enc *encoder) binwrite(v interface{}) {
+func (enc *encoder) binwrite(v any) {
if err := binary.Write(enc.out, enc.order, v); err != nil {
panic(err)
}
// Encode encodes the given values to the underlying reader. All written values
// are aligned properly as required by the D-Bus spec.
-func (enc *encoder) Encode(vs ...interface{}) (err error) {
+func (enc *encoder) Encode(vs ...any) (err error) {
defer func() {
err, _ = recover().(error)
}()
var (
ErrMsgInvalidArg = Error{
"org.freedesktop.DBus.Error.InvalidArgs",
- []interface{}{"Invalid type / number of args"},
+ []any{"Invalid type / number of args"},
}
ErrMsgNoObject = Error{
"org.freedesktop.DBus.Error.NoSuchObject",
- []interface{}{"No such object"},
+ []any{"No such object"},
}
ErrMsgUnknownMethod = Error{
"org.freedesktop.DBus.Error.UnknownMethod",
- []interface{}{"Unknown / invalid method"},
+ []any{"Unknown / invalid method"},
}
ErrMsgUnknownInterface = Error{
"org.freedesktop.DBus.Error.UnknownInterface",
- []interface{}{"Object does not implement the interface"},
+ []any{"Object does not implement the interface"},
}
)
func MakeNoObjectError(path ObjectPath) Error {
return Error{
"org.freedesktop.DBus.Error.NoSuchObject",
- []interface{}{fmt.Sprintf("No such object '%s'", string(path))},
+ []any{fmt.Sprintf("No such object '%s'", string(path))},
}
}
func MakeUnknownMethodError(methodName string) Error {
return Error{
"org.freedesktop.DBus.Error.UnknownMethod",
- []interface{}{fmt.Sprintf("Unknown / invalid method '%s'", methodName)},
+ []any{fmt.Sprintf("Unknown / invalid method '%s'", methodName)},
}
}
func MakeUnknownInterfaceError(ifaceName string) Error {
return Error{
"org.freedesktop.DBus.Error.UnknownInterface",
- []interface{}{fmt.Sprintf("Object does not implement the interface '%s'", ifaceName)},
+ []any{fmt.Sprintf("Object does not implement the interface '%s'", ifaceName)},
}
}
func MakeFailedError(err error) *Error {
return &Error{
"org.freedesktop.DBus.Error.Failed",
- []interface{}{err.Error()},
+ []any{err.Error()},
}
}
return name
}
-func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
+func getMethods(in any, mapping map[string]string) map[string]reflect.Value {
if in == nil {
return nil
}
return methods
}
-func getAllMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
+func getAllMethods(in any, mapping map[string]string) map[string]reflect.Value {
if in == nil {
return nil
}
return methods
}
-func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
- pointers := make([]interface{}, m.NumArguments())
- decode := make([]interface{}, 0, len(body))
+func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []any) ([]any, error) {
+ pointers := make([]any, m.NumArguments())
+ decode := make([]any, 0, len(body))
for i := 0; i < m.NumArguments(); i++ {
tp := reflect.TypeOf(m.ArgumentValue(i))
return pointers, nil
}
-func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
+func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]any, error) {
if decoder, ok := m.(ArgumentDecoder); ok {
return decoder.DecodeArguments(conn, sender, msg, msg.Body)
}
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
}
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
- reply.Body = make([]interface{}, len(ret))
- for i := 0; i < len(ret); i++ {
- reply.Body[i] = ret[i]
- }
+ reply.Body = make([]any, len(ret))
+ copy(reply.Body, ret)
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
- if err := reply.IsValid(); err != nil {
- fmt.Fprintf(os.Stderr, "dbus: dropping invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err)
- } else {
- conn.sendMessageAndIfClosed(reply, nil)
+ if err := conn.sendMessageAndIfClosed(reply, nil); err != nil {
+ if _, ok := err.(FormatError); ok {
+ fmt.Fprintf(os.Stderr, "dbus: replacing invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err)
+ }
}
}
}
// Emit emits the given signal on the message bus. The name parameter must be
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
-func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
+func (conn *Conn) Emit(path ObjectPath, name string, values ...any) error {
i := strings.LastIndex(name, ".")
if i == -1 {
return errors.New("dbus: invalid method name")
if len(values) > 0 {
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
}
- if err := msg.IsValid(); err != nil {
- return err
- }
var closed bool
- conn.sendMessageAndIfClosed(msg, func() {
+ err := conn.sendMessageAndIfClosed(msg, func() {
closed = true
})
if closed {
return ErrClosed
}
- return nil
+ return err
}
// Export registers the given value to be exported as an object on the
// the given combination of path and interface.
//
// Export returns an error if path is not a valid path name.
-func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
+func (conn *Conn) Export(v any, path ObjectPath, iface string) error {
return conn.ExportWithMap(v, nil, path, iface)
}
// type parameter to your method signature. If the error returned is not nil,
// it is sent back to the caller as an error. Otherwise, a method reply is
// sent with the other return values as its body.
-func (conn *Conn) ExportAll(v interface{}, path ObjectPath, iface string) error {
+func (conn *Conn) ExportAll(v any, path ObjectPath, iface string) error {
return conn.export(getAllMethods(v, nil), path, iface, false)
}
//
// 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 {
+func (conn *Conn) ExportWithMap(v any, mapping map[string]string, path ObjectPath, iface string) error {
return conn.export(getMethods(v, mapping), path, iface, false)
}
// Note that more specific export paths take precedence over less specific. For
// example, a method call using the ObjectPath /foo/bar/baz will call a method
// exported on /foo/bar before a method exported on /foo.
-func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
+func (conn *Conn) ExportSubtree(v any, path ObjectPath, iface string) error {
return conn.ExportSubtreeWithMap(v, nil, path, iface)
}
//
// 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 {
+func (conn *Conn) ExportSubtreeWithMap(v any, mapping map[string]string, path ObjectPath, iface string) error {
return conn.export(getMethods(v, mapping), path, iface, true)
}
// 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 {
+func (conn *Conn) ExportMethodTable(methods map[string]any, 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 {
+func (conn *Conn) ExportSubtreeMethodTable(methods map[string]any, 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 {
+func (conn *Conn) exportMethodTable(methods map[string]any, path ObjectPath, iface string, includeSubtree bool) error {
var out map[string]reflect.Value
if methods != nil {
out = make(map[string]reflect.Value)
ReleaseNameReplyNotOwner
)
+func (rep ReleaseNameReply) String() string {
+ switch rep {
+ case ReleaseNameReplyReleased:
+ return "released"
+ case ReleaseNameReplyNonExistent:
+ return "non existent"
+ case ReleaseNameReplyNotOwner:
+ return "not owner"
+ }
+ return "unknown"
+}
+
// RequestNameFlags represents the possible flags for a RequestName call.
type RequestNameFlags uint32
RequestNameReplyExists
RequestNameReplyAlreadyOwner
)
+
+func (rep RequestNameReply) String() string {
+ switch rep {
+ case RequestNameReplyPrimaryOwner:
+ return "primary owner"
+ case RequestNameReplyInQueue:
+ return "in queue"
+ case RequestNameReplyExists:
+ return "exists"
+ case RequestNameReplyAlreadyOwner:
+ return "already owner"
+ }
+ return "unknown"
+}
+++ /dev/null
-package dbus
-
-import (
- "os"
- "os/user"
-)
-
-// Get returns the home directory of the current user, which is usually the
-// value of HOME environment variable. In case it is not set or empty, os/user
-// package is used.
-//
-// If linking statically with cgo enabled against glibc, make sure the
-// osusergo build tag is used.
-//
-// If needing to do nss lookups, do not disable cgo or set osusergo.
-func getHomeDir() string {
- homeDir := os.Getenv("HOME")
- if homeDir != "" {
- return homeDir
- }
- if u, err := user.Current(); err == nil {
- return u.HomeDir
- }
- return "/"
-}
return MatchOption{key, value}
}
-// doesn't make sense to export this option because clients can only
-// subscribe to messages with signal type.
-func withMatchType(typ string) MatchOption {
- return WithMatchOption("type", typ)
+// It does not make sense to have a public WithMatchType function
+// because clients can only subscribe to messages with signal type.
+func withMatchTypeSignal() MatchOption {
+ return WithMatchOption("type", "signal")
}
// WithMatchSender sets sender match option.
Type
Flags
Headers map[HeaderField]Variant
- Body []interface{}
+ Body []any
serial uint32
}
if err != nil {
return nil, err
}
- binary.Read(bytes.NewBuffer(b), order, &hlength)
+ if err := binary.Read(bytes.NewBuffer(b), order, &hlength); err != nil {
+ return nil, err
+ }
if hlength+length+16 > 1<<27 {
return nil, InvalidMessageError("message is too long")
}
}
}
- if err = msg.IsValid(); err != nil {
+ if err = msg.validateHeader(); err != nil {
return nil, err
}
sig, _ := msg.Headers[FieldSignature].value.(Signature)
if err := msg.validateHeader(); err != nil {
return nil, err
}
- var vs [7]interface{}
+ var vs [7]any
switch order {
case binary.LittleEndian:
vs[0] = byte('l')
return
}
enc.align(8)
- body.WriteTo(&buf)
+ if _, err := body.WriteTo(&buf); err != nil {
+ return nil, err
+ }
if buf.Len() > 1<<27 {
- return make([]int, 0), InvalidMessageError("message is too long")
+ return nil, InvalidMessageError("message is too long")
}
if _, err := buf.WriteTo(out); err != nil {
- return make([]int, 0), err
+ return nil, err
}
return enc.fds, nil
}
// IsValid checks whether msg is a valid message and returns an
// InvalidMessageError or FormatError if it is not.
func (msg *Message) IsValid() error {
- var b bytes.Buffer
- return msg.EncodeTo(&b, nativeEndian)
+ return msg.EncodeTo(io.Discard, nativeEndian)
}
func (msg *Message) validateHeader() error {
// BusObject is the interface of a remote object on which methods can be
// 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
+ Call(method string, flags Flags, args ...any) *Call
+ CallWithContext(ctx context.Context, method string, flags Flags, args ...any) *Call
+ Go(method string, flags Flags, ch chan *Call, args ...any) *Call
+ GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...any) *Call
AddMatchSignal(iface, member string, options ...MatchOption) *Call
RemoveMatchSignal(iface, member string, options ...MatchOption) *Call
GetProperty(p string) (Variant, error)
- StoreProperty(p string, value interface{}) error
- SetProperty(p string, v interface{}) error
+ StoreProperty(p string, value any) error
+ SetProperty(p string, v any) error
Destination() string
Path() ObjectPath
}
}
// Call calls a method with (*Object).Go and waits for its reply.
-func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
+func (o *Object) Call(method string, flags Flags, args ...any) *Call {
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 {
+func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...any) *Call {
return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done
}
// Deprecated: use (*Conn) AddMatchSignal instead.
func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call {
base := []MatchOption{
- withMatchType("signal"),
+ withMatchTypeSignal(),
WithMatchInterface(iface),
WithMatchMember(member),
}
// Deprecated: use (*Conn) RemoveMatchSignal instead.
func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call {
base := []MatchOption{
- withMatchType("signal"),
+ withMatchTypeSignal(),
WithMatchInterface(iface),
WithMatchMember(member),
}
//
// 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 {
+func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...any) *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 {
+func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...any) *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 {
+func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...any) *Call {
if ctx == nil {
panic("nil context")
}
// StoreProperty calls org.freedesktop.DBus.Properties.Get on the given
// object. The property name must be given in interface.member notation.
// It stores the returned property into the provided value.
-func (o *Object) StoreProperty(p string, value interface{}) error {
+func (o *Object) StoreProperty(p string, value any) error {
idx := strings.LastIndex(p, ".")
if idx == -1 || idx+1 == len(p) {
return errors.New("dbus: invalid property " + p)
// SetProperty calls org.freedesktop.DBus.Properties.Set on the given
// object. The property name must be given in interface.member notation.
-func (o *Object) SetProperty(p string, v interface{}) error {
+// Panics if v is not a valid Variant type.
+func (o *Object) SetProperty(p string, v any) error {
+ // v might already be a variant...
+ variant, ok := v.(Variant)
+ if !ok {
+ // Otherwise, make it into one.
+ variant = MakeVariant(v)
+ }
idx := strings.LastIndex(p, ".")
if idx == -1 || idx+1 == len(p) {
return errors.New("dbus: invalid property " + p)
iface := p[:idx]
prop := p[idx+1:]
- return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err
+ return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, variant).Err
}
// Destination returns the destination that calls on (o *Object) are sent to.
var queue []*Signal
for {
if len(queue) == 0 {
- signal, ok := <- scd.in
+ signal, ok := <-scd.in
if !ok {
return
}
// 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.
+// representing all methods or not per the D-Bus specification.
type ServerObject interface {
LookupInterface(name string) (Interface, 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)
+ Call(args ...any) ([]any, 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{}
+ ArgumentValue(position int) any
// 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{}
+ ReturnValue(position int) any
}
// An Argument Decoder can decode arguments using the non-standard mechanism
// To decode the arguments of a method the sender and message are
// provided in case 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)
+ DecodeArguments(conn *Conn, sender string, msg *Message, args []any) ([]any, error)
}
// A SignalHandler is responsible for delivering a signal.
// "org.freedesktop.DBus.Error.Failed" error. By implementing this
// interface as well a custom encoding may be provided.
type DBusError interface {
- DBusError() (string, []interface{})
+ DBusError() (string, []any)
}
// SerialGenerator is responsible for serials generation.
// SignatureOf returns the concatenation of all the signatures of the given
// values. It panics if one of them is not representable in D-Bus.
-func SignatureOf(vs ...interface{}) Signature {
+func SignatureOf(vs ...any) Signature {
var s string
for _, v := range vs {
s += getSignature(reflect.TypeOf(v), &depthCounter{})
return cnt.arrayDepth <= 32 && cnt.structDepth <= 32 && cnt.dictEntryDepth <= 32
}
-func (cnt depthCounter) EnterArray() *depthCounter {
+func (cnt *depthCounter) EnterArray() *depthCounter {
cnt.arrayDepth++
- return &cnt
+ return cnt
}
-func (cnt depthCounter) EnterStruct() *depthCounter {
+func (cnt *depthCounter) EnterStruct() *depthCounter {
cnt.structDepth++
- return &cnt
+ return cnt
}
-func (cnt depthCounter) EnterDictEntry() *depthCounter {
+func (cnt *depthCounter) EnterDictEntry() *depthCounter {
cnt.dictEntryDepth++
- return &cnt
+ return cnt
}
// Try to read a single type from this string. If it was successful, err is nil
i++
rem = s[i+1:]
s = s[2:i]
+ if len(s) == 0 {
+ return SignatureError{Sig: s, Reason: "empty dict"}, ""
+ }
if err, _ = validSingle(s[:1], depth.EnterArray().EnterDictEntry()); err != nil {
return err, ""
}
-//+build !windows
+//go:build !windows
+// +build !windows
package dbus
import (
"errors"
- "io/ioutil"
"net"
+ "os"
)
func init() {
if err != nil {
return nil, err
}
- b, err := ioutil.ReadFile(noncefile)
+ b, err := os.ReadFile(noncefile)
if err != nil {
+ socket.Close()
return nil, err
}
_, err = socket.Write(b)
if err != nil {
+ socket.Close()
return nil, err
}
return NewConn(socket)
-//+build !windows,!solaris
+//go:build !windows && !solaris
+// +build !windows,!solaris
package dbus
"syscall"
)
+// msghead represents the part of the message header
+// that has a constant size (byte order + 15 bytes).
+type msghead struct {
+ Type Type
+ Flags Flags
+ Proto byte
+ BodyLen uint32
+ Serial uint32
+ HeaderLen uint32
+}
+
type oobReader struct {
conn *net.UnixConn
oob []byte
buf [4096]byte
+
+ // The following fields are used to reduce memory allocs.
+ headers []header
+ csheader []byte
+ b *bytes.Buffer
+ r *bytes.Reader
+ dec *decoder
+ msghead
}
func (o *oobReader) Read(b []byte) (n int, err error) {
hasUnixFDs bool
}
+func newUnixTransportFromConn(conn *net.UnixConn) transport {
+ t := new(unixTransport)
+ t.UnixConn = conn
+ t.hasUnixFDs = true
+
+ return t
+}
+
func newUnixTransport(keys string) (transport, error) {
var err error
}
func (t *unixTransport) ReadMessage() (*Message, error) {
- var (
- blen, hlen uint32
- csheader [16]byte
- headers []header
- order binary.ByteOrder
- unixfds uint32
- )
// 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.
if t.rdr == nil {
- t.rdr = &oobReader{conn: t.UnixConn}
+ t.rdr = &oobReader{
+ conn: t.UnixConn,
+ // This buffer is used to decode the part of the header that has a constant size.
+ csheader: make([]byte, 16),
+ b: &bytes.Buffer{},
+ // The reader helps to read from the buffer several times.
+ r: &bytes.Reader{},
+ dec: &decoder{},
+ }
} else {
- t.rdr.oob = nil
+ t.rdr.oob = t.rdr.oob[:0]
+ t.rdr.headers = t.rdr.headers[:0]
}
+ var (
+ r = t.rdr.r
+ b = t.rdr.b
+ dec = t.rdr.dec
+ )
- // 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(t.rdr, csheader[:]); err != nil {
+ _, err := io.ReadFull(t.rdr, t.rdr.csheader)
+ if err != nil {
return nil, err
}
- switch csheader[0] {
+
+ var order binary.ByteOrder
+ switch t.rdr.csheader[0] {
case 'l':
order = binary.LittleEndian
case 'B':
default:
return nil, InvalidMessageError("invalid byte order")
}
- // csheader[4:8] -> length of message body, csheader[12:16] -> length of
- // header fields (without alignment)
- binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen)
- binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen)
+
+ r.Reset(t.rdr.csheader[1:])
+ if err := binary.Read(r, order, &t.rdr.msghead); err != nil {
+ return nil, err
+ }
+
+ msg := &Message{
+ Type: t.rdr.msghead.Type,
+ Flags: t.rdr.msghead.Flags,
+ serial: t.rdr.msghead.Serial,
+ }
+ // Length of header fields (without alignment).
+ hlen := t.rdr.msghead.HeaderLen
if hlen%8 != 0 {
hlen += 8 - (hlen % 8)
}
+ if hlen+t.rdr.msghead.BodyLen+16 > 1<<27 {
+ return nil, InvalidMessageError("message is too long")
+ }
- // decode headers and look for unix fds
- headerdata := make([]byte, hlen+4)
- copy(headerdata, csheader[12:])
- if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil {
+ // Decode headers and look for unix fds.
+ b.Reset()
+ if _, err = b.Write(t.rdr.csheader[12:]); err != nil {
return nil, err
}
- dec := newDecoder(bytes.NewBuffer(headerdata), order, make([]int, 0))
+ if _, err = io.CopyN(b, t.rdr, int64(hlen)); err != nil {
+ return nil, err
+ }
+ dec.Reset(b, order, nil)
dec.pos = 12
vs, err := dec.Decode(Signature{"a(yv)"})
if err != nil {
return nil, err
}
- Store(vs, &headers)
- for _, v := range headers {
+ if err = Store(vs, &t.rdr.headers); err != nil {
+ return nil, err
+ }
+ var unixfds uint32
+ for _, v := range t.rdr.headers {
if v.Field == byte(FieldUnixFDs) {
unixfds, _ = v.Variant.value.(uint32)
}
}
- all := make([]byte, 16+hlen+blen)
- copy(all, csheader[:])
- copy(all[16:], headerdata[4:])
- if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil {
+
+ msg.Headers = make(map[HeaderField]Variant)
+ for _, v := range t.rdr.headers {
+ msg.Headers[HeaderField(v.Field)] = v.Variant
+ }
+
+ dec.align(8)
+ body := make([]byte, t.rdr.BodyLen)
+ if _, err = io.ReadFull(t.rdr, body); err != nil {
return nil, err
}
+ r.Reset(body)
+
if unixfds != 0 {
if !t.hasUnixFDs {
return nil, errors.New("dbus: got unix fds on unsupported transport")
if err != nil {
return nil, err
}
- msg, err := DecodeMessageWithFDs(bytes.NewBuffer(all), fds)
- if err != nil {
+ dec.Reset(r, order, fds)
+ if err = decodeMessageBody(msg, dec); err != nil {
return nil, err
}
// substitute the values in the message body (which are indices for the
}
return msg, nil
}
- return DecodeMessage(bytes.NewBuffer(all))
+
+ dec.Reset(r, order, nil)
+ if err = decodeMessageBody(msg, dec); err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+func decodeMessageBody(msg *Message, dec *decoder) error {
+ if err := msg.validateHeader(); err != nil {
+ return err
+ }
+
+ sig, _ := msg.Headers[FieldSignature].value.(Signature)
+ if sig.str == "" {
+ return nil
+ }
+
+ var err error
+ msg.Body, err = dec.Decode(sig)
+ return err
}
func (t *unixTransport) SendMessage(msg *Message) error {
package dbus
-/*
-const int sizeofPtr = sizeof(void*);
-#define _WANT_UCRED
-#include <sys/types.h>
-#include <sys/ucred.h>
-*/
-import "C"
-
import (
"io"
"os"
"syscall"
"unsafe"
+
+ "golang.org/x/sys/unix"
)
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
// https://golang.org/src/syscall/ztypes_freebsd_amd64.go
+//
+// Note: FreeBSD actually uses a 'struct cmsgcred' which starts with
+// these fields and adds a list of the additional groups for the
+// sender.
type Ucred struct {
- Pid int32
- Uid uint32
- Gid uint32
+ Pid int32
+ Uid uint32
+ Euid 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
+// https://github.com/freebsd/freebsd/blob/master/sys/sys/socket.h
+//
+// The cmsgcred structure contains the above four fields, followed by
+// a uint16 count of additional groups, uint16 padding to align and a
+// 16 element array of uint32 for the additional groups. The size is
+// the same across all supported platforms.
const (
- SizeofUcred = C.sizeof_struct_ucred
+ SizeofCmsgcred = 84 // 4*4 + 2*2 + 16*4
)
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
func cmsgAlignOf(salen int) int {
- salign := C.sizeofPtr
+ salign := unix.SizeofPtr
return (salen + salign - 1) & ^(salign - 1)
}
// for sending to another process. This can be used for
// authentication.
func UnixCredentials(ucred *Ucred) []byte {
- b := make([]byte, syscall.CmsgSpace(SizeofUcred))
+ b := make([]byte, syscall.CmsgSpace(SizeofCmsgcred))
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
h.Level = syscall.SOL_SOCKET
h.Type = syscall.SCM_CREDS
- h.SetLen(syscall.CmsgLen(SizeofUcred))
+ h.SetLen(syscall.CmsgLen(SizeofCmsgcred))
*((*Ucred)(cmsgData(h))) = *ucred
return b
}
// Variant represents the D-Bus variant type.
type Variant struct {
sig Signature
- value interface{}
+ value any
}
// 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 {
+func MakeVariant(v any) Variant {
return MakeVariantWithSignature(v, SignatureOf(v))
}
// MakeVariantWithSignature converts the given value to a Variant.
-func MakeVariantWithSignature(v interface{}, s Signature) Variant {
+func MakeVariantWithSignature(v any, s Signature) Variant {
return Variant{s, v}
}
}
rv := reflect.ValueOf(v.value)
switch rv.Kind() {
- case reflect.Slice:
+ case reflect.Slice, reflect.Array:
if rv.Len() == 0 {
return "[]", false
}
}
buf.WriteByte('}')
return buf.String(), unamb
+ case reflect.Struct:
+ if rv.NumField() == 0 {
+ return "()", false
+ }
+ unamb := true
+ var buf bytes.Buffer
+ buf.WriteByte('(')
+ for i := 0; i < rv.NumField(); i++ {
+ s, b := MakeVariant(rv.Field(i).Interface()).format()
+ unamb = unamb && b
+ buf.WriteString(s)
+ buf.WriteString(",")
+ if i != rv.NumField()-1 {
+ buf.WriteString(" ")
+ }
+ }
+ buf.WriteByte(')')
+ return buf.String(), unamb
+
}
return `"INVALID"`, true
}
}
// Value returns the underlying value of v.
-func (v Variant) Value() interface{} {
+func (v Variant) Value() any {
return v.value
}
// Store converts the variant into a native go type using the same
// mechanism as the "Store" function.
-func (v Variant) Store(value interface{}) error {
+func (v Variant) Store(value any) error {
return storeInterfaces(v.value, value)
}
l.start = l.pos
}
-func (l *varLexer) errorf(format string, v ...interface{}) lexState {
+func (l *varLexer) errorf(format string, v ...any) lexState {
l.tokens = append(l.tokens, varToken{
tokError,
fmt.Sprintf(format, v...),
Infer() (Signature, error)
String() string
Sigs() sigSet
- Value(Signature) (interface{}, error)
+ Value(Signature) (any, error)
}
func varMakeNode(p *varParser) (varNode, error) {
type numNode struct {
sig Signature
str string
- val interface{}
+ val any
}
var numSigSet = sigSet{
return numSigSet
}
-func (n numNode) Value(sig Signature) (interface{}, error) {
+func (n numNode) Value(sig Signature) (any, error) {
if n.sig.str != "" && n.sig != sig {
return nil, varTypeError{n.str, sig}
}
return numNode{sig: sig, val: num}, nil
}
-func varNumAs(s string, sig Signature) (interface{}, error) {
+func varNumAs(s string, sig Signature) (any, error) {
isUnsigned := false
size := 32
switch sig.str {
return nil, varTypeError{s, sig}
}
base := 10
- if strings.HasPrefix(s, "0x") {
+ if after, ok := strings.CutPrefix(s, "0x"); ok {
base = 16
- s = s[2:]
+ s = after
}
- if strings.HasPrefix(s, "0") && len(s) != 1 {
+ if after, ok := strings.CutPrefix(s, "0"); ok && len(s) != 1 {
base = 8
- s = s[1:]
+ s = after
}
if isUnsigned {
i, err := strconv.ParseUint(s, base, size)
if err != nil {
return nil, err
}
- var v interface{} = i
+ var v any = i
switch sig.str {
case "y":
v = byte(i)
if err != nil {
return nil, err
}
- var v interface{} = i
+ var v any = i
switch sig.str {
case "n":
v = int16(i)
type stringNode struct {
sig Signature
- str string // parsed
- val interface{} // has correct type
+ str string // parsed
+ val any // has correct type
}
var stringSigSet = sigSet{
return stringSigSet
}
-func (n stringNode) Value(sig Signature) (interface{}, error) {
+func (n stringNode) Value(sig Signature) (any, error) {
if n.sig.str != "" && n.sig != sig {
return nil, varTypeError{n.str, sig}
}
return boolSigSet
}
-func (b boolNode) Value(sig Signature) (interface{}, error) {
+func (b boolNode) Value(sig Signature) (any, error) {
if sig.str != "b" {
return nil, varTypeError{b.String(), sig}
}
type arrayNode struct {
set sigSet
children []varNode
- val interface{}
}
func (n arrayNode) Infer() (Signature, error) {
return n.set
}
-func (n arrayNode) Value(sig Signature) (interface{}, error) {
+func (n arrayNode) Value(sig Signature) (any, error) {
if n.set.Empty() {
// no type information whatsoever, so this must be an empty slice
return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil
return variantSet
}
-func (n variantNode) Value(sig Signature) (interface{}, error) {
+func (n variantNode) Value(sig Signature) (any, error) {
if sig.str != "v" {
return nil, varTypeError{n.String(), sig}
}
type dictNode struct {
kset, vset sigSet
children []dictEntry
- val interface{}
}
func (n dictNode) Infer() (Signature, error) {
return r
}
-func (n dictNode) Value(sig Signature) (interface{}, error) {
+func (n dictNode) Value(sig Signature) (any, error) {
set := n.Sigs()
if set.Empty() {
// no type information -> empty dict
return byteStringSet
}
-func (b byteStringNode) Value(sig Signature) (interface{}, error) {
+func (b byteStringNode) Value(sig Signature) (any, error) {
if sig.str != "ay" {
return nil, varTypeError{b.String(), sig}
}
github.com/gobwas/glob/syntax/lexer
github.com/gobwas/glob/util/runes
github.com/gobwas/glob/util/strings
-# github.com/godbus/dbus/v5 v5.1.0
-## explicit; go 1.12
+# github.com/godbus/dbus/v5 v5.2.0
+## explicit; go 1.20
github.com/godbus/dbus/v5
# github.com/gogo/protobuf v1.3.2
## explicit; go 1.15