diff --git a/nix/packages/nixos-facter/default.nix b/nix/packages/nixos-facter/default.nix index 1863819..72e71cd 100644 --- a/nix/packages/nixos-facter/default.nix +++ b/nix/packages/nixos-facter/default.nix @@ -38,7 +38,7 @@ in modules = ./gomod2nix.toml; buildInputs = [ - pkgs.libusb1 + pkgs.systemdMinimal.dev perSystem.hwinfo.default ]; @@ -48,9 +48,7 @@ in ]; runtimeInputs = with pkgs; [ - libusb1 util-linux - pciutils ]; ldflags = [ diff --git a/pkg/hwinfo/hardware.go b/pkg/hwinfo/hardware.go index fa5dd40..87ed5f5 100644 --- a/pkg/hwinfo/hardware.go +++ b/pkg/hwinfo/hardware.go @@ -191,6 +191,90 @@ const ( HardwareClassAll ) +// BaseClass values (superset of PCI classes) +// +//go:generate enumer -type=BaseClass -json -transform=snake -trimprefix BaseClass -output=./hardware_enum_base_class.go +type BaseClass uint16 + +// these *must* match standard PCI class numbers +const ( + BaseClassNone BaseClass = iota + BaseClassStorage + BaseClassNetwork + BaseClassDisplay + BaseClassMultimedia + + BaseClassMemory + BaseClassBridge + BaseClassComm + BaseClassSystem + BaseClassInput + BaseClassDocking + + BaseClassProcessor + BaseClassSerial + BaseClassWireless + BaseClassI2o + BaseClassOther BaseClass = 0xff +) + +// add our own base classes here (starting at 0x100 as PCI values are 8 bit) +const ( + BaseClassMonitor BaseClass = iota + 0x100 + BaseClassInternal + BaseClassModem + BaseClassIsdn + BaseClassPs2 + BaseClassMouse + + BaseClassStorageDevice + BaseClassNetworkInterface + BaseClassKeyboard + BaseClassPrinter + + BaseClassHub + BaseClassBraille + BaseClassScanner + BaseClassJoystick + BaseClassChipcard + BaseClassCamera + + BaseClassFramebuffer + BaseClassDvb + BaseClassTv + BaseClassPartition + BaseClassDsl + BaseClassBluetooth + BaseClassFingerprint + + BaseClassMmcController + BaseClassTouchpad +) + +// SubClassKeyboard values of BaseClassKeyboard +// +//go:generate enumer -type=SubClassKeyboard -json -transform=snake -trimprefix SubClassKeyboard -output=./hardware_enum_sub_class_keyboard.go +type SubClassKeyboard uint16 + +const ( + SubClassKeyboardKbd SubClassKeyboard = iota + SubClassKeyboardConsole +) + +// SubClassMouse values of BaseClassMouse +// +//go:generate enumer -type=SubClassMouse -json -transform=snake -trimprefix SubClassMouse -output=./hardware_enum_sub_class_mouse.go +type SubClassMouse uint16 + +const ( + SubClassMousePs2 SubClassMouse = iota + SubClassMouseSer + SubClassMouseBus + SubClassMouseUsb + SubClassMouseSun + SubClassMouseOther SubClassMouse = 0x80 +) + // Slot represents a bus and slot number. // Bits 0-7: slot number, 8-31 bus number type Slot uint @@ -260,6 +344,55 @@ const ( HotplugFirewire ) +// Bus types similar to PCI bridge subclasses +// +//go:generate enumer -type=Bus -json -transform=snake -trimprefix Bus -output=./hardware_enum_bus.go +type Bus uint16 + +const ( + BusNone Bus = iota + BusIsa + BusEisa + BusMc + BusPci + BusPcmcia + BusNubus + + BusCardbus + BusOther +) + +const ( + /** outside the range of the PCI values **/ + + BusPs2 Bus = iota + 0x80 + BusSerial + BusParallel + BusFloppy + BusScsi + BusIde + BusUsb + + BusAdb + BusRaid + BusSbus + BusI2o + BusVio + BusCcw + BusIucv + BusPs3SystemBus + + BusVirtio + BusIbmebus + BusGameport + BusUisvirtpci + BusMmc + BusSdio + BusNd + + BusNvme +) + // HardwareDevice represents a hardware component detected in the system. type HardwareDevice struct { // Index is a unique index provided by hwinfo, starting at 1 diff --git a/pkg/hwinfo/hardware_enum_base_class.go b/pkg/hwinfo/hardware_enum_base_class.go new file mode 100644 index 0000000..36bf292 --- /dev/null +++ b/pkg/hwinfo/hardware_enum_base_class.go @@ -0,0 +1,263 @@ +// Code generated by "enumer -type=BaseClass -json -transform=snake -trimprefix BaseClass -output=./hardware_enum_base_class.go"; DO NOT EDIT. + +package hwinfo + +import ( + "encoding/json" + "fmt" + "strings" +) + +const ( + _BaseClassName_0 = "nonestoragenetworkdisplaymultimediamemorybridgecommsysteminputdockingprocessorserialwirelessi2o" + _BaseClassLowerName_0 = "nonestoragenetworkdisplaymultimediamemorybridgecommsysteminputdockingprocessorserialwirelessi2o" + _BaseClassName_1 = "othermonitorinternalmodemisdnps2mousestorage_devicenetwork_interfacekeyboardprinterhubbraillescannerjoystickchipcardcameraframebufferdvbtvpartitiondslbluetoothfingerprintmmc_controllertouchpad" + _BaseClassLowerName_1 = "othermonitorinternalmodemisdnps2mousestorage_devicenetwork_interfacekeyboardprinterhubbraillescannerjoystickchipcardcameraframebufferdvbtvpartitiondslbluetoothfingerprintmmc_controllertouchpad" +) + +var ( + _BaseClassIndex_0 = [...]uint8{0, 4, 11, 18, 25, 35, 41, 47, 51, 57, 62, 69, 78, 84, 92, 95} + _BaseClassIndex_1 = [...]uint8{0, 5, 12, 20, 25, 29, 32, 37, 51, 68, 76, 83, 86, 93, 100, 108, 116, 122, 133, 136, 138, 147, 150, 159, 170, 184, 192} +) + +func (i BaseClass) String() string { + switch { + case 0 <= i && i <= 14: + return _BaseClassName_0[_BaseClassIndex_0[i]:_BaseClassIndex_0[i+1]] + case 255 <= i && i <= 280: + i -= 255 + return _BaseClassName_1[_BaseClassIndex_1[i]:_BaseClassIndex_1[i+1]] + default: + return fmt.Sprintf("BaseClass(%d)", i) + } +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _BaseClassNoOp() { + var x [1]struct{} + _ = x[BaseClassNone-(0)] + _ = x[BaseClassStorage-(1)] + _ = x[BaseClassNetwork-(2)] + _ = x[BaseClassDisplay-(3)] + _ = x[BaseClassMultimedia-(4)] + _ = x[BaseClassMemory-(5)] + _ = x[BaseClassBridge-(6)] + _ = x[BaseClassComm-(7)] + _ = x[BaseClassSystem-(8)] + _ = x[BaseClassInput-(9)] + _ = x[BaseClassDocking-(10)] + _ = x[BaseClassProcessor-(11)] + _ = x[BaseClassSerial-(12)] + _ = x[BaseClassWireless-(13)] + _ = x[BaseClassI2o-(14)] + _ = x[BaseClassOther-(255)] + _ = x[BaseClassMonitor-(256)] + _ = x[BaseClassInternal-(257)] + _ = x[BaseClassModem-(258)] + _ = x[BaseClassIsdn-(259)] + _ = x[BaseClassPs2-(260)] + _ = x[BaseClassMouse-(261)] + _ = x[BaseClassStorageDevice-(262)] + _ = x[BaseClassNetworkInterface-(263)] + _ = x[BaseClassKeyboard-(264)] + _ = x[BaseClassPrinter-(265)] + _ = x[BaseClassHub-(266)] + _ = x[BaseClassBraille-(267)] + _ = x[BaseClassScanner-(268)] + _ = x[BaseClassJoystick-(269)] + _ = x[BaseClassChipcard-(270)] + _ = x[BaseClassCamera-(271)] + _ = x[BaseClassFramebuffer-(272)] + _ = x[BaseClassDvb-(273)] + _ = x[BaseClassTv-(274)] + _ = x[BaseClassPartition-(275)] + _ = x[BaseClassDsl-(276)] + _ = x[BaseClassBluetooth-(277)] + _ = x[BaseClassFingerprint-(278)] + _ = x[BaseClassMmcController-(279)] + _ = x[BaseClassTouchpad-(280)] +} + +var _BaseClassValues = []BaseClass{BaseClassNone, BaseClassStorage, BaseClassNetwork, BaseClassDisplay, BaseClassMultimedia, BaseClassMemory, BaseClassBridge, BaseClassComm, BaseClassSystem, BaseClassInput, BaseClassDocking, BaseClassProcessor, BaseClassSerial, BaseClassWireless, BaseClassI2o, BaseClassOther, BaseClassMonitor, BaseClassInternal, BaseClassModem, BaseClassIsdn, BaseClassPs2, BaseClassMouse, BaseClassStorageDevice, BaseClassNetworkInterface, BaseClassKeyboard, BaseClassPrinter, BaseClassHub, BaseClassBraille, BaseClassScanner, BaseClassJoystick, BaseClassChipcard, BaseClassCamera, BaseClassFramebuffer, BaseClassDvb, BaseClassTv, BaseClassPartition, BaseClassDsl, BaseClassBluetooth, BaseClassFingerprint, BaseClassMmcController, BaseClassTouchpad} + +var _BaseClassNameToValueMap = map[string]BaseClass{ + _BaseClassName_0[0:4]: BaseClassNone, + _BaseClassLowerName_0[0:4]: BaseClassNone, + _BaseClassName_0[4:11]: BaseClassStorage, + _BaseClassLowerName_0[4:11]: BaseClassStorage, + _BaseClassName_0[11:18]: BaseClassNetwork, + _BaseClassLowerName_0[11:18]: BaseClassNetwork, + _BaseClassName_0[18:25]: BaseClassDisplay, + _BaseClassLowerName_0[18:25]: BaseClassDisplay, + _BaseClassName_0[25:35]: BaseClassMultimedia, + _BaseClassLowerName_0[25:35]: BaseClassMultimedia, + _BaseClassName_0[35:41]: BaseClassMemory, + _BaseClassLowerName_0[35:41]: BaseClassMemory, + _BaseClassName_0[41:47]: BaseClassBridge, + _BaseClassLowerName_0[41:47]: BaseClassBridge, + _BaseClassName_0[47:51]: BaseClassComm, + _BaseClassLowerName_0[47:51]: BaseClassComm, + _BaseClassName_0[51:57]: BaseClassSystem, + _BaseClassLowerName_0[51:57]: BaseClassSystem, + _BaseClassName_0[57:62]: BaseClassInput, + _BaseClassLowerName_0[57:62]: BaseClassInput, + _BaseClassName_0[62:69]: BaseClassDocking, + _BaseClassLowerName_0[62:69]: BaseClassDocking, + _BaseClassName_0[69:78]: BaseClassProcessor, + _BaseClassLowerName_0[69:78]: BaseClassProcessor, + _BaseClassName_0[78:84]: BaseClassSerial, + _BaseClassLowerName_0[78:84]: BaseClassSerial, + _BaseClassName_0[84:92]: BaseClassWireless, + _BaseClassLowerName_0[84:92]: BaseClassWireless, + _BaseClassName_0[92:95]: BaseClassI2o, + _BaseClassLowerName_0[92:95]: BaseClassI2o, + _BaseClassName_1[0:5]: BaseClassOther, + _BaseClassLowerName_1[0:5]: BaseClassOther, + _BaseClassName_1[5:12]: BaseClassMonitor, + _BaseClassLowerName_1[5:12]: BaseClassMonitor, + _BaseClassName_1[12:20]: BaseClassInternal, + _BaseClassLowerName_1[12:20]: BaseClassInternal, + _BaseClassName_1[20:25]: BaseClassModem, + _BaseClassLowerName_1[20:25]: BaseClassModem, + _BaseClassName_1[25:29]: BaseClassIsdn, + _BaseClassLowerName_1[25:29]: BaseClassIsdn, + _BaseClassName_1[29:32]: BaseClassPs2, + _BaseClassLowerName_1[29:32]: BaseClassPs2, + _BaseClassName_1[32:37]: BaseClassMouse, + _BaseClassLowerName_1[32:37]: BaseClassMouse, + _BaseClassName_1[37:51]: BaseClassStorageDevice, + _BaseClassLowerName_1[37:51]: BaseClassStorageDevice, + _BaseClassName_1[51:68]: BaseClassNetworkInterface, + _BaseClassLowerName_1[51:68]: BaseClassNetworkInterface, + _BaseClassName_1[68:76]: BaseClassKeyboard, + _BaseClassLowerName_1[68:76]: BaseClassKeyboard, + _BaseClassName_1[76:83]: BaseClassPrinter, + _BaseClassLowerName_1[76:83]: BaseClassPrinter, + _BaseClassName_1[83:86]: BaseClassHub, + _BaseClassLowerName_1[83:86]: BaseClassHub, + _BaseClassName_1[86:93]: BaseClassBraille, + _BaseClassLowerName_1[86:93]: BaseClassBraille, + _BaseClassName_1[93:100]: BaseClassScanner, + _BaseClassLowerName_1[93:100]: BaseClassScanner, + _BaseClassName_1[100:108]: BaseClassJoystick, + _BaseClassLowerName_1[100:108]: BaseClassJoystick, + _BaseClassName_1[108:116]: BaseClassChipcard, + _BaseClassLowerName_1[108:116]: BaseClassChipcard, + _BaseClassName_1[116:122]: BaseClassCamera, + _BaseClassLowerName_1[116:122]: BaseClassCamera, + _BaseClassName_1[122:133]: BaseClassFramebuffer, + _BaseClassLowerName_1[122:133]: BaseClassFramebuffer, + _BaseClassName_1[133:136]: BaseClassDvb, + _BaseClassLowerName_1[133:136]: BaseClassDvb, + _BaseClassName_1[136:138]: BaseClassTv, + _BaseClassLowerName_1[136:138]: BaseClassTv, + _BaseClassName_1[138:147]: BaseClassPartition, + _BaseClassLowerName_1[138:147]: BaseClassPartition, + _BaseClassName_1[147:150]: BaseClassDsl, + _BaseClassLowerName_1[147:150]: BaseClassDsl, + _BaseClassName_1[150:159]: BaseClassBluetooth, + _BaseClassLowerName_1[150:159]: BaseClassBluetooth, + _BaseClassName_1[159:170]: BaseClassFingerprint, + _BaseClassLowerName_1[159:170]: BaseClassFingerprint, + _BaseClassName_1[170:184]: BaseClassMmcController, + _BaseClassLowerName_1[170:184]: BaseClassMmcController, + _BaseClassName_1[184:192]: BaseClassTouchpad, + _BaseClassLowerName_1[184:192]: BaseClassTouchpad, +} + +var _BaseClassNames = []string{ + _BaseClassName_0[0:4], + _BaseClassName_0[4:11], + _BaseClassName_0[11:18], + _BaseClassName_0[18:25], + _BaseClassName_0[25:35], + _BaseClassName_0[35:41], + _BaseClassName_0[41:47], + _BaseClassName_0[47:51], + _BaseClassName_0[51:57], + _BaseClassName_0[57:62], + _BaseClassName_0[62:69], + _BaseClassName_0[69:78], + _BaseClassName_0[78:84], + _BaseClassName_0[84:92], + _BaseClassName_0[92:95], + _BaseClassName_1[0:5], + _BaseClassName_1[5:12], + _BaseClassName_1[12:20], + _BaseClassName_1[20:25], + _BaseClassName_1[25:29], + _BaseClassName_1[29:32], + _BaseClassName_1[32:37], + _BaseClassName_1[37:51], + _BaseClassName_1[51:68], + _BaseClassName_1[68:76], + _BaseClassName_1[76:83], + _BaseClassName_1[83:86], + _BaseClassName_1[86:93], + _BaseClassName_1[93:100], + _BaseClassName_1[100:108], + _BaseClassName_1[108:116], + _BaseClassName_1[116:122], + _BaseClassName_1[122:133], + _BaseClassName_1[133:136], + _BaseClassName_1[136:138], + _BaseClassName_1[138:147], + _BaseClassName_1[147:150], + _BaseClassName_1[150:159], + _BaseClassName_1[159:170], + _BaseClassName_1[170:184], + _BaseClassName_1[184:192], +} + +// BaseClassString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func BaseClassString(s string) (BaseClass, error) { + if val, ok := _BaseClassNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _BaseClassNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to BaseClass values", s) +} + +// BaseClassValues returns all values of the enum +func BaseClassValues() []BaseClass { + return _BaseClassValues +} + +// BaseClassStrings returns a slice of all String values of the enum +func BaseClassStrings() []string { + strs := make([]string, len(_BaseClassNames)) + copy(strs, _BaseClassNames) + return strs +} + +// IsABaseClass returns "true" if the value is listed in the enum definition. "false" otherwise +func (i BaseClass) IsABaseClass() bool { + for _, v := range _BaseClassValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for BaseClass +func (i BaseClass) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for BaseClass +func (i *BaseClass) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("BaseClass should be a string, got %s", data) + } + + var err error + *i, err = BaseClassString(s) + return err +} diff --git a/pkg/hwinfo/hardware_enum_bus.go b/pkg/hwinfo/hardware_enum_bus.go new file mode 100644 index 0000000..877ee25 --- /dev/null +++ b/pkg/hwinfo/hardware_enum_bus.go @@ -0,0 +1,227 @@ +// Code generated by "enumer -type=Bus -json -transform=snake -trimprefix Bus -output=./hardware_enum_bus.go"; DO NOT EDIT. + +package hwinfo + +import ( + "encoding/json" + "fmt" + "strings" +) + +const ( + _BusName_0 = "noneisaeisamcpcipcmcianubuscardbusother" + _BusLowerName_0 = "noneisaeisamcpcipcmcianubuscardbusother" + _BusName_1 = "ps2serialparallelfloppyscsiideusbadbraidsbusi2ovioccwiucvps3_system_busvirtioibmebusgameportuisvirtpcimmcsdiondnvme" + _BusLowerName_1 = "ps2serialparallelfloppyscsiideusbadbraidsbusi2ovioccwiucvps3_system_busvirtioibmebusgameportuisvirtpcimmcsdiondnvme" +) + +var ( + _BusIndex_0 = [...]uint8{0, 4, 7, 11, 13, 16, 22, 27, 34, 39} + _BusIndex_1 = [...]uint8{0, 3, 9, 17, 23, 27, 30, 33, 36, 40, 44, 47, 50, 53, 57, 71, 77, 84, 92, 102, 105, 109, 111, 115} +) + +func (i Bus) String() string { + switch { + case 0 <= i && i <= 8: + return _BusName_0[_BusIndex_0[i]:_BusIndex_0[i+1]] + case 128 <= i && i <= 150: + i -= 128 + return _BusName_1[_BusIndex_1[i]:_BusIndex_1[i+1]] + default: + return fmt.Sprintf("Bus(%d)", i) + } +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _BusNoOp() { + var x [1]struct{} + _ = x[BusNone-(0)] + _ = x[BusIsa-(1)] + _ = x[BusEisa-(2)] + _ = x[BusMc-(3)] + _ = x[BusPci-(4)] + _ = x[BusPcmcia-(5)] + _ = x[BusNubus-(6)] + _ = x[BusCardbus-(7)] + _ = x[BusOther-(8)] + _ = x[BusPs2-(128)] + _ = x[BusSerial-(129)] + _ = x[BusParallel-(130)] + _ = x[BusFloppy-(131)] + _ = x[BusScsi-(132)] + _ = x[BusIde-(133)] + _ = x[BusUsb-(134)] + _ = x[BusAdb-(135)] + _ = x[BusRaid-(136)] + _ = x[BusSbus-(137)] + _ = x[BusI2o-(138)] + _ = x[BusVio-(139)] + _ = x[BusCcw-(140)] + _ = x[BusIucv-(141)] + _ = x[BusPs3SystemBus-(142)] + _ = x[BusVirtio-(143)] + _ = x[BusIbmebus-(144)] + _ = x[BusGameport-(145)] + _ = x[BusUisvirtpci-(146)] + _ = x[BusMmc-(147)] + _ = x[BusSdio-(148)] + _ = x[BusNd-(149)] + _ = x[BusNvme-(150)] +} + +var _BusValues = []Bus{BusNone, BusIsa, BusEisa, BusMc, BusPci, BusPcmcia, BusNubus, BusCardbus, BusOther, BusPs2, BusSerial, BusParallel, BusFloppy, BusScsi, BusIde, BusUsb, BusAdb, BusRaid, BusSbus, BusI2o, BusVio, BusCcw, BusIucv, BusPs3SystemBus, BusVirtio, BusIbmebus, BusGameport, BusUisvirtpci, BusMmc, BusSdio, BusNd, BusNvme} + +var _BusNameToValueMap = map[string]Bus{ + _BusName_0[0:4]: BusNone, + _BusLowerName_0[0:4]: BusNone, + _BusName_0[4:7]: BusIsa, + _BusLowerName_0[4:7]: BusIsa, + _BusName_0[7:11]: BusEisa, + _BusLowerName_0[7:11]: BusEisa, + _BusName_0[11:13]: BusMc, + _BusLowerName_0[11:13]: BusMc, + _BusName_0[13:16]: BusPci, + _BusLowerName_0[13:16]: BusPci, + _BusName_0[16:22]: BusPcmcia, + _BusLowerName_0[16:22]: BusPcmcia, + _BusName_0[22:27]: BusNubus, + _BusLowerName_0[22:27]: BusNubus, + _BusName_0[27:34]: BusCardbus, + _BusLowerName_0[27:34]: BusCardbus, + _BusName_0[34:39]: BusOther, + _BusLowerName_0[34:39]: BusOther, + _BusName_1[0:3]: BusPs2, + _BusLowerName_1[0:3]: BusPs2, + _BusName_1[3:9]: BusSerial, + _BusLowerName_1[3:9]: BusSerial, + _BusName_1[9:17]: BusParallel, + _BusLowerName_1[9:17]: BusParallel, + _BusName_1[17:23]: BusFloppy, + _BusLowerName_1[17:23]: BusFloppy, + _BusName_1[23:27]: BusScsi, + _BusLowerName_1[23:27]: BusScsi, + _BusName_1[27:30]: BusIde, + _BusLowerName_1[27:30]: BusIde, + _BusName_1[30:33]: BusUsb, + _BusLowerName_1[30:33]: BusUsb, + _BusName_1[33:36]: BusAdb, + _BusLowerName_1[33:36]: BusAdb, + _BusName_1[36:40]: BusRaid, + _BusLowerName_1[36:40]: BusRaid, + _BusName_1[40:44]: BusSbus, + _BusLowerName_1[40:44]: BusSbus, + _BusName_1[44:47]: BusI2o, + _BusLowerName_1[44:47]: BusI2o, + _BusName_1[47:50]: BusVio, + _BusLowerName_1[47:50]: BusVio, + _BusName_1[50:53]: BusCcw, + _BusLowerName_1[50:53]: BusCcw, + _BusName_1[53:57]: BusIucv, + _BusLowerName_1[53:57]: BusIucv, + _BusName_1[57:71]: BusPs3SystemBus, + _BusLowerName_1[57:71]: BusPs3SystemBus, + _BusName_1[71:77]: BusVirtio, + _BusLowerName_1[71:77]: BusVirtio, + _BusName_1[77:84]: BusIbmebus, + _BusLowerName_1[77:84]: BusIbmebus, + _BusName_1[84:92]: BusGameport, + _BusLowerName_1[84:92]: BusGameport, + _BusName_1[92:102]: BusUisvirtpci, + _BusLowerName_1[92:102]: BusUisvirtpci, + _BusName_1[102:105]: BusMmc, + _BusLowerName_1[102:105]: BusMmc, + _BusName_1[105:109]: BusSdio, + _BusLowerName_1[105:109]: BusSdio, + _BusName_1[109:111]: BusNd, + _BusLowerName_1[109:111]: BusNd, + _BusName_1[111:115]: BusNvme, + _BusLowerName_1[111:115]: BusNvme, +} + +var _BusNames = []string{ + _BusName_0[0:4], + _BusName_0[4:7], + _BusName_0[7:11], + _BusName_0[11:13], + _BusName_0[13:16], + _BusName_0[16:22], + _BusName_0[22:27], + _BusName_0[27:34], + _BusName_0[34:39], + _BusName_1[0:3], + _BusName_1[3:9], + _BusName_1[9:17], + _BusName_1[17:23], + _BusName_1[23:27], + _BusName_1[27:30], + _BusName_1[30:33], + _BusName_1[33:36], + _BusName_1[36:40], + _BusName_1[40:44], + _BusName_1[44:47], + _BusName_1[47:50], + _BusName_1[50:53], + _BusName_1[53:57], + _BusName_1[57:71], + _BusName_1[71:77], + _BusName_1[77:84], + _BusName_1[84:92], + _BusName_1[92:102], + _BusName_1[102:105], + _BusName_1[105:109], + _BusName_1[109:111], + _BusName_1[111:115], +} + +// BusString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func BusString(s string) (Bus, error) { + if val, ok := _BusNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _BusNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Bus values", s) +} + +// BusValues returns all values of the enum +func BusValues() []Bus { + return _BusValues +} + +// BusStrings returns a slice of all String values of the enum +func BusStrings() []string { + strs := make([]string, len(_BusNames)) + copy(strs, _BusNames) + return strs +} + +// IsABus returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Bus) IsABus() bool { + for _, v := range _BusValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for Bus +func (i Bus) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for Bus +func (i *Bus) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("Bus should be a string, got %s", data) + } + + var err error + *i, err = BusString(s) + return err +} diff --git a/pkg/hwinfo/hardware_enum_sub_class_keyboard.go b/pkg/hwinfo/hardware_enum_sub_class_keyboard.go new file mode 100644 index 0000000..66eb977 --- /dev/null +++ b/pkg/hwinfo/hardware_enum_sub_class_keyboard.go @@ -0,0 +1,96 @@ +// Code generated by "enumer -type=SubClassKeyboard -json -transform=snake -trimprefix SubClassKeyboard -output=./hardware_enum_sub_class_keyboard.go"; DO NOT EDIT. + +package hwinfo + +import ( + "encoding/json" + "fmt" + "strings" +) + +const _SubClassKeyboardName = "kbdconsole" + +var _SubClassKeyboardIndex = [...]uint8{0, 3, 10} + +const _SubClassKeyboardLowerName = "kbdconsole" + +func (i SubClassKeyboard) String() string { + if i >= SubClassKeyboard(len(_SubClassKeyboardIndex)-1) { + return fmt.Sprintf("SubClassKeyboard(%d)", i) + } + return _SubClassKeyboardName[_SubClassKeyboardIndex[i]:_SubClassKeyboardIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _SubClassKeyboardNoOp() { + var x [1]struct{} + _ = x[SubClassKeyboardKbd-(0)] + _ = x[SubClassKeyboardConsole-(1)] +} + +var _SubClassKeyboardValues = []SubClassKeyboard{SubClassKeyboardKbd, SubClassKeyboardConsole} + +var _SubClassKeyboardNameToValueMap = map[string]SubClassKeyboard{ + _SubClassKeyboardName[0:3]: SubClassKeyboardKbd, + _SubClassKeyboardLowerName[0:3]: SubClassKeyboardKbd, + _SubClassKeyboardName[3:10]: SubClassKeyboardConsole, + _SubClassKeyboardLowerName[3:10]: SubClassKeyboardConsole, +} + +var _SubClassKeyboardNames = []string{ + _SubClassKeyboardName[0:3], + _SubClassKeyboardName[3:10], +} + +// SubClassKeyboardString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func SubClassKeyboardString(s string) (SubClassKeyboard, error) { + if val, ok := _SubClassKeyboardNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _SubClassKeyboardNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to SubClassKeyboard values", s) +} + +// SubClassKeyboardValues returns all values of the enum +func SubClassKeyboardValues() []SubClassKeyboard { + return _SubClassKeyboardValues +} + +// SubClassKeyboardStrings returns a slice of all String values of the enum +func SubClassKeyboardStrings() []string { + strs := make([]string, len(_SubClassKeyboardNames)) + copy(strs, _SubClassKeyboardNames) + return strs +} + +// IsASubClassKeyboard returns "true" if the value is listed in the enum definition. "false" otherwise +func (i SubClassKeyboard) IsASubClassKeyboard() bool { + for _, v := range _SubClassKeyboardValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for SubClassKeyboard +func (i SubClassKeyboard) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for SubClassKeyboard +func (i *SubClassKeyboard) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("SubClassKeyboard should be a string, got %s", data) + } + + var err error + *i, err = SubClassKeyboardString(s) + return err +} diff --git a/pkg/hwinfo/hardware_enum_sub_class_mouse.go b/pkg/hwinfo/hardware_enum_sub_class_mouse.go new file mode 100644 index 0000000..c2f2f86 --- /dev/null +++ b/pkg/hwinfo/hardware_enum_sub_class_mouse.go @@ -0,0 +1,122 @@ +// Code generated by "enumer -type=SubClassMouse -json -transform=snake -trimprefix SubClassMouse -output=./hardware_enum_sub_class_mouse.go"; DO NOT EDIT. + +package hwinfo + +import ( + "encoding/json" + "fmt" + "strings" +) + +const ( + _SubClassMouseName_0 = "ps2serbususbsun" + _SubClassMouseLowerName_0 = "ps2serbususbsun" + _SubClassMouseName_1 = "other" + _SubClassMouseLowerName_1 = "other" +) + +var ( + _SubClassMouseIndex_0 = [...]uint8{0, 3, 6, 9, 12, 15} + _SubClassMouseIndex_1 = [...]uint8{0, 5} +) + +func (i SubClassMouse) String() string { + switch { + case 0 <= i && i <= 4: + return _SubClassMouseName_0[_SubClassMouseIndex_0[i]:_SubClassMouseIndex_0[i+1]] + case i == 128: + return _SubClassMouseName_1 + default: + return fmt.Sprintf("SubClassMouse(%d)", i) + } +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _SubClassMouseNoOp() { + var x [1]struct{} + _ = x[SubClassMousePs2-(0)] + _ = x[SubClassMouseSer-(1)] + _ = x[SubClassMouseBus-(2)] + _ = x[SubClassMouseUsb-(3)] + _ = x[SubClassMouseSun-(4)] + _ = x[SubClassMouseOther-(128)] +} + +var _SubClassMouseValues = []SubClassMouse{SubClassMousePs2, SubClassMouseSer, SubClassMouseBus, SubClassMouseUsb, SubClassMouseSun, SubClassMouseOther} + +var _SubClassMouseNameToValueMap = map[string]SubClassMouse{ + _SubClassMouseName_0[0:3]: SubClassMousePs2, + _SubClassMouseLowerName_0[0:3]: SubClassMousePs2, + _SubClassMouseName_0[3:6]: SubClassMouseSer, + _SubClassMouseLowerName_0[3:6]: SubClassMouseSer, + _SubClassMouseName_0[6:9]: SubClassMouseBus, + _SubClassMouseLowerName_0[6:9]: SubClassMouseBus, + _SubClassMouseName_0[9:12]: SubClassMouseUsb, + _SubClassMouseLowerName_0[9:12]: SubClassMouseUsb, + _SubClassMouseName_0[12:15]: SubClassMouseSun, + _SubClassMouseLowerName_0[12:15]: SubClassMouseSun, + _SubClassMouseName_1[0:5]: SubClassMouseOther, + _SubClassMouseLowerName_1[0:5]: SubClassMouseOther, +} + +var _SubClassMouseNames = []string{ + _SubClassMouseName_0[0:3], + _SubClassMouseName_0[3:6], + _SubClassMouseName_0[6:9], + _SubClassMouseName_0[9:12], + _SubClassMouseName_0[12:15], + _SubClassMouseName_1[0:5], +} + +// SubClassMouseString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func SubClassMouseString(s string) (SubClassMouse, error) { + if val, ok := _SubClassMouseNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _SubClassMouseNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to SubClassMouse values", s) +} + +// SubClassMouseValues returns all values of the enum +func SubClassMouseValues() []SubClassMouse { + return _SubClassMouseValues +} + +// SubClassMouseStrings returns a slice of all String values of the enum +func SubClassMouseStrings() []string { + strs := make([]string, len(_SubClassMouseNames)) + copy(strs, _SubClassMouseNames) + return strs +} + +// IsASubClassMouse returns "true" if the value is listed in the enum definition. "false" otherwise +func (i SubClassMouse) IsASubClassMouse() bool { + for _, v := range _SubClassMouseValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for SubClassMouse +func (i SubClassMouse) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for SubClassMouse +func (i *SubClassMouse) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("SubClassMouse should be a string, got %s", data) + } + + var err error + *i, err = SubClassMouseString(s) + return err +} diff --git a/pkg/hwinfo/hwinfo.go b/pkg/hwinfo/hwinfo.go index b3216af..817a889 100644 --- a/pkg/hwinfo/hwinfo.go +++ b/pkg/hwinfo/hwinfo.go @@ -60,12 +60,17 @@ func Scan(probes []ProbeFeature) ([]Smbios, []HardwareDevice, error) { } var hardwareItems []HardwareDevice + var deviceIdx uint for hd := data.hd; hd != nil; hd = hd.next { item, err := NewHardwareDevice(hd) if err != nil { return nil, nil, err } + if item.Index > deviceIdx { + deviceIdx = item.Index + } + if excludeDevice(item) { continue } @@ -73,5 +78,13 @@ func Scan(probes []ProbeFeature) ([]Smbios, []HardwareDevice, error) { hardwareItems = append(hardwareItems, *item) } + // probe for additional inputs that hwinfo does not support + touchpads, err := captureTouchpads(deviceIdx + 1) + if err != nil { + return nil, nil, err + } + + hardwareItems = append(hardwareItems, touchpads...) + return smbiosItems, hardwareItems, nil } diff --git a/pkg/hwinfo/id.go b/pkg/hwinfo/id.go index b5a29a2..c8b0e27 100644 --- a/pkg/hwinfo/id.go +++ b/pkg/hwinfo/id.go @@ -83,3 +83,17 @@ func NewID(id C.hd_id_t) *ID { } return &result } + +func NewBusID(bus Bus) *ID { + return &ID{ + Name: bus.String(), + Value: uint16(bus), + } +} + +func NewBaseClassID(bc BaseClass) *ID { + return &ID{ + Name: bc.String(), + Value: uint16(bc), + } +} diff --git a/pkg/hwinfo/input.go b/pkg/hwinfo/input.go new file mode 100644 index 0000000..5d61108 --- /dev/null +++ b/pkg/hwinfo/input.go @@ -0,0 +1,109 @@ +package hwinfo + +import ( + "fmt" + + "github.com/numtide/nixos-facter/pkg/linux/input" +) + +// captureTouchpads scans the input devices and identifies touchpads, returning a slice of HardwareDevice structs or an +// error. +// It accepts a deviceIdx to ensure it continues on from the last device index generated by hwinfo. +func captureTouchpads(deviceIdx uint) ([]HardwareDevice, error) { + inputDevices, err := input.ReadDevices(nil, true) + if err != nil { + return nil, fmt.Errorf("failed to read input devices: %w", err) + } + + var result []HardwareDevice //nolint:prealloc + + for _, inputDevice := range inputDevices { + if !inputDevice.Udev.Input.IsTouchpad { + // currently, we are only interested in touchpads, as hwinfo does not capture them + // eventually, we may take over more of the input processing that hwinfo performs + continue + } + + if len(inputDevice.Handlers) == 0 { + // I believe this shouldn't be possible, and if it does occur, we should error + return nil, fmt.Errorf("no handlers found for input device %s", inputDevice.Sysfs) + } + + // create a hardware entry for the report + hd := HardwareDevice{ + // todo AttachedTo: it's unclear how to work this out + Class: HardwareClassMouse, + BaseClass: NewBaseClassID(BaseClassTouchpad), + Vendor: &ID{ + Name: inputDevice.Udev.Vendor, + Value: inputDevice.Vendor, + }, + Device: &ID{ + Name: inputDevice.Udev.Model, + Value: inputDevice.Product, + }, + SysfsID: inputDevice.Sysfs, + } + + switch inputDevice.Bus { + case input.BusI2c: + hd.BusType = NewBusID(BusSerial) + hd.SubClass = &ID{ + Name: SubClassMouseBus.String(), + Value: uint16(SubClassMouseSer), + } + + case input.BusUsb: + hd.BusType = NewBusID(BusUsb) + hd.SubClass = &ID{ + Name: SubClassMouseUsb.String(), + Value: uint16(SubClassMouseUsb), + } + case input.BusI8042: + hd.BusType = NewBusID(BusPs2) + hd.SubClass = &ID{ + Name: SubClassMousePs2.String(), + Value: uint16(SubClassMousePs2), + } + + case input.BusPci, + input.BusIsapnp, + input.BusHil, + input.BusBluetooth, + input.BusVirtual, + input.BusIsa, + input.BusXtkbd, + input.BusRs232, + input.BusGameport, + input.BusParport, + input.BusAmiga, + input.BusAdb, + input.BusHost, + input.BusGsc, + input.BusAtari, + input.BusSpi, + input.BusRmi, + input.BusCec, + input.BusIntelIshtp, + input.BusAmdSfh: + // todo unsure if touchpads can be on any other bus type + return nil, fmt.Errorf("unsupported bus type: %s", inputDevice.Bus) + } + + // todo should we error if no event handler is found? + if handler := inputDevice.EventHandler(); handler != "" { + hd.UnixDeviceNames = append(hd.UnixDeviceNames, fmt.Sprintf("/dev/input/%s", handler)) + } + + if handler := inputDevice.MouseHandler(); handler != "" { + hd.UnixDeviceNames = append(hd.UnixDeviceNames, fmt.Sprintf("/dev/input/%s", handler)) + } + + hd.Index = deviceIdx + result = append(result, hd) + + deviceIdx++ + } + + return result, nil +} diff --git a/pkg/linux/input/input.go b/pkg/linux/input/input.go new file mode 100644 index 0000000..666ebe1 --- /dev/null +++ b/pkg/linux/input/input.go @@ -0,0 +1,240 @@ +package input + +import ( + "bufio" + "errors" + "fmt" + "io" + "log/slog" + "os" + "regexp" + "strconv" + "strings" + + "github.com/numtide/nixos-facter/pkg/udev" +) + +//go:generate enumer -type=Bus -json -text -trimprefix Bus -output=./input_bus.go +type Bus uint16 + +// Codes taken from +// https://github.com/torvalds/linux/blob/cfaaa7d010d1fc58f9717fcc8591201e741d2d49/include/uapi/linux/input.h#L254 +const ( + BusPci Bus = iota + 1 + BusIsapnp + BusUsb + BusHil + BusBluetooth + BusVirtual + + BusIsa Bus = iota + 10 + BusI8042 + BusXtkbd + BusRs232 + BusGameport + BusParport + BusAmiga + BusAdb + BusI2c + BusHost + BusGsc + BusAtari + BusSpi + BusRmi + BusCec + BusIntelIshtp + BusAmdSfh +) + +const ( + devicesPath = "/proc/bus/input/devices" +) + +var ( + capRegex = regexp.MustCompile(`^B: (\w+)=(.*)$`) + nameRegex = regexp.MustCompile(`^N: Name="(.*)"$`) + physRegex = regexp.MustCompile(`^P: Phys=(.*)$`) + sysfsRegex = regexp.MustCompile(`^S: Sysfs=(.*)$`) + basicRegex = regexp.MustCompile( + `^I: Bus=([0-9abcdef]{4}) Vendor=([0-9abcdef]{4}) Product=([0-9abcdef]{4}) Version=([0-9abcdef]{4})$`, + ) + + handlersRegex = regexp.MustCompile(`^H: Handlers=(.*)`) + mouseHandlerRegex = regexp.MustCompile(`mouse\d+`) + eventHandlerRegex = regexp.MustCompile(`event\d+`) +) + +type Device struct { + Bus Bus + Vendor uint16 + Product uint16 + Version uint16 + Name string + Handlers []string + Sysfs string + Phys string + Capabilities map[string]string + Udev *udev.Udev +} + +func (d *Device) Path() string { + return "/dev/input/" + d.Name +} + +func (d *Device) MouseHandler() string { + for _, handler := range d.Handlers { + if mouseHandlerRegex.MatchString(handler) { + return handler + } + } + + return "" +} + +func (d *Device) EventHandler() string { + for _, handler := range d.Handlers { + if eventHandlerRegex.MatchString(handler) { + return handler + } + } + + return "" +} + +func ReadDevices(r io.ReadCloser, udevAnnotate bool) ([]*Device, error) { + var err error + + if r == nil { + r, err = os.Open(devicesPath) + if err != nil { + return nil, fmt.Errorf("failed to open %s: %w", devicesPath, err) + } + defer r.Close() + } + + var ( + device *Device + devices []*Device + ) + + scanner := bufio.NewScanner(r) + + for scanner.Scan() { + line := scanner.Text() + + if line == "" { + // try to append udev data + if udevAnnotate { + device.Udev, err = udev.Read("/sys" + device.Sysfs) + + if errors.Is(err, udev.ErrNotFound) { + slog.Warn("udev data not found", "name", device.Name, "sysfs", device.Sysfs) + } else if err != nil { + return nil, fmt.Errorf("failed to annotate with udev data: %s", err) + } + } + + devices = append(devices, device) + device = nil + + continue + } + + if device == nil { + device = &Device{ + Capabilities: make(map[string]string), + } + } + + switch line[:3] { + case "I: ": + if err := readBasicInfo(line, device); err != nil { + return nil, fmt.Errorf("failed to read basic info: %s", err) + } + case "N: ": + matches := nameRegex.FindStringSubmatch(line) + if len(matches) != 2 { + return nil, fmt.Errorf("invalid name: %s", line) + } + + device.Name = matches[1] + + case "P: ": + matches := physRegex.FindStringSubmatch(line) + if len(matches) != 2 { + return nil, fmt.Errorf("invalid phys: %s", line) + } + + device.Phys = matches[1] + + case "S: ": + matches := sysfsRegex.FindStringSubmatch(line) + if len(matches) != 2 { + return nil, fmt.Errorf("invalid sysfs: %s", line) + } + + device.Sysfs = matches[1] + + case "B: ": + matches := capRegex.FindStringSubmatch(line) + if len(matches) != 3 { + return nil, fmt.Errorf("invalid capability: %s", line) + } + + device.Capabilities[matches[1]] = matches[2] + + case "H: ": + matches := handlersRegex.FindStringSubmatch(line) + if len(matches) != 2 { + return nil, fmt.Errorf("invalid handlers: %s", line) + } + + device.Handlers = strings.Split( + strings.Trim(matches[1], " "), + " ", + ) + + default: // do nothing + } + } + + return devices, nil +} + +func readBasicInfo(line string, device *Device) error { + matches := basicRegex.FindStringSubmatch(line) + + if len(matches) != 5 { + return fmt.Errorf("invalid basic info: %s", line) + } + + bus, err := strconv.ParseUint(matches[1], 16, 16) + if err != nil { + return fmt.Errorf("invalid bus: %s", matches[1]) + } + + device.Bus = Bus(bus) + + vendor, err := strconv.ParseUint(matches[2], 16, 16) + if err != nil { + return fmt.Errorf("invalid vendor: %s", matches[2]) + } + + device.Vendor = uint16(vendor) + + product, err := strconv.ParseUint(matches[3], 16, 16) + if err != nil { + return fmt.Errorf("invalid product: %s", matches[3]) + } + + device.Product = uint16(product) + + version, err := strconv.ParseUint(matches[4], 16, 16) + if err != nil { + return fmt.Errorf("invalid version: %s", matches[4]) + } + + device.Version = uint16(version) + + return nil +} diff --git a/pkg/linux/input/input_bus.go b/pkg/linux/input/input_bus.go new file mode 100644 index 0000000..423cea8 --- /dev/null +++ b/pkg/linux/input/input_bus.go @@ -0,0 +1,204 @@ +// Code generated by "enumer -type=Bus -json -text -trimprefix Bus -output=./input_bus.go"; DO NOT EDIT. + +package input + +import ( + "encoding/json" + "fmt" + "strings" +) + +const ( + _BusName_0 = "PciIsapnpUsbHilBluetoothVirtual" + _BusLowerName_0 = "pciisapnpusbhilbluetoothvirtual" + _BusName_1 = "IsaI8042XtkbdRs232GameportParportAmigaAdbI2cHostGscAtariSpiRmiCecIntelIshtpAmdSfh" + _BusLowerName_1 = "isai8042xtkbdrs232gameportparportamigaadbi2chostgscatarispirmicecintelishtpamdsfh" +) + +var ( + _BusIndex_0 = [...]uint8{0, 3, 9, 12, 15, 24, 31} + _BusIndex_1 = [...]uint8{0, 3, 8, 13, 18, 26, 33, 38, 41, 44, 48, 51, 56, 59, 62, 65, 75, 81} +) + +func (i Bus) String() string { + switch { + case 1 <= i && i <= 6: + i -= 1 + return _BusName_0[_BusIndex_0[i]:_BusIndex_0[i+1]] + case 16 <= i && i <= 32: + i -= 16 + return _BusName_1[_BusIndex_1[i]:_BusIndex_1[i+1]] + default: + return fmt.Sprintf("Bus(%d)", i) + } +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _BusNoOp() { + var x [1]struct{} + _ = x[BusPci-(1)] + _ = x[BusIsapnp-(2)] + _ = x[BusUsb-(3)] + _ = x[BusHil-(4)] + _ = x[BusBluetooth-(5)] + _ = x[BusVirtual-(6)] + _ = x[BusIsa-(16)] + _ = x[BusI8042-(17)] + _ = x[BusXtkbd-(18)] + _ = x[BusRs232-(19)] + _ = x[BusGameport-(20)] + _ = x[BusParport-(21)] + _ = x[BusAmiga-(22)] + _ = x[BusAdb-(23)] + _ = x[BusI2c-(24)] + _ = x[BusHost-(25)] + _ = x[BusGsc-(26)] + _ = x[BusAtari-(27)] + _ = x[BusSpi-(28)] + _ = x[BusRmi-(29)] + _ = x[BusCec-(30)] + _ = x[BusIntelIshtp-(31)] + _ = x[BusAmdSfh-(32)] +} + +var _BusValues = []Bus{BusPci, BusIsapnp, BusUsb, BusHil, BusBluetooth, BusVirtual, BusIsa, BusI8042, BusXtkbd, BusRs232, BusGameport, BusParport, BusAmiga, BusAdb, BusI2c, BusHost, BusGsc, BusAtari, BusSpi, BusRmi, BusCec, BusIntelIshtp, BusAmdSfh} + +var _BusNameToValueMap = map[string]Bus{ + _BusName_0[0:3]: BusPci, + _BusLowerName_0[0:3]: BusPci, + _BusName_0[3:9]: BusIsapnp, + _BusLowerName_0[3:9]: BusIsapnp, + _BusName_0[9:12]: BusUsb, + _BusLowerName_0[9:12]: BusUsb, + _BusName_0[12:15]: BusHil, + _BusLowerName_0[12:15]: BusHil, + _BusName_0[15:24]: BusBluetooth, + _BusLowerName_0[15:24]: BusBluetooth, + _BusName_0[24:31]: BusVirtual, + _BusLowerName_0[24:31]: BusVirtual, + _BusName_1[0:3]: BusIsa, + _BusLowerName_1[0:3]: BusIsa, + _BusName_1[3:8]: BusI8042, + _BusLowerName_1[3:8]: BusI8042, + _BusName_1[8:13]: BusXtkbd, + _BusLowerName_1[8:13]: BusXtkbd, + _BusName_1[13:18]: BusRs232, + _BusLowerName_1[13:18]: BusRs232, + _BusName_1[18:26]: BusGameport, + _BusLowerName_1[18:26]: BusGameport, + _BusName_1[26:33]: BusParport, + _BusLowerName_1[26:33]: BusParport, + _BusName_1[33:38]: BusAmiga, + _BusLowerName_1[33:38]: BusAmiga, + _BusName_1[38:41]: BusAdb, + _BusLowerName_1[38:41]: BusAdb, + _BusName_1[41:44]: BusI2c, + _BusLowerName_1[41:44]: BusI2c, + _BusName_1[44:48]: BusHost, + _BusLowerName_1[44:48]: BusHost, + _BusName_1[48:51]: BusGsc, + _BusLowerName_1[48:51]: BusGsc, + _BusName_1[51:56]: BusAtari, + _BusLowerName_1[51:56]: BusAtari, + _BusName_1[56:59]: BusSpi, + _BusLowerName_1[56:59]: BusSpi, + _BusName_1[59:62]: BusRmi, + _BusLowerName_1[59:62]: BusRmi, + _BusName_1[62:65]: BusCec, + _BusLowerName_1[62:65]: BusCec, + _BusName_1[65:75]: BusIntelIshtp, + _BusLowerName_1[65:75]: BusIntelIshtp, + _BusName_1[75:81]: BusAmdSfh, + _BusLowerName_1[75:81]: BusAmdSfh, +} + +var _BusNames = []string{ + _BusName_0[0:3], + _BusName_0[3:9], + _BusName_0[9:12], + _BusName_0[12:15], + _BusName_0[15:24], + _BusName_0[24:31], + _BusName_1[0:3], + _BusName_1[3:8], + _BusName_1[8:13], + _BusName_1[13:18], + _BusName_1[18:26], + _BusName_1[26:33], + _BusName_1[33:38], + _BusName_1[38:41], + _BusName_1[41:44], + _BusName_1[44:48], + _BusName_1[48:51], + _BusName_1[51:56], + _BusName_1[56:59], + _BusName_1[59:62], + _BusName_1[62:65], + _BusName_1[65:75], + _BusName_1[75:81], +} + +// BusString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func BusString(s string) (Bus, error) { + if val, ok := _BusNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _BusNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Bus values", s) +} + +// BusValues returns all values of the enum +func BusValues() []Bus { + return _BusValues +} + +// BusStrings returns a slice of all String values of the enum +func BusStrings() []string { + strs := make([]string, len(_BusNames)) + copy(strs, _BusNames) + return strs +} + +// IsABus returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Bus) IsABus() bool { + for _, v := range _BusValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for Bus +func (i Bus) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for Bus +func (i *Bus) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("Bus should be a string, got %s", data) + } + + var err error + *i, err = BusString(s) + return err +} + +// MarshalText implements the encoding.TextMarshaler interface for Bus +func (i Bus) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for Bus +func (i *Bus) UnmarshalText(text []byte) error { + var err error + *i, err = BusString(string(text)) + return err +} diff --git a/pkg/linux/input/input_test.go b/pkg/linux/input/input_test.go new file mode 100644 index 0000000..0ae61f2 --- /dev/null +++ b/pkg/linux/input/input_test.go @@ -0,0 +1,119 @@ +//nolint:lll +package input_test + +import ( + "bytes" + "io" + "testing" + + "github.com/numtide/nixos-facter/pkg/linux/input" + "github.com/stretchr/testify/require" +) + +const ( + devices = `I: Bus=0003 Vendor=1038 Product=1634 Version=0111 +N: Name="SteelSeries SteelSeries Apex 9 TKL" +P: Phys=usb-0000:08:00.3-2.4.2/input2 +S: Sysfs=/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:08:00.3/usb3/3-2/3-2.4/3-2.4.2/3-2.4.2:1.2/0003:1038:1634.000B/input/input11 +U: Uniq= +H: Handlers=sysrq kbd leds event4 +B: PROP=0 +B: EV=120013 +B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe +B: MSC=10 +B: LED=7 + +I: Bus=0003 Vendor=046d Product=407b Version=0111 +N: Name="Logitech MX Vertical" +P: Phys=usb-0000:08:00.3-2.2/input2:1 +S: Sysfs=/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:08:00.3/usb3/3-2/3-2.2/3-2.2:1.2/0003:046D:C52B.0004/0003:046D:407B.0005/input/input8 +U: Uniq=5c-c6-5d-d4 +H: Handlers=sysrq kbd leds event1 mouse0 +B: PROP=0 +B: EV=12001f +B: KEY=3f00033fff 0 0 483ffff17aff32d bfd4444600000000 ffff0001 130ff38b17d007 ffff7bfad9415fff ffbeffdfffefffff fffffffffffffffe +B: REL=1943 +B: ABS=100000000 +B: MSC=10 +B: LED=1f + +I: Bus=0003 Vendor=b58e Product=9e84 Version=0100 +N: Name="Blue Microphones Yeti Stereo Microphone Consumer Control" +P: Phys=usb-0000:0f:00.3-4/input3 +S: Sysfs=/devices/pci0000:00/0000:00:08.1/0000:0f:00.3/usb5/5-4/5-4:1.3/0003:B58E:9E84.0001/input/input0 +U: Uniq=797_2020/06/26_02565 +H: Handlers=kbd event0 +B: PROP=0 +B: EV=1b +B: KEY=1 0 7800000000 e000000000000 0 +B: ABS=10000000000 +B: MSC=10 + +` +) + +func TestReadDevices(t *testing.T) { + as := require.New(t) + r := io.NopCloser(bytes.NewReader([]byte(devices))) + + devices, err := input.ReadDevices(r, false) + as.NoError(err) + + expected := []*input.Device{ + { + Bus: input.BusUsb, + Vendor: uint16(0x1038), + Product: uint16(0x1634), + Version: uint16(0x0111), + Name: "SteelSeries SteelSeries Apex 9 TKL", + Phys: "usb-0000:08:00.3-2.4.2/input2", + Sysfs: "/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:08:00.3/usb3/3-2/3-2.4/3-2.4.2/3-2.4.2:1.2/0003:1038:1634.000B/input/input11", + Handlers: []string{"sysrq", "kbd", "leds", "event4"}, + Capabilities: map[string]string{ + "PROP": "0", + "EV": "120013", + "KEY": "1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe", + "MSC": "10", + "LED": "7", + }, + }, + { + Bus: input.BusUsb, + Vendor: uint16(0x046d), + Product: uint16(0x407b), + Version: uint16(0x0111), + Name: "Logitech MX Vertical", + Phys: "usb-0000:08:00.3-2.2/input2:1", + Sysfs: "/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:08:00.3/usb3/3-2/3-2.2/3-2.2:1.2/0003:046D:C52B.0004/0003:046D:407B.0005/input/input8", + Handlers: []string{"sysrq", "kbd", "leds", "event1", "mouse0"}, + Capabilities: map[string]string{ + "PROP": "0", + "EV": "12001f", + "KEY": "3f00033fff 0 0 483ffff17aff32d bfd4444600000000 ffff0001 130ff38b17d007 ffff7bfad9415fff ffbeffdfffefffff fffffffffffffffe", + "REL": "1943", + "ABS": "100000000", + "MSC": "10", + "LED": "1f", + }, + }, + { + Bus: input.BusUsb, + Vendor: uint16(0xb58e), + Product: uint16(0x9e84), + Version: uint16(0x0100), + Name: "Blue Microphones Yeti Stereo Microphone Consumer Control", + Phys: "usb-0000:0f:00.3-4/input3", + Sysfs: "/devices/pci0000:00/0000:00:08.1/0000:0f:00.3/usb5/5-4/5-4:1.3/0003:B58E:9E84.0001/input/input0", + Handlers: []string{"kbd", "event0"}, + Capabilities: map[string]string{ + "PROP": "0", + "EV": "1b", + "KEY": "1 0 7800000000 e000000000000 0", + "ABS": "10000000000", + "MSC": "10", + }, + }, + } + + as.EqualValues(expected, devices) +} diff --git a/pkg/udev/udev.go b/pkg/udev/udev.go new file mode 100644 index 0000000..6a52c64 --- /dev/null +++ b/pkg/udev/udev.go @@ -0,0 +1,325 @@ +package udev + +/* +#cgo LDFLAGS: -ludev +#include +#include +#include + +struct kv_pair { + char *key; + char *value; +}; + +struct kv_pair *facter_udev_get_device_properties(struct udev_device *device, size_t *size) { + *size = 0; + size_t count = 0; + struct kv_pair *results = NULL; + struct udev_list_entry *entry = NULL; + + udev_list_entry_foreach( + entry, + udev_device_get_properties_list_entry(device) + ) { + struct kv_pair *new_results = realloc(results, (count + 1) * sizeof(struct kv_pair)); + + if (!new_results) { + // Handle allocation failure + for (size_t i = 0; i < count; ++i) { + free(results[i].key); + free(results[i].value); + } + free(results); + return NULL; + } + + results = new_results; + results[count].key = strdup(udev_list_entry_get_name(entry)); + results[count].value = strdup(udev_list_entry_get_value(entry)); + + count++; + }; + + *size = count; + return results; +} +*/ +import "C" + +import ( + "errors" + "fmt" + "strconv" + "unsafe" +) + +//go:generate enumer -type=Bus -json -text -transform=snake -trimprefix Bus -output=./udev_bus.go +type Bus int + +const ( + BusAta Bus = iota // ATA (IDE) devices + BusBluetooth + BusI8042 + BusI2c // Inter-Integrated Circuit + BusIeee1394 // Firewire + BusPci + BusPciExpress + BusPcmcia // Personal Computer Memory Card International Association + BusPlatform // Devices on a system's motherboard + BusScsi + BusSerial + BusSerio // Serial interface controller devices + BusSpi // Serial Peripheral Interface connected devices + BusUsb + BusVirtio +) + +//go:generate enumer -type=Type -json -text -trimprefix Type -output=./udev_type.go +type Type int + +const ( + TypeDisk Type = iota + TypeCD + TypeFloppy + TypeTape + TypePartition + TypeUsb + TypeScsi + TypePci + TypeNetwork + TypeMouse + TypeKeyboard + TypePrinter + TypeAudio + TypeVideo + TypeGeneric +) + +var ErrNotFound = errors.New("udev data not found") + +type Input struct { + IsAccelerometer bool + IsJoystick bool + IsJoystickIntegration bool + IsKey bool + IsKeyboard bool + IsMouse bool + IsPointingStick bool + IsSwitch bool + IsTablet bool + IsTabletPad bool + IsTouchpad bool + IsTouchpadIntegration bool + IsTouchscreen bool + IsTrackball bool +} + +func NewUdevInput(env map[string]string) *Input { + if env["ID_INPUT"] != "1" { + return nil + } + + return &Input{ + IsAccelerometer: env["ID_INPUT_ACCELEROMETER"] == "1", + IsJoystick: env["ID_INPUT_JOYSTICK"] == "1", + IsJoystickIntegration: env["ID_INPUT_JOYSTICK_INTEGRATION"] == "1", + IsKey: env["ID_INPUT_KEY"] == "1", + IsKeyboard: env["ID_INPUT_KEYBOARD"] == "1", + IsMouse: env["ID_INPUT_MOUSE"] == "1", + IsPointingStick: env["ID_INPUT_POINTINGSTICK"] == "1", + IsSwitch: env["ID_INPUT_SWITCH"] == "1", + IsTablet: env["ID_INPUT_TABLET"] == "1", + IsTabletPad: env["ID_INPUT_TABLET_PAD"] == "1", + IsTouchpad: env["ID_INPUT_TOUCHPAD"] == "1", + IsTouchpadIntegration: env["ID_INPUT_TOUCHPAD_INTEGRATION"] == "1", + IsTouchscreen: env["ID_INPUT_TOUCHSCREEN"] == "1", + IsTrackball: env["ID_INPUT_TRACKBALL"] == "1", + } +} + +type Usb struct { + Model string + ModelID uint16 + Vendor string + VendorID uint16 + Revision uint16 + Serial string + Type string + Interfaces string + InterfaceNum string + Driver string +} + +func NewUdevUsb(env map[string]string) (*Usb, error) { + if bus := env["ID_BUS"]; bus != "usb" { + return nil, fmt.Errorf("invalid bus: %s", bus) + } + + result := &Usb{ + Model: env["ID_USB_MODEL"], + Vendor: env["ID_USB_VENDOR"], + Serial: env["ID_SERIAL"], + Type: env["ID_USB_TYPE"], + Interfaces: env["ID_USB_INTERFACES"], + InterfaceNum: env["ID_USB_INTERFACE_NUM"], + Driver: env["ID_USB_DRIVER"], + } + + modelID, err := strconv.ParseUint(env["ID_USB_MODEL_ID"], 16, 16) + if err != nil { + return nil, fmt.Errorf("failed to parse model id: %w", err) + } + + result.ModelID = uint16(modelID) + + vendorID, err := strconv.ParseUint(env["ID_USB_VENDOR_ID"], 16, 16) + if err != nil { + return nil, fmt.Errorf("failed to parse vendor id: %w", err) + } + + result.VendorID = uint16(vendorID) + + revision, err := strconv.ParseUint(env["ID_USB_REVISION"], 16, 16) + if err != nil { + return nil, fmt.Errorf("failed to parse revision: %w", err) + } + + result.Revision = uint16(revision) + + return result, nil +} + +type Pci struct { + Class string + SubClass string + Interface string +} + +func NewUdevPci(env map[string]string) (*Pci, error) { + if bus := env["ID_BUS"]; bus != "pci" { + return nil, fmt.Errorf("invalid bus: %s", bus) + } + + result := &Pci{ + Class: env["ID_PCI_CLASS"], + SubClass: env["ID_PCI_SUBCLASS"], + Interface: env["ID_PCI_INTERFACE"], + } + + return result, nil +} + +type Udev struct { + Bus Bus + Type Type + Model string + ModelID uint16 + Vendor string + VendorID uint16 + Revision uint16 + Serial string + SerialShort string + + Usb *Usb + Pci *Pci + Input *Input +} + +func NewUdev(env map[string]string) (result *Udev, err error) { + result = &Udev{ + Model: env["ID_MODEL"], + Vendor: env["ID_VENDOR"], + Serial: env["ID_SERIAL"], + SerialShort: env["ID_SERIAL_SHORT"], + Input: NewUdevInput(env), + } + + if bus, ok := env["ID_BUS"]; ok { + if err := result.Bus.UnmarshalText([]byte(bus)); err != nil { + return nil, fmt.Errorf("failed to parse bus: %w", err) + } + } + + if str, ok := env["ID_MODEL_ID"]; ok { + modelID, err := strconv.ParseUint(str, 16, 16) + if err != nil { + return nil, fmt.Errorf("failed to parse model id: %w", err) + } + + result.ModelID = uint16(modelID) + } + + if str, ok := env["ID_VENDOR_ID"]; ok { + vendorID, err := strconv.ParseUint(str, 16, 16) + if err != nil { + return nil, fmt.Errorf("failed to parse vendor id: %w", err) + } + + result.VendorID = uint16(vendorID) + } + + if str, ok := env["ID_REVISION"]; ok { + revision, err := strconv.ParseUint(str, 16, 16) + if err != nil { + return nil, fmt.Errorf("failed to parse revision: %w", err) + } + + result.Revision = uint16(revision) + } + + switch result.Bus { + case BusUsb: + if result.Usb, err = NewUdevUsb(env); err != nil { + return nil, fmt.Errorf("failed to parse usb: %w", err) + } + case BusPci: + if result.Pci, err = NewUdevPci(env); err != nil { + return nil, fmt.Errorf("failed to parse pci: %w", err) + } + default: // do nothing + } + + return result, nil +} + +func Read(sysPath string) (*Udev, error) { + udev := C.udev_new() + if udev == nil { + return nil, errors.New("failed to create udev") + } + + defer C.udev_unref(udev) + + device := C.udev_device_new_from_syspath(udev, C.CString(sysPath)) + if device == nil { + return nil, ErrNotFound + } + + defer C.udev_device_unref(device) + + count := C.size_t(0) + + propsArray := C.facter_udev_get_device_properties(device, &count) + if propsArray == nil { + return nil, fmt.Errorf("failed to get device properties") + } + + defer C.free(unsafe.Pointer(propsArray)) + + env := make(map[string]string) + propsSlice := unsafe.Slice(propsArray, count) + + for idx := range propsSlice { + kv := (*C.struct_kv_pair)(unsafe.Pointer(&propsSlice[idx])) + + key := C.GoString(kv.key) + value := C.GoString(kv.value) + + C.free(unsafe.Pointer(kv.key)) + C.free(unsafe.Pointer(kv.value)) + + env[key] = value + } + + return NewUdev(env) +} diff --git a/pkg/udev/udev_bus.go b/pkg/udev/udev_bus.go new file mode 100644 index 0000000..6906953 --- /dev/null +++ b/pkg/udev/udev_bus.go @@ -0,0 +1,160 @@ +// Code generated by "enumer -type=Bus -json -text -transform=snake -trimprefix Bus -output=./udev_bus.go"; DO NOT EDIT. + +package udev + +import ( + "encoding/json" + "fmt" + "strings" +) + +const _BusName = "atabluetoothi8042i2cieee1394pcipci_expresspcmciaplatformscsiserialseriospiusbvirtio" + +var _BusIndex = [...]uint8{0, 3, 12, 17, 20, 28, 31, 42, 48, 56, 60, 66, 71, 74, 77, 83} + +const _BusLowerName = "atabluetoothi8042i2cieee1394pcipci_expresspcmciaplatformscsiserialseriospiusbvirtio" + +func (i Bus) String() string { + if i < 0 || i >= Bus(len(_BusIndex)-1) { + return fmt.Sprintf("Bus(%d)", i) + } + return _BusName[_BusIndex[i]:_BusIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _BusNoOp() { + var x [1]struct{} + _ = x[BusAta-(0)] + _ = x[BusBluetooth-(1)] + _ = x[BusI8042-(2)] + _ = x[BusI2c-(3)] + _ = x[BusIeee1394-(4)] + _ = x[BusPci-(5)] + _ = x[BusPciExpress-(6)] + _ = x[BusPcmcia-(7)] + _ = x[BusPlatform-(8)] + _ = x[BusScsi-(9)] + _ = x[BusSerial-(10)] + _ = x[BusSerio-(11)] + _ = x[BusSpi-(12)] + _ = x[BusUsb-(13)] + _ = x[BusVirtio-(14)] +} + +var _BusValues = []Bus{BusAta, BusBluetooth, BusI8042, BusI2c, BusIeee1394, BusPci, BusPciExpress, BusPcmcia, BusPlatform, BusScsi, BusSerial, BusSerio, BusSpi, BusUsb, BusVirtio} + +var _BusNameToValueMap = map[string]Bus{ + _BusName[0:3]: BusAta, + _BusLowerName[0:3]: BusAta, + _BusName[3:12]: BusBluetooth, + _BusLowerName[3:12]: BusBluetooth, + _BusName[12:17]: BusI8042, + _BusLowerName[12:17]: BusI8042, + _BusName[17:20]: BusI2c, + _BusLowerName[17:20]: BusI2c, + _BusName[20:28]: BusIeee1394, + _BusLowerName[20:28]: BusIeee1394, + _BusName[28:31]: BusPci, + _BusLowerName[28:31]: BusPci, + _BusName[31:42]: BusPciExpress, + _BusLowerName[31:42]: BusPciExpress, + _BusName[42:48]: BusPcmcia, + _BusLowerName[42:48]: BusPcmcia, + _BusName[48:56]: BusPlatform, + _BusLowerName[48:56]: BusPlatform, + _BusName[56:60]: BusScsi, + _BusLowerName[56:60]: BusScsi, + _BusName[60:66]: BusSerial, + _BusLowerName[60:66]: BusSerial, + _BusName[66:71]: BusSerio, + _BusLowerName[66:71]: BusSerio, + _BusName[71:74]: BusSpi, + _BusLowerName[71:74]: BusSpi, + _BusName[74:77]: BusUsb, + _BusLowerName[74:77]: BusUsb, + _BusName[77:83]: BusVirtio, + _BusLowerName[77:83]: BusVirtio, +} + +var _BusNames = []string{ + _BusName[0:3], + _BusName[3:12], + _BusName[12:17], + _BusName[17:20], + _BusName[20:28], + _BusName[28:31], + _BusName[31:42], + _BusName[42:48], + _BusName[48:56], + _BusName[56:60], + _BusName[60:66], + _BusName[66:71], + _BusName[71:74], + _BusName[74:77], + _BusName[77:83], +} + +// BusString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func BusString(s string) (Bus, error) { + if val, ok := _BusNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _BusNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Bus values", s) +} + +// BusValues returns all values of the enum +func BusValues() []Bus { + return _BusValues +} + +// BusStrings returns a slice of all String values of the enum +func BusStrings() []string { + strs := make([]string, len(_BusNames)) + copy(strs, _BusNames) + return strs +} + +// IsABus returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Bus) IsABus() bool { + for _, v := range _BusValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for Bus +func (i Bus) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for Bus +func (i *Bus) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("Bus should be a string, got %s", data) + } + + var err error + *i, err = BusString(s) + return err +} + +// MarshalText implements the encoding.TextMarshaler interface for Bus +func (i Bus) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for Bus +func (i *Bus) UnmarshalText(text []byte) error { + var err error + *i, err = BusString(string(text)) + return err +} diff --git a/pkg/udev/udev_type.go b/pkg/udev/udev_type.go new file mode 100644 index 0000000..1d64cfd --- /dev/null +++ b/pkg/udev/udev_type.go @@ -0,0 +1,160 @@ +// Code generated by "enumer -type=Type -json -text -trimprefix Type -output=./udev_type.go"; DO NOT EDIT. + +package udev + +import ( + "encoding/json" + "fmt" + "strings" +) + +const _TypeName = "DiskCDFloppyTapePartitionUsbScsiPciNetworkMouseKeyboardPrinterAudioVideoGeneric" + +var _TypeIndex = [...]uint8{0, 4, 6, 12, 16, 25, 28, 32, 35, 42, 47, 55, 62, 67, 72, 79} + +const _TypeLowerName = "diskcdfloppytapepartitionusbscsipcinetworkmousekeyboardprinteraudiovideogeneric" + +func (i Type) String() string { + if i < 0 || i >= Type(len(_TypeIndex)-1) { + return fmt.Sprintf("Type(%d)", i) + } + return _TypeName[_TypeIndex[i]:_TypeIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _TypeNoOp() { + var x [1]struct{} + _ = x[TypeDisk-(0)] + _ = x[TypeCD-(1)] + _ = x[TypeFloppy-(2)] + _ = x[TypeTape-(3)] + _ = x[TypePartition-(4)] + _ = x[TypeUsb-(5)] + _ = x[TypeScsi-(6)] + _ = x[TypePci-(7)] + _ = x[TypeNetwork-(8)] + _ = x[TypeMouse-(9)] + _ = x[TypeKeyboard-(10)] + _ = x[TypePrinter-(11)] + _ = x[TypeAudio-(12)] + _ = x[TypeVideo-(13)] + _ = x[TypeGeneric-(14)] +} + +var _TypeValues = []Type{TypeDisk, TypeCD, TypeFloppy, TypeTape, TypePartition, TypeUsb, TypeScsi, TypePci, TypeNetwork, TypeMouse, TypeKeyboard, TypePrinter, TypeAudio, TypeVideo, TypeGeneric} + +var _TypeNameToValueMap = map[string]Type{ + _TypeName[0:4]: TypeDisk, + _TypeLowerName[0:4]: TypeDisk, + _TypeName[4:6]: TypeCD, + _TypeLowerName[4:6]: TypeCD, + _TypeName[6:12]: TypeFloppy, + _TypeLowerName[6:12]: TypeFloppy, + _TypeName[12:16]: TypeTape, + _TypeLowerName[12:16]: TypeTape, + _TypeName[16:25]: TypePartition, + _TypeLowerName[16:25]: TypePartition, + _TypeName[25:28]: TypeUsb, + _TypeLowerName[25:28]: TypeUsb, + _TypeName[28:32]: TypeScsi, + _TypeLowerName[28:32]: TypeScsi, + _TypeName[32:35]: TypePci, + _TypeLowerName[32:35]: TypePci, + _TypeName[35:42]: TypeNetwork, + _TypeLowerName[35:42]: TypeNetwork, + _TypeName[42:47]: TypeMouse, + _TypeLowerName[42:47]: TypeMouse, + _TypeName[47:55]: TypeKeyboard, + _TypeLowerName[47:55]: TypeKeyboard, + _TypeName[55:62]: TypePrinter, + _TypeLowerName[55:62]: TypePrinter, + _TypeName[62:67]: TypeAudio, + _TypeLowerName[62:67]: TypeAudio, + _TypeName[67:72]: TypeVideo, + _TypeLowerName[67:72]: TypeVideo, + _TypeName[72:79]: TypeGeneric, + _TypeLowerName[72:79]: TypeGeneric, +} + +var _TypeNames = []string{ + _TypeName[0:4], + _TypeName[4:6], + _TypeName[6:12], + _TypeName[12:16], + _TypeName[16:25], + _TypeName[25:28], + _TypeName[28:32], + _TypeName[32:35], + _TypeName[35:42], + _TypeName[42:47], + _TypeName[47:55], + _TypeName[55:62], + _TypeName[62:67], + _TypeName[67:72], + _TypeName[72:79], +} + +// TypeString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func TypeString(s string) (Type, error) { + if val, ok := _TypeNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _TypeNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Type values", s) +} + +// TypeValues returns all values of the enum +func TypeValues() []Type { + return _TypeValues +} + +// TypeStrings returns a slice of all String values of the enum +func TypeStrings() []string { + strs := make([]string, len(_TypeNames)) + copy(strs, _TypeNames) + return strs +} + +// IsAType returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Type) IsAType() bool { + for _, v := range _TypeValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for Type +func (i Type) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for Type +func (i *Type) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("Type should be a string, got %s", data) + } + + var err error + *i, err = TypeString(s) + return err +} + +// MarshalText implements the encoding.TextMarshaler interface for Type +func (i Type) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for Type +func (i *Type) UnmarshalText(text []byte) error { + var err error + *i, err = TypeString(string(text)) + return err +}