github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 Name() string 34 BasePath() 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 func dbusSessionBus() (*dbus.Conn, error) { 45 // use a private connection to the session bus, this way we can manage 46 // its lifetime without worrying of breaking other code 47 conn, err := dbus.SessionBusPrivate() 48 if err != nil { 49 return nil, err 50 } 51 if err := conn.Auth(nil); err != nil { 52 conn.Close() 53 return nil, err 54 } 55 if err := conn.Hello(); err != nil { 56 conn.Close() 57 return nil, err 58 } 59 return conn, nil 60 } 61 62 func (ud *Userd) Init() error { 63 var err error 64 65 ud.conn, err = dbusSessionBus() 66 if err != nil { 67 return err 68 } 69 70 ud.dbusIfaces = []dbusInterface{ 71 &Launcher{ud.conn}, 72 &Settings{ud.conn}, 73 } 74 for _, iface := range ud.dbusIfaces { 75 // export the interfaces at the godbus API level first to avoid 76 // the race between being able to handle a call to an interface 77 // at the object level and the actual well-known object name 78 // becoming available on the bus 79 xml := "<node>" + iface.IntrospectionData() + introspect.IntrospectDataString + "</node>" 80 ud.conn.Export(iface, iface.BasePath(), iface.Name()) 81 ud.conn.Export(introspect.Introspectable(xml), iface.BasePath(), "org.freedesktop.DBus.Introspectable") 82 83 // beyond this point the name is available and all handlers must 84 // have been set up 85 reply, err := ud.conn.RequestName(iface.Name(), dbus.NameFlagDoNotQueue) 86 if err != nil { 87 return err 88 } 89 90 if reply != dbus.RequestNameReplyPrimaryOwner { 91 return fmt.Errorf("cannot obtain bus name '%s'", iface.Name()) 92 } 93 } 94 return nil 95 } 96 97 func (ud *Userd) Start() { 98 logger.Noticef("Starting snap userd") 99 100 ud.tomb.Go(func() error { 101 // Listen to keep our thread up and running. All DBus bits 102 // are running in the background 103 <-ud.tomb.Dying() 104 ud.conn.Close() 105 106 err := ud.tomb.Err() 107 if err != nil && err != tomb.ErrStillAlive { 108 return err 109 } 110 return nil 111 }) 112 } 113 114 func (ud *Userd) Stop() error { 115 ud.tomb.Kill(nil) 116 return ud.tomb.Wait() 117 } 118 119 func (ud *Userd) Dying() <-chan struct{} { 120 return ud.tomb.Dying() 121 }