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  }