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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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  	"errors"
    24  	"fmt"
    25  	"net/http"
    26  	"os"
    27  	"os/user"
    28  	"path/filepath"
    29  	"strings"
    30  	"time"
    31  
    32  	"gopkg.in/check.v1"
    33  
    34  	snaprun "github.com/snapcore/snapd/cmd/snap"
    35  	"github.com/snapcore/snapd/dirs"
    36  	"github.com/snapcore/snapd/logger"
    37  	"github.com/snapcore/snapd/osutil"
    38  	"github.com/snapcore/snapd/sandbox/cgroup"
    39  	"github.com/snapcore/snapd/sandbox/selinux"
    40  	"github.com/snapcore/snapd/snap"
    41  	"github.com/snapcore/snapd/snap/snaptest"
    42  	"github.com/snapcore/snapd/testutil"
    43  	"github.com/snapcore/snapd/x11"
    44  )
    45  
    46  var mockYaml = []byte(`name: snapname
    47  version: 1.0
    48  apps:
    49   app:
    50    command: run-app
    51   svc:
    52    command: run-svc
    53    daemon: simple
    54  hooks:
    55   configure:
    56  `)
    57  
    58  var mockYamlBaseNone1 = []byte(`name: snapname1
    59  version: 1.0
    60  base: none
    61  apps:
    62   app:
    63    command: run-app
    64  `)
    65  
    66  var mockYamlBaseNone2 = []byte(`name: snapname2
    67  version: 1.0
    68  base: none
    69  hooks:
    70   configure:
    71  `)
    72  
    73  type RunSuite struct {
    74  	fakeHome string
    75  	BaseSnapSuite
    76  }
    77  
    78  var _ = check.Suite(&RunSuite{})
    79  
    80  func (s *RunSuite) SetUpTest(c *check.C) {
    81  	s.BaseSnapSuite.SetUpTest(c)
    82  	s.fakeHome = c.MkDir()
    83  
    84  	u, err := user.Current()
    85  	c.Assert(err, check.IsNil)
    86  	s.AddCleanup(snaprun.MockUserCurrent(func() (*user.User, error) {
    87  		return &user.User{Uid: u.Uid, HomeDir: s.fakeHome}, nil
    88  	}))
    89  	s.AddCleanup(snaprun.MockCreateTransientScopeForTracking(func(string, *cgroup.TrackingOptions) error {
    90  		return nil
    91  	}))
    92  }
    93  
    94  func (s *RunSuite) TestInvalidParameters(c *check.C) {
    95  	invalidParameters := []string{"run", "--hook=configure", "--command=command-name", "--", "snap-name"}
    96  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
    97  	c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
    98  
    99  	invalidParameters = []string{"run", "--hook=configure", "--timer=10:00-12:00", "--", "snap-name"}
   100  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   101  	c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
   102  
   103  	invalidParameters = []string{"run", "--command=command-name", "--timer=10:00-12:00", "--", "snap-name"}
   104  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   105  	c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
   106  
   107  	invalidParameters = []string{"run", "-r=1", "--command=command-name", "--", "snap-name"}
   108  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   109  	c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*")
   110  
   111  	invalidParameters = []string{"run", "-r=1", "--", "snap-name"}
   112  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   113  	c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*")
   114  
   115  	invalidParameters = []string{"run", "--hook=configure", "--", "foo", "bar", "snap-name"}
   116  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
   117  	c.Check(err, check.ErrorMatches, ".*too many arguments for hook \"configure\": bar.*")
   118  }
   119  
   120  func (s *RunSuite) TestRunCmdWithBaseNone(c *check.C) {
   121  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   122  
   123  	// mock installed snap
   124  	snaptest.MockSnapCurrent(c, string(mockYamlBaseNone1), &snap.SideInfo{
   125  		Revision: snap.R("1"),
   126  	})
   127  	snaptest.MockSnapCurrent(c, string(mockYamlBaseNone2), &snap.SideInfo{
   128  		Revision: snap.R("1"),
   129  	})
   130  
   131  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname1.app", "--arg1", "arg2"})
   132  	c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`)
   133  
   134  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname2"})
   135  	c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`)
   136  }
   137  
   138  func (s *RunSuite) TestSnapRunWhenMissingConfine(c *check.C) {
   139  	_, r := logger.MockLogger()
   140  	defer r()
   141  
   142  	// mock installed snap
   143  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   144  		Revision: snap.R("x2"),
   145  	})
   146  
   147  	// redirect exec
   148  	var execs [][]string
   149  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   150  		execs = append(execs, args)
   151  		return nil
   152  	})
   153  	defer restorer()
   154  
   155  	// and run it!
   156  	// a regular run will fail
   157  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   158  	c.Assert(err, check.ErrorMatches, `.* your core/snapd package`)
   159  	// a hook run will not fail
   160  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"})
   161  	c.Assert(err, check.IsNil)
   162  
   163  	// but nothing is run ever
   164  	c.Check(execs, check.IsNil)
   165  }
   166  
   167  func (s *RunSuite) TestSnapRunAppIntegration(c *check.C) {
   168  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   169  
   170  	tmpdir := os.Getenv("TMPDIR")
   171  	if tmpdir == "" {
   172  		tmpdir = "/var/tmp"
   173  		os.Setenv("TMPDIR", tmpdir)
   174  		defer os.Unsetenv("TMPDIR")
   175  	}
   176  
   177  	// mock installed snap
   178  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   179  		Revision: snap.R("x2"),
   180  	})
   181  
   182  	// redirect exec
   183  	execArg0 := ""
   184  	execArgs := []string{}
   185  	execEnv := []string{}
   186  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   187  		execArg0 = arg0
   188  		execArgs = args
   189  		execEnv = envv
   190  		return nil
   191  	})
   192  	defer restorer()
   193  
   194  	// and run it!
   195  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   196  	c.Assert(err, check.IsNil)
   197  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   198  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   199  	c.Check(execArgs, check.DeepEquals, []string{
   200  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   201  		"snap.snapname.app",
   202  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   203  		"snapname.app", "--arg1", "arg2"})
   204  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   205  	c.Check(execEnv, testutil.Contains, fmt.Sprintf("TMPDIR=%s", tmpdir))
   206  }
   207  
   208  func (s *RunSuite) TestSnapRunClassicAppIntegration(c *check.C) {
   209  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   210  
   211  	tmpdir := os.Getenv("TMPDIR")
   212  	if tmpdir == "" {
   213  		tmpdir = "/var/tmp"
   214  		os.Setenv("TMPDIR", tmpdir)
   215  		defer os.Unsetenv("TMPDIR")
   216  	}
   217  
   218  	// mock installed snap
   219  	snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{
   220  		Revision: snap.R("x2"),
   221  	})
   222  
   223  	// redirect exec
   224  	execArg0 := ""
   225  	execArgs := []string{}
   226  	execEnv := []string{}
   227  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   228  		execArg0 = arg0
   229  		execArgs = args
   230  		execEnv = envv
   231  		return nil
   232  	})
   233  	defer restorer()
   234  
   235  	// and run it!
   236  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   237  	c.Assert(err, check.IsNil)
   238  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   239  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   240  	c.Check(execArgs, check.DeepEquals, []string{
   241  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"), "--classic",
   242  		"snap.snapname.app",
   243  		filepath.Join(dirs.DistroLibExecDir, "snap-exec"),
   244  		"snapname.app", "--arg1", "arg2"})
   245  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   246  	c.Check(execEnv, testutil.Contains, fmt.Sprintf("SNAP_SAVED_TMPDIR=%s", tmpdir))
   247  }
   248  
   249  func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromCore(c *check.C) {
   250  	mountedCorePath := filepath.Join(dirs.SnapMountDir, "core/current")
   251  	mountedCoreLibExecPath := filepath.Join(mountedCorePath, dirs.CoreLibExecDir)
   252  
   253  	defer mockSnapConfine(mountedCoreLibExecPath)()
   254  
   255  	// mock installed snap
   256  	snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{
   257  		Revision: snap.R("x2"),
   258  	})
   259  
   260  	restore := snaprun.MockOsReadlink(func(name string) (string, error) {
   261  		// pretend 'snap' is reexeced from 'core'
   262  		return filepath.Join(mountedCorePath, "usr/bin/snap"), nil
   263  	})
   264  	defer restore()
   265  
   266  	execArgs := []string{}
   267  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   268  		execArgs = args
   269  		return nil
   270  	})
   271  	defer restorer()
   272  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   273  	c.Assert(err, check.IsNil)
   274  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   275  	c.Check(execArgs, check.DeepEquals, []string{
   276  		filepath.Join(mountedCoreLibExecPath, "snap-confine"), "--classic",
   277  		"snap.snapname.app",
   278  		filepath.Join(mountedCoreLibExecPath, "snap-exec"),
   279  		"snapname.app", "--arg1", "arg2"})
   280  }
   281  
   282  func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromSnapd(c *check.C) {
   283  	mountedSnapdPath := filepath.Join(dirs.SnapMountDir, "snapd/current")
   284  	mountedSnapdLibExecPath := filepath.Join(mountedSnapdPath, dirs.CoreLibExecDir)
   285  
   286  	defer mockSnapConfine(mountedSnapdLibExecPath)()
   287  
   288  	// mock installed snap
   289  	snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{
   290  		Revision: snap.R("x2"),
   291  	})
   292  
   293  	restore := snaprun.MockOsReadlink(func(name string) (string, error) {
   294  		// pretend 'snap' is reexeced from 'core'
   295  		return filepath.Join(mountedSnapdPath, "usr/bin/snap"), nil
   296  	})
   297  	defer restore()
   298  
   299  	execArgs := []string{}
   300  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   301  		execArgs = args
   302  		return nil
   303  	})
   304  	defer restorer()
   305  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   306  	c.Assert(err, check.IsNil)
   307  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   308  	c.Check(execArgs, check.DeepEquals, []string{
   309  		filepath.Join(mountedSnapdLibExecPath, "snap-confine"), "--classic",
   310  		"snap.snapname.app",
   311  		filepath.Join(mountedSnapdLibExecPath, "snap-exec"),
   312  		"snapname.app", "--arg1", "arg2"})
   313  }
   314  
   315  func (s *RunSuite) TestSnapRunAppWithCommandIntegration(c *check.C) {
   316  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   317  
   318  	// mock installed snap
   319  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   320  		Revision: snap.R(42),
   321  	})
   322  
   323  	// redirect exec
   324  	execArg0 := ""
   325  	execArgs := []string{}
   326  	execEnv := []string{}
   327  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   328  		execArg0 = arg0
   329  		execArgs = args
   330  		execEnv = envv
   331  		return nil
   332  	})
   333  	defer restorer()
   334  
   335  	// and run it!
   336  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=my-command", "--", "snapname.app", "arg1", "arg2"})
   337  	c.Assert(err, check.IsNil)
   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"),
   341  		"snap.snapname.app",
   342  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   343  		"--command=my-command", "snapname.app", "arg1", "arg2"})
   344  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   345  }
   346  
   347  func (s *RunSuite) TestSnapRunCreateDataDirs(c *check.C) {
   348  	info, err := snap.InfoFromSnapYaml(mockYaml)
   349  	c.Assert(err, check.IsNil)
   350  	info.SideInfo.Revision = snap.R(42)
   351  
   352  	err = snaprun.CreateUserDataDirs(info)
   353  	c.Assert(err, check.IsNil)
   354  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname/42")), check.Equals, true)
   355  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname/common")), check.Equals, true)
   356  }
   357  
   358  func (s *RunSuite) TestParallelInstanceSnapRunCreateDataDirs(c *check.C) {
   359  	info, err := snap.InfoFromSnapYaml(mockYaml)
   360  	c.Assert(err, check.IsNil)
   361  	info.SideInfo.Revision = snap.R(42)
   362  	info.InstanceKey = "foo"
   363  
   364  	err = snaprun.CreateUserDataDirs(info)
   365  	c.Assert(err, check.IsNil)
   366  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/42")), check.Equals, true)
   367  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/common")), check.Equals, true)
   368  	// mount point for snap instance mapping has been created
   369  	c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname")), check.Equals, true)
   370  	// and it's empty inside
   371  	m, err := filepath.Glob(filepath.Join(s.fakeHome, "/snap/snapname/*"))
   372  	c.Assert(err, check.IsNil)
   373  	c.Assert(m, check.HasLen, 0)
   374  }
   375  
   376  func (s *RunSuite) TestSnapRunHookIntegration(c *check.C) {
   377  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   378  
   379  	// mock installed snap
   380  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   381  		Revision: snap.R(42),
   382  	})
   383  
   384  	// redirect exec
   385  	execArg0 := ""
   386  	execArgs := []string{}
   387  	execEnv := []string{}
   388  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   389  		execArg0 = arg0
   390  		execArgs = args
   391  		execEnv = envv
   392  		return nil
   393  	})
   394  	defer restorer()
   395  
   396  	// Run a hook from the active revision
   397  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"})
   398  	c.Assert(err, check.IsNil)
   399  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   400  	c.Check(execArgs, check.DeepEquals, []string{
   401  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   402  		"snap.snapname.hook.configure",
   403  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   404  		"--hook=configure", "snapname"})
   405  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   406  }
   407  
   408  func (s *RunSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) {
   409  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   410  
   411  	// mock installed snap
   412  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   413  		Revision: snap.R(42),
   414  	})
   415  
   416  	// redirect exec
   417  	execArg0 := ""
   418  	execArgs := []string{}
   419  	execEnv := []string{}
   420  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   421  		execArg0 = arg0
   422  		execArgs = args
   423  		execEnv = envv
   424  		return nil
   425  	})
   426  	defer restorer()
   427  
   428  	// Specifically pass "unset" which would use the active version.
   429  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=unset", "--", "snapname"})
   430  	c.Assert(err, check.IsNil)
   431  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   432  	c.Check(execArgs, check.DeepEquals, []string{
   433  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   434  		"snap.snapname.hook.configure",
   435  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   436  		"--hook=configure", "snapname"})
   437  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   438  }
   439  
   440  func (s *RunSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) {
   441  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   442  
   443  	// mock installed snap
   444  	// Create both revisions 41 and 42
   445  	snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{
   446  		Revision: snap.R(41),
   447  	})
   448  	snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{
   449  		Revision: snap.R(42),
   450  	})
   451  
   452  	// redirect exec
   453  	execArg0 := ""
   454  	execArgs := []string{}
   455  	execEnv := []string{}
   456  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   457  		execArg0 = arg0
   458  		execArgs = args
   459  		execEnv = envv
   460  		return nil
   461  	})
   462  	defer restorer()
   463  
   464  	// Run a hook on revision 41
   465  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"})
   466  	c.Assert(err, check.IsNil)
   467  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   468  	c.Check(execArgs, check.DeepEquals, []string{
   469  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   470  		"snap.snapname.hook.configure",
   471  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   472  		"--hook=configure", "snapname"})
   473  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=41")
   474  }
   475  
   476  func (s *RunSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) {
   477  	// Only create revision 42
   478  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   479  		Revision: snap.R(42),
   480  	})
   481  
   482  	// redirect exec
   483  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   484  		return nil
   485  	})
   486  	defer restorer()
   487  
   488  	// Attempt to run a hook on revision 41, which doesn't exist
   489  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"})
   490  	c.Assert(err, check.NotNil)
   491  	c.Check(err, check.ErrorMatches, "cannot find .*")
   492  }
   493  
   494  func (s *RunSuite) TestSnapRunHookInvalidRevisionIntegration(c *check.C) {
   495  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=invalid", "--", "snapname"})
   496  	c.Assert(err, check.NotNil)
   497  	c.Check(err, check.ErrorMatches, "invalid snap revision: \"invalid\"")
   498  }
   499  
   500  func (s *RunSuite) TestSnapRunHookMissingHookIntegration(c *check.C) {
   501  	// Only create revision 42
   502  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   503  		Revision: snap.R(42),
   504  	})
   505  
   506  	// redirect exec
   507  	called := false
   508  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   509  		called = true
   510  		return nil
   511  	})
   512  	defer restorer()
   513  
   514  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=missing-hook", "--", "snapname"})
   515  	c.Assert(err, check.ErrorMatches, `cannot find hook "missing-hook" in "snapname"`)
   516  	c.Check(called, check.Equals, false)
   517  }
   518  
   519  func (s *RunSuite) TestSnapRunErorsForUnknownRunArg(c *check.C) {
   520  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--unknown", "--", "snapname.app", "--arg1", "arg2"})
   521  	c.Assert(err, check.ErrorMatches, "unknown flag `unknown'")
   522  }
   523  
   524  func (s *RunSuite) TestSnapRunErorsForMissingApp(c *check.C) {
   525  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=shell"})
   526  	c.Assert(err, check.ErrorMatches, "need the application to run as argument")
   527  }
   528  
   529  func (s *RunSuite) TestSnapRunErorrForUnavailableApp(c *check.C) {
   530  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "not-there"})
   531  	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))
   532  }
   533  
   534  func (s *RunSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) {
   535  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   536  
   537  	// mock installed snap
   538  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   539  		Revision: snap.R(42),
   540  	})
   541  
   542  	// redirect exec
   543  	execEnv := []string{}
   544  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   545  		execEnv = envv
   546  		return nil
   547  	})
   548  	defer restorer()
   549  
   550  	// set a SNAP{,_*} variable in the environment
   551  	os.Setenv("SNAP_NAME", "something-else")
   552  	os.Setenv("SNAP_ARCH", "PDP-7")
   553  	defer os.Unsetenv("SNAP_NAME")
   554  	defer os.Unsetenv("SNAP_ARCH")
   555  	// but unrelated stuff is ok
   556  	os.Setenv("SNAP_THE_WORLD", "YES")
   557  	defer os.Unsetenv("SNAP_THE_WORLD")
   558  
   559  	// and ensure those SNAP_ vars get overridden
   560  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   561  	c.Assert(err, check.IsNil)
   562  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   563  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
   564  	c.Check(execEnv, check.Not(testutil.Contains), "SNAP_NAME=something-else")
   565  	c.Check(execEnv, check.Not(testutil.Contains), "SNAP_ARCH=PDP-7")
   566  	c.Check(execEnv, testutil.Contains, "SNAP_THE_WORLD=YES")
   567  }
   568  
   569  func (s *RunSuite) TestSnapRunSnapdHelperPath(c *check.C) {
   570  	_, r := logger.MockLogger()
   571  	defer r()
   572  
   573  	var osReadlinkResult string
   574  	restore := snaprun.MockOsReadlink(func(name string) (string, error) {
   575  		return osReadlinkResult, nil
   576  	})
   577  	defer restore()
   578  
   579  	tool := "snap-confine"
   580  	for _, t := range []struct {
   581  		readlink string
   582  		expected string
   583  	}{
   584  		{
   585  			filepath.Join(dirs.SnapMountDir, "core/current/usr/bin/snap"),
   586  			filepath.Join(dirs.SnapMountDir, "core/current", dirs.CoreLibExecDir, tool),
   587  		},
   588  		{
   589  			filepath.Join(dirs.SnapMountDir, "snapd/current/usr/bin/snap"),
   590  			filepath.Join(dirs.SnapMountDir, "snapd/current", dirs.CoreLibExecDir, tool),
   591  		},
   592  		{
   593  			filepath.Join("/usr/bin/snap"),
   594  			filepath.Join(dirs.DistroLibExecDir, tool),
   595  		},
   596  		{
   597  			filepath.Join("/home/foo/ws/snapd/snap"),
   598  			filepath.Join(dirs.DistroLibExecDir, tool),
   599  		},
   600  		// unexpected case
   601  		{
   602  			filepath.Join(dirs.SnapMountDir, "snapd2/current/bin/snap"),
   603  			filepath.Join(dirs.DistroLibExecDir, tool),
   604  		},
   605  	} {
   606  		osReadlinkResult = t.readlink
   607  		toolPath, err := snaprun.SnapdHelperPath(tool)
   608  		c.Assert(err, check.IsNil)
   609  		c.Check(toolPath, check.Equals, t.expected)
   610  	}
   611  }
   612  
   613  func (s *RunSuite) TestSnapRunAppIntegrationFromCore(c *check.C) {
   614  	defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))()
   615  
   616  	// mock installed snap
   617  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   618  		Revision: snap.R("x2"),
   619  	})
   620  
   621  	// pretend to be running from core
   622  	restorer := snaprun.MockOsReadlink(func(string) (string, error) {
   623  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
   624  	})
   625  	defer restorer()
   626  
   627  	// redirect exec
   628  	execArg0 := ""
   629  	execArgs := []string{}
   630  	execEnv := []string{}
   631  	restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   632  		execArg0 = arg0
   633  		execArgs = args
   634  		execEnv = envv
   635  		return nil
   636  	})
   637  	defer restorer()
   638  
   639  	// and run it!
   640  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   641  	c.Assert(err, check.IsNil)
   642  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   643  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
   644  	c.Check(execArgs, check.DeepEquals, []string{
   645  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
   646  		"snap.snapname.app",
   647  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   648  		"snapname.app", "--arg1", "arg2"})
   649  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   650  }
   651  
   652  func (s *RunSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) {
   653  	defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "snapd", "222", dirs.CoreLibExecDir))()
   654  
   655  	// mock installed snap
   656  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   657  		Revision: snap.R("x2"),
   658  	})
   659  
   660  	// pretend to be running from snapd
   661  	restorer := snaprun.MockOsReadlink(func(string) (string, error) {
   662  		return filepath.Join(dirs.SnapMountDir, "snapd/222/usr/bin/snap"), nil
   663  	})
   664  	defer restorer()
   665  
   666  	// redirect exec
   667  	execArg0 := ""
   668  	execArgs := []string{}
   669  	execEnv := []string{}
   670  	restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   671  		execArg0 = arg0
   672  		execArgs = args
   673  		execEnv = envv
   674  		return nil
   675  	})
   676  	defer restorer()
   677  
   678  	// and run it!
   679  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
   680  	c.Assert(err, check.IsNil)
   681  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   682  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine"))
   683  	c.Check(execArgs, check.DeepEquals, []string{
   684  		filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine"),
   685  		"snap.snapname.app",
   686  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   687  		"snapname.app", "--arg1", "arg2"})
   688  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   689  }
   690  
   691  func (s *RunSuite) TestSnapRunXauthorityMigration(c *check.C) {
   692  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   693  
   694  	u, err := user.Current()
   695  	c.Assert(err, check.IsNil)
   696  
   697  	// Ensure XDG_RUNTIME_DIR exists for the user we're testing with
   698  	err = os.MkdirAll(filepath.Join(dirs.XdgRuntimeDirBase, u.Uid), 0700)
   699  	c.Assert(err, check.IsNil)
   700  
   701  	// mock installed snap; happily this also gives us a directory
   702  	// below /tmp which the Xauthority migration expects.
   703  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   704  		Revision: snap.R("x2"),
   705  	})
   706  
   707  	// redirect exec
   708  	execArg0 := ""
   709  	execArgs := []string{}
   710  	execEnv := []string{}
   711  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   712  		execArg0 = arg0
   713  		execArgs = args
   714  		execEnv = envv
   715  		return nil
   716  	})
   717  	defer restorer()
   718  
   719  	xauthPath, err := x11.MockXauthority(2)
   720  	c.Assert(err, check.IsNil)
   721  	defer os.Remove(xauthPath)
   722  
   723  	defer snaprun.MockGetEnv(func(name string) string {
   724  		if name == "XAUTHORITY" {
   725  			return xauthPath
   726  		}
   727  		return ""
   728  	})()
   729  
   730  	// and run it!
   731  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
   732  	c.Assert(err, check.IsNil)
   733  	c.Assert(rest, check.DeepEquals, []string{"snapname.app"})
   734  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   735  	c.Check(execArgs, check.DeepEquals, []string{
   736  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   737  		"snap.snapname.app",
   738  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   739  		"snapname.app"})
   740  
   741  	expectedXauthPath := filepath.Join(dirs.XdgRuntimeDirBase, u.Uid, ".Xauthority")
   742  	c.Check(execEnv, testutil.Contains, fmt.Sprintf("XAUTHORITY=%s", expectedXauthPath))
   743  
   744  	info, err := os.Stat(expectedXauthPath)
   745  	c.Assert(err, check.IsNil)
   746  	c.Assert(info.Mode().Perm(), check.Equals, os.FileMode(0600))
   747  
   748  	err = x11.ValidateXauthorityFile(expectedXauthPath)
   749  	c.Assert(err, check.IsNil)
   750  }
   751  
   752  // build the args for a hypothetical completer
   753  func mkCompArgs(compPoint string, argv ...string) []string {
   754  	out := []string{
   755  		"99", // COMP_TYPE
   756  		"99", // COMP_KEY
   757  		"",   // COMP_POINT
   758  		"2",  // COMP_CWORD
   759  		" ",  // COMP_WORDBREAKS
   760  	}
   761  	out[2] = compPoint
   762  	out = append(out, strings.Join(argv, " "))
   763  	out = append(out, argv...)
   764  	return out
   765  }
   766  
   767  func (s *RunSuite) TestAntialiasHappy(c *check.C) {
   768  	c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil)
   769  
   770  	inArgs := mkCompArgs("10", "alias", "alias", "bo-alias")
   771  
   772  	// first not so happy because no alias symlink
   773  	app, outArgs := snaprun.Antialias("alias", inArgs)
   774  	c.Check(app, check.Equals, "alias")
   775  	c.Check(outArgs, check.DeepEquals, inArgs)
   776  
   777  	c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil)
   778  
   779  	// now really happy
   780  	app, outArgs = snaprun.Antialias("alias", inArgs)
   781  	c.Check(app, check.Equals, "an-app")
   782  	c.Check(outArgs, check.DeepEquals, []string{
   783  		"99", // COMP_TYPE (no change)
   784  		"99", // COMP_KEY (no change)
   785  		"11", // COMP_POINT (+1 because "an-app" is one longer than "alias")
   786  		"2",  // COMP_CWORD (no change)
   787  		" ",  // COMP_WORDBREAKS (no change)
   788  		"an-app alias bo-alias", // COMP_LINE (argv[0] changed)
   789  		"an-app",                // argv (arv[0] changed)
   790  		"alias",
   791  		"bo-alias",
   792  	})
   793  }
   794  
   795  func (s *RunSuite) TestAntialiasBailsIfUnhappy(c *check.C) {
   796  	// alias exists but args are somehow wonky
   797  	c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil)
   798  	c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil)
   799  
   800  	// weird1 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to COMP_WORDS[0]
   801  	weird1 := mkCompArgs("6", "alias", "")
   802  	weird1[5] = "xxxxx "
   803  	// weird2 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to the first word in COMP_LINE
   804  	weird2 := mkCompArgs("6", "xxxxx", "")
   805  	weird2[5] = "alias "
   806  
   807  	for desc, inArgs := range map[string][]string{
   808  		"nil args":                                               nil,
   809  		"too-short args":                                         {"alias"},
   810  		"COMP_POINT not a number":                                mkCompArgs("hello", "alias"),
   811  		"COMP_POINT is inside argv[0]":                           mkCompArgs("2", "alias", ""),
   812  		"COMP_POINT is outside argv":                             mkCompArgs("99", "alias", ""),
   813  		"COMP_WORDS[0] is not argv[0]":                           mkCompArgs("10", "not-alias", ""),
   814  		"mismatch between argv[0], COMP_LINE and COMP_WORDS, #1": weird1,
   815  		"mismatch between argv[0], COMP_LINE and COMP_WORDS, #2": weird2,
   816  	} {
   817  		// antialias leaves args alone if it's too short
   818  		app, outArgs := snaprun.Antialias("alias", inArgs)
   819  		c.Check(app, check.Equals, "alias", check.Commentf(desc))
   820  		c.Check(outArgs, check.DeepEquals, inArgs, check.Commentf(desc))
   821  	}
   822  }
   823  
   824  func (s *RunSuite) TestSnapRunAppWithStraceIntegration(c *check.C) {
   825  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   826  
   827  	// mock installed snap
   828  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   829  		Revision: snap.R("x2"),
   830  	})
   831  
   832  	// pretend we have sudo and simulate some useful output that would
   833  	// normally come from strace
   834  	sudoCmd := testutil.MockCommand(c, "sudo", fmt.Sprintf(`
   835  echo "stdout output 1"
   836  >&2 echo 'execve("/path/to/snap-confine")'
   837  >&2 echo "snap-confine/snap-exec strace stuff"
   838  >&2 echo "getuid() = 1000"
   839  >&2 echo 'execve("%s/snapName/x2/bin/foo")'
   840  >&2 echo "interessting strace output"
   841  >&2 echo "and more"
   842  echo "stdout output 2"
   843  `, dirs.SnapMountDir))
   844  	defer sudoCmd.Restore()
   845  
   846  	// pretend we have strace
   847  	straceCmd := testutil.MockCommand(c, "strace", "")
   848  	defer straceCmd.Restore()
   849  
   850  	user, err := user.Current()
   851  	c.Assert(err, check.IsNil)
   852  
   853  	// and run it under strace
   854  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace", "--", "snapname.app", "--arg1", "arg2"})
   855  	c.Assert(err, check.IsNil)
   856  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   857  	c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{
   858  		{
   859  			"sudo", "-E",
   860  			filepath.Join(straceCmd.BinDir(), "strace"),
   861  			"-u", user.Username,
   862  			"-f",
   863  			"-e", "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep",
   864  			filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   865  			"snap.snapname.app",
   866  			filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   867  			"snapname.app", "--arg1", "arg2",
   868  		},
   869  	})
   870  	c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n")
   871  	c.Check(s.Stderr(), check.Equals, fmt.Sprintf("execve(%q)\ninteressting strace output\nand more\n", filepath.Join(dirs.SnapMountDir, "snapName/x2/bin/foo")))
   872  
   873  	s.ResetStdStreams()
   874  	sudoCmd.ForgetCalls()
   875  
   876  	// try again without filtering
   877  	rest, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace=--raw", "--", "snapname.app", "--arg1", "arg2"})
   878  	c.Assert(err, check.IsNil)
   879  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   880  	c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{
   881  		{
   882  			"sudo", "-E",
   883  			filepath.Join(straceCmd.BinDir(), "strace"),
   884  			"-u", user.Username,
   885  			"-f",
   886  			"-e", "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep",
   887  			filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   888  			"snap.snapname.app",
   889  			filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   890  			"snapname.app", "--arg1", "arg2",
   891  		},
   892  	})
   893  	c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n")
   894  	expectedFullFmt := `execve("/path/to/snap-confine")
   895  snap-confine/snap-exec strace stuff
   896  getuid() = 1000
   897  execve("%s/snapName/x2/bin/foo")
   898  interessting strace output
   899  and more
   900  `
   901  	c.Check(s.Stderr(), check.Equals, fmt.Sprintf(expectedFullFmt, dirs.SnapMountDir))
   902  }
   903  
   904  func (s *RunSuite) TestSnapRunAppWithStraceOptions(c *check.C) {
   905  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   906  
   907  	// mock installed snap
   908  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   909  		Revision: snap.R("x2"),
   910  	})
   911  
   912  	// pretend we have sudo
   913  	sudoCmd := testutil.MockCommand(c, "sudo", "")
   914  	defer sudoCmd.Restore()
   915  
   916  	// pretend we have strace
   917  	straceCmd := testutil.MockCommand(c, "strace", "")
   918  	defer straceCmd.Restore()
   919  
   920  	user, err := user.Current()
   921  	c.Assert(err, check.IsNil)
   922  
   923  	// and run it under strace
   924  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--strace=-tt --raw -o "file with spaces"`, "--", "snapname.app", "--arg1", "arg2"})
   925  	c.Assert(err, check.IsNil)
   926  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   927  	c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{
   928  		{
   929  			"sudo", "-E",
   930  			filepath.Join(straceCmd.BinDir(), "strace"),
   931  			"-u", user.Username,
   932  			"-f",
   933  			"-e", "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep",
   934  			"-tt",
   935  			"-o",
   936  			"file with spaces",
   937  			filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   938  			"snap.snapname.app",
   939  			filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   940  			"snapname.app", "--arg1", "arg2",
   941  		},
   942  	})
   943  }
   944  
   945  func (s *RunSuite) TestSnapRunShellIntegration(c *check.C) {
   946  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   947  
   948  	// mock installed snap
   949  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   950  		Revision: snap.R("x2"),
   951  	})
   952  
   953  	// redirect exec
   954  	execArg0 := ""
   955  	execArgs := []string{}
   956  	execEnv := []string{}
   957  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   958  		execArg0 = arg0
   959  		execArgs = args
   960  		execEnv = envv
   961  		return nil
   962  	})
   963  	defer restorer()
   964  
   965  	// and run it!
   966  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--shell", "--", "snapname.app", "--arg1", "arg2"})
   967  	c.Assert(err, check.IsNil)
   968  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
   969  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
   970  	c.Check(execArgs, check.DeepEquals, []string{
   971  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
   972  		"snap.snapname.app",
   973  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
   974  		"--command=shell", "snapname.app", "--arg1", "arg2"})
   975  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
   976  }
   977  
   978  func (s *RunSuite) TestSnapRunAppTimer(c *check.C) {
   979  	defer mockSnapConfine(dirs.DistroLibExecDir)()
   980  
   981  	// mock installed snap
   982  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
   983  		Revision: snap.R("x2"),
   984  	})
   985  
   986  	// redirect exec
   987  	execArg0 := ""
   988  	execArgs := []string{}
   989  	execCalled := false
   990  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
   991  		execArg0 = arg0
   992  		execArgs = args
   993  		execCalled = true
   994  		return nil
   995  	})
   996  	defer restorer()
   997  
   998  	fakeNow := time.Date(2018, 02, 12, 9, 55, 0, 0, time.Local)
   999  	restorer = snaprun.MockTimeNow(func() time.Time {
  1000  		// Monday Feb 12, 9:55
  1001  		return fakeNow
  1002  	})
  1003  	defer restorer()
  1004  
  1005  	// pretend we are outside of timer range
  1006  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"})
  1007  	c.Assert(err, check.IsNil)
  1008  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1009  	c.Assert(execCalled, check.Equals, false)
  1010  
  1011  	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"
  1012  `, fakeNow.Format(time.RFC3339)))
  1013  	s.ResetStdStreams()
  1014  
  1015  	restorer = snaprun.MockTimeNow(func() time.Time {
  1016  		// Monday Feb 12, 10:20
  1017  		return time.Date(2018, 02, 12, 10, 20, 0, 0, time.Local)
  1018  	})
  1019  	defer restorer()
  1020  
  1021  	// and run it under strace
  1022  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"})
  1023  	c.Assert(err, check.IsNil)
  1024  	c.Assert(execCalled, check.Equals, true)
  1025  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
  1026  	c.Check(execArgs, check.DeepEquals, []string{
  1027  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1028  		"snap.snapname.app",
  1029  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1030  		"snapname.app", "--arg1", "arg2"})
  1031  }
  1032  
  1033  func (s *RunSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) {
  1034  	_, r := logger.MockLogger()
  1035  	defer r()
  1036  
  1037  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1038  
  1039  	// mock installed snap
  1040  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1041  		Revision: snap.R("1"),
  1042  	})
  1043  
  1044  	// pretend we have sudo
  1045  	sudoCmd := testutil.MockCommand(c, "sudo", "echo unhappy; exit 12")
  1046  	defer sudoCmd.Restore()
  1047  
  1048  	// pretend we have strace
  1049  	straceCmd := testutil.MockCommand(c, "strace", "")
  1050  	defer straceCmd.Restore()
  1051  
  1052  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--trace-exec", "--", "snapname.app", "--arg1", "arg2"})
  1053  	c.Assert(err, check.ErrorMatches, "exit status 12")
  1054  	c.Assert(rest, check.DeepEquals, []string{"--", "snapname.app", "--arg1", "arg2"})
  1055  	c.Check(s.Stdout(), check.Equals, "unhappy\n")
  1056  	c.Check(s.Stderr(), check.Equals, "")
  1057  }
  1058  
  1059  func (s *RunSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
  1060  	logbuf, restorer := logger.MockLogger()
  1061  	defer restorer()
  1062  
  1063  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1064  
  1065  	// mock installed snap
  1066  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1067  		Revision: snap.R("x2"),
  1068  	})
  1069  
  1070  	// redirect exec
  1071  	execCalled := 0
  1072  	restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error {
  1073  		execCalled++
  1074  		return nil
  1075  	})
  1076  	defer restorer()
  1077  
  1078  	verifyCalls := 0
  1079  	restoreCalls := 0
  1080  	isEnabledCalls := 0
  1081  	enabled := false
  1082  	verify := true
  1083  
  1084  	snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)
  1085  
  1086  	restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) {
  1087  		c.Check(what, check.Equals, snapUserDir)
  1088  		verifyCalls++
  1089  		return verify, nil
  1090  	})
  1091  	defer restorer()
  1092  
  1093  	restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error {
  1094  		c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true})
  1095  		c.Check(what, check.Equals, snapUserDir)
  1096  		restoreCalls++
  1097  		return nil
  1098  	})
  1099  	defer restorer()
  1100  
  1101  	restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) {
  1102  		isEnabledCalls++
  1103  		return enabled, nil
  1104  	})
  1105  	defer restorer()
  1106  
  1107  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1108  	c.Assert(err, check.IsNil)
  1109  	c.Check(execCalled, check.Equals, 1)
  1110  	c.Check(isEnabledCalls, check.Equals, 1)
  1111  	c.Check(verifyCalls, check.Equals, 0)
  1112  	c.Check(restoreCalls, check.Equals, 0)
  1113  
  1114  	// pretend SELinux is on
  1115  	enabled = true
  1116  
  1117  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1118  	c.Assert(err, check.IsNil)
  1119  	c.Check(execCalled, check.Equals, 2)
  1120  	c.Check(isEnabledCalls, check.Equals, 2)
  1121  	c.Check(verifyCalls, check.Equals, 1)
  1122  	c.Check(restoreCalls, check.Equals, 0)
  1123  
  1124  	// pretend the context does not match
  1125  	verify = false
  1126  
  1127  	logbuf.Reset()
  1128  
  1129  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1130  	c.Assert(err, check.IsNil)
  1131  	c.Check(execCalled, check.Equals, 3)
  1132  	c.Check(isEnabledCalls, check.Equals, 3)
  1133  	c.Check(verifyCalls, check.Equals, 2)
  1134  	c.Check(restoreCalls, check.Equals, 1)
  1135  
  1136  	// and we let the user know what we're doing
  1137  	c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("restoring default SELinux context of %s", snapUserDir))
  1138  }
  1139  
  1140  func (s *RunSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) {
  1141  	logbuf, restorer := logger.MockLogger()
  1142  	defer restorer()
  1143  
  1144  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1145  
  1146  	// mock installed snap
  1147  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1148  		Revision: snap.R("x2"),
  1149  	})
  1150  
  1151  	// redirect exec
  1152  	execCalled := 0
  1153  	restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error {
  1154  		execCalled++
  1155  		return nil
  1156  	})
  1157  	defer restorer()
  1158  
  1159  	verifyCalls := 0
  1160  	restoreCalls := 0
  1161  	isEnabledCalls := 0
  1162  	enabledErr := errors.New("enabled failed")
  1163  	verifyErr := errors.New("verify failed")
  1164  	restoreErr := errors.New("restore failed")
  1165  
  1166  	snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)
  1167  
  1168  	restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) {
  1169  		c.Check(what, check.Equals, snapUserDir)
  1170  		verifyCalls++
  1171  		return false, verifyErr
  1172  	})
  1173  	defer restorer()
  1174  
  1175  	restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error {
  1176  		c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true})
  1177  		c.Check(what, check.Equals, snapUserDir)
  1178  		restoreCalls++
  1179  		return restoreErr
  1180  	})
  1181  	defer restorer()
  1182  
  1183  	restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) {
  1184  		isEnabledCalls++
  1185  		return enabledErr == nil, enabledErr
  1186  	})
  1187  	defer restorer()
  1188  
  1189  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1190  	// these errors are only logged, but we still run the snap
  1191  	c.Assert(err, check.IsNil)
  1192  	c.Check(execCalled, check.Equals, 1)
  1193  	c.Check(logbuf.String(), testutil.Contains, "cannot determine SELinux status: enabled failed")
  1194  	c.Check(isEnabledCalls, check.Equals, 1)
  1195  	c.Check(verifyCalls, check.Equals, 0)
  1196  	c.Check(restoreCalls, check.Equals, 0)
  1197  	// pretend selinux is on
  1198  	enabledErr = nil
  1199  
  1200  	logbuf.Reset()
  1201  
  1202  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1203  	c.Assert(err, check.IsNil)
  1204  	c.Check(execCalled, check.Equals, 2)
  1205  	c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("failed to verify SELinux context of %s: verify failed", snapUserDir))
  1206  	c.Check(isEnabledCalls, check.Equals, 2)
  1207  	c.Check(verifyCalls, check.Equals, 1)
  1208  	c.Check(restoreCalls, check.Equals, 0)
  1209  
  1210  	// pretend the context does not match
  1211  	verifyErr = nil
  1212  
  1213  	logbuf.Reset()
  1214  
  1215  	_, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"})
  1216  	c.Assert(err, check.IsNil)
  1217  	c.Check(execCalled, check.Equals, 3)
  1218  	c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("cannot restore SELinux context of %s: restore failed", snapUserDir))
  1219  	c.Check(isEnabledCalls, check.Equals, 3)
  1220  	c.Check(verifyCalls, check.Equals, 2)
  1221  	c.Check(restoreCalls, check.Equals, 1)
  1222  }
  1223  
  1224  // systemctl is-system-running returns "running" in normal situations.
  1225  func (s *RunSuite) TestIsStoppingRunning(c *check.C) {
  1226  	systemctl := testutil.MockCommand(c, "systemctl", `
  1227  case "$1" in
  1228  	is-system-running)
  1229  		echo "running"
  1230  		exit 0
  1231  		;;
  1232  esac
  1233  `)
  1234  	defer systemctl.Restore()
  1235  	stop, err := snaprun.IsStopping()
  1236  	c.Check(err, check.IsNil)
  1237  	c.Check(stop, check.Equals, false)
  1238  	c.Check(systemctl.Calls(), check.DeepEquals, [][]string{
  1239  		{"systemctl", "is-system-running"},
  1240  	})
  1241  }
  1242  
  1243  // systemctl is-system-running returns "stopping" when the system is
  1244  // shutting down or rebooting. At the same time it returns a non-zero
  1245  // exit status.
  1246  func (s *RunSuite) TestIsStoppingStopping(c *check.C) {
  1247  	systemctl := testutil.MockCommand(c, "systemctl", `
  1248  case "$1" in
  1249  	is-system-running)
  1250  		echo "stopping"
  1251  		exit 1
  1252  		;;
  1253  esac
  1254  `)
  1255  	defer systemctl.Restore()
  1256  	stop, err := snaprun.IsStopping()
  1257  	c.Check(err, check.IsNil)
  1258  	c.Check(stop, check.Equals, true)
  1259  	c.Check(systemctl.Calls(), check.DeepEquals, [][]string{
  1260  		{"systemctl", "is-system-running"},
  1261  	})
  1262  }
  1263  
  1264  // systemctl is-system-running can often return "degraded"
  1265  // Let's make sure that is not confusing us.
  1266  func (s *RunSuite) TestIsStoppingDegraded(c *check.C) {
  1267  	systemctl := testutil.MockCommand(c, "systemctl", `
  1268  case "$1" in
  1269  	is-system-running)
  1270  		echo "degraded"
  1271  		exit 1
  1272  		;;
  1273  esac
  1274  `)
  1275  	defer systemctl.Restore()
  1276  	stop, err := snaprun.IsStopping()
  1277  	c.Check(err, check.IsNil)
  1278  	c.Check(stop, check.Equals, false)
  1279  	c.Check(systemctl.Calls(), check.DeepEquals, [][]string{
  1280  		{"systemctl", "is-system-running"},
  1281  	})
  1282  }
  1283  
  1284  func (s *RunSuite) TestSnapRunTrackingApps(c *check.C) {
  1285  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1286  	defer restore()
  1287  
  1288  	// mock installed snap
  1289  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1290  		Revision: snap.R("x2"),
  1291  	})
  1292  
  1293  	// pretend to be running from core
  1294  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1295  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1296  	})
  1297  	defer restore()
  1298  
  1299  	created := false
  1300  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1301  		c.Assert(securityTag, check.Equals, "snap.snapname.app")
  1302  		c.Assert(opts, check.NotNil)
  1303  		c.Assert(opts.AllowSessionBus, check.Equals, true)
  1304  		created = true
  1305  		return nil
  1306  	})
  1307  	defer restore()
  1308  
  1309  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1310  		panic("apps need to create a scope and do not use systemd service tracking")
  1311  	})
  1312  	defer restore()
  1313  
  1314  	// redirect exec
  1315  	execArg0 := ""
  1316  	execArgs := []string{}
  1317  	execEnv := []string{}
  1318  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1319  		execArg0 = arg0
  1320  		execArgs = args
  1321  		execEnv = envv
  1322  		return nil
  1323  	})
  1324  	defer restore()
  1325  
  1326  	// and run it!
  1327  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
  1328  	c.Assert(err, check.IsNil)
  1329  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1330  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1331  	c.Check(execArgs, check.DeepEquals, []string{
  1332  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1333  		"snap.snapname.app",
  1334  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1335  		"snapname.app", "--arg1", "arg2"})
  1336  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1337  	c.Assert(created, check.Equals, true)
  1338  }
  1339  
  1340  func (s *RunSuite) TestSnapRunTrackingHooks(c *check.C) {
  1341  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1342  	defer restore()
  1343  
  1344  	// mock installed snap
  1345  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1346  		Revision: snap.R("x2"),
  1347  	})
  1348  
  1349  	// pretend to be running from core
  1350  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1351  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1352  	})
  1353  	defer restore()
  1354  
  1355  	created := false
  1356  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1357  		c.Assert(securityTag, check.Equals, "snap.snapname.hook.configure")
  1358  		c.Assert(opts, check.NotNil)
  1359  		c.Assert(opts.AllowSessionBus, check.Equals, false)
  1360  		created = true
  1361  		return nil
  1362  	})
  1363  	defer restore()
  1364  
  1365  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1366  		panic("hooks need to create a scope and do not use systemd service tracking")
  1367  	})
  1368  	defer restore()
  1369  
  1370  	// redirect exec
  1371  	execArg0 := ""
  1372  	execArgs := []string{}
  1373  	execEnv := []string{}
  1374  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1375  		execArg0 = arg0
  1376  		execArgs = args
  1377  		execEnv = envv
  1378  		return nil
  1379  	})
  1380  	defer restore()
  1381  
  1382  	// and run it!
  1383  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook", "configure", "-r", "x2", "snapname"})
  1384  	c.Assert(err, check.IsNil)
  1385  	c.Assert(rest, check.DeepEquals, []string{"snapname"})
  1386  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1387  	c.Check(execArgs, check.DeepEquals, []string{
  1388  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1389  		"snap.snapname.hook.configure",
  1390  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1391  		"--hook=configure", "snapname"})
  1392  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1393  	c.Assert(created, check.Equals, true)
  1394  }
  1395  
  1396  func (s *RunSuite) TestSnapRunTrackingServices(c *check.C) {
  1397  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1398  	defer restore()
  1399  
  1400  	// mock installed snap
  1401  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1402  		Revision: snap.R("x2"),
  1403  	})
  1404  
  1405  	// pretend to be running from core
  1406  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1407  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1408  	})
  1409  	defer restore()
  1410  
  1411  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1412  		panic("services rely on systemd tracking, should not have created a transient scope")
  1413  	})
  1414  	defer restore()
  1415  
  1416  	confirmed := false
  1417  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1418  		confirmed = true
  1419  		c.Assert(securityTag, check.Equals, "snap.snapname.svc")
  1420  		return nil
  1421  	})
  1422  	defer restore()
  1423  
  1424  	// redirect exec
  1425  	execArg0 := ""
  1426  	execArgs := []string{}
  1427  	execEnv := []string{}
  1428  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1429  		execArg0 = arg0
  1430  		execArgs = args
  1431  		execEnv = envv
  1432  		return nil
  1433  	})
  1434  	defer restore()
  1435  
  1436  	// and run it!
  1437  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc", "--arg1", "arg2"})
  1438  	c.Assert(err, check.IsNil)
  1439  	c.Assert(rest, check.DeepEquals, []string{"snapname.svc", "--arg1", "arg2"})
  1440  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1441  	c.Check(execArgs, check.DeepEquals, []string{
  1442  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1443  		"snap.snapname.svc",
  1444  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1445  		"snapname.svc", "--arg1", "arg2"})
  1446  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1447  	c.Assert(confirmed, check.Equals, true)
  1448  }
  1449  
  1450  func (s *RunSuite) TestSnapRunTrackingFailure(c *check.C) {
  1451  	restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))
  1452  	defer restore()
  1453  
  1454  	// mock installed snap
  1455  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1456  		Revision: snap.R("x2"),
  1457  	})
  1458  
  1459  	// pretend to be running from core
  1460  	restore = snaprun.MockOsReadlink(func(string) (string, error) {
  1461  		return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil
  1462  	})
  1463  	defer restore()
  1464  
  1465  	created := false
  1466  	restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error {
  1467  		c.Assert(securityTag, check.Equals, "snap.snapname.app")
  1468  		c.Assert(opts, check.NotNil)
  1469  		c.Assert(opts.AllowSessionBus, check.Equals, true)
  1470  		created = true
  1471  		// Pretend that the tracking system was unable to track this application.
  1472  		return cgroup.ErrCannotTrackProcess
  1473  	})
  1474  	defer restore()
  1475  
  1476  	restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error {
  1477  		panic("apps need to create a scope and do not use systemd service tracking")
  1478  	})
  1479  	defer restore()
  1480  
  1481  	// redirect exec
  1482  	execArg0 := ""
  1483  	execArgs := []string{}
  1484  	execEnv := []string{}
  1485  	restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1486  		execArg0 = arg0
  1487  		execArgs = args
  1488  		execEnv = envv
  1489  		return nil
  1490  	})
  1491  	defer restore()
  1492  
  1493  	// Capture the debug log that is printed by this test.
  1494  	os.Setenv("SNAPD_DEBUG", "1")
  1495  	defer os.Unsetenv("SNAPD_DEBUG")
  1496  	logbuf, restore := logger.MockLogger()
  1497  	defer restore()
  1498  
  1499  	// and run it!
  1500  	rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"})
  1501  	c.Assert(err, check.IsNil)
  1502  	c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"})
  1503  	c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"))
  1504  	c.Check(execArgs, check.DeepEquals, []string{
  1505  		filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"),
  1506  		"snap.snapname.app",
  1507  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1508  		"snapname.app", "--arg1", "arg2"})
  1509  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
  1510  	c.Assert(created, check.Equals, true)
  1511  
  1512  	// Ensure that the debug message is printed.
  1513  	c.Assert(logbuf.String(), testutil.Contains, "snapd cannot track the started application\n")
  1514  }
  1515  
  1516  var mockKernelYaml = []byte(`name: pc-kernel
  1517  type: kernel
  1518  version: 1.0
  1519  hooks:
  1520   fde-setup:
  1521  `)
  1522  
  1523  func (s *RunSuite) TestSnapRunHookKernelImplicitBase(c *check.C) {
  1524  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1525  
  1526  	nModel := 0
  1527  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
  1528  		switch r.URL.Path {
  1529  		case "/v2/model":
  1530  			switch nModel {
  1531  			case 0:
  1532  				c.Check(r.Method, check.Equals, "GET")
  1533  				c.Check(r.URL.RawQuery, check.Equals, "")
  1534  				fmt.Fprintln(w, happyUC20ModelAssertionResponse)
  1535  			default:
  1536  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1)
  1537  			}
  1538  			nModel++
  1539  		}
  1540  	})
  1541  
  1542  	// mock installed kernel
  1543  	snaptest.MockSnapCurrent(c, string(mockKernelYaml), &snap.SideInfo{
  1544  		Revision: snap.R(42),
  1545  	})
  1546  
  1547  	// redirect exec
  1548  	execArg0 := ""
  1549  	execArgs := []string{}
  1550  	execEnv := []string{}
  1551  	restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error {
  1552  		execArg0 = arg0
  1553  		execArgs = args
  1554  		execEnv = envv
  1555  		return nil
  1556  	})
  1557  	defer restorer()
  1558  
  1559  	// Run a hook from the active revision
  1560  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=fde-setup", "--", "pc-kernel"})
  1561  	c.Assert(err, check.IsNil)
  1562  	c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine"))
  1563  	c.Check(execArgs, check.DeepEquals, []string{
  1564  		filepath.Join(dirs.DistroLibExecDir, "snap-confine"),
  1565  		"--base", "core20",
  1566  		"snap.pc-kernel.hook.fde-setup",
  1567  		filepath.Join(dirs.CoreLibExecDir, "snap-exec"),
  1568  		"--hook=fde-setup", "pc-kernel"})
  1569  	c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
  1570  	c.Check(nModel, check.Equals, 1)
  1571  }
  1572  
  1573  func (s *RunSuite) TestRunGdbserverNoGdbserver(c *check.C) {
  1574  	oldPath := os.Getenv("PATH")
  1575  	os.Setenv("PATH", "/no-path:/really-not")
  1576  	defer os.Setenv("PATH", oldPath)
  1577  
  1578  	defer mockSnapConfine(dirs.DistroLibExecDir)()
  1579  	snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
  1580  		Revision: snap.R("x2"),
  1581  	})
  1582  
  1583  	_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--gdbserver", "snapname.app"})
  1584  	c.Assert(err, check.ErrorMatches, "please install gdbserver on your system")
  1585  }