/analytics/core/CliMulti/Process.php

https://gitlab.com/yasminmostfa/thomas-site · PHP · 257 lines · 180 code · 51 blank · 26 comment · 25 complexity · e25620baca0acfd96f7cf6f1f0590a74 MD5 · raw file

  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 running
  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. private $pid = null;
  26. public function __construct($pid)
  27. {
  28. if (!Filesystem::isValidFilename($pid)) {
  29. throw new \Exception('The given pid has an invalid format');
  30. }
  31. $pidDir = CliMulti::getTmpPath();
  32. Filesystem::mkdir($pidDir);
  33. $this->isSupported = self::isSupported();
  34. $this->pidFile = $pidDir . '/' . $pid . '.pid';
  35. $this->timeCreation = time();
  36. $this->pid = $pid;
  37. $this->markAsNotStarted();
  38. }
  39. public function getPid()
  40. {
  41. return $this->pid;
  42. }
  43. private function markAsNotStarted()
  44. {
  45. $content = $this->getPidFileContent();
  46. if ($this->doesPidFileExist($content)) {
  47. return;
  48. }
  49. $this->writePidFileContent('');
  50. }
  51. public function hasStarted($content = null)
  52. {
  53. if (is_null($content)) {
  54. $content = $this->getPidFileContent();
  55. }
  56. if (!$this->doesPidFileExist($content)) {
  57. // process is finished, this means there was a start before
  58. return true;
  59. }
  60. if ('' === trim($content)) {
  61. // pid file is overwritten by startProcess()
  62. return false;
  63. }
  64. // process is probably running or pid file was not removed
  65. return true;
  66. }
  67. public function hasFinished()
  68. {
  69. $content = $this->getPidFileContent();
  70. return !$this->doesPidFileExist($content);
  71. }
  72. public function getSecondsSinceCreation()
  73. {
  74. return time() - $this->timeCreation;
  75. }
  76. public function startProcess()
  77. {
  78. $this->writePidFileContent(getmypid());
  79. }
  80. public function isRunning()
  81. {
  82. $content = $this->getPidFileContent();
  83. if (!$this->doesPidFileExist($content)) {
  84. return false;
  85. }
  86. if (!$this->pidFileSizeIsNormal()) {
  87. $this->finishProcess();
  88. return false;
  89. }
  90. if ($this->isProcessStillRunning($content)) {
  91. return true;
  92. }
  93. if ($this->hasStarted($content)) {
  94. $this->finishProcess();
  95. }
  96. return false;
  97. }
  98. private function pidFileSizeIsNormal()
  99. {
  100. $size = Filesystem::getFileSize($this->pidFile);
  101. return $size !== null && $size < 500;
  102. }
  103. public function finishProcess()
  104. {
  105. Filesystem::deleteFileIfExists($this->pidFile);
  106. }
  107. private function doesPidFileExist($content)
  108. {
  109. return false !== $content;
  110. }
  111. private function isProcessStillRunning($content)
  112. {
  113. if (!$this->isSupported) {
  114. return true;
  115. }
  116. $lockedPID = trim($content);
  117. $runningPIDs = self::getRunningProcesses();
  118. return !empty($lockedPID) && in_array($lockedPID, $runningPIDs);
  119. }
  120. private function getPidFileContent()
  121. {
  122. return @file_get_contents($this->pidFile);
  123. }
  124. private function writePidFileContent($content)
  125. {
  126. file_put_contents($this->pidFile, $content);
  127. }
  128. public static function isSupported()
  129. {
  130. if (SettingsServer::isWindows()) {
  131. return false;
  132. }
  133. if (self::shellExecFunctionIsDisabled()) {
  134. return false;
  135. }
  136. if (self::isSystemNotSupported()) {
  137. return false;
  138. }
  139. if (!self::commandExists('ps') || !self::returnsSuccessCode('ps') || !self::commandExists('awk')) {
  140. return false;
  141. }
  142. if (count(self::getRunningProcesses()) > 0) {
  143. return true;
  144. }
  145. if (!self::isProcFSMounted()) {
  146. return false;
  147. }
  148. return true;
  149. }
  150. private static function isSystemNotSupported()
  151. {
  152. $uname = @shell_exec('uname -a 2> /dev/null');
  153. if (empty($uname)) {
  154. $uname = php_uname();
  155. }
  156. if (strpos($uname, 'synology') !== false) {
  157. return true;
  158. }
  159. return false;
  160. }
  161. private static function shellExecFunctionIsDisabled()
  162. {
  163. $command = 'shell_exec';
  164. $disabled = explode(',', ini_get('disable_functions'));
  165. $disabled = array_map('trim', $disabled);
  166. return in_array($command, $disabled) || !function_exists($command);
  167. }
  168. private static function returnsSuccessCode($command)
  169. {
  170. $exec = $command . ' > /dev/null 2>&1; echo $?';
  171. $returnCode = shell_exec($exec);
  172. $returnCode = trim($returnCode);
  173. return 0 == (int) $returnCode;
  174. }
  175. private static function commandExists($command)
  176. {
  177. $result = @shell_exec('which ' . escapeshellarg($command) . ' 2> /dev/null');
  178. return !empty($result);
  179. }
  180. /**
  181. * ps -e requires /proc
  182. * @return bool
  183. */
  184. private static function isProcFSMounted()
  185. {
  186. if (is_resource(@fopen('/proc', 'r'))) {
  187. return true;
  188. }
  189. // Testing if /proc is a resource with @fopen fails on systems with open_basedir set.
  190. // by using stat we not only test the existence of /proc but also confirm it's a 'proc' filesystem
  191. $type = @shell_exec('stat -f -c "%T" /proc 2>/dev/null');
  192. return strpos($type, 'proc') === 0;
  193. }
  194. /**
  195. * @return int[] The ids of the currently running processes
  196. */
  197. public static function getRunningProcesses()
  198. {
  199. $ids = explode("\n", trim(`ps ex 2>/dev/null | awk '{print $1}' 2>/dev/null`));
  200. $ids = array_map('intval', $ids);
  201. $ids = array_filter($ids, function ($id) {
  202. return $id > 0;
  203. });
  204. return $ids;
  205. }
  206. }