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