neklo_monitor - Version 1.1.2

Version Notes

Download this release

Release Info

Developer NEKLO
Extension neklo_monitor
Version 1.1.2
Comparing to
See all releases


Version 1.1.2

Files changed (129) hide show
  1. app/code/community/Neklo/Core/Block/System/Contact.php +16 -0
  2. app/code/community/Neklo/Core/Block/System/Contact/Header.php +17 -0
  3. app/code/community/Neklo/Core/Block/System/Contact/Send.php +20 -0
  4. app/code/community/Neklo/Core/Block/System/Contact/Send/Button.php +35 -0
  5. app/code/community/Neklo/Core/Block/System/Extension.php +16 -0
  6. app/code/community/Neklo/Core/Block/System/Extension/List.php +104 -0
  7. app/code/community/Neklo/Core/Block/System/Newsletter/Subscribe.php +20 -0
  8. app/code/community/Neklo/Core/Block/System/Newsletter/Subscribe/Button.php +35 -0
  9. app/code/community/Neklo/Core/Helper/Config.php +16 -0
  10. app/code/community/Neklo/Core/Helper/Data.php +28 -0
  11. app/code/community/Neklo/Core/Helper/Extension.php +104 -0
  12. app/code/community/Neklo/Core/Model/Feed.php +92 -0
  13. app/code/community/Neklo/Core/Model/Feed/Extension.php +48 -0
  14. app/code/community/Neklo/Core/Model/Observer.php +52 -0
  15. app/code/community/Neklo/Core/Model/Source/Reason.php +28 -0
  16. app/code/community/Neklo/Core/Model/Source/Subscription/Type.php +45 -0
  17. app/code/community/Neklo/Core/Model/System/Config/Backend/Empty.php +9 -0
  18. app/code/community/Neklo/Core/controllers/Adminhtml/Neklo/Core/ContactController.php +51 -0
  19. app/code/community/Neklo/Core/controllers/Adminhtml/Neklo/Core/NewsletterController.php +47 -0
  20. app/code/community/Neklo/Core/etc/adminhtml.xml +26 -0
  21. app/code/community/Neklo/Core/etc/config.xml +83 -0
  22. app/code/community/Neklo/Core/etc/system.xml +164 -0
  23. app/code/community/Neklo/Monitor/Autoload.php +32 -0
  24. app/code/community/Neklo/Monitor/Block/Adminhtml/System/Config/Frontend/Label.php +9 -0
  25. app/code/community/Neklo/Monitor/Block/Adminhtml/System/Config/Frontend/Status.php +35 -0
  26. app/code/community/Neklo/Monitor/Controller/Abstract.php +72 -0
  27. app/code/community/Neklo/Monitor/Helper/Config.php +148 -0
  28. app/code/community/Neklo/Monitor/Helper/Country.php +19 -0
  29. app/code/community/Neklo/Monitor/Helper/Data.php +5 -0
  30. app/code/community/Neklo/Monitor/Helper/Date.php +15 -0
  31. app/code/community/Neklo/Monitor/Helper/Request.php +177 -0
  32. app/code/community/Neklo/Monitor/Helper/Request/Validator.php +108 -0
  33. app/code/community/Neklo/Monitor/Model/Cron/Abstract.php +66 -0
  34. app/code/community/Neklo/Monitor/Model/Cron/Server.php +46 -0
  35. app/code/community/Neklo/Monitor/Model/Cron/Store.php +58 -0
  36. app/code/community/Neklo/Monitor/Model/Gateway/Connector.php +104 -0
  37. app/code/community/Neklo/Monitor/Model/Linfo.php +296 -0
  38. app/code/community/Neklo/Monitor/Model/Linfo/Os/Linux.php +39 -0
  39. app/code/community/Neklo/Monitor/Model/Log.php +6 -0
  40. app/code/community/Neklo/Monitor/Model/Minfo.php +69 -0
  41. app/code/community/Neklo/Monitor/Model/Minfo/Log.php +195 -0
  42. app/code/community/Neklo/Monitor/Model/Minfo/Parser.php +206 -0
  43. app/code/community/Neklo/Monitor/Model/Minfo/Report.php +154 -0
  44. app/code/community/Neklo/Monitor/Model/Resource/Minfo/Log.php +55 -0
  45. app/code/community/Neklo/Monitor/Model/Resource/Minfo/Log/Collection.php +10 -0
  46. app/code/community/Neklo/Monitor/Model/Resource/Minfo/Report.php +49 -0
  47. app/code/community/Neklo/Monitor/Model/Resource/Minfo/Report/Collection.php +10 -0
  48. app/code/community/Neklo/Monitor/Model/System/Config/Backend/Empty.php +9 -0
  49. app/code/community/Neklo/Monitor/Model/System/Config/Backend/Token.php +17 -0
  50. app/code/community/Neklo/Monitor/Model/System/Config/Source/Server/Type.php +35 -0
  51. app/code/community/Neklo/Monitor/controllers/AuthController.php +30 -0
  52. app/code/community/Neklo/Monitor/controllers/CustomerController.php +212 -0
  53. app/code/community/Neklo/Monitor/controllers/DashboardController.php +437 -0
  54. app/code/community/Neklo/Monitor/controllers/InfoController.php +73 -0
  55. app/code/community/Neklo/Monitor/controllers/OrderController.php +118 -0
  56. app/code/community/Neklo/Monitor/controllers/ProductController.php +32 -0
  57. app/code/community/Neklo/Monitor/controllers/Report/SalesController.php +187 -0
  58. app/code/community/Neklo/Monitor/controllers/State/CacheController.php +46 -0
  59. app/code/community/Neklo/Monitor/controllers/State/IndexerController.php +53 -0
  60. app/code/community/Neklo/Monitor/controllers/Var/LogController.php +86 -0
  61. app/code/community/Neklo/Monitor/controllers/Var/ReportController.php +89 -0
  62. app/code/community/Neklo/Monitor/etc/adminhtml.xml +26 -0
  63. app/code/community/Neklo/Monitor/etc/config.xml +95 -0
  64. app/code/community/Neklo/Monitor/etc/system.xml +177 -0
  65. app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-install-1.0.0.php +24 -0
  66. app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-upgrade-1.0.0-1.1.0.php +26 -0
  67. app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-upgrade-1.1.0-1.1.1.php +18 -0
  68. app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-upgrade-1.1.1-1.1.2.php +16 -0
  69. app/design/adminhtml/default/default/layout/neklo/core.xml +10 -0
  70. app/design/adminhtml/default/default/template/neklo/core/system/contact/button.phtml +190 -0
  71. app/design/adminhtml/default/default/template/neklo/core/system/contact/header.phtml +2 -0
  72. app/design/adminhtml/default/default/template/neklo/core/system/extension/list.phtml +25 -0
  73. app/design/adminhtml/default/default/template/neklo/core/system/subscribe/button.phtml +180 -0
  74. app/etc/modules/Neklo_Core.xml +10 -0
  75. app/etc/modules/Neklo_Monitor.xml +9 -0
  76. app/locale/en_US/Neklo_Core.csv +19 -0
  77. lib/Linfo/Common.php +190 -0
  78. lib/Linfo/Exceptions/FatalException.php +25 -0
  79. lib/Linfo/Extension/Apcaccess.php +200 -0
  80. lib/Linfo/Extension/Cups.php +229 -0
  81. lib/Linfo/Extension/Dhcpd3_leases.php +292 -0
  82. lib/Linfo/Extension/Dnsmasq_dhcpd.php +151 -0
  83. lib/Linfo/Extension/Extension.php +33 -0
  84. lib/Linfo/Extension/Ipmi.php +128 -0
  85. lib/Linfo/Extension/Libvirt.php +216 -0
  86. lib/Linfo/Extension/Smb.php +323 -0
  87. lib/Linfo/Extension/Soldat.php +250 -0
  88. lib/Linfo/Extension/Transmission.php +363 -0
  89. lib/Linfo/Extension/Truecrypt.php +215 -0
  90. lib/Linfo/Extension/Utorrent.php +297 -0
  91. lib/Linfo/Lang/de.php +101 -0
  92. lib/Linfo/Lang/en.php +107 -0
  93. lib/Linfo/Lang/fi.php +104 -0
  94. lib/Linfo/Lang/fr.php +101 -0
  95. lib/Linfo/Lang/it.php +105 -0
  96. lib/Linfo/Lang/pl.php +103 -0
  97. lib/Linfo/Lang/pt.php +104 -0
  98. lib/Linfo/Lang/zh.php +105 -0
  99. lib/Linfo/Linfo.php +504 -0
  100. lib/Linfo/Meta/Errors.php +77 -0
  101. lib/Linfo/Meta/Timer.php +35 -0
  102. lib/Linfo/OS/BSDcommon.php +157 -0
  103. lib/Linfo/OS/Darwin.php +622 -0
  104. lib/Linfo/OS/DragonFly.php +415 -0
  105. lib/Linfo/OS/FreeBSD.php +729 -0
  106. lib/Linfo/OS/Linux.php +1729 -0
  107. lib/Linfo/OS/Minix.php +139 -0
  108. lib/Linfo/OS/NetBSD.php +498 -0
  109. lib/Linfo/OS/OS.php +106 -0
  110. lib/Linfo/OS/OpenBSD.php +469 -0
  111. lib/Linfo/OS/SunOS.php +456 -0
  112. lib/Linfo/OS/Unixcommon.php +77 -0
  113. lib/Linfo/OS/Windows.php +769 -0
  114. lib/Linfo/Output/Html.php +1006 -0
  115. lib/Linfo/Output/Json.php +45 -0
  116. lib/Linfo/Output/Ncurses.php +260 -0
  117. lib/Linfo/Output/Output.php +29 -0
  118. lib/Linfo/Output/Serialized.php +27 -0
  119. lib/Linfo/Output/Xml.php +326 -0
  120. lib/Linfo/Parsers/CallExt.php +130 -0
  121. lib/Linfo/Parsers/Hddtemp.php +180 -0
  122. lib/Linfo/Parsers/Hwpci.php +308 -0
  123. lib/Linfo/Parsers/Mbmon.php +93 -0
  124. lib/Linfo/Parsers/Sensord.php +70 -0
  125. package.xml +2 -0
  126. skin/adminhtml/default/default/neklo/core/css/style.css +52 -0
  127. skin/adminhtml/default/default/neklo/core/images/ok.gif +0 -0
  128. skin/adminhtml/default/default/neklo/core/images/update.gif +0 -0
  129. skin/adminhtml/default/default/neklo/monitor/css/styles.css +6 -0
app/code/community/Neklo/Core/Block/System/Contact.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Contact extends Mage_Adminhtml_Block_System_Config_Form_Fieldset
4
+ {
5
+ protected function _getHeaderHtml($element)
6
+ {
7
+ return parent::_getHeaderHtml($element) . $this->_getAfterHeaderHtml();
8
+ }
9
+
10
+ protected function _getAfterHeaderHtml()
11
+ {
12
+ $subscribeButton = $this->getLayout()->createBlock('neklo_core/system_contact_header', 'neklo_core_contact_header');
13
+ $subscribeButton->setTemplate('neklo/core/system/contact/header.phtml');
14
+ return $subscribeButton->toHtml();
15
+ }
16
+ }
app/code/community/Neklo/Core/Block/System/Contact/Header.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Contact_Header extends Mage_Adminhtml_Block_Template
4
+ {
5
+ const STORE_URL = 'http://store.neklo.com/';
6
+ const STORE_LABEL = 'store.neklo.com';
7
+
8
+ public function getStoreUrl()
9
+ {
10
+ return self::STORE_URL;
11
+ }
12
+
13
+ public function getStoreLabel()
14
+ {
15
+ return self::STORE_LABEL;
16
+ }
17
+ }
app/code/community/Neklo/Core/Block/System/Contact/Send.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Contact_Send extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ public function render(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ $element->setScope(false);
8
+ $element->setCanUseWebsiteValue(false);
9
+ $element->setCanUseDefaultValue(false);
10
+ return parent::render($element);
11
+ }
12
+
13
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
14
+ {
15
+ $subscribeButton = $this->getLayout()->createBlock('neklo_core/system_contact_send_button', 'neklo_core_contact_send');
16
+ $subscribeButton->setTemplate('neklo/core/system/contact/button.phtml');
17
+ $subscribeButton->setContainerId($element->getContainer()->getHtmlId());
18
+ return $subscribeButton->toHtml();
19
+ }
20
+ }
app/code/community/Neklo/Core/Block/System/Contact/Send/Button.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Contact_Send_Button extends Mage_Adminhtml_Block_Template
4
+ {
5
+ /**
6
+ * @return Mage_Adminhtml_Block_Widget_Button
7
+ */
8
+ public function getButton()
9
+ {
10
+ $button = $this->getLayout()->createBlock('adminhtml/widget_button');
11
+ $button
12
+ ->setType('button')
13
+ ->setLabel($this->__('Send'))
14
+ ->setStyle("width:280px")
15
+ ->setId('neklo_core_contact_send')
16
+ ;
17
+ return $button;
18
+ }
19
+
20
+ /**
21
+ * @return string
22
+ */
23
+ public function getButtonHtml()
24
+ {
25
+ return $this->getButton()->toHtml();
26
+ }
27
+
28
+ /**
29
+ * @return string
30
+ */
31
+ public function getContainerId()
32
+ {
33
+ return parent::getContainerId();
34
+ }
35
+ }
app/code/community/Neklo/Core/Block/System/Extension.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Extension extends Mage_Adminhtml_Block_System_Config_Form_Fieldset
4
+ {
5
+ protected function _getHeaderHtml($element)
6
+ {
7
+ return parent::_getHeaderHtml($element) . $this->_getContentHtml();
8
+ }
9
+
10
+ protected function _getContentHtml()
11
+ {
12
+ $extensionListBlock = $this->getLayout()->createBlock('neklo_core/system_extension_list', 'neklo_core_extension_list');
13
+ $extensionListBlock->setTemplate('neklo/core/system/extension/list.phtml');
14
+ return $extensionListBlock->toHtml();
15
+ }
16
+ }
app/code/community/Neklo/Core/Block/System/Extension/List.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Extension_List extends Mage_Adminhtml_Block_Template
4
+ {
5
+ const DOMAIN = 'http://store.neklo.com/';
6
+ const IMAGE_EXTENSION = '.jpg';
7
+
8
+ protected $_feedData = null;
9
+
10
+ /**
11
+ * @param string $code
12
+ *
13
+ * @return bool
14
+ */
15
+ public function canShowExtension($code)
16
+ {
17
+ $feedData = $this->_getExtensionInfo(strtolower($code));
18
+ return !!count($feedData);
19
+ }
20
+
21
+ /**
22
+ * @return array
23
+ */
24
+ public function getExtensionList()
25
+ {
26
+ return Mage::helper('neklo_core/extension')->getModuleConfigList();
27
+ }
28
+
29
+ /**
30
+ * @param string $code
31
+ *
32
+ * @return mixed
33
+ */
34
+ public function getExtensionName($code)
35
+ {
36
+ $feedData = $this->_getExtensionInfo(strtolower($code));
37
+ if (!array_key_exists('name', $feedData)) {
38
+ return $code;
39
+ }
40
+ return $feedData['name'];
41
+ }
42
+
43
+ /**
44
+ * @param string $code
45
+ * @param $config
46
+ *
47
+ * @return bool
48
+ */
49
+ public function isExtensionVersionOutdated($code, $config)
50
+ {
51
+ $currentVersion = $this->getExtensionVersion($config);
52
+ $lastVersion = $this->getLastExtensionVersion($code);
53
+ return version_compare($currentVersion, $lastVersion) === -1;
54
+ }
55
+
56
+ public function getExtensionVersion($config)
57
+ {
58
+ $version = (string)$config->version;
59
+ if (!$version) {
60
+ return '';
61
+ }
62
+ return $version;
63
+ }
64
+
65
+ public function getLastExtensionVersion($code)
66
+ {
67
+ $feedData = $this->_getExtensionInfo(strtolower($code));
68
+ if (!array_key_exists('version', $feedData)) {
69
+ return '0';
70
+ }
71
+ return $feedData['version'];
72
+ }
73
+
74
+ public function getExtensionUrl($code)
75
+ {
76
+ $feedData = $this->_getExtensionInfo(strtolower($code));
77
+ if (!array_key_exists('url', $feedData)) {
78
+ return null;
79
+ }
80
+ return $feedData['url'];
81
+ }
82
+
83
+ public function getImageUrl($code)
84
+ {
85
+ $imgUrl = self::DOMAIN . 'cache/' . ($this->_getCacheKey() ? $this->_getCacheKey() . '/' : '') . strtolower($code) . self::IMAGE_EXTENSION;
86
+ return $imgUrl;
87
+ }
88
+
89
+ protected function _getCacheKey()
90
+ {
91
+ return Mage::helper('neklo_core/extension')->getCacheKey();
92
+ }
93
+
94
+ protected function _getExtensionInfo($code)
95
+ {
96
+ if (is_null($this->_feedData)) {
97
+ $this->_feedData = Mage::getModel('neklo_core/feed_extension')->getFeed();
98
+ }
99
+ if (!array_key_exists($code, $this->_feedData)) {
100
+ return array();
101
+ }
102
+ return $this->_feedData[$code];
103
+ }
104
+ }
app/code/community/Neklo/Core/Block/System/Newsletter/Subscribe.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Newsletter_Subscribe extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ public function render(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ $element->setScope(false);
8
+ $element->setCanUseWebsiteValue(false);
9
+ $element->setCanUseDefaultValue(false);
10
+ return parent::render($element);
11
+ }
12
+
13
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
14
+ {
15
+ $subscribeButton = $this->getLayout()->createBlock('neklo_core/system_newsletter_subscribe_button', 'neklo_core_subscribe');
16
+ $subscribeButton->setTemplate('neklo/core/system/subscribe/button.phtml');
17
+ $subscribeButton->setContainerId($element->getContainer()->getHtmlId());
18
+ return $subscribeButton->toHtml();
19
+ }
20
+ }
app/code/community/Neklo/Core/Block/System/Newsletter/Subscribe/Button.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Block_System_Newsletter_Subscribe_Button extends Mage_Adminhtml_Block_Template
4
+ {
5
+ /**
6
+ * @return Mage_Adminhtml_Block_Widget_Button
7
+ */
8
+ public function getButton()
9
+ {
10
+ $button = $this->getLayout()->createBlock('adminhtml/widget_button');
11
+ $button
12
+ ->setType('button')
13
+ ->setLabel($this->__('Subscribe'))
14
+ ->setStyle("width:280px")
15
+ ->setId('neklo_core_subscribe')
16
+ ;
17
+ return $button;
18
+ }
19
+
20
+ /**
21
+ * @return string
22
+ */
23
+ public function getButtonHtml()
24
+ {
25
+ return $this->getButton()->toHtml();
26
+ }
27
+
28
+ /**
29
+ * @return string
30
+ */
31
+ public function getContainerId()
32
+ {
33
+ return parent::getContainerId();
34
+ }
35
+ }
app/code/community/Neklo/Core/Helper/Config.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Helper_Config extends Mage_Compiler_Helper_Data
4
+ {
5
+ const NOTIFICATION_TYPE = 'neklo_core/notification/type';
6
+
7
+ /**
8
+ * @param null|int|Mage_Core_Model_Store $store
9
+ *
10
+ * @return array
11
+ */
12
+ public function getNotificationTypeList($store = null)
13
+ {
14
+ return explode(',', Mage::getStoreConfig(self::NOTIFICATION_TYPE, $store));
15
+ }
16
+ }
app/code/community/Neklo/Core/Helper/Data.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+ const DOMAIN = 'http://store.neklo.com/';
6
+ const LOGO_IMG = 'neklo.png';
7
+
8
+ public function __()
9
+ {
10
+ $args = func_get_args();
11
+ if ($args[0] == '[NEKLO]') {
12
+ return '<img src="' . $this->_getLogoUrl() . '" height="11" alt="Neklo" title="" />';
13
+ }
14
+ $expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->_getModuleName());
15
+ array_unshift($args, $expr);
16
+ return Mage::app()->getTranslator()->translate($args);
17
+ }
18
+
19
+ protected function _getLogoUrl()
20
+ {
21
+ return self::DOMAIN . 'cache/' . ($this->_getCacheKey() ? $this->_getCacheKey() . '/' : '') . self::LOGO_IMG;
22
+ }
23
+
24
+ protected function _getCacheKey()
25
+ {
26
+ return Mage::helper('neklo_core/extension')->getCacheKey();
27
+ }
28
+ }
app/code/community/Neklo/Core/Helper/Extension.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Helper_Extension extends Mage_Core_Helper_Abstract
4
+ {
5
+ protected $_protectedModuleCodeList = array(
6
+ 'Neklo_Core'
7
+ );
8
+
9
+ protected $_cacheKey = null;
10
+ protected $_moduleConfigList = null;
11
+ protected $_moduleList = null;
12
+
13
+ public function getModuleList()
14
+ {
15
+ if (is_null($this->_moduleList)) {
16
+ $moduleList = array();
17
+ foreach ($this->getModuleConfigList() as $moduleCode => $moduleConfig) {
18
+ $moduleList[$moduleCode] = array(
19
+ 'name' => $moduleConfig->extension_name ? $moduleConfig->extension_name : $moduleCode,
20
+ 'version' => $moduleConfig->version,
21
+ );
22
+ }
23
+ $this->_moduleList = $moduleList;
24
+ }
25
+ return $this->_moduleList;
26
+ }
27
+
28
+ public function getModuleConfigList()
29
+ {
30
+ if (is_null($this->_moduleConfigList)) {
31
+ $moduleConfigList = (array)Mage::getConfig()->getNode('modules')->children();
32
+ ksort($moduleConfigList);
33
+ $moduleList = array();
34
+ foreach ($moduleConfigList as $moduleCode => $moduleConfig) {
35
+ if (!$this->_canShowExtension($moduleCode, $moduleConfig)) {
36
+ continue;
37
+ }
38
+ $moduleList[$moduleCode] = $moduleConfig;
39
+ }
40
+ $this->_moduleConfigList = $moduleList;
41
+ }
42
+ return $this->_moduleConfigList;
43
+ }
44
+
45
+ public function getCacheKey()
46
+ {
47
+ if (is_null($this->_cacheKey)) {
48
+ $cacheList = array();
49
+ foreach ($this->getModuleConfigList() as $moduleCode => $moduleConfig) {
50
+ $version = explode('.', $moduleConfig->version);
51
+ $version = (intval($version[0]) - 1) << 12 | intval($version[1]) << 6 | intval($version[2]) << 0;
52
+ $cacheList[] = dechex(intval($moduleConfig->build)) . 't' . dechex(intval($moduleConfig->build) - hexdec($moduleConfig->encoding)) . 't' . substr(md5(strtolower($moduleCode)), 0, 2) . $version;
53
+ }
54
+ $this->_cacheKey = implode('n', $cacheList);
55
+ }
56
+ return $this->_cacheKey;
57
+ }
58
+
59
+ /**
60
+ * @param string $code
61
+ * @param Mage_Core_Model_Config_Element $config
62
+ *
63
+ * @return bool
64
+ */
65
+ protected function _canShowExtension($code, $config)
66
+ {
67
+ if (!$code || !$config) {
68
+ return false;
69
+ }
70
+ if (!($config instanceof Mage_Core_Model_Config_Element)) {
71
+ return false;
72
+ }
73
+ if (!is_object($config) || !method_exists($config, 'is') || !$config->is('active', 'true')) {
74
+ return false;
75
+ }
76
+ if (!$this->_isNekloExtension($code)) {
77
+ return false;
78
+ }
79
+ if ($this->_isProtectedExtension($code)) {
80
+ return false;
81
+ }
82
+ return true;
83
+ }
84
+
85
+ /**
86
+ * @param string $code
87
+ *
88
+ * @return bool
89
+ */
90
+ protected function _isNekloExtension($code)
91
+ {
92
+ return (strstr($code,'Neklo_') !== false);
93
+ }
94
+
95
+ /**
96
+ * @param string $code
97
+ *
98
+ * @return bool
99
+ */
100
+ protected function _isProtectedExtension($code)
101
+ {
102
+ return in_array($code, $this->_protectedModuleCodeList);
103
+ }
104
+ }
app/code/community/Neklo/Core/Model/Feed.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Model_Feed extends Mage_AdminNotification_Model_Feed
4
+ {
5
+ const XML_USE_HTTPS_PATH = 'neklo_core/notification/use_https';
6
+ const XML_FEED_URL_PATH = 'neklo_core/notification/feed_url';
7
+ const XML_FREQUENCY_PATH = 'neklo_core/notification/frequency';
8
+
9
+ const LAST_CHECK_CACHE_KEY = 'neklo_core_admin_notifications_last_check';
10
+
11
+ public function getFrequency()
12
+ {
13
+ return Mage::getStoreConfig(self::XML_FREQUENCY_PATH) * 3600;
14
+ }
15
+
16
+ public function getLastUpdate()
17
+ {
18
+ return Mage::app()->loadCache(self::LAST_CHECK_CACHE_KEY);
19
+ }
20
+
21
+ public function setLastUpdate()
22
+ {
23
+ Mage::app()->saveCache(time(), self::LAST_CHECK_CACHE_KEY);
24
+ return $this;
25
+ }
26
+
27
+ public function getFeedUrl()
28
+ {
29
+ if (is_null($this->_feedUrl)) {
30
+ $this->_feedUrl = (Mage::getStoreConfigFlag(self::XML_USE_HTTPS_PATH) ? 'https://' : 'http://') . Mage::getStoreConfig(self::XML_FEED_URL_PATH);
31
+ }
32
+ return $this->_feedUrl;
33
+ }
34
+
35
+ public function checkUpdate()
36
+ {
37
+ if (($this->getFrequency() + $this->getLastUpdate()) > time()) {
38
+ return $this;
39
+ }
40
+
41
+ $feedData = array();
42
+ $feedXml = $this->getFeedData();
43
+ if ($feedXml && $feedXml->channel && $feedXml->channel->item) {
44
+ foreach ($feedXml->channel->item as $item) {
45
+ if (!$this->_isAllowedItem($item)) {
46
+ continue;
47
+ }
48
+ $feedData[] = array(
49
+ 'severity' => (int)$item->severity,
50
+ 'date_added' => $this->getDate((string)$item->pubDate),
51
+ 'title' => (string)$item->title,
52
+ 'description' => (string)$item->description,
53
+ 'url' => (string)$item->link,
54
+ );
55
+ }
56
+ if ($feedData) {
57
+ $inboxParser = Mage::getModel('adminnotification/inbox');
58
+ if ($inboxParser) {
59
+ $inboxParser->parse(array_reverse($feedData));
60
+ }
61
+ }
62
+ }
63
+ $this->setLastUpdate();
64
+ return $this;
65
+ }
66
+
67
+ protected function _isAllowedItem($item)
68
+ {
69
+ $itemType = $item->type ? $item->type : Neklo_Core_Model_Source_Subscription_Type::INFO_CODE;
70
+ $allowedTypeList = Mage::helper('neklo_core/config')->getNotificationTypeList();
71
+ if ($itemType == Neklo_Core_Model_Source_Subscription_Type::UPDATE_CODE) {
72
+ if (in_array(Neklo_Core_Model_Source_Subscription_Type::UPDATE_ALL_CODE, $allowedTypeList)) {
73
+ return true;
74
+ }
75
+ if (in_array(Neklo_Core_Model_Source_Subscription_Type::UPDATE_CODE, $allowedTypeList)) {
76
+ $installedExtensionList = array_keys(Mage::helper('neklo_core/extension')->getModuleList());
77
+ $isPresent = false;
78
+ foreach ($item->extension->children() as $extensionCode) {
79
+ if (in_array((string)$extensionCode, $installedExtensionList)) {
80
+ $isPresent = true;
81
+ }
82
+ }
83
+ return $isPresent;
84
+ }
85
+ }
86
+ if (!in_array($itemType, $allowedTypeList)) {
87
+
88
+ return false;
89
+ }
90
+ return true;
91
+ }
92
+ }
app/code/community/Neklo/Core/Model/Feed/Extension.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Model_Feed_Extension
4
+ {
5
+ const FEED_URL = 'http://store.neklo.com/feed.json';
6
+ const CACHE_ID = 'NEKLO_EXTENSION_FEED';
7
+ const CACHE_LIFETIME = 172800;
8
+
9
+ public function getFeed()
10
+ {
11
+ if (!$feed = Mage::app()->loadCache(self::CACHE_ID)) {
12
+ $feed = $this->_getFeedFromResource();
13
+ $this->_save($feed);
14
+ }
15
+ $feedArray = Mage::helper('core')->jsonDecode($feed);
16
+ if (!is_array($feedArray)) {
17
+ $feedArray = array();
18
+ }
19
+ return $feedArray;
20
+ }
21
+
22
+ protected function _getFeedFromResource()
23
+ {
24
+ $ch = curl_init();
25
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
26
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
27
+ curl_setopt($ch, CURLOPT_URL, self::FEED_URL);
28
+ curl_setopt(
29
+ $ch,
30
+ CURLOPT_HTTPHEADER,
31
+ array(
32
+ 'Content-Type: application/json'
33
+ )
34
+ );
35
+ $result = curl_exec($ch);
36
+ if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200) {
37
+ $result = '{}';
38
+ }
39
+ curl_close($ch);
40
+ return $result;
41
+ }
42
+
43
+ protected function _save($feed)
44
+ {
45
+ Mage::app()->saveCache($feed, self::CACHE_ID, array(), self::CACHE_LIFETIME);
46
+ return $this;
47
+ }
48
+ }
app/code/community/Neklo/Core/Model/Observer.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Model_Observer
4
+ {
5
+ public function checkUpdate(Varien_Event_Observer $observer)
6
+ {
7
+ if (Mage::getSingleton('admin/session')->isLoggedIn()) {
8
+ Mage::getModel('neklo_core/feed')->checkUpdate();
9
+ }
10
+ }
11
+
12
+ public function sortModuleTabList(Varien_Event_Observer $observer)
13
+ {
14
+ $configContainerBlock = $observer->getBlock();
15
+ if (!$configContainerBlock instanceof Mage_Adminhtml_Block_System_Config_Tabs) {
16
+ return null;
17
+ }
18
+ $tabList = $configContainerBlock->getTabs();
19
+ foreach ($tabList as $tab) {
20
+ if ($tab->getId() !== 'neklo') {
21
+ continue;
22
+ }
23
+
24
+ $sectionList = $tab->getSections();
25
+ if (!$sectionList || !$sectionList->getSize()) {
26
+ continue;
27
+ }
28
+
29
+ $sectionListArray = $sectionList->toArray();
30
+ $sectionListArray = $sectionListArray['items'];
31
+ usort($sectionListArray, array($this, '_sort'));
32
+
33
+ $tab->getSections()->clear();
34
+ foreach ($sectionListArray as $_section) {
35
+ $section = new Varien_Object($_section);
36
+ $section->setId($_section['id']);
37
+ $tab->getSections()->addItem($section);
38
+ }
39
+ }
40
+ }
41
+
42
+ protected function _sort($a, $b)
43
+ {
44
+ if ($a['id'] == 'neklo_core') {
45
+ return 1;
46
+ }
47
+ if ($b['id'] == 'neklo_core') {
48
+ return -1;
49
+ }
50
+ return strcasecmp($a['label'], $b['label']);
51
+ }
52
+ }
app/code/community/Neklo/Core/Model/Source/Reason.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Model_Source_Reason
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ $reasonList = array();
8
+ $reasonList[''] = $this->__('Please select');
9
+ $reasonList['Magento v' . Mage::getVersion()] = $this->__('Magento Related Support');
10
+ $reasonList['New Extension'] = $this->__('Request New Extension Development');
11
+
12
+ $moduleList = Mage::helper('neklo_core/extension')->getModuleList();
13
+ foreach ($moduleList as $moduleCode => $moduleData) {
14
+ $moduleTitle = $moduleData['name'] . ' v' . $moduleData['version'];
15
+ $reasonList[$moduleCode . ' ' . $moduleData['version']] = $this->__('%s Support', $moduleTitle);
16
+ }
17
+
18
+ $reasonList['other'] = $this->__('Other Reason');
19
+ return $reasonList;
20
+ }
21
+
22
+ public function __()
23
+ {
24
+ $args = func_get_args();
25
+ $helper = Mage::helper('neklo_core');
26
+ return call_user_func_array(array($helper, "__"), $args);
27
+ }
28
+ }
app/code/community/Neklo/Core/Model/Source/Subscription/Type.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Model_Source_Subscription_Type
4
+ {
5
+ const UPDATE_CODE = 'UPDATE';
6
+ const UPDATE_LABEL = 'My extensions updates';
7
+
8
+ const RELEASE_CODE = 'RELEASE';
9
+ const RELEASE_LABEL = 'New Releases';
10
+
11
+ const UPDATE_ALL_CODE = 'UPDATE_ALL';
12
+ const UPDATE_ALL_LABEL = 'All extensions updates';
13
+
14
+ const PROMO_CODE = 'PROMO';
15
+ const PROMO_LABEL = 'Promotions / Discounts';
16
+
17
+ const INFO_CODE = 'INFO';
18
+ const INFO_LABEL = 'Other information';
19
+
20
+ public function toOptionArray()
21
+ {
22
+ return array(
23
+ array(
24
+ 'value' => self::UPDATE_CODE,
25
+ 'label' => Mage::helper('neklo_core')->__(self::UPDATE_LABEL),
26
+ ),
27
+ array(
28
+ 'value' => self::RELEASE_CODE,
29
+ 'label' => Mage::helper('neklo_core')->__(self::RELEASE_LABEL),
30
+ ),
31
+ array(
32
+ 'value' => self::UPDATE_ALL_CODE,
33
+ 'label' => Mage::helper('neklo_core')->__(self::UPDATE_ALL_LABEL),
34
+ ),
35
+ array(
36
+ 'value' => self::PROMO_CODE,
37
+ 'label' => Mage::helper('neklo_core')->__(self::PROMO_LABEL),
38
+ ),
39
+ array(
40
+ 'value' => self::INFO_CODE,
41
+ 'label' => Mage::helper('neklo_core')->__(self::INFO_LABEL),
42
+ )
43
+ );
44
+ }
45
+ }
app/code/community/Neklo/Core/Model/System/Config/Backend/Empty.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Model_System_Config_Backend_Empty extends Mage_Core_Model_Config_Data
4
+ {
5
+ public function getValue()
6
+ {
7
+ return null;
8
+ }
9
+ }
app/code/community/Neklo/Core/controllers/Adminhtml/Neklo/Core/ContactController.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Adminhtml_Neklo_Core_ContactController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ const CONTACT_URL = 'http://store.neklo.com/neklo_support/index/index/';
6
+
7
+ public function sendAction()
8
+ {
9
+ $result = array(
10
+ 'success' => true,
11
+ );
12
+ try {
13
+ $data = $this->getRequest()->getPost();
14
+ $data['version'] = Mage::getVersion();
15
+ $data['url'] = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
16
+ $data['id'] = '<order_item_customer></order_item_customer>';
17
+ $this->_sendContactEmail($data);
18
+ } catch (Exception $e) {
19
+ Mage::logException($e);
20
+ $result['success'] = false;
21
+ $this->getResponse()->setBody(Zend_Json::encode($result));
22
+ return;
23
+ }
24
+ $this->getResponse()->setBody(Zend_Json::encode($result));
25
+ }
26
+
27
+ protected function _sendContactEmail($data)
28
+ {
29
+ $params = Mage::helper('core')->urlEncode(Mage::helper('core')->jsonEncode($data));
30
+ if ($params) {
31
+ $httpClient = new Varien_Http_Client();
32
+ $httpClient
33
+ ->setMethod(Zend_Http_Client::POST)
34
+ ->setUri(self::CONTACT_URL)
35
+ ->setConfig(
36
+ array(
37
+ 'maxredirects' => 0,
38
+ 'timeout' => 30,
39
+ )
40
+ )
41
+ ->setRawData($params)
42
+ ->request()
43
+ ;
44
+ }
45
+ }
46
+
47
+ protected function _isAllowed()
48
+ {
49
+ return Mage::getSingleton('admin/session')->isAllowed('system/config/neklo_core');
50
+ }
51
+ }
app/code/community/Neklo/Core/controllers/Adminhtml/Neklo/Core/NewsletterController.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Core_Adminhtml_Neklo_Core_NewsletterController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ const SUBSCRIBE_URL = 'http://store.neklo.com/neklo_subscribe/index/index/';
6
+
7
+ public function subscribeAction()
8
+ {
9
+ $result = array(
10
+ 'success' => true,
11
+ );
12
+ try {
13
+ $data = $this->getRequest()->getPost();
14
+ $this->_subscribe($data);
15
+ } catch (Exception $e) {
16
+ $result['success'] = false;
17
+ $this->getResponse()->setBody(Zend_Json::encode($result));
18
+ return;
19
+ }
20
+ $this->getResponse()->setBody(Zend_Json::encode($result));
21
+ }
22
+
23
+ protected function _subscribe($data)
24
+ {
25
+ $params = Mage::helper('core')->urlEncode(Mage::helper('core')->jsonEncode($data));
26
+ if ($params) {
27
+ $httpClient = new Varien_Http_Client();
28
+ $httpClient
29
+ ->setMethod(Zend_Http_Client::POST)
30
+ ->setUri(self::SUBSCRIBE_URL)
31
+ ->setConfig(
32
+ array(
33
+ 'maxredirects' => 0,
34
+ 'timeout' => 30,
35
+ )
36
+ )
37
+ ->setRawData($params)
38
+ ->request()
39
+ ;
40
+ }
41
+ }
42
+
43
+ protected function _isAllowed()
44
+ {
45
+ return Mage::getSingleton('admin/session')->isAllowed('system/config/neklo_core');
46
+ }
47
+ }
app/code/community/Neklo/Core/etc/adminhtml.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <all>
6
+ <title>Allow Everything</title>
7
+ </all>
8
+ <admin>
9
+ <children>
10
+ <system>
11
+ <children>
12
+ <config>
13
+ <children>
14
+ <neklo_core translate="title" module="neklo_core">
15
+ <title>Neklo Extensions &amp; Contact</title>
16
+ <sort_order>9999</sort_order>
17
+ </neklo_core>
18
+ </children>
19
+ </config>
20
+ </children>
21
+ </system>
22
+ </children>
23
+ </admin>
24
+ </resources>
25
+ </acl>
26
+ </config>
app/code/community/Neklo/Core/etc/config.xml ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Neklo_Core>
5
+ <version>1.1.1</version>
6
+ </Neklo_Core>
7
+ </modules>
8
+ <admin>
9
+ <routers>
10
+ <adminhtml>
11
+ <args>
12
+ <modules>
13
+ <neklo before="Mage_Adminhtml">Neklo_Core_Adminhtml</neklo>
14
+ </modules>
15
+ </args>
16
+ </adminhtml>
17
+ </routers>
18
+ </admin>
19
+ <global>
20
+ <blocks>
21
+ <neklo_core>
22
+ <class>Neklo_Core_Block</class>
23
+ </neklo_core>
24
+ </blocks>
25
+ <helpers>
26
+ <neklo_core>
27
+ <class>Neklo_Core_Helper</class>
28
+ </neklo_core>
29
+ </helpers>
30
+ <models>
31
+ <neklo_core>
32
+ <class>Neklo_Core_Model</class>
33
+ </neklo_core>
34
+ </models>
35
+ </global>
36
+ <adminhtml>
37
+ <layout>
38
+ <updates>
39
+ <awall module="Neklo_Core">
40
+ <file>neklo/core.xml</file>
41
+ </awall>
42
+ </updates>
43
+ </layout>
44
+ <translate>
45
+ <modules>
46
+ <Neklo_Core>
47
+ <files>
48
+ <default>Neklo_Core.csv</default>
49
+ </files>
50
+ </Neklo_Core>
51
+ </modules>
52
+ </translate>
53
+ <events>
54
+ <controller_action_predispatch>
55
+ <observers>
56
+ <neklo_core_admin_notification>
57
+ <class>neklo_core/observer</class>
58
+ <method>checkUpdate</method>
59
+ </neklo_core_admin_notification>
60
+ </observers>
61
+ </controller_action_predispatch>
62
+ <adminhtml_block_html_before>
63
+ <observers>
64
+ <sort_module_tab_list>
65
+ <class>neklo_core/observer</class>
66
+ <method>sortModuleTabList</method>
67
+ </sort_module_tab_list>
68
+ </observers>
69
+ </adminhtml_block_html_before>
70
+ </events>
71
+ </adminhtml>
72
+ <default>
73
+ <neklo_core>
74
+ <notification>
75
+ <feed_url>store.neklo.com/magento-update/magento-notifications.rss</feed_url>
76
+ <use_https>0</use_https>
77
+ <frequency>24</frequency>
78
+ <last_update>0</last_update>
79
+ <type>UPDATE,RELEASE,UPDATE_ALL,PROMO,INFO</type>
80
+ </notification>
81
+ </neklo_core>
82
+ </default>
83
+ </config>
app/code/community/Neklo/Core/etc/system.xml ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <neklo translate="label" module="neklo_core">
5
+ <label>[NEKLO]</label>
6
+ <sort_order>310</sort_order>
7
+ </neklo>
8
+ </tabs>
9
+ <sections>
10
+ <neklo_core translate="label" module="neklo_core">
11
+ <label>Extensions &amp; Contact</label>
12
+ <tab>neklo</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>9999</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <extension translate="label">
20
+ <label>Extensions Information</label>
21
+ <frontend_type>text</frontend_type>
22
+ <frontend_model>neklo_core/system_extension</frontend_model>
23
+ <sort_order>10</sort_order>
24
+ <show_in_default>1</show_in_default>
25
+ <show_in_website>1</show_in_website>
26
+ <show_in_store>1</show_in_store>
27
+ <expanded>1</expanded>
28
+ </extension>
29
+ <contact translate="label">
30
+ <label>Contact Form</label>
31
+ <frontend_type>text</frontend_type>
32
+ <frontend_model>neklo_core/system_contact</frontend_model>
33
+ <sort_order>20</sort_order>
34
+ <show_in_default>1</show_in_default>
35
+ <show_in_website>0</show_in_website>
36
+ <show_in_store>0</show_in_store>
37
+ <expanded>1</expanded>
38
+ <fields>
39
+ <name translate="label">
40
+ <label>Contact Name</label>
41
+ <frontend_type>text</frontend_type>
42
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
43
+ <sort_order>10</sort_order>
44
+ <show_in_default>1</show_in_default>
45
+ <show_in_website>0</show_in_website>
46
+ <show_in_store>0</show_in_store>
47
+ </name>
48
+ <email translate="label">
49
+ <label>Contact Email</label>
50
+ <frontend_type>text</frontend_type>
51
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
52
+ <sort_order>20</sort_order>
53
+ <show_in_default>1</show_in_default>
54
+ <show_in_website>0</show_in_website>
55
+ <show_in_store>0</show_in_store>
56
+ </email>
57
+ <subject translate="label">
58
+ <label>Subject</label>
59
+ <frontend_type>text</frontend_type>
60
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
61
+ <sort_order>30</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <show_in_website>0</show_in_website>
64
+ <show_in_store>0</show_in_store>
65
+ </subject>
66
+ <reason translate="label">
67
+ <label>Reason</label>
68
+ <frontend_type>select</frontend_type>
69
+ <source_model>neklo_core/source_reason</source_model>
70
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
71
+ <sort_order>40</sort_order>
72
+ <show_in_default>1</show_in_default>
73
+ <show_in_website>0</show_in_website>
74
+ <show_in_store>0</show_in_store>
75
+ </reason>
76
+ <other_reason translate="label">
77
+ <label>Other Reason</label>
78
+ <frontend_type>text</frontend_type>
79
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
80
+ <sort_order>50</sort_order>
81
+ <show_in_default>1</show_in_default>
82
+ <show_in_website>0</show_in_website>
83
+ <show_in_store>0</show_in_store>
84
+ <depends>
85
+ <reason>other</reason>
86
+ </depends>
87
+ </other_reason>
88
+ <message translate="label">
89
+ <label>Message</label>
90
+ <frontend_type>textarea</frontend_type>
91
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
92
+ <sort_order>60</sort_order>
93
+ <show_in_default>1</show_in_default>
94
+ <show_in_website>0</show_in_website>
95
+ <show_in_store>0</show_in_store>
96
+ </message>
97
+ <button>
98
+ <frontend_model>neklo_core/system_contact_send</frontend_model>
99
+ <sort_order>70</sort_order>
100
+ <show_in_default>1</show_in_default>
101
+ <show_in_website>0</show_in_website>
102
+ <show_in_store>0</show_in_store>
103
+ </button>
104
+ </fields>
105
+ </contact>
106
+ <newsletter translate="label">
107
+ <label>Subscribe to Newsletter</label>
108
+ <frontend_type>text</frontend_type>
109
+ <sort_order>30</sort_order>
110
+ <show_in_default>1</show_in_default>
111
+ <show_in_website>0</show_in_website>
112
+ <show_in_store>0</show_in_store>
113
+ <expanded>1</expanded>
114
+ <fields>
115
+ <name translate="label">
116
+ <label>Name</label>
117
+ <frontend_type>text</frontend_type>
118
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
119
+ <sort_order>10</sort_order>
120
+ <show_in_default>1</show_in_default>
121
+ <show_in_website>0</show_in_website>
122
+ <show_in_store>0</show_in_store>
123
+ </name>
124
+ <email translate="label">
125
+ <label>Email</label>
126
+ <frontend_type>text</frontend_type>
127
+ <backend_model>neklo_core/system_config_backend_empty</backend_model>
128
+ <sort_order>20</sort_order>
129
+ <show_in_default>1</show_in_default>
130
+ <show_in_website>0</show_in_website>
131
+ <show_in_store>0</show_in_store>
132
+ </email>
133
+ <subscribe_button>
134
+ <frontend_model>neklo_core/system_newsletter_subscribe</frontend_model>
135
+ <sort_order>30</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>0</show_in_website>
138
+ <show_in_store>0</show_in_store>
139
+ </subscribe_button>
140
+ </fields>
141
+ </newsletter>
142
+ <notification>
143
+ <label>Notifications</label>
144
+ <frontend_type>text</frontend_type>
145
+ <sort_order>40</sort_order>
146
+ <show_in_default>1</show_in_default>
147
+ <show_in_website>0</show_in_website>
148
+ <show_in_store>0</show_in_store>
149
+ <fields>
150
+ <type translate="label">
151
+ <label>Receive news about:</label>
152
+ <frontend_type>multiselect</frontend_type>
153
+ <sort_order>10</sort_order>
154
+ <show_in_default>1</show_in_default>
155
+ <show_in_website>0</show_in_website>
156
+ <show_in_store>0</show_in_store>
157
+ <source_model>neklo_core/source_subscription_type</source_model>
158
+ </type>
159
+ </fields>
160
+ </notification>
161
+ </groups>
162
+ </neklo_core>
163
+ </sections>
164
+ </config>
app/code/community/Neklo/Monitor/Autoload.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Autoload
4
+ {
5
+ static protected $_instance;
6
+ protected static $registered = false;
7
+
8
+ static public function instance()
9
+ {
10
+ if (!self::$_instance) {
11
+ self::$_instance = new self();
12
+ }
13
+ return self::$_instance;
14
+ }
15
+
16
+ static public function register()
17
+ {
18
+ if (self::$registered) {
19
+ return;
20
+ }
21
+ spl_autoload_register(array(self::instance(), 'autoload'), false, true);
22
+ self::$registered = true;
23
+ }
24
+
25
+ public function autoload($class)
26
+ {
27
+ $classFile = str_replace('\\', '/', $class) . '.php';
28
+ if (strpos($classFile, '/') !== false) {
29
+ include $classFile;
30
+ }
31
+ }
32
+ }
app/code/community/Neklo/Monitor/Block/Adminhtml/System/Config/Frontend/Label.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Block_Adminhtml_System_Config_Frontend_Label extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ return '<p id="'. $element->getHtmlId() . '">' . parent::_getElementHtml($element) .'</p>';
8
+ }
9
+ }
app/code/community/Neklo/Monitor/Block/Adminhtml/System/Config/Frontend/Status.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Block_Adminhtml_System_Config_Frontend_Status extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ $element->setBold(true);
8
+ if ($this->_getConfig()->isConnected()) {
9
+ $element->setValue($this->__('Connected to Gateway'));
10
+ $element->addClass('gateway_status')->addClass('success');
11
+ } else {
12
+ $element->setValue($this->__('Not Connected to Gateway'));
13
+ $element->addClass('gateway_status')->addClass('error');
14
+ }
15
+ return '<p id="'. $element->getHtmlId() . '" ' . $element->serialize($element->getHtmlAttributes()) . '>' . parent::_getElementHtml($element) .'</p>';
16
+ }
17
+
18
+ protected function _prepareLayout()
19
+ {
20
+ /* @var $head Mage_Page_Block_Html_Head */
21
+ $head = $this->getLayout()->getBlock('head');
22
+ if ($head) {
23
+ $head->addItem('skin_css', 'neklo/monitor/css/styles.css');
24
+ }
25
+ return parent::_prepareLayout();
26
+ }
27
+
28
+ /**
29
+ * @return Neklo_Monitor_Helper_Config
30
+ */
31
+ protected function _getConfig()
32
+ {
33
+ return Mage::helper('neklo_monitor/config');
34
+ }
35
+ }
app/code/community/Neklo/Monitor/Controller/Abstract.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Controller_Abstract extends Mage_Core_Controller_Front_Action
4
+ {
5
+ const PAGE_SIZE = 50;
6
+
7
+ // some controllers (auth) should skip isConnected checking @see preDispatch
8
+ protected $_allowConnectedOnly = true;
9
+
10
+ public function preDispatch()
11
+ {
12
+ parent::preDispatch();
13
+
14
+ if (!$this->_getConfigHelper()->isEnabled()) {
15
+ $this->_forward('noRoute');
16
+ return $this;
17
+ }
18
+
19
+ // similar to $this->getFullActionName()
20
+ $action = strtolower($this->getRequest()->getRequestedControllerName().'/'.
21
+ $this->getRequest()->getRequestedActionName());
22
+
23
+ if (!$this->_getRequestHelper()->isValidRequest($action)) {
24
+ $this->_forward('noRoute');
25
+ return $this;
26
+ }
27
+
28
+ if ($this->_allowConnectedOnly && !$this->_getConfigHelper()->isConnected()) {
29
+ $this->_forward('noRoute');
30
+ return $this;
31
+ }
32
+
33
+ $accountPlan = $this->_getRequestHelper()->getParam('plan', null);
34
+ if (!isset($accountPlan['type'])) {
35
+ $accountPlan['type'] = false;
36
+ }
37
+ if (!isset($accountPlan['frequency'])) {
38
+ $accountPlan['frequency'] = false;
39
+ }
40
+ $this->_getConfigHelper()->updateGatewayConfig(
41
+ array(
42
+ 'type' => $accountPlan['type'],
43
+ 'frequency' => $accountPlan['frequency'],
44
+ )
45
+ );
46
+
47
+ return $this;
48
+ }
49
+
50
+ protected function _jsonResult($data)
51
+ {
52
+ $this->getResponse()->setHeader('Content-type', 'text/json');
53
+ $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($data));
54
+ }
55
+
56
+ /**
57
+ * @return Neklo_Monitor_Helper_Request
58
+ */
59
+ protected function _getRequestHelper()
60
+ {
61
+ return Mage::helper('neklo_monitor/request');
62
+ }
63
+
64
+ /**
65
+ * @return Neklo_Monitor_Helper_Config
66
+ */
67
+ protected function _getConfigHelper()
68
+ {
69
+ return Mage::helper('neklo_monitor/config');
70
+ }
71
+
72
+ }
app/code/community/Neklo/Monitor/Helper/Config.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Helper_Config extends Mage_Core_Helper_Data
4
+ {
5
+ const GENERAL_IS_ENABLED = 'neklo_monitor/general/is_enabled';
6
+
7
+ const SECURITY_TOKEN = 'neklo_monitor/gateway/token';
8
+ const SECURITY_TOKEN_GENERATED_AT = 'neklo_monitor/gateway/token_generated_at';
9
+ const SECURITY_TOKEN_INTERVAL = 600;
10
+
11
+ const GATEWAY_SERVER_TYPE = 'neklo_monitor/gateway/server_type';
12
+ const GATEWAY_SID = 'neklo_monitor/gateway/sid';
13
+ const GATEWAY_PLAN = 'neklo_monitor/gateway/plan_type';
14
+ const GATEWAY_FREQUENCY = 'neklo_monitor/gateway/plan_frequency';
15
+ const GATEWAY_LAST_UPDATE = 'neklo_monitor/gateway/last_update';
16
+
17
+ protected $_gatewayConfig = array(
18
+ 'type' => self::GATEWAY_PLAN,
19
+ 'frequency' => self::GATEWAY_FREQUENCY,
20
+ );
21
+
22
+ public function isEnabled()
23
+ {
24
+ return Mage::getStoreConfigFlag(self::GENERAL_IS_ENABLED);
25
+ }
26
+
27
+ public function getToken()
28
+ {
29
+ if ($this->_isNeedUpdateToken()) {
30
+ return $this->_updateToken();
31
+ }
32
+ return Mage::getStoreConfig(self::SECURITY_TOKEN);
33
+ }
34
+
35
+ protected function _isNeedUpdateToken()
36
+ {
37
+ return (time() - $this->getTokenCreatedAt() > self::SECURITY_TOKEN_INTERVAL);
38
+ }
39
+
40
+ protected function _updateToken()
41
+ {
42
+ $hash = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM) . Mage::getStoreConfig(self::SECURITY_TOKEN);
43
+ $hash = Mage::helper('core')->encrypt($hash);
44
+ $hash = preg_replace("/[^A-Za-z]/", '', $hash);
45
+ $token = strtoupper(substr($hash, 0, 5));
46
+ $this->_saveConfig(self::SECURITY_TOKEN, $token);
47
+ $this->_updateTokenCreatedAt();
48
+
49
+ // reinit configuration cache
50
+ Mage::getConfig()->reinit();
51
+
52
+ return $token;
53
+ }
54
+
55
+ protected function _updateTokenCreatedAt($time = null)
56
+ {
57
+ if (is_null($time)) {
58
+ $time = time();
59
+ }
60
+ $this->_saveConfig(self::SECURITY_TOKEN_GENERATED_AT, (int)$time);
61
+ }
62
+
63
+ public function getTokenCreatedAt()
64
+ {
65
+ return (int)Mage::getStoreConfig(self::SECURITY_TOKEN_GENERATED_AT);
66
+ }
67
+
68
+ public function getGatewayServerType()
69
+ {
70
+ return Mage::getStoreConfig(self::GATEWAY_SERVER_TYPE);
71
+ }
72
+
73
+ public function getGatewayServerUri()
74
+ {
75
+ $serverType = $this->getGatewayServerType();
76
+ return Mage::getModel('neklo_monitor/system_config_source_server_type')->getServerUri($serverType);
77
+ }
78
+
79
+ public function getGatewaySid()
80
+ {
81
+ $serverType = $this->getGatewayServerType();
82
+ return Mage::helper('core')->decrypt(Mage::getStoreConfig(self::GATEWAY_SID . '_' . $serverType));
83
+ }
84
+
85
+ public function isConnected()
86
+ {
87
+ $serverType = $this->getGatewayServerType();
88
+ return Mage::getStoreConfigFlag(self::GATEWAY_SID . '_' . $serverType);
89
+ }
90
+
91
+ public function connect($sid)
92
+ {
93
+ $serverType = $this->getGatewayServerType();
94
+ $encryptedSid = Mage::helper('core')->encrypt($sid);
95
+ $this->_saveConfig(self::GATEWAY_SID . '_' . $serverType, $encryptedSid);
96
+ $this->_updateTokenCreatedAt(0); // invalidate Token
97
+
98
+ // reinit configuration cache
99
+ Mage::getConfig()->reinit();
100
+ }
101
+
102
+ public function updateGatewayConfig($config)
103
+ {
104
+ $serverType = $this->getGatewayServerType();
105
+ foreach ($this->_gatewayConfig as $field => $configPath) {
106
+ if (!array_key_exists($field, $config) || !$config[$field]) {
107
+ continue;
108
+ }
109
+ $this->_saveConfig($configPath . '_' . $serverType, $config[$field]);
110
+ }
111
+
112
+ // reinit configuration cache
113
+ Mage::getConfig()->reinit();
114
+ }
115
+
116
+ public function getGatewayPlan()
117
+ {
118
+ $serverType = $this->getGatewayServerType();
119
+ return Mage::getStoreConfig(self::GATEWAY_PLAN . '_' . $serverType);
120
+ }
121
+
122
+ public function getGatewayFrequency()
123
+ {
124
+ $serverType = $this->getGatewayServerType();
125
+ return Mage::getStoreConfig(self::GATEWAY_FREQUENCY . '_' . $serverType);
126
+ }
127
+
128
+ public function getGatewayLastUpdate()
129
+ {
130
+ $serverType = $this->getGatewayServerType();
131
+ return Mage::getStoreConfig(self::GATEWAY_LAST_UPDATE . '_' . $serverType);
132
+ }
133
+
134
+ public function updateGatewayLastUpdate()
135
+ {
136
+ $serverType = $this->getGatewayServerType();
137
+ $this->_saveConfig(self::GATEWAY_LAST_UPDATE . '_' . $serverType, time());
138
+
139
+ // reinit configuration cache
140
+ Mage::getConfig()->reinit();
141
+ }
142
+
143
+ protected function _saveConfig($path, $value, $scope = 'default', $scopeId = 0)
144
+ {
145
+ $configModel = Mage::getModel('core/config');
146
+ $configModel->saveConfig($path, $value, $scope, $scopeId);
147
+ }
148
+ }
app/code/community/Neklo/Monitor/Helper/Country.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Helper_Country extends Mage_Core_Helper_Data
4
+ {
5
+ protected $_countryList = array();
6
+
7
+ public function getCountryName($countryCode)
8
+ {
9
+ if (!array_key_exists($countryCode, $this->_countryList)) {
10
+ $countryName = Mage::app()->getLocale()->getCountryTranslation($countryCode);
11
+ if ($countryName) {
12
+ $this->_countryList[$countryCode] = $countryName;
13
+ } else {
14
+ $this->_countryList[$countryCode] = null;
15
+ }
16
+ }
17
+ return $this->_countryList[$countryCode];
18
+ }
19
+ }
app/code/community/Neklo/Monitor/Helper/Data.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Helper_Data extends Mage_Core_Helper_Data
4
+ {
5
+ }
app/code/community/Neklo/Monitor/Helper/Date.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Helper_Date extends Mage_Core_Helper_Data
4
+ {
5
+ public function convertToTimestamp($input)
6
+ {
7
+ $zDate = new Zend_Date($input);
8
+ return (int)$zDate->getTimestamp();
9
+ }
10
+
11
+ public function convertToString($time = null)
12
+ {
13
+ return date('Y-m-d H:i:s', $time);
14
+ }
15
+ }
app/code/community/Neklo/Monitor/Helper/Request.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Helper_Request
4
+ {
5
+ protected $_postData = null;
6
+
7
+ protected $_validateParamMap = array(
8
+ 'token' => 'isValidToken',
9
+ 'sid' => 'isValidSid',
10
+ 'device_id' => 'isValidDeviceId',
11
+ 'plan' => 'isValidPlan',
12
+ 'store' => 'isValidStore',
13
+ 'hash' => 'isValidHash',
14
+ 'from' => 'isValidTimestamp',
15
+ 'to' => 'isValidTimestamp',
16
+ 'group' => 'isValidGroupByPeriod',
17
+ 'status' => 'isValidOrderStatus',
18
+ );
19
+
20
+ protected $_requestParamMap = array(
21
+ 'common//common' => array(
22
+ 'sid',
23
+ 'device_id',
24
+ 'plan',
25
+ ),
26
+
27
+ 'auth/index' => array( // common//common + token
28
+ 'sid',
29
+ 'device_id',
30
+ 'plan',
31
+ 'token',
32
+ ),
33
+
34
+ 'state_indexer/list' => 'common//common',
35
+
36
+ 'state_cache/list' => 'common//common',
37
+
38
+ 'var_report/list' => 'common//common',
39
+ 'var_report/view' => array( // common//common + hash
40
+ 'sid',
41
+ 'device_id',
42
+ 'plan',
43
+ 'hash',
44
+ ),
45
+
46
+ 'var_log/list' => 'common//common',
47
+ 'var_log/view' => array( // common//common + hash
48
+ 'sid',
49
+ 'device_id',
50
+ 'plan',
51
+ 'hash',
52
+ ),
53
+
54
+ 'info/storeviewlist' => 'common//common',
55
+ 'info/total' => array( // common//common + store + from
56
+ 'sid',
57
+ 'device_id',
58
+ 'plan',
59
+ 'store',
60
+ 'from',
61
+ ),
62
+
63
+ 'dashboard//common' => array( // common//common + store
64
+ 'sid',
65
+ 'device_id',
66
+ 'plan',
67
+ 'store',
68
+ ),
69
+ 'dashboard/total' => 'dashboard//common',
70
+ 'dashboard/bestseller' => 'dashboard//common',
71
+ 'dashboard/mostviewed' => 'dashboard//common',
72
+ 'dashboard/newcustomers' => 'dashboard//common',
73
+ 'dashboard/topcustomers' => 'dashboard//common',
74
+ 'dashboard/lastorders' => 'dashboard//common',
75
+ 'dashboard/lastsearches' => 'dashboard//common',
76
+ 'dashboard/topsearches' => 'dashboard//common',
77
+ 'dashboard/chart' => 'dashboard//common',
78
+
79
+ 'customer/list' => 'dashboard//common',
80
+ 'customer/online' => 'dashboard//common',
81
+
82
+ 'product/outofstock' => 'dashboard//common',
83
+ 'order/list' => 'dashboard//common',
84
+
85
+ 'report_sales//common' => array(
86
+ 'sid',
87
+ 'device_id',
88
+ 'plan',
89
+ 'store',
90
+ 'from',
91
+ 'to',
92
+ 'group',
93
+ 'status',
94
+ ),
95
+ 'report_sales/order' => 'report_sales//common',
96
+ 'report_sales/tax' => 'report_sales//common',
97
+ 'report_sales/invoiced' => 'report_sales//common',
98
+ 'report_sales/shipping' => 'report_sales//common',
99
+ 'report_sales/refunded' => 'report_sales//common',
100
+ 'report_sales/coupons' => 'report_sales//common',
101
+ );
102
+
103
+ public function isValidRequest($route)
104
+ {
105
+ if (!array_key_exists($route, $this->_requestParamMap)) {
106
+ // check if 404 route requested - prevent infinite redirects (controller reached 100 redirects exception)
107
+ if (strpos($route, '/')) {
108
+ list($_contrl, $_action) = explode('/', $route);
109
+ if (strtolower($_action) == 'noroute') {
110
+ return true;
111
+ }
112
+ }
113
+ return false;
114
+ }
115
+ if (!is_array($this->_requestParamMap[$route])) {
116
+ // get parent action
117
+ $route = $this->_requestParamMap[$route];
118
+ if (!array_key_exists($route, $this->_requestParamMap)) {
119
+ return false;
120
+ }
121
+ }
122
+ $requestParamList = $this->_getPostData();
123
+ // Validate all params
124
+ foreach ($this->_requestParamMap[$route] as $param) {
125
+ if (!array_key_exists($param, $requestParamList)) {
126
+ return false;
127
+ }
128
+ if (array_key_exists($param, $this->_validateParamMap)) {
129
+ $validateMethod = $this->_validateParamMap[$param];
130
+ if (method_exists($this->getValidator(), $validateMethod) && !$this->getValidator()->$validateMethod($requestParamList[$param])) {
131
+ return false;
132
+ }
133
+ }
134
+ }
135
+ return true;
136
+ }
137
+
138
+ /**
139
+ * @return Neklo_Monitor_Helper_Request_Validator
140
+ */
141
+ public function getValidator()
142
+ {
143
+ return Mage::helper('neklo_monitor/request_validator');
144
+ }
145
+
146
+ public function getParam($keyName, $default)
147
+ {
148
+ $postData = $this->_getPostData();
149
+ if (array_key_exists($keyName, $postData) && $postData[$keyName]) {
150
+ return $postData[$keyName];
151
+ }
152
+ return $default;
153
+ }
154
+
155
+ /**
156
+ * @return array
157
+ */
158
+ protected function _getPostData()
159
+ {
160
+ if ($this->_postData === null) {
161
+
162
+ $this->_postData = array();
163
+ $input = file_get_contents('php://input');
164
+ if ($input
165
+ && substr_count('{', $input) == substr_count('}', $input)
166
+ && substr_count('[', $input) == substr_count(']', $input)) {
167
+ try {
168
+ $this->_postData = Mage::helper('core')->jsonDecode($input);
169
+ } catch (Exception $e) {
170
+
171
+ }
172
+ }
173
+ }
174
+
175
+ return $this->_postData;
176
+ }
177
+ }
app/code/community/Neklo/Monitor/Helper/Request/Validator.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Helper_Request_Validator
4
+ {
5
+ public function isValidToken($token)
6
+ {
7
+ if (!$token) {
8
+ return false;
9
+ }
10
+ if ($this->_getConfig()->getToken() !== $token) {
11
+ return false;
12
+ }
13
+ return true;
14
+ }
15
+
16
+ public function isValidSid($sid)
17
+ {
18
+ if (strlen($sid) !== 32) {
19
+ return false;
20
+ }
21
+ if ($this->_getConfig()->getGatewaySid() && $this->_getConfig()->getGatewaySid() !== $sid) {
22
+ return false;
23
+ }
24
+ return true;
25
+ }
26
+
27
+ public function isValidHash($hash)
28
+ {
29
+ if (strlen($hash) !== 32) {
30
+ return false;
31
+ }
32
+ return true;
33
+ }
34
+
35
+ public function isValidDeviceId($deviceId)
36
+ {
37
+ if (!$deviceId) {
38
+ return false;
39
+ }
40
+ // TODO: add expression for device id
41
+ return true;
42
+ }
43
+
44
+ public function isValidPlan($plan)
45
+ {
46
+ if (!is_array($plan)) {
47
+ return false;
48
+ }
49
+ if (!array_key_exists('type', $plan) || !$plan['type']) {
50
+ return false;
51
+ }
52
+ if (!array_key_exists('frequency', $plan) || !$plan['frequency']) {
53
+ return false;
54
+ }
55
+ return true;
56
+ }
57
+
58
+ public function isValidStore($storeId)
59
+ {
60
+ $storeId = (int)$storeId;
61
+ if ($storeId) {
62
+ $store = Mage::app()->getStore($storeId);
63
+ if (!$store->getId() || $storeId != $store->getId()) {
64
+ return false;
65
+ }
66
+ }
67
+ return true;
68
+ }
69
+
70
+ public function isValidTimestamp($time)
71
+ {
72
+ if (is_numeric($time)) {
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+
78
+ public function isValidGroupByPeriod($period)
79
+ {
80
+ if (!in_array($period, array(
81
+ 'day', 'month', 'year'
82
+ ))) {
83
+ return false;
84
+ }
85
+ return true;
86
+ }
87
+
88
+ public function isValidOrderStatus($status)
89
+ {
90
+ // parameter is optional
91
+ if (!$status) {
92
+ return true;
93
+ }
94
+ // but we validate its value if sent
95
+ if (!in_array($status, array_keys(Mage::getSingleton('sales/order_config')->getStatuses()))) {
96
+ return false;
97
+ }
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * @return Neklo_Monitor_Helper_Config
103
+ */
104
+ protected function _getConfig()
105
+ {
106
+ return Mage::helper('neklo_monitor/config');
107
+ }
108
+ }
app/code/community/Neklo/Monitor/Model/Cron/Abstract.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Neklo_Monitor_Model_Cron_Abstract
4
+ {
5
+ protected $_name = '';
6
+
7
+ public function run(Mage_Cron_Model_Schedule $schedule)
8
+ {
9
+ if (!$this->_getConfig()->isEnabled()) {
10
+ $schedule->setMessages('Disabled');
11
+ return;
12
+ }
13
+
14
+ if (!$this->_getConfig()->isConnected()) {
15
+ $schedule->setMessages('Not connected');
16
+ return;
17
+ }
18
+
19
+ if ($this->_isLocked()) {
20
+ $schedule->setMessages('Locked');
21
+ return;
22
+ }
23
+
24
+ $this->_lock();
25
+ $this->_passData($schedule);
26
+
27
+ $msg = $schedule->getMessages();
28
+ if ($msg) {
29
+ $msg .= "\n";
30
+ }
31
+ $msg .= 'Sent';
32
+ $schedule->setMessages($msg);
33
+ }
34
+
35
+ abstract protected function _passData(Mage_Cron_Model_Schedule $schedule);
36
+
37
+ protected function _isLocked()
38
+ {
39
+ $lockedAt = Mage::app()->loadCache($this->_name);
40
+ if ($lockedAt && (time() - $lockedAt < $this->_getConfig()->getGatewayFrequency() * 60)) {
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+
46
+ protected function _lock()
47
+ {
48
+ Mage::app()->saveCache(time(), $this->_name, array(), $this->_getConfig()->getGatewayFrequency() * 60);
49
+ }
50
+
51
+ /**
52
+ * @return Neklo_Monitor_Helper_Config
53
+ */
54
+ protected function _getConfig()
55
+ {
56
+ return Mage::helper('neklo_monitor/config');
57
+ }
58
+
59
+ /**
60
+ * @return Neklo_Monitor_Model_Gateway_Connector
61
+ */
62
+ protected function _getConnector()
63
+ {
64
+ return Mage::getSingleton('neklo_monitor/gateway_connector');
65
+ }
66
+ }
app/code/community/Neklo/Monitor/Model/Cron/Server.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Cron_Server extends Neklo_Monitor_Model_Cron_Abstract
4
+ {
5
+ protected $_name = 'neklo_monitor_cron_server_lock_id';
6
+
7
+ protected function _passData(Mage_Cron_Model_Schedule $schedule)
8
+ {
9
+ $serverData = $this->_collectServerData($schedule);
10
+ try {
11
+ $gatewayConfig = $this->_getConnector()->sendInfo('server', $serverData);
12
+ $this->_getConfig()->updateGatewayConfig($gatewayConfig);
13
+ $this->_getConfig()->updateGatewayLastUpdate();
14
+ } catch (Exception $e) {
15
+ Mage::logException($e);
16
+ $msg = $schedule->getMessages();
17
+ if ($msg) {
18
+ $msg .= "\n";
19
+ }
20
+ $msg .= $e->getMessage();
21
+ $schedule->setMessages($msg);
22
+ }
23
+ }
24
+
25
+ protected function _collectServerData(Mage_Cron_Model_Schedule $schedule)
26
+ {
27
+ $info = null;
28
+ try {
29
+ Neklo_Monitor_Autoload::register();
30
+ /** @var Neklo_Monitor_Model_Linfo $linfo */
31
+ $linfo = Mage::getModel('neklo_monitor/linfo');
32
+ $linfo->scan();
33
+ $info = $linfo->getInfo();
34
+ } catch (Exception $e) {
35
+ Mage::logException($e);
36
+ $msg = $schedule->getMessages();
37
+ if ($msg) {
38
+ $msg .= "\n";
39
+ }
40
+ $msg .= $e->getMessage();
41
+ $schedule->setMessages($msg);
42
+ }
43
+ return $info;
44
+ }
45
+
46
+ }
app/code/community/Neklo/Monitor/Model/Cron/Store.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Cron_Store extends Neklo_Monitor_Model_Cron_Abstract
4
+ {
5
+ protected $_name = 'neklo_monitor_cron_store_lock_id';
6
+
7
+ protected function _passData(Mage_Cron_Model_Schedule $schedule)
8
+ {
9
+ $storeData = $this->_collectStoreData($schedule);
10
+ try {
11
+ $gatewayConfig = $this->_getConnector()->sendInfo('store', $storeData);
12
+ $this->_getConfig()->updateGatewayConfig($gatewayConfig);
13
+ $this->_getConfig()->updateGatewayLastUpdate();
14
+ } catch (Exception $e) {
15
+ Mage::logException($e);
16
+ $msg = $schedule->getMessages();
17
+ if ($msg) {
18
+ $msg .= "\n";
19
+ }
20
+ $msg .= $e->getMessage();
21
+ $schedule->setMessages($msg);
22
+ }
23
+ }
24
+
25
+ protected function _collectStoreData(Mage_Cron_Model_Schedule $schedule)
26
+ {
27
+ $info = null;
28
+ try {
29
+ /* @var $minfo Neklo_Monitor_Model_Minfo */
30
+ $minfo = Mage::getModel('neklo_monitor/minfo');
31
+ $minfo->scan();
32
+ $info = $minfo->getInfo();
33
+ } catch (Exception $e) {
34
+ Mage::logException($e);
35
+ $msg = $schedule->getMessages();
36
+ if ($msg) {
37
+ $msg .= "\n";
38
+ }
39
+ $msg .= $e->getMessage();
40
+ $schedule->setMessages($msg);
41
+ }
42
+ return $info;
43
+ }
44
+
45
+ public function collect()
46
+ {
47
+ if (!$this->_getConfig()->isEnabled()) {
48
+ return;
49
+ }
50
+
51
+ /** @var Neklo_Monitor_Model_Minfo_Parser $parser */
52
+ $parser = Mage::getModel('neklo_monitor/minfo_parser');
53
+ $parser->generateReportStats();
54
+ $parser->generateLogStats('system');
55
+ $parser->generateLogStats('exception');
56
+ }
57
+
58
+ }
app/code/community/Neklo/Monitor/Model/Gateway/Connector.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Gateway_Connector
4
+ {
5
+ protected $_client = null;
6
+
7
+ /**
8
+ * @return null|Varien_Http_Client
9
+ * @throws Zend_Http_Client_Exception
10
+ */
11
+ public function getClient()
12
+ {
13
+ if ($this->_client === null) {
14
+ $this->_client = new Varien_Http_Client();
15
+ $this->_client
16
+ ->setMethod(Zend_Http_Client::POST)
17
+ ->setConfig(
18
+ array(
19
+ 'maxredirects' => 0,
20
+ 'timeout' => 30,
21
+ )
22
+ )
23
+ ->setHeaders('SID', $this->_getConfig()->getGatewaySid())
24
+ ;
25
+ }
26
+ return $this->_client;
27
+ }
28
+
29
+ public function sendInfo($type, $info)
30
+ {
31
+ $requestData = array(
32
+ $type => $info,
33
+ );
34
+ $client = $this->getClient();
35
+
36
+ $client->setUri($this->_getUri());
37
+
38
+ $result = $client
39
+ ->setRawData(Mage::helper('core')->jsonEncode($requestData))
40
+ ->request()
41
+ ;
42
+
43
+ if (!$result->isSuccessful()) {
44
+ throw new Exception('Error sending request: '.$result->getMessage());
45
+ }
46
+
47
+ return Mage::helper('core')->jsonDecode($this->_getBody($result));
48
+ }
49
+
50
+ protected function _getUri()
51
+ {
52
+ return $this->_getConfig()->getGatewayServerUri() . 'server/info';
53
+ }
54
+
55
+ /**
56
+ * @return Neklo_Monitor_Helper_Config
57
+ */
58
+ protected function _getConfig()
59
+ {
60
+ return Mage::helper('neklo_monitor/config');
61
+ }
62
+
63
+ /**
64
+ * similar to Zend_Http_Response::getBody()
65
+ */
66
+ protected function _getBody(Zend_Http_Response $response)
67
+ {
68
+ $body = $response->getRawBody();
69
+
70
+ // 'transfer-encoding' header is 'chunked', but the body does not seem to be chunked, hmm
71
+ // just silent catch for such cases
72
+ try {
73
+ // Decode the body if it was transfer-encoded
74
+ if (strtolower($response->getHeader('transfer-encoding')) == 'chunked') {
75
+ // Handle chunked body
76
+ $body = Zend_Http_Response::decodeChunkedBody($body);
77
+ }
78
+ } catch (Zend_Http_Exception $e) {
79
+ if (false === strpos($e->getMessage(), 'Error parsing body')) {
80
+ throw $e;
81
+ }
82
+ }
83
+
84
+ // Decode any content-encoding (gzip or deflate) if needed
85
+ switch (strtolower($response->getHeader('content-encoding'))) {
86
+
87
+ // Handle gzip encoding
88
+ case 'gzip':
89
+ $body = Zend_Http_Response::decodeGzip($body);
90
+ break;
91
+
92
+ // Handle deflate encoding
93
+ case 'deflate':
94
+ $body = Zend_Http_Response::decodeDeflate($body);
95
+ break;
96
+
97
+ default:
98
+ break;
99
+ }
100
+
101
+ return $body;
102
+ }
103
+
104
+ }
app/code/community/Neklo/Monitor/Model/Linfo.php ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Linfo extends \Linfo\Linfo
4
+ {
5
+ protected $_defaultConfig = array(
6
+ 'byte_notation' => 1024,
7
+ 'dates' => Varien_Date::DATETIME_INTERNAL_FORMAT,
8
+ 'language' => 'en',
9
+ 'show' => array(
10
+ 'os' => true,
11
+ 'distro' => true,
12
+ 'load' => true,
13
+ 'ram' => true,
14
+ 'mounts' => true,
15
+ 'network' => true,
16
+ 'uptime' => true,
17
+ 'cpu' => true,
18
+ 'connection' => true,
19
+ 'kernel' => false,
20
+ 'ip' => false,
21
+ 'hd' => false,
22
+ 'mounts_options' => false,
23
+ 'webservice' => false,
24
+ 'phpversion' => false,
25
+ 'process_stats' => false,
26
+ 'hostname' => false,
27
+ 'devices' => false,
28
+ 'model' => false,
29
+ 'numLoggedIn' => false,
30
+ 'virtualization' => false,
31
+ 'duplicate_mounts' => false,
32
+ 'temps' => false,
33
+ 'raid' => false,
34
+ 'battery' => false,
35
+ 'sound' => false,
36
+ 'wifi' => false,
37
+ 'services' => false,
38
+ ),
39
+ 'hide' => array(
40
+ 'filesystems' => array(
41
+ 'tmpfs',
42
+ 'ecryptfs',
43
+ 'nfsd',
44
+ 'rpc_pipefs',
45
+ 'usbfs',
46
+ 'devpts',
47
+ 'fusectl',
48
+ 'securityfs',
49
+ 'fuse.truecrypt',
50
+ ),
51
+ 'storage_devices' => array(
52
+ 'gvfs-fuse-daemon',
53
+ 'none',
54
+ ),
55
+ 'mountpoints_regex' => array(
56
+ '/^\/.+$/s'
57
+ ),
58
+ 'fs_mount_options' => array(
59
+ 'ecryptfs',
60
+ ),
61
+ ),
62
+ 'cpu_usage' => true,
63
+ 'show_errors' => false,
64
+ );
65
+
66
+ protected $_infoMap = array(
67
+ // General
68
+ 'timestamp' => 'server_created_at',
69
+
70
+ // OS
71
+ 'OS' => 'os/name',
72
+ 'Distro/name' => 'os/distro/name',
73
+ 'Distro/version' => 'os/distro/version',
74
+
75
+ // RAM
76
+ 'RAM/total' => 'ram/total',
77
+ 'RAM/free' => 'ram/free',
78
+
79
+ // Mounts
80
+ 'Mounts/*/size' => 'mount/*/total',
81
+ 'Mounts/*/free' => 'mount/*/free',
82
+ 'Mounts/*/free_percent' => 'mount/*/free_percent',
83
+ 'Mounts/*/used' => 'mount/*/used',
84
+ 'Mounts/*/used_percent' => 'mount/*/used_percent',
85
+
86
+ // CPU
87
+ 'Load' => 'cpu/load',
88
+ 'CPUArchitecture' => 'cpu/architecture',
89
+ 'CPU/*/Vendor' => 'cpu/core/*/vendor',
90
+ 'CPU/*/Model' => 'cpu/core/*/model',
91
+ 'CPU/*/MHz' => 'cpu/core/*/mhz',
92
+ 'CPU/*/usage_percentage' => 'cpu/core/*/usage_percent',
93
+
94
+ // Network
95
+ 'Network Devices/*/state' => 'network/*/state',
96
+ 'Network Devices/*/recieved/bytes' => 'network/*/received/bytes',
97
+ 'Network Devices/*/recieved/errors' => 'network/*/received/errors',
98
+ 'Network Devices/*/recieved/packets' => 'network/*/received/packets',
99
+ 'Network Devices/*/sent/bytes' => 'network/*/sent/bytes',
100
+ 'Network Devices/*/sent/errors' => 'network/*/sent/errors',
101
+ 'Network Devices/*/sent/packets' => 'network/*/sent/packets',
102
+
103
+ // UpTime
104
+ 'UpTime/bootedTimestamp' => 'uptime/booted_timestamp',
105
+ 'UpTime/text' => 'uptime/booted_timestamp_text',
106
+
107
+ // Connection
108
+ 'Connection/connect' => 'connection/connect',
109
+ 'Connection/ttfb' => 'connection/ttfb',
110
+ 'Connection/total' => 'connection/total',
111
+ );
112
+
113
+ public function &getInfo()
114
+ {
115
+ $timestamp = time();
116
+ $info = parent::getInfo();
117
+ $info['timestamp'] = $timestamp;
118
+
119
+ $infoMap = $this->_prepareInfoMap($info);
120
+ $result = $this->_applyInfoMap($info, $infoMap);
121
+
122
+ return $result;
123
+ }
124
+
125
+ protected function _applyInfoMap($data, $infoMap)
126
+ {
127
+ $result = array();
128
+ foreach ($infoMap as $linfoPath => $monitorPath) {
129
+
130
+ $linfoPathList = explode('/', $linfoPath);
131
+ $linfoKeyValue = $data;
132
+ foreach ($linfoPathList as $key) {
133
+ if (!array_key_exists($key, $linfoKeyValue)) {
134
+ continue;
135
+ }
136
+ $linfoKeyValue = $linfoKeyValue[$key];
137
+ }
138
+
139
+ $monitorPathList = explode('/', $monitorPath);
140
+ $monitorKeyValue = &$result;
141
+ foreach ($monitorPathList as $key) {
142
+ if (!array_key_exists($key, $monitorKeyValue)) {
143
+ $monitorKeyValue[$key] = array();
144
+ }
145
+ $monitorKeyValue = &$monitorKeyValue[$key];
146
+ }
147
+ $monitorKeyValue = $linfoKeyValue;
148
+ }
149
+ return $result;
150
+ }
151
+
152
+ protected function _prepareInfoMap($data)
153
+ {
154
+ $_infoMap = array();
155
+ foreach ($this->_infoMap as $linfoPath => $monitorPath) {
156
+ if (strpos($linfoPath, '*') === false) {
157
+ $_infoMap[$linfoPath] = $monitorPath;
158
+ continue;
159
+ }
160
+
161
+ $linfoPathList = explode('/', $linfoPath);
162
+ $linfoKeyValue = $data;
163
+ foreach ($linfoPathList as $key) {
164
+ if ($key == '*') {
165
+ $realInfoKeyList = array_keys($linfoKeyValue);
166
+ foreach ($realInfoKeyList as $realInfoKey) {
167
+ $_infoMap[str_replace('*', $realInfoKey, $linfoPath)] = str_replace('*', $realInfoKey, $monitorPath);
168
+ }
169
+ continue;
170
+ }
171
+ if (!array_key_exists($key, $linfoKeyValue)) {
172
+ continue;
173
+ }
174
+ $linfoKeyValue = $linfoKeyValue[$key];
175
+ }
176
+ }
177
+ return $_infoMap;
178
+ }
179
+
180
+ public function scan()
181
+ {
182
+ parent::scan();
183
+ $this->_scanAdditional();
184
+ }
185
+
186
+ protected function loadSettings($settings = array())
187
+ {
188
+ if (!is_array($settings) || !count($settings)) {
189
+ $settings = $this->_defaultConfig;
190
+ }
191
+
192
+ // Running unit tests?
193
+ if (defined('LINFO_TESTING')) {
194
+ $this->settings = \Linfo\Common::getVarFromFile($this->linfo_testdir.'/test_settings.php', 'settings');
195
+ if (!is_array($this->settings)) {
196
+ throw new \Linfo\Exceptions\FatalException('Failed getting test-specific settings');
197
+ }
198
+ return;
199
+ }
200
+
201
+ // Don't just blindly assume we have the ob_* functions...
202
+ if (!function_exists('ob_start')) {
203
+ $settings['compress_content'] = false;
204
+ }
205
+
206
+ if (!isset($settings['hide'])) {
207
+ $settings['hide'] = array(
208
+ 'filesystems' => array(),
209
+ 'storage_devices' => array(),
210
+ );
211
+ }
212
+
213
+ // Make sure these are arrays
214
+ $settings['hide']['filesystems'] = is_array($settings['hide']['filesystems']) ? $settings['hide']['filesystems'] : array();
215
+ $settings['hide']['storage_devices'] = is_array($settings['hide']['storage_devices']) ? $settings['hide']['storage_devices'] : array();
216
+
217
+ // Make sure these are always hidden
218
+ $settings['hide']['filesystems'][] = 'rootfs';
219
+ $settings['hide']['filesystems'][] = 'binfmt_misc';
220
+
221
+ // Default timeformat
222
+ $settings['dates'] = array_key_exists('dates', $settings) ? $settings['dates'] : 'm/d/y h:i A (T)';
223
+
224
+ // Default to english translation if garbage is passed
225
+ if (empty($settings['language']) || !preg_match('/^[a-z]{2}$/', $settings['language'])) {
226
+ $settings['language'] = 'en';
227
+ }
228
+
229
+ // If it can't be found default to english
230
+ if (!is_file($this->linfo_localdir.'lib/Linfo/Lang/'.$settings['language'].'.php')) {
231
+ $settings['language'] = 'en';
232
+ }
233
+
234
+ $this->settings = $settings;
235
+ }
236
+
237
+ protected function loadLanguage()
238
+ {
239
+ // Running unit tests?
240
+ if (defined('LINFO_TESTING')) {
241
+ $this->lang = require $this->linfo_testdir.'/test_lang.php';
242
+ if (!is_array($this->lang)) {
243
+ throw new \Linfo\Exceptions\FatalException('Failed getting test-specific language');
244
+ }
245
+
246
+ return;
247
+ }
248
+
249
+ // Load translation, defaulting to english of keys are missing (assuming
250
+ // we're not using english anyway and the english translation indeed exists)
251
+ if (is_file($this->linfo_localdir.'lib/Linfo/Lang/en.php') && $this->settings['language'] != 'en') {
252
+ $this->lang = array_merge(require($this->linfo_localdir.'lib/Linfo/Lang/en.php'),
253
+ require($this->linfo_localdir.'lib/Linfo/Lang/'.$this->settings['language'].'.php'));
254
+ }
255
+
256
+ // Otherwise snag desired translation, be it english or a non-english without english to fall back on
257
+ else {
258
+ $this->lang = require $this->linfo_localdir.'lib/Linfo/Lang/'.$this->settings['language'].'.php';
259
+ }
260
+ }
261
+
262
+ protected function _scanAdditional()
263
+ {
264
+ $os = $this->getOS();
265
+
266
+ if (!$os) {
267
+ throw new \Linfo\Exceptions\FatalException('Unknown/unsupported operating system');
268
+ }
269
+
270
+ $parser = Mage::getModel('neklo_monitor/linfo_os_' . strtolower($os), $this->settings);
271
+
272
+ $reflector = new ReflectionClass($parser);
273
+
274
+ $fields = array(
275
+ 'Connection' => array(
276
+ 'show' => !empty($this->settings['show']['connection']),
277
+ 'default' => '',
278
+ 'method' => 'getConnection',
279
+ ),
280
+ );
281
+
282
+ foreach ($fields as $key => $data) {
283
+ if (!$data['show']) {
284
+ $this->info[$key] = $data['default'];
285
+ continue;
286
+ }
287
+
288
+ try {
289
+ $method = $reflector->getMethod($data['method']);
290
+ $this->info[$key] = $method->invoke($parser);
291
+ } catch (ReflectionException $e) {
292
+ $this->info[$key] = $data['default'];
293
+ }
294
+ }
295
+ }
296
+ }
app/code/community/Neklo/Monitor/Model/Linfo/Os/Linux.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Linfo_Os_Linux extends \Linfo\OS\Linux
4
+ {
5
+ protected $exec = null;
6
+
7
+ public function __construct(array $settings)
8
+ {
9
+ parent::__construct($settings);
10
+ $this->exec = new \Linfo\Parsers\CallExt();
11
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
12
+ }
13
+
14
+ public function getConnection()
15
+ {
16
+ if (!empty($this->settings['connection'])) {
17
+ $t = new \Linfo\Meta\Timer('Connection');
18
+ }
19
+ $result = array();
20
+ try {
21
+ $command = $this->exec->exec('curl', ' -so /dev/null -w "connect:%{time_connect};ttfb:%{time_starttransfer};total:%{time_total};" ' . Mage::getBaseUrl());
22
+ $lines = explode(";", $command);
23
+ $result = array();
24
+ foreach ($lines as $line) {
25
+ if (!$line) {
26
+ continue;
27
+ }
28
+ list($key, $value) = explode(':', $line);
29
+ if (!$key || !$value) {
30
+ continue;
31
+ }
32
+ $result[$key] = $value;
33
+ }
34
+ } catch (Exception $e) {
35
+ \Linfo\Meta\Errors::add('Linux Core', 'Failed running curl.');
36
+ }
37
+ return $result;
38
+ }
39
+ }
app/code/community/Neklo/Monitor/Model/Log.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Log
4
+ {
5
+
6
+ }
app/code/community/Neklo/Monitor/Model/Minfo.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Minfo
4
+ {
5
+ protected $_info = array();
6
+
7
+ protected $_config = array(
8
+ 'magento' => array(
9
+ 'var_log' => true,
10
+ 'var_report' => true,
11
+ 'customer_online' => true,
12
+ 'products_outofstock' => true,
13
+ ),
14
+ );
15
+
16
+ public function getInfo()
17
+ {
18
+ return $this->_info;
19
+ }
20
+
21
+ public function scan()
22
+ {
23
+ /** @var Neklo_Monitor_Model_Minfo_Parser $parser */
24
+ $parser = Mage::getModel('neklo_monitor/minfo_parser');
25
+
26
+ $timestamp = time();
27
+ $fields = array(
28
+ 'var_log' => array(
29
+ 'show' => !empty($this->_config['magento']['var_log']),
30
+ 'default' => null,
31
+ 'method' => 'getVarLog',
32
+ ),
33
+ 'var_report' => array(
34
+ 'show' => !empty($this->_config['magento']['var_report']),
35
+ 'default' => null,
36
+ 'method' => 'getVarReport',
37
+ ),
38
+ 'customer_online' => array(
39
+ 'show' => !empty($this->_config['magento']['customer_online']),
40
+ 'default' => null,
41
+ 'method' => 'getCustomerOnline',
42
+ ),
43
+ 'products_outofstock' => array(
44
+ 'show' => !empty($this->_config['magento']['customer_online']),
45
+ 'default' => null,
46
+ 'method' => 'getProductsOutofstock',
47
+ ),
48
+ );
49
+
50
+ foreach ($fields as $key => $data) {
51
+ $this->_info[$key] = $data['default'];
52
+
53
+ if (!$data['show']) {
54
+ continue;
55
+ }
56
+
57
+ try {
58
+ $methodName = $data['method'];
59
+ if (method_exists($parser, $methodName)) {
60
+ $this->_info[$key] = $parser->$methodName();
61
+ }
62
+ } catch (Exception $e) {
63
+ Mage::logException($e);
64
+ }
65
+ }
66
+ $this->_info['server_created_at'] = $timestamp;
67
+ return $this;
68
+ }
69
+ }
app/code/community/Neklo/Monitor/Model/Minfo/Log.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @method Neklo_Monitor_Model_Resource_Minfo_Log getResource()
5
+ */
6
+ class Neklo_Monitor_Model_Minfo_Log extends Mage_Core_Model_Abstract
7
+ {
8
+ protected function _construct()
9
+ {
10
+ $this->_init('neklo_monitor/minfo_log');
11
+ }
12
+
13
+ public function generateLogs($type)
14
+ {
15
+ $logActive = Mage::getStoreConfig('dev/log/active');
16
+ if (!Mage::getIsDeveloperMode() && !$logActive) {
17
+ return false;
18
+ }
19
+
20
+ $file = Mage::getStoreConfig('dev/log/file');
21
+ if ('system' != $type) {
22
+ $type = 'exception';
23
+ $file = Mage::getStoreConfig('dev/log/exception_file');
24
+ }
25
+ $logFile = Mage::getBaseDir('var') . DS . 'log' . DS . $file;
26
+ if (!file_exists($logFile)) {
27
+ return false;
28
+ }
29
+
30
+ $logs = array();
31
+
32
+ $fh = fopen($logFile, "r");
33
+ if (!$fh) {
34
+ return false;
35
+ }
36
+
37
+ $maxTime = $this->getResource()->fetchMaxTime($type);
38
+
39
+ // read log lines according to format
40
+ // '%timestamp% %priorityName% (%priority%): %message%' . PHP_EOL;
41
+ // see Mage::log()
42
+ // exception's $message starts with "/n", i.e. can be found on next line
43
+
44
+ while (($line = fgets($fh)) !== false) {
45
+ // line should contain datetime, priorityName, $priority value least
46
+ // e.g. "2016-04-18T13:39:42+00:00 ERR (3):"
47
+ // yes, minimal priorityName is ERR, see Zend_Log
48
+ if (strlen($line) < 34) {
49
+ continue;
50
+ }
51
+
52
+ // skip exception stack traces
53
+ if ('exception' == $type && 0 === strpos($line, '#')) {
54
+ continue;
55
+ }
56
+
57
+ // search for line with datetime at the beginning
58
+ // timestamp format is YYYY-mm-ddTHH:ii:ss+00:00
59
+ $offsetTimezone = strpos($line, '+00:00 ');
60
+ if (19 !== $offsetTimezone) { // 19 is strlen of 'YYYY-mm-ddTHH:ii:ss'
61
+ continue;
62
+ }
63
+
64
+ $lineDate = substr($line, 0, $offsetTimezone);
65
+ $lineTime = strtotime($lineDate);
66
+ // invalid date?
67
+ if (false === $lineTime) {
68
+ continue;
69
+ }
70
+ // process only new dates
71
+ if ($lineTime < $maxTime) {
72
+ continue;
73
+ }
74
+
75
+ $offsetPriority = $offsetTimezone + 7; // 7 is strlen of '+00:00 ', i.e. 19+7=26
76
+ $offsetMessage = strpos($line, ': ', $offsetPriority);
77
+ if (false === $offsetMessage) {
78
+ continue; // hmmm
79
+ }
80
+ $linePriorityInfo = substr($line, $offsetPriority, $offsetMessage - $offsetPriority);
81
+ list($linePriorityName, $linePriorityValue) = explode(' ', $linePriorityInfo);
82
+ // skip manual debug lines in system.log
83
+ if ('debug' == strtolower($linePriorityName)) {
84
+ continue;
85
+ }
86
+
87
+ $offsetMessage += 2; // ': ' between $priority and $message
88
+ if ('exception' == $type || $offsetMessage >= strlen($line)) {
89
+ // read next line for message
90
+ $line = fgets($fh);
91
+ if (false == $line) {
92
+ break; // EOF
93
+ }
94
+ $lineMessage = $line;
95
+ } else {
96
+ $lineMessage = substr($line, $offsetMessage);
97
+ }
98
+ $lineMessage = trim($lineMessage);
99
+
100
+ $hash = md5($lineMessage);
101
+ if (isset($logs[$hash])) {
102
+ $logs[$hash]['qty']++;
103
+ if ($lineTime > $logs[$hash]['last_time']) {
104
+ $logs[$hash]['last_time'] = $lineTime;
105
+ }
106
+ $logs[$hash]['times'][] = $lineTime;
107
+ } else {
108
+ $logs[$hash] = array(
109
+ 'hash' => $hash,
110
+ 'message' => $lineMessage,
111
+ 'qty' => 1,
112
+ 'last_time' => $lineTime,
113
+ 'times' => array($lineTime),
114
+ );
115
+ }
116
+ }
117
+
118
+ fclose($fh);
119
+
120
+ // calculate first_time
121
+ foreach ($logs as $hash => $_log) {
122
+ $_times = $_log['times'];
123
+ sort($_times);
124
+ $logs[$hash]['first_time'] = current($_times);
125
+ }
126
+
127
+
128
+ list($inserted, $updated) = $this->getResource()->saveLogs($type, $logs);
129
+
130
+ return ($inserted+$updated);
131
+ }
132
+
133
+ protected function _afterLoad()
134
+ {
135
+ $list = explode(',', $this->_getData('times'));
136
+
137
+ // sort by time DESC
138
+ $list = array_unique($list, SORT_NUMERIC);
139
+ sort($list, SORT_NUMERIC);
140
+ $list = array_reverse($list);
141
+
142
+ $this->_data['times_list'] = $list;
143
+ return parent::_afterLoad();
144
+ }
145
+
146
+ /**
147
+ * @return Varien_Data_Collection
148
+ */
149
+ public function getTimesCollection($startFrom = 0, $limit = null, $filter = array())
150
+ {
151
+ // apply filters
152
+
153
+ $list = $this->_getData('times_list');
154
+ if ($filter) {
155
+ foreach ($list as $_key => $_data) {
156
+ $valid = true;
157
+ foreach ($filter as $_expr => $_value) {
158
+ if ('lt' == $_expr) { if ($_data >= $_value) $valid = false; }
159
+ // else if ('lteq' == $_expr) { if ($_data[$_field] > $_value) $valid = false; }
160
+ // else if ('gt' == $_expr) { if ($_data[$_field] <= $_value) $valid = false; }
161
+ else if ('gteq' == $_expr) { if ($_data < $_value) $valid = false; }
162
+ // else if ('eq' == $_expr) { if ($_data[$_field] <> $_value) $valid = false; }
163
+ // else if ('neq' == $_expr) { if ($_data[$_field] == $_value) $valid = false; }
164
+ }
165
+ if (!$valid) {
166
+ unset($list[$_key]);
167
+ }
168
+ }
169
+ }
170
+
171
+ // apply limits
172
+
173
+ $lastIdx = count($list) - 1;
174
+ if (is_null($limit)) {
175
+ $finishAt = $lastIdx;
176
+ } else {
177
+ $finishAt = $startFrom + $limit - 1;
178
+ if ($finishAt > $lastIdx) {
179
+ $finishAt = $lastIdx;
180
+ }
181
+ }
182
+
183
+ $collection = new Varien_Data_Collection();
184
+ $k = $startFrom;
185
+ $list = array_values($list); // avoid assoc array, convert to numeric array keys
186
+ while ($k <= $finishAt) {
187
+ $_file = new Varien_Object($list[$k]);
188
+ $collection->addItem($_file);
189
+ $k++;
190
+ }
191
+
192
+ return $collection;
193
+ }
194
+
195
+ }
app/code/community/Neklo/Monitor/Model/Minfo/Parser.php ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_Minfo_Parser
4
+ {
5
+ const VAR_REPORT = 'report';
6
+ const VAR_LOG = 'log';
7
+
8
+ public function getVarLog()
9
+ {
10
+ $statFiles = $this->_getDirectoryStats(self::VAR_LOG);
11
+ $statDb = $this->_getCollectedLogStats();
12
+ return array(
13
+ 'size' => $statFiles->getSize(),
14
+ 'count' => $statFiles->getCount(),
15
+ 'details' => $statDb->getDetails(),
16
+ );
17
+ }
18
+
19
+ public function getVarReport()
20
+ {
21
+ $statFiles = $this->_getDirectoryStats(self::VAR_REPORT);
22
+ $statDb = $this->_getCollectedReportStats();
23
+ return array(
24
+ 'size' => $statFiles->getSize(),
25
+ 'count' => $statFiles->getCount(),
26
+ 'details' => $statDb->getDetails(),
27
+ );
28
+ }
29
+
30
+ protected function _getDirectoryStats($directory)
31
+ {
32
+ $directoryPath = Mage::getBaseDir('var') . DS . $directory;
33
+ if (!is_dir($directoryPath) || !is_readable($directoryPath)) {
34
+ return new Varien_Object();
35
+ }
36
+
37
+ $size = 0;
38
+ $count = 0;
39
+ $directoryIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directoryPath));
40
+ foreach ($directoryIterator as $file) {
41
+ /* @var $file SplFileInfo */
42
+ if (!$file->isFile()) {
43
+ continue;
44
+ }
45
+ $size += $file->getSize();
46
+ $count++;
47
+ }
48
+
49
+ $stats = new Varien_Object(array(
50
+ 'size' => (int) $size,
51
+ 'count' => (int) $count,
52
+ ));
53
+
54
+ return $stats;
55
+ }
56
+
57
+ protected function _getCollectedLogStats()
58
+ {
59
+ /** @var Neklo_Monitor_Model_Resource_Minfo_Log_Collection $collection */
60
+ $collection = Mage::getResourceModel('neklo_monitor/minfo_log_collection');
61
+ $collection->addFieldToSelect(array('type', 'qty', 'hash'));
62
+ $collection->getSelect(); // Init fields for select
63
+ $collection->load();
64
+
65
+ $gatewayReport = array();
66
+ foreach ($collection as $hash => $data) {
67
+ $gatewayReport[] = array(
68
+ 'type' => $data['type'],
69
+ 'hash' => $data['hash'],
70
+ 'qty' => (int) $data['qty'],
71
+ );
72
+ }
73
+
74
+ $stats = new Varien_Object(array(
75
+ 'details' => $gatewayReport,
76
+ ));
77
+
78
+ return $stats;
79
+ }
80
+
81
+ protected function _getCollectedReportStats()
82
+ {
83
+ /** @var Neklo_Monitor_Model_Resource_Minfo_Report_Collection $collection */
84
+ $collection = Mage::getResourceModel('neklo_monitor/minfo_report_collection');
85
+ $collection->addFieldToSelect(array('qty', 'hash'));
86
+ $collection->getSelect(); // Init fields for select
87
+ $collection->load();
88
+
89
+ $gatewayReport = array();
90
+ foreach ($collection as $hash => $data) {
91
+ $gatewayReport[] = array(
92
+ 'hash' => $data['hash'],
93
+ 'qty' => (int) $data['qty'],
94
+ );
95
+ }
96
+
97
+ $stats = new Varien_Object(array(
98
+ 'details' => $gatewayReport,
99
+ ));
100
+
101
+ return $stats;
102
+ }
103
+
104
+ public function getCustomerOnline()
105
+ {
106
+ /** @var Mage_Log_Model_Visitor_Online $logModel */
107
+ $logModel = Mage::getModel('log/visitor_online');
108
+ $logModel->prepare();
109
+ /* @var $collection Mage_Log_Model_Mysql4_Visitor_Online_Collection */
110
+ $collection = $logModel->getCollection();
111
+ return array('count' => $collection->getSize());
112
+ }
113
+
114
+ public function getProductsOutofstock()
115
+ {
116
+ $collection = $this->getProductsOutofstockCollection();
117
+ return array('count' => $collection->getSize());
118
+ }
119
+
120
+ /**
121
+ * @param null $storeId
122
+ * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection
123
+ */
124
+ public function getProductsOutofstockCollection($storeId = null)
125
+ {
126
+ /* @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */
127
+ $collection = Mage::getResourceModel('catalog/product_collection');
128
+ if ($storeId) {
129
+ $collection->addStoreFilter($storeId);
130
+ }
131
+
132
+ // copy-pasted from CE 1.4 Layer Model
133
+ /*
134
+ $attributes = Mage::getSingleton('catalog/config')->getProductAttributes();
135
+ $collection->addAttributeToSelect($attributes)
136
+ ->addMinimalPrice()
137
+ ->addFinalPrice()
138
+ ->addTaxPercents()
139
+ ;
140
+ */
141
+
142
+ $collection->addAttributeToSelect(
143
+ array(
144
+ 'name',
145
+ 'price',
146
+ 'small_image', // exists in collection when Flat Product is enabled
147
+ )
148
+ );
149
+ $collection
150
+ ->joinField(
151
+ 'is_in_stock',
152
+ 'cataloginventory/stock_item',
153
+ 'is_in_stock',
154
+ 'product_id=entity_id',
155
+ '{{table}}.stock_id=1',
156
+ 'left'
157
+ )
158
+ ->addAttributeToFilter('is_in_stock', 0)
159
+ // TODO: investigate qty = 0
160
+ // ->joinField(
161
+ // 'qty',
162
+ // 'cataloginventory/stock_item',
163
+ // 'qty',
164
+ // 'product_id=entity_id',
165
+ // '{{table}}.stock_id=1',
166
+ // 'left'
167
+ // )
168
+ // ->addAttributeToFilter('qty', array('eq' => 0))
169
+ ;
170
+ return $collection;
171
+ }
172
+
173
+ public function generateReportStats()
174
+ {
175
+ $directoryPath = Mage::getBaseDir('var') . DS . self::VAR_REPORT . DS;
176
+ if (!is_dir($directoryPath)) {
177
+ return false;
178
+ }
179
+
180
+ $count = 0;
181
+ $directoryIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directoryPath));
182
+ $files = array();
183
+ foreach ($directoryIterator as $_file) {
184
+ /** @var SplFileInfo $_file */
185
+ if (!$_file->isFile()) {
186
+ continue;
187
+ }
188
+ $files[$_file->getMTime()][$_file->getFilename()] = $_file;
189
+ $count++;
190
+ }
191
+ ksort($files);
192
+ return Mage::getSingleton('neklo_monitor/minfo_report')->generateReports($files);
193
+ }
194
+
195
+ public function generateLogStats($type)
196
+ {
197
+ return Mage::getSingleton('neklo_monitor/minfo_log')->generateLogs($type);
198
+ }
199
+
200
+ protected function _format($bytes)
201
+ {
202
+ $exp = (int)floor(log($bytes) / log(1024));
203
+ $symbols = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB');
204
+ return sprintf('%.2f ' . $symbols[$exp], ($bytes / pow(1024, floor($exp))));
205
+ }
206
+ }
app/code/community/Neklo/Monitor/Model/Minfo/Report.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @method Neklo_Monitor_Model_Resource_Minfo_Report getResource()
5
+ */
6
+ class Neklo_Monitor_Model_Minfo_Report extends Mage_Core_Model_Abstract
7
+ {
8
+ protected function _construct()
9
+ {
10
+ $this->_init('neklo_monitor/minfo_report');
11
+ }
12
+
13
+ public function generateReports($files)
14
+ {
15
+ $reports = array();
16
+
17
+ // $maxMtime = $this->getResource()->fetchMaxMtime();
18
+ foreach ($files as $_mtime => $_files) {
19
+ // if ($_mtime < $maxMtime) {
20
+ // continue;
21
+ // }
22
+ foreach ($_files as $_name => $_file) {
23
+ /** @var SplFileInfo $_file */
24
+ $_path = $_file->getPathname();
25
+ $_dataSer = file_get_contents($_path);
26
+ if ('a:5:{i:0;s:' != substr($_dataSer, 0, 11)) {
27
+ continue;
28
+ }
29
+ $_data = @unserialize($_dataSer);
30
+ if ($_data && is_array($_data)) {
31
+ $message = $_data[0];
32
+ $hash = md5($message);
33
+ if (isset($reports[$hash])) {
34
+ $reports[$hash]['qty']++;
35
+ if ($_mtime > $reports[$hash]['last_time']) {
36
+ $reports[$hash]['last_time'] = $_mtime;
37
+ }
38
+ $reports[$hash]['files'][] = array(
39
+ 'name' => ''.$_name,
40
+ 'path' => ''.$_path,
41
+ 'time' => (int) $_mtime,
42
+ 'size' => (int) $_file->getSize(),
43
+ );
44
+ } else {
45
+ $reports[$hash] = array(
46
+ 'hash' => $hash,
47
+ 'message' => $message,
48
+ 'qty' => 1,
49
+ 'last_time' => $_mtime,
50
+ 'files' => array(
51
+ array(
52
+ 'name' => ''.$_name,
53
+ 'path' => ''.$_path,
54
+ 'time' => (int) $_mtime,
55
+ 'size' => (int) $_file->getSize(),
56
+ )
57
+ ),
58
+ );
59
+ }
60
+ }
61
+ }
62
+
63
+ // calculate first_mtime
64
+ foreach ($reports as $hash => $_report) {
65
+ $minTime = time()*2; // server file mtime (incl. locale timezone) might be bigger than now GMT
66
+ foreach ($_report['files'] as $_file) {
67
+ if ($_file['time'] < $minTime) {
68
+ $minTime = $_file['time'];
69
+ }
70
+ }
71
+ $reports[$hash]['first_time'] = $minTime;
72
+ }
73
+ }
74
+
75
+ list($inserted, $updated) = $this->getResource()->saveReports($reports);
76
+
77
+ return ($inserted+$updated);
78
+ }
79
+
80
+ protected function _afterLoad()
81
+ {
82
+ $files = Mage::helper('core')->jsonDecode($this->_getData('files'));
83
+
84
+ $list = array();
85
+ foreach ($files as $_data) {
86
+ $key = $_data['time'] . '_' . $_data['name'];
87
+ // $_data['key'] = $key;
88
+ $list[$key] = $_data;
89
+ }
90
+
91
+ // sort by mtime DESC
92
+
93
+ ksort($list);
94
+ $list = array_reverse($list, true);
95
+
96
+ $this->_data['files_list'] = $list;
97
+ return parent::_afterLoad();
98
+ }
99
+
100
+ /**
101
+ * @return Varien_Data_Collection
102
+ */
103
+ public function getFilesCollection($startFrom = 0, $limit = null, $filter = array())
104
+ {
105
+ // apply filters
106
+
107
+ $list = $this->_getData('files_list');
108
+ if ($filter) {
109
+ foreach ($list as $_key => $_data) {
110
+ $valid = true;
111
+ foreach ($filter as $_field => $_cond) {
112
+ if (!isset($_data[$_field])) {
113
+ $_data[$_field] = 0; // temporary, will not affect $list
114
+ }
115
+ foreach ($_cond as $_expr => $_value) {
116
+ if ('lt' == $_expr) { if ($_data[$_field] >= $_value) $valid = false; }
117
+ // else if ('lteq' == $_expr) { if ($_data[$_field] > $_value) $valid = false; }
118
+ // else if ('gt' == $_expr) { if ($_data[$_field] <= $_value) $valid = false; }
119
+ else if ('gteq' == $_expr) { if ($_data[$_field] < $_value) $valid = false; }
120
+ // else if ('eq' == $_expr) { if ($_data[$_field] <> $_value) $valid = false; }
121
+ // else if ('neq' == $_expr) { if ($_data[$_field] == $_value) $valid = false; }
122
+ }
123
+ }
124
+ if (!$valid) {
125
+ unset($list[$_key]);
126
+ }
127
+ }
128
+ }
129
+
130
+ // apply limits
131
+
132
+ $lastIdx = count($list) - 1;
133
+ if (is_null($limit)) {
134
+ $finishAt = $lastIdx;
135
+ } else {
136
+ $finishAt = $startFrom + $limit - 1;
137
+ if ($finishAt > $lastIdx) {
138
+ $finishAt = $lastIdx;
139
+ }
140
+ }
141
+
142
+ $collection = new Varien_Data_Collection();
143
+ $k = $startFrom;
144
+ $list = array_values($list); // avoid assoc array, convert to numeric array keys
145
+ while ($k <= $finishAt) {
146
+ $_file = new Varien_Object($list[$k]);
147
+ $collection->addItem($_file);
148
+ $k++;
149
+ }
150
+
151
+ return $collection;
152
+ }
153
+
154
+ }
app/code/community/Neklo/Monitor/Model/Resource/Minfo/Log.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Neklo_Monitor_Model_Resource_Minfo_Log extends Mage_Core_Model_Mysql4_Abstract
5
+ {
6
+ protected function _construct()
7
+ {
8
+ $this->_init('neklo_monitor/log', 'log_id');
9
+ }
10
+
11
+ public function fetchMaxtime($type)
12
+ {
13
+ $select = $this->_getReadAdapter()->select()
14
+ ->from($this->getMainTable(), array('max_time' => new Zend_Db_Expr('MAX(`last_time`)')))
15
+ ->where('type = ?', $type);
16
+ return (int) $this->_getReadAdapter()->fetchOne($select);
17
+ }
18
+
19
+ public function saveLogs($type, $logs)
20
+ {
21
+ $conn = $this->_getWriteAdapter();
22
+ $updated = $inserted = 0;
23
+ foreach ($logs as $hash => $data) {
24
+
25
+ $select = $conn->select()
26
+ ->from($this->getMainTable())
27
+ ->where('hash = ?', $hash)
28
+ ->where('type = ?', $type);
29
+ $row = $conn->fetchRow($select);
30
+
31
+ $data['times'] = array_unique($data['times'], SORT_NUMERIC);
32
+ sort($data['times'], SORT_NUMERIC);
33
+ $data['times'] = array_reverse($data['times']);
34
+
35
+ $dbData = array(
36
+ 'last_time' => $data['last_time'],
37
+ 'qty' => $data['qty'],
38
+ 'times' => implode(',', $data['times']),
39
+ );
40
+ if ($row) {
41
+ $conn->update($this->getMainTable(), $dbData, array('log_id = ?' => $row['log_id']));
42
+ $updated++;
43
+ } else {
44
+ $dbData['first_time'] = $data['first_time'];
45
+ $dbData['hash'] = $hash;
46
+ $dbData['type'] = $type;
47
+ $dbData['message'] = $data['message'];
48
+ $conn->insert($this->getMainTable(), $dbData);
49
+ $inserted++;
50
+ }
51
+ }
52
+
53
+ return array($inserted, $updated);
54
+ }
55
+ }
app/code/community/Neklo/Monitor/Model/Resource/Minfo/Log/Collection.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Neklo_Monitor_Model_Resource_Minfo_Log_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
5
+ {
6
+ protected function _construct()
7
+ {
8
+ $this->_init('neklo_monitor/minfo_log');
9
+ }
10
+ }
app/code/community/Neklo/Monitor/Model/Resource/Minfo/Report.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Neklo_Monitor_Model_Resource_Minfo_Report extends Mage_Core_Model_Mysql4_Abstract
5
+ {
6
+ protected function _construct()
7
+ {
8
+ $this->_init('neklo_monitor/report', 'report_id');
9
+ }
10
+
11
+ public function fetchMaxMtime()
12
+ {
13
+ $select = $this->_getReadAdapter()->select()
14
+ ->from($this->getMainTable(), array('max_mtime' => new Zend_Db_Expr('MAX(`last_time`)')));
15
+ return (int) $this->_getReadAdapter()->fetchOne($select);
16
+ }
17
+
18
+ public function saveReports($reports)
19
+ {
20
+ $conn = $this->_getWriteAdapter();
21
+ $updated = $inserted = 0;
22
+ foreach ($reports as $hash => $data) {
23
+
24
+ $select = $conn->select()
25
+ ->from($this->getMainTable())
26
+ ->where('hash = ?', $hash);
27
+ $row = $conn->fetchRow($select);
28
+
29
+ $dbData = array(
30
+ 'last_time' => $data['last_time'],
31
+ 'qty' => $data['qty'],
32
+ 'files' => Mage::helper('core')->jsonEncode($data['files']),
33
+ );
34
+ if ($row) {
35
+ $conn->update($this->getMainTable(), $dbData, array('report_id = ?' => $row['report_id']));
36
+ $updated++;
37
+ } else {
38
+ $dbData['hash'] = $hash;
39
+ $dbData['message'] = $data['message'];
40
+ $dbData['first_time'] = $data['first_time'];
41
+ $conn->insert($this->getMainTable(), $dbData);
42
+ $inserted++;
43
+ }
44
+ }
45
+
46
+ return array($inserted, $updated);
47
+ }
48
+
49
+ }
app/code/community/Neklo/Monitor/Model/Resource/Minfo/Report/Collection.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Neklo_Monitor_Model_Resource_Minfo_Report_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
5
+ {
6
+ protected function _construct()
7
+ {
8
+ $this->_init('neklo_monitor/minfo_report');
9
+ }
10
+ }
app/code/community/Neklo/Monitor/Model/System/Config/Backend/Empty.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_System_Config_Backend_Empty extends Mage_Core_Model_Config_Data
4
+ {
5
+ public function getValue()
6
+ {
7
+ return null;
8
+ }
9
+ }
app/code/community/Neklo/Monitor/Model/System/Config/Backend/Token.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_System_Config_Backend_Token extends Mage_Core_Model_Config_Data
4
+ {
5
+ public function getValue()
6
+ {
7
+ return $this->_getConfig()->getToken();
8
+ }
9
+
10
+ /**
11
+ * @return Neklo_Monitor_Helper_Config
12
+ */
13
+ protected function _getConfig()
14
+ {
15
+ return Mage::helper('neklo_monitor/config');
16
+ }
17
+ }
app/code/community/Neklo/Monitor/Model/System/Config/Source/Server/Type.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Model_System_Config_Source_Server_Type
4
+ {
5
+ const PRODUCTION_CODE = 'production';
6
+ const PRODUCTION_LABEL = 'Production';
7
+ const PRODUCTION_URL = 'http://magento1.m1stats.neklodev.com/';
8
+
9
+ const SANDBOX_CODE = 'sandbox';
10
+ const SANDBOX_LABEL = 'Sandbox';
11
+ const SANDBOX_URL = 'http://magento1.m1stats.neklodev.com/';
12
+
13
+ public function toOptionArray()
14
+ {
15
+ $helper = Mage::helper('neklo_monitor');
16
+ return array(
17
+ array(
18
+ 'value' => self::PRODUCTION_CODE,
19
+ 'label' => $helper->__(self::PRODUCTION_LABEL)
20
+ ),
21
+ array(
22
+ 'value' => self::SANDBOX_CODE,
23
+ 'label' => $helper->__(self::SANDBOX_LABEL)
24
+ ),
25
+ );
26
+ }
27
+
28
+ public function getServerUri($type)
29
+ {
30
+ if ($type === self::PRODUCTION_CODE) {
31
+ return self::PRODUCTION_URL;
32
+ }
33
+ return self::SANDBOX_URL;
34
+ }
35
+ }
app/code/community/Neklo/Monitor/controllers/AuthController.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_AuthController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function preDispatch()
6
+ {
7
+ // allow disconnected requests to any auth-action
8
+ // either indexAction or norouteAction()
9
+ // @see parent::preDispatch
10
+ $this->_allowConnectedOnly = false;
11
+
12
+ return parent::preDispatch();
13
+ }
14
+
15
+ public function indexAction()
16
+ {
17
+ if (!$this->_getConfigHelper()->isConnected()) {
18
+ $sid = $this->_getRequestHelper()->getParam('sid', null);
19
+ $this->_getConfigHelper()->connect($sid);
20
+ }
21
+
22
+ // Return store icon and store name
23
+ $result = array(
24
+ 'name' => Mage::getStoreConfig('design/head/default_title'),
25
+ 'icon' => Mage::getDesign()->getSkinUrl('favicon.ico'),
26
+ );
27
+
28
+ $this->_jsonResult($result);
29
+ }
30
+ }
app/code/community/Neklo/Monitor/controllers/CustomerController.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_CustomerController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function listAction()
6
+ {
7
+ /** @var Neklo_Monitor_Helper_Date $hlpDate */
8
+ $hlpDate = Mage::helper('neklo_monitor/date');
9
+ /** @var Neklo_Monitor_Helper_Country $hlpCountry */
10
+ $hlpCountry = Mage::helper('neklo_monitor/country');
11
+
12
+ /* @var $collection Mage_Customer_Model_Entity_Customer_Collection */
13
+ $collection = Mage::getResourceModel('customer/customer_collection');
14
+ $collection
15
+ ->addNameToSelect()
16
+ ->addAttributeToSelect('email')
17
+ ->addAttributeToSelect('created_at')
18
+ ->addAttributeToSelect('group_id')
19
+ ->joinAttribute('billing_postcode', 'customer_address/postcode', 'default_billing', null, 'left')
20
+ ->joinAttribute('billing_city', 'customer_address/city', 'default_billing', null, 'left')
21
+ ->joinAttribute('billing_telephone', 'customer_address/telephone', 'default_billing', null, 'left')
22
+ ->joinAttribute('billing_region', 'customer_address/region', 'default_billing', null, 'left')
23
+ ->joinAttribute('billing_country_id', 'customer_address/country_id', 'default_billing', null, 'left')
24
+ ->setOrder('created_at')
25
+ ;
26
+
27
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
28
+ if ($storeId) {
29
+ $collection->addFieldToFilter('store_id', $storeId);
30
+ }
31
+
32
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
33
+ $queryDate = $hlpDate->convertToString($queryTimestamp);
34
+ if ($queryTimestamp > 0) {
35
+ $collection->addFieldToFilter('created_at', array('lt' => $queryDate));
36
+ }
37
+
38
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
39
+ $page = ceil($offset / self::PAGE_SIZE) + 1;
40
+ $collection->setPage($page, self::PAGE_SIZE);
41
+
42
+ $groupList = Mage::getResourceModel('customer/group_collection')
43
+ ->addFieldToFilter('customer_group_id', array('gt' => 0))
44
+ ->load()
45
+ ->toOptionHash()
46
+ ;
47
+
48
+ $customerList = array(
49
+ 'result' => array(),
50
+ );
51
+ foreach ($collection as $customer) {
52
+
53
+ if ((array_key_exists($customer->getData('group_id'), $groupList))) {
54
+ $customerGroup = $groupList[$customer->getData('group_id')];
55
+ } else {
56
+ $customerGroup = 'N/A';
57
+ }
58
+
59
+ $customerData = array(
60
+ 'id' => $customer->getData('entity_id'),
61
+ 'email' => $customer->getData('email'),
62
+ 'name' => $customer->getData('name'),
63
+ 'created_at' => $hlpDate->convertToTimestamp($customer->getData('created_at')),
64
+ 'group' => $customerGroup,
65
+ 'billing_country' => $hlpCountry->getCountryName($customer->getData('billing_country_id')),
66
+ 'billing_region' => $customer->getData('billing_region'),
67
+ 'billing_city' => $customer->getData('billing_city'),
68
+ 'billing_postcode' => $customer->getData('billing_postcode'),
69
+ 'billing_telephone' => $customer->getData('billing_telephone'),
70
+ );
71
+
72
+ $customerList['result'][] = $customerData;
73
+ }
74
+
75
+ // get new entities count
76
+
77
+ if ($queryTimestamp > 0) {
78
+ /* @var $collection Mage_Customer_Model_Entity_Customer_Collection */
79
+ $collection = Mage::getResourceModel('customer/customer_collection');
80
+ $collection->addFieldToFilter('created_at', array('gteq' => $queryDate));
81
+ $customerList['new_entities_count'] = $collection->getSize();
82
+ // $customerList['sql'] = $collection->getSelectCountSql()->__toString();
83
+ }
84
+
85
+ $this->_jsonResult($customerList);
86
+ }
87
+
88
+ public function onlineAction()
89
+ {
90
+ /* @var $logModel Mage_Log_Model_Visitor_Online */
91
+ $logModel = Mage::getModel('log/visitor_online');
92
+ $logModel->prepare();
93
+
94
+ // get online customers list $customerIdList
95
+
96
+ /* @var $logVisitorCustomers Mage_Log_Model_Mysql4_Visitor_Online_Collection */
97
+ $logVisitorCustomers = $logModel->getCollection();
98
+ $logVisitorCustomers->addFieldToFilter('customer_id', array('notnull' => true));
99
+
100
+ $customerIdList = $logVisitorCustomers->getColumnValues('customer_id');
101
+ $customerIdList = array_unique($customerIdList);
102
+ $customerIdList = array_filter($customerIdList);
103
+
104
+ // fetch online customers info $customerList
105
+
106
+ $groupList = Mage::getResourceModel('customer/group_collection')
107
+ ->addFieldToFilter('customer_group_id', array('gt' => 0))
108
+ ->load()
109
+ ->toOptionHash()
110
+ ;
111
+
112
+ /* @var $customerCollection Mage_Customer_Model_Entity_Customer_Collection */
113
+ $customerCollection = Mage::getResourceModel('customer/customer_collection');
114
+ $customerCollection
115
+ ->addNameToSelect()
116
+ ->addFieldToFilter('entity_id', array('in' => $customerIdList))
117
+ ->addAttributeToSelect('email')
118
+ ->addAttributeToSelect('created_at')
119
+ ->addAttributeToSelect('group_id')
120
+ ->joinAttribute('billing_postcode', 'customer_address/postcode', 'default_billing', null, 'left')
121
+ ->joinAttribute('billing_city', 'customer_address/city', 'default_billing', null, 'left')
122
+ ->joinAttribute('billing_telephone', 'customer_address/telephone', 'default_billing', null, 'left')
123
+ ->joinAttribute('billing_region', 'customer_address/region', 'default_billing', null, 'left')
124
+ ->joinAttribute('billing_country_id', 'customer_address/country_id', 'default_billing', null, 'left')
125
+ ;
126
+
127
+ /** @var Neklo_Monitor_Helper_Date $hlpDate */
128
+ $hlpDate = Mage::helper('neklo_monitor/date');
129
+ /** @var Neklo_Monitor_Helper_Country $hlpCountry */
130
+ $hlpCountry = Mage::helper('neklo_monitor/country');
131
+
132
+ $customerList = array();
133
+ foreach ($customerCollection as $customer) {
134
+ if ((array_key_exists($customer->getData('group_id'), $groupList))) {
135
+ $customerGroup = $groupList[$customer->getData('group_id')];
136
+ } else {
137
+ $customerGroup = 'N/A';
138
+ }
139
+
140
+ $customerData = array(
141
+ 'id' => $customer->getData('entity_id'),
142
+ 'email' => $customer->getData('email'),
143
+ 'name' => $customer->getData('name'),
144
+ 'created_at' => $hlpDate->convertToTimestamp($customer->getData('created_at')),
145
+ 'group' => $customerGroup,
146
+ 'billing_country' => $hlpCountry->getCountryName($customer->getData('billing_country_id')),
147
+ 'billing_region' => $customer->getData('billing_region'),
148
+ 'billing_city' => $customer->getData('billing_city'),
149
+ 'billing_postcode' => $customer->getData('billing_postcode'),
150
+ 'billing_telephone' => $customer->getData('billing_telephone'),
151
+ );
152
+
153
+ $customerList[$customer->getData('entity_id')] = $customerData;
154
+ }
155
+
156
+ // collect online visitors list $visitorList along with customers data
157
+
158
+ /* @var $collection Mage_Log_Model_Mysql4_Visitor_Online_Collection */
159
+ $collection = $logModel->getCollection();
160
+ $collection->addFieldToFilter('last_url', array('nlike' => '%neklo_monitor%'));
161
+
162
+ // for pages lists - load next page rows despite newly inserted rows
163
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
164
+ $queryDate = $hlpDate->convertToString($queryTimestamp);
165
+ if ($queryTimestamp > 0) {
166
+ $collection->addFieldToFilter('last_visit_at', array('lt' => $queryDate));
167
+ }
168
+
169
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
170
+ $page = ceil($offset / self::PAGE_SIZE) + 1;
171
+ $collection->setCurPage($page);
172
+ $collection->setPageSize(self::PAGE_SIZE);
173
+
174
+ $visitorList = array(
175
+ 'result' => array(),
176
+ );
177
+ // $visitorList['sql0'] = $logVisitorCustomers->getSelectSql(true);
178
+ // $visitorList['sql1'] = $collection->getSelectSql(true);
179
+ // $visitorList['sql2'] = $customerCollection->getSelectSql(true);
180
+ foreach ($collection as $visitor) {
181
+ $visitorData = array(
182
+ 'id' => $visitor->getData('visitor_id'),
183
+ "type" => $visitor->getData('visitor_type'),
184
+ "remote_addr" => $visitor->getData('remote_addr'),
185
+ "first_visit_at" => $hlpDate->convertToTimestamp($visitor->getData('first_visit_at')),
186
+ "last_visit_at" => $hlpDate->convertToTimestamp($visitor->getData('last_visit_at')),
187
+ "last_url" => $visitor->getData('last_url'),
188
+ );
189
+
190
+ $customerId = $visitor->getData('customer_id');
191
+ if ($customerId && array_key_exists($customerId, $customerList)) {
192
+ $visitorData['customer'] = $customerList[$customerId];
193
+ }
194
+
195
+ $visitorList['result'][] = $visitorData;
196
+ }
197
+
198
+ // get new entities count
199
+
200
+ if ($queryTimestamp > 0) {
201
+ /* @var $collection Mage_Log_Model_Mysql4_Visitor_Online_Collection */
202
+ $collection = $logModel->getCollection();
203
+ $collection->addFieldToFilter('last_url', array('nlike' => '%neklo_monitor%'));
204
+ $collection->addFieldToFilter('last_visit_at', array('gteq' => $queryDate));
205
+ $visitorList['new_entities_count'] = $collection->getSize();
206
+ // $visitorList['sql_new'] = $collection->getSelectCountSql()->__toString();
207
+ }
208
+
209
+ $this->_jsonResult($visitorList);
210
+ }
211
+
212
+ }
app/code/community/Neklo/Monitor/controllers/DashboardController.php ADDED
@@ -0,0 +1,437 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_DashboardController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function totalAction()
6
+ {
7
+ if (Mage::helper('core')->isModuleEnabled('Mage_Reports')) {
8
+ /* @var $collection Mage_Reports_Model_Mysql4_Order_Collection */
9
+ $collection = Mage::getResourceModel('reports/order_collection');
10
+ $collection->calculateSales(false);
11
+
12
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
13
+ if ($storeId) {
14
+ $collection->addFieldToFilter('store_id', (int)$storeId);
15
+ }
16
+
17
+ $collection->setPageSize(1);
18
+ $collection->setCurPage(1);
19
+
20
+ $collection->load();
21
+ $salesStats = $collection->getFirstItem();
22
+
23
+ $result = array(
24
+ 'lifetime' => Mage::helper('core')->currency($salesStats->getLifetime(), true, false),
25
+ 'average' => Mage::helper('core')->currency($salesStats->getAverage(), true, false),
26
+ );
27
+ } else {
28
+ $result = array();
29
+ }
30
+
31
+ $this->_jsonResult($result);
32
+ }
33
+
34
+ public function bestsellerAction()
35
+ {
36
+ /* @var $collection Mage_Sales_Model_Mysql4_Report_Bestsellers_Collection */
37
+ $collection = Mage::getResourceModel('sales/report_bestsellers_collection')
38
+ ->setModel('catalog/product')
39
+ ;
40
+
41
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
42
+ if ($storeId) {
43
+ $collection->addStoreFilter((int)$storeId);
44
+ }
45
+
46
+ $collection->setPageSize(5);
47
+ $collection->setCurPage(1);
48
+
49
+ $productIdList = $collection->getColumnValues('product_id');
50
+
51
+ /* @var $productCollection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */
52
+ $productCollection = Mage::getResourceModel('catalog/product_collection');
53
+ $productCollection->addFieldToFilter('entity_id', array('in' => $productIdList));
54
+ $productCollection->addAttributeToSelect(
55
+ array(
56
+ 'sku', // exists in collection when Flat Product is enabled
57
+ 'small_image', // exists in collection when Flat Product is enabled
58
+ )
59
+ );
60
+ $skuList = array();
61
+ $thumbList = array();
62
+ $hlp = Mage::helper('catalog/image');
63
+ /** @var Mage_Catalog_Helper_Image $hlp */
64
+ foreach ($productCollection as $row) {
65
+ /** @var Mage_Catalog_Model_Product $row */
66
+ $skuList[$row->getId()] = $row->getSku();
67
+
68
+ $hlp->init($row, 'small_image');
69
+ $thumbList[$row->getId()] = array(
70
+ 'image2xUrl' => $hlp->resize(224, 300)->__toString(),
71
+ 'image3xUrl' => $hlp->resize(336, 450)->__toString(),
72
+ );
73
+ }
74
+
75
+ $result = array();
76
+ foreach ($collection as $row) {
77
+ if (isset($skuList[$row->getData('product_id')])) {
78
+ $result[] = array(
79
+ 'id' => $row->getData('product_id'),
80
+ 'name' => $row->getData('product_name'),
81
+ 'price' => Mage::helper('core')->currency($row->getData('product_price'), true, false),
82
+ 'sku' => $skuList[$row->getData('product_id')],
83
+ 'qty' => (int)$row->getData('qty_ordered'),
84
+ 'image2xUrl' => $thumbList[$row->getData('product_id')]['image2xUrl'],
85
+ 'image3xUrl' => $thumbList[$row->getData('product_id')]['image3xUrl'],
86
+ );
87
+ }
88
+ }
89
+
90
+ $this->_jsonResult($result);
91
+ }
92
+
93
+ public function mostviewedAction()
94
+ {
95
+ /* @var $collection Mage_Reports_Model_Mysql4_Product_Collection */
96
+ $collection = Mage::getResourceModel('reports/product_collection')
97
+ ->addAttributeToSelect(
98
+ array(
99
+ 'price',
100
+ 'name',
101
+ 'small_image',
102
+ )
103
+ )
104
+ ->addViewsCount()
105
+ ;
106
+
107
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
108
+ if ($storeId) {
109
+ $collection
110
+ ->setStoreId((int)$storeId)
111
+ ->addStoreFilter((int)$storeId)
112
+ ;
113
+ }
114
+ $collection->setPageSize(5);
115
+ $collection->setCurPage(1);
116
+ $collection->load();
117
+
118
+ $hlp = Mage::helper('catalog/image');
119
+ /** @var Mage_Catalog_Helper_Image $hlp */
120
+ $result = array();
121
+ foreach ($collection as $row) {
122
+ /** @var Mage_Catalog_Model_Product $row */
123
+ $hlp->init($row, 'small_image');
124
+ $result[] = array(
125
+ 'id' => $row->getEntityId(),
126
+ 'name' => $row->getName(),
127
+ 'price' => Mage::helper('core')->currency($row->getPrice(), true, false),
128
+ 'sku' => $row->getSku(),
129
+ 'views' => (int)$row->getData('views'),
130
+ 'image2xUrl' => $hlp->resize(224, 300)->__toString(),
131
+ 'image3xUrl' => $hlp->resize(336, 450)->__toString(),
132
+ );
133
+ }
134
+
135
+ $this->_jsonResult($result);
136
+ }
137
+
138
+ public function newcustomersAction()
139
+ {
140
+ /* @var $collection Mage_Reports_Model_Mysql4_Customer_Collection */
141
+ $collection = Mage::getResourceModel('reports/customer_collection')->addCustomerName();
142
+ $storeFilter = 0;
143
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
144
+ if ($storeId) {
145
+ $collection->addAttributeToFilter('store_id', $storeId);
146
+ $storeFilter = 1;
147
+ }
148
+ $collection->addOrdersStatistics($storeFilter);
149
+ $collection->orderByCustomerRegistration();
150
+ $collection->setPageSize(5);
151
+ $collection->setCurPage(1);
152
+ $collection->load();
153
+
154
+ $groupList = Mage::getResourceModel('customer/group_collection')
155
+ ->addFieldToFilter('customer_group_id', array('gt' => 0))
156
+ ->load()
157
+ ->toOptionHash()
158
+ ;
159
+
160
+ $result = array();
161
+ foreach ($collection as $row) {
162
+
163
+ if ((array_key_exists($row->getData('group_id'), $groupList))) {
164
+ $customerGroup = $groupList[$row->getData('group_id')];
165
+ } else {
166
+ $customerGroup = 'N/A';
167
+ }
168
+
169
+ $customerData = array(
170
+ 'id' => $row->getData('entity_id'),
171
+ 'email' => $row->getData('email'),
172
+ 'name' => $row->getData('name'),
173
+ 'created_at' => Mage::helper('neklo_monitor/date')->convertToTimestamp($row->getData('created_at')),
174
+ 'group' => $customerGroup,
175
+ 'average_order_amount' => Mage::helper('core')->currency($row->getData('orders_avg_amount'), true, false),
176
+ 'total_order_amount' => Mage::helper('core')->currency($row->getData('orders_sum_amount'), true, false),
177
+ 'order_count' => (int)$row->getData('orders_count'),
178
+ );
179
+
180
+ $result[] = $customerData;
181
+ }
182
+
183
+ $this->_jsonResult($result);
184
+ }
185
+
186
+ public function topcustomersAction()
187
+ {
188
+ /* @var $collection Mage_Reports_Model_Mysql4_Order_Collection */
189
+ $collection = Mage::getResourceModel('reports/order_collection');
190
+ $collection
191
+ ->groupByCustomer()
192
+ ->addOrdersCount()
193
+ ->joinCustomerName()
194
+ ;
195
+ $storeFilter = 0;
196
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
197
+ if ($storeId) {
198
+ $collection->addAttributeToFilter('store_id', $storeId);
199
+ $storeFilter = 1;
200
+ }
201
+ $collection
202
+ ->addSumAvgTotals($storeFilter)
203
+ ->orderByTotalAmount()
204
+ ;
205
+
206
+ $collection->getSelect()->joinLeft(
207
+ array('customer' => $collection->getTable('customer/entity')),
208
+ 'main_table.customer_id = customer.entity_id',
209
+ array('customer_created_at' => 'customer.created_at')
210
+ );
211
+
212
+ $groupList = Mage::getResourceModel('customer/group_collection')
213
+ ->addFieldToFilter('customer_group_id', array('gt' => 0))
214
+ ->load()
215
+ ->toOptionHash()
216
+ ;
217
+
218
+ $collection->setPageSize(5);
219
+ $collection->setCurPage(1);
220
+
221
+ $result = array();
222
+ foreach ($collection as $row) {
223
+ if ((array_key_exists($row->getData('customer_group_id'), $groupList))) {
224
+ $customerGroup = $groupList[$row->getData('customer_group_id')];
225
+ } else {
226
+ $customerGroup = 'N/A';
227
+ }
228
+
229
+ $customerData = array(
230
+ 'id' => $row->getData('customer_id'),
231
+ 'email' => $row->getData('customer_email'),
232
+ 'name' => $row->getData('name'),
233
+ 'created_at' => Mage::helper('neklo_monitor/date')->convertToTimestamp($row->getData('customer_created_at')),
234
+ 'group' => $customerGroup,
235
+ 'average_order_amount' => Mage::helper('core')->currency($row->getData('orders_avg_amount'), true, false),
236
+ 'total_order_amount' => Mage::helper('core')->currency($row->getData('orders_sum_amount'), true, false),
237
+ 'order_count' => (int)$row->getData('orders_count'),
238
+ );
239
+
240
+ $result[] = $customerData;
241
+ }
242
+
243
+ $this->_jsonResult($result);
244
+ }
245
+
246
+ public function lastordersAction()
247
+ {
248
+ /* @var $collection Mage_Reports_Model_Mysql4_Order_Collection */
249
+ $collection = Mage::getResourceModel('reports/order_collection')
250
+ ->addItemCountExpr()
251
+ ->joinCustomerName('customer')
252
+ ->orderByCreatedAt()
253
+ ;
254
+
255
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
256
+ if ($storeId) {
257
+ $collection->addAttributeToFilter('store_id', $storeId);
258
+ $collection->addRevenueToSelect();
259
+ } else {
260
+ $collection->addRevenueToSelect(true);
261
+ }
262
+
263
+ $collection->setPageSize(5);
264
+ $collection->setCurPage(1);
265
+
266
+ $orderStatusList = Mage::getSingleton('sales/order_config')->getStatuses();
267
+
268
+ $groupList = Mage::getResourceModel('customer/group_collection')
269
+ ->addFieldToFilter('customer_group_id', array('gt' => 0))
270
+ ->load()
271
+ ->toOptionHash()
272
+ ;
273
+
274
+ $result = array();
275
+ foreach ($collection as $row) {
276
+ if ((array_key_exists($row->getData('status'), $orderStatusList))) {
277
+ $orderStatus = $orderStatusList[$row->getData('status')];
278
+ } else {
279
+ $orderStatus = 'N/A';
280
+ }
281
+
282
+ if ((array_key_exists($row->getData('customer_group_id'), $groupList))) {
283
+ $customerGroup = $groupList[$row->getData('customer_group_id')];
284
+ } else {
285
+ $customerGroup = 'N/A';
286
+ }
287
+
288
+ $orderData = array(
289
+ 'id' => $row->getData('entity_id'),
290
+ 'increment_id' => $row->getData('increment_id'),
291
+ 'created_at' => Mage::helper('neklo_monitor/date')->convertToTimestamp($row->getData('created_at')),
292
+ 'status' => $orderStatus,
293
+ 'grand_total' => Mage::helper('core')->currency($row->getData('revenue'), true, false),
294
+ 'items_count' => (int)$row->getData('items_count'),
295
+ 'customer' => array(
296
+ 'id' => $row->getData('customer_id'),
297
+ 'email' => $row->getData('customer_email'),
298
+ 'name' => $row->getData('customer'),
299
+ 'group' => $customerGroup,
300
+ ),
301
+ );
302
+
303
+ $result[] = $orderData;
304
+ }
305
+
306
+ $this->_jsonResult($result);
307
+ }
308
+
309
+ public function lastsearchesAction()
310
+ {
311
+ /* @var $collection Mage_CatalogSearch_Model_Mysql4_Query_Collection */
312
+ $collection = Mage::getResourceModel('catalogsearch/query_collection');
313
+ $collection->setRecentQueryFilter();
314
+
315
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
316
+ if ($storeId) {
317
+ $collection->addAttributeToFilter('store_id', $storeId);
318
+ }
319
+
320
+ $collection->setPageSize(5);
321
+ $collection->setCurPage(1);
322
+
323
+ $result = array();
324
+ foreach ($collection as $row) {
325
+ $searchData = array(
326
+ 'id' => $row->getData('query_id'),
327
+ 'query' => $row->getData('query_text'),
328
+ 'number_of_uses' => $row->getData('popularity'),
329
+ 'number_of_results' => $row->getData('num_results'),
330
+ 'last_usage' => Mage::helper('neklo_monitor/date')->convertToTimestamp($row->getData('updated_at')),
331
+ );
332
+ $result[] = $searchData;
333
+ }
334
+
335
+ $this->_jsonResult($result);
336
+ }
337
+
338
+ public function topsearchesAction()
339
+ {
340
+ /* @var $collection Mage_CatalogSearch_Model_Mysql4_Query_Collection */
341
+ $collection = Mage::getResourceModel('catalogsearch/query_collection');
342
+
343
+ $storeId = $this->_getRequestHelper()->getParam('store', '');
344
+ $collection->setPopularQueryFilter($storeId);
345
+
346
+ $collection->setPageSize(5);
347
+ $collection->setCurPage(1);
348
+
349
+ $result = array();
350
+ foreach ($collection as $row) {
351
+ $searchData = array(
352
+ 'query' => $row->getData('name'),
353
+ 'number_of_uses' => $row->getData('popularity'),
354
+ 'number_of_results' => $row->getData('num_results'),
355
+ 'last_usage' => Mage::helper('neklo_monitor/date')->convertToTimestamp($row->getData('updated_at')),
356
+ );
357
+ $result[] = $searchData;
358
+ }
359
+
360
+ $this->_jsonResult($result);
361
+ }
362
+
363
+ public function chartAction()
364
+ {
365
+ $chartHelper = Mage::helper('adminhtml/dashboard_order');
366
+
367
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
368
+ if ($storeId) {
369
+ $chartHelper->setParam('store', $storeId);
370
+ }
371
+
372
+ $chartType = $this->_getRequestHelper()->getParam('type', 'quantity');
373
+ if (!$chartType || !in_array($chartType, array('quantity', 'revenue'))) {
374
+ $chartType = 'quantity';
375
+ }
376
+
377
+ $availablePeriodList = Mage::helper('adminhtml/dashboard_data')->getDatePeriods();
378
+ $period = $this->_getRequestHelper()->getParam('period', '24h');
379
+ if (!$period || !in_array($period, array_keys($availablePeriodList))) {
380
+ $period = '24h';
381
+ }
382
+ $chartHelper->setParam('period', $period);
383
+ switch ($period) {
384
+ case '24h':
385
+ $periodMask = 'yyyy-MM-dd HH:00';
386
+ break;
387
+ case '7d':
388
+ case '1m':
389
+ $periodMask = 'yyyy-MM-dd';
390
+ break;
391
+ case '1y':
392
+ case '2y':
393
+ $periodMask = 'yyyy-MM';
394
+ break;
395
+ }
396
+
397
+ $chartData = array();
398
+ $items = $chartHelper->getCollection()->getItems();
399
+ foreach ($items as $item) {
400
+ $zDate = new Zend_Date($item->getData('range'), $periodMask);
401
+ $chartData[$zDate->getTimestamp()] = (float)$item->getData($chartType);
402
+ }
403
+
404
+ list ($dateStart, $dateEnd) = Mage::getResourceModel('reports/order_collection')->getDateRange($period, '', '', true);
405
+ while ($dateStart->compare($dateEnd) < 0) {
406
+ $timestamp = $dateStart->getTimestamp();
407
+ switch ($period) {
408
+ case '24h':
409
+ $dateStart->addHour(1);
410
+ break;
411
+ case '7d':
412
+ case '1m':
413
+ $dateStart->addDay(1);
414
+ break;
415
+ case '1y':
416
+ case '2y':
417
+ $dateStart->addMonth(1);
418
+ break;
419
+ }
420
+ if (!array_key_exists($timestamp, $chartData)) {
421
+ $chartData[$timestamp] = 0;
422
+ }
423
+ }
424
+ ksort($chartData);
425
+
426
+ $result = array();
427
+ foreach ($chartData as $date => $value) {
428
+ $result[] = array(
429
+ 'date' => $date,
430
+ 'value' => $value,
431
+ );
432
+ }
433
+
434
+ $this->_jsonResult($result);
435
+ }
436
+
437
+ }
app/code/community/Neklo/Monitor/controllers/InfoController.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_InfoController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function totalAction()
6
+ {
7
+ /** @var Neklo_Monitor_Helper_Date $hlpDate */
8
+ $hlpDate = Mage::helper('neklo_monitor/date');
9
+
10
+ $fromTimestamp = $this->_getRequestHelper()->getParam('from', 0);
11
+ $fromDate = $hlpDate->convertToString($fromTimestamp);
12
+
13
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
14
+
15
+ // get new orders list
16
+
17
+ /* @var $orderCollection Mage_Sales_Model_Mysql4_Order_Grid_Collection */
18
+ $orderCollection = Mage::getResourceModel('sales/order_grid_collection');
19
+ if ($storeId) {
20
+ $orderCollection->addFieldToFilter('store_id', $storeId);
21
+ }
22
+ $orderCollection->addFieldToFilter('created_at', array('gt' => $fromDate));
23
+ $ordersCount = $orderCollection->getSize();
24
+
25
+ // get new customers list
26
+
27
+ /* @var $custCollection Mage_Customer_Model_Entity_Customer_Collection */
28
+ $custCollection = Mage::getResourceModel('customer/customer_collection');
29
+ $orderCollection->addFieldToFilter('created_at', array('gt' => $fromDate));
30
+ $customersCount = $custCollection->getSize();
31
+
32
+ $result = array(
33
+ 'orders_count' => $ordersCount,
34
+ 'customers_count' => $customersCount,
35
+ );
36
+
37
+ $this->_jsonResult($result);
38
+ }
39
+
40
+ public function storeviewlistAction()
41
+ {
42
+ $websiteList = array();
43
+ /* @var $website Mage_Core_Model_Website */
44
+ foreach (Mage::app()->getWebsites() as $key => $website) {
45
+ $groupList = array();
46
+ foreach ($website->getGroups() as $group) {
47
+ $storeViewList = array();
48
+ foreach ($group->getStores() as $store) {
49
+ $storeViewList[] = array(
50
+ 'store_id' => $store->getId(),
51
+ 'name' => $store->getName(),
52
+ );
53
+ }
54
+ $groupList[] = array(
55
+ 'group_id' => $group->getId(),
56
+ 'name' => $group->getName(),
57
+ 'store' => $storeViewList,
58
+ );
59
+ }
60
+
61
+ $websiteList[] = array(
62
+ 'website_id' => $website->getId(),
63
+ 'name' => $website->getName(),
64
+ 'group' => $groupList,
65
+ );
66
+ }
67
+ $result = array(
68
+ 'website' => $websiteList,
69
+ );
70
+
71
+ $this->_jsonResult($result);
72
+ }
73
+ }
app/code/community/Neklo/Monitor/controllers/OrderController.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_OrderController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function listAction()
6
+ {
7
+ /* @var $collection Mage_Sales_Model_Mysql4_Order_Grid_Collection */
8
+ $collection = Mage::getResourceModel('sales/order_grid_collection');
9
+
10
+ /** @var Neklo_Monitor_Helper_Date $hlpDate */
11
+ $hlpDate = Mage::helper('neklo_monitor/date');
12
+
13
+ // for pages lists - load next page rows despite newly inserted rows
14
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
15
+ $queryDate = $hlpDate->convertToString($queryTimestamp);
16
+ if ($queryTimestamp > 0) {
17
+ $collection->addFieldToFilter('main_table.created_at', array('lt' => $queryDate));
18
+ }
19
+
20
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
21
+ $page = ceil($offset / self::PAGE_SIZE) + 1;
22
+ $collection->setPage($page, self::PAGE_SIZE);
23
+
24
+ $collection->getSelect()
25
+ ->join(
26
+ array('ce' => $collection->getTable('customer/entity')),
27
+ 'main_table.customer_id = ce.entity_id',
28
+ array(
29
+ 'email' => 'ce.email',
30
+ 'customer_group_id' => 'ce.group_id',
31
+ )
32
+ )
33
+ ;
34
+
35
+ if ($customerId = $this->_getRequestHelper()->getParam('customer_id', null)) {
36
+ $collection->addFieldToFilter('customer_id', $customerId);
37
+ }
38
+
39
+ $orderItemsSelect = $collection->getConnection()->select();
40
+ $orderItemsSelect
41
+ ->from(
42
+ $collection->getTable('sales/order_item'),
43
+ array(
44
+ 'order_id' => 'order_id',
45
+ 'items_count' => 'count(item_id)',
46
+ )
47
+ )
48
+ ->group('order_id')
49
+ ;
50
+
51
+ $collection->getSelect()
52
+ ->join(
53
+ array('oi' => $orderItemsSelect),
54
+ 'main_table.entity_id = oi.order_id',
55
+ array(
56
+ 'items_count' => 'oi.items_count',
57
+ )
58
+ )
59
+ ;
60
+
61
+ $orderStatusList = Mage::getSingleton('sales/order_config')->getStatuses();
62
+
63
+ $groupList = Mage::getResourceModel('customer/group_collection')
64
+ ->addFieldToFilter('customer_group_id', array('gt' => 0))
65
+ ->load()
66
+ ->toOptionHash()
67
+ ;
68
+
69
+ $orderList = array(
70
+ 'result' => array(),
71
+ );
72
+ foreach ($collection as $order) {
73
+ /** @var $order Mage_Sales_Model_Order */
74
+ if ((array_key_exists($order->getStatus(), $orderStatusList))) {
75
+ $orderStatus = $orderStatusList[$order->getStatus()];
76
+ } else {
77
+ $orderStatus = 'N/A';
78
+ }
79
+
80
+ $orderData = array(
81
+ 'id' => $order->getId(),
82
+ 'increment_id' => $order->getIncrementId(),
83
+ 'created_at' => $hlpDate->convertToTimestamp($order->getCreatedAt()),
84
+ 'status' => $orderStatus,
85
+ 'grand_total' => Mage::helper('core')->currency($order->getBaseGrandTotal(), true, false),
86
+ 'items_count' => (int)$order->getItemsCount(),
87
+ );
88
+
89
+ if ((array_key_exists($order->getCustomerGroupId(), $groupList))) {
90
+ $customerGroup = $groupList[$order->getCustomerGroupId()];
91
+ } else {
92
+ $customerGroup = 'N/A';
93
+ }
94
+
95
+ $customerData = array(
96
+ 'id' => $order->getCustomerId(),
97
+ 'email' => $order->getEmail(),
98
+ 'name' => $order->getBillingName(),
99
+ 'group' => $customerGroup,
100
+ );
101
+ $orderData['customer'] = $customerData;
102
+
103
+ $orderList['result'][] = $orderData;
104
+ }
105
+
106
+ // get new entities count
107
+
108
+ if ($queryTimestamp > 0) {
109
+ /* @var $collection Mage_Sales_Model_Mysql4_Order_Grid_Collection */
110
+ $collection = Mage::getResourceModel('sales/order_grid_collection');
111
+ $collection->addFieldToFilter('main_table.created_at', array('gteq' => $queryDate));
112
+ $orderList['new_entities_count'] = $collection->getSize();
113
+ // $orderList['sql'] = $collection->getSelectCountSql()->__toString();
114
+ }
115
+
116
+ $this->_jsonResult($orderList);
117
+ }
118
+ }
app/code/community/Neklo/Monitor/controllers/ProductController.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_ProductController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function outofstockAction()
6
+ {
7
+ $storeId = $this->_getRequestHelper()->getParam('store', null);
8
+
9
+ /** @var Neklo_Monitor_Model_Minfo_Parser $parser */
10
+ $parser = Mage::getModel('neklo_monitor/minfo_parser');
11
+ $collection = $parser->getProductsOutofstockCollection($storeId);
12
+
13
+ $outOfStockProductList = array();
14
+ $hlp = Mage::helper('catalog/image');
15
+ /** @var Mage_Catalog_Helper_Image $hlp */
16
+ foreach ($collection as $row) {
17
+ /** @var Mage_Catalog_Model_Product $row */
18
+ $hlp->init($row, 'small_image');
19
+ $outOfStockProductList[] = array(
20
+ 'id' => $row->getEntityId(),
21
+ 'name' => $row->getName(),
22
+ 'price' => Mage::helper('core')->currency($row->getPrice(), true, false),
23
+ 'sku' => $row->getSku(),
24
+ 'image2xUrl' => $hlp->resize(224, 300)->__toString(),
25
+ 'image3xUrl' => $hlp->resize(336, 450)->__toString(),
26
+ );
27
+ }
28
+
29
+ $this->_jsonResult($outOfStockProductList);
30
+ }
31
+
32
+ }
app/code/community/Neklo/Monitor/controllers/Report/SalesController.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Report_SalesController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ // @see Mage_Adminhtml_Block_Report_Grid_Abstract::_prepareCollection()
6
+ protected function _prepareResourceCollection($resourceCollectionName)
7
+ {
8
+ /** @var Neklo_Monitor_Helper_Date $hlpDate */
9
+ $hlpDate = Mage::helper('neklo_monitor/date');
10
+
11
+ $storeIds = $this->_getRequestHelper()->getParam('store', null);
12
+ $periodType = $this->_getRequestHelper()->getParam('group', null);
13
+
14
+ $fromTimestamp = $this->_getRequestHelper()->getParam('from', null);
15
+ $fromDate = $hlpDate->convertToString($fromTimestamp);
16
+
17
+ $toTimestamp = $this->_getRequestHelper()->getParam('to', null);
18
+ $toDate = $hlpDate->convertToString($toTimestamp);
19
+
20
+ $orderStatuses = $this->_getRequestHelper()->getParam('status', null);
21
+
22
+ /** @var Mage_Sales_Model_Mysql4_Report_Collection_Abstract $resourceCollection */
23
+ $resourceCollection = Mage::getResourceModel($resourceCollectionName);
24
+ $resourceCollection
25
+ ->setPeriod($periodType)
26
+ ->setDateRange($fromDate, $toDate)
27
+ ->addStoreFilter(explode(',', $storeIds))
28
+ ->addOrderStatusFilter($orderStatuses)
29
+ // ->setAggregatedColumns($this->_getAggregatedColumns())
30
+ ;
31
+
32
+ /* if 'show_empty_rows'
33
+ Mage::helper('reports')->prepareIntervalsCollection(
34
+ $mainCollection,
35
+ $fromDate,
36
+ $toDate,
37
+ $periodType
38
+ );
39
+ */
40
+
41
+ /** @var Mage_Sales_Model_Mysql4_Report_Collection_Abstract $totalsCollection */
42
+ $totalsCollection = Mage::getResourceModel($resourceCollectionName);
43
+ $totalsCollection
44
+ ->setPeriod($periodType)
45
+ ->setDateRange($fromDate, $toDate)
46
+ ->addStoreFilter(explode(',', $storeIds))
47
+ ->addOrderStatusFilter($orderStatuses)
48
+ // ->setAggregatedColumns($this->_getAggregatedColumns())
49
+ ->isTotals(true);
50
+
51
+ return array($resourceCollection, $totalsCollection);
52
+ }
53
+
54
+ /**
55
+ * @param Mage_Sales_Model_Mysql4_Report_Collection_Abstract $resourceCollection
56
+ * @param Mage_Sales_Model_Mysql4_Report_Collection_Abstract $totalsCollection
57
+ * @param array $aggregatedColumns
58
+ * @return array
59
+ */
60
+ protected function _fetchReport($resourceCollection, $totalsCollection, $aggregatedColumns = array())
61
+ {
62
+ /** @var Mage_Reports_Model_Grouped_Collection $mainCollection */
63
+ $mainCollection = Mage::getModel('reports/grouped_collection');
64
+ $mainCollection->setColumnGroupBy('period');
65
+ $mainCollection->setResourceCollection($resourceCollection);
66
+
67
+ $mainCollection->load();
68
+
69
+ $list = array(
70
+ 'report' => array(),
71
+ 'totals' => false,
72
+ );
73
+ foreach ($mainCollection->getItems() as $_period => $_item) {
74
+ /** @var Mage_Adminhtml_Model_Report_Item $_item */
75
+ $list['report'][] = $_item->getData();
76
+ }
77
+
78
+ if ($aggregatedColumns) {
79
+ $totalsCollection->setAggregatedColumns($aggregatedColumns);
80
+ $totalsCollection->load();
81
+ foreach ($totalsCollection as $_item) {
82
+ $list['totals'] = $_item->getData();
83
+ break;
84
+ }
85
+ }
86
+
87
+ return $list;
88
+ }
89
+
90
+ public function orderAction()
91
+ {
92
+ $resourceCollectionName = 'sales/report_order_updatedat_collection';
93
+ // $resourceCollectionName = 'sales/report_order_collection';
94
+ list($resourceCollection, $totalsCollection) = $this->_prepareResourceCollection($resourceCollectionName);
95
+ $list = $this->_fetchReport($resourceCollection, $totalsCollection, array(
96
+ 'orders_count' => 'SUM(orders_count)',
97
+ 'total_qty_ordered' => 'SUM(total_qty_ordered)',
98
+ 'total_qty_invoiced' => 'SUM(total_qty_invoiced)',
99
+ 'total_income_amount' => 'SUM(total_income_amount)',
100
+ 'total_revenue_amount' => 'SUM(total_revenue_amount)',
101
+ 'total_profit_amount' => 'SUM(total_profit_amount)',
102
+ 'total_invoiced_amount' => 'SUM(total_invoiced_amount)',
103
+ 'total_canceled_amount' => 'SUM(total_canceled_amount)',
104
+ 'total_paid_amount' => 'SUM(total_paid_amount)',
105
+ 'total_refunded_amount' => 'SUM(total_refunded_amount)',
106
+ 'total_tax_amount' => 'SUM(total_tax_amount)',
107
+ 'total_tax_amount_actual' => 'SUM(total_tax_amount_actual)',
108
+ 'total_shipping_amount' => 'SUM(total_shipping_amount)',
109
+ 'total_shipping_amount_actual' => 'SUM(total_shipping_amount_actual)',
110
+ 'total_discount_amount' => 'SUM(total_discount_amount)',
111
+ 'total_discount_amount_actual' => 'SUM(total_discount_amount_actual)',
112
+ ));
113
+ $this->_jsonResult($list);
114
+ }
115
+
116
+ public function taxAction()
117
+ {
118
+ $resourceCollectionName = 'tax/report_updatedat_collection';
119
+ // $resourceCollectionName = 'tax/report_collection';
120
+ list($resourceCollection, $totalsCollection) = $this->_prepareResourceCollection($resourceCollectionName);
121
+ $list = $this->_fetchReport($resourceCollection, $totalsCollection, array(
122
+ 'orders_count' => 'SUM(orders_count)',
123
+ 'tax_base_amount_sum' => 'SUM(tax_base_amount_sum)',
124
+ ));
125
+ $this->_jsonResult($list);
126
+ }
127
+
128
+ public function invoicedAction()
129
+ {
130
+ $resourceCollectionName = 'sales/report_invoiced_collection_invoiced';
131
+ // $resourceCollectionName = 'sales/report_invoiced_collection_order';
132
+ list($resourceCollection, $totalsCollection) = $this->_prepareResourceCollection($resourceCollectionName);
133
+ $list = $this->_fetchReport($resourceCollection, $totalsCollection, array(
134
+ 'orders_count' => 'SUM(orders_count)',
135
+ 'orders_invoiced' => 'SUM(orders_invoiced)',
136
+ 'invoiced' => 'SUM(invoiced)',
137
+ 'invoiced_captured' => 'SUM(invoiced_captured)',
138
+ 'invoiced_not_captured' => 'SUM(invoiced_not_captured)',
139
+ ));
140
+ $this->_jsonResult($list);
141
+ }
142
+
143
+ public function shippingAction()
144
+ {
145
+ $resourceCollectionName = 'sales/report_shipping_collection_shipment';
146
+ // $resourceCollectionName = 'sales/report_shipping_collection_order';
147
+ list($resourceCollection, $totalsCollection) = $this->_prepareResourceCollection($resourceCollectionName);
148
+ $list = $this->_fetchReport($resourceCollection, $totalsCollection, array(
149
+ 'orders_count' => 'SUM(orders_count)',
150
+ 'total_shipping' => 'SUM(total_shipping)',
151
+ 'total_shipping_actual' => 'SUM(total_shipping_actual)',
152
+ ));
153
+ $this->_jsonResult($list);
154
+ }
155
+
156
+ public function refundedAction()
157
+ {
158
+ $resourceCollectionName = 'sales/report_refunded_collection_refunded';
159
+ // $resourceCollectionName = 'sales/report_refunded_collection_order';
160
+ list($resourceCollection, $totalsCollection) = $this->_prepareResourceCollection($resourceCollectionName);
161
+ $list = $this->_fetchReport($resourceCollection, $totalsCollection, array(
162
+ 'orders_count' => 'SUM(orders_count)',
163
+ 'refunded' => 'SUM(refunded)',
164
+ 'online_refunded' => 'SUM(online_refunded)',
165
+ 'offline_refunded' => 'SUM(offline_refunded)',
166
+ ));
167
+ $this->_jsonResult($list);
168
+ }
169
+
170
+ public function couponsAction()
171
+ {
172
+ $resourceCollectionName = 'salesrule/report_updatedat_collection';
173
+ // $resourceCollectionName = 'salesrule/report_collection';
174
+ list($resourceCollection, $totalsCollection) = $this->_prepareResourceCollection($resourceCollectionName);
175
+ $list = $this->_fetchReport($resourceCollection, $totalsCollection, array(
176
+ 'coupon_uses' => 'SUM(coupon_uses)',
177
+ 'subtotal_amount' => 'SUM(subtotal_amount)',
178
+ 'discount_amount' => 'SUM(discount_amount)',
179
+ 'total_amount' => 'SUM(total_amount)',
180
+ 'subtotal_amount_actual' => 'SUM(subtotal_amount_actual)',
181
+ 'discount_amount_actual' => 'SUM(discount_amount_actual)',
182
+ 'total_amount_actual' => 'SUM(total_amount_actual)',
183
+ ));
184
+ $this->_jsonResult($list);
185
+ }
186
+
187
+ }
app/code/community/Neklo/Monitor/controllers/State/CacheController.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_State_CacheController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function listAction()
6
+ {
7
+ // get cachers list and their statuses
8
+
9
+ $cachers = array();
10
+ $invalidatedTypes = Mage::app()->getCacheInstance()->getInvalidatedTypes();
11
+ foreach (Mage::app()->getCacheInstance()->getTypes() as $type => $data) {
12
+ if (isset($invalidatedTypes[$data->getId()])) {
13
+ $status = 2; // $this->__('Invalidated');
14
+ } else {
15
+ if ($data->getStatus()) {
16
+ $status = 1; // $this->__('Enabled');
17
+ } else {
18
+ $status = 0; // $this->__('Disabled');
19
+ }
20
+ }
21
+
22
+ $cachers[] = array(
23
+ 'id' => $data->getId(),
24
+ 'label' => $data->getCacheType(),
25
+ 'status' => $status,
26
+ );
27
+ }
28
+
29
+ $result = array(
30
+ 'result' => $cachers,
31
+ );
32
+
33
+ $this->_jsonResult($result);
34
+ }
35
+
36
+ public function refreshAction()
37
+ {
38
+ // TODO
39
+ }
40
+
41
+ public function flushallAction()
42
+ {
43
+ // TODO
44
+ }
45
+
46
+ }
app/code/community/Neklo/Monitor/controllers/State/IndexerController.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_State_IndexerController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function listAction()
6
+ {
7
+ // get indexers list and their statuses
8
+
9
+ $indexers = array();
10
+ /** @var Mage_Index_Model_Mysql4_Process_Collection $collection */
11
+ $collection = Mage::getResourceModel('index/process_collection');
12
+
13
+ foreach ($collection as $key => $item) {
14
+ /** @var Mage_Index_Model_Process $item */
15
+ if (method_exists($item->getIndexer(), 'isVisible')) { // Older Magento versions do not have this method
16
+ if (!$item->getIndexer()->isVisible()) {
17
+ $collection->removeItemByKey($key);
18
+ continue;
19
+ }
20
+ }
21
+ if ($item->isLocked()) {
22
+ $item->setStatus(Mage_Index_Model_Process::STATUS_RUNNING);
23
+ }
24
+ $indexer = array(
25
+ 'id' => $item->getData('indexer_code'),
26
+ 'label' => $item->getIndexer()->getName(),
27
+ 'status' => $item->getStatus(), // (Mage_Index_Model_Process::STATUS_PENDING || STATUS_RUNNING || STATUS_REQUIRE_REINDEX)
28
+ 'update_required' => 0,
29
+ );
30
+ if (method_exists($item, 'getUnprocessedEventsCollection')) { // Older Magento versions do not have this method
31
+ $indexer['update_required'] = ($item->getUnprocessedEventsCollection()->count() > 0 ? 1 : 0); // (0 || 1)
32
+ }
33
+ $indexers[] = $indexer;
34
+ }
35
+
36
+ $result = array(
37
+ 'result' => $indexers,
38
+ );
39
+
40
+ $this->_jsonResult($result);
41
+ }
42
+
43
+ public function reindexAction()
44
+ {
45
+ // TODO
46
+ }
47
+
48
+ public function reindexallAction()
49
+ {
50
+ // TODO
51
+ }
52
+
53
+ }
app/code/community/Neklo/Monitor/controllers/Var/LogController.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Var_LogController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function listAction()
6
+ {
7
+ /* @var $collection Neklo_Monitor_Model_Resource_Minfo_Log_Collection */
8
+ $collection = Mage::getResourceModel('neklo_monitor/minfo_log_collection');
9
+
10
+ // for pages lists - load next page rows despite newly inserted rows
11
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
12
+ if ($queryTimestamp > 0) {
13
+ $collection->addFieldToFilter('last_time', array('lt' => $queryTimestamp));
14
+ }
15
+
16
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
17
+ $page = ceil($offset / self::PAGE_SIZE) + 1;
18
+ $collection->setPageSize(self::PAGE_SIZE);
19
+ $collection->setCurPage($page);
20
+
21
+ $collection->setOrder('last_time');
22
+
23
+ $list = array('result' => array());
24
+ foreach ($collection as $log) {
25
+ /** @var Neklo_Monitor_Model_Minfo_Log $log */
26
+ $list['result'][] = array(
27
+ 'type' => $log->getData('type'),
28
+ 'hash' => $log->getData('hash'),
29
+ 'message' => $log->getData('message'),
30
+ 'qty' => (int)$log->getData('qty'),
31
+ 'last_time' => (int)$log->getData('last_time'),
32
+ 'first_time' => (int)$log->getData('first_time'),
33
+ );
34
+ }
35
+ // $list['sql1'] = $collection->getSelectSql(true);
36
+
37
+ // get new entities count
38
+
39
+ if ($queryTimestamp > 0) {
40
+ /* @var $collection Neklo_Monitor_Model_Resource_Minfo_Log_Collection */
41
+ $collection = Mage::getResourceModel('neklo_monitor/minfo_log_collection');
42
+ $collection->addFieldToFilter('last_time', array('gteq' => $queryTimestamp));
43
+ $list['new_entities_count'] = $collection->getSize();
44
+ // $list['sql2'] = $collection->getSelectCountSql()->__toString();
45
+ }
46
+
47
+ $this->_jsonResult($list);
48
+ }
49
+
50
+ public function viewAction()
51
+ {
52
+ $hash = $this->_getRequestHelper()->getParam('hash', '');
53
+
54
+ /* @var $log Neklo_Monitor_Model_Minfo_Log */
55
+ $log = Mage::getModel('neklo_monitor/minfo_log');
56
+ $log->load($hash, 'hash');
57
+
58
+ // for pages lists - load next page rows despite newly inserted rows
59
+ $filter = array();
60
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
61
+ if ($queryTimestamp > 0) {
62
+ $filter = array('lt' => $queryTimestamp);
63
+ }
64
+
65
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
66
+
67
+ $collection = $log->getTimesCollection($offset, self::PAGE_SIZE, $filter);
68
+
69
+ $list = array('result' => array());
70
+ Mage::log($collection->getItems());
71
+ foreach ($collection as $_logTime) {
72
+ /** @var Varien_Object $_logTime */
73
+ $list['result'][] = (int)$_logTime->getData();
74
+ }
75
+
76
+ // get new entities count
77
+
78
+ if ($queryTimestamp > 0) {
79
+ $collection = $log->getTimesCollection(0, null, array('gteq' => $queryTimestamp));
80
+ $list['new_entities_count'] = $collection->getSize();
81
+ }
82
+
83
+ $this->_jsonResult($list);
84
+ }
85
+
86
+ }
app/code/community/Neklo/Monitor/controllers/Var/ReportController.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Neklo_Monitor_Var_ReportController extends Neklo_Monitor_Controller_Abstract
4
+ {
5
+ public function listAction()
6
+ {
7
+ /* @var $collection Neklo_Monitor_Model_Resource_Minfo_Report_Collection */
8
+ $collection = Mage::getResourceModel('neklo_monitor/minfo_report_collection');
9
+
10
+ // for pages lists - load next page rows despite newly inserted rows
11
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
12
+ if ($queryTimestamp > 0) {
13
+ $collection->addFieldToFilter('last_time', array('lt' => $queryTimestamp));
14
+ }
15
+
16
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
17
+ $page = ceil($offset / self::PAGE_SIZE) + 1;
18
+ $collection->setPageSize(self::PAGE_SIZE);
19
+ $collection->setCurPage($page);
20
+
21
+ $collection->setOrder('last_time');
22
+
23
+ $list = array('result' => array());
24
+ foreach ($collection as $report) {
25
+ /** @var Neklo_Monitor_Model_Minfo_Report $report */
26
+ $list['result'][] = array(
27
+ 'hash' => $report->getData('hash'),
28
+ 'message' => $report->getData('message'),
29
+ 'qty' => (int) $report->getData('qty'),
30
+ 'last_time' => (int) $report->getData('last_time'),
31
+ 'first_time' => (int) $report->getData('first_time'),
32
+ );
33
+ }
34
+ // $list['sql1'] = $collection->getSelectSql(true);
35
+
36
+ // get new entities count
37
+
38
+ if ($queryTimestamp > 0) {
39
+ /* @var $collection Neklo_Monitor_Model_Resource_Minfo_Report_Collection */
40
+ $collection = Mage::getResourceModel('neklo_monitor/minfo_report_collection');
41
+ $collection->addFieldToFilter('last_time', array('gteq' => $queryTimestamp));
42
+ $list['new_entities_count'] = $collection->getSize();
43
+ // $list['sql2'] = $collection->getSelectCountSql()->__toString();
44
+ }
45
+
46
+ $this->_jsonResult($list);
47
+ }
48
+
49
+ public function viewAction()
50
+ {
51
+ $hash = $this->_getRequestHelper()->getParam('hash', '');
52
+
53
+ /* @var $report Neklo_Monitor_Model_Minfo_Report */
54
+ $report = Mage::getModel('neklo_monitor/minfo_report');
55
+ $report->load($hash, 'hash');
56
+
57
+ // for pages lists - load next page rows despite newly inserted rows
58
+ $filter = array();
59
+ $queryTimestamp = (int) $this->_getRequestHelper()->getParam('query_timestamp', 0);
60
+ if ($queryTimestamp > 0) {
61
+ $filter = array('mtime' => array('lt' => $queryTimestamp));
62
+ }
63
+
64
+ $offset = $this->_getRequestHelper()->getParam('offset', 0);
65
+
66
+ $collection = $report->getFilesCollection($offset, self::PAGE_SIZE, $filter);
67
+
68
+ $list = array('result' => array());
69
+ foreach ($collection as $_reportFile) {
70
+ /** @var Varien_Object $_reportFile */
71
+ $list['result'][] = array(
72
+ 'path' => '' . $_reportFile->getData('path'),
73
+ 'name' => '' . $_reportFile->getData('name'),
74
+ 'time' => (int) $_reportFile->getData('time'),
75
+ 'size' => (int) $_reportFile->getData('size'),
76
+ );
77
+ }
78
+
79
+ // get new entities count
80
+
81
+ if ($queryTimestamp > 0) {
82
+ $collection = $report->getFilesCollection(0, null, array('mtime' => array('gteq' => $queryTimestamp)));
83
+ $list['new_entities_count'] = $collection->getSize();
84
+ }
85
+
86
+ $this->_jsonResult($list);
87
+ }
88
+
89
+ }
app/code/community/Neklo/Monitor/etc/adminhtml.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <all>
6
+ <title>Allow Everything</title>
7
+ </all>
8
+ <admin>
9
+ <children>
10
+ <system>
11
+ <children>
12
+ <config>
13
+ <children>
14
+ <neklo_monitor translate="title" module="neklo_monitor">
15
+ <title>Neklo LLC - Monitor</title>
16
+ <sort_order>150</sort_order>
17
+ </neklo_monitor>
18
+ </children>
19
+ </config>
20
+ </children>
21
+ </system>
22
+ </children>
23
+ </admin>
24
+ </resources>
25
+ </acl>
26
+ </config>
app/code/community/Neklo/Monitor/etc/config.xml ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <modules>
4
+ <Neklo_Monitor>
5
+ <version>1.1.2</version>
6
+ </Neklo_Monitor>
7
+ </modules>
8
+ <global>
9
+ <blocks>
10
+ <neklo_monitor>
11
+ <class>Neklo_Monitor_Block</class>
12
+ </neklo_monitor>
13
+ <neklo_monitor_adminhtml>
14
+ <class>Neklo_Monitor_Block_Adminhtml</class>
15
+ </neklo_monitor_adminhtml>
16
+ </blocks>
17
+ <helpers>
18
+ <neklo_monitor>
19
+ <class>Neklo_Monitor_Helper</class>
20
+ </neklo_monitor>
21
+ </helpers>
22
+ <models>
23
+ <neklo_monitor>
24
+ <class>Neklo_Monitor_Model</class>
25
+ <resourceModel>neklo_monitor_resource</resourceModel>
26
+ </neklo_monitor>
27
+ <neklo_monitor_resource>
28
+ <class>Neklo_Monitor_Model_Resource</class>
29
+ <entities>
30
+ <report><table>neklo_monitor_report</table></report>
31
+ <log><table>neklo_monitor_log</table></log>
32
+ </entities>
33
+ </neklo_monitor_resource>
34
+ </models>
35
+ <resources>
36
+ <neklo_monitor_setup>
37
+ <setup>
38
+ <module>Neklo_Monitor</module>
39
+ </setup>
40
+ </neklo_monitor_setup>
41
+ </resources>
42
+ </global>
43
+ <frontend>
44
+ <routers>
45
+ <neklo_monitor>
46
+ <use>standard</use>
47
+ <args>
48
+ <module>Neklo_Monitor</module>
49
+ <frontName>neklo_monitor</frontName>
50
+ </args>
51
+ </neklo_monitor>
52
+ </routers>
53
+ </frontend>
54
+ <crontab>
55
+ <jobs>
56
+ <neklo_monitor_send_server_info>
57
+ <schedule>
58
+ <cron_expr>* * * * *</cron_expr>
59
+ </schedule>
60
+ <run>
61
+ <model>neklo_monitor/cron_server::run</model>
62
+ </run>
63
+ </neklo_monitor_send_server_info>
64
+ <neklo_monitor_send_store_info>
65
+ <schedule>
66
+ <cron_expr>* * * * *</cron_expr>
67
+ </schedule>
68
+ <run>
69
+ <model>neklo_monitor/cron_store::run</model>
70
+ </run>
71
+ </neklo_monitor_send_store_info>
72
+ <neklo_monitor_collect_store>
73
+ <schedule>
74
+ <cron_expr>*/5 * * * *</cron_expr>
75
+ </schedule>
76
+ <run>
77
+ <model>neklo_monitor/cron_store::collect</model>
78
+ </run>
79
+ </neklo_monitor_collect_store>
80
+ </jobs>
81
+ </crontab>
82
+ <default>
83
+ <neklo_monitor>
84
+ <general>
85
+ <is_enabled>0</is_enabled>
86
+ </general>
87
+ <gateway>
88
+ <token></token>
89
+ <token_generated_at></token_generated_at>
90
+ <server_type>production</server_type>
91
+ <sid></sid>
92
+ </gateway>
93
+ </neklo_monitor>
94
+ </default>
95
+ </config>
app/code/community/Neklo/Monitor/etc/system.xml ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <neklo translate="label" module="neklo_monitor">
5
+ <label>[NEKLO]</label>
6
+ <sort_order>310</sort_order>
7
+ </neklo>
8
+ </tabs>
9
+ <sections>
10
+ <neklo_monitor translate="label" module="neklo_monitor">
11
+ <label>Monitor</label>
12
+ <tab>neklo</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>9999</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>0</show_in_website>
17
+ <show_in_store>0</show_in_store>
18
+ <groups>
19
+ <general translate="label">
20
+ <label>General Settings</label>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>10</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>0</show_in_website>
25
+ <show_in_store>0</show_in_store>
26
+ <fields>
27
+ <is_enabled translate="label">
28
+ <label>Is Enabled</label>
29
+ <frontend_type>select</frontend_type>
30
+ <source_model>adminhtml/system_config_source_yesno</source_model>
31
+ <sort_order>10</sort_order>
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>0</show_in_website>
34
+ <show_in_store>0</show_in_store>
35
+ </is_enabled>
36
+ </fields>
37
+ </general>
38
+ <!--security translate="label">
39
+ <label>Security Settings</label>
40
+ <frontend_type>text</frontend_type>
41
+ <sort_order>20</sort_order>
42
+ <show_in_default>1</show_in_default>
43
+ <show_in_website>0</show_in_website>
44
+ <show_in_store>0</show_in_store>
45
+ <fields>
46
+ </fields>
47
+ </security-->
48
+ <gateway>
49
+ <label>Gateway Settings</label>
50
+ <frontend_type>text</frontend_type>
51
+ <sort_order>30</sort_order>
52
+ <show_in_default>1</show_in_default>
53
+ <show_in_website>0</show_in_website>
54
+ <show_in_store>0</show_in_store>
55
+ <fields>
56
+ <token translate="label">
57
+ <label>Token</label>
58
+ <frontend_type>label</frontend_type>
59
+ <backend_model>neklo_monitor/system_config_backend_token</backend_model>
60
+ <sort_order>19</sort_order>
61
+ <show_in_default>1</show_in_default>
62
+ <show_in_website>0</show_in_website>
63
+ <show_in_store>0</show_in_store>
64
+ </token>
65
+ <!--server_type>
66
+ <label>Server Type</label>
67
+ <frontend_type>select</frontend_type>
68
+ <source_model>neklo_monitor/system_config_source_server_type</source_model>
69
+ <sort_order>10</sort_order>
70
+ <show_in_default>1</show_in_default>
71
+ <show_in_website>0</show_in_website>
72
+ <show_in_store>0</show_in_store>
73
+ </server_type-->
74
+ <status_production translate="label">
75
+ <label>Status</label>
76
+ <frontend_type>label</frontend_type>
77
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_status</frontend_model>
78
+ <backend_model>neklo_monitor/system_config_backend_empty</backend_model>
79
+ <sort_order>20</sort_order>
80
+ <show_in_default>1</show_in_default>
81
+ <show_in_website>0</show_in_website>
82
+ <show_in_store>0</show_in_store>
83
+ <depends>
84
+ <server_type>production</server_type>
85
+ </depends>
86
+ </status_production>
87
+ <!--status_sandbox translate="label">
88
+ <label>Status</label>
89
+ <frontend_type>label</frontend_type>
90
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_status</frontend_model>
91
+ <backend_model>neklo_monitor/system_config_backend_empty</backend_model>
92
+ <sort_order>20</sort_order>
93
+ <show_in_default>1</show_in_default>
94
+ <show_in_website>0</show_in_website>
95
+ <show_in_store>0</show_in_store>
96
+ <depends>
97
+ <server_type>sandbox</server_type>
98
+ </depends>
99
+ </status_sandbox>
100
+ <plan_production translate="label">
101
+ <label>Plan</label>
102
+ <frontend_type>label</frontend_type>
103
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_label</frontend_model>
104
+ <sort_order>30</sort_order>
105
+ <show_in_default>1</show_in_default>
106
+ <show_in_website>0</show_in_website>
107
+ <show_in_store>0</show_in_store>
108
+ <depends>
109
+ <server_type>production</server_type>
110
+ </depends>
111
+ </plan_production>
112
+ <plan_sandbox translate="label">
113
+ <label>Plan</label>
114
+ <frontend_type>label</frontend_type>
115
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_label</frontend_model>
116
+ <sort_order>30</sort_order>
117
+ <show_in_default>1</show_in_default>
118
+ <show_in_website>0</show_in_website>
119
+ <show_in_store>0</show_in_store>
120
+ <depends>
121
+ <server_type>sandbox</server_type>
122
+ </depends>
123
+ </plan_sandbox>
124
+ <frequency_production translate="label">
125
+ <label>Frequency</label>
126
+ <frontend_type>label</frontend_type>
127
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_label</frontend_model>
128
+ <sort_order>40</sort_order>
129
+ <show_in_default>1</show_in_default>
130
+ <show_in_website>0</show_in_website>
131
+ <show_in_store>0</show_in_store>
132
+ <depends>
133
+ <server_type>production</server_type>
134
+ </depends>
135
+ </frequency_production>
136
+ <frequency_sandbox translate="label">
137
+ <label>Frequency</label>
138
+ <frontend_type>label</frontend_type>
139
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_label</frontend_model>
140
+ <sort_order>40</sort_order>
141
+ <show_in_default>1</show_in_default>
142
+ <show_in_website>0</show_in_website>
143
+ <show_in_store>0</show_in_store>
144
+ <depends>
145
+ <server_type>sandbox</server_type>
146
+ </depends>
147
+ </frequency_sandbox>
148
+ <last_update_production translate="label">
149
+ <label>Last Update</label>
150
+ <frontend_type>label</frontend_type>
151
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_label</frontend_model>
152
+ <sort_order>50</sort_order>
153
+ <show_in_default>1</show_in_default>
154
+ <show_in_website>0</show_in_website>
155
+ <show_in_store>0</show_in_store>
156
+ <depends>
157
+ <server_type>production</server_type>
158
+ </depends>
159
+ </last_update_production>
160
+ <last_update_sandbox translate="label">
161
+ <label>Last Update</label>
162
+ <frontend_type>label</frontend_type>
163
+ <frontend_model>neklo_monitor_adminhtml/system_config_frontend_label</frontend_model>
164
+ <sort_order>50</sort_order>
165
+ <show_in_default>1</show_in_default>
166
+ <show_in_website>0</show_in_website>
167
+ <show_in_store>0</show_in_store>
168
+ <depends>
169
+ <server_type>sandbox</server_type>
170
+ </depends>
171
+ </last_update_sandbox-->
172
+ </fields>
173
+ </gateway>
174
+ </groups>
175
+ </neklo_monitor>
176
+ </sections>
177
+ </config>
app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** @var $installer Mage_Core_Model_Resource_Setup */
4
+ $installer = $this;
5
+
6
+ $installer->startSetup();
7
+ $tbl = $installer->getTable('neklo_monitor/report');
8
+ $installer->run("
9
+
10
+ CREATE TABLE `$tbl` (
11
+ `report_id` INT(11) NOT NULL AUTO_INCREMENT,
12
+ `last_mtime` INT(11) UNSIGNED NOT NULL,
13
+ `qty` INT(11) unsigned NOT NULL,
14
+ `message` TEXT NOT NULL,
15
+ `hash` VARCHAR(32) NOT NULL,
16
+ `files` TEXT NOT NULL,
17
+ PRIMARY KEY (`report_id`),
18
+ KEY `hash` (`hash`),
19
+ KEY `last_mtime` (`last_mtime`)
20
+ ) ENGINE=InnoDB;
21
+
22
+ ");
23
+
24
+ $installer->endSetup();
app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-upgrade-1.0.0-1.1.0.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** @var $installer Mage_Core_Model_Resource_Setup */
4
+ $installer = $this;
5
+
6
+ $installer->startSetup();
7
+ $tbl = $installer->getTable('neklo_monitor/log');
8
+ $installer->run("
9
+
10
+ CREATE TABLE `$tbl` (
11
+ `log_id` INT(11) NOT NULL AUTO_INCREMENT,
12
+ `type` VARCHAR(10) NOT NULL,
13
+ `last_time` INT(11) UNSIGNED NOT NULL,
14
+ `qty` INT(11) unsigned NOT NULL,
15
+ `message` TEXT NOT NULL,
16
+ `hash` VARCHAR(32) NOT NULL,
17
+ `times` TEXT NOT NULL,
18
+ PRIMARY KEY (`log_id`),
19
+ KEY `hash` (`hash`),
20
+ KEY `last_time` (`last_time`),
21
+ KEY `type` (`type`)
22
+ ) ENGINE=InnoDB;
23
+
24
+ ");
25
+
26
+ $installer->endSetup();
app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-upgrade-1.1.0-1.1.1.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** @var $installer Mage_Core_Model_Resource_Setup */
4
+ $installer = $this;
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->run("
9
+
10
+ ALTER TABLE `{$installer->getTable('neklo_monitor/report')}`
11
+ ADD COLUMN `first_mtime` INT(11) UNSIGNED NOT NULL AFTER `report_id`;
12
+
13
+ ALTER TABLE `{$installer->getTable('neklo_monitor/log')}`
14
+ ADD COLUMN `first_time` INT(11) UNSIGNED NOT NULL AFTER `type`;
15
+
16
+ ");
17
+
18
+ $installer->endSetup();
app/code/community/Neklo/Monitor/sql/neklo_monitor_setup/mysql4-upgrade-1.1.1-1.1.2.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** @var $installer Mage_Core_Model_Resource_Setup */
4
+ $installer = $this;
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->run("
9
+
10
+ ALTER TABLE `{$installer->getTable('neklo_monitor/report')}`
11
+ CHANGE COLUMN `first_mtime` `first_time` INT(11) UNSIGNED NOT NULL,
12
+ CHANGE COLUMN `last_mtime` `last_time` INT(11) UNSIGNED NOT NULL;
13
+
14
+ ");
15
+
16
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/neklo/core.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <adminhtml_system_config_edit>
4
+ <reference name="head">
5
+ <action method="addCss">
6
+ <name>neklo/core/css/style.css</name>
7
+ </action>
8
+ </reference>
9
+ </adminhtml_system_config_edit>
10
+ </layout>
app/design/adminhtml/default/default/template/neklo/core/system/contact/button.phtml ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* @var $this Neklo_Core_Block_System_Contact_Send_Button */ ?>
2
+ <?php echo $this->getButtonHtml(); ?>
3
+ <div class="neklo_core_message contact"></div>
4
+ <script>
5
+ var NekloCoreContact = Class.create({
6
+ initialize: function (config) {
7
+ this.initConfig(config);
8
+ this.initElements();
9
+ this.initObservers();
10
+ },
11
+
12
+ initConfig: function (config) {
13
+ this.config = config;
14
+ this.sendUrl = this.config.sendUrl || '';
15
+
16
+ this.successMessage = this.config.successMessage || '';
17
+ this.errorMessage = this.config.errorMessage || '';
18
+
19
+ this.successMessageClass = this.config.successMessageClass || '';
20
+ this.errorMessageClass = this.config.errorMessageClass || '';
21
+
22
+
23
+ this.formContainerId = this.config.formContainerId || '';
24
+ this.formElementSelectorList = this.config.formElementSelectorList || [];
25
+ },
26
+
27
+ initElements: function () {
28
+ this.sendButton = $(this.config.sendButtonId) || null;
29
+ this.loadingMask = $(this.config.loadingMaskId) || null;
30
+ this.messageContainer = $$(this.config.messageContainerSelector).first() || null;
31
+ },
32
+
33
+ initObservers: function () {
34
+ if (this.sendButton) {
35
+ this.sendButton.observe('click', this.send.bind(this));
36
+ }
37
+ },
38
+
39
+ send: function () {
40
+ if (!this.sendUrl) {
41
+ return;
42
+ }
43
+ if (!this.validate()) {
44
+ return;
45
+ }
46
+
47
+ var me = this;
48
+ var sendData = {};
49
+ $H(this.formElementSelectorList).map().each(function(elementSelector) {
50
+ if (Validation.isVisible($(me.formContainerId + '_' + elementSelector.key))) {
51
+ sendData[elementSelector.key] = $(me.formContainerId + '_' + elementSelector.key).getValue();
52
+ }
53
+ });
54
+
55
+ new Ajax.Request(
56
+ this.sendUrl,
57
+ {
58
+ method: 'post',
59
+ parameters: sendData,
60
+ onCreate: this._onSendCreate.bind(this),
61
+ onComplete: this._onSendComplete.bind(this),
62
+ onSuccess: this._onSendSuccess.bind(this),
63
+ onFailure: this._onSendFailure.bind(this)
64
+ }
65
+ );
66
+ },
67
+
68
+ validate: function () {
69
+ var me = this;
70
+ var result = true;
71
+ $H(this.formElementSelectorList).map().each(function(elementSelector) {
72
+ var element = $(me.formContainerId + '_' + elementSelector.key);
73
+ elementSelector.value.each(function(className) {
74
+ element.addClassName(className);
75
+ });
76
+ result = Validation.validate($(me.formContainerId + '_' + elementSelector.key)) && result;
77
+ elementSelector.value.each(function(className) {
78
+ element.removeClassName(className);
79
+ });
80
+ });
81
+ return result;
82
+ },
83
+
84
+ showLoadingMask: function () {
85
+ if (this.loadingMask) {
86
+ this.loadingMask.show();
87
+ }
88
+ },
89
+
90
+ hideLoadingMask: function () {
91
+ if (this.loadingMask) {
92
+ this.loadingMask.hide();
93
+ }
94
+ },
95
+
96
+ _onSendCreate: function () {
97
+ this.clearMessageContainer();
98
+ this.showLoadingMask();
99
+ },
100
+
101
+ _onSendComplete: function () {
102
+ this.hideLoadingMask();
103
+ },
104
+
105
+ _onSendSuccess: function (response) {
106
+ try {
107
+ var result = response.responseText.evalJSON();
108
+ if (typeof(result.success) != 'undefined') {
109
+ if (result.success) {
110
+ this.showSuccess();
111
+ this.clearForm();
112
+ } else {
113
+ this.showError();
114
+ }
115
+ }
116
+ } catch (e) {
117
+ this.showError();
118
+ }
119
+ },
120
+
121
+ _onSendFailure: function () {
122
+ this.showError();
123
+ },
124
+
125
+ showSuccess: function () {
126
+ this.showMessage(this.successMessage, this.successMessageClass);
127
+ },
128
+
129
+ showError: function () {
130
+ this.showMessage(this.errorMessage, this.errorMessageClass);
131
+ },
132
+
133
+ showMessage: function (message, className) {
134
+ this.clearMessageContainer();
135
+ var messageElement = new Element('p', {'class': className}).update(this.prepareMessage(message));
136
+ this.messageContainer.appendChild(messageElement);
137
+ },
138
+
139
+ clearMessageContainer: function () {
140
+ this.messageContainer.update('');
141
+ },
142
+
143
+ prepareMessage: function (message) {
144
+ if ((typeof message) == 'string') {
145
+ return message;
146
+ }
147
+ if (Array.isArray(message)) {
148
+ return message.join("<br/>");
149
+ }
150
+ return '';
151
+ },
152
+
153
+ clearForm: function() {
154
+ var me = this;
155
+ $H(this.formElementSelectorList).map().each(function(elementSelector) {
156
+ $(me.formContainerId + '_' + elementSelector.key).setValue('');
157
+ });
158
+ }
159
+ });
160
+
161
+ var contactForm = new NekloCoreContact({
162
+ 'sendUrl': '<?php echo $this->getUrl('adminhtml/neklo_core_contact/send'); ?>',
163
+
164
+ 'successMessage': [
165
+ '<?php echo $this->jsQuoteEscape($this->__("Thank you for your request.")); ?>',
166
+ '<?php echo $this->jsQuoteEscape($this->__("We'll respond as soon as possible.")); ?>',
167
+ '<?php echo $this->jsQuoteEscape($this->__("We'll send copy of your request to your email.")); ?>'
168
+ ],
169
+ 'errorMessage': [
170
+ '<?php echo $this->jsQuoteEscape($this->__('Oops! Something went wrong!')); ?>'
171
+ ],
172
+
173
+ 'successMessageClass': 'success',
174
+ 'errorMessageClass': 'error',
175
+
176
+ 'formContainerId' : '<?php echo $this->getContainerId(); ?>',
177
+ 'formElementSelectorList': {
178
+ 'name': ['required-entry'],
179
+ 'email': ['required-entry', 'validate-email'],
180
+ 'subject': ['required-entry'],
181
+ 'reason': ['required-entry'],
182
+ 'other_reason': ['required-entry'],
183
+ 'message': ['required-entry']
184
+ },
185
+
186
+ 'sendButtonId': 'neklo_core_contact_send',
187
+ 'loadingMaskId': 'loading-mask',
188
+ 'messageContainerSelector': '.neklo_core_message.contact'
189
+ });
190
+ </script>
app/design/adminhtml/default/default/template/neklo/core/system/contact/header.phtml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php /* @var $this Neklo_Core_Block_System_Contact_Header */ ?>
2
+ <h4><?php echo $this->__('Contact Neklo Support Team or visit <a href="%s" target="_blank">%s</a> for additional information', $this->getStoreUrl(), $this->getStoreLabel()); ?></h4>
app/design/adminhtml/default/default/template/neklo/core/system/extension/list.phtml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* @var $this Neklo_Core_Block_System_Extension_List */ ?>
2
+ <h4><?php echo $this->__('Installed Neklo Extensions') ?></h4>
3
+ <?php $moduleConfigList = $this->getExtensionList(); ?>
4
+ <ul id="neklo_core_extension_list">
5
+ <?php foreach ($moduleConfigList as $moduleCode => $moduleConfig): ?>
6
+ <?php if (!$this->canShowExtension($moduleCode)) continue; ?>
7
+ <li class="neklo-item">
8
+ <?php if ($this->getExtensionUrl($moduleCode)): ?>
9
+ <a class="neklo-link" href="<?php echo $this->getExtensionUrl($moduleCode); ?>" target="_blank">
10
+ <?php endif; ?>
11
+ <div class="ovh">
12
+ <div class="neklo-row neklo-ext-name"><?php echo $this->getExtensionName($moduleCode); ?> <?php echo $this->getExtensionVersion($moduleConfig); ?></div>
13
+ <div class="neklo-img neklo-row"><img src="<?php echo $this->getImageUrl($moduleCode); ?>"/></div>
14
+ <?php if ($this->isExtensionVersionOutdated($moduleCode, $moduleConfig)): ?>
15
+ <?php echo $this->__('New release %s is available', $this->getLastExtensionVersion($moduleCode)); ?> <img src="<?php echo $this->getSkinUrl('neklo/core/images/update.gif'); ?>" alt="">
16
+ <?php else: ?>
17
+ <?php echo $this->__('Your version is up to date'); ?> <img src="<?php echo $this->getSkinUrl('neklo/core/images/ok.gif'); ?>" alt="">
18
+ <?php endif; ?>
19
+ </div>
20
+ <?php if ($this->getExtensionUrl($moduleCode)): ?>
21
+ </a>
22
+ <?php endif; ?>
23
+ </li>
24
+ <?php endforeach; ?>
25
+ </ul>
app/design/adminhtml/default/default/template/neklo/core/system/subscribe/button.phtml ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* @var $this Neklo_Core_Block_System_Newsletter_Subscribe_Button */ ?>
2
+ <?php echo $this->getButtonHtml(); ?>
3
+ <div class="neklo_core_message subscribe"></div>
4
+ <script>
5
+ var NekloCoreSubscribe = Class.create({
6
+ initialize: function (config) {
7
+ this.initConfig(config);
8
+ this.initElements();
9
+ this.initObservers();
10
+ },
11
+
12
+ initConfig: function (config) {
13
+ this.config = config;
14
+ this.subscribeUrl = this.config.subscribeUrl || '';
15
+
16
+ this.successMessage = this.config.successMessage || '';
17
+ this.errorMessage = this.config.errorMessage || '';
18
+
19
+ this.successMessageClass = this.config.successMessageClass || '';
20
+ this.errorMessageClass = this.config.errorMessageClass || '';
21
+
22
+
23
+ this.formContainerId = this.config.formContainerId || '';
24
+ this.formElementSelectorList = this.config.formElementSelectorList || [];
25
+ },
26
+
27
+ initElements: function () {
28
+ this.sendButton = $(this.config.subscribeButtonId) || null;
29
+ this.loadingMask = $(this.config.loadingMaskId) || null;
30
+ this.messageContainer = $$(this.config.messageContainerSelector).first() || null;
31
+ },
32
+
33
+ initObservers: function () {
34
+ if (this.sendButton) {
35
+ this.sendButton.observe('click', this.subscribe.bind(this));
36
+ }
37
+ },
38
+
39
+ subscribe: function () {
40
+ if (!this.subscribeUrl) {
41
+ return;
42
+ }
43
+ if (!this.validate()) {
44
+ return;
45
+ }
46
+
47
+ var me = this;
48
+ var subscribeData = {};
49
+ $H(this.formElementSelectorList).map().each(function(elementSelector) {
50
+ if (Validation.isVisible($(me.formContainerId + '_' + elementSelector.key))) {
51
+ subscribeData[elementSelector.key] = $(me.formContainerId + '_' + elementSelector.key).getValue();
52
+ }
53
+ });
54
+
55
+ new Ajax.Request(
56
+ this.subscribeUrl,
57
+ {
58
+ method: 'post',
59
+ parameters: subscribeData,
60
+ onCreate: this._onSubscribeCreate.bind(this),
61
+ onComplete: this._onSubscribeComplete.bind(this),
62
+ onSuccess: this._onSubscribeSuccess.bind(this),
63
+ onFailure: this._onSubscribeFailure.bind(this)
64
+ }
65
+ );
66
+ },
67
+
68
+ validate: function () {
69
+ var me = this;
70
+ var result = true;
71
+ $H(this.formElementSelectorList).map().each(function(elementSelector) {
72
+ var element = $(me.formContainerId + '_' + elementSelector.key);
73
+ elementSelector.value.each(function(className) {
74
+ element.addClassName(className);
75
+ });
76
+ result = Validation.validate($(me.formContainerId + '_' + elementSelector.key)) && result;
77
+ elementSelector.value.each(function(className) {
78
+ element.removeClassName(className);
79
+ });
80
+ });
81
+ return result;
82
+ },
83
+
84
+ showLoadingMask: function () {
85
+ if (this.loadingMask) {
86
+ this.loadingMask.show();
87
+ }
88
+ },
89
+
90
+ hideLoadingMask: function () {
91
+ if (this.loadingMask) {
92
+ this.loadingMask.hide();
93
+ }
94
+ },
95
+
96
+ _onSubscribeCreate: function () {
97
+ this.clearMessageContainer();
98
+ this.showLoadingMask();
99
+ },
100
+
101
+ _onSubscribeComplete: function () {
102
+ this.hideLoadingMask();
103
+ },
104
+
105
+ _onSubscribeSuccess: function (response) {
106
+ try {
107
+ var result = response.responseText.evalJSON();
108
+ if (typeof(result.success) != 'undefined') {
109
+ if (result.success) {
110
+ this.showSuccess();
111
+ this.clearForm();
112
+ } else {
113
+ this.showError();
114
+ }
115
+ }
116
+ } catch (e) {
117
+ this.showError();
118
+ }
119
+ },
120
+
121
+ _onSubscribeFailure: function () {
122
+ this.showError();
123
+ },
124
+
125
+ showSuccess: function () {
126
+ this.showMessage(this.successMessage, this.successMessageClass);
127
+ },
128
+
129
+ showError: function () {
130
+ this.showMessage(this.errorMessage, this.errorMessageClass);
131
+ },
132
+
133
+ showMessage: function (message, className) {
134
+ this.clearMessageContainer();
135
+ var messageElement = new Element('p', {'class': className}).update(this.prepareMessage(message));
136
+ this.messageContainer.appendChild(messageElement);
137
+ },
138
+
139
+ clearMessageContainer: function () {
140
+ this.messageContainer.update('');
141
+ },
142
+
143
+ prepareMessage: function (message) {
144
+ if ((typeof message) == 'string') {
145
+ return message;
146
+ }
147
+ if (Array.isArray(message)) {
148
+ return message.join("<br/>");
149
+ }
150
+ return '';
151
+ },
152
+
153
+ clearForm: function() {
154
+ var me = this;
155
+ $H(this.formElementSelectorList).map().each(function(elementSelector) {
156
+ $(me.formContainerId + '_' + elementSelector.key).setValue('');
157
+ });
158
+ }
159
+ });
160
+
161
+ var subscribeForm = new NekloCoreSubscribe({
162
+ 'subscribeUrl': '<?php echo $this->getUrl('adminhtml/neklo_core_newsletter/subscribe'); ?>',
163
+
164
+ 'successMessage': '<?php echo $this->jsQuoteEscape($this->__('Successfully subscribed')); ?>',
165
+ 'errorMessage': '<?php echo $this->jsQuoteEscape($this->__('Oops! Something went wrong!')); ?>',
166
+
167
+ 'successMessageClass': 'success',
168
+ 'errorMessageClass': 'error',
169
+
170
+ 'formContainerId' : '<?php echo $this->getContainerId(); ?>',
171
+ 'formElementSelectorList': {
172
+ 'name': ['required-entry'],
173
+ 'email': ['required-entry', 'validate-email']
174
+ },
175
+
176
+ 'subscribeButtonId': 'neklo_core_subscribe',
177
+ 'loadingMaskId': 'loading-mask',
178
+ 'messageContainerSelector': '.neklo_core_message.subscribe'
179
+ });
180
+ </script>
app/etc/modules/Neklo_Core.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Neklo_Core>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ <extension_name>Neklo Core</extension_name>
8
+ </Neklo_Core>
9
+ </modules>
10
+ </config>
app/etc/modules/Neklo_Monitor.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Neklo_Monitor>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Neklo_Monitor>
8
+ </modules>
9
+ </config>
app/locale/en_US/Neklo_Core.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %s Support (%s),%s Support (%s)
2
+ Contact Email,Contact Email
3
+ Contact Form,Contact Form
4
+ Contact Name,Contact Name
5
+ "Contact Neklo Support Team or visit <a href=""%s"" target=""_blank"">%s</a> for additional information","Contact Neklo Support Team or visit <a href=""%s"" target=""_blank"">%s</a> for additional information"
6
+ Extensions &amp; Contact,Extensions &amp; Contact
7
+ Extensions Information,Extensions Information
8
+ Installed Neklo Extensions,Installed Neklo Extensions
9
+ free,free
10
+ Magento Related Support (paid),Magento Related Support (paid)
11
+ Message,Message
12
+ Neklo Extensions &amp; Contact,Neklo Extensions &amp; Contact
13
+ Other Reason,Other Reason
14
+ Reason,Reason
15
+ Request New Extension Development (paid),Request New Extension Development (paid)
16
+ paid,paid
17
+ Please select,Please select
18
+ Send,Send
19
+ Subject,Subject
lib/Linfo/Common.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2014 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo;
20
+
21
+ class Common
22
+ {
23
+ protected static $settings = array(),
24
+ $lang = array();
25
+
26
+ // Used for unit tests
27
+ public static $path_prefix = false;
28
+
29
+ public static function config(Linfo $linfo)
30
+ {
31
+ self::$settings = $linfo->getSettings();
32
+ self::$lang = $linfo->getLang();
33
+ }
34
+
35
+ public static function unconfig()
36
+ {
37
+ self::$settings = array();
38
+ self::$lang = array();
39
+ }
40
+
41
+ // Certain files, specifcally the pci/usb ids files, vary in location from
42
+ // linux distro to linux distro. This function, when passed an array of
43
+ // possible file location, picks the first it finds and returns it. When
44
+ // none are found, it returns false
45
+ public static function locateActualPath($paths)
46
+ {
47
+ foreach ((array) $paths as $path) {
48
+ if (is_file($path)) {
49
+ return $path;
50
+ }
51
+ }
52
+
53
+ return false;
54
+ }
55
+
56
+ // Append a string to the end of each element in a 2d array
57
+ public static function arrayAppendString($array, $string = '', $format = '%1s%2s')
58
+ {
59
+
60
+ // Get to it
61
+ foreach ($array as $k => $v) {
62
+ $array[$k] = is_string($v) ? sprintf($format, $v, $string) : $v;
63
+ }
64
+
65
+ // Give
66
+ return $array;
67
+ }
68
+
69
+ // Get a file who's contents should just be an int. Returns zero on failure.
70
+ public static function getIntFromFile($file)
71
+ {
72
+ return self::getContents($file, 0);
73
+ }
74
+
75
+ // Convert bytes to stuff like KB MB GB TB etc
76
+ public static function byteConvert($size, $precision = 2)
77
+ {
78
+
79
+ // Sanity check
80
+ if (!is_numeric($size)) {
81
+ return '?';
82
+ }
83
+
84
+ // Get the notation
85
+ $notation = self::$settings['byte_notation'] == 1000 ? 1000 : 1024;
86
+
87
+ // Fixes large disk size overflow issue
88
+ // Found at http://www.php.net/manual/en/function.disk-free-space.php#81207
89
+ $types = array('B', 'KB', 'MB', 'GB', 'TB');
90
+ $types_i = array('B', 'KiB', 'MiB', 'GiB', 'TiB');
91
+ for ($i = 0; $size >= $notation && $i < (count($types) - 1); $size /= $notation, $i++);
92
+
93
+ return(round($size, $precision).' '.($notation == 1000 ? $types[$i] : $types_i[$i]));
94
+ }
95
+
96
+ // Like above, but for seconds
97
+ public static function secondsConvert($uptime)
98
+ {
99
+
100
+ // Method here heavily based on freebsd's uptime source
101
+ $uptime += $uptime > 60 ? 30 : 0;
102
+ $years = floor($uptime / 31556926);
103
+ $uptime %= 31556926;
104
+ $days = floor($uptime / 86400);
105
+ $uptime %= 86400;
106
+ $hours = floor($uptime / 3600);
107
+ $uptime %= 3600;
108
+ $minutes = floor($uptime / 60);
109
+ $seconds = floor($uptime % 60);
110
+
111
+ // Send out formatted string
112
+ $return = array();
113
+
114
+ if ($years > 0) {
115
+ $return[] = $years.' '.($years > 1 ? self::$lang['years'] : substr(self::$lang['years'], 0, strlen(self::$lang['years']) - 1));
116
+ }
117
+
118
+ if ($days > 0) {
119
+ $return[] = $days.' '.self::$lang['days'];
120
+ }
121
+
122
+ if ($hours > 0) {
123
+ $return[] = $hours.' '.self::$lang['hours'];
124
+ }
125
+
126
+ if ($minutes > 0) {
127
+ $return[] = $minutes.' '.self::$lang['minutes'];
128
+ }
129
+
130
+ if ($seconds > 0) {
131
+ $return[] = $seconds.(date('m/d') == '06/03' ? ' sex' : ' '.self::$lang['seconds']);
132
+ }
133
+
134
+ return implode(', ', $return);
135
+ }
136
+
137
+ // Get a file's contents, or default to second param
138
+ public static function getContents($file, $default = '')
139
+ {
140
+ if (is_string(self::$path_prefix)) {
141
+ $file = self::$path_prefix.$file;
142
+ }
143
+
144
+ return !is_file($file) || !is_readable($file) || !($contents = @file_get_contents($file)) ? $default : trim($contents);
145
+ }
146
+
147
+ // Like above, but in lines instead of a big string
148
+ public static function getLines($file)
149
+ {
150
+ return !is_file($file) || !is_readable($file) || !($lines = @file($file, FILE_SKIP_EMPTY_LINES)) ? array() : $lines;
151
+ }
152
+
153
+ // Make a string safe for being in an xml tag name
154
+ public static function xmlStringSanitize($string)
155
+ {
156
+ return strtolower(preg_replace('/([^a-zA-Z]+)/', '_', $string));
157
+ }
158
+
159
+ // Get a variable from a file. Include it in this function to avoid
160
+ // clobbering the main namespace
161
+ public static function getVarFromFile($file, $variable)
162
+ {
163
+
164
+ // Let's not waste our time, now
165
+ if (!is_file($file)) {
166
+ return false;
167
+ }
168
+
169
+ require $file;
170
+
171
+ // Double dollar sign means treat variable contents
172
+ // as the name of a variable.
173
+ if (isset($$variable)) {
174
+ return $$variable;
175
+ }
176
+
177
+ return false;
178
+ }
179
+
180
+ // Prevent silly conditionals like if (in_array() || in_array() || in_array())
181
+ // Poor man's python's any() on a list comprehension kinda
182
+ public static function anyInArray($needles, $haystack)
183
+ {
184
+ if (!is_array($needles) || !is_array($haystack)) {
185
+ return false;
186
+ }
187
+
188
+ return count(array_intersect($needles, $haystack)) > 0;
189
+ }
190
+ }
lib/Linfo/Exceptions/FatalException.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2014 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo\Exceptions;
20
+
21
+ use Exception;
22
+
23
+ class FatalException extends Exception
24
+ {
25
+ }
lib/Linfo/Extension/Apcaccess.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This lets you view the command output of the APC program apcaccess.
6
+
7
+ Make sure that you have your UPS connected correctly, the apc package installed, and that
8
+ running apcaccess produces output you find interesting enough for Linfo to display.
9
+
10
+ Installation:
11
+ - The following lines must be added to your config.inc.php:
12
+ $settings['extensions']['apcaccess'] = true;
13
+
14
+ */
15
+
16
+ /*
17
+ * This file is part of Linfo (c) 2011 Joseph Gillotti.
18
+ *
19
+ * Linfo is free software: you can redistribute it and/or modify
20
+ * it under the terms of the GNU General Public License as published by
21
+ * the Free Software Foundation, either version 3 of the License, or
22
+ * (at your option) any later version.
23
+ *
24
+ * Linfo is distributed in the hope that it will be useful,
25
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ * GNU General Public License for more details.
28
+ *
29
+ * You should have received a copy of the GNU General Public License
30
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
31
+ *
32
+ */
33
+
34
+ namespace Linfo\Extension;
35
+
36
+ use Linfo\Linfo;
37
+ use Linfo\Common;
38
+ use Linfo\Meta\Errors;
39
+ use Linfo\Parsers\CallExt;
40
+ use Linfo\Meta\Timer;
41
+ use Exception;
42
+
43
+ /*
44
+ * Get status on apcaccess volumes.
45
+ */
46
+ class Apcaccess implements Extension
47
+ {
48
+ // Store these tucked away here
49
+ private $_CallExt,
50
+ $_res;
51
+
52
+ // Localize important classes
53
+ public function __construct(Linfo $linfo)
54
+ {
55
+ $this->_CallExt = new CallExt();
56
+ $this->_CallExt->setSearchPaths(array('/usr/bin', '/usr/local/bin', '/sbin', '/usr/local/sbin'));
57
+ }
58
+
59
+ // call apcaccess and parse it
60
+ private function _call()
61
+ {
62
+
63
+ // Time this
64
+ $t = new Timer('apcaccess Extension');
65
+
66
+ // Deal with calling it
67
+ try {
68
+ $result = $this->_CallExt->exec('apcaccess');
69
+ } catch (Exception $e) {
70
+ // messed up somehow
71
+ Errors::add('apcaccess Extension', $e->getMessage());
72
+ $this->_res = false;
73
+
74
+ // Don't bother going any further
75
+ return false;
76
+ }
77
+
78
+ // Store them here
79
+ $this->_res = array();
80
+
81
+ // Get name
82
+ if (preg_match('/^UPSNAME\s+:\s+(.+)$/m', $result, $m)) {
83
+ $this->_res['name'] = $m[1];
84
+ }
85
+
86
+ // Get model
87
+ if (preg_match('/^MODEL\s+:\s+(.+)$/m', $result, $m)) {
88
+ $this->_res['model'] = $m[1];
89
+ }
90
+
91
+ // Get battery voltage
92
+ if (preg_match('/^BATTV\s+:\s+(\d+\.\d+)/m', $result, $m)) {
93
+ $this->_res['volts'] = $m[1];
94
+ }
95
+
96
+ // Get charge percentage, and get it cool
97
+ if (preg_match('/^BCHARGE\s+:\s+(\d+(?:\.\d+)?)/m', $result, $m)) {
98
+ $charge = (int) $m[1];
99
+ $this->_res['charge'] = '
100
+ <div class="bar_chart">
101
+ <div class="bar_inner" style="width: '.(int) $charge.'%;">
102
+ <div class="bar_text">
103
+ '.($charge ? $charge.'%' : '?').'
104
+ </div>
105
+ </div>
106
+ </div>
107
+ ';
108
+ }
109
+
110
+ // Get time remaning
111
+ if (preg_match('/^TIMELEFT\s+:\s+([\d\.]+)/m', $result, $m)) {
112
+ $this->_res['time_left'] = Common::secondsConvert($m[1] * 60);
113
+ }
114
+
115
+ // Get status
116
+ if (preg_match('/^STATUS\s+:\s+([A-Z]+)/m', $result, $m)) {
117
+ $this->_res['status'] = $m[1] == 'ONBATT' ? 'On Battery' : ucfirst(strtolower($m[1]));
118
+ }
119
+
120
+ // Load percentage looking cool
121
+ if (preg_match('/^LOADPCT\s+:\s+(\d+\.\d+)/m', $result, $m)) {
122
+ $load = (int) $m[1];
123
+ $this->_res['load'] = '
124
+ <div class="bar_chart">
125
+ <div class="bar_inner" style="width: '.(int) $load.'%;">
126
+ <div class="bar_text">
127
+ '.($load ? $load.'%' : '?').'
128
+ </div>
129
+ </div>
130
+ </div>
131
+ ';
132
+ }
133
+
134
+ // Attempt getting wattage
135
+ if (isset($load) && preg_match('/^NOMPOWER\s+:\s+(\d+)/m', $result, $m)) {
136
+ $watts = (int) $m['1'];
137
+ $this->_res['watts_used'] = $load * round($watts / 100);
138
+ } else {
139
+ $this->_res['watts_used'] = false;
140
+ }
141
+
142
+ // Apparent success
143
+ return true;
144
+ }
145
+
146
+ // Called to get working
147
+ public function work()
148
+ {
149
+ $this->_call();
150
+ }
151
+
152
+ // Get result. Essentially take results and make it usable by the Common::createTable function
153
+ public function result()
154
+ {
155
+
156
+ // Don't bother if it didn't go well
157
+ if ($this->_res === false) {
158
+ return false;
159
+ }
160
+
161
+ // Store rows here
162
+ $rows = array();
163
+
164
+ // Start showing connections
165
+ $rows[] = array(
166
+ 'type' => 'header',
167
+ 'columns' => array(
168
+ 'UPS Name',
169
+ 'Model',
170
+ 'Battery Volts',
171
+ 'Battery Charge',
172
+ 'Time Left',
173
+ 'Current Load',
174
+ $this->_res['watts_used'] ? 'Current Usage' : false,
175
+ 'Status',
176
+ ),
177
+ );
178
+
179
+ // And all the values
180
+ $rows[] = array(
181
+ 'type' => 'values',
182
+ 'columns' => array(
183
+ $this->_res['name'],
184
+ $this->_res['model'],
185
+ $this->_res['volts'],
186
+ $this->_res['charge'],
187
+ $this->_res['time_left'],
188
+ $this->_res['load'],
189
+ $this->_res['watts_used'] ? $this->_res['watts_used'].'W' : false,
190
+ $this->_res['status'],
191
+ ),
192
+ );
193
+
194
+ // Give it off
195
+ return array(
196
+ 'root_title' => 'APC UPS Status',
197
+ 'rows' => $rows,
198
+ );
199
+ }
200
+ }
lib/Linfo/Extension/Cups.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This impliments a CUPS printer queue status parser.
6
+
7
+ Installation:
8
+ - The following lines must be added to your config.inc.php:
9
+ $settings['extensions']['cups'] = true;
10
+
11
+ */
12
+
13
+ /*
14
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
15
+ *
16
+ * Linfo is free software: you can redistribute it and/or modify
17
+ * it under the terms of the GNU General Public License as published by
18
+ * the Free Software Foundation, either version 3 of the License, or
19
+ * (at your option) any later version.
20
+ *
21
+ * Linfo is distributed in the hope that it will be useful,
22
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ * GNU General Public License for more details.
25
+ *
26
+ * You should have received a copy of the GNU General Public License
27
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
28
+ *
29
+ */
30
+
31
+ namespace Linfo\Extension;
32
+
33
+ use Linfo\Linfo;
34
+ use Linfo\Common;
35
+ use Linfo\Meta\Errors;
36
+ use Linfo\Parsers\CallExt;
37
+ use Linfo\Meta\Timer;
38
+ use Exception;
39
+
40
+ /*
41
+ * Get info on a cups install by running lpq
42
+ */
43
+ class Cups implements Extension
44
+ {
45
+ // Store these tucked away here
46
+ private $_CallExt,
47
+ $_res;
48
+
49
+ // Localize important classes
50
+ public function __construct(Linfo $linfo)
51
+ {
52
+ $this->_CallExt = new CallExt();
53
+ $this->_CallExt->setSearchPaths(array('/usr/bin', '/usr/local/bin', '/sbin', '/usr/local/sbin'));
54
+ }
55
+
56
+ // call lpq and parse it
57
+ private function _call()
58
+ {
59
+
60
+ // Time this
61
+ $t = new Timer('CUPS extension');
62
+
63
+ // Deal with calling it
64
+ try {
65
+ $result = $this->_CallExt->exec('lpstat', '-p -o -l');
66
+ } catch (Exception $e) {
67
+ // messed up somehow
68
+ Errors::add('CUPS Extension', $e->getMessage());
69
+ $this->_res = false;
70
+
71
+ // Don't bother going any further
72
+ return false;
73
+ }
74
+
75
+ // Split it into lines
76
+ $lines = explode("\n", $result);
77
+
78
+ // Hold temporarily values here
79
+ $printers = array();
80
+ $queue = array();
81
+ $begin_queue_list = false;
82
+
83
+ // Go through it line by line
84
+ for ($i = 0, $num = count($lines); $i < $num; ++$i) {
85
+
86
+ // So regexes don't break on endlines
87
+ $lines[$i] = trim($lines[$i]);
88
+
89
+ // If there are no entries, don't waste time and end here
90
+ if ($lines[$i] == 'no entries') {
91
+ break;
92
+ } elseif (preg_match('/^printer (.+) is idle\. (.+)$/', $lines[$i], $printers_match) == 1) {
93
+ $printers[] = array(
94
+ 'name' => str_replace('_', ' ', $printers_match[1]),
95
+ 'status' => $printers_match[2],
96
+ );
97
+ }
98
+
99
+ // A printer entry
100
+ elseif (preg_match('/^(.+)+ is (ready|ready and printing|not ready)$/', $lines[$i], $printers_match) == 1) {
101
+ $printers[] = array(
102
+ 'name' => str_replace('_', ' ', $printers_match[1]),
103
+ 'status' => $printers_match[2],
104
+ );
105
+ }
106
+
107
+ // The beginning of the queue list
108
+ elseif (preg_match('/^Rank\s+Owner\s+Job\s+File\(s\)\s+Total Size$/', $lines[$i])) {
109
+ $begin_queue_list = true;
110
+ }
111
+
112
+ // A job in the queue
113
+ elseif ($begin_queue_list && preg_match('/^([a-z0-9]+)\s+(\S+)\s+(\d+)\s+(.+)\s+(\d+) bytes$/', $lines[$i], $queue_match)) {
114
+ $queue[] = array(
115
+ 'rank' => $queue_match[1],
116
+ 'owner' => $queue_match[2],
117
+ 'job' => $queue_match[3],
118
+ 'files' => $queue_match[4],
119
+ 'size' => Common::byteConvert($queue_match[5]),
120
+ );
121
+ }
122
+ }
123
+
124
+ // Save result lset
125
+ $this->_res = array(
126
+ 'printers' => $printers,
127
+ 'queue' => $queue,
128
+ );
129
+
130
+ // Apparent success
131
+ return true;
132
+ }
133
+
134
+ // Called to get working
135
+ public function work()
136
+ {
137
+ $this->_call();
138
+ }
139
+
140
+ // Get result. Essentially take results and make it usable by the Common::createTable function
141
+ public function result()
142
+ {
143
+
144
+ // Don't bother if it didn't go well
145
+ if ($this->_res == false) {
146
+ return false;
147
+ }
148
+
149
+ // it did; continue
150
+ else {
151
+
152
+ // Store rows here
153
+ $rows = array();
154
+
155
+ // start off printers list
156
+ $rows[] = array(
157
+ 'type' => 'header',
158
+ 'columns' => array(
159
+ array(5, 'Printers'),
160
+ ),
161
+ );
162
+ $rows[] = array(
163
+ 'type' => 'header',
164
+ 'columns' => array(
165
+ 'Name',
166
+ array(4, 'Status'),
167
+ ),
168
+ );
169
+
170
+ // show printers if we have them
171
+ if (count($this->_res['printers']) == 0) {
172
+ $rows[] = array('type' => 'none', 'columns' => array(array(5, 'None found')));
173
+ } else {
174
+ foreach ($this->_res['printers'] as $printer) {
175
+ $rows[] = array(
176
+ 'type' => 'values',
177
+ 'columns' => array(
178
+ $printer['name'],
179
+ array(4, $printer['status']),
180
+ ),
181
+ );
182
+ }
183
+ }
184
+
185
+ // show printer queue list
186
+ $rows[] = array(
187
+ 'type' => 'header',
188
+ 'columns' => array(
189
+ array(5, 'Queue'),
190
+ ),
191
+ );
192
+
193
+ $rows[] = array(
194
+ 'type' => 'header',
195
+ 'columns' => array(
196
+ 'Rank',
197
+ 'Owner',
198
+ 'Job',
199
+ 'Files',
200
+ 'Size',
201
+ ),
202
+ );
203
+
204
+ // Go through each item in the lsit
205
+ if (count($this->_res['queue']) == 0) {
206
+ $rows[] = array('type' => 'none', 'columns' => array(array(5, 'Empty')));
207
+ } else {
208
+ foreach ($this->_res['queue'] as $job) {
209
+ $rows[] = array(
210
+ 'type' => 'values',
211
+ 'columns' => array(
212
+ $job['rank'],
213
+ $job['owner'],
214
+ $job['job'],
215
+ $job['files'],
216
+ $job['size'],
217
+ ),
218
+ );
219
+ }
220
+ }
221
+
222
+ // give info
223
+ return array(
224
+ 'root_title' => 'CUPS Printer Status',
225
+ 'rows' => $rows,
226
+ );
227
+ }
228
+ }
229
+ }
lib/Linfo/Extension/Dhcpd3_leases.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This implements a ddhcpd.leases parser for dhcp3 servers.
6
+
7
+ Installation:
8
+ - The following lines must be added to your config.inc.php:
9
+ $settings['extensions']['dhcpd3_leases'] = true;
10
+ $settings['dhcpd3_hide_mac'] = true; // set to false to show mac addresses
11
+
12
+ */
13
+
14
+ /**
15
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
16
+ *
17
+ * Linfo is free software: you can redistribute it and/or modify
18
+ * it under the terms of the GNU General Public License as published by
19
+ * the Free Software Foundation, either version 3 of the License, or
20
+ * (at your option) any later version.
21
+ *
22
+ * Linfo is distributed in the hope that it will be useful,
23
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
+ * GNU General Public License for more details.
26
+ *
27
+ * You should have received a copy of the GNU General Public License
28
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
29
+ */
30
+
31
+ /**
32
+ * Keep out hackers...
33
+ */
34
+ namespace Linfo\Extension;
35
+
36
+ use Linfo\Linfo;
37
+ use Linfo\Common;
38
+ use Linfo\Meta\Errors;
39
+ use Linfo\Meta\Timer;
40
+
41
+ /**
42
+ * Get status on dhcp3 leases.
43
+ */
44
+ class Dhcpd3_leases implements Extension
45
+ {
46
+ // How dates should look
47
+ const
48
+ DATE_FORMAT = 'm/d/y h:i A';
49
+
50
+ // Store these tucked away here
51
+ private
52
+ $_hide_mac,
53
+ $_res,
54
+ $_leases = array();
55
+
56
+ /**
57
+ * localize important stuff.
58
+ * @param Linfo $linfo
59
+ */
60
+ public function __construct(Linfo $linfo)
61
+ {
62
+ $settings = $linfo->getSettings();
63
+
64
+ // Should we hide mac addresses, to prevent stuff like mac address spoofing?
65
+ $this->_hide_mac = array_key_exists('dhcpd3_hide_mac', $settings) ? (bool) $settings['dhcpd3_hide_mac'] : false;
66
+
67
+ // Find leases file
68
+ $this->_leases_file = Common::locateActualPath(array(
69
+ '/var/lib/dhcp/dhcpd.leases', // modern-er debian
70
+ '/var/lib/dhcp3/dhcpd.leases', // debian/ubuntu/others probably
71
+ '/var/lib/dhcpd/dhcpd.leases', // Possibly redhatish distros and others
72
+ '/var/state/dhcp/dhcpd.leases', // Arch linux, maybe others
73
+ '/var/db/dhcpd/dhcpd.leases', // FreeBSD
74
+ '/var/db/dhcpd.leases', // OpenBSD/NetBSD/Darwin(lol)/DragonFLY afaik
75
+ ));
76
+ }
77
+
78
+ /**
79
+ * Deal with it.
80
+ */
81
+ private function _call()
82
+ {
83
+ // Time this
84
+ $t = new Timer('dhcpd3 leases extension');
85
+
86
+ // We couldn't find leases file?
87
+ if ($this->_leases_file === false) {
88
+ Errors::add('dhcpd3 leases extension', 'couldn\'t find leases file');
89
+ $this->_res = false;
90
+
91
+ return;
92
+ }
93
+
94
+ // Get contents
95
+ $contents = Common::getContents($this->_leases_file, false);
96
+
97
+ // Couldn't?
98
+ if ($contents === false) {
99
+ Errors::add('dhcpd3 leases extension', 'Error getting contents of leases file');
100
+ $this->_res = false;
101
+
102
+ return;
103
+ }
104
+
105
+ // All dates in the file are in UTC format. Attempt finding out local time zone to convert UTC to local.
106
+ // This prevents confusing the hell out of people.
107
+ $do_date_conversion = false;
108
+ $local_timezone = false;
109
+
110
+ // Make sure we have what we need. Stuff this requires doesn't exist on certain php installations
111
+ if (function_exists('date_default_timezone_get') && class_exists('DateTime') && class_exists('DateTimeZone')) {
112
+ // I only want this called once, hence value stored here. It also might fail
113
+ $local_timezone = @date_default_timezone_get();
114
+
115
+ // Make sure it didn't fail
116
+ if ($local_timezone !== false && is_string($local_timezone)) {
117
+ $do_date_conversion = true;
118
+ } // Say we'll allow conversion later on
119
+ }
120
+
121
+ // Get it into lines
122
+ $lines = explode("\n", $contents);
123
+
124
+ // Store temp entries here
125
+ $curr = false;
126
+
127
+ // Parse each line, while ignoring certain useless'ish values
128
+ // I'd do a single preg_match_all() using multiline regex, but the values in each lease block are inconsistent. :-/
129
+ for ($i = 0, $num_lines = count($lines); $i < $num_lines; ++$i) {
130
+
131
+ // Kill padding whitespace
132
+ $lines[$i] = trim($lines[$i]);
133
+
134
+ // Last line in entry
135
+ if ($lines[$i] == '}') {
136
+ // Have we a current entry to save?
137
+ if (is_array($curr)) {
138
+ $this->_leases[] = $curr;
139
+ }
140
+
141
+ // Make it empty for next time
142
+ $curr = false;
143
+ }
144
+
145
+ // First line in entry. Save IP
146
+ elseif (preg_match('/^lease (\d+\.\d+\.\d+\.\d+) \{$/', $lines[$i], $m)) {
147
+ $curr = array('ip' => $m[1]);
148
+ }
149
+
150
+ // Line with lease start
151
+ elseif ($curr && preg_match('/^starts \d+ (\d+\/\d+\/\d+ \d+:\d+:\d+);$/', $lines[$i], $m)) {
152
+
153
+ // Get it in unix time stamp for prettier formatting later and easier tz offset conversion
154
+ $curr['lease_start'] = strtotime($m[1]);
155
+
156
+ // Handle offset conversion
157
+ if ($do_date_conversion) {
158
+
159
+ // This handy class helps out with timezone offsets. Pass it original date, not unix timestamp
160
+ $d = new DateTime($m[1], new DateTimeZone($local_timezone));
161
+ $offset = $d->getOffset();
162
+
163
+ // If ofset looks good, deal with it
164
+ if (is_numeric($offset) && $offset != 0) {
165
+ $curr['lease_start'] += $offset;
166
+ }
167
+ }
168
+ }
169
+
170
+ // Line with lease end
171
+ elseif ($curr && preg_match('/^ends \d+ (\d+\/\d+\/\d+ \d+:\d+:\d+);$/', $lines[$i], $m)) {
172
+
173
+ // Get it in unix time stamp for prettier formatting later and easier tz offset conversion
174
+ $curr['lease_end'] = strtotime($m[1]);
175
+
176
+ // Handle offset conversion
177
+ if ($do_date_conversion) {
178
+
179
+ // This handy class helps out with timezone offsets. Pass it original date, not unix timestamp
180
+ $d = new DateTime($m[1], new DateTimeZone($local_timezone));
181
+ $offset = $d->getOffset();
182
+
183
+ // If ofset looks good, deal with it
184
+ if (is_numeric($offset) && $offset != 0) {
185
+ $curr['lease_end'] += $offset;
186
+ }
187
+ }
188
+
189
+ // Is this old?
190
+ // The file seems to contain all leases since the dhcpd server was started for the first time
191
+ if (time() > $curr['lease_end']) {
192
+
193
+ // Kill current entry and ignore any following parts of this lease
194
+ $curr = false;
195
+
196
+ // Jump out right now
197
+ continue;
198
+ }
199
+ }
200
+
201
+ // Line with MAC address
202
+ elseif (!$this->_hide_mac && $curr && preg_match('/^hardware ethernet (\w+:\w+:\w+:\w+:\w+:\w+);$/', $lines[$i], $m)) {
203
+ $curr['mac'] = $m[1];
204
+ }
205
+
206
+ // [optional] Line with hostname
207
+ elseif ($curr && preg_match('/^client\-hostname "([^"]+)";$/', $lines[$i], $m)) {
208
+ $curr['hostname'] = $m[1];
209
+ }
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Do the job.
215
+ */
216
+ public function work()
217
+ {
218
+ $this->_call();
219
+ }
220
+
221
+ /**
222
+ * Return result.
223
+ *
224
+ * @return false on failure|array of the leases
225
+ */
226
+ public function result()
227
+ {
228
+ // Don't bother if it didn't go well
229
+ if ($this->_res === false) {
230
+ return false;
231
+ }
232
+
233
+ // Store rows here
234
+ $rows = array();
235
+
236
+ // Start showing connections
237
+ $rows[] = array(
238
+ 'type' => 'header',
239
+ 'columns' =>
240
+
241
+ // Not hiding mac address?
242
+ !$this->_hide_mac ? array(
243
+ 'IP Address',
244
+ 'MAC Address',
245
+ 'Hostname',
246
+ 'Lease Start',
247
+ 'Lease End',
248
+ ) :
249
+
250
+ // Hiding it indeed
251
+ array(
252
+ 'IP Address',
253
+ 'Hostname',
254
+ 'Lease Start',
255
+ 'Lease End',
256
+ ),
257
+ );
258
+
259
+ // Append each lease
260
+ for ($i = 0, $num_leases = count($this->_leases); $i < $num_leases; ++$i) {
261
+ $rows[] = array(
262
+ 'type' => 'values',
263
+ 'columns' =>
264
+
265
+ // Not hiding mac addresses?
266
+ !$this->_hide_mac ? array(
267
+ $this->_leases[$i]['ip'],
268
+ $this->_leases[$i]['mac'],
269
+ array_key_exists('hostname', $this->_leases[$i]) ?
270
+ $this->_leases[$i]['hostname'] : '<em>unknown</em>',
271
+ date(self::DATE_FORMAT, $this->_leases[$i]['lease_start']),
272
+ date(self::DATE_FORMAT, $this->_leases[$i]['lease_end']),
273
+ ) :
274
+
275
+ // Hiding them indeed
276
+ array(
277
+ $this->_leases[$i]['ip'],
278
+ array_key_exists('hostname', $this->_leases[$i]) ?
279
+ $this->_leases[$i]['hostname'] : '<em>unknown</em>',
280
+ date(self::DATE_FORMAT, $this->_leases[$i]['lease_start']),
281
+ date(self::DATE_FORMAT, $this->_leases[$i]['lease_end']),
282
+ ),
283
+ );
284
+ }
285
+
286
+ // Give it off
287
+ return array(
288
+ 'root_title' => 'DHCPD IP Leases',
289
+ 'rows' => $rows,
290
+ );
291
+ }
292
+ }
lib/Linfo/Extension/Dnsmasq_dhcpd.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This parses dnsmasq's dhcpd leases file. Commonly used to show dynamic IP's given to
6
+ libvirt's virtual machines. This does not require libvirt-php to be installed.
7
+
8
+ Installation:
9
+ - The following lines must be added to your config.inc.php:
10
+ $settings['extensions']['Dnsmasq_dhcpd'] = true;
11
+ $settings['dnsmasq_hide_mac'] = true; // set to false to show mac addresses
12
+ $settings['dnsmasq_leases'] = 'path'; // change path to the leases file. defaults to /var/lib/libvirt/dnsmasq/default.leases
13
+
14
+ */
15
+
16
+ /**
17
+ * This file is part of Linfo (c) 2015 Joseph Gillotti.
18
+ *
19
+ * Linfo is free software: you can redistribute it and/or modify
20
+ * it under the terms of the GNU General Public License as published by
21
+ * the Free Software Foundation, either version 3 of the License, or
22
+ * (at your option) any later version.
23
+ *
24
+ * Linfo is distributed in the hope that it will be useful,
25
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ * GNU General Public License for more details.
28
+ *
29
+ * You should have received a copy of the GNU General Public License
30
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
31
+ */
32
+
33
+ /**
34
+ * Keep out hackers...
35
+ */
36
+ namespace Linfo\Extension;
37
+
38
+ use Linfo\Linfo;
39
+ use Linfo\Common;
40
+ use Linfo\Meta\Errors;
41
+ use Linfo\Meta\Timer;
42
+
43
+ /**
44
+ * Get status on dhcp3 leases.
45
+ */
46
+ class Dnsmasq_dhcpd implements Extension
47
+ {
48
+ // How dates should look
49
+ const
50
+ DATE_FORMAT = 'm/d/y h:i A';
51
+
52
+ // Store these tucked away here
53
+ private
54
+ $_hide_mac,
55
+ $_leases = array();
56
+
57
+ /**
58
+ * localize important stuff.
59
+ * @param Linfo $linfo
60
+ */
61
+ public function __construct(Linfo $linfo)
62
+ {
63
+ $settings = $linfo->getSettings();
64
+
65
+ // Should we hide mac addresses, to prevent stuff like mac address spoofing?
66
+ $this->_hide_mac = array_key_exists('dnsmasq_hide_mac', $settings) ? (bool) $settings['dnsmasq_hide_mac'] : false;
67
+
68
+ // Find leases file
69
+ $this->_leases_file = isset($settings['dnsmasq_leases']) && is_file($settings['dnsmasq_leases']) ?
70
+ $settings['dnsmasq_leases'] : Common::locateActualPath(array(
71
+ '/var/lib/libvirt/dnsmasq/default.leases',
72
+ ));
73
+ }
74
+
75
+ /**
76
+ * Do the job.
77
+ */
78
+ public function work()
79
+ {
80
+ $t = new Timer('dnsmasq leases extension');
81
+
82
+ foreach (Common::getLines($this->_leases_file) as $line) {
83
+ if (!preg_match('/^(\d+) ([a-z0-9:]+) (\S+) (\S+)/', $line, $m))
84
+ continue;
85
+ $this->_leases[] = array_combine(array('lease_end', 'mac', 'ip', 'hostname'), array_slice($m, 1));
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Return result.
91
+ *
92
+ * @return array of the leases
93
+ */
94
+ public function result()
95
+ {
96
+ // Store rows here
97
+ $rows = array();
98
+
99
+ // Start showing connections
100
+ $rows[] = array(
101
+ 'type' => 'header',
102
+ 'columns' =>
103
+
104
+ // Not hiding mac address?
105
+ !$this->_hide_mac ? array(
106
+ 'IP Address',
107
+ 'MAC Address',
108
+ 'Hostname',
109
+ 'Lease End',
110
+ ) :
111
+
112
+ // Hiding it indeed
113
+ array(
114
+ 'IP Address',
115
+ 'Hostname',
116
+ 'Lease End',
117
+ ),
118
+ );
119
+
120
+ // Append each lease
121
+ foreach ($this->_leases as $lease) {
122
+ $rows[] = array(
123
+ 'type' => 'values',
124
+ 'columns' =>
125
+
126
+ // Not hiding mac addresses?
127
+ !$this->_hide_mac ? array(
128
+ $lease['ip'],
129
+ $lease['mac'],
130
+ array_key_exists('hostname', $lease) ?
131
+ $lease['hostname'] : '<em>unknown</em>',
132
+ date(self::DATE_FORMAT, $lease['lease_end']),
133
+ ) :
134
+
135
+ // Hiding them indeed
136
+ array(
137
+ $lease['ip'],
138
+ array_key_exists('hostname', $lease) ?
139
+ $lease['hostname'] : '<em>unknown</em>',
140
+ date(self::DATE_FORMAT, $lease['lease_end']),
141
+ ),
142
+ );
143
+ }
144
+
145
+ // Give it off
146
+ return array(
147
+ 'root_title' => 'DnsMasq DHCPD IP Leases',
148
+ 'rows' => $rows,
149
+ );
150
+ }
151
+ }
lib/Linfo/Extension/Extension.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Extension;
22
+
23
+ use Linfo\Linfo;
24
+
25
+ /*
26
+ * Extensions must conform to this
27
+ */
28
+ interface Extension
29
+ {
30
+ public function __construct(Linfo $linfo); // Have them localize useful things
31
+ public function work(); // Do the job
32
+ public function result(); // Return the result
33
+ }
lib/Linfo/Extension/Ipmi.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This implements a ipmi status checker for temps/voltages
6
+
7
+ Installation:
8
+ - The following lines must be added to your config.inc.php:
9
+ $settings['extensions']['ipmi'] = true;
10
+
11
+ - The ipmitool command most likely needs to be run as root, so,
12
+ if you don't have php running as root, configure sudo appropriately
13
+ for the user the php scripts are running as, comment out 'Defaults requiretty' in your sudoers
14
+ file, and add 'ipmitool' to the $settings['sudo_apps'] array in config.inc.php
15
+
16
+ */
17
+
18
+ /*
19
+ * This file is part of Linfo (c) 2011 Joseph Gillotti.
20
+ *
21
+ * Linfo is free software: you can redistribute it and/or modify
22
+ * it under the terms of the GNU General Public License as published by
23
+ * the Free Software Foundation, either version 3 of the License, or
24
+ * (at your option) any later version.
25
+ *
26
+ * Linfo is distributed in the hope that it will be useful,
27
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29
+ * GNU General Public License for more details.
30
+ *
31
+ * You should have received a copy of the GNU General Public License
32
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
33
+ *
34
+ */
35
+
36
+ namespace Linfo\Extension;
37
+
38
+ use Linfo\Linfo;
39
+ use Linfo\Meta\Errors;
40
+ use Linfo\Meta\Timer;
41
+ use Linfo\Parsers\CallExt;
42
+ use Exception;
43
+
44
+ /**
45
+ * IPMI extension for temps/voltages.
46
+ *
47
+ * @author Joseph Gillotti
48
+ */
49
+ class Ipmi implements Extension
50
+ {
51
+ // Minimum version of Linfo required
52
+ const
53
+ LINFO_INTEGRATE = true,
54
+ EXTENSION_NAME = 'ipmi';
55
+
56
+ // Store these tucked away here
57
+ private $_CallExt,
58
+ $linfo;
59
+
60
+ // Start us off
61
+ public function __construct(Linfo $linfo)
62
+ {
63
+ $this->linfo = $linfo;
64
+ $this->_CallExt = new CallExt();
65
+ $this->_CallExt->setSearchPaths(array('/usr/bin', '/usr/local/bin', '/sbin', '/usr/local/sbin'));
66
+ }
67
+
68
+ // Work it, baby
69
+ public function work()
70
+ {
71
+ $info = &$this->linfo->getInfo();
72
+
73
+ // Make sure this is an array
74
+ $info['Temps'] = (array) $info['Temps'];
75
+
76
+ // Time this
77
+ $t = new Timer(self::EXTENSION_NAME.' Extension');
78
+
79
+ // Deal with calling it
80
+ try {
81
+ $result = $this->_CallExt->exec('ipmitool', ' sdr');
82
+ } catch (Exception $e) {
83
+ // messed up somehow
84
+ Errors::add(self::EXTENSION_NAME.' Extension', $e->getMessage());
85
+
86
+ return;
87
+ }
88
+
89
+ // Match it up
90
+ if (!preg_match_all('/^([^|]+)\| ([\d\.]+ (?:Volts|degrees [CF]))\s+\| ok$/m', $result, $matches, PREG_SET_ORDER)) {
91
+ return;
92
+ }
93
+
94
+ // Go through with it
95
+ foreach ($matches as $m) {
96
+
97
+ // Separate them by normal spaces
98
+ $v_parts = explode(' ', trim($m[2]));
99
+
100
+ // Deal with the type of it
101
+ switch ($v_parts[1]) {
102
+ case 'Volts':
103
+ $unit = 'v';
104
+ break;
105
+ case 'degrees':
106
+ $unit = $v_parts[2];
107
+ break;
108
+ default:
109
+ $unit = '';
110
+ break;
111
+ }
112
+
113
+ // Save this one
114
+ $info['Temps'][] = array(
115
+ 'path' => 'N/A',
116
+ 'name' => trim($m[1]),
117
+ 'temp' => $v_parts[0],
118
+ 'unit' => $unit,
119
+ );
120
+ }
121
+ }
122
+
123
+ // Not needed
124
+ public function result()
125
+ {
126
+ return false;
127
+ }
128
+ }
lib/Linfo/Extension/Libvirt.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This shows a cursory list of running VMs managed by libvirt and their stats.
6
+ Requires libvirt php extension (http://libvirt.org/php/):
7
+ sudo apt-get install php5-libvirt-php
8
+
9
+ To enable this extension, add/tweak the following to your config.inc.php
10
+
11
+ $settings['extensions']['libvirt'] = true;
12
+ $settings['libvirt_connection'] = array(
13
+ 'url' => 'qemu:///system', // For xen do 'xen:///' instead
14
+ 'credentials' => NULL
15
+ );
16
+
17
+
18
+ */
19
+
20
+ /**
21
+ * This file is part of Linfo (c) 2013 Joseph Gillotti.
22
+ *
23
+ * Linfo is free software: you can redistribute it and/or modify
24
+ * it under the terms of the GNU General Public License as published by
25
+ * the Free Software Foundation, either version 3 of the License, or
26
+ * (at your option) any later version.
27
+ *
28
+ * Linfo is distributed in the hope that it will be useful,
29
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
+ * GNU General Public License for more details.
32
+ *
33
+ * You should have received a copy of the GNU General Public License
34
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
35
+ */
36
+
37
+ /**
38
+ * Keep out hackers...
39
+ */
40
+ namespace Linfo\Extension;
41
+
42
+ use Linfo\Linfo;
43
+ use Linfo\Common;
44
+ use Linfo\Meta\Errors;
45
+ use Linfo\Meta\Timer;
46
+
47
+ /**
48
+ * Get status on libvirt VMs.
49
+ */
50
+ class Libvirt implements Extension
51
+ {
52
+ private
53
+ $VMs = array(),
54
+ $connection = false,
55
+ $connectionSettings = array(),
56
+ $res = false;
57
+
58
+ public function __construct(Linfo $linfo)
59
+ {
60
+ $settings = $linfo->getSettings();
61
+
62
+ $this->connectionSettings = $settings['libvirt_connection'];
63
+ }
64
+
65
+ private function connect()
66
+ {
67
+ if (!($this->connection =
68
+ @libvirt_connect($this->connectionSettings['url'], true))) {
69
+ Errors::add('libvirt extension', 'Error connecting');
70
+ $this->res = false;
71
+
72
+ return false;
73
+ }
74
+
75
+ return true;
76
+ }
77
+
78
+ public function work()
79
+ {
80
+ $t = new Timer('libvirt extension');
81
+
82
+ if (!extension_loaded('libvirt')) {
83
+ Errors::add('libvirt extension', 'Libvirt PHP extension not installed');
84
+ $this->res = false;
85
+
86
+ return;
87
+ }
88
+
89
+ if (!$this->connect()) {
90
+ Errors::add('libvirt extension', 'Failed connecting');
91
+ return;
92
+ }
93
+
94
+ if (!($doms = libvirt_list_domains($this->connection))) {
95
+ Errors::add('libvirt extension', 'Failed getting domain list');
96
+ $this->res = false;
97
+
98
+ return;
99
+ }
100
+
101
+ foreach ($doms as $name) {
102
+ if (!($domain = libvirt_domain_lookup_by_name($this->connection, $name))) {
103
+ continue;
104
+ }
105
+
106
+ if (!($info = libvirt_domain_get_info($domain)) || !is_array($info)) {
107
+ continue;
108
+ }
109
+
110
+ $info['autostart'] = libvirt_domain_get_autostart($domain);
111
+
112
+ if ($info['autostart'] == 1) {
113
+ $info['autostart'] = 'Yes';
114
+ } elseif ($info['autostart'] == 0) {
115
+ $info['autostart'] = 'No';
116
+ } else {
117
+ $info['autostart'] = 'N/A';
118
+ }
119
+
120
+ $info['nets'] = array();
121
+
122
+ $nets = @libvirt_domain_get_interface_devices($domain);
123
+
124
+ foreach ($nets as $key => $net) {
125
+ if (!is_numeric($key)) {
126
+ continue;
127
+ }
128
+ $info['nets'][] = $net;
129
+ }
130
+
131
+ $info['storage'] = array();
132
+
133
+ foreach ((array) @libvirt_domain_get_disk_devices($domain) as $blockName) {
134
+ if (!is_string($blockName)) {
135
+ continue;
136
+ }
137
+
138
+ // Sometime device exists but libvirt fails to get more docs. just settle for device name
139
+ if (!($blockInfo = @libvirt_domain_get_block_info($domain, $blockName)) || !is_array($blockInfo)) {
140
+ $info['storage'][] = array(
141
+ 'device' => $blockName,
142
+ );
143
+ continue;
144
+ }
145
+
146
+ if (isset($blockInfo['partition']) && !isset($blockInfo['file'])) {
147
+ $blockInfo['file'] = $blockInfo['partition'];
148
+ }
149
+
150
+ $info['storage'][] = $blockInfo;
151
+ }
152
+
153
+ $this->VMs[$name] = $info;
154
+ }
155
+
156
+ $this->res = true;
157
+ }
158
+
159
+ public function result()
160
+ {
161
+ if (!$this->res) {
162
+ return false;
163
+ }
164
+
165
+ $rows[] = array(
166
+ 'type' => 'header',
167
+ 'columns' => array(
168
+ 'VM Name',
169
+ 'Status',
170
+ 'RAM Allocation',
171
+ 'CPUs',
172
+ 'CPU Time',
173
+ 'Autostart',
174
+ 'Block Storage',
175
+ 'Network Devices',
176
+ ),
177
+ );
178
+
179
+ $running = 0;
180
+ $allram = 0;
181
+
182
+ foreach ($this->VMs as $name => $info) {
183
+ $disks = array();
184
+
185
+ foreach ($info['storage'] as $disk) {
186
+ $disks[] = $disk['device']
187
+ .(isset($disk['file']) && isset($disk['capacity']) ? ': '.$disk['file'].' ('.Common::byteConvert($disk['capacity'], 2).')' : '');
188
+ }
189
+
190
+ $rows[] = array(
191
+ 'type' => 'values',
192
+ 'columns' => array(
193
+ $name,
194
+ $info['state'] == 1 ? '<span style="color: green;">On</span>' : '<span style="color: maroon;">Off</span>',
195
+ Common::byteConvert($info['memory'] * 1024, 2),
196
+ $info['nrVirtCpu'],
197
+ $info['cpuUsed'] ? $info['cpuUsed'] : 'N/A',
198
+ $info['autostart'],
199
+ $disks ? implode('<br />', $disks) : 'None',
200
+ $info['nets'] ? implode('<br />', $info['nets']) : 'None',
201
+ ),
202
+ );
203
+
204
+ if ($info['state'] == 1)
205
+ $running++;
206
+
207
+ $allram += $info['memory'];
208
+ }
209
+
210
+ // Give it off
211
+ return array(
212
+ 'root_title' => 'libvirt Virtual Machines <span style="font-size: 80%;">('.$running.' running - using '.Common::byteConvert($allram * 1024, 2).' RAM)</span>',
213
+ 'rows' => $rows,
214
+ );
215
+ }
216
+ }
lib/Linfo/Extension/Smb.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This impliments a current samba usage status
6
+
7
+ Installation:
8
+ - The following lines must be added to your config.inc.php:
9
+ $settings['extensions']['smb'] = true;
10
+
11
+
12
+ */
13
+
14
+ /*
15
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
16
+ *
17
+ * Linfo is free software: you can redistribute it and/or modify
18
+ * it under the terms of the GNU General Public License as published by
19
+ * the Free Software Foundation, either version 3 of the License, or
20
+ * (at your option) any later version.
21
+ *
22
+ * Linfo is distributed in the hope that it will be useful,
23
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
+ * GNU General Public License for more details.
26
+ *
27
+ * You should have received a copy of the GNU General Public License
28
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
29
+ *
30
+ */
31
+
32
+ namespace Linfo\Extension;
33
+
34
+ use Linfo\Linfo;
35
+ use Linfo\Meta\Errors;
36
+ use Linfo\Meta\Timer;
37
+ use Linfo\Parsers\CallExt;
38
+ use Exception;
39
+
40
+ /*
41
+ * Get info on a samba install by running smbstatus
42
+ */
43
+ class Smb implements Extension
44
+ {
45
+ // Store these tucked away here
46
+ private $_CallExt,
47
+ $_res,
48
+ $_date_format = 'm/d/y @ h:i A';
49
+
50
+ // Localize important classes
51
+ public function __construct(Linfo $linfo)
52
+ {
53
+ $this->_CallExt = new CallExt();
54
+ $this->_CallExt->setSearchPaths(array('/usr/bin', '/usr/local/bin', '/sbin', '/usr/local/sbin'));
55
+ }
56
+
57
+ // call samba and parse it
58
+ private function _call()
59
+ {
60
+
61
+ // Time this
62
+ $t = new Timer('Samba Status extension');
63
+
64
+ // Deal with calling it
65
+ try {
66
+ $result = $this->_CallExt->exec('smbstatus');
67
+ } catch (Exception $e) {
68
+ // messed up somehow
69
+ Errors::add('Samba Status Extension', $e->getMessage());
70
+ $this->_res = false;
71
+
72
+ // Don't bother going any further
73
+ return false;
74
+ }
75
+
76
+ // Split it into lines
77
+ $lines = explode("\n", $result);
78
+
79
+ // Store temp stuff here
80
+ $connections = array();
81
+ $services = array();
82
+ $files = array();
83
+ $current_location = false;
84
+
85
+ // Parse
86
+ for ($i = 0, $num = count($lines); $i < $num; ++$i) {
87
+
88
+ // Deal with pointlessness appropriately
89
+ $lines[$i] = trim($lines[$i]);
90
+
91
+ // Is this pointless?
92
+ if ($lines[$i] == '' || preg_match('/^\-+$/', $lines[$i])) {
93
+ continue;
94
+ }
95
+
96
+ // Beginning connections list?
97
+ elseif (preg_match('/^PID\s+Username\s+Group\s+Machine$/', $lines[$i])) {
98
+ $current_location = 'c';
99
+ }
100
+
101
+ // A connection?
102
+ elseif ($current_location == 'c' && preg_match('/^(\d+)\s+(\w+)\s+(\w+)\s+(\S+)\s+\(([^)]+)\)$/', $lines[$i], $connection_match)) {
103
+ $connections[] = array(
104
+ 'pid' => $connection_match[1],
105
+ 'username' => $connection_match[2],
106
+ 'group' => $connection_match[3],
107
+ 'hostname' => $connection_match[4],
108
+ 'ip' => $connection_match[5],
109
+ );
110
+ }
111
+
112
+ // Beginning services list?
113
+ elseif (preg_match('/^Service\s+pid\s+machine\s+Connected at$/', $lines[$i])) {
114
+ $current_location = 's';
115
+ }
116
+
117
+ // A service?
118
+ elseif ($current_location == 's' && preg_match('/^(\w+)\s+(\d+)\s+(\S+)\s+([a-zA-z]+ [a-zA-Z]+ \d+ \d+:\d+:\d+ \d+)$/', $lines[$i], $service_match)) {
119
+ $services[] = array(
120
+ 'service' => $service_match[1],
121
+ 'pid' => $service_match[2],
122
+ 'machine' => $service_match[3],
123
+ 'date' => strtotime($service_match[4]),
124
+ );
125
+ }
126
+
127
+ // Beginning locked files list?
128
+ elseif (preg_match('/^Pid\s+Uid\s+DenyMode\s+Access\s+R\/W\s+Oplock\s+SharePath\s+Name\s+Time$/', $lines[$i])) {
129
+ $current_location = 'f';
130
+ }
131
+
132
+ // A locked file?
133
+ elseif ($current_location == 'f' && preg_match('/^(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+([A-Z]+)\s+([A-Z+]+)\s+(\S+)\s+(.+)\s+([a-zA-Z]+ [a-zA-Z]+ \d+ \d+:\d+:\d+ \d+)$/', $lines[$i], $file_match)) {
134
+ $files[] = array(
135
+ 'pid' => $file_match[1],
136
+ 'uid' => $file_match[2],
137
+ 'deny_mode' => $file_match[3],
138
+ 'access' => $file_match[4],
139
+ 'rw' => $file_match[5],
140
+ 'oplock' => $file_match[6],
141
+ 'share' => $file_match[7],
142
+ 'filename' => $file_match[8],
143
+ 'date' => strtotime($file_match[9]),
144
+ );
145
+ }
146
+ }
147
+
148
+ // Give result
149
+ $this->_res = array(
150
+ 'connections' => $connections,
151
+ 'services' => $services,
152
+ 'files' => $files,
153
+ );
154
+
155
+ // Success
156
+ return true;
157
+ }
158
+
159
+ public function work()
160
+ {
161
+ $this->_call();
162
+ }
163
+ public function result()
164
+ {
165
+ // Don't bother if it didn't go well
166
+ if ($this->_res === false) {
167
+ return false;
168
+ }
169
+ // it did; continue
170
+ else {
171
+
172
+ // Store rows here
173
+ $rows = array();
174
+
175
+ // Start showing connections
176
+ $rows[] = array(
177
+ 'type' => 'header',
178
+ 'columns' => array(
179
+ array(5, 'Connections'),
180
+ ),
181
+ );
182
+ $rows[] = array(
183
+ 'type' => 'header',
184
+ 'columns' => array(
185
+ 'Username',
186
+ 'Group',
187
+ array(3,'Machine'),
188
+ ),
189
+ );
190
+
191
+ // Show them
192
+ if (count($this->_res['connections']) > 0) {
193
+ foreach ($this->_res['connections'] as $conn) {
194
+ $rows[] = array(
195
+ 'type' => 'values',
196
+ 'columns' => array(
197
+ $conn['username'],
198
+ $conn['group'],
199
+ array(3,$conn['hostname'].($conn['hostname'] != $conn['ip'] ? ' <span class="perc">('.$conn['ip'].')</span>' : '')),
200
+ ),
201
+ );
202
+ }
203
+ } else {
204
+ $rows[] = array(
205
+ 'type' => 'none',
206
+ 'columns' => array(
207
+ array(5, 'None found'),
208
+ ),
209
+ );
210
+ }
211
+
212
+ // Now services
213
+ $rows[] = array(
214
+ 'type' => 'header',
215
+ 'columns' => array(
216
+ array(5, 'Services'),
217
+ ),
218
+ );
219
+ $rows[] = array(
220
+ 'type' => 'header',
221
+ 'columns' => array(
222
+ 'Service',
223
+ 'Machine',
224
+ array(3,'Date'),
225
+ ),
226
+ );
227
+
228
+ // Show them
229
+ if (count($this->_res['services']) > 0) {
230
+ // Show them
231
+ foreach ($this->_res['services'] as $service) {
232
+ $rows[] = array(
233
+ 'type' => 'values',
234
+ 'columns' => array(
235
+ $service['service'],
236
+ $service['machine'],
237
+ array(3, date($this->_date_format, $service['date'])),
238
+ ),
239
+ );
240
+ }
241
+ } else {
242
+ $rows[] = array(
243
+ 'type' => 'none',
244
+ 'columns' => array(
245
+ array(5, 'None found'),
246
+ ),
247
+ );
248
+ }
249
+
250
+ // Files time
251
+ $rows[] = array(
252
+ 'type' => 'header',
253
+ 'columns' => array(
254
+ array(5, 'Locked files'),
255
+ ),
256
+ );
257
+ $rows[] = array(
258
+ 'type' => 'header',
259
+ 'columns' => array(
260
+ 'UID',
261
+ 'Mode',
262
+ 'Share',
263
+ 'Filename',
264
+ 'Date',
265
+ ),
266
+ );
267
+
268
+ // Show them
269
+ if (count($this->_res['files']) > 0) {
270
+ foreach ($this->_res['files'] as $f) {
271
+
272
+ // See if we can turn the uid into a username
273
+ $username = false;
274
+ if (function_exists('posix_getpwuid')) {
275
+ if ($user_info = @posix_getpwuid($f['uid'])) {
276
+ $username = $user_info['name'];
277
+ }
278
+ }
279
+
280
+ // Try making better sense of the R/W column
281
+ switch ($f['rw']) {
282
+ case 'RDONLY':
283
+ $rw = 'Read Only';
284
+ break;
285
+ case 'RDWR':
286
+ $rw = 'Read/Write';
287
+ break;
288
+ case 'WRONLY':
289
+ $rw = 'Write Only';
290
+ break;
291
+ default:
292
+ $rw = false;
293
+ break;
294
+ }
295
+
296
+ // Save entry
297
+ $rows[] = array(
298
+ 'type' => 'values',
299
+ 'columns' => array(
300
+ $f['uid'].($username != false ? ' ('.$username.')' : ''),
301
+ $rw ? $rw : $f['rw'],
302
+ $f['share'],
303
+ $f['filename'],
304
+ date($this->_date_format, $f['date']),
305
+ ), );
306
+ }
307
+ } else {
308
+ $rows[] = array(
309
+ 'type' => 'none',
310
+ 'columns' => array(
311
+ array(5, 'None found'),
312
+ ),
313
+ );
314
+ }
315
+
316
+ // Give it off
317
+ return array(
318
+ 'root_title' => 'Samba Status',
319
+ 'rows' => $rows,
320
+ );
321
+ }
322
+ }
323
+ }
lib/Linfo/Extension/Soldat.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This impliments a soldat (soldat.pl) dedicated server gamestat.txt parser
6
+
7
+ Installation:
8
+ - Copy/move the class.ext.soldat.php into the lib/ folder
9
+ - The following lines must be added to your config.inc.php:
10
+ $settings['extensions']['soldat'] = true;
11
+
12
+ // paths to the gamestat.txt files
13
+ $settings['soldat_servers'] = array(
14
+ //'CTF #1' => '/home/soldat/ctf/logs/gamestat.txt' # example usage
15
+ );
16
+
17
+ */
18
+
19
+ /*
20
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
21
+ *
22
+ * Linfo is free software: you can redistribute it and/or modify
23
+ * it under the terms of the GNU General Public License as published by
24
+ * the Free Software Foundation, either version 3 of the License, or
25
+ * (at your option) any later version.
26
+ *
27
+ * Linfo is distributed in the hope that it will be useful,
28
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
29
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30
+ * GNU General Public License for more details.
31
+ *
32
+ * You should have received a copy of the GNU General Public License
33
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
34
+ *
35
+ */
36
+
37
+ namespace Linfo\Extension;
38
+
39
+ use Linfo\Linfo;
40
+ use Linfo\Common;
41
+ use Linfo\Meta\Errors;
42
+
43
+ /*
44
+ * Get status on soldat game servers
45
+ */
46
+ class Soldat implements Extension
47
+ {
48
+ // Store these tucked away here
49
+ private
50
+ $_res,
51
+ $_servers;
52
+
53
+ // Localize important classes
54
+ public function __construct(Linfo $linfo)
55
+ {
56
+ $settings = $linfo->getSettings();
57
+ $this->_servers = (array) $settings['soldat_servers'];
58
+ }
59
+
60
+ // work it
61
+ private function _call()
62
+ {
63
+ $this->_res = array();
64
+ foreach ($this->_servers as $name => $path) {
65
+ $lines = Common::getLines($path);
66
+ if (count($lines) == 0) {
67
+ continue;
68
+ }
69
+ $info = self::readgamestat($lines);
70
+ $this->_res[] = array('name' => $name, 'info' => $info);
71
+ }
72
+ }
73
+
74
+ // Called to get working
75
+ public function work()
76
+ {
77
+ $this->_call();
78
+ }
79
+
80
+ // Get result. Essentially take results and make it usable by the Common::createTable function
81
+ public function result()
82
+ {
83
+
84
+ // Don't bother if it didn't go well
85
+ if ($this->_res == false) {
86
+ return false;
87
+ }
88
+
89
+ // Store rows here
90
+ $rows = array();
91
+
92
+ // Table header
93
+ $rows[] = array(
94
+ 'type' => 'header',
95
+ 'columns' => array(
96
+ 'Name',
97
+ 'Map',
98
+ 'Time Left',
99
+ 'Gametype',
100
+ 'Players',
101
+ ),
102
+ );
103
+
104
+ // Have we?
105
+ if (count($this->_res) == 0) {
106
+ $rows[] = array('type' => 'none', 'columns' => array(array(5, 'None found')));
107
+ }
108
+
109
+ // We do
110
+ else {
111
+
112
+ // Go through each server
113
+ foreach ($this->_res as $server) {
114
+
115
+ // No players? Set player column to 'none'
116
+ if ($server['info']['num_players'] == 0) {
117
+ $players = 'None';
118
+ }
119
+
120
+ // We do; populate a mini players table
121
+ else {
122
+
123
+ // Show team column?
124
+ $show_team = in_array($server['info']['mode'], array(
125
+ 'Infiltration',
126
+ 'Capture the Flag',
127
+ 'Teammatch',
128
+ ));
129
+
130
+ // Start table
131
+ $players = '
132
+ <table class="mini" style="text-align: center;">
133
+ <tr>
134
+ <th>Name</th>'.(
135
+ $show_team ? '
136
+ <th>Team</th>' : '').'
137
+ <th>Score</th>
138
+ <th>Deaths</th>
139
+ <th>Ping</th>
140
+ </tr>';
141
+
142
+ // Add each player to it
143
+ foreach ($server['info']['players'] as $player) {
144
+ $players .= '
145
+ <tr'.($show_team ? (' style="color: '.(
146
+ array_key_exists($player['team'], self::$team2color) ?
147
+ self::$team2color[$player['team']] : 'purple'
148
+ ).';"') : '').'>
149
+ <td>'.htmlspecialchars($player['name']).'</td>'.(
150
+ $show_team ? '
151
+ <td>'.(array_key_exists($player['team'], self::$team2name) ?
152
+ self::$team2name[$player['team']] : 'None').'</td>' : '').'
153
+ <td>'.$player['kills'].'</td>
154
+ <td>'.$player['deaths'].'</td>
155
+ <td>'.$player['ping'].'</td>
156
+ </tr>';
157
+ }
158
+
159
+ // End table
160
+ $players .= '
161
+ </table>
162
+ ';
163
+ }
164
+
165
+ // Save result in master table
166
+ $rows[] = array(
167
+ 'type' => 'values',
168
+ 'columns' => array(
169
+ $server['name'],
170
+ $server['info']['map'],
171
+ $server['info']['timeleft'],
172
+ $server['info']['mode'],
173
+ $players,
174
+ ),
175
+ );
176
+ }
177
+ }
178
+
179
+ // Give info
180
+ return array(
181
+ 'root_title' => 'Soldat Servers',
182
+ 'rows' => $rows,
183
+ );
184
+ }
185
+
186
+ // Deal with team color
187
+ public static $team2color = array(
188
+ 0 => '#333',
189
+ 1 => 'red',
190
+ 2 => 'blue',
191
+ 3 => '#006600',
192
+ 4 => '#FFD700',
193
+ );
194
+
195
+ // Deal with team name
196
+ public static $team2name = array(
197
+ 0 => 'None',
198
+ 1 => 'Alpha',
199
+ 2 => 'Bravo',
200
+ 3 => 'Charlie',
201
+ 4 => 'Delta',
202
+ );
203
+
204
+ /*
205
+ gamestat.txt parser
206
+ Copyright (C) 2007 JRG Productions
207
+ http://soldat.jrgp.org/programs/gstp/gstp.phps
208
+ */
209
+ public static function readgamestat($i)
210
+ {
211
+ //this array contains the info that will be returned
212
+ $info = array(
213
+ 'num_players' => trim(str_replace('Players: ', '', $i[1])),
214
+ 'map' => trim(str_replace('Map: ', '', $i[2])),
215
+ 'mode' => trim(str_replace('Gamemode: ', '', $i[3])),
216
+ 'timeleft' => trim(str_replace('Timeleft: ', '', $i[4])),
217
+ );
218
+ //support for the teambased gamemodes
219
+ if ($info['mode'] == 'Capture the Flag' || $info['mode'] == 'Infiltration' || $info['mode'] == 'Teammatch') {
220
+ $info['teams']['alpha'] = trim(str_replace('Team 1: ', '', $i[5]));
221
+ $info['teams']['bravo'] = trim(str_replace('Team 2: ', '', $i[6]));
222
+ $info['teams']['charlie'] = trim(str_replace('Team 3: ', '', $i[7]));
223
+ $info['teams']['delta'] = trim(str_replace('Team 4: ', '', $i[8]));
224
+ $players_line = 10;
225
+ } else {
226
+ $players_line = 6;
227
+ }
228
+ $pla = '';
229
+ //get the player info a string
230
+ for ($l = $players_line; $line = $i[$l], $l < count($i); ++$l) {
231
+ $pla .= $line;
232
+ }
233
+ //explode then chunk that string
234
+ $players_info = array_chunk(explode("\n", $pla), 5);
235
+ //kill the last element since its empty
236
+ array_pop($players_info);
237
+ //add each player to the new array
238
+ foreach ($players_info as $p) {
239
+ $info['players'][] = array(
240
+ 'name' => $p[0],
241
+ 'kills' => $p[1],
242
+ 'deaths' => $p[2],
243
+ 'team' => $p[3],
244
+ 'ping' => $p[4],
245
+ );
246
+ }
247
+ //return the info
248
+ return $info;
249
+ }
250
+ }
lib/Linfo/Extension/Transmission.php ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This implements a transmission-remote parsing extension which displays status of running torrents
6
+
7
+ Installation:
8
+ - The following lines must be added to your config.inc.php:
9
+ $settings['extensions']['transmission'] = true;
10
+ $settings['transmission_auth'] = array(
11
+ //'user' => 'jim', # Both of these must exist if you wish to use auth
12
+ //'pass' => 'pwnz!'
13
+ );
14
+ $settings['transmission_host'] = array(
15
+ // 'server' => 'localhost', # uncomment to set a specific host
16
+ // 'port' => 9091 # uncomment to set a specific port
17
+ );
18
+
19
+
20
+ // If you want download/upload/ratio/duration stats, make sure the web server user can
21
+ // read this folder, which is in the home directory of hteu ser that transmission is
22
+ // running as
23
+ $settings['transmission_folder'] = '/home/user/.config/transmission/';
24
+
25
+ */
26
+
27
+ /**
28
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
29
+ *
30
+ * Linfo is free software: you can redistribute it and/or modify
31
+ * it under the terms of the GNU General Public License as published by
32
+ * the Free Software Foundation, either version 3 of the License, or
33
+ * (at your option) any later version.
34
+ *
35
+ * Linfo is distributed in the hope that it will be useful,
36
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
37
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38
+ * GNU General Public License for more details.
39
+ *
40
+ * You should have received a copy of the GNU General Public License
41
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
42
+ */
43
+
44
+ /**
45
+ * Keep out hackers...
46
+ */
47
+ namespace Linfo\Extension;
48
+
49
+ use Linfo\Linfo;
50
+ use Linfo\Common;
51
+ use Linfo\Meta\Errors;
52
+ use Linfo\Meta\Timer;
53
+ use Linfo\Parsers\CallExt;
54
+ use Exception;
55
+
56
+ /**
57
+ * Get status on transmission torrents.
58
+ */
59
+ class Transmission implements Extension
60
+ {
61
+ // Store these tucked away here
62
+ private $_CallExt,
63
+ $_res,
64
+ $_torrents = array(),
65
+ $_stats = false,
66
+ $_auth,
67
+ $_host;
68
+
69
+ /**
70
+ * localize important stuff.
71
+ * @param Linfo $linfo
72
+ */
73
+ public function __construct(Linfo $linfo)
74
+ {
75
+ $settings = $linfo->getSettings();
76
+
77
+ // Classes we need
78
+ $this->_CallExt = new CallExt();
79
+ $this->_CallExt->setSearchPaths(array('/usr/bin', '/usr/local/bin'));
80
+
81
+ // Transmission specific settings
82
+ $this->_auth = array_key_exists('transmission_auth', $settings) ? (array) $settings['transmission_auth'] : array();
83
+ $this->_host = array_key_exists('transmission_host', $settings) ? (array) $settings['transmission_host'] : array();
84
+
85
+ // Path to home dir folder
86
+ $this->_folder = array_key_exists('transmission_folder', $settings) && is_dir($settings['transmission_folder']) && is_readable($settings['transmission_folder']) ? $settings['transmission_folder'] : false;
87
+ }
88
+
89
+ /**
90
+ * Deal with it.
91
+ */
92
+ private function _call()
93
+ {
94
+ // Time this
95
+ $t = new Timer('Transmission extension');
96
+
97
+ // Deal with stats, if possible
98
+ if ($this->_folder && ($stats_contents = Common::getContents($this->_folder.'stats.json', false)) && $stats_contents != false) {
99
+ $stats_vals = @json_decode($stats_contents, true);
100
+ if (is_array($stats_vals)) {
101
+ $this->_stats = $stats_vals;
102
+ }
103
+ }
104
+
105
+ // Deal with calling it
106
+ try {
107
+ // Start up args
108
+ $args = '';
109
+
110
+ // Specifc host/port?
111
+ if (array_key_exists('server', $this->_host) && array_key_exists('port', $this->_host) && is_numeric($this->_host['port'])) {
112
+ $args .= ' \''.$this->_host['server'].'\':'.$this->_host['port'];
113
+ }
114
+
115
+ // We need some auth?
116
+ if (array_key_exists('user', $this->_auth) && array_key_exists('pass', $this->_auth)) {
117
+ $args .= ' --auth=\''.$this->_auth['user'].'\':\''.$this->_auth['pass'].'\'';
118
+ }
119
+
120
+ // Rest of it, including result
121
+ $result = $this->_CallExt->exec('transmission-remote', $args.' -l');
122
+ } catch (Exception $e) {
123
+ // messed up somehow
124
+ Errors::add('Transmission extension: ', $e->getMessage());
125
+ $this->_res = false;
126
+
127
+ // Don't bother going any further
128
+ return;
129
+ }
130
+
131
+ $this->_res = true;
132
+
133
+ // Get first line
134
+ $first_line = reset(explode("\n", $result, 1));
135
+
136
+ // Invalid host?
137
+ if (strpos($first_line, 'Couldn\'t resolve host name') !== false) {
138
+ Errors::add('Transmission extension: Invalid Host');
139
+ $this->_res = false;
140
+
141
+ return;
142
+ }
143
+
144
+ // Invalid auth?
145
+ if (strpos($first_line, '401: Unauthorized') !== false) {
146
+ Errors::add('Transmission extension: Invalid Authentication');
147
+ $this->_res = false;
148
+
149
+ return;
150
+ }
151
+
152
+ // Match teh torrents!
153
+ if (preg_match_all('/^\s+(\d+)\*?\s+(\d+)\%\s+(\d+\.\d+ \w+|None)\s+((?:\d+ )?\w+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+|None)\s+(Up & Down|Seeding|Idle|Stopped)\s+(.+)$/m', $result, $matches, PREG_SET_ORDER) > 0) {
154
+
155
+ // Use this to sort them
156
+ $sort_done = array();
157
+ $sort_ratio = array();
158
+ $sort_name = array();
159
+
160
+ // Save the matches
161
+ for ($i = 0, $num = count($matches); $i < $num; ++$i) {
162
+
163
+ // Save this one
164
+ $this->_torrents[$i] = array(
165
+ 'id' => $matches[$i][1],
166
+ 'done' => $matches[$i][2],
167
+ 'have' => $matches[$i][3],
168
+ 'eta' => $matches[$i][4],
169
+ 'up' => $matches[$i][5] * 1024, // always in KIB
170
+ 'down' => $matches[$i][6] * 1024, // ^
171
+ 'ratio' => $matches[$i][7],
172
+ 'state' => $matches[$i][8],
173
+ 'torrent' => $matches[$i][9],
174
+ );
175
+
176
+ // Use this for sorting
177
+ $sort_done[$i] = (int) $matches[$i][2];
178
+ $sort_ratio[$i] = (float) $matches[$i][7];
179
+ $sort_name[$i] = $matches[$i][9];
180
+ }
181
+
182
+ // Sort
183
+ array_multisort($sort_done, SORT_DESC, $sort_ratio, SORT_DESC, $sort_name, SORT_ASC, $this->_torrents);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Do the job.
189
+ */
190
+ public function work()
191
+ {
192
+ $this->_call();
193
+ }
194
+
195
+ /**
196
+ * Return result.
197
+ *
198
+ * @return false on failure|array of the torrents
199
+ */
200
+ public function result()
201
+ {
202
+ // Don't bother if it didn't go well
203
+ if ($this->_res === false) {
204
+ return false;
205
+ }
206
+ // it did; continue
207
+
208
+ // Store rows here
209
+ $rows = array();
210
+
211
+ // Start showing connections
212
+ $rows[] = array(
213
+ 'type' => 'header',
214
+ 'columns' => array(
215
+ 'Torrent',
216
+ array(1, 'Done', '10%'),
217
+ 'State',
218
+ 'Have',
219
+ 'Uploaded',
220
+ 'Time Left',
221
+ 'Ratio',
222
+ 'Up',
223
+ 'Down',
224
+ ),
225
+ );
226
+
227
+ // No torrents?
228
+ if (count($this->_torrents) == 0) {
229
+ $rows[] = array(
230
+ 'type' => 'none',
231
+ 'columns' => array(
232
+ array(9, 'None found'),
233
+ ),
234
+ );
235
+ } else {
236
+
237
+ // Store a total amount of certain torrents here:
238
+ $status_tally = array();
239
+
240
+ // As well as uploaded/downloaded
241
+ $status_tally['Downloaded'] = 0;
242
+ $status_tally['Uploaded'] = 0;
243
+ $status_tally['Ratio'] = '';
244
+
245
+ // Go through each torrent
246
+ foreach ($this->_torrents as $torrent) {
247
+
248
+ // Status count tally
249
+ $status_tally[$torrent['state']] = !array_key_exists($torrent['state'], $status_tally) ? 1 : $status_tally[$torrent['state']] + 1;
250
+
251
+ // Make some sense of the have so we can get it into bytes, which we can then have fun with
252
+ $have_bytes = false;
253
+ if ($torrent['have'] != 'None') {
254
+ $have_parts = explode(' ', $torrent['have'], 2);
255
+ if (is_numeric($have_parts[0]) && $have_parts[0] > 0) {
256
+ switch ($have_parts[1]) {
257
+ case 'TiB':
258
+ $have_bytes = (float) $have_parts[0] * 1099511627776;
259
+ break;
260
+ case 'GiB':
261
+ $have_bytes = (float) $have_parts[0] * 1073741824;
262
+ break;
263
+ case 'MiB':
264
+ $have_bytes = (float) $have_parts[0] * 1048576;
265
+ break;
266
+ case 'KiB':
267
+ $have_bytes = (float) $have_parts[0] * 1024;
268
+ break;
269
+ }
270
+ }
271
+ }
272
+
273
+ // Try getting amount uploaded, based upon ratio and exact amount downloaded above
274
+ $uploaded_bytes = false;
275
+ if (is_numeric($have_bytes) && $have_bytes > 0 && is_numeric($torrent['ratio']) && $torrent['ratio'] > 0) {
276
+ $uploaded_bytes = $torrent['ratio'] * $have_bytes;
277
+ }
278
+
279
+ // Save amount uploaded/downloaded tally
280
+ if (is_numeric($have_bytes) && $have_bytes > 0 && is_numeric($uploaded_bytes) && $uploaded_bytes > 0) {
281
+ $status_tally['Downloaded'] += $have_bytes;
282
+ $status_tally['Uploaded'] += $uploaded_bytes;
283
+ }
284
+
285
+ // Save result
286
+ $rows[] = array(
287
+ 'type' => 'values',
288
+ 'columns' => array(
289
+ wordwrap(htmlspecialchars($torrent['torrent']), 50, ' ', true),
290
+ '<div class="bar_chart">
291
+ <div class="bar_inner" style="width: '.(int) $torrent['done'].'%;">
292
+ <div class="bar_text">
293
+ '.($torrent['done'] ? $torrent['done'].'%' : '0%').'
294
+ </div>
295
+ </div>
296
+ </div>
297
+ ',
298
+ $torrent['state'],
299
+ $have_bytes !== false ? Common::byteConvert($have_bytes) : $torrent['have'],
300
+ $uploaded_bytes !== false ? Common::byteConvert($uploaded_bytes) : 'None',
301
+ $torrent['eta'],
302
+ $torrent['ratio'],
303
+ Common::byteConvert($torrent['up']).'/s',
304
+ Common::byteConvert($torrent['down']).'/s',
305
+ ),
306
+ );
307
+ }
308
+
309
+ // Finish the size totals
310
+ $status_tally['Ratio'] = $status_tally['Downloaded'] > 0 && $status_tally['Uploaded'] > 0 ? round($status_tally['Uploaded'] / $status_tally['Downloaded'], 2) : 'N/A';
311
+ $status_tally['Downloaded'] = $status_tally['Downloaded'] > 0 ? Common::byteConvert($status_tally['Downloaded']) : 'None';
312
+ $status_tally['Uploaded'] = $status_tally['Uploaded'] > 0 ? Common::byteConvert($status_tally['Uploaded']) : 'None';
313
+
314
+ // Create a row for the tally of statuses
315
+ if (count($status_tally) > 0) {
316
+
317
+ // Store list of k: v'ish values here
318
+ $tally_contents = array();
319
+
320
+ // Populate that
321
+ foreach ($status_tally as $state => $tally) {
322
+ $tally_contents[] = "$state: $tally";
323
+ }
324
+
325
+ // Save this final row
326
+ $rows[] = array(
327
+ 'type' => 'values',
328
+ 'columns' => array(
329
+ array(9, implode(', ', $tally_contents)),
330
+ ),
331
+ );
332
+ }
333
+ }
334
+
335
+ // Handle stats which might not exist
336
+ if (
337
+ is_array($this->_stats) &&
338
+ array_key_exists('downloaded-bytes', $this->_stats) &&
339
+ array_key_exists('uploaded-bytes', $this->_stats) &&
340
+ array_key_exists('seconds-active', $this->_stats
341
+ )) {
342
+ $extra_vals = array(
343
+ 'title' => 'Transmission Stats',
344
+ 'values' => array(
345
+ array('Total Downloaded', Common::byteConvert($this->_stats['downloaded-bytes'])),
346
+ array('Total Uploaded', Common::byteConvert($this->_stats['uploaded-bytes'])),
347
+ $this->_stats['uploaded-bytes'] > 0 && $this->_stats['downloaded-bytes'] > 0 ? array('Total Ratio', round($this->_stats['uploaded-bytes'] / $this->_stats['downloaded-bytes'], 3)) : false,
348
+ array('Duration', Common::secondsConvert($this->_stats['seconds-active'])),
349
+ ),
350
+ );
351
+ } else {
352
+ $extra_vals = false;
353
+ }
354
+
355
+ // Give it off
356
+ return array(
357
+ 'root_title' => 'Transmission Torrents',
358
+ 'rows' => $rows,
359
+ 'extra_type' => 'k->v',
360
+ 'extra_vals' => $extra_vals,
361
+ );
362
+ }
363
+ }
lib/Linfo/Extension/Truecrypt.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This implements a truecrypt mounted volume status shower
6
+
7
+ Installation:
8
+ - The following lines must be added to your config.inc.php:
9
+ $settings['extensions']['truecrypt'] = true;
10
+
11
+ */
12
+
13
+ /*
14
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
15
+ *
16
+ * Linfo is free software: you can redistribute it and/or modify
17
+ * it under the terms of the GNU General Public License as published by
18
+ * the Free Software Foundation, either version 3 of the License, or
19
+ * (at your option) any later version.
20
+ *
21
+ * Linfo is distributed in the hope that it will be useful,
22
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ * GNU General Public License for more details.
25
+ *
26
+ * You should have received a copy of the GNU General Public License
27
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
28
+ *
29
+ */
30
+
31
+ namespace Linfo\Extension;
32
+
33
+ use Linfo\Linfo;
34
+ use Linfo\Parsers\CallExt;
35
+ use Linfo\Meta\Errors;
36
+ use Linfo\Meta\Timer;
37
+ use Exception;
38
+
39
+ /*
40
+ * Get status on truecrypt volumes. very experimental
41
+ */
42
+ class Truecrypt implements Extension
43
+ {
44
+ // Store these tucked away here
45
+ private $_CallExt,
46
+ $_res;
47
+
48
+ // Localize important classes
49
+ public function __construct(Linfo $linfo)
50
+ {
51
+ $this->_CallExt = new CallExt();
52
+ $this->_CallExt->setSearchPaths(array('/usr/bin', '/usr/local/bin', '/sbin', '/usr/local/sbin'));
53
+ }
54
+
55
+ // call truecrypt and parse it
56
+ private function _call()
57
+ {
58
+
59
+ // Time this
60
+ $t = new Timer('Truecrypt Extension');
61
+
62
+ // Deal with calling it
63
+ try {
64
+ $result = $this->_CallExt->exec('truecrypt', '-l -v');
65
+ } catch (Exception $e) {
66
+ // messed up somehow
67
+ Errors::add('Truecrypt Extension', $e->getMessage());
68
+ $this->_res = false;
69
+
70
+ // Don't bother going any further
71
+ return false;
72
+ }
73
+
74
+ // Store them here
75
+ $this->_res = array();
76
+
77
+ // Current one
78
+ $curr = false;
79
+
80
+ // Lines of output
81
+ $lines = explode("\n", $result);
82
+
83
+ // Go through each line
84
+ for ($i = 0, $num = count($lines); $i < $num; ++$i) {
85
+
86
+ // Extract juicy info
87
+ if (!preg_match('/^([^:]+): ([^$]+)$/', $lines[$i], $line_match)) {
88
+ continue;
89
+ }
90
+
91
+ // Decide what to do with that
92
+ switch ($line_match[1]) {
93
+
94
+ // It starts here
95
+ case 'Slot':
96
+ if ($curr === false) {
97
+ $curr = array('slot' => $line_match[2]);
98
+ } elseif (is_array($curr)) {
99
+ $this->_res[] = $curr;
100
+ $curr = false;
101
+ }
102
+ break;
103
+
104
+ // Volume.
105
+ case 'Volume':
106
+ if (is_array($curr)) {
107
+ $curr['volume'] = $line_match[2];
108
+ }
109
+ break;
110
+
111
+ // Virtual device
112
+ case 'Virtual Device':
113
+ if (is_array($curr)) {
114
+ $curr['virtual_device'] = $line_match[2];
115
+ }
116
+ break;
117
+
118
+ // Where it might be mounted
119
+ case 'Mount Directory':
120
+ if (is_array($curr)) {
121
+ $curr['mount_directory'] = $line_match[2];
122
+ }
123
+ break;
124
+
125
+ // Size of it
126
+ case 'Size':
127
+ if (is_array($curr)) {
128
+ $curr['size'] = $line_match[2];
129
+ }
130
+ break;
131
+
132
+ // Is it read only?
133
+ case 'Read-Only':
134
+ if (is_array($curr)) {
135
+ $curr['read_only'] = $line_match[2];
136
+ }
137
+ break;
138
+
139
+ // We deliberately ignore most keys for security reasons
140
+ default:
141
+ continue;
142
+ break;
143
+ }
144
+ }
145
+
146
+ // Save a remaining one
147
+ if (is_array($curr) && count($curr) > 0) {
148
+ $this->_res[] = $curr;
149
+ }
150
+
151
+ // Apparent success
152
+ return true;
153
+ }
154
+
155
+ // Called to get working
156
+ public function work()
157
+ {
158
+ $this->_call();
159
+ }
160
+
161
+ // Get result. Essentially take results and make it usable by the Common::createTable function
162
+ public function result()
163
+ {
164
+
165
+ // Don't bother if it didn't go well
166
+ if ($this->_res == false) {
167
+ return false;
168
+ }
169
+
170
+ // it did; continue
171
+ else {
172
+
173
+ // Store rows here
174
+ $rows = array();
175
+
176
+ // start off volume list
177
+ $rows[] = array(
178
+ 'type' => 'header',
179
+ 'columns' => array(
180
+ 'Slot',
181
+ 'Volume',
182
+ 'Virtual Device',
183
+ 'Mount Point',
184
+ 'Size',
185
+ 'Read Only',
186
+ ),
187
+ );
188
+
189
+ // show volumes if we have them
190
+ if (count($this->_res) == 0) {
191
+ $rows[] = array('type' => 'none', 'columns' => array(array(6, 'None found')));
192
+ } else {
193
+ foreach ((array) $this->_res as $vol) {
194
+ $rows[] = array(
195
+ 'type' => 'values',
196
+ 'columns' => array(
197
+ $vol['slot'],
198
+ $vol['volume'],
199
+ $vol['virtual_device'],
200
+ $vol['mount_directory'],
201
+ $vol['size'],
202
+ $vol['read_only'],
203
+ ),
204
+ );
205
+ }
206
+ }
207
+
208
+ // Give info
209
+ return array(
210
+ 'root_title' => 'Truecrypt Volumes',
211
+ 'rows' => $rows,
212
+ );
213
+ }
214
+ }
215
+ }
lib/Linfo/Extension/Utorrent.php ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+
5
+ This connects to the web ui provided by utorrent headless for Linux. It works
6
+ by forging the HTTP requests, essentially pretending it's a web browser.
7
+
8
+ Requires libcurl extension. (apt-get install php5-curl)
9
+
10
+ To enable this extension, add/tweak the following to your config.inc.php
11
+
12
+ $settings['extensions']['utorrent'] = true;
13
+ $settings['utorrent_connection'] = array(
14
+ 'host' => 'localhost',
15
+ 'port' => 8080,
16
+ 'user' => 'admin',
17
+ 'pass' => ''
18
+ );
19
+
20
+ Optionally, you can add multiple regexes to filter torrents. Use something
21
+ like the following to strip out torrents with XXX in their name:
22
+
23
+ $settings['utorrent_filter'] = array(
24
+ '/XXX/i'
25
+ );
26
+
27
+ Set the following to not show torrent names and just show the hashes
28
+
29
+ $settings['utorrent_hide_name'] = true;
30
+
31
+ */
32
+
33
+ /*
34
+
35
+ Known to work with this verson of uTorrent:
36
+ Product Version 3.3
37
+ Source Revision 30235
38
+ Build Date 2013-10-14 10:42:53 -0700
39
+ UI Revision 30235
40
+
41
+ */
42
+
43
+ /**
44
+ * This file is part of Linfo (c) 2014 Joseph Gillotti.
45
+ *
46
+ * Linfo is free software: you can redistribute it and/or modify
47
+ * it under the terms of the GNU General Public License as published by
48
+ * the Free Software Foundation, either version 3 of the License, or
49
+ * (at your option) any later version.
50
+ *
51
+ * Linfo is distributed in the hope that it will be useful,
52
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
53
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54
+ * GNU General Public License for more details.
55
+ *
56
+ * You should have received a copy of the GNU General Public License
57
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
58
+ */
59
+
60
+ /**
61
+ * Keep out hackers...
62
+ */
63
+ namespace Linfo\Extension;
64
+
65
+ use Linfo\Linfo;
66
+ use Linfo\Common;
67
+ use Linfo\Meta\Errors;
68
+ use Linfo\Meta\Timer;
69
+ use Linfo\Output\Html;
70
+
71
+ /**
72
+ * Get status on torrents running under uTorrent.
73
+ */
74
+ class Utorrent implements Extension
75
+ {
76
+ private
77
+ $torrents = array(),
78
+ $connectionSettings = array(),
79
+ $stats = array('uploaded' => 0, 'downloaded' => 0),
80
+ $cookiefile = false,
81
+ $res = false;
82
+
83
+ // Keys corresponding to json array returned by utorrent.
84
+ // Ripped from utorrent/web/js/webui/constants.js. If this extension stops working
85
+ // they probably changed the keys in that file. Kindly fix this dictionary using that file plox and submit a patch ;)
86
+ protected static $torrent_keys = array(
87
+ 'TORRENT_HASH' => 0,
88
+ 'TORRENT_STATUS' => 1,
89
+ 'TORRENT_NAME' => 2,
90
+ 'TORRENT_SIZE' => 3, // bytes
91
+ 'TORRENT_PROGRESS' => 4,
92
+ 'TORRENT_DOWNLOADED' => 5, // bytes out of size
93
+ 'TORRENT_UPLOADED' => 6,
94
+ 'TORRENT_RATIO' => 7,
95
+ 'TORRENT_UPSPEED' => 8,
96
+ 'TORRENT_DOWNSPEED' => 9,
97
+ 'TORRENT_ETA' => 10,
98
+ 'TORRENT_LABEL' => 11,
99
+ 'TORRENT_PEERS_CONNECTED' => 12,
100
+ 'TORRENT_PEERS_SWARM' => 13,
101
+ 'TORRENT_SEEDS_CONNECTED' => 14,
102
+ 'TORRENT_SEEDS_SWARM' => 15,
103
+ 'TORRENT_AVAILABILITY' => 16,
104
+ 'TORRENT_QUEUE_POSITION' => 17,
105
+ 'TORRENT_REMAINING' => 18,
106
+ 'TORRENT_DOWNLOAD_URL' => 19,
107
+ 'TORRENT_RSS_FEED_URL' => 20,
108
+ 'TORRENT_STATUS_MESSAGE' => 21,
109
+ 'TORRENT_STREAM_ID' => 22,
110
+ 'TORRENT_DATE_ADDED' => 23,
111
+ 'TORRENT_DATE_COMPLETED' => 24,
112
+ 'TORRENT_APP_UPDATE_URL' => 25,
113
+ 'TORRENT_SAVE_PATH' => 26,
114
+ );
115
+
116
+ // First we log in to token.html using our admin/password. This gives us a token hash and
117
+ // cookie used for subsequent requests. Then we use these details to access the json list of torrents
118
+ const
119
+ TOKEN_URL = 'http://%s:%s/gui/token.html',
120
+ LIST_URL = 'http://%s:%s/gui/?token=%s&list=%s';
121
+
122
+ public function __construct(Linfo $linfo)
123
+ {
124
+ $settings = $linfo->getSettings();
125
+ $this->connectionSettings = $settings['utorrent_connection'];
126
+ $this->regexFilters = isset($settings['utorrent_filter']) && is_array($settings['utorrent_filter']) ? $settings['utorrent_filter'] : array();
127
+ $this->hideName = isset($settings['utorrent_hide_name']) ? !empty($settings['utorrent_hide_name']) : false;
128
+ }
129
+
130
+ public function work()
131
+ {
132
+ $t = new Timer('utorrent extension');
133
+
134
+ $this->res = false;
135
+
136
+ if (!extension_loaded('curl')) {
137
+ Errors::add('utorrent extension', 'Curl PHP extension not installed');
138
+
139
+ return;
140
+ }
141
+
142
+ if (!isset($this->connectionSettings['host']) || !isset($this->connectionSettings['port']) || !isset($this->connectionSettings['user'])) {
143
+ Errors::add('utorrent extension', 'Missing $setting[\'utorrent_connection\'] details in config..');
144
+
145
+ return;
146
+ }
147
+
148
+ $token_url = sprintf(self::TOKEN_URL, $this->connectionSettings['host'], $this->connectionSettings['port']);
149
+
150
+ // Start up our curl session to be used for both requests. It is going to store the cookies utorrent
151
+ // uses
152
+ $curl = curl_init();
153
+
154
+ // For curl to actually process cokies we need to give it a filename. This should be filed as a
155
+ // bug to curl, especially since something like /dev/null works
156
+ $this->cookiefile = tempnam('/tmp', 'linfo_utorrent');
157
+
158
+ curl_setopt_array($curl, array(
159
+ CURLOPT_RETURNTRANSFER => true,
160
+ CURLOPT_USERPWD => $this->connectionSettings['user'].':',
161
+ CURLOPT_COOKIEJAR => $this->cookiefile ?: '/dev/null', // If tempnam fails this will fail on Windows
162
+ ));
163
+
164
+ // Get token
165
+ curl_setopt($curl, CURLOPT_URL, $token_url);
166
+ $result = curl_exec($curl);
167
+
168
+ if (preg_match('/\>([^<]+)\</', $result, $m)) {
169
+ $token = $m[1];
170
+ } else {
171
+ Errors::add('utorrent extension', 'Failed parsing token');
172
+ $this->cleanup();
173
+
174
+ return;
175
+ }
176
+
177
+ // Get list of torrents? Do our best to forge this (ajax) request
178
+ curl_setopt_array($curl, array(
179
+ CURLOPT_HTTPHEADER => array(
180
+ 'X-Requested-With: XMLHttpRequest',
181
+ 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0',
182
+ 'Host: '.$this->connectionSettings['host'].($this->connectionSettings['port'] != 80 ? ':'.$this->connectionSettings['port'] : ''),
183
+ 'Referer: http://'.$this->connectionSettings['host'].($this->connectionSettings['port'] != 80 ? ':'.$this->connectionSettings['port'] : '').'/gui/web/index.html',
184
+ ),
185
+ ));
186
+
187
+ $list_url = sprintf(self::LIST_URL, $this->connectionSettings['host'], $this->connectionSettings['port'], $token, '1');
188
+ curl_setopt($curl, CURLOPT_URL, $list_url);
189
+
190
+ $result = curl_exec($curl);
191
+
192
+ if (!($response = @json_decode($result, true))) {
193
+ Errors::add('utorrent extension', 'Failed parsing json object');
194
+ $this->cleanup();
195
+
196
+ return;
197
+ }
198
+
199
+ // Not going to be needing curl again
200
+ curl_close($curl);
201
+
202
+ if (!isset($response['torrents']) || !is_array($response['torrents'])) {
203
+ Errors::add('utorrent extension', 'torrents array key not found in json response object');
204
+ $this->cleanup();
205
+
206
+ return;
207
+ }
208
+
209
+ $torrent_names = array();
210
+ $torrent_states = array();
211
+
212
+ foreach ($response['torrents'] as $torrent_src) {
213
+ $torrent = array();
214
+ foreach (self::$torrent_keys as $key => $index) {
215
+ $torrent[$key] = $torrent_src[$index];
216
+ }
217
+
218
+ foreach ($this->regexFilters as $regex) {
219
+ if (preg_match($regex, $torrent['TORRENT_NAME'])) {
220
+ continue 2;
221
+ }
222
+ }
223
+
224
+ $this->torrents[] = $torrent;
225
+ $torrent_names[] = $torrent['TORRENT_NAME'];
226
+ $torrent_states[] = $torrent['TORRENT_STATUS_MESSAGE'];
227
+
228
+ $this->stats['downloaded'] += $torrent['TORRENT_DOWNLOADED'];
229
+ $this->stats['uploaded'] += $torrent['TORRENT_UPLOADED'];
230
+ }
231
+
232
+ // Sort by state and then name ascending (show downloading/etc first)
233
+ array_multisort($torrent_states, SORT_ASC,
234
+ $torrent_names, SORT_ASC, $this->torrents);
235
+
236
+ $this->res = true;
237
+ $this->cleanup();
238
+ }
239
+
240
+ public function result()
241
+ {
242
+ if (!$this->res) {
243
+ return false;
244
+ }
245
+
246
+ $rows[] = array(
247
+ 'type' => 'header',
248
+ 'columns' => array(
249
+ 'Torrent/hash'.($this->hideName ? ' (names hidden)' : ''),
250
+ 'Size',
251
+ 'Progress',
252
+ 'Status',
253
+ 'Seeds',
254
+ 'Peers',
255
+ 'Downloaded',
256
+ 'Uploaded',
257
+ 'Ratio',
258
+ 'Speeds',
259
+ ),
260
+ );
261
+
262
+ foreach ($this->torrents as $name => $info) {
263
+ $rows[] = array(
264
+ 'type' => 'values',
265
+ 'columns' => array(
266
+ ($this->hideName ? '' : $info['TORRENT_NAME'].'<br />')
267
+ .'<span style="font-size: 80%; font-family: monaco, monospace, courier;">'.$info['TORRENT_HASH'].'</span>',
268
+ Common::byteConvert($info['TORRENT_SIZE']),
269
+ Html::generateBarChart($info['TORRENT_PROGRESS'] / 10),
270
+ $info['TORRENT_STATUS_MESSAGE'],
271
+ $info['TORRENT_SEEDS_CONNECTED'].'/'.$info['TORRENT_SEEDS_SWARM'],
272
+ $info['TORRENT_SEEDS_CONNECTED'].'/'.$info['TORRENT_PEERS_SWARM'],
273
+ Common::byteConvert($info['TORRENT_DOWNLOADED']),
274
+ Common::byteConvert($info['TORRENT_UPLOADED']),
275
+ $info['TORRENT_RATIO'] > 0 ? (round($info['TORRENT_RATIO'] / 1000, 2) ?: '0.0') : '0.0',
276
+ Common::byteConvert($info['TORRENT_DOWNSPEED']).'/s &darr; '.
277
+ Common::byteConvert($info['TORRENT_UPSPEED']).'/s &uarr; ',
278
+ ),
279
+ );
280
+ }
281
+
282
+ // Give it off
283
+ return array(
284
+ 'root_title' => '&micro;Torrent <span style="font-size: 80%;">('.Common::byteConvert($this->stats['downloaded']).' &darr; '
285
+ .Common::byteConvert($this->stats['uploaded']).' &uarr; '.round($this->stats['uploaded'] / $this->stats['downloaded'], 2).' ratio)</span>',
286
+ 'rows' => $rows,
287
+ );
288
+ }
289
+
290
+ private function cleanup()
291
+ {
292
+ // If we succeeded creating that temp file kill it off
293
+ if ($this->cookiefile && is_file($this->cookiefile)) {
294
+ @unlink($this->cookiefile);
295
+ }
296
+ }
297
+ }
lib/Linfo/Lang/de.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * German translation
25
+ */
26
+
27
+ return array(
28
+ 'header' => 'Systeminformationen',
29
+ 'core' => 'Kern',
30
+ 'os' => 'OS',
31
+ 'kernel' => 'Kernel',
32
+ 'accessed_ip' => 'Adapter-IP',
33
+ 'uptime' => 'Uptime',
34
+ 'hostname' => 'Hostname',
35
+ 'cpus' => 'CPUs',
36
+ 'phpversion' => 'PHP-Version',
37
+ 'webservice' => 'HTTP-Server',
38
+ 'load' => 'Auslastung/Load',
39
+ 'processes' => 'Prozesse',
40
+ 'threads' => 'Threads',
41
+ 'total' => 'Summe',
42
+ 'memory' => 'Speicher',
43
+ 'type' => 'Typ',
44
+ 'free' => 'Frei',
45
+ 'used' => 'Belegt',
46
+ 'size' => 'Größe',
47
+ 'physical' => 'Physikalisch',
48
+ 'swap' => 'Auslagerungspartition',
49
+ 'network_devices' => 'Netzwerkgeräte',
50
+ 'device_name' => 'Gerätename',
51
+ 'amount_sent' => 'Gesendet',
52
+ 'amount_received' => 'Empfangen',
53
+ 'state' => 'Zustand',
54
+ 'temps_voltages' => 'Temperaturen / Spannungen',
55
+ 'path' => 'Pfad',
56
+ 'device' => 'Gerät',
57
+ 'value' => 'Wert',
58
+ 'hardware' => 'Hardware',
59
+ 'vendor' => 'Hersteller',
60
+ 'drives' => 'Festplatten',
61
+ 'name' => 'Name',
62
+ 'reads' => 'Lesezugriffe',
63
+ 'writes' => 'Schreibzugriffe',
64
+ 'filesystem_mounts' => 'Eingehängte Datenträger',
65
+ 'mount_point' => 'Einhängepunkte',
66
+ 'filesystem' => 'Dateisystem',
67
+ 'percent_used' => 'Prozent belegt',
68
+ 'raid_arrays' => 'RAID Arrays',
69
+ 'level' => 'Level',
70
+ 'status' => 'Status',
71
+ 'devices' => 'Geräte',
72
+ 'label' => 'Label',
73
+ 'active' => 'Activ',
74
+ 'batteries' => 'Batterien',
75
+ 'charge' => 'Laden',
76
+ 'unknown' => 'Unbekannt',
77
+ 'footer_app' => 'Von %s in %s Sekunden generiert.',
78
+ 'none_found' => 'Keine gefunden',
79
+ 'sound_cards' => 'Soundkarten',
80
+ 'number' => 'Nummer',
81
+ 'card' => 'Karte',
82
+ 'message' => 'Nachricht',
83
+ 'from_where' => 'Quelle',
84
+ 'mount_options' => 'Einhängeoptionen',
85
+ 'error_head' => 'Fehler beim Sammeln der Daten',
86
+ 'timer' => 'Timer',
87
+ 'area' => 'Bereich',
88
+ 'time_taken' => 'Zum Holen benötigte Zeit',
89
+ 'days' => 'Tage',
90
+ 'hours' => 'Stunden',
91
+ 'minutes' => 'Minuten',
92
+ 'seconds' => 'Sekunden',
93
+ 'pid' => 'PID',
94
+ 'service' => 'Dienst',
95
+ 'services' => 'Dienste',
96
+ 'memory_usage' => 'Speichernutzung',
97
+ 'distro' => 'Distribution',
98
+ 'cpu_arch' => 'Architektur',
99
+ 'model' => 'Modell',
100
+ 'numLoggedIn' => 'Aktive Benutzer',
101
+ );
lib/Linfo/Lang/en.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * English translation
25
+ */
26
+
27
+ return array(
28
+ 'header' => 'System Health and Information',
29
+ 'core' => 'Core',
30
+ 'os' => 'OS',
31
+ 'kernel' => 'Kernel',
32
+ 'accessed_ip' => 'Accessed IP',
33
+ 'uptime' => 'Uptime',
34
+ 'hostname' => 'Hostname',
35
+ 'cpus' => 'CPUs',
36
+ 'phpversion' => 'PHP version',
37
+ 'webservice' => 'HTTP server',
38
+ 'load' => 'Load',
39
+ 'processes' => 'Processes',
40
+ 'threads' => 'Threads',
41
+ 'total' => 'Total',
42
+ 'memory' => 'Memory',
43
+ 'type' => 'Type',
44
+ 'free' => 'Free',
45
+ 'used' => 'Used',
46
+ 'size' => 'Size',
47
+ 'physical' => 'Physical',
48
+ 'swap' => 'Swap',
49
+ 'network_devices' => 'Network Devices',
50
+ 'device_name' => 'Device Name',
51
+ 'amount_sent' => 'Amount Sent',
52
+ 'amount_received' => 'Amount Received',
53
+ 'state' => 'State',
54
+ 'temps_voltages' => 'Temperatures / Voltages',
55
+ 'path' => 'Path',
56
+ 'device' => 'Device',
57
+ 'value' => 'Value',
58
+ 'hardware' => 'Hardware',
59
+ 'vendor' => 'Vendor',
60
+ 'drives' => 'Drives',
61
+ 'name' => 'Name',
62
+ 'reads' => 'Reads',
63
+ 'writes' => 'Writes',
64
+ 'filesystem_mounts' => 'Filesystem Mounts',
65
+ 'mount_point' => 'Mount Point',
66
+ 'filesystem' => 'Filesystem',
67
+ 'percent_used' => 'Percent Used',
68
+ 'raid_arrays' => 'RAID Arrays',
69
+ 'level' => 'Level',
70
+ 'status' => 'Status',
71
+ 'devices' => 'Devices',
72
+ 'label' => 'Label',
73
+ 'active' => 'Active',
74
+ 'batteries' => 'Batteries',
75
+ 'charge' => 'Charge',
76
+ 'unknown' => 'Unknown',
77
+ 'footer_app' => 'Generated by %s in %s seconds.',
78
+ 'none_found' => 'None Found',
79
+ 'sound_cards' => 'Sound Cards',
80
+ 'number' => 'Number',
81
+ 'card' => 'Card',
82
+ 'message' => 'Message',
83
+ 'from_where' => 'Source',
84
+ 'mount_options' => 'Mount Options',
85
+ 'error_head' => 'Data Gathering Errors',
86
+ 'timer' => 'Timer',
87
+ 'area' => 'Area',
88
+ 'time_taken' => 'Time taken to fetch',
89
+ 'days' => 'days',
90
+ 'years' => 'years',
91
+ 'hours' => 'hours',
92
+ 'minutes' => 'minutes',
93
+ 'seconds' => 'seconds',
94
+ 'pid' => 'PID',
95
+ 'service' => 'Service',
96
+ 'services' => 'Services',
97
+ 'memory_usage' => 'Memory Usage',
98
+ 'distro' => 'Distribution',
99
+ 'cpu_arch' => 'Architecture',
100
+ 'model' => 'Model',
101
+ 'numLoggedIn' => 'Active Users',
102
+ 'virtualization' => 'Virtualization',
103
+ 'guest' => 'Guest',
104
+ 'host' => 'Host',
105
+ 'cpu_usage' => 'Overall CPU Usage',
106
+ 'port_speed' => 'Port Speed',
107
+ );
lib/Linfo/Lang/fi.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * Finnish translation
25
+ */
26
+
27
+ return array(
28
+ 'header' => 'J&auml;rjestelm&auml;n kunto ja asetuksetn',
29
+ 'core' => 'Perustiedot',
30
+ 'os' => 'K&auml;ytt&ocirc;j&auml;rjestelm&auml;',
31
+ 'kernel' => 'Kernel',
32
+ 'accessed_ip' => 'IP osoite',
33
+ 'uptime' => 'K&auml;ynniss&auml;',
34
+ 'hostname' => 'Nimi',
35
+ 'cpus' => 'CPUt',
36
+ 'phpversion' => 'PHP versio',
37
+ 'webservice' => 'HTTP-palvelin',
38
+ 'load' => 'Kuorma',
39
+ 'processes' => 'Prosessit',
40
+ 'threads' => 'S&auml;ikeit&auml;',
41
+ 'total' => 'Yhteens&auml;',
42
+ 'memory' => 'Muisti',
43
+ 'type' => 'Tyyppi',
44
+ 'free' => 'Vapaana',
45
+ 'used' => 'K&auml;ytetty',
46
+ 'size' => 'Koko',
47
+ 'physical' => 'Fyysinen',
48
+ 'swap' => 'Swap',
49
+ 'network_devices' => 'Verkkolaitteet',
50
+ 'device_name' => 'Laitenimi',
51
+ 'amount_sent' => 'L&auml;hetetty',
52
+ 'amount_received' => 'Saapunut',
53
+ 'state' => 'Tila',
54
+ 'temps_voltages' => 'L&auml;mp&ocirc;tila / J&auml;nnitteet',
55
+ 'path' => 'Polku',
56
+ 'device' => 'Laite',
57
+ 'value' => 'Arvo',
58
+ 'hardware' => 'Kovo',
59
+ 'vendor' => 'Valmistaja',
60
+ 'type' => 'Tytppi',
61
+ 'drives' => 'Asemat',
62
+ 'name' => 'Nimi',
63
+ 'reads' => 'Luettu',
64
+ 'writes' => 'Kirjoitettu',
65
+ 'size' => 'Koko',
66
+ 'filesystem_mounts' => 'Tietoj&auml;rjestelm&auml;t',
67
+ 'mount_point' => 'Liitospiste',
68
+ 'filesystem' => 'Tietoj&auml;rjestelm&auml;',
69
+ 'used' => 'K&auml;ytetty',
70
+ 'free' => 'Vapaana',
71
+ 'percent_used' => 'K&auml;ytetty %',
72
+ 'raid_arrays' => 'RAID pakat',
73
+ 'level' => 'Taso',
74
+ 'status' => 'Status',
75
+ 'devices' => 'Asemat',
76
+ 'label' => 'Label',
77
+ 'active' => 'Aktivoitu',
78
+ 'batteries' => 'Patterit',
79
+ 'charge' => 'Lataus',
80
+ 'unknown' => 'Tuntematon',
81
+ 'footer_app' => 'Luotu: %s aika %s sekunttia.',
82
+ 'none_found' => 'Ei osumia',
83
+ 'sound_cards' => '&auml;&auml;nikortit',
84
+ 'number' => 'Numero',
85
+ 'card' => 'Kortti',
86
+ 'message' => 'Viesti',
87
+ 'from_where' => 'L&auml;hde',
88
+ 'mount_options' => 'Liitos optiot',
89
+ 'error_head' => 'Virheet datan noudossa',
90
+ 'timer' => 'Ajastin',
91
+ 'area' => 'Area',
92
+ 'time_taken' => 'Haun kesto',
93
+ 'days' => 'p&auml;iv&auml;&auml;',
94
+ 'minutes' => 'minuuttiat',
95
+ 'seconds' => 'sekunttia',
96
+ 'hours' => 'tuntia',
97
+ 'pid' => 'PID',
98
+ 'service' => 'Palvelu',
99
+ 'services' => 'Palvelut',
100
+ 'memory_usage' => 'Muistin k&auml;ytt&ocirc;',
101
+ 'distro' => 'Jakelu',
102
+ 'cpu_arch' => 'Arkkitektuuri',
103
+ 'model' => 'Malli',
104
+ );
lib/Linfo/Lang/fr.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * Traduction française : Jean-Bernard Yata yata.jb@revolsys.fr
25
+ */
26
+
27
+ return array(
28
+ 'header' => 'Informations Générales du Système',
29
+ 'core' => 'Principal',
30
+ 'os' => 'Système d\'exploitation',
31
+ 'kernel' => 'Noyau',
32
+ 'accessed_ip' => 'Adresse IP utilisée',
33
+ 'uptime' => 'Uptime',
34
+ 'hostname' => 'Nom d\'hôte',
35
+ 'cpus' => 'Processeur(s)',
36
+ 'phpversion' => 'version de PHP',
37
+ 'webservice' => 'serveur HTTP',
38
+ 'load' => 'Charge',
39
+ 'processes' => 'Processus',
40
+ 'threads' => 'Opérations en cours',
41
+ 'total' => 'Totale',
42
+ 'memory' => 'Mémoire',
43
+ 'type' => 'Type',
44
+ 'free' => 'Libre',
45
+ 'used' => 'Utilisée',
46
+ 'size' => 'Taille',
47
+ 'physical' => 'Physique',
48
+ 'swap' => 'Echange',
49
+ 'network_devices' => 'Interfaces réseau',
50
+ 'device_name' => 'Interface',
51
+ 'amount_sent' => 'Paquets envoyés',
52
+ 'amount_received' => 'Paquets reçus',
53
+ 'state' => 'Etat',
54
+ 'temps_voltages' => 'Températures / Voltages',
55
+ 'path' => 'Chemin système',
56
+ 'device' => 'équipement',
57
+ 'value' => 'Valeur',
58
+ 'hardware' => 'Matériel',
59
+ 'vendor' => 'Fabriquant',
60
+ 'drives' => 'Lecteurs',
61
+ 'name' => 'Nom',
62
+ 'reads' => 'Cycles de lecture',
63
+ 'writes' => 'Cycle d\'écriture',
64
+ 'filesystem_mounts' => 'Points de montage',
65
+ 'mount_point' => 'Point de montage',
66
+ 'filesystem' => 'Système de fichiers',
67
+ 'percent_used' => '% utilisé',
68
+ 'raid_arrays' => 'Volumes RAID',
69
+ 'level' => 'Niveau',
70
+ 'status' => 'Etat',
71
+ 'devices' => 'Périphériques',
72
+ 'label' => 'Etiquette',
73
+ 'active' => 'Active',
74
+ 'batteries' => 'Batteries',
75
+ 'charge' => 'Charge',
76
+ 'unknown' => 'Non renseigné',
77
+ 'footer_app' => 'Générée le %s en %s secondes.',
78
+ 'none_found' => 'Introuvé',
79
+ 'sound_cards' => 'Cartes son',
80
+ 'number' => 'Nombre',
81
+ 'card' => 'Carte',
82
+ 'message' => 'Message',
83
+ 'from_where' => 'Source',
84
+ 'mount_options' => 'Options de montage',
85
+ 'error_head' => 'Erreur lors de la collecte des informations',
86
+ 'timer' => 'Horodateur',
87
+ 'area' => 'Zone',
88
+ 'time_taken' => 'Durée de collecte',
89
+ 'days' => 'jours',
90
+ 'hours' => 'heures',
91
+ 'minutes' => 'minutes',
92
+ 'seconds' => 'secondes',
93
+ 'pid' => 'PID',
94
+ 'service' => 'Service',
95
+ 'services' => 'Services',
96
+ 'memory_usage' => 'Utilisation mémoire',
97
+ 'distro' => 'Distribution',
98
+ 'cpu_arch' => 'Architecture',
99
+ 'model' => 'Modèle',
100
+ 'numLoggedIn' => 'Utilisateurs actifs',
101
+ );
lib/Linfo/Lang/it.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * Italian translation
25
+ * By mte90 - http://www.mte90.net
26
+ */
27
+
28
+ return array(
29
+ 'header' => 'Informazioni e Stato del Sistema',
30
+ 'core' => 'Sistema',
31
+ 'os' => 'SO',
32
+ 'kernel' => 'Kernel',
33
+ 'accessed_ip' => 'IP',
34
+ 'uptime' => 'Uptime',
35
+ 'hostname' => 'Hostname',
36
+ 'cpus' => 'CPU',
37
+ 'phpversion' => 'versione di PHP',
38
+ 'webservice' => 'server HTTP',
39
+ 'load' => 'Carico',
40
+ 'processes' => 'Processi',
41
+ 'threads' => 'Thread',
42
+ 'total' => 'Totale',
43
+ 'memory' => 'Memoria',
44
+ 'type' => 'Tipo',
45
+ 'free' => 'Libero',
46
+ 'used' => 'Usato',
47
+ 'size' => 'Dimensioni',
48
+ 'physical' => 'Physical',
49
+ 'swap' => 'Swap',
50
+ 'network_devices' => 'Dispositivi di Rete',
51
+ 'device_name' => 'Nome Dispositivo',
52
+ 'amount_sent' => 'Totale Inviato',
53
+ 'amount_received' => 'Totale Ricevuto',
54
+ 'state' => 'Stato',
55
+ 'temps_voltages' => 'Temperatura / Voltaggio',
56
+ 'path' => 'Percorso',
57
+ 'device' => 'Dispositivo',
58
+ 'value' => 'Valore',
59
+ 'hardware' => 'Hardware',
60
+ 'vendor' => 'Produttore',
61
+ 'type' => 'Tipo',
62
+ 'drives' => 'Unit&agrave;',
63
+ 'name' => 'Nome',
64
+ 'reads' => 'Letture',
65
+ 'writes' => 'Scritture',
66
+ 'size' => 'Dimensioni',
67
+ 'filesystem_mounts' => 'Montaggio FileSystem',
68
+ 'mount_point' => 'Punto di Montaggio',
69
+ 'filesystem' => 'Filesystem',
70
+ 'used' => 'Usato',
71
+ 'free' => 'Libero',
72
+ 'percent_used' => 'Percentuale Usata',
73
+ 'raid_arrays' => 'RAID Array',
74
+ 'level' => 'Livello',
75
+ 'status' => 'Stato',
76
+ 'devices' => 'Dispositivi',
77
+ 'label' => 'Etichetta',
78
+ 'active' => 'Attivo',
79
+ 'batteries' => 'Batterie',
80
+ 'charge' => 'Caricamento',
81
+ 'unknown' => 'Sconosciuto',
82
+ 'footer_app' => 'Generatato da %s in %s secondi.',
83
+ 'none_found' => 'Nessun risultato',
84
+ 'sound_cards' => 'Schede Audio',
85
+ 'number' => 'Numero',
86
+ 'card' => 'Scheda',
87
+ 'message' => 'Messaggio',
88
+ 'from_where' => 'Sorgente',
89
+ 'mount_options' => 'opzioni di Montaggio',
90
+ 'error_head' => 'Data Gathering Error',
91
+ 'timer' => 'Timer',
92
+ 'area' => 'Area',
93
+ 'time_taken' => 'Tempo usato per la generazione',
94
+ 'days' => 'giorni',
95
+ 'minutes' => 'minuti',
96
+ 'seconds' => 'secondi',
97
+ 'hours' => 'ore',
98
+ 'pid' => 'PID',
99
+ 'service' => 'Servizio',
100
+ 'services' => 'Servizi',
101
+ 'memory_usage' => 'Uso della Memoria',
102
+ 'distro' => 'Distribuzione',
103
+ 'cpu_arch' => 'Architettura',
104
+ 'model' => 'Modello',
105
+ );
lib/Linfo/Lang/pl.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * Polish translation
25
+ */
26
+
27
+ return array(
28
+ 'header' => 'Aktualny stan systemu i informacja',
29
+ 'core' => 'Rdzeń',
30
+ 'os' => 'OS',
31
+ 'kernel' => 'Kernel',
32
+ 'accessed_ip' => 'Przydzielone IP',
33
+ 'uptime' => 'Działa',
34
+ 'hostname' => 'Nazwa komputera',
35
+ 'cpus' => 'CPUs',
36
+ 'phpversion' => 'wersja PHP',
37
+ 'webservice' => 'serwer HTTP',
38
+ 'load' => 'Obciążenie',
39
+ 'processes' => 'Procesy',
40
+ 'threads' => 'Wątki',
41
+ 'total' => 'Razem',
42
+ 'memory' => 'Pamięci',
43
+ 'type' => 'Typ',
44
+ 'free' => 'Wolnych',
45
+ 'used' => 'Używanego', //duplicate below
46
+ 'size' => 'Rozmiar', //duplicate below
47
+ 'physical' => 'Fizyczna',
48
+ 'swap' => 'Swap',
49
+ 'network_devices' => 'Urządzenia sieciowe',
50
+ 'device_name' => 'Nazwa urządzenia',
51
+ 'amount_sent' => 'Wysłane',
52
+ 'amount_received' => 'Otrzymane',
53
+ 'state' => 'Stan',
54
+ 'temps_voltages' => 'Temperatura / Napięcie',
55
+ 'path' => 'Ścieżka',
56
+ 'device' => 'Urządzenie',
57
+ 'value' => 'Wartość',
58
+ 'hardware' => 'Sprzęt',
59
+ 'vendor' => 'Dostawca',
60
+ 'drives' => 'Dyski',
61
+ 'name' => 'Nazwa',
62
+ 'reads' => 'Odczytanych',
63
+ 'writes' => 'Zapisanych',
64
+ 'size' => 'Łącznie', //duplicate
65
+ 'filesystem_mounts' => 'Zamonotwany system plików',
66
+ 'mount_point' => 'Punkt montowania',
67
+ 'filesystem' => 'System plików',
68
+ 'used' => 'Użytych',//duplicate
69
+ 'percent_used' => 'Procent zużycia',
70
+ 'raid_arrays' => 'RAID Arrays',
71
+ 'level' => 'Poziom',
72
+ 'status' => 'Stan',
73
+ 'devices' => 'Urządzenia',
74
+ 'label' => 'Etykieta',
75
+ 'active' => 'Aktywne',
76
+ 'batteries' => 'Baterie',
77
+ 'charge' => 'Naładowanie',
78
+ 'unknown' => 'Nieznany',
79
+ 'footer_app' => '%s Wygenerowane przez w sekund %s.',
80
+ 'none_found' => 'Nieznalezione',
81
+ 'sound_cards' => 'Karty dźwiękowe',
82
+ 'number' => 'Numer',
83
+ 'card' => 'Karta',
84
+ 'from_where' => 'Źródło',
85
+ 'message' => 'Wiadomość',
86
+ 'mount_options' => 'Opcje montowania',
87
+ 'error_head' => 'Błędy gromadzenia danych',
88
+ 'timer' => 'Zegar',
89
+ 'area' => 'Strefa',
90
+ 'time_taken' => 'Czas potrzebny do pobierania',
91
+ 'days' => 'dni',
92
+ 'hours' => 'godzina',
93
+ 'minutes' => 'minuta',
94
+ 'seconds' => 'sekund',
95
+ 'pid' => 'PID',
96
+ 'service' => 'Usługa',
97
+ 'services' => 'Usługi',
98
+ 'memory_usage' => 'Zużycie pamięci',
99
+ 'distro' => 'Dystrybucja',
100
+ 'cpu_arch' => 'Architektura',
101
+ 'model' => 'Model', //todo
102
+ 'numLoggedIn' => 'Active Users', //todo
103
+ );
lib/Linfo/Lang/pt.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2012 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * Portuguese translation
25
+ * Fábio Diniz Rossi
26
+ * fdrossi@gmail.com
27
+ */
28
+
29
+ return array(
30
+ 'header' => 'Informações do Sistema',
31
+ 'core' => 'Hardware',
32
+ 'os' => 'Sistema Operacional',
33
+ 'kernel' => 'Kernel',
34
+ 'accessed_ip' => 'Endereço IP de acesso',
35
+ 'uptime' => 'Tempo Ligado',
36
+ 'hostname' => 'Nome da Máquina',
37
+ 'cpus' => 'CPUs',
38
+ 'phpversion' => 'versão PHP',
39
+ 'webservice' => 'servidor HTTP',
40
+ 'load' => 'Carregado',
41
+ 'processes' => 'Processos',
42
+ 'threads' => 'Threads',
43
+ 'total' => 'Total',
44
+ 'memory' => 'Memória',
45
+ 'type' => 'Tipo',
46
+ 'free' => 'Livre',
47
+ 'used' => 'Usado',
48
+ 'size' => 'Tamanho',
49
+ 'physical' => 'Físico',
50
+ 'swap' => 'Swap',
51
+ 'network_devices' => 'Dispositivos de Rede',
52
+ 'device_name' => 'Nome de Dispositivos',
53
+ 'amount_sent' => 'Enviados',
54
+ 'amount_received' => 'Recebidos',
55
+ 'state' => 'Estado',
56
+ 'temps_voltages' => 'Temperaturas / Voltagens',
57
+ 'path' => 'Caminho',
58
+ 'device' => 'Dispositivo',
59
+ 'value' => 'Valor',
60
+ 'hardware' => 'Hardware',
61
+ 'vendor' => 'Fabricante',
62
+ 'drives' => 'Drives',
63
+ 'name' => 'Nome',
64
+ 'reads' => 'Leituras',
65
+ 'writes' => 'Escritas',
66
+ 'filesystem_mounts' => 'Sistemas de Arquivos Montados',
67
+ 'mount_point' => 'Ponto de Montagem',
68
+ 'filesystem' => 'Sistema de Arquivos',
69
+ 'percent_used' => 'Percentual Usado',
70
+ 'raid_arrays' => 'RAID',
71
+ 'level' => 'Nível',
72
+ 'status' => 'Estado',
73
+ 'devices' => 'Dispositivos',
74
+ 'label' => 'Rótulo',
75
+ 'active' => 'Ativo',
76
+ 'batteries' => 'Baterias',
77
+ 'charge' => 'Carga',
78
+ 'unknown' => 'Desconhecido',
79
+ 'footer_app' => 'Gerado por %s em %s segundos.',
80
+ 'none_found' => 'Não Encontrado',
81
+ 'sound_cards' => 'Placa de Som',
82
+ 'number' => 'Numero',
83
+ 'card' => 'Placa',
84
+ 'message' => 'Mensagem',
85
+ 'from_where' => 'Fonte',
86
+ 'mount_options' => 'Opções de Montagem',
87
+ 'error_head' => 'Erro de Dados',
88
+ 'timer' => 'Tempo',
89
+ 'area' => 'Area',
90
+ 'time_taken' => 'Tempo de busca',
91
+ 'days' => 'dias',
92
+ 'years' => 'anos',
93
+ 'hours' => 'horas',
94
+ 'minutes' => 'minutos',
95
+ 'seconds' => 'segundos',
96
+ 'pid' => 'PID',
97
+ 'service' => 'Serviço',
98
+ 'services' => 'Serviços',
99
+ 'memory_usage' => 'Uso da Memória',
100
+ 'distro' => 'Distribuição',
101
+ 'cpu_arch' => 'Arquitetura',
102
+ 'model' => 'Modelo',
103
+ 'numLoggedIn' => 'Usuários Ativos',
104
+ );
lib/Linfo/Lang/zh.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Lang;
22
+
23
+ /*
24
+ * Chinese translation
25
+ * 如果您有任何对翻译的建议请联系:Digimoncn@Gmail.com
26
+ */
27
+
28
+ return array(
29
+ 'header' => 'System Health and Information',
30
+ 'core' => '核心信息',
31
+ 'os' => '操作系统',
32
+ 'kernel' => '内核版本',
33
+ 'accessed_ip' => 'IP地址',
34
+ 'uptime' => '在线时间',
35
+ 'hostname' => '主机名',
36
+ 'cpus' => 'CPU',
37
+ 'phpversion' => 'PHP版本',
38
+ 'webservice' => 'HTTP服務器',
39
+ 'load' => '负载',
40
+ 'processes' => '进程',
41
+ 'threads' => '线程',
42
+ 'total' => '总计',
43
+ 'memory' => '内存',
44
+ 'type' => '类型',
45
+ 'free' => '可用',
46
+ 'used' => '已用',
47
+ 'size' => '总大小',
48
+ 'physical' => '物理',
49
+ 'swap' => 'Swap',
50
+ 'network_devices' => '网络设备',
51
+ 'device_name' => '设备名称',
52
+ 'amount_sent' => '发送数据',
53
+ 'amount_received' => '接收数据',
54
+ 'state' => '状态',
55
+ 'temps_voltages' => '温度 / 电压',
56
+ 'path' => '地址',
57
+ 'device' => '设备',
58
+ 'value' => '值',
59
+ 'hardware' => '硬件',
60
+ 'vendor' => '制造商',
61
+ 'type' => '类型',
62
+ 'drives' => '硬盘',
63
+ 'name' => '名称',
64
+ 'reads' => '读取',
65
+ 'writes' => '写入',
66
+ 'size' => '总容量',
67
+ 'filesystem_mounts' => '文件系统监控',
68
+ 'mount_point' => '挂载点',
69
+ 'filesystem' => '文件系统',
70
+ 'used' => '已用',
71
+ 'free' => '可用',
72
+ 'percent_used' => '百分比',
73
+ 'raid_arrays' => 'RAID阵列',
74
+ 'level' => 'RAID类型',
75
+ 'status' => '状态',
76
+ 'devices' => '设备',
77
+ 'label' => '标签',
78
+ 'active' => '可用',
79
+ 'batteries' => '电池',
80
+ 'charge' => '剩余',
81
+ 'unknown' => '未知',
82
+ 'footer_app' => '由%s 生成,共花费 %s 秒.',
83
+ 'none_found' => '未知',
84
+ 'sound_cards' => '声卡',
85
+ 'number' => '数量',
86
+ 'card' => '卡片',
87
+ 'message' => '消息',
88
+ 'from_where' => '来源',
89
+ 'mount_options' => '挂载设置',
90
+ 'error_head' => '数据汇总错误',
91
+ 'timer' => '计时器',
92
+ 'area' => '区域',
93
+ 'time_taken' => '获取用时',
94
+ 'days' => '天',
95
+ 'minutes' => '分钟',
96
+ 'seconds' => '秒',
97
+ 'hours' => '小时',
98
+ 'pid' => 'PID',
99
+ 'service' => '服务',
100
+ 'services' => '服务',
101
+ 'memory_usage' => '内存使用',
102
+ 'distro' => '发行版',
103
+ 'cpu_arch' => '架构',
104
+ 'model' => '型号',
105
+ );
lib/Linfo/Linfo.php ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2014, 2015 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo;
20
+
21
+ use Linfo\Parsers\CallExt;
22
+ use Linfo\Exceptions\FatalException;
23
+ use Linfo\Meta\Errors;
24
+ use ReflectionClass;
25
+ use ReflectionException;
26
+
27
+ /**
28
+ * Linfo.
29
+ *
30
+ * Serve as the script's "controller". Leverages other classes. Loads settings,
31
+ * outputs them in formats, runs extensions, etc.
32
+ *
33
+ * @throws FatalException
34
+ */
35
+ class Linfo
36
+ {
37
+ protected $settings = array(),
38
+ $lang = array(),
39
+ $info = array(),
40
+ $parser = null,
41
+
42
+ $app_name = 'Linfo',
43
+ $version = '',
44
+ $time_start = 0,
45
+ $linfo_testdir = null,
46
+ $linfo_localdir = null;
47
+
48
+ public function __construct($settings = array())
49
+ {
50
+
51
+ // Time us
52
+ $this->time_start = microtime(true);
53
+
54
+ // Some paths..
55
+ $this->linfo_testdir = dirname(dirname(__DIR__)).'/tests';
56
+ $this->linfo_localdir = dirname(dirname(__DIR__)).'/';
57
+
58
+ // Get our version from git setattribs
59
+ $scm = '2016-01-30 18:17:52 -0800';
60
+ list($this->version) = strpos($scm, '$') !== false ? array('git') : explode(' ', $scm);
61
+
62
+ // Run through dependencies / sanity checking
63
+ if (!extension_loaded('pcre') && !function_exists('preg_match') && !function_exists('preg_match_all')) {
64
+ throw new FatalException($this->app_name.' needs the `pcre\' extension to be loaded. http://us2.php.net/manual/en/book.pcre.php');
65
+ }
66
+
67
+ // Warnings usually displayed to browser happen if date.timezone isn't set in php 5.3+
68
+ if (!ini_get('date.timezone')) {
69
+ @ini_set('date.timezone', 'Etc/UTC');
70
+ }
71
+
72
+ // Load our settings/language
73
+ $this->loadSettings($settings);
74
+ $this->loadLanguage();
75
+
76
+ // Some classes need our vars; config them
77
+ Common::config($this);
78
+ CallExt::config($this);
79
+
80
+ // Determine OS
81
+ $os = $this->getOS();
82
+
83
+ if (!$os) {
84
+ throw new FatalException('Unknown/unsupported operating system');
85
+ }
86
+
87
+ $distro_class = '\\Linfo\\OS\\'.$os;
88
+ $this->parser = new $distro_class($this->settings);
89
+ }
90
+
91
+ // Load everything, while obeying permissions...
92
+ public function scan()
93
+ {
94
+ $reflector = new ReflectionClass($this->parser);
95
+
96
+ // Prime parser. Do things not appropriate to do in constructor. Most OS classes
97
+ // don't have this.
98
+ if ($reflector->hasMethod('init') && ($method = $reflector->getMethod('init'))) {
99
+ $method->invoke($this->parser);
100
+ }
101
+
102
+ // Array fields, tied to method names and default values...
103
+ $fields = array(
104
+ 'OS' => array(
105
+ 'show' => !empty($this->settings['show']['os']),
106
+ 'default' => '',
107
+ 'method' => 'getOS',
108
+ ),
109
+
110
+ 'Kernel' => array(
111
+ 'show' => !empty($this->settings['show']['kernel']),
112
+ 'default' => '',
113
+ 'method' => 'getKernel',
114
+ ),
115
+
116
+ 'AccessedIP' => array(
117
+ 'show' => !isset($this->settings['show']['ip']) || !empty($this->settings['show']['ip']),
118
+ 'default' => '',
119
+ 'method' => 'getAccessedIP',
120
+ ),
121
+
122
+ 'Distro' => array(
123
+ 'show' => !empty($this->settings['show']['distro']),
124
+ 'default' => '',
125
+ 'method' => 'getDistro',
126
+ ),
127
+
128
+ 'RAM' => array(
129
+ 'show' => !empty($this->settings['show']['ram']),
130
+ 'default' => array(),
131
+ 'method' => 'getRam',
132
+ ),
133
+
134
+ 'HD' => array(
135
+ 'show' => !empty($this->settings['show']['hd']),
136
+ 'default' => array(),
137
+ 'method' => 'getHD',
138
+ ),
139
+
140
+ 'Mounts' => array(
141
+ 'show' => !empty($this->settings['show']['mounts']),
142
+ 'default' => array(),
143
+ 'method' => 'getMounts',
144
+ ),
145
+
146
+ 'Load' => array(
147
+ 'show' => !empty($this->settings['show']['load']),
148
+ 'default' => array(),
149
+ 'method' => 'getLoad',
150
+ ),
151
+
152
+ 'HostName' => array(
153
+ 'show' => !empty($this->settings['show']['hostname']),
154
+ 'default' => '',
155
+ 'method' => 'getHostName',
156
+ ),
157
+
158
+ 'UpTime' => array(
159
+ 'show' => !empty($this->settings['show']['uptime']),
160
+ 'default' => array(),
161
+ 'method' => 'getUpTime',
162
+ ),
163
+
164
+ 'CPU' => array(
165
+ 'show' => !empty($this->settings['show']['cpu']),
166
+ 'default' => array(),
167
+ 'method' => 'getCPU',
168
+ ),
169
+
170
+ 'Model' => array(
171
+ 'show' => !empty($this->settings['show']['model']),
172
+ 'default' => array(),
173
+ 'method' => 'getModel',
174
+ ),
175
+
176
+ 'CPUArchitecture' => array(
177
+ 'show' => !empty($this->settings['show']['cpu']),
178
+ 'default' => '',
179
+ 'method' => 'getCPUArchitecture',
180
+ ),
181
+
182
+ 'Network Devices' => array(
183
+ 'show' => !empty($this->settings['show']['network']),
184
+ 'default' => array(),
185
+ 'method' => 'getNet',
186
+ ),
187
+
188
+ 'Devices' => array(
189
+ 'show' => !empty($this->settings['show']['devices']),
190
+ 'default' => array(),
191
+ 'method' => 'getDevs',
192
+ ),
193
+
194
+ 'Temps' => array(
195
+ 'show' => !empty($this->settings['show']['temps']),
196
+ 'default' => array(),
197
+ 'method' => 'getTemps',
198
+ ),
199
+
200
+ 'Battery' => array(
201
+ 'show' => !empty($this->settings['show']['battery']),
202
+ 'default' => array(),
203
+ 'method' => 'getBattery',
204
+ ),
205
+
206
+ 'Raid' => array(
207
+ 'show' => !empty($this->settings['show']['raid']),
208
+ 'default' => array(),
209
+ 'method' => 'getRAID',
210
+ ),
211
+
212
+ 'Wifi' => array(
213
+ 'show' => !empty($this->settings['show']['wifi']),
214
+ 'default' => array(),
215
+ 'method' => 'getWifi',
216
+ ),
217
+
218
+ 'SoundCards' => array(
219
+ 'show' => !empty($this->settings['show']['sound']),
220
+ 'default' => array(),
221
+ 'method' => 'getSoundCards',
222
+ ),
223
+
224
+ 'processStats' => array(
225
+ 'show' => !empty($this->settings['show']['process_stats']),
226
+ 'default' => array(),
227
+ 'method' => 'getProcessStats',
228
+ ),
229
+
230
+ 'services' => array(
231
+ 'show' => !empty($this->settings['show']['services']),
232
+ 'default' => array(),
233
+ 'method' => 'getServices',
234
+ ),
235
+
236
+ 'numLoggedIn' => array(
237
+ 'show' => !empty($this->settings['show']['numLoggedIn']),
238
+ 'default' => false,
239
+ 'method' => 'getnumLoggedIn',
240
+ ),
241
+
242
+ 'virtualization' => array(
243
+ 'show' => !empty($this->settings['show']['virtualization']),
244
+ 'default' => array(),
245
+ 'method' => 'getVirtualization',
246
+ ),
247
+
248
+ 'cpuUsage' => array(
249
+ 'show' => !empty($this->settings['cpu_usage']),
250
+ 'default' => false,
251
+ 'method' => 'getCPUUsage',
252
+ ),
253
+
254
+ 'phpVersion' => array(
255
+ 'show' => !empty($this->settings['show']['phpversion']),
256
+ 'default' => false,
257
+ 'method' => 'getPhpVersion',
258
+ ),
259
+
260
+ 'webService' => array(
261
+ 'show' => !empty($this->settings['show']['webservice']),
262
+ 'default' => false,
263
+ 'method' => 'getWebService',
264
+ ),
265
+
266
+ // Extra info such as which fields to not show
267
+ 'contains' => array(
268
+ 'show' => true,
269
+ 'default' => array(),
270
+ 'method' => 'getContains',
271
+ ),
272
+ );
273
+
274
+ foreach ($fields as $key => $data) {
275
+ if (!$data['show']) {
276
+ $this->info[$key] = $data['default'];
277
+ continue;
278
+ }
279
+
280
+ try {
281
+ $method = $reflector->getMethod($data['method']);
282
+ $this->info[$key] = $method->invoke($this->parser);
283
+ } catch (ReflectionException $e) {
284
+ $this->info[$key] = $data['default'];
285
+ }
286
+ }
287
+
288
+ // Add a timestamp
289
+ $this->info['timestamp'] = date('c');
290
+
291
+ // Run extra extensions
292
+ $this->runExtensions();
293
+ }
294
+
295
+ protected function loadSettings($settings = array())
296
+ {
297
+
298
+ // Running unit tests?
299
+ if (defined('LINFO_TESTING')) {
300
+ $this->settings = Common::getVarFromFile($this->linfo_testdir.'/test_settings.php', 'settings');
301
+ if (!is_array($this->settings)) {
302
+ throw new FatalException('Failed getting test-specific settings');
303
+ }
304
+
305
+ return;
306
+ }
307
+
308
+ // Don't just blindly assume we have the ob_* functions...
309
+ if (!function_exists('ob_start')) {
310
+ $settings['compress_content'] = false;
311
+ }
312
+
313
+ if (!isset($settings['hide'])) {
314
+ $settings['hide'] = array(
315
+ 'filesystems' => array(),
316
+ 'storage_devices' => array(),
317
+ );
318
+ }
319
+
320
+ // Make sure these are arrays
321
+ $settings['hide']['filesystems'] = is_array($settings['hide']['filesystems']) ? $settings['hide']['filesystems'] : array();
322
+ $settings['hide']['storage_devices'] = is_array($settings['hide']['storage_devices']) ? $settings['hide']['storage_devices'] : array();
323
+
324
+ // Make sure these are always hidden
325
+ $settings['hide']['filesystems'][] = 'rootfs';
326
+ $settings['hide']['filesystems'][] = 'binfmt_misc';
327
+
328
+ // Default timeformat
329
+ $settings['dates'] = array_key_exists('dates', $settings) ? $settings['dates'] : 'm/d/y h:i A (T)';
330
+
331
+ // Default to english translation if garbage is passed
332
+ if (empty($settings['language']) || !preg_match('/^[a-z]{2}$/', $settings['language'])) {
333
+ $settings['language'] = 'en';
334
+ }
335
+
336
+ // If it can't be found default to english
337
+ if (!is_file($this->linfo_localdir.'src/Linfo/Lang/'.$settings['language'].'.php')) {
338
+ $settings['language'] = 'en';
339
+ }
340
+
341
+ $this->settings = $settings;
342
+ }
343
+
344
+ protected function loadLanguage()
345
+ {
346
+
347
+ // Running unit tests?
348
+ if (defined('LINFO_TESTING')) {
349
+ $this->lang = require $this->linfo_testdir.'/test_lang.php';
350
+ if (!is_array($this->lang)) {
351
+ throw new FatalException('Failed getting test-specific language');
352
+ }
353
+
354
+ return;
355
+ }
356
+
357
+ // Load translation, defaulting to english of keys are missing (assuming
358
+ // we're not using english anyway and the english translation indeed exists)
359
+ if (is_file($this->linfo_localdir.'src/Linfo/Lang/en.php') && $this->settings['language'] != 'en') {
360
+ $this->lang = array_merge(require($this->linfo_localdir.'src/Linfo/Lang/en.php'),
361
+ require($this->linfo_localdir.'src/Linfo/Lang/'.$this->settings['language'].'.php'));
362
+ }
363
+
364
+ // Otherwise snag desired translation, be it english or a non-english without english to fall back on
365
+ else {
366
+ $this->lang = require $this->linfo_localdir.'src/Linfo/Lang/'.$this->settings['language'].'.php';
367
+ }
368
+ }
369
+
370
+ protected function getOS()
371
+ {
372
+ list($os) = explode('_', PHP_OS, 2);
373
+
374
+ // This magical constant knows all
375
+ switch ($os) {
376
+
377
+ // These are supported
378
+ case 'Linux':
379
+ case 'FreeBSD':
380
+ case 'DragonFly':
381
+ case 'OpenBSD':
382
+ case 'NetBSD':
383
+ case 'Minix':
384
+ case 'Darwin':
385
+ case 'SunOS':
386
+ return PHP_OS;
387
+ break;
388
+ case 'WINNT':
389
+ return 'Windows';
390
+ break;
391
+ }
392
+
393
+ // So anything else isn't
394
+ return false;
395
+ }
396
+
397
+ /*
398
+ * getInfo()
399
+ *
400
+ * Returning reference so extensions can modify result
401
+ */
402
+ public function &getInfo()
403
+ {
404
+ return $this->info;
405
+ }
406
+
407
+ protected function runExtensions()
408
+ {
409
+ $this->info['extensions'] = array();
410
+
411
+ if (!array_key_exists('extensions', $this->settings) || count($this->settings['extensions']) == 0) {
412
+ return;
413
+ }
414
+
415
+ // Go through each enabled extension
416
+ foreach ((array) $this->settings['extensions'] as $ext => $enabled) {
417
+
418
+ // Is it really enabled?
419
+ if (empty($enabled)) {
420
+ continue;
421
+ }
422
+
423
+ // Anti hack
424
+ if (!preg_match('/^[a-z0-9-_]+$/i', $ext)) {
425
+ Errors::add('Extension Loader', 'Not going to load "'.$ext.'" extension as only characters allowed in name are letters/numbers/-_');
426
+ continue;
427
+ }
428
+
429
+ // Support older config files with lowercase
430
+ if (preg_match('/^[a-z]/', $ext)) {
431
+ $ext = ucfirst($ext);
432
+ }
433
+
434
+ // Try loading our class..
435
+ try {
436
+ $reflector = new ReflectionClass('\\Linfo\\Extension\\'.$ext);
437
+ $ext_class = $reflector->newInstance($this);
438
+ } catch (ReflectionException $e) {
439
+ Errors::add('Extension Loader', 'Cannot instantiate class for "'.$ext.'" extension: '.$e->getMessage());
440
+ continue;
441
+ }
442
+
443
+ // Deal with it
444
+ $ext_class->work();
445
+
446
+ // Does this edit the $info directly, instead of creating a separate output table type thing?
447
+ if (!$reflector->hasConstant('LINFO_INTEGRATE')) {
448
+
449
+ // Result
450
+ $result = $ext_class->result();
451
+
452
+ // Save result if it's good
453
+ if ($result != false) {
454
+ $this->info['extensions'][$ext] = $result;
455
+ }
456
+ }
457
+ }
458
+ }
459
+
460
+ public function getLang()
461
+ {
462
+ return $this->lang;
463
+ }
464
+
465
+ public function getSettings()
466
+ {
467
+ return $this->settings;
468
+ }
469
+
470
+ public function getAppName()
471
+ {
472
+ return $this->app_name;
473
+ }
474
+
475
+ public function getVersion()
476
+ {
477
+ return $this->version;
478
+ }
479
+
480
+ public function getTimeStart()
481
+ {
482
+ return $this->time_start;
483
+ }
484
+
485
+ public function getParser()
486
+ {
487
+ return $this->parser;
488
+ }
489
+
490
+ public function getLocalDir()
491
+ {
492
+ return $this->linfo_localdir;
493
+ }
494
+
495
+ public function getTestDir()
496
+ {
497
+ return $this->linfo_testdir;
498
+ }
499
+
500
+ public function getCacheDir()
501
+ {
502
+ return dirname(dirname(__DIR__)).'/cache/';
503
+ }
504
+ }
lib/Linfo/Meta/Errors.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo\Meta;
20
+
21
+ /**
22
+ * Use this class for all error handling.
23
+ */
24
+ class Errors
25
+ {
26
+
27
+ /**
28
+ * Store error messages here.
29
+ *
30
+ * @var array
31
+ */
32
+ private static $errors = array();
33
+
34
+ /**
35
+ * Add an error message.
36
+ *
37
+ * @static
38
+ * @param string $whence name of error message source
39
+ * @param string $message error message text
40
+ */
41
+ public static function add($whence, $message)
42
+ {
43
+ self::$errors[] = array($whence, $message);
44
+ }
45
+
46
+ /**
47
+ * Get all error messages.
48
+ *
49
+ * @static
50
+ * @return array of errors
51
+ */
52
+ public static function show()
53
+ {
54
+ return self::$errors;
55
+ }
56
+
57
+ /**
58
+ * How many are there?
59
+ *
60
+ * @static
61
+ * @return int number of errors
62
+ */
63
+ public static function num()
64
+ {
65
+ return count(self::$errors);
66
+ }
67
+
68
+ /**
69
+ * Used mainly for unit tests.
70
+ *
71
+ * @static
72
+ */
73
+ public static function clear()
74
+ {
75
+ self::$errors = array();
76
+ }
77
+ }
lib/Linfo/Meta/Timer.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Linfo\Meta;
4
+
5
+ class Timer
6
+ {
7
+ protected static $timers = array();
8
+ protected $id = null;
9
+ protected $start = null;
10
+
11
+ public static function getResults()
12
+ {
13
+ return self::$timers;
14
+ }
15
+
16
+ public static function clear()
17
+ {
18
+ self::$timers = array();
19
+ }
20
+
21
+ public function __construct($id)
22
+ {
23
+ $this->id = $id;
24
+ $this->start = microtime(true);
25
+ }
26
+
27
+ public function __destruct()
28
+ {
29
+ $duration = microtime(true) - $this->start;
30
+ self::$timers[] = array(
31
+ 'id' => $this->id,
32
+ 'duration' => $duration,
33
+ );
34
+ }
35
+ }
lib/Linfo/OS/BSDcommon.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Linfo\Common;
24
+ use Linfo\Parsers\CallExt;
25
+ use Exception;
26
+
27
+ /*
28
+ * The BSD os's are largely similar and thus draw from this class.
29
+ */
30
+
31
+ abstract class BSDcommon extends Unixcommon
32
+ {
33
+ // Store these
34
+ protected $settings,
35
+ $exec,
36
+ $dmesg,
37
+ $sysctl = array();
38
+
39
+ // Start us off
40
+ protected function __construct($settings)
41
+ {
42
+
43
+ // Localize settings
44
+ $this->settings = $settings;
45
+
46
+ // Exec running
47
+ $this->exec = new CallExt();
48
+
49
+ // Get dmesg
50
+ $this->loadDmesg();
51
+ }
52
+
53
+ // Save dmesg
54
+ protected function loadDmesg()
55
+ {
56
+ $this->dmesg = Common::getContents('/var/run/dmesg.boot');
57
+ }
58
+
59
+ // Use sysctl to get something, and cache result.
60
+ // Also allow getting multiple keys at once, in which case sysctl
61
+ // will only be called once instead of multiple times (assuming it doesn't break)
62
+ protected function getSysCTL($keys, $do_return = true)
63
+ {
64
+
65
+ // Get the keys as an array, so we can treat it as an array of keys
66
+ $keys = (array) $keys;
67
+
68
+ // Store the results of which here
69
+ $results = array();
70
+
71
+ // Go through each
72
+ foreach ($keys as $k => $v) {
73
+ $keys[$k] = escapeshellarg($v);
74
+
75
+ // Check and see if we have any of these already. If so, use previous
76
+ // values and don't retrive them again
77
+ if (array_key_exists($v, $this->sysctl)) {
78
+ unset($keys[$k]);
79
+ $results[$v] = $this->sysctl[$v];
80
+ }
81
+ }
82
+
83
+ // Try running sysctl to get all the values together
84
+ try {
85
+ // Result of sysctl
86
+ $command = $this->exec->exec('sysctl', implode(' ', $keys));
87
+
88
+ // Place holder
89
+ $current_key = false;
90
+
91
+ // Go through each line
92
+ foreach (explode("\n", $command) as $line) {
93
+
94
+ // If this is the beginning of one of the keys' values
95
+ if (preg_match('/^([a-z0-9\.\-\_]+)\s*(?:\:|=)(.+)/', $line, $line_match) == 1) {
96
+ if ($line_match[1] != $current_key) {
97
+ $current_key = $line_match[1];
98
+ $results[$line_match[1]] = trim($line_match[2]);
99
+ }
100
+ }
101
+
102
+ // If this line is a continuation of one of the keys' values
103
+ elseif ($current_key != false) {
104
+ $results[$current_key] .= "\n".trim($line);
105
+ }
106
+ }
107
+ }
108
+
109
+ // Something broke with that sysctl call; try getting
110
+ // all the values separately (slower)
111
+ catch (Exception $e) {
112
+
113
+ // Go through each
114
+ foreach ($keys as $v) {
115
+
116
+ // Try it
117
+ try {
118
+ $results[$v] = $this->exec->exec('sysctl', $v);
119
+ }
120
+
121
+ // Didn't work again... just forget it and set value to empty string
122
+ catch (Exception $e) {
123
+ $results[$v] = '';
124
+ }
125
+ }
126
+ }
127
+
128
+ // Cache these incase they're called upon again
129
+ $this->sysctl = array_merge($results, $this->sysctl);
130
+
131
+ // Return an array of all values retrieved, or if just one was
132
+ // requested, then that one as a string
133
+ if ($do_return) {
134
+ return count($results) == 1 ? reset($results) : $results;
135
+ }
136
+ else {
137
+ return null;
138
+ }
139
+ }
140
+
141
+
142
+ // System load averages
143
+ public function getLoad()
144
+ {
145
+ if (!empty($this->settings['timer'])) {
146
+ $t = new Timer('Load Averages');
147
+ }
148
+
149
+ $parts = explode(' ', trim($this->sysctl['vm.loadavg']));
150
+
151
+ if (!$parts) {
152
+ return array();
153
+ }
154
+
155
+ return array_combine(array('now', '5min', '15min'), $parts);
156
+ }
157
+ }
lib/Linfo/OS/Darwin.php ADDED
@@ -0,0 +1,622 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010, 2012 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Meta\Timer;
25
+ use Linfo\Meta\Errors;
26
+ use Linfo\Common;
27
+
28
+ /*
29
+ * Alpha osx class
30
+ * Differs very slightly from the FreeBSD, especially in the fact that
31
+ * only root can access dmesg
32
+ */
33
+
34
+ class Darwin extends BSDcommon
35
+ {
36
+ // Encapsulate these
37
+ protected $settings,
38
+ $exec,
39
+ $dmesg;
40
+
41
+ // Start us off
42
+ public function __construct($settings)
43
+ {
44
+
45
+ // Instantiate parent
46
+ parent::__construct($settings);
47
+
48
+ // We search these folders for our commands
49
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/sbin'));
50
+
51
+ // We need these sysctl values
52
+ $this->GetSysCTL(array(
53
+ 'machdep.cpu.vendor',
54
+ 'machdep.cpu.brand_string',
55
+ 'hw.cpufrequency',
56
+ 'hw.ncpu',
57
+ 'vm.swapusage',
58
+ 'hw.memsize',
59
+ 'hw.usermem',
60
+ 'kern.boottime',
61
+ 'vm.loadavg',
62
+ 'hw.model',
63
+ ), false);
64
+
65
+ // And get this info for when the above fails
66
+ try {
67
+ $this->systemProfiler = $this->exec->exec('system_profiler', 'SPHardwareDataType SPSoftwareDataType SPPowerDataType');
68
+ } catch (Exception $e) {
69
+ // Meh
70
+ Errors::add('Linfo Mac OS 10', 'Error using system_profiler');
71
+ }
72
+ }
73
+
74
+ // What we should leave out
75
+ public function getContains()
76
+ {
77
+ return array(
78
+ 'hw_vendor' => false,
79
+ 'drives_rw_stats' => false,
80
+ 'drives_vendor' => false,
81
+ 'nic_type' => false,
82
+ 'nic_port_speed' => false,
83
+ );
84
+ }
85
+
86
+ // Operating system
87
+ public function getOS()
88
+ {
89
+ return 'Darwin ('.(preg_match('/^\s+System Version: ([^\(]+)/m', $this->systemProfiler, $m) ? $m[1] : 'Mac OS X').')';
90
+ }
91
+
92
+ // Hostname
93
+ public function getHostname()
94
+ {
95
+ return preg_match('/^\s*Computer Name:\s+(.+)\s*$/m', $this->systemProfiler, $m) ? $m[1] : php_uname('n');
96
+ }
97
+
98
+ // Get mounted file systems
99
+ public function getMounts()
100
+ {
101
+ // Time?
102
+ if (!empty($this->settings['timer'])) {
103
+ $t = new Timer('Mounted file systems');
104
+ }
105
+
106
+ // Get result of mount command
107
+ try {
108
+ $res = $this->exec->exec('mount');
109
+ } catch (Exception $e) {
110
+ Errors::add('Linfo Core', 'Error running `mount` command');
111
+
112
+ return array();
113
+ }
114
+
115
+ // Parse it
116
+ if (preg_match_all('/(.+)\s+on\s+(.+)\s+\((\w+).*\)\n/i', $res, $m, PREG_SET_ORDER) == 0) {
117
+ return array();
118
+ }
119
+
120
+ // Store them here
121
+ $mounts = array();
122
+
123
+ // Deal with each entry
124
+ foreach ($m as $mount) {
125
+
126
+ // Should we not show this?
127
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
128
+ continue;
129
+ }
130
+
131
+ // Get these
132
+ $size = @disk_total_space($mount[2]);
133
+ $free = @disk_free_space($mount[2]);
134
+ $used = $size - $free;
135
+
136
+ // Might be good, go for it
137
+ $mounts[] = array(
138
+ 'device' => $mount[1],
139
+ 'mount' => $mount[2],
140
+ 'type' => $mount[3],
141
+ 'size' => $size ,
142
+ 'used' => $used,
143
+ 'free' => $free,
144
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
145
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
146
+ );
147
+ }
148
+
149
+ // Give it
150
+ return $mounts;
151
+ }
152
+
153
+ // Get network interfaces
154
+ public function getNet()
155
+ {
156
+ // Time?
157
+ if (!empty($this->settings['timer'])) {
158
+ $t = new Timer('Network Devices');
159
+ }
160
+
161
+ // Store return vals here
162
+ $return = array();
163
+
164
+ // Use netstat to get info
165
+ try {
166
+ $netstat = $this->exec->exec('netstat', '-nbdi');
167
+ } catch (Exception $e) {
168
+ Errors::add('Linfo Core', 'Error using `netstat` to get network info');
169
+
170
+ return $return;
171
+ }
172
+
173
+ // Initially get interfaces themselves along with numerical stats
174
+ //
175
+ // Example output:
176
+ // Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll Drop
177
+ // lo0 16384 <Link#1> 1945 0 429565 1945 0 429565 0
178
+ // en0 1500 <Link#4> 58:b0:35:f9:fd:2b 0 0 0 0 0 59166 0
179
+ // fw0 4078 <Link#6> d8:30:62:ff:fe:f5:c8:9c 0 0 0 0 0 346 0
180
+ if (preg_match_all(
181
+ '/^
182
+ ([a-z0-9*]+)\s* # Name
183
+ \w+\s+ # Mtu
184
+ <Link\#\w+> # Network
185
+ (?:\D+|\s+\w+:\w+:\w+:\w+:\w+:\w+\s+) # MAC address
186
+ (\w+)\s+ # Ipkts
187
+ (\w+)\s+ # Ierrs
188
+ (\w+)\s+ # Ibytes
189
+ (\w+)\s+ # Opkts
190
+ (\w+)\s+ # Oerrs
191
+ (\w+)\s+ # Obytes
192
+ (\w+)\s+ # Coll
193
+ (\w+)?\s* # Drop
194
+ $/mx', $netstat, $netstat_match, PREG_SET_ORDER) == 0) {
195
+ return $return;
196
+ }
197
+
198
+ // Try using ifconfig to get states of the network interfaces
199
+ $statuses = array();
200
+ try {
201
+ // Output of ifconfig command
202
+ $ifconfig = $this->exec->exec('ifconfig', '-a');
203
+
204
+ // Set this to false to prevent wasted regexes
205
+ $current_nic = false;
206
+
207
+ // Go through each line
208
+ foreach ((array) explode("\n", $ifconfig) as $line) {
209
+
210
+ // Approachign new nic def
211
+ if (preg_match('/^(\w+):/', $line, $m) == 1) {
212
+ $current_nic = $m[1];
213
+ }
214
+
215
+ // Hopefully match its status
216
+ elseif ($current_nic && preg_match('/status: (\w+)$/', $line, $m) == 1) {
217
+ $statuses[$current_nic] = $m[1];
218
+ $current_nic = false;
219
+ }
220
+ }
221
+ } catch (Exception $e) {
222
+ }
223
+
224
+ // Save info
225
+ foreach ($netstat_match as $net) {
226
+
227
+ // Determine status
228
+ switch (array_key_exists($net[1], $statuses) ? $statuses[$net[1]] : 'unknown') {
229
+
230
+ case 'active':
231
+ $state = 'up';
232
+ break;
233
+
234
+ case 'inactive':
235
+ $state = 'down';
236
+ break;
237
+
238
+ default:
239
+ $state = 'unknown';
240
+ break;
241
+ }
242
+
243
+ // Save info
244
+ $return[$net[1]] = array(
245
+
246
+ // These came from netstat
247
+ 'recieved' => array(
248
+ 'bytes' => $net[4],
249
+ 'errors' => $net[3],
250
+ 'packets' => $net[2],
251
+ ),
252
+ 'sent' => array(
253
+ 'bytes' => $net[7],
254
+ 'errors' => $net[6],
255
+ 'packets' => $net[5],
256
+ ),
257
+
258
+ // This came from ifconfig -a
259
+ 'state' => $state,
260
+
261
+ // Not sure where to get his
262
+ 'type' => '?',
263
+ );
264
+ }
265
+
266
+ // Return it
267
+ return $return;
268
+ }
269
+
270
+ // Get uptime
271
+ public function getUpTime()
272
+ {
273
+
274
+ // Time?
275
+ if (!empty($this->settings['timer'])) {
276
+ $t = new Timer('Uptime');
277
+ }
278
+
279
+ // Extract boot part of it
280
+ if (preg_match('/^\{ sec \= (\d+).+$/', $this->sysctl['kern.boottime'], $m) == 0) {
281
+ return '';
282
+ }
283
+
284
+ return array(
285
+ 'text' => Common::secondsConvert(time() - $m[1]),
286
+ 'bootedTimestamp' => $m[1],
287
+ );
288
+ }
289
+
290
+ // Get stats on processes
291
+ public function getProcessStats()
292
+ {
293
+
294
+ // Time?
295
+ if (!empty($this->settings['timer'])) {
296
+ $t = new Timer('Process Stats');
297
+ }
298
+
299
+ // We'll return this after stuffing it with useful info
300
+ $result = array(
301
+ 'exists' => true,
302
+ 'totals' => array(
303
+ 'running' => 0,
304
+ 'zombie' => 0,
305
+ 'sleeping' => 0,
306
+ 'stopped' => 0,
307
+ 'idle' => 0,
308
+ ),
309
+ 'proc_total' => 0,
310
+ 'threads' => false, // I'm not sure how to get this
311
+ );
312
+
313
+ // Use ps
314
+ try {
315
+ // Get it
316
+ $ps = $this->exec->exec('ps', 'ax');
317
+
318
+ // Match them
319
+ preg_match_all('/^\s*\d+\s+[\w?]+\s+([A-Z])\S*\s+.+$/m', $ps, $processes, PREG_SET_ORDER);
320
+
321
+ // Get total
322
+ $result['proc_total'] = count($processes);
323
+
324
+ // Go through
325
+ foreach ($processes as $process) {
326
+ switch ($process[1]) {
327
+ case 'S':
328
+ case 'I':
329
+ $result['totals']['sleeping']++;
330
+ break;
331
+ case 'Z':
332
+ $result['totals']['zombie']++;
333
+ break;
334
+ case 'R':
335
+ case 'D':
336
+ $result['totals']['running']++;
337
+ break;
338
+ case 'T':
339
+ $result['totals']['stopped']++;
340
+ break;
341
+ case 'W':
342
+ $result['totals']['idle']++;
343
+ break;
344
+ }
345
+ }
346
+ } catch (Exception $e) {
347
+ Errors::add('Linfo Core', 'Error using `ps` to get process info');
348
+ }
349
+
350
+ // Give
351
+ return $result;
352
+ }
353
+
354
+ // Get cpus
355
+ public function getCPU()
356
+ {
357
+ // Time?
358
+ if (!empty($this->settings['timer'])) {
359
+ $t = new Timer('CPUs');
360
+ }
361
+
362
+ // Was machdep mean to us? Likely on ppc macs
363
+ if (empty($this->sysctl['machdep.cpu.brand_string']) && preg_match('/^\s+Processor Name:\s+(.+)(?= \([\d\.]+\))/m', $this->systemProfiler, $m)) {
364
+ $this->sysctl['machdep.cpu.brand_string'] = $m[1];
365
+ }
366
+
367
+ if (empty($this->sysctl['machdep.cpu.vendor'])) {
368
+ $this->sysctl['machdep.cpu.vendor'] = false;
369
+ }
370
+
371
+ // Store them here
372
+ $cpus = array();
373
+
374
+ // The same one multiple times
375
+ for ($i = 0; $i < $this->sysctl['hw.ncpu']; ++$i) {
376
+ $cpus[] = array(
377
+ 'Model' => $this->sysctl['machdep.cpu.brand_string'],
378
+ 'MHz' => $this->sysctl['hw.cpufrequency'] / 1000000,
379
+ 'Vendor' => $this->sysctl['machdep.cpu.vendor'],
380
+
381
+ );
382
+ }
383
+
384
+ return $cpus;
385
+ }
386
+
387
+ // Get ram usage
388
+ public function getRam()
389
+ {
390
+
391
+ // Time?
392
+ if (!empty($this->settings['timer'])) {
393
+ $t = new Timer('Memory');
394
+ }
395
+
396
+ // Start us off
397
+ $return = array();
398
+ $return['type'] = 'Physical';
399
+ $return['total'] = $this->sysctl['hw.memsize'];
400
+ $return['free'] = $this->sysctl['hw.memsize'] - $this->sysctl['hw.usermem'];
401
+ $return['swapTotal'] = 0;
402
+ $return['swapFree'] = 0;
403
+ $return['swapInfo'] = array();
404
+
405
+ // Sort out swap
406
+ if (preg_match('/total = ([\d\.]+)M\s+used = ([\d\.]+)M\s+free = ([\d\.]+)M/', $this->sysctl['vm.swapusage'], $swap_match)) {
407
+ list(, $swap_total, $swap_used, $swap_free) = $swap_match;
408
+ $return['swapTotal'] = $swap_total * 1000000;
409
+ $return['swapFree'] = $swap_free * 1000000;
410
+ }
411
+
412
+ // Return ram info
413
+ return $return;
414
+ }
415
+
416
+ // Model of mac
417
+ public function getModel()
418
+ {
419
+ if (preg_match('/^\s+Model Name:\s+(.+)/m', $this->systemProfiler, $m)) {
420
+ return $m[1];
421
+ }
422
+
423
+ if (preg_match('/^([a-zA-Z]+)/', $this->sysctl['hw.model'], $m)) {
424
+ return $m[1];
425
+ } else {
426
+ return $this->sysctl['hw.model'];
427
+ }
428
+ }
429
+
430
+ // Battery
431
+ public function getBattery()
432
+ {
433
+ // Time?
434
+ if (!empty($this->settings['timer'])) {
435
+ $t = new Timer('Battery');
436
+ }
437
+
438
+ // Store any we find here
439
+ $batteries = array();
440
+
441
+ // Lines
442
+ $lines = explode("\n", $this->systemProfiler);
443
+
444
+ // Hunt
445
+ $bat = array();
446
+ $in_bat_field = false;
447
+
448
+ foreach ($lines as $line) {
449
+ if (preg_match('/^\s+Battery Information/', $line)) {
450
+ $in_bat_field = true;
451
+ continue;
452
+ } elseif (preg_match('/^\s+System Power Settings/', $line)) {
453
+ $in_bat_field = false;
454
+ break;
455
+ } elseif ($in_bat_field && preg_match('/^\s+Fully charged: ([a-zA-Z]+)/i', $line, $m)) {
456
+ $bat['charged'] = $m[1] == 'Yes';
457
+ } elseif ($in_bat_field && preg_match('/^\s+Charging: ([a-zA-Z]+)/i', $line, $m)) {
458
+ $bat['charging'] = $m[1] == 'Yes';
459
+ } elseif ($in_bat_field && preg_match('/^\s+Charge remaining \(mAh\): (\d+)/i', $line, $m)) {
460
+ $bat['charge_now'] = (int) $m[1];
461
+ } elseif ($in_bat_field && preg_match('/^\s+Full charge capacity \(mAh\): (\d+)/i', $line, $m)) {
462
+ $bat['charge_full'] = (int) $m[1];
463
+ } elseif ($in_bat_field && preg_match('/^\s+Serial Number: ([A-Z0-9]+)/i', $line, $m)) {
464
+ $bat['serial'] = $m[1];
465
+ } elseif ($in_bat_field && preg_match('/^\s+Manufacturer: (\w+)/i', $line, $m)) {
466
+ $bat['vendor'] = $m[1];
467
+ } elseif ($in_bat_field && preg_match('/^\s+Device name: (\w+)/i', $line, $m)) {
468
+ $bat['name'] = $m[1];
469
+ }
470
+ }
471
+
472
+ // If we have what we need, append
473
+ if (isset($bat['charge_full']) && isset($bat['charge_now']) && isset($bat['charged']) && isset($bat['charging'])) {
474
+ $batteries[] = array(
475
+ 'charge_full' => $bat['charge_full'],
476
+ 'charge_now' => $bat['charge_now'],
477
+ 'percentage' => $bat['charge_full'] > 0 && $bat['charge_now'] > 0 ? round($bat['charge_now'] / $bat['charge_full'], 4) * 100 .'%' : '?',
478
+ 'device' => $bat['vendor'].' - '.$bat['name'],
479
+ 'state' => $bat['charging'] ? 'Charging' : ($bat['charged'] ? 'Fully Charged' : 'Discharging'),
480
+ );
481
+ }
482
+
483
+ // Give
484
+ return $batteries;
485
+ }
486
+
487
+ // drives
488
+ public function getHD()
489
+ {
490
+ // Time?
491
+ if (!empty($this->settings['timer'])) {
492
+ $t = new Timer('Drives');
493
+ }
494
+
495
+ // Use system profiler to get info
496
+ try {
497
+ $res = $this->exec->exec('diskutil', ' list');
498
+ } catch (Exception $e) {
499
+ Errors::add('Linfo drives', 'Error using `diskutil list` to get drives');
500
+
501
+ return array();
502
+ }
503
+
504
+ // Get it into lines
505
+ $lines = explode("\n", $res);
506
+
507
+ // Keep drives here
508
+ $drives = array();
509
+
510
+ // Work on tmp drive here
511
+ $tmp = false;
512
+
513
+ for ($i = 0, $num_lines = count($lines); $i < $num_lines; ++$i) {
514
+
515
+ // A drive or partition entry
516
+ if (preg_match('/^\s+(\d+):\s+([a-zA-Z0-9\_]+)\s+([\s\w]*) \*?(\d+(?:\.\d+)? [A-Z])B\s+([a-z0-9]+)/', $lines[$i], $m)) {
517
+
518
+ // Get size sorted out
519
+ $size_parts = explode(' ', $m[4]);
520
+ switch ($size_parts[1]) {
521
+ case 'K':
522
+ $size = $size_parts[0] * 1000;
523
+ break;
524
+ case 'M':
525
+ $size = $size_parts[0] * 1000000;
526
+ break;
527
+ case 'G':
528
+ $size = $size_parts[0] * 1000000000;
529
+ break;
530
+ case 'T':
531
+ $size = $size_parts[0] * 1000000000000;
532
+ break;
533
+ case 'P':
534
+ $size = $size_parts[0] * 1000000000000000;
535
+ break;
536
+ default:
537
+ $size = false;
538
+ break;
539
+ }
540
+
541
+ // A drive?
542
+ if ($m[1] == 0) {
543
+
544
+ // Finish prior drive
545
+ if (is_array($tmp)) {
546
+ $drives[] = $tmp;
547
+ }
548
+
549
+ // Try getting the name
550
+ $drive_name = false; // I'm pessimistic
551
+ try {
552
+ $drive_res = $this->exec->exec('diskutil', ' info /dev/'.$m[5]);
553
+ if (preg_match('/^\s+Device \/ Media Name:\s+(.+)/m', $drive_res, $drive_m)) {
554
+ $drive_name = $drive_m[1];
555
+ }
556
+ } catch (Exception $e) {
557
+ }
558
+
559
+ // Start this one off
560
+ $tmp = array(
561
+ 'name' => $drive_name,
562
+ 'vendor' => 'Unknown',
563
+ 'device' => '/dev/'.$m[5],
564
+ 'reads' => false,
565
+ 'writes' => false,
566
+ 'size' => $size,
567
+ 'partitions' => array(),
568
+ );
569
+ }
570
+
571
+ // Or a partition
572
+ elseif ($m[1] > 0) {
573
+
574
+ // Save it
575
+ $tmp['partitions'][] = array(
576
+ 'size' => $size,
577
+ 'name' => '/dev/'.$m[5],
578
+ );
579
+ }
580
+ }
581
+ }
582
+
583
+ // Save a drive
584
+ if (is_array($tmp)) {
585
+ $drives[] = $tmp;
586
+ }
587
+
588
+ // Give
589
+ return $drives;
590
+ }
591
+
592
+ public function getVirtualization()
593
+ {
594
+
595
+ // Time?
596
+ if (!empty($this->settings['timer'])) {
597
+ $t = new Timer('Determining virtualization type');
598
+ }
599
+
600
+ // All results on google show this file only being present and related to VMware Fusion
601
+ if (file_exists('/dev/vmmon')) {
602
+ return array('type' => 'host', 'method' => 'VMWare');
603
+ }
604
+
605
+ return false;
606
+ }
607
+
608
+ public function getLoad()
609
+ {
610
+ if (!empty($this->settings['timer'])) {
611
+ $t = new Timer('Load Averages');
612
+ }
613
+
614
+ $loads = $this->sysctl['vm.loadavg'];
615
+
616
+ if (preg_match('/([\d\.]+) ([\d\.]+) ([\d\.]+)/', $loads, $m)) {
617
+ return array_combine(array('now', '5min', '15min'), array_slice($m, 1, 3));
618
+ } else {
619
+ return array();
620
+ }
621
+ }
622
+ }
lib/Linfo/OS/DragonFly.php ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2011 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Meta\Timer;
25
+ use Linfo\Meta\Errors;
26
+ use Linfo\Common;
27
+ use Linfo\Parsers\Hwpci;
28
+
29
+ class DragonFly extends BSDcommon
30
+ {
31
+ // Encapsulate these
32
+ protected $settings,
33
+ $exec,
34
+ $dmesg;
35
+
36
+ // Start us off
37
+ public function __construct($settings)
38
+ {
39
+
40
+ // Initiate parent
41
+ parent::__construct($settings);
42
+
43
+ // We search these folders for our commands
44
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
45
+
46
+ // sysctl values we'll access below
47
+ $this->GetSysCTL(array(
48
+ 'kern.boottime',
49
+ 'vm.loadavg',
50
+ 'hw.model',
51
+ 'hw.ncpu',
52
+ 'hw.clockrate',
53
+ ), false);
54
+ }
55
+
56
+ // What we should leave out
57
+ public function getContains()
58
+ {
59
+ return array(
60
+ 'drives_rw_stats' => false,
61
+ 'nic_type' => false,
62
+ );
63
+ }
64
+
65
+ // Return OS type
66
+ public function getOS()
67
+ {
68
+
69
+ // Obviously
70
+ return 'DragonFly BSD';
71
+ }
72
+
73
+ // Get mounted file systems
74
+ public function getMounts()
75
+ {
76
+
77
+ // Time?
78
+ if (!empty($this->settings['timer'])) {
79
+ $t = new Timer('Mounted file systems');
80
+ }
81
+
82
+ // Get result of mount command
83
+ try {
84
+ $res = $this->exec->exec('mount');
85
+ } catch (Exception $e) {
86
+ Errors::add('Linfo Core', 'Error running `mount` command');
87
+
88
+ return array();
89
+ }
90
+
91
+ // Parse it
92
+ if (preg_match_all('/^(\S+) on (\S+) \((\w+)(?:, (.+))?\)/m', $res, $m, PREG_SET_ORDER) == 0) {
93
+ return array();
94
+ }
95
+
96
+ // Store them here
97
+ $mounts = array();
98
+
99
+ // Deal with each entry
100
+ foreach ($m as $mount) {
101
+
102
+ // Should we not show this?
103
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
104
+ continue;
105
+ }
106
+
107
+ // Get these
108
+ $size = @disk_total_space($mount[2]);
109
+ $free = @disk_free_space($mount[2]);
110
+ $used = $size - $free;
111
+
112
+ // Optionally get mount options
113
+ if (
114
+ $this->settings['show']['mounts_options'] &&
115
+ !in_array($mount[3], (array) $this->settings['hide']['fs_mount_options']) &&
116
+ isset($mount[4])
117
+ ) {
118
+ $mount_options = explode(', ', $mount[4]);
119
+ } else {
120
+ $mount_options = array();
121
+ }
122
+
123
+ // Might be good, go for it
124
+ $mounts[] = array(
125
+ 'device' => $mount[1],
126
+ 'mount' => $mount[2],
127
+ 'type' => $mount[3],
128
+ 'size' => $size ,
129
+ 'used' => $used,
130
+ 'free' => $free,
131
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
132
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
133
+ 'options' => $mount_options,
134
+ );
135
+ }
136
+
137
+ // Give it
138
+ return $mounts;
139
+ }
140
+
141
+ // Get ram usage
142
+ public function getRam()
143
+ {
144
+
145
+ // Time?
146
+ if (!empty($this->settings['timer'])) {
147
+ $t = new Timer('Memory');
148
+ }
149
+
150
+ // We'll return the contents of this
151
+ $return = array();
152
+
153
+ // Start us off at zilch
154
+ $return['type'] = 'Virtual';
155
+ $return['total'] = 0;
156
+ $return['free'] = 0;
157
+ $return['swapTotal'] = 0;
158
+ $return['swapFree'] = 0;
159
+ $return['swapInfo'] = array();
160
+
161
+ // Get swap
162
+
163
+ // Return it
164
+ return $return;
165
+ }
166
+
167
+ // Get uptime
168
+ public function getUpTime()
169
+ {
170
+
171
+ // Time?
172
+ if (!empty($this->settings['timer'])) {
173
+ $t = new Timer('Uptime');
174
+ }
175
+
176
+ // Use sysctl to get unix timestamp of boot. Very elegant!
177
+ if (preg_match('/^\{ sec \= (\d+).+$/', $this->sysctl['kern.boottime'], $m) == 0) {
178
+ return '';
179
+ }
180
+
181
+ // Boot unix timestamp
182
+ $booted = $m[1];
183
+
184
+ // Get it textual, as in days/minutes/hours/etc
185
+ return array(
186
+ 'text' => Common::secondsConvert(time() - $booted),
187
+ 'bootedTimestamp' => $booted,
188
+ );
189
+ }
190
+
191
+ // RAID Stats
192
+ public function getRAID()
193
+ {
194
+ }
195
+
196
+ // Done
197
+ public function getNet()
198
+ {
199
+
200
+ // Time?
201
+ if (!empty($this->settings['timer'])) {
202
+ $t = new Timer('Network Devices');
203
+ }
204
+
205
+ // Use netstat to get nic names and stats
206
+ try {
207
+ $netstat = $this->exec->exec('netstat', '-nibd');
208
+ } catch (Exception $e) {
209
+ Errors::add('Linfo Core', 'error using netstat');
210
+
211
+ return array();
212
+ }
213
+
214
+ // Store nics here
215
+ $nets = array();
216
+
217
+ // Match that up
218
+ if (!preg_match_all('/^([\da-z]+\*?)\s+\d+\s+<Link#\d+>(?:\s+[a-z0-9:]+)?\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/m', $netstat, $netstat_m, PREG_SET_ORDER)) {
219
+ return array();
220
+ }
221
+
222
+ // Go through each match
223
+ foreach ($netstat_m as $m) {
224
+ $nets[$m[1]] = array(
225
+ 'recieved' => array(
226
+ 'bytes' => $m[4],
227
+ 'errors' => $m[3],
228
+ 'packets' => $m[2],
229
+ ),
230
+ 'sent' => array(
231
+ 'bytes' => $m[7],
232
+ 'errors' => $m[6],
233
+ 'packets' => $m[5],
234
+ ),
235
+ 'state' => 'unknown',
236
+ 'type' => 'N/A',
237
+ );
238
+ }
239
+
240
+ // Try getting the statuses with ifconfig
241
+ try {
242
+
243
+ // Store current nic here
244
+ $current_nic = false;
245
+
246
+ $ifconfig = $this->exec->exec('ifconfig', '-a');
247
+
248
+ // Go through each line
249
+ foreach (explode("\n", $ifconfig) as $line) {
250
+
251
+ // Approaching new nic?
252
+ if (preg_match('/^([a-z0-9]+):/', $line, $m)) {
253
+ if (array_key_exists($m[1], $nets)) {
254
+ $current_nic = $m[1];
255
+ } else {
256
+ $current_nic = false;
257
+ }
258
+ }
259
+
260
+ // In a nick and found a status entry
261
+ elseif ($current_nic && preg_match('/^\s+status: ([^$]+)$/', $line, $m)) {
262
+
263
+ // Decide what it is and save it
264
+ switch ($m[1]) {
265
+ case 'active':
266
+ $nets[$current_nic]['state'] = 'up';
267
+ break;
268
+ case 'inactive':
269
+ case 'no carrier':
270
+ $nets[$current_nic]['state'] = 'down';
271
+ break;
272
+ default:
273
+ $nets[$current_nic]['state'] = 'unknown';
274
+ break;
275
+ }
276
+
277
+ // Don't waste further time until we find another nic entry
278
+ $current_nic = false;
279
+ }
280
+ }
281
+ } catch (Exception $e) {
282
+ Errors::add('Linfo Core', 'error using ifconfig to get nic statuses');
283
+ }
284
+
285
+ // Give nets
286
+ return $nets;
287
+ }
288
+
289
+ // Get CPU's
290
+ // I still don't really like how this is done
291
+ public function getCPU()
292
+ {
293
+
294
+ // Time?
295
+ if (!empty($this->settings['timer'])) {
296
+ $t = new Timer('CPUs');
297
+ }
298
+
299
+ // Store them here
300
+ $cpus = array();
301
+
302
+ // Stuff it with identical cpus
303
+ for ($i = 0; $i < $this->sysctl['hw.ncpu']; ++$i) {
304
+
305
+ // Save each
306
+ $cpus[] = array(
307
+ 'Model' => $this->sysctl['hw.model'],
308
+ 'MHz' => $this->sysctl['hw.clockrate'],
309
+ );
310
+ }
311
+
312
+ // Return
313
+ return $cpus;
314
+ }
315
+
316
+ // Parse dmesg boot log
317
+ public function getDevs()
318
+ {
319
+
320
+ // Time?
321
+ if (!empty($this->settings['timer'])) {
322
+ $t = new Timer('Hardware Devices');
323
+ }
324
+
325
+ $hw = new Hwpci(null, '/usr/share/misc/pci_vendors');
326
+ $hw->work('dragonfly');
327
+
328
+ return $hw->result();
329
+ }
330
+
331
+ // APM? Seems to only support either one battery of them all collectively
332
+ public function getBattery()
333
+ {
334
+
335
+ // Time?
336
+ if (!empty($this->settings['timer'])) {
337
+ $t = new Timer('Batteries');
338
+ }
339
+
340
+ return array();
341
+ }
342
+
343
+ // Get stats on processes
344
+ public function getProcessStats()
345
+ {
346
+
347
+ // Time?
348
+ if (!empty($this->settings['timer'])) {
349
+ $t = new Timer('Process Stats');
350
+ }
351
+
352
+ // We'll return this after stuffing it with useful info
353
+ $result = array(
354
+ 'exists' => true,
355
+ 'totals' => array(
356
+ 'running' => 0,
357
+ 'zombie' => 0,
358
+ 'sleeping' => 0,
359
+ 'stopped' => 0,
360
+ 'idle' => 0,
361
+ ),
362
+ 'proc_total' => 0,
363
+ 'threads' => false, // I'm not sure how to get this
364
+ );
365
+
366
+ // Use ps
367
+ try {
368
+ // Get it
369
+ $ps = $this->exec->exec('ps', 'ax');
370
+
371
+ // Match them
372
+ preg_match_all('/^\s*\d+\s+[\w?]+\s+([A-Z])\S*\s+.+$/m', $ps, $processes, PREG_SET_ORDER);
373
+
374
+ // Get total
375
+ $result['proc_total'] = count($processes);
376
+
377
+ // Go through
378
+ foreach ($processes as $process) {
379
+ switch ($process[1]) {
380
+ case 'S':
381
+ case 'I':
382
+ $result['totals']['sleeping']++;
383
+ break;
384
+ case 'Z':
385
+ $result['totals']['zombie']++;
386
+ break;
387
+ case 'R':
388
+ case 'D':
389
+ $result['totals']['running']++;
390
+ break;
391
+ case 'T':
392
+ $result['totals']['stopped']++;
393
+ break;
394
+ case 'W':
395
+ $result['totals']['idle']++;
396
+ break;
397
+ }
398
+ }
399
+ } catch (Exception $e) {
400
+ Errors::add('Linfo Core', 'Error using `ps` to get process info');
401
+ }
402
+
403
+ // Give
404
+ return $result;
405
+ }
406
+
407
+ // idk
408
+ public function getTemps()
409
+ {
410
+ // Time?
411
+ if (!empty($this->settings['timer'])) {
412
+ $t = new Timer('Temperature');
413
+ }
414
+ }
415
+ }
lib/Linfo/OS/FreeBSD.php ADDED
@@ -0,0 +1,729 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Meta\Timer;
25
+ use Linfo\Meta\Errors;
26
+ use Linfo\Common;
27
+ use Linfo\Parsers\Hwpci;
28
+
29
+ /*
30
+ * Mostly complete FreeBSD info class.
31
+ *
32
+ * Note: When Linux compatibility is enabled and /proc is mounted, it only
33
+ * contains process info; none of the hardware/system/network status that Linux /proc has.
34
+ */
35
+
36
+ class FreeBSD extends BSDcommon
37
+ {
38
+ // Encapsulate these
39
+ protected $settings,
40
+ $exec,
41
+ $dmesg,
42
+ $version;
43
+
44
+ // Start us off
45
+ public function __construct($settings)
46
+ {
47
+
48
+ // Initiate parent
49
+ parent::__construct($settings);
50
+
51
+ // We search these folders for our commands
52
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
53
+
54
+ // sysctl values we'll access below
55
+ $this->GetSysCTL(array(
56
+
57
+ // Has unix timestamp of boot time
58
+ 'kern.boottime',
59
+
60
+ // Ram stuff
61
+ 'vm.vmtotal',
62
+ 'vm.loadavg',
63
+
64
+ // CPU related
65
+ 'hw.model',
66
+ 'hw.ncpu',
67
+ 'hw.clockrate',
68
+ ), false);
69
+
70
+ // Save version
71
+ if (preg_match('/^([\d\.]+)/', php_uname('r'), $vm) != 0) {
72
+ $this->version = (float) $vm[1];
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Return a list of things to hide from view..
78
+ *
79
+ * @return array
80
+ */
81
+ public function getContains()
82
+ {
83
+ return array(
84
+ 'drives_rw_stats' => false,
85
+ 'nic_port_speed' => false,
86
+ );
87
+ }
88
+
89
+ // Get mounted file systems
90
+ public function getMounts()
91
+ {
92
+
93
+ // Time?
94
+ if (!empty($this->settings['timer'])) {
95
+ $t = new Timer('Mounted file systems');
96
+ }
97
+
98
+ // Get result of mount command
99
+ try {
100
+ $res = $this->exec->exec('mount');
101
+ } catch (Exception $e) {
102
+ Errors::add('Linfo Core', 'Error running `mount` command');
103
+
104
+ return array();
105
+ }
106
+
107
+ // Parse it
108
+ if (preg_match_all('/^(\S+) on (\S+) \((\w+)(?:, (.+))?\)/m', $res, $m, PREG_SET_ORDER) == 0) {
109
+ return array();
110
+ }
111
+
112
+ // Store them here
113
+ $mounts = array();
114
+
115
+ // Deal with each entry
116
+ foreach ($m as $mount) {
117
+
118
+ // Should we not show this?
119
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
120
+ continue;
121
+ }
122
+
123
+ // Get these
124
+ $size = @disk_total_space($mount[2]);
125
+ $free = @disk_free_space($mount[2]);
126
+ $used = $size - $free;
127
+
128
+ // Optionally get mount options
129
+ if (
130
+ $this->settings['show']['mounts_options'] &&
131
+ !in_array($mount[3], (array) $this->settings['hide']['fs_mount_options']) &&
132
+ isset($mount[4])
133
+ ) {
134
+ $mount_options = explode(', ', $mount[4]);
135
+ } else {
136
+ $mount_options = array();
137
+ }
138
+
139
+ // Might be good, go for it
140
+ $mounts[] = array(
141
+ 'device' => $mount[1],
142
+ 'mount' => $mount[2],
143
+ 'type' => $mount[3],
144
+ 'size' => $size ,
145
+ 'used' => $used,
146
+ 'free' => $free,
147
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
148
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
149
+ 'options' => $mount_options,
150
+ );
151
+ }
152
+
153
+ // Give it
154
+ return $mounts;
155
+ }
156
+
157
+ // Get ram usage
158
+ public function getRam()
159
+ {
160
+
161
+ // Time?
162
+ if (!empty($this->settings['timer'])) {
163
+ $t = new Timer('Memory');
164
+ }
165
+
166
+ // We'll return the contents of this
167
+ $return = array();
168
+
169
+ // Start us off at zilch
170
+ $return['type'] = 'Virtual';
171
+ $return['total'] = 0;
172
+ $return['free'] = 0;
173
+ $return['swapTotal'] = 0;
174
+ $return['swapFree'] = 0;
175
+ $return['swapInfo'] = array();
176
+
177
+ // Parse the vm.vmtotal sysctl entry
178
+ if (!preg_match_all('/([a-z\ ]+):\s*\(Total: (\d+)\w,? Active:? (\d+)\w\)\n/i', $this->sysctl['vm.vmtotal'], $rm, PREG_SET_ORDER)) {
179
+ return $return;
180
+ }
181
+
182
+ // Parse each entry
183
+ foreach ($rm as $r) {
184
+ if ($r[1] == 'Real Memory') {
185
+ $return['total'] = $r[2] * 1024;
186
+ $return['free'] = ($r[2] - $r[3]) * 1024;
187
+ }
188
+ }
189
+
190
+ // Swap info
191
+ try {
192
+ $swapinfo = $this->exec->exec('swapinfo', '-k');
193
+ // Parse swap info
194
+ @preg_match_all('/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)/m', $swapinfo, $sm, PREG_SET_ORDER);
195
+ foreach ($sm as $swap) {
196
+ $return['swapTotal'] += $swap[2] * 1024;
197
+ $return['swapFree'] += (($swap[2] - $swap[3]) * 1024);
198
+ $ft = @filetype($swap[1]); // TODO: I'd rather it be Partition or File
199
+ $return['swapInfo'][] = array(
200
+ 'device' => $swap[1],
201
+ 'size' => $swap[2] * 1024,
202
+ 'used' => $swap[3] * 1024,
203
+ 'type' => ucfirst($ft),
204
+ );
205
+ }
206
+ } catch (Exception $e) {
207
+ Errors::add('Linfo Core', 'Error using `swapinfo` to get swap usage');
208
+ // meh
209
+ }
210
+
211
+ // Return it
212
+ return $return;
213
+ }
214
+
215
+ // Get uptime
216
+ public function getUpTime()
217
+ {
218
+
219
+ // Time?
220
+ if (!empty($this->settings['timer'])) {
221
+ $t = new Timer('Uptime');
222
+ }
223
+
224
+ // Use sysctl to get unix timestamp of boot. Very elegant!
225
+ if (preg_match('/^\{ sec \= (\d+).+$/', $this->sysctl['kern.boottime'], $m) == 0) {
226
+ return '';
227
+ }
228
+
229
+ // Boot unix timestamp
230
+ $booted = $m[1];
231
+
232
+ // Give it
233
+ return array(
234
+ 'text' => Common::secondsConvert(time() - $booted),
235
+ 'bootedTimestamp' => $booted,
236
+ );
237
+ }
238
+
239
+ // RAID Stats
240
+ public function getRAID()
241
+ {
242
+
243
+ // Time?
244
+ if (!empty($this->settings['timer'])) {
245
+ $t = new Timer('RAID');
246
+ }
247
+
248
+ // Store raid arrays here
249
+ $return = array();
250
+
251
+ // Counter for each raid array
252
+ $i = 0;
253
+
254
+ // Gmirror?
255
+ if (array_key_exists('gmirror', $this->settings['raid']) && !empty($this->settings['raid']['gmirror'])) {
256
+ try {
257
+ // Run gmirror status program to get raid array status
258
+ $res = $this->exec->exec('gmirror', 'status');
259
+
260
+ // Divide that into lines
261
+ $lines = explode("\n", $res);
262
+
263
+ // First is worthless
264
+ unset($lines[0]);
265
+
266
+ // Parse the remaining ones
267
+ foreach ($lines as $line => $content) {
268
+
269
+ // Hitting a new raid definition
270
+ if (preg_match('/^(\w+)\/(\w+)\s+(\w+)\s+(\w+)$/', $content, $m)) {
271
+ ++$i;
272
+
273
+ switch ($m[1]) {
274
+ case 'mirror':
275
+ $m[1] = 1;
276
+ break;
277
+ case 'stripe':
278
+ $m[1] = 0;
279
+ break;
280
+ default:
281
+ $m[1] = 'unknown';
282
+ break;
283
+ }
284
+
285
+ switch ($m[3]) {
286
+ case 'COMPLETE':
287
+ $m[3] = 'normal';
288
+ break;
289
+ case 'DEGRADED':
290
+ $m[3] = 'failed';
291
+ break;
292
+ default:
293
+ $m[3] = 'unknown';
294
+ break;
295
+ }
296
+
297
+ // Save result set
298
+ $return[$i] = array(
299
+ 'device' => $m[2],
300
+ 'level' => $m[1],
301
+ 'status' => $m[3],
302
+ 'drives' => array(array('drive' => $m[4], 'state' => 'unknown')),
303
+ 'size' => 'unknown',
304
+ 'count' => '?/?',
305
+ );
306
+ }
307
+
308
+ // Hitting a new device in a raid definition
309
+ elseif (preg_match('/^ (\w+)$/', $content, $m)) {
310
+
311
+ // This migh be part of a raid dev; save it if it is
312
+ if (array_key_exists($i, $return)) {
313
+ $return[$i]['drives'][] = array('drive' => $m[1], 'state' => 'unknown');
314
+ }
315
+ }
316
+ }
317
+ } catch (Exception $e) {
318
+ Errors::add('RAID', 'Error using gmirror to get raid info');
319
+ // Don't jump out; allow potential more raid array
320
+ // mechanisms to be gathered and outputted
321
+ }
322
+ }
323
+
324
+ // Give off raid info
325
+ return $return;
326
+ }
327
+
328
+ // Done
329
+ public function getNet()
330
+ {
331
+
332
+ // Time?
333
+ if (!empty($this->settings['timer'])) {
334
+ $t = new Timer('Network Devices');
335
+ }
336
+
337
+ // Store return vals here
338
+ $return = array();
339
+
340
+ // Use netstat to get info
341
+ try {
342
+ $netstat = $this->exec->exec('netstat', '-nbdi');
343
+ } catch (Exception $e) {
344
+ Errors::add('Linfo Core', 'Error using `netstat` to get network info');
345
+
346
+ return $return;
347
+ }
348
+
349
+ // Initially get interfaces themselves along with numerical stats
350
+ if (preg_match_all('/^(\w+\w)\*?\s*\w+\s+<Link\#\w+>(?:\D+|\s+\w+:\w+:\w+:\w+:\w+:\w+\s+)(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+/m', $netstat, $netstat_match, PREG_SET_ORDER) == 0) {
351
+ return $return;
352
+ }
353
+
354
+ // Try using ifconfig to get states of the network interfaces
355
+ $statuses = array();
356
+ try {
357
+ // Output of ifconfig command
358
+ $ifconfig = $this->exec->exec('ifconfig', '-a');
359
+
360
+ // Set this to false to prevent wasted regexes
361
+ $current_nic = false;
362
+
363
+ // Go through each line
364
+ foreach ((array) explode("\n", $ifconfig) as $line) {
365
+
366
+ // Approachign new nic def
367
+ if (preg_match('/^(\w+):/', $line, $m) == 1) {
368
+ $current_nic = $m[1];
369
+ }
370
+
371
+ // Hopefully match its status
372
+ elseif ($current_nic && preg_match('/^\s+status: ([^\$]+)/', $line, $m) == 1) {
373
+ $statuses[$current_nic] = $m[1];
374
+ $current_nic = false;
375
+ }
376
+ }
377
+ } catch (Exception $e) {
378
+ }
379
+
380
+ // Get type from dmesg boot
381
+ $type = array();
382
+ $type_nics = array();
383
+
384
+ // Store the to-be detected nics here
385
+ foreach ($netstat_match as $net) {
386
+ $type_nics[] = $net[1];
387
+ }
388
+
389
+ // Go through dmesg looking for them
390
+ if (preg_match_all('/^(\w+): <.+>.+on ([a-z]+)\d+/m', $this->dmesg, $type_match, PREG_SET_ORDER)) {
391
+
392
+ // Go through each
393
+ foreach ($type_match as $type_nic_match) {
394
+
395
+ // Is this one of our detected nics?
396
+ if (in_array($type_nic_match[1], $type_nics)) {
397
+
398
+ // Yes; save status
399
+ $type[$type_nic_match[1]] = $type_nic_match[2];
400
+ }
401
+ }
402
+ }
403
+
404
+ // Save info
405
+ foreach ($netstat_match as $net) {
406
+
407
+ // Determine status
408
+ switch (array_key_exists($net[1], $statuses) ? $statuses[$net[1]] : 'unknown') {
409
+
410
+ case 'active':
411
+ $state = 'up';
412
+ break;
413
+
414
+ case 'inactive':
415
+ case 'no carrier':
416
+ $state = 'down';
417
+ break;
418
+
419
+ default:
420
+ $state = 'unknown';
421
+ break;
422
+ }
423
+
424
+ // Save info
425
+ $return[$net[1]] = array(
426
+
427
+ // These came from netstat
428
+ 'recieved' => array(
429
+ 'bytes' => (int) $net[$this->version >= 8 ? 5 : 4],
430
+ 'errors' => $net[3],
431
+ 'packets' => $net[2],
432
+ ),
433
+ 'sent' => array(
434
+ 'bytes' => (int) $net[$this->version >= 8 ? 8 : 7],
435
+ 'errors' => $net[6],
436
+ 'packets' => $net[5],
437
+ ),
438
+
439
+ // This came from ifconfig -a
440
+ 'state' => $state,
441
+
442
+ // And this came from dmeg.
443
+ // TODO: Value for following is usually vague
444
+ 'type' => array_key_exists($net[1], $type) ? strtoupper($type[$net[1]]) : 'N/A',
445
+ );
446
+ }
447
+
448
+ // Return it
449
+ return $return;
450
+ }
451
+
452
+ // Get CPU's
453
+ // I still don't really like how this is done
454
+ // todo: support multiple non-identical cpu's
455
+ public function getCPU()
456
+ {
457
+
458
+ // Time?
459
+ if (!empty($this->settings['timer'])) {
460
+ $t = new Timer('CPUs');
461
+ }
462
+
463
+ // Store them here
464
+ $cpus = array();
465
+
466
+ // Stuff it with identical cpus
467
+ for ($i = 0; $i < $this->sysctl['hw.ncpu']; ++$i) {
468
+
469
+ // Save each
470
+ $cpus[] = array(
471
+ 'Model' => $this->sysctl['hw.model'],
472
+ 'MHz' => (int) trim($this->sysctl['hw.clockrate']),
473
+ );
474
+ }
475
+
476
+ // Return
477
+ return $cpus;
478
+ }
479
+
480
+ // TODO: Get reads/writes and partitions for the drives
481
+ public function getHD()
482
+ {
483
+
484
+ // Time?
485
+ if (!empty($this->settings['timer'])) {
486
+ $t = new Timer('Drives');
487
+ }
488
+
489
+ // Keep them here
490
+ $drives = array();
491
+
492
+ // Must they change the format of everything with each release?!?!?!?!
493
+ switch ($this->version) {
494
+
495
+ case 8.2:
496
+ $cur = false;
497
+
498
+ // Each line of dmesg boot log
499
+ foreach ((array) explode("\n", $this->dmesg) as $line) {
500
+
501
+ // Start of a drive entry which spans multiple lines
502
+ if (preg_match('/^((?:ad|da|acd|cd)\d+) at/', $line, $m)) {
503
+ $cur = array('device' => '/dev/'.$m[1]);
504
+ }
505
+
506
+ // Branding of this drive
507
+ elseif ($cur && preg_match('/^((?:ad|da|acd|cd)\d+): \<([^>]+)\>/', $line, $m)) {
508
+ if ('/dev/'.$m[1] != $cur['device']) {
509
+ continue;
510
+ }
511
+ $halves = explode(' ', $m[2]);
512
+ if (count($halves) > 1) {
513
+ $cur['vendor'] = $halves[0];
514
+ $cur['name'] = $halves[1];
515
+ } else {
516
+ $cur['vendor'] = false;
517
+ $cur['name'] = $m[1];
518
+ }
519
+ }
520
+
521
+ // Lastly the size; gather it and save it
522
+ elseif ($cur && preg_match('/^((?:ad|da|acd|cd)\d+): (\d+)MB/', $line, $m)) {
523
+ if ('/dev/'.$m[1] != $cur['device']) {
524
+ $cur = false;
525
+ continue;
526
+ }
527
+ $cur['size'] = $m[2] * 1048576;
528
+ $drives[] = $cur;
529
+ $cur = false;
530
+ }
531
+ }
532
+ break;
533
+
534
+ default:
535
+ if (preg_match_all('/^((?:ad|da|acd|cd)\d+)\: ((?:\w+|\d+\w+)) \<(\S+)\s+([^>]+)\>/m', $this->dmesg, $m, PREG_SET_ORDER) > 0) {
536
+ foreach ($m as $drive) {
537
+ $drives[] = array(
538
+ 'name' => $drive[4],
539
+ 'vendor' => $drive[3],
540
+ 'device' => '/dev/'.$drive[1],
541
+ 'size' => preg_match('/^(\d+)MB$/', $drive[2], $m) == 1 ? $m[1] * 1048576 : false,
542
+ 'reads' => false,
543
+ 'writes' => false,
544
+ );
545
+ }
546
+ }
547
+ break;
548
+ }
549
+
550
+ // Return
551
+ return $drives;
552
+ }
553
+
554
+ // Parse dmesg boot log
555
+ public function getDevs()
556
+ {
557
+
558
+ // Time?
559
+ if (!empty($this->settings['timer'])) {
560
+ $t = new Timer('Hardware Devices');
561
+ }
562
+
563
+ // Class that does it
564
+ $hw = new Hwpci(false, '/usr/share/misc/pci_vendors');
565
+ $hw->work('freebsd');
566
+
567
+ return $hw->result();
568
+ }
569
+
570
+ // APM? Seems to only support either one battery of them all collectively
571
+ public function getBattery()
572
+ {
573
+
574
+ // Time?
575
+ if (!empty($this->settings['timer'])) {
576
+ $t = new Timer('Batteries');
577
+ }
578
+
579
+ // Store them here
580
+ $batts = array();
581
+
582
+ // Get result of program
583
+ try {
584
+ $res = $this->exec->exec('apm', '-abl');
585
+ } catch (Exception $e) {
586
+ Errors::add('Linfo Core', 'Error using `apm` battery info');
587
+
588
+ return $batts;
589
+ }
590
+
591
+ // Values from program
592
+ list(, $bat_status, $percentage) = explode("\n", $res);
593
+
594
+ // Interpret status code
595
+ switch ($bat_status) {
596
+ case 0:
597
+ $status = 'High';
598
+ break;
599
+ case 1:
600
+ $status = 'Low';
601
+ break;
602
+ case 2:
603
+ $status = 'Critical';
604
+ break;
605
+ case 3:
606
+ $status = 'Charging';
607
+ break;
608
+ default:
609
+ $status = 'Unknown';
610
+ break;
611
+ }
612
+
613
+ // Save battery
614
+ $batts[] = array(
615
+ 'percentage' => $percentage.'%',
616
+ 'state' => $status,
617
+ 'device' => 'battery',
618
+ );
619
+
620
+ // Return
621
+ return $batts;
622
+ }
623
+
624
+ // Get stats on processes
625
+ public function getProcessStats()
626
+ {
627
+
628
+ // Time?
629
+ if (!empty($this->settings['timer'])) {
630
+ $t = new Timer('Process Stats');
631
+ }
632
+
633
+ // We'll return this after stuffing it with useful info
634
+ $result = array(
635
+ 'exists' => true,
636
+ 'totals' => array(
637
+ 'running' => 0,
638
+ 'zombie' => 0,
639
+ 'sleeping' => 0,
640
+ 'stopped' => 0,
641
+ 'idle' => 0,
642
+ ),
643
+ 'proc_total' => 0,
644
+ 'threads' => false, // I'm not sure how to get this
645
+ );
646
+
647
+ // Use ps
648
+ try {
649
+ // Get it
650
+ $ps = $this->exec->exec('ps', 'ax');
651
+
652
+ // Match them
653
+ preg_match_all('/^\s*\d+\s+[\w?]+\s+([A-Z])\S*\s+.+$/m', $ps, $processes, PREG_SET_ORDER);
654
+
655
+ // Get total
656
+ $result['proc_total'] = count($processes);
657
+
658
+ // Go through
659
+ foreach ($processes as $process) {
660
+ switch ($process[1]) {
661
+ case 'S':
662
+ case 'I':
663
+ $result['totals']['sleeping']++;
664
+ break;
665
+ case 'Z':
666
+ $result['totals']['zombie']++;
667
+ break;
668
+ case 'R':
669
+ case 'D':
670
+ $result['totals']['running']++;
671
+ break;
672
+ case 'T':
673
+ $result['totals']['stopped']++;
674
+ break;
675
+ case 'W':
676
+ $result['totals']['idle']++;
677
+ break;
678
+ }
679
+ }
680
+ } catch (Exception $e) {
681
+ Errors::add('Linfo Core', 'Error using `ps` to get process info');
682
+ }
683
+
684
+ // Give
685
+ return $result;
686
+ }
687
+
688
+ // idk
689
+ public function getTemps()
690
+ {
691
+ // Time?
692
+ if (!empty($this->settings['timer'])) {
693
+ $t = new Timer('Temperature');
694
+ }
695
+ }
696
+
697
+ public function getLoad()
698
+ {
699
+ if (!empty($this->settings['timer'])) {
700
+ $t = new Timer('Load Averages');
701
+ }
702
+
703
+ $loads = $this->sysctl['vm.loadavg'];
704
+
705
+ if (preg_match('/([\d\.]+) ([\d\.]+) ([\d\.]+)/', $loads, $m)) {
706
+ return array_combine(array('now', '5min', '15min'), array_slice($m, 1, 3));
707
+ } else {
708
+ return array();
709
+ }
710
+ }
711
+
712
+ public function getVirtualization()
713
+ {
714
+
715
+ // Time?
716
+ if (!empty($this->settings['timer'])) {
717
+ $t = new Timer('Determining virtualization type');
718
+ }
719
+
720
+ // KVM guest? Try to expand this with support for other hypervisors..
721
+ if (preg_match('/^Hypervisor:\s+Origin\s+=\s+"([^"]+)"/m', $this->dmesg, $m)) {
722
+ if (strpos($m[1], 'KVM') !== false) {
723
+ return array('type' => 'guest', 'method' => 'KVM');
724
+ }
725
+ }
726
+
727
+ return false;
728
+ }
729
+ }
lib/Linfo/OS/Linux.php ADDED
@@ -0,0 +1,1729 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo\OS;
20
+
21
+ use Linfo\Meta\Timer;
22
+ use Linfo\Meta\Errors;
23
+ use Linfo\Common;
24
+ use Linfo\Exceptions\FatalException;
25
+ use Linfo\Parsers\Hwpci;
26
+ use Linfo\Parsers\Sensord;
27
+ use Linfo\Parsers\Hddtemp;
28
+ use Linfo\Parsers\Mbmon;
29
+ use Exception;
30
+
31
+ /**
32
+ * Get info on a usual linux system
33
+ * Works by exclusively looking around /proc and /sys
34
+ * Totally ignores CallExt class, very deliberately
35
+ * Also deliberately ignores trying to find out the distro.
36
+ */
37
+ class Linux extends Unixcommon
38
+ {
39
+ // Keep these tucked away
40
+ protected $settings;
41
+
42
+ // Generally disabled as it's slowww
43
+ protected $cpu_percent = array('overall' => false, 'cpus' => array());
44
+
45
+ /**
46
+ * Constructor. Localizes settings.
47
+ *
48
+ * @param array $settings of linfo settings
49
+ * @throws FatalException
50
+ */
51
+ public function __construct($settings)
52
+ {
53
+
54
+ // Localize settings
55
+ $this->settings = $settings;
56
+
57
+ // Make sure we have what we need
58
+ if (!is_dir('/sys') || !is_dir('/proc')) {
59
+ throw new FatalException('This needs access to /proc and /sys to work.');
60
+ }
61
+ }
62
+
63
+ public function init()
64
+ {
65
+ if (isset($this->settings['cpu_usage']) && !empty($this->settings['cpu_usage'])) {
66
+ $this->determineCPUPercentage();
67
+ }
68
+ }
69
+
70
+ /**
71
+ * getKernel.
72
+ *
73
+ * @return string kernel version
74
+ */
75
+ public function getKernel()
76
+ {
77
+
78
+ // Time?
79
+ if (!empty($this->settings['timer'])) {
80
+ $t = new Timer('Kernel');
81
+ }
82
+
83
+ // File containing info
84
+ $file = '/proc/version';
85
+
86
+ // Make sure we can use it
87
+ if (!is_file($file) || !is_readable($file)) {
88
+ Errors::add('Linfo Core', '/proc/version not found');
89
+
90
+ return 'Unknown';
91
+ }
92
+
93
+ // Get it
94
+ $contents = Common::getContents($file);
95
+
96
+ // Parse it
97
+ if (preg_match('/^Linux version (\S+).+$/', $contents, $match) != 1) {
98
+ Errors::add('Linfo Core', 'Error parsing /proc/version');
99
+
100
+ return 'Unknown';
101
+ }
102
+
103
+ // Return it
104
+ return $match[1];
105
+ }
106
+
107
+ /**
108
+ * getHostName.
109
+ *
110
+ * @return string the host name
111
+ */
112
+ public function getHostName()
113
+ {
114
+
115
+ // Time?
116
+ if (!empty($this->settings['timer'])) {
117
+ $t = new Timer('Hostname');
118
+ }
119
+
120
+ // File containing info
121
+ $file = '/proc/sys/kernel/hostname';
122
+
123
+ // Get it
124
+ $hostname = Common::getContents($file, false);
125
+
126
+ // Failed?
127
+ if ($hostname === false) {
128
+ Errors::add('Linfo Core', 'Error getting /proc/sys/kernel/hostname');
129
+
130
+ return 'Unknown';
131
+ }
132
+
133
+ // Didn't fail; return it
134
+ return $this->ensureFQDN($hostname);
135
+ }
136
+
137
+ /**
138
+ * getRam.
139
+ *
140
+ * @return array the memory information
141
+ */
142
+ public function getRam()
143
+ {
144
+
145
+ // Time?
146
+ if (!empty($this->settings['timer'])) {
147
+ $t = new Timer('Memory');
148
+ }
149
+
150
+ // We'll return the contents of this
151
+ $return = array();
152
+
153
+ // Files containing juicy info
154
+ $procFileSwap = '/proc/swaps';
155
+ $procFileMem = '/proc/meminfo';
156
+
157
+ // First off, these need to exist..
158
+ if (!is_readable($procFileSwap) || !is_readable($procFileMem)) {
159
+ Errors::add('Linfo Core', '/proc/swaps and/or /proc/meminfo are not readable');
160
+
161
+ return array();
162
+ }
163
+
164
+ // To hold their values
165
+ $memVals = array();
166
+ $swapVals = array();
167
+
168
+ // Get memContents
169
+ @preg_match_all('/^([^:]+)\:\s+(\d+)\s*(?:k[bB])?\s*/m', Common::getContents($procFileMem), $matches, PREG_SET_ORDER);
170
+
171
+ // Deal with it
172
+ foreach ((array) $matches as $memInfo) {
173
+ $memVals[$memInfo[1]] = $memInfo[2];
174
+ }
175
+
176
+ // Get swapContents
177
+ @preg_match_all('/^(\S+)\s+(\S+)\s+(\d+)\s(\d+)/m', Common::getContents($procFileSwap), $matches, PREG_SET_ORDER);
178
+ foreach ((array) $matches as $swapDevice) {
179
+
180
+ // Append each swap device
181
+ $swapVals[] = array(
182
+ 'device' => $swapDevice[1],
183
+ 'type' => $swapDevice[2],
184
+ 'size' => $swapDevice[3] * 1024,
185
+ 'used' => $swapDevice[4] * 1024,
186
+ );
187
+ }
188
+
189
+ // Get individual vals
190
+ $return['type'] = 'Physical';
191
+ $return['total'] = @$memVals['MemTotal'] * 1024;
192
+ $return['free'] = @$memVals['MemFree'] * 1024 + @$memVals['Cached'] * 1024 + @$memVals['Buffers'] * 1024;
193
+ $return['swapTotal'] = @$memVals['SwapTotal'] * 1024;
194
+ $return['swapFree'] = @$memVals['SwapFree'] * 1024 + @$memVals['SwapCached'] * 1024;
195
+ $return['swapCached'] = @$memVals['SwapCached'] * 1024;
196
+ $return['swapInfo'] = @$swapVals;
197
+
198
+ // Return it
199
+ return $return;
200
+ }
201
+
202
+ /**
203
+ * getCPU.
204
+ *
205
+ * @return array of cpu info
206
+ */
207
+ public function getCPU()
208
+ {
209
+
210
+ // Time?
211
+ if (!empty($this->settings['timer'])) {
212
+ $t = new Timer('CPUs');
213
+ }
214
+
215
+ // File that has it
216
+ $file = '/proc/cpuinfo';
217
+
218
+ // Not there?
219
+ if (!is_file($file) || !is_readable($file)) {
220
+ Errors::add('Linfo Core', '/proc/cpuinfo not readable');
221
+
222
+ return array();
223
+ }
224
+
225
+ /*
226
+ * Get all info for all CPUs from the cpuinfo file
227
+ */
228
+
229
+ // Get contents
230
+ $contents = Common::getContents($file);
231
+
232
+ // Lines
233
+ $lines = explode("\n", $contents);
234
+
235
+ // Store CPUs here
236
+ $cpus = array();
237
+
238
+ // Holder for current CPU info
239
+ $cur_cpu = array();
240
+
241
+ // Go through lines in file
242
+ $num_lines = count($lines);
243
+
244
+ // We use the key of the first line to separate CPUs
245
+ $first_line = substr($lines[0], 0, strpos($lines[0], ' '));
246
+
247
+ for ($i = 0; $i < $num_lines; ++$i) {
248
+
249
+ // Approaching new CPU? Save current and start new info for this
250
+ if (strpos($lines[$i], $first_line) === 0 && count($cur_cpu) > 0) {
251
+ $cpus[] = $cur_cpu;
252
+ $cur_cpu = array();
253
+
254
+ // Default to unknown
255
+ $cur_cpu['Model'] = 'Unknown';
256
+ }
257
+
258
+ // Info here
259
+ $line = explode(':', $lines[$i], 2);
260
+
261
+ if (!array_key_exists(1, $line)) {
262
+ continue;
263
+ }
264
+
265
+ $key = trim($line[0]);
266
+ $value = trim($line[1]);
267
+
268
+ // What we want are MHZ, Vendor, and Model.
269
+ switch ($key) {
270
+
271
+ // CPU model
272
+ case 'model name':
273
+ case 'cpu':
274
+ case 'Processor':
275
+ $cur_cpu['Model'] = $value;
276
+ break;
277
+
278
+ // Speed in MHz
279
+ case 'cpu MHz':
280
+ $cur_cpu['MHz'] = $value;
281
+ break;
282
+
283
+ case 'Cpu0ClkTck': // Old sun boxes
284
+ $cur_cpu['MHz'] = hexdec($value) / 1000000;
285
+ break;
286
+
287
+ // Brand/vendor
288
+ case 'vendor_id':
289
+ $cur_cpu['Vendor'] = $value;
290
+ break;
291
+
292
+ // ID. Corresponds to percentage if enabled below
293
+ case 'processor':
294
+ if (isset($this->cpu_percent['cpus'][$value])) {
295
+ $cur_cpu['usage_percentage'] = $this->cpu_percent['cpus'][$value];
296
+ }
297
+ break;
298
+ }
299
+ }
300
+
301
+ // Save remaining one
302
+ if (count($cur_cpu) > 0) {
303
+ $cpus[] = $cur_cpu;
304
+ }
305
+
306
+ // Return them
307
+ return $cpus;
308
+ }
309
+
310
+ // Famously interesting uptime
311
+ public function getUpTime()
312
+ {
313
+
314
+ // Time?
315
+ if (!empty($this->settings['timer'])) {
316
+ $t = new Timer('Uptime');
317
+ }
318
+
319
+ // Get contents
320
+ $contents = Common::getContents('/proc/uptime', false);
321
+
322
+ // eh?
323
+ if ($contents === false) {
324
+ Errors::add('Linfo Core', '/proc/uptime does not exist.');
325
+
326
+ return 'Unknown';
327
+ }
328
+
329
+ // Seconds
330
+ list($seconds) = explode(' ', $contents, 1);
331
+
332
+ // Get it textual, as in days/minutes/hours/etc
333
+ $uptime = Common::secondsConvert(ceil($seconds));
334
+
335
+ // Now find out when the system was booted
336
+ $contents = Common::getContents('/proc/stat', false);
337
+
338
+ // Ugh
339
+ if ($contents === false) {
340
+ return $uptime;
341
+ } // Settle for just uptime
342
+
343
+ // Get date of boot
344
+ if (preg_match('/^btime (\d+)$/m', $contents, $boot) != 1) {
345
+ return $uptime;
346
+ }
347
+
348
+ // Okay?
349
+ list(, $boot) = $boot;
350
+
351
+ return array(
352
+ 'text' => $uptime,
353
+ 'bootedTimestamp' => $boot,
354
+ );
355
+ }
356
+
357
+ /**
358
+ * getHD.
359
+ *
360
+ * @return array the hard drive info
361
+ */
362
+ public function getHD()
363
+ {
364
+
365
+ // Time?
366
+ if (!empty($this->settings['timer'])) {
367
+ $t = new Timer('Drives');
368
+ }
369
+
370
+ // Get partitions
371
+ $partitions = array();
372
+ $partitions_contents = Common::getContents('/proc/partitions');
373
+ if (@preg_match_all('/(\d+)\s+([a-z]{3})(\d+)$/m', $partitions_contents, $partitions_match, PREG_SET_ORDER) > 0) {
374
+ // Go through each match
375
+ foreach ($partitions_match as $partition) {
376
+ $partitions[$partition[2]][] = array(
377
+ 'size' => $partition[1] * 1024,
378
+ 'number' => $partition[3],
379
+ );
380
+ }
381
+ }
382
+
383
+ // Store drives here
384
+ $drives = array();
385
+
386
+ // Get actual drives
387
+ foreach ((array) @glob('/sys/block/*/device/model', GLOB_NOSORT) as $path) {
388
+
389
+ // Parts of the path
390
+ $parts = explode('/', $path);
391
+
392
+ // Attempt getting read/write stats
393
+ if (preg_match('/^(\d+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+$/', Common::getContents(dirname(dirname($path)).'/stat'), $statMatches) !== 1) {
394
+ // Didn't get it
395
+ $reads = false;
396
+ $writes = false;
397
+ } else {
398
+ // Got it, save it
399
+ list(, $reads, $writes) = $statMatches;
400
+ }
401
+
402
+ // Append this drive on
403
+ $drives[] = array(
404
+ 'name' => Common::getContents($path, 'Unknown').(Common::getContents(dirname(dirname($path)).'/queue/rotational') == 0 ? ' (SSD)' : ''),
405
+ 'vendor' => Common::getContents(dirname($path).'/vendor', 'Unknown'),
406
+ 'device' => '/dev/'.$parts[3],
407
+ 'reads' => $reads,
408
+ 'writes' => $writes,
409
+ 'size' => Common::getContents(dirname(dirname($path)).'/size', 0) * 512,
410
+ 'partitions' => array_key_exists($parts[3], $partitions) && is_array($partitions[$parts[3]]) ? $partitions[$parts[3]] : false,
411
+ );
412
+ }
413
+
414
+ // Return drives
415
+ return $drives;
416
+ }
417
+
418
+ /**
419
+ * getTemps.
420
+ *
421
+ * @return array the temps
422
+ */
423
+ public function getTemps()
424
+ {
425
+
426
+ // Time?
427
+ if (!empty($this->settings['timer'])) {
428
+ $t = new Timer('Temperature');
429
+ }
430
+
431
+ // Hold them here
432
+ $return = array();
433
+
434
+ // hddtemp?
435
+ if (array_key_exists('hddtemp', (array) $this->settings['temps']) && !empty($this->settings['temps']['hddtemp']) && isset($this->settings['hddtemp'])) {
436
+ try {
437
+ // Initiate class
438
+ $hddtemp = new Hddtemp($this->settings);
439
+
440
+ // Set mode, as in either daemon or syslog
441
+ $hddtemp->setMode($this->settings['hddtemp']['mode']);
442
+
443
+ // If we're daemon, save host and port
444
+ if ($this->settings['hddtemp']['mode'] == 'daemon') {
445
+ $hddtemp->setAddress(
446
+ $this->settings['hddtemp']['address']['host'],
447
+ $this->settings['hddtemp']['address']['port']);
448
+ }
449
+
450
+ // Result after working it
451
+ $hddtemp_res = $hddtemp->work();
452
+
453
+ // If it's an array, it worked
454
+ if (is_array($hddtemp_res)) {
455
+ // Save result
456
+ $return = array_merge($return, $hddtemp_res);
457
+ }
458
+ }
459
+
460
+ // There was an issue
461
+ catch (\Exception $e) {
462
+ Errors::add('hddtemp parser', $e->getMessage());
463
+ }
464
+ }
465
+
466
+ // mbmon?
467
+ if (array_key_exists('mbmon', (array) $this->settings['temps']) && !empty($this->settings['temps']['mbmon']) && isset($this->settings['mbmon'])) {
468
+ try {
469
+ // Initiate class
470
+ $mbmon = new Mbmon();
471
+
472
+ // Set host and port
473
+ $mbmon->setAddress(
474
+ $this->settings['mbmon']['address']['host'],
475
+ $this->settings['mbmon']['address']['port']);
476
+
477
+ // Get result after working it
478
+ $mbmon_res = $mbmon->work();
479
+
480
+ // If it's an array, it worked
481
+ if (is_array($mbmon_res)) {
482
+ // Save result
483
+ $return = array_merge($return, $mbmon_res);
484
+ }
485
+ } catch (Exception $e) {
486
+ Errors::add('mbmon parser', $e->getMessage());
487
+ }
488
+ }
489
+
490
+ // sensord? (part of lm-sensors)
491
+ if (array_key_exists('sensord', (array) $this->settings['temps']) && !empty($this->settings['temps']['sensord'])) {
492
+ try {
493
+ // Iniatate class
494
+ $sensord = new Sensord();
495
+
496
+ // Work it
497
+ $sensord_res = $sensord->work();
498
+
499
+ // If it's an array, it worked
500
+ if (is_array($sensord_res)) {
501
+ // Save result
502
+ $return = array_merge($return, $sensord_res);
503
+ }
504
+ } catch (Exception $e) {
505
+ Errors::add('sensord parser', $e->getMessage());
506
+ }
507
+ }
508
+
509
+ // hwmon? (probably the fastest of what's here)
510
+ // too simple to be in its own class
511
+ if (array_key_exists('hwmon', (array) $this->settings['temps']) && !empty($this->settings['temps']['hwmon'])) {
512
+
513
+ // Store them here
514
+ $hwmon_vals = array();
515
+
516
+ // Wacky location
517
+ foreach ((array) @glob('/sys/class/hwmon/hwmon*/{,device/}*_input', GLOB_NOSORT | GLOB_BRACE) as $path) {
518
+ $initpath = rtrim($path, 'input');
519
+ $value = Common::getContents($path);
520
+ $base = basename($path);
521
+ $labelpath = $initpath.'label';
522
+ $showemptyfans = isset($this->settings['temps_show0rpmfans']) ? $this->settings['temps_show0rpmfans'] : false;
523
+ $drivername = @basename(@readlink(dirname($path).'/driver')) ?: false;
524
+
525
+ // Temperatures
526
+ if (is_file($labelpath) && strpos($base, 'temp') === 0) {
527
+ $label = Common::getContents($labelpath);
528
+ $value /= $value > 10000 ? 1000 : 1;
529
+ $unit = 'C'; // I don't think this is ever going to be in F
530
+ }
531
+
532
+ // Fan RPMs
533
+ elseif (preg_match('/^fan(\d+)_/', $base, $m)) {
534
+ $label = 'fan'.$m[1];
535
+ $unit = 'RPM';
536
+
537
+ if ($value == 0 && !$showemptyfans) {
538
+ continue;
539
+ }
540
+ }
541
+
542
+ // Volts
543
+ elseif (preg_match('/^in(\d+)_/', $base, $m)) {
544
+ $unit = 'V';
545
+ $value /= 1000;
546
+ $label = Common::getContents($labelpath) ?: 'in'.$m[1];
547
+ } else {
548
+ continue;
549
+ }
550
+
551
+ // Append values
552
+ $hwmon_vals[] = array(
553
+ 'path' => '',
554
+ 'name' => $label.($drivername ? ' <span class="faded">('.$drivername.')</span>' : ''),
555
+ 'temp' => $value,
556
+ 'unit' => $unit,
557
+ );
558
+ }
559
+
560
+ // Save any if we have any
561
+ if (count($hwmon_vals) > 0) {
562
+ $return = array_merge($return, $hwmon_vals);
563
+ }
564
+ }
565
+
566
+ // Laptop backlight percentage
567
+ foreach ((array) @glob('/sys/{devices/virtual,class}/backlight/*/max_brightness', GLOB_NOSORT | GLOB_BRACE) as $bl) {
568
+ $dir = dirname($bl);
569
+ if (!is_file($dir.'/actual_brightness')) {
570
+ continue;
571
+ }
572
+ $max = Common::getIntFromFile($bl);
573
+ $cur = Common::getIntFromFile($dir.'/actual_brightness');
574
+ if ($max < 0 || $cur < 0) {
575
+ continue;
576
+ }
577
+ $return[] = array(
578
+ 'name' => 'Backlight brightness',
579
+ 'temp' => round($cur / $max, 2) * 100,
580
+ 'unit' => '%',
581
+ 'path' => 'N/A',
582
+ 'bar' => true,
583
+ );
584
+ }
585
+
586
+ // Done
587
+ return $return;
588
+ }
589
+
590
+ /**
591
+ * getMounts.
592
+ *
593
+ * @return array the mounted the file systems
594
+ */
595
+ public function getMounts()
596
+ {
597
+
598
+ // Time?
599
+ if (!empty($this->settings['timer'])) {
600
+ $t = new Timer('Mounted file systems');
601
+ }
602
+
603
+ // File
604
+ $contents = Common::getContents('/proc/mounts', false);
605
+
606
+ // Can't?
607
+ if ($contents == false) {
608
+ Errors::add('Linfo Core', '/proc/mounts does not exist');
609
+ }
610
+
611
+ // Parse
612
+ if (@preg_match_all('/^(\S+) (\S+) (\S+) (.+) \d \d$/m', $contents, $match, PREG_SET_ORDER) === false) {
613
+ Errors::add('Linfo Core', 'Error parsing /proc/mounts');
614
+ }
615
+
616
+ // Return these
617
+ $mounts = array();
618
+
619
+ // Populate
620
+ foreach ($match as $mount) {
621
+
622
+ // Should we not show this?
623
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
624
+ continue;
625
+ }
626
+
627
+ // Should we not show this? (regex)
628
+ if (isset($this->settings['hide']['mountpoints_regex']) && is_array($this->settings['hide']['mountpoints_regex'])) {
629
+ foreach ($this->settings['hide']['mountpoints_regex'] as $regex) {
630
+ if (@preg_match($regex, $mount[2])) {
631
+ continue 2;
632
+ }
633
+ }
634
+ }
635
+
636
+ // Spaces and other things in the mount path are escaped C style. Fix that.
637
+ $mount[2] = stripcslashes($mount[2]);
638
+
639
+ // Get these
640
+ $size = @disk_total_space($mount[2]);
641
+ $free = @disk_free_space($mount[2]);
642
+ $used = $size != false && $free != false ? $size - $free : false;
643
+
644
+ // If it's a symlink, find out where it really goes.
645
+ // (using realpath instead of readlink because the former gives absolute paths)
646
+ $symlink = is_link($mount[1]) ? realpath($mount[1]) : false;
647
+
648
+ // Optionally get mount options
649
+ if ($this->settings['show']['mounts_options'] && !in_array($mount[3], (array) $this->settings['hide']['fs_mount_options'])) {
650
+ $mount_options = explode(',', $mount[4]);
651
+ } else {
652
+ $mount_options = array();
653
+ }
654
+
655
+ // Might be good, go for it
656
+ $mounts[] = array(
657
+ 'device' => $symlink != false ? $symlink : $mount[1],
658
+ 'mount' => $mount[2],
659
+ 'type' => $mount[3],
660
+ 'size' => $size,
661
+ 'used' => $used,
662
+ 'free' => $free,
663
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
664
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
665
+ 'options' => $mount_options,
666
+ );
667
+ }
668
+
669
+ // Return
670
+ return $mounts;
671
+ }
672
+
673
+ /**
674
+ * getDevs.
675
+ *
676
+ * @return array of devices
677
+ */
678
+ public function getDevs()
679
+ {
680
+
681
+ // Time?
682
+ if (!empty($this->settings['timer'])) {
683
+ $t = new Timer('Hardware Devices');
684
+ }
685
+
686
+ // Location of useful paths
687
+ $pci_ids = Common::locateActualPath(array(
688
+ '/usr/share/misc/pci.ids', // debian/ubuntu
689
+ '/usr/share/pci.ids', // opensuse
690
+ '/usr/share/hwdata/pci.ids', // centos. maybe also redhat/fedora
691
+ ));
692
+ $usb_ids = Common::locateActualPath(array(
693
+ '/usr/share/misc/usb.ids', // debian/ubuntu
694
+ '/usr/share/usb.ids', // opensuse
695
+ '/usr/share/hwdata/usb.ids', // centos. maybe also redhat/fedora
696
+ ));
697
+
698
+ // Did we not get them?
699
+ $pci_ids || Errors::add('Linux Device Finder', 'Cannot find pci.ids; ensure pciutils is installed.');
700
+ $usb_ids || Errors::add('Linux Device Finder', 'Cannot find usb.ids; ensure usbutils is installed.');
701
+
702
+ // Class that does it
703
+ $hw = new Hwpci($usb_ids, $pci_ids);
704
+ $hw->work('linux');
705
+
706
+ return $hw->result();
707
+ }
708
+
709
+ /**
710
+ * getRAID.
711
+ *
712
+ * @return array of raid arrays
713
+ */
714
+ public function getRAID()
715
+ {
716
+
717
+ // Time?
718
+ if (!empty($this->settings['timer'])) {
719
+ $t = new Timer('RAID');
720
+ }
721
+
722
+ // Store it here
723
+ $raidinfo = array();
724
+
725
+ // mdadm?
726
+ if (array_key_exists('mdadm', (array) $this->settings['raid']) && !empty($this->settings['raid']['mdadm'])) {
727
+
728
+ // Try getting contents
729
+ $mdadm_contents = Common::getContents('/proc/mdstat', false);
730
+
731
+ // No?
732
+ if ($mdadm_contents === false) {
733
+ Errors::add('Linux softraid mdstat parser', '/proc/mdstat does not exist.');
734
+ }
735
+
736
+ // Parse
737
+ @preg_match_all('/(\S+)\s*:\s*(\w+)\s*raid(\d+)\s*([\w+\[\d+\] (\(\w\))?]+)\n\s+(\d+) blocks[^[]+\[(\d\/\d)\] \[([U\_]+)\]/mi', (string) $mdadm_contents, $match, PREG_SET_ORDER);
738
+
739
+ // Store them here
740
+ $mdadm_arrays = array();
741
+
742
+ // Deal with entries
743
+ foreach ((array) $match as $array) {
744
+
745
+ // Temporarily store drives here
746
+ $drives = array();
747
+
748
+ // Parse drives
749
+ foreach (explode(' ', $array[4]) as $drive) {
750
+
751
+ // Parse?
752
+ if (preg_match('/([\w\d]+)\[\d+\](\(\w\))?/', $drive, $match_drive) == 1) {
753
+
754
+ // Determine a status other than normal, like if it failed or is a spare
755
+ if (array_key_exists(2, $match_drive)) {
756
+ switch ($match_drive[2]) {
757
+ case '(S)':
758
+ $drive_state = 'spare';
759
+ break;
760
+ case '(F)':
761
+ $drive_state = 'failed';
762
+ break;
763
+ case null:
764
+ $drive_state = 'normal';
765
+ break;
766
+
767
+ // I'm not sure if there are status codes other than the above
768
+ default:
769
+ $drive_state = 'unknown';
770
+ break;
771
+ }
772
+ } else {
773
+ $drive_state = 'normal';
774
+ }
775
+
776
+ // Append this drive to the temp drives array
777
+ $drives[] = array(
778
+ 'drive' => '/dev/'.$match_drive[1],
779
+ 'state' => $drive_state,
780
+ );
781
+ }
782
+ }
783
+
784
+ // Add record of this array to arrays list
785
+ $mdadm_arrays[] = array(
786
+ 'device' => '/dev/'.$array[1],
787
+ 'status' => $array[2],
788
+ 'level' => $array[3],
789
+ 'drives' => $drives,
790
+ 'size' => Common::byteConvert($array[5] * 1024),
791
+ 'count' => $array[6],
792
+ 'chart' => $array[7],
793
+ );
794
+ }
795
+
796
+ // Append MD arrays to main raidinfo if it's good
797
+ if (is_array($mdadm_arrays) && count($mdadm_arrays) > 0) {
798
+ $raidinfo = array_merge($raidinfo, $mdadm_arrays);
799
+ }
800
+ }
801
+
802
+ // Return info
803
+ return $raidinfo;
804
+ }
805
+
806
+ /**
807
+ * getLoad.
808
+ *
809
+ * @return array of current system load values
810
+ */
811
+ public function getLoad()
812
+ {
813
+
814
+ // Time?
815
+ if (!empty($this->settings['timer'])) {
816
+ $t = new Timer('Load Averages');
817
+ }
818
+
819
+ // File that has it
820
+ $file = '/proc/loadavg';
821
+
822
+ // Get contents
823
+ $contents = Common::getContents($file, false);
824
+
825
+ // ugh
826
+ if ($contents === false) {
827
+ Errors::add('Linfo Core', '/proc/loadavg unreadable');
828
+ return array();
829
+ }
830
+
831
+ // Parts
832
+ $parts = array_slice(explode(' ', $contents), 0, 3);
833
+
834
+ if (!$parts) {
835
+ return array();
836
+ }
837
+
838
+ return array_combine(array('now', '5min', '15min'), $parts);
839
+ }
840
+
841
+ /**
842
+ * getNet.
843
+ *
844
+ * @return array of network devices
845
+ */
846
+ public function getNet()
847
+ {
848
+
849
+ // Time?
850
+ if (!empty($this->settings['timer'])) {
851
+ $t = new Timer('Network Devices');
852
+ }
853
+
854
+ // Hold our return values
855
+ $return = array();
856
+
857
+ // Get values for each device
858
+ foreach ((array) @glob('/sys/class/net/*', GLOB_NOSORT) as $path) {
859
+ $nic = basename($path);
860
+
861
+ // States
862
+ $operstate_contents = Common::getContents($path.'/operstate');
863
+ switch ($operstate_contents) {
864
+ case 'down':
865
+ case 'up':
866
+ case 'unknown':
867
+ $state = $operstate_contents;
868
+ break;
869
+
870
+ default:
871
+ $state = 'unknown';
872
+ break;
873
+ }
874
+
875
+ if ($state = 'unknown' && file_exists($path.'/carrier')) {
876
+ $carrier = Common::getContents($path.'/carrier', false);
877
+ if (!empty($carrier)) {
878
+ $state = 'up';
879
+ } else {
880
+ $state = 'down';
881
+ }
882
+ }
883
+
884
+ // Try the weird ways of getting type (https://stackoverflow.com/a/16060638)
885
+ $type = false;
886
+ $typeCode = Common::getIntFromFile($path.'/type');
887
+
888
+ if ($typeCode == 772) {
889
+ $type = 'Loopback';
890
+ } elseif ($typeCode == 65534) {
891
+ $type = 'Tunnel';
892
+ } elseif ($typeCode == 776) {
893
+ $type = 'IPv6 in IPv4';
894
+ }
895
+
896
+ if (!$type) {
897
+ $type_contents = strtoupper(Common::getContents($path.'/device/modalias'));
898
+ list($type_match) = explode(':', $type_contents, 2);
899
+
900
+ if (in_array($type_match, array('PCI', 'USB'))) {
901
+ $type = 'Ethernet ('.$type_match.')';
902
+
903
+ // Driver maybe?
904
+ if (($uevent_contents = @parse_ini_file($path.'/device/uevent')) && isset($uevent_contents['DRIVER'])) {
905
+ $type .= ' ('.$uevent_contents['DRIVER'].')';
906
+ }
907
+ } elseif ($type_match == 'VIRTIO') {
908
+ $type = 'VirtIO';
909
+ } elseif ($type_contents == 'XEN:VIF') {
910
+ $type = 'Xen (VIF)';
911
+ } elseif ($type_contents == 'XEN-BACKEND:VIF') {
912
+ $type = 'Xen Backend (VIF)';
913
+ } elseif (is_dir($path.'/bridge')) {
914
+ $type = 'Bridge';
915
+ } elseif (is_dir($path.'/bonding')) {
916
+ $type = 'Bond';
917
+ }
918
+
919
+ // TODO find some way of finding out what provides the virt-specific kvm vnet devices
920
+ }
921
+
922
+ $speed = Common::getIntFromFile($path.'/speed');
923
+
924
+ // Save and get info for each
925
+ $return[$nic] = array(
926
+
927
+ // Stats are stored in simple files just containing the number
928
+ 'recieved' => array(
929
+ 'bytes' => Common::getIntFromFile($path.'/statistics/rx_bytes'),
930
+ 'errors' => Common::getIntFromFile($path.'/statistics/rx_errors'),
931
+ 'packets' => Common::getIntFromFile($path.'/statistics/rx_packets'),
932
+ ),
933
+ 'sent' => array(
934
+ 'bytes' => Common::getIntFromFile($path.'/statistics/tx_bytes'),
935
+ 'errors' => Common::getIntFromFile($path.'/statistics/tx_errors'),
936
+ 'packets' => Common::getIntFromFile($path.'/statistics/rx_packets'),
937
+ ),
938
+
939
+ // These were determined above
940
+ 'state' => $state,
941
+ 'type' => $type ?: 'N/A',
942
+ 'port_speed' => $speed > 0 ? $speed : false,
943
+ );
944
+ }
945
+
946
+ // Return array of info
947
+ return $return;
948
+ }
949
+
950
+ /**
951
+ * getBattery.
952
+ *
953
+ * @return array of battery status
954
+ */
955
+ public function getBattery()
956
+ {
957
+
958
+ // Time?
959
+ if (!empty($this->settings['timer'])) {
960
+ $t = new Timer('Batteries');
961
+ }
962
+
963
+ // Return values
964
+ $return = array();
965
+
966
+ // Here they should be
967
+ $bats = (array) @glob('/sys/class/power_supply/BAT*', GLOB_NOSORT);
968
+
969
+ // Get vals for each battery
970
+ foreach ($bats as $b) {
971
+ foreach (array($b.'/manufacturer', $b.'/status') as $f) {
972
+ if (!is_file($f)) {
973
+ continue 2;
974
+ }
975
+ }
976
+
977
+ // Get these from the simple text files
978
+ switch (true) {
979
+ case is_file($b.'/energy_full'):
980
+ $charge_full = Common::getIntFromFile($b.'/energy_full');
981
+ $charge_now = Common::getIntFromFile($b.'/energy_now');
982
+ break;
983
+ case is_file($b.'/charge_full'):
984
+ $charge_full = Common::getIntFromFile($b.'/charge_full');
985
+ $charge_now = Common::getIntFromFile($b.'/charge_now');
986
+ break;
987
+ default:
988
+ continue;
989
+ break;
990
+ }
991
+
992
+ // Alleged percentage
993
+ $percentage = $charge_now != 0 && $charge_full != 0 ? (round($charge_now / $charge_full, 4) * 100) : '?';
994
+
995
+ // Save result set
996
+ $return[] = array(
997
+ 'charge_full' => $charge_full,
998
+ 'charge_now' => $charge_now,
999
+ 'percentage' => (is_numeric($percentage) && $percentage > 100 ? 100 : $percentage),
1000
+ 'device' => Common::getContents($b.'/manufacturer').' '.Common::getContents($b.'/model_name', 'Unknown'),
1001
+ 'state' => Common::getContents($b.'/status', 'Unknown'),
1002
+ );
1003
+ }
1004
+
1005
+ // Give it
1006
+ return $return;
1007
+ }
1008
+
1009
+ /**
1010
+ * getWifi.
1011
+ *
1012
+ * @return array of wifi devices
1013
+ */
1014
+ public function getWifi()
1015
+ {
1016
+
1017
+ // Time?
1018
+ if (!empty($this->settings['timer'])) {
1019
+ $t = new Timer('Wifi');
1020
+ }
1021
+
1022
+ // Return these
1023
+ $return = array();
1024
+
1025
+ // In here
1026
+ $contents = Common::getContents('/proc/net/wireless');
1027
+
1028
+ // Oi
1029
+ if ($contents == false) {
1030
+ Errors::add('Linux WiFi info parser', '/proc/net/wireless does not exist');
1031
+
1032
+ return $return;
1033
+ }
1034
+
1035
+ // Parse
1036
+ @preg_match_all('/^ (\S+)\:\s*(\d+)\s*(\S+)\s*(\S+)\s*(\S+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*$/m', $contents, $match, PREG_SET_ORDER);
1037
+
1038
+ // Match
1039
+ foreach ($match as $wlan) {
1040
+ $return[] = array(
1041
+ 'device' => $wlan[1],
1042
+ 'status' => $wlan[2],
1043
+ 'quality_link' => $wlan[3],
1044
+ 'quality_level' => $wlan[4],
1045
+ 'quality_noise' => $wlan[5],
1046
+ 'dis_nwid' => $wlan[6],
1047
+ 'dis_crypt' => $wlan[7],
1048
+ 'dis_frag' => $wlan[8],
1049
+ 'dis_retry' => $wlan[9],
1050
+ 'dis_misc' => $wlan[10],
1051
+ 'mis_beac' => $wlan[11],
1052
+ );
1053
+ }
1054
+
1055
+ // Done
1056
+ return $return;
1057
+ }
1058
+
1059
+ /**
1060
+ * getSoundCards.
1061
+ *
1062
+ * @return array of soundcards
1063
+ */
1064
+ public function getSoundCards()
1065
+ {
1066
+
1067
+ // Time?
1068
+ if (!empty($this->settings['timer'])) {
1069
+ $t = new Timer('Sound cards');
1070
+ }
1071
+
1072
+ // This should be it
1073
+ $file = '/proc/asound/cards';
1074
+
1075
+ // eh?
1076
+ if (!is_file($file)) {
1077
+ Errors::add('Linux sound card detector', '/proc/asound/cards does not exist');
1078
+ }
1079
+
1080
+ // Get contents and parse
1081
+ $contents = Common::getContents($file);
1082
+
1083
+ // Parse
1084
+ if (preg_match_all('/^\s*(\d+)\s\[[\s\w]+\]:\s(.+)$/m', $contents, $matches, PREG_SET_ORDER) == 0) {
1085
+ return array();
1086
+ }
1087
+
1088
+ // eh?
1089
+ $cards = array();
1090
+
1091
+ // Deal with results
1092
+ foreach ($matches as $card) {
1093
+ $cards[] = array(
1094
+ 'number' => $card[1],
1095
+ 'card' => $card[2],
1096
+ );
1097
+ }
1098
+
1099
+ // Give cards
1100
+ return $cards;
1101
+ }
1102
+
1103
+ /**
1104
+ * getProcessStats.
1105
+ *
1106
+ * @return array of process stats
1107
+ */
1108
+ public function getProcessStats()
1109
+ {
1110
+
1111
+ // Time?
1112
+ if (!empty($this->settings['timer'])) {
1113
+ $t = new Timer('Process Stats');
1114
+ }
1115
+
1116
+ // We'll return this after stuffing it with useful info
1117
+ $result = array(
1118
+ 'exists' => true,
1119
+ 'totals' => array(
1120
+ 'running' => 0,
1121
+ 'zombie' => 0,
1122
+ 'sleeping' => 0,
1123
+ 'stopped' => 0,
1124
+ ),
1125
+ 'proc_total' => 0,
1126
+ 'threads' => 0,
1127
+ );
1128
+
1129
+ // Get all the paths to each process' status file
1130
+ $processes = (array) @glob('/proc/*/status', GLOB_NOSORT);
1131
+
1132
+ // Total
1133
+ $result['proc_total'] = count($processes);
1134
+
1135
+ // Go through each
1136
+ foreach ($processes as $process) {
1137
+
1138
+ // Don't waste time if we can't use it
1139
+ if (!is_readable($process)) {
1140
+ continue;
1141
+ }
1142
+
1143
+ // Get that file's contents
1144
+ $status_contents = Common::getContents($process);
1145
+
1146
+ // Try getting state
1147
+ @preg_match('/^State:\s+(\w)/m', $status_contents, $state_match);
1148
+
1149
+ // Well? Determine state
1150
+ switch ($state_match[1]) {
1151
+ case 'D': // disk sleep? wtf?
1152
+ case 'S':
1153
+ $result['totals']['sleeping']++;
1154
+ break;
1155
+ case 'Z':
1156
+ $result['totals']['zombie']++;
1157
+ break;
1158
+ case 'R':
1159
+ $result['totals']['running']++;
1160
+ break;
1161
+ case 'T':
1162
+ $result['totals']['stopped']++;
1163
+ break;
1164
+ }
1165
+
1166
+ // Try getting number of threads
1167
+ @preg_match('/^Threads:\s+(\d+)/m', $status_contents, $threads_match);
1168
+
1169
+ // Well?
1170
+ if ($threads_match) {
1171
+ list(, $threads) = $threads_match;
1172
+ }
1173
+
1174
+ // Append it on if it's good
1175
+ if (is_numeric($threads)) {
1176
+ $result['threads'] = $result['threads'] + $threads;
1177
+ }
1178
+ }
1179
+
1180
+ // Give off result
1181
+ return $result;
1182
+ }
1183
+
1184
+ /**
1185
+ * getServices.
1186
+ *
1187
+ * @return array the services
1188
+ */
1189
+ public function getServices()
1190
+ {
1191
+
1192
+ // Time?
1193
+ if (!empty($this->settings['timer'])) {
1194
+ $t = new Timer('Services');
1195
+ }
1196
+
1197
+ // We allowed?
1198
+ if (empty($this->settings['show']['services']) || !is_array($this->settings['services']) || count($this->settings['services']) == 0) {
1199
+ return array();
1200
+ }
1201
+
1202
+ // Temporarily keep statuses here
1203
+ $statuses = array();
1204
+
1205
+ $this->settings['services']['executables'] = (array) $this->settings['services']['executables'];
1206
+ $this->settings['services']['pidFiles'] = (array) $this->settings['services']['pidFiles'];
1207
+
1208
+ // Convert paths of executables to PID files
1209
+ $pids = array();
1210
+ $do_process_search = false;
1211
+ if (count($this->settings['services']['executables']) > 0) {
1212
+ $potential_paths = @glob('/proc/*/cmdline');
1213
+ if (is_array($potential_paths)) {
1214
+ $num_paths = count($potential_paths);
1215
+ $do_process_search = true;
1216
+ }
1217
+ }
1218
+
1219
+ // Should we go ahead and do the PID search based on executables?
1220
+ if ($do_process_search) {
1221
+ // Precache all process cmdlines
1222
+ for ($i = 0; $i < $num_paths; ++$i) {
1223
+ $cmdline_cache[$i] = explode("\x00", Common::getContents($potential_paths[$i]));
1224
+ }
1225
+
1226
+ // Go through the list of executables to search for
1227
+ foreach ($this->settings['services']['executables'] as $service => $exec) {
1228
+ // Go through pid file list. for loops are faster than foreach
1229
+ for ($i = 0; $i < $num_paths; ++$i) {
1230
+ $cmdline = $cmdline_cache[$i];
1231
+ $match = false;
1232
+ if (is_array($exec)) {
1233
+ $match = true;
1234
+ foreach ($exec as $argn => $argv) {
1235
+ if (isset($cmdline[$argn]) && $cmdline[$argn] != $argv) {
1236
+ $match = false;
1237
+ }
1238
+ }
1239
+ } elseif ($cmdline[0] == $exec) {
1240
+ $match = true;
1241
+ }
1242
+ // If this one matches, stop here and save it
1243
+ if ($match) {
1244
+ // Get pid out of path to cmdline file
1245
+ $pids[$service] = substr($potential_paths[$i], 6 /*strlen('/proc/')*/,
1246
+ strpos($potential_paths[$i], '/', 7) - 6);
1247
+ break;
1248
+ }
1249
+ }
1250
+ }
1251
+ }
1252
+
1253
+ // PID files
1254
+ foreach ($this->settings['services']['pidFiles'] as $service => $file) {
1255
+ $pid = Common::getContents($file, false);
1256
+ if ($pid != false && is_numeric($pid)) {
1257
+ $pids[$service] = $pid;
1258
+ }
1259
+ }
1260
+
1261
+ // Deal with PIDs
1262
+ foreach ($pids as $service => $pid) {
1263
+ $path = '/proc/'.$pid.'/status';
1264
+ $status_contents = Common::getContents($path, false);
1265
+ if ($status_contents == false) {
1266
+ $statuses[$service] = array('state' => 'Down', 'threads' => 'N/A', 'pid' => $pid);
1267
+ continue;
1268
+ }
1269
+
1270
+ // Attempt getting info out of it
1271
+ if (!preg_match_all('/^(\w+):\s+(\w+)/m', $status_contents, $status_matches, PREG_SET_ORDER)) {
1272
+ continue;
1273
+ }
1274
+
1275
+ // Initially set these as pointless
1276
+ $state = false;
1277
+ $threads = false;
1278
+ $mem = false;
1279
+
1280
+ // Go through
1281
+ for ($i = 0, $num = count($status_matches); $i < $num; ++$i) {
1282
+
1283
+ // What have we here?
1284
+ switch ($status_matches[$i][1]) {
1285
+
1286
+ // State section
1287
+ case 'State':
1288
+ switch ($status_matches[$i][2]) {
1289
+ case 'D': // disk sleep? wtf?
1290
+ case 'S':
1291
+ $state = 'Up (Sleeping)';
1292
+ break;
1293
+ case 'Z':
1294
+ $state = 'Zombie';
1295
+ break;
1296
+ // running
1297
+ case 'R':
1298
+ $state = 'Up (Running)';
1299
+ break;
1300
+ // stopped
1301
+ case 'T':
1302
+ $state = 'Up (Stopped)';
1303
+ break;
1304
+ default:
1305
+ continue;
1306
+ break;
1307
+ }
1308
+ break;
1309
+
1310
+ // Mem usage
1311
+ case 'VmRSS':
1312
+ if (is_numeric($status_matches[$i][2])) {
1313
+ $mem = $status_matches[$i][2] * 1024;
1314
+ } // Measured in kilobytes; we want bytes
1315
+ break;
1316
+
1317
+ // Thread count
1318
+ case 'Threads':
1319
+ if (is_numeric($status_matches[$i][2])) {
1320
+ $threads = $status_matches[$i][2];
1321
+ }
1322
+
1323
+ // Thread count should be last. Stop here to possibly save time assuming we have the other values
1324
+ if ($state !== false && $mem !== false && $threads !== false) {
1325
+ break;
1326
+ }
1327
+ break;
1328
+ }
1329
+ }
1330
+
1331
+ // Save info
1332
+ $statuses[$service] = array(
1333
+ 'state' => $state ? $state : '?',
1334
+ 'threads' => $threads,
1335
+ 'pid' => $pid,
1336
+ 'memory_usage' => $mem,
1337
+ );
1338
+ }
1339
+
1340
+ return $statuses;
1341
+ }
1342
+
1343
+ /**
1344
+ * getDistro.
1345
+ *
1346
+ * @return array the distro,version or false
1347
+ */
1348
+ public function getDistro()
1349
+ {
1350
+
1351
+ // Time?
1352
+ if (!empty($this->settings['timer'])) {
1353
+ $t = new Timer('Determining Distrobution');
1354
+ }
1355
+
1356
+ // Seems the best way of doing it, as opposed to calling 'lsb_release -a', parsing /etc/issue, or
1357
+ // just checking if distro specific version files exist without actually parsing them:
1358
+ // - Allows multiple files of the same name for different distros/versions of distros, provided each
1359
+ // - uses different regular expression syntax.
1360
+ // - Also permits files that contain only the distro release version and nothing else,
1361
+ // - in which case passing false instead of a regex string snags the contents.
1362
+ // - And even also supports empty files, and just uses said file to identify the distro and ignore version
1363
+
1364
+ $contents_distros = array(
1365
+ array(
1366
+ 'file' => '/etc/redhat-release',
1367
+ 'regex' => '/^CentOS.+release (?P<version>[\d\.]+) \((?P<codename>[^)]+)\)$/i',
1368
+ 'distro' => 'CentOS',
1369
+ ),
1370
+ array(
1371
+ 'file' => '/etc/redhat-release',
1372
+ 'regex' => '/^Red Hat.+release (?P<version>\S+) \((?P<codename>[^)]+)\)$/i',
1373
+ 'distro' => 'RedHat',
1374
+ ),
1375
+ array(
1376
+ 'file' => '/etc/lsb-release',
1377
+ 'closure' => function ($ini) {
1378
+ return ($info = @parse_ini_string($ini)) &&
1379
+ isset($info['DISTRIB_ID']) &&
1380
+ isset($info['DISTRIB_RELEASE']) &&
1381
+ isset($info['DISTRIB_CODENAME']) ? array(
1382
+ 'distro' => $info['DISTRIB_ID'],
1383
+ 'version' => $info['DISTRIB_RELEASE'],
1384
+ 'codename' => $info['DISTRIB_CODENAME'],
1385
+ ) : false;
1386
+ }
1387
+ ),
1388
+ array(
1389
+ 'file' => '/etc/os-release',
1390
+ 'closure' => function ($ini) {
1391
+ return ($info = @parse_ini_string($ini)) &&
1392
+ isset($info['ID']) &&
1393
+ isset($info['VERSION']) ? array(
1394
+ 'distro' => $info['ID'],
1395
+ 'version' => $info['VERSION']
1396
+ ) : false;
1397
+ },
1398
+ ),
1399
+ array(
1400
+ 'file' => '/etc/fedora-release',
1401
+ 'regex' => '/^Fedora(?: Core)? release (?P<version>\d+) \((?P<codename>[^)]+)\)$/',
1402
+ 'distro' => 'Fedora',
1403
+ ),
1404
+ array(
1405
+ 'file' => '/etc/gentoo-release',
1406
+ 'regex' => '/(?P<version>[\d\.]+)$/',
1407
+ 'distro' => 'Gentoo',
1408
+ ),
1409
+ array(
1410
+ 'file' => '/etc/SuSE-release',
1411
+ 'regex' => '/^VERSION = (?P<version>[\d\.]+)$/m',
1412
+ 'distro' => 'openSUSE',
1413
+ ),
1414
+ array(
1415
+ 'file' => '/etc/slackware-version',
1416
+ 'regex' => '/(?P<version>[\d\.]+)$/',
1417
+ 'distro' => 'Slackware',
1418
+ ),
1419
+ array(
1420
+ 'file' => '/etc/debian_version',
1421
+ 'distro' => 'Debian',
1422
+ ),
1423
+ );
1424
+
1425
+ foreach ($contents_distros as $distro) {
1426
+ if (!($contents = Common::getContents($distro['file'], false))) {
1427
+ continue;
1428
+ }
1429
+ if (isset($distro['closure']) && ($info = $distro['closure']($contents))) {
1430
+ return array(
1431
+ 'name' => ucfirst($info['distro']),
1432
+ 'version' => $info['version'].(isset($info['codename']) ? ' ('.ucfirst($info['codename']).')' : ''),
1433
+ );
1434
+ } elseif (isset($distro['regex']) && preg_match($distro['regex'], $contents, $info)) {
1435
+ return array(
1436
+ 'name' => $distro['distro'],
1437
+ 'version' => $info['version'].(isset($info['codename']) ? ' ('.ucfirst($info['codename']).')' : ''),
1438
+ );
1439
+ } elseif (isset($distro['distro'])) {
1440
+ return array(
1441
+ 'name' => $distro['distro'],
1442
+ 'version' => $contents,
1443
+ );
1444
+ }
1445
+ }
1446
+
1447
+ $existence_distros = array(
1448
+ '/etc/arch-release' => 'Arch',
1449
+ '/etc/mklinux-release' => 'MkLinux',
1450
+ '/etc/tinysofa-release ' => 'TinySofa',
1451
+ '/etc/turbolinux-release ' => 'TurboLinux',
1452
+ '/etc/yellowdog-release ' => 'YellowDog',
1453
+ '/etc/annvix-release ' => 'Annvix',
1454
+ '/etc/arklinux-release ' => 'Arklinux',
1455
+ '/etc/aurox-release ' => 'AuroxLinux',
1456
+ '/etc/blackcat-release ' => 'BlackCat',
1457
+ '/etc/cobalt-release ' => 'Cobalt',
1458
+ '/etc/immunix-release ' => 'Immunix',
1459
+ '/etc/lfs-release ' => 'Linux-From-Scratch',
1460
+ '/etc/linuxppc-release ' => 'Linux-PPC',
1461
+ '/etc/mklinux-release ' => 'MkLinux',
1462
+ '/etc/nld-release ' => 'NovellLinuxDesktop',
1463
+ );
1464
+
1465
+ foreach ($existence_distros as $file => $distro) {
1466
+ if (is_file($file)) {
1467
+ return array(
1468
+ 'name' => $distro,
1469
+ 'version' => false,
1470
+ );
1471
+ }
1472
+ }
1473
+
1474
+ // Return lack of result if we didn't find it
1475
+ return false;
1476
+ }
1477
+
1478
+ /**
1479
+ * getNumLoggedIn.
1480
+ *
1481
+ * @return number of logged in users with shells
1482
+ */
1483
+ public function getNumLoggedIn()
1484
+ {
1485
+
1486
+ // Snag command line of every process in system
1487
+ $procs = glob('/proc/*/cmdline', GLOB_NOSORT);
1488
+
1489
+ // Store unqiue users here
1490
+ $users = array();
1491
+
1492
+ // Each process
1493
+ foreach ($procs as $proc) {
1494
+
1495
+ // Does the process match a popular shell, such as bash, csh, etc?
1496
+ if (preg_match('/(?:bash|csh|zsh|ksh)$/', Common::getContents($proc, ''))) {
1497
+
1498
+ // Who owns it, anyway?
1499
+ $owner = fileowner(dirname($proc));
1500
+
1501
+ // Careful..
1502
+ if (!is_numeric($owner)) {
1503
+ continue;
1504
+ }
1505
+
1506
+ // Have we not seen this user before?
1507
+ if (!in_array($owner, $users)) {
1508
+ $users[] = $owner;
1509
+ }
1510
+ }
1511
+ }
1512
+
1513
+ // Give number of unique users with shells running
1514
+ return count($users);
1515
+ }
1516
+
1517
+ /**
1518
+ * getVirtualization. Potentially not very accurate especially since you can virtualize hypervisors,
1519
+ * kernel module names change frequently, you can load (some of) these modules if you aren't a host/guest, etc.
1520
+ *
1521
+ * @return array('type' => 'guest', 'method' => kvm or vmware or xen or openvz) or array('type' => 'host', 'methods' = ['intel', 'amd'])
1522
+ */
1523
+ public function getVirtualization()
1524
+ {
1525
+
1526
+ // Time?
1527
+ if (!empty($this->settings['timer'])) {
1528
+ $t = new Timer('Determining virtualization type');
1529
+ }
1530
+
1531
+ // OpenVZ host?
1532
+ if (is_file('/proc/vz/version')) {
1533
+ return array('type' => 'host', 'method' => 'OpenVZ');
1534
+ }
1535
+
1536
+ // OpenVZ guest?
1537
+ elseif (is_file('/proc/vz/veinfo')) {
1538
+ return array('type' => 'guest', 'method' => 'OpenVZ');
1539
+ }
1540
+
1541
+ // Try getting kernel modules
1542
+ $modules = array();
1543
+ if (preg_match_all('/^(\S+)/m', Common::getContents('/proc/modules', ''), $matches, PREG_SET_ORDER)) {
1544
+ foreach ($matches as $match) {
1545
+ $modules[] = $match[1];
1546
+ }
1547
+ }
1548
+
1549
+ // Sometimes /proc/modules is missing what is in this dir on VMs
1550
+ foreach (@glob('/sys/bus/pci/drivers/*') as $name) {
1551
+ $modules[] = basename($name);
1552
+ }
1553
+
1554
+ // VMware guest. Tested on debian under vmware fusion for mac...
1555
+ if (Common::anyInArray(array('vmw_balloon', 'vmwgfx', 'vmw_vmci'), $modules)) {
1556
+ return array('type' => 'guest', 'method' => 'VMWare');
1557
+ }
1558
+
1559
+ // VMware Host! tested on rhel6 running vmware..workstation?
1560
+ if (Common::anyInArray(array('vmnet', 'vmci', 'vmmon'), $modules)) {
1561
+ return array('type' => 'host', 'method' => 'VMWare');
1562
+ }
1563
+
1564
+ // Looks like it might be xen...
1565
+ if (Common::anyInArray(array('xenfs', 'xen_gntdev', 'xen_evtchn', 'xen_blkfront', 'xen_netfront'), $modules) || is_dir('/proc/xen')) {
1566
+
1567
+ // Guest or host?
1568
+ if (Common::anyInArray(array('xen-netback', 'xen_blkback'), $modules) || strpos('control_d', Common::getContents('/proc/xen/capabilities', '')) !== false) {
1569
+ return array('type' => 'host', 'method' => 'Xen');
1570
+ } else {
1571
+ return array('type' => 'guest', 'method' => 'Xen');
1572
+ }
1573
+ }
1574
+
1575
+ // VirtualBox Host! Tested on lucid running vbox..
1576
+ if (in_array('vboxdrv', $modules)) {
1577
+ return array('type' => 'host', 'method' => 'VirtualBox');
1578
+ }
1579
+
1580
+ // VirtualBox Guest! Tested on wheezy under mac vbox
1581
+ if (in_array('vboxguest', $modules)) {
1582
+ return array('type' => 'guest', 'method' => 'VirtualBox');
1583
+ }
1584
+
1585
+ // Looks like it might be KVM HOST!
1586
+ if (Common::anyInArray(array('kvm_intel', 'kvm_amd'), $modules)) {
1587
+ return array('type' => 'host', 'method' => 'KVM');
1588
+ }
1589
+
1590
+ // Looks like it might be a KVM or QEMU guest! This is a bit lame since Xen can also use virtio but its less likely (?)
1591
+ if (Common::anyInArray(array('virtio', 'virtio_balloon', 'virtio_pci', 'virtio-pci', 'virtio_blk', 'virtio_net'), $modules)) {
1592
+ return array('type' => 'guest', 'method' => 'Qemu/KVM');
1593
+ }
1594
+
1595
+ // idk
1596
+ return false;
1597
+ }
1598
+
1599
+ /**
1600
+ * Get overall CPU usage. Depends on determineCPUPercentage() being called prior.
1601
+ */
1602
+ public function getCPUUsage()
1603
+ {
1604
+ return $this->cpu_percent['overall'] === false ? false : $this->cpu_percent['overall'];
1605
+ }
1606
+
1607
+ /**
1608
+ * Parse lines from /proc/stat. Used by determineCPUPercentage function.
1609
+ * @param $key
1610
+ * @param $line
1611
+ * @return float
1612
+ */
1613
+ private function cpuPercent($key, $line)
1614
+ {
1615
+
1616
+ // With each iteration we compare what we got to last time's version
1617
+ // as the file changes every milisecond or something
1618
+ static $prev = array();
1619
+
1620
+ // Using regex/explode is excessive here, not unlike rest of linfo :/
1621
+ $ret = sscanf($line, '%Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu');
1622
+
1623
+ // Negative? That's crazy talk now
1624
+ foreach ($ret as $k => $v) {
1625
+ if ($v < 0) {
1626
+ $ret[$k] = 0;
1627
+ }
1628
+ }
1629
+
1630
+ // First time; set our vals
1631
+ if (!isset($prev[$key])) {
1632
+ $prev[$key] = $ret;
1633
+ }
1634
+
1635
+ // Subsequent time; difference with last time
1636
+ else {
1637
+ $orig = $ret;
1638
+ foreach ($ret as $k => $v) {
1639
+ $ret[$k] -= $prev[$key][$k];
1640
+ }
1641
+ $prev[$key] = $orig;
1642
+ }
1643
+
1644
+ // Refer back to top.c for the reasoning here. I just copied the algorithm without
1645
+ // trying to understand why.
1646
+ $scale = 100.0 / (float) array_sum($ret);
1647
+ $cpu_percent = $ret[0] * $scale;
1648
+
1649
+ return round($cpu_percent, 2);
1650
+ }
1651
+
1652
+ /**
1653
+ * Most controersial and different function in linfo. Updates $this->cpu_percent array. Sleeps 1 second
1654
+ * to do this which is how it gets accurate details. Code stolen from procps' source for the Linux top command.
1655
+ *
1656
+ * @void
1657
+ */
1658
+ public function determineCPUPercentage()
1659
+ {
1660
+ // Time?
1661
+ if (!empty($this->settings['timer'])) {
1662
+ $t = new Timer('Determining CPU usage');
1663
+ }
1664
+
1665
+ $iterations = 2;
1666
+
1667
+ // Probably only inline function here. Only used once so it makes sense.
1668
+
1669
+ for ($i = 0; $i < $iterations; ++$i) {
1670
+ $contents = Common::getContents('/proc/stat', false);
1671
+
1672
+ // Yay we can't read it so we won't sleep below!
1673
+ if (!$contents) {
1674
+ continue;
1675
+ }
1676
+
1677
+ // Overall system CPU usage
1678
+ if (preg_match('/^cpu\s+(.+)/', $contents, $m)) {
1679
+ $this->cpu_percent['overall'] = $this->cpuPercent('overall', $m[1]);
1680
+ }
1681
+
1682
+ // CPU usage per CPU
1683
+ if (preg_match_all('/^cpu(\d+)\s+(.+)/m', $contents, $cpus, PREG_SET_ORDER)) {
1684
+ foreach ($cpus as $cpu) {
1685
+ $this->cpu_percent['cpus'][$cpu[1]] = $this->cpuPercent('c'.$cpu[1], $cpu[2]);
1686
+ }
1687
+ }
1688
+
1689
+ // Following two lines make me want to puke as they go against everything linfo stands for
1690
+ // this functionality will always be disabled by default
1691
+ // Sleep *between* iterations and only if we're doing at least two of them
1692
+ if ($iterations > 1 && $i != $iterations - 1) {
1693
+ sleep(1);
1694
+ }
1695
+ }
1696
+ }
1697
+
1698
+ /**
1699
+ * Get brand/name of motherboard/server through /sys' interface to dmidecode.
1700
+ */
1701
+ public function getModel()
1702
+ {
1703
+ $info = array();
1704
+ $vendor = Common::getContents('/sys/devices/virtual/dmi/id/board_vendor', false);
1705
+ $name = Common::getContents('/sys/devices/virtual/dmi/id/board_name', false);
1706
+ $product = Common::getContents('/sys/devices/virtual/dmi/id/product_name', false);
1707
+
1708
+ if (!$name) {
1709
+ return false;
1710
+ }
1711
+
1712
+ // Don't add vendor to the mix if the name starts with it
1713
+ if ($vendor && strpos($name, $vendor) !== 0) {
1714
+ $info[] = $vendor;
1715
+ }
1716
+
1717
+ $info[] = $name;
1718
+
1719
+ $infostr = implode(' ', $info);
1720
+
1721
+ // product name is usually bullshit, but *occasionally* it's a useful name of the computer, such as
1722
+ // dell latitude e6500 or hp z260
1723
+ if ($product && strpos($name, $product) === false && strpos($product, 'Filled') === false) {
1724
+ return $product.' ('.$infostr.')';
1725
+ } else {
1726
+ return $infostr;
1727
+ }
1728
+ }
1729
+ }
lib/Linfo/OS/Minix.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Parsers\CallExt;
25
+
26
+ /*
27
+ * Get info on a Minix system
28
+ * ---
29
+ * Note: the cli tools on minix are so meager that getting real detail
30
+ * out of it (like nic stats / fs types / etc) is either difficult or
31
+ * impossible. Nevertheless, this is my attempt at doing so.
32
+ */
33
+
34
+ class Minix extends OS
35
+ {
36
+ // Store these here
37
+ protected $settings,
38
+ $exec;
39
+
40
+ // Start us off by localizing the settings and initializing the external
41
+ // application running class
42
+ public function __construct($settings)
43
+ {
44
+
45
+ // Localize settings
46
+ $this->settings = $settings;
47
+
48
+ // Start up external app loader
49
+ $this->exec = new CallExt();
50
+
51
+ // Have it look in these places
52
+ $this->exec->setSearchPaths(array('/usr/bin', '/usr/local/bin', '/bin'));
53
+ }
54
+
55
+ // Mounted file systems
56
+ // ---
57
+ // Note: the `mount` command does not have file system type
58
+ // and php's disk_free_space/disk_total_space functions don't seem
59
+ // to work here
60
+ public function getMounts()
61
+ {
62
+
63
+ // Try using the `mount` command to get mounted file systems
64
+ try {
65
+ $res = $this->exec->exec('mount');
66
+ } catch (Exception $e) {
67
+ return array();
68
+ }
69
+
70
+ // Try matching up the output
71
+ if (preg_match_all('/^(\S+) is .+ mounted on (\S+) \(.+\)$/m', $res, $mount_matches, PREG_SET_ORDER) == 0) {
72
+ return array();
73
+ }
74
+
75
+ // Store them here
76
+ $mounts = array();
77
+
78
+ // Go through each match
79
+ foreach ($mount_matches as $mount) {
80
+
81
+ // These might be a waste
82
+ $size = @disk_total_space($mount[2]);
83
+ $free = @disk_free_space($mount[2]);
84
+ $used = $size - $free;
85
+
86
+ // Save it
87
+ $mounts[] = array(
88
+ 'device' => $mount[1],
89
+ 'mount' => $mount[2],
90
+ 'type' => '?', // Haven't a clue on how to get this on minix
91
+ 'size' => $size,
92
+ 'used' => $used,
93
+ 'free' => $free,
94
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
95
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
96
+ );
97
+ }
98
+
99
+ // Return them
100
+ return $mounts;
101
+ }
102
+
103
+ // Get network interfaces
104
+ // ---
105
+ // netstat isn't installed by default and ifconfig doesn't have
106
+ // much functionality for viewing status, so I can't seem to get
107
+ // more than just name of interface
108
+ public function getNet()
109
+ {
110
+
111
+ // Try getting it.
112
+ try {
113
+ $res = $this->exec->exec('ifconfig', '-a');
114
+ } catch (Exception $e) {
115
+ return array();
116
+ }
117
+
118
+ // Match up the entries
119
+ if (preg_match_all('/^([^:]+)/m', $res, $net_matches, PREG_SET_ORDER) == 0) {
120
+ return array();
121
+ }
122
+
123
+ // Store them here
124
+ $nets = array();
125
+
126
+ // Go through each
127
+ foreach ($net_matches as $net) {
128
+
129
+ // Save this one
130
+ $nets[$net[1]] = array(
131
+ 'state' => '?',
132
+ 'type' => '?',
133
+ );
134
+ }
135
+
136
+ // Give them
137
+ return $nets;
138
+ }
139
+ }
lib/Linfo/OS/NetBSD.php ADDED
@@ -0,0 +1,498 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Meta\Timer;
25
+ use Linfo\Meta\Errors;
26
+ use Linfo\Common;
27
+
28
+ /*
29
+ * NetBSD info class. Differs slightly from FreeBSD's
30
+ * TODO: netbsd's /proc contains really useful info
31
+ * possibly get some stuff from it if it exists
32
+ */
33
+
34
+ class NetBSD extends BSDcommon
35
+ {
36
+ // Encapsulate these
37
+ protected $settings,
38
+ $exec,
39
+ $error,
40
+ $dmesg;
41
+
42
+ // Start us off
43
+ public function __construct($settings)
44
+ {
45
+
46
+ // Initiate parent
47
+ parent::__construct($settings);
48
+
49
+ // We search these folders for our commands
50
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/pkg/bin', '/usr/sbin'));
51
+
52
+ // sysctl values we'll access below
53
+ $this->GetSysCTL(array('kern.boottime', 'vm.loadavg'), false);
54
+ }
55
+
56
+ // Mounted file systems
57
+ public function getMounts()
58
+ {
59
+
60
+ // Time it
61
+ if (!empty($this->settings['timer'])) {
62
+ $t = new Timer('Mounted file systems');
63
+ }
64
+
65
+ // Try getting mount command
66
+ try {
67
+ $res = $this->exec->exec('mount');
68
+ } catch (Exception $e) {
69
+ Errors::add('Linfo Core', 'Error running `mount` command');
70
+
71
+ return array();
72
+ }
73
+
74
+ // Match the file systems
75
+ if (@preg_match_all('/^(\S+) on (\S+) type (\S+)/m', $res, $mount_match, PREG_SET_ORDER) == 0) {
76
+ return array();
77
+ }
78
+
79
+ // Store them here
80
+ $mounts = array();
81
+
82
+ // Go through each
83
+ foreach ($mount_match as $mount) {
84
+ // Should we not show this?
85
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
86
+ continue;
87
+ }
88
+
89
+ // Get these
90
+ $size = @disk_total_space($mount[2]);
91
+ $free = @disk_free_space($mount[2]);
92
+ $used = $size - $free;
93
+
94
+ // Might be good, go for it
95
+ $mounts[] = array(
96
+ 'device' => $mount[1],
97
+ 'mount' => $mount[2],
98
+ 'type' => $mount[3],
99
+ 'size' => $size ,
100
+ 'used' => $used,
101
+ 'free' => $free,
102
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
103
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
104
+ );
105
+ }
106
+
107
+ // Give them
108
+ return $mounts;
109
+ }
110
+
111
+ // Get the always gloatable uptime
112
+ public function getUpTime()
113
+ {
114
+ // Time?
115
+ if (!empty($this->settings['timer'])) {
116
+ $t = new Timer('Uptime');
117
+ }
118
+
119
+ // Use sysctl
120
+ $booted = strtotime($this->sysctl['kern.boottime']);
121
+
122
+ // Give it
123
+ return array(
124
+ 'text' => Common::secondsConvert(time() - $booted),
125
+ 'bootedTimestamp' => $booted,
126
+ );
127
+ }
128
+
129
+ // Get network devices
130
+ public function getNet()
131
+ {
132
+ // Time?
133
+ if (!empty($this->settings['timer'])) {
134
+ $t = new Timer('Network Devices');
135
+ }
136
+
137
+ // Try using netstat
138
+ try {
139
+ $res = $this->exec->exec('netstat', '-nbdi');
140
+ } catch (Exception $e) {
141
+ Errors::add('Linfo Core', 'Error using `netstat` to get network info');
142
+
143
+ return array();
144
+ }
145
+
146
+ // Match the interfaces themselves
147
+ if (preg_match_all('/^(\S+)\s+\d+\s+<Link>\s+[a-z0-9\:]+\s+(\d+)\s+(\d+)\s+\d+$/m', $res, $net_matches, PREG_SET_ORDER) == 0) {
148
+ return array();
149
+ }
150
+
151
+ // Store statuses for each here
152
+ $statuses = array();
153
+
154
+ // Try using ifconfig to get statuses for each interface
155
+ try {
156
+ $ifconfig = $this->exec->exec('ifconfig', '-a');
157
+ $current_nic = false;
158
+ foreach ((array) explode("\n", $ifconfig) as $line) {
159
+ if (preg_match('/^(\w+):/m', $line, $m) == 1) {
160
+ $current_nic = $m[1];
161
+ } elseif ($current_nic != false && preg_match('/^\s+status: (\w+)$/m', $line, $m) == 1) {
162
+ $statuses[$current_nic] = $m[1];
163
+ $current_nic = false;
164
+ }
165
+ }
166
+ } catch (Exception $e) {
167
+ }
168
+
169
+ // Store interfaces here
170
+ $nets = array();
171
+
172
+ // Go through each
173
+ foreach ($net_matches as $net) {
174
+
175
+ // See if we successfully found a status, and use it if so
176
+ switch (array_key_exists($net[1], $statuses) ? $statuses[$net[1]] : 'unknown') {
177
+ case 'active':
178
+ $state = 'up';
179
+ break;
180
+ case 'inactive':
181
+ $state = 'down';
182
+ break;
183
+ default:
184
+ $state = 'unknown';
185
+ break;
186
+ }
187
+
188
+ // Save this interface
189
+ $nets[$net[1]] = array(
190
+ 'recieved' => array(
191
+ 'bytes' => $net[2],
192
+ ),
193
+ 'sent' => array(
194
+ 'bytes' => $net[3],
195
+ ),
196
+ 'state' => $state,
197
+ 'type' => 'Unknown', // TODO
198
+ );
199
+ }
200
+
201
+ // Give it
202
+ return $nets;
203
+ }
204
+
205
+ // Get drives
206
+ public function getHD()
207
+ {
208
+
209
+ // Time?
210
+ if (!empty($this->settings['timer'])) {
211
+ $t = new Timer('CPU');
212
+ }
213
+
214
+ $drives = array();
215
+ $curr_hd = false;
216
+
217
+ // Parse dmesg
218
+ foreach (explode("\n", $this->dmesg) as $dmesg_line) {
219
+
220
+ // Beginning of a drive entry
221
+ if (preg_match('/^([a-z]{2}\d) at [^:]+: <([^>]+)> (\w+)/', $dmesg_line, $init_match)) {
222
+
223
+ // If it's a cdrom just stop here and save it.
224
+ if ($init_match[3] == 'cdrom') {
225
+
226
+ // Save entry
227
+ $drives[] = array(
228
+ 'name' => preg_match('/^([^,]+)/', $init_match[2], $cd_match) ? $cd_match[1] : $init_match[2],
229
+ 'vendor' => false, // I don't know if this is possible
230
+ 'device' => '/dev/'.$init_match[1],
231
+
232
+ // Not sure how to get the following:
233
+ 'size' => false,
234
+ 'reads' => false,
235
+ 'writes' => false,
236
+ );
237
+ }
238
+
239
+ // Otherwise prep for further info on a later line
240
+ elseif ($init_match[3] == 'disk') {
241
+ $curr_hd = array($init_match[1], $init_match[2], $init_match[3]);
242
+ }
243
+
244
+ // Don't go any farther with this line
245
+ continue;
246
+ }
247
+
248
+ // A hard drive setting line, that has size and stuff
249
+ elseif ($curr_hd != false && preg_match('/^'.preg_quote($curr_hd[0]).': (\d+) MB/', $dmesg_line, $drive_match)) {
250
+
251
+ // Try getting vendor or name
252
+ $make = preg_match('/^([^,]+), ([^,]+)/', $curr_hd[1], $v_match) ? array($v_match[1], $v_match[2]) : false;
253
+
254
+ // Save entry
255
+ $drives[] = array(
256
+ 'name' => $make ? $make[1] : $curr_hd[1],
257
+ 'vendor' => $make ? $make[0] : false,
258
+ 'device' => '/dev/'.$curr_hd[0],
259
+ 'size' => $drive_match[1] * 1048576,
260
+
261
+ // Not sure how to get the following:
262
+ 'reads' => false,
263
+ 'writes' => false,
264
+ );
265
+
266
+ // We're done with this drive
267
+ $curr_hd = false;
268
+
269
+ // Don't go any farther with this line
270
+ continue;
271
+ }
272
+ }
273
+
274
+ // Give drives
275
+ return $drives;
276
+ }
277
+
278
+ // Get cpu's
279
+ public function getCPU()
280
+ {
281
+
282
+ // Time?
283
+ if (!empty($this->settings['timer'])) {
284
+ $t = new Timer('CPU');
285
+ }
286
+
287
+ // Parse dmesg
288
+ if (preg_match_all('/^cpu\d+ at [^:]+: (\S+) ([^,]+), (\d+)MHz/m', $this->dmesg, $cpu_matches, PREG_SET_ORDER) == 0) {
289
+ return array();
290
+ }
291
+
292
+ // Store them here
293
+ $cpus = array();
294
+
295
+ // Store as many as possible
296
+ foreach ($cpu_matches as $cpu_m) {
297
+ $cpus[] = array(
298
+ 'Model' => $cpu_m[2],
299
+ 'MHz' => $cpu_m[3],
300
+ 'Vendor' => $cpu_m[1],
301
+ );
302
+ }
303
+
304
+ // Give them
305
+ return $cpus;
306
+ }
307
+
308
+ // Get ram usage
309
+ public function getRam()
310
+ {
311
+
312
+ // Time?
313
+ if (!empty($this->settings['timer'])) {
314
+ $t = new Timer('Memory');
315
+ }
316
+
317
+ // Start us off at zilch
318
+ $return = array();
319
+ $return['type'] = 'Virtual';
320
+ $return['total'] = 0;
321
+ $return['free'] = 0;
322
+ $return['swapTotal'] = 0;
323
+ $return['swapFree'] = 0;
324
+ $return['swapInfo'] = array();
325
+
326
+ // Get virtual memory usage with vmstat
327
+ try {
328
+ // Get result of vmstat
329
+ $vmstat = $this->exec->exec('vmstat', '-s');
330
+
331
+ // Get bytes per page
332
+ preg_match('/^\s+(\d+) bytes per page$/m', $vmstat, $bytes_per_page);
333
+
334
+ // Did we?
335
+ if (!is_numeric($bytes_per_page[1]) || $bytes_per_page[1] < 0) {
336
+ throw new Exception('Error parsing page size out of `vmstat`');
337
+ } else {
338
+ list(, $bytes_per_page) = $bytes_per_page;
339
+ }
340
+
341
+ // Get available ram
342
+ preg_match('/^\s+(\d+) pages managed$/m', $vmstat, $available_ram);
343
+
344
+ // Did we?
345
+ if (!is_numeric($available_ram[1])) {
346
+ throw new Exception('Error parsing managed pages out of `vmstat`');
347
+ } else {
348
+ list(, $available_ram) = $available_ram;
349
+ }
350
+
351
+ // Get free ram
352
+ preg_match('/^\s+(\d+) pages free$/m', $vmstat, $free_ram);
353
+
354
+ // Did we?
355
+ if (!is_numeric($free_ram[1])) {
356
+ throw new Exception('Error parsing free pages out of `vmstat`');
357
+ } else {
358
+ list(, $free_ram) = $free_ram;
359
+ }
360
+
361
+ // Okay, cool. Total them up
362
+ $return['total'] = $available_ram * $bytes_per_page;
363
+ $return['free'] = $free_ram * $bytes_per_page;
364
+ } catch (Exception $e) {
365
+ Errors::add('Linfo Core', 'Error using `vmstat` to get memory usage');
366
+ }
367
+
368
+ // Get swap
369
+ try {
370
+ $swapinfo = $this->exec->exec('swapctl', '-l');
371
+ @preg_match_all('/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)/m', $swapinfo, $sm, PREG_SET_ORDER);
372
+ foreach ($sm as $swap) {
373
+ $return['swapTotal'] += $swap[2] * 1024;
374
+ $return['swapFree'] += (($swap[2] - $swap[3]) * 1024);
375
+ $ft = is_file($swap[1]) ? @filetype($swap[1]) : 'Unknown'; // TODO: I'd rather it be Partition or File
376
+ $return['swapInfo'][] = array(
377
+ 'device' => $swap[1],
378
+ 'size' => $swap[2] * 1024,
379
+ 'used' => $swap[3] * 1024,
380
+ 'type' => ucfirst($ft),
381
+ );
382
+ }
383
+ } catch (Exception $e) {
384
+ Errors::add('Linfo Core', 'Error using `swapctl` to get swap usage');
385
+ }
386
+
387
+ // Give it off
388
+ return $return;
389
+ }
390
+
391
+ // Get devices
392
+ public function getDevs()
393
+ {
394
+
395
+ // Time?
396
+ if (!empty($this->settings['timer'])) {
397
+ $t = new Timer('Hardware Devices');
398
+ }
399
+
400
+ // Get them
401
+ if (preg_match_all('/^([a-z]+\d+) at ([a-z]+)\d+[^:]+:(.+)/m', $this->dmesg, $devices_match, PREG_SET_ORDER) == 0) {
402
+ return array();
403
+ }
404
+
405
+ // Keep them here
406
+ $devices = array();
407
+
408
+ // Store the type column for each key
409
+ $sort_type = array();
410
+
411
+ // Stuff it
412
+ foreach ($devices_match as $device) {
413
+ if ($device[2] == 'ppb' || strpos($device[3], 'vendor') !== false) {
414
+ continue;
415
+ }
416
+
417
+ // Only call this once
418
+ $type = strtoupper($device[2]);
419
+
420
+ // Stuff entry
421
+ $devices[] = array(
422
+ 'vendor' => false, // Maybe todo?
423
+ 'device' => $device[3],
424
+ 'type' => $type,
425
+ );
426
+
427
+ // For the sorting of this entry
428
+ $sort_type[] = $type;
429
+ }
430
+
431
+ // Sort
432
+ array_multisort($devices, SORT_STRING, $sort_type);
433
+
434
+ // Give them
435
+ return $devices;
436
+ }
437
+
438
+ // Get stats on processes
439
+ public function getProcessStats()
440
+ {
441
+
442
+ // Time?
443
+ if (!empty($this->settings['timer'])) {
444
+ $t = new Timer('Process Stats');
445
+ }
446
+
447
+ // We'll return this after stuffing it with useful info
448
+ $result = array(
449
+ 'exists' => true,
450
+ 'totals' => array(
451
+ 'running' => 0,
452
+ 'zombie' => 0,
453
+ 'sleeping' => 0,
454
+ 'stopped' => 0,
455
+ ),
456
+ 'proc_total' => 0,
457
+ 'threads' => false, // I'm not sure how to get this
458
+ );
459
+
460
+ // Use ps
461
+ try {
462
+ // Get it
463
+ $ps = $this->exec->exec('ps', 'ax');
464
+
465
+ // Match them
466
+ preg_match_all('/^\s*\d+\s+[\w?]+\s+([A-Z])\S*\s+.+$/m', $ps, $processes, PREG_SET_ORDER);
467
+
468
+ // Get total
469
+ $result['proc_total'] = count($processes);
470
+
471
+ // Go through
472
+ foreach ($processes as $process) {
473
+ switch ($process[1]) {
474
+ case 'S':
475
+ case 'I':
476
+ $result['totals']['sleeping']++;
477
+ break;
478
+ case 'Z':
479
+ $result['totals']['zombie']++;
480
+ break;
481
+ case 'R':
482
+ case 'D':
483
+ case 'O':
484
+ $result['totals']['running']++;
485
+ break;
486
+ case 'T':
487
+ $result['totals']['stopped']++;
488
+ break;
489
+ }
490
+ }
491
+ } catch (Exception $e) {
492
+ Errors::add('Linfo Core', 'Error using `ps` to get process info');
493
+ }
494
+
495
+ // Give
496
+ return $result;
497
+ }
498
+ }
lib/Linfo/OS/OS.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2014 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ /**
21
+ * Keep out hackers...
22
+ */
23
+ namespace Linfo\OS;
24
+
25
+ use Linfo\Exceptions\FatalException;
26
+
27
+ abstract class OS
28
+ {
29
+ public function __call($name, $args)
30
+ {
31
+ throw new FatalException('Method '.$name.' not present.');
32
+ }
33
+
34
+ /**
35
+ * getAccessedIP
36
+ *
37
+ * @return string SERVER_ADDR or LOCAL_ADDR key in $_SERVER superglobal or Unknown
38
+ */
39
+ public function getAccessedIP()
40
+ {
41
+ return isset($_SERVER['SERVER_ADDR']) && $_SERVER['SERVER_ADDR'] ? $_SERVER['SERVER_ADDR'] : (isset($_SERVER['LOCAL_ADDR']) && $_SERVER['LOCAL_ADDR'] ? $_SERVER['LOCAL_ADDR'] : 'Unknown');
42
+ }
43
+
44
+ /**
45
+ * getWebService
46
+ *
47
+ * @return string SERVER_SOFTWARE key in $_SERVER superglobal or Unknown
48
+ */
49
+ public function getWebService()
50
+ {
51
+ return isset($_SERVER['SERVER_SOFTWARE']) && $_SERVER['SERVER_SOFTWARE'] ? $_SERVER['SERVER_SOFTWARE'] : 'Unknown';
52
+ }
53
+
54
+ /**
55
+ * getPhpVersion
56
+ *
57
+ * @return string the version of php
58
+ */
59
+ public function getPhpVersion()
60
+ {
61
+ return phpversion();
62
+ }
63
+
64
+ /**
65
+ * getCPUArchitecture
66
+ *
67
+ * @return string the arch and bits
68
+ */
69
+ public function getCPUArchitecture()
70
+ {
71
+ return php_uname('m');
72
+ }
73
+
74
+ /**
75
+ * getKernel
76
+ *
77
+ * @return string the OS kernel. A few OS classes override this.
78
+ */
79
+ public function getKernel()
80
+ {
81
+ return php_uname('r');
82
+ }
83
+
84
+ /**
85
+ * getHostName
86
+ *
87
+ * @return string the OS' hostname A few OS classes override this.
88
+ */
89
+ public function getHostName()
90
+ {
91
+
92
+ // Take advantage of that function again
93
+ return php_uname('n');
94
+ }
95
+
96
+ /**
97
+ * getOS
98
+ *
99
+ * @return string the OS' name.
100
+ */
101
+ public function getOS()
102
+ {
103
+ $parts = explode('\\', get_class($this));
104
+ return array_pop($parts);
105
+ }
106
+ }
lib/Linfo/OS/OpenBSD.php ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010, 2012 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Meta\Timer;
25
+ use Linfo\Meta\Errors;
26
+ use Linfo\Common;
27
+
28
+ /*
29
+ * OpenBSD info class.
30
+ * todo: as much functionality as the freebsd version
31
+ */
32
+
33
+ class OpenBSD extends BSDcommon
34
+ {
35
+ // Encapsulate these
36
+ protected $settings,
37
+ $exec,
38
+ $dmesg;
39
+
40
+ // Start us off
41
+ public function __construct($settings)
42
+ {
43
+
44
+ // Initiate parent
45
+ parent::__construct($settings);
46
+
47
+ // We search these folders for our commands
48
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
49
+
50
+ // sysctl values we'll access below
51
+ $this->GetSysCTL(array(
52
+
53
+ // Has unix timestamp of boot time
54
+ 'kern.boottime',
55
+
56
+ // Ram stuff
57
+ 'hw.physmem',
58
+
59
+ // CPU related
60
+ 'hw.model',
61
+ 'hw.ncpu',
62
+ 'hw.cpuspeed',
63
+
64
+ 'vm.loadavg',
65
+ ), false);
66
+ }
67
+
68
+ // What we should leave out
69
+ public function getContains()
70
+ {
71
+ return array(
72
+ 'drives_rw_stats' => false,
73
+ 'hw_vendor' => false,
74
+ 'drives_vendor' => false,
75
+ );
76
+ }
77
+
78
+ // Get mounted file systems and their disk usage stats
79
+ public function getMounts()
80
+ {
81
+ // Time?
82
+ if (!empty($this->settings['timer'])) {
83
+ $t = new Timer('Mounted file systems');
84
+ }
85
+
86
+ // Get result of mount command
87
+ try {
88
+ $mount_res = $this->exec->exec('mount');
89
+ } catch (Exception $e) {
90
+ Errors::add('Linfo Core', 'Error running `mount` command');
91
+
92
+ return array();
93
+ }
94
+
95
+ // Match that up
96
+ if (preg_match_all('/^(\S+) on (\S+) type (\S+) \(.+\)$/m', $mount_res, $mount_matches, PREG_SET_ORDER) == 0) {
97
+ return array();
98
+ }
99
+
100
+ // Store them here
101
+ $mounts = array();
102
+
103
+ // Go through
104
+ foreach ($mount_matches as $mount) {
105
+ // Should we not show this?
106
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
107
+ continue;
108
+ }
109
+
110
+ // Get these
111
+ $size = @disk_total_space($mount[2]);
112
+ $free = @disk_free_space($mount[2]);
113
+ $used = $size - $free;
114
+
115
+ // Might be good, go for it
116
+ $mounts[] = array(
117
+ 'device' => $mount[1],
118
+ 'mount' => $mount[2],
119
+ 'type' => $mount[3],
120
+ 'size' => $size ,
121
+ 'used' => $used,
122
+ 'free' => $free,
123
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
124
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
125
+ );
126
+ }
127
+
128
+ // Give it
129
+ return $mounts;
130
+ }
131
+
132
+ // Get memory usage statistics
133
+ public function getRam()
134
+ {
135
+ $return = array();
136
+ $return['swapTotal'] = 0;
137
+ $return['swapFree'] = 0;
138
+ $return['swapInfo'] = array();
139
+
140
+ // Get amount of real hard ram, in bytes
141
+ $return['total'] = $this->sysctl['hw.physmem'];
142
+
143
+ // Get real
144
+ try {
145
+ $vmstat = $this->exec->exec('vmstat');
146
+ if (preg_match('/\s\d\s\d\s\d\s+\d+\s+(\d+)/', $vmstat, $vmstat_match)) {
147
+ $hard_ram_free = $vmstat_match[1];
148
+ $return['type'] = 'Physical';
149
+ $return['free'] = $return['total'] - ($hard_ram_free * 1024);
150
+ }
151
+ } catch (Exception $e) {
152
+ Errors::add('Linfo Core', 'Error using `vmstat` to get memory usage usage');
153
+ }
154
+
155
+ // Get swap
156
+ try {
157
+ $swapinfo = $this->exec->exec('swapctl', '-k');
158
+ @preg_match_all('/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)/m', $swapinfo, $sm, PREG_SET_ORDER);
159
+ foreach ($sm as $swap) {
160
+ $return['swapTotal'] += $swap[2] * 1024;
161
+ $return['swapFree'] += (($swap[2] - $swap[3]) * 1024);
162
+ $ft = is_file($swap[1]) ? @filetype($swap[1]) : 'Unknown'; // TODO: I'd rather it be Partition or File
163
+ $return['swapInfo'][] = array(
164
+ 'device' => $swap[1],
165
+ 'size' => $swap[2] * 1024,
166
+ 'used' => $swap[3] * 1024,
167
+ 'type' => ucfirst($ft),
168
+ );
169
+ }
170
+ } catch (Exception $e) {
171
+ Errors::add('Linfo Core', 'Error using `swapctl` to get swap usage');
172
+ }
173
+
174
+ // Give it
175
+ return $return;
176
+ }
177
+
178
+ // Get hardware devices
179
+ public function getDevs()
180
+ {
181
+ // Time?
182
+ if (!empty($this->settings['timer'])) {
183
+ $t = new Timer('Hardware Devices');
184
+ }
185
+
186
+ // Match them
187
+ if (preg_match_all('/([a-z]+\d+) at ([a-z]+)\d*.+ "(.+)"/m', $this->dmesg, $devices_match, PREG_SET_ORDER) == 0) {
188
+ return array();
189
+ }
190
+
191
+ // Store them here
192
+ $devices = array();
193
+
194
+ // Stuff them
195
+ foreach ($devices_match as $match) {
196
+ $type = strtoupper($match[2]);
197
+
198
+ $devices[] = array(
199
+ 'vendor' => false, // hmm
200
+ 'device' => $match[3],
201
+ 'type' => $type,
202
+
203
+ );
204
+ }
205
+
206
+ // Give them
207
+ return $devices;
208
+ }
209
+
210
+ // Get hard disk drives and the like
211
+ public function getHD()
212
+ {
213
+
214
+ // Time?
215
+ if (!empty($this->settings['timer'])) {
216
+ $t = new Timer('CPU');
217
+ }
218
+
219
+ $drives = array();
220
+ $curr_hd = false;
221
+
222
+ // Parse dmesg
223
+ foreach (explode("\n", $this->dmesg) as $dmesg_line) {
224
+ if (preg_match('/^(\w+) at .+<([^>]+)>/', $dmesg_line, $hd_start_match)) {
225
+ $curr_hd = $hd_start_match;
226
+ } elseif ($curr_hd !== false && preg_match('/^'.preg_quote($curr_hd[1]).':.*\b(\d+)MB/', $dmesg_line, $hd_spec_match)) {
227
+ $drives[] = array(
228
+ 'name' => trim($curr_hd[2], ', '),
229
+ 'vendor' => false,
230
+ 'device' => '/dev/'.$curr_hd[1],
231
+ 'size' => $hd_spec_match[1] * 1048576,
232
+
233
+ // Not sure how to get the following:
234
+ 'reads' => false,
235
+ 'writes' => false,
236
+
237
+ );
238
+ $curr_hd = false;
239
+ } else {
240
+ $curr_hd = false;
241
+ }
242
+ }
243
+
244
+ // give it
245
+ return $drives;
246
+ }
247
+
248
+ // Get uptime
249
+ public function getUpTime()
250
+ {
251
+
252
+ // Time?
253
+ if (!empty($this->settings['timer'])) {
254
+ $t = new Timer('Uptime');
255
+ }
256
+
257
+ // Short and sweet
258
+ $booted = $this->sysctl['kern.boottime'];
259
+
260
+ if ($booted == false) {
261
+ return 'Unknown';
262
+ }
263
+
264
+ // Is it not a timestamp?
265
+ if (!is_numeric($booted)) {
266
+ $booted = strtotime($booted);
267
+ }
268
+
269
+ // Give it
270
+ return array(
271
+ 'text' => Common::secondsConvert(time() - $booted),
272
+ 'bootedTimestamp' => $booted,
273
+ );
274
+ }
275
+
276
+ // Get network devices, their stats, status, and type
277
+ public function getNet()
278
+ {
279
+
280
+ // Time?
281
+ if (!empty($this->settings['timer'])) {
282
+ $t = new Timer('Network Devices');
283
+ }
284
+
285
+ // Get result of netstat command
286
+ try {
287
+ $res = $this->exec->exec('netstat', '-nbi');
288
+ } catch (Exception $e) {
289
+ Errors::add('Linfo Core', 'Error using `netstat` to get network info');
290
+
291
+ return array();
292
+ }
293
+
294
+ // Get initial matches
295
+ if (preg_match_all('/^([a-z0-9]+)\*?\s+\d+\s+<Link>(?:\s+[a-z0-9\:]+)?\s+(\d+)\s+(\d+)$/m', $res, $net_matches, PREG_SET_ORDER) == 0) {
296
+ return array();
297
+ }
298
+
299
+ // Store statuses for each here
300
+ $statuses = array();
301
+
302
+ // Try using ifconfig to get statuses for each interface
303
+ try {
304
+ $ifconfig = $this->exec->exec('ifconfig', '-a');
305
+ $current_nic = false;
306
+ foreach ((array) explode("\n", $ifconfig) as $line) {
307
+ if (preg_match('/^(\w+):/m', $line, $m) == 1) {
308
+ $current_nic = $m[1];
309
+ } elseif ($current_nic != false && preg_match('/^\s+status: ([^$]+)$/m', $line, $m) == 1) {
310
+ $statuses[$current_nic] = $m[1];
311
+ $current_nic = false;
312
+ }
313
+ }
314
+ } catch (Exception $e) {
315
+ }
316
+
317
+ // Get type from dmesg boot
318
+ $type = array();
319
+ $type_nics = array();
320
+
321
+ // Store the to-be detected nics here
322
+ foreach ($net_matches as $net) {
323
+ $type_nics[] = $net[1];
324
+ }
325
+
326
+ // Go through dmesg looking for them
327
+ if (preg_match_all('/^(\w+) at ([a-zA-Z]+)\d*.+address [\w\:]+.+/m', $this->dmesg, $type_match, PREG_SET_ORDER)) {
328
+
329
+ // Go through each
330
+ foreach ($type_match as $type_nic_match) {
331
+
332
+ // Is this one of our detected nics?
333
+ if (in_array($type_nic_match[1], $type_nics)) {
334
+
335
+ // Yes; save status
336
+ $type[$type_nic_match[1]] = $type_nic_match[2];
337
+ }
338
+ }
339
+ }
340
+
341
+ // Save them here
342
+ $nets = array();
343
+
344
+ // Go through each
345
+ foreach ($net_matches as $net) {
346
+
347
+ // See if we successfully found a status, and use it if so
348
+ switch (array_key_exists($net[1], $statuses) ? $statuses[$net[1]] : 'unknown') {
349
+ case 'active':
350
+ $state = 'up';
351
+ break;
352
+ case 'inactive':
353
+ case 'no carrier':
354
+ $state = 'down';
355
+ break;
356
+ default:
357
+ $state = 'unknown';
358
+ break;
359
+ }
360
+
361
+ // Save this interface
362
+ $nets[$net[1]] = array(
363
+
364
+ // pulled from netstat
365
+ 'recieved' => array(
366
+ 'bytes' => $net[2],
367
+ ),
368
+ 'sent' => array(
369
+ 'bytes' => $net[3],
370
+ ),
371
+
372
+ // pulled from ifconfig
373
+ 'state' => $state,
374
+
375
+ // pulled from dmesg
376
+ 'type' => array_key_exists($net[1], $type) ? strtoupper($type[$net[1]]) : 'N/A',
377
+ );
378
+ }
379
+
380
+ // Give them
381
+ return $nets;
382
+ }
383
+
384
+ // processors...
385
+ public function getCPU()
386
+ {
387
+
388
+ // Time?
389
+ if (!empty($this->settings['timer'])) {
390
+ $t = new Timer('CPUs');
391
+ }
392
+
393
+ // Store them here
394
+ $cpus = array();
395
+
396
+ // Stuff it with identical cpus
397
+ for ($i = 0; $i < $this->sysctl['hw.ncpu']; ++$i) {
398
+
399
+ // Save each
400
+ $cpus[] = array(
401
+ 'Model' => $this->sysctl['hw.model'],
402
+ 'MHz' => $this->sysctl['hw.cpuspeed'],
403
+ );
404
+ }
405
+
406
+ // Return
407
+ return $cpus;
408
+ }
409
+
410
+ // Get process stats
411
+ public function getProcessStats()
412
+ {
413
+ // Time?
414
+ if (!empty($this->settings['timer'])) {
415
+ $t = new Timer('Process Stats');
416
+ }
417
+
418
+ // We'll return this after stuffing it with useful info
419
+ $result = array(
420
+ 'exists' => true,
421
+ 'totals' => array(
422
+ 'running' => 0,
423
+ 'zombie' => 0,
424
+ 'sleeping' => 0,
425
+ 'stopped' => 0,
426
+ ),
427
+ 'proc_total' => 0,
428
+ 'threads' => false, // I'm not sure how to get this
429
+ );
430
+
431
+ // Use ps
432
+ try {
433
+ // Get it
434
+ $ps = $this->exec->exec('ps', 'ax');
435
+
436
+ // Match them
437
+ preg_match_all('/^\s*\d+\s+[\w?]+\s+([A-Z])\S*\s+.+$/m', $ps, $processes, PREG_SET_ORDER);
438
+
439
+ // Get total
440
+ $result['proc_total'] = count($processes);
441
+
442
+ // Go through
443
+ foreach ($processes as $process) {
444
+ switch ($process[1]) {
445
+ case 'S':
446
+ case 'I':
447
+ $result['totals']['sleeping']++;
448
+ break;
449
+ case 'Z':
450
+ $result['totals']['zombie']++;
451
+ break;
452
+ case 'R':
453
+ case 'D':
454
+ case 'O':
455
+ $result['totals']['running']++;
456
+ break;
457
+ case 'T':
458
+ $result['totals']['stopped']++;
459
+ break;
460
+ }
461
+ }
462
+ } catch (Exception $e) {
463
+ Errors::add('Linfo Core', 'Error using `ps` to get process info');
464
+ }
465
+
466
+ // Give
467
+ return $result;
468
+ }
469
+ }
lib/Linfo/OS/SunOS.php ADDED
@@ -0,0 +1,456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Exception;
24
+ use Linfo\Meta\Timer;
25
+ use Linfo\Meta\Errors;
26
+ use Linfo\Common;
27
+ use Linfo\Parsers\CallExt;
28
+
29
+ class SunOS extends OS
30
+ {
31
+ // Encapsulate these
32
+ protected $settings,
33
+ $exec,
34
+ $kstat = array();
35
+
36
+ // Start us off
37
+ public function __construct($settings)
38
+ {
39
+
40
+ // Localize settings
41
+ $this->settings = $settings;
42
+
43
+ // External exec runnign
44
+ $this->exec = new CallExt();
45
+
46
+ // We search these folders for our commands
47
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
48
+
49
+ // Used multpile times so might as well just get it once. here
50
+ $this->release = php_uname('r');
51
+
52
+ // Get multiple kstat values at once and store them here. It seems kstat is SunOS' version of BSD's sysctl
53
+ $this->loadkstat(array(
54
+
55
+ // unix time stamp of system boot
56
+ 'unix:0:system_misc:boot_time',
57
+
58
+ // usual 3 system load values
59
+ 'unix:0:system_misc:avenrun_1min',
60
+ 'unix:0:system_misc:avenrun_5min',
61
+ 'unix:0:system_misc:avenrun_15min',
62
+
63
+ // physical ram info
64
+ 'unix:0:seg_cache:slab_size',
65
+ 'unix:0:system_pages:pagestotal',
66
+ 'unix:0:system_pages:pagesfree',
67
+
68
+ // Info on all CPUs
69
+ 'cpu_info:0:',
70
+
71
+ // Network interface stats
72
+ 'link:0:',
73
+ ));
74
+ }
75
+
76
+ // Get kstat values. *extremely* similar in practice to the sysctl nature of the bsd's
77
+ // -
78
+ // Use kstat to get something, and cache result.
79
+ // Also allow getting multiple keys at once, in which case sysctl
80
+ // will only be called once instead of multiple times (assuming it doesn't break)
81
+ protected function loadkstat($keys)
82
+ {
83
+
84
+ // Time?
85
+ if (!empty($this->settings['timer'])) {
86
+ $t = new Timer('Solaris Kstat Parsing');
87
+ }
88
+
89
+ $results = array();
90
+
91
+ foreach ($keys as $k => $v) {
92
+ if (array_key_exists($v, $this->kstat)) {
93
+ unset($keys[$k]);
94
+ }
95
+ }
96
+
97
+ try {
98
+ $command = $this->exec->exec('kstat', ' -p '.implode(' ', array_map('escapeshellarg', $keys)));
99
+ $lines = explode("\n", $command);
100
+ } catch (Exception $e) {
101
+ Errors::add('Solaris Core', 'Failed running kstat.');
102
+ }
103
+
104
+ if (!is_array($lines)) {
105
+ return;
106
+ }
107
+
108
+ // Not very efficient as it loops over each line for every key that exists, but it is
109
+ // very effective and thorough
110
+ foreach ($keys as $key) {
111
+ foreach ($lines as $line) {
112
+ $line = trim($line);
113
+
114
+ if (strpos($line, $key) !== 0) {
115
+ continue;
116
+ }
117
+
118
+ $value = ltrim(substr($line, strlen($key)));
119
+ if (isset($results[$key])) {
120
+ $results[$key] .= "\n".$value;
121
+ } else {
122
+ $results[$key] = $value;
123
+ }
124
+ }
125
+ }
126
+
127
+ $this->kstat = array_merge($results, $this->kstat);
128
+ }
129
+
130
+ // Return OS type
131
+ public function getOS()
132
+ {
133
+
134
+ // Get SunOS version
135
+ $v = reset(explode('.', $this->release, 2));
136
+
137
+ // Stuff 4 and under is SunOS. 5 and up is Solaris
138
+ switch ($v) {
139
+ case ($v > 4):
140
+ return 'Solaris';
141
+ break;
142
+ default:
143
+ return 'SunOS';
144
+ break;
145
+ }
146
+
147
+ // What's next is determining what variant of Solaris,
148
+ // eg: opensolaris (R.I.P.), nexenta, illumos, etc
149
+ }
150
+
151
+ // Get kernel version
152
+ public function getKernel()
153
+ {
154
+ return $this->release;
155
+ }
156
+
157
+ // Mounted file systems
158
+ public function getMounts()
159
+ {
160
+
161
+ // Time?
162
+ if (!empty($this->settings['timer'])) {
163
+ $t = new Timer('Mounted file systems');
164
+ }
165
+
166
+ // Run mount command
167
+ try {
168
+ $res = $this->exec->exec('mount', '-p');
169
+ } catch (Exception $e) {
170
+ Errors::add('Linfo Core', 'Error running `mount` command');
171
+
172
+ return array();
173
+ }
174
+
175
+ // Parse it
176
+ if (!preg_match_all('/^(\S+) - (\S+) (\w+).+/m', $res, $mount_matches, PREG_SET_ORDER)) {
177
+ return array();
178
+ }
179
+
180
+ // Store them here
181
+ $mounts = array();
182
+
183
+ // Deal with each entry
184
+ foreach ($mount_matches as $mount) {
185
+
186
+ // Should we not show this?
187
+ if (in_array($mount[1], $this->settings['hide']['storage_devices']) || in_array($mount[3], $this->settings['hide']['filesystems'])) {
188
+ continue;
189
+ }
190
+
191
+ // Get these
192
+ $size = @disk_total_space($mount[2]);
193
+ $free = @disk_free_space($mount[2]);
194
+ $used = $size - $free;
195
+
196
+ // Might be good, go for it
197
+ $mounts[] = array(
198
+ 'device' => $mount[1],
199
+ 'mount' => $mount[2],
200
+ 'type' => $mount[3],
201
+ 'size' => $size ,
202
+ 'used' => $used,
203
+ 'free' => $free,
204
+ 'free_percent' => ((bool) $free != false && (bool) $size != false ? round($free / $size, 2) * 100 : false),
205
+ 'used_percent' => ((bool) $used != false && (bool) $size != false ? round($used / $size, 2) * 100 : false),
206
+ );
207
+ }
208
+
209
+ // Give it
210
+ return $mounts;
211
+ }
212
+
213
+ // Get ram stats
214
+ public function getRAM()
215
+ {
216
+
217
+ // Time?
218
+ if (!empty($this->settings['timer'])) {
219
+ $t = new Timer('Memory');
220
+ }
221
+
222
+ // Give
223
+ return array(
224
+ 'type' => 'Physical',
225
+ 'total' => $this->kstat['unix:0:system_pages:pagestotal'] * $this->kstat['unix:0:seg_cache:slab_size'],
226
+ 'free' => $this->kstat['unix:0:system_pages:pagesfree'] * $this->kstat['unix:0:seg_cache:slab_size'],
227
+ 'swapInfo' => array(),
228
+ );
229
+ }
230
+
231
+ public function getProcessStats()
232
+ {
233
+
234
+ // Time?
235
+ if (!empty($this->settings['timer'])) {
236
+ $t = new Timer('Process Stats');
237
+ }
238
+
239
+ // We'll return this after stuffing it with useful info
240
+ $result = array(
241
+ 'exists' => true,
242
+ 'totals' => array(
243
+ 'running' => 0,
244
+ 'zombie' => 0,
245
+ 'sleeping' => 0,
246
+ 'stopped' => 0,
247
+ 'idle' => 0,
248
+ ),
249
+ 'proc_total' => 0,
250
+ 'threads' => false, // I'm not sure how to get this
251
+ );
252
+
253
+ // Use ps
254
+ try {
255
+ // Get it
256
+ $ps = $this->exec->exec('ps', '-fe -o s');
257
+
258
+ // Go through it
259
+ foreach (explode("\n", trim($ps)) as $process) {
260
+
261
+ // Decide what this is
262
+ switch ($process) {
263
+ case 'S':
264
+ $result['totals']['sleeping']++;
265
+ break;
266
+ case 'Z':
267
+ $result['totals']['zombie']++;
268
+ break;
269
+ case 'R':
270
+ case 'O':
271
+ $result['totals']['running']++;
272
+ break;
273
+ case 'T':
274
+ $result['totals']['stopped']++;
275
+ break;
276
+ }
277
+
278
+ // Increment total
279
+ ++$result['proc_total'];
280
+ }
281
+ }
282
+
283
+ // Something bad happened
284
+ catch (Exception $e) {
285
+ Errors::add('Linfo Core', 'Error using `ps` to get process info');
286
+ }
287
+
288
+ // Give
289
+ return $result;
290
+ }
291
+
292
+ // uptime
293
+ public function getUpTime()
294
+ {
295
+ $booted = $this->kstat['unix:0:system_misc:boot_time'];
296
+
297
+ return array(
298
+ 'text' => Common::secondsConvert(time() - $booted),
299
+ 'bootedTimestamp' => $booted,
300
+ );
301
+ }
302
+
303
+ // load
304
+ public function getLoad()
305
+ {
306
+ // Give
307
+ return array(
308
+ 'now' => round($this->kstat['unix:0:system_misc:avenrun_1min'] / 256, 2),
309
+ '5min' => round($this->kstat['unix:0:system_misc:avenrun_5min'] / 256, 2),
310
+ '15min' => round($this->kstat['unix:0:system_misc:avenrun_10min'] / 256, 2),
311
+ );
312
+ }
313
+
314
+ public function getCPU()
315
+ {
316
+ $cpus = array();
317
+
318
+ foreach (explode("\n", $this->kstat['cpu_info:0:']) as $line) {
319
+ if (!preg_match('/^cpu_info(\d+):(\S+)\s+(.+)/', trim($line), $m)) {
320
+ continue;
321
+ }
322
+ if (!isset($cpus[$m[1]])) {
323
+ $cpus[$m[1]] = array();
324
+ }
325
+
326
+ $cur_cpu = &$cpus[$m[1]];
327
+
328
+ $value = trim($m[3]);
329
+ switch ($m[2]) {
330
+ case 'vendor_id':
331
+ $cur_cpu['Vendor'] = $value;
332
+ break;
333
+
334
+ case 'clock_MHz':
335
+ $cur_cpu['MHz'] = $value;
336
+ break;
337
+
338
+ case 'brand':
339
+ $cur_cpu['Model'] = $value;
340
+ break;
341
+ }
342
+ }
343
+
344
+ return $cpus;
345
+ }
346
+
347
+ public function getNet()
348
+ {
349
+ $nets = array();
350
+
351
+ // ifconfig for nics/statuses
352
+ try {
353
+ $ifconfig = $this->exec->exec('ifconfig', '-a');
354
+ } catch (Exception $e) {
355
+ Errors::add('Solaris Core', 'Failed running ifconfig -a.');
356
+
357
+ return array();
358
+ }
359
+
360
+ foreach (explode("\n", $ifconfig) as $line) {
361
+ if (!preg_match('/^([^:]+):[^<]+<([^>]+)>/', trim($line), $m)) {
362
+ continue;
363
+ }
364
+
365
+ $nic = $m[1];
366
+ $flags = explode(',', strtolower($m[2]));
367
+
368
+ if (isset($nets[$nic])) {
369
+ continue;
370
+ }
371
+
372
+ $type = null;
373
+
374
+ if (in_array('loopback', $flags)) {
375
+ $type = 'Loopback';
376
+ }
377
+
378
+ $nets[$nic] = array(
379
+
380
+ // To be filled in later
381
+ 'recieved' => array(
382
+ 'bytes' => null,
383
+ 'packets' => null,
384
+ 'errors' => null,
385
+ ),
386
+ 'sent' => array(
387
+ 'bytes' => null,
388
+ 'bytes' => null,
389
+ 'errors' => null,
390
+ ),
391
+
392
+ // Should find a better way of getting these
393
+ 'state' => in_array('up', $flags) ? 'up' : 'Unknown',
394
+ 'type' => $type,
395
+ );
396
+ }
397
+
398
+ // kstat for more stats
399
+ foreach (explode("\n", $this->kstat['link:0:']) as $line) {
400
+ if (!preg_match('/^([^:]+):(\S+)\s+(\S+)/', trim($line), $m)) {
401
+ continue;
402
+ }
403
+
404
+ list(, $nic, $key, $value) = $m;
405
+
406
+ if (!isset($nets[$nic])) {
407
+ continue;
408
+ }
409
+
410
+ $cur_nic = &$nets[$nic];
411
+
412
+ switch ($key) {
413
+ case 'ipackets64':
414
+ $cur_nic['recieved']['packets'] = $value;
415
+ break;
416
+ case 'opackets64':
417
+ $cur_nic['sent']['packets'] = $value;
418
+ break;
419
+ case 'rbytes64':
420
+ $cur_nic['recieved']['bytes'] = $value;
421
+ break;
422
+ case 'obytes64':
423
+ $cur_nic['sent']['bytes'] = $value;
424
+ break;
425
+ }
426
+ }
427
+
428
+ // dladm for more stats...
429
+ try {
430
+ $dladm = $this->exec->exec('dladm', 'show-link');
431
+ foreach (explode("\n", $dladm) as $line) {
432
+ if (!preg_match('/^(\S+)\s+(\S+)\s+\d+\s+(\S+)/', $line, $m)) {
433
+ continue;
434
+ }
435
+
436
+ if (!isset($nets[$m[1]])) {
437
+ continue;
438
+ }
439
+
440
+ if (!$nets[$m[1]]['type'] && $m[2] == 'phys') {
441
+ $nets[$m[1]]['type'] = 'Physical';
442
+ }
443
+
444
+ if (!$nets[$m[1]]['state'] || $nets[$m[1]]['state'] == 'unknown') {
445
+ $nets[$m[1]]['state'] = $m[3];
446
+ }
447
+ }
448
+ } catch (Exception $e) {
449
+ Errors::add('Solaris Core', 'Failed running dladm show-link.');
450
+
451
+ return array();
452
+ }
453
+
454
+ return $nets;
455
+ }
456
+ }
lib/Linfo/OS/Unixcommon.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2014 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ /**
21
+ * Keep out hackers...
22
+ */
23
+ namespace Linfo\OS;
24
+
25
+ use Linfo\Common;
26
+
27
+ /*
28
+ * The Unix os's are largely similar and thus draw from this class.
29
+ */
30
+ abstract class Unixcommon extends OS
31
+ {
32
+ public function ensureFQDN($hostname)
33
+ {
34
+ $parts = explode('.', $hostname);
35
+ $num_parts = count($parts);
36
+
37
+ // Already FQDN, like a boss..
38
+ if ($num_parts >= 2) {
39
+ return $hostname;
40
+ }
41
+
42
+ // Don't bother trying to expand on .local
43
+ if ($num_parts > 0 && $parts[$num_parts - 1] == '.local') {
44
+ return $hostname;
45
+ }
46
+
47
+ // This relies on reading /etc/hosts.
48
+ if (!($contents = Common::getContents('/etc/hosts', false))) {
49
+ return $hostname;
50
+ }
51
+
52
+ preg_match_all('/^[^\s#]+\s+(.+)/m', $contents, $matches, PREG_SET_ORDER);
53
+
54
+ // Lets see if we can do some magic with /etc/hosts..
55
+ foreach ($matches as $match) {
56
+ if (!preg_match_all('/(\S+)/', $match[1], $hosts, PREG_SET_ORDER)) {
57
+ continue;
58
+ }
59
+
60
+ foreach ($hosts as $host) {
61
+
62
+ // I don't want to expand on localhost as it's pointlesss
63
+ if (strpos('localhost', $host[1]) !== false) {
64
+ continue;
65
+ }
66
+
67
+ $entry_parts = explode('.', $host[1]);
68
+ if (count($entry_parts) > 1 && $entry_parts[0] == $hostname) {
69
+ return $host[1];
70
+ }
71
+ }
72
+ }
73
+
74
+ // Couldn't make it better :/
75
+ return $hostname;
76
+ }
77
+ }
lib/Linfo/OS/Windows.php ADDED
@@ -0,0 +1,769 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\OS;
22
+
23
+ use Linfo\Meta\Timer;
24
+ use Linfo\Common;
25
+ use Linfo\Exceptions\FatalException;
26
+ use COM;
27
+
28
+ /**
29
+ * Get info on Windows systems
30
+ * Written and maintained by Oliver Kuckertz (mologie).
31
+ */
32
+ class Windows extends OS
33
+ {
34
+ // Keep these tucked away
35
+ protected $settings;
36
+
37
+ private $wmi, $windows_version;
38
+
39
+ /**
40
+ * Constructor. Localizes settings.
41
+ *
42
+ * @param array $settings of linfo settings
43
+ * @throws FatalException
44
+ */
45
+ public function __construct($settings)
46
+ {
47
+
48
+ // Localize settings
49
+ $this->settings = $settings;
50
+
51
+ // Get WMI instance
52
+ $this->wmi = new COM('winmgmts:{impersonationLevel=impersonate}//./root/cimv2');
53
+
54
+ if (!is_object($this->wmi)) {
55
+ throw new FatalException('This needs access to WMI. Please enable DCOM in php.ini and allow the current user to access the WMI DCOM object.');
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Return a list of things to hide from view..
61
+ *
62
+ * @return array
63
+ */
64
+ public function getContains()
65
+ {
66
+ return array(
67
+ 'drives_rw_stats' => false,
68
+ 'nic_port_speed' => false,
69
+ );
70
+ }
71
+
72
+ /**
73
+ * getOS.
74
+ *
75
+ * @return string current windows version
76
+ */
77
+ public function getOS()
78
+ {
79
+ foreach ($this->wmi->ExecQuery('SELECT Caption FROM Win32_OperatingSystem') as $os) {
80
+ return $os->Caption;
81
+ }
82
+
83
+ return 'Windows';
84
+ }
85
+
86
+ /**
87
+ * getKernel.
88
+ *
89
+ * @return string kernel version
90
+ */
91
+ public function getKernel()
92
+ {
93
+
94
+ // Time?
95
+ if (!empty($this->settings['timer'])) {
96
+ $t = new Timer('Kernel');
97
+ }
98
+
99
+ foreach ($this->wmi->ExecQuery('SELECT WindowsVersion FROM Win32_Process WHERE Handle = 0') as $process) {
100
+ $this->windows_version = $process->WindowsVersion;
101
+
102
+ return $process->WindowsVersion;
103
+ }
104
+
105
+ return 'Unknown';
106
+ }
107
+
108
+ /**
109
+ * getHostName.
110
+ *
111
+ * @return string the host name
112
+ */
113
+ public function getHostName()
114
+ {
115
+
116
+ // Time?
117
+ if (!empty($this->settings['timer'])) {
118
+ $t = new Timer('Hostname');
119
+ }
120
+
121
+ foreach ($this->wmi->ExecQuery('SELECT Name FROM Win32_ComputerSystem') as $cs) {
122
+ return $cs->Name;
123
+ }
124
+
125
+ return 'Unknown';
126
+ }
127
+
128
+ /**
129
+ * getRam.
130
+ *
131
+ * @return array the memory information
132
+ */
133
+ public function getRam()
134
+ {
135
+
136
+ // Time?
137
+ if (!empty($this->settings['timer'])) {
138
+ $t = new Timer('Memory');
139
+ }
140
+
141
+ $total_memory = 0;
142
+ $free_memory = 0;
143
+
144
+ foreach ($this->wmi->ExecQuery('SELECT TotalPhysicalMemory FROM Win32_ComputerSystem') as $cs) {
145
+ $total_memory = $cs->TotalPhysicalMemory;
146
+ break;
147
+ }
148
+
149
+ foreach ($this->wmi->ExecQuery('SELECT FreePhysicalMemory FROM Win32_OperatingSystem') as $os) {
150
+ $free_memory = $os->FreePhysicalMemory;
151
+ break;
152
+ }
153
+
154
+ return array(
155
+ 'type' => 'Physical',
156
+ 'total' => $total_memory,
157
+ 'free' => $free_memory * 1024,
158
+ );
159
+ }
160
+
161
+ /**
162
+ * getCPU.
163
+ *
164
+ * @return array of cpu info
165
+ */
166
+ public function getCPU()
167
+ {
168
+
169
+ // Time?
170
+ if (!empty($this->settings['timer'])) {
171
+ $t = new Timer('CPUs');
172
+ }
173
+
174
+ $cpus = array();
175
+ $alt = false;
176
+ $object = $this->wmi->ExecQuery('SELECT Name, Manufacturer, CurrentClockSpeed, NumberOfLogicalProcessors,LoadPercentage FROM Win32_Processor');
177
+
178
+ if (!is_object($object)) {
179
+ $object = $this->wmi->ExecQuery('SELECT Name, Manufacturer, CurrentClockSpeed,LoadPercentage FROM Win32_Processor');
180
+ $alt = true;
181
+ }
182
+
183
+ foreach ($object as $cpu) {
184
+ $curr = array(
185
+ 'Model' => $cpu->Name,
186
+ 'Vendor' => $cpu->Manufacturer,
187
+ 'MHz' => $cpu->CurrentClockSpeed,
188
+ );
189
+
190
+ if ($cpu->LoadPercentage != '') {
191
+ $curr['usage_percentage'] = $cpu->LoadPercentage;
192
+ }
193
+
194
+ if (!$alt) {
195
+ for ($i = 0; $i < $cpu->NumberOfLogicalProcessors; ++$i) {
196
+ $cpus[] = $curr;
197
+ }
198
+ } else {
199
+ $cpus[] = $curr;
200
+ }
201
+ }
202
+
203
+ return $cpus;
204
+ }
205
+
206
+ /**
207
+ * getUpTime.
208
+ *
209
+ * @return string uptime
210
+ */
211
+ public function getUpTime()
212
+ {
213
+
214
+ // Time?
215
+ if (!empty($this->settings['timer'])) {
216
+ $t = new Timer('Uptime');
217
+ }
218
+
219
+ $booted_str = '';
220
+
221
+ foreach ($this->wmi->ExecQuery('SELECT LastBootUpTime FROM Win32_OperatingSystem') as $os) {
222
+ $booted_str = $os->LastBootUpTime;
223
+ break;
224
+ }
225
+
226
+ $booted = array(
227
+ 'year' => substr($booted_str, 0, 4),
228
+ 'month' => substr($booted_str, 4, 2),
229
+ 'day' => substr($booted_str, 6, 2),
230
+ 'hour' => substr($booted_str, 8, 2),
231
+ 'minute' => substr($booted_str, 10, 2),
232
+ 'second' => substr($booted_str, 12, 2),
233
+ );
234
+ $booted_ts = mktime($booted['hour'], $booted['minute'], $booted['second'], $booted['month'], $booted['day'], $booted['year']);
235
+
236
+ return array(
237
+ 'text' => Common::secondsConvert(time() - $booted_ts),
238
+ 'bootedTimestamp' => $booted_ts,
239
+ );
240
+ }
241
+
242
+ /**
243
+ * getHD.
244
+ *
245
+ * @return array the hard drive info
246
+ */
247
+ public function getHD()
248
+ {
249
+
250
+ // Time?
251
+ if (!empty($this->settings['timer'])) {
252
+ $t = new Timer('Drives');
253
+ }
254
+
255
+ $drives = array();
256
+ $partitions = array();
257
+
258
+ foreach ($this->wmi->ExecQuery('SELECT DiskIndex, Size, DeviceID, Type FROM Win32_DiskPartition') as $partition) {
259
+ $partitions[$partition->DiskIndex][] = array(
260
+ 'size' => $partition->Size,
261
+ 'name' => $partition->DeviceID.' ('.$partition->Type.')',
262
+ );
263
+ }
264
+
265
+ foreach ($this->wmi->ExecQuery('SELECT Caption, DeviceID, Index, Size FROM Win32_DiskDrive') as $drive) {
266
+ $caption = explode(' ', $drive->Caption);
267
+ $drives[] = array(
268
+ 'name' => $drive->Caption,
269
+ 'vendor' => reset($caption),
270
+ 'device' => $drive->DeviceID,
271
+ 'reads' => false,
272
+ 'writes' => false,
273
+ 'size' => $drive->Size,
274
+ 'partitions' => array_key_exists($drive->Index, $partitions) && is_array($partitions[$drive->Index]) ? $partitions[$drive->Index] : false,
275
+ );
276
+ }
277
+
278
+ usort($drives, array('Linfo\OS\Windows', 'compare_drives'));
279
+
280
+ return $drives;
281
+ }
282
+
283
+ /**
284
+ * getTemps.
285
+ *
286
+ * @return array the temps
287
+ */
288
+ public function getTemps()
289
+ {
290
+
291
+ // Time?
292
+ if (!empty($this->settings['timer'])) {
293
+ $t = new Timer('Temperature');
294
+ }
295
+
296
+ return array(); // TODO
297
+ }
298
+
299
+ /**
300
+ * getMounts.
301
+ *
302
+ * @return array the mounted the file systems
303
+ */
304
+ public function getMounts()
305
+ {
306
+
307
+ // Time?
308
+ if (!empty($this->settings['timer'])) {
309
+ $t = new Timer('Mounted file systems');
310
+ }
311
+
312
+ $volumes = array();
313
+
314
+ if ($this->windows_version > '6.1.0000') {
315
+ $object = $this->wmi->ExecQuery('SELECT Automount, BootVolume, Compressed, IndexingEnabled, Label, Caption, FileSystem, Capacity, FreeSpace, DriveType FROM Win32_Volume');
316
+ } else {
317
+ $object = $this->wmi->ExecQuery('SELECT Compressed, Name, FileSystem, Size, FreeSpace, DriveType FROM Win32_LogicalDisk');
318
+ }
319
+
320
+ foreach ($object as $volume) {
321
+ $options = array();
322
+ if ($this->windows_version > '6.1.0000') {
323
+ if ($volume->Automount) {
324
+ $options[] = 'automount';
325
+ }
326
+ if ($volume->BootVolume) {
327
+ $options[] = 'boot';
328
+ }
329
+ if ($volume->IndexingEnabled) {
330
+ $options[] = 'indexed';
331
+ }
332
+ }
333
+ if ($volume->Compressed) {
334
+ $options[] = 'compressed';
335
+ }
336
+ $capacity = ($this->windows_version > '6.1.0000') ? $volume->Capacity : $volume->Size;
337
+ $label = ($this->windows_version > '6.1.0000') ? $volume->Label : $volume->Name;
338
+ $mount = ($this->windows_version > '6.1.0000') ? $volume->Caption : $label.'\\';
339
+ $a = array(
340
+ 'device' => false,
341
+ 'label' => $label,
342
+ 'devtype' => '',
343
+ 'mount' => $mount,
344
+ 'type' => $volume->FileSystem,
345
+ 'size' => $capacity,
346
+ 'used' => $capacity - $volume->FreeSpace,
347
+ 'free' => $volume->FreeSpace,
348
+ 'free_percent' => 0,
349
+ 'used_percent' => 0,
350
+ 'options' => $options,
351
+ );
352
+
353
+ switch ($volume->DriveType) {
354
+ case 2:
355
+ $a['devtype'] = 'Removable drive';
356
+ break;
357
+ case 3:
358
+ $a['devtype'] = 'Fixed drive';
359
+ break;
360
+ case 4:
361
+ $a['devtype'] = 'Remote drive';
362
+ break;
363
+ case 5:
364
+ $a['devtype'] = 'CD-ROM';
365
+ break;
366
+ case 6:
367
+ $a['devtype'] = 'RAM disk';
368
+ break;
369
+ default:
370
+ $a['devtype'] = 'Unknown';
371
+ break;
372
+ }
373
+
374
+ if ($capacity != 0) {
375
+ $a['free_percent'] = round($volume->FreeSpace / $capacity, 2) * 100;
376
+ $a['used_percent'] = round(($capacity - $volume->FreeSpace) / $capacity, 2) * 100;
377
+ }
378
+
379
+ $volumes[] = $a;
380
+ }
381
+
382
+ usort($volumes, array('Linfo\OS\Windows', 'compare_mounts'));
383
+
384
+ return $volumes;
385
+ }
386
+
387
+ /**
388
+ * getDevs.
389
+ *
390
+ * @return array of devices
391
+ */
392
+ public function getDevs()
393
+ {
394
+
395
+ // Time?
396
+ if (!empty($this->settings['timer'])) {
397
+ $t = new Timer('Hardware Devices');
398
+ }
399
+
400
+ $devs = array();
401
+
402
+ foreach ($this->wmi->ExecQuery('SELECT DeviceID, Caption, Manufacturer FROM Win32_PnPEntity') as $pnpdev) {
403
+ $devId = explode('\\', $pnpdev->DeviceID);
404
+ $type = reset($devId);
405
+ if (($type != 'USB' && $type != 'PCI') || (empty($pnpdev->Caption) || $pnpdev->Manufacturer[0] == '(')) {
406
+ continue;
407
+ }
408
+ $manufacturer = $pnpdev->Manufacturer;
409
+ $caption = $pnpdev->Caption;
410
+ if (function_exists('iconv')) {
411
+ $manufacturer = iconv('Windows-1252', 'UTF-8//TRANSLIT', $manufacturer);
412
+ $caption = iconv('Windows-1252', 'UTF-8//TRANSLIT', $caption);
413
+ }
414
+ $devs[] = array(
415
+ 'vendor' => $manufacturer,
416
+ 'device' => $caption,
417
+ 'type' => $type,
418
+ );
419
+ }
420
+
421
+ // Sort by 1. Type, 2. Vendor
422
+ usort($devs, array('Linfo\OS\Windows', 'compare_devices'));
423
+
424
+ return $devs;
425
+ }
426
+
427
+ /**
428
+ * getRAID.
429
+ *
430
+ * @return array of raid arrays
431
+ */
432
+ public function getRAID()
433
+ {
434
+
435
+ // Time?
436
+ if (!empty($this->settings['timer'])) {
437
+ $t = new Timer('RAID');
438
+ }
439
+
440
+ return array();
441
+ }
442
+
443
+ /**
444
+ * getLoad.
445
+ *
446
+ * @return array of current system load values
447
+ */
448
+ public function getLoad()
449
+ {
450
+
451
+ // Time?
452
+ if (!empty($this->settings['timer'])) {
453
+ $t = new Timer('Load Averages');
454
+ }
455
+
456
+ $load = array();
457
+ foreach ($this->wmi->ExecQuery('SELECT LoadPercentage FROM Win32_Processor') as $cpu) {
458
+ $load[] = $cpu->LoadPercentage;
459
+ }
460
+
461
+ return round(array_sum($load) / count($load), 2).'%';
462
+ }
463
+
464
+ /**
465
+ * getNet.
466
+ *
467
+ * @return array of network devices
468
+ */
469
+ public function getNet()
470
+ {
471
+
472
+ // Time?
473
+ if (!empty($this->settings['timer'])) {
474
+ $t = new Timer('Network Devices');
475
+ }
476
+
477
+ $return = array();
478
+ $i = 0;
479
+
480
+ if ($this->windows_version > '6.1.0000') {
481
+ $object = $this->wmi->ExecQuery('SELECT AdapterType, Name, NetConnectionStatus, GUID FROM Win32_NetworkAdapter WHERE PhysicalAdapter = TRUE');
482
+ } else {
483
+ $object = $this->wmi->ExecQuery('SELECT AdapterType, Name, NetConnectionStatus FROM Win32_NetworkAdapter WHERE NetConnectionStatus != NULL');
484
+ }
485
+
486
+ foreach ($object as $net) {
487
+ // Save and get info for each
488
+ $return[$net->Name] = array(
489
+ 'recieved' => array(
490
+ 'bytes' => 0,
491
+ 'errors' => 0,
492
+ 'packets' => 0,
493
+ ),
494
+ 'sent' => array(
495
+ 'bytes' => 0,
496
+ 'errors' => 0,
497
+ 'packets' => 0,
498
+ ),
499
+ 'state' => 0,
500
+ 'type' => $net->AdapterType,
501
+ );
502
+ switch ($net->NetConnectionStatus) {
503
+ case 0:
504
+ $return[$net->Name]['state'] = 'down';
505
+ break;
506
+ case 1:
507
+ $return[$net->Name]['state'] = 'Connecting';
508
+ break;
509
+ case 2:
510
+ $return[$net->Name]['state'] = 'up';
511
+ break;
512
+ case 3:
513
+ $return[$net->Name]['state'] = 'Disconnecting';
514
+ break;
515
+ case 4:
516
+ $return[$net->Name]['state'] = 'down'; // MSDN 'Hardware not present'
517
+ break;
518
+ case 5:
519
+ $return[$net->Name]['state'] = 'Hardware disabled';
520
+ break;
521
+ case 6:
522
+ $return[$net->Name]['state'] = 'Hardware malfunction';
523
+ break;
524
+ case 7:
525
+ $return[$net->Name]['state'] = 'Media disconnected';
526
+ break;
527
+ case 8:
528
+ $return[$net->Name]['state'] = 'Authenticating';
529
+ break;
530
+ case 9:
531
+ $return[$net->Name]['state'] = 'Authentication succeeded';
532
+ break;
533
+ case 10:
534
+ $return[$net->Name]['state'] = 'Authentication failed';
535
+ break;
536
+ case 11:
537
+ $return[$net->Name]['state'] = 'Invalid address';
538
+ break;
539
+ case 12:
540
+ $return[$net->Name]['state'] = 'Credentials required';
541
+ break;
542
+ default:
543
+ $return[$net->Name]['state'] = 'unknown';
544
+ break;
545
+ }
546
+ // @Microsoft: An index would be nice here indeed.
547
+ if ($this->windows_version > '6.1.0000') {
548
+ $canonname = preg_replace('/[^A-Za-z0-9- ]/', '_', $net->Name);
549
+ $isatapname = 'isatap.'.$net->GUID;
550
+ $result = $this->wmi->ExecQuery("SELECT BytesReceivedPersec, PacketsReceivedErrors, PacketsReceivedPersec, BytesSentPersec, PacketsSentPersec FROM Win32_PerfRawData_Tcpip_NetworkInterface WHERE Name = '$canonname' OR Name = '$isatapname'");
551
+ } else {
552
+ $canonname = preg_replace('/[^A-Za-z0-9- ]/', '_', $net->Name);
553
+ $result = $this->wmi->ExecQuery("SELECT BytesReceivedPersec, PacketsReceivedErrors, PacketsReceivedPersec, BytesSentPersec, PacketsSentPersec FROM Win32_PerfRawData_Tcpip_NetworkInterface WHERE Name = '$canonname'");
554
+ }
555
+ foreach ($result as $netspeed) {
556
+ $return[$net->Name]['recieved'] = array(
557
+ 'bytes' => (int) $netspeed->BytesReceivedPersec,
558
+ 'errors' => (int) $netspeed->PacketsReceivedErrors,
559
+ 'packets' => (int) $netspeed->PacketsReceivedPersec,
560
+ );
561
+ $return[$net->Name]['sent'] = array(
562
+ 'bytes' => (int) $netspeed->BytesSentPersec,
563
+ 'erros' => 0,
564
+ 'packets' => (int) $netspeed->PacketsSentPersec,
565
+ );
566
+ }
567
+ ++$i;
568
+ }
569
+
570
+ return $return;
571
+ }
572
+
573
+ /**
574
+ * getBattery.
575
+ *
576
+ * @return array of battery status
577
+ */
578
+ public function getBattery()
579
+ {
580
+
581
+ // Time?
582
+ if (!empty($this->settings['timer'])) {
583
+ $t = new Timer('Batteries');
584
+ }
585
+
586
+ return array(); // TODO
587
+ }
588
+
589
+ /**
590
+ * getWifi.
591
+ *
592
+ * @return array of wifi devices
593
+ */
594
+ public function getWifi()
595
+ {
596
+
597
+ // Time?
598
+ if (!empty($this->settings['timer'])) {
599
+ $t = new Timer('Wifi');
600
+ }
601
+ }
602
+
603
+ /**
604
+ * getSoundCards.
605
+ *
606
+ * @return array of soundcards
607
+ */
608
+ public function getSoundCards()
609
+ {
610
+
611
+ // Time?
612
+ if (!empty($this->settings['timer'])) {
613
+ $t = new Timer('Sound cards');
614
+ }
615
+
616
+ $cards = array();
617
+ $i = 1;
618
+
619
+ foreach ($this->wmi->ExecQuery('SELECT Caption, Manufacturer FROM Win32_SoundDevice') as $card) {
620
+ $manufacturer = $card->Manufacturer;
621
+ $caption = $card->Caption;
622
+ if (function_exists('iconv')) {
623
+ $manufacturer = iconv('Windows-1252', 'UTF-8//TRANSLIT', $manufacturer);
624
+ $caption = iconv('Windows-1252', 'UTF-8//TRANSLIT', $caption);
625
+ }
626
+ $cards[] = array(
627
+ 'number' => $i,
628
+ 'vendor' => $manufacturer,
629
+ 'card' => $caption,
630
+ );
631
+ ++$i;
632
+ }
633
+
634
+ return $cards;
635
+ }
636
+
637
+ /**
638
+ * getProcessStats.
639
+ *
640
+ * @return array of process stats
641
+ */
642
+ public function getProcessStats()
643
+ {
644
+
645
+ // Time?
646
+ if (!empty($this->settings['timer'])) {
647
+ $t = new Timer('Process Stats');
648
+ }
649
+
650
+ $result = array(
651
+ 'exists' => true,
652
+ 'proc_total' => 0,
653
+ 'threads' => 0,
654
+ );
655
+
656
+ foreach ($this->wmi->ExecQuery('SELECT ThreadCount FROM Win32_Process') as $proc) {
657
+ $result['threads'] += (int) $proc->ThreadCount;
658
+ ++$result['proc_total'];
659
+ }
660
+
661
+ return $result;
662
+ }
663
+
664
+ /**
665
+ * getServices.
666
+ *
667
+ * @return array the services
668
+ */
669
+ public function getServices()
670
+ {
671
+ return array(); // TODO
672
+ }
673
+
674
+ /**
675
+ * getDistro.
676
+ *
677
+ * @return array the distro,version or false
678
+ */
679
+ public function getDistro()
680
+ {
681
+ return false;
682
+ }
683
+
684
+ /**
685
+ * getCPUArchitecture.
686
+ *
687
+ * @return string the arch and bits
688
+ */
689
+ public function getCPUArchitecture()
690
+ {
691
+
692
+ // Time?
693
+ if (!empty($this->settings['timer'])) {
694
+ $t = new Timer('CPU architecture');
695
+ }
696
+
697
+ foreach ($this->wmi->ExecQuery('SELECT Architecture FROM Win32_Processor') as $cpu) {
698
+ switch ($cpu->Architecture) {
699
+ case 0:
700
+ return 'x86';
701
+ case 1:
702
+ return 'MIPS';
703
+ case 2:
704
+ return 'Alpha';
705
+ case 3:
706
+ return 'PowerPC';
707
+ case 6:
708
+ return 'Itanium-based systems';
709
+ case 9:
710
+ return 'x64';
711
+ }
712
+ }
713
+
714
+ return 'Unknown';
715
+ }
716
+
717
+ /**
718
+ * @ignore
719
+ * @param $a
720
+ * @param $b
721
+ * @return int
722
+ */
723
+ public static function compare_devices($a, $b)
724
+ {
725
+ if ($a['type'] == $b['type']) {
726
+ if ($a['vendor'] == $b['vendor']) {
727
+ if ($a['device'] == $b['device']) {
728
+ return 0;
729
+ }
730
+
731
+ return ($a['device'] > $b['device']) ? 1 : -1;
732
+ }
733
+
734
+ return ($a['vendor'] > $b['vendor']) ? 1 : -1;
735
+ }
736
+
737
+ return ($a['type'] > $b['type']) ? 1 : -1;
738
+ }
739
+
740
+ /**
741
+ * @ignore
742
+ * @param $a
743
+ * @param $b
744
+ * @return int
745
+ */
746
+ public static function compare_drives($a, $b)
747
+ {
748
+ if ($a['device'] == $b['device']) {
749
+ return 0;
750
+ }
751
+
752
+ return ($a['device'] > $b['device']) ? 1 : -1;
753
+ }
754
+
755
+ /**
756
+ * @ignore
757
+ * @param $a
758
+ * @param $b
759
+ * @return int
760
+ */
761
+ public static function compare_mounts($a, $b)
762
+ {
763
+ if ($a['mount'] == $b['mount']) {
764
+ return 0;
765
+ }
766
+
767
+ return ($a['mount'] > $b['mount']) ? 1 : -1;
768
+ }
769
+ }
lib/Linfo/Output/Html.php ADDED
@@ -0,0 +1,1006 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Linfo\Output;
4
+
5
+ use Linfo\Linfo;
6
+ use Linfo\Common;
7
+ use Linfo\Meta\Errors;
8
+ use Linfo\Meta\Timer;
9
+
10
+ class Html implements Output
11
+ {
12
+ protected $linfo;
13
+
14
+ public function __construct(Linfo $linfo)
15
+ {
16
+ $this->linfo = $linfo;
17
+ }
18
+
19
+ /**
20
+ * Create a progress bar looking thingy. Put into a function here
21
+ * as its being increasingly used elsewhere. TODO refactor linfo and
22
+ * stop leaving functions in global namespace.
23
+ * @param $percent
24
+ * @param bool $text
25
+ * @return string
26
+ */
27
+ public static function generateBarChart($percent, $text = false)
28
+ {
29
+ return '
30
+ <div class="new_bar_outer">
31
+ <div class="new_bar_bg" style="width: '.$percent.'%; "></div>
32
+ <div class="new_bar_text">'.($text ?: $percent.'%').'</div>
33
+ </div>
34
+ ';
35
+ }
36
+
37
+ public static function fadedText($text)
38
+ {
39
+ return '<span class="faded">'.$text.'</span>';
40
+ }
41
+
42
+ // Create a table out of an array. Mostly used by extensions
43
+ /*
44
+ Example array structure:
45
+
46
+ $structure = array(
47
+ 'root_title' => 'Name',
48
+ 'rows' => array(
49
+ 01 = array(
50
+ 'type' => 'header',
51
+ 'columns' => array(
52
+ 'Column 1',
53
+ 'Column 2',
54
+ // OR array(colspannumber, 'value')
55
+ )
56
+ )
57
+ 02 => array(
58
+ 'type' => 'values',
59
+ 'columns' => array(
60
+ 'Value 1',
61
+ 'Value 2',
62
+ // OR array(colspannumber, 'value')
63
+ )
64
+ )
65
+ )
66
+ );
67
+ */
68
+ public static function createTable($structure)
69
+ {
70
+
71
+ // Start it off
72
+ $html = '
73
+ <div class="infoTable">
74
+ <h2>'.$structure['root_title'].'</h2>
75
+ <table>';
76
+
77
+ // Go throuch each row
78
+ foreach ($structure['rows'] as $row) {
79
+
80
+ // Let stuff be killed
81
+ $row['columns'] = array_filter($row['columns']);
82
+
83
+ // Ignore this if it's empty
84
+ if (empty($row['columns'])) {
85
+ continue;
86
+ }
87
+
88
+ // Start the typical tr
89
+ $html .= '
90
+ <tr>';
91
+
92
+ // Is this row a header?
93
+ if ($row['type'] == 'header') {
94
+ foreach ($row['columns'] as $v) {
95
+ $html .= is_array($v) ? '
96
+ <th colspan="'.$v[0].'"'.(array_key_exists('2', $v) ? ' style="width: '.$v[2].';"' : '').'>'.$v[1].'</th>' : '
97
+ <th>'.$v.'</th>';
98
+ }
99
+ }
100
+
101
+ // Or is it a row saying nothing was found?
102
+ elseif ($row['type'] == 'none') {
103
+ foreach ($row['columns'] as $v) {
104
+ $html .= is_array($v) ? '
105
+ <td colspan="'.$v[0].'" class="none">'.$v[1].'</td>' : '
106
+ <td class="none">'.$v.'</td>';
107
+ }
108
+ }
109
+
110
+ // Or is it values?
111
+ elseif ($row['type'] == 'values') {
112
+ foreach ($row['columns'] as $v) {
113
+ $html .= is_array($v) ? '
114
+ <td colspan="'.$v[0].'">'.$v[1].'</td>' : '
115
+ <td>'.$v.'</td>';
116
+ }
117
+ }
118
+
119
+ // End the usual tr
120
+ $html .= '
121
+ </tr>';
122
+ }
123
+
124
+ // Closing tags
125
+ $html .= '
126
+ </table>
127
+ </div>';
128
+
129
+ // Give it
130
+ return $html;
131
+ }
132
+
133
+ public function output()
134
+ {
135
+ $lang = $this->linfo->getLang();
136
+ $settings = $this->linfo->getSettings();
137
+ $info = $this->linfo->getInfo();
138
+ $appName = $this->linfo->getAppName();
139
+ $version = $this->linfo->getVersion();
140
+
141
+ // Fun icons
142
+ $show_icons = array_key_exists('icons', $settings) ? !empty($settings['icons']) : true;
143
+ $os_icon = $info['OS'] == 'Windows' ? 'windows' : strtolower(str_replace(' ', '', current(explode('(', $info['OS']))));
144
+ $distro_icon = $info['OS'] == 'Linux' && is_array($info['Distro']) && $info['Distro']['name'] ? strtolower(str_replace(' ', '', $info['Distro']['name'])) : false;
145
+
146
+ // Start compressed output buffering. Try to not do this if we've had errors or otherwise already outputted stuff
147
+ if ((!function_exists('error_get_last') || !error_get_last()) && (!isset($settings['compress_content']) || $settings['compress_content'])) {
148
+ ob_start(function_exists('ob_gzhandler') ? 'ob_gzhandler' : null);
149
+ }
150
+
151
+ // See if we have a specific theme file installed
152
+ if (isset($settings['theme']) && strpos($settings['theme'], '..') === false && file_exists('layout/theme_'.$settings['theme'].'.css')) {
153
+ $theme_css = 'theme_'.$settings['theme'].'.css';
154
+ }
155
+
156
+ // Does default exist?? Don't bitch at me for assigning an array key in an if-then
157
+ elseif (($settings['theme'] = 'default') && file_exists('layout/theme_'.$settings['theme'].'.css')) {
158
+ $theme_css = 'theme_'.$settings['theme'].'.css';
159
+ }
160
+
161
+ // if not, do the old way
162
+ else {
163
+ $theme_css = 'styles.css';
164
+ }
165
+
166
+ // Proceed to letting it all out
167
+ echo '<!DOCTYPE html>
168
+ <html>
169
+ <head>
170
+ <meta charset="UTF-8">
171
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
172
+ <title>'.$appName.' - '.$info['HostName'].'</title>
173
+ <link href="./layout/favicon.ico" type="image/x-icon" rel="shortcut icon">
174
+ <link href="./layout/'.$theme_css.'" rel="stylesheet">'.($show_icons ? '
175
+ <link href="./layout/icons.css" rel="stylesheet">' : ''
176
+ ).'
177
+ <script src="./layout/scripts.min.js"></script>
178
+ <meta name="generator" content="'.$appName.' ('.$version.')">
179
+ <meta name="author" content="Joseph Gillotti &amp; friends">
180
+ <!--[if lt IE 8]>
181
+ <link href="./layout/old_ie.css" type="text/css" rel="stylesheet">
182
+ <![endif]-->
183
+ <link rel="stylesheet" type="text/css" href="./layout/mobile.css" media="screen and (max-width: 640px)">
184
+ </head>
185
+ <body id="info">
186
+ <div id="header">
187
+ <h1>'.$info['HostName'].'</h1>
188
+ <div class="subtitle">'.$lang['header'].'</div>
189
+ </div>
190
+ <div class="col2">
191
+ <div class="col">
192
+ <div class="infoTable">
193
+ <h2>'.$lang['core'].'</h2>
194
+ <table>';
195
+
196
+ // Linfo Core. Decide what to show.
197
+ $core = array();
198
+
199
+ // OS? (with icon, if we have it)
200
+ if (!empty($settings['show']['os'])) {
201
+ $core[] = array($lang['os'], ($show_icons && (file_exists($this->linfo->getLocalDir().'layout/icons/os_'.$os_icon.'.gif') || file_exists($this->linfo->getLocalDir().'layout/icons/os_'.$os_icon.'.png')) ? '<span class="icon icon_os_'.$os_icon.'"></span>' : '').$info['OS']);
202
+ }
203
+
204
+ // Distribution? (with icon, if we have it)
205
+ if (!empty($settings['show']['distro']) && array_key_exists('Distro', $info) && is_array($info['Distro'])) {
206
+ $core[] = array($lang['distro'], ($show_icons && $distro_icon && (file_exists($this->linfo->getLocalDir().'layout/icons/distro_'.$distro_icon.'.gif') || file_exists($this->linfo->getLocalDir().'layout/icons/distro_'.$distro_icon.'.png')) ? '<span class="icon icon_distro_'.$distro_icon.'"></span>' : '').$info['Distro']['name'].($info['Distro']['version'] ? ' - '.$info['Distro']['version'] : ''));
207
+ }
208
+
209
+ // Virtualization
210
+ if (!empty($settings['show']['virtualization']) && isset($info['virtualization']) && !empty($info['virtualization'])) {
211
+ $vmval = false;
212
+
213
+ if ($info['virtualization']['type'] == 'guest') {
214
+ $vmval = '<span class="icon icon_vm_'.str_replace('/', '_', strtolower($info['virtualization']['method'])).'"></span>'.$info['virtualization']['method'].' '.$lang['guest'];
215
+ } elseif ($info['virtualization']['type'] == 'host') {
216
+ $vmval = '<span class="icon icon_vm_'.str_replace('/', '_', strtolower($info['virtualization']['method'])).'"></span>'.$info['virtualization']['method'].' '.$lang['host'];
217
+ }
218
+
219
+ if ($vmval) {
220
+ $core[] = array($lang['virtualization'], $vmval);
221
+ }
222
+ }
223
+
224
+ // Kernel
225
+ if (!empty($settings['show']['kernel'])) {
226
+ $core[] = array($lang['kernel'], $info['Kernel']);
227
+ }
228
+
229
+ // Model?
230
+ if (!empty($settings['show']['model']) && array_key_exists('Model', $info) && !empty($info['Model'])) {
231
+ $core[] = array($lang['model'], $info['Model']);
232
+ }
233
+
234
+ // IP
235
+ if (!isset($settings['show']['ip']) || !empty($settings['show']['ip'])) {
236
+ $core[] = array($lang['accessed_ip'], $info['AccessedIP']);
237
+ }
238
+
239
+ // Uptime
240
+ if (!empty($settings['show']['uptime']) && $info['UpTime']) {
241
+ $core[] = array($lang['uptime'],
242
+ $info['UpTime']['text'].
243
+ (isset($info['UpTime']['bootedTimestamp']) && $info['UpTime']['bootedTimestamp'] ? '; booted '.date($settings['dates'], $info['UpTime']['bootedTimestamp']) : ''), );
244
+ }
245
+
246
+ // Hostname
247
+ if (!empty($settings['show']['hostname'])) {
248
+ $core[] = array($lang['hostname'], $info['HostName']);
249
+ }
250
+
251
+ //Web server
252
+ if (!empty($settings['show']['webservice'])) {
253
+ $core[] = array($lang['webservice'], $info['webService']);
254
+ }
255
+
256
+ //Php version
257
+ if (!empty($settings['show']['phpversion'])) {
258
+ $core[] = array($lang['phpversion'], $info['phpVersion']);
259
+ }
260
+
261
+ // The CPUs
262
+ if (!empty($settings['show']['cpu'])) {
263
+ $cpus = array();
264
+
265
+ foreach ((array) $info['CPU'] as $cpu) {
266
+ $cpu_html =
267
+ (array_key_exists('Vendor', $cpu) && !empty($cpu['Vendor']) ? $cpu['Vendor'].' - ' : '').
268
+ $cpu['Model'].
269
+ (array_key_exists('MHz', $cpu) ?
270
+ ($cpu['MHz'] < 1000 ? ' ('.$cpu['MHz'].' MHz)' : ' ('.round($cpu['MHz'] / 1000, 3).' GHz)') : '').
271
+ (array_key_exists('usage_percentage', $cpu) ? ' ('.$cpu['usage_percentage'].'%)' : '');
272
+
273
+ if (array_key_exists('usage_percentage', $cpu)) {
274
+ $cpu_html = '<div class="new_bar_left" style="margin-top: 3px; margin-bottom: 3px;">'.self::generateBarChart($cpu['usage_percentage'], $cpu_html).'</div>';
275
+ } else {
276
+ $cpu_html .= '<br>';
277
+ }
278
+
279
+ $cpus[] = $cpu_html;
280
+ }
281
+ $core[] = array('CPUs ('.count($info['CPU']).')', implode('', $cpus));
282
+ }
283
+
284
+ // CPU Usage?
285
+ if (!empty($settings['cpu_usage']) && isset($info['cpuUsage']) && $info['cpuUsage'] !== false) {
286
+ $core[] = array($lang['cpu_usage'],self::generateBarChart($info['cpuUsage']));
287
+ }
288
+
289
+ // System Load
290
+ if (!empty($settings['show']['load'])) {
291
+ $core[] = array($lang['load'],implode(' ', (array) $info['Load']));
292
+ }
293
+
294
+ // CPU architecture. Permissions goes hand in hand with normal CPU
295
+ if (!empty($settings['show']['cpu']) && array_key_exists('CPUArchitecture', $info)) {
296
+ $core[] = array($lang['cpu_arch'], $info['CPUArchitecture']);
297
+ }
298
+
299
+ // We very well may not have process stats
300
+ if (!empty($settings['show']['process_stats']) && $info['processStats']['exists']) {
301
+
302
+ // Different os' have different keys of info
303
+ $proc_stats = array();
304
+
305
+ // Load the keys
306
+ if (array_key_exists('totals', $info['processStats']) && is_array($info['processStats']['totals'])) {
307
+ foreach ($info['processStats']['totals'] as $k => $v) {
308
+ $proc_stats[] = $k.': '.number_format($v);
309
+ }
310
+ }
311
+
312
+ // Total as well
313
+ $proc_stats[] = 'total: '.number_format($info['processStats']['proc_total']);
314
+
315
+ // Show them
316
+ $core[] = array($lang['processes'], implode('; ', $proc_stats));
317
+
318
+ // We might not have threads
319
+ if ($info['processStats']['threads'] !== false) {
320
+ $core[] = array($lang['threads'], number_format($info['processStats']['threads']));
321
+ }
322
+ }
323
+
324
+ // Users with active shells
325
+ if (!empty($settings['show']['numLoggedIn']) && array_key_exists('numLoggedIn', $info) && $info['numLoggedIn']) {
326
+ $core[] = array($lang['numLoggedIn'], $info['numLoggedIn']);
327
+ }
328
+
329
+ // Show
330
+ foreach ($core as $val) {
331
+ echo '
332
+ <tr>
333
+ <th>'.$val[0].'</th>
334
+ <td>'.$val[1].'</td>
335
+ </tr>
336
+ ';
337
+ }
338
+
339
+ echo '
340
+ </table>
341
+ </div>';
342
+
343
+ // Show memory?
344
+ if (!empty($settings['show']['ram'])) {
345
+ echo '
346
+ <div class="infoTable">
347
+ <h2>'.$lang['memory'].'</h2>
348
+ <table>
349
+ <colgroup>
350
+ <col style="width: 12%;" />
351
+ <col style="width: 23%;" />
352
+ <col style="width: 23%;" />
353
+ <col style="width: 23%;" />
354
+ <col style="width: 23%;" />
355
+ </colgroup>
356
+ <tr>
357
+ <th>'.$lang['type'].'</th>
358
+ <th>'.$lang['size'].'</th>
359
+ <th>'.$lang['used'].'</th>
360
+ <th>'.$lang['free'].'</th>
361
+ <th>'.$lang['percent_used'].'</th>
362
+ </tr>
363
+ <tr>
364
+ <td>'.$info['RAM']['type'].'</td>
365
+ <td>'.Common::byteConvert($info['RAM']['total']).'</td>
366
+ <td>'.Common::byteConvert($info['RAM']['total'] - $info['RAM']['free']).'</td>
367
+ <td>'.Common::byteConvert($info['RAM']['free']).'</td>
368
+ <td>'.self::generateBarChart(round(($info['RAM']['total'] - $info['RAM']['free']) * 100 / $info['RAM']['total'])).'</td>
369
+ </tr>';
370
+ $have_swap = (isset($info['RAM']['swapFree']) || isset($info['RAM']['swapTotal']));
371
+ if ($have_swap && !empty($info['RAM']['swapTotal'])) {
372
+ // Show detailed swap info?
373
+ $show_detailed_swap = is_array($info['RAM']['swapInfo']) && count($info['RAM']['swapInfo']) > 0;
374
+ echo'
375
+ <tr>
376
+ <td'.($show_detailed_swap ? ' rowspan="2"' : '').'>Swap</td>
377
+ <td>'.Common::byteConvert(@$info['RAM']['swapTotal']).'</td>
378
+ <td>'.Common::byteConvert(@$info['RAM']['swapTotal'] - $info['RAM']['swapFree']).'</td>
379
+ <td>'.Common::byteConvert(@$info['RAM']['swapFree']).'</td>
380
+ <td>'.self::generateBarChart(round(($info['RAM']['swapTotal'] - $info['RAM']['swapFree']) * 100 / $info['RAM']['swapTotal'])).'</td>
381
+ </tr>';
382
+
383
+ // As in we have at least one swap device present. Show them.
384
+ if ($show_detailed_swap) {
385
+ echo '
386
+ <tr>
387
+ <td colspan="4">
388
+ <table class="mini center">
389
+ <colgroup>
390
+ <col style="width: 25%;" />
391
+ <col style="width: 25%;" />
392
+ <col style="width: 25%;" />
393
+ <col style="width: 25%;" />
394
+ </colgroup>
395
+ <tr>
396
+ <th>'.$lang['device'].'</th>
397
+ <th>'.$lang['type'].'</th>
398
+ <th>'.$lang['size'].'</th>
399
+ <th>'.$lang['used'].'</th>
400
+ </tr>';
401
+ foreach ($info['RAM']['swapInfo'] as $swap) {
402
+ echo '
403
+ <tr>
404
+ <td>'.$swap['device'].'</td>
405
+ <td>'.ucfirst($swap['type']).'</td>
406
+ <td>'.Common::byteConvert($swap['size']).'</td>
407
+ <td>'.Common::byteConvert($swap['used']).'</td>
408
+ </tr>
409
+ ';
410
+ }
411
+ echo '
412
+ </table>
413
+ </td>
414
+ </tr>';
415
+ }
416
+ }
417
+
418
+ echo '
419
+ </table>
420
+ </div>';
421
+ }
422
+
423
+ // Network Devices?
424
+ if (!empty($settings['show']['network'])) {
425
+ $show_type = array_key_exists('nic_type', $info['contains']) ? $info['contains']['nic_type'] : true;
426
+ $show_speed = array_key_exists('nic_port_speed', $info['contains']) ? $info['contains']['nic_port_speed'] : true;
427
+ echo '
428
+ <div class="infoTable network_devices">
429
+ <h2>'.$lang['network_devices'].'</h2>
430
+ <table>
431
+ <tr>
432
+ <th>'.$lang['device_name'].'</th>'.($show_type ? '
433
+ <th>'.$lang['type'].'</th>' : '').($show_speed ? '
434
+ <th>'.$lang['port_speed'].'</th>' : '').'
435
+ <th>'.$lang['amount_sent'].'</th>
436
+ <th>'.$lang['amount_received'].'</th>
437
+ <th>'.$lang['state'].'</th>
438
+ </tr>';
439
+
440
+ if (count($info['Network Devices']) > 0) {
441
+ foreach ($info['Network Devices'] as $device => $stats) {
442
+ echo '
443
+ <tr>
444
+ <td>'.$device.'</td>'.($show_type ? '
445
+ <td>'.$stats['type'].'</td>' : '').($show_speed ? '
446
+ <td>'.(isset($stats['port_speed']) && $stats['port_speed'] !== false ? $stats['port_speed'].'Mb/s' : '').'</td>' : '').'
447
+ <td>'.Common::byteConvert($stats['sent']['bytes']).'</td>
448
+ <td>'.Common::byteConvert($stats['recieved']['bytes']).'</td>
449
+ <td class="net_'.$stats['state'].'">'.ucfirst($stats['state']).'</td>
450
+ </tr>';
451
+ }
452
+ } else {
453
+ echo '<tr><td colspan="5" class="none">'.$lang['none_found'].'</td></tr>';
454
+ }
455
+ echo '
456
+ </table>
457
+ </div>';
458
+ }
459
+
460
+ // Show temps?
461
+ if (!empty($settings['show']['temps']) && count($info['Temps']) > 0) {
462
+ echo '
463
+ <div class="infoTable">
464
+ <h2>'.$lang['temps_voltages'].'</h2>
465
+ <table>
466
+ <tr><th>'.$lang['path'].'</th><th>'.$lang['device'].'</th><th>'.$lang['value'].'</th></tr>
467
+ ';
468
+ if (count($info['Temps']) > 0) {
469
+ foreach ($info['Temps'] as $stat) {
470
+ echo '
471
+ <tr>
472
+ <td>'.$stat['path'].'</td>
473
+ <td>'.$stat['name'].'</td>
474
+ <td>'.(
475
+ array_key_exists('bar', $stat) && $stat['bar'] && $stat['unit'] == '%' ?
476
+ '<div class="bar_chart">
477
+ <div class="bar_inner" style="width: '.$stat['temp'].'%;">
478
+ <div class="bar_text">
479
+ '.($stat['temp'] > -1 ? $stat['temp'] : '?').'%
480
+ </div>
481
+ </div>
482
+ </div>
483
+ ':
484
+ $stat['temp'].' '.$stat['unit']).'</td>
485
+ </tr>
486
+ ';
487
+ }
488
+ } else {
489
+ echo '<tr><td colspan="3" class="none">'.$lang['none_found'].'</td></tr>';
490
+ }
491
+ echo '
492
+ </table>
493
+ </div>';
494
+ }
495
+
496
+ // Show battery?
497
+ if (!empty($settings['show']['battery']) && count($info['Battery']) > 0) {
498
+ echo '
499
+ <div class="infoTable">
500
+ <h2>'.$lang['batteries'].'</h2>
501
+ <table>
502
+ <tr>
503
+ <th>'.$lang['device'].'</th>
504
+ <th>'.$lang['state'].'</th>
505
+ <th>'.$lang['charge'].' %</th>
506
+ </tr>
507
+ ';
508
+ foreach ($info['Battery'] as $bat) {
509
+ echo '
510
+ <tr>
511
+ <td>'.$bat['device'].'</td>
512
+ <td>'.$bat['state'].'</td>
513
+ <td>'.self::generateBarChart((int) $bat['percentage'], $bat['percentage'] > -1 ? $bat['percentage'].'%' : 'N/A').'</td>
514
+ </tr>
515
+ ';
516
+ }
517
+ echo '
518
+ </table>
519
+ </div>';
520
+ }
521
+
522
+ // Show services?
523
+ if (!empty($settings['show']['services']) && count($info['services']) > 0) {
524
+ echo '
525
+ <div class="infoTable">
526
+ <h2>'.$lang['services'].'</h2>
527
+ <table>
528
+ <tr>
529
+ <th>'.$lang['service'].'</th>
530
+ <th>'.$lang['state'].'</th>
531
+ <th>'.$lang['pid'].'</th>
532
+ <th>Threads</th>
533
+ <th>'.$lang['memory_usage'].'</th>
534
+ </tr>
535
+ ';
536
+
537
+ // Show them
538
+ foreach ($info['services'] as $service => $state) {
539
+ $state_parts = explode(' ', $state['state'], 2);
540
+ echo '
541
+ <tr>
542
+ <td>'.$service.'</td>
543
+ <td>
544
+ <span class="service_'.strtolower($state_parts[0]).'">'.$state_parts[0].'</span>
545
+ '.(array_key_exists(1, $state_parts) ? self::fadedText($state_parts[1]).'</span>' : '').'</td>
546
+ <td>'.$state['pid'].'</td>
547
+ <td>',$state['threads'] ? $state['threads'] : '?','</td>
548
+ <td>',$state['memory_usage'] ? Common::byteConvert($state['memory_usage']) : '?','</td>
549
+ </tr>
550
+ ';
551
+ }
552
+
553
+ echo '
554
+ </table>
555
+ </div>';
556
+ }
557
+
558
+ echo '
559
+ </div>
560
+ <div class="col">';
561
+
562
+ // Show hardware?
563
+ if (!empty($settings['show']['devices'])) {
564
+
565
+ // Don't show vendor?
566
+ $show_vendor = array_key_exists('hw_vendor', $info['contains']) ? ($info['contains']['hw_vendor'] === false ? false : true) : true;
567
+
568
+ echo '
569
+ <div class="infoTable">
570
+ <h2>'.$lang['hardware'].'</h2>
571
+ <table>
572
+ <tr>
573
+ <th>'.$lang['type'].'</th>
574
+ ',($show_vendor ? '<th>'.$lang['vendor'].'</th>' : ''),'
575
+ <th>'.$lang['device'].'</th>
576
+ </tr>
577
+ ';
578
+ $num_devs = count($info['Devices']);
579
+ if ($num_devs > 0) {
580
+ for ($i = 0; $i < $num_devs; ++$i) {
581
+ echo '
582
+ <tr>
583
+ <td class="center">'.$info['Devices'][$i]['type'].'</td>
584
+ ',$show_vendor ? '<td>'.($info['Devices'][$i]['vendor'] ? $info['Devices'][$i]['vendor'] : 'Unknown').'</td>' : '','
585
+ <td>'.$info['Devices'][$i]['device'].'</td>
586
+ </tr>';
587
+ }
588
+ } else {
589
+ echo '<tr><td colspan="3" class="none">'.$lang['none_found'].'</td></tr>';
590
+ }
591
+ echo '
592
+ </table>
593
+ </div>';
594
+ }
595
+
596
+ // Show drives?
597
+ if (!empty($settings['show']['hd'])) {
598
+
599
+ // Should we not show the Reads and Writes columns?
600
+ $show_stats = array_key_exists('drives_rw_stats', $info['contains']) ? ($info['contains']['drives_rw_stats'] === false ? false : true) : true;
601
+
602
+ // Or vendor columns?
603
+ $show_vendor = array_key_exists('drives_vendor', $info['contains']) ? ($info['contains']['drives_vendor'] === false ? false : true) : true;
604
+
605
+ echo '
606
+ <div class="infoTable">
607
+ <h2>'.$lang['drives'].'</h2>
608
+ <table>
609
+ <tr>
610
+ <th>'.$lang['path'].'</th>
611
+ ',$show_vendor ? '<th>'.$lang['vendor'] : '','</th>
612
+ <th>'.$lang['name'].'</th>
613
+ ',$show_stats ? '<th>'.$lang['reads'].'</th>
614
+ <th>'.$lang['writes'].'</th>' : '','
615
+ <th>'.$lang['size'].'</th>
616
+ </tr>';
617
+ if (count($info['HD']) > 0) {
618
+ foreach ($info['HD'] as $drive) {
619
+ echo '
620
+ <tr>
621
+ <td>'.$drive['device'].'</td>
622
+ ',$show_vendor ? '<td>'.($drive['vendor'] ? $drive['vendor'] : $lang['unknown']).'</td>' : '','
623
+ <td>',$drive['name'] ? $drive['name'] : $lang['unknown'],'</td>
624
+ ', $show_stats ? '<td>'.($drive['reads'] !== false ? number_format($drive['reads']) : $lang['unknown']).'</td>
625
+ <td>'.($drive['writes'] !== false ? number_format($drive['writes']) : $lang['unknown']).'</td>' : '','
626
+ <td>',$drive['size'] ? Common::byteConvert($drive['size']) : $lang['unknown'],'</td>
627
+ </tr>';
628
+
629
+ // If we've got partitions for this drive, show them too
630
+ if (array_key_exists('partitions', $drive) && is_array($drive['partitions']) && count($drive['partitions']) > 0) {
631
+ echo '
632
+ <tr>
633
+ <td colspan="6">';
634
+
635
+ // Each
636
+ foreach ($drive['partitions'] as $partition) {
637
+ echo '
638
+ &#9492; '.(isset($partition['number']) ? $drive['device'].$partition['number'] : $partition['name']).' - '.Common::byteConvert($partition['size']).'<br />';
639
+ }
640
+
641
+ echo '
642
+ </td>
643
+ </tr>
644
+ ';
645
+ }
646
+ }
647
+ } else {
648
+ echo '<tr><td colspan="6" class="none">'.$lang['none_found'].'</td></tr>';
649
+ }
650
+
651
+ echo '
652
+ </table>
653
+ </div>';
654
+ }
655
+
656
+ // Show sound card stuff?
657
+ if (!empty($settings['show']['sound']) && count($info['SoundCards']) > 0) {
658
+ echo '
659
+ <div class="infoTable">
660
+ <h2>'.$lang['sound_cards'].'</h2>
661
+ <table>
662
+ <tr>
663
+ <th>'.$lang['number'].'</th>
664
+ <th>'.$lang['vendor'].'</th>
665
+ <th>'.$lang['card'].'</th>
666
+ </tr>';
667
+ foreach ($info['SoundCards'] as $card) {
668
+ if (empty($card['vendor'])) {
669
+ $card['vendor'] = 'Unknown';
670
+ }
671
+ echo '
672
+ <tr>
673
+ <td>'.$card['number'].'</td>
674
+ <td>'.$card['vendor'].'</td>
675
+ <td>'.$card['card'].'</td>
676
+ </tr>';
677
+ }
678
+ echo '
679
+ </table>
680
+ </div>
681
+ ';
682
+ }
683
+
684
+ echo '
685
+ </div>
686
+ </div>';
687
+
688
+ // Show file system mounts?
689
+ if (!empty($settings['show']['mounts'])) {
690
+ $has_devices = false;
691
+ $has_labels = false;
692
+ $has_types = false;
693
+ foreach ($info['Mounts'] as $mount) {
694
+ if (!empty($mount['device'])) {
695
+ $has_devices = true;
696
+ }
697
+ if (!empty($mount['label'])) {
698
+ $has_labels = true;
699
+ }
700
+ if (!empty($mount['devtype'])) {
701
+ $has_types = true;
702
+ }
703
+ }
704
+ $addcolumns = 0;
705
+ if ($settings['show']['mounts_options']) {
706
+ $addcolumns++;
707
+ }
708
+ if ($has_devices) {
709
+ $addcolumns++;
710
+ }
711
+ if ($has_labels) {
712
+ $addcolumns++;
713
+ }
714
+ if ($has_types) {
715
+ $addcolumns++;
716
+ }
717
+ echo '
718
+ <div class="infoTable filesystem_mounts">
719
+ <h2>'.$lang['filesystem_mounts'].'</h2>
720
+ <table>
721
+ <tr>';
722
+ if ($has_types) {
723
+ echo '<th>'.$lang['type'].'</th>';
724
+ }
725
+ if ($has_devices) {
726
+ echo '<th>'.$lang['device'].'</th>';
727
+ }
728
+ echo '<th>'.$lang['mount_point'].'</th>';
729
+ if ($has_labels) {
730
+ echo '<th>'.$lang['label'].'</th>';
731
+ }
732
+ echo'
733
+ <th>'.$lang['filesystem'].'</th>',$settings['show']['mounts_options'] ? '
734
+ <th>'.$lang['mount_options'].'</th>' : '','
735
+ <th>'.$lang['size'].'</th>
736
+ <th>'.$lang['used'].'</th>
737
+ <th>'.$lang['free'].'</th>
738
+ <th style="width: 12%;">'.$lang['percent_used'].'</th>
739
+ </tr>
740
+ ';
741
+
742
+ // Calc totals
743
+ $total_size = 0;
744
+ $total_used = 0;
745
+ $total_free = 0;
746
+
747
+ // Don't add totals for duplicates. (same filesystem mount twice in different places)
748
+ $done_devices = array();
749
+
750
+ // Are there any?
751
+ if (count($info['Mounts']) > 0) {
752
+
753
+ // Go through each
754
+ foreach ($info['Mounts'] as $mount) {
755
+
756
+ // Only add totals for this device if we haven't already
757
+ if (!in_array($mount['device'], $done_devices)) {
758
+ $total_size += $mount['size'];
759
+ $total_used += $mount['used'];
760
+ $total_free += $mount['free'];
761
+ if (!empty($mount['device'])) {
762
+ $done_devices[] = $mount['device'];
763
+ }
764
+ }
765
+
766
+ // Possibly don't show this twice
767
+ elseif (array_key_exists('duplicate_mounts', $settings['show']) && empty($settings['show']['duplicate_mounts'])) {
768
+ continue;
769
+ }
770
+
771
+ // If it's an NFS mount it's likely in the form of server:path (without a trailing slash),
772
+ // but if the path is just / it likely just shows up as server:,
773
+ // which is vague. If there isn't a /, add one
774
+ if (preg_match('/^.+:$/', $mount['device']) == 1) {
775
+ $mount['device'] .= DIRECTORY_SEPARATOR;
776
+ }
777
+
778
+ echo '<tr>';
779
+ if ($has_types) {
780
+ echo '<td>'.$mount['devtype'].'</td>';
781
+ }
782
+ if ($has_devices) {
783
+ echo '<td>'.$mount['device'].'</td>';
784
+ }
785
+ echo '<td>'.$mount['mount'].'</td>';
786
+ if ($has_labels) {
787
+ echo '<td>'.$mount['label'].'</td>';
788
+ }
789
+ echo'
790
+ <td>'.$mount['type'].'</td>', $settings['show']['mounts_options'] ? '
791
+ <td>'.(empty($mount['options']) ? '<em>unknown</em>' : '<ul><li>'.implode('</li><li>', $mount['options']).'</li></ul>').'</td>' : '','
792
+ <td>'.Common::byteConvert($mount['size']).'</td>
793
+ <td>'.Common::byteConvert($mount['used']).
794
+ ($mount['used_percent'] !== false ? ' <span class="perc">('.$mount['used_percent'].'%)</span>' : '').'</td>
795
+ <td>'.Common::byteConvert($mount['free']).
796
+ ($mount['free_percent'] !== false ? ' <span class="perc">('.$mount['free_percent'].'%)</span>' : '').'</td>
797
+ <td>
798
+ '.self::generateBarChart((int) $mount['used_percent'], $mount['used_percent'] ? $mount['used_percent'].'%' : 'N/A').'
799
+ </td>
800
+ </tr>';
801
+ }
802
+ } else {
803
+ echo '<tr><td colspan="',6 + $addcolumns,'" class="none">None found</td></tr>';
804
+ }
805
+
806
+ // Show totals and finish table
807
+ $total_used_perc = $total_size > 0 && $total_used > 0 ? round($total_used / $total_size, 2) * 100 : 0;
808
+ echo '
809
+ <tr class="alt">
810
+ <td colspan="',2 + $addcolumns,'">Totals: </td>
811
+ <td>'.Common::byteConvert($total_size).'</td>
812
+ <td>'.Common::byteConvert($total_used).'</td>
813
+ <td>'.Common::byteConvert($total_free).'</td>
814
+ <td>
815
+ '.self::generateBarChart($total_used_perc, $total_used_perc.'%').'
816
+ </td>
817
+ </tr>
818
+ </table>
819
+ </div>';
820
+ }
821
+
822
+ // Show RAID Arrays?
823
+ if (!empty($settings['show']['raid']) && count($info['Raid']) > 0) {
824
+ echo '
825
+ <div class="infoTable drives">
826
+ <h2>'.$lang['raid_arrays'].'</h2>
827
+ <table>
828
+ <colgroup>
829
+ <col style="width: 10%;" />
830
+ <col style="width: 30%;" />
831
+ <col style="width: 10%;" />
832
+ <col style="width: 10%;" />
833
+ <col style="width: 30%;" />
834
+ <col style="width: 10%;" />
835
+ </colgroup>
836
+ <tr>
837
+ <th>'.$lang['name'].'</th>
838
+ <th>'.$lang['level'].'</th>
839
+ <th>'.$lang['status'].'</th>
840
+ <th>'.$lang['size'].'</th>
841
+ <th>'.$lang['devices'].'</th>
842
+ <th>'.$lang['active'].'</th>
843
+ </tr>
844
+ ';
845
+ if (count($info['Raid']) > 0) {
846
+ foreach ($info['Raid'] as $raid) {
847
+ $active = explode('/', $raid['count']);
848
+ // http://en.wikipedia.org/wiki/Standard_RAID_levels
849
+ switch ($raid['level']) {
850
+ case 0:
851
+ $type = 'Stripe';
852
+ break;
853
+ case 1:
854
+ $type = 'Mirror';
855
+ break;
856
+ case 10:
857
+ $type = 'Mirrored Stripe';
858
+ break;
859
+ case 5:
860
+ case 6:
861
+ $type = 'Distributed Parity Block-Level Striping';
862
+ break;
863
+ default:
864
+ $type = false;
865
+ break;
866
+ }
867
+ echo '
868
+ <tr>
869
+ <td>'.$raid['device'].'</td>
870
+ <td>'.$raid['level'].($type ? ' <span class="caption">('.$type.')</span>' : '').'</td>
871
+ <td>'.ucfirst($raid['status']).'</td>
872
+ <td>'.$raid['size'].'</td>
873
+ <td><table class="mini center margin_auto"><tr><th>'.$lang['device'].'</th><th>'.$lang['state'].'</th></tr>';
874
+
875
+ foreach ($raid['drives'] as $drive) {
876
+ echo '<tr><td>'.$drive['drive'].'</td><td class="raid_'.$drive['state'].'">'.ucfirst($drive['state']).'</td></tr>';
877
+ }
878
+
879
+ echo '</table></td>
880
+ <td>'.$active[1].'/'.$active[0].'</td>
881
+ </tr>
882
+ ';
883
+ }
884
+ } else {
885
+ echo '<tr><td colspan="6" class="none">'.$lang['none_found'].'</td></tr>';
886
+ }
887
+
888
+ echo '
889
+ </table>
890
+ </div>';
891
+ }
892
+
893
+ // Feel like showing errors? Are there any even?
894
+ if (!empty($settings['show_errors']) && Errors::num() > 0) {
895
+ echo '
896
+ <div id="errorList" class="infoTable">
897
+ <h2>'.$lang['error_head'].'</h2>
898
+ <table>
899
+ <tr>
900
+ <th>'.$lang['from_where'].'</th>
901
+ <th>'.$lang['message'].'</th>
902
+ </tr>';
903
+
904
+ foreach (Errors::show() as $error) {
905
+ echo '
906
+ <tr>
907
+ <td>'.$error[0].'</td>
908
+ <td>'.$error[1].'</td>
909
+ </tr>
910
+ ';
911
+ }
912
+
913
+ echo '
914
+ </table>
915
+ </div>
916
+ ';
917
+ }
918
+
919
+ // Additional extensions
920
+ if (count($info['extensions']) > 0) {
921
+ foreach ($info['extensions'] as $ext) {
922
+ if (is_array($ext) && count($ext) > 0) {
923
+
924
+ // Decide how to show something extra
925
+ switch (array_key_exists('extra_type', $ext) && !empty($ext['extra_vals']) ? $ext['extra_type'] : false) {
926
+
927
+ // Table with a key->value table to the right of it
928
+ // Useful for stats or other stuff pertaining to
929
+ // the main info to the left
930
+ case 'k->v':
931
+ echo '
932
+ <div class="col2_side">
933
+ <div class="col2_side_left">
934
+ '.self::createTable($ext).'
935
+ </div>
936
+ <div class="col2_side_right">
937
+ <div class="infoTable">
938
+ <h2>'.$ext['extra_vals']['title'].'</h2>
939
+ <table>';
940
+
941
+ // Give each value
942
+ foreach (array_filter($ext['extra_vals']['values']) as $v) {
943
+ echo '
944
+ <tr>
945
+ <th>'.$v[0].'</th>
946
+ <td>'.$v[1].'</td>
947
+ </tr>';
948
+ }
949
+ echo'
950
+ </table>
951
+ </div>
952
+ </div>
953
+ </div>
954
+ ';
955
+ break;
956
+
957
+ // Nothing extra; just the table
958
+ default:
959
+ echo self::createTable($ext);
960
+ break;
961
+ }
962
+ }
963
+ }
964
+ }
965
+
966
+ // Feel like showing timed results?
967
+ if (!empty($settings['timer'])) {
968
+ echo '
969
+ <div id="timerList" class="infoTable">
970
+ <h2>'.$lang['timer'].'</h2>
971
+ <table>
972
+ <tr>
973
+ <th>'.$lang['area'].'</th>
974
+ <th>'.$lang['time_taken'].'</th>
975
+ </tr>';
976
+
977
+ foreach (Timer::getResults() as $result) {
978
+ echo '
979
+ <tr>
980
+ <td>'.$result['id'].'</td>
981
+ <td>'.round($result['duration'], 3).' '.$lang['seconds'].'</td>
982
+ </tr>
983
+ ';
984
+ }
985
+
986
+ echo '
987
+ </table>
988
+ </div>
989
+ ';
990
+ }
991
+
992
+ echo '
993
+ <div id="foot">
994
+ '.sprintf($lang['footer_app'], '<a href="https://github.com/jrgp/linfo"><em>'.$appName.'</em></a> ('.$version.')', round(microtime(true) - $this->linfo->getTimeStart(), 2)).'<br>
995
+ <em>'.$appName.'</em> &copy; 2010 &ndash; '.(date('Y') > 2011 ? date('Y') : 2011).'
996
+ Joseph Gillotti '.(date('m/d') == '06/03' ? ' (who turns '.(date('Y') - 1993).' today!)' : '').'&amp; friends. Source code licensed under GPL.
997
+ </div>
998
+ <div id="foot_time">
999
+ <br />
1000
+ Generated on '.date($settings['dates']).'
1001
+ </div>
1002
+ <script>Linfo.init()</script>
1003
+ </body>
1004
+ </html>';
1005
+ }
1006
+ }
lib/Linfo/Output/Json.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Linfo\Output;
4
+
5
+ use Linfo\Linfo;
6
+ use Linfo\Exceptions\FatalException;
7
+
8
+ class Json implements Output
9
+ {
10
+ protected $linfo;
11
+
12
+ public function __construct(Linfo $linfo)
13
+ {
14
+ $this->linfo = $linfo;
15
+ }
16
+
17
+ public function output()
18
+ {
19
+ $settings = $this->linfo->getSettings();
20
+
21
+ if (!headers_sent()) {
22
+ header('Content-Type: application/json');
23
+ }
24
+
25
+ // Make sure we have JSON
26
+ if (!function_exists('json_encode')) {
27
+ throw new FatalException('{"error":"JSON extension not loaded"}');
28
+ }
29
+
30
+ // Output buffering, along with compression (if supported)
31
+ if (!isset($settings['compress_content']) || $settings['compress_content']) {
32
+ ob_start(function_exists('ob_gzhandler') ? 'ob_gzhandler' : null);
33
+ }
34
+
35
+ // Give it. Support JSON-P like functionality if the ?callback param looks like a valid javascript
36
+ // function name, including object traversal.
37
+ echo array_key_exists('callback', $_GET) && preg_match('/^[a-z0-9\_\.]+$/i', $_GET['callback']) ?
38
+ $_GET['callback'].'('.json_encode($this->linfo->getInfo()).')' : json_encode($this->linfo->getInfo());
39
+
40
+ // Send it all out
41
+ if (!isset($settings['compress_content']) || $settings['compress_content']) {
42
+ ob_end_flush();
43
+ }
44
+ }
45
+ }
lib/Linfo/Output/Ncurses.php ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2011, 2015 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo\Output;
20
+
21
+ use Linfo\Linfo;
22
+ use Linfo\Common;
23
+ use Linfo\Exceptions\FatalException;
24
+
25
+ /**
26
+ * Output in ncurses format for client side CLI functionality.
27
+ *
28
+ * @author Joseph Gillotti
29
+ */
30
+ class Ncurses implements Output
31
+ {
32
+ private $linfo,
33
+
34
+ // Store our windows here
35
+ $_windows = array(),
36
+ $_max_dims = array(),
37
+
38
+ // ncurses loaded?
39
+ $loaded = true;
40
+
41
+ public function __construct(Linfo $linfo)
42
+ {
43
+ $this->linfo = $linfo;
44
+
45
+ // We obviously need this
46
+ if (!extension_loaded('ncurses')) {
47
+ $this->loaded = false;
48
+ throw new FatalException("PHP ncurses extension not loaded.\nRefer to http://php.net/manual/en/ncurses.installation.php for details.");
49
+ }
50
+
51
+ // Start ncurses
52
+ ncurses_init();
53
+
54
+ ncurses_start_color();
55
+ ncurses_init_pair(1, NCURSES_COLOR_YELLOW, NCURSES_COLOR_BLUE);
56
+
57
+ ncurses_timeout(0);
58
+ }
59
+
60
+ // Make sure ncurses_end() always gets called no matter what;
61
+ // not doing so will leave the terminal messed up until the user
62
+ // runs 'reset'
63
+ public function __destruct()
64
+ {
65
+ if ($this->loaded) {
66
+ ncurses_end();
67
+ }
68
+ }
69
+
70
+ public function output()
71
+ {
72
+
73
+ // Gain access to translations
74
+ $lang = $this->linfo->getLang();
75
+
76
+ // And info
77
+ $this->linfo->scan();
78
+ $info = $this->linfo->getInfo();
79
+
80
+ // Say we're called more than once. Kill previous remnants
81
+ if (count($this->_windows) > 0) {
82
+ $this->_kill_windows();
83
+ }
84
+
85
+ // Get dimensions and give lovely header text
86
+ $fullscreen = ncurses_newwin(0, 0, 0, 0);
87
+ ncurses_wrefresh($fullscreen);
88
+ ncurses_getmaxyx($fullscreen, $x, $y);
89
+ ncurses_mvwaddstr($fullscreen, 0, 0, 'Generated by '.$this->linfo->getAppName().' ('.$this->linfo->getVersion().') (ncurses) on '.date('m/d/Y @ h:i:s A (T)'));
90
+ ncurses_wrefresh($fullscreen);
91
+ $this->_max_dims = array($x, $y);
92
+
93
+ // Some important windows
94
+ $core_wins = array(
95
+ array(
96
+ 'name' => $lang['core'],
97
+ 'content' => array(
98
+ array($lang['os'], $info['OS']),
99
+ array_key_exists('Distro', $info) ? array($lang['distro'], $info['Distro']['name'].($info['Distro']['version'] ? ' '.$info['Distro']['version'] : '')) : false,
100
+ array($lang['kernel'], $info['Kernel']),
101
+ array_key_exists('Model', $info) && !empty($info['Model']) ? array($lang['model'], $info['Model']) : false,
102
+ array($lang['uptime'], str_ireplace(array(' ', 'days', 'minutes', 'hours', 'seconds'), array('', 'd', 'm', 'h', 's'), $info['UpTime']['text'])),
103
+ array($lang['hostname'], $info['HostName']),
104
+
105
+ array_key_exists('CPUArchitecture', $info) ? array($lang['cpu_arch'], $info['CPUArchitecture']) : false,
106
+
107
+ array($lang['load'], implode(' ', (array) $info['Load'])),
108
+ ),
109
+ ),
110
+ array(
111
+ 'name' => $lang['memory'],
112
+ 'content' => array(
113
+ array($lang['size'], Common::byteConvert($info['RAM']['total'])),
114
+ array($lang['used'], Common::byteConvert($info['RAM']['total'] - $info['RAM']['free'])),
115
+ array($lang['free'], Common::byteConvert($info['RAM']['free'])),
116
+ ),
117
+ ),
118
+ );
119
+
120
+ // Show them
121
+ $h = 1;
122
+ foreach ($core_wins as $win) {
123
+ list($width, $height) = $this->_window_with_lines($win['name'], $win['content'], $h, 0);
124
+ $h += $height + 1;
125
+ }
126
+
127
+ // Makeshift event loop
128
+ while (true) {
129
+
130
+ // Die on input
131
+ $getch = ncurses_getch();
132
+ if ($getch > 0 && $getch == 113) {
133
+ $this->__destruct();
134
+ echo "\nEnding at your request.\n";
135
+ exit(0);
136
+ }
137
+
138
+ // Stop temporariy
139
+ ncurses_napms(1000);
140
+
141
+ // Call ourselves
142
+ $this->output();
143
+ }
144
+ }
145
+
146
+ // Create a window with various lines as content
147
+ private function _window_with_lines($name, $lines, $x = 5, $y = 5, $set_width = false)
148
+ {
149
+
150
+ // Need an array of lines.
151
+ $lines = (array) $lines;
152
+
153
+ // Ignore disabled liens
154
+ $lines = array_filter($lines);
155
+
156
+ // Do we not have a specific set width? Calculate the longest line
157
+ if (!is_numeric($set_width)) {
158
+ $longest_line = strlen($name) + 10;
159
+ foreach ($lines as $line) {
160
+ $length = strlen(implode('', $line));
161
+ $longest_line = $length > $longest_line ? $length : $longest_line;
162
+ }
163
+ $width = $longest_line + 4;
164
+ }
165
+
166
+ // Otherwise we do have a set with
167
+ else {
168
+ $width = $set_width;
169
+ }
170
+
171
+ // Calculate window hight
172
+ $height = 3 + count($lines);
173
+
174
+ // Create window
175
+ $win = ncurses_newwin($height, $width, $x, $y);
176
+
177
+ // This character will be the side borders
178
+ $side = ord('|');
179
+
180
+ // Do the borders of the window
181
+ ncurses_wborder($win, $side, $side, ord('-'), ord('-'), ord('/'), ord('\\'), ord('\\'), ord('/'));
182
+
183
+ // Add window title string
184
+ ncurses_mvwaddstr($win, 1, 1, $this->_charpad($name, $width, 'c', '='));
185
+
186
+ // Keep track of vertical position for each line
187
+ $v = 1;
188
+
189
+ // Go through and output each line, while incrementing line position counter
190
+ foreach ($lines as $line) {
191
+ ncurses_mvwaddstr($win, $v + 1, 1, $this->_charpad($line[0].$this->_charpad($line[1], $width - strlen($line[0]), 'r', '.'), $width, 'n'));
192
+ ++$v;
193
+ }
194
+
195
+ // Show it
196
+ ncurses_wrefresh($win);
197
+
198
+ // Store it so we can kill it later
199
+ $this->_windows[] = &$win;
200
+
201
+ // Return window dimensions
202
+ return array($width, $height);
203
+ }
204
+
205
+ // Kill all windows
206
+ private function _kill_windows()
207
+ {
208
+ foreach ($this->_windows as $win) {
209
+ is_resource($win) &&
210
+ ncurses_delwin($win);
211
+ }
212
+ }
213
+
214
+ // Because I got tired of sprintf
215
+ private function _charpad($string, $length, $direction, $filler = ' ')
216
+ {
217
+
218
+ // Keep length of string handy here
219
+ $strlen = strlen($string);
220
+
221
+ // Difference between max length and string length
222
+ $difference = $length - $strlen;
223
+
224
+ // If the string length is bigger than the max, just return string truncated to the max length
225
+ if ($difference < 0) {
226
+ return substr($string, 0, $length);
227
+ }
228
+
229
+ // Deal with direction
230
+ switch ($direction) {
231
+
232
+ // Right aligned (padded to the left)
233
+ case 'r':
234
+ return str_repeat($filler, $difference - 2).$string;
235
+ break;
236
+
237
+ // Left aligned (padded to the right)
238
+ case 'l':
239
+ return $string.str_repeat($filler, $difference - 2);
240
+ break;
241
+
242
+ // Centered (padded left and right)
243
+ case 'c':
244
+ $cdiff = floor($difference / 2) - ($difference % 2 == 0 ? 1 : 0);
245
+
246
+ return str_repeat($filler, $cdiff - 1).$string.str_repeat($filler, $cdiff);
247
+ break;
248
+
249
+ // Not padded; returned as is (provided not longer than max, as tested above)
250
+ case 'n':
251
+ return $string;
252
+ break;
253
+
254
+ // Uhh not sure?
255
+ default:
256
+ return '';
257
+ break;
258
+ }
259
+ }
260
+ }
lib/Linfo/Output/Output.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2015 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Output;
22
+
23
+ use Linfo\Linfo;
24
+
25
+ interface Output
26
+ {
27
+ public function __construct(Linfo $linfo);
28
+ public function output();
29
+ }
lib/Linfo/Output/Serialized.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Linfo\Output;
4
+
5
+ use Linfo\Linfo;
6
+
7
+ class Serialized implements Output
8
+ {
9
+ protected $linfo;
10
+
11
+ public function __construct(Linfo $linfo)
12
+ {
13
+ $this->linfo = $linfo;
14
+ }
15
+
16
+ public function output()
17
+ {
18
+ $settings = $this->linfo->getSettings();
19
+
20
+ // Output buffering, along with compression (if supported)
21
+ if (!isset($settings['compress_content']) || $settings['compress_content']) {
22
+ ob_start(function_exists('ob_gzhandler') ? 'ob_gzhandler' : null);
23
+ }
24
+
25
+ echo serialize($this->linfo->getInfo());
26
+ }
27
+ }
lib/Linfo/Output/Xml.php ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Linfo\Output;
4
+
5
+ use Exception;
6
+ use Linfo\Linfo;
7
+ use Linfo\Common;
8
+ use Linfo\Exceptions\FatalException;
9
+ use SimpleXMLElement;
10
+
11
+ class Xml implements Output
12
+ {
13
+ protected $linfo;
14
+
15
+ public function __construct(Linfo $linfo)
16
+ {
17
+ if (!extension_loaded('SimpleXML')) {
18
+ throw new FatalException('Cannot generate XML. Install php\'s SimpleXML extension.');
19
+ }
20
+
21
+ $this->linfo = $linfo;
22
+ }
23
+
24
+ public function output()
25
+ {
26
+ $lang = $this->linfo->getLang();
27
+ $settings = $this->linfo->getSettings();
28
+ $info = $this->linfo->getInfo();
29
+
30
+ try {
31
+ // Start it up
32
+ $xml = new SimpleXMLElement('<?xml version="1.0"?><linfo></linfo>');
33
+
34
+ // Deal with core stuff
35
+ $core_elem = $xml->addChild('core');
36
+ $core = array();
37
+ if (!empty($settings['show']['os'])) {
38
+ $core[] = array('os', $info['OS']);
39
+ }
40
+ if (!empty($settings['show']['distro']) && isset($info['Distro']) && is_array($info['Distro'])) {
41
+ $core[] = array('distro', $info['Distro']['name'].($info['Distro']['version'] ? ' - '.$info['Distro']['version'] : ''));
42
+ }
43
+ if (!empty($settings['show']['kernel'])) {
44
+ $core[] = array('kernel', $info['Kernel']);
45
+ }
46
+ if (!empty($settings['show']['webservice'])) {
47
+ $core[] = array('webservice', $info['webService']);
48
+ }
49
+ if (!empty($settings['show']['phpversion'])) {
50
+ $core[] = array('phpversion', $info['phpVersion']);
51
+ }
52
+ if (!isset($settings['show']['ip']) || !empty($settings['show']['ip'])) {
53
+ $core[] = array('accessed_ip', (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'Unknown'));
54
+ }
55
+ if (!empty($settings['show']['uptime'])) {
56
+ $core[] = array('uptime', $info['UpTime']['text']);
57
+ }
58
+ if (!empty($settings['show']['hostname'])) {
59
+ $core[] = array('hostname', $info['HostName']);
60
+ }
61
+ if (!empty($settings['show']['cpu'])) {
62
+ $cpus = '';
63
+ foreach ((array) $info['CPU'] as $cpu) {
64
+ $cpus .=
65
+ (array_key_exists('Vendor', $cpu) && empty($cpu['Vendor']) ? $cpu['Vendor'].' - ' : '').
66
+ $cpu['Model'].
67
+ (array_key_exists('MHz', $cpu) ?
68
+ ($cpu['MHz'] < 1000 ? ' ('.$cpu['MHz'].' MHz)' : ' ('.round($cpu['MHz'] / 1000, 3).' GHz)') : '').
69
+ '<br />';
70
+ }
71
+ $core[] = array('cpus', $cpus);
72
+ }
73
+ if (!empty($settings['show']['model']) && array_key_exists('Model', $info) && !empty($info['Model'])) {
74
+ $core[] = array('model', $info['Model']);
75
+ }
76
+ if (!empty($settings['show']['process_stats']) && $info['processStats']['exists']) {
77
+ $proc_stats = array();
78
+ if (array_key_exists('totals', $info['processStats']) && is_array($info['processStats']['totals'])) {
79
+ foreach ($info['processStats']['totals'] as $k => $v) {
80
+ $proc_stats[] = $k.': '.number_format($v);
81
+ }
82
+ }
83
+ $proc_stats[] = 'total: '.number_format($info['processStats']['proc_total']);
84
+ $core[] = array('processes', implode('; ', $proc_stats));
85
+ if ($info['processStats']['threads'] !== false) {
86
+ $core[] = array('threads', number_format($info['processStats']['threads']));
87
+ }
88
+ }
89
+ if (!empty($settings['show']['load'])) {
90
+ $core[] = array('load', implode(' ', (array) $info['Load']));
91
+ }
92
+
93
+ // Adding each core stuff
94
+ for ($i = 0, $core_num = count($core); $i < $core_num; ++$i) {
95
+ $core_elem->addChild($core[$i][0], $core[$i][1]);
96
+ }
97
+
98
+ // RAM
99
+ if (!empty($settings['show']['ram'])) {
100
+ $mem = $xml->addChild('memory');
101
+ $core_mem = $mem->addChild($info['RAM']['type']);
102
+ $core_mem->addChild('free', $info['RAM']['free']);
103
+ $core_mem->addChild('total', $info['RAM']['total']);
104
+ $core_mem->addChild('used', $info['RAM']['total'] - $info['RAM']['free']);
105
+ if (isset($info['RAM']['swapFree']) || isset($info['RAM']['swapTotal'])) {
106
+ $swap = $mem->addChild('swap');
107
+ $swap_core = $swap->addChild('core');
108
+ $swap_core->addChild('free', $info['RAM']['swapFree']);
109
+ $swap_core->addChild('total', $info['RAM']['swapTotal']);
110
+ $swap_core->addChild('used', $info['RAM']['swapTotal'] - $info['RAM']['swapFree']);
111
+ if (is_array($info['RAM']['swapInfo']) && count($info['RAM']['swapInfo']) > 0) {
112
+ $swap_devices = $swap->addChild('devices');
113
+ foreach ($info['RAM']['swapInfo'] as $swap_dev) {
114
+ $swap_dev_elem = $swap_devices->addChild('device');
115
+ $swap_dev_elem->addAttribute('device', $swap_dev['device']);
116
+ $swap_dev_elem->addAttribute('type', $swap_dev['type']);
117
+ $swap_dev_elem->addAttribute('size', $swap_dev['size']);
118
+ $swap_dev_elem->addAttribute('used', $swap_dev['used']);
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // NET
125
+ if (!empty($settings['show']['network']) && isset($info['Network Devices']) && is_array($info['Network Devices'])) {
126
+ $net = $xml->addChild('net');
127
+ foreach ($info['Network Devices'] as $device => $stats) {
128
+ $nic = $net->addChild('interface');
129
+ $nic->addAttribute('device', $device);
130
+ $nic->addAttribute('type', $stats['type']);
131
+ $nic->addAttribute('sent', $stats['sent']['bytes']);
132
+ $nic->addAttribute('recieved', $stats['recieved']['bytes']);
133
+ }
134
+ }
135
+
136
+ // TEMPS
137
+ if (!empty($settings['show']['temps']) && isset($info['Temps']) && count($info['Temps']) > 0) {
138
+ $temps = $xml->addChild('temps');
139
+ foreach ($info['Temps'] as $stat) {
140
+ $temp = $temps->addChild('temp');
141
+ $temp->addAttribute('path', $stat['path']);
142
+ $temp->addAttribute('name', $stat['name']);
143
+ $temp->addAttribute('temp', $stat['temp'].' '.$stat['unit']);
144
+ }
145
+ }
146
+
147
+ // Batteries
148
+ if (!empty($settings['show']['battery']) && isset($info['Battery']) && count($info['Battery']) > 0) {
149
+ $bats = $xml->addChild('batteries');
150
+ foreach ($info['Battery'] as $bat) {
151
+ $bat = $bats->addChild('battery');
152
+ $bat->addAttribute('device', $bat['device']);
153
+ $bat->addAttribute('state', $bat['state']);
154
+ $bat->addAttribute('percentage', $bat['percentage']);
155
+ }
156
+ }
157
+
158
+ // SERVICES
159
+ if (!empty($settings['show']['services']) && isset($info['services']) && count($info['services']) > 0) {
160
+ $services = $xml->addChild('services');
161
+ foreach ($info['services'] as $service => $state) {
162
+ $state_parts = explode(' ', $state['state'], 2);
163
+ $service_elem = $services->addChild('service');
164
+ $service_elem->addAttribute('name', $service);
165
+ $service_elem->addAttribute('state', $state_parts[0].(array_key_exists(1, $state_parts) ? ' '.$state_parts[1] : ''));
166
+ $service_elem->addAttribute('pid', $state['pid']);
167
+ $service_elem->addAttribute('threads', $state['threads'] ? $state['threads'] : '?');
168
+ $service_elem->addAttribute('mem_usage', $state['memory_usage'] ? $state['memory_usage'] : '?');
169
+ }
170
+ }
171
+
172
+ // DEVICES
173
+ if (!empty($settings['show']['devices']) && isset($info['Devices'])) {
174
+ $show_vendor = array_key_exists('hw_vendor', $info['contains']) ? ($info['contains']['hw_vendor'] === false ? false : true) : true;
175
+ $devices = $xml->addChild('devices');
176
+ for ($i = 0, $num_devs = count($info['Devices']); $i < $num_devs; ++$i) {
177
+ $device = $devices->addChild('device');
178
+ $device->addAttribute('type', $info['Devices'][$i]['type']);
179
+ if ($show_vendor) {
180
+ $device->addAttribute('vendor', $info['Devices'][$i]['vendor']);
181
+ }
182
+ $device->addAttribute('name', $info['Devices'][$i]['device']);
183
+ }
184
+ }
185
+
186
+ // DRIVES
187
+ if (!empty($settings['show']['hd']) && isset($info['HD']) && is_array($info['HD'])) {
188
+ $show_stats = array_key_exists('drives_rw_stats', $info['contains']) ? ($info['contains']['drives_rw_stats'] === false ? false : true) : true;
189
+ $drives = $xml->addChild('drives');
190
+ foreach ($info['HD'] as $drive) {
191
+ $drive_elem = $drives->addChild('drive');
192
+ $drive_elem->addAttribute('device', $drive['device']);
193
+ $drive_elem->addAttribute('vendor', $drive['vendor'] ? $drive['vendor'] : $lang['unknown']);
194
+ $drive_elem->addAttribute('name', $drive['name']);
195
+ if ($show_stats) {
196
+ $drive_elem->addAttribute('reads', $drive['reads'] ? $drive['reads'] : 'unknown');
197
+ $drive_elem->addAttribute('writes', $drive['writes'] ? $drive['writes'] : 'unknown');
198
+ }
199
+ $drive_elem->addAttribute('size', $drive['size'] ? $drive['size'] : 'unknown');
200
+ if (is_array($drive['partitions']) && count($drive['partitions']) > 0) {
201
+ $partitions = $drive_elem->addChild('partitions');
202
+ foreach ($drive['partitions'] as $partition) {
203
+ $partition_elem = $partitions->addChild('partition');
204
+ $partition_elem->addAttribute('name', isset($partition['number']) ? $drive['device'].$partition['number'] : $partition['name']);
205
+ $partition_elem->addAttribute('size', $partition['size']);
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ // Sound cards? lol
212
+ if (!empty($settings['show']['sound']) && isset($info['SoundCards']) && count($info['SoundCards']) > 0) {
213
+ $cards = $xml->addChild('soundcards');
214
+ foreach ($info['SoundCards'] as $card) {
215
+ $card_elem = $cards->addChild('card');
216
+ $card_elem->addAttribute('number', $card['number']);
217
+ $card_elem->addAttribute('vendor', empty($card['vendor']) ? 'unknown' : $card['vendor']);
218
+ $card_elem->addAttribute('card', $card['card']);
219
+ }
220
+ }
221
+
222
+ // File system mounts
223
+ if (!empty($settings['show']['mounts'])) {
224
+ $has_devices = false;
225
+ $has_labels = false;
226
+ $has_types = false;
227
+ foreach ($info['Mounts'] as $mount) {
228
+ if (!empty($mount['device'])) {
229
+ $has_devices = true;
230
+ }
231
+ if (!empty($mount['label'])) {
232
+ $has_labels = true;
233
+ }
234
+ if (!empty($mount['devtype'])) {
235
+ $has_types = true;
236
+ }
237
+ }
238
+ $mounts = $xml->addChild('mounts');
239
+ foreach ($info['Mounts'] as $mount) {
240
+ $mount_elem = $mounts->addChild('mount');
241
+ if (preg_match('/^.+:$/', $mount['device']) == 1) {
242
+ $mount['device'] .= DIRECTORY_SEPARATOR;
243
+ }
244
+ if ($has_types) {
245
+ $mount_elem->addAttribute('type', $mount['devtype']);
246
+ }
247
+ if ($has_devices) {
248
+ $mount_elem->addAttribute('device', $mount['device']);
249
+ }
250
+ $mount_elem->addAttribute('mountpoint', $mount['mount']);
251
+ if ($has_labels) {
252
+ $mount_elem->addAttribute('label', $mount['label']);
253
+ }
254
+ $mount_elem->addAttribute('fstype', $mount['type']);
255
+ if ($settings['show']['mounts_options'] && !empty($mount['options'])) {
256
+ $mount_elem->addAttribute('options', implode(',', $mount['options']));
257
+ }
258
+ $mount_elem->addAttribute('size', $mount['size']);
259
+ $mount_elem->addAttribute('used', $mount['used']);
260
+ $mount_elem->addAttribute('free', $mount['free']);
261
+ }
262
+ }
263
+
264
+ // RAID arrays
265
+ if (!empty($settings['show']['raid']) && isset($info['Raid']) && count($info['Raid']) > 0) {
266
+ $raid_elem = $xml->addChild('raid');
267
+ foreach ($info['Raid'] as $raid) {
268
+ $array = $raid_elem->addChild('array');
269
+ $active = explode('/', $raid['count']);
270
+ $array->addAttribute('device', $raid['device']);
271
+ $array->addAttribute('level', $raid['level']);
272
+ $array->addAttribute('status', $raid['status']);
273
+ $array->addAttribute('size', $raid['size']);
274
+ $array->addAttribute('active', $active[1].'/'.$active[0]);
275
+ $drives = $array->addChild('drives');
276
+ foreach ($raid['drives'] as $drive) {
277
+ $drive_elem = $drives->addChild('drive');
278
+ $drive_elem->addAttribute('drive', $drive['drive']);
279
+ $drive_elem->addAttribute('state', $drive['state']);
280
+ }
281
+ }
282
+ }
283
+
284
+ // Timestamp
285
+ $xml->addChild('timestamp', $info['timestamp']);
286
+
287
+ // Extensions
288
+ if (count($info['extensions']) > 0) {
289
+ $extensions = $xml->addChild('extensions');
290
+ foreach ($info['extensions'] as $ext) {
291
+ $header = false;
292
+ if (is_array($ext) && count($ext) > 0) {
293
+ $this_ext = $extensions->addChild(Common::xmlStringSanitize($ext['root_title']));
294
+ foreach ((array) $ext['rows'] as $i => $row) {
295
+ if ($row['type'] == 'header') {
296
+ $header = $i;
297
+ } elseif ($row['type'] == 'values') {
298
+ $this_row = $this_ext->addChild('row');
299
+ if ($header !== false && array_key_exists($header, $ext['rows'])) {
300
+ foreach ($ext['rows'][$header]['columns'] as $ri => $rc) {
301
+ $this_row->addChild(
302
+ Common::xmlStringSanitize($rc),
303
+ $ext['rows'][$i]['columns'][$ri]
304
+ );
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ // Out it
314
+ if (!headers_sent()) {
315
+ header('Content-type: text/xml');
316
+ }
317
+ echo $xml->asXML();
318
+
319
+ // Comment which has stats and generator
320
+ echo '<!-- Generated in '.round(microtime(true) - $this->linfo->getTimeStart(), 2).
321
+ ' seconds by '.$this->linfo->getAppName().' ('.$this->linfo->getVersion().')-->';
322
+ } catch (Exception $e) {
323
+ throw new FatalException('Creation of XML error: '.$e->getMessage());
324
+ }
325
+ }
326
+ }
lib/Linfo/Parsers/CallExt.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo\Parsers;
20
+
21
+ use Linfo\Common;
22
+ use Linfo\Linfo;
23
+ use Exception;
24
+
25
+ /**
26
+ * Class used to call external programs.
27
+ */
28
+ class CallExt
29
+ {
30
+ protected static $settings = array();
31
+
32
+ public static function config(Linfo $linfo)
33
+ {
34
+ self::$settings = $linfo->getSettings();
35
+ }
36
+
37
+ /**
38
+ * Maintain a count of how many external programs we call.
39
+ *
40
+ * @var int
41
+ */
42
+ public static $callCount = 0;
43
+
44
+ /**
45
+ * Store results of commands here to avoid calling them more than once.
46
+ *
47
+ * @var array
48
+ */
49
+ protected $cliCache = array();
50
+
51
+ /**
52
+ * Store paths to look for executables here.
53
+ *
54
+ * @var array
55
+ */
56
+ protected $searchPaths = array();
57
+
58
+ /**
59
+ * Say where we'll search for execs.
60
+ *
61
+ * @param array $paths list of paths
62
+ */
63
+ public function setSearchPaths($paths)
64
+ {
65
+
66
+ // Merge in possible custom paths
67
+ if (isset(self::$settings['additional_paths'])
68
+ && is_array(self::$settings['additional_paths'])
69
+ && count(self::$settings['additional_paths']) > 0) {
70
+ $paths = array_merge(self::$settings['additional_paths'], $paths);
71
+ }
72
+
73
+ // Make sure they all have a trailing slash
74
+ foreach ($paths as $k => $v) {
75
+ $paths[$k] .= substr($v, -1) == '/' ? '' : '/';
76
+ }
77
+
78
+ // Save them
79
+ $this->searchPaths = $paths;
80
+ }
81
+
82
+ /**
83
+ * Run a command and cache its output for later.
84
+ *
85
+ * @throws Exception
86
+ *
87
+ * @param string $name name of executable to call
88
+ * @param string $switches command arguments
89
+ */
90
+ public function exec($name, $switches = '')
91
+ {
92
+
93
+ // Sometimes it is necessary to call a program with sudo
94
+ $attempt_sudo = array_key_exists('sudo_apps', self::$settings) && in_array($name, self::$settings['sudo_apps']);
95
+
96
+ // Have we gotten it before?
97
+ if (array_key_exists($name.$switches, $this->cliCache)) {
98
+ return $this->cliCache[$name.$switches];
99
+ }
100
+
101
+ // Try finding the exec
102
+ foreach ($this->searchPaths as $path) {
103
+
104
+ // Found it; run it
105
+ if (is_file($path.$name) && is_executable($path.$name)) {
106
+
107
+ // Complete command, path switches and all
108
+ $command = "$path$name $switches";
109
+
110
+ // Sudoing?
111
+ $command = $attempt_sudo ? Common::locateActualPath(Common::arrayAppendString($this->searchPaths, 'sudo', '%2s%1s')).' '.$command : $command;
112
+
113
+ // Result of command
114
+ $result = `$command`;
115
+
116
+ // Increment call count
117
+ ++self::$callCount;
118
+
119
+ // Cache that
120
+ $this->cliCache[$name.$switches] = $result;
121
+
122
+ // Give result
123
+ return $result;
124
+ }
125
+ }
126
+
127
+ // Never got it
128
+ throw new Exception('Exec `'.$name.'\' not found');
129
+ }
130
+ }
lib/Linfo/Parsers/Hddtemp.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Parsers;
22
+
23
+ use Linfo\Common;
24
+ use Exception;
25
+
26
+ /*
27
+ * Deal with hddtemp
28
+ */
29
+ class Hddtemp
30
+ {
31
+ // Store these
32
+ protected $mode, $host, $port, $settings;
33
+
34
+ // Default socket connect timeout
35
+ const timeout = 3;
36
+
37
+ // Start us off
38
+ public function __construct($settings)
39
+ {
40
+ $this->settings = $settings;
41
+ }
42
+
43
+ // Localize mode
44
+ public function setMode($mode)
45
+ {
46
+ $this->mode = $mode;
47
+ }
48
+
49
+ /*
50
+ * For connecting to HDDTemp daemon
51
+ */
52
+
53
+ // Localize host and port
54
+ public function setAddress($host, $port = 7634)
55
+ {
56
+ $this->host = $host;
57
+ $this->port = $port;
58
+ }
59
+
60
+ // Connect to host/port and get info
61
+ private function getSock()
62
+ {
63
+ // Try connecting
64
+ if (!($sock = @fsockopen($this->host, $this->port, $errno, $errstr, self::timeout))) {
65
+ throw new Exception('Error connecting');
66
+ }
67
+
68
+ // Try getting stuff
69
+ $buffer = '';
70
+ while ($mid = @fgets($sock)) {
71
+ $buffer .= $mid;
72
+ }
73
+
74
+ // Quit
75
+ @fclose($sock);
76
+
77
+ // Output:
78
+ return $buffer;
79
+ }
80
+
81
+ // Parse and return info from daemon socket
82
+ private function parseSockData($data)
83
+ {
84
+
85
+ // Kill surounding ||'s and split it by pipes
86
+ $drives = explode('||', trim($data, '|'));
87
+
88
+ // Return our stuff here
89
+ $return = array();
90
+
91
+ // Go through each
92
+ foreach ($drives as $drive) {
93
+
94
+ // Extract stuff from it
95
+ list($path, $name, $temp, $unit) = explode('|', trim($drive));
96
+
97
+ // Ignore /dev/sg?
98
+ if (!empty($this->settings['hide']['sg']) && substr($path, 0, 7) == '/dev/sg') {
99
+ continue;
100
+ }
101
+
102
+ // Ignore no longer existant devices?
103
+ if (!file_exists($path) && is_readable('/dev')) {
104
+ continue;
105
+ }
106
+
107
+ // Save it
108
+ $return[] = array(
109
+ 'path' => $path,
110
+ 'name' => $name,
111
+ 'temp' => $temp,
112
+ 'unit' => strtoupper($unit),
113
+ );
114
+ }
115
+
116
+ // Give off results
117
+ return $return;
118
+ }
119
+
120
+ /*
121
+ * For parsing the syslog looking for hddtemp entries
122
+ * POTENTIALLY BUGGY -- only tested on debian/ubuntu flavored syslogs
123
+ * Also slow as balls as it parses the entire syslog instead of
124
+ * using something like tail
125
+ */
126
+ private function parseSysLogData()
127
+ {
128
+ $file = '/var/log/syslog';
129
+ if (!is_file($file) || !is_readable($file)) {
130
+ return array();
131
+ }
132
+ $devices = array();
133
+ foreach (Common::getLines($file) as $line) {
134
+ if (preg_match('/\w+\s*\d+ \d{2}:\d{2}:\d{2} \w+ hddtemp\[\d+\]: (.+): (.+): (\d+) ([CF])/i', trim($line), $match) == 1) {
135
+ // Replace current record of dev with updated temp
136
+ $devices[$match[1]] = array($match[2], $match[3], $match[4]);
137
+ }
138
+ }
139
+ $return = array();
140
+ foreach ($devices as $dev => $stat) {
141
+ $return[] = array(
142
+ 'path' => $dev,
143
+ 'name' => $stat[0],
144
+ 'temp' => $stat[1],
145
+ 'unit' => strtoupper($stat[2]),
146
+ );
147
+ }
148
+
149
+ return $return;
150
+ }
151
+
152
+ /*
153
+ * Wrapper function around the private ones here which do the
154
+ * actual work, and returns temps
155
+ */
156
+
157
+ // Use supplied mode, and optionally host/port, to get temps and return them
158
+ public function work()
159
+ {
160
+
161
+ // Deal with differences in mode
162
+ switch ($this->mode) {
163
+
164
+ // Connect to daemon mode
165
+ case 'daemon':
166
+ return $this->parseSockData($this->getSock());
167
+ break;
168
+
169
+ // Syslog every n seconds
170
+ case 'syslog':
171
+ return $this->parseSysLogData();
172
+ break;
173
+
174
+ // Some other mode
175
+ default:
176
+ throw new Exception('Not supported mode');
177
+ break;
178
+ }
179
+ }
180
+ }
lib/Linfo/Parsers/Hwpci.php ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ namespace Linfo\Parsers;
20
+
21
+ use Exception;
22
+ use Linfo\Meta\Errors;
23
+ use Linfo\Common;
24
+
25
+ /**
26
+ * Deal with pci.ids and usb.ids workings.
27
+ *
28
+ * @author Joe Gillotti
29
+ */
30
+ class Hwpci
31
+ {
32
+ private $_use_json = false,
33
+ $_usb_file = '',
34
+ $_pci_file = '',
35
+ $_cache_file = '',
36
+ $_existing_cache_vals = array(),
37
+ $_usb_entries = array(),
38
+ $_pci_entries = array(),
39
+ $_usb_devices = array(),
40
+ $_pci_devices = array(),
41
+ $_result = array(),
42
+ $exec;
43
+
44
+ /**
45
+ * Constructor.
46
+ * @param $usb_file
47
+ * @param $pci_file
48
+ */
49
+ public function __construct($usb_file, $pci_file)
50
+ {
51
+
52
+ // Localize paths to the ids files
53
+ $this->_pci_file = $pci_file;
54
+ $this->_usb_file = $usb_file;
55
+
56
+ // Prefer json, but check for it
57
+ $this->_use_json = function_exists('json_encode') && function_exists('json_decode');
58
+
59
+ // Allow the same web root to be used for multiple insances of linfo, across multiple machines using
60
+ // nfs or whatever, and to have a different cache file for each
61
+ $sys_id = is_readable('/proc/sys/kernel/hostname') ?
62
+ '_'.substr(md5(Common::getContents('/proc/sys/kernel/hostname')), 0, 10) : '_x';
63
+
64
+ // Path to the cache file
65
+ $this->_cache_file = dirname(dirname(dirname(__DIR__))).'/cache/ids_cache'.$sys_id.($this->_use_json ? '.json' : '');
66
+
67
+ // Load contents of cache
68
+ $this->_populate_cache();
69
+
70
+ // Might need these
71
+ $this->exec = new CallExt();
72
+ $this->exec->setSearchPaths(array('/sbin', '/bin', '/usr/bin', '/usr/local/bin', '/usr/sbin'));
73
+ }
74
+
75
+ /**
76
+ * Run the cache file.
77
+ */
78
+ private function _populate_cache()
79
+ {
80
+ if ($this->_use_json) {
81
+ if (is_readable($this->_cache_file) &&
82
+ ($loaded = @json_decode(Common::getContents($this->_cache_file, ''), true)) && is_array($loaded)) {
83
+ $this->_existing_cache_vals = $loaded;
84
+ }
85
+ } else {
86
+ if (is_readable($this->_cache_file) &&
87
+ ($loaded = @unserialize(Common::getContents($this->_cache_file, false))) && is_array($loaded)) {
88
+ $this->_existing_cache_vals = $loaded;
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Get the USB ids from /sys.
95
+ */
96
+ private function _fetchUsbIdsLinux()
97
+ {
98
+ foreach ((array) @glob('/sys/bus/usb/devices/*', GLOB_NOSORT) as $path) {
99
+
100
+ // First try uevent
101
+ if (is_readable($path.'/uevent') &&
102
+ preg_match('/^product=([^\/]+)\/([^\/]+)\/[^$]+$/m', strtolower(Common::getContents($path.'/uevent')), $match)) {
103
+ $this->_usb_entries[str_pad($match[1], 4, '0', STR_PAD_LEFT)][str_pad($match[2], 4, '0', STR_PAD_LEFT)] = 1;
104
+ }
105
+
106
+ // And next modalias
107
+ elseif (is_readable($path.'/modalias') &&
108
+ preg_match('/^usb:v([0-9A-Z]{4})p([0-9A-Z]{4})/', Common::getContents($path.'/modalias'), $match)) {
109
+ $this->_usb_entries[strtolower($match[1])][strtolower($match[2])] = 1;
110
+ }
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Get the PCI ids from /sys.
116
+ */
117
+ private function _fetchPciIdsLinux()
118
+ {
119
+ foreach ((array) @glob('/sys/bus/pci/devices/*', GLOB_NOSORT) as $path) {
120
+
121
+ // See if we can use simple vendor/device files and avoid taking time with regex
122
+ if (($f_device = Common::getContents($path.'/device', '')) && ($f_vend = Common::getContents($path.'/vendor', '')) &&
123
+ $f_device != '' && $f_vend != '') {
124
+ list(, $v_id) = explode('x', $f_vend, 2);
125
+ list(, $d_id) = explode('x', $f_device, 2);
126
+ $this->_pci_entries[$v_id][$d_id] = 1;
127
+ }
128
+
129
+ // Try uevent nextly
130
+ elseif (is_readable($path.'/uevent') &&
131
+ preg_match('/pci\_(?:subsys_)?id=(\w+):(\w+)/', strtolower(Common::getContents($path.'/uevent')), $match)) {
132
+ $this->_pci_entries[$match[1]][$match[2]] = 1;
133
+ }
134
+
135
+ // Now for modalias
136
+ elseif (is_readable($path.'/modalias') &&
137
+ preg_match('/^pci:v0{4}([0-9A-Z]{4})d0{4}([0-9A-Z]{4})/', Common::getContents($path.'/modalias'), $match)) {
138
+ $this->_pci_entries[strtolower($match[1])][strtolower($match[2])] = 1;
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Use the pci.ids file to translate the ids to names.
145
+ */
146
+ private function _fetchPciNames()
147
+ {
148
+ for ($v = false, $file = @fopen($this->_pci_file, 'r'); $file != false && $contents = fgets($file);) {
149
+ if (preg_match('/^(\S{4})\s+([^$]+)$/', $contents, $vend_match) == 1) {
150
+ $v = $vend_match;
151
+ } elseif (preg_match('/^\s+(\S{4})\s+([^$]+)$/', $contents, $dev_match) == 1) {
152
+ if ($v && isset($this->_pci_entries[strtolower($v[1])][strtolower($dev_match[1])])) {
153
+ $this->_pci_devices[$v[1]][$dev_match[1]] = array('vendor' => rtrim($v[2]), 'device' => rtrim($dev_match[2]));
154
+ }
155
+ }
156
+ }
157
+ $file && @fclose($file);
158
+ }
159
+
160
+ /**
161
+ * Use the usb.ids file to translate the ids to names.
162
+ */
163
+ private function _fetchUsbNames()
164
+ {
165
+ for ($v = false, $file = @fopen($this->_usb_file, 'r'); $file != false && $contents = fgets($file);) {
166
+ if (preg_match('/^(\S{4})\s+([^$]+)$/', $contents, $vend_match) == 1) {
167
+ $v = $vend_match;
168
+ } elseif (preg_match('/^\s+(\S{4})\s+([^$]+)$/', $contents, $dev_match) == 1) {
169
+ if ($v && isset($this->_usb_entries[strtolower($v[1])][strtolower($dev_match[1])])) {
170
+ $this->_usb_devices[strtolower($v[1])][$dev_match[1]] = array('vendor' => rtrim($v[2]), 'device' => rtrim($dev_match[2]));
171
+ }
172
+ }
173
+ }
174
+ $file && @fclose($file);
175
+ }
176
+
177
+ /**
178
+ * Decide if the cache file is sufficient enough to not parse the ids files.
179
+ */
180
+ private function _is_cache_worthy()
181
+ {
182
+ $pci_good = true;
183
+ foreach (array_keys($this->_pci_entries) as $vendor) {
184
+ foreach (array_keys($this->_pci_entries[$vendor]) as $dever) {
185
+ if (!isset($this->_existing_cache_vals['hw']['pci'][$vendor][$dever])) {
186
+ $pci_good = false;
187
+ break 2;
188
+ }
189
+ }
190
+ }
191
+ $usb_good = true;
192
+ foreach (array_keys($this->_usb_entries) as $vendor) {
193
+ foreach (array_keys($this->_usb_entries[$vendor]) as $dever) {
194
+ if (!isset($this->_existing_cache_vals['hw']['usb'][$vendor][$dever])) {
195
+ $usb_good = false;
196
+ break 2;
197
+ }
198
+ }
199
+ }
200
+
201
+ return array('pci' => $pci_good, 'usb' => $usb_good);
202
+ }
203
+
204
+ /*
205
+ * Write cache file with latest info
206
+ *
207
+ * @access private
208
+ */
209
+ private function _write_cache()
210
+ {
211
+ if (is_writable(dirname(dirname(dirname(__DIR__))).'/cache')) {
212
+ @file_put_contents($this->_cache_file, $this->_use_json ?
213
+ json_encode(array(
214
+ 'hw' => array(
215
+ 'pci' => $this->_pci_devices,
216
+ 'usb' => $this->_usb_devices,
217
+ ),
218
+ ))
219
+ : serialize(array(
220
+ 'hw' => array(
221
+ 'pci' => $this->_pci_devices,
222
+ 'usb' => $this->_usb_devices,
223
+ ),
224
+ )));
225
+ }
226
+ }
227
+
228
+ /*
229
+ * Parse pciconf to get pci ids
230
+ *
231
+ * @access private
232
+ */
233
+ private function _fetchPciIdsPciConf()
234
+ {
235
+ try {
236
+ $pciconf = $this->exec->exec('pciconf', '-l');
237
+ } catch (Exception $e) {
238
+ Errors::add('Linfo Core', 'Error using `pciconf -l` to get hardware info');
239
+
240
+ return;
241
+ }
242
+
243
+ if (preg_match_all('/^.+chip=0x([a-z0-9]{4})([a-z0-9]{4})/m', $pciconf, $devs, PREG_SET_ORDER) == 0) {
244
+ return;
245
+ }
246
+
247
+ foreach ($devs as $dev) {
248
+ $this->_pci_entries[$dev[2]][$dev[1]] = 1;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Do its goddam job.
254
+ * @param $os
255
+ */
256
+ public function work($os)
257
+ {
258
+ switch ($os) {
259
+ case 'linux':
260
+ $this->_fetchPciIdsLinux();
261
+ $this->_fetchUsbIdsLinux();
262
+ break;
263
+ case 'freebsd':
264
+ case 'dragonfly':
265
+ $this->_fetchPciIdsPciConf();
266
+ break;
267
+ default:
268
+ return;
269
+ break;
270
+ }
271
+ $worthiness = $this->_is_cache_worthy();
272
+ $save_cache = false;
273
+ if (!$worthiness['pci']) {
274
+ $save_cache = true;
275
+ $this->_fetchPciNames();
276
+ } else {
277
+ $this->_pci_devices = isset($this->_existing_cache_vals['hw']['pci']) ? $this->_existing_cache_vals['hw']['pci'] : array();
278
+ }
279
+ if (!$worthiness['usb']) {
280
+ $save_cache = true;
281
+ $this->_fetchUsbNames();
282
+ } else {
283
+ $this->_usb_devices = isset($this->_existing_cache_vals['hw']['usb']) ? $this->_existing_cache_vals['hw']['usb'] : array();
284
+ }
285
+ if ($save_cache) {
286
+ $this->_write_cache();
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Compile and return results.
292
+ */
293
+ public function result()
294
+ {
295
+ foreach (array_keys((array) $this->_pci_devices) as $v) {
296
+ foreach ($this->_pci_devices[$v] as $d) {
297
+ $this->_result[] = array('vendor' => $d['vendor'], 'device' => $d['device'], 'type' => 'PCI');
298
+ }
299
+ }
300
+ foreach (array_keys((array) $this->_usb_devices) as $v) {
301
+ foreach ($this->_usb_devices[$v] as $d) {
302
+ $this->_result[] = array('vendor' => $d['vendor'], 'device' => $d['device'], 'type' => 'USB');
303
+ }
304
+ }
305
+
306
+ return $this->_result;
307
+ }
308
+ }
lib/Linfo/Parsers/Mbmon.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Parsers;
22
+
23
+ use Exception;
24
+
25
+ /*
26
+ * Deal with MbMon
27
+ */
28
+ class Mbmon
29
+ {
30
+ // Store these
31
+ protected $host, $port;
32
+
33
+ // Default socket connect timeout
34
+ const timeout = 3;
35
+
36
+ // Localize host and port
37
+ public function setAddress($host, $port = 411)
38
+ {
39
+ $this->host = $host;
40
+ $this->port = $port;
41
+ }
42
+
43
+ // Connect to host/port and get info
44
+ private function getSock()
45
+ {
46
+ // Try connecting
47
+ if (!($sock = @fsockopen($this->host, $this->port, $errno, $errstr, self::timeout))) {
48
+ throw new Exception('Error connecting');
49
+ }
50
+
51
+ // Try getting stuff
52
+ $buffer = '';
53
+ while ($mid = @fgets($sock)) {
54
+ $buffer .= $mid;
55
+ }
56
+
57
+ // Quit
58
+ @fclose($sock);
59
+
60
+ // Output:
61
+ return $buffer;
62
+ }
63
+
64
+ // Parse and return info from daemon socket
65
+ private function parseSockData($data)
66
+ {
67
+ $return = array();
68
+
69
+ $lines = (array) explode("\n", trim($data));
70
+
71
+ foreach ($lines as $line) {
72
+ if (preg_match('/(\w+)\s*:\s*([-+]?[\d\.]+)/i', $line, $match) == 1) {
73
+ $return[] = array(
74
+ 'path' => 'N/A',
75
+ 'name' => $match[1],
76
+ 'temp' => $match[2],
77
+ 'unit' => '', // TODO
78
+ );
79
+ }
80
+ }
81
+
82
+ return $return;
83
+ }
84
+
85
+ // Do work and return temps
86
+ public function work()
87
+ {
88
+ $sockResult = $this->getSock();
89
+ $temps = $this->parseSockData($sockResult);
90
+
91
+ return $temps;
92
+ }
93
+ }
lib/Linfo/Parsers/Sensord.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Linfo (c) 2010 Joseph Gillotti.
5
+ *
6
+ * Linfo is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Linfo is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Linfo. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ */
20
+
21
+ namespace Linfo\Parsers;
22
+
23
+ use Linfo\Common;
24
+
25
+ /*
26
+ * Main class
27
+ */
28
+
29
+ class Sensord
30
+ {
31
+ public function work()
32
+ {
33
+ $temps = $this->parseSysLog();
34
+
35
+ return $temps;
36
+ }
37
+
38
+ private function parseSysLog()
39
+ {
40
+
41
+ /*
42
+ * For parsing the syslog looking for sensord entries
43
+ * POTENTIALLY BUGGY -- only tested on debian/ubuntu flavored syslogs
44
+ * Also slow as balls as it parses the entire syslog instead of
45
+ * using something like tail
46
+ */
47
+ $file = '/var/log/syslog';
48
+ if (!is_file($file) || !is_readable($file)) {
49
+ return array();
50
+ }
51
+ $devices = array();
52
+ foreach (Common::getLines($file) as $line) {
53
+ if (preg_match('/\w+\s*\d+ \d{2}:\d{2}:\d{2} \w+ sensord:\s*(.+):\s*(.+)/i', trim($line), $match) == 1) {
54
+ // Replace current record of dev with updated temp
55
+ $devices[$match[1]] = $match[2];
56
+ }
57
+ }
58
+ $return = array();
59
+ foreach ($devices as $dev => $stat) {
60
+ $return[] = array(
61
+ 'path' => 'N/A', // These likely won't have paths
62
+ 'name' => $dev,
63
+ 'temp' => $stat,
64
+ 'unit' => '', // Usually included in above
65
+ );
66
+ }
67
+
68
+ return $return;
69
+ }
70
+ }
package.xml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?xml version="1.0"?>
2
+ <package><name>Neklo_Monitor</name><version>1.1.2</version><stability>stable</stability><license>License</license><channel>community</channel><extends></extends><summary>First Release</summary><description>Connector for Magento Monitor Application</description><notes></notes><authors><author><name>NEKLO</name><user>NEKLO</user><email>info@neklo.com</email></author></authors><date>2016-05-03</date><time>6:49:59</time><compatible></compatible><dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies><contents><target name="mage"><dir name="app"><dir name="etc"><dir name="modules"><file name="Neklo_Core.xml" hash="335032ff690c5272626dca9106642680"/><file name="Neklo_Monitor.xml" hash="e93ded9f7368552a843959e953a2bf10"/></dir></dir><dir name="code"><dir name="community"><dir name="Neklo"><dir name="Monitor"><file name="Autoload.php" hash="612c4e0d0a4db5be84c29650655085f6"/><dir name="etc"><file name="adminhtml.xml" hash="897debfe836ca9d58971b44b2232187e"/><file name="config.xml" hash="87d9106b97e2b1b04945ec934165c6e6"/><file name="system.xml" hash="7df39a98e617da03ebcf0ce1539c15c9"/></dir><dir name="controllers"><file name="AuthController.php" hash="c164010a1b5237587ad4edef5bd14b1d"/><file name="CustomerController.php" hash="b1b11923eff5372e95997e8f0a3716ae"/><file name="DashboardController.php" hash="004e0bd94c30ab5e262e193738eb07e4"/><file name="InfoController.php" hash="6bfe3889fa3454d39e9fa39003cc8d20"/><file name="OrderController.php" hash="6500c3870cd0f4e472de7b474530c60b"/><file name="ProductController.php" hash="811e66a117cd0c27d785eac38bde614e"/><dir name="State"><file name="CacheController.php" hash="efb77eeae42be795d7618c620270d0d7"/><file name="IndexerController.php" hash="fa23622aabd0f1d40d4d03de692ae0a2"/></dir><dir name="Var"><file name="LogController.php" hash="b0c9fb0510dd81d3de890d1050d8ac18"/><file name="ReportController.php" hash="5e871965109e78c57252fd221c8e3a9b"/></dir><dir name="Report"><file name="SalesController.php" hash="25e00ff2fd368450d26bc0d4bc11676c"/></dir></dir><dir name="Controller"><file name="Abstract.php" hash="c451f6e756bac9cbc5460c3b65812bb6"/></dir><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Frontend"><file name="Label.php" hash="bd831c28818a1b998dda55df056b28b4"/><file name="Status.php" hash="c94359824abaf9a4e23f00364071979b"/></dir></dir></dir></dir></dir><dir name="sql"><dir name="neklo_monitor_setup"><file name="mysql4-install-1.0.0.php" hash="3a5ee59cda3ec27eca737f515eec4a71"/><file name="mysql4-upgrade-1.0.0-1.1.0.php" hash="bba6559d5040c937d775a76df6611b77"/><file name="mysql4-upgrade-1.1.0-1.1.1.php" hash="f3bbc768ae43e1b45009de02c519dcd7"/><file name="mysql4-upgrade-1.1.1-1.1.2.php" hash="0a03caae9eb41f5f8bcf1a2a8476a13a"/></dir></dir><dir name="Model"><file name="Linfo.php" hash="585d42953c5585d9851dad82c0881410"/><file name="Log.php" hash="7ae437f9f39e62317faa9a2c4b57a3d2"/><file name="Minfo.php" hash="eaab8efeb41a9d2dd073093b869fe440"/><dir name="System"><dir name="Config"><dir name="Source"><dir name="Server"><file name="Type.php" hash="4c86daaa4320ac41b7bdba25f1285056"/></dir></dir><dir name="Backend"><file name="Empty.php" hash="556343bcf15ea8b294a15e77f6662cf8"/><file name="Token.php" hash="7669597367cd26bd848d0d8251feb4b7"/></dir></dir></dir><dir name="Cron"><file name="Abstract.php" hash="7183a30f5a456ee29a3e58f814999e87"/><file name="Server.php" hash="5f3e4ac9492b6e7d658fabeaa0e35e44"/><file name="Store.php" hash="97a0691ceda29f4b52481666b5ab9406"/></dir><dir name="Resource"><dir name="Minfo"><file name="Log.php" hash="54f05d3ced0a1aee770177265695eac9"/><file name="Report.php" hash="acb9c02688a9403a67904fda72c57533"/><dir name="Log"><file name="Collection.php" hash="fc6cbcd4c15227046cb7e85dfdad614d"/></dir><dir name="Report"><file name="Collection.php" hash="a9c74ab52be17dec7c4fafb883bda732"/></dir></dir></dir><dir name="Minfo"><file name="Log.php" hash="d787eeaab4cae6f3d9fe7e696c427014"/><file name="Parser.php" hash="2d7f4603d25d63b0416d338924add4e9"/><file name="Report.php" hash="d97960716a8c152d1614b8d0c752e064"/></dir><dir name="Linfo"><dir name="Os"><file name="Linux.php" hash="e61e9aceb9723b5c8e72200274b7f6ab"/></dir></dir><dir name="Gateway"><file name="Connector.php" hash="3f9667344ccd34dbe4461e71891335fa"/></dir></dir><dir name="Helper"><file name="Config.php" hash="235ca11d4945dfa3341a12ef6968a0cd"/><file name="Country.php" hash="15176ed5b48639f8c50242b126e9dea4"/><file name="Data.php" hash="ebba3993c28262dc258166187cc41aba"/><file name="Date.php" hash="446cc8698132f4f31886332bdc7ac9d6"/><file name="Request.php" hash="dab02c51e8da47f8cb2b57999576ed5c"/><dir name="Request"><file name="Validator.php" hash="16375fad2da02f6732fefd465a388774"/></dir></dir></dir><dir name="Core"><dir name="etc"><file name="adminhtml.xml" hash="68b00ad4118462d74b0c0a7126063462"/><file name="config.xml" hash="3fc06c04d63578d873a4d4145785e4c0"/><file name="system.xml" hash="f9ee62f79b22584cc6180ec5e8049539"/></dir><dir name="controllers"><dir name="Adminhtml"><dir name="Neklo"><dir name="Core"><file name="ContactController.php" hash="fe735a9c3c0ad9c4ecb88edd009059a9"/><file name="NewsletterController.php" hash="b9342c80bf94c29a463251b1c7b02705"/></dir></dir></dir></dir><dir name="Block"><dir name="System"><file name="Contact.php" hash="a0e89ca48de64bb8526c35598f99802b"/><file name="Extension.php" hash="8cd6609bd4f00bf8a8f5913d20e98e65"/><dir name="Contact"><file name="Header.php" hash="a6c4f8dbf002a0d2f6be0ea37445a562"/><file name="Send.php" hash="333d3059033faa826ef3781946676b16"/><dir name="Send"><file name="Button.php" hash="c94e2ae59246a968d7eb2e89e63485c8"/></dir></dir><dir name="Newsletter"><file name="Subscribe.php" hash="1015b8e49758e40c829667e94bb06220"/><dir name="Subscribe"><file name="Button.php" hash="98162d3c3280bdc3da75c90c45fc9500"/></dir></dir><dir name="Extension"><file name="List.php" hash="0006ff459c6b19de9349fa31c8be6b2b"/></dir></dir></dir><dir name="Model"><file name="Feed.php" hash="b5e0d2343b2c9db1f4d86ca0e8b3a922"/><file name="Observer.php" hash="dfc8c917e3f3882d1d99c734831c6e0b"/><dir name="Source"><file name="Reason.php" hash="02eb3c4cfe3433e5a8194862aef636c4"/><dir name="Subscription"><file name="Type.php" hash="616c3f7e7e4ebe3e42e16d54d159d378"/></dir></dir><dir name="System"><dir name="Config"><dir name="Backend"><file name="Empty.php" hash="2af2d53c7c56b9ea4a148a2862da224b"/></dir></dir></dir><dir name="Feed"><file name="Extension.php" hash="1ff201b02db7827df350b11c19902fa2"/></dir></dir><dir name="Helper"><file name="Config.php" hash="42eff18ca97b961e1186a790abcb8690"/><file name="Data.php" hash="5492cb13c72b737d85e9009f0f623336"/><file name="Extension.php" hash="c0c987bd848e5270ec31544460438eab"/></dir></dir></dir></dir></dir><dir name="design"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="neklo"><file name="core.xml" hash="8851b2c111c8e0745fb78af2bb57e6a6"/></dir></dir><dir name="template"><dir name="neklo"><dir name="core"><dir name="system"><dir name="contact"><file name="button.phtml" hash="f7b5f21ddb974aa32c888c29f3057d18"/><file name="header.phtml" hash="3c0a401955a9b2bac799aa65b5bf7a81"/></dir><dir name="extension"><file name="list.phtml" hash="29378800cc0a7488badb2698a02220d7"/></dir><dir name="subscribe"><file name="button.phtml" hash="865b84befba4d00e6a1e4de2b989776e"/></dir></dir></dir></dir></dir></dir></dir></dir></dir><dir name="locale"><dir name="en_US"><file name="Neklo_Core.csv" hash="c6abfbb8be878de9c02339e2ecfc4e16"/></dir></dir></dir><dir name="skin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="neklo"><dir name="core"><dir name="images"><file name="ok.gif" hash="a38bc2ee6e116e39c6e2e3013ee50f5e"/><file name="update.gif" hash="8342e11f7739fcfa25134707f0536ed6"/></dir><dir name="css"><file name="style.css" hash="77e35507d6f2e748afc08f11f322120d"/></dir></dir><dir name="monitor"><dir name="css"><file name="styles.css" hash="ab8dc36c3c18794064b0ea8b8c8334fb"/></dir></dir></dir></dir></dir></dir></dir><dir name="lib"><dir name="Linfo"><file name="Common.php" hash="3d55bb51d9de7438c044a012ae50175b"/><file name="Linfo.php" hash="d26c95e093e3c4d5eb456c6c5dfa1579"/><dir name="Output"><file name="Html.php" hash="5ec70c500dda56a561d188dd6e908e49"/><file name="Json.php" hash="4616bcc1ddd3eaa312ef2235e7da1586"/><file name="Ncurses.php" hash="60dbe6e0b2eb00dedccc972271a712fa"/><file name="Output.php" hash="34ef640283d567d93300a2b9c107d276"/><file name="Serialized.php" hash="aa749737127da4bb25c941557d1cfb81"/><file name="Xml.php" hash="7fb392ee95df014c30dd52130e9db45c"/></dir><dir name="Exceptions"><file name="FatalException.php" hash="ac7ae108aa403bc300ba9086410cf0a9"/></dir><dir name="Extension"><file name="Apcaccess.php" hash="8e274bd0036006e064c0cd85fbd732a9"/><file name="Cups.php" hash="25bb7dfe794d5ff211c9862615e2192b"/><file name="Dhcpd3_leases.php" hash="40ffcf2c2ec8618ff8155d197c15bbf1"/><file name="Dnsmasq_dhcpd.php" hash="bc751a49a09f06d8e4492f7ddf432cea"/><file name="Extension.php" hash="d09316ce5276805b702fdc1115b4adc3"/><file name="Ipmi.php" hash="5536abf11a4f06e0b91852f5e17b77bc"/><file name="Libvirt.php" hash="889dcd93239aecb42d6f80b2618e87b1"/><file name="Smb.php" hash="d6d4e995bf54fbdfeec0a51f70faec37"/><file name="Soldat.php" hash="bce89097e6540818f153b2df5b71b0b5"/><file name="Transmission.php" hash="e438b16dc2040d8676c7ba2c1448b6ef"/><file name="Truecrypt.php" hash="f4838a69d9eda202a5d64e8bb8ced36c"/><file name="Utorrent.php" hash="14119c01579d58a01f6489a0865855f7"/></dir><dir name="Lang"><file name="de.php" hash="e313cc8346be39d62607278bd986af23"/><file name="en.php" hash="8ee1b778ed29c64e02e3e1f118016f82"/><file name="fi.php" hash="10d10cd954719a7cee69d5bfbf67889c"/><file name="fr.php" hash="6edcf8ee5251dee98ee39f84fdd52647"/><file name="it.php" hash="d65c5225fa3ef16fd2966137c18e83f4"/><file name="pl.php" hash="2d9b92e225acf0461540d1224a0141f5"/><file name="pt.php" hash="9dd24ee347278edc24f589f6e3f12c58"/><file name="zh.php" hash="2f9ae1cfcf4e5a80127200bf9c9d0b8b"/></dir><dir name="OS"><file name="BSDcommon.php" hash="3b6c83b58e56e0a0eac250f70451be5a"/><file name="Darwin.php" hash="fc45b3648d75c96596d8c7a8559d2d74"/><file name="DragonFly.php" hash="c5ffffe8e6899a0f8352c1fd0f763135"/><file name="FreeBSD.php" hash="f07d480ebda610153b5f6d42d0143f37"/><file name="Linux.php" hash="5e36caf50252aa24fab289ae4239a591"/><file name="Minix.php" hash="22c19a5e45c4715cfcb0db1f42fd5793"/><file name="NetBSD.php" hash="f576ac8083757848bbd179420c182aeb"/><file name="OS.php" hash="49f2a6e9b1e3e56815896f0364049bef"/><file name="OpenBSD.php" hash="cd6b1bd659a30465903ebc913382b223"/><file name="SunOS.php" hash="2cfa3e63e51ca1d8928f2f521be26b3d"/><file name="Unixcommon.php" hash="9981cca8d8805302983ebe20dc2ef04d"/><file name="Windows.php" hash="f73cd25acaf3774da31b922ab3287038"/></dir><dir name="Meta"><file name="Errors.php" hash="e614a650fd523918b010b4297c4c2acd"/><file name="Timer.php" hash="ea244a5a388fa8cc470799cbdd96b866"/></dir><dir name="Parsers"><file name="CallExt.php" hash="6a671a39e8debf7e4c950491f45d8ee4"/><file name="Hddtemp.php" hash="3d0007ab7349702042a88f1e9ca23649"/><file name="Hwpci.php" hash="e26d72856736d83470e82916ba78a3ca"/><file name="Mbmon.php" hash="aa3782625c7542059838e475ccb13326"/><file name="Sensord.php" hash="2be994a2c77ba0a35398a6fc96a4c77a"/></dir></dir></dir></target></contents></package>
skin/adminhtml/default/default/neklo/core/css/style.css ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .ovh{
2
+ overflow: hidden;
3
+ display: inline-block;
4
+ font-size: 16px;
5
+ color: #494848;
6
+ text-align: center;
7
+ }
8
+ .neklo-img{
9
+ width: 150px;
10
+ height: 150px;
11
+ margin: 0 auto;
12
+ }
13
+ .neklo-img img{
14
+ width: 100%;
15
+ height: 100%;
16
+ }
17
+ .neklo-row{
18
+ margin-bottom: 20px;
19
+ }
20
+ .neklo-link, .neklo-link:hover{
21
+ color: #494848;
22
+ text-decoration: none;
23
+ }
24
+ .neklo-link:hover .neklo-ext-name{
25
+ text-decoration: underline;
26
+ }
27
+ .neklo-ext-name{
28
+ font-weight: bold;
29
+ min-height: 36px;
30
+ margin-bottom: 10px;
31
+ }
32
+ .neklo-item{
33
+ border: 1px solid #ccc;
34
+ display: inline-block;
35
+ padding: 20px;
36
+ margin: 0 20px 20px 0 !important;
37
+ vertical-align: top;
38
+ width: 270px;
39
+ text-align: center;
40
+ }
41
+ .neklo_core_message {
42
+ text-align: center;
43
+ padding: 5px 0;
44
+ font-weight: bold;
45
+ width: 280px;
46
+ }
47
+ .neklo_core_message .error {
48
+ color: #D40707;
49
+ }
50
+ .neklo_core_message .success {
51
+ color: #3d6611;
52
+ }
skin/adminhtml/default/default/neklo/core/images/ok.gif ADDED
Binary file
skin/adminhtml/default/default/neklo/core/images/update.gif ADDED
Binary file
skin/adminhtml/default/default/neklo/monitor/css/styles.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ .gateway_status.success {
2
+ color:#008000;
3
+ }
4
+ .gateway_status.error {
5
+ color:#FF0000;
6
+ }