1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2020 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package dbusutil
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
"github.com/snapcore/snapd/dirs"
)
// isSessionBusLikelyPresent checks for the apparent availability of DBus session bus.
//
// The code matches what go-dbus does when it tries to detect the session bus:
// - the presence of the environment variable DBUS_SESSION_BUS_ADDRESS
// - the presence of the bus socket address in the file /run/user/UID/dbus-session
// - the presence of the bus socket in /run/user/UID/bus
func isSessionBusLikelyPresent() bool {
if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" {
return true
}
uid := os.Getuid()
if fi, err := os.Stat(fmt.Sprintf("%s/%d/dbus-session", dirs.XdgRuntimeDirBase, uid)); err == nil {
if fi.Mode()&os.ModeType == 0 {
return true
}
}
if fi, err := os.Stat(fmt.Sprintf("%s/%d/bus", dirs.XdgRuntimeDirBase, uid)); err == nil {
if fi.Mode()&os.ModeType == os.ModeSocket {
return true
}
}
return false
}
// SessionBus is like dbus.SessionBus but it avoids auto-starting
// a new dbus-daemon when a bus is not already available.
//
// The go-dbus package will launch a session bus instance on demand when none
// is present, something we do not want to do. In all contexts where there is a
// need to use the session bus, we expect session bus daemon to have been started
// and managed by the corresponding user session manager.
//
// This function is mockable by either MockConnections or
// MockOnlySessionBusAvailable.
var SessionBus = func() (*dbus.Conn, error) {
if isSessionBusLikelyPresent() {
return dbus.SessionBus()
}
return nil, fmt.Errorf("cannot find session bus")
}
// SystemBus is like dbus.SystemBus and is provided for completeness.
//
// This function is mockable by either MockConnections or
// MockOnlySystemBusAvailable.
var SystemBus = func() (*dbus.Conn, error) {
return dbus.SystemBus()
}
// MockConnections mocks the connection functions system and session buses.
func MockConnections(system, session func() (*dbus.Conn, error)) (restore func()) {
oldSystem := SystemBus
oldSession := SessionBus
SystemBus = system
SessionBus = session
return func() {
SystemBus = oldSystem
SessionBus = oldSession
}
}
// MockOnlySystemBusAvailable makes SystemBus return the given connection.
//
// In addition calling SessionBus will panic.
func MockOnlySystemBusAvailable(conn *dbus.Conn) (restore func()) {
systemBus := func() (*dbus.Conn, error) { return conn, nil }
sessionBus := func() (*dbus.Conn, error) {
panic("DBus session bus should not have been used")
}
return MockConnections(systemBus, sessionBus)
}
// MockOnlySessionBusAvailable makes SessionBus return the given connection.
//
// In addition calling SystemBus will panic.
func MockOnlySessionBusAvailable(conn *dbus.Conn) (restore func()) {
systemBus := func() (*dbus.Conn, error) {
panic("DBus system bus should not have been used")
}
sessionBus := func() (*dbus.Conn, error) { return conn, nil }
return MockConnections(systemBus, sessionBus)
}
// SessionBusPrivate opens a connection to the D-Bus session bus
// independent of the default shared connection.
func SessionBusPrivate() (*dbus.Conn, error) {
if !isSessionBusLikelyPresent() {
return nil, fmt.Errorf("cannot find session bus")
}
conn, err := dbus.SessionBusPrivate()
if err != nil {
return nil, err
}
if err := conn.Auth(nil); err != nil {
conn.Close()
return nil, err
}
if err := conn.Hello(); err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
|