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