github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/interfaces/builtin/serial_port_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 builtin_test 21 22 import ( 23 . "gopkg.in/check.v1" 24 25 "github.com/snapcore/snapd/interfaces" 26 "github.com/snapcore/snapd/interfaces/apparmor" 27 "github.com/snapcore/snapd/interfaces/builtin" 28 "github.com/snapcore/snapd/interfaces/hotplug" 29 "github.com/snapcore/snapd/interfaces/udev" 30 "github.com/snapcore/snapd/snap" 31 "github.com/snapcore/snapd/snap/snaptest" 32 "github.com/snapcore/snapd/testutil" 33 ) 34 35 type SerialPortInterfaceSuite struct { 36 testutil.BaseTest 37 iface interfaces.Interface 38 39 // OS Snap 40 testSlot1 *interfaces.ConnectedSlot 41 testSlot1Info *snap.SlotInfo 42 testSlot2 *interfaces.ConnectedSlot 43 testSlot2Info *snap.SlotInfo 44 testSlot3 *interfaces.ConnectedSlot 45 testSlot3Info *snap.SlotInfo 46 testSlot4 *interfaces.ConnectedSlot 47 testSlot4Info *snap.SlotInfo 48 testSlot5 *interfaces.ConnectedSlot 49 testSlot5Info *snap.SlotInfo 50 testSlot6 *interfaces.ConnectedSlot 51 testSlot6Info *snap.SlotInfo 52 testSlot7 *interfaces.ConnectedSlot 53 testSlot7Info *snap.SlotInfo 54 testSlot8 *interfaces.ConnectedSlot 55 testSlot8Info *snap.SlotInfo 56 testSlot9 *interfaces.ConnectedSlot 57 testSlot9Info *snap.SlotInfo 58 testSlotCleaned *interfaces.ConnectedSlot 59 testSlotCleanedInfo *snap.SlotInfo 60 missingPathSlot *interfaces.ConnectedSlot 61 missingPathSlotInfo *snap.SlotInfo 62 badPathSlot1 *interfaces.ConnectedSlot 63 badPathSlot1Info *snap.SlotInfo 64 badPathSlot2 *interfaces.ConnectedSlot 65 badPathSlot2Info *snap.SlotInfo 66 badPathSlot3 *interfaces.ConnectedSlot 67 badPathSlot3Info *snap.SlotInfo 68 badPathSlot4 *interfaces.ConnectedSlot 69 badPathSlot4Info *snap.SlotInfo 70 badPathSlot5 *interfaces.ConnectedSlot 71 badPathSlot5Info *snap.SlotInfo 72 badPathSlot6 *interfaces.ConnectedSlot 73 badPathSlot6Info *snap.SlotInfo 74 badPathSlot7 *interfaces.ConnectedSlot 75 badPathSlot7Info *snap.SlotInfo 76 badPathSlot8 *interfaces.ConnectedSlot 77 badPathSlot8Info *snap.SlotInfo 78 badPathSlot9 *interfaces.ConnectedSlot 79 badPathSlot9Info *snap.SlotInfo 80 badPathSlot10 *interfaces.ConnectedSlot 81 badPathSlot10Info *snap.SlotInfo 82 badPathSlot11 *interfaces.ConnectedSlot 83 badPathSlot11Info *snap.SlotInfo 84 badInterfaceSlot *interfaces.ConnectedSlot 85 badInterfaceSlotInfo *snap.SlotInfo 86 87 // Gadget Snap 88 testUDev1 *interfaces.ConnectedSlot 89 testUDev1Info *snap.SlotInfo 90 testUDev2 *interfaces.ConnectedSlot 91 testUDev2Info *snap.SlotInfo 92 testUDev3 *interfaces.ConnectedSlot 93 testUDev3Info *snap.SlotInfo 94 testUDevBadValue1 *interfaces.ConnectedSlot 95 testUDevBadValue1Info *snap.SlotInfo 96 testUDevBadValue2 *interfaces.ConnectedSlot 97 testUDevBadValue2Info *snap.SlotInfo 98 testUDevBadValue3 *interfaces.ConnectedSlot 99 testUDevBadValue3Info *snap.SlotInfo 100 testUDevBadValue4 *interfaces.ConnectedSlot 101 testUDevBadValue4Info *snap.SlotInfo 102 testUDevBadValue5 *interfaces.ConnectedSlot 103 testUDevBadValue5Info *snap.SlotInfo 104 105 // Consuming Snap 106 testPlugPort1 *interfaces.ConnectedPlug 107 testPlugPort1Info *snap.PlugInfo 108 testPlugPort2 *interfaces.ConnectedPlug 109 testPlugPort2Info *snap.PlugInfo 110 testPlugPort3 *interfaces.ConnectedPlug 111 testPlugPort3Info *snap.PlugInfo 112 } 113 114 var _ = Suite(&SerialPortInterfaceSuite{ 115 iface: builtin.MustInterface("serial-port"), 116 }) 117 118 func (s *SerialPortInterfaceSuite) SetUpTest(c *C) { 119 osSnapInfo := snaptest.MockInfo(c, ` 120 name: ubuntu-core 121 version: 0 122 type: os 123 slots: 124 test-port-1: 125 interface: serial-port 126 path: /dev/ttyS0 127 test-port-2: 128 interface: serial-port 129 path: /dev/ttyUSB927 130 test-port-3: 131 interface: serial-port 132 path: /dev/ttyS42 133 test-port-4: 134 interface: serial-port 135 path: /dev/ttyO0 136 test-port-5: 137 interface: serial-port 138 path: /dev/ttyACM0 139 test-port-6: 140 interface: serial-port 141 path: /dev/ttyAMA0 142 test-port-7: 143 interface: serial-port 144 path: /dev/ttyXRUSB0 145 test-port-8: 146 interface: serial-port 147 path: /dev/ttymxc2 148 test-port-9: 149 interface: serial-port 150 path: /dev/ttySC0 151 test-port-unclean: 152 interface: serial-port 153 path: /dev/./././ttyS1//// 154 missing-path: serial-port 155 bad-path-1: 156 interface: serial-port 157 path: path 158 bad-path-2: 159 interface: serial-port 160 path: /dev/tty 161 bad-path-3: 162 interface: serial-port 163 path: /dev/tty0 164 bad-path-4: 165 interface: serial-port 166 path: /dev/tty63 167 bad-path-5: 168 interface: serial-port 169 path: /dev/ttyUSB 170 bad-path-6: 171 interface: serial-port 172 path: /dev/usb 173 bad-path-7: 174 interface: serial-port 175 path: /dev/ttyprintk 176 bad-path-8: 177 interface: serial-port 178 path: /dev/ttyO 179 bad-path-9: 180 interface: serial-port 181 path: /dev/ttyS 182 bad-path-10: 183 interface: serial-port 184 path: /dev/ttySC 185 bad-path-11: 186 interface: serial-port 187 path: /dev/ttyillegal0 188 bad-interface: other-interface 189 `, nil) 190 s.testSlot1Info = osSnapInfo.Slots["test-port-1"] 191 s.testSlot1 = interfaces.NewConnectedSlot(s.testSlot1Info, nil, nil) 192 s.testSlot2Info = osSnapInfo.Slots["test-port-2"] 193 s.testSlot2 = interfaces.NewConnectedSlot(s.testSlot2Info, nil, nil) 194 s.testSlot3Info = osSnapInfo.Slots["test-port-3"] 195 s.testSlot3 = interfaces.NewConnectedSlot(s.testSlot3Info, nil, nil) 196 s.testSlot4Info = osSnapInfo.Slots["test-port-4"] 197 s.testSlot4 = interfaces.NewConnectedSlot(s.testSlot4Info, nil, nil) 198 s.testSlot5Info = osSnapInfo.Slots["test-port-5"] 199 s.testSlot5 = interfaces.NewConnectedSlot(s.testSlot5Info, nil, nil) 200 s.testSlot6Info = osSnapInfo.Slots["test-port-6"] 201 s.testSlot6 = interfaces.NewConnectedSlot(s.testSlot6Info, nil, nil) 202 s.testSlot7Info = osSnapInfo.Slots["test-port-7"] 203 s.testSlot7 = interfaces.NewConnectedSlot(s.testSlot7Info, nil, nil) 204 s.testSlot8Info = osSnapInfo.Slots["test-port-8"] 205 s.testSlot8 = interfaces.NewConnectedSlot(s.testSlot8Info, nil, nil) 206 s.testSlot9Info = osSnapInfo.Slots["test-port-9"] 207 s.testSlot9 = interfaces.NewConnectedSlot(s.testSlot9Info, nil, nil) 208 s.testSlotCleanedInfo = osSnapInfo.Slots["test-port-unclean"] 209 s.testSlotCleaned = interfaces.NewConnectedSlot(s.testSlotCleanedInfo, nil, nil) 210 s.missingPathSlotInfo = osSnapInfo.Slots["missing-path"] 211 s.missingPathSlot = interfaces.NewConnectedSlot(s.missingPathSlotInfo, nil, nil) 212 s.badPathSlot1Info = osSnapInfo.Slots["bad-path-1"] 213 s.badPathSlot1 = interfaces.NewConnectedSlot(s.badPathSlot1Info, nil, nil) 214 s.badPathSlot2Info = osSnapInfo.Slots["bad-path-2"] 215 s.badPathSlot2 = interfaces.NewConnectedSlot(s.badPathSlot2Info, nil, nil) 216 s.badPathSlot3Info = osSnapInfo.Slots["bad-path-3"] 217 s.badPathSlot3 = interfaces.NewConnectedSlot(s.badPathSlot3Info, nil, nil) 218 s.badPathSlot4Info = osSnapInfo.Slots["bad-path-4"] 219 s.badPathSlot4 = interfaces.NewConnectedSlot(s.badPathSlot4Info, nil, nil) 220 s.badPathSlot5Info = osSnapInfo.Slots["bad-path-5"] 221 s.badPathSlot5 = interfaces.NewConnectedSlot(s.badPathSlot5Info, nil, nil) 222 s.badPathSlot6Info = osSnapInfo.Slots["bad-path-6"] 223 s.badPathSlot6 = interfaces.NewConnectedSlot(s.badPathSlot6Info, nil, nil) 224 s.badPathSlot7Info = osSnapInfo.Slots["bad-path-7"] 225 s.badPathSlot7 = interfaces.NewConnectedSlot(s.badPathSlot7Info, nil, nil) 226 s.badPathSlot8Info = osSnapInfo.Slots["bad-path-8"] 227 s.badPathSlot8 = interfaces.NewConnectedSlot(s.badPathSlot8Info, nil, nil) 228 s.badPathSlot9Info = osSnapInfo.Slots["bad-path-9"] 229 s.badPathSlot9 = interfaces.NewConnectedSlot(s.badPathSlot9Info, nil, nil) 230 s.badPathSlot10Info = osSnapInfo.Slots["bad-path-10"] 231 s.badPathSlot10 = interfaces.NewConnectedSlot(s.badPathSlot10Info, nil, nil) 232 s.badPathSlot11Info = osSnapInfo.Slots["bad-path-11"] 233 s.badPathSlot11 = interfaces.NewConnectedSlot(s.badPathSlot11Info, nil, nil) 234 s.badInterfaceSlotInfo = osSnapInfo.Slots["bad-interface"] 235 s.badInterfaceSlot = interfaces.NewConnectedSlot(s.badInterfaceSlotInfo, nil, nil) 236 237 gadgetSnapInfo := snaptest.MockInfo(c, ` 238 name: some-device 239 version: 0 240 type: gadget 241 slots: 242 test-udev-1: 243 interface: serial-port 244 usb-vendor: 0x0001 245 usb-product: 0x0001 246 path: /dev/serial-port-zigbee 247 test-udev-2: 248 interface: serial-port 249 usb-vendor: 0xffff 250 usb-product: 0xffff 251 path: /dev/serial-port-mydevice 252 test-udev-3: 253 interface: serial-port 254 usb-vendor: 0xabcd 255 usb-product: 0x1234 256 usb-interface-number: 0 257 path: /dev/serial-port-myserial 258 test-udev-bad-value-1: 259 interface: serial-port 260 usb-vendor: -1 261 usb-product: 0xffff 262 path: /dev/serial-port-mydevice 263 test-udev-bad-value-2: 264 interface: serial-port 265 usb-vendor: 0x1234 266 usb-product: 0x10000 267 path: /dev/serial-port-mydevice 268 test-udev-bad-value-3: 269 interface: serial-port 270 usb-vendor: 0x789a 271 usb-product: 0x4321 272 path: /dev/my-device 273 test-udev-bad-value-4: 274 interface: serial-port 275 usb-vendor: 0x1234 276 usb-product: 0x4321 277 usb-interface-number: -1 278 path: /dev/serial-port-mybadinterface 279 test-udev-bad-value-5: 280 interface: serial-port 281 usb-vendor: 0x1234 282 usb-product: 0x4321 283 usb-interface-number: 32 284 path: /dev/serial-port-overinterfacenumber 285 `, nil) 286 s.testUDev1Info = gadgetSnapInfo.Slots["test-udev-1"] 287 s.testUDev1 = interfaces.NewConnectedSlot(s.testUDev1Info, nil, nil) 288 s.testUDev2Info = gadgetSnapInfo.Slots["test-udev-2"] 289 s.testUDev2 = interfaces.NewConnectedSlot(s.testUDev2Info, nil, nil) 290 s.testUDev3Info = gadgetSnapInfo.Slots["test-udev-3"] 291 s.testUDev3 = interfaces.NewConnectedSlot(s.testUDev3Info, nil, nil) 292 s.testUDevBadValue1Info = gadgetSnapInfo.Slots["test-udev-bad-value-1"] 293 s.testUDevBadValue1 = interfaces.NewConnectedSlot(s.testUDevBadValue1Info, nil, nil) 294 s.testUDevBadValue2Info = gadgetSnapInfo.Slots["test-udev-bad-value-2"] 295 s.testUDevBadValue3 = interfaces.NewConnectedSlot(s.testUDevBadValue2Info, nil, nil) 296 s.testUDevBadValue3Info = gadgetSnapInfo.Slots["test-udev-bad-value-3"] 297 s.testUDevBadValue3 = interfaces.NewConnectedSlot(s.testUDevBadValue3Info, nil, nil) 298 s.testUDevBadValue4Info = gadgetSnapInfo.Slots["test-udev-bad-value-4"] 299 s.testUDevBadValue4 = interfaces.NewConnectedSlot(s.testUDevBadValue4Info, nil, nil) 300 s.testUDevBadValue5Info = gadgetSnapInfo.Slots["test-udev-bad-value-5"] 301 s.testUDevBadValue5 = interfaces.NewConnectedSlot(s.testUDevBadValue5Info, nil, nil) 302 303 consumingSnapInfo := snaptest.MockInfo(c, ` 304 name: client-snap 305 version: 0 306 plugs: 307 plug-for-port-1: 308 interface: serial-port 309 plug-for-port-2: 310 interface: serial-port 311 plug-for-port-3: 312 interface: serial-port 313 314 apps: 315 app-accessing-1-port: 316 command: foo 317 plugs: [serial-port] 318 app-accessing-2-ports: 319 command: bar 320 plugs: [plug-for-port-1, plug-for-port-2] 321 app-accessing-3rd-port: 322 command: foo 323 plugs: [plug-for-port-3] 324 `, nil) 325 s.testPlugPort1Info = consumingSnapInfo.Plugs["plug-for-port-1"] 326 s.testPlugPort1 = interfaces.NewConnectedPlug(s.testPlugPort1Info, nil, nil) 327 s.testPlugPort2Info = consumingSnapInfo.Plugs["plug-for-port-2"] 328 s.testPlugPort2 = interfaces.NewConnectedPlug(s.testPlugPort2Info, nil, nil) 329 s.testPlugPort3Info = consumingSnapInfo.Plugs["plug-for-port-3"] 330 s.testPlugPort3 = interfaces.NewConnectedPlug(s.testPlugPort3Info, nil, nil) 331 } 332 333 func (s *SerialPortInterfaceSuite) TestName(c *C) { 334 c.Assert(s.iface.Name(), Equals, "serial-port") 335 } 336 337 func (s *SerialPortInterfaceSuite) TestSanitizeCoreSnapSlots(c *C) { 338 for _, slot := range []*snap.SlotInfo{s.testSlot1Info, s.testSlot2Info, s.testSlot3Info, s.testSlot4Info, s.testSlot5Info, s.testSlot6Info, s.testSlot7Info, s.testSlot8Info} { 339 c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), IsNil) 340 } 341 } 342 343 func (s *SerialPortInterfaceSuite) TestSanitizeBadCoreSnapSlots(c *C) { 344 // Slots without the "path" attribute are rejected. 345 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.missingPathSlotInfo), ErrorMatches, `serial-port slot must have a path attribute`) 346 347 // Slots with incorrect value of the "path" attribute are rejected. 348 for _, slot := range []*snap.SlotInfo{s.badPathSlot1Info, s.badPathSlot2Info, s.badPathSlot3Info, s.badPathSlot4Info, s.badPathSlot5Info, s.badPathSlot6Info, s.badPathSlot7Info, s.badPathSlot8Info, s.badPathSlot9Info, s.badPathSlot10Info, s.badPathSlot11Info} { 349 c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches, "serial-port path attribute must be a valid device node") 350 } 351 } 352 353 func (s *SerialPortInterfaceSuite) TestSanitizeGadgetSnapSlots(c *C) { 354 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev1Info), IsNil) 355 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev2Info), IsNil) 356 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev3Info), IsNil) 357 } 358 359 func (s *SerialPortInterfaceSuite) TestSanitizeBadGadgetSnapSlots(c *C) { 360 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue1Info), ErrorMatches, "serial-port usb-vendor attribute not valid: -1") 361 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue2Info), ErrorMatches, "serial-port usb-product attribute not valid: 65536") 362 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue3Info), ErrorMatches, "serial-port path attribute specifies invalid symlink location") 363 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue4Info), ErrorMatches, "serial-port usb-interface-number attribute cannot be negative or larger than 31") 364 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue5Info), ErrorMatches, "serial-port usb-interface-number attribute cannot be negative or larger than 31") 365 } 366 367 func (s *SerialPortInterfaceSuite) TestPermanentSlotUDevSnippets(c *C) { 368 spec := &udev.Specification{} 369 for _, slot := range []*snap.SlotInfo{s.testSlot1Info, s.testSlot2Info, s.testSlot3Info, s.testSlot4Info} { 370 err := spec.AddPermanentSlot(s.iface, slot) 371 c.Assert(err, IsNil) 372 c.Assert(spec.Snippets(), HasLen, 0) 373 } 374 375 expectedSnippet1 := `# serial-port 376 IMPORT{builtin}="usb_id" 377 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", SYMLINK+="serial-port-zigbee"` 378 err := spec.AddPermanentSlot(s.iface, s.testUDev1Info) 379 c.Assert(err, IsNil) 380 c.Assert(spec.Snippets(), HasLen, 1) 381 snippet := spec.Snippets()[0] 382 c.Assert(snippet, Equals, expectedSnippet1) 383 384 spec = &udev.Specification{} 385 expectedSnippet2 := `# serial-port 386 IMPORT{builtin}="usb_id" 387 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", SYMLINK+="serial-port-mydevice"` 388 err = spec.AddPermanentSlot(s.iface, s.testUDev2Info) 389 c.Assert(err, IsNil) 390 c.Assert(spec.Snippets(), HasLen, 1) 391 snippet = spec.Snippets()[0] 392 c.Assert(snippet, Equals, expectedSnippet2) 393 394 spec = &udev.Specification{} 395 // The ENV{ID_USB_INTERFACE_NUM} is set to two hex digits 396 // For instance, the expectedSnippet3 is set to 00 397 expectedSnippet3 := `# serial-port 398 IMPORT{builtin}="usb_id" 399 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="abcd", ATTRS{idProduct}=="1234", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="serial-port-myserial"` 400 err = spec.AddPermanentSlot(s.iface, s.testUDev3Info) 401 c.Assert(err, IsNil) 402 c.Assert(spec.Snippets(), HasLen, 1) 403 snippet = spec.Snippets()[0] 404 c.Assert(snippet, Equals, expectedSnippet3) 405 } 406 407 func (s *SerialPortInterfaceSuite) TestConnectedPlugUDevSnippets(c *C) { 408 // add the plug for the slot with just path 409 spec := &udev.Specification{} 410 err := spec.AddConnectedPlug(s.iface, s.testPlugPort1, s.testSlot1) 411 c.Assert(err, IsNil) 412 c.Assert(spec.Snippets(), HasLen, 2) 413 snippet := spec.Snippets()[0] 414 expectedSnippet1 := `# serial-port 415 SUBSYSTEM=="tty", KERNEL=="ttyS0", TAG+="snap_client-snap_app-accessing-2-ports"` 416 c.Assert(snippet, Equals, expectedSnippet1) 417 extraSnippet := spec.Snippets()[1] 418 expectedExtraSnippet1 := `TAG=="snap_client-snap_app-accessing-2-ports", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"` 419 c.Assert(extraSnippet, Equals, expectedExtraSnippet1) 420 421 // add plug for the first slot with product and vendor ids 422 spec = &udev.Specification{} 423 err = spec.AddConnectedPlug(s.iface, s.testPlugPort1, s.testUDev1) 424 c.Assert(err, IsNil) 425 c.Assert(spec.Snippets(), HasLen, 2) 426 snippet = spec.Snippets()[0] 427 expectedSnippet2 := `# serial-port 428 IMPORT{builtin}="usb_id" 429 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-ports"` 430 c.Assert(snippet, Equals, expectedSnippet2) 431 extraSnippet = spec.Snippets()[1] 432 expectedExtraSnippet2 := `TAG=="snap_client-snap_app-accessing-2-ports", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"` 433 c.Assert(extraSnippet, Equals, expectedExtraSnippet2) 434 435 // add plug for the first slot with product and vendor ids 436 spec = &udev.Specification{} 437 err = spec.AddConnectedPlug(s.iface, s.testPlugPort2, s.testUDev2) 438 c.Assert(err, IsNil) 439 c.Assert(spec.Snippets(), HasLen, 2) 440 snippet = spec.Snippets()[0] 441 expectedSnippet3 := `# serial-port 442 IMPORT{builtin}="usb_id" 443 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-2-ports"` 444 c.Assert(snippet, Equals, expectedSnippet3) 445 extraSnippet = spec.Snippets()[1] 446 expectedExtraSnippet3 := `TAG=="snap_client-snap_app-accessing-2-ports", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"` 447 c.Assert(extraSnippet, Equals, expectedExtraSnippet3) 448 449 // add plug for the first slot with product and vendor ids and usb interface number 450 spec = &udev.Specification{} 451 err = spec.AddConnectedPlug(s.iface, s.testPlugPort2, s.testUDev3) 452 c.Assert(err, IsNil) 453 c.Assert(spec.Snippets(), HasLen, 2) 454 snippet = spec.Snippets()[0] 455 expectedSnippet4 := `# serial-port 456 IMPORT{builtin}="usb_id" 457 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="abcd", ATTRS{idProduct}=="1234", ENV{ID_USB_INTERFACE_NUM}=="00", TAG+="snap_client-snap_app-accessing-2-ports"` 458 c.Assert(snippet, Equals, expectedSnippet4) 459 extraSnippet = spec.Snippets()[1] 460 expectedExtraSnippet4 := `TAG=="snap_client-snap_app-accessing-2-ports", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"` 461 c.Assert(extraSnippet, Equals, expectedExtraSnippet4) 462 } 463 464 func (s *SerialPortInterfaceSuite) TestConnectedPlugAppArmorSnippets(c *C) { 465 checkConnectedPlugSnippet := func(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot, expectedSnippet string) { 466 apparmorSpec := &apparmor.Specification{} 467 err := apparmorSpec.AddConnectedPlug(s.iface, plug, slot) 468 c.Assert(err, IsNil) 469 470 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.client-snap.app-accessing-2-ports"}) 471 snippet := apparmorSpec.SnippetForTag("snap.client-snap.app-accessing-2-ports") 472 c.Assert(snippet, DeepEquals, expectedSnippet) 473 } 474 475 expectedSnippet1 := `/dev/ttyS0 rwk,` 476 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot1, expectedSnippet1) 477 expectedSnippet2 := `/dev/ttyUSB927 rwk,` 478 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot2, expectedSnippet2) 479 480 expectedSnippet3 := `/dev/ttyS42 rwk,` 481 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot3, expectedSnippet3) 482 483 expectedSnippet4 := `/dev/ttyO0 rwk,` 484 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot4, expectedSnippet4) 485 486 expectedSnippet5 := `/dev/ttyACM0 rwk,` 487 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot5, expectedSnippet5) 488 489 expectedSnippet6 := `/dev/ttyAMA0 rwk,` 490 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot6, expectedSnippet6) 491 492 expectedSnippet7 := `/dev/ttyXRUSB0 rwk,` 493 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot7, expectedSnippet7) 494 495 expectedSnippet8 := `/dev/ttymxc2 rwk,` 496 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot8, expectedSnippet8) 497 498 expectedSnippet9 := `/dev/ttySC0 rwk,` 499 checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot9, expectedSnippet9) 500 501 expectedSnippet10 := `/dev/tty[A-Z]*[0-9] rwk,` 502 checkConnectedPlugSnippet(s.testPlugPort1, s.testUDev1, expectedSnippet10) 503 504 expectedSnippet11 := `/dev/tty[A-Z]*[0-9] rwk,` 505 checkConnectedPlugSnippet(s.testPlugPort2, s.testUDev2, expectedSnippet11) 506 507 expectedSnippet12 := `/dev/tty[A-Z]*[0-9] rwk,` 508 checkConnectedPlugSnippet(s.testPlugPort2, s.testUDev3, expectedSnippet12) 509 } 510 511 func (s *SerialPortInterfaceSuite) TestConnectedPlugUDevSnippetsForPath(c *C) { 512 checkConnectedPlugSnippet := func(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot, expectedSnippet string, expectedExtraSnippet string) { 513 udevSpec := &udev.Specification{} 514 err := udevSpec.AddConnectedPlug(s.iface, plug, slot) 515 c.Assert(err, IsNil) 516 517 c.Assert(udevSpec.Snippets(), HasLen, 2) 518 snippet := udevSpec.Snippets()[0] 519 c.Assert(snippet, Equals, expectedSnippet) 520 extraSnippet := udevSpec.Snippets()[1] 521 c.Assert(extraSnippet, Equals, expectedExtraSnippet) 522 } 523 524 // these have only path 525 expectedSnippet1 := `# serial-port 526 SUBSYSTEM=="tty", KERNEL=="ttyS0", TAG+="snap_client-snap_app-accessing-3rd-port"` 527 expectedExtraSnippet1 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 528 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot1, expectedSnippet1, expectedExtraSnippet1) 529 530 expectedSnippet2 := `# serial-port 531 SUBSYSTEM=="tty", KERNEL=="ttyUSB927", TAG+="snap_client-snap_app-accessing-3rd-port"` 532 expectedExtraSnippet2 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 533 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot2, expectedSnippet2, expectedExtraSnippet2) 534 535 expectedSnippet3 := `# serial-port 536 SUBSYSTEM=="tty", KERNEL=="ttyS42", TAG+="snap_client-snap_app-accessing-3rd-port"` 537 expectedExtraSnippet3 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 538 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot3, expectedSnippet3, expectedExtraSnippet3) 539 540 expectedSnippet4 := `# serial-port 541 SUBSYSTEM=="tty", KERNEL=="ttyO0", TAG+="snap_client-snap_app-accessing-3rd-port"` 542 expectedExtraSnippet4 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 543 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot4, expectedSnippet4, expectedExtraSnippet4) 544 545 expectedSnippet5 := `# serial-port 546 SUBSYSTEM=="tty", KERNEL=="ttyACM0", TAG+="snap_client-snap_app-accessing-3rd-port"` 547 expectedExtraSnippet5 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 548 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot5, expectedSnippet5, expectedExtraSnippet5) 549 550 expectedSnippet6 := `# serial-port 551 SUBSYSTEM=="tty", KERNEL=="ttyAMA0", TAG+="snap_client-snap_app-accessing-3rd-port"` 552 expectedExtraSnippet6 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 553 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot6, expectedSnippet6, expectedExtraSnippet6) 554 555 expectedSnippet7 := `# serial-port 556 SUBSYSTEM=="tty", KERNEL=="ttyXRUSB0", TAG+="snap_client-snap_app-accessing-3rd-port"` 557 expectedExtraSnippet7 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 558 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot7, expectedSnippet7, expectedExtraSnippet7) 559 560 expectedSnippet8 := `# serial-port 561 SUBSYSTEM=="tty", KERNEL=="ttymxc2", TAG+="snap_client-snap_app-accessing-3rd-port"` 562 expectedExtraSnippet8 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 563 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot8, expectedSnippet8, expectedExtraSnippet8) 564 565 expectedSnippet9 := `# serial-port 566 SUBSYSTEM=="tty", KERNEL=="ttySC0", TAG+="snap_client-snap_app-accessing-3rd-port"` 567 expectedExtraSnippet9 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 568 checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot9, expectedSnippet9, expectedExtraSnippet9) 569 570 // these have product and vendor ids 571 expectedSnippet10 := `# serial-port 572 IMPORT{builtin}="usb_id" 573 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-3rd-port"` 574 expectedExtraSnippet10 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 575 checkConnectedPlugSnippet(s.testPlugPort3, s.testUDev1, expectedSnippet10, expectedExtraSnippet10) 576 577 expectedSnippet11 := `# serial-port 578 IMPORT{builtin}="usb_id" 579 SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-3rd-port"` 580 expectedExtraSnippet11 := `TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"` 581 checkConnectedPlugSnippet(s.testPlugPort3, s.testUDev2, expectedSnippet11, expectedExtraSnippet11) 582 } 583 584 func (s *SerialPortInterfaceSuite) TestHotplugDeviceDetected(c *C) { 585 hotplugIface := s.iface.(hotplug.Definer) 586 di, err := hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/ttyUSB0", "ID_VENDOR_ID": "1234", "ID_MODEL_ID": "5678", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 587 c.Assert(err, IsNil) 588 proposedSlot, err := hotplugIface.HotplugDeviceDetected(di) 589 c.Assert(err, IsNil) 590 c.Assert(proposedSlot, DeepEquals, &hotplug.ProposedSlot{Attrs: map[string]interface{}{"path": "/dev/ttyUSB0", "usb-vendor": "1234", "usb-product": "5678"}}) 591 } 592 593 func (s *SerialPortInterfaceSuite) TestHotplugDeviceDetectedNotSerialPort(c *C) { 594 hotplugIface := s.iface.(hotplug.Definer) 595 di, err := hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/other", "ID_VENDOR_ID": "1234", "ID_MODEL_ID": "5678", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 596 c.Assert(err, IsNil) 597 proposedSlot, err := hotplugIface.HotplugDeviceDetected(di) 598 c.Assert(err, IsNil) 599 c.Assert(proposedSlot, IsNil) 600 } 601 602 func (s *SerialPortInterfaceSuite) TestHotplugHandledByGadget(c *C) { 603 byGadgetPred := s.iface.(hotplug.HandledByGadgetPredicate) 604 di, err := hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/ttyXRUSB0", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 605 c.Assert(err, IsNil) 606 607 c.Assert(byGadgetPred.HandledByGadget(di, s.testSlot5Info), Equals, false) 608 // matching path /dev/ttyXRUSB0 609 c.Assert(byGadgetPred.HandledByGadget(di, s.testSlot7Info), Equals, true) 610 611 // matching on vendor, model, usb interface num 612 di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "abcd", "ID_MODEL_ID": "1234", "ID_USB_INTERFACE_NUM": "00", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 613 c.Assert(err, IsNil) 614 c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, true) 615 // model doesn't match, everything else matches 616 di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "abcd", "ID_MODEL_ID": "ffff", "ID_USB_INTERFACE_NUM": "00", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 617 c.Assert(err, IsNil) 618 c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, false) 619 // vendor doesn't match, everything else matches 620 di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "eeee", "ID_MODEL_ID": "1234", "ID_USB_INTERFACE_NUM": "00", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 621 c.Assert(err, IsNil) 622 c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, false) 623 // usb interface doesn't match, everything else matches 624 di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "abcd", "ID_MODEL_ID": "1234", "ID_USB_INTERFACE_NUM": "ff", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 625 c.Assert(err, IsNil) 626 c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, false) 627 628 // usb interface num is optional, match on vendor/model 629 di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "ffff", "ID_MODEL_ID": "ffff", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"}) 630 c.Assert(err, IsNil) 631 c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev2Info), Equals, true) 632 } 633 634 func (s *SerialPortInterfaceSuite) TestInterfaces(c *C) { 635 c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) 636 }