github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/boot/cmdline_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 boot_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/boot"
    30  	"github.com/snapcore/snapd/boot/boottest"
    31  	"github.com/snapcore/snapd/bootloader"
    32  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    33  	"github.com/snapcore/snapd/osutil"
    34  	"github.com/snapcore/snapd/testutil"
    35  )
    36  
    37  var _ = Suite(&kernelCommandLineSuite{})
    38  
    39  // baseBootSuite is used to setup the common test environment
    40  type kernelCommandLineSuite struct {
    41  	testutil.BaseTest
    42  	rootDir string
    43  }
    44  
    45  func (s *kernelCommandLineSuite) SetUpTest(c *C) {
    46  	s.BaseTest.SetUpTest(c)
    47  	s.rootDir = c.MkDir()
    48  
    49  	err := os.MkdirAll(filepath.Join(s.rootDir, "proc"), 0755)
    50  	c.Assert(err, IsNil)
    51  	restore := osutil.MockProcCmdline(filepath.Join(s.rootDir, "proc/cmdline"))
    52  	s.AddCleanup(restore)
    53  }
    54  
    55  func (s *kernelCommandLineSuite) mockProcCmdlineContent(c *C, newContent string) {
    56  	mockProcCmdline := filepath.Join(s.rootDir, "proc/cmdline")
    57  	err := ioutil.WriteFile(mockProcCmdline, []byte(newContent), 0644)
    58  	c.Assert(err, IsNil)
    59  }
    60  
    61  func (s *kernelCommandLineSuite) TestModeAndLabel(c *C) {
    62  	for _, tc := range []struct {
    63  		cmd   string
    64  		mode  string
    65  		label string
    66  		err   string
    67  	}{{
    68  		cmd:   "snapd_recovery_mode= snapd_recovery_system=this-is-a-label other-option=foo",
    69  		mode:  boot.ModeInstall,
    70  		label: "this-is-a-label",
    71  	}, {
    72  		cmd:   "snapd_recovery_system=label foo=bar foobaz=\\0\\0123 snapd_recovery_mode=install",
    73  		label: "label",
    74  		mode:  boot.ModeInstall,
    75  	}, {
    76  		cmd:  "snapd_recovery_mode=run snapd_recovery_system=1234",
    77  		mode: boot.ModeRun,
    78  	}, {
    79  		cmd: "option=1 other-option=\0123 none",
    80  		err: "cannot detect mode nor recovery system to use",
    81  	}, {
    82  		cmd: "snapd_recovery_mode=install-foo",
    83  		err: `cannot use unknown mode "install-foo"`,
    84  	}, {
    85  		// no recovery system label
    86  		cmd: "snapd_recovery_mode=install foo=bar",
    87  		err: `cannot specify install mode without system label`,
    88  	}, {
    89  		cmd: "snapd_recovery_system=1234",
    90  		err: `cannot specify system label without a mode`,
    91  	}, {
    92  		// multiple kernel command line params end up using the last one - this
    93  		// effectively matches the kernel handling too
    94  		cmd:  "snapd_recovery_mode=install snapd_recovery_system=1234 snapd_recovery_mode=run",
    95  		mode: "run",
    96  		// label gets unset because it's not used for run mode
    97  		label: "",
    98  	}, {
    99  		cmd:   "snapd_recovery_system=not-this-one snapd_recovery_mode=install snapd_recovery_system=1234",
   100  		mode:  "install",
   101  		label: "1234",
   102  	}} {
   103  		c.Logf("tc: %q", tc)
   104  		s.mockProcCmdlineContent(c, tc.cmd)
   105  
   106  		mode, label, err := boot.ModeAndRecoverySystemFromKernelCommandLine()
   107  		if tc.err == "" {
   108  			c.Assert(err, IsNil)
   109  			c.Check(mode, Equals, tc.mode)
   110  			c.Check(label, Equals, tc.label)
   111  		} else {
   112  			c.Assert(err, ErrorMatches, tc.err)
   113  		}
   114  	}
   115  }
   116  
   117  func (s *kernelCommandLineSuite) TestComposeCommandLineNotManagedHappy(c *C) {
   118  	model := boottest.MakeMockUC20Model()
   119  
   120  	bl := bootloadertest.Mock("btloader", c.MkDir())
   121  	bootloader.Force(bl)
   122  	defer bootloader.Force(nil)
   123  
   124  	cmdline, err := boot.ComposeRecoveryCommandLine(model, "20200314")
   125  	c.Assert(err, IsNil)
   126  	c.Assert(cmdline, Equals, "")
   127  
   128  	cmdline, err = boot.ComposeCommandLine(model)
   129  	c.Assert(err, IsNil)
   130  	c.Assert(cmdline, Equals, "")
   131  
   132  	tbl := bl.WithTrustedAssets()
   133  	bootloader.Force(tbl)
   134  
   135  	cmdline, err = boot.ComposeRecoveryCommandLine(model, "20200314")
   136  	c.Assert(err, IsNil)
   137  	c.Assert(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=20200314")
   138  
   139  	cmdline, err = boot.ComposeCommandLine(model)
   140  	c.Assert(err, IsNil)
   141  	c.Assert(cmdline, Equals, "snapd_recovery_mode=run")
   142  }
   143  
   144  func (s *kernelCommandLineSuite) TestComposeCommandLineNotUC20(c *C) {
   145  	model := boottest.MakeMockModel()
   146  
   147  	bl := bootloadertest.Mock("btloader", c.MkDir())
   148  	bootloader.Force(bl)
   149  	defer bootloader.Force(nil)
   150  	cmdline, err := boot.ComposeRecoveryCommandLine(model, "20200314")
   151  	c.Assert(err, IsNil)
   152  	c.Check(cmdline, Equals, "")
   153  
   154  	cmdline, err = boot.ComposeCommandLine(model)
   155  	c.Assert(err, IsNil)
   156  	c.Check(cmdline, Equals, "")
   157  }
   158  
   159  func (s *kernelCommandLineSuite) TestComposeCommandLineManagedHappy(c *C) {
   160  	model := boottest.MakeMockUC20Model()
   161  
   162  	tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets()
   163  	bootloader.Force(tbl)
   164  	defer bootloader.Force(nil)
   165  
   166  	tbl.StaticCommandLine = "panic=-1"
   167  
   168  	cmdline, err := boot.ComposeRecoveryCommandLine(model, "20200314")
   169  	c.Assert(err, IsNil)
   170  	c.Assert(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=20200314 panic=-1")
   171  	cmdline, err = boot.ComposeCommandLine(model)
   172  	c.Assert(err, IsNil)
   173  	c.Assert(cmdline, Equals, "snapd_recovery_mode=run panic=-1")
   174  
   175  	cmdline, err = boot.ComposeRecoveryCommandLine(model, "20200314")
   176  	c.Assert(err, IsNil)
   177  	c.Assert(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=20200314 panic=-1")
   178  	cmdline, err = boot.ComposeCommandLine(model)
   179  	c.Assert(err, IsNil)
   180  	c.Assert(cmdline, Equals, "snapd_recovery_mode=run panic=-1")
   181  }
   182  
   183  func (s *kernelCommandLineSuite) TestComposeCandidateCommandLineManagedHappy(c *C) {
   184  	model := boottest.MakeMockUC20Model()
   185  
   186  	tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets()
   187  	bootloader.Force(tbl)
   188  	defer bootloader.Force(nil)
   189  
   190  	tbl.StaticCommandLine = "panic=-1"
   191  	tbl.CandidateStaticCommandLine = "candidate panic=0"
   192  
   193  	cmdline, err := boot.ComposeCandidateCommandLine(model)
   194  	c.Assert(err, IsNil)
   195  	c.Assert(cmdline, Equals, "snapd_recovery_mode=run candidate panic=0")
   196  }
   197  
   198  func (s *kernelCommandLineSuite) TestComposeCandidateRecoveryCommandLineManagedHappy(c *C) {
   199  	model := boottest.MakeMockUC20Model()
   200  
   201  	tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets()
   202  	bootloader.Force(tbl)
   203  	defer bootloader.Force(nil)
   204  
   205  	tbl.StaticCommandLine = "panic=-1"
   206  	tbl.CandidateStaticCommandLine = "candidate panic=0"
   207  
   208  	cmdline, err := boot.ComposeCandidateRecoveryCommandLine(model, "1234")
   209  	c.Assert(err, IsNil)
   210  	c.Check(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=1234 candidate panic=0")
   211  
   212  	cmdline, err = boot.ComposeCandidateRecoveryCommandLine(model, "")
   213  	c.Assert(err, ErrorMatches, "internal error: system is unset")
   214  	c.Check(cmdline, Equals, "")
   215  }