github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/interfaces/hotplug/deviceinfo.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 hotplug 21 22 import ( 23 "fmt" 24 "path/filepath" 25 26 "github.com/snapcore/snapd/dirs" 27 ) 28 29 type hotplugDeviceInfoData struct { 30 // map of all attributes returned for given uevent. 31 Data map[string]string `json:"data"` 32 } 33 34 // HotplugDeviceInfo carries information about added/removed device detected at runtime. 35 type HotplugDeviceInfo struct { 36 hotplugDeviceInfoData 37 } 38 39 // NewHotplugDeviceInfo creates HotplugDeviceInfo structure related to udev add or remove event. 40 func NewHotplugDeviceInfo(env map[string]string) (*HotplugDeviceInfo, error) { 41 if _, ok := env["DEVPATH"]; !ok { 42 return nil, fmt.Errorf("missing device path attribute") 43 } 44 return &HotplugDeviceInfo{ 45 hotplugDeviceInfoData: hotplugDeviceInfoData{Data: env}, 46 }, nil 47 } 48 49 // Returns the value of "SUBSYSTEM" attribute of the udev event associated with the device, e.g. "usb". 50 // Subsystem value is always present. 51 func (h *HotplugDeviceInfo) Subsystem() string { 52 return h.Data["SUBSYSTEM"] 53 } 54 55 // Returns full device path under /sysfs, e.g /sys/devices/pci0000:00/0000:00:14.0/usb1/1-2. 56 // The path is derived from DEVPATH attribute of the udev event. 57 func (h *HotplugDeviceInfo) DevicePath() string { 58 // DEVPATH is guaranteed to exist (checked in the ctor). 59 path, _ := h.Attribute("DEVPATH") 60 return filepath.Join(dirs.SysfsDir, path) 61 } 62 63 // Returns the value of "MINOR" attribute of the udev event associated with the device. 64 // The Minor value may be empty. 65 func (h *HotplugDeviceInfo) Minor() string { 66 return h.Data["MINOR"] 67 } 68 69 // Returns the value of "MAJOR" attribute of the udev event associated with the device. 70 // The Major value may be empty. 71 func (h *HotplugDeviceInfo) Major() string { 72 return h.Data["MAJOR"] 73 } 74 75 // Returns the value of "DEVNAME" attribute of the udev event associated with the device, e.g. "/dev/ttyUSB0". 76 // The DeviceName value may be empty. 77 func (h *HotplugDeviceInfo) DeviceName() string { 78 return h.Data["DEVNAME"] 79 } 80 81 // Returns the value of "DEVTYPE" attribute of the udev event associated with the device, e.g. "usb_device". 82 // The DeviceType value may be empty. 83 func (h *HotplugDeviceInfo) DeviceType() string { 84 return h.Data["DEVTYPE"] 85 } 86 87 // Generic method for getting arbitrary attribute from the uevent data. 88 func (h *HotplugDeviceInfo) Attribute(name string) (string, bool) { 89 val, ok := h.Data[name] 90 return val, ok 91 } 92 93 func (h *HotplugDeviceInfo) firstAttrValueOf(tryAttrs ...string) string { 94 for _, attr := range tryAttrs { 95 if val, _ := h.Attribute(attr); val != "" { 96 return val 97 } 98 } 99 return "" 100 } 101 102 func (h *HotplugDeviceInfo) String() string { 103 return h.str(70) 104 } 105 106 // ShortString returns a string representation of the device with more aggressive truncating of model/vendor name. 107 func (h *HotplugDeviceInfo) ShortString() string { 108 return h.str(16) 109 } 110 111 func (h *HotplugDeviceInfo) str(maxModelOrVendorLen int) string { 112 var nameOrPath string 113 114 // devname is the name of the device under /dev, eg. /dev/ttyS0; 115 // prefer devname over devpath as this is the one used to talk to the device. 116 if nameOrPath = h.DeviceName(); nameOrPath == "" { 117 // devpath is the path of the device under /sys, eg. /sys/devices/pnp0/00:04/tty/ttyS0. 118 nameOrPath = h.DevicePath() 119 } 120 121 modelOrVendor := h.firstAttrValueOf("ID_MODEL_FROM_DATABASE", "ID_MODEL", "ID_MODEL_ID", "ID_VENDOR_FROM_DATABASE", "ID_VENDOR", "ID_VENDOR_ID") 122 if len(modelOrVendor) > maxModelOrVendorLen { 123 modelOrVendor = modelOrVendor[0:maxModelOrVendorLen] + "…" 124 } 125 126 var serial string 127 if modelOrVendor != "" { 128 serial = h.firstAttrValueOf("ID_SERIAL_SHORT", "ID_SERIAL") 129 } else { 130 serial = h.firstAttrValueOf("ID_SERIAL", "ID_SERIAL_SHORT") 131 } 132 hasSerial := (serial != "" && serial != "noserial") 133 134 s := nameOrPath 135 if modelOrVendor != "" || hasSerial { 136 s += " (" 137 if modelOrVendor != "" { 138 s += modelOrVendor 139 if hasSerial { 140 s += "; " 141 } 142 } 143 if hasSerial { 144 s += "serial: " + serial 145 } 146 s += ")" 147 } 148 149 return s 150 }