summaryrefslogblamecommitdiffstats
path: root/server/lib/pci/index.js
blob: 5a7d4681e06b3db6c7d5af3edc2f78b2b7471026 (plain) (tree)































































































































































































                                                                                                                                                    
/* 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:
 *  {
 *      '<vendor_id>': {
 *          'name': '<vendor_name>',
 *          'devices': {
 *              '<device_id>': {
 *                  'name': '<device_name>',
 *                  'subsystems': {
 *                      '<subvendor_id>': {
 *                          '<subdevice_id>': {
 *                              'name': '<subsystem_name>'
 *                          }
 *                      }
 *                  }
 *              }
 *          }
 *      }
 *  }
 *
 *  # C class class_name
 *  #   subclass  subclass_name     <-- single tab
 *  #     prog-if  prog-if_name     <-- two tabs
 *  parsed structure:
 *  {
 *      '<class_id>': {
 *          'name': '<class_name>',
 *          'subclasses': {
 *              '<subclass_id>': {
 *                  'name': '<subclass_name>',
 *                  'interfaces': {
 *                      '<interface_id>': {
 *                        'name': '<interface_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
}