github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_userd_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  // +build !darwin
     3  
     4  /*
     5   * Copyright (C) 2016-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_test
    22  
    23  import (
    24  	"fmt"
    25  	"net"
    26  	"net/http"
    27  	"os"
    28  	"os/user"
    29  	"path"
    30  	"path/filepath"
    31  	"strings"
    32  	"syscall"
    33  	"time"
    34  
    35  	. "gopkg.in/check.v1"
    36  
    37  	snap "github.com/snapcore/snapd/cmd/snap"
    38  	"github.com/snapcore/snapd/dirs"
    39  	"github.com/snapcore/snapd/logger"
    40  	"github.com/snapcore/snapd/osutil"
    41  	"github.com/snapcore/snapd/testutil"
    42  	"github.com/snapcore/snapd/usersession/autostart"
    43  )
    44  
    45  type userdSuite struct {
    46  	BaseSnapSuite
    47  	testutil.DBusTest
    48  
    49  	agentSocketPath string
    50  }
    51  
    52  var _ = Suite(&userdSuite{})
    53  
    54  func (s *userdSuite) SetUpTest(c *C) {
    55  	s.BaseSnapSuite.SetUpTest(c)
    56  	s.DBusTest.SetUpTest(c)
    57  
    58  	_, restore := logger.MockLogger()
    59  	s.AddCleanup(restore)
    60  
    61  	xdgRuntimeDir := fmt.Sprintf("%s/%d", dirs.XdgRuntimeDirBase, os.Getuid())
    62  	c.Assert(os.MkdirAll(xdgRuntimeDir, 0700), IsNil)
    63  	s.agentSocketPath = fmt.Sprintf("%s/snapd-session-agent.socket", xdgRuntimeDir)
    64  }
    65  
    66  func (s *userdSuite) TearDownTest(c *C) {
    67  	s.BaseSnapSuite.TearDownTest(c)
    68  	s.DBusTest.TearDownTest(c)
    69  }
    70  
    71  func (s *userdSuite) TestUserdBadCommandline(c *C) {
    72  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"userd", "extra-arg"})
    73  	c.Assert(err, ErrorMatches, "too many arguments for command")
    74  }
    75  
    76  type mockSignal struct{}
    77  
    78  func (m *mockSignal) String() string {
    79  	return "<test signal>"
    80  }
    81  
    82  func (m *mockSignal) Signal() {}
    83  
    84  func (s *userdSuite) TestUserdDBus(c *C) {
    85  	sigCh := make(chan os.Signal, 1)
    86  	sigStopCalls := 0
    87  
    88  	restore := snap.MockSignalNotify(func(sig ...os.Signal) (chan os.Signal, func()) {
    89  		c.Assert(sig, DeepEquals, []os.Signal{syscall.SIGINT, syscall.SIGTERM})
    90  		return sigCh, func() { sigStopCalls++ }
    91  	})
    92  	defer restore()
    93  
    94  	go func() {
    95  		myPid := os.Getpid()
    96  
    97  		defer func() {
    98  			sigCh <- &mockSignal{}
    99  		}()
   100  
   101  		names := map[string]bool{
   102  			"io.snapcraft.Launcher": false,
   103  			"io.snapcraft.Settings": false,
   104  		}
   105  		for i := 0; i < 1000; i++ {
   106  			seenCount := 0
   107  			for name, seen := range names {
   108  				if seen {
   109  					seenCount++
   110  					continue
   111  				}
   112  				pid, err := testutil.DBusGetConnectionUnixProcessID(s.SessionBus, name)
   113  				c.Logf("name: %v pid: %v err: %v", name, pid, err)
   114  				if pid == myPid {
   115  					names[name] = true
   116  					seenCount++
   117  				}
   118  			}
   119  			if seenCount == len(names) {
   120  				return
   121  			}
   122  			time.Sleep(10 * time.Millisecond)
   123  		}
   124  		c.Fatalf("not all names have appeared on the bus: %v", names)
   125  	}()
   126  
   127  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"userd"})
   128  	c.Assert(err, IsNil)
   129  	c.Check(rest, DeepEquals, []string{})
   130  	c.Check(strings.ToLower(s.Stdout()), Equals, "exiting on <test signal>.\n")
   131  	c.Check(sigStopCalls, Equals, 1)
   132  }
   133  
   134  func (s *userdSuite) makeAgentClient() *http.Client {
   135  	transport := &http.Transport{
   136  		Dial: func(_, _ string) (net.Conn, error) {
   137  			return net.Dial("unix", s.agentSocketPath)
   138  		},
   139  		DisableKeepAlives: true,
   140  	}
   141  	return &http.Client{Transport: transport}
   142  }
   143  
   144  func (s *userdSuite) TestSessionAgentSocket(c *C) {
   145  	sigCh := make(chan os.Signal, 1)
   146  	sigStopCalls := 0
   147  
   148  	restore := snap.MockSignalNotify(func(sig ...os.Signal) (chan os.Signal, func()) {
   149  		c.Assert(sig, DeepEquals, []os.Signal{syscall.SIGINT, syscall.SIGTERM})
   150  		return sigCh, func() { sigStopCalls++ }
   151  	})
   152  	defer restore()
   153  
   154  	go func() {
   155  		defer func() {
   156  			sigCh <- &mockSignal{}
   157  		}()
   158  
   159  		// Wait for command to create socket file
   160  		for i := 0; i < 1000; i++ {
   161  			if osutil.FileExists(s.agentSocketPath) {
   162  				break
   163  			}
   164  			time.Sleep(10 * time.Millisecond)
   165  		}
   166  
   167  		// Check that agent functions
   168  		client := s.makeAgentClient()
   169  		response, err := client.Get("http://localhost/v1/session-info")
   170  		c.Assert(err, IsNil)
   171  		defer response.Body.Close()
   172  		c.Check(response.StatusCode, Equals, 200)
   173  	}()
   174  
   175  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"userd", "--agent"})
   176  	c.Assert(err, IsNil)
   177  	c.Check(rest, DeepEquals, []string{})
   178  	c.Check(strings.ToLower(s.Stdout()), Equals, "exiting on <test signal>.\n")
   179  	c.Check(sigStopCalls, Equals, 1)
   180  }
   181  
   182  func (s *userdSuite) TestSignalNotify(c *C) {
   183  	ch, stop := snap.SignalNotify(syscall.SIGUSR1)
   184  	defer stop()
   185  	go func() {
   186  		myPid := os.Getpid()
   187  		me, err := os.FindProcess(myPid)
   188  		c.Assert(err, IsNil)
   189  		err = me.Signal(syscall.SIGUSR1)
   190  		c.Assert(err, IsNil)
   191  	}()
   192  	select {
   193  	case sig := <-ch:
   194  		c.Assert(sig, Equals, syscall.SIGUSR1)
   195  	case <-time.After(5 * time.Second):
   196  		c.Fatal("signal not received within 5s")
   197  	}
   198  }
   199  
   200  func (s *userdSuite) TestAutostartSessionAppsRestrictsPermissions(c *C) {
   201  	userDir := path.Join(c.MkDir(), "home")
   202  	mockUserCurrent := func() (*user.User, error) {
   203  		return &user.User{HomeDir: userDir}, nil
   204  	}
   205  	r := snap.MockUserCurrent(mockUserCurrent)
   206  	defer r()
   207  
   208  	r = autostart.MockUserCurrent(mockUserCurrent)
   209  	defer r()
   210  
   211  	// first make the "snap" dir permissive with 0755 perms
   212  	err := os.MkdirAll(filepath.Join(userDir, "snap"), 0755)
   213  	c.Assert(err, IsNil)
   214  
   215  	// make sure the perms are as we expect them if somehow the dir already
   216  	// existed, MkdirAll wouldn't have changed the perms
   217  	st, err := os.Stat(filepath.Join(userDir, "snap"))
   218  	c.Assert(err, IsNil)
   219  	c.Assert(st.Mode()&os.ModePerm, Equals, os.FileMode(0755))
   220  
   221  	// run autostart
   222  	args, err := snap.Parser(snap.Client()).ParseArgs([]string{"userd", "--autostart"})
   223  	c.Assert(err, IsNil)
   224  	c.Assert(args, DeepEquals, []string{})
   225  
   226  	// make sure that the directory was restricted
   227  	st, err = os.Stat(filepath.Join(userDir, "snap"))
   228  	c.Assert(err, IsNil)
   229  	c.Assert(st.Mode()&os.ModePerm, Equals, os.FileMode(0700))
   230  }
   231  
   232  func (s *userdSuite) TestAutostartSessionAppsLogsWhenItCannotRestrictPermissions(c *C) {
   233  	userDir := path.Join(c.MkDir(), "home")
   234  	mockUserCurrent := func() (*user.User, error) {
   235  		return &user.User{HomeDir: userDir}, nil
   236  	}
   237  	r := snap.MockUserCurrent(mockUserCurrent)
   238  	defer r()
   239  
   240  	r = autostart.MockUserCurrent(mockUserCurrent)
   241  	defer r()
   242  
   243  	r = snap.MockOsChmod(func(name string, mode os.FileMode) error {
   244  		c.Assert(name, Equals, filepath.Join(userDir, "snap"))
   245  		c.Assert(mode, Equals, os.FileMode(0700))
   246  
   247  		return fmt.Errorf("cannot os.Chmod because the test says so")
   248  	})
   249  	defer r()
   250  
   251  	// run autostart
   252  	args, err := snap.Parser(snap.Client()).ParseArgs([]string{"userd", "--autostart"})
   253  	c.Assert(err, IsNil)
   254  	c.Assert(args, DeepEquals, []string{})
   255  
   256  	c.Assert(s.stderr.String(), testutil.Contains, "cannot os.Chmod because the test says so")
   257  }
   258  
   259  func (s *userdSuite) TestAutostartSessionAppsRestrictsPermissionsNoCreateSnapDir(c *C) {
   260  	userDir := path.Join(c.MkDir(), "home")
   261  	mockUserCurrent := func() (*user.User, error) {
   262  		return &user.User{HomeDir: userDir}, nil
   263  	}
   264  	r := snap.MockUserCurrent(mockUserCurrent)
   265  	defer r()
   266  
   267  	r = autostart.MockUserCurrent(mockUserCurrent)
   268  	defer r()
   269  
   270  	// ensure that the "snap" dir doesn't already exist
   271  	c.Assert(filepath.Join(userDir, "snap"), testutil.FileAbsent)
   272  
   273  	// run autostart
   274  	args, err := snap.Parser(snap.Client()).ParseArgs([]string{"userd", "--autostart"})
   275  	c.Assert(err, IsNil)
   276  	c.Assert(args, DeepEquals, []string{})
   277  
   278  	// make sure that the directory was not created
   279  	c.Assert(filepath.Join(userDir, "snap"), testutil.FileAbsent)
   280  }