github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/systemd/systemd_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2015 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 systemd_test
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"strconv"
    30  	"testing"
    31  	"time"
    32  
    33  	. "gopkg.in/check.v1"
    34  
    35  	"github.com/snapcore/snapd/dirs"
    36  	"github.com/snapcore/snapd/osutil"
    37  	"github.com/snapcore/snapd/osutil/squashfs"
    38  	"github.com/snapcore/snapd/sandbox/selinux"
    39  	"github.com/snapcore/snapd/testutil"
    40  
    41  	. "github.com/snapcore/snapd/systemd"
    42  )
    43  
    44  type testreporter struct {
    45  	msgs []string
    46  }
    47  
    48  func (tr *testreporter) Notify(msg string) {
    49  	tr.msgs = append(tr.msgs, msg)
    50  }
    51  
    52  // Hook up check.v1 into the "go test" runner
    53  func Test(t *testing.T) { TestingT(t) }
    54  
    55  // systemd's testsuite
    56  type SystemdTestSuite struct {
    57  	i      int
    58  	argses [][]string
    59  	errors []error
    60  	outs   [][]byte
    61  
    62  	j        int
    63  	jns      []string
    64  	jsvcs    [][]string
    65  	jouts    [][]byte
    66  	jerrs    []error
    67  	jfollows []bool
    68  
    69  	rep *testreporter
    70  
    71  	restoreSystemctl  func()
    72  	restoreJournalctl func()
    73  	restoreSELinux    func()
    74  }
    75  
    76  var _ = Suite(&SystemdTestSuite{})
    77  
    78  func (s *SystemdTestSuite) SetUpTest(c *C) {
    79  	dirs.SetRootDir(c.MkDir())
    80  	err := os.MkdirAll(dirs.SnapServicesDir, 0755)
    81  	c.Assert(err, IsNil)
    82  	c.Assert(os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755), IsNil)
    83  
    84  	// force UTC timezone, for reproducible timestamps
    85  	os.Setenv("TZ", "")
    86  
    87  	s.restoreSystemctl = MockSystemctl(s.myRun)
    88  	s.i = 0
    89  	s.argses = nil
    90  	s.errors = nil
    91  	s.outs = nil
    92  
    93  	s.restoreJournalctl = MockJournalctl(s.myJctl)
    94  	s.j = 0
    95  	s.jns = nil
    96  	s.jsvcs = nil
    97  	s.jouts = nil
    98  	s.jerrs = nil
    99  	s.jfollows = nil
   100  
   101  	s.rep = new(testreporter)
   102  
   103  	s.restoreSELinux = selinux.MockIsEnabled(func() (bool, error) { return false, nil })
   104  }
   105  
   106  func (s *SystemdTestSuite) TearDownTest(c *C) {
   107  	s.restoreSystemctl()
   108  	s.restoreJournalctl()
   109  	s.restoreSELinux()
   110  }
   111  
   112  func (s *SystemdTestSuite) myRun(args ...string) (out []byte, err error) {
   113  	s.argses = append(s.argses, args)
   114  	if s.i < len(s.outs) {
   115  		out = s.outs[s.i]
   116  	}
   117  	if s.i < len(s.errors) {
   118  		err = s.errors[s.i]
   119  	}
   120  	s.i++
   121  	return out, err
   122  }
   123  
   124  func (s *SystemdTestSuite) myJctl(svcs []string, n int, follow bool) (io.ReadCloser, error) {
   125  	var err error
   126  	var out []byte
   127  
   128  	s.jns = append(s.jns, strconv.Itoa(n))
   129  	s.jsvcs = append(s.jsvcs, svcs)
   130  	s.jfollows = append(s.jfollows, follow)
   131  
   132  	if s.j < len(s.jouts) {
   133  		out = s.jouts[s.j]
   134  	}
   135  	if s.j < len(s.jerrs) {
   136  		err = s.jerrs[s.j]
   137  	}
   138  	s.j++
   139  
   140  	if out == nil {
   141  		return nil, err
   142  	}
   143  
   144  	return ioutil.NopCloser(bytes.NewReader(out)), err
   145  }
   146  
   147  func (s *SystemdTestSuite) TestDaemonReload(c *C) {
   148  	err := New(SystemMode, s.rep).DaemonReload()
   149  	c.Assert(err, IsNil)
   150  	c.Assert(s.argses, DeepEquals, [][]string{{"daemon-reload"}})
   151  }
   152  
   153  func (s *SystemdTestSuite) TestDaemonReexec(c *C) {
   154  	err := New(SystemMode, s.rep).DaemonReexec()
   155  	c.Assert(err, IsNil)
   156  	c.Assert(s.argses, DeepEquals, [][]string{{"daemon-reexec"}})
   157  }
   158  
   159  func (s *SystemdTestSuite) TestStart(c *C) {
   160  	err := New(SystemMode, s.rep).Start("foo")
   161  	c.Assert(err, IsNil)
   162  	c.Check(s.argses, DeepEquals, [][]string{{"start", "foo"}})
   163  }
   164  
   165  func (s *SystemdTestSuite) TestStartMany(c *C) {
   166  	err := New(SystemMode, s.rep).Start("foo", "bar", "baz")
   167  	c.Assert(err, IsNil)
   168  	c.Check(s.argses, DeepEquals, [][]string{{"start", "foo", "bar", "baz"}})
   169  }
   170  
   171  func (s *SystemdTestSuite) TestStop(c *C) {
   172  	restore := MockStopDelays(time.Millisecond, 25*time.Second)
   173  	defer restore()
   174  	s.outs = [][]byte{
   175  		nil, // for the "stop" itself
   176  		[]byte("ActiveState=whatever\n"),
   177  		[]byte("ActiveState=active\n"),
   178  		[]byte("ActiveState=inactive\n"),
   179  	}
   180  	s.errors = []error{nil, nil, nil, nil, &Timeout{}}
   181  	err := New(SystemMode, s.rep).Stop("foo", 1*time.Second)
   182  	c.Assert(err, IsNil)
   183  	c.Assert(s.argses, HasLen, 4)
   184  	c.Check(s.argses[0], DeepEquals, []string{"stop", "foo"})
   185  	c.Check(s.argses[1], DeepEquals, []string{"show", "--property=ActiveState", "foo"})
   186  	c.Check(s.argses[1], DeepEquals, s.argses[2])
   187  	c.Check(s.argses[1], DeepEquals, s.argses[3])
   188  }
   189  
   190  func (s *SystemdTestSuite) TestStatus(c *C) {
   191  	s.outs = [][]byte{
   192  		[]byte(`
   193  Type=simple
   194  Id=foo.service
   195  ActiveState=active
   196  UnitFileState=enabled
   197  
   198  Type=simple
   199  Id=bar.service
   200  ActiveState=reloading
   201  UnitFileState=static
   202  
   203  Type=potato
   204  Id=baz.service
   205  ActiveState=inactive
   206  UnitFileState=disabled
   207  `[1:]),
   208  		[]byte(`
   209  Id=some.timer
   210  ActiveState=active
   211  UnitFileState=enabled
   212  
   213  Id=other.socket
   214  ActiveState=active
   215  UnitFileState=disabled
   216  `[1:]),
   217  	}
   218  	s.errors = []error{nil}
   219  	out, err := New(SystemMode, s.rep).Status("foo.service", "bar.service", "baz.service", "some.timer", "other.socket")
   220  	c.Assert(err, IsNil)
   221  	c.Check(out, DeepEquals, []*UnitStatus{
   222  		{
   223  			Daemon:   "simple",
   224  			UnitName: "foo.service",
   225  			Active:   true,
   226  			Enabled:  true,
   227  		}, {
   228  			Daemon:   "simple",
   229  			UnitName: "bar.service",
   230  			Active:   true,
   231  			Enabled:  true,
   232  		}, {
   233  			Daemon:   "potato",
   234  			UnitName: "baz.service",
   235  			Active:   false,
   236  			Enabled:  false,
   237  		}, {
   238  			UnitName: "some.timer",
   239  			Active:   true,
   240  			Enabled:  true,
   241  		}, {
   242  			UnitName: "other.socket",
   243  			Active:   true,
   244  			Enabled:  false,
   245  		},
   246  	})
   247  	c.Check(s.rep.msgs, IsNil)
   248  	c.Assert(s.argses, DeepEquals, [][]string{
   249  		{"show", "--property=Id,ActiveState,UnitFileState,Type", "foo.service", "bar.service", "baz.service"},
   250  		{"show", "--property=Id,ActiveState,UnitFileState", "some.timer", "other.socket"},
   251  	})
   252  }
   253  
   254  func (s *SystemdTestSuite) TestStatusBadNumberOfValues(c *C) {
   255  	s.outs = [][]byte{
   256  		[]byte(`
   257  Type=simple
   258  Id=foo.service
   259  ActiveState=active
   260  UnitFileState=enabled
   261  
   262  Type=simple
   263  Id=foo.service
   264  ActiveState=active
   265  UnitFileState=enabled
   266  `[1:]),
   267  	}
   268  	s.errors = []error{nil}
   269  	out, err := New(SystemMode, s.rep).Status("foo.service")
   270  	c.Check(err, ErrorMatches, "cannot get unit status: expected 1 results, got 2")
   271  	c.Check(out, IsNil)
   272  	c.Check(s.rep.msgs, IsNil)
   273  }
   274  
   275  func (s *SystemdTestSuite) TestStatusBadLine(c *C) {
   276  	s.outs = [][]byte{
   277  		[]byte(`
   278  Type=simple
   279  Id=foo.service
   280  ActiveState=active
   281  UnitFileState=enabled
   282  Potatoes
   283  `[1:]),
   284  	}
   285  	s.errors = []error{nil}
   286  	out, err := New(SystemMode, s.rep).Status("foo.service")
   287  	c.Assert(err, ErrorMatches, `.* bad line "Potatoes" .*`)
   288  	c.Check(out, IsNil)
   289  }
   290  
   291  func (s *SystemdTestSuite) TestStatusBadId(c *C) {
   292  	s.outs = [][]byte{
   293  		[]byte(`
   294  Type=simple
   295  Id=bar.service
   296  ActiveState=active
   297  UnitFileState=enabled
   298  `[1:]),
   299  	}
   300  	s.errors = []error{nil}
   301  	out, err := New(SystemMode, s.rep).Status("foo.service")
   302  	c.Assert(err, ErrorMatches, `.* queried status of "foo.service" but got status of "bar.service"`)
   303  	c.Check(out, IsNil)
   304  }
   305  
   306  func (s *SystemdTestSuite) TestStatusBadField(c *C) {
   307  	s.outs = [][]byte{
   308  		[]byte(`
   309  Type=simple
   310  Id=foo.service
   311  ActiveState=active
   312  UnitFileState=enabled
   313  Potatoes=false
   314  `[1:]),
   315  	}
   316  	s.errors = []error{nil}
   317  	out, err := New(SystemMode, s.rep).Status("foo.service")
   318  	c.Assert(err, ErrorMatches, `.* unexpected field "Potatoes" .*`)
   319  	c.Check(out, IsNil)
   320  }
   321  
   322  func (s *SystemdTestSuite) TestStatusMissingRequiredFieldService(c *C) {
   323  	s.outs = [][]byte{
   324  		[]byte(`
   325  Id=foo.service
   326  ActiveState=active
   327  `[1:]),
   328  	}
   329  	s.errors = []error{nil}
   330  	out, err := New(SystemMode, s.rep).Status("foo.service")
   331  	c.Assert(err, ErrorMatches, `.* missing UnitFileState, Type .*`)
   332  	c.Check(out, IsNil)
   333  }
   334  
   335  func (s *SystemdTestSuite) TestStatusMissingRequiredFieldTimer(c *C) {
   336  	s.outs = [][]byte{
   337  		[]byte(`
   338  Id=foo.timer
   339  ActiveState=active
   340  `[1:]),
   341  	}
   342  	s.errors = []error{nil}
   343  	out, err := New(SystemMode, s.rep).Status("foo.timer")
   344  	c.Assert(err, ErrorMatches, `.* missing UnitFileState .*`)
   345  	c.Check(out, IsNil)
   346  }
   347  
   348  func (s *SystemdTestSuite) TestStatusDupeField(c *C) {
   349  	s.outs = [][]byte{
   350  		[]byte(`
   351  Type=simple
   352  Id=foo.service
   353  ActiveState=active
   354  ActiveState=active
   355  UnitFileState=enabled
   356  `[1:]),
   357  	}
   358  	s.errors = []error{nil}
   359  	out, err := New(SystemMode, s.rep).Status("foo.service")
   360  	c.Assert(err, ErrorMatches, `.* duplicate field "ActiveState" .*`)
   361  	c.Check(out, IsNil)
   362  }
   363  
   364  func (s *SystemdTestSuite) TestStatusEmptyField(c *C) {
   365  	s.outs = [][]byte{
   366  		[]byte(`
   367  Type=simple
   368  Id=
   369  ActiveState=active
   370  UnitFileState=enabled
   371  `[1:]),
   372  	}
   373  	s.errors = []error{nil}
   374  	out, err := New(SystemMode, s.rep).Status("foo.service")
   375  	c.Assert(err, ErrorMatches, `.* empty field "Id" .*`)
   376  	c.Check(out, IsNil)
   377  }
   378  
   379  func (s *SystemdTestSuite) TestStopTimeout(c *C) {
   380  	restore := MockStopDelays(time.Millisecond, 25*time.Second)
   381  	defer restore()
   382  	err := New(SystemMode, s.rep).Stop("foo", 10*time.Millisecond)
   383  	c.Assert(err, FitsTypeOf, &Timeout{})
   384  	c.Assert(len(s.rep.msgs) > 0, Equals, true)
   385  	c.Check(s.rep.msgs[0], Equals, "Waiting for foo to stop.")
   386  }
   387  
   388  func (s *SystemdTestSuite) TestDisable(c *C) {
   389  	err := New(SystemMode, s.rep).Disable("foo")
   390  	c.Assert(err, IsNil)
   391  	c.Check(s.argses, DeepEquals, [][]string{{"disable", "foo"}})
   392  }
   393  
   394  func (s *SystemdTestSuite) TestUnderRootDisable(c *C) {
   395  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Disable("foo")
   396  	c.Assert(err, IsNil)
   397  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "disable", "foo"}})
   398  }
   399  
   400  func (s *SystemdTestSuite) TestAvailable(c *C) {
   401  	err := Available()
   402  	c.Assert(err, IsNil)
   403  	c.Check(s.argses, DeepEquals, [][]string{{"--version"}})
   404  }
   405  
   406  func (s *SystemdTestSuite) TestVersion(c *C) {
   407  	s.outs = [][]byte{
   408  		[]byte("systemd 223\n+PAM\n"),
   409  		[]byte("systemd 245 (245.4-4ubuntu3)\n+PAM +AUDIT +SELINUX +IMA\n"),
   410  		// error cases
   411  		[]byte("foo 223\n+PAM\n"),
   412  		[]byte(""),
   413  		[]byte("systemd abc\n+PAM\n"),
   414  	}
   415  
   416  	v, err := Version()
   417  	c.Assert(err, IsNil)
   418  	c.Check(v, Equals, 223)
   419  
   420  	v, err = Version()
   421  	c.Assert(err, IsNil)
   422  	c.Check(v, Equals, 245)
   423  
   424  	_, err = Version()
   425  	c.Assert(err, ErrorMatches, `cannot parse systemd version: expected "systemd", got "foo"`)
   426  
   427  	_, err = Version()
   428  	c.Assert(err, ErrorMatches, `cannot read systemd version: <nil>`)
   429  
   430  	_, err = Version()
   431  	c.Assert(err, ErrorMatches, `cannot convert systemd version to number: abc`)
   432  
   433  	c.Check(s.argses, DeepEquals, [][]string{
   434  		{"--version"},
   435  		{"--version"},
   436  		{"--version"},
   437  		{"--version"},
   438  		{"--version"},
   439  	})
   440  }
   441  
   442  func (s *SystemdTestSuite) TestEnable(c *C) {
   443  	err := New(SystemMode, s.rep).Enable("foo")
   444  	c.Assert(err, IsNil)
   445  	c.Check(s.argses, DeepEquals, [][]string{{"enable", "foo"}})
   446  }
   447  
   448  func (s *SystemdTestSuite) TestEnableUnderRoot(c *C) {
   449  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Enable("foo")
   450  	c.Assert(err, IsNil)
   451  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "enable", "foo"}})
   452  }
   453  
   454  func (s *SystemdTestSuite) TestMask(c *C) {
   455  	err := New(SystemMode, s.rep).Mask("foo")
   456  	c.Assert(err, IsNil)
   457  	c.Check(s.argses, DeepEquals, [][]string{{"mask", "foo"}})
   458  }
   459  
   460  func (s *SystemdTestSuite) TestMaskUnderRoot(c *C) {
   461  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Mask("foo")
   462  	c.Assert(err, IsNil)
   463  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "mask", "foo"}})
   464  }
   465  
   466  func (s *SystemdTestSuite) TestUnmask(c *C) {
   467  	err := New(SystemMode, s.rep).Unmask("foo")
   468  	c.Assert(err, IsNil)
   469  	c.Check(s.argses, DeepEquals, [][]string{{"unmask", "foo"}})
   470  }
   471  
   472  func (s *SystemdTestSuite) TestUnmaskUnderRoot(c *C) {
   473  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Unmask("foo")
   474  	c.Assert(err, IsNil)
   475  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "unmask", "foo"}})
   476  }
   477  
   478  func (s *SystemdTestSuite) TestRestart(c *C) {
   479  	restore := MockStopDelays(time.Millisecond, 25*time.Second)
   480  	defer restore()
   481  	s.outs = [][]byte{
   482  		nil, // for the "stop" itself
   483  		[]byte("ActiveState=inactive\n"),
   484  		nil, // for the "start"
   485  	}
   486  	s.errors = []error{nil, nil, nil, nil, &Timeout{}}
   487  	err := New(SystemMode, s.rep).Restart("foo", 100*time.Millisecond)
   488  	c.Assert(err, IsNil)
   489  	c.Check(s.argses, HasLen, 3)
   490  	c.Check(s.argses[0], DeepEquals, []string{"stop", "foo"})
   491  	c.Check(s.argses[1], DeepEquals, []string{"show", "--property=ActiveState", "foo"})
   492  	c.Check(s.argses[2], DeepEquals, []string{"start", "foo"})
   493  }
   494  
   495  func (s *SystemdTestSuite) TestKill(c *C) {
   496  	c.Assert(New(SystemMode, s.rep).Kill("foo", "HUP", ""), IsNil)
   497  	c.Check(s.argses, DeepEquals, [][]string{{"kill", "foo", "-s", "HUP", "--kill-who=all"}})
   498  }
   499  
   500  func (s *SystemdTestSuite) TestIsTimeout(c *C) {
   501  	c.Check(IsTimeout(os.ErrInvalid), Equals, false)
   502  	c.Check(IsTimeout(&Timeout{}), Equals, true)
   503  }
   504  
   505  func (s *SystemdTestSuite) TestLogErrJctl(c *C) {
   506  	s.jerrs = []error{&Timeout{}}
   507  
   508  	reader, err := New(SystemMode, s.rep).LogReader([]string{"foo"}, 24, false)
   509  	c.Check(err, NotNil)
   510  	c.Check(reader, IsNil)
   511  	c.Check(s.jns, DeepEquals, []string{"24"})
   512  	c.Check(s.jsvcs, DeepEquals, [][]string{{"foo"}})
   513  	c.Check(s.jfollows, DeepEquals, []bool{false})
   514  	c.Check(s.j, Equals, 1)
   515  }
   516  
   517  func (s *SystemdTestSuite) TestLogs(c *C) {
   518  	expected := `{"a": 1}
   519  {"a": 2}
   520  `
   521  	s.jouts = [][]byte{[]byte(expected)}
   522  
   523  	reader, err := New(SystemMode, s.rep).LogReader([]string{"foo"}, 24, false)
   524  	c.Check(err, IsNil)
   525  	logs, err := ioutil.ReadAll(reader)
   526  	c.Assert(err, IsNil)
   527  	c.Check(string(logs), Equals, expected)
   528  	c.Check(s.jns, DeepEquals, []string{"24"})
   529  	c.Check(s.jsvcs, DeepEquals, [][]string{{"foo"}})
   530  	c.Check(s.jfollows, DeepEquals, []bool{false})
   531  	c.Check(s.j, Equals, 1)
   532  }
   533  
   534  func (s *SystemdTestSuite) TestLogPID(c *C) {
   535  	c.Check(Log{}.PID(), Equals, "-")
   536  	c.Check(Log{"_PID": "99"}.PID(), Equals, "99")
   537  	c.Check(Log{"SYSLOG_PID": "99"}.PID(), Equals, "99")
   538  	// things starting with underscore are "trusted", so we trust
   539  	// them more than the user-settable ones:
   540  	c.Check(Log{"_PID": "42", "SYSLOG_PID": "99"}.PID(), Equals, "42")
   541  }
   542  
   543  func (s *SystemdTestSuite) TestTime(c *C) {
   544  	t, err := Log{}.Time()
   545  	c.Check(t.IsZero(), Equals, true)
   546  	c.Check(err, ErrorMatches, "no timestamp")
   547  
   548  	t, err = Log{"__REALTIME_TIMESTAMP": "what"}.Time()
   549  	c.Check(t.IsZero(), Equals, true)
   550  	c.Check(err, ErrorMatches, `timestamp not a decimal number: "what"`)
   551  
   552  	t, err = Log{"__REALTIME_TIMESTAMP": "0"}.Time()
   553  	c.Check(err, IsNil)
   554  	c.Check(t.String(), Equals, "1970-01-01 00:00:00 +0000 UTC")
   555  
   556  	t, err = Log{"__REALTIME_TIMESTAMP": "42"}.Time()
   557  	c.Check(err, IsNil)
   558  	c.Check(t.String(), Equals, "1970-01-01 00:00:00.000042 +0000 UTC")
   559  }
   560  
   561  func (s *SystemdTestSuite) TestMountUnitPath(c *C) {
   562  	c.Assert(MountUnitPath("/apps/hello/1.1"), Equals, filepath.Join(dirs.SnapServicesDir, "apps-hello-1.1.mount"))
   563  }
   564  
   565  func makeMockFile(c *C, path string) {
   566  	err := os.MkdirAll(filepath.Dir(path), 0755)
   567  	c.Assert(err, IsNil)
   568  	err = ioutil.WriteFile(path, nil, 0644)
   569  	c.Assert(err, IsNil)
   570  }
   571  
   572  func (s *SystemdTestSuite) TestAddMountUnit(c *C) {
   573  	rootDir := dirs.GlobalRootDir
   574  
   575  	restore := squashfs.MockNeedsFuse(false)
   576  	defer restore()
   577  
   578  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   579  	makeMockFile(c, mockSnapPath)
   580  
   581  	mountUnitName, err := NewUnderRoot(rootDir, SystemMode, nil).AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
   582  	c.Assert(err, IsNil)
   583  	defer os.Remove(mountUnitName)
   584  
   585  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   586  [Unit]
   587  Description=Mount unit for foo, revision 42
   588  Before=snapd.service
   589  
   590  [Mount]
   591  What=%s
   592  Where=/snap/snapname/123
   593  Type=squashfs
   594  Options=nodev,ro,x-gdu.hide
   595  LazyUnmount=yes
   596  
   597  [Install]
   598  WantedBy=multi-user.target
   599  `[1:], mockSnapPath))
   600  
   601  	c.Assert(s.argses, DeepEquals, [][]string{
   602  		{"daemon-reload"},
   603  		{"--root", rootDir, "enable", "snap-snapname-123.mount"},
   604  		{"start", "snap-snapname-123.mount"},
   605  	})
   606  }
   607  
   608  func (s *SystemdTestSuite) TestAddMountUnitForDirs(c *C) {
   609  	restore := squashfs.MockNeedsFuse(false)
   610  	defer restore()
   611  
   612  	// a directory instead of a file produces a different output
   613  	snapDir := c.MkDir()
   614  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foodir", "x1", snapDir, "/snap/snapname/x1", "squashfs")
   615  	c.Assert(err, IsNil)
   616  	defer os.Remove(mountUnitName)
   617  
   618  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   619  [Unit]
   620  Description=Mount unit for foodir, revision x1
   621  Before=snapd.service
   622  
   623  [Mount]
   624  What=%s
   625  Where=/snap/snapname/x1
   626  Type=none
   627  Options=nodev,ro,x-gdu.hide,bind
   628  LazyUnmount=yes
   629  
   630  [Install]
   631  WantedBy=multi-user.target
   632  `[1:], snapDir))
   633  
   634  	c.Assert(s.argses, DeepEquals, [][]string{
   635  		{"daemon-reload"},
   636  		{"enable", "snap-snapname-x1.mount"},
   637  		{"start", "snap-snapname-x1.mount"},
   638  	})
   639  }
   640  
   641  func (s *SystemdTestSuite) TestWriteSELinuxMountUnit(c *C) {
   642  	restore := selinux.MockIsEnabled(func() (bool, error) { return true, nil })
   643  	defer restore()
   644  	restore = selinux.MockIsEnforcing(func() (bool, error) { return true, nil })
   645  	defer restore()
   646  	restore = squashfs.MockNeedsFuse(false)
   647  	defer restore()
   648  
   649  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   650  	err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755)
   651  	c.Assert(err, IsNil)
   652  	err = ioutil.WriteFile(mockSnapPath, nil, 0644)
   653  	c.Assert(err, IsNil)
   654  
   655  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
   656  	c.Assert(err, IsNil)
   657  	defer os.Remove(mountUnitName)
   658  
   659  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   660  [Unit]
   661  Description=Mount unit for foo, revision 42
   662  Before=snapd.service
   663  
   664  [Mount]
   665  What=%s
   666  Where=/snap/snapname/123
   667  Type=squashfs
   668  Options=nodev,context=system_u:object_r:snappy_snap_t:s0,ro,x-gdu.hide
   669  LazyUnmount=yes
   670  
   671  [Install]
   672  WantedBy=multi-user.target
   673  `[1:], mockSnapPath))
   674  }
   675  
   676  func (s *SystemdTestSuite) TestFuseInContainer(c *C) {
   677  	if !osutil.FileExists("/dev/fuse") {
   678  		c.Skip("No /dev/fuse on the system")
   679  	}
   680  
   681  	systemdCmd := testutil.MockCommand(c, "systemd-detect-virt", `
   682  echo lxc
   683  exit 0
   684  	`)
   685  	defer systemdCmd.Restore()
   686  
   687  	fuseCmd := testutil.MockCommand(c, "squashfuse", `
   688  exit 0
   689  	`)
   690  	defer fuseCmd.Restore()
   691  
   692  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   693  	err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755)
   694  	c.Assert(err, IsNil)
   695  	err = ioutil.WriteFile(mockSnapPath, nil, 0644)
   696  	c.Assert(err, IsNil)
   697  
   698  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "x1", mockSnapPath, "/snap/snapname/123", "squashfs")
   699  	c.Assert(err, IsNil)
   700  	defer os.Remove(mountUnitName)
   701  
   702  	c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   703  [Unit]
   704  Description=Mount unit for foo, revision x1
   705  Before=snapd.service
   706  
   707  [Mount]
   708  What=%s
   709  Where=/snap/snapname/123
   710  Type=fuse.squashfuse
   711  Options=nodev,ro,x-gdu.hide,allow_other
   712  LazyUnmount=yes
   713  
   714  [Install]
   715  WantedBy=multi-user.target
   716  `[1:], mockSnapPath))
   717  }
   718  
   719  func (s *SystemdTestSuite) TestFuseOutsideContainer(c *C) {
   720  	systemdCmd := testutil.MockCommand(c, "systemd-detect-virt", `
   721  echo none
   722  exit 0
   723  	`)
   724  	defer systemdCmd.Restore()
   725  
   726  	fuseCmd := testutil.MockCommand(c, "squashfuse", `
   727  exit 0
   728  	`)
   729  	defer fuseCmd.Restore()
   730  
   731  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   732  	err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755)
   733  	c.Assert(err, IsNil)
   734  	err = ioutil.WriteFile(mockSnapPath, nil, 0644)
   735  	c.Assert(err, IsNil)
   736  
   737  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "x1", mockSnapPath, "/snap/snapname/123", "squashfs")
   738  	c.Assert(err, IsNil)
   739  	defer os.Remove(mountUnitName)
   740  
   741  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   742  [Unit]
   743  Description=Mount unit for foo, revision x1
   744  Before=snapd.service
   745  
   746  [Mount]
   747  What=%s
   748  Where=/snap/snapname/123
   749  Type=squashfs
   750  Options=nodev,ro,x-gdu.hide
   751  LazyUnmount=yes
   752  
   753  [Install]
   754  WantedBy=multi-user.target
   755  `[1:], mockSnapPath))
   756  }
   757  
   758  func (s *SystemdTestSuite) TestJctl(c *C) {
   759  	var args []string
   760  	var err error
   761  	MockOsutilStreamCommand(func(name string, myargs ...string) (io.ReadCloser, error) {
   762  		c.Check(cap(myargs) <= len(myargs)+2, Equals, true, Commentf("cap:%d, len:%d", cap(myargs), len(myargs)))
   763  		args = myargs
   764  		return nil, nil
   765  	})
   766  
   767  	_, err = Jctl([]string{"foo", "bar"}, 10, false)
   768  	c.Assert(err, IsNil)
   769  	c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "-n", "10", "-u", "foo", "-u", "bar"})
   770  	_, err = Jctl([]string{"foo", "bar", "baz"}, 99, true)
   771  	c.Assert(err, IsNil)
   772  	c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "-n", "99", "-f", "-u", "foo", "-u", "bar", "-u", "baz"})
   773  	_, err = Jctl([]string{"foo", "bar"}, -1, false)
   774  	c.Assert(err, IsNil)
   775  	c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "--no-tail", "-u", "foo", "-u", "bar"})
   776  }
   777  
   778  func (s *SystemdTestSuite) TestIsActiveUnderRoot(c *C) {
   779  	sysErr := &Error{}
   780  	// manpage states that systemctl returns exit code 3 for inactive
   781  	// services, however we should check any non-0 exit status
   782  	sysErr.SetExitCode(1)
   783  	sysErr.SetMsg([]byte("inactive\n"))
   784  	s.errors = []error{sysErr}
   785  
   786  	_, err := NewUnderRoot("xyzzy", SystemMode, s.rep).IsActive("foo")
   787  	c.Assert(err, IsNil)
   788  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "is-active", "foo"}})
   789  }
   790  
   791  func (s *SystemdTestSuite) TestIsActiveIsInactive(c *C) {
   792  	sysErr := &Error{}
   793  	// manpage states that systemctl returns exit code 3 for inactive
   794  	// services, however we should check any non-0 exit status
   795  	sysErr.SetExitCode(1)
   796  	sysErr.SetMsg([]byte("inactive\n"))
   797  	s.errors = []error{sysErr}
   798  
   799  	active, err := New(SystemMode, s.rep).IsActive("foo")
   800  	c.Assert(active, Equals, false)
   801  	c.Assert(err, IsNil)
   802  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
   803  }
   804  
   805  func (s *SystemdTestSuite) TestIsActiveIsFailed(c *C) {
   806  	sysErr := &Error{}
   807  	// seen in the wild to be reported for a 'failed' service
   808  	sysErr.SetExitCode(3)
   809  	sysErr.SetMsg([]byte("failed\n"))
   810  	s.errors = []error{sysErr}
   811  
   812  	active, err := New(SystemMode, s.rep).IsActive("foo")
   813  	c.Assert(active, Equals, false)
   814  	c.Assert(err, IsNil)
   815  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
   816  }
   817  
   818  func (s *SystemdTestSuite) TestIsActiveIsActive(c *C) {
   819  	s.errors = []error{nil}
   820  
   821  	active, err := New(SystemMode, s.rep).IsActive("foo")
   822  	c.Assert(active, Equals, true)
   823  	c.Assert(err, IsNil)
   824  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
   825  }
   826  
   827  func (s *SystemdTestSuite) TestIsActiveUnexpectedErr(c *C) {
   828  	sysErr := &Error{}
   829  	sysErr.SetExitCode(1)
   830  	sysErr.SetMsg([]byte("random-failure\n"))
   831  	s.errors = []error{sysErr}
   832  
   833  	active, err := NewUnderRoot("xyzzy", SystemMode, s.rep).IsActive("foo")
   834  	c.Assert(active, Equals, false)
   835  	c.Assert(err, ErrorMatches, ".* failed with exit status 1: random-failure\n")
   836  }
   837  
   838  func makeMockMountUnit(c *C, mountDir string) string {
   839  	mountUnit := MountUnitPath(dirs.StripRootDir(mountDir))
   840  	err := ioutil.WriteFile(mountUnit, nil, 0644)
   841  	c.Assert(err, IsNil)
   842  	return mountUnit
   843  }
   844  
   845  // FIXME: also test for the "IsMounted" case
   846  func (s *SystemdTestSuite) TestRemoveMountUnit(c *C) {
   847  	rootDir := dirs.GlobalRootDir
   848  
   849  	restore := osutil.MockMountInfo("")
   850  	defer restore()
   851  
   852  	mountDir := rootDir + "/snap/foo/42"
   853  	mountUnit := makeMockMountUnit(c, mountDir)
   854  	err := NewUnderRoot(rootDir, SystemMode, nil).RemoveMountUnitFile(mountDir)
   855  
   856  	c.Assert(err, IsNil)
   857  	// the file is gone
   858  	c.Check(osutil.FileExists(mountUnit), Equals, false)
   859  	// and the unit is disabled and the daemon reloaded
   860  	c.Check(s.argses, DeepEquals, [][]string{
   861  		{"--root", rootDir, "disable", "snap-foo-42.mount"},
   862  		{"daemon-reload"},
   863  	})
   864  }
   865  
   866  func (s *SystemdTestSuite) TestDaemonReloadMutex(c *C) {
   867  	s.testDaemonReloadMutex(c, Systemd.DaemonReload)
   868  }
   869  
   870  func (s *SystemdTestSuite) testDaemonReloadMutex(c *C, reload func(Systemd) error) {
   871  	rootDir := dirs.GlobalRootDir
   872  	sysd := NewUnderRoot(rootDir, SystemMode, nil)
   873  
   874  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   875  	makeMockFile(c, mockSnapPath)
   876  
   877  	// create a go-routine that will try to daemon-reload like crazy
   878  	stopCh := make(chan bool, 1)
   879  	stoppedCh := make(chan bool, 1)
   880  	go func() {
   881  		for {
   882  			sysd.DaemonReload()
   883  			select {
   884  			case <-stopCh:
   885  				close(stoppedCh)
   886  				return
   887  			default:
   888  				//pass
   889  			}
   890  		}
   891  	}()
   892  
   893  	// And now add a mount unit file while the go-routine tries to
   894  	// daemon-reload. This will be serialized, if not this would
   895  	// panic because systemd.daemonReloadNoLock ensures the lock is
   896  	// taken when this happens.
   897  	_, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/foo/42", "squashfs")
   898  	c.Assert(err, IsNil)
   899  	close(stopCh)
   900  	<-stoppedCh
   901  }
   902  
   903  func (s *SystemdTestSuite) TestDaemonReexecMutex(c *C) {
   904  	s.testDaemonReloadMutex(c, Systemd.DaemonReexec)
   905  }
   906  
   907  func (s *SystemdTestSuite) TestUserMode(c *C) {
   908  	rootDir := dirs.GlobalRootDir
   909  	sysd := NewUnderRoot(rootDir, UserMode, nil)
   910  
   911  	c.Assert(sysd.Enable("foo"), IsNil)
   912  	c.Check(s.argses[0], DeepEquals, []string{"--user", "--root", rootDir, "enable", "foo"})
   913  	c.Assert(sysd.Start("foo"), IsNil)
   914  	c.Check(s.argses[1], DeepEquals, []string{"--user", "start", "foo"})
   915  }
   916  
   917  func (s *SystemdTestSuite) TestGlobalUserMode(c *C) {
   918  	rootDir := dirs.GlobalRootDir
   919  	sysd := NewUnderRoot(rootDir, GlobalUserMode, nil)
   920  
   921  	c.Assert(sysd.Enable("foo"), IsNil)
   922  	c.Check(s.argses[0], DeepEquals, []string{"--user", "--global", "--root", rootDir, "enable", "foo"})
   923  	c.Assert(sysd.Disable("foo"), IsNil)
   924  	c.Check(s.argses[1], DeepEquals, []string{"--user", "--global", "--root", rootDir, "disable", "foo"})
   925  	c.Assert(sysd.Mask("foo"), IsNil)
   926  	c.Check(s.argses[2], DeepEquals, []string{"--user", "--global", "--root", rootDir, "mask", "foo"})
   927  	c.Assert(sysd.Unmask("foo"), IsNil)
   928  	c.Check(s.argses[3], DeepEquals, []string{"--user", "--global", "--root", rootDir, "unmask", "foo"})
   929  	_, err := sysd.IsEnabled("foo")
   930  	c.Check(err, IsNil)
   931  	c.Check(s.argses[4], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "foo"})
   932  
   933  	// Commands that don't make sense for GlobalUserMode panic
   934  	c.Check(sysd.DaemonReload, Panics, "cannot call daemon-reload with GlobalUserMode")
   935  	c.Check(sysd.DaemonReexec, Panics, "cannot call daemon-reexec with GlobalUserMode")
   936  	c.Check(func() { sysd.Start("foo") }, Panics, "cannot call start with GlobalUserMode")
   937  	c.Check(func() { sysd.StartNoBlock("foo") }, Panics, "cannot call start with GlobalUserMode")
   938  	c.Check(func() { sysd.Stop("foo", 0) }, Panics, "cannot call stop with GlobalUserMode")
   939  	c.Check(func() { sysd.Restart("foo", 0) }, Panics, "cannot call restart with GlobalUserMode")
   940  	c.Check(func() { sysd.Kill("foo", "HUP", "") }, Panics, "cannot call kill with GlobalUserMode")
   941  	c.Check(func() { sysd.Status("foo") }, Panics, "cannot call status with GlobalUserMode")
   942  	c.Check(func() { sysd.IsActive("foo") }, Panics, "cannot call is-active with GlobalUserMode")
   943  }
   944  
   945  const unitTemplate = `
   946  [Unit]
   947  Description=Mount unit for foo, revision 42
   948  Before=snapd.service
   949  
   950  [Mount]
   951  What=%s
   952  Where=/snap/snapname/123
   953  Type=%s
   954  Options=%s
   955  LazyUnmount=yes
   956  
   957  [Install]
   958  WantedBy=multi-user.target
   959  `
   960  
   961  func (s *SystemdTestSuite) TestPreseedModeAddMountUnit(c *C) {
   962  	sysd := NewEmulationMode(dirs.GlobalRootDir)
   963  
   964  	restore := squashfs.MockNeedsFuse(false)
   965  	defer restore()
   966  
   967  	mockMountCmd := testutil.MockCommand(c, "mount", "")
   968  	defer mockMountCmd.Restore()
   969  
   970  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   971  	makeMockFile(c, mockSnapPath)
   972  
   973  	mountUnitName, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
   974  	c.Assert(err, IsNil)
   975  	defer os.Remove(mountUnitName)
   976  
   977  	// systemd was not called
   978  	c.Check(s.argses, HasLen, 0)
   979  	// mount was called
   980  	c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "squashfs", mockSnapPath, "/snap/snapname/123", "-o", "nodev,ro,x-gdu.hide"})
   981  	// unit was enabled with a symlink
   982  	c.Check(osutil.IsSymlink(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", mountUnitName)), Equals, true)
   983  	c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(unitTemplate[1:], mockSnapPath, "squashfs", "nodev,ro,x-gdu.hide"))
   984  }
   985  
   986  func (s *SystemdTestSuite) TestPreseedModeAddMountUnitWithFuse(c *C) {
   987  	sysd := NewEmulationMode(dirs.GlobalRootDir)
   988  
   989  	restore := MockSquashFsType(func() (string, []string) { return "fuse.squashfuse", []string{"a,b,c"} })
   990  	defer restore()
   991  
   992  	mockMountCmd := testutil.MockCommand(c, "mount", "")
   993  	defer mockMountCmd.Restore()
   994  
   995  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   996  	makeMockFile(c, mockSnapPath)
   997  
   998  	mountUnitName, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
   999  	c.Assert(err, IsNil)
  1000  	defer os.Remove(mountUnitName)
  1001  
  1002  	c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "fuse.squashfuse", mockSnapPath, "/snap/snapname/123", "-o", "nodev,a,b,c"})
  1003  	c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(unitTemplate[1:], mockSnapPath, "squashfs", "nodev,ro,x-gdu.hide"))
  1004  }
  1005  
  1006  func (s *SystemdTestSuite) TestPreseedModeMountError(c *C) {
  1007  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1008  
  1009  	restore := squashfs.MockNeedsFuse(false)
  1010  	defer restore()
  1011  
  1012  	mockMountCmd := testutil.MockCommand(c, "mount", `echo "some failure"; exit 1`)
  1013  	defer mockMountCmd.Restore()
  1014  
  1015  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
  1016  	makeMockFile(c, mockSnapPath)
  1017  
  1018  	_, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
  1019  	c.Assert(err, ErrorMatches, `cannot mount .*/var/lib/snappy/snaps/foo_1.0.snap \(squashfs\) at /snap/snapname/123 in preseed mode: exit status 1; some failure\n`)
  1020  }
  1021  
  1022  func (s *SystemdTestSuite) TestPreseedModeRemoveMountUnit(c *C) {
  1023  	mountDir := dirs.GlobalRootDir + "/snap/foo/42"
  1024  
  1025  	restore := MockOsutilIsMounted(func(path string) (bool, error) {
  1026  		c.Check(path, Equals, mountDir)
  1027  		return true, nil
  1028  	})
  1029  	defer restore()
  1030  
  1031  	mockUmountCmd := testutil.MockCommand(c, "umount", "")
  1032  	defer mockUmountCmd.Restore()
  1033  
  1034  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1035  
  1036  	mountUnit := makeMockMountUnit(c, mountDir)
  1037  	symlinkPath := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", filepath.Base(mountUnit))
  1038  	c.Assert(os.Symlink(mountUnit, symlinkPath), IsNil)
  1039  	c.Assert(sysd.RemoveMountUnitFile(mountDir), IsNil)
  1040  
  1041  	// the file is gone
  1042  	c.Check(osutil.FileExists(mountUnit), Equals, false)
  1043  	// unit symlink is gone
  1044  	c.Check(osutil.IsSymlink(symlinkPath), Equals, false)
  1045  	// and systemd was not called
  1046  	c.Check(s.argses, HasLen, 0)
  1047  	// umount was called
  1048  	c.Check(mockUmountCmd.Calls(), DeepEquals, [][]string{{"umount", "-d", "-l", mountDir}})
  1049  }
  1050  
  1051  func (s *SystemdTestSuite) TestPreseedModeRemoveMountUnitUnmounted(c *C) {
  1052  	mountDir := dirs.GlobalRootDir + "/snap/foo/42"
  1053  
  1054  	restore := MockOsutilIsMounted(func(path string) (bool, error) {
  1055  		c.Check(path, Equals, mountDir)
  1056  		return false, nil
  1057  	})
  1058  	defer restore()
  1059  
  1060  	mockUmountCmd := testutil.MockCommand(c, "umount", "")
  1061  	defer mockUmountCmd.Restore()
  1062  
  1063  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1064  	mountUnit := makeMockMountUnit(c, mountDir)
  1065  	symlinkPath := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", filepath.Base(mountUnit))
  1066  	c.Assert(os.Symlink(mountUnit, symlinkPath), IsNil)
  1067  
  1068  	c.Assert(sysd.RemoveMountUnitFile(mountDir), IsNil)
  1069  
  1070  	// the file is gone
  1071  	c.Check(osutil.FileExists(mountUnit), Equals, false)
  1072  	// unit symlink is gone
  1073  	c.Check(osutil.IsSymlink(symlinkPath), Equals, false)
  1074  	// and systemd was not called
  1075  	c.Check(s.argses, HasLen, 0)
  1076  	// umount was not called
  1077  	c.Check(mockUmountCmd.Calls(), HasLen, 0)
  1078  }
  1079  
  1080  func (s *SystemdTestSuite) TestPreseedModeBindmountNotSupported(c *C) {
  1081  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1082  
  1083  	restore := squashfs.MockNeedsFuse(false)
  1084  	defer restore()
  1085  
  1086  	mockSnapPath := c.MkDir()
  1087  
  1088  	_, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "")
  1089  	c.Assert(err, ErrorMatches, `bind-mounted directory is not supported in emulation mode`)
  1090  }
  1091  
  1092  func (s *SystemdTestSuite) TestEnableInEmulationMode(c *C) {
  1093  	sysd := NewEmulationMode("/path")
  1094  	c.Assert(sysd.Enable("foo"), IsNil)
  1095  
  1096  	sysd = NewEmulationMode("")
  1097  	c.Assert(sysd.Enable("bar"), IsNil)
  1098  	c.Check(s.argses, DeepEquals, [][]string{
  1099  		{"--root", "/path", "enable", "foo"},
  1100  		{"--root", dirs.GlobalRootDir, "enable", "bar"}})
  1101  }
  1102  
  1103  func (s *SystemdTestSuite) TestDisableInEmulationMode(c *C) {
  1104  	sysd := NewEmulationMode("/path")
  1105  	c.Assert(sysd.Disable("foo"), IsNil)
  1106  
  1107  	c.Check(s.argses, DeepEquals, [][]string{
  1108  		{"--root", "/path", "disable", "foo"}})
  1109  }
  1110  
  1111  func (s *SystemdTestSuite) TestMaskInEmulationMode(c *C) {
  1112  	sysd := NewEmulationMode("/path")
  1113  	c.Assert(sysd.Mask("foo"), IsNil)
  1114  
  1115  	c.Check(s.argses, DeepEquals, [][]string{
  1116  		{"--root", "/path", "mask", "foo"}})
  1117  }
  1118  
  1119  func (s *SystemdTestSuite) TestUnmaskInEmulationMode(c *C) {
  1120  	sysd := NewEmulationMode("/path")
  1121  	c.Assert(sysd.Unmask("foo"), IsNil)
  1122  
  1123  	c.Check(s.argses, DeepEquals, [][]string{
  1124  		{"--root", "/path", "unmask", "foo"}})
  1125  }