gitee.com/mysnapcore/mysnapd@v0.1.0/boot/cmdline_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 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 "gitee.com/mysnapcore/mysnapd/boot" 30 "gitee.com/mysnapcore/mysnapd/boot/boottest" 31 "gitee.com/mysnapcore/mysnapd/bootloader" 32 "gitee.com/mysnapcore/mysnapd/bootloader/bootloadertest" 33 "gitee.com/mysnapcore/mysnapd/osutil" 34 "gitee.com/mysnapcore/mysnapd/snap/snaptest" 35 "gitee.com/mysnapcore/mysnapd/testutil" 36 ) 37 38 var _ = Suite(&kernelCommandLineSuite{}) 39 40 // baseBootSuite is used to setup the common test environment 41 type kernelCommandLineSuite struct { 42 testutil.BaseTest 43 rootDir string 44 } 45 46 func (s *kernelCommandLineSuite) SetUpTest(c *C) { 47 s.BaseTest.SetUpTest(c) 48 s.rootDir = c.MkDir() 49 50 err := os.MkdirAll(filepath.Join(s.rootDir, "proc"), 0755) 51 c.Assert(err, IsNil) 52 restore := osutil.MockProcCmdline(filepath.Join(s.rootDir, "proc/cmdline")) 53 s.AddCleanup(restore) 54 } 55 56 func (s *kernelCommandLineSuite) mockProcCmdlineContent(c *C, newContent string) { 57 mockProcCmdline := filepath.Join(s.rootDir, "proc/cmdline") 58 err := ioutil.WriteFile(mockProcCmdline, []byte(newContent), 0644) 59 c.Assert(err, IsNil) 60 } 61 62 func (s *kernelCommandLineSuite) TestModeAndLabel(c *C) { 63 for _, tc := range []struct { 64 cmd string 65 mode string 66 label string 67 err string 68 }{{ 69 cmd: "snapd_recovery_mode= snapd_recovery_system=this-is-a-label other-option=foo", 70 mode: boot.ModeInstall, 71 label: "this-is-a-label", 72 }, { 73 cmd: "snapd_recovery_system=label foo=bar foobaz=\\0\\0123 snapd_recovery_mode=install", 74 label: "label", 75 mode: boot.ModeInstall, 76 }, { 77 cmd: "snapd_recovery_mode=run snapd_recovery_system=1234", 78 mode: boot.ModeRun, 79 }, { 80 cmd: "snapd_recovery_mode=recover snapd_recovery_system=1234", 81 label: "1234", 82 mode: boot.ModeRecover, 83 }, { 84 cmd: "snapd_recovery_mode=factory-reset snapd_recovery_system=1234", 85 label: "1234", 86 mode: boot.ModeFactoryReset, 87 }, { 88 cmd: "option=1 other-option=\0123 none", 89 err: "cannot detect mode nor recovery system to use", 90 }, { 91 cmd: "snapd_recovery_mode=install-foo", 92 err: `cannot use unknown mode "install-foo"`, 93 }, { 94 // no recovery system label 95 cmd: "snapd_recovery_mode=install foo=bar", 96 err: `cannot specify install mode without system label`, 97 }, { 98 cmd: "snapd_recovery_system=1234", 99 err: `cannot specify system label without a mode`, 100 }, { 101 // multiple kernel command line params end up using the last one - this 102 // effectively matches the kernel handling too 103 cmd: "snapd_recovery_mode=install snapd_recovery_system=1234 snapd_recovery_mode=run", 104 mode: "run", 105 // label gets unset because it's not used for run mode 106 label: "", 107 }, { 108 cmd: "snapd_recovery_system=not-this-one snapd_recovery_mode=install snapd_recovery_system=1234", 109 mode: "install", 110 label: "1234", 111 }, { 112 cmd: "snapd_recovery_mode=cloudimg-rootfs", 113 mode: boot.ModeRunCVM, 114 }} { 115 c.Logf("tc: %q", tc) 116 s.mockProcCmdlineContent(c, tc.cmd) 117 118 mode, label, err := boot.ModeAndRecoverySystemFromKernelCommandLine() 119 if tc.err == "" { 120 c.Assert(err, IsNil) 121 c.Check(mode, Equals, tc.mode) 122 c.Check(label, Equals, tc.label) 123 } else { 124 c.Assert(err, ErrorMatches, tc.err) 125 } 126 } 127 } 128 129 func (s *kernelCommandLineSuite) TestComposeCommandLineNotManagedHappy(c *C) { 130 model := boottest.MakeMockUC20Model() 131 132 bl := bootloadertest.Mock("btloader", c.MkDir()) 133 bootloader.Force(bl) 134 defer bootloader.Force(nil) 135 136 cmdline, err := boot.ComposeRecoveryCommandLine(model, "20200314", "") 137 c.Assert(err, IsNil) 138 c.Assert(cmdline, Equals, "") 139 140 cmdline, err = boot.ComposeCommandLine(model, "") 141 c.Assert(err, IsNil) 142 c.Assert(cmdline, Equals, "") 143 144 tbl := bl.WithTrustedAssets() 145 bootloader.Force(tbl) 146 147 cmdline, err = boot.ComposeRecoveryCommandLine(model, "20200314", "") 148 c.Assert(err, IsNil) 149 c.Assert(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=20200314") 150 151 cmdline, err = boot.ComposeCommandLine(model, "") 152 c.Assert(err, IsNil) 153 c.Assert(cmdline, Equals, "snapd_recovery_mode=run") 154 } 155 156 func (s *kernelCommandLineSuite) TestComposeCommandLineNotUC20(c *C) { 157 model := boottest.MakeMockModel() 158 159 bl := bootloadertest.Mock("btloader", c.MkDir()) 160 bootloader.Force(bl) 161 defer bootloader.Force(nil) 162 cmdline, err := boot.ComposeRecoveryCommandLine(model, "20200314", "") 163 c.Assert(err, IsNil) 164 c.Check(cmdline, Equals, "") 165 166 cmdline, err = boot.ComposeCommandLine(model, "") 167 c.Assert(err, IsNil) 168 c.Check(cmdline, Equals, "") 169 } 170 171 func (s *kernelCommandLineSuite) TestComposeCommandLineManagedHappy(c *C) { 172 model := boottest.MakeMockUC20Model() 173 174 tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets() 175 bootloader.Force(tbl) 176 defer bootloader.Force(nil) 177 178 tbl.StaticCommandLine = "panic=-1" 179 180 cmdline, err := boot.ComposeRecoveryCommandLine(model, "20200314", "") 181 c.Assert(err, IsNil) 182 c.Assert(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=20200314 panic=-1") 183 cmdline, err = boot.ComposeCommandLine(model, "") 184 c.Assert(err, IsNil) 185 c.Assert(cmdline, Equals, "snapd_recovery_mode=run panic=-1") 186 187 cmdline, err = boot.ComposeRecoveryCommandLine(model, "20200314", "") 188 c.Assert(err, IsNil) 189 c.Assert(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=20200314 panic=-1") 190 cmdline, err = boot.ComposeCommandLine(model, "") 191 c.Assert(err, IsNil) 192 c.Assert(cmdline, Equals, "snapd_recovery_mode=run panic=-1") 193 } 194 195 func (s *kernelCommandLineSuite) TestComposeCandidateCommandLineManagedHappy(c *C) { 196 model := boottest.MakeMockUC20Model() 197 198 tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets() 199 bootloader.Force(tbl) 200 defer bootloader.Force(nil) 201 202 tbl.StaticCommandLine = "panic=-1" 203 tbl.CandidateStaticCommandLine = "candidate panic=0" 204 205 cmdline, err := boot.ComposeCandidateCommandLine(model, "") 206 c.Assert(err, IsNil) 207 c.Assert(cmdline, Equals, "snapd_recovery_mode=run candidate panic=0") 208 } 209 210 func (s *kernelCommandLineSuite) TestComposeCandidateRecoveryCommandLineManagedHappy(c *C) { 211 model := boottest.MakeMockUC20Model() 212 213 tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets() 214 bootloader.Force(tbl) 215 defer bootloader.Force(nil) 216 217 tbl.StaticCommandLine = "panic=-1" 218 tbl.CandidateStaticCommandLine = "candidate panic=0" 219 220 cmdline, err := boot.ComposeCandidateRecoveryCommandLine(model, "1234", "") 221 c.Assert(err, IsNil) 222 c.Check(cmdline, Equals, "snapd_recovery_mode=recover snapd_recovery_system=1234 candidate panic=0") 223 224 cmdline, err = boot.ComposeCandidateRecoveryCommandLine(model, "", "") 225 c.Assert(err, ErrorMatches, "internal error: system is unset") 226 c.Check(cmdline, Equals, "") 227 } 228 229 const gadgetSnapYaml = `name: gadget 230 version: 1.0 231 type: gadget 232 ` 233 234 func (s *kernelCommandLineSuite) TestComposeCommandLineWithGadget(c *C) { 235 model := boottest.MakeMockUC20Model() 236 237 tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets() 238 bootloader.Force(tbl) 239 defer bootloader.Force(nil) 240 241 tbl.StaticCommandLine = "panic=-1" 242 tbl.CandidateStaticCommandLine = "candidate panic=0" 243 244 for _, tc := range []struct { 245 which string 246 files [][]string 247 expCommandLine string 248 errMsg string 249 }{{ 250 which: "current", 251 files: [][]string{ 252 {"cmdline.extra", "cmdline extra"}, 253 }, 254 expCommandLine: "snapd_recovery_mode=run panic=-1 cmdline extra", 255 }, { 256 which: "candidate", 257 files: [][]string{ 258 {"cmdline.extra", "cmdline extra"}, 259 }, 260 expCommandLine: "snapd_recovery_mode=run candidate panic=0 cmdline extra", 261 }, { 262 which: "current", 263 files: [][]string{ 264 {"cmdline.full", "cmdline full"}, 265 }, 266 expCommandLine: "snapd_recovery_mode=run cmdline full", 267 }, { 268 which: "candidate", 269 files: [][]string{ 270 {"cmdline.full", "cmdline full"}, 271 }, 272 expCommandLine: "snapd_recovery_mode=run cmdline full", 273 }, { 274 which: "candidate", 275 files: [][]string{ 276 {"cmdline.extra", `bad-quote="`}, 277 }, 278 errMsg: `cannot use kernel command line from gadget: invalid kernel command line in cmdline.extra: unbalanced quoting`, 279 }} { 280 sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, append([][]string{ 281 {"meta/snap.yaml", gadgetSnapYaml}, 282 }, tc.files...)) 283 var cmdline string 284 var err error 285 switch tc.which { 286 case "current": 287 cmdline, err = boot.ComposeCommandLine(model, sf) 288 case "candidate": 289 cmdline, err = boot.ComposeCandidateCommandLine(model, sf) 290 default: 291 c.Fatalf("unexpected command line type") 292 } 293 if tc.errMsg == "" { 294 c.Assert(err, IsNil) 295 c.Assert(cmdline, Equals, tc.expCommandLine) 296 } else { 297 c.Assert(err, ErrorMatches, tc.errMsg) 298 } 299 } 300 } 301 302 func (s *kernelCommandLineSuite) TestComposeRecoveryCommandLineWithGadget(c *C) { 303 model := boottest.MakeMockUC20Model() 304 305 tbl := bootloadertest.Mock("btloader", c.MkDir()).WithTrustedAssets() 306 bootloader.Force(tbl) 307 defer bootloader.Force(nil) 308 309 tbl.StaticCommandLine = "panic=-1" 310 tbl.CandidateStaticCommandLine = "candidate panic=0" 311 system := "1234" 312 313 for _, tc := range []struct { 314 which string 315 files [][]string 316 expCommandLine string 317 errMsg string 318 }{{ 319 which: "current", 320 files: [][]string{ 321 {"cmdline.extra", "cmdline extra"}, 322 }, 323 expCommandLine: "snapd_recovery_mode=recover snapd_recovery_system=1234 panic=-1 cmdline extra", 324 }, { 325 which: "candidate", 326 files: [][]string{ 327 {"cmdline.extra", "cmdline extra"}, 328 }, 329 expCommandLine: "snapd_recovery_mode=recover snapd_recovery_system=1234 candidate panic=0 cmdline extra", 330 }, { 331 which: "current", 332 files: [][]string{ 333 {"cmdline.full", "cmdline full"}, 334 }, 335 expCommandLine: "snapd_recovery_mode=recover snapd_recovery_system=1234 cmdline full", 336 }, { 337 which: "candidate", 338 files: [][]string{ 339 {"cmdline.full", "cmdline full"}, 340 }, 341 expCommandLine: "snapd_recovery_mode=recover snapd_recovery_system=1234 cmdline full", 342 }, { 343 which: "candidate", 344 files: [][]string{ 345 {"cmdline.extra", `bad-quote="`}, 346 }, 347 errMsg: `cannot use kernel command line from gadget: invalid kernel command line in cmdline.extra: unbalanced quoting`, 348 }} { 349 sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, append([][]string{ 350 {"meta/snap.yaml", gadgetSnapYaml}, 351 }, tc.files...)) 352 var cmdline string 353 var err error 354 switch tc.which { 355 case "current": 356 cmdline, err = boot.ComposeRecoveryCommandLine(model, system, sf) 357 case "candidate": 358 cmdline, err = boot.ComposeCandidateRecoveryCommandLine(model, system, sf) 359 default: 360 c.Fatalf("unexpected command line type") 361 } 362 if tc.errMsg == "" { 363 c.Assert(err, IsNil) 364 c.Assert(cmdline, Equals, tc.expCommandLine) 365 } else { 366 c.Assert(err, ErrorMatches, tc.errMsg) 367 } 368 } 369 } 370 371 func (s *kernelCommandLineSuite) TestBootVarsForGadgetCommandLine(c *C) { 372 for _, tc := range []struct { 373 errMsg string 374 files [][]string 375 expectedVars map[string]string 376 }{{ 377 files: [][]string{ 378 {"cmdline.extra", "foo bar baz"}, 379 }, 380 expectedVars: map[string]string{ 381 "snapd_extra_cmdline_args": "foo bar baz", 382 "snapd_full_cmdline_args": "", 383 }, 384 }, { 385 files: [][]string{ 386 {"cmdline.extra", "snapd.debug=1"}, 387 }, 388 expectedVars: map[string]string{ 389 "snapd_extra_cmdline_args": "snapd.debug=1", 390 "snapd_full_cmdline_args": "", 391 }, 392 }, { 393 files: [][]string{ 394 {"cmdline.extra", "snapd_foo"}, 395 }, 396 errMsg: `cannot use kernel command line from gadget: invalid kernel command line in cmdline.extra: disallowed kernel argument \"snapd_foo\"`, 397 }, { 398 files: [][]string{ 399 {"cmdline.full", "full foo bar baz"}, 400 }, 401 expectedVars: map[string]string{ 402 "snapd_extra_cmdline_args": "", 403 "snapd_full_cmdline_args": "full foo bar baz", 404 }, 405 }, { 406 // with no arguments boot variables should be cleared 407 files: [][]string{}, 408 expectedVars: map[string]string{ 409 "snapd_extra_cmdline_args": "", 410 "snapd_full_cmdline_args": "", 411 }, 412 }} { 413 sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, append([][]string{ 414 {"meta/snap.yaml", gadgetSnapYaml}, 415 }, tc.files...)) 416 vars, err := boot.BootVarsForTrustedCommandLineFromGadget(sf) 417 if tc.errMsg == "" { 418 c.Assert(err, IsNil) 419 c.Assert(vars, DeepEquals, tc.expectedVars) 420 } else { 421 c.Assert(err, ErrorMatches, tc.errMsg) 422 } 423 } 424 }