1 <?php
  2
  3 function getContents($file, $default = '') {
  4   if (!is_file($file) || !($contents = @file_get_contents($file)))
  5     return $default;
  6   else
  7     return trim($contents);
  8 }
  9
 10 /**
 11  * Deal with pci.ids and usb.ids workings
 12  * @author Joe Gillotti
 13  */
 14 class HW_IDS {
 15
 16   private
 17     $_usb_file = '/usr/share/misc/usb.ids',
 18     $_pci_file = '/usr/share/misc/pci.ids',
 19     $_cache_file = '',
 20     $_existing_cache_vals = array(),
 21     $_usb_entries = array(),
 22     $_pci_entries = array(),
 23     $_usb_devices = array(),
 24     $_pci_devices = array();
 25
 26   /**
 27    * Constructor
 28    */
 29   public function __construct() {
 30     $this->_cache_file = dirname(__FILE__).'/cache.json';
 31     $this->_populate_cache();
 32   }
 33
 34   /**
 35    * Run the cache file
 36    */
 37   private function _populate_cache() {
 38     if (function_exists('json_decode') && is_readable($this->_cache_file))
 39       $this->_existing_cache_vals = (array) @json_decode(getContents($this->_cache_file, ''), true);
 40   }
 41   
 42   /**
 43    * Get the USB ids from /sys
 44    */
 45   private function _fetchUsbIds() {
 46     $usb_paths = (array) @glob('/sys/bus/usb/devices/*/uevent', GLOB_NOSORT);
 47     $num_usb_paths = count($usb_paths);
 48     for ($i = 0; $i < $num_usb_paths; $i++) {
 49       $path = $usb_paths[$i];
 50       if (is_readable($path) && preg_match('/^product=([^\/]+)\/([^\/]+)\/[^$]+$/m', strtolower(getContents($path)), $match) == 1) {
 51         $this->_usb_entries[str_pad($match[1], 4, '0', STR_PAD_LEFT)][str_pad($match[2], 4, '0', STR_PAD_LEFT)] = 1;
 52       }
 53     }
 54   }
 55
 56   /**
 57    * Get the PCI ids from /sys
 58    */
 59   private function _fetchPciIds() {
 60     $pci_paths = (array) @glob('/sys/bus/pci/devices/*/uevent', GLOB_NOSORT);
 61     $num_pci_paths = count($pci_paths);
 62     for ($i = 0; $i < $num_pci_paths; $i++) {
 63       $path = $pci_paths[$i];
 64       if (is_readable($path) && preg_match('/pci\_(?:subsys_)?id=(\w+):(\w+)/', strtolower(getContents($path)), $match) == 1) {
 65         $this->_pci_entries[$match[1]][$match[2]] = 1;
 66       }
 67       else {
 68         $path = dirname($path);
 69         $vendor = getContents($path.'/subsystem_vendor', false);
 70         $device = getContents($path.'/subsystem_device', false);
 71         if ($vendor !== false && $device !== false) {
 72           $vendor = str_pad(strtoupper(substr($vendor, 2)), 4, '0', STR_PAD_LEFT);
 73           $device = str_pad(strtoupper(substr($device, 2)), 4, '0', STR_PAD_LEFT);
 74           $this->_pci_entries[$vendor][$device] = 1;
 75         }
 76       }
 77     }
 78   }
 79
 80   /**
 81    * Use the pci.ids file to translate the ids to names
 82    */
 83   private function _fetchPciNames() {
 84     for ($v = false, $file = @fopen($this->_pci_file, 'r'); $file != false && $contents = fgets($file);) {
 85         if (preg_match('/^(\S{4})\s+([^$]+)$/', $contents, $vend_match) == 1) {
 86           $v = $vend_match;
 87         }
 88         elseif(preg_match('/^\s+(\S{4})\s+([^$]+)$/', $contents, $dev_match) == 1) {
 89           if($v && isset($this->_pci_entries[strtolower($v[1])][strtolower($dev_match[1])])) {
 90             $this->_pci_devices[$v[1]][$dev_match[1]] = array('vendor=> rtrim($v[2]), 'device=> rtrim($dev_match[2]));
 91           }
 92         }
 93     }
 94     $file && @fclose($file);
 95   }
 96   
 97   /**
 98    * Use the usb.ids file to translate the ids to names
 99    */
100   private function _fetchUsbNames() {
101     for ($v = false, $file = @fopen($this->_usb_file, 'r'); $file != false && $contents = fgets($file);) {
102         if (preg_match('/^(\S{4})\s+([^$]+)$/', $contents, $vend_match) == 1) {
103           $v = $vend_match;
104         }
105         elseif(preg_match('/^\s+(\S{4})\s+([^$]+)$/', $contents, $dev_match) == 1) {
106           if($v && isset($this->_usb_entries[strtolower($v[1])][strtolower($dev_match[1])])) {
107             $this->_usb_devices[$v[1]][$dev_match[1]] = array('vendor=> rtrim($v[2]), 'device=> rtrim($dev_match[2]));
108           }
109         }
110     }
111     $file && @fclose($file);
112   }
113
114   /**
115    * Decide if the cache file is sufficient enough to not parse the ids files
116    */
117   public function _is_cache_worthy() {
118     $pci_good = true;
119     foreach(array_keys($this->_pci_entries) as $vendor) {
120       foreach (array_keys($this->_pci_entries[$vendor]) as $dever) {
121         if (!isset($this->_existing_cache_vals['hw']['pci'][$vendor][$dever])) {
122           $pci_good = false;
123           break 2;
124         }
125       }
126     }
127     $usb_good = true;
128     foreach(array_keys($this->_usb_entries) as $vendor) {
129       foreach (array_keys($this->_usb_entries[$vendor]) as $dever) {
130         if (!isset($this->_existing_cache_vals['hw']['usb'][$vendor][$dever])) {
131           $usb_good = false;
132           break 2;
133         }
134       }
135     }
136     return array('pci=> $pci_good, 'usb=> $usb_good);
137   }
138
139   /*
140    * Write cache file with latest shit
141    */
142   public function _write_cache() {
143     if (function_exists('json_encode'))
144       @file_put_contents($this->_cache_file, json_encode(array(
145         'hw=> array(
146           'pci=> $this->_pci_devices,
147           'usb=> $this->_usb_devices
148         )
149       )));
150   }
151
152   /**
153    * Do its goddam job
154    */
155   public function work() {
156     $this->_fetchPciIds();
157     $this->_fetchUsbIds();
158     $worthiness = $this->_is_cache_worthy();
159     $save_cache = false;
160     if (!$worthiness['pci']) {
161       $save_cache = true;
162       $this->_fetchPciNames();
163     }
164     else 
165       $this->_pci_devices = $this->_existing_cache_vals['hw']['pci'];
166     if (!$worthiness['usb']) {
167       $save_cache = true;
168       $this->_fetchUsbNames();
169     }
170     else 
171       $this->_usb_devices = $this->_existing_cache_vals['hw']['usb'];
172     if ($save_cache)
173       $this->_write_cache();
174   }
175
176   /**
177    * Get results
178    */
179    public function result() {
180     return array(
181       'pci=> $this->_pci_devices,
182       'usb=> $this->_usb_devices
183     );
184    }
185 }
186
187 $hw = new HW_IDS;
188 $hw->work();
189 var_dump($hw->result());