PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / CliMulti / Process.php
matomo / app / core / CliMulti Last commit date
CliPhp.php 6 years ago Output.php 6 years ago Process.php 6 years ago RequestCommand.php 6 years ago
Process.php
274 lines
1 <?php
2 /**
3 * Piwik - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 */
8 namespace Piwik\CliMulti;
9
10 use Piwik\CliMulti;
11 use Piwik\Filesystem;
12 use Piwik\SettingsServer;
13
14 /**
15 * There are three different states
16 * - PID file exists with empty content: Process is created but not started
17 * - PID file exists with the actual process PID as content: Process is running
18 * - PID file does not exist: Process is marked as finished
19 *
20 * Class Process
21 */
22 class Process
23 {
24 private $pidFile = '';
25 private $timeCreation = null;
26 private $isSupported = null;
27 private $pid = null;
28
29 public function __construct($pid)
30 {
31 if (!Filesystem::isValidFilename($pid)) {
32 throw new \Exception('The given pid has an invalid format');
33 }
34
35 $pidDir = CliMulti::getTmpPath();
36 Filesystem::mkdir($pidDir);
37
38 $this->isSupported = self::isSupported();
39 $this->pidFile = $pidDir . '/' . $pid . '.pid';
40 $this->timeCreation = time();
41 $this->pid = $pid;
42
43 $this->markAsNotStarted();
44 }
45
46 public function getPid()
47 {
48 return $this->pid;
49 }
50
51 private function markAsNotStarted()
52 {
53 $content = $this->getPidFileContent();
54
55 if ($this->doesPidFileExist($content)) {
56 return;
57 }
58
59 $this->writePidFileContent('');
60 }
61
62 public function hasStarted($content = null)
63 {
64 if (is_null($content)) {
65 $content = $this->getPidFileContent();
66 }
67
68 if (!$this->doesPidFileExist($content)) {
69 // process is finished, this means there was a start before
70 return true;
71 }
72
73 if ('' === trim($content)) {
74 // pid file is overwritten by startProcess()
75 return false;
76 }
77
78 // process is probably running or pid file was not removed
79 return true;
80 }
81
82 public function hasFinished()
83 {
84 $content = $this->getPidFileContent();
85
86 return !$this->doesPidFileExist($content);
87 }
88
89 public function getSecondsSinceCreation()
90 {
91 return time() - $this->timeCreation;
92 }
93
94 public function startProcess()
95 {
96 $this->writePidFileContent(getmypid());
97 }
98
99 public function isRunning()
100 {
101 $content = $this->getPidFileContent();
102
103 if (!$this->doesPidFileExist($content)) {
104 return false;
105 }
106
107 if (!$this->pidFileSizeIsNormal()) {
108 $this->finishProcess();
109 return false;
110 }
111
112 if ($this->isProcessStillRunning($content)) {
113 return true;
114 }
115
116 if ($this->hasStarted($content)) {
117 $this->finishProcess();
118 }
119
120 return false;
121 }
122
123 private function pidFileSizeIsNormal()
124 {
125 $size = Filesystem::getFileSize($this->pidFile);
126
127 return $size !== null && $size < 500;
128 }
129
130 public function finishProcess()
131 {
132 Filesystem::deleteFileIfExists($this->pidFile);
133 }
134
135 private function doesPidFileExist($content)
136 {
137 return false !== $content;
138 }
139
140 private function isProcessStillRunning($content)
141 {
142 if (!$this->isSupported) {
143 return true;
144 }
145
146 $lockedPID = trim($content);
147 $runningPIDs = self::getRunningProcesses();
148
149 return !empty($lockedPID) && in_array($lockedPID, $runningPIDs);
150 }
151
152 private function getPidFileContent()
153 {
154 return @file_get_contents($this->pidFile);
155 }
156
157 private function writePidFileContent($content)
158 {
159 file_put_contents($this->pidFile, $content);
160 }
161
162 public static function isSupported()
163 {
164 if (SettingsServer::isWindows()) {
165 return false;
166 }
167
168 if (self::isMethodDisabled('shell_exec')) {
169 return false;
170 }
171
172 if (self::isMethodDisabled('getmypid')) {
173 return false;
174 }
175
176 if (self::isSystemNotSupported()) {
177 return false;
178 }
179
180 if (!self::commandExists('ps') || !self::returnsSuccessCode('ps') || !self::commandExists('awk')) {
181 return false;
182 }
183
184 if (!in_array(getmypid(), self::getRunningProcesses())) {
185 return false;
186 }
187
188 if (!self::isProcFSMounted() && !SettingsServer::isMac()) {
189 return false;
190 }
191
192 return true;
193 }
194
195 private static function isSystemNotSupported()
196 {
197 $uname = @shell_exec('uname -a 2> /dev/null');
198
199 if (empty($uname)) {
200 $uname = php_uname();
201 }
202
203 if (strpos($uname, 'synology') !== false) {
204 return true;
205 }
206 return false;
207 }
208
209 public static function isMethodDisabled($command)
210 {
211 if (!function_exists($command)) {
212 return true;
213 }
214
215 $disabled = explode(',', ini_get('disable_functions'));
216 $disabled = array_map('trim', $disabled);
217 return in_array($command, $disabled) || !function_exists($command);
218 }
219
220 private static function returnsSuccessCode($command)
221 {
222 $exec = $command . ' > /dev/null 2>&1; echo $?';
223 $returnCode = shell_exec($exec);
224 $returnCode = trim($returnCode);
225 return 0 == (int) $returnCode;
226 }
227
228 private static function commandExists($command)
229 {
230 $result = @shell_exec('which ' . escapeshellarg($command) . ' 2> /dev/null');
231
232 return !empty($result);
233 }
234
235 /**
236 * ps -e requires /proc
237 * @return bool
238 */
239 private static function isProcFSMounted()
240 {
241 if (is_resource(@fopen('/proc', 'r'))) {
242 return true;
243 }
244 // Testing if /proc is a resource with @fopen fails on systems with open_basedir set.
245 // by using stat we not only test the existence of /proc but also confirm it's a 'proc' filesystem
246 $type = @shell_exec('stat -f -c "%T" /proc 2>/dev/null');
247 return strpos($type, 'proc') === 0;
248 }
249
250 public static function getListOfRunningProcesses()
251 {
252 $processes = `ps ex 2>/dev/null`;
253 if (empty($processes)) {
254 return array();
255 }
256 return explode("\n", $processes);
257 }
258
259 /**
260 * @return int[] The ids of the currently running processes
261 */
262 public static function getRunningProcesses()
263 {
264 $ids = explode("\n", trim(`ps ex 2>/dev/null | awk '! /defunct/ {print $1}' 2>/dev/null`));
265
266 $ids = array_map('intval', $ids);
267 $ids = array_filter($ids, function ($id) {
268 return $id > 0;
269 });
270
271 return $ids;
272 }
273 }
274