123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996 |
- package dbus
- import (
- "context"
- "errors"
- "io"
- "os"
- "strings"
- "sync"
- )
- var (
- systemBus *Conn
- systemBusLck sync.Mutex
- sessionBus *Conn
- sessionBusLck sync.Mutex
- )
- // ErrClosed is the error returned by calls on a closed connection.
- var ErrClosed = errors.New("dbus: connection closed by user")
- // Conn represents a connection to a message bus (usually, the system or
- // session bus).
- //
- // Connections are either shared or private. Shared connections
- // are shared between calls to the functions that return them. As a result,
- // the methods Close, Auth and Hello must not be called on them.
- //
- // Multiple goroutines may invoke methods on a connection simultaneously.
- type Conn struct {
- transport
- ctx context.Context
- cancelCtx context.CancelFunc
- closeOnce sync.Once
- closeErr error
- busObj BusObject
- unixFD bool
- uuid string
- handler Handler
- signalHandler SignalHandler
- serialGen SerialGenerator
- inInt Interceptor
- outInt Interceptor
- auth []Auth
- names *nameTracker
- calls *callTracker
- outHandler *outputHandler
- eavesdropped chan<- *Message
- eavesdroppedLck sync.Mutex
- }
- // SessionBus returns a shared connection to the session bus, connecting to it
- // if not already done.
- func SessionBus() (conn *Conn, err error) {
- sessionBusLck.Lock()
- defer sessionBusLck.Unlock()
- if sessionBus != nil &&
- sessionBus.Connected() {
- return sessionBus, nil
- }
- defer func() {
- if conn != nil {
- sessionBus = conn
- }
- }()
- conn, err = ConnectSessionBus()
- return
- }
- 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
- }
- if !autolaunch {
- return "", errors.New("dbus: couldn't determine address of session bus")
- }
- return getSessionBusPlatformAddress()
- }
- // SessionBusPrivate returns a new private connection to the session bus.
- func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
- address, err := getSessionBusAddress(true)
- if err != nil {
- return nil, err
- }
- return Dial(address, opts...)
- }
- // SessionBusPrivate 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)
- if err != nil {
- return nil, err
- }
- 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
- // not already done.
- func SystemBus() (conn *Conn, err error) {
- systemBusLck.Lock()
- defer systemBusLck.Unlock()
- if systemBus != nil &&
- systemBus.Connected() {
- return systemBus, nil
- }
- defer func() {
- if conn != nil {
- systemBus = conn
- }
- }()
- conn, err = ConnectSystemBus()
- return
- }
- // ConnectSessionBus connects to the session bus.
- func ConnectSessionBus(opts ...ConnOption) (*Conn, error) {
- address, err := getSessionBusAddress(true)
- if err != nil {
- return nil, err
- }
- return Connect(address, opts...)
- }
- // ConnectSystemBus connects to the system bus.
- func ConnectSystemBus(opts ...ConnOption) (*Conn, error) {
- return Connect(getSystemBusPlatformAddress(), opts...)
- }
- // Connect connects to the given address.
- //
- // Returned connection is ready to use and doesn't require calling
- // Auth and Hello methods to make it usable.
- func Connect(address string, opts ...ConnOption) (*Conn, error) {
- conn, err := Dial(address, 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
- }
- // SystemBusPrivate returns a new private connection to the system bus.
- // Note: this connection is not ready to use. One must perform Auth and Hello
- // on the connection before it is usable.
- 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, opts ...ConnOption) (*Conn, error) {
- tr, err := getTransport(address)
- if err != nil {
- return nil, err
- }
- 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, WithHandler(handler), 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
- }
- }
- // WithAuth sets authentication methods for the auth conversation.
- func WithAuth(methods ...Auth) ConnOption {
- return func(conn *Conn) error {
- conn.auth = methods
- return nil
- }
- }
- // Interceptor intercepts incoming and outgoing messages.
- type Interceptor func(msg *Message)
- // WithIncomingInterceptor sets the given interceptor for incoming messages.
- func WithIncomingInterceptor(interceptor Interceptor) ConnOption {
- return func(conn *Conn) error {
- conn.inInt = interceptor
- return nil
- }
- }
- // WithOutgoingInterceptor sets the given interceptor for outgoing messages.
- func WithOutgoingInterceptor(interceptor Interceptor) ConnOption {
- return func(conn *Conn) error {
- conn.outInt = interceptor
- return nil
- }
- }
- // WithContext overrides the default context for the connection.
- func WithContext(ctx context.Context) ConnOption {
- return func(conn *Conn) error {
- conn.ctx = ctx
- return nil
- }
- }
- // NewConn creates a new private *Conn from an already established connection.
- 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, opts ...ConnOption) (*Conn, error) {
- conn := new(Conn)
- conn.transport = tr
- for _, opt := range opts {
- if err := opt(conn); err != nil {
- return nil, err
- }
- }
- if conn.ctx == nil {
- conn.ctx = context.Background()
- }
- conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx)
- 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")
- go func() {
- <-conn.ctx.Done()
- conn.Close()
- }()
- return conn, nil
- }
- // BusObject returns the object owned by the bus daemon which handles
- // administrative requests.
- func (conn *Conn) BusObject() BusObject {
- return conn.busObj
- }
- // Close closes the connection. Any blocked operations will return with errors
- // 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.closeOnce.Do(func() {
- conn.outHandler.close()
- if term, ok := conn.signalHandler.(Terminator); ok {
- term.Terminate()
- }
- if term, ok := conn.handler.(Terminator); ok {
- term.Terminate()
- }
- conn.eavesdroppedLck.Lock()
- if conn.eavesdropped != nil {
- close(conn.eavesdropped)
- }
- conn.eavesdroppedLck.Unlock()
- conn.cancelCtx()
- conn.closeErr = conn.transport.Close()
- })
- return conn.closeErr
- }
- // Context returns the context associated with the connection. The
- // context will be cancelled when the connection is closed.
- func (conn *Conn) Context() context.Context {
- return conn.ctx
- }
- // Connected returns whether conn is connected
- func (conn *Conn) Connected() bool {
- return conn.ctx.Err() == nil
- }
- // Eavesdrop causes conn to send all incoming messages to the given channel
- // without further processing. Method replies, errors and signals will not be
- // sent to the appropriate channels and method calls will not be handled. If nil
- // is passed, the normal behaviour is restored.
- //
- // The caller has to make sure that ch is sufficiently buffered;
- // if a message arrives when a write to ch is not possible, the message is
- // discarded.
- func (conn *Conn) Eavesdrop(ch chan<- *Message) {
- conn.eavesdroppedLck.Lock()
- conn.eavesdropped = ch
- conn.eavesdroppedLck.Unlock()
- }
- // getSerial returns an unused serial.
- func (conn *Conn) getSerial() uint32 {
- return conn.serialGen.GetSerial()
- }
- // Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
- // called after authentication, but before sending any other messages to the
- // bus. Hello must not be called for shared connections.
- func (conn *Conn) Hello() error {
- var s string
- err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
- if err != nil {
- return err
- }
- conn.names.acquireUniqueConnectionName(s)
- return nil
- }
- // inWorker runs in an own goroutine, reading incoming messages from the
- // transport and dispatching them appropriately.
- func (conn *Conn) inWorker() {
- sequenceGen := newSequenceGenerator()
- for {
- msg, err := conn.ReadMessage()
- if err != nil {
- if _, ok := err.(InvalidMessageError); !ok {
- // Some read error occurred (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(sequenceGen, err)
- return
- }
- // invalid messages are ignored
- continue
- }
- 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 := dest == "" ||
- !conn.names.uniqueNameIsKnown() ||
- conn.names.isKnownName(dest)
- if !found {
- // Eavesdropped a message, but no channel for it is registered.
- // Ignore it.
- continue
- }
- if conn.inInt != nil {
- conn.inInt(msg)
- }
- sequence := sequenceGen.next()
- switch msg.Type {
- case TypeError:
- conn.serialGen.RetireSerial(conn.calls.handleDBusError(sequence, msg))
- case TypeMethodReply:
- conn.serialGen.RetireSerial(conn.calls.handleReply(sequence, msg))
- case TypeSignal:
- conn.handleSignal(sequence, msg)
- case TypeMethodCall:
- go conn.handleCall(msg)
- }
- }
- }
- func (conn *Conn) handleSignal(sequence Sequence, 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")
- }
- 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.names.acquireName(name)
- }
- }
- signal := &Signal{
- Sender: sender,
- Path: msg.Headers[FieldPath].value.(ObjectPath),
- Name: iface + "." + member,
- Body: msg.Body,
- Sequence: sequence,
- }
- 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 {
- return conn.names.listKnownNames()
- }
- // Object returns the object identified by the given destination name and path.
- func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
- return &Object{conn, dest, path}
- }
- func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
- if msg.serial == 0 {
- msg.serial = conn.getSerial()
- }
- if conn.outInt != nil {
- conn.outInt(msg)
- }
- err := conn.outHandler.sendAndIfClosed(msg, ifClosed)
- if err != nil {
- conn.handleSendError(msg, err)
- } else if msg.Type != TypeMethodCall {
- conn.serialGen.RetireSerial(msg.serial)
- }
- }
- 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))
- }
- }
- conn.serialGen.RetireSerial(msg.serial)
- }
- // Send sends the given message to the message bus. You usually don't need to
- // use this; use the higher-level equivalents (Call / Go, Emit and Export)
- // instead. If msg is a method call and NoReplyExpected is not set, a non-nil
- // call is returned and the same value is sent to ch (which must be buffered)
- // 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 {
- 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")
- }
- if ch == nil {
- ch = make(chan *Call, 1)
- } else if cap(ch) == 0 {
- panic("dbus: unbuffered channel passed to (*Conn).Send")
- }
- var call *Call
- ctx, canceler := context.WithCancel(ctx)
- msg.serial = conn.getSerial()
- if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
- call = new(Call)
- call.Destination, _ = msg.Headers[FieldDestination].value.(string)
- call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
- iface, _ := msg.Headers[FieldInterface].value.(string)
- member, _ := msg.Headers[FieldMember].value.(string)
- call.Method = iface + "." + member
- call.Args = msg.Body
- call.Done = ch
- call.ctx = ctx
- call.ctxCanceler = canceler
- conn.calls.track(msg.serial, call)
- if ctx.Err() != nil {
- // short path: don't even send the message if context already cancelled
- conn.calls.handleSendError(msg, ctx.Err())
- return call
- }
- go func() {
- <-ctx.Done()
- conn.calls.handleSendError(msg, ctx.Err())
- }()
- conn.sendMessageAndIfClosed(msg, func() {
- conn.calls.handleSendError(msg, ErrClosed)
- canceler()
- })
- } else {
- canceler()
- call = &Call{Err: nil, Done: ch}
- ch <- call
- conn.sendMessageAndIfClosed(msg, func() {
- call = &Call{Err: ErrClosed}
- })
- }
- return call
- }
- // sendError creates an error message corresponding to the parameters and sends
- // it to conn.out.
- 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.Headers = make(map[HeaderField]Variant)
- if dest != "" {
- msg.Headers[FieldDestination] = MakeVariant(dest)
- }
- msg.Headers[FieldErrorName] = MakeVariant(e.Name)
- msg.Headers[FieldReplySerial] = MakeVariant(serial)
- msg.Body = e.Body
- if len(e.Body) > 0 {
- msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
- }
- 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{}) {
- msg := new(Message)
- msg.Type = TypeMethodReply
- msg.Headers = make(map[HeaderField]Variant)
- if dest != "" {
- msg.Headers[FieldDestination] = MakeVariant(dest)
- }
- msg.Headers[FieldReplySerial] = MakeVariant(serial)
- msg.Body = values
- if len(values) > 0 {
- msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
- }
- conn.sendMessageAndIfClosed(msg, nil)
- }
- // AddMatchSignal registers the given match rule to receive broadcast
- // signals based on their contents.
- func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
- return conn.AddMatchSignalContext(context.Background(), options...)
- }
- // AddMatchSignalContext acts like AddMatchSignal but takes a context.
- func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error {
- options = append([]MatchOption{withMatchType("signal")}, options...)
- return conn.busObj.CallWithContext(
- ctx,
- "org.freedesktop.DBus.AddMatch", 0,
- formatMatchOptions(options),
- ).Store()
- }
- // RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
- func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
- return conn.RemoveMatchSignalContext(context.Background(), options...)
- }
- // RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context.
- func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error {
- options = append([]MatchOption{withMatchType("signal")}, options...)
- return conn.busObj.CallWithContext(
- ctx,
- "org.freedesktop.DBus.RemoveMatch", 0,
- formatMatchOptions(options),
- ).Store()
- }
- // Signal registers the given channel to be passed all received signal messages.
- //
- // Multiple of these channels can be registered at the same time. The channel is
- // closed if the Conn is closed; it should not be closed by the caller before
- // RemoveSignal was called on it.
- //
- // 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.
- //
- // Panics if the signal handler is not a `SignalRegistrar`.
- func (conn *Conn) Signal(ch chan<- *Signal) {
- handler, ok := conn.signalHandler.(SignalRegistrar)
- if !ok {
- panic("cannot use this method with a non SignalRegistrar handler")
- }
- handler.AddSignal(ch)
- }
- // RemoveSignal removes the given channel from the list of the registered channels.
- //
- // Panics if the signal handler is not a `SignalRegistrar`.
- func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
- handler, ok := conn.signalHandler.(SignalRegistrar)
- if !ok {
- panic("cannot use this method with a non SignalRegistrar handler")
- }
- handler.RemoveSignal(ch)
- }
- // SupportsUnixFDs returns whether the underlying transport supports passing of
- // unix file descriptors. If this is false, method calls containing unix file
- // descriptors will return an error and emitted signals containing them will
- // not be sent.
- func (conn *Conn) SupportsUnixFDs() bool {
- return conn.unixFD
- }
- // Error represents a D-Bus message of type Error.
- type Error struct {
- Name string
- Body []interface{}
- }
- func NewError(name string, body []interface{}) *Error {
- return &Error{name, body}
- }
- func (e Error) Error() string {
- if len(e.Body) >= 1 {
- s, ok := e.Body[0].(string)
- if ok {
- return s
- }
- }
- return e.Name
- }
- // Signal represents a D-Bus message of type Signal. The name member is given in
- // "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
- type Signal struct {
- Sender string
- Path ObjectPath
- Name string
- Body []interface{}
- Sequence Sequence
- }
- // transport is a D-Bus transport.
- type transport interface {
- // Read and Write raw data (for example, for the authentication protocol).
- io.ReadWriteCloser
- // Send the initial null byte used for the EXTERNAL mechanism.
- SendNullByte() error
- // Returns whether this transport supports passing Unix FDs.
- SupportsUnixFDs() bool
- // Signal the transport that Unix FD passing is enabled for this connection.
- EnableUnixFDs()
- // Read / send a message, handling things like Unix FDs.
- ReadMessage() (*Message, error)
- SendMessage(*Message) error
- }
- var (
- transports = make(map[string]func(string) (transport, error))
- )
- func getTransport(address string) (transport, error) {
- var err error
- var t transport
- addresses := strings.Split(address, ";")
- for _, v := range addresses {
- i := strings.IndexRune(v, ':')
- if i == -1 {
- err = errors.New("dbus: invalid bus address (no transport)")
- continue
- }
- f := transports[v[:i]]
- if f == nil {
- err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
- continue
- }
- t, err = f(v[i+1:])
- if err == nil {
- return t, nil
- }
- }
- return nil, err
- }
- // 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])
- if err != nil {
- // No way to return an error.
- return ""
- }
- return val
- }
- }
- 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 {
- if ifClosed != nil {
- 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(sequence Sequence, msg *Message) uint32 {
- serial := msg.Headers[FieldReplySerial].value.(uint32)
- tracker.lck.RLock()
- _, ok := tracker.calls[serial]
- tracker.lck.RUnlock()
- if ok {
- tracker.finalizeWithBody(serial, sequence, msg.Body)
- }
- return serial
- }
- func (tracker *callTracker) handleDBusError(sequence Sequence, 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, sequence, 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, NoSequence, 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()
- }
- }
- func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, 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.ResponseSequence = sequence
- c.done()
- }
- }
- func (tracker *callTracker) finalizeWithError(sn uint32, sequence Sequence, 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.ResponseSequence = sequence
- c.done()
- }
- }
- func (tracker *callTracker) finalizeAllWithError(sequenceGen *sequenceGenerator, err error) {
- tracker.lck.Lock()
- closedCalls := make([]*Call, 0, len(tracker.calls))
- for sn := range tracker.calls {
- closedCalls = append(closedCalls, tracker.calls[sn])
- }
- tracker.calls = map[uint32]*Call{}
- tracker.lck.Unlock()
- for _, call := range closedCalls {
- call.Err = err
- call.ResponseSequence = sequenceGen.next()
- call.done()
- }
- }
|