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  }