PageRenderTime 31ms CodeModel.GetById 5ms RepoModel.GetById 0ms app.codeStats 0ms

/core/CliMulti/Process.php

https://github.com/CodeYellowBV/piwik
PHP | 208 lines | 146 code | 41 blank | 21 comment | 19 complexity | 57460429e2b5223621e59945eea6f07a MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. */
  8. namespace Piwik\CliMulti;
  9. use Piwik\CliMulti;
  10. use Piwik\Filesystem;
  11. use Piwik\SettingsServer;
  12. /**
  13. * There are three different states
  14. * - PID file exists with empty content: Process is created but not started
  15. * - PID file exists with the actual process PID as content: Process is runnning
  16. * - PID file does not exist: Process is marked as finished
  17. *
  18. * Class Process
  19. */
  20. class Process
  21. {
  22. private $pidFile = '';
  23. private $timeCreation = null;
  24. private $isSupported = null;
  25. public function __construct($pid)
  26. {
  27. if (!Filesystem::isValidFilename($pid)) {
  28. throw new \Exception('The given pid has an invalid format');
  29. }
  30. $pidDir = CliMulti::getTmpPath();
  31. Filesystem::mkdir($pidDir);
  32. $this->isSupported = self::isSupported();
  33. $this->pidFile = $pidDir . '/' . $pid . '.pid';
  34. $this->timeCreation = time();
  35. $this->markAsNotStarted();
  36. }
  37. private function markAsNotStarted()
  38. {
  39. $content = $this->getPidFileContent();
  40. if ($this->doesPidFileExist($content)) {
  41. return;
  42. }
  43. $this->writePidFileContent('');
  44. }
  45. public function hasStarted($content = null)
  46. {
  47. if (is_null($content)) {
  48. $content = $this->getPidFileContent();
  49. }
  50. if (!$this->doesPidFileExist($content)) {
  51. // process is finished, this means there was a start before
  52. return true;
  53. }
  54. if ('' === trim($content)) {
  55. // pid file is overwritten by startProcess()
  56. return false;
  57. }
  58. // process is probably running or pid file was not removed
  59. return true;
  60. }
  61. public function hasFinished()
  62. {
  63. $content = $this->getPidFileContent();
  64. return !$this->doesPidFileExist($content);
  65. }
  66. public function getSecondsSinceCreation()
  67. {
  68. return time() - $this->timeCreation;
  69. }
  70. public function startProcess()
  71. {
  72. $this->writePidFileContent(getmypid());
  73. }
  74. public function isRunning()
  75. {
  76. $content = $this->getPidFileContent();
  77. if (!$this->doesPidFileExist($content)) {
  78. return false;
  79. }
  80. if ($this->isProcessStillRunning($content)) {
  81. return true;
  82. }
  83. if ($this->hasStarted($content)) {
  84. $this->finishProcess();
  85. }
  86. return false;
  87. }
  88. public function finishProcess()
  89. {
  90. Filesystem::deleteFileIfExists($this->pidFile);
  91. }
  92. private function doesPidFileExist($content)
  93. {
  94. return false !== $content;
  95. }
  96. private function isProcessStillRunning($content)
  97. {
  98. if (!$this->isSupported) {
  99. return true;
  100. }
  101. $lockedPID = trim($content);
  102. $runningPIDs = explode("\n", trim( `ps -e | awk '{print $1}'` ));
  103. return !empty($lockedPID) && in_array($lockedPID, $runningPIDs);
  104. }
  105. private function getPidFileContent()
  106. {
  107. return @file_get_contents($this->pidFile);
  108. }
  109. private function writePidFileContent($content)
  110. {
  111. file_put_contents($this->pidFile, $content);
  112. }
  113. public static function isSupported()
  114. {
  115. if (SettingsServer::isWindows()) {
  116. return false;
  117. }
  118. if (self::shellExecFunctionIsDisabled()) {
  119. return false;
  120. }
  121. if (self::isSystemNotSupported()) {
  122. return false;
  123. }
  124. if(!self::isProcFSMounted()) {
  125. return false;
  126. }
  127. if (static::commandExists('ps') && self::returnsSuccessCode('ps') && self::commandExists('awk')) {
  128. return true;
  129. }
  130. return false;
  131. }
  132. private static function isSystemNotSupported()
  133. {
  134. $uname = shell_exec('uname -a');
  135. if(strpos($uname, 'synology') !== false) {
  136. return true;
  137. }
  138. return false;
  139. }
  140. private static function shellExecFunctionIsDisabled()
  141. {
  142. $command = 'shell_exec';
  143. $disabled = explode(',', ini_get('disable_functions'));
  144. $disabled = array_map('trim', $disabled);
  145. return in_array($command, $disabled);
  146. }
  147. private static function returnsSuccessCode($command)
  148. {
  149. $exec = $command . ' > /dev/null 2>&1; echo $?';
  150. $returnCode = shell_exec($exec);
  151. $returnCode = trim($returnCode);
  152. return 0 == (int) $returnCode;
  153. }
  154. private static function commandExists($command)
  155. {
  156. $result = shell_exec('which ' . escapeshellarg($command) . ' 2> /dev/null');
  157. return !empty($result);
  158. }
  159. /**
  160. * ps -e requires /proc
  161. * @return bool
  162. */
  163. private static function isProcFSMounted()
  164. {
  165. return is_resource(@fopen('/proc', 'r'));
  166. }
  167. }