github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/daemon/daemon_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 daemon
    21  
    22  import (
    23  	"fmt"
    24  
    25  	"bytes"
    26  	"encoding/json"
    27  	"errors"
    28  	"io/ioutil"
    29  	"net"
    30  	"net/http"
    31  	"net/http/httptest"
    32  	"os"
    33  	"path/filepath"
    34  	"sync"
    35  	"syscall"
    36  	"testing"
    37  	"time"
    38  
    39  	"github.com/gorilla/mux"
    40  	"gopkg.in/check.v1"
    41  
    42  	"github.com/snapcore/snapd/client"
    43  	"github.com/snapcore/snapd/dirs"
    44  	"github.com/snapcore/snapd/logger"
    45  	"github.com/snapcore/snapd/osutil"
    46  	"github.com/snapcore/snapd/overlord/auth"
    47  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    48  	"github.com/snapcore/snapd/overlord/ifacestate"
    49  	"github.com/snapcore/snapd/overlord/patch"
    50  	"github.com/snapcore/snapd/overlord/snapstate"
    51  	"github.com/snapcore/snapd/overlord/standby"
    52  	"github.com/snapcore/snapd/overlord/state"
    53  	"github.com/snapcore/snapd/polkit"
    54  	"github.com/snapcore/snapd/snap"
    55  	"github.com/snapcore/snapd/store"
    56  	"github.com/snapcore/snapd/systemd"
    57  	"github.com/snapcore/snapd/testutil"
    58  )
    59  
    60  // Hook up check.v1 into the "go test" runner
    61  func Test(t *testing.T) { check.TestingT(t) }
    62  
    63  type daemonSuite struct {
    64  	testutil.BaseTest
    65  
    66  	authorized      bool
    67  	err             error
    68  	lastPolkitFlags polkit.CheckFlags
    69  	notified        []string
    70  }
    71  
    72  var _ = check.Suite(&daemonSuite{})
    73  
    74  func (s *daemonSuite) checkAuthorization(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) {
    75  	s.lastPolkitFlags = flags
    76  	return s.authorized, s.err
    77  }
    78  
    79  func (s *daemonSuite) SetUpTest(c *check.C) {
    80  	s.BaseTest.SetUpTest(c)
    81  
    82  	dirs.SetRootDir(c.MkDir())
    83  	s.AddCleanup(osutil.MockMountInfo(""))
    84  
    85  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
    86  	c.Assert(err, check.IsNil)
    87  	systemdSdNotify = func(notif string) error {
    88  		s.notified = append(s.notified, notif)
    89  		return nil
    90  	}
    91  	s.notified = nil
    92  	polkitCheckAuthorization = s.checkAuthorization
    93  	s.AddCleanup(ifacestate.MockSecurityBackends(nil))
    94  }
    95  
    96  func (s *daemonSuite) TearDownTest(c *check.C) {
    97  	systemdSdNotify = systemd.SdNotify
    98  	dirs.SetRootDir("")
    99  	s.authorized = false
   100  	s.err = nil
   101  	logger.SetLogger(logger.NullLogger)
   102  
   103  	s.BaseTest.TearDownTest(c)
   104  }
   105  
   106  func (s *daemonSuite) TearDownSuite(c *check.C) {
   107  	polkitCheckAuthorization = polkit.CheckAuthorization
   108  }
   109  
   110  // build a new daemon, with only a little of Init(), suitable for the tests
   111  func newTestDaemon(c *check.C) *Daemon {
   112  	d, err := New()
   113  	c.Assert(err, check.IsNil)
   114  	d.addRoutes()
   115  
   116  	// don't actually try to talk to the store on snapstate.Ensure
   117  	// needs doing after the call to devicestate.Manager (which
   118  	// happens in daemon.New via overlord.New)
   119  	snapstate.CanAutoRefresh = nil
   120  
   121  	return d
   122  }
   123  
   124  // a Response suitable for testing
   125  type mockHandler struct {
   126  	cmd        *Command
   127  	lastMethod string
   128  }
   129  
   130  func (mck *mockHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   131  	mck.lastMethod = r.Method
   132  }
   133  
   134  func (s *daemonSuite) TestCommandMethodDispatch(c *check.C) {
   135  	fakeUserAgent := "some-agent-talking-to-snapd/1.0"
   136  
   137  	cmd := &Command{d: newTestDaemon(c)}
   138  	mck := &mockHandler{cmd: cmd}
   139  	rf := func(innerCmd *Command, req *http.Request, user *auth.UserState) Response {
   140  		c.Assert(cmd, check.Equals, innerCmd)
   141  		c.Check(store.ClientUserAgent(req.Context()), check.Equals, fakeUserAgent)
   142  		return mck
   143  	}
   144  	cmd.GET = rf
   145  	cmd.PUT = rf
   146  	cmd.POST = rf
   147  
   148  	for _, method := range []string{"GET", "POST", "PUT"} {
   149  		req, err := http.NewRequest(method, "", nil)
   150  		req.Header.Add("User-Agent", fakeUserAgent)
   151  		c.Assert(err, check.IsNil)
   152  
   153  		rec := httptest.NewRecorder()
   154  		cmd.ServeHTTP(rec, req)
   155  		c.Check(rec.Code, check.Equals, 401, check.Commentf(method))
   156  
   157  		rec = httptest.NewRecorder()
   158  		req.RemoteAddr = "pid=100;uid=0;socket=;"
   159  
   160  		cmd.ServeHTTP(rec, req)
   161  		c.Check(mck.lastMethod, check.Equals, method)
   162  		c.Check(rec.Code, check.Equals, 200)
   163  	}
   164  
   165  	req, err := http.NewRequest("POTATO", "", nil)
   166  	c.Assert(err, check.IsNil)
   167  	req.RemoteAddr = "pid=100;uid=0;socket=;"
   168  
   169  	rec := httptest.NewRecorder()
   170  	cmd.ServeHTTP(rec, req)
   171  	c.Check(rec.Code, check.Equals, 405)
   172  }
   173  
   174  func (s *daemonSuite) TestCommandRestartingState(c *check.C) {
   175  	d := newTestDaemon(c)
   176  
   177  	cmd := &Command{d: d}
   178  	cmd.GET = func(*Command, *http.Request, *auth.UserState) Response {
   179  		return SyncResponse(nil, nil)
   180  	}
   181  	req, err := http.NewRequest("GET", "", nil)
   182  	c.Assert(err, check.IsNil)
   183  	req.RemoteAddr = "pid=100;uid=0;socket=;"
   184  
   185  	rec := httptest.NewRecorder()
   186  	cmd.ServeHTTP(rec, req)
   187  	c.Check(rec.Code, check.Equals, 200)
   188  	var rst struct {
   189  		Maintenance *errorResult `json:"maintenance"`
   190  	}
   191  	err = json.Unmarshal(rec.Body.Bytes(), &rst)
   192  	c.Assert(err, check.IsNil)
   193  	c.Check(rst.Maintenance, check.IsNil)
   194  
   195  	state.MockRestarting(d.overlord.State(), state.RestartSystem)
   196  	rec = httptest.NewRecorder()
   197  	cmd.ServeHTTP(rec, req)
   198  	c.Check(rec.Code, check.Equals, 200)
   199  	err = json.Unmarshal(rec.Body.Bytes(), &rst)
   200  	c.Assert(err, check.IsNil)
   201  	c.Check(rst.Maintenance, check.DeepEquals, &errorResult{
   202  		Kind:    client.ErrorKindSystemRestart,
   203  		Message: "system is restarting",
   204  	})
   205  
   206  	state.MockRestarting(d.overlord.State(), state.RestartDaemon)
   207  	rec = httptest.NewRecorder()
   208  	cmd.ServeHTTP(rec, req)
   209  	c.Check(rec.Code, check.Equals, 200)
   210  	err = json.Unmarshal(rec.Body.Bytes(), &rst)
   211  	c.Assert(err, check.IsNil)
   212  	c.Check(rst.Maintenance, check.DeepEquals, &errorResult{
   213  		Kind:    client.ErrorKindDaemonRestart,
   214  		Message: "daemon is restarting",
   215  	})
   216  }
   217  
   218  func (s *daemonSuite) TestMaintenanceJsonDeletedOnStart(c *check.C) {
   219  	// write a maintenance.json file that has that the system is restarting
   220  	maintErr := &errorResult{
   221  		Kind:    client.ErrorKindDaemonRestart,
   222  		Message: systemRestartMsg,
   223  	}
   224  
   225  	b, err := json.Marshal(maintErr)
   226  	c.Assert(err, check.IsNil)
   227  	c.Assert(os.MkdirAll(filepath.Dir(dirs.SnapdMaintenanceFile), 0755), check.IsNil)
   228  	c.Assert(ioutil.WriteFile(dirs.SnapdMaintenanceFile, b, 0644), check.IsNil)
   229  
   230  	d := newTestDaemon(c)
   231  	makeDaemonListeners(c, d)
   232  
   233  	// after starting, maintenance.json should be removed
   234  	d.Start()
   235  	c.Assert(dirs.SnapdMaintenanceFile, testutil.FileAbsent)
   236  	d.Stop(nil)
   237  }
   238  
   239  func (s *daemonSuite) TestFillsWarnings(c *check.C) {
   240  	d := newTestDaemon(c)
   241  
   242  	cmd := &Command{d: d}
   243  	cmd.GET = func(*Command, *http.Request, *auth.UserState) Response {
   244  		return SyncResponse(nil, nil)
   245  	}
   246  	req, err := http.NewRequest("GET", "", nil)
   247  	c.Assert(err, check.IsNil)
   248  	req.RemoteAddr = "pid=100;uid=0;socket=;"
   249  
   250  	rec := httptest.NewRecorder()
   251  	cmd.ServeHTTP(rec, req)
   252  	c.Check(rec.Code, check.Equals, 200)
   253  	var rst struct {
   254  		WarningTimestamp *time.Time `json:"warning-timestamp,omitempty"`
   255  		WarningCount     int        `json:"warning-count,omitempty"`
   256  	}
   257  	err = json.Unmarshal(rec.Body.Bytes(), &rst)
   258  	c.Assert(err, check.IsNil)
   259  	c.Check(rst.WarningCount, check.Equals, 0)
   260  	c.Check(rst.WarningTimestamp, check.IsNil)
   261  
   262  	st := d.overlord.State()
   263  	st.Lock()
   264  	st.Warnf("hello world")
   265  	st.Unlock()
   266  
   267  	rec = httptest.NewRecorder()
   268  	cmd.ServeHTTP(rec, req)
   269  	c.Check(rec.Code, check.Equals, 200)
   270  	err = json.Unmarshal(rec.Body.Bytes(), &rst)
   271  	c.Assert(err, check.IsNil)
   272  	c.Check(rst.WarningCount, check.Equals, 1)
   273  	c.Check(rst.WarningTimestamp, check.NotNil)
   274  }
   275  
   276  func (s *daemonSuite) TestGuestAccess(c *check.C) {
   277  	get := &http.Request{Method: "GET"}
   278  	put := &http.Request{Method: "PUT"}
   279  	pst := &http.Request{Method: "POST"}
   280  	del := &http.Request{Method: "DELETE"}
   281  
   282  	cmd := &Command{d: newTestDaemon(c)}
   283  	c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized)
   284  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   285  	c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized)
   286  	c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized)
   287  
   288  	cmd = &Command{d: newTestDaemon(c), RootOnly: true}
   289  	c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized)
   290  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   291  	c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized)
   292  	c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized)
   293  
   294  	cmd = &Command{d: newTestDaemon(c), UserOK: true}
   295  	c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized)
   296  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   297  	c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized)
   298  	c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized)
   299  
   300  	cmd = &Command{d: newTestDaemon(c), GuestOK: true}
   301  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   302  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   303  	c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized)
   304  	c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized)
   305  }
   306  
   307  func (s *daemonSuite) TestSnapctlAccessSnapOKWithUser(c *check.C) {
   308  	remoteAddr := "pid=100;uid=1000;socket=" + dirs.SnapSocket + ";"
   309  	get := &http.Request{Method: "GET", RemoteAddr: remoteAddr}
   310  	put := &http.Request{Method: "PUT", RemoteAddr: remoteAddr}
   311  	pst := &http.Request{Method: "POST", RemoteAddr: remoteAddr}
   312  	del := &http.Request{Method: "DELETE", RemoteAddr: remoteAddr}
   313  
   314  	cmd := &Command{d: newTestDaemon(c), SnapOK: true}
   315  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   316  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   317  	c.Check(cmd.canAccess(pst, nil), check.Equals, accessOK)
   318  	c.Check(cmd.canAccess(del, nil), check.Equals, accessOK)
   319  }
   320  
   321  func (s *daemonSuite) TestSnapctlAccessSnapOKWithRoot(c *check.C) {
   322  	remoteAddr := "pid=100;uid=0;socket=" + dirs.SnapSocket + ";"
   323  	get := &http.Request{Method: "GET", RemoteAddr: remoteAddr}
   324  	put := &http.Request{Method: "PUT", RemoteAddr: remoteAddr}
   325  	pst := &http.Request{Method: "POST", RemoteAddr: remoteAddr}
   326  	del := &http.Request{Method: "DELETE", RemoteAddr: remoteAddr}
   327  
   328  	cmd := &Command{d: newTestDaemon(c), SnapOK: true}
   329  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   330  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   331  	c.Check(cmd.canAccess(pst, nil), check.Equals, accessOK)
   332  	c.Check(cmd.canAccess(del, nil), check.Equals, accessOK)
   333  }
   334  
   335  func (s *daemonSuite) TestUserAccess(c *check.C) {
   336  	get := &http.Request{Method: "GET", RemoteAddr: "pid=100;uid=42;socket=;"}
   337  	put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=42;socket=;"}
   338  
   339  	cmd := &Command{d: newTestDaemon(c)}
   340  	c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized)
   341  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   342  
   343  	cmd = &Command{d: newTestDaemon(c), RootOnly: true}
   344  	c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized)
   345  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   346  
   347  	cmd = &Command{d: newTestDaemon(c), UserOK: true}
   348  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   349  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   350  
   351  	cmd = &Command{d: newTestDaemon(c), GuestOK: true}
   352  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   353  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   354  
   355  	// Since this request has a RemoteAddr, it must be coming from the snapd
   356  	// socket instead of the snap one. In that case, SnapOK should have no
   357  	// bearing on the default behavior, which is to deny access.
   358  	cmd = &Command{d: newTestDaemon(c), SnapOK: true}
   359  	c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized)
   360  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   361  }
   362  
   363  func (s *daemonSuite) TestLoggedInUserAccess(c *check.C) {
   364  	user := &auth.UserState{}
   365  	get := &http.Request{Method: "GET", RemoteAddr: "pid=100;uid=42;socket=;"}
   366  	put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=42;socket=;"}
   367  
   368  	cmd := &Command{d: newTestDaemon(c)}
   369  	c.Check(cmd.canAccess(get, user), check.Equals, accessOK)
   370  	c.Check(cmd.canAccess(put, user), check.Equals, accessOK)
   371  
   372  	cmd = &Command{d: newTestDaemon(c), RootOnly: true}
   373  	c.Check(cmd.canAccess(get, user), check.Equals, accessUnauthorized)
   374  	c.Check(cmd.canAccess(put, user), check.Equals, accessUnauthorized)
   375  
   376  	cmd = &Command{d: newTestDaemon(c), UserOK: true}
   377  	c.Check(cmd.canAccess(get, user), check.Equals, accessOK)
   378  	c.Check(cmd.canAccess(put, user), check.Equals, accessOK)
   379  
   380  	cmd = &Command{d: newTestDaemon(c), GuestOK: true}
   381  	c.Check(cmd.canAccess(get, user), check.Equals, accessOK)
   382  	c.Check(cmd.canAccess(put, user), check.Equals, accessOK)
   383  
   384  	cmd = &Command{d: newTestDaemon(c), SnapOK: true}
   385  	c.Check(cmd.canAccess(get, user), check.Equals, accessOK)
   386  	c.Check(cmd.canAccess(put, user), check.Equals, accessOK)
   387  }
   388  
   389  func (s *daemonSuite) TestSuperAccess(c *check.C) {
   390  	get := &http.Request{Method: "GET", RemoteAddr: "pid=100;uid=0;socket=;"}
   391  	put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=0;socket=;"}
   392  
   393  	cmd := &Command{d: newTestDaemon(c)}
   394  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   395  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   396  
   397  	cmd = &Command{d: newTestDaemon(c), RootOnly: true}
   398  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   399  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   400  
   401  	cmd = &Command{d: newTestDaemon(c), UserOK: true}
   402  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   403  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   404  
   405  	cmd = &Command{d: newTestDaemon(c), GuestOK: true}
   406  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   407  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   408  
   409  	cmd = &Command{d: newTestDaemon(c), SnapOK: true}
   410  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   411  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   412  }
   413  
   414  func (s *daemonSuite) TestPolkitAccess(c *check.C) {
   415  	put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=42;socket=;"}
   416  	cmd := &Command{d: newTestDaemon(c), PolkitOK: "polkit.action"}
   417  
   418  	// polkit says user is not authorised
   419  	s.authorized = false
   420  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   421  
   422  	// polkit grants authorisation
   423  	s.authorized = true
   424  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   425  
   426  	// an error occurs communicating with polkit
   427  	s.err = errors.New("error")
   428  	c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized)
   429  
   430  	// if the user dismisses the auth request, forbid access
   431  	s.err = polkit.ErrDismissed
   432  	c.Check(cmd.canAccess(put, nil), check.Equals, accessCancelled)
   433  }
   434  
   435  func (s *daemonSuite) TestPolkitAccessForGet(c *check.C) {
   436  	get := &http.Request{Method: "GET", RemoteAddr: "pid=100;uid=42;socket=;"}
   437  	cmd := &Command{d: newTestDaemon(c), PolkitOK: "polkit.action"}
   438  
   439  	// polkit can grant authorisation for GET requests
   440  	s.authorized = true
   441  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   442  
   443  	// for UserOK commands, polkit is not consulted
   444  	cmd.UserOK = true
   445  	polkitCheckAuthorization = func(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) {
   446  		panic("polkit.CheckAuthorization called")
   447  	}
   448  	c.Check(cmd.canAccess(get, nil), check.Equals, accessOK)
   449  }
   450  
   451  func (s *daemonSuite) TestPolkitInteractivity(c *check.C) {
   452  	put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=42;socket=;", Header: make(http.Header)}
   453  	cmd := &Command{d: newTestDaemon(c), PolkitOK: "polkit.action"}
   454  	s.authorized = true
   455  
   456  	var logbuf bytes.Buffer
   457  	log, err := logger.New(&logbuf, logger.DefaultFlags)
   458  	c.Assert(err, check.IsNil)
   459  	logger.SetLogger(log)
   460  
   461  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   462  	c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckNone)
   463  	c.Check(logbuf.String(), check.Equals, "")
   464  
   465  	put.Header.Set(client.AllowInteractionHeader, "true")
   466  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   467  	c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckAllowInteraction)
   468  	c.Check(logbuf.String(), check.Equals, "")
   469  
   470  	// bad values are logged and treated as false
   471  	put.Header.Set(client.AllowInteractionHeader, "garbage")
   472  	c.Check(cmd.canAccess(put, nil), check.Equals, accessOK)
   473  	c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckNone)
   474  	c.Check(logbuf.String(), testutil.Contains, "error parsing X-Allow-Interaction header:")
   475  }
   476  
   477  func (s *daemonSuite) TestAddRoutes(c *check.C) {
   478  	d := newTestDaemon(c)
   479  
   480  	expected := make([]string, len(api))
   481  	for i, v := range api {
   482  		if v.PathPrefix != "" {
   483  			expected[i] = v.PathPrefix
   484  			continue
   485  		}
   486  		expected[i] = v.Path
   487  	}
   488  
   489  	got := make([]string, 0, len(api))
   490  	c.Assert(d.router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
   491  		got = append(got, route.GetName())
   492  		return nil
   493  	}), check.IsNil)
   494  
   495  	c.Check(got, check.DeepEquals, expected) // this'll stop being true if routes are added that aren't commands (e.g. for the favicon)
   496  
   497  	// XXX: still waiting to know how to check d.router.NotFoundHandler has been set to NotFound
   498  	//      the old test relied on undefined behaviour:
   499  	//      c.Check(fmt.Sprintf("%p", d.router.NotFoundHandler), check.Equals, fmt.Sprintf("%p", NotFound))
   500  }
   501  
   502  type witnessAcceptListener struct {
   503  	net.Listener
   504  
   505  	accept  chan struct{}
   506  	accept1 bool
   507  
   508  	idempotClose sync.Once
   509  	closeErr     error
   510  	closed       chan struct{}
   511  }
   512  
   513  func (l *witnessAcceptListener) Accept() (net.Conn, error) {
   514  	if !l.accept1 {
   515  		l.accept1 = true
   516  		close(l.accept)
   517  	}
   518  	return l.Listener.Accept()
   519  }
   520  
   521  func (l *witnessAcceptListener) Close() error {
   522  	l.idempotClose.Do(func() {
   523  		l.closeErr = l.Listener.Close()
   524  		if l.closed != nil {
   525  			close(l.closed)
   526  		}
   527  	})
   528  	return l.closeErr
   529  }
   530  
   531  func (s *daemonSuite) markSeeded(d *Daemon) {
   532  	st := d.overlord.State()
   533  	st.Lock()
   534  	st.Set("seeded", true)
   535  	devicestatetest.SetDevice(st, &auth.DeviceState{
   536  		Brand:  "canonical",
   537  		Model:  "pc",
   538  		Serial: "serialserial",
   539  	})
   540  	st.Unlock()
   541  }
   542  
   543  func (s *daemonSuite) TestStartStop(c *check.C) {
   544  	d := newTestDaemon(c)
   545  	// mark as already seeded
   546  	s.markSeeded(d)
   547  	// and pretend we have snaps
   548  	st := d.overlord.State()
   549  	st.Lock()
   550  	snapstate.Set(st, "core", &snapstate.SnapState{
   551  		Active: true,
   552  		Sequence: []*snap.SideInfo{
   553  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
   554  		},
   555  		Current: snap.R(1),
   556  	})
   557  	st.Unlock()
   558  	// 1 snap => extended timeout 30s + 5s
   559  	const extendedTimeoutUSec = "EXTEND_TIMEOUT_USEC=35000000"
   560  
   561  	l1, err := net.Listen("tcp", "127.0.0.1:0")
   562  	c.Assert(err, check.IsNil)
   563  	l2, err := net.Listen("tcp", "127.0.0.1:0")
   564  	c.Assert(err, check.IsNil)
   565  
   566  	snapdAccept := make(chan struct{})
   567  	d.snapdListener = &witnessAcceptListener{Listener: l1, accept: snapdAccept}
   568  
   569  	snapAccept := make(chan struct{})
   570  	d.snapListener = &witnessAcceptListener{Listener: l2, accept: snapAccept}
   571  
   572  	c.Assert(d.Start(), check.IsNil)
   573  
   574  	c.Check(s.notified, check.DeepEquals, []string{extendedTimeoutUSec, "READY=1"})
   575  
   576  	snapdDone := make(chan struct{})
   577  	go func() {
   578  		select {
   579  		case <-snapdAccept:
   580  		case <-time.After(2 * time.Second):
   581  			c.Fatal("snapd accept was not called")
   582  		}
   583  		close(snapdDone)
   584  	}()
   585  
   586  	snapDone := make(chan struct{})
   587  	go func() {
   588  		select {
   589  		case <-snapAccept:
   590  		case <-time.After(2 * time.Second):
   591  			c.Fatal("snapd accept was not called")
   592  		}
   593  		close(snapDone)
   594  	}()
   595  
   596  	<-snapdDone
   597  	<-snapDone
   598  
   599  	err = d.Stop(nil)
   600  	c.Check(err, check.IsNil)
   601  
   602  	c.Check(s.notified, check.DeepEquals, []string{extendedTimeoutUSec, "READY=1", "STOPPING=1"})
   603  }
   604  
   605  func (s *daemonSuite) TestRestartWiring(c *check.C) {
   606  	d := newTestDaemon(c)
   607  	// mark as already seeded
   608  	s.markSeeded(d)
   609  
   610  	l, err := net.Listen("tcp", "127.0.0.1:0")
   611  	c.Assert(err, check.IsNil)
   612  
   613  	snapdAccept := make(chan struct{})
   614  	d.snapdListener = &witnessAcceptListener{Listener: l, accept: snapdAccept}
   615  
   616  	snapAccept := make(chan struct{})
   617  	d.snapListener = &witnessAcceptListener{Listener: l, accept: snapAccept}
   618  
   619  	c.Assert(d.Start(), check.IsNil)
   620  	stoppedYet := false
   621  	defer func() {
   622  		if !stoppedYet {
   623  			d.Stop(nil)
   624  		}
   625  	}()
   626  
   627  	snapdDone := make(chan struct{})
   628  	go func() {
   629  		select {
   630  		case <-snapdAccept:
   631  		case <-time.After(2 * time.Second):
   632  			c.Fatal("snapd accept was not called")
   633  		}
   634  		close(snapdDone)
   635  	}()
   636  
   637  	snapDone := make(chan struct{})
   638  	go func() {
   639  		select {
   640  		case <-snapAccept:
   641  		case <-time.After(2 * time.Second):
   642  			c.Fatal("snap accept was not called")
   643  		}
   644  		close(snapDone)
   645  	}()
   646  
   647  	<-snapdDone
   648  	<-snapDone
   649  
   650  	d.overlord.State().RequestRestart(state.RestartDaemon)
   651  
   652  	select {
   653  	case <-d.Dying():
   654  	case <-time.After(2 * time.Second):
   655  		c.Fatal("RequestRestart -> overlord -> Kill chain didn't work")
   656  	}
   657  
   658  	d.Stop(nil)
   659  	stoppedYet = true
   660  
   661  	c.Assert(s.notified, check.DeepEquals, []string{"EXTEND_TIMEOUT_USEC=30000000", "READY=1", "STOPPING=1"})
   662  }
   663  
   664  func (s *daemonSuite) TestGracefulStop(c *check.C) {
   665  	d := newTestDaemon(c)
   666  
   667  	responding := make(chan struct{})
   668  	doRespond := make(chan bool, 1)
   669  
   670  	d.router.HandleFunc("/endp", func(w http.ResponseWriter, r *http.Request) {
   671  		close(responding)
   672  		if <-doRespond {
   673  			w.Write([]byte("OKOK"))
   674  		} else {
   675  			w.Write([]byte("Gone"))
   676  		}
   677  	})
   678  
   679  	// mark as already seeded
   680  	s.markSeeded(d)
   681  	// and pretend we have snaps
   682  	st := d.overlord.State()
   683  	st.Lock()
   684  	snapstate.Set(st, "core", &snapstate.SnapState{
   685  		Active: true,
   686  		Sequence: []*snap.SideInfo{
   687  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
   688  		},
   689  		Current: snap.R(1),
   690  	})
   691  	st.Unlock()
   692  
   693  	snapdL, err := net.Listen("tcp", "127.0.0.1:0")
   694  	c.Assert(err, check.IsNil)
   695  
   696  	snapL, err := net.Listen("tcp", "127.0.0.1:0")
   697  	c.Assert(err, check.IsNil)
   698  
   699  	snapdAccept := make(chan struct{})
   700  	snapdClosed := make(chan struct{})
   701  	d.snapdListener = &witnessAcceptListener{Listener: snapdL, accept: snapdAccept, closed: snapdClosed}
   702  
   703  	snapAccept := make(chan struct{})
   704  	d.snapListener = &witnessAcceptListener{Listener: snapL, accept: snapAccept}
   705  
   706  	c.Assert(d.Start(), check.IsNil)
   707  
   708  	snapdAccepting := make(chan struct{})
   709  	go func() {
   710  		select {
   711  		case <-snapdAccept:
   712  		case <-time.After(2 * time.Second):
   713  			c.Fatal("snapd accept was not called")
   714  		}
   715  		close(snapdAccepting)
   716  	}()
   717  
   718  	snapAccepting := make(chan struct{})
   719  	go func() {
   720  		select {
   721  		case <-snapAccept:
   722  		case <-time.After(2 * time.Second):
   723  			c.Fatal("snapd accept was not called")
   724  		}
   725  		close(snapAccepting)
   726  	}()
   727  
   728  	<-snapdAccepting
   729  	<-snapAccepting
   730  
   731  	alright := make(chan struct{})
   732  
   733  	go func() {
   734  		res, err := http.Get(fmt.Sprintf("http://%s/endp", snapdL.Addr()))
   735  		c.Assert(err, check.IsNil)
   736  		c.Check(res.StatusCode, check.Equals, 200)
   737  		body, err := ioutil.ReadAll(res.Body)
   738  		res.Body.Close()
   739  		c.Assert(err, check.IsNil)
   740  		c.Check(string(body), check.Equals, "OKOK")
   741  		close(alright)
   742  	}()
   743  	go func() {
   744  		<-snapdClosed
   745  		time.Sleep(200 * time.Millisecond)
   746  		doRespond <- true
   747  	}()
   748  
   749  	<-responding
   750  	err = d.Stop(nil)
   751  	doRespond <- false
   752  	c.Check(err, check.IsNil)
   753  
   754  	select {
   755  	case <-alright:
   756  	case <-time.After(2 * time.Second):
   757  		c.Fatal("never got proper response")
   758  	}
   759  }
   760  
   761  func (s *daemonSuite) TestGracefulStopHasLimits(c *check.C) {
   762  	d := newTestDaemon(c)
   763  
   764  	// mark as already seeded
   765  	s.markSeeded(d)
   766  
   767  	restore := MockShutdownTimeout(time.Second)
   768  	defer restore()
   769  
   770  	responding := make(chan struct{})
   771  	doRespond := make(chan bool, 1)
   772  
   773  	d.router.HandleFunc("/endp", func(w http.ResponseWriter, r *http.Request) {
   774  		close(responding)
   775  		if <-doRespond {
   776  			for {
   777  				// write in a loop to keep the handler running
   778  				if _, err := w.Write([]byte("OKOK")); err != nil {
   779  					break
   780  				}
   781  				time.Sleep(50 * time.Millisecond)
   782  			}
   783  		} else {
   784  			w.Write([]byte("Gone"))
   785  		}
   786  	})
   787  
   788  	snapdL, err := net.Listen("tcp", "127.0.0.1:0")
   789  	c.Assert(err, check.IsNil)
   790  
   791  	snapL, err := net.Listen("tcp", "127.0.0.1:0")
   792  	c.Assert(err, check.IsNil)
   793  
   794  	snapdAccept := make(chan struct{})
   795  	snapdClosed := make(chan struct{})
   796  	d.snapdListener = &witnessAcceptListener{Listener: snapdL, accept: snapdAccept, closed: snapdClosed}
   797  
   798  	snapAccept := make(chan struct{})
   799  	d.snapListener = &witnessAcceptListener{Listener: snapL, accept: snapAccept}
   800  
   801  	c.Assert(d.Start(), check.IsNil)
   802  
   803  	snapdAccepting := make(chan struct{})
   804  	go func() {
   805  		select {
   806  		case <-snapdAccept:
   807  		case <-time.After(2 * time.Second):
   808  			c.Fatal("snapd accept was not called")
   809  		}
   810  		close(snapdAccepting)
   811  	}()
   812  
   813  	snapAccepting := make(chan struct{})
   814  	go func() {
   815  		select {
   816  		case <-snapAccept:
   817  		case <-time.After(2 * time.Second):
   818  			c.Fatal("snapd accept was not called")
   819  		}
   820  		close(snapAccepting)
   821  	}()
   822  
   823  	<-snapdAccepting
   824  	<-snapAccepting
   825  
   826  	clientErr := make(chan error)
   827  
   828  	go func() {
   829  		_, err := http.Get(fmt.Sprintf("http://%s/endp", snapdL.Addr()))
   830  		c.Assert(err, check.NotNil)
   831  		clientErr <- err
   832  		close(clientErr)
   833  	}()
   834  	go func() {
   835  		<-snapdClosed
   836  		time.Sleep(200 * time.Millisecond)
   837  		doRespond <- true
   838  	}()
   839  
   840  	<-responding
   841  	err = d.Stop(nil)
   842  	doRespond <- false
   843  	c.Check(err, check.IsNil)
   844  
   845  	select {
   846  	case cErr := <-clientErr:
   847  		c.Check(cErr, check.ErrorMatches, ".*: EOF")
   848  	case <-time.After(5 * time.Second):
   849  		c.Fatal("never got proper response")
   850  	}
   851  }
   852  
   853  func (s *daemonSuite) testRestartSystemWiring(c *check.C, restartKind state.RestartType) {
   854  	d := newTestDaemon(c)
   855  	// mark as already seeded
   856  	s.markSeeded(d)
   857  
   858  	l, err := net.Listen("tcp", "127.0.0.1:0")
   859  	c.Assert(err, check.IsNil)
   860  
   861  	snapdAccept := make(chan struct{})
   862  	d.snapdListener = &witnessAcceptListener{Listener: l, accept: snapdAccept}
   863  
   864  	snapAccept := make(chan struct{})
   865  	d.snapListener = &witnessAcceptListener{Listener: l, accept: snapAccept}
   866  
   867  	c.Assert(d.Start(), check.IsNil)
   868  	defer d.Stop(nil)
   869  
   870  	st := d.overlord.State()
   871  
   872  	snapdDone := make(chan struct{})
   873  	go func() {
   874  		select {
   875  		case <-snapdAccept:
   876  		case <-time.After(2 * time.Second):
   877  			c.Fatal("snapd accept was not called")
   878  		}
   879  		close(snapdDone)
   880  	}()
   881  
   882  	snapDone := make(chan struct{})
   883  	go func() {
   884  		select {
   885  		case <-snapAccept:
   886  		case <-time.After(2 * time.Second):
   887  			c.Fatal("snap accept was not called")
   888  		}
   889  		close(snapDone)
   890  	}()
   891  
   892  	<-snapdDone
   893  	<-snapDone
   894  
   895  	oldRebootNoticeWait := rebootNoticeWait
   896  	oldRebootWaitTimeout := rebootWaitTimeout
   897  	defer func() {
   898  		reboot = rebootImpl
   899  		rebootNoticeWait = oldRebootNoticeWait
   900  		rebootWaitTimeout = oldRebootWaitTimeout
   901  	}()
   902  	rebootWaitTimeout = 100 * time.Millisecond
   903  	rebootNoticeWait = 150 * time.Millisecond
   904  
   905  	var delays []time.Duration
   906  	reboot = func(d time.Duration) error {
   907  		delays = append(delays, d)
   908  		return nil
   909  	}
   910  
   911  	st.Lock()
   912  	st.RequestRestart(restartKind)
   913  	st.Unlock()
   914  
   915  	defer func() {
   916  		d.mu.Lock()
   917  		d.requestedRestart = state.RestartUnset
   918  		d.mu.Unlock()
   919  	}()
   920  
   921  	select {
   922  	case <-d.Dying():
   923  	case <-time.After(2 * time.Second):
   924  		c.Fatal("RequestRestart -> overlord -> Kill chain didn't work")
   925  	}
   926  
   927  	d.mu.Lock()
   928  	rs := d.requestedRestart
   929  	d.mu.Unlock()
   930  
   931  	c.Check(rs, check.Equals, restartKind)
   932  
   933  	c.Check(delays, check.HasLen, 1)
   934  	c.Check(delays[0], check.DeepEquals, rebootWaitTimeout)
   935  
   936  	now := time.Now()
   937  
   938  	err = d.Stop(nil)
   939  
   940  	c.Check(err, check.ErrorMatches, "expected reboot did not happen")
   941  
   942  	c.Check(delays, check.HasLen, 2)
   943  	if restartKind == state.RestartSystem {
   944  		c.Check(delays[1], check.DeepEquals, 1*time.Minute)
   945  	} else if restartKind == state.RestartSystemNow {
   946  		c.Check(delays[1], check.DeepEquals, time.Duration(0))
   947  	}
   948  
   949  	// we are not stopping, we wait for the reboot instead
   950  	c.Check(s.notified, check.DeepEquals, []string{"EXTEND_TIMEOUT_USEC=30000000", "READY=1"})
   951  
   952  	st.Lock()
   953  	defer st.Unlock()
   954  	var rebootAt time.Time
   955  	err = st.Get("daemon-system-restart-at", &rebootAt)
   956  	c.Assert(err, check.IsNil)
   957  	if restartKind == state.RestartSystem {
   958  		approxAt := now.Add(time.Minute)
   959  		c.Check(rebootAt.After(approxAt) || rebootAt.Equal(approxAt), check.Equals, true)
   960  	} else if restartKind == state.RestartSystemNow {
   961  		// should be good enough
   962  		c.Check(rebootAt.Before(now.Add(10*time.Second)), check.Equals, true)
   963  	}
   964  
   965  	// finally check that maintenance.json was written appropriate for this
   966  	// restart reason
   967  	b, err := ioutil.ReadFile(dirs.SnapdMaintenanceFile)
   968  	c.Assert(err, check.IsNil)
   969  
   970  	maintErr := &errorResult{}
   971  	c.Assert(json.Unmarshal(b, maintErr), check.IsNil)
   972  
   973  	exp := maintenanceForRestartType(restartKind)
   974  	c.Assert(maintErr, check.DeepEquals, exp)
   975  }
   976  
   977  func (s *daemonSuite) TestRestartSystemGracefulWiring(c *check.C) {
   978  	s.testRestartSystemWiring(c, state.RestartSystem)
   979  }
   980  
   981  func (s *daemonSuite) TestRestartSystemImmediateWiring(c *check.C) {
   982  	s.testRestartSystemWiring(c, state.RestartSystemNow)
   983  }
   984  
   985  func (s *daemonSuite) TestRebootHelper(c *check.C) {
   986  	cmd := testutil.MockCommand(c, "shutdown", "")
   987  	defer cmd.Restore()
   988  
   989  	tests := []struct {
   990  		delay    time.Duration
   991  		delayArg string
   992  	}{
   993  		{-1, "+0"},
   994  		{0, "+0"},
   995  		{time.Minute, "+1"},
   996  		{10 * time.Minute, "+10"},
   997  		{30 * time.Second, "+0"},
   998  	}
   999  
  1000  	for _, t := range tests {
  1001  		err := reboot(t.delay)
  1002  		c.Assert(err, check.IsNil)
  1003  		c.Check(cmd.Calls(), check.DeepEquals, [][]string{
  1004  			{"shutdown", "-r", t.delayArg, "reboot scheduled to update the system"},
  1005  		})
  1006  
  1007  		cmd.ForgetCalls()
  1008  	}
  1009  }
  1010  
  1011  func makeDaemonListeners(c *check.C, d *Daemon) {
  1012  	snapdL, err := net.Listen("tcp", "127.0.0.1:0")
  1013  	c.Assert(err, check.IsNil)
  1014  
  1015  	snapL, err := net.Listen("tcp", "127.0.0.1:0")
  1016  	c.Assert(err, check.IsNil)
  1017  
  1018  	snapdAccept := make(chan struct{})
  1019  	snapdClosed := make(chan struct{})
  1020  	d.snapdListener = &witnessAcceptListener{Listener: snapdL, accept: snapdAccept, closed: snapdClosed}
  1021  
  1022  	snapAccept := make(chan struct{})
  1023  	d.snapListener = &witnessAcceptListener{Listener: snapL, accept: snapAccept}
  1024  }
  1025  
  1026  // This test tests that when the snapd calls a restart of the system
  1027  // a sigterm (from e.g. systemd) is handled when it arrives before
  1028  // stop is fully done.
  1029  func (s *daemonSuite) TestRestartShutdownWithSigtermInBetween(c *check.C) {
  1030  	oldRebootNoticeWait := rebootNoticeWait
  1031  	defer func() {
  1032  		rebootNoticeWait = oldRebootNoticeWait
  1033  	}()
  1034  	rebootNoticeWait = 150 * time.Millisecond
  1035  
  1036  	cmd := testutil.MockCommand(c, "shutdown", "")
  1037  	defer cmd.Restore()
  1038  
  1039  	d := newTestDaemon(c)
  1040  	makeDaemonListeners(c, d)
  1041  	s.markSeeded(d)
  1042  
  1043  	c.Assert(d.Start(), check.IsNil)
  1044  	st := d.overlord.State()
  1045  
  1046  	st.Lock()
  1047  	st.RequestRestart(state.RestartSystem)
  1048  	st.Unlock()
  1049  
  1050  	ch := make(chan os.Signal, 2)
  1051  	ch <- syscall.SIGTERM
  1052  	// stop will check if we got a sigterm in between (which we did)
  1053  	err := d.Stop(ch)
  1054  	c.Assert(err, check.IsNil)
  1055  }
  1056  
  1057  // This test tests that when there is a shutdown we close the sigterm
  1058  // handler so that systemd can kill snapd.
  1059  func (s *daemonSuite) TestRestartShutdown(c *check.C) {
  1060  	oldRebootNoticeWait := rebootNoticeWait
  1061  	oldRebootWaitTimeout := rebootWaitTimeout
  1062  	defer func() {
  1063  		rebootNoticeWait = oldRebootNoticeWait
  1064  		rebootWaitTimeout = oldRebootWaitTimeout
  1065  	}()
  1066  	rebootWaitTimeout = 100 * time.Millisecond
  1067  	rebootNoticeWait = 150 * time.Millisecond
  1068  
  1069  	cmd := testutil.MockCommand(c, "shutdown", "")
  1070  	defer cmd.Restore()
  1071  
  1072  	d := newTestDaemon(c)
  1073  	makeDaemonListeners(c, d)
  1074  	s.markSeeded(d)
  1075  
  1076  	c.Assert(d.Start(), check.IsNil)
  1077  	st := d.overlord.State()
  1078  
  1079  	st.Lock()
  1080  	st.RequestRestart(state.RestartSystem)
  1081  	st.Unlock()
  1082  
  1083  	sigCh := make(chan os.Signal, 2)
  1084  	// stop (this will timeout but that's not relevant for this test)
  1085  	d.Stop(sigCh)
  1086  
  1087  	// ensure that the sigCh got closed as part of the stop
  1088  	_, chOpen := <-sigCh
  1089  	c.Assert(chOpen, check.Equals, false)
  1090  }
  1091  
  1092  func (s *daemonSuite) TestRestartExpectedRebootDidNotHappen(c *check.C) {
  1093  	curBootID, err := osutil.BootID()
  1094  	c.Assert(err, check.IsNil)
  1095  
  1096  	fakeState := []byte(fmt.Sprintf(`{"data":{"patch-level":%d,"patch-sublevel":%d,"some":"data","refresh-privacy-key":"0123456789ABCDEF","system-restart-from-boot-id":%q,"daemon-system-restart-at":"%s"},"changes":null,"tasks":null,"last-change-id":0,"last-task-id":0,"last-lane-id":0}`, patch.Level, patch.Sublevel, curBootID, time.Now().UTC().Format(time.RFC3339)))
  1097  	err = ioutil.WriteFile(dirs.SnapStateFile, fakeState, 0600)
  1098  	c.Assert(err, check.IsNil)
  1099  
  1100  	oldRebootNoticeWait := rebootNoticeWait
  1101  	oldRebootRetryWaitTimeout := rebootRetryWaitTimeout
  1102  	defer func() {
  1103  		rebootNoticeWait = oldRebootNoticeWait
  1104  		rebootRetryWaitTimeout = oldRebootRetryWaitTimeout
  1105  	}()
  1106  	rebootRetryWaitTimeout = 100 * time.Millisecond
  1107  	rebootNoticeWait = 150 * time.Millisecond
  1108  
  1109  	cmd := testutil.MockCommand(c, "shutdown", "")
  1110  	defer cmd.Restore()
  1111  
  1112  	d := newTestDaemon(c)
  1113  	c.Check(d.overlord, check.IsNil)
  1114  	c.Check(d.expectedRebootDidNotHappen, check.Equals, true)
  1115  
  1116  	var n int
  1117  	d.state.Lock()
  1118  	err = d.state.Get("daemon-system-restart-tentative", &n)
  1119  	d.state.Unlock()
  1120  	c.Check(err, check.IsNil)
  1121  	c.Check(n, check.Equals, 1)
  1122  
  1123  	c.Assert(d.Start(), check.IsNil)
  1124  
  1125  	c.Check(s.notified, check.DeepEquals, []string{"READY=1"})
  1126  
  1127  	select {
  1128  	case <-d.Dying():
  1129  	case <-time.After(2 * time.Second):
  1130  		c.Fatal("expected reboot not happening should proceed to try to shutdown again")
  1131  	}
  1132  
  1133  	sigCh := make(chan os.Signal, 2)
  1134  	// stop (this will timeout but thats not relevant for this test)
  1135  	d.Stop(sigCh)
  1136  
  1137  	// an immediate shutdown was scheduled again
  1138  	c.Check(cmd.Calls(), check.DeepEquals, [][]string{
  1139  		{"shutdown", "-r", "+0", "reboot scheduled to update the system"},
  1140  	})
  1141  }
  1142  
  1143  func (s *daemonSuite) TestRestartExpectedRebootOK(c *check.C) {
  1144  	fakeState := []byte(fmt.Sprintf(`{"data":{"patch-level":%d,"patch-sublevel":%d,"some":"data","refresh-privacy-key":"0123456789ABCDEF","system-restart-from-boot-id":%q,"daemon-system-restart-at":"%s"},"changes":null,"tasks":null,"last-change-id":0,"last-task-id":0,"last-lane-id":0}`, patch.Level, patch.Sublevel, "boot-id-0", time.Now().UTC().Format(time.RFC3339)))
  1145  	err := ioutil.WriteFile(dirs.SnapStateFile, fakeState, 0600)
  1146  	c.Assert(err, check.IsNil)
  1147  
  1148  	cmd := testutil.MockCommand(c, "shutdown", "")
  1149  	defer cmd.Restore()
  1150  
  1151  	d := newTestDaemon(c)
  1152  	c.Assert(d.overlord, check.NotNil)
  1153  
  1154  	st := d.overlord.State()
  1155  	st.Lock()
  1156  	defer st.Unlock()
  1157  	var v interface{}
  1158  	// these were cleared
  1159  	c.Check(st.Get("daemon-system-restart-at", &v), check.Equals, state.ErrNoState)
  1160  	c.Check(st.Get("system-restart-from-boot-id", &v), check.Equals, state.ErrNoState)
  1161  }
  1162  
  1163  func (s *daemonSuite) TestRestartExpectedRebootGiveUp(c *check.C) {
  1164  	// we give up trying to restart the system after 3 retry tentatives
  1165  	curBootID, err := osutil.BootID()
  1166  	c.Assert(err, check.IsNil)
  1167  
  1168  	fakeState := []byte(fmt.Sprintf(`{"data":{"patch-level":%d,"patch-sublevel":%d,"some":"data","refresh-privacy-key":"0123456789ABCDEF","system-restart-from-boot-id":%q,"daemon-system-restart-at":"%s","daemon-system-restart-tentative":3},"changes":null,"tasks":null,"last-change-id":0,"last-task-id":0,"last-lane-id":0}`, patch.Level, patch.Sublevel, curBootID, time.Now().UTC().Format(time.RFC3339)))
  1169  	err = ioutil.WriteFile(dirs.SnapStateFile, fakeState, 0600)
  1170  	c.Assert(err, check.IsNil)
  1171  
  1172  	cmd := testutil.MockCommand(c, "shutdown", "")
  1173  	defer cmd.Restore()
  1174  
  1175  	d := newTestDaemon(c)
  1176  	c.Assert(d.overlord, check.NotNil)
  1177  
  1178  	st := d.overlord.State()
  1179  	st.Lock()
  1180  	defer st.Unlock()
  1181  	var v interface{}
  1182  	// these were cleared
  1183  	c.Check(st.Get("daemon-system-restart-at", &v), check.Equals, state.ErrNoState)
  1184  	c.Check(st.Get("system-restart-from-boot-id", &v), check.Equals, state.ErrNoState)
  1185  	c.Check(st.Get("daemon-system-restart-tentative", &v), check.Equals, state.ErrNoState)
  1186  }
  1187  
  1188  func (s *daemonSuite) TestRestartIntoSocketModeNoNewChanges(c *check.C) {
  1189  	restore := standby.MockStandbyWait(5 * time.Millisecond)
  1190  	defer restore()
  1191  
  1192  	d := newTestDaemon(c)
  1193  	makeDaemonListeners(c, d)
  1194  
  1195  	// mark as already seeded, we also have no snaps so this will
  1196  	// go into socket activation mode
  1197  	s.markSeeded(d)
  1198  
  1199  	c.Assert(d.Start(), check.IsNil)
  1200  	// pretend some ensure happened
  1201  	for i := 0; i < 5; i++ {
  1202  		c.Check(d.overlord.StateEngine().Ensure(), check.IsNil)
  1203  		time.Sleep(5 * time.Millisecond)
  1204  	}
  1205  
  1206  	select {
  1207  	case <-d.Dying():
  1208  		// exit the loop
  1209  	case <-time.After(15 * time.Second):
  1210  		c.Errorf("daemon did not stop after 15s")
  1211  	}
  1212  	err := d.Stop(nil)
  1213  	c.Check(err, check.Equals, ErrRestartSocket)
  1214  	c.Check(d.restartSocket, check.Equals, true)
  1215  }
  1216  
  1217  func (s *daemonSuite) TestRestartIntoSocketModePendingChanges(c *check.C) {
  1218  	restore := standby.MockStandbyWait(5 * time.Millisecond)
  1219  	defer restore()
  1220  
  1221  	d := newTestDaemon(c)
  1222  	makeDaemonListeners(c, d)
  1223  
  1224  	// mark as already seeded, we also have no snaps so this will
  1225  	// go into socket activation mode
  1226  	s.markSeeded(d)
  1227  	st := d.overlord.State()
  1228  
  1229  	c.Assert(d.Start(), check.IsNil)
  1230  	// pretend some ensure happened
  1231  	for i := 0; i < 5; i++ {
  1232  		c.Check(d.overlord.StateEngine().Ensure(), check.IsNil)
  1233  		time.Sleep(5 * time.Millisecond)
  1234  	}
  1235  
  1236  	select {
  1237  	case <-d.Dying():
  1238  		// Pretend we got change while shutting down, this can
  1239  		// happen when e.g. the user requested a `snap install
  1240  		// foo` at the same time as the code in the overlord
  1241  		// checked that it can go into socket activated
  1242  		// mode. I.e. the daemon was processing the request
  1243  		// but no change was generated at the time yet.
  1244  		st.Lock()
  1245  		chg := st.NewChange("fake-install", "fake install some snap")
  1246  		chg.AddTask(st.NewTask("fake-install-task", "fake install task"))
  1247  		chgStatus := chg.Status()
  1248  		st.Unlock()
  1249  		// ensure our change is valid and ready
  1250  		c.Check(chgStatus, check.Equals, state.DoStatus)
  1251  	case <-time.After(5 * time.Second):
  1252  		c.Errorf("daemon did not stop after 5s")
  1253  	}
  1254  	// when the daemon got a pending change it just restarts
  1255  	err := d.Stop(nil)
  1256  	c.Check(err, check.IsNil)
  1257  	c.Check(d.restartSocket, check.Equals, false)
  1258  }
  1259  
  1260  func (s *daemonSuite) TestConnTrackerCanShutdown(c *check.C) {
  1261  	ct := &connTracker{conns: make(map[net.Conn]struct{})}
  1262  	c.Check(ct.CanStandby(), check.Equals, true)
  1263  
  1264  	con := &net.IPConn{}
  1265  	ct.trackConn(con, http.StateActive)
  1266  	c.Check(ct.CanStandby(), check.Equals, false)
  1267  
  1268  	ct.trackConn(con, http.StateIdle)
  1269  	c.Check(ct.CanStandby(), check.Equals, true)
  1270  }
  1271  
  1272  func doTestReq(c *check.C, cmd *Command, mth string) *httptest.ResponseRecorder {
  1273  	req, err := http.NewRequest(mth, "", nil)
  1274  	c.Assert(err, check.IsNil)
  1275  	req.RemoteAddr = "pid=100;uid=0;socket=;"
  1276  	rec := httptest.NewRecorder()
  1277  	cmd.ServeHTTP(rec, req)
  1278  	return rec
  1279  }
  1280  
  1281  func (s *daemonSuite) TestDegradedModeReply(c *check.C) {
  1282  	d := newTestDaemon(c)
  1283  	cmd := &Command{d: d}
  1284  	cmd.GET = func(*Command, *http.Request, *auth.UserState) Response {
  1285  		return SyncResponse(nil, nil)
  1286  	}
  1287  	cmd.POST = func(*Command, *http.Request, *auth.UserState) Response {
  1288  		return SyncResponse(nil, nil)
  1289  	}
  1290  
  1291  	// pretend we are in degraded mode
  1292  	d.SetDegradedMode(fmt.Errorf("foo error"))
  1293  
  1294  	// GET is ok even in degraded mode
  1295  	rec := doTestReq(c, cmd, "GET")
  1296  	c.Check(rec.Code, check.Equals, 200)
  1297  	// POST is not allowed
  1298  	rec = doTestReq(c, cmd, "POST")
  1299  	c.Check(rec.Code, check.Equals, 500)
  1300  	// verify we get the error
  1301  	var v struct{ Result errorResult }
  1302  	c.Assert(json.NewDecoder(rec.Body).Decode(&v), check.IsNil)
  1303  	c.Check(v.Result.Message, check.Equals, "foo error")
  1304  
  1305  	// clean degraded mode
  1306  	d.SetDegradedMode(nil)
  1307  	rec = doTestReq(c, cmd, "POST")
  1308  	c.Check(rec.Code, check.Equals, 200)
  1309  }