From 763c5bd4e68e56b556526f0c06b0d300d12c2e79 Mon Sep 17 00:00:00 2001 From: Jannik Schönartz Date: Fri, 29 Jan 2021 12:13:41 +0000 Subject: [server/pci] Add lib for downloading and parsing the current pci id file --- server/lib/pci/index.js | 192 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 server/lib/pci/index.js diff --git a/server/lib/pci/index.js b/server/lib/pci/index.js new file mode 100644 index 0000000..5a7d468 --- /dev/null +++ b/server/lib/pci/index.js @@ -0,0 +1,192 @@ +/* global __appdir */ +var path = require('path') +const fs = require('fs') +const crypto = require('crypto') +const axios = require('axios') +const pciRepoApi = 'https://api.github.com/repos/pciutils/pciids/contents/' +const pciRepoDataApi = 'https://api.github.com/repos/pciutils/pciids/git/blobs/' +const pciIdsFilePath = path.join(__appdir, 'lib', 'pci', 'pci.ids') +var parsedPciList = {} +var parsedDeviceClasses = {} + +// Non-Api way to the the raw file: +// const pciIdsUrl = 'https://github.com/pciutils/pciids/raw/master/pci.ids' + +module.exports = { + parseIds: async function (vendorId, deviceId, subvendorId, subdeviceId, classId, subclassId) { + await updatePciIds() + if (Object.keys(parsedPciList).length === 0) await parsePciList() + + let pciInformation = { + 'vendorId': vendorId, + 'vendor': vendorId ? parsedPciList[vendorId].name : undefined, + 'deviceId': deviceId, + 'device': deviceId ? parsedPciList[vendorId].devices[deviceId].name : undefined, + 'subvendorId': subvendorId, + 'subvendor': (subvendorId && subdeviceId) ? parsedPciList[vendorId].devices[deviceId].subsystems[subvendorId][subdeviceId].name : undefined, + 'classId': classId, + 'class': classId ? parsedDeviceClasses[classId].name : undefined, + 'subclassId': subclassId, + 'subclass': subclassId ? parsedDeviceClasses[classId].subclasses[subclassId].name : undefined + } + + return pciInformation + } +} + +async function updatePciIds () { + // Check if the file in the git is newer than the file created. + + let response = await axios.get(pciRepoApi) + response = response.data + + // Get SHA of the pci.ids file + const sha = response.filter(x => x.name === 'pci.ids')[0].sha + + if (fs.existsSync(pciIdsFilePath)) { + /* Calculate own sha value + * Git sha1 value of a file is calculated like this: + * 'blob' + ' ' + content.length + '\0' + content + */ + const pciids = fs.readFileSync(pciIdsFilePath) + const blobprefix = 'blob' + ' ' + pciids.length + '\0' + const sha1sum = crypto.createHash('sha1').update(blobprefix + pciids).digest('hex') + + // Exit if there isn't a new version + if (sha === sha1sum) return + } + + // Download the new list + await downloadPciIds(sha) +} + +async function downloadPciIds (sha) { + // Get the newest version from the github api + let request = await axios.get(pciRepoDataApi + sha) + request = request.data + + // Decode base64 encoding + const buffer = Buffer.from(request.content, 'base64') + const file = buffer.toString('utf-8') + + // Override the content in the pci.ids file with the new one + fs.writeFile(pciIdsFilePath, file, { flag: 'w' }, (err) => { + if (err) throw err + + // Reparse the pci file + parsePciList() + }) +} + +/* + * Syntax of the pci.ids file: + * # vendor vendor_name + * # device device_name <-- single tab + * # subvendor subdevice subsystem_name <-- two tabs + * parsed structure: + * { + * '': { + * 'name': '', + * 'devices': { + * '': { + * 'name': '', + * 'subsystems': { + * '': { + * '': { + * 'name': '' + * } + * } + * } + * } + * } + * } + * } + * + * # C class class_name + * # subclass subclass_name <-- single tab + * # prog-if prog-if_name <-- two tabs + * parsed structure: + * { + * '': { + * 'name': '', + * 'subclasses': { + * '': { + * 'name': '', + * 'interfaces': { + * '': { + * 'name': '' + * } + * } + * } + * } + * } + * } + */ +function parsePciList () { + let result = {} + let deviceClasses = {} + + // Read file + const lines = fs.readFileSync(pciIdsFilePath).toString().split('\n') + const regexVendor = /^[0-9A-Fa-f]{4}/ + const regexDevice = /^\t[0-9A-Fa-f]{4}/ + const regexSubVD = /^\t\t[0-9A-Fa-f]{4}/ + + const regexClass = /^C [0-9A-Fa-f]{2}/ + const regexSubclass = /^\t[0-9A-Fa-f]{2}/ + const regexProgIf = /^\t\t[0-9A-Fa-f]{2}/ + + let lastVendorId = '' + let lastDeviceId = '' + let lastClassId = '' + let lastSubClassId = '' + + for (let line of lines) { + if (line.startsWith('#')) continue + else if (line === '') continue + else if (line.match(regexVendor)) { + const vendor = line.substring(0, 4) + const name = line.substring(6) + result[vendor] = { + 'name': name, + 'devices': {} + } + lastVendorId = vendor + } else if (line.match(regexDevice)) { + const device = line.substring(1, 5) + const name = line.substring(7) + result[lastVendorId].devices[device] = { + 'name': name, + 'subsystems': {} + } + lastDeviceId = device + } else if (line.match(regexSubVD)) { + const subvendor = line.substring(2, 6) + const subdevice = line.substring(7, 11) + const subsystem = line.substring(13) + + if (result[lastVendorId].devices[lastDeviceId].subsystems[subvendor] === undefined) result[lastVendorId].devices[lastDeviceId][subvendor] = {} + result[lastVendorId].devices[lastDeviceId][subvendor][subdevice] = { + 'name': subsystem + } + } else if (line.match(regexClass)) { + deviceClasses[line.substring(2, 4)] = { + 'name': line.substring(6), + 'subclasses': {} + } + lastClassId = line.substring(2, 4) + } else if (line.match(regexSubclass)) { + deviceClasses[lastClassId].subclasses[line.substring(1, 3)] = { + 'name': line.substring(5), + 'interfaces': {} + } + lastSubClassId = line.substring(1, 3) + } else if (line.match(regexProgIf)) { + deviceClasses[lastClassId].subclasses[lastSubClassId].interfaces[line.substring(2, 4)] = { + 'name': line.substring(6) + } + } + } + parsedPciList = result + parsedDeviceClasses = deviceClasses +} -- cgit v1.2.3-55-g7522