PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/class.HW_IDS.php

https://bitbucket.org/rmarefaty/linfo
PHP | 312 lines | 193 code | 29 blank | 90 comment | 46 complexity | 899ff93b4052abccf4a589e0fc9063b6 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /**
  3. * This file is part of Linfo (c) 2010 Joseph Gillotti.
  4. *
  5. * Linfo is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * Linfo 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 Linfo. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /**
  19. * Keep out hackers...
  20. */
  21. defined('IN_INFO') or exit;
  22. /**
  23. * Deal with pci.ids and usb.ids workings
  24. * @author Joe Gillotti
  25. */
  26. class HW_IDS {
  27. private
  28. $_use_json = false,
  29. $_usb_file = '',
  30. $_pci_file = '',
  31. $_cache_file = '',
  32. $_existing_cache_vals = array(),
  33. $_usb_entries = array(),
  34. $_pci_entries = array(),
  35. $_usb_devices = array(),
  36. $_pci_devices = array(),
  37. $_result = array(),
  38. $exec,
  39. $error;
  40. /**
  41. * Constructor
  42. *
  43. * @access public
  44. */
  45. public function __construct($usb_file, $pci_file) {
  46. // Localize paths to the ids files
  47. $this->_pci_file = $pci_file;
  48. $this->_usb_file = $usb_file;
  49. // Prefer json, but check for it
  50. $this->_use_json = function_exists('json_encode') && function_exists('json_decode');
  51. // Allow the same web root to be used for multiple insances of linfo, across multiple machines using
  52. // nfs or whatever, and to have a different cache file for each
  53. $sys_id = is_readable('/proc/sys/kernel/hostname') ?
  54. '_'.substr(md5(getContents('/proc/sys/kernel/hostname')), 0, 10) : '_x';
  55. // Path to the cache file
  56. $this->_cache_file = CACHE_PATH.'/ids_cache'.$sys_id.($this->_use_json ? '.json' : '');
  57. // Load contents of cache
  58. $this->_populate_cache();
  59. // Might need these
  60. $this->exec = new CallExt;
  61. $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
  62. $this->error = LinfoError::Fledging();
  63. }
  64. /**
  65. * Run the cache file
  66. *
  67. * @access private
  68. */
  69. private function _populate_cache() {
  70. if ($this->_use_json) {
  71. if (is_readable($this->_cache_file) &&
  72. ($loaded = @json_decode(getContents($this->_cache_file, ''), true)) && is_array($loaded))
  73. $this->_existing_cache_vals = $loaded;
  74. }
  75. else {
  76. if (is_readable($this->_cache_file) &&
  77. ($loaded = @unserialize(getContents($this->_cache_file, false))) && is_array($loaded))
  78. $this->_existing_cache_vals = $loaded;
  79. }
  80. }
  81. /**
  82. * Get the USB ids from /sys
  83. *
  84. * @access private
  85. */
  86. private function _fetchUsbIdsLinux() {
  87. $usb_paths = (array) @glob('/sys/bus/usb/devices/*', GLOB_NOSORT);
  88. $num_usb_paths = count($usb_paths);
  89. for ($i = 0; $i < $num_usb_paths; $i++) {
  90. $path = $usb_paths[$i];
  91. // First try uevent
  92. if (is_readable($path.'/uevent') &&
  93. preg_match('/^product=([^\/]+)\/([^\/]+)\/[^$]+$/m', strtolower(getContents($path.'/uevent')), $match)) {
  94. $this->_usb_entries[str_pad($match[1], 4, '0', STR_PAD_LEFT)][str_pad($match[2], 4, '0', STR_PAD_LEFT)] = 1;
  95. }
  96. // And next modalias
  97. elseif (is_readable($path.'/modalias') &&
  98. preg_match('/^usb:v([0-9A-Z]{4})p([0-9A-Z]{4})/', getContents($path.'/modalias'), $match)) {
  99. $this->_usb_entries[strtolower($match[1])][strtolower($match[2])] = 1;
  100. }
  101. }
  102. }
  103. /**
  104. * Get the PCI ids from /sys
  105. *
  106. * @access private
  107. */
  108. private function _fetchPciIdsLinux() {
  109. $pci_paths = (array) @glob('/sys/bus/pci/devices/*', GLOB_NOSORT);
  110. $num_pci_paths = count($pci_paths);
  111. for ($i = 0; $i < $num_pci_paths; $i++) {
  112. $path = $pci_paths[$i];
  113. // See if we can use simple vendor/device files and avoid taking time with regex
  114. if (($f_device = getContents($path.'/device', '')) && ($f_vend = getContents($path.'/vendor', '')) &&
  115. $f_device != '' && $f_vend != '') {
  116. list(, $v_id) = explode('x', $f_vend, 2);
  117. list(, $d_id) = explode('x', $f_device, 2);
  118. $this->_pci_entries[$v_id][$d_id] = 1;
  119. }
  120. // Try uevent nextly
  121. elseif (is_readable($path.'/uevent') &&
  122. preg_match('/pci\_(?:subsys_)?id=(\w+):(\w+)/', strtolower(getContents($path.'/uevent')), $match)) {
  123. $this->_pci_entries[$match[1]][$match[2]] = 1;
  124. }
  125. // Now for modalias
  126. elseif (is_readable($path.'/modalias') &&
  127. preg_match('/^pci:v0{4}([0-9A-Z]{4})d0{4}([0-9A-Z]{4})/', getContents($path.'/modalias'), $match)) {
  128. $this->_pci_entries[strtolower($match[1])][strtolower($match[2])] = 1;
  129. }
  130. }
  131. }
  132. /**
  133. * Use the pci.ids file to translate the ids to names
  134. *
  135. * @access private
  136. */
  137. private function _fetchPciNames() {
  138. for ($v = false, $file = @fopen($this->_pci_file, 'r'); $file != false && $contents = fgets($file);) {
  139. if (preg_match('/^(\S{4})\s+([^$]+)$/', $contents, $vend_match) == 1) {
  140. $v = $vend_match;
  141. }
  142. elseif(preg_match('/^\s+(\S{4})\s+([^$]+)$/', $contents, $dev_match) == 1) {
  143. if($v && isset($this->_pci_entries[strtolower($v[1])][strtolower($dev_match[1])])) {
  144. $this->_pci_devices[$v[1]][$dev_match[1]] = array('vendor' => rtrim($v[2]), 'device' => rtrim($dev_match[2]));
  145. }
  146. }
  147. }
  148. $file && @fclose($file);
  149. }
  150. /**
  151. * Use the usb.ids file to translate the ids to names
  152. *
  153. * @access private
  154. */
  155. private function _fetchUsbNames() {
  156. for ($v = false, $file = @fopen($this->_usb_file, 'r'); $file != false && $contents = fgets($file);) {
  157. if (preg_match('/^(\S{4})\s+([^$]+)$/', $contents, $vend_match) == 1) {
  158. $v = $vend_match;
  159. }
  160. elseif(preg_match('/^\s+(\S{4})\s+([^$]+)$/', $contents, $dev_match) == 1) {
  161. if($v && isset($this->_usb_entries[strtolower($v[1])][strtolower($dev_match[1])])) {
  162. $this->_usb_devices[strtolower($v[1])][$dev_match[1]] = array('vendor' => rtrim($v[2]), 'device' => rtrim($dev_match[2]));
  163. }
  164. }
  165. }
  166. $file && @fclose($file);
  167. }
  168. /**
  169. * Decide if the cache file is sufficient enough to not parse the ids files
  170. *
  171. * @access private
  172. */
  173. private function _is_cache_worthy() {
  174. $pci_good = true;
  175. foreach(array_keys($this->_pci_entries) as $vendor) {
  176. foreach (array_keys($this->_pci_entries[$vendor]) as $dever) {
  177. if (!isset($this->_existing_cache_vals['hw']['pci'][$vendor][$dever])) {
  178. $pci_good = false;
  179. break 2;
  180. }
  181. }
  182. }
  183. $usb_good = true;
  184. foreach(array_keys($this->_usb_entries) as $vendor) {
  185. foreach (array_keys($this->_usb_entries[$vendor]) as $dever) {
  186. if (!isset($this->_existing_cache_vals['hw']['usb'][$vendor][$dever])) {
  187. $usb_good = false;
  188. break 2;
  189. }
  190. }
  191. }
  192. return array('pci' => $pci_good, 'usb' => $usb_good);
  193. }
  194. /*
  195. * Write cache file with latest shit
  196. *
  197. * @access private
  198. */
  199. private function _write_cache() {
  200. if (is_writable(CACHE_PATH))
  201. @file_put_contents($this->_cache_file, $this->_use_json ?
  202. json_encode(array(
  203. 'hw' => array(
  204. 'pci' => $this->_pci_devices,
  205. 'usb' => $this->_usb_devices
  206. )
  207. ))
  208. :serialize(array(
  209. 'hw' => array(
  210. 'pci' => $this->_pci_devices,
  211. 'usb' => $this->_usb_devices
  212. )
  213. )));
  214. }
  215. /*
  216. * Parse pciconf to get pci ids
  217. *
  218. * @access private
  219. */
  220. private function _fetchPciIdsPciConf() {
  221. try {
  222. $pciconf = $this->exec->exec('pciconf', '-l');
  223. }
  224. catch(CallExtException $e) {
  225. $this->error->add('Linfo Core', 'Error using `pciconf -l` to get hardware info');
  226. return;
  227. }
  228. if (preg_match_all('/^.+chip=0x([a-z0-9]{4})([a-z0-9]{4})/m', $pciconf, $devs, PREG_SET_ORDER) == 0)
  229. return;
  230. foreach ($devs as $dev)
  231. $this->_pci_entries[$dev[2]][$dev[1]] = 1;
  232. }
  233. /**
  234. * Do its goddam job
  235. *
  236. * @access public
  237. */
  238. public function work($os) {
  239. switch ($os) {
  240. case 'linux':
  241. $this->_fetchPciIdsLinux();
  242. $this->_fetchUsbIdsLinux();
  243. break;
  244. case 'freebsd':
  245. case 'dragonfly':
  246. $this->_fetchPciIdsPciConf();
  247. break;
  248. default:
  249. return;
  250. break;
  251. }
  252. $worthiness = $this->_is_cache_worthy();
  253. $save_cache = false;
  254. if (!$worthiness['pci']) {
  255. $save_cache = true;
  256. $this->_fetchPciNames();
  257. }
  258. else
  259. $this->_pci_devices = $this->_existing_cache_vals['hw']['pci'];
  260. if (!$worthiness['usb']) {
  261. $save_cache = true;
  262. $this->_fetchUsbNames();
  263. }
  264. else
  265. $this->_usb_devices = $this->_existing_cache_vals['hw']['usb'];
  266. if ($save_cache)
  267. $this->_write_cache();
  268. }
  269. /**
  270. * Compile and return results
  271. *
  272. * @access public
  273. */
  274. public function result() {
  275. foreach (array_keys((array)$this->_pci_devices) as $v)
  276. foreach ($this->_pci_devices[$v] as $d)
  277. $this->_result[] = array('vendor' => $d['vendor'], 'device' => $d['device'], 'type' => 'PCI');
  278. foreach (array_keys((array)$this->_usb_devices) as $v)
  279. foreach ($this->_usb_devices[$v] as $d)
  280. $this->_result[] = array('vendor' => $d['vendor'], 'device' => $d['device'], 'type' => 'USB');
  281. return $this->_result;
  282. }
  283. }