File: device_utils_usb_events.go

package info (click to toggle)
incus 6.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,392 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (144 lines) | stat: -rw-r--r-- 3,909 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package device

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	deviceConfig "github.com/lxc/incus/v6/internal/server/device/config"
	"github.com/lxc/incus/v6/internal/server/instance"
	"github.com/lxc/incus/v6/internal/server/state"
	"github.com/lxc/incus/v6/shared/logger"
)

// USBEvent represents the properties of a USB device uevent.
type USBEvent struct {
	Action string

	Vendor  string
	Product string
	Serial  string

	Path        string
	Major       uint32
	Minor       uint32
	UeventParts []string
	UeventLen   int

	BusNum int
	DevNum int
}

// usbHandlers stores the event handler callbacks for USB events.
var usbHandlers = map[string]func(USBEvent) (*deviceConfig.RunConfig, error){}

// usbMutex controls access to the usbHandlers map.
var usbMutex sync.Mutex

// usbRegisterHandler registers a handler function to be called whenever a USB device event occurs.
func usbRegisterHandler(inst instance.Instance, deviceName string, handler func(USBEvent) (*deviceConfig.RunConfig, error)) {
	usbMutex.Lock()
	defer usbMutex.Unlock()

	// Null delimited string of project name, instance name and device name.
	key := fmt.Sprintf("%s\000%s\000%s", inst.Project().Name, inst.Name(), deviceName)
	usbHandlers[key] = handler
}

// usbUnregisterHandler removes a registered USB handler function for a device.
func usbUnregisterHandler(inst instance.Instance, deviceName string) {
	usbMutex.Lock()
	defer usbMutex.Unlock()

	// Null delimited string of project name, instance name and device name.
	key := fmt.Sprintf("%s\000%s\000%s", inst.Project().Name, inst.Name(), deviceName)
	delete(usbHandlers, key)
}

// USBRunHandlers executes any handlers registered for USB events.
func USBRunHandlers(state *state.State, event *USBEvent) {
	usbMutex.Lock()
	defer usbMutex.Unlock()

	for key, hook := range usbHandlers {
		keyParts := strings.SplitN(key, "\000", 3)
		projectName := keyParts[0]
		instanceName := keyParts[1]
		deviceName := keyParts[2]

		if hook == nil {
			delete(usbHandlers, key)
			continue
		}

		runConf, err := hook(*event)
		if err != nil {
			logger.Error("USB event hook failed", logger.Ctx{"err": err, "project": projectName, "instance": instanceName, "device": deviceName})
			continue
		}

		// If runConf supplied, load instance and call its USB event handler function so
		// any instance specific device actions can occur.
		if runConf != nil {
			instance, err := instance.LoadByProjectAndName(state, projectName, instanceName)
			if err != nil {
				logger.Error("USB event loading instance failed", logger.Ctx{"err": err, "project": projectName, "instance": instanceName, "device": deviceName})
				continue
			}

			err = instance.DeviceEventHandler(runConf)
			if err != nil {
				logger.Error("USB event instance handler failed", logger.Ctx{"err": err, "project": projectName, "instance": instanceName, "device": deviceName})
				continue
			}
		}
	}
}

// USBNewEvent instantiates a new USBEvent struct.
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
	}

	minorInt, err := strconv.ParseUint(minor, 10, 32)
	if err != nil {
		return USBEvent{}, err
	}

	busnumInt, err := strconv.Atoi(busnum)
	if err != nil {
		return USBEvent{}, err
	}

	devnumInt, err := strconv.Atoi(devnum)
	if err != nil {
		return USBEvent{}, err
	}

	path := devname
	if devname == "" {
		path = fmt.Sprintf("/dev/bus/usb/%03d/%03d", busnumInt, devnumInt)
	} else {
		if !filepath.IsAbs(devname) {
			path = fmt.Sprintf("/dev/%s", devname)
		}
	}

	return USBEvent{
		action,
		vendor,
		product,
		serial,
		path,
		uint32(majorInt),
		uint32(minorInt),
		ueventParts,
		ueventLen,
		busnumInt,
		devnumInt,
	}, nil
}