gitee.com/mysnapcore/mysnapd@v0.1.0/cmd/snap/cmd_run_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2022 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 main_test
    21  
    22  import (
    23  	"encoding/json"
    24  	"errors"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"net/http"
    28  	"os"
    29  	"os/user"
    30  	"path/filepath"
    31  	"strings"
    32  	"time"
    33  
    34  	"gopkg.in/check.v1"
    35  
    36  	snaprun "gitee.com/mysnapcore/mysnapd/cmd/snap"
    37  	"gitee.com/mysnapcore/mysnapd/cmd/snaplock/runinhibit"
    38  	"gitee.com/mysnapcore/mysnapd/dirs"
    39  	"gitee.com/mysnapcore/mysnapd/features"
    40  	"gitee.com/mysnapcore/mysnapd/logger"
    41  	"gitee.com/mysnapcore/mysnapd/osutil"
    42  	"gitee.com/mysnapcore/mysnapd/osutil/strace"
    43  	"gitee.com/mysnapcore/mysnapd/progress"
    44  	"gitee.com/mysnapcore/mysnapd/progress/progresstest"
    45  	"gitee.com/mysnapcore/mysnapd/sandbox/cgroup"
    46  	"gitee.com/mysnapcore/mysnapd/sandbox/selinux"
    47  	"gitee.com/mysnapcore/mysnapd/snap"
    48  	"gitee.com/mysnapcore/mysnapd/snap/snaptest"
    49  	"gitee.com/mysnapcore/mysnapd/testutil"
    50  	usersessionclient "gitee.com/mysnapcore/mysnapd/usersession/client"
    51  	"gitee.com/mysnapcore/mysnapd/x11"
    52  )
    53  
    54  var mockYaml = []byte(`name: snapname
    55  version: 1.0
    56  apps:
    57   app:
    58    command: run-app
    59   svc:
    60    command: run-svc
    61    daemon: simple
    62  hooks:
    63   configure:
    64  `)
    65  
    66  var mockYamlBaseNone1 = []byte(`name: snapname1
    67  version: 1.0
    68  base: none
    69  apps:
    70   app:
    71    command: run-app
    72  `)
    73  
    74  var mockYamlBaseNone2 = []byte(`name: snapname2
    75  version: 1.0
    76  base: none
    77  hooks:
    78   configure:
    79  `)
    80  
    81  type RunSuite struct {
    82  	fakeHome string
    83  	BaseSnapSuite
    84  }
    85  
    86  var _ = check.Suite(&RunSuite{})
    87  
    88  func (s *RunSuite) SetUpTest(c *check.C) {
    89  	s.BaseSnapSuite.SetUpTest(c)
    90  	s.fakeHome = c.MkDir()
    91  
    92  	u, err := user.Current()
    93  	c.Assert(err, check.IsNil)
    94  	s.AddCleanup(snaprun.MockUserCurrent(func() (*user.User, error) {
    95  		return &user.User{Uid: u.Uid, HomeDir: s.fakeHome}, nil
    96  	}))
    97  	s.AddCleanup(snaprun.MockCreateTransientScopeForTracking(func(string, *cgroup.TrackingOptions) error {
    98  		return nil
    99  	}))
   100  	restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(false)
   101  	s.AddCleanup(restoreIsGraphicalSession)
   102  }
   103  
   104  func (s *RunSuite) TestInvalidParameters(c *check.C) {
   105  	invalidParameters := []string{"run", "--hook=configure", "--command=command-name", "--", "snap-name"}
   106  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   107  	c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
   108  
   109  	invalidParameters = []string{"run", "--hook=configure", "--timer=10:00-12:00", "--", "snap-name"}
   110  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   111  	c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
   112  
   113  	invalidParameters = []string{"run", "--command=command-name", "--timer=10:00-12:00", "--", "snap-name"}
   114  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   115  	c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
   116  
   117  	invalidParameters = []string{"run", "-r=1", "--command=command-name", "--", "snap-name"}
   118  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   119  	c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*")
   120  
   121  	invalidParameters = []string{"run", "-r=1", "--", "snap-name"}
   122  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   123  	c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*")
   124  
   125  	invalidParameters = []string{"run", "--hook=configure", "--", "foo", "bar", "snap-name"}
   126  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   127  	c.Check(err, check.ErrorMatches, ".*too many arguments for hook \"configure\": bar.*")
   128  }
   129  
   130  func (s *RunSuite) TestRunCmdWithBaseNone(c *check.C) {
   131  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   132  
   133  	// mock installed snap
   134  	snaptest.MockSnapCurrent(c, string(mockYamlBaseNone1), &snap.SideInfo{
   135  		Revision: snap.R("1"),
   136  	})
   137  	snaptest.MockSnapCurrent(c, string(mockYamlBaseNone2), &snap.SideInfo{
   138  		Revision: snap.R("1"),
   139  	})
   140  
   141  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname1.app", "--arg1", "arg2"})
   142  	c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`)
   143  
   144  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname2"})
   145  	c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`)
   146  }
   147  
   148  func (s *RunSuite) TestSnapRunWhenMissingConfine(c *check.C) {
   149  	_, r := logger.MockLogger()
   150  	defer r()
   151  
   152  	// mock installed snap
   153  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   154  		Revision: snap.R("x2"),
   155  	})
   156  
   157  	// redirect exec
   158  	var execs [][]string
   159  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   160  		execs = append(execs, args)
   161  		return nil
   162  	})
   163  	defer restorer()
   164  
   165  	// and run it!
   166  	// a regular run will fail
   167  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   168  	c.Assert(err, check.ErrorMatches, `.* your core/snapd package`)
   169  	// a hook run will not fail
   170  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"})
   171  	c.Assert(err, check.IsNil)
   172  
   173  	// but nothing is run ever
   174  	c.Check(execs, check.IsNil)
   175  }
   176  
   177  func (s *RunSuite) TestSnapRunAppIntegration(c *check.C) {
   178  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   179  
   180  	tmpdir := os.Getenv("TMPDIR")
   181  	if tmpdir == "" {
   182  		tmpdir = "/var/tmp"
   183  		os.Setenv("TMPDIR", tmpdir)
   184  		defer os.Unsetenv("TMPDIR")
   185  	}
   186  
   187  	// mock installed snap
   188  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   189  		Revision: snap.R("x2"),
   190  	})
   191  
   192  	// redirect exec
   193  	execArg0 := ""
   194  	execArgs := []string{}
   195  	execEnv := []string{}
   196  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   197  		execArg0 = arg0
   198  		execArgs = args
   199  		execEnv = envv
   200  		return nil
   201  	})
   202  	defer restorer()
   203  
   204  	// and run it!
   205  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   206  	c.Assert(err, check.IsNil)
   207  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   208  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   209  	c.Check(execArgs, check.DeepEquals, []string{
   210  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   211  		"snap.snapname.app",
   212  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   213  		"snapname.app", "--arg1", "arg2"})
   214  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   215  	c.Check(execEnv, testutil.Contains, fmt.Sprintf("TMPDIR=%s", tmpdir))
   216  }
   217  
   218  func (s *RunSuite) TestSnapRunAppRunsChecksInhibitionLock(c *check.C) {
   219  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   220  
   221  	// mock installed snap
   222  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{Revision: snap.R("x2")})
   223  
   224  	var execArg0 string
   225  	var execArgs []string
   226  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   227  		execArg0 = arg0
   228  		execArgs = args
   229  		return nil
   230  	})
   231  	defer restorer()
   232  
   233  	c.Assert(runinhibit.LockWithHint("snapname", runinhibit.HintInhibitedForRefresh), check.IsNil)
   234  	c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), check.IsNil)
   235  	c.Assert(ioutil.WriteFile(features.RefreshAppAwareness.ControlFile(), []byte(nil), 0644), check.IsNil)
   236  
   237  	var called int
   238  	restore := snaprun.MockWaitInhibitUnlock(func(snapName string, waitFor runinhibit.Hint) (bool, error) {
   239  		called++
   240  		return false, nil
   241  	})
   242  	defer restore()
   243  
   244  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1"})
   245  	c.Assert(err, check.IsNil)
   246  	c.Check(called, check.Equals, 2)
   247  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1"})
   248  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   249  	c.Check(execArgs, check.DeepEquals, []string{
   250  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   251  		"snap.snapname.app",
   252  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   253  		"snapname.app", "--arg1"})
   254  }
   255  
   256  func (s *RunSuite) TestSnapRunHookNoRuninhibit(c *check.C) {
   257  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   258  
   259  	// mock installed snap
   260  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   261  		Revision: snap.R(42),
   262  	})
   263  
   264  	// redirect exec
   265  	execArg0 := ""
   266  	execArgs := []string{}
   267  	execEnv := []string{}
   268  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   269  		execArg0 = arg0
   270  		execArgs = args
   271  		execEnv = envv
   272  		return nil
   273  	})
   274  	defer restorer()
   275  
   276  	var called bool
   277  	restore := snaprun.MockWaitInhibitUnlock(func(snapName string, waitFor runinhibit.Hint) (bool, error) {
   278  		called = true
   279  		c.Errorf("WaitInhibitUnlock should not have been called")
   280  		return false, nil
   281  	})
   282  	defer restore()
   283  
   284  	c.Assert(runinhibit.LockWithHint("snapname", runinhibit.HintInhibitedForRefresh), check.IsNil)
   285  	c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), check.IsNil)
   286  	c.Assert(ioutil.WriteFile(features.RefreshAppAwareness.ControlFile(), []byte(nil), 0644), check.IsNil)
   287  
   288  	// Run a hook from the active revision
   289  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"})
   290  	c.Assert(err, check.IsNil)
   291  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   292  	c.Check(execArgs, check.DeepEquals, []string{
   293  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   294  		"snap.snapname.hook.configure",
   295  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   296  		"--hook=configure", "snapname"})
   297  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   298  	c.Check(called, check.Equals, false)
   299  }
   300  
   301  func (s *RunSuite) TestSnapRunAppRuninhibitSkipsServices(c *check.C) {
   302  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   303  
   304  	// mock installed snap
   305  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{Revision: snap.R("x2")})
   306  
   307  	var execArg0 string
   308  	var execArgs []string
   309  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   310  		execArg0 = arg0
   311  		execArgs = args
   312  		return nil
   313  	})
   314  	defer restorer()
   315  
   316  	c.Assert(runinhibit.LockWithHint("snapname", runinhibit.HintInhibitedForRefresh), check.IsNil)
   317  	c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), check.IsNil)
   318  	c.Assert(ioutil.WriteFile(features.RefreshAppAwareness.ControlFile(), []byte(nil), 0644), check.IsNil)
   319  
   320  	var called bool
   321  	restore := snaprun.MockWaitInhibitUnlock(func(snapName string, waitFor runinhibit.Hint) (bool, error) {
   322  		called = true
   323  		c.Errorf("WaitInhibitUnlock should not have been called")
   324  		return false, nil
   325  	})
   326  	defer restore()
   327  
   328  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
   329  		c.Assert(securityTag, check.Equals, "snap.snapname.svc")
   330  		return nil
   331  	})
   332  	defer restore()
   333  
   334  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc"})
   335  	c.Assert(err, check.IsNil)
   336  	c.Check(called, check.Equals, false)
   337  	c.Assert(rest, check.DeepEquals, []string{"snapname.svc"})
   338  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   339  	c.Check(execArgs, check.DeepEquals, []string{
   340  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"), "snap.snapname.svc",
   341  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"), "snapname.svc"})
   342  }
   343  
   344  func (s *RunSuite) TestSnapRunClassicAppIntegration(c *check.C) {
   345  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   346  
   347  	tmpdir := os.Getenv("TMPDIR")
   348  	if tmpdir == "" {
   349  		tmpdir = "/var/tmp"
   350  		os.Setenv("TMPDIR", tmpdir)
   351  		defer os.Unsetenv("TMPDIR")
   352  	}
   353  
   354  	// mock installed snap
   355  	snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{
   356  		Revision: snap.R("x2"),
   357  	})
   358  
   359  	// redirect exec
   360  	execArg0 := ""
   361  	execArgs := []string{}
   362  	execEnv := []string{}
   363  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   364  		execArg0 = arg0
   365  		execArgs = args
   366  		execEnv = envv
   367  		return nil
   368  	})
   369  	defer restorer()
   370  
   371  	// and run it!
   372  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   373  	c.Assert(err, check.IsNil)
   374  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   375  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   376  	c.Check(execArgs, check.DeepEquals, []string{
   377  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"), "--classic",
   378  		"snap.snapname.app",
   379  		filepath.Join(dirs.DistroLibExecDir, "snap-exec"),
   380  		"snapname.app", "--arg1", "arg2"})
   381  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   382  	c.Check(execEnv, testutil.Contains, fmt.Sprintf("SNAP_SAVED_TMPDIR=%s", tmpdir))
   383  }
   384  
   385  func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromCore(c *check.C) {
   386  	mountedCorePath := filepath.Join(dirs.SnapMountDir, "core/current")
   387  	mountedCoreLibExecPath := filepath.Join(mountedCorePath, dirs.CoreLibExecDir)
   388  
   389  	defer mockSnapConfine(mountedCoreLibExecPath)()
   390  
   391  	// mock installed snap
   392  	snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{
   393  		Revision: snap.R("x2"),
   394  	})
   395  
   396  	restore := snaprun.MockOsReadlink(func(name string) (string, error) {
   397  		// pretend 'snap' is reexeced from 'core'
   398  		return filepath.Join(mountedCorePath, "usr/bin/snap"), nil
   399  	})
   400  	defer restore()
   401  
   402  	execArgs := []string{}
   403  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   404  		execArgs = args
   405  		return nil
   406  	})
   407  	defer restorer()
   408  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   409  	c.Assert(err, check.IsNil)
   410  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   411  	c.Check(execArgs, check.DeepEquals, []string{
   412  		filepath.Join(mountedCoreLibExecPath, "snap-confine"), "--classic",
   413  		"snap.snapname.app",
   414  		filepath.Join(mountedCoreLibExecPath, "snap-exec"),
   415  		"snapname.app", "--arg1", "arg2"})
   416  }
   417  
   418  func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromSnapd(c *check.C) {
   419  	mountedSnapdPath := filepath.Join(dirs.SnapMountDir, "snapd/current")
   420  	mountedSnapdLibExecPath := filepath.Join(mountedSnapdPath, dirs.CoreLibExecDir)
   421  
   422  	defer mockSnapConfine(mountedSnapdLibExecPath)()
   423  
   424  	// mock installed snap
   425  	snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{
   426  		Revision: snap.R("x2"),
   427  	})
   428  
   429  	restore := snaprun.MockOsReadlink(func(name string) (string, error) {
   430  		// pretend 'snap' is reexeced from 'core'
   431  		return filepath.Join(mountedSnapdPath, "usr/bin/snap"), nil
   432  	})
   433  	defer restore()
   434  
   435  	execArgs := []string{}
   436  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   437  		execArgs = args
   438  		return nil
   439  	})
   440  	defer restorer()
   441  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   442  	c.Assert(err, check.IsNil)
   443  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   444  	c.Check(execArgs, check.DeepEquals, []string{
   445  		filepath.Join(mountedSnapdLibExecPath, "snap-confine"), "--classic",
   446  		"snap.snapname.app",
   447  		filepath.Join(mountedSnapdLibExecPath, "snap-exec"),
   448  		"snapname.app", "--arg1", "arg2"})
   449  }
   450  
   451  func (s *RunSuite) TestSnapRunAppWithCommandIntegration(c *check.C) {
   452  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   453  
   454  	// mock installed snap
   455  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   456  		Revision: snap.R(42),
   457  	})
   458  
   459  	// redirect exec
   460  	execArg0 := ""
   461  	execArgs := []string{}
   462  	execEnv := []string{}
   463  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   464  		execArg0 = arg0
   465  		execArgs = args
   466  		execEnv = envv
   467  		return nil
   468  	})
   469  	defer restorer()
   470  
   471  	// and run it!
   472  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=my-command", "--", "snapname.app", "arg1", "arg2"})
   473  	c.Assert(err, check.IsNil)
   474  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   475  	c.Check(execArgs, check.DeepEquals, []string{
   476  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   477  		"snap.snapname.app",
   478  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   479  		"--command=my-command", "snapname.app", "arg1", "arg2"})
   480  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   481  }
   482  
   483  func (s *RunSuite) TestSnapRunCreateDataDirs(c *check.C) {
   484  	for _, t := range []struct {
   485  		snapDir string
   486  		opts    *dirs.SnapDirOptions
   487  	}{
   488  		{snapDir: dirs.UserHomeSnapDir},
   489  		{snapDir: dirs.UserHomeSnapDir, opts: &dirs.SnapDirOptions{}},
   490  		{snapDir: dirs.HiddenSnapDataHomeDir, opts: &dirs.SnapDirOptions{HiddenSnapDataDir: true}},
   491  	} {
   492  		s.testSnapRunCreateDataDirs(c, t.snapDir, t.opts)
   493  		c.Assert(os.RemoveAll(s.fakeHome), check.IsNil)
   494  		s.fakeHome = c.MkDir()
   495  	}
   496  }
   497  
   498  func (s *RunSuite) testSnapRunCreateDataDirs(c *check.C, snapDir string, opts *dirs.SnapDirOptions) {
   499  	info, err := snap.InfoFromSnapYaml(mockYaml)
   500  	c.Assert(err, check.IsNil)
   501  	info.SideInfo.Revision = snap.R(42)
   502  
   503  	err = snaprun.CreateUserDataDirs(info, opts)
   504  	c.Assert(err, check.IsNil)
   505  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, snapDir, "snapname/42")), check.Equals, true)
   506  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, snapDir, "snapname/common")), check.Equals, true)
   507  
   508  	// check we don't create the alternative dir
   509  	nonExistentDir := dirs.HiddenSnapDataHomeDir
   510  	if snapDir == dirs.HiddenSnapDataHomeDir {
   511  		nonExistentDir = dirs.UserHomeSnapDir
   512  	}
   513  
   514  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, nonExistentDir)), check.Equals, false)
   515  }
   516  
   517  func (s *RunSuite) TestParallelInstanceSnapRunCreateDataDirs(c *check.C) {
   518  	info, err := snap.InfoFromSnapYaml(mockYaml)
   519  	c.Assert(err, check.IsNil)
   520  	info.SideInfo.Revision = snap.R(42)
   521  	info.InstanceKey = "foo"
   522  
   523  	err = snaprun.CreateUserDataDirs(info, nil)
   524  	c.Assert(err, check.IsNil)
   525  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/42")), check.Equals, true)
   526  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/common")), check.Equals, true)
   527  	// mount point for snap instance mapping has been created
   528  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname")), check.Equals, true)
   529  	// and it's empty inside
   530  	m, err := filepath.Glob(filepath.Join(s.fakeHome, "/snap/snapname/*"))
   531  	c.Assert(err, check.IsNil)
   532  	c.Assert(m, check.HasLen, 0)
   533  }
   534  
   535  func (s *RunSuite) TestSnapRunHookIntegration(c *check.C) {
   536  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   537  
   538  	// mock installed snap
   539  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   540  		Revision: snap.R(42),
   541  	})
   542  
   543  	// redirect exec
   544  	execArg0 := ""
   545  	execArgs := []string{}
   546  	execEnv := []string{}
   547  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   548  		execArg0 = arg0
   549  		execArgs = args
   550  		execEnv = envv
   551  		return nil
   552  	})
   553  	defer restorer()
   554  
   555  	// Run a hook from the active revision
   556  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"})
   557  	c.Assert(err, check.IsNil)
   558  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   559  	c.Check(execArgs, check.DeepEquals, []string{
   560  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   561  		"snap.snapname.hook.configure",
   562  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   563  		"--hook=configure", "snapname"})
   564  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   565  }
   566  
   567  func (s *RunSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) {
   568  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   569  
   570  	// mock installed snap
   571  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   572  		Revision: snap.R(42),
   573  	})
   574  
   575  	// redirect exec
   576  	execArg0 := ""
   577  	execArgs := []string{}
   578  	execEnv := []string{}
   579  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   580  		execArg0 = arg0
   581  		execArgs = args
   582  		execEnv = envv
   583  		return nil
   584  	})
   585  	defer restorer()
   586  
   587  	// Specifically pass "unset" which would use the active version.
   588  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=unset", "--", "snapname"})
   589  	c.Assert(err, check.IsNil)
   590  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   591  	c.Check(execArgs, check.DeepEquals, []string{
   592  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   593  		"snap.snapname.hook.configure",
   594  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   595  		"--hook=configure", "snapname"})
   596  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   597  }
   598  
   599  func (s *RunSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) {
   600  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   601  
   602  	// mock installed snap
   603  	// Create both revisions 41 and 42
   604  	snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{
   605  		Revision: snap.R(41),
   606  	})
   607  	snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{
   608  		Revision: snap.R(42),
   609  	})
   610  
   611  	// redirect exec
   612  	execArg0 := ""
   613  	execArgs := []string{}
   614  	execEnv := []string{}
   615  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   616  		execArg0 = arg0
   617  		execArgs = args
   618  		execEnv = envv
   619  		return nil
   620  	})
   621  	defer restorer()
   622  
   623  	// Run a hook on revision 41
   624  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"})
   625  	c.Assert(err, check.IsNil)
   626  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   627  	c.Check(execArgs, check.DeepEquals, []string{
   628  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   629  		"snap.snapname.hook.configure",
   630  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   631  		"--hook=configure", "snapname"})
   632  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=41")
   633  }
   634  
   635  func (s *RunSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) {
   636  	// Only create revision 42
   637  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   638  		Revision: snap.R(42),
   639  	})
   640  
   641  	// redirect exec
   642  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   643  		return nil
   644  	})
   645  	defer restorer()
   646  
   647  	// Attempt to run a hook on revision 41, which doesn't exist
   648  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"})
   649  	c.Assert(err, check.NotNil)
   650  	c.Check(err, check.ErrorMatches, "cannot find .*")
   651  }
   652  
   653  func (s *RunSuite) TestSnapRunHookInvalidRevisionIntegration(c *check.C) {
   654  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=invalid", "--", "snapname"})
   655  	c.Assert(err, check.NotNil)
   656  	c.Check(err, check.ErrorMatches, "invalid snap revision: \"invalid\"")
   657  }
   658  
   659  func (s *RunSuite) TestSnapRunHookMissingHookIntegration(c *check.C) {
   660  	// Only create revision 42
   661  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   662  		Revision: snap.R(42),
   663  	})
   664  
   665  	// redirect exec
   666  	called := false
   667  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   668  		called = true
   669  		return nil
   670  	})
   671  	defer restorer()
   672  
   673  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=missing-hook", "--", "snapname"})
   674  	c.Assert(err, check.ErrorMatches, `cannot find hook "missing-hook" in "snapname"`)
   675  	c.Check(called, check.Equals, false)
   676  }
   677  
   678  func (s *RunSuite) TestSnapRunErorsForUnknownRunArg(c *check.C) {
   679  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--unknown", "--", "snapname.app", "--arg1", "arg2"})
   680  	c.Assert(err, check.ErrorMatches, "unknown flag `unknown'")
   681  }
   682  
   683  func (s *RunSuite) TestSnapRunErorsForMissingApp(c *check.C) {
   684  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=shell"})
   685  	c.Assert(err, check.ErrorMatches, "need the application to run as argument")
   686  }
   687  
   688  func (s *RunSuite) TestSnapRunErorrForUnavailableApp(c *check.C) {
   689  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "not-there"})
   690  	c.Assert(err, check.ErrorMatches, fmt.Sprintf("cannot find current revision for snap not-there: readlink %s/not-there/current: no such file or directory", dirs.SnapMountDir))
   691  }
   692  
   693  func (s *RunSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) {
   694  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   695  
   696  	// mock installed snap
   697  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   698  		Revision: snap.R(42),
   699  	})
   700  
   701  	// redirect exec
   702  	execEnv := []string{}
   703  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   704  		execEnv = envv
   705  		return nil
   706  	})
   707  	defer restorer()
   708  
   709  	// set a SNAP{,_*} variable in the environment
   710  	os.Setenv("SNAP_NAME", "something-else")
   711  	os.Setenv("SNAP_ARCH", "PDP-7")
   712  	defer os.Unsetenv("SNAP_NAME")
   713  	defer os.Unsetenv("SNAP_ARCH")
   714  	// but unrelated stuff is ok
   715  	os.Setenv("SNAP_THE_WORLD", "YES")
   716  	defer os.Unsetenv("SNAP_THE_WORLD")
   717  
   718  	// and ensure those SNAP_ vars get overridden
   719  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   720  	c.Assert(err, check.IsNil)
   721  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   722  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   723  	c.Check(execEnv, check.Not(testutil.Contains), "SNAP_NAME=something-else")
   724  	c.Check(execEnv, check.Not(testutil.Contains), "SNAP_ARCH=PDP-7")
   725  	c.Check(execEnv, testutil.Contains, "SNAP_THE_WORLD=YES")
   726  }
   727  
   728  func (s *RunSuite) TestSnapRunSnapdHelperPath(c *check.C) {
   729  	_, r := logger.MockLogger()
   730  	defer r()
   731  
   732  	var osReadlinkResult string
   733  	restore := snaprun.MockOsReadlink(func(name string) (string, error) {
   734  		return osReadlinkResult, nil
   735  	})
   736  	defer restore()
   737  
   738  	tool := "snap-confine"
   739  	for _, t := range []struct {
   740  		readlink string
   741  		expected string
   742  	}{
   743  		{
   744  			filepath.Join(dirs.SnapMountDir, "core/current/usr/bin/snap"),
   745  			filepath.Join(dirs.SnapMountDir, "core/current", dirs.CoreLibExecDir, tool),
   746  		},
   747  		{
   748  			filepath.Join(dirs.SnapMountDir, "snapd/current/usr/bin/snap"),
   749  			filepath.Join(dirs.SnapMountDir, "snapd/current", dirs.CoreLibExecDir, tool),
   750  		},
   751  		{
   752  			filepath.Join("/usr/bin/snap"),
   753  			filepath.Join(dirs.DistroLibExecDir, tool),
   754  		},
   755  		{
   756  			filepath.Join("/home/foo/ws/snapd/snap"),
   757  			filepath.Join(dirs.DistroLibExecDir, tool),
   758  		},
   759  		// unexpected case
   760  		{
   761  			filepath.Join(dirs.SnapMountDir, "snapd2/current/bin/snap"),
   762  			filepath.Join(dirs.DistroLibExecDir, tool),
   763  		},
   764  	} {
   765  		osReadlinkResult = t.readlink
   766  		toolPath, err := snaprun.SnapdHelperPath(tool)
   767  		c.Assert(err, check.IsNil)
   768  		c.Check(toolPath, check.Equals, t.expected)
   769  	}
   770  }
   771  
   772  func (s *RunSuite) TestSnapRunAppIntegrationFromCore(c *check.C) {
   773  	defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))()
   774  
   775  	// mock installed snap
   776  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   777  		Revision: snap.R("x2"),
   778  	})
   779  
   780  	// pretend to be running from core
   781  	restorer := snaprun.MockOsReadlink(func(string) (string, error) {
   782  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
   783  	})
   784  	defer restorer()
   785  
   786  	// redirect exec
   787  	execArg0 := ""
   788  	execArgs := []string{}
   789  	execEnv := []string{}
   790  	restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   791  		execArg0 = arg0
   792  		execArgs = args
   793  		execEnv = envv
   794  		return nil
   795  	})
   796  	defer restorer()
   797  
   798  	// and run it!
   799  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   800  	c.Assert(err, check.IsNil)
   801  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   802  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
   803  	c.Check(execArgs, check.DeepEquals, []string{
   804  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
   805  		"snap.snapname.app",
   806  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   807  		"snapname.app", "--arg1", "arg2"})
   808  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   809  }
   810  
   811  func (s *RunSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) {
   812  	defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "snapd", "222", dirs.CoreLibExecDir))()
   813  
   814  	// mock installed snap
   815  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   816  		Revision: snap.R("x2"),
   817  	})
   818  
   819  	// pretend to be running from snapd
   820  	restorer := snaprun.MockOsReadlink(func(string) (string, error) {
   821  		return filepath.Join(dirs.SnapMountDir, "snapd/222/usr/bin/snap"), nil
   822  	})
   823  	defer restorer()
   824  
   825  	// redirect exec
   826  	execArg0 := ""
   827  	execArgs := []string{}
   828  	execEnv := []string{}
   829  	restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   830  		execArg0 = arg0
   831  		execArgs = args
   832  		execEnv = envv
   833  		return nil
   834  	})
   835  	defer restorer()
   836  
   837  	// and run it!
   838  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   839  	c.Assert(err, check.IsNil)
   840  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   841  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine"))
   842  	c.Check(execArgs, check.DeepEquals, []string{
   843  		filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine"),
   844  		"snap.snapname.app",
   845  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   846  		"snapname.app", "--arg1", "arg2"})
   847  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   848  }
   849  
   850  func (s *RunSuite) TestSnapRunXauthorityMigration(c *check.C) {
   851  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   852  
   853  	u, err := user.Current()
   854  	c.Assert(err, check.IsNil)
   855  
   856  	// Ensure XDG_RUNTIME_DIR exists for the user we're testing with
   857  	err = os.MkdirAll(filepath.Join(dirs.XdgRuntimeDirBase, u.Uid), 0700)
   858  	c.Assert(err, check.IsNil)
   859  
   860  	// mock installed snap; happily this also gives us a directory
   861  	// below /tmp which the Xauthority migration expects.
   862  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   863  		Revision: snap.R("x2"),
   864  	})
   865  
   866  	// redirect exec
   867  	execArg0 := ""
   868  	execArgs := []string{}
   869  	execEnv := []string{}
   870  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   871  		execArg0 = arg0
   872  		execArgs = args
   873  		execEnv = envv
   874  		return nil
   875  	})
   876  	defer restorer()
   877  
   878  	xauthPath, err := x11.MockXauthority(2)
   879  	c.Assert(err, check.IsNil)
   880  	defer os.Remove(xauthPath)
   881  
   882  	defer snaprun.MockGetEnv(func(name string) string {
   883  		if name == "XAUTHORITY" {
   884  			return xauthPath
   885  		}
   886  		return ""
   887  	})()
   888  
   889  	// and run it!
   890  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
   891  	c.Assert(err, check.IsNil)
   892  	c.Assert(rest, check.DeepEquals, []string{"snapname.app"})
   893  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   894  	c.Check(execArgs, check.DeepEquals, []string{
   895  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   896  		"snap.snapname.app",
   897  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   898  		"snapname.app"})
   899  
   900  	expectedXauthPath := filepath.Join(dirs.XdgRuntimeDirBase, u.Uid, ".Xauthority")
   901  	c.Check(execEnv, testutil.Contains, fmt.Sprintf("XAUTHORITY=%s", expectedXauthPath))
   902  
   903  	info, err := os.Stat(expectedXauthPath)
   904  	c.Assert(err, check.IsNil)
   905  	c.Assert(info.Mode().Perm(), check.Equals, os.FileMode(0600))
   906  
   907  	err = x11.ValidateXauthorityFile(expectedXauthPath)
   908  	c.Assert(err, check.IsNil)
   909  }
   910  
   911  // build the args for a hypothetical completer
   912  func mkCompArgs(compPoint string, argv ...string) []string {
   913  	out := []string{
   914  		"99", // COMP_TYPE
   915  		"99", // COMP_KEY
   916  		"",   // COMP_POINT
   917  		"2",  // COMP_CWORD
   918  		" ",  // COMP_WORDBREAKS
   919  	}
   920  	out[2] = compPoint
   921  	out = append(out, strings.Join(argv, " "))
   922  	out = append(out, argv...)
   923  	return out
   924  }
   925  
   926  func (s *RunSuite) TestAntialiasHappy(c *check.C) {
   927  	c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil)
   928  
   929  	inArgs := mkCompArgs("10", "alias", "alias", "bo-alias")
   930  
   931  	// first not so happy because no alias symlink
   932  	app, outArgs := snaprun.Antialias("alias", inArgs)
   933  	c.Check(app, check.Equals, "alias")
   934  	c.Check(outArgs, check.DeepEquals, inArgs)
   935  
   936  	c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil)
   937  
   938  	// now really happy
   939  	app, outArgs = snaprun.Antialias("alias", inArgs)
   940  	c.Check(app, check.Equals, "an-app")
   941  	c.Check(outArgs, check.DeepEquals, []string{
   942  		"99",                    // COMP_TYPE (no change)
   943  		"99",                    // COMP_KEY (no change)
   944  		"11",                    // COMP_POINT (+1 because "an-app" is one longer than "alias")
   945  		"2",                     // COMP_CWORD (no change)
   946  		" ",                     // COMP_WORDBREAKS (no change)
   947  		"an-app alias bo-alias", // COMP_LINE (argv[0] changed)
   948  		"an-app",                // argv (arv[0] changed)
   949  		"alias",
   950  		"bo-alias",
   951  	})
   952  }
   953  
   954  func (s *RunSuite) TestAntialiasBailsIfUnhappy(c *check.C) {
   955  	// alias exists but args are somehow wonky
   956  	c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil)
   957  	c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil)
   958  
   959  	// weird1 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to COMP_WORDS[0]
   960  	weird1 := mkCompArgs("6", "alias", "")
   961  	weird1[5] = "xxxxx "
   962  	// weird2 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to the first word in COMP_LINE
   963  	weird2 := mkCompArgs("6", "xxxxx", "")
   964  	weird2[5] = "alias "
   965  
   966  	for desc, inArgs := range map[string][]string{
   967  		"nil args":                     nil,
   968  		"too-short args":               {"alias"},
   969  		"COMP_POINT not a number":      mkCompArgs("hello", "alias"),
   970  		"COMP_POINT is inside argv[0]": mkCompArgs("2", "alias", ""),
   971  		"COMP_POINT is outside argv":   mkCompArgs("99", "alias", ""),
   972  		"COMP_WORDS[0] is not argv[0]": mkCompArgs("10", "not-alias", ""),
   973  		"mismatch between argv[0], COMP_LINE and COMP_WORDS, #1": weird1,
   974  		"mismatch between argv[0], COMP_LINE and COMP_WORDS, #2": weird2,
   975  	} {
   976  		// antialias leaves args alone if it's too short
   977  		app, outArgs := snaprun.Antialias("alias", inArgs)
   978  		c.Check(app, check.Equals, "alias", check.Commentf(desc))
   979  		c.Check(outArgs, check.DeepEquals, inArgs, check.Commentf(desc))
   980  	}
   981  }
   982  
   983  func (s *RunSuite) TestSnapRunAppWithStraceIntegration(c *check.C) {
   984  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   985  
   986  	// mock installed snap
   987  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   988  		Revision: snap.R("x2"),
   989  	})
   990  
   991  	// pretend we have sudo and simulate some useful output that would
   992  	// normally come from strace
   993  	sudoCmd := testutil.MockCommand(c, "sudo", fmt.Sprintf(`
   994  echo "stdout output 1"
   995  >&2 echo 'execve("/path/to/snap-confine")'
   996  >&2 echo "snap-confine/snap-exec strace stuff"
   997  >&2 echo "getuid() = 1000"
   998  >&2 echo 'execve("%s/snapName/x2/bin/foo")'
   999  >&2 echo "interessting strace output"
  1000  >&2 echo "and more"
  1001  echo "stdout output 2"
  1002  `, dirs.SnapMountDir))
  1003  	defer sudoCmd.Restore()
  1004  
  1005  	// pretend we have strace
  1006  	straceCmd := testutil.MockCommand(c, "strace", "")
  1007  	defer straceCmd.Restore()
  1008  
  1009  	user, err := user.Current()
  1010  	c.Assert(err, check.IsNil)
  1011  
  1012  	// and run it under strace
  1013  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace", "--", "snapname.app", "--arg1", "arg2"})
  1014  	c.Assert(err, check.IsNil)
  1015  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1016  	c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{
  1017  		{
  1018  			"sudo", "-E",
  1019  			filepath.Join(straceCmd.BinDir(), "strace"),
  1020  			"-u", user.Username,
  1021  			"-f",
  1022  			"-e", strace.ExcludedSyscalls,
  1023  			filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1024  			"snap.snapname.app",
  1025  			filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1026  			"snapname.app", "--arg1", "arg2",
  1027  		},
  1028  	})
  1029  	c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n")
  1030  	c.Check(s.Stderr(), check.Equals, fmt.Sprintf("execve(%q)\ninteressting strace output\nand more\n", filepath.Join(dirs.SnapMountDir, "snapName/x2/bin/foo")))
  1031  
  1032  	s.ResetStdStreams()
  1033  	sudoCmd.ForgetCalls()
  1034  
  1035  	// try again without filtering
  1036  	rest, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace=--raw", "--", "snapname.app", "--arg1", "arg2"})
  1037  	c.Assert(err, check.IsNil)
  1038  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1039  	c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{
  1040  		{
  1041  			"sudo", "-E",
  1042  			filepath.Join(straceCmd.BinDir(), "strace"),
  1043  			"-u", user.Username,
  1044  			"-f",
  1045  			"-e", strace.ExcludedSyscalls,
  1046  			filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1047  			"snap.snapname.app",
  1048  			filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1049  			"snapname.app", "--arg1", "arg2",
  1050  		},
  1051  	})
  1052  	c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n")
  1053  	expectedFullFmt := `execve("/path/to/snap-confine")
  1054  snap-confine/snap-exec strace stuff
  1055  getuid() = 1000
  1056  execve("%s/snapName/x2/bin/foo")
  1057  interessting strace output
  1058  and more
  1059  `
  1060  	c.Check(s.Stderr(), check.Equals, fmt.Sprintf(expectedFullFmt, dirs.SnapMountDir))
  1061  }
  1062  
  1063  func (s *RunSuite) TestSnapRunAppWithStraceOptions(c *check.C) {
  1064  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1065  
  1066  	// mock installed snap
  1067  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1068  		Revision: snap.R("x2"),
  1069  	})
  1070  
  1071  	// pretend we have sudo
  1072  	sudoCmd := testutil.MockCommand(c, "sudo", "")
  1073  	defer sudoCmd.Restore()
  1074  
  1075  	// pretend we have strace
  1076  	straceCmd := testutil.MockCommand(c, "strace", "")
  1077  	defer straceCmd.Restore()
  1078  
  1079  	user, err := user.Current()
  1080  	c.Assert(err, check.IsNil)
  1081  
  1082  	// and run it under strace
  1083  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--strace=-tt --raw -o "file with spaces"`, "--", "snapname.app", "--arg1", "arg2"})
  1084  	c.Assert(err, check.IsNil)
  1085  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1086  	c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{
  1087  		{
  1088  			"sudo", "-E",
  1089  			filepath.Join(straceCmd.BinDir(), "strace"),
  1090  			"-u", user.Username,
  1091  			"-f",
  1092  			"-e", strace.ExcludedSyscalls,
  1093  			"-tt",
  1094  			"-o",
  1095  			"file with spaces",
  1096  			filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1097  			"snap.snapname.app",
  1098  			filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1099  			"snapname.app", "--arg1", "arg2",
  1100  		},
  1101  	})
  1102  }
  1103  
  1104  func (s *RunSuite) TestSnapRunShellIntegration(c *check.C) {
  1105  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1106  
  1107  	// mock installed snap
  1108  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1109  		Revision: snap.R("x2"),
  1110  	})
  1111  
  1112  	// redirect exec
  1113  	execArg0 := ""
  1114  	execArgs := []string{}
  1115  	execEnv := []string{}
  1116  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1117  		execArg0 = arg0
  1118  		execArgs = args
  1119  		execEnv = envv
  1120  		return nil
  1121  	})
  1122  	defer restorer()
  1123  
  1124  	// and run it!
  1125  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--shell", "--", "snapname.app", "--arg1", "arg2"})
  1126  	c.Assert(err, check.IsNil)
  1127  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1128  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
  1129  	c.Check(execArgs, check.DeepEquals, []string{
  1130  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1131  		"snap.snapname.app",
  1132  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1133  		"--command=shell", "snapname.app", "--arg1", "arg2"})
  1134  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1135  }
  1136  
  1137  func (s *RunSuite) TestSnapRunAppTimer(c *check.C) {
  1138  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1139  
  1140  	// mock installed snap
  1141  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1142  		Revision: snap.R("x2"),
  1143  	})
  1144  
  1145  	// redirect exec
  1146  	execArg0 := ""
  1147  	execArgs := []string{}
  1148  	execCalled := false
  1149  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1150  		execArg0 = arg0
  1151  		execArgs = args
  1152  		execCalled = true
  1153  		return nil
  1154  	})
  1155  	defer restorer()
  1156  
  1157  	fakeNow := time.Date(2018, 02, 12, 9, 55, 0, 0, time.Local)
  1158  	restorer = snaprun.MockTimeNow(func() time.Time {
  1159  		// Monday Feb 12, 9:55
  1160  		return fakeNow
  1161  	})
  1162  	defer restorer()
  1163  
  1164  	// pretend we are outside of timer range
  1165  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"})
  1166  	c.Assert(err, check.IsNil)
  1167  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1168  	c.Assert(execCalled, check.Equals, false)
  1169  
  1170  	c.Check(s.Stderr(), check.Equals, fmt.Sprintf(`%s: attempted to run "snapname.app" timer outside of scheduled time "mon,10:00~12:00,,fri,13:00"
  1171  `, fakeNow.Format(time.RFC3339)))
  1172  	s.ResetStdStreams()
  1173  
  1174  	restorer = snaprun.MockTimeNow(func() time.Time {
  1175  		// Monday Feb 12, 10:20
  1176  		return time.Date(2018, 02, 12, 10, 20, 0, 0, time.Local)
  1177  	})
  1178  	defer restorer()
  1179  
  1180  	// and run it under strace
  1181  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"})
  1182  	c.Assert(err, check.IsNil)
  1183  	c.Assert(execCalled, check.Equals, true)
  1184  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
  1185  	c.Check(execArgs, check.DeepEquals, []string{
  1186  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1187  		"snap.snapname.app",
  1188  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1189  		"snapname.app", "--arg1", "arg2"})
  1190  }
  1191  
  1192  func (s *RunSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) {
  1193  	_, r := logger.MockLogger()
  1194  	defer r()
  1195  
  1196  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1197  
  1198  	// mock installed snap
  1199  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1200  		Revision: snap.R("1"),
  1201  	})
  1202  
  1203  	// pretend we have sudo
  1204  	sudoCmd := testutil.MockCommand(c, "sudo", "echo unhappy; exit 12")
  1205  	defer sudoCmd.Restore()
  1206  
  1207  	// pretend we have strace
  1208  	straceCmd := testutil.MockCommand(c, "strace", "")
  1209  	defer straceCmd.Restore()
  1210  
  1211  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--trace-exec", "--", "snapname.app", "--arg1", "arg2"})
  1212  	c.Assert(err, check.ErrorMatches, "exit status 12")
  1213  	c.Assert(rest, check.DeepEquals, []string{"--", "snapname.app", "--arg1", "arg2"})
  1214  	c.Check(s.Stdout(), check.Equals, "unhappy\n")
  1215  	c.Check(s.Stderr(), check.Equals, "")
  1216  }
  1217  
  1218  func (s *RunSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
  1219  	logbuf, restorer := logger.MockLogger()
  1220  	defer restorer()
  1221  
  1222  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1223  
  1224  	// mock installed snap
  1225  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1226  		Revision: snap.R("x2"),
  1227  	})
  1228  
  1229  	// redirect exec
  1230  	execCalled := 0
  1231  	restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error {
  1232  		execCalled++
  1233  		return nil
  1234  	})
  1235  	defer restorer()
  1236  
  1237  	verifyCalls := 0
  1238  	restoreCalls := 0
  1239  	isEnabledCalls := 0
  1240  	enabled := false
  1241  	verify := true
  1242  
  1243  	snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)
  1244  
  1245  	restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) {
  1246  		c.Check(what, check.Equals, snapUserDir)
  1247  		verifyCalls++
  1248  		return verify, nil
  1249  	})
  1250  	defer restorer()
  1251  
  1252  	restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error {
  1253  		c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true})
  1254  		c.Check(what, check.Equals, snapUserDir)
  1255  		restoreCalls++
  1256  		return nil
  1257  	})
  1258  	defer restorer()
  1259  
  1260  	restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) {
  1261  		isEnabledCalls++
  1262  		return enabled, nil
  1263  	})
  1264  	defer restorer()
  1265  
  1266  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1267  	c.Assert(err, check.IsNil)
  1268  	c.Check(execCalled, check.Equals, 1)
  1269  	c.Check(isEnabledCalls, check.Equals, 1)
  1270  	c.Check(verifyCalls, check.Equals, 0)
  1271  	c.Check(restoreCalls, check.Equals, 0)
  1272  
  1273  	// pretend SELinux is on
  1274  	enabled = true
  1275  
  1276  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1277  	c.Assert(err, check.IsNil)
  1278  	c.Check(execCalled, check.Equals, 2)
  1279  	c.Check(isEnabledCalls, check.Equals, 2)
  1280  	c.Check(verifyCalls, check.Equals, 1)
  1281  	c.Check(restoreCalls, check.Equals, 0)
  1282  
  1283  	// pretend the context does not match
  1284  	verify = false
  1285  
  1286  	logbuf.Reset()
  1287  
  1288  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1289  	c.Assert(err, check.IsNil)
  1290  	c.Check(execCalled, check.Equals, 3)
  1291  	c.Check(isEnabledCalls, check.Equals, 3)
  1292  	c.Check(verifyCalls, check.Equals, 2)
  1293  	c.Check(restoreCalls, check.Equals, 1)
  1294  
  1295  	// and we let the user know what we're doing
  1296  	c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("restoring default SELinux context of %s", snapUserDir))
  1297  }
  1298  
  1299  func (s *RunSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) {
  1300  	logbuf, restorer := logger.MockLogger()
  1301  	defer restorer()
  1302  
  1303  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1304  
  1305  	// mock installed snap
  1306  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1307  		Revision: snap.R("x2"),
  1308  	})
  1309  
  1310  	// redirect exec
  1311  	execCalled := 0
  1312  	restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error {
  1313  		execCalled++
  1314  		return nil
  1315  	})
  1316  	defer restorer()
  1317  
  1318  	verifyCalls := 0
  1319  	restoreCalls := 0
  1320  	isEnabledCalls := 0
  1321  	enabledErr := errors.New("enabled failed")
  1322  	verifyErr := errors.New("verify failed")
  1323  	restoreErr := errors.New("restore failed")
  1324  
  1325  	snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)
  1326  
  1327  	restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) {
  1328  		c.Check(what, check.Equals, snapUserDir)
  1329  		verifyCalls++
  1330  		return false, verifyErr
  1331  	})
  1332  	defer restorer()
  1333  
  1334  	restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error {
  1335  		c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true})
  1336  		c.Check(what, check.Equals, snapUserDir)
  1337  		restoreCalls++
  1338  		return restoreErr
  1339  	})
  1340  	defer restorer()
  1341  
  1342  	restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) {
  1343  		isEnabledCalls++
  1344  		return enabledErr == nil, enabledErr
  1345  	})
  1346  	defer restorer()
  1347  
  1348  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1349  	// these errors are only logged, but we still run the snap
  1350  	c.Assert(err, check.IsNil)
  1351  	c.Check(execCalled, check.Equals, 1)
  1352  	c.Check(logbuf.String(), testutil.Contains, "cannot determine SELinux status: enabled failed")
  1353  	c.Check(isEnabledCalls, check.Equals, 1)
  1354  	c.Check(verifyCalls, check.Equals, 0)
  1355  	c.Check(restoreCalls, check.Equals, 0)
  1356  	// pretend selinux is on
  1357  	enabledErr = nil
  1358  
  1359  	logbuf.Reset()
  1360  
  1361  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1362  	c.Assert(err, check.IsNil)
  1363  	c.Check(execCalled, check.Equals, 2)
  1364  	c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("failed to verify SELinux context of %s: verify failed", snapUserDir))
  1365  	c.Check(isEnabledCalls, check.Equals, 2)
  1366  	c.Check(verifyCalls, check.Equals, 1)
  1367  	c.Check(restoreCalls, check.Equals, 0)
  1368  
  1369  	// pretend the context does not match
  1370  	verifyErr = nil
  1371  
  1372  	logbuf.Reset()
  1373  
  1374  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1375  	c.Assert(err, check.IsNil)
  1376  	c.Check(execCalled, check.Equals, 3)
  1377  	c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("cannot restore SELinux context of %s: restore failed", snapUserDir))
  1378  	c.Check(isEnabledCalls, check.Equals, 3)
  1379  	c.Check(verifyCalls, check.Equals, 2)
  1380  	c.Check(restoreCalls, check.Equals, 1)
  1381  }
  1382  
  1383  // systemctl is-system-running returns "running" in normal situations.
  1384  func (s *RunSuite) TestIsStoppingRunning(c *check.C) {
  1385  	systemctl := testutil.MockCommand(c, "systemctl", `
  1386  case "$1" in
  1387  	is-system-running)
  1388  		echo "running"
  1389  		exit 0
  1390  		;;
  1391  esac
  1392  `)
  1393  	defer systemctl.Restore()
  1394  	stop, err := snaprun.IsStopping()
  1395  	c.Check(err, check.IsNil)
  1396  	c.Check(stop, check.Equals, false)
  1397  	c.Check(systemctl.Calls(), check.DeepEquals, [][]string{
  1398  		{"systemctl", "is-system-running"},
  1399  	})
  1400  }
  1401  
  1402  // systemctl is-system-running returns "stopping" when the system is
  1403  // shutting down or rebooting. At the same time it returns a non-zero
  1404  // exit status.
  1405  func (s *RunSuite) TestIsStoppingStopping(c *check.C) {
  1406  	systemctl := testutil.MockCommand(c, "systemctl", `
  1407  case "$1" in
  1408  	is-system-running)
  1409  		echo "stopping"
  1410  		exit 1
  1411  		;;
  1412  esac
  1413  `)
  1414  	defer systemctl.Restore()
  1415  	stop, err := snaprun.IsStopping()
  1416  	c.Check(err, check.IsNil)
  1417  	c.Check(stop, check.Equals, true)
  1418  	c.Check(systemctl.Calls(), check.DeepEquals, [][]string{
  1419  		{"systemctl", "is-system-running"},
  1420  	})
  1421  }
  1422  
  1423  // systemctl is-system-running can often return "degraded"
  1424  // Let's make sure that is not confusing us.
  1425  func (s *RunSuite) TestIsStoppingDegraded(c *check.C) {
  1426  	systemctl := testutil.MockCommand(c, "systemctl", `
  1427  case "$1" in
  1428  	is-system-running)
  1429  		echo "degraded"
  1430  		exit 1
  1431  		;;
  1432  esac
  1433  `)
  1434  	defer systemctl.Restore()
  1435  	stop, err := snaprun.IsStopping()
  1436  	c.Check(err, check.IsNil)
  1437  	c.Check(stop, check.Equals, false)
  1438  	c.Check(systemctl.Calls(), check.DeepEquals, [][]string{
  1439  		{"systemctl", "is-system-running"},
  1440  	})
  1441  }
  1442  
  1443  func (s *RunSuite) TestSnapRunTrackingApps(c *check.C) {
  1444  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1445  	defer restore()
  1446  
  1447  	// mock installed snap
  1448  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1449  		Revision: snap.R("x2"),
  1450  	})
  1451  
  1452  	// pretend to be running from core
  1453  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1454  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1455  	})
  1456  	defer restore()
  1457  
  1458  	created := false
  1459  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1460  		c.Assert(securityTag, check.Equals, "snap.snapname.app")
  1461  		c.Assert(opts, check.NotNil)
  1462  		c.Assert(opts.AllowSessionBus, check.Equals, true)
  1463  		created = true
  1464  		return nil
  1465  	})
  1466  	defer restore()
  1467  
  1468  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1469  		panic("apps need to create a scope and do not use systemd service tracking")
  1470  	})
  1471  	defer restore()
  1472  
  1473  	// redirect exec
  1474  	execArg0 := ""
  1475  	execArgs := []string{}
  1476  	execEnv := []string{}
  1477  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1478  		execArg0 = arg0
  1479  		execArgs = args
  1480  		execEnv = envv
  1481  		return nil
  1482  	})
  1483  	defer restore()
  1484  
  1485  	// and run it!
  1486  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
  1487  	c.Assert(err, check.IsNil)
  1488  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1489  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1490  	c.Check(execArgs, check.DeepEquals, []string{
  1491  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1492  		"snap.snapname.app",
  1493  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1494  		"snapname.app", "--arg1", "arg2"})
  1495  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1496  	c.Assert(created, check.Equals, true)
  1497  }
  1498  
  1499  func (s *RunSuite) TestSnapRunTrackingHooks(c *check.C) {
  1500  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1501  	defer restore()
  1502  
  1503  	// mock installed snap
  1504  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1505  		Revision: snap.R("x2"),
  1506  	})
  1507  
  1508  	// pretend to be running from core
  1509  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1510  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1511  	})
  1512  	defer restore()
  1513  
  1514  	created := false
  1515  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1516  		c.Assert(securityTag, check.Equals, "snap.snapname.hook.configure")
  1517  		c.Assert(opts, check.NotNil)
  1518  		c.Assert(opts.AllowSessionBus, check.Equals, false)
  1519  		created = true
  1520  		return nil
  1521  	})
  1522  	defer restore()
  1523  
  1524  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1525  		panic("hooks need to create a scope and do not use systemd service tracking")
  1526  	})
  1527  	defer restore()
  1528  
  1529  	// redirect exec
  1530  	execArg0 := ""
  1531  	execArgs := []string{}
  1532  	execEnv := []string{}
  1533  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1534  		execArg0 = arg0
  1535  		execArgs = args
  1536  		execEnv = envv
  1537  		return nil
  1538  	})
  1539  	defer restore()
  1540  
  1541  	// and run it!
  1542  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook", "configure", "-r", "x2", "snapname"})
  1543  	c.Assert(err, check.IsNil)
  1544  	c.Assert(rest, check.DeepEquals, []string{"snapname"})
  1545  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1546  	c.Check(execArgs, check.DeepEquals, []string{
  1547  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1548  		"snap.snapname.hook.configure",
  1549  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1550  		"--hook=configure", "snapname"})
  1551  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1552  	c.Assert(created, check.Equals, true)
  1553  }
  1554  
  1555  func (s *RunSuite) TestSnapRunTrackingServices(c *check.C) {
  1556  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1557  	defer restore()
  1558  
  1559  	// mock installed snap
  1560  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1561  		Revision: snap.R("x2"),
  1562  	})
  1563  
  1564  	// pretend to be running from core
  1565  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1566  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1567  	})
  1568  	defer restore()
  1569  
  1570  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1571  		panic("services rely on systemd tracking, should not have created a transient scope")
  1572  	})
  1573  	defer restore()
  1574  
  1575  	confirmed := false
  1576  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1577  		confirmed = true
  1578  		c.Assert(securityTag, check.Equals, "snap.snapname.svc")
  1579  		return nil
  1580  	})
  1581  	defer restore()
  1582  
  1583  	// redirect exec
  1584  	execArg0 := ""
  1585  	execArgs := []string{}
  1586  	execEnv := []string{}
  1587  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1588  		execArg0 = arg0
  1589  		execArgs = args
  1590  		execEnv = envv
  1591  		return nil
  1592  	})
  1593  	defer restore()
  1594  
  1595  	// and run it!
  1596  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc", "--arg1", "arg2"})
  1597  	c.Assert(err, check.IsNil)
  1598  	c.Assert(rest, check.DeepEquals, []string{"snapname.svc", "--arg1", "arg2"})
  1599  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1600  	c.Check(execArgs, check.DeepEquals, []string{
  1601  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1602  		"snap.snapname.svc",
  1603  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1604  		"snapname.svc", "--arg1", "arg2"})
  1605  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1606  	c.Assert(confirmed, check.Equals, true)
  1607  }
  1608  
  1609  func (s *RunSuite) TestSnapRunTrackingServicesWhenRunByUser(c *check.C) {
  1610  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1611  	defer restore()
  1612  
  1613  	// mock installed snap
  1614  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1615  		Revision: snap.R("x2"),
  1616  	})
  1617  
  1618  	// pretend to be running from core
  1619  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1620  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1621  	})
  1622  	defer restore()
  1623  
  1624  	var createTransientScopeOpts *cgroup.TrackingOptions
  1625  	var createTransientScopeCalls int
  1626  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1627  		createTransientScopeCalls++
  1628  		createTransientScopeOpts = opts
  1629  		return nil
  1630  	})
  1631  	defer restore()
  1632  
  1633  	confirmCalls := 0
  1634  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1635  		confirmCalls++
  1636  		c.Assert(securityTag, check.Equals, "snap.snapname.svc")
  1637  		return cgroup.ErrCannotTrackProcess
  1638  	})
  1639  	defer restore()
  1640  
  1641  	// redirect exec
  1642  	execArg0 := ""
  1643  	execArgs := []string{}
  1644  	execEnv := []string{}
  1645  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1646  		execArg0 = arg0
  1647  		execArgs = args
  1648  		execEnv = envv
  1649  		return nil
  1650  	})
  1651  	defer restore()
  1652  
  1653  	// invoked as: snap run -- snapname.svc --arg1 arg2
  1654  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc", "--arg1", "arg2"})
  1655  	c.Assert(err, check.IsNil)
  1656  	c.Assert(rest, check.DeepEquals, []string{"snapname.svc", "--arg1", "arg2"})
  1657  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1658  	c.Check(execArgs, check.DeepEquals, []string{
  1659  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1660  		"snap.snapname.svc",
  1661  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1662  		"snapname.svc", "--arg1", "arg2"})
  1663  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1664  	c.Assert(confirmCalls, check.Equals, 1)
  1665  	c.Assert(createTransientScopeCalls, check.Equals, 1)
  1666  	c.Assert(createTransientScopeOpts, check.DeepEquals, &cgroup.TrackingOptions{
  1667  		AllowSessionBus: true,
  1668  	})
  1669  }
  1670  
  1671  func (s *RunSuite) TestSnapRunTrackingFailure(c *check.C) {
  1672  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1673  	defer restore()
  1674  
  1675  	// mock installed snap
  1676  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1677  		Revision: snap.R("x2"),
  1678  	})
  1679  
  1680  	// pretend to be running from core
  1681  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1682  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1683  	})
  1684  	defer restore()
  1685  
  1686  	created := false
  1687  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1688  		c.Assert(securityTag, check.Equals, "snap.snapname.app")
  1689  		c.Assert(opts, check.NotNil)
  1690  		c.Assert(opts.AllowSessionBus, check.Equals, true)
  1691  		created = true
  1692  		// Pretend that the tracking system was unable to track this application.
  1693  		return cgroup.ErrCannotTrackProcess
  1694  	})
  1695  	defer restore()
  1696  
  1697  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1698  		panic("apps need to create a scope and do not use systemd service tracking")
  1699  	})
  1700  	defer restore()
  1701  
  1702  	// redirect exec
  1703  	execArg0 := ""
  1704  	execArgs := []string{}
  1705  	execEnv := []string{}
  1706  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1707  		execArg0 = arg0
  1708  		execArgs = args
  1709  		execEnv = envv
  1710  		return nil
  1711  	})
  1712  	defer restore()
  1713  
  1714  	// Capture the debug log that is printed by this test.
  1715  	os.Setenv("SNAPD_DEBUG", "1")
  1716  	defer os.Unsetenv("SNAPD_DEBUG")
  1717  	logbuf, restore := logger.MockLogger()
  1718  	defer restore()
  1719  
  1720  	// and run it!
  1721  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
  1722  	c.Assert(err, check.IsNil)
  1723  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1724  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1725  	c.Check(execArgs, check.DeepEquals, []string{
  1726  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1727  		"snap.snapname.app",
  1728  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1729  		"snapname.app", "--arg1", "arg2"})
  1730  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1731  	c.Assert(created, check.Equals, true)
  1732  
  1733  	// Ensure that the debug message is printed.
  1734  	c.Assert(logbuf.String(), testutil.Contains, "snapd cannot track the started application\n")
  1735  }
  1736  
  1737  var mockKernelYaml = []byte(`name: pc-kernel
  1738  type: kernel
  1739  version: 1.0
  1740  hooks:
  1741   fde-setup:
  1742  `)
  1743  
  1744  func (s *RunSuite) TestSnapRunHookKernelImplicitBase(c *check.C) {
  1745  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1746  
  1747  	nModel := 0
  1748  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
  1749  		switch r.URL.Path {
  1750  		case "/v2/model":
  1751  			switch nModel {
  1752  			case 0:
  1753  				c.Check(r.Method, check.Equals, "GET")
  1754  				c.Check(r.URL.RawQuery, check.Equals, "")
  1755  				fmt.Fprint(w, happyUC20ModelAssertionResponse)
  1756  			default:
  1757  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1)
  1758  			}
  1759  			nModel++
  1760  		}
  1761  	})
  1762  
  1763  	// mock installed kernel
  1764  	snaptest.MockSnapCurrent(c, string(mockKernelYaml), &snap.SideInfo{
  1765  		Revision: snap.R(42),
  1766  	})
  1767  
  1768  	// redirect exec
  1769  	execArg0 := ""
  1770  	execArgs := []string{}
  1771  	execEnv := []string{}
  1772  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1773  		execArg0 = arg0
  1774  		execArgs = args
  1775  		execEnv = envv
  1776  		return nil
  1777  	})
  1778  	defer restorer()
  1779  
  1780  	// Run a hook from the active revision
  1781  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=fde-setup", "--", "pc-kernel"})
  1782  	c.Assert(err, check.IsNil)
  1783  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
  1784  	c.Check(execArgs, check.DeepEquals, []string{
  1785  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1786  		"--base", "core20",
  1787  		"snap.pc-kernel.hook.fde-setup",
  1788  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1789  		"--hook=fde-setup", "pc-kernel"})
  1790  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
  1791  	c.Check(nModel, check.Equals, 1)
  1792  }
  1793  
  1794  func (s *RunSuite) TestRunGdbserverNoGdbserver(c *check.C) {
  1795  	oldPath := os.Getenv("PATH")
  1796  	os.Setenv("PATH", "/no-path:/really-not")
  1797  	defer os.Setenv("PATH", oldPath)
  1798  
  1799  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1800  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1801  		Revision: snap.R("x2"),
  1802  	})
  1803  
  1804  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--gdbserver", "snapname.app"})
  1805  	c.Assert(err, check.ErrorMatches, "please install gdbserver on your system")
  1806  }
  1807  
  1808  func (s *RunSuite) TestWaitInhibitUnlock(c *check.C) {
  1809  	var called int
  1810  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1811  		called++
  1812  		if called < 5 {
  1813  			return runinhibit.HintInhibitedForRefresh, nil
  1814  		}
  1815  		return runinhibit.HintNotInhibited, nil
  1816  	})
  1817  	defer restore()
  1818  
  1819  	notInhibited, err := snaprun.WaitInhibitUnlock("some-snap", runinhibit.HintNotInhibited)
  1820  	c.Assert(err, check.IsNil)
  1821  	c.Check(notInhibited, check.Equals, true)
  1822  	c.Check(called, check.Equals, 5)
  1823  }
  1824  
  1825  func (s *RunSuite) TestWaitInhibitUnlockWaitsForSpecificHint(c *check.C) {
  1826  	var called int
  1827  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1828  		called++
  1829  		if called < 5 {
  1830  			return runinhibit.HintInhibitedGateRefresh, nil
  1831  		}
  1832  		return runinhibit.HintInhibitedForRefresh, nil
  1833  	})
  1834  	defer restore()
  1835  
  1836  	notInhibited, err := snaprun.WaitInhibitUnlock("some-snap", runinhibit.HintInhibitedForRefresh)
  1837  	c.Assert(err, check.IsNil)
  1838  	c.Check(notInhibited, check.Equals, false)
  1839  	c.Check(called, check.Equals, 5)
  1840  }
  1841  
  1842  func (s *RunSuite) TestWaitWhileInhibitedNoop(c *check.C) {
  1843  	var called int
  1844  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1845  		called++
  1846  		if called < 2 {
  1847  			return runinhibit.HintInhibitedGateRefresh, nil
  1848  		}
  1849  		return runinhibit.HintNotInhibited, nil
  1850  	})
  1851  	defer restore()
  1852  
  1853  	meter := &progresstest.Meter{}
  1854  	defer progress.MockMeter(meter)()
  1855  
  1856  	c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedGateRefresh), check.IsNil)
  1857  	c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.IsNil)
  1858  	c.Check(called, check.Equals, 2)
  1859  
  1860  	c.Check(meter.Values, check.HasLen, 0)
  1861  	c.Check(meter.Written, check.HasLen, 0)
  1862  	c.Check(meter.Finishes, check.Equals, 0)
  1863  	c.Check(meter.Labels, check.HasLen, 0)
  1864  	c.Check(meter.Labels, check.HasLen, 0)
  1865  }
  1866  
  1867  func (s *RunSuite) TestWaitWhileInhibitedTextFlow(c *check.C) {
  1868  	var called int
  1869  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1870  		called++
  1871  		if called < 2 {
  1872  			return runinhibit.HintInhibitedForRefresh, nil
  1873  		}
  1874  		return runinhibit.HintNotInhibited, nil
  1875  	})
  1876  	defer restore()
  1877  
  1878  	meter := &progresstest.Meter{}
  1879  	defer progress.MockMeter(meter)()
  1880  
  1881  	c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedGateRefresh), check.IsNil)
  1882  	c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.IsNil)
  1883  	c.Check(called, check.Equals, 2)
  1884  
  1885  	c.Check(s.Stdout(), check.Equals, "snap package cannot be used now: gate-refresh\n")
  1886  	c.Check(meter.Values, check.HasLen, 0)
  1887  	c.Check(meter.Written, check.HasLen, 0)
  1888  	c.Check(meter.Finishes, check.Equals, 1)
  1889  	c.Check(meter.Labels, check.DeepEquals, []string{"please wait..."})
  1890  }
  1891  
  1892  func (s *RunSuite) TestWaitWhileInhibitedGraphicalSessionFlow(c *check.C) {
  1893  	restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(true)
  1894  	defer restoreIsGraphicalSession()
  1895  
  1896  	var notification *usersessionclient.PendingSnapRefreshInfo
  1897  	restorePendingRefreshNotification := snaprun.MockPendingRefreshNotification(func(refreshInfo *usersessionclient.PendingSnapRefreshInfo) error {
  1898  		notification = refreshInfo
  1899  		return nil
  1900  	})
  1901  	defer restorePendingRefreshNotification()
  1902  
  1903  	var finishNotification *usersessionclient.FinishedSnapRefreshInfo
  1904  	restoreFinishRefreshNotification := snaprun.MockFinishRefreshNotification(func(refreshInfo *usersessionclient.FinishedSnapRefreshInfo) error {
  1905  		finishNotification = refreshInfo
  1906  		return nil
  1907  	})
  1908  	defer restoreFinishRefreshNotification()
  1909  
  1910  	var called int
  1911  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1912  		called++
  1913  		if called < 2 {
  1914  			return runinhibit.HintInhibitedForRefresh, nil
  1915  		}
  1916  		return runinhibit.HintNotInhibited, nil
  1917  	})
  1918  	defer restore()
  1919  
  1920  	c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedForRefresh), check.IsNil)
  1921  	c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.IsNil)
  1922  	c.Check(called, check.Equals, 2)
  1923  
  1924  	c.Check(s.Stdout(), check.Equals, "")
  1925  	c.Check(notification, check.DeepEquals, &usersessionclient.PendingSnapRefreshInfo{
  1926  		InstanceName:  "some-snap",
  1927  		TimeRemaining: 0,
  1928  	})
  1929  	c.Check(finishNotification, check.DeepEquals, &usersessionclient.FinishedSnapRefreshInfo{
  1930  		InstanceName: "some-snap",
  1931  	})
  1932  }
  1933  
  1934  func (s *RunSuite) TestWaitWhileInhibitedGraphicalSessionFlowError(c *check.C) {
  1935  	restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(true)
  1936  	defer restoreIsGraphicalSession()
  1937  
  1938  	restorePendingRefreshNotification := snaprun.MockPendingRefreshNotification(func(refreshInfo *usersessionclient.PendingSnapRefreshInfo) error {
  1939  		return fmt.Errorf("boom")
  1940  	})
  1941  	defer restorePendingRefreshNotification()
  1942  
  1943  	c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedForRefresh), check.IsNil)
  1944  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1945  		return runinhibit.HintInhibitedForRefresh, nil
  1946  	})
  1947  	defer restore()
  1948  
  1949  	c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.ErrorMatches, "boom")
  1950  }
  1951  
  1952  func (s *RunSuite) TestWaitWhileInhibitedGraphicalSessionFlowErrorOnFinish(c *check.C) {
  1953  	restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(true)
  1954  	defer restoreIsGraphicalSession()
  1955  
  1956  	restorePendingRefreshNotification := snaprun.MockPendingRefreshNotification(func(refreshInfo *usersessionclient.PendingSnapRefreshInfo) error {
  1957  		return nil
  1958  	})
  1959  	defer restorePendingRefreshNotification()
  1960  
  1961  	restoreFinishRefreshNotification := snaprun.MockFinishRefreshNotification(func(refreshInfo *usersessionclient.FinishedSnapRefreshInfo) error {
  1962  		return fmt.Errorf("boom")
  1963  	})
  1964  	defer restoreFinishRefreshNotification()
  1965  
  1966  	c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedForRefresh), check.IsNil)
  1967  	n := 0
  1968  	restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) {
  1969  		n++
  1970  		if n == 1 {
  1971  			return runinhibit.HintInhibitedForRefresh, nil
  1972  		}
  1973  		return runinhibit.HintNotInhibited, nil
  1974  	})
  1975  	defer restore()
  1976  
  1977  	c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.ErrorMatches, "boom")
  1978  }
  1979  
  1980  func (s *RunSuite) TestCreateSnapDirPermissions(c *check.C) {
  1981  	usr, err := user.Current()
  1982  	c.Assert(err, check.IsNil)
  1983  
  1984  	usr.HomeDir = s.fakeHome
  1985  	snaprun.MockUserCurrent(func() (*user.User, error) {
  1986  		return usr, nil
  1987  	})
  1988  
  1989  	info := &snap.Info{SuggestedName: "some-snap"}
  1990  	c.Assert(snaprun.CreateUserDataDirs(info, nil), check.IsNil)
  1991  
  1992  	fi, err := os.Stat(filepath.Join(s.fakeHome, dirs.UserHomeSnapDir))
  1993  	c.Assert(err, check.IsNil)
  1994  	c.Assert(fi.Mode()&os.ModePerm, check.Equals, os.FileMode(0700))
  1995  }
  1996  
  1997  func (s *RunSuite) TestGetSnapDirOptions(c *check.C) {
  1998  	root := c.MkDir()
  1999  	dirs.SnapSeqDir = root
  2000  	dirs.FeaturesDir = root
  2001  
  2002  	// write sequence file
  2003  	seqFile := filepath.Join(dirs.SnapSeqDir, "somesnap.json")
  2004  	str := struct {
  2005  		MigratedHidden        bool `json:"migrated-hidden"`
  2006  		MigratedToExposedHome bool `json:"migrated-exposed-home"`
  2007  	}{
  2008  		MigratedHidden:        true,
  2009  		MigratedToExposedHome: true,
  2010  	}
  2011  	data, err := json.Marshal(&str)
  2012  	c.Assert(err, check.IsNil)
  2013  	c.Assert(ioutil.WriteFile(seqFile, data, 0660), check.IsNil)
  2014  
  2015  	// write control file for hidden dir feature
  2016  	c.Assert(ioutil.WriteFile(features.HiddenSnapDataHomeDir.ControlFile(), []byte{}, 0660), check.IsNil)
  2017  
  2018  	opts, err := snaprun.GetSnapDirOptions("somesnap")
  2019  	c.Assert(err, check.IsNil)
  2020  	c.Assert(opts, check.DeepEquals, &dirs.SnapDirOptions{HiddenSnapDataDir: true, MigratedToExposedHome: true})
  2021  }
  2022  
  2023  func (s *RunSuite) TestRunDebugLog(c *check.C) {
  2024  	oldDebug, isSet := os.LookupEnv("SNAPD_DEBUG")
  2025  	if isSet {
  2026  		defer os.Setenv("SNAPD_DEBUG", oldDebug)
  2027  	} else {
  2028  		defer os.Unsetenv("SNAPD_DEBUG")
  2029  	}
  2030  
  2031  	logBuf, r := logger.MockLogger()
  2032  	defer r()
  2033  
  2034  	restore := mockSnapConfine(dirs.DistroLibExecDir)
  2035  	defer restore()
  2036  	execArg0 := ""
  2037  	execArgs := []string{}
  2038  	execEnv := []string{}
  2039  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  2040  		execArg0 = arg0
  2041  		execArgs = args
  2042  		execEnv = envv
  2043  		return nil
  2044  	})
  2045  	defer restore()
  2046  
  2047  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  2048  		Revision: snap.R("12"),
  2049  	})
  2050  
  2051  	// this will modify the current process environment
  2052  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--debug-log", "snapname.app"})
  2053  	c.Assert(err, check.IsNil)
  2054  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
  2055  	c.Check(execArgs, check.DeepEquals, []string{
  2056  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  2057  		"snap.snapname.app",
  2058  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  2059  		"snapname.app"})
  2060  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=12")
  2061  	c.Check(execEnv, testutil.Contains, "SNAPD_DEBUG=1")
  2062  	// also set in env
  2063  	c.Check(os.Getenv("SNAPD_DEBUG"), check.Equals, "1")
  2064  	// and we've let the user know that logging was enabled
  2065  	c.Check(logBuf.String(), testutil.Contains, "DEBUG: enabled debug logging of early snap startup")
  2066  }