github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/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  	"encoding/json"
    25  	"fmt"
    26  	"io"
    27  	"io/ioutil"
    28  	"os"
    29  	"path/filepath"
    30  	"strconv"
    31  	"testing"
    32  	"time"
    33  
    34  	. "gopkg.in/check.v1"
    35  
    36  	"github.com/snapcore/snapd/dirs"
    37  	"github.com/snapcore/snapd/gadget/quantity"
    38  	"github.com/snapcore/snapd/osutil"
    39  	"github.com/snapcore/snapd/osutil/squashfs"
    40  	"github.com/snapcore/snapd/sandbox/selinux"
    41  	. "github.com/snapcore/snapd/systemd"
    42  	"github.com/snapcore/snapd/testutil"
    43  )
    44  
    45  type testreporter struct {
    46  	msgs []string
    47  }
    48  
    49  func (tr *testreporter) Notify(msg string) {
    50  	tr.msgs = append(tr.msgs, msg)
    51  }
    52  
    53  // Hook up check.v1 into the "go test" runner
    54  func Test(t *testing.T) { TestingT(t) }
    55  
    56  // systemd's testsuite
    57  type SystemdTestSuite struct {
    58  	i      int
    59  	argses [][]string
    60  	errors []error
    61  	outs   [][]byte
    62  
    63  	j        int
    64  	jns      []string
    65  	jsvcs    [][]string
    66  	jouts    [][]byte
    67  	jerrs    []error
    68  	jfollows []bool
    69  
    70  	rep *testreporter
    71  
    72  	restoreSystemctl  func()
    73  	restoreJournalctl func()
    74  	restoreSELinux    func()
    75  }
    76  
    77  var _ = Suite(&SystemdTestSuite{})
    78  
    79  func (s *SystemdTestSuite) SetUpTest(c *C) {
    80  	dirs.SetRootDir(c.MkDir())
    81  	err := os.MkdirAll(dirs.SnapServicesDir, 0755)
    82  	c.Assert(err, IsNil)
    83  	c.Assert(os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755), IsNil)
    84  
    85  	// force UTC timezone, for reproducible timestamps
    86  	os.Setenv("TZ", "")
    87  
    88  	s.restoreSystemctl = MockSystemctl(s.myRun)
    89  	s.i = 0
    90  	s.argses = nil
    91  	s.errors = nil
    92  	s.outs = nil
    93  
    94  	s.restoreJournalctl = MockJournalctl(s.myJctl)
    95  	s.j = 0
    96  	s.jns = nil
    97  	s.jsvcs = nil
    98  	s.jouts = nil
    99  	s.jerrs = nil
   100  	s.jfollows = nil
   101  
   102  	s.rep = new(testreporter)
   103  
   104  	s.restoreSELinux = selinux.MockIsEnabled(func() (bool, error) { return false, nil })
   105  }
   106  
   107  func (s *SystemdTestSuite) TearDownTest(c *C) {
   108  	s.restoreSystemctl()
   109  	s.restoreJournalctl()
   110  	s.restoreSELinux()
   111  }
   112  
   113  func (s *SystemdTestSuite) myRun(args ...string) (out []byte, err error) {
   114  	s.argses = append(s.argses, args)
   115  	if s.i < len(s.outs) {
   116  		out = s.outs[s.i]
   117  	}
   118  	if s.i < len(s.errors) {
   119  		err = s.errors[s.i]
   120  	}
   121  	s.i++
   122  	return out, err
   123  }
   124  
   125  func (s *SystemdTestSuite) myJctl(svcs []string, n int, follow bool) (io.ReadCloser, error) {
   126  	var err error
   127  	var out []byte
   128  
   129  	s.jns = append(s.jns, strconv.Itoa(n))
   130  	s.jsvcs = append(s.jsvcs, svcs)
   131  	s.jfollows = append(s.jfollows, follow)
   132  
   133  	if s.j < len(s.jouts) {
   134  		out = s.jouts[s.j]
   135  	}
   136  	if s.j < len(s.jerrs) {
   137  		err = s.jerrs[s.j]
   138  	}
   139  	s.j++
   140  
   141  	if out == nil {
   142  		return nil, err
   143  	}
   144  
   145  	return ioutil.NopCloser(bytes.NewReader(out)), err
   146  }
   147  
   148  func (s *SystemdTestSuite) TestDaemonReload(c *C) {
   149  	err := New(SystemMode, s.rep).DaemonReload()
   150  	c.Assert(err, IsNil)
   151  	c.Assert(s.argses, DeepEquals, [][]string{{"daemon-reload"}})
   152  }
   153  
   154  func (s *SystemdTestSuite) TestDaemonReexec(c *C) {
   155  	err := New(SystemMode, s.rep).DaemonReexec()
   156  	c.Assert(err, IsNil)
   157  	c.Assert(s.argses, DeepEquals, [][]string{{"daemon-reexec"}})
   158  }
   159  
   160  func (s *SystemdTestSuite) TestStart(c *C) {
   161  	err := New(SystemMode, s.rep).Start("foo")
   162  	c.Assert(err, IsNil)
   163  	c.Check(s.argses, DeepEquals, [][]string{{"start", "foo"}})
   164  }
   165  
   166  func (s *SystemdTestSuite) TestStartMany(c *C) {
   167  	err := New(SystemMode, s.rep).Start("foo", "bar", "baz")
   168  	c.Assert(err, IsNil)
   169  	c.Check(s.argses, DeepEquals, [][]string{{"start", "foo", "bar", "baz"}})
   170  }
   171  
   172  func (s *SystemdTestSuite) TestStop(c *C) {
   173  	restore := MockStopDelays(time.Millisecond, 25*time.Second)
   174  	defer restore()
   175  	s.outs = [][]byte{
   176  		nil, // for the "stop" itself
   177  		[]byte("ActiveState=whatever\n"),
   178  		[]byte("ActiveState=active\n"),
   179  		[]byte("ActiveState=inactive\n"),
   180  	}
   181  	s.errors = []error{nil, nil, nil, nil, &Timeout{}}
   182  	err := New(SystemMode, s.rep).Stop("foo", 1*time.Second)
   183  	c.Assert(err, IsNil)
   184  	c.Assert(s.argses, HasLen, 4)
   185  	c.Check(s.argses[0], DeepEquals, []string{"stop", "foo"})
   186  	c.Check(s.argses[1], DeepEquals, []string{"show", "--property=ActiveState", "foo"})
   187  	c.Check(s.argses[1], DeepEquals, s.argses[2])
   188  	c.Check(s.argses[1], DeepEquals, s.argses[3])
   189  }
   190  
   191  func (s *SystemdTestSuite) TestStatus(c *C) {
   192  	s.outs = [][]byte{
   193  		[]byte(`
   194  Type=simple
   195  Id=foo.service
   196  ActiveState=active
   197  UnitFileState=enabled
   198  
   199  Type=simple
   200  Id=bar.service
   201  ActiveState=reloading
   202  UnitFileState=static
   203  
   204  Type=potato
   205  Id=baz.service
   206  ActiveState=inactive
   207  UnitFileState=disabled
   208  
   209  Type=
   210  Id=missing.service
   211  ActiveState=inactive
   212  UnitFileState=
   213  `[1:]),
   214  		[]byte(`
   215  Id=some.timer
   216  ActiveState=active
   217  UnitFileState=enabled
   218  
   219  Id=other.socket
   220  ActiveState=active
   221  UnitFileState=disabled
   222  `[1:]),
   223  	}
   224  	s.errors = []error{nil}
   225  	out, err := New(SystemMode, s.rep).Status("foo.service", "bar.service", "baz.service", "missing.service", "some.timer", "other.socket")
   226  	c.Assert(err, IsNil)
   227  	c.Check(out, DeepEquals, []*UnitStatus{
   228  		{
   229  			Daemon:    "simple",
   230  			UnitName:  "foo.service",
   231  			Active:    true,
   232  			Enabled:   true,
   233  			Installed: true,
   234  		}, {
   235  			Daemon:    "simple",
   236  			UnitName:  "bar.service",
   237  			Active:    true,
   238  			Enabled:   true,
   239  			Installed: true,
   240  		}, {
   241  			Daemon:    "potato",
   242  			UnitName:  "baz.service",
   243  			Active:    false,
   244  			Enabled:   false,
   245  			Installed: true,
   246  		}, {
   247  			Daemon:    "",
   248  			UnitName:  "missing.service",
   249  			Active:    false,
   250  			Enabled:   false,
   251  			Installed: false,
   252  		}, {
   253  			UnitName:  "some.timer",
   254  			Active:    true,
   255  			Enabled:   true,
   256  			Installed: true,
   257  		}, {
   258  			UnitName:  "other.socket",
   259  			Active:    true,
   260  			Enabled:   false,
   261  			Installed: true,
   262  		},
   263  	})
   264  	c.Check(s.rep.msgs, IsNil)
   265  	c.Assert(s.argses, DeepEquals, [][]string{
   266  		{"show", "--property=Id,ActiveState,UnitFileState,Type", "foo.service", "bar.service", "baz.service", "missing.service"},
   267  		{"show", "--property=Id,ActiveState,UnitFileState", "some.timer", "other.socket"},
   268  	})
   269  }
   270  
   271  func (s *SystemdTestSuite) TestStatusBadNumberOfValues(c *C) {
   272  	s.outs = [][]byte{
   273  		[]byte(`
   274  Type=simple
   275  Id=foo.service
   276  ActiveState=active
   277  UnitFileState=enabled
   278  
   279  Type=simple
   280  Id=foo.service
   281  ActiveState=active
   282  UnitFileState=enabled
   283  `[1:]),
   284  	}
   285  	s.errors = []error{nil}
   286  	out, err := New(SystemMode, s.rep).Status("foo.service")
   287  	c.Check(err, ErrorMatches, "cannot get unit status: expected 1 results, got 2")
   288  	c.Check(out, IsNil)
   289  	c.Check(s.rep.msgs, IsNil)
   290  }
   291  
   292  func (s *SystemdTestSuite) TestStatusBadLine(c *C) {
   293  	s.outs = [][]byte{
   294  		[]byte(`
   295  Type=simple
   296  Id=foo.service
   297  ActiveState=active
   298  UnitFileState=enabled
   299  Potatoes
   300  `[1:]),
   301  	}
   302  	s.errors = []error{nil}
   303  	out, err := New(SystemMode, s.rep).Status("foo.service")
   304  	c.Assert(err, ErrorMatches, `.* bad line "Potatoes" .*`)
   305  	c.Check(out, IsNil)
   306  }
   307  
   308  func (s *SystemdTestSuite) TestStatusBadId(c *C) {
   309  	s.outs = [][]byte{
   310  		[]byte(`
   311  Type=simple
   312  Id=bar.service
   313  ActiveState=active
   314  UnitFileState=enabled
   315  `[1:]),
   316  	}
   317  	s.errors = []error{nil}
   318  	out, err := New(SystemMode, s.rep).Status("foo.service")
   319  	c.Assert(err, ErrorMatches, `.* queried status of "foo.service" but got status of "bar.service"`)
   320  	c.Check(out, IsNil)
   321  }
   322  
   323  func (s *SystemdTestSuite) TestStatusBadField(c *C) {
   324  	s.outs = [][]byte{
   325  		[]byte(`
   326  Type=simple
   327  Id=foo.service
   328  ActiveState=active
   329  UnitFileState=enabled
   330  Potatoes=false
   331  `[1:]),
   332  	}
   333  	s.errors = []error{nil}
   334  	out, err := New(SystemMode, s.rep).Status("foo.service")
   335  	c.Assert(err, ErrorMatches, `.* unexpected field "Potatoes" .*`)
   336  	c.Check(out, IsNil)
   337  }
   338  
   339  func (s *SystemdTestSuite) TestStatusMissingRequiredFieldService(c *C) {
   340  	s.outs = [][]byte{
   341  		[]byte(`
   342  Id=foo.service
   343  ActiveState=active
   344  `[1:]),
   345  	}
   346  	s.errors = []error{nil}
   347  	out, err := New(SystemMode, s.rep).Status("foo.service")
   348  	c.Assert(err, ErrorMatches, `.* missing UnitFileState, Type .*`)
   349  	c.Check(out, IsNil)
   350  }
   351  
   352  func (s *SystemdTestSuite) TestStatusMissingRequiredFieldTimer(c *C) {
   353  	s.outs = [][]byte{
   354  		[]byte(`
   355  Id=foo.timer
   356  ActiveState=active
   357  `[1:]),
   358  	}
   359  	s.errors = []error{nil}
   360  	out, err := New(SystemMode, s.rep).Status("foo.timer")
   361  	c.Assert(err, ErrorMatches, `.* missing UnitFileState .*`)
   362  	c.Check(out, IsNil)
   363  }
   364  
   365  func (s *SystemdTestSuite) TestStatusDupeField(c *C) {
   366  	s.outs = [][]byte{
   367  		[]byte(`
   368  Type=simple
   369  Id=foo.service
   370  ActiveState=active
   371  ActiveState=active
   372  UnitFileState=enabled
   373  `[1:]),
   374  	}
   375  	s.errors = []error{nil}
   376  	out, err := New(SystemMode, s.rep).Status("foo.service")
   377  	c.Assert(err, ErrorMatches, `.* duplicate field "ActiveState" .*`)
   378  	c.Check(out, IsNil)
   379  }
   380  
   381  func (s *SystemdTestSuite) TestStatusEmptyField(c *C) {
   382  	s.outs = [][]byte{
   383  		[]byte(`
   384  Type=simple
   385  Id=
   386  ActiveState=active
   387  UnitFileState=enabled
   388  `[1:]),
   389  	}
   390  	s.errors = []error{nil}
   391  	out, err := New(SystemMode, s.rep).Status("foo.service")
   392  	c.Assert(err, ErrorMatches, `.* empty field "Id" .*`)
   393  	c.Check(out, IsNil)
   394  }
   395  
   396  func (s *SystemdTestSuite) TestStopTimeout(c *C) {
   397  	restore := MockStopDelays(time.Millisecond, 25*time.Second)
   398  	defer restore()
   399  	err := New(SystemMode, s.rep).Stop("foo", 10*time.Millisecond)
   400  	c.Assert(err, FitsTypeOf, &Timeout{})
   401  	c.Assert(len(s.rep.msgs) > 0, Equals, true)
   402  	c.Check(s.rep.msgs[0], Equals, "Waiting for foo to stop.")
   403  }
   404  
   405  func (s *SystemdTestSuite) TestDisable(c *C) {
   406  	err := New(SystemMode, s.rep).Disable("foo")
   407  	c.Assert(err, IsNil)
   408  	c.Check(s.argses, DeepEquals, [][]string{{"disable", "foo"}})
   409  }
   410  
   411  func (s *SystemdTestSuite) TestUnderRootDisable(c *C) {
   412  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Disable("foo")
   413  	c.Assert(err, IsNil)
   414  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "disable", "foo"}})
   415  }
   416  
   417  func (s *SystemdTestSuite) TestAvailable(c *C) {
   418  	err := Available()
   419  	c.Assert(err, IsNil)
   420  	c.Check(s.argses, DeepEquals, [][]string{{"--version"}})
   421  }
   422  
   423  func (s *SystemdTestSuite) TestVersion(c *C) {
   424  	s.outs = [][]byte{
   425  		[]byte("systemd 223\n+PAM\n"),
   426  		[]byte("systemd 245 (245.4-4ubuntu3)\n+PAM +AUDIT +SELINUX +IMA\n"),
   427  		// error cases
   428  		[]byte("foo 223\n+PAM\n"),
   429  		[]byte(""),
   430  		[]byte("systemd abc\n+PAM\n"),
   431  	}
   432  
   433  	v, err := Version()
   434  	c.Assert(err, IsNil)
   435  	c.Check(v, Equals, 223)
   436  
   437  	v, err = Version()
   438  	c.Assert(err, IsNil)
   439  	c.Check(v, Equals, 245)
   440  
   441  	_, err = Version()
   442  	c.Assert(err, ErrorMatches, `cannot parse systemd version: expected "systemd", got "foo"`)
   443  
   444  	_, err = Version()
   445  	c.Assert(err, ErrorMatches, `cannot read systemd version: <nil>`)
   446  
   447  	_, err = Version()
   448  	c.Assert(err, ErrorMatches, `cannot convert systemd version to number: abc`)
   449  
   450  	c.Check(s.argses, DeepEquals, [][]string{
   451  		{"--version"},
   452  		{"--version"},
   453  		{"--version"},
   454  		{"--version"},
   455  		{"--version"},
   456  	})
   457  }
   458  
   459  func (s *SystemdTestSuite) TestEnable(c *C) {
   460  	err := New(SystemMode, s.rep).Enable("foo")
   461  	c.Assert(err, IsNil)
   462  	c.Check(s.argses, DeepEquals, [][]string{{"enable", "foo"}})
   463  }
   464  
   465  func (s *SystemdTestSuite) TestEnableUnderRoot(c *C) {
   466  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Enable("foo")
   467  	c.Assert(err, IsNil)
   468  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "enable", "foo"}})
   469  }
   470  
   471  func (s *SystemdTestSuite) TestMask(c *C) {
   472  	err := New(SystemMode, s.rep).Mask("foo")
   473  	c.Assert(err, IsNil)
   474  	c.Check(s.argses, DeepEquals, [][]string{{"mask", "foo"}})
   475  }
   476  
   477  func (s *SystemdTestSuite) TestMaskUnderRoot(c *C) {
   478  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Mask("foo")
   479  	c.Assert(err, IsNil)
   480  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "mask", "foo"}})
   481  }
   482  
   483  func (s *SystemdTestSuite) TestUnmask(c *C) {
   484  	err := New(SystemMode, s.rep).Unmask("foo")
   485  	c.Assert(err, IsNil)
   486  	c.Check(s.argses, DeepEquals, [][]string{{"unmask", "foo"}})
   487  }
   488  
   489  func (s *SystemdTestSuite) TestUnmaskUnderRoot(c *C) {
   490  	err := NewUnderRoot("xyzzy", SystemMode, s.rep).Unmask("foo")
   491  	c.Assert(err, IsNil)
   492  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "unmask", "foo"}})
   493  }
   494  
   495  func (s *SystemdTestSuite) TestRestart(c *C) {
   496  	restore := MockStopDelays(time.Millisecond, 25*time.Second)
   497  	defer restore()
   498  	s.outs = [][]byte{
   499  		nil, // for the "stop" itself
   500  		[]byte("ActiveState=inactive\n"),
   501  		nil, // for the "start"
   502  	}
   503  	s.errors = []error{nil, nil, nil, nil, &Timeout{}}
   504  	err := New(SystemMode, s.rep).Restart("foo", 100*time.Millisecond)
   505  	c.Assert(err, IsNil)
   506  	c.Check(s.argses, HasLen, 3)
   507  	c.Check(s.argses[0], DeepEquals, []string{"stop", "foo"})
   508  	c.Check(s.argses[1], DeepEquals, []string{"show", "--property=ActiveState", "foo"})
   509  	c.Check(s.argses[2], DeepEquals, []string{"start", "foo"})
   510  }
   511  
   512  func (s *SystemdTestSuite) TestKill(c *C) {
   513  	c.Assert(New(SystemMode, s.rep).Kill("foo", "HUP", ""), IsNil)
   514  	c.Check(s.argses, DeepEquals, [][]string{{"kill", "foo", "-s", "HUP", "--kill-who=all"}})
   515  }
   516  
   517  func (s *SystemdTestSuite) TestIsTimeout(c *C) {
   518  	c.Check(IsTimeout(os.ErrInvalid), Equals, false)
   519  	c.Check(IsTimeout(&Timeout{}), Equals, true)
   520  }
   521  
   522  func (s *SystemdTestSuite) TestLogErrJctl(c *C) {
   523  	s.jerrs = []error{&Timeout{}}
   524  
   525  	reader, err := New(SystemMode, s.rep).LogReader([]string{"foo"}, 24, false)
   526  	c.Check(err, NotNil)
   527  	c.Check(reader, IsNil)
   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) TestLogs(c *C) {
   535  	expected := `{"a": 1}
   536  {"a": 2}
   537  `
   538  	s.jouts = [][]byte{[]byte(expected)}
   539  
   540  	reader, err := New(SystemMode, s.rep).LogReader([]string{"foo"}, 24, false)
   541  	c.Check(err, IsNil)
   542  	logs, err := ioutil.ReadAll(reader)
   543  	c.Assert(err, IsNil)
   544  	c.Check(string(logs), Equals, expected)
   545  	c.Check(s.jns, DeepEquals, []string{"24"})
   546  	c.Check(s.jsvcs, DeepEquals, [][]string{{"foo"}})
   547  	c.Check(s.jfollows, DeepEquals, []bool{false})
   548  	c.Check(s.j, Equals, 1)
   549  }
   550  
   551  // mustJSONMarshal panic's if the value cannot be marshaled
   552  func mustJSONMarshal(v interface{}) *json.RawMessage {
   553  	b, err := json.Marshal(v)
   554  	if err != nil {
   555  		panic(fmt.Sprintf("couldn't marshal json in test fixture: %v", err))
   556  	}
   557  	msg := json.RawMessage(b)
   558  	return &msg
   559  }
   560  
   561  func (s *SystemdTestSuite) TestLogPIDWithNonTrivialKeyValues(c *C) {
   562  	l1 := Log{
   563  		"_PID": mustJSONMarshal([]string{}),
   564  	}
   565  	l2 := Log{
   566  		"_PID": mustJSONMarshal(6),
   567  	}
   568  	l3 := Log{
   569  		"_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}),
   570  	}
   571  	l4 := Log{
   572  		"SYSLOG_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}),
   573  	}
   574  	l5 := Log{
   575  		"_PID":       mustJSONMarshal("42"),
   576  		"SYSLOG_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}),
   577  	}
   578  	l6 := Log{
   579  		"_PID":       mustJSONMarshal([]string{"42"}),
   580  		"SYSLOG_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}),
   581  	}
   582  	l7 := Log{
   583  		"_PID":       mustJSONMarshal([]string{"42", "42"}),
   584  		"SYSLOG_PID": mustJSONMarshal([]string{"singlepid"}),
   585  	}
   586  	c.Check(Log{}.PID(), Equals, "-")
   587  	c.Check(l1.PID(), Equals, "-")
   588  	c.Check(l2.PID(), Equals, "-")
   589  	c.Check(l3.PID(), Equals, "-")
   590  	c.Check(l4.PID(), Equals, "-")
   591  	// things starting with underscore are "trusted", so we trust
   592  	// them more than the user-settable ones:
   593  	c.Check(l5.PID(), Equals, "42")
   594  	c.Check(l6.PID(), Equals, "42")
   595  	c.Check(l7.PID(), Equals, "singlepid")
   596  }
   597  
   598  func (s *SystemdTestSuite) TestLogsMessageWithNonUniqueKeys(c *C) {
   599  
   600  	tt := []struct {
   601  		msg     *json.RawMessage
   602  		exp     string
   603  		comment string
   604  	}{
   605  		{
   606  			mustJSONMarshal("m1"),
   607  			"m1",
   608  			"simple string",
   609  		},
   610  		{
   611  			mustJSONMarshal("Я"),
   612  			"Я",
   613  			"simple utf-8 string",
   614  		},
   615  		{
   616  			mustJSONMarshal([]rune{65, 66, 67, 192, 69}),
   617  			"ABC\xc0E",
   618  			"invalid utf-8 bytes",
   619  		},
   620  		{
   621  			mustJSONMarshal(""),
   622  			"",
   623  			"empty string",
   624  		},
   625  		{
   626  			mustJSONMarshal([]string{"m1", "m2", "m3"}),
   627  			"m1\nm2\nm3",
   628  			"slice of strings",
   629  		},
   630  		{
   631  			// this is just "hello" in ascii
   632  			mustJSONMarshal([]rune{104, 101, 108, 108, 111}),
   633  			"hello",
   634  			"rune arrays are converted to strings",
   635  		},
   636  		{
   637  			// this is "hello\r" in ascii, the \r char is unprintable
   638  			mustJSONMarshal([]rune{104, 101, 108, 108, 111, 13}),
   639  			"hello\r",
   640  			"rune arrays are converted to strings",
   641  		},
   642  		{
   643  			// this is "hel" and "lo" in ascii
   644  			mustJSONMarshal([][]rune{
   645  				{104, 101, 108},
   646  				{108, 111},
   647  			}),
   648  			"hel\nlo",
   649  			"arrays of rune arrays are converted to arrays of strings",
   650  		},
   651  		{
   652  			// this is invalid utf-8 string followed by a valid one
   653  			mustJSONMarshal([][]byte{
   654  				{65, 66, 67, 192, 69},
   655  				{104, 101, 108, 108, 111},
   656  			}),
   657  			"ABC\xc0E\nhello",
   658  			"arrays of bytes, some are invalid utf-8 strings",
   659  		},
   660  		{
   661  			mustJSONMarshal(5),
   662  			"- (error decoding original message: unsupported JSON encoding format)",
   663  			"invalid message format of raw scalar number",
   664  		},
   665  		{
   666  			mustJSONMarshal(map[string]int{"hello": 1}),
   667  			"- (error decoding original message: unsupported JSON encoding format)",
   668  			"invalid message format of map object",
   669  		},
   670  	}
   671  
   672  	// trivial case
   673  	c.Check(Log{}.Message(), Equals, "-")
   674  
   675  	// case where the JSON has a "null" JSON value for the key, which happens if
   676  	// the actual message is too large for journald to send
   677  	// we can't use the mustJSONMarshal helper for this in the test table
   678  	// because that gets decoded by Go differently than a verbatim nil here, it
   679  	// gets interpreted as the empty string rather than nil directly
   680  	c.Check(Log{"MESSAGE": nil}.Message(), Equals, "- (error decoding original message: message key \"MESSAGE\" truncated)")
   681  
   682  	for _, t := range tt {
   683  		if t.msg == nil {
   684  
   685  		}
   686  		c.Check(Log{
   687  			"MESSAGE": t.msg,
   688  		}.Message(), Equals, t.exp, Commentf(t.comment))
   689  	}
   690  }
   691  
   692  func (s *SystemdTestSuite) TestLogSID(c *C) {
   693  	c.Check(Log{}.SID(), Equals, "-")
   694  	c.Check(Log{"SYSLOG_IDENTIFIER": mustJSONMarshal("abcdef")}.SID(), Equals, "abcdef")
   695  	c.Check(Log{"SYSLOG_IDENTIFIER": mustJSONMarshal([]string{"abcdef"})}.SID(), Equals, "abcdef")
   696  	// multiple string values are not supported
   697  	c.Check(Log{"SYSLOG_IDENTIFIER": mustJSONMarshal([]string{"abc", "def"})}.SID(), Equals, "-")
   698  
   699  }
   700  
   701  func (s *SystemdTestSuite) TestLogPID(c *C) {
   702  	c.Check(Log{}.PID(), Equals, "-")
   703  	c.Check(Log{"_PID": mustJSONMarshal("99")}.PID(), Equals, "99")
   704  	c.Check(Log{"SYSLOG_PID": mustJSONMarshal("99")}.PID(), Equals, "99")
   705  	// things starting with underscore are "trusted", so we trust
   706  	// them more than the user-settable ones:
   707  	c.Check(Log{
   708  		"_PID":       mustJSONMarshal("42"),
   709  		"SYSLOG_PID": mustJSONMarshal("99"),
   710  	}.PID(), Equals, "42")
   711  }
   712  
   713  func (s *SystemdTestSuite) TestTime(c *C) {
   714  	t, err := Log{}.Time()
   715  	c.Check(t.IsZero(), Equals, true)
   716  	c.Check(err, ErrorMatches, "key \"__REALTIME_TIMESTAMP\" missing from message")
   717  
   718  	// multiple timestampe keys mean we don't have a timestamp
   719  	t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal([]string{"1", "2"})}.Time()
   720  	c.Check(t.IsZero(), Equals, true)
   721  	c.Check(err, ErrorMatches, `no timestamp`)
   722  
   723  	t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal("what")}.Time()
   724  	c.Check(t.IsZero(), Equals, true)
   725  	c.Check(err, ErrorMatches, `timestamp not a decimal number: "what"`)
   726  
   727  	t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal("0")}.Time()
   728  	c.Check(err, IsNil)
   729  	c.Check(t.String(), Equals, "1970-01-01 00:00:00 +0000 UTC")
   730  
   731  	t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal("42")}.Time()
   732  	c.Check(err, IsNil)
   733  	c.Check(t.String(), Equals, "1970-01-01 00:00:00.000042 +0000 UTC")
   734  }
   735  
   736  func (s *SystemdTestSuite) TestMountUnitPath(c *C) {
   737  	c.Assert(MountUnitPath("/apps/hello/1.1"), Equals, filepath.Join(dirs.SnapServicesDir, "apps-hello-1.1.mount"))
   738  }
   739  
   740  func makeMockFile(c *C, path string) {
   741  	err := os.MkdirAll(filepath.Dir(path), 0755)
   742  	c.Assert(err, IsNil)
   743  	err = ioutil.WriteFile(path, nil, 0644)
   744  	c.Assert(err, IsNil)
   745  }
   746  
   747  func (s *SystemdTestSuite) TestAddMountUnit(c *C) {
   748  	rootDir := dirs.GlobalRootDir
   749  
   750  	restore := squashfs.MockNeedsFuse(false)
   751  	defer restore()
   752  
   753  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   754  	makeMockFile(c, mockSnapPath)
   755  
   756  	mountUnitName, err := NewUnderRoot(rootDir, SystemMode, nil).AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
   757  	c.Assert(err, IsNil)
   758  	defer os.Remove(mountUnitName)
   759  
   760  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   761  [Unit]
   762  Description=Mount unit for foo, revision 42
   763  Before=snapd.service
   764  After=zfs-mount.service
   765  
   766  [Mount]
   767  What=%s
   768  Where=/snap/snapname/123
   769  Type=squashfs
   770  Options=nodev,ro,x-gdu.hide,x-gvfs-hide
   771  LazyUnmount=yes
   772  
   773  [Install]
   774  WantedBy=multi-user.target
   775  `[1:], mockSnapPath))
   776  
   777  	c.Assert(s.argses, DeepEquals, [][]string{
   778  		{"daemon-reload"},
   779  		{"--root", rootDir, "enable", "snap-snapname-123.mount"},
   780  		{"start", "snap-snapname-123.mount"},
   781  	})
   782  }
   783  
   784  func (s *SystemdTestSuite) TestAddMountUnitForDirs(c *C) {
   785  	restore := squashfs.MockNeedsFuse(false)
   786  	defer restore()
   787  
   788  	// a directory instead of a file produces a different output
   789  	snapDir := c.MkDir()
   790  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foodir", "x1", snapDir, "/snap/snapname/x1", "squashfs")
   791  	c.Assert(err, IsNil)
   792  	defer os.Remove(mountUnitName)
   793  
   794  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   795  [Unit]
   796  Description=Mount unit for foodir, revision x1
   797  Before=snapd.service
   798  After=zfs-mount.service
   799  
   800  [Mount]
   801  What=%s
   802  Where=/snap/snapname/x1
   803  Type=none
   804  Options=nodev,ro,x-gdu.hide,x-gvfs-hide,bind
   805  LazyUnmount=yes
   806  
   807  [Install]
   808  WantedBy=multi-user.target
   809  `[1:], snapDir))
   810  
   811  	c.Assert(s.argses, DeepEquals, [][]string{
   812  		{"daemon-reload"},
   813  		{"enable", "snap-snapname-x1.mount"},
   814  		{"start", "snap-snapname-x1.mount"},
   815  	})
   816  }
   817  
   818  func (s *SystemdTestSuite) TestWriteSELinuxMountUnit(c *C) {
   819  	restore := selinux.MockIsEnabled(func() (bool, error) { return true, nil })
   820  	defer restore()
   821  	restore = selinux.MockIsEnforcing(func() (bool, error) { return true, nil })
   822  	defer restore()
   823  	restore = squashfs.MockNeedsFuse(false)
   824  	defer restore()
   825  
   826  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   827  	err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755)
   828  	c.Assert(err, IsNil)
   829  	err = ioutil.WriteFile(mockSnapPath, nil, 0644)
   830  	c.Assert(err, IsNil)
   831  
   832  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
   833  	c.Assert(err, IsNil)
   834  	defer os.Remove(mountUnitName)
   835  
   836  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   837  [Unit]
   838  Description=Mount unit for foo, revision 42
   839  Before=snapd.service
   840  After=zfs-mount.service
   841  
   842  [Mount]
   843  What=%s
   844  Where=/snap/snapname/123
   845  Type=squashfs
   846  Options=nodev,context=system_u:object_r:snappy_snap_t:s0,ro,x-gdu.hide,x-gvfs-hide
   847  LazyUnmount=yes
   848  
   849  [Install]
   850  WantedBy=multi-user.target
   851  `[1:], mockSnapPath))
   852  }
   853  
   854  func (s *SystemdTestSuite) TestFuseInContainer(c *C) {
   855  	if !osutil.FileExists("/dev/fuse") {
   856  		c.Skip("No /dev/fuse on the system")
   857  	}
   858  
   859  	systemdCmd := testutil.MockCommand(c, "systemd-detect-virt", `
   860  echo lxc
   861  exit 0
   862  	`)
   863  	defer systemdCmd.Restore()
   864  
   865  	fuseCmd := testutil.MockCommand(c, "squashfuse", `
   866  exit 0
   867  	`)
   868  	defer fuseCmd.Restore()
   869  
   870  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   871  	err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755)
   872  	c.Assert(err, IsNil)
   873  	err = ioutil.WriteFile(mockSnapPath, nil, 0644)
   874  	c.Assert(err, IsNil)
   875  
   876  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "x1", mockSnapPath, "/snap/snapname/123", "squashfs")
   877  	c.Assert(err, IsNil)
   878  	defer os.Remove(mountUnitName)
   879  
   880  	c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   881  [Unit]
   882  Description=Mount unit for foo, revision x1
   883  Before=snapd.service
   884  After=zfs-mount.service
   885  
   886  [Mount]
   887  What=%s
   888  Where=/snap/snapname/123
   889  Type=fuse.squashfuse
   890  Options=nodev,ro,x-gdu.hide,x-gvfs-hide,allow_other
   891  LazyUnmount=yes
   892  
   893  [Install]
   894  WantedBy=multi-user.target
   895  `[1:], mockSnapPath))
   896  }
   897  
   898  func (s *SystemdTestSuite) TestFuseOutsideContainer(c *C) {
   899  	systemdCmd := testutil.MockCommand(c, "systemd-detect-virt", `
   900  echo none
   901  exit 0
   902  	`)
   903  	defer systemdCmd.Restore()
   904  
   905  	fuseCmd := testutil.MockCommand(c, "squashfuse", `
   906  exit 0
   907  	`)
   908  	defer fuseCmd.Restore()
   909  
   910  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
   911  	err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755)
   912  	c.Assert(err, IsNil)
   913  	err = ioutil.WriteFile(mockSnapPath, nil, 0644)
   914  	c.Assert(err, IsNil)
   915  
   916  	mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "x1", mockSnapPath, "/snap/snapname/123", "squashfs")
   917  	c.Assert(err, IsNil)
   918  	defer os.Remove(mountUnitName)
   919  
   920  	c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(`
   921  [Unit]
   922  Description=Mount unit for foo, revision x1
   923  Before=snapd.service
   924  After=zfs-mount.service
   925  
   926  [Mount]
   927  What=%s
   928  Where=/snap/snapname/123
   929  Type=squashfs
   930  Options=nodev,ro,x-gdu.hide,x-gvfs-hide
   931  LazyUnmount=yes
   932  
   933  [Install]
   934  WantedBy=multi-user.target
   935  `[1:], mockSnapPath))
   936  }
   937  
   938  func (s *SystemdTestSuite) TestJctl(c *C) {
   939  	var args []string
   940  	var err error
   941  	MockOsutilStreamCommand(func(name string, myargs ...string) (io.ReadCloser, error) {
   942  		c.Check(cap(myargs) <= len(myargs)+2, Equals, true, Commentf("cap:%d, len:%d", cap(myargs), len(myargs)))
   943  		args = myargs
   944  		return nil, nil
   945  	})
   946  
   947  	_, err = Jctl([]string{"foo", "bar"}, 10, false)
   948  	c.Assert(err, IsNil)
   949  	c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "-n", "10", "-u", "foo", "-u", "bar"})
   950  	_, err = Jctl([]string{"foo", "bar", "baz"}, 99, true)
   951  	c.Assert(err, IsNil)
   952  	c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "-n", "99", "-f", "-u", "foo", "-u", "bar", "-u", "baz"})
   953  	_, err = Jctl([]string{"foo", "bar"}, -1, false)
   954  	c.Assert(err, IsNil)
   955  	c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "--no-tail", "-u", "foo", "-u", "bar"})
   956  }
   957  
   958  func (s *SystemdTestSuite) TestIsActiveUnderRoot(c *C) {
   959  	sysErr := &Error{}
   960  	// manpage states that systemctl returns exit code 3 for inactive
   961  	// services, however we should check any non-0 exit status
   962  	sysErr.SetExitCode(1)
   963  	sysErr.SetMsg([]byte("inactive\n"))
   964  	s.errors = []error{sysErr}
   965  
   966  	_, err := NewUnderRoot("xyzzy", SystemMode, s.rep).IsActive("foo")
   967  	c.Assert(err, IsNil)
   968  	c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "is-active", "foo"}})
   969  }
   970  
   971  func (s *SystemdTestSuite) TestIsActiveIsInactive(c *C) {
   972  	sysErr := &Error{}
   973  	// manpage states that systemctl returns exit code 3 for inactive
   974  	// services, however we should check any non-0 exit status
   975  	sysErr.SetExitCode(1)
   976  	sysErr.SetMsg([]byte("inactive\n"))
   977  	s.errors = []error{sysErr}
   978  
   979  	active, err := New(SystemMode, s.rep).IsActive("foo")
   980  	c.Assert(active, Equals, false)
   981  	c.Assert(err, IsNil)
   982  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
   983  }
   984  
   985  func (s *SystemdTestSuite) TestIsActiveIsInactiveAlternativeMessage(c *C) {
   986  	sysErr := &Error{}
   987  	// on Centos 7, with systemd 219 we see "unknown" returned when querying the
   988  	// active state for a slice unit which does not exist, check that we handle
   989  	// this case properly as well
   990  	sysErr.SetExitCode(3)
   991  	sysErr.SetMsg([]byte("unknown\n"))
   992  	s.errors = []error{sysErr}
   993  
   994  	active, err := New(SystemMode, s.rep).IsActive("foo")
   995  	c.Assert(active, Equals, false)
   996  	c.Assert(err, IsNil)
   997  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
   998  }
   999  
  1000  func (s *SystemdTestSuite) TestIsActiveIsFailed(c *C) {
  1001  	sysErr := &Error{}
  1002  	// seen in the wild to be reported for a 'failed' service
  1003  	sysErr.SetExitCode(3)
  1004  	sysErr.SetMsg([]byte("failed\n"))
  1005  	s.errors = []error{sysErr}
  1006  
  1007  	active, err := New(SystemMode, s.rep).IsActive("foo")
  1008  	c.Assert(active, Equals, false)
  1009  	c.Assert(err, IsNil)
  1010  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
  1011  }
  1012  
  1013  func (s *SystemdTestSuite) TestIsActiveIsActive(c *C) {
  1014  	s.errors = []error{nil}
  1015  
  1016  	active, err := New(SystemMode, s.rep).IsActive("foo")
  1017  	c.Assert(active, Equals, true)
  1018  	c.Assert(err, IsNil)
  1019  	c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}})
  1020  }
  1021  
  1022  func (s *SystemdTestSuite) TestIsActiveUnexpectedErr(c *C) {
  1023  	sysErr := &Error{}
  1024  	sysErr.SetExitCode(1)
  1025  	sysErr.SetMsg([]byte("random-failure\n"))
  1026  	s.errors = []error{sysErr}
  1027  
  1028  	active, err := NewUnderRoot("xyzzy", SystemMode, s.rep).IsActive("foo")
  1029  	c.Assert(active, Equals, false)
  1030  	c.Assert(err, ErrorMatches, ".* failed with exit status 1: random-failure\n")
  1031  }
  1032  
  1033  func makeMockMountUnit(c *C, mountDir string) string {
  1034  	mountUnit := MountUnitPath(dirs.StripRootDir(mountDir))
  1035  	err := ioutil.WriteFile(mountUnit, nil, 0644)
  1036  	c.Assert(err, IsNil)
  1037  	return mountUnit
  1038  }
  1039  
  1040  // FIXME: also test for the "IsMounted" case
  1041  func (s *SystemdTestSuite) TestRemoveMountUnit(c *C) {
  1042  	rootDir := dirs.GlobalRootDir
  1043  
  1044  	restore := osutil.MockMountInfo("")
  1045  	defer restore()
  1046  
  1047  	mountDir := rootDir + "/snap/foo/42"
  1048  	mountUnit := makeMockMountUnit(c, mountDir)
  1049  	err := NewUnderRoot(rootDir, SystemMode, nil).RemoveMountUnitFile(mountDir)
  1050  
  1051  	c.Assert(err, IsNil)
  1052  	// the file is gone
  1053  	c.Check(osutil.FileExists(mountUnit), Equals, false)
  1054  	// and the unit is disabled and the daemon reloaded
  1055  	c.Check(s.argses, DeepEquals, [][]string{
  1056  		{"--root", rootDir, "disable", "snap-foo-42.mount"},
  1057  		{"daemon-reload"},
  1058  	})
  1059  }
  1060  
  1061  func (s *SystemdTestSuite) TestDaemonReloadMutex(c *C) {
  1062  	s.testDaemonReloadMutex(c, Systemd.DaemonReload)
  1063  }
  1064  
  1065  func (s *SystemdTestSuite) testDaemonReloadMutex(c *C, reload func(Systemd) error) {
  1066  	rootDir := dirs.GlobalRootDir
  1067  	sysd := NewUnderRoot(rootDir, SystemMode, nil)
  1068  
  1069  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
  1070  	makeMockFile(c, mockSnapPath)
  1071  
  1072  	// create a go-routine that will try to daemon-reload like crazy
  1073  	stopCh := make(chan bool, 1)
  1074  	stoppedCh := make(chan bool, 1)
  1075  	go func() {
  1076  		for {
  1077  			sysd.DaemonReload()
  1078  			select {
  1079  			case <-stopCh:
  1080  				close(stoppedCh)
  1081  				return
  1082  			default:
  1083  				//pass
  1084  			}
  1085  		}
  1086  	}()
  1087  
  1088  	// And now add a mount unit file while the go-routine tries to
  1089  	// daemon-reload. This will be serialized, if not this would
  1090  	// panic because systemd.daemonReloadNoLock ensures the lock is
  1091  	// taken when this happens.
  1092  	_, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/foo/42", "squashfs")
  1093  	c.Assert(err, IsNil)
  1094  	close(stopCh)
  1095  	<-stoppedCh
  1096  }
  1097  
  1098  func (s *SystemdTestSuite) TestDaemonReexecMutex(c *C) {
  1099  	s.testDaemonReloadMutex(c, Systemd.DaemonReexec)
  1100  }
  1101  
  1102  func (s *SystemdTestSuite) TestUserMode(c *C) {
  1103  	rootDir := dirs.GlobalRootDir
  1104  	sysd := NewUnderRoot(rootDir, UserMode, nil)
  1105  
  1106  	c.Assert(sysd.Enable("foo"), IsNil)
  1107  	c.Check(s.argses[0], DeepEquals, []string{"--user", "--root", rootDir, "enable", "foo"})
  1108  	c.Assert(sysd.Start("foo"), IsNil)
  1109  	c.Check(s.argses[1], DeepEquals, []string{"--user", "start", "foo"})
  1110  }
  1111  
  1112  func (s *SystemdTestSuite) TestGlobalUserMode(c *C) {
  1113  	rootDir := dirs.GlobalRootDir
  1114  	sysd := NewUnderRoot(rootDir, GlobalUserMode, nil)
  1115  
  1116  	c.Assert(sysd.Enable("foo"), IsNil)
  1117  	c.Check(s.argses[0], DeepEquals, []string{"--user", "--global", "--root", rootDir, "enable", "foo"})
  1118  	c.Assert(sysd.Disable("foo"), IsNil)
  1119  	c.Check(s.argses[1], DeepEquals, []string{"--user", "--global", "--root", rootDir, "disable", "foo"})
  1120  	c.Assert(sysd.Mask("foo"), IsNil)
  1121  	c.Check(s.argses[2], DeepEquals, []string{"--user", "--global", "--root", rootDir, "mask", "foo"})
  1122  	c.Assert(sysd.Unmask("foo"), IsNil)
  1123  	c.Check(s.argses[3], DeepEquals, []string{"--user", "--global", "--root", rootDir, "unmask", "foo"})
  1124  	_, err := sysd.IsEnabled("foo")
  1125  	c.Check(err, IsNil)
  1126  	c.Check(s.argses[4], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "foo"})
  1127  
  1128  	// Commands that don't make sense for GlobalUserMode panic
  1129  	c.Check(sysd.DaemonReload, Panics, "cannot call daemon-reload with GlobalUserMode")
  1130  	c.Check(sysd.DaemonReexec, Panics, "cannot call daemon-reexec with GlobalUserMode")
  1131  	c.Check(func() { sysd.Start("foo") }, Panics, "cannot call start with GlobalUserMode")
  1132  	c.Check(func() { sysd.StartNoBlock("foo") }, Panics, "cannot call start with GlobalUserMode")
  1133  	c.Check(func() { sysd.Stop("foo", 0) }, Panics, "cannot call stop with GlobalUserMode")
  1134  	c.Check(func() { sysd.Restart("foo", 0) }, Panics, "cannot call restart with GlobalUserMode")
  1135  	c.Check(func() { sysd.Kill("foo", "HUP", "") }, Panics, "cannot call kill with GlobalUserMode")
  1136  	c.Check(func() { sysd.IsActive("foo") }, Panics, "cannot call is-active with GlobalUserMode")
  1137  }
  1138  
  1139  func (s *SystemdTestSuite) TestStatusGlobalUserMode(c *C) {
  1140  	output := []byte("enabled\ndisabled\nstatic\n")
  1141  	sysdErr := &Error{}
  1142  	sysdErr.SetExitCode(1)
  1143  	sysdErr.SetMsg(output)
  1144  
  1145  	s.outs = [][]byte{output, nil, output}
  1146  	s.errors = []error{nil, sysdErr, nil}
  1147  
  1148  	rootDir := dirs.GlobalRootDir
  1149  	sysd := NewUnderRoot(rootDir, GlobalUserMode, nil)
  1150  	sts, err := sysd.Status("foo", "bar", "baz")
  1151  	c.Check(err, IsNil)
  1152  	c.Check(sts, DeepEquals, []*UnitStatus{
  1153  		{UnitName: "foo", Enabled: true},
  1154  		{UnitName: "bar", Enabled: false},
  1155  		{UnitName: "baz", Enabled: true},
  1156  	})
  1157  	c.Check(s.argses[0], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "foo", "bar", "baz"})
  1158  
  1159  	// Output is collected if systemctl has a non-zero exit status
  1160  	sts, err = sysd.Status("one", "two", "three")
  1161  	c.Check(err, IsNil)
  1162  	c.Check(sts, DeepEquals, []*UnitStatus{
  1163  		{UnitName: "one", Enabled: true},
  1164  		{UnitName: "two", Enabled: false},
  1165  		{UnitName: "three", Enabled: true},
  1166  	})
  1167  	c.Check(s.argses[1], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "one", "two", "three"})
  1168  
  1169  	// An error is returned if the wrong number of statuses are returned
  1170  	sts, err = sysd.Status("one")
  1171  	c.Check(err, ErrorMatches, "cannot get enabled status of services: expected 1 results, got 3")
  1172  	c.Check(sts, IsNil)
  1173  	c.Check(s.argses[2], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "one"})
  1174  }
  1175  
  1176  const unitTemplate = `
  1177  [Unit]
  1178  Description=Mount unit for foo, revision 42
  1179  Before=snapd.service
  1180  After=zfs-mount.service
  1181  
  1182  [Mount]
  1183  What=%s
  1184  Where=/snap/snapname/123
  1185  Type=%s
  1186  Options=%s
  1187  LazyUnmount=yes
  1188  
  1189  [Install]
  1190  WantedBy=multi-user.target
  1191  `
  1192  
  1193  func (s *SystemdTestSuite) TestPreseedModeAddMountUnit(c *C) {
  1194  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1195  
  1196  	restore := squashfs.MockNeedsFuse(false)
  1197  	defer restore()
  1198  
  1199  	mockMountCmd := testutil.MockCommand(c, "mount", "")
  1200  	defer mockMountCmd.Restore()
  1201  
  1202  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
  1203  	makeMockFile(c, mockSnapPath)
  1204  
  1205  	mountUnitName, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
  1206  	c.Assert(err, IsNil)
  1207  	defer os.Remove(mountUnitName)
  1208  
  1209  	// systemd was not called
  1210  	c.Check(s.argses, HasLen, 0)
  1211  	// mount was called
  1212  	c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "squashfs", mockSnapPath, "/snap/snapname/123", "-o", "nodev,ro,x-gdu.hide,x-gvfs-hide"})
  1213  	// unit was enabled with a symlink
  1214  	c.Check(osutil.IsSymlink(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", mountUnitName)), Equals, true)
  1215  	c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(unitTemplate[1:], mockSnapPath, "squashfs", "nodev,ro,x-gdu.hide,x-gvfs-hide"))
  1216  }
  1217  
  1218  func (s *SystemdTestSuite) TestPreseedModeAddMountUnitWithFuse(c *C) {
  1219  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1220  
  1221  	restore := MockSquashFsType(func() (string, []string) { return "fuse.squashfuse", []string{"a,b,c"} })
  1222  	defer restore()
  1223  
  1224  	mockMountCmd := testutil.MockCommand(c, "mount", "")
  1225  	defer mockMountCmd.Restore()
  1226  
  1227  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
  1228  	makeMockFile(c, mockSnapPath)
  1229  
  1230  	mountUnitName, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
  1231  	c.Assert(err, IsNil)
  1232  	defer os.Remove(mountUnitName)
  1233  
  1234  	c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "fuse.squashfuse", mockSnapPath, "/snap/snapname/123", "-o", "nodev,a,b,c"})
  1235  	c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(unitTemplate[1:], mockSnapPath, "squashfs", "nodev,ro,x-gdu.hide,x-gvfs-hide"))
  1236  }
  1237  
  1238  func (s *SystemdTestSuite) TestPreseedModeMountError(c *C) {
  1239  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1240  
  1241  	restore := squashfs.MockNeedsFuse(false)
  1242  	defer restore()
  1243  
  1244  	mockMountCmd := testutil.MockCommand(c, "mount", `echo "some failure"; exit 1`)
  1245  	defer mockMountCmd.Restore()
  1246  
  1247  	mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap")
  1248  	makeMockFile(c, mockSnapPath)
  1249  
  1250  	_, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs")
  1251  	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`)
  1252  }
  1253  
  1254  func (s *SystemdTestSuite) TestPreseedModeRemoveMountUnit(c *C) {
  1255  	mountDir := dirs.GlobalRootDir + "/snap/foo/42"
  1256  
  1257  	restore := MockOsutilIsMounted(func(path string) (bool, error) {
  1258  		c.Check(path, Equals, mountDir)
  1259  		return true, nil
  1260  	})
  1261  	defer restore()
  1262  
  1263  	mockUmountCmd := testutil.MockCommand(c, "umount", "")
  1264  	defer mockUmountCmd.Restore()
  1265  
  1266  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1267  
  1268  	mountUnit := makeMockMountUnit(c, mountDir)
  1269  	symlinkPath := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", filepath.Base(mountUnit))
  1270  	c.Assert(os.Symlink(mountUnit, symlinkPath), IsNil)
  1271  	c.Assert(sysd.RemoveMountUnitFile(mountDir), IsNil)
  1272  
  1273  	// the file is gone
  1274  	c.Check(osutil.FileExists(mountUnit), Equals, false)
  1275  	// unit symlink is gone
  1276  	c.Check(osutil.IsSymlink(symlinkPath), Equals, false)
  1277  	// and systemd was not called
  1278  	c.Check(s.argses, HasLen, 0)
  1279  	// umount was called
  1280  	c.Check(mockUmountCmd.Calls(), DeepEquals, [][]string{{"umount", "-d", "-l", mountDir}})
  1281  }
  1282  
  1283  func (s *SystemdTestSuite) TestPreseedModeRemoveMountUnitUnmounted(c *C) {
  1284  	mountDir := dirs.GlobalRootDir + "/snap/foo/42"
  1285  
  1286  	restore := MockOsutilIsMounted(func(path string) (bool, error) {
  1287  		c.Check(path, Equals, mountDir)
  1288  		return false, nil
  1289  	})
  1290  	defer restore()
  1291  
  1292  	mockUmountCmd := testutil.MockCommand(c, "umount", "")
  1293  	defer mockUmountCmd.Restore()
  1294  
  1295  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1296  	mountUnit := makeMockMountUnit(c, mountDir)
  1297  	symlinkPath := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", filepath.Base(mountUnit))
  1298  	c.Assert(os.Symlink(mountUnit, symlinkPath), IsNil)
  1299  
  1300  	c.Assert(sysd.RemoveMountUnitFile(mountDir), IsNil)
  1301  
  1302  	// the file is gone
  1303  	c.Check(osutil.FileExists(mountUnit), Equals, false)
  1304  	// unit symlink is gone
  1305  	c.Check(osutil.IsSymlink(symlinkPath), Equals, false)
  1306  	// and systemd was not called
  1307  	c.Check(s.argses, HasLen, 0)
  1308  	// umount was not called
  1309  	c.Check(mockUmountCmd.Calls(), HasLen, 0)
  1310  }
  1311  
  1312  func (s *SystemdTestSuite) TestPreseedModeBindmountNotSupported(c *C) {
  1313  	sysd := NewEmulationMode(dirs.GlobalRootDir)
  1314  
  1315  	restore := squashfs.MockNeedsFuse(false)
  1316  	defer restore()
  1317  
  1318  	mockSnapPath := c.MkDir()
  1319  
  1320  	_, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "")
  1321  	c.Assert(err, ErrorMatches, `bind-mounted directory is not supported in emulation mode`)
  1322  }
  1323  
  1324  func (s *SystemdTestSuite) TestEnableInEmulationMode(c *C) {
  1325  	sysd := NewEmulationMode("/path")
  1326  	c.Assert(sysd.Enable("foo"), IsNil)
  1327  
  1328  	sysd = NewEmulationMode("")
  1329  	c.Assert(sysd.Enable("bar"), IsNil)
  1330  	c.Check(s.argses, DeepEquals, [][]string{
  1331  		{"--root", "/path", "enable", "foo"},
  1332  		{"--root", dirs.GlobalRootDir, "enable", "bar"}})
  1333  }
  1334  
  1335  func (s *SystemdTestSuite) TestDisableInEmulationMode(c *C) {
  1336  	sysd := NewEmulationMode("/path")
  1337  	c.Assert(sysd.Disable("foo"), IsNil)
  1338  
  1339  	c.Check(s.argses, DeepEquals, [][]string{
  1340  		{"--root", "/path", "disable", "foo"}})
  1341  }
  1342  
  1343  func (s *SystemdTestSuite) TestMaskInEmulationMode(c *C) {
  1344  	sysd := NewEmulationMode("/path")
  1345  	c.Assert(sysd.Mask("foo"), IsNil)
  1346  
  1347  	c.Check(s.argses, DeepEquals, [][]string{
  1348  		{"--root", "/path", "mask", "foo"}})
  1349  }
  1350  
  1351  func (s *SystemdTestSuite) TestUnmaskInEmulationMode(c *C) {
  1352  	sysd := NewEmulationMode("/path")
  1353  	c.Assert(sysd.Unmask("foo"), IsNil)
  1354  
  1355  	c.Check(s.argses, DeepEquals, [][]string{
  1356  		{"--root", "/path", "unmask", "foo"}})
  1357  }
  1358  
  1359  func (s *SystemdTestSuite) TestMountHappy(c *C) {
  1360  	sysd := New(SystemMode, nil)
  1361  
  1362  	cmd := testutil.MockCommand(c, "systemd-mount", "")
  1363  	defer cmd.Restore()
  1364  
  1365  	c.Assert(sysd.Mount("foo", "bar"), IsNil)
  1366  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1367  		{"systemd-mount", "foo", "bar"},
  1368  	})
  1369  	cmd.ForgetCalls()
  1370  	c.Assert(sysd.Mount("foo", "bar", "-o", "bind"), IsNil)
  1371  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1372  		{"systemd-mount", "-o", "bind", "foo", "bar"},
  1373  	})
  1374  }
  1375  
  1376  func (s *SystemdTestSuite) TestMountErr(c *C) {
  1377  	sysd := New(SystemMode, nil)
  1378  
  1379  	cmd := testutil.MockCommand(c, "systemd-mount", `echo "failed"; exit 111`)
  1380  	defer cmd.Restore()
  1381  
  1382  	err := sysd.Mount("foo", "bar")
  1383  	c.Assert(err, ErrorMatches, "failed")
  1384  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1385  		{"systemd-mount", "foo", "bar"},
  1386  	})
  1387  }
  1388  
  1389  func (s *SystemdTestSuite) TestUmountHappy(c *C) {
  1390  	sysd := New(SystemMode, nil)
  1391  
  1392  	cmd := testutil.MockCommand(c, "systemd-mount", "")
  1393  	defer cmd.Restore()
  1394  
  1395  	c.Assert(sysd.Umount("bar"), IsNil)
  1396  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1397  		{"systemd-mount", "--umount", "bar"},
  1398  	})
  1399  }
  1400  
  1401  func (s *SystemdTestSuite) TestUmountErr(c *C) {
  1402  	sysd := New(SystemMode, nil)
  1403  
  1404  	cmd := testutil.MockCommand(c, "systemd-mount", `echo "failed"; exit 111`)
  1405  	defer cmd.Restore()
  1406  
  1407  	err := sysd.Umount("bar")
  1408  	c.Assert(err, ErrorMatches, "failed")
  1409  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1410  		{"systemd-mount", "--umount", "bar"},
  1411  	})
  1412  }
  1413  
  1414  func (s *SystemdTestSuite) TestCurrentUsageFamilyReallyInvalid(c *C) {
  1415  	s.outs = [][]byte{
  1416  		[]byte(`gahstringsarehard`),
  1417  		[]byte(`gahstringsarehard`),
  1418  	}
  1419  	sysd := New(SystemMode, s.rep)
  1420  	_, err := sysd.CurrentMemoryUsage("bar.service")
  1421  	c.Assert(err, ErrorMatches, `invalid property format from systemd for MemoryCurrent \(got gahstringsarehard\)`)
  1422  	_, err = sysd.CurrentTasksCount("bar.service")
  1423  	c.Assert(err, ErrorMatches, `invalid property format from systemd for TasksCurrent \(got gahstringsarehard\)`)
  1424  	c.Check(s.argses, DeepEquals, [][]string{
  1425  		{"show", "--property", "MemoryCurrent", "bar.service"},
  1426  		{"show", "--property", "TasksCurrent", "bar.service"},
  1427  	})
  1428  }
  1429  
  1430  func (s *SystemdTestSuite) TestCurrentUsageFamilyInactive(c *C) {
  1431  	s.outs = [][]byte{
  1432  		[]byte(`MemoryCurrent=[not set]`),
  1433  		[]byte(`TasksCurrent=[not set]`),
  1434  	}
  1435  	sysd := New(SystemMode, s.rep)
  1436  	_, err := sysd.CurrentMemoryUsage("bar.service")
  1437  	c.Assert(err, ErrorMatches, "memory usage unavailable")
  1438  	_, err = sysd.CurrentTasksCount("bar.service")
  1439  	c.Assert(err, ErrorMatches, "tasks count unavailable")
  1440  	c.Check(s.argses, DeepEquals, [][]string{
  1441  		{"show", "--property", "MemoryCurrent", "bar.service"},
  1442  		{"show", "--property", "TasksCurrent", "bar.service"},
  1443  	})
  1444  }
  1445  
  1446  func (s *SystemdTestSuite) TestCurrentUsageFamilyInvalid(c *C) {
  1447  	s.outs = [][]byte{
  1448  		[]byte(`MemoryCurrent=blahhhhhhhhhhhhhh`),
  1449  		[]byte(`TasksCurrent=blahhhhhhhhhhhhhh`),
  1450  	}
  1451  	sysd := New(SystemMode, s.rep)
  1452  	_, err := sysd.CurrentMemoryUsage("bar.service")
  1453  	c.Assert(err, ErrorMatches, `invalid property value from systemd for MemoryCurrent: cannot parse "blahhhhhhhhhhhhhh" as an integer`)
  1454  	_, err = sysd.CurrentTasksCount("bar.service")
  1455  	c.Assert(err, ErrorMatches, `invalid property value from systemd for TasksCurrent: cannot parse "blahhhhhhhhhhhhhh" as an integer`)
  1456  	c.Check(s.argses, DeepEquals, [][]string{
  1457  		{"show", "--property", "MemoryCurrent", "bar.service"},
  1458  		{"show", "--property", "TasksCurrent", "bar.service"},
  1459  	})
  1460  }
  1461  
  1462  func (s *SystemdTestSuite) TestCurrentUsageFamilyHappy(c *C) {
  1463  	s.outs = [][]byte{
  1464  		[]byte(`MemoryCurrent=1024`),
  1465  		[]byte(`MemoryCurrent=18446744073709551615`), // special value from systemd bug
  1466  		[]byte(`TasksCurrent=10`),
  1467  	}
  1468  	sysd := New(SystemMode, s.rep)
  1469  	memUsage, err := sysd.CurrentMemoryUsage("bar.service")
  1470  	c.Assert(err, IsNil)
  1471  	c.Assert(memUsage, Equals, quantity.SizeKiB)
  1472  	memUsage, err = sysd.CurrentMemoryUsage("bar.service")
  1473  	c.Assert(err, IsNil)
  1474  	const sixteenExb = quantity.Size(1<<64 - 1)
  1475  	c.Assert(memUsage, Equals, sixteenExb)
  1476  	tasksUsage, err := sysd.CurrentTasksCount("bar.service")
  1477  	c.Assert(tasksUsage, Equals, uint64(10))
  1478  	c.Assert(err, IsNil)
  1479  	c.Check(s.argses, DeepEquals, [][]string{
  1480  		{"show", "--property", "MemoryCurrent", "bar.service"},
  1481  		{"show", "--property", "MemoryCurrent", "bar.service"},
  1482  		{"show", "--property", "TasksCurrent", "bar.service"},
  1483  	})
  1484  }
  1485  
  1486  func (s *SystemdTestSuite) TestInactiveEnterTimestampZero(c *C) {
  1487  	s.outs = [][]byte{
  1488  		[]byte(`InactiveEnterTimestamp=`),
  1489  	}
  1490  	sysd := New(SystemMode, s.rep)
  1491  	stamp, err := sysd.InactiveEnterTimestamp("bar.service")
  1492  	c.Assert(err, IsNil)
  1493  	c.Check(s.argses, DeepEquals, [][]string{
  1494  		{"show", "--property", "InactiveEnterTimestamp", "bar.service"},
  1495  	})
  1496  	c.Check(stamp.IsZero(), Equals, true)
  1497  }
  1498  
  1499  func (s *SystemdTestSuite) TestInactiveEnterTimestampValidWhitespace(c *C) {
  1500  	s.outs = [][]byte{
  1501  		[]byte(`InactiveEnterTimestamp=Fri 2021-04-16 15:32:21 UTC
  1502  `),
  1503  	}
  1504  
  1505  	stamp, err := New(SystemMode, s.rep).InactiveEnterTimestamp("bar.service")
  1506  	c.Assert(err, IsNil)
  1507  	c.Check(s.argses, DeepEquals, [][]string{
  1508  		{"show", "--property", "InactiveEnterTimestamp", "bar.service"},
  1509  	})
  1510  	c.Check(stamp.Equal(time.Date(2021, time.April, 16, 15, 32, 21, 0, time.UTC)), Equals, true)
  1511  }
  1512  
  1513  func (s *SystemdTestSuite) TestInactiveEnterTimestampValid(c *C) {
  1514  	s.outs = [][]byte{
  1515  		[]byte(`InactiveEnterTimestamp=Fri 2021-04-16 15:32:21 UTC`),
  1516  	}
  1517  
  1518  	stamp, err := New(SystemMode, s.rep).InactiveEnterTimestamp("bar.service")
  1519  	c.Assert(err, IsNil)
  1520  	c.Check(s.argses, DeepEquals, [][]string{
  1521  		{"show", "--property", "InactiveEnterTimestamp", "bar.service"},
  1522  	})
  1523  	c.Check(stamp.Equal(time.Date(2021, time.April, 16, 15, 32, 21, 0, time.UTC)), Equals, true)
  1524  }
  1525  
  1526  func (s *SystemdTestSuite) TestInactiveEnterTimestampFailure(c *C) {
  1527  	s.outs = [][]byte{
  1528  		[]byte(`mocked failure`),
  1529  	}
  1530  	s.errors = []error{
  1531  		fmt.Errorf("mocked failure"),
  1532  	}
  1533  	stamp, err := New(SystemMode, s.rep).InactiveEnterTimestamp("bar.service")
  1534  	c.Assert(err, ErrorMatches, "mocked failure")
  1535  	c.Check(stamp.IsZero(), Equals, true)
  1536  }
  1537  
  1538  func (s *SystemdTestSuite) TestInactiveEnterTimestampMalformed(c *C) {
  1539  	s.outs = [][]byte{
  1540  		[]byte(`InactiveEnterTimestamp`),
  1541  		[]byte(``),
  1542  		[]byte(`some random garbage
  1543  with newlines`),
  1544  	}
  1545  	sysd := New(SystemMode, s.rep)
  1546  	for i := 0; i < len(s.outs); i++ {
  1547  		s.argses = nil
  1548  		stamp, err := sysd.InactiveEnterTimestamp("bar.service")
  1549  		c.Assert(err.Error(), testutil.Contains, `invalid property format from systemd for InactiveEnterTimestamp (got`)
  1550  		c.Check(s.argses, DeepEquals, [][]string{
  1551  			{"show", "--property", "InactiveEnterTimestamp", "bar.service"},
  1552  		})
  1553  		c.Check(stamp.IsZero(), Equals, true)
  1554  	}
  1555  }
  1556  
  1557  func (s *SystemdTestSuite) TestInactiveEnterTimestampMalformedMore(c *C) {
  1558  	s.outs = [][]byte{
  1559  		[]byte(`InactiveEnterTimestamp=0`), // 0 is valid for InactiveEnterTimestampMonotonic
  1560  	}
  1561  	sysd := New(SystemMode, s.rep)
  1562  
  1563  	stamp, err := sysd.InactiveEnterTimestamp("bar.service")
  1564  
  1565  	c.Assert(err, ErrorMatches, `internal error: systemctl time output \(0\) is malformed`)
  1566  	c.Check(s.argses, DeepEquals, [][]string{
  1567  		{"show", "--property", "InactiveEnterTimestamp", "bar.service"},
  1568  	})
  1569  	c.Check(stamp.IsZero(), Equals, true)
  1570  }
  1571  
  1572  type systemdErrorSuite struct{}
  1573  
  1574  var _ = Suite(&systemdErrorSuite{})
  1575  
  1576  func (s *systemdErrorSuite) TestErrorStringNormalError(c *C) {
  1577  	systemctl := testutil.MockCommand(c, "systemctl", `echo "I fail"; exit 11`)
  1578  	defer systemctl.Restore()
  1579  
  1580  	_, err := Version()
  1581  	c.Check(err, ErrorMatches, `systemctl command \[--version\] failed with exit status 11: I fail\n`)
  1582  }
  1583  
  1584  func (s *systemdErrorSuite) TestErrorStringNoOutput(c *C) {
  1585  	systemctl := testutil.MockCommand(c, "systemctl", `exit 22`)
  1586  	defer systemctl.Restore()
  1587  
  1588  	_, err := Version()
  1589  	c.Check(err, ErrorMatches, `systemctl command \[--version\] failed with exit status 22`)
  1590  }
  1591  
  1592  func (s *systemdErrorSuite) TestErrorStringNoSystemctl(c *C) {
  1593  	oldPath := os.Getenv("PATH")
  1594  	os.Setenv("PATH", "/xxx")
  1595  	defer func() { os.Setenv("PATH", oldPath) }()
  1596  
  1597  	_, err := Version()
  1598  	c.Check(err, ErrorMatches, `systemctl command \[--version\] failed with: exec: "systemctl": executable file not found in \$PATH`)
  1599  }