github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/cmd/snap/cmd_userd.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  // +build !darwin
     3  
     4  /*
     5   * Copyright (C) 2017-2019 Canonical Ltd
     6   *
     7   * This program is free software: you can redistribute it and/or modify
     8   * it under the terms of the GNU General Public License version 3 as
     9   * published by the Free Software Foundation.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14   * GNU General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU General Public License
    17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18   *
    19   */
    20  
    21  package main
    22  
    23  import (
    24  	"fmt"
    25  	"os"
    26  	"os/signal"
    27  	"path/filepath"
    28  	"syscall"
    29  
    30  	"github.com/jessevdk/go-flags"
    31  
    32  	"github.com/snapcore/snapd/dirs"
    33  	"github.com/snapcore/snapd/i18n"
    34  	"github.com/snapcore/snapd/snapdtool"
    35  	"github.com/snapcore/snapd/usersession/agent"
    36  	"github.com/snapcore/snapd/usersession/autostart"
    37  	"github.com/snapcore/snapd/usersession/userd"
    38  )
    39  
    40  type cmdUserd struct {
    41  	Autostart bool `long:"autostart"`
    42  	Agent     bool `long:"agent"`
    43  }
    44  
    45  var shortUserdHelp = i18n.G("Start the userd service")
    46  var longUserdHelp = i18n.G(`
    47  The userd command starts the snap user session service.
    48  `)
    49  
    50  func init() {
    51  	cmd := addCommand("userd",
    52  		shortUserdHelp,
    53  		longUserdHelp,
    54  		func() flags.Commander {
    55  			return &cmdUserd{}
    56  		}, map[string]string{
    57  			// TRANSLATORS: This should not start with a lowercase letter.
    58  			"autostart": i18n.G("Autostart user applications"),
    59  			// TRANSLATORS: This should not start with a lowercase letter.
    60  			"agent": i18n.G("Run the user session agent"),
    61  		}, nil)
    62  	cmd.hidden = true
    63  }
    64  
    65  var osChmod = os.Chmod
    66  
    67  func maybeFixupUsrSnapPermissions() error {
    68  	usr, err := userCurrent()
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	usrSnapDir := filepath.Join(usr.HomeDir, dirs.UserHomeSnapDir)
    74  
    75  	// restrict the user's "snap dir", i.e. /home/$USER/snap, to be private with
    76  	// permissions o0700 so that other users cannot read the data there, some
    77  	// snaps such as chromium etc may store secrets inside this directory
    78  	// note that this operation is safe since `userd --autostart` runs as the
    79  	// user so there is no issue with this modification being performed as root,
    80  	// and being vulnerable to symlink switching attacks, etc.
    81  	if err := osChmod(usrSnapDir, 0700); err != nil {
    82  		// if the dir doesn't exist for some reason (i.e. maybe this user has
    83  		// never used snaps but snapd is still installed) then ignore the error
    84  		if !os.IsNotExist(err) {
    85  			return fmt.Errorf("cannot restrict user snap home dir %q: %v", usrSnapDir, err)
    86  		}
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  func (x *cmdUserd) Execute(args []string) error {
    93  	if len(args) > 0 {
    94  		return ErrExtraArgs
    95  	}
    96  
    97  	if x.Autostart {
    98  		// autostart is called when starting the graphical session, use that as
    99  		// an opportunity to fix ~/snap permission bits
   100  		if err := maybeFixupUsrSnapPermissions(); err != nil {
   101  			fmt.Fprintf(Stderr, "failure fixing ~/snap permissions: %v\n", err)
   102  		}
   103  
   104  		return x.runAutostart()
   105  	}
   106  
   107  	if x.Agent {
   108  		return x.runAgent()
   109  	}
   110  
   111  	return x.runUserd()
   112  }
   113  
   114  var signalNotify = signalNotifyImpl
   115  
   116  func (x *cmdUserd) runUserd() error {
   117  	var userd userd.Userd
   118  	if err := userd.Init(); err != nil {
   119  		return err
   120  	}
   121  	userd.Start()
   122  
   123  	ch, stop := signalNotify(syscall.SIGINT, syscall.SIGTERM)
   124  	defer stop()
   125  
   126  	select {
   127  	case sig := <-ch:
   128  		fmt.Fprintf(Stdout, "Exiting on %s.\n", sig)
   129  	case <-userd.Dying():
   130  		// something called Stop()
   131  	}
   132  
   133  	return userd.Stop()
   134  }
   135  
   136  func (x *cmdUserd) runAgent() error {
   137  	agent, err := agent.New()
   138  	if err != nil {
   139  		return err
   140  	}
   141  	agent.Version = snapdtool.Version
   142  	agent.Start()
   143  
   144  	ch, stop := signalNotify(syscall.SIGINT, syscall.SIGTERM)
   145  	defer stop()
   146  
   147  	select {
   148  	case sig := <-ch:
   149  		fmt.Fprintf(Stdout, "Exiting on %s.\n", sig)
   150  	case <-agent.Dying():
   151  		// something called Stop()
   152  	}
   153  
   154  	return agent.Stop()
   155  }
   156  
   157  func (x *cmdUserd) runAutostart() error {
   158  	if err := autostart.AutostartSessionApps(); err != nil {
   159  		return fmt.Errorf("autostart failed for the following apps:\n%v", err)
   160  	}
   161  	return nil
   162  }
   163  
   164  func signalNotifyImpl(sig ...os.Signal) (ch chan os.Signal, stop func()) {
   165  	ch = make(chan os.Signal, len(sig))
   166  	signal.Notify(ch, sig...)
   167  	stop = func() { signal.Stop(ch) }
   168  	return ch, stop
   169  }