github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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 64 // MockOnlySessionBusAvailable. 65 var SessionBus = func() (*dbus.Conn, error) { 66 if isSessionBusLikelyPresent() { 67 return dbus.SessionBus() 68 } 69 return nil, fmt.Errorf("cannot find session bus") 70 } 71 72 // SystemBus is like dbus.SystemBus and is provided for completeness. 73 // 74 // This function is mockable by either MockConnections or 75 // MockOnlySystemBusAvailable. 76 var SystemBus = func() (*dbus.Conn, error) { 77 return dbus.SystemBus() 78 } 79 80 // MockConnections mocks the connection functions system and session buses. 81 func MockConnections(system, session func() (*dbus.Conn, error)) (restore func()) { 82 oldSystem := SystemBus 83 oldSession := SessionBus 84 SystemBus = system 85 SessionBus = session 86 return func() { 87 SystemBus = oldSystem 88 SessionBus = oldSession 89 } 90 } 91 92 // MockOnlySystemBusAvailable makes SystemBus return the given connection. 93 // 94 // In addition calling SessionBus will panic. 95 func MockOnlySystemBusAvailable(conn *dbus.Conn) (restore func()) { 96 systemBus := func() (*dbus.Conn, error) { return conn, nil } 97 sessionBus := func() (*dbus.Conn, error) { 98 panic("DBus session bus should not have been used") 99 } 100 return MockConnections(systemBus, sessionBus) 101 } 102 103 // MockOnlySessionBusAvailable makes SessionBus return the given connection. 104 // 105 // In addition calling SystemBus will panic. 106 func MockOnlySessionBusAvailable(conn *dbus.Conn) (restore func()) { 107 systemBus := func() (*dbus.Conn, error) { 108 panic("DBus system bus should not have been used") 109 } 110 sessionBus := func() (*dbus.Conn, error) { return conn, nil } 111 return MockConnections(systemBus, sessionBus) 112 }