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