The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6530
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === For https://github.com/lxc/lxd/issues/1750
From ba35c3c2169151d6c3d36cc5bc94d7edf7e5fa75 Mon Sep 17 00:00:00 2001 From: David Mao <david.mao...@gmail.com> Date: Sat, 16 Nov 2019 17:43:47 -0600 Subject: [PATCH 1/5] lxd/device: Add unix_hotplug device type Signed-off-by: Lillian Jan Johnson <lillianjanjohn...@gmail.com> Signed-off-by: David Mao <david....@utexas.edu> --- lxd/device/device_utils_unix.go | 21 ++ .../device_utils_unix_hotplug_events.go | 119 ++++++++++ lxd/device/unix_hotplug.go | 212 ++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 lxd/device/device_utils_unix_hotplug_events.go create mode 100644 lxd/device/unix_hotplug.go diff --git a/lxd/device/device_utils_unix.go b/lxd/device/device_utils_unix.go index 2576d80152..0fe80873a7 100644 --- a/lxd/device/device_utils_unix.go +++ b/lxd/device/device_utils_unix.go @@ -378,6 +378,27 @@ func unixDeviceSetupCharNum(s *state.State, devicesPath string, typePrefix strin return unixDeviceSetup(s, devicesPath, typePrefix, deviceName, configCopy, defaultMode, runConf) } +// unixDeviceSetupBlockNum calls unixDeviceSetup and overrides the supplied device config with the +// type as "unix-block" and the supplied major and minor numbers. This function can be used when you +// already know the device's major and minor numbers to avoid unixDeviceSetup() having to stat the +// device to ascertain these attributes. If defaultMode is true or mode is supplied in the device +// config then the origin device does not need to be accessed for its file mode. +func unixDeviceSetupBlockNum(s *state.State, devicesPath string, typePrefix string, deviceName string, m deviceConfig.Device, major uint32, minor uint32, path string, defaultMode bool, runConf *deviceConfig.RunConfig) error { + configCopy := deviceConfig.Device{} + for k, v := range m { + configCopy[k] = v + } + + // Overridng these in the config copy should avoid the need for unixDeviceSetup to stat + // the origin device to ascertain this information. + configCopy["type"] = "unix-block" + configCopy["major"] = fmt.Sprintf("%d", major) + configCopy["minor"] = fmt.Sprintf("%d", minor) + configCopy["path"] = path + + return unixDeviceSetup(s, devicesPath, typePrefix, deviceName, configCopy, defaultMode, runConf) +} + // UnixDeviceExists checks if the unix device already exists in devices path. func UnixDeviceExists(devicesPath string, prefix string, path string) bool { relativeDestPath := strings.TrimPrefix(path, "/") diff --git a/lxd/device/device_utils_unix_hotplug_events.go b/lxd/device/device_utils_unix_hotplug_events.go new file mode 100644 index 0000000000..20683723eb --- /dev/null +++ b/lxd/device/device_utils_unix_hotplug_events.go @@ -0,0 +1,119 @@ +package device + +import ( + "fmt" + "strconv" + "strings" + "sync" + + deviceConfig "github.com/lxc/lxd/lxd/device/config" + "github.com/lxc/lxd/lxd/state" + log "github.com/lxc/lxd/shared/log15" + "github.com/lxc/lxd/shared/logger" +) + +// UnixHotplugEvent represents the properties of a Unix hotplug device uevent. +type UnixHotplugEvent struct { + Action string + + Vendor string + Product string + + Path string + Major uint32 + Minor uint32 + Subsystem string + UeventParts []string + UeventLen int +} + +// unixHotplugHandlers stores the event handler callbacks for Unix hotplug events. +var unixHotplugHandlers = map[string]func(UnixHotplugEvent) (*deviceConfig.RunConfig, error){} + +// unixHotplugMutex controls access to the unixHotplugHandlers map. +var unixHotplugMutex sync.Mutex + +// unixHotplugRegisterHandler registers a handler function to be called whenever a Unix hotplug device event occurs. +func unixHotplugRegisterHandler(instance Instance, deviceName string, handler func(UnixHotplugEvent) (*deviceConfig.RunConfig, error)) { + unixHotplugMutex.Lock() + defer unixHotplugMutex.Unlock() + + // Null delimited string of project name, instance name and device name. + key := fmt.Sprintf("%s\000%s\000%s", instance.Project(), instance.Name(), deviceName) + unixHotplugHandlers[key] = handler +} + +// unixHotplugUnregisterHandler removes a registered Unix hotplug handler function for a device. +func unixHotplugUnregisterHandler(instance Instance, deviceName string) { + unixHotplugMutex.Lock() + defer unixHotplugMutex.Unlock() + + // Null delimited string of project name, instance name and device name. + key := fmt.Sprintf("%s\000%s\000%s", instance.Project(), instance.Name(), deviceName) + delete(unixHotplugHandlers, key) +} + +// unixHotplugRunHandlers executes any handlers registered for Unix hotplug events. +func UnixHotplugRunHandlers(state *state.State, event *UnixHotplugEvent) { + unixHotplugMutex.Lock() + defer unixHotplugMutex.Unlock() + + for key, hook := range unixHotplugHandlers { + keyParts := strings.SplitN(key, "\000", 3) + projectName := keyParts[0] + instanceName := keyParts[1] + deviceName := keyParts[2] + + if hook == nil { + delete(unixHotplugHandlers, key) + continue + } + + runConf, err := hook(*event) + if err != nil { + logger.Error("Unix hotplug event hook failed", log.Ctx{"err": err, "project": projectName, "instance": instanceName, "device": deviceName}) + continue + } + + // If runConf supplied, load instance and call its Unix hotplug event handler function so + // any instance specific device actions can occur. + if runConf != nil { + instance, err := InstanceLoadByProjectAndName(state, projectName, instanceName) + if err != nil { + logger.Error("Unix hotplug event loading instance failed", log.Ctx{"err": err, "project": projectName, "instance": instanceName, "device": deviceName}) + continue + } + + err = instance.DeviceEventHandler(runConf) + if err != nil { + logger.Error("Unix hotplug event instance handler failed", log.Ctx{"err": err, "project": projectName, "instance": instanceName, "device": deviceName}) + continue + } + } + } +} + +// UnixHotplugNewEvent instantiates a new UnixHotplugEvent struct. +func UnixHotplugNewEvent(action string, vendor string, product string, major string, minor string, subsystem string, devname string, ueventParts []string, ueventLen int) (UnixHotplugEvent, error) { + majorInt, err := strconv.ParseUint(major, 10, 32) + if err != nil { + return UnixHotplugEvent{}, err + } + + minorInt, err := strconv.ParseUint(minor, 10, 32) + if err != nil { + return UnixHotplugEvent{}, err + } + + return UnixHotplugEvent{ + action, + vendor, + product, + devname, + uint32(majorInt), + uint32(minorInt), + subsystem, + ueventParts, + ueventLen, + }, nil +} diff --git a/lxd/device/unix_hotplug.go b/lxd/device/unix_hotplug.go new file mode 100644 index 0000000000..94bc378318 --- /dev/null +++ b/lxd/device/unix_hotplug.go @@ -0,0 +1,212 @@ +package device + +import ( + "fmt" + "strconv" + "strings" + + deviceConfig "github.com/lxc/lxd/lxd/device/config" + "github.com/farjump/go-libudev" + "github.com/lxc/lxd/lxd/instance/instancetype" + "github.com/lxc/lxd/shared" +) + +// unixHotplugIsOurDevice indicates whether the unixHotplug device event qualifies as part of our device. +// This function is not defined against the unixHotplug struct type so that it can be used in event +// callbacks without needing to keep a reference to the unixHotplug device struct. +func unixHotplugIsOurDevice(config deviceConfig.Device, unixHotplug *UnixHotplugEvent) bool { + // Check if event matches criteria for this device, if not return. + if (config["vendorid"] != "" && config["vendorid"] != unixHotplug.Vendor) || (config["productid"] != "" && config["productid"] != unixHotplug.Product) { + return false + } + + return true +} + +type unixHotplug struct { + deviceCommon +} + +// isRequired indicates whether the device config requires this device to start OK. +func (d *unixHotplug) isRequired() bool { + // Defaults to not required. + if shared.IsTrue(d.config["required"]) { + return true + } + + return false +} + +// validateConfig checks the supplied config for correctness. +func (d *unixHotplug) validateConfig() error { + if d.instance.Type() != instancetype.Container { + return ErrUnsupportedDevType + } + + rules := map[string]func(string) error{ + "vendorid": shared.IsDeviceID, + "productid": shared.IsDeviceID, + "uid": unixValidUserID, + "gid": unixValidUserID, + "mode": unixValidOctalFileMode, + "required": shared.IsBool, + } + + err := d.config.Validate(rules) + if err != nil { + return err + } + + if d.config["vendorid"] == "" && d.config["productid"] == "" { + return fmt.Errorf("Unix hotplug devices require a vendorid or a productid") + } + + return nil +} + +// Register is run after the device is started or when LXD starts. +func (d *unixHotplug) Register() error { + // Extract variables needed to run the event hook so that the reference to this device + // struct is not needed to be kept in memory. + devicesPath := d.instance.DevicesPath() + devConfig := d.config + deviceName := d.name + state := d.state + + // Handler for when a UnixHotplug event occurs. + f := func(e UnixHotplugEvent) (*deviceConfig.RunConfig, error) { + if !unixHotplugIsOurDevice(devConfig, &e) { + return nil, nil + } + + runConf := deviceConfig.RunConfig{} + + if e.Action == "add" { + if e.Subsystem == "char" { + err := unixDeviceSetupCharNum(state, devicesPath, "unix", deviceName, devConfig, e.Major, e.Minor, e.Path, false, &runConf) + if err != nil { + return nil, err + } + } else { + err := unixDeviceSetupBlockNum(state, devicesPath, "unix", deviceName, devConfig, e.Major, e.Minor, e.Path, false, &runConf) + if err != nil { + return nil, err + } + } + } else if e.Action == "remove" { + relativeTargetPath := strings.TrimPrefix(e.Path, "/") + err := unixDeviceRemove(devicesPath, "unix", deviceName, relativeTargetPath, &runConf) + if err != nil { + return nil, err + } + + // Add a post hook function to remove the specific unix hotplug device file after unmount. + runConf.PostHooks = []func() error{func() error { + err := unixDeviceDeleteFiles(state, devicesPath, "unix", deviceName, relativeTargetPath) + if err != nil { + return fmt.Errorf("Failed to delete files for device '%s': %v", deviceName, err) + } + + return nil + }} + } + + runConf.Uevents = append(runConf.Uevents, e.UeventParts) + + return &runConf, nil + } + + unixHotplugRegisterHandler(d.instance, d.name, f) + + return nil +} + +// Start is run when the device is added to the instance +func (d *unixHotplug) Start() (*deviceConfig.RunConfig, error) { + runConf := deviceConfig.RunConfig{} + runConf.PostHooks = []func() error{d.Register} + + device := d.loadUnixDevice() + if d.isRequired() && device == nil { + return nil, fmt.Errorf("Required Unix Hotplug device not found") + } + if device == nil { + return &runConf, nil + } + + i, err := strconv.ParseUint(device.PropertyValue("MAJOR"), 10, 32) + if err != nil { + return nil, err + } + major := uint32(i) + j, err := strconv.ParseUint(device.PropertyValue("MINOR"), 10, 32) + if err != nil { + return nil, err + } + minor := uint32(j) + + // setup device + if device.Subsystem() == "char" { + err = unixDeviceSetupCharNum(d.state, d.instance.DevicesPath(), "unix", d.name, d.config, major, minor, device.Devnode(), false, &runConf) + } else if device.Subsystem() == "block" { + err = unixDeviceSetupBlockNum(d.state, d.instance.DevicesPath(), "unix", d.name, d.config, major, minor, device.Devnode(), false, &runConf) + } + + if err != nil { + return nil, err + } + + return &runConf, nil +} + +// Stop is run when the device is removed from the instance +func (d *unixHotplug) Stop() (*deviceConfig.RunConfig, error) { + unixHotplugUnregisterHandler(d.instance, d.name) + + runConf := deviceConfig.RunConfig{ + PostHooks: []func() error{d.postStop}, + } + + err := unixDeviceRemove(d.instance.DevicesPath(), "unix", d.name, "", &runConf) + if err != nil { + return nil, err + } + + return &runConf, nil +} + +// postStop is run after the device is removed from the instance +func (d *unixHotplug) postStop() error { + err := unixDeviceDeleteFiles(d.state, d.instance.DevicesPath(), "unix", d.name, "") + if err != nil { + return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err) + } + + return nil +} + +// loadUnixDevice scans the host machine for unix devices with matching product/vendor ids +// and returns the first matching device with the subsystem type char or block +func (d *unixHotplug) loadUnixDevice() *udev.Device { + // Find device if exists + u := udev.Udev{} + e := u.NewEnumerate() + + if d.config["vendorid"] != "" { + e.AddMatchProperty("ID_VENDOR_ID", d.config["vendorid"]) + } + if d.config["productid"] != "" { + e.AddMatchProperty("ID_MODEL_ID", d.config["productid"]) + } + e.AddMatchIsInitialized() + devices, _ := e.Devices() + var device *udev.Device + for i := range devices { + device = devices[i] + if device.Subsystem() == "block" || device.Subsystem() == "char" { + return device + } + } + + return nil +} From 77b8a0019cdc06cd3a894e6381acdafaf7c6f16d Mon Sep 17 00:00:00 2001 From: David Mao <david.mao...@gmail.com> Date: Tue, 19 Nov 2019 18:05:39 -0600 Subject: [PATCH 2/5] lxd/device: Add support for listening to unix char and block udev events Signed-off-by: David Mao <david....@utexas.edu> Signed-off-by: Lillian Jan Johnson <lillianjanjohn...@gmail.com> --- lxd/device/device.go | 19 +++++----- lxd/devices.go | 88 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/lxd/device/device.go b/lxd/device/device.go index c608f80bc9..9745867d7c 100644 --- a/lxd/device/device.go +++ b/lxd/device/device.go @@ -9,15 +9,16 @@ import ( // devTypes defines supported top-level device type creation functions. var devTypes = map[string]func(deviceConfig.Device) device{ - "nic": nicLoadByType, - "infiniband": infinibandLoadByType, - "proxy": func(c deviceConfig.Device) device { return &proxy{} }, - "gpu": func(c deviceConfig.Device) device { return &gpu{} }, - "usb": func(c deviceConfig.Device) device { return &usb{} }, - "unix-char": func(c deviceConfig.Device) device { return &unixCommon{} }, - "unix-block": func(c deviceConfig.Device) device { return &unixCommon{} }, - "disk": func(c deviceConfig.Device) device { return &disk{} }, - "none": func(c deviceConfig.Device) device { return &none{} }, + "nic": nicLoadByType, + "infiniband": infinibandLoadByType, + "proxy": func(c deviceConfig.Device) device { return &proxy{} }, + "gpu": func(c deviceConfig.Device) device { return &gpu{} }, + "usb": func(c deviceConfig.Device) device { return &usb{} }, + "unix-char": func(c deviceConfig.Device) device { return &unixCommon{} }, + "unix-block": func(c deviceConfig.Device) device { return &unixCommon{} }, + "unix-hotplug": func(c deviceConfig.Device) device { return &unixHotplug{} }, + "disk": func(c deviceConfig.Device) device { return &disk{} }, + "none": func(c deviceConfig.Device) device { return &none{} }, } // VolatileSetter is a function that accepts one or more key/value strings to save into the LXD diff --git a/lxd/devices.go b/lxd/devices.go index 5939a0c494..7a9cd3738b 100644 --- a/lxd/devices.go +++ b/lxd/devices.go @@ -34,7 +34,7 @@ func (c deviceTaskCPUs) Len() int { return len(c) } func (c deviceTaskCPUs) Less(i, j int) bool { return *c[i].count < *c[j].count } func (c deviceTaskCPUs) Swap(i, j int) { c[i], c[j] = c[j], c[i] } -func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent, error) { +func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent, chan device.UnixHotplugEvent, error) { NETLINK_KOBJECT_UEVENT := 15 UEVENT_BUFFER_SIZE := 2048 @@ -43,25 +43,26 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent NETLINK_KOBJECT_UEVENT, ) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } nl := unix.SockaddrNetlink{ Family: unix.AF_NETLINK, Pid: uint32(os.Getpid()), - Groups: 1, + Groups: 3, } err = unix.Bind(fd, &nl) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } chCPU := make(chan []string, 1) chNetwork := make(chan []string, 0) chUSB := make(chan device.USBEvent) + chUnix := make(chan device.UnixHotplugEvent) - go func(chCPU chan []string, chNetwork chan []string, chUSB chan device.USBEvent) { + go func(chCPU chan []string, chNetwork chan []string, chUSB chan device.USBEvent, chUnix chan device.UnixHotplugEvent) { b := make([]byte, UEVENT_BUFFER_SIZE*2) for { r, err := unix.Read(fd, b) @@ -72,6 +73,7 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent ueventBuf := make([]byte, r) copy(ueventBuf, b) ueventLen := 0 + udevEvent := false ueventParts := strings.Split(string(ueventBuf), "\x00") props := map[string]string{} for _, part := range ueventParts { @@ -79,6 +81,10 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent continue } + if strings.HasPrefix(part, "libudev") { + udevEvent = true + } + ueventLen += len(part) + 1 fields := strings.SplitN(part, "=", 2) @@ -180,10 +186,74 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent chUSB <- usb } + if (props["SUBSYSTEM"] == "char" || props["SUBSYSTEM"] == "block") && udevEvent { + vendor, ok := props["ID_VENDOR_ID"] + if !ok { + continue + } + + product, ok := props["ID_MODEL_ID"] + if !ok { + continue + } + + major, ok := props["MAJOR"] + if !ok { + continue + } + + minor, ok := props["MINOR"] + if !ok { + continue + } + + devname, ok := props["DEVNAME"] + if !ok { + continue + } + + busnum, ok := props["BUSNUM"] + if !ok { + continue + } + + devnum, ok := props["DEVNUM"] + if !ok { + continue + } + + zeroPad := func(s string, l int) string { + return strings.Repeat("0", l-len(s)) + s + } + + unix, err := device.UnixHotplugNewEvent( + props["ACTION"], + /* udev doesn't zero pad these, while + * everything else does, so let's zero pad them + * for consistency + */ + zeroPad(vendor, 4), + zeroPad(product, 4), + major, + minor, + busnum, + devnum, + devname, + ueventParts[:len(ueventParts)-1], + ueventLen, + ) + if err != nil { + logger.Error("Error reading unix device", log.Ctx{"err": err, "path": props["PHYSDEVPATH"]}) + continue + } + + chUnix <- unix + } + } - }(chCPU, chNetwork, chUSB) + }(chCPU, chNetwork, chUSB, chUnix) - return chCPU, chNetwork, chUSB, nil + return chCPU, chNetwork, chUSB, chUnix, nil } func parseCpuset(cpu string) ([]int, error) { @@ -440,7 +510,7 @@ func deviceNetworkPriority(s *state.State, netif string) { } func deviceEventListener(s *state.State) { - chNetlinkCPU, chNetlinkNetwork, chUSB, err := deviceNetlinkListener() + chNetlinkCPU, chNetlinkNetwork, chUSB, chUnix, err := deviceNetlinkListener() if err != nil { logger.Errorf("scheduler: Couldn't setup netlink listener: %v", err) return @@ -475,6 +545,8 @@ func deviceEventListener(s *state.State) { networkAutoAttach(s.Cluster, e[0]) case e := <-chUSB: device.USBRunHandlers(s, &e) + case e := <-chUnix: + device.UnixHotplugRunHandlers(s, &e) case e := <-cgroup.DeviceSchedRebalance: if len(e) != 3 { logger.Errorf("Scheduler: received an invalid rebalance event") From b817720285fad8e128f1e90407d978876409cf91 Mon Sep 17 00:00:00 2001 From: David Mao <david.mao...@gmail.com> Date: Wed, 27 Nov 2019 10:12:26 -0600 Subject: [PATCH 3/5] lxd/db: added device type Signed-off-by: Lillian J. Johnson <lillianjanjohn...@gmail.com> Signed-off-by: David Mao <david....@utexas.edu> --- lxd/db/devices.go | 4 ++++ lxd/devices.go | 25 ++++++++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/lxd/db/devices.go b/lxd/db/devices.go index 3691fd3478..e4fca429ed 100644 --- a/lxd/db/devices.go +++ b/lxd/db/devices.go @@ -29,6 +29,8 @@ func dbDeviceTypeToString(t int) (string, error) { return "infiniband", nil case 8: return "proxy", nil + case 9: + return "unix-hotplug", nil default: return "", fmt.Errorf("Invalid device type %d", t) } @@ -54,6 +56,8 @@ func dbDeviceTypeToInt(t string) (int, error) { return 7, nil case "proxy": return 8, nil + case "unix-hotplug": + return 9, nil default: return -1, fmt.Errorf("Invalid device type %s", t) } diff --git a/lxd/devices.go b/lxd/devices.go index 7a9cd3738b..1fbe2e123d 100644 --- a/lxd/devices.go +++ b/lxd/devices.go @@ -83,6 +83,7 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent if strings.HasPrefix(part, "libudev") { udevEvent = true + continue } ueventLen += len(part) + 1 @@ -97,7 +98,7 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent ueventLen-- - if props["SUBSYSTEM"] == "cpu" { + if props["SUBSYSTEM"] == "cpu" && !udevEvent { if props["DRIVER"] != "processor" { continue } @@ -114,7 +115,7 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent } } - if props["SUBSYSTEM"] == "net" { + if props["SUBSYSTEM"] == "net" && !udevEvent { if props["ACTION"] != "add" && props["ACTION"] != "removed" { continue } @@ -127,7 +128,7 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent chNetwork <- []string{props["INTERFACE"], props["ACTION"]} } - if props["SUBSYSTEM"] == "usb" { + if props["SUBSYSTEM"] == "usb" && !udevEvent { parts := strings.Split(props["PRODUCT"], "/") if len(parts) < 2 { continue @@ -187,6 +188,11 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent } if (props["SUBSYSTEM"] == "char" || props["SUBSYSTEM"] == "block") && udevEvent { + subsystem, ok := props["SUBSYSTEM"] + if !ok { + continue + } + vendor, ok := props["ID_VENDOR_ID"] if !ok { continue @@ -212,16 +218,6 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent continue } - busnum, ok := props["BUSNUM"] - if !ok { - continue - } - - devnum, ok := props["DEVNUM"] - if !ok { - continue - } - zeroPad := func(s string, l int) string { return strings.Repeat("0", l-len(s)) + s } @@ -236,8 +232,7 @@ func deviceNetlinkListener() (chan []string, chan []string, chan device.USBEvent zeroPad(product, 4), major, minor, - busnum, - devnum, + subsystem, devname, ueventParts[:len(ueventParts)-1], ueventLen, From 486e500177dfc88dafadce5a1e835c27e20996cc Mon Sep 17 00:00:00 2001 From: Lily <Lily> Date: Fri, 29 Nov 2019 10:10:37 -0600 Subject: [PATCH 4/5] api: Add extention for new device type unix hotplug Signed-off-by: David Mao <david....@utexas.edu> Signed-off-by: Lillian Jan Johnson <lillianjanjohn...@gmail.com> --- doc/api-extensions.md | 3 +++ shared/version/api.go | 1 + 2 files changed, 4 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 1b836a0623..980dddb2ac 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -886,3 +886,6 @@ This allows for existing a CEPH RDB or FS to be directly connected to a LXD cont ## virtual\_machines Add virtual machine support. + +## unix\_hotplug\_devices +Adds support for unix char and block device hotplugging. diff --git a/shared/version/api.go b/shared/version/api.go index 1afdc1b2d0..1f36ee5c4f 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -179,6 +179,7 @@ var APIExtensions = []string{ "container_syscall_intercept_mount_fuse", "container_disk_ceph", "virtual-machines", + "unix_hotplug_devices", } // APIExtensionsCount returns the number of available API extensions. From a382a1c8c33a0abb7d325aa17aed508d99d540d6 Mon Sep 17 00:00:00 2001 From: Lily <Lily> Date: Fri, 29 Nov 2019 10:15:00 -0600 Subject: [PATCH 5/5] doc/instances: added new device type unix hotplug Signed-off-by: David Mao <david....@utexas.edu> Signed-off-by: Lillian Jan Johnson <lillianjanjohn...@gmail.com> --- doc/instances.md | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/doc/instances.md b/doc/instances.md index 9ef017d60a..da4ae31c4b 100644 --- a/doc/instances.md +++ b/doc/instances.md @@ -221,17 +221,18 @@ lxc profile device add <profile> <name> <type> [key=value]... ## Device types LXD supports the following device types: -ID (database) | Name | Condition | Description -:-- | :-- | :-- | :-- -0 | [none](#type-none) | - | Inheritance blocker -1 | [nic](#type-nic) | - | Network interface -2 | [disk](#type-disk) | - | Mountpoint inside the instance -3 | [unix-char](#type-unix-char) | container | Unix character device -4 | [unix-block](#type-unix-block) | container | Unix block device -5 | [usb](#type-usb) | container | USB device -6 | [gpu](#type-gpu) | container | GPU device -7 | [infiniband](#type-infiniband) | container | Infiniband device -8 | [proxy](#type-proxy) | container | Proxy device +ID (database) | Name | Condition | Description +:-- | :-- | :-- | :-- +0 | [none](#type-none) | - | Inheritance blocker +1 | [nic](#type-nic) | - | Network interface +2 | [disk](#type-disk) | - | Mountpoint inside the instance +3 | [unix-char](#type-unix-char) | container | Unix character device +4 | [unix-block](#type-unix-block) | container | Unix block device +5 | [usb](#type-usb) | container | USB device +6 | [gpu](#type-gpu) | container | GPU device +7 | [infiniband](#type-infiniband) | container | Infiniband device +8 | [proxy](#type-proxy) | container | Proxy device +9 | [unix-hotplug](#type-unix-hotplug) | container | Unix hotplug device ### Type: none A none type device doesn't have any property and doesn't create anything inside the instance. @@ -666,6 +667,22 @@ proxy\_protocol | bool | false | no | Whether to use the HAP security.uid | int | 0 | no | What UID to drop privilege to security.gid | int | 0 | no | What GID to drop privilege to +### Type: unix-hotplug +Unix hotplug device entries make the requested unix device +appear in the container's `/dev` and allow read/write operations to it +if the device exists on the host system. + +The following properties exist: + +Key | Type | Default | Required | Description +:-- | :-- | :-- | :-- | :-- +vendorid | string | - | no | The vendor id of the USB device. +productid | string | - | no | The product id of the USB device. +uid | int | 0 | no | UID of the device owner in the container +gid | int | 0 | no | GID of the device owner in the container +mode | int | 0660 | no | Mode of the device in the container +required | boolean | false | no | Whether or not this device is required to start the container. (The default is false, and all devices are hot-pluggable) + ``` lxc config device add <instance> <device-name> proxy listen=<type>:<addr>:<port>[-<port>][,<port>] connect=<type>:<addr>:<port> bind=<host/instance> ```
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel