api.processmon.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. /**
  3. * StarDust-managed process monitor implementation
  4. */
  5. class ProcessMon {
  6. /**
  7. * Stardust process menager instance placeholder
  8. *
  9. * @var object
  10. */
  11. protected $stardust = '';
  12. /**
  13. * System messages helper instance placeholder
  14. *
  15. * @var object
  16. */
  17. protected $messages = '';
  18. /**
  19. * Contains all process stats as processName=>data
  20. *
  21. * @var array
  22. */
  23. protected $allProcess = array();
  24. /**
  25. * Contains default system ps path
  26. *
  27. * @var string
  28. */
  29. protected $psPath = '/bin/ps';
  30. /**
  31. * some predefined stuff like routes, etc here
  32. */
  33. const URL_ME = '?module=processmon';
  34. const ROUTE_ALL = 'showall';
  35. const ROUTE_ACTIVE = 'onlyactive';
  36. const ROUTE_FINISHED = 'finished';
  37. const ROUTE_ZENMODE = 'processzen';
  38. const ROUTE_STOP = 'shutdownprocess';
  39. const ROUTE_BRUTAL = 'brutality';
  40. const ZEN_TIMEOUT = 1000;
  41. public function __construct() {
  42. $this->initMessages();
  43. $this->initStarDust();
  44. }
  45. /**
  46. * Inits system messages helper
  47. *
  48. * @return void
  49. */
  50. protected function initMessages() {
  51. $this->messages = new UbillingMessageHelper();
  52. }
  53. /**
  54. * Inits process manager for further usage
  55. *
  56. * @return void
  57. */
  58. protected function initStarDust() {
  59. $this->stardust = new StarDust();
  60. }
  61. /**
  62. * Loads all process states into protected property
  63. *
  64. * @return void
  65. */
  66. protected function loadAllProcessStates() {
  67. $this->allProcess = $this->stardust->getAllStates();
  68. }
  69. /**
  70. * Renders default module controls
  71. *
  72. * @return string
  73. */
  74. public function renderControls() {
  75. $result = '';
  76. $result .= wf_BackLink('?module=report_sysload');
  77. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_ALL . '=true', wf_img('skins/icon_thread.png') . ' ' . __('All'), false, 'ubButton') . ' ';
  78. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_ACTIVE . '=true', wf_img('skins/icon_active.gif') . ' ' . __('Active'), false, 'ubButton') . ' ';
  79. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_FINISHED . '=true', wf_img('skins/icon_inactive.gif') . ' ' . __('Finished'), false, 'ubButton') . ' ';
  80. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_ZENMODE . '=true', wf_img('skins/zen.png') . ' ' . __('Zen'), false, 'ubButton') . ' ';
  81. return($result);
  82. }
  83. /**
  84. * Returns all running PID-s array as pid=>processString
  85. *
  86. * @return array
  87. */
  88. protected function getRunningPids() {
  89. $result = array();
  90. $rawResult = shell_exec($this->psPath . ' ax');
  91. if (!empty($rawResult)) {
  92. $rawResult = explodeRows($rawResult);
  93. foreach ($rawResult as $io => $eachLine) {
  94. $eachLine = trim($eachLine);
  95. $rawLine = $eachLine;
  96. $eachLine = explode(' ', $eachLine);
  97. if (isset($eachLine[0])) {
  98. $eachPid = $eachLine[0];
  99. if (is_numeric($eachPid)) {
  100. $result[$eachPid] = $rawLine;
  101. }
  102. }
  103. }
  104. }
  105. return($result);
  106. }
  107. /**
  108. * Stops running process by its name
  109. *
  110. * @global object $ubillingConfig
  111. * @param string $processName
  112. * @param bool $brutal
  113. *
  114. * @return void/string
  115. */
  116. public function stopProcess($processName, $brutal = false) {
  117. global $ubillingConfig;
  118. $result = '';
  119. $this->loadAllProcessStates();
  120. if (isset($this->allProcess[$processName])) {
  121. $processData = $this->allProcess[$processName];
  122. $processPid = $processData['pid'];
  123. $runningPids = $this->getRunningPids();
  124. if (isset($runningPids[$processPid])) {
  125. $billCfg = $ubillingConfig->getBilling();
  126. $sudoPath = $billCfg['SUDO'];
  127. $killPath = $billCfg['KILL'];
  128. $stopModifier = ($brutal) ? '-9' : '';
  129. $stopLabel = ($brutal) ? 'BRUTAL' : '';
  130. $command = $sudoPath . ' ' . $killPath . ' ' . $stopModifier . ' ' . $processPid;
  131. $shutdownResult = shell_exec($command);
  132. if (!empty($shutdownResult)) {
  133. $result .= $shutdownResult;
  134. }
  135. log_register('PROCESSMON PROCESS `' . $processName . '` KILLED ' . $stopLabel . ' WITH PID [' . $processPid . ']');
  136. } else {
  137. $result .= __('No matching process PID was found') . ': ' . $processName . ' PID ' . $processPid;
  138. log_register('PROCESSMON PROCESS `' . $processName . '` KILL FAILED PID [' . $processPid . '] NOT FOUND');
  139. }
  140. } else {
  141. $result .= __('No matching process was found') . ': ' . $processName;
  142. log_register('PROCESSMON PROCESS `' . $processName . '` KILL FAILED PROCESS NOT FOUND');
  143. }
  144. return($result);
  145. }
  146. /**
  147. * Renders process shutdown form
  148. *
  149. * @param string $processName
  150. *
  151. * @return string
  152. */
  153. protected function renderShutdownForm($processName) {
  154. $result = '';
  155. $shutdownUrl = self::URL_ME . '&' . self::ROUTE_STOP . '=' . $processName;
  156. $brutalityUrl = $shutdownUrl . '&' . self::ROUTE_BRUTAL . '=true';
  157. $stopAlert = __('Are you serious') . ' ' . __('Stop the process') . ' ' . $processName . '?';
  158. $result .= wf_JSAlert($shutdownUrl, wf_img('skins/stop.png') . ' ' . __('Stop the process') . ' ' . $processName, $stopAlert, '', 'ubButton');
  159. $result .= wf_delimiter();
  160. $result .= wf_JSAlert($brutalityUrl, wf_img('skins/skull.png') . ' ' . __('Stop the process with extreme cruelty'), $stopAlert, '', 'ubButton');
  161. return($result);
  162. }
  163. /**
  164. * Renders and filters process list
  165. *
  166. * @return string
  167. */
  168. public function renderProcessList() {
  169. $result = '';
  170. $this->loadAllProcessStates();
  171. //some filters here
  172. $renderActive = true;
  173. $renderFinished = true;
  174. $zenFlag = false;
  175. $counter = 0;
  176. $runningPids = array();
  177. if (ubRouting::checkGet(self::ROUTE_ACTIVE)) {
  178. $renderFinished = false;
  179. }
  180. if (ubRouting::checkGet(self::ROUTE_FINISHED)) {
  181. $renderActive = false;
  182. }
  183. if (ubRouting::checkGet(self::ROUTE_ALL)) {
  184. $renderActive = true;
  185. $renderFinished = true;
  186. }
  187. if (ubRouting::checkGet(self::ROUTE_ZENMODE)) {
  188. $renderFinished = false;
  189. $zenFlag = true;
  190. }
  191. if (!empty($this->allProcess)) {
  192. if (!$zenFlag) {
  193. $runningPids = $this->getRunningPids();
  194. }
  195. $cells = wf_TableCell(__('PID'), '10%');
  196. $cells .= wf_TableCell(__('Name'), '22%');
  197. $cells .= wf_TableCell('⛏️ ' . __('Active'), '8%');
  198. $cells .= wf_TableCell('⏳ ' . __('from'), '15%');
  199. $cells .= wf_TableCell('⏳ ' . __('to'), '15%');
  200. $cells .= wf_TableCell('⏱️ ' . __('time'), '30%');
  201. $rows = wf_TableRow($cells, 'row1');
  202. foreach ($this->allProcess as $processName => $processData) {
  203. $rowRender = true;
  204. $activityLed = ($processData['finished']) ? wf_img('skins/icon_inactive.gif') : wf_img('skins/icon_active.gif');
  205. $activityFlag = ($processData['finished']) ? 0 : 1;
  206. $startTime = ($processData['start']) ? date("Y-m-d H:i:s", $processData['start']) : '';
  207. $endTime = ($processData['end']) ? date("Y-m-d H:i:s", $processData['end']) : '';
  208. $execTime = 0;
  209. $runningLabel = ($processData['finished']) ? '' : ' 🏁 ';
  210. if ($processData['finished']) {
  211. $runTime = ($processData['end'] - $processData['start']);
  212. if (!$renderFinished) {
  213. $rowRender = false;
  214. }
  215. } else {
  216. $runTime = $processData['realtime'];
  217. if (!$renderActive) {
  218. $rowRender = false;
  219. }
  220. }
  221. if ($runTime > 3) {
  222. $execTime = zb_formatTime($runTime);
  223. } else {
  224. $execTime = $processData['realtime'] . ' ' . __('sec.');
  225. }
  226. if ($rowRender) {
  227. $pidLabel = $processData['pid'];
  228. //process management form if its running
  229. if (isset($runningPids[$processData['pid']]) AND $activityFlag) {
  230. $processString = $runningPids[$processData['pid']];
  231. $pidModal = wf_modalAuto($processData['pid'], __('Manage') . ' ' . $processName, $this->renderShutdownForm($processName));
  232. $pidLabel = $pidModal;
  233. }
  234. //highligthing dead processes with no PIDs
  235. if (!$zenFlag) {
  236. if (!isset($runningPids[$processData['pid']]) AND $activityFlag) {
  237. $pidLabel = $pidLabel . ' ' . wf_img_sized('skins/skull.png', __('Dead'), '12');
  238. }
  239. }
  240. $cells = wf_TableCell($pidLabel);
  241. $cells .= wf_TableCell($processName);
  242. $cells .= wf_TableCell($activityLed, '', '', 'sorttable_customkey="' . $activityFlag . '"');
  243. $cells .= wf_TableCell($startTime);
  244. $cells .= wf_TableCell($endTime);
  245. $cells .= wf_TableCell($runningLabel . $execTime, '', '', 'sorttable_customkey="' . $processData['realtime'] . '"');
  246. $rows .= wf_TableRow($cells, 'row5');
  247. $counter++;
  248. }
  249. }
  250. if ($counter > 0) {
  251. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  252. $result .= __('Total') . ' ' . __('processes') . ': ' . $counter;
  253. } else {
  254. $result .= $this->messages->getStyledMessage(__('Nothing to show'), 'info');
  255. }
  256. } else {
  257. $result .= $this->messages->getStyledMessage(__('Nothing found'), 'warning');
  258. }
  259. return($result);
  260. }
  261. }