github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/udev/backend_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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 udev_test 21 22 import ( 23 "bytes" 24 "os" 25 "path/filepath" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/dirs" 30 "github.com/snapcore/snapd/interfaces" 31 "github.com/snapcore/snapd/interfaces/ifacetest" 32 "github.com/snapcore/snapd/interfaces/udev" 33 "github.com/snapcore/snapd/sandbox/cgroup" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/testutil" 36 "github.com/snapcore/snapd/timings" 37 ) 38 39 type backendSuite struct { 40 ifacetest.BackendSuite 41 42 udevadmCmd *testutil.MockCmd 43 meas *timings.Span 44 } 45 46 var _ = Suite(&backendSuite{}) 47 48 var testedConfinementOpts = []interfaces.ConfinementOptions{ 49 {}, 50 {DevMode: true}, 51 {JailMode: true}, 52 {Classic: true}, 53 } 54 55 func createSnippetForApps(apps map[string]*snap.AppInfo) string { 56 var buffer bytes.Buffer 57 for appName := range apps { 58 buffer.WriteString(appName) 59 } 60 return buffer.String() 61 } 62 63 func (s *backendSuite) SetUpTest(c *C) { 64 s.Backend = &udev.Backend{} 65 66 s.BackendSuite.SetUpTest(c) 67 c.Assert(s.Repo.AddBackend(s.Backend), IsNil) 68 69 // Mock away any real udev interaction 70 s.udevadmCmd = testutil.MockCommand(c, "udevadm", "") 71 // Prepare a directory for udev rules 72 // NOTE: Normally this is a part of the OS snap. 73 err := os.MkdirAll(dirs.SnapUdevRulesDir, 0700) 74 c.Assert(err, IsNil) 75 76 perf := timings.New(nil) 77 s.meas = perf.StartSpan("", "") 78 } 79 80 func (s *backendSuite) TearDownTest(c *C) { 81 s.udevadmCmd.Restore() 82 83 s.BackendSuite.TearDownTest(c) 84 } 85 86 // Tests for Setup() and Remove() 87 func (s *backendSuite) TestName(c *C) { 88 c.Check(s.Backend.Name(), Equals, interfaces.SecurityUDev) 89 } 90 91 func (s *backendSuite) TestInstallingSnapWritesAndLoadsRules(c *C) { 92 // NOTE: Hand out a permanent snippet so that .rules file is generated. 93 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 94 spec.AddSnippet("dummy") 95 return nil 96 } 97 for _, opts := range testedConfinementOpts { 98 s.udevadmCmd.ForgetCalls() 99 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 100 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 101 // file called "70-snap.sambda.rules" was created 102 _, err := os.Stat(fname) 103 c.Check(err, IsNil) 104 // udevadm was used to reload rules and re-run triggers 105 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 106 {"udevadm", "control", "--reload-rules"}, 107 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 108 // FIXME: temporary until spec.TriggerSubsystem() can 109 // be called during disconnect 110 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 111 {"udevadm", "settle", "--timeout=10"}, 112 }) 113 s.RemoveSnap(c, snapInfo) 114 } 115 } 116 117 func (s *backendSuite) TestInstallingSnapWithHookWritesAndLoadsRules(c *C) { 118 // NOTE: Hand out a permanent snippet so that .rules file is generated. 119 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 120 spec.AddSnippet("dummy") 121 return nil 122 } 123 s.Iface.UDevPermanentPlugCallback = func(spec *udev.Specification, slot *snap.PlugInfo) error { 124 spec.AddSnippet("dummy") 125 return nil 126 } 127 for _, opts := range testedConfinementOpts { 128 s.udevadmCmd.ForgetCalls() 129 snapInfo := s.InstallSnap(c, opts, "", ifacetest.HookYaml, 0) 130 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.foo.rules") 131 132 // Verify that "70-snap.foo.rules" was created. 133 _, err := os.Stat(fname) 134 c.Check(err, IsNil) 135 136 // Verify that udevadm was used to reload rules and re-run triggers. 137 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 138 {"udevadm", "control", "--reload-rules"}, 139 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 140 // FIXME: temporary until spec.TriggerSubsystem() can 141 // be called during disconnect 142 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 143 {"udevadm", "settle", "--timeout=10"}, 144 }) 145 s.RemoveSnap(c, snapInfo) 146 } 147 } 148 149 func (s *backendSuite) TestSecurityIsStable(c *C) { 150 // NOTE: Hand out a permanent snippet so that .rules file is generated. 151 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 152 spec.AddSnippet("dummy") 153 return nil 154 } 155 for _, opts := range testedConfinementOpts { 156 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 157 s.udevadmCmd.ForgetCalls() 158 err := s.Backend.Setup(snapInfo, opts, s.Repo, s.meas) 159 c.Assert(err, IsNil) 160 // rules are not re-loaded when nothing changes 161 c.Check(s.udevadmCmd.Calls(), HasLen, 0) 162 s.RemoveSnap(c, snapInfo) 163 } 164 } 165 166 func (s *backendSuite) TestRemovingSnapRemovesAndReloadsRules(c *C) { 167 // NOTE: Hand out a permanent snippet so that .rules file is generated. 168 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 169 spec.AddSnippet("dummy") 170 return nil 171 } 172 for _, opts := range testedConfinementOpts { 173 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 174 s.udevadmCmd.ForgetCalls() 175 s.RemoveSnap(c, snapInfo) 176 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 177 // file called "70-snap.sambda.rules" was removed 178 _, err := os.Stat(fname) 179 c.Check(os.IsNotExist(err), Equals, true) 180 // udevadm was used to reload rules and re-run triggers 181 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 182 {"udevadm", "control", "--reload-rules"}, 183 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 184 // FIXME: temporary until spec.TriggerSubsystem() can 185 // be called during disconnect 186 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 187 {"udevadm", "settle", "--timeout=10"}, 188 }) 189 } 190 } 191 192 func (s *backendSuite) TestUpdatingSnapToOneWithMoreApps(c *C) { 193 // NOTE: Hand out a permanent snippet so that .rules file is generated. 194 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 195 spec.AddSnippet(createSnippetForApps(slot.Apps)) 196 return nil 197 } 198 for _, opts := range testedConfinementOpts { 199 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 200 s.udevadmCmd.ForgetCalls() 201 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1WithNmbd, 0) 202 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 203 // file called "70-snap.sambda.rules" was created 204 _, err := os.Stat(fname) 205 c.Check(err, IsNil) 206 // udevadm was used to reload rules and re-run triggers 207 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 208 {"udevadm", "control", "--reload-rules"}, 209 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 210 // FIXME: temporary until spec.TriggerSubsystem() can 211 // be called during disconnect 212 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 213 {"udevadm", "settle", "--timeout=10"}, 214 }) 215 s.RemoveSnap(c, snapInfo) 216 } 217 } 218 219 func (s *backendSuite) TestUpdatingSnapToOneWithMoreHooks(c *C) { 220 // NOTE: Hand out a permanent snippet so that .rules file is generated. 221 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 222 spec.AddSnippet(createSnippetForApps(slot.Apps)) 223 return nil 224 } 225 s.Iface.UDevPermanentPlugCallback = func(spec *udev.Specification, slot *snap.PlugInfo) error { 226 spec.AddSnippet("dummy") 227 return nil 228 } 229 for _, opts := range testedConfinementOpts { 230 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 231 s.udevadmCmd.ForgetCalls() 232 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlWithHook, 0) 233 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 234 235 // Verify that "70-snap.samba.rules" was created 236 _, err := os.Stat(fname) 237 c.Check(err, IsNil) 238 239 // Verify that udevadm was used to reload rules and re-run triggers 240 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 241 {"udevadm", "control", "--reload-rules"}, 242 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 243 // FIXME: temporary until spec.TriggerSubsystem() can 244 // be called during disconnect 245 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 246 {"udevadm", "settle", "--timeout=10"}, 247 }) 248 s.RemoveSnap(c, snapInfo) 249 } 250 } 251 252 func (s *backendSuite) TestUpdatingSnapToOneWithFewerApps(c *C) { 253 // NOTE: Hand out a permanent snippet so that .rules file is generated. 254 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 255 spec.AddSnippet(createSnippetForApps(slot.Apps)) 256 return nil 257 } 258 for _, opts := range testedConfinementOpts { 259 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1WithNmbd, 0) 260 s.udevadmCmd.ForgetCalls() 261 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0) 262 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 263 // file called "70-snap.sambda.rules" still exists 264 _, err := os.Stat(fname) 265 c.Check(err, IsNil) 266 // udevadm was used to reload rules and re-run triggers 267 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 268 {"udevadm", "control", "--reload-rules"}, 269 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 270 // FIXME: temporary until spec.TriggerSubsystem() can 271 // be called during disconnect 272 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 273 {"udevadm", "settle", "--timeout=10"}, 274 }) 275 s.RemoveSnap(c, snapInfo) 276 } 277 } 278 279 func (s *backendSuite) TestUpdatingSnapToOneWithFewerHooks(c *C) { 280 // NOTE: Hand out a permanent snippet so that .rules file is generated. 281 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 282 spec.AddSnippet(createSnippetForApps(slot.Apps)) 283 return nil 284 } 285 s.Iface.UDevPermanentPlugCallback = func(spec *udev.Specification, slot *snap.PlugInfo) error { 286 spec.AddSnippet("dummy") 287 return nil 288 } 289 for _, opts := range testedConfinementOpts { 290 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlWithHook, 0) 291 s.udevadmCmd.ForgetCalls() 292 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0) 293 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 294 // file called "70-snap.sambda.rules" still exists 295 _, err := os.Stat(fname) 296 c.Check(err, IsNil) 297 // Verify that udevadm was used to reload rules and re-run triggers 298 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 299 {"udevadm", "control", "--reload-rules"}, 300 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 301 // FIXME: temporary until spec.TriggerSubsystem() can 302 // be called during disconnect 303 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 304 {"udevadm", "settle", "--timeout=10"}, 305 }) 306 s.RemoveSnap(c, snapInfo) 307 } 308 } 309 310 func (s *backendSuite) TestCombineSnippetsWithActualSnippets(c *C) { 311 // NOTE: Hand out a permanent snippet so that .rules file is generated. 312 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 313 spec.AddSnippet("dummy") 314 return nil 315 } 316 for _, opts := range testedConfinementOpts { 317 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 318 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 319 if opts.DevMode || opts.Classic { 320 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy\n") 321 } else { 322 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\ndummy\n") 323 } 324 stat, err := os.Stat(fname) 325 c.Assert(err, IsNil) 326 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 327 s.RemoveSnap(c, snapInfo) 328 } 329 } 330 331 func (s *backendSuite) TestControlsDeviceCgroup(c *C) { 332 // NOTE: Hand out a permanent snippet so that .rules file is generated. 333 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 334 spec.AddSnippet("dummy") 335 spec.SetControlsDeviceCgroup() 336 return nil 337 } 338 for _, opts := range testedConfinementOpts { 339 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 340 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 341 c.Check(fname, testutil.FileAbsent) 342 s.RemoveSnap(c, snapInfo) 343 } 344 } 345 346 func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWithNewline(c *C) { 347 // NOTE: Hand out a permanent snippet so that .rules file is generated. 348 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 349 spec.AddSnippet("dummy1\ndummy2") 350 return nil 351 } 352 for _, opts := range testedConfinementOpts { 353 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 354 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 355 if opts.DevMode || opts.Classic { 356 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy1\n#dummy2\n") 357 } else { 358 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\ndummy1\ndummy2\n") 359 } 360 stat, err := os.Stat(fname) 361 c.Assert(err, IsNil) 362 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 363 s.RemoveSnap(c, snapInfo) 364 } 365 } 366 func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWhenPlugNoApps(c *C) { 367 // NOTE: Hand out a permanent snippet so that .rules file is generated. 368 s.Iface.UDevPermanentPlugCallback = func(spec *udev.Specification, slot *snap.PlugInfo) error { 369 spec.AddSnippet("dummy") 370 return nil 371 } 372 for _, opts := range testedConfinementOpts { 373 snapInfo := s.InstallSnap(c, opts, "", ifacetest.PlugNoAppsYaml, 0) 374 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.foo.rules") 375 if opts.DevMode || opts.Classic { 376 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy\n") 377 } else { 378 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\ndummy\n") 379 } 380 stat, err := os.Stat(fname) 381 c.Assert(err, IsNil) 382 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 383 s.RemoveSnap(c, snapInfo) 384 } 385 } 386 387 func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWhenSlotNoApps(c *C) { 388 // NOTE: Hand out a permanent snippet so that .rules file is generated. 389 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 390 spec.AddSnippet("dummy") 391 return nil 392 } 393 for _, opts := range testedConfinementOpts { 394 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SlotNoAppsYaml, 0) 395 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.foo.rules") 396 if opts.DevMode || opts.Classic { 397 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy\n") 398 } else { 399 c.Check(fname, testutil.FileEquals, "# This file is automatically generated.\ndummy\n") 400 } 401 stat, err := os.Stat(fname) 402 c.Assert(err, IsNil) 403 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 404 s.RemoveSnap(c, snapInfo) 405 } 406 } 407 408 func (s *backendSuite) TestCombineSnippetsWithoutAnySnippets(c *C) { 409 for _, opts := range testedConfinementOpts { 410 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 411 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 412 _, err := os.Stat(fname) 413 // Without any snippets, there the .rules file is not created. 414 c.Check(os.IsNotExist(err), Equals, true) 415 s.RemoveSnap(c, snapInfo) 416 } 417 } 418 419 func (s *backendSuite) TestUpdatingSnapToOneWithoutSlots(c *C) { 420 // NOTE: Hand out a permanent snippet so that .rules file is generated. 421 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 422 spec.AddSnippet("dummy") 423 return nil 424 } 425 for _, opts := range testedConfinementOpts { 426 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 427 s.udevadmCmd.ForgetCalls() 428 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1NoSlot, 0) 429 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 430 // file called "70-snap.sambda.rules" was removed 431 _, err := os.Stat(fname) 432 c.Check(os.IsNotExist(err), Equals, true) 433 // Verify that udevadm was used to reload rules and re-run triggers 434 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 435 {"udevadm", "control", "--reload-rules"}, 436 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 437 // FIXME: temporary until spec.TriggerSubsystem() can 438 // be called during disconnect 439 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 440 {"udevadm", "settle", "--timeout=10"}, 441 }) 442 s.RemoveSnap(c, snapInfo) 443 } 444 } 445 446 func (s *backendSuite) TestUpdatingSnapWithoutSlotsToOneWithoutSlots(c *C) { 447 // NOTE: Hand out a permanent snippet so that .rules file is generated. 448 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 449 spec.AddSnippet("dummy") 450 return nil 451 } 452 for _, opts := range testedConfinementOpts { 453 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1NoSlot, 0) 454 // file called "70-snap.sambda.rules" does not exist 455 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 456 _, err := os.Stat(fname) 457 c.Check(os.IsNotExist(err), Equals, true) 458 s.udevadmCmd.ForgetCalls() 459 460 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1WithNmbdNoSlot, 0) 461 // file called "70-snap.sambda.rules" still does not exist 462 _, err = os.Stat(fname) 463 c.Check(os.IsNotExist(err), Equals, true) 464 // Verify that udevadm was used to reload rules and re-run triggers 465 c.Check(len(s.udevadmCmd.Calls()), Equals, 0) 466 s.RemoveSnap(c, snapInfo) 467 } 468 } 469 470 func (s *backendSuite) TestInstallingSnapWritesAndLoadsRulesWithInputSubsystem(c *C) { 471 // NOTE: Hand out a permanent snippet so that .rules file is generated. 472 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 473 spec.TriggerSubsystem("input") 474 spec.AddSnippet("dummy") 475 return nil 476 } 477 for _, opts := range testedConfinementOpts { 478 s.udevadmCmd.ForgetCalls() 479 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 480 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 481 // file called "70-snap.sambda.rules" was created 482 _, err := os.Stat(fname) 483 c.Check(err, IsNil) 484 // udevadm was used to reload rules and re-run triggers 485 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 486 {"udevadm", "control", "--reload-rules"}, 487 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 488 {"udevadm", "trigger", "--subsystem-match=input"}, 489 {"udevadm", "settle", "--timeout=10"}, 490 }) 491 s.RemoveSnap(c, snapInfo) 492 } 493 } 494 495 func (s *backendSuite) TestInstallingSnapWritesAndLoadsRulesWithInputJoystickSubsystem(c *C) { 496 // NOTE: Hand out a permanent snippet so that .rules file is generated. 497 s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *snap.SlotInfo) error { 498 spec.TriggerSubsystem("input/joystick") 499 spec.AddSnippet("dummy") 500 return nil 501 } 502 for _, opts := range testedConfinementOpts { 503 s.udevadmCmd.ForgetCalls() 504 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 505 fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") 506 // file called "70-snap.sambda.rules" was created 507 _, err := os.Stat(fname) 508 c.Check(err, IsNil) 509 // udevadm was used to reload rules and re-run triggers 510 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 511 {"udevadm", "control", "--reload-rules"}, 512 {"udevadm", "trigger", "--subsystem-nomatch=input"}, 513 {"udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1"}, 514 {"udevadm", "settle", "--timeout=10"}, 515 }) 516 s.RemoveSnap(c, snapInfo) 517 } 518 } 519 520 func (s *backendSuite) TestSandboxFeatures(c *C) { 521 restore := cgroup.MockVersion(cgroup.V1, nil) 522 defer restore() 523 524 c.Assert(s.Backend.SandboxFeatures(), DeepEquals, []string{ 525 "device-filtering", 526 "device-cgroup-v1", 527 "tagging", 528 }) 529 530 restore = cgroup.MockVersion(cgroup.V2, nil) 531 defer restore() 532 c.Assert(s.Backend.SandboxFeatures(), DeepEquals, []string{ 533 "tagging", 534 }) 535 }