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