github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/usersession/userd/userd.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 userd 21 22 import ( 23 "fmt" 24 25 "github.com/godbus/dbus" 26 "github.com/godbus/dbus/introspect" 27 "gopkg.in/tomb.v2" 28 29 "github.com/snapcore/snapd/logger" 30 ) 31 32 type dbusInterface interface { 33 Interface() string 34 ObjectPath() dbus.ObjectPath 35 IntrospectionData() string 36 } 37 38 type Userd struct { 39 tomb tomb.Tomb 40 conn *dbus.Conn 41 dbusIfaces []dbusInterface 42 } 43 44 // userdBusNames contains the list of bus names userd will acquire on 45 // the session bus. It is unnecessary (and undesirable) to add more 46 // names here when adding new interfaces to the daemon. 47 var userdBusNames = []string{ 48 "io.snapcraft.Launcher", 49 "io.snapcraft.Settings", 50 } 51 52 func dbusSessionBus() (*dbus.Conn, error) { 53 // use a private connection to the session bus, this way we can manage 54 // its lifetime without worrying of breaking other code 55 conn, err := dbus.SessionBusPrivate() 56 if err != nil { 57 return nil, err 58 } 59 if err := conn.Auth(nil); err != nil { 60 conn.Close() 61 return nil, err 62 } 63 if err := conn.Hello(); err != nil { 64 conn.Close() 65 return nil, err 66 } 67 return conn, nil 68 } 69 70 func (ud *Userd) Init() error { 71 var err error 72 73 ud.conn, err = dbusSessionBus() 74 if err != nil { 75 return err 76 } 77 78 ud.dbusIfaces = []dbusInterface{ 79 &Launcher{ud.conn}, 80 &Settings{ud.conn}, 81 } 82 for _, iface := range ud.dbusIfaces { 83 // export the interfaces at the godbus API level first to avoid 84 // the race between being able to handle a call to an interface 85 // at the object level and the actual well-known object name 86 // becoming available on the bus 87 xml := "<node>" + iface.IntrospectionData() + introspect.IntrospectDataString + "</node>" 88 ud.conn.Export(iface, iface.ObjectPath(), iface.Interface()) 89 ud.conn.Export(introspect.Introspectable(xml), iface.ObjectPath(), "org.freedesktop.DBus.Introspectable") 90 91 } 92 93 for _, name := range userdBusNames { 94 // beyond this point the name is available and all handlers must 95 // have been set up 96 reply, err := ud.conn.RequestName(name, dbus.NameFlagDoNotQueue) 97 if err != nil { 98 return err 99 } 100 101 if reply != dbus.RequestNameReplyPrimaryOwner { 102 return fmt.Errorf("cannot obtain bus name '%s'", name) 103 } 104 } 105 return nil 106 } 107 108 func (ud *Userd) Start() { 109 logger.Noticef("Starting snap userd") 110 111 ud.tomb.Go(func() error { 112 // Listen to keep our thread up and running. All DBus bits 113 // are running in the background 114 <-ud.tomb.Dying() 115 ud.conn.Close() 116 117 err := ud.tomb.Err() 118 if err != nil && err != tomb.ErrStillAlive { 119 return err 120 } 121 return nil 122 }) 123 } 124 125 func (ud *Userd) Stop() error { 126 ud.tomb.Kill(nil) 127 return ud.tomb.Wait() 128 } 129 130 func (ud *Userd) Dying() <-chan struct{} { 131 return ud.tomb.Dying() 132 }