Skip to content

Commit

Permalink
Merge pull request #629 from MHaukelid/593_v2
Browse files Browse the repository at this point in the history
Add serial number option for type usb, and make busnum and devnum available as config keys for device type usb.
  • Loading branch information
stgraber authored Mar 21, 2024
2 parents 3843524 + 2b210b2 commit 75ee076
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 2 deletions.
6 changes: 6 additions & 0 deletions cmd/incusd/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ func deviceNetlinkListener() (chan []string, chan device.USBEvent, chan device.U
continue
}

serial, ok := props["SERIAL"]
if !ok {
continue
}

major, ok := props["MAJOR"]
if !ok {
continue
Expand Down Expand Up @@ -218,6 +223,7 @@ func deviceNetlinkListener() (chan []string, chan device.USBEvent, chan device.U
*/
zeroPad(parts[0], 4),
zeroPad(parts[1], 4),
serial,
major,
minor,
busnum,
Expand Down
5 changes: 5 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2387,3 +2387,8 @@ The `JWT` must have the certificate's fingerprint as its `Subject` and must be s
## `oidc_claim`

This introduces a new `oidc.claim` server configuration key which can be used to specify what OpenID Connect claim to use as the username.

## `device_usb_serial`

This adds a new configuration key `serial` for device type `usb`.
Feature has been added, to make it possible to distinguish between devices with identical `vendorid` and `productid`.
3 changes: 3 additions & 0 deletions doc/reference/devices_usb.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ Key | Type | Default | Description
`required` | bool | `false` | Whether this device is required to start the instance (the default is `false`, and all devices can be hotplugged)
`uid` | int | `0` | Only for containers: UID of the device owner in the instance
`vendorid` | string | - | The vendor ID of the USB device
`serial` | string | - | The serial number of the USB device
`busnum` | int | - | The bus number of which the USB device is attached
`devnum` | int | - | The device number of the USB device
5 changes: 5 additions & 0 deletions doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5156,6 +5156,11 @@ definitions:
example: "2221"
type: string
x-go-name: ProductID
serial:
description: USB serial number
example: DAE005fp
type: string
x-go-name: Serial
speed:
description: Transfer speed (Mbit/s)
example: 12
Expand Down
4 changes: 3 additions & 1 deletion internal/server/device/device_utils_usb_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type USBEvent struct {

Vendor string
Product string
Serial string

Path string
Major uint32
Expand Down Expand Up @@ -97,7 +98,7 @@ func USBRunHandlers(state *state.State, event *USBEvent) {
}

// USBNewEvent instantiates a new USBEvent struct.
func USBNewEvent(action string, vendor string, product string, major string, minor string, busnum string, devnum string, devname string, ueventParts []string, ueventLen int) (USBEvent, error) {
func USBNewEvent(action string, vendor string, product string, serial string, major string, minor string, busnum string, devnum string, devname string, ueventParts []string, ueventLen int) (USBEvent, error) {
majorInt, err := strconv.ParseUint(major, 10, 32)
if err != nil {
return USBEvent{}, err
Expand Down Expand Up @@ -131,6 +132,7 @@ func USBNewEvent(action string, vendor string, product string, major string, min
action,
vendor,
product,
serial,
path,
uint32(majorInt),
uint32(minorInt),
Expand Down
11 changes: 10 additions & 1 deletion internal/server/device/usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const usbDevPath = "/sys/bus/usb/devices"
// callbacks without needing to keep a reference to the usb device struct.
func usbIsOurDevice(config deviceConfig.Device, usb *USBEvent) bool {
// Check if event matches criteria for this device, if not return.
if (config["vendorid"] != "" && config["vendorid"] != usb.Vendor) || (config["productid"] != "" && config["productid"] != usb.Product) {
if (config["vendorid"] != "" && config["vendorid"] != usb.Vendor) ||
(config["productid"] != "" && config["productid"] != usb.Product) ||
(config["serial"] != "" && config["serial"] != usb.Serial) ||
(config["busnum"] != "" && config["busnum"] != fmt.Sprintf("%d", usb.BusNum)) ||
(config["devnum"] != "" && config["devnum"] != fmt.Sprintf("%d", usb.DevNum)) {
return false
}

Expand Down Expand Up @@ -52,10 +56,13 @@ func (d *usb) validateConfig(instConf instance.ConfigReader) error {
rules := map[string]func(string) error{
"vendorid": validate.Optional(validate.IsDeviceID),
"productid": validate.Optional(validate.IsDeviceID),
"serial": validate.Optional(validate.IsAny),
"uid": unixValidUserID,
"gid": unixValidUserID,
"mode": unixValidOctalFileMode,
"required": validate.Optional(validate.IsBool),
"busnum": validate.Optional(validate.IsUint32),
"devnum": validate.Optional(validate.IsUint32),
}

err := d.config.Validate(rules)
Expand Down Expand Up @@ -265,6 +272,7 @@ func (d *usb) loadUsb() ([]USBEvent, error) {
"add",
values["idVendor"],
values["idProduct"],
values["serial"],
parts[0],
parts[1],
values["busnum"],
Expand All @@ -291,6 +299,7 @@ func (d *usb) loadRawValues(p string) (map[string]string, error) {
values := map[string]string{
"idVendor": "",
"idProduct": "",
"serial": "",
"dev": "",
"busnum": "",
"devnum": "",
Expand Down
11 changes: 11 additions & 0 deletions internal/server/resources/usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ func GetUSB() (*api.ResourcesUSB, error) {
return nil, fmt.Errorf("Failed to read %q: %w", filepath.Join(devicePath, "devnum"), err)
}

// Get serial number
deviceSerialPath := filepath.Join(devicePath, "iSerial")
if sysfsExists(deviceSerialPath) {
content, err := os.ReadFile(deviceSerialPath)
if err != nil {
return nil, fmt.Errorf("Failed to read %q: %w", deviceSerialPath, err)
}

device.Serial = strings.TrimSpace(string(content))
}

// Get product ID
var productID uint64

Expand Down
1 change: 1 addition & 0 deletions internal/version/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ var APIExtensions = []string{
"shared_custom_block_volumes",
"auth_tls_jwt",
"oidc_claim",
"device_usb_serial",
}

// APIExtensionsCount returns the number of available API extensions.
Expand Down
6 changes: 6 additions & 0 deletions shared/api/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,12 @@ type ResourcesUSBDevice struct {
// Example: 3
DeviceAddress uint64 `json:"device_address" yaml:"device_address"`

// USB serial number
// Example: DAE005fp
//
// API extension: device_usb_serial.
Serial string `json:"serial" yaml:"serial"`

// List of USB interfaces
Interfaces []ResourcesUSBDeviceInterface `json:"interfaces" yaml:"interfaces"`

Expand Down

0 comments on commit 75ee076

Please sign in to comment.