]> git.mxchange.org Git - friendica.git/blob - src/Core/Process.php
Create Core\Process as a base for followup work
[friendica.git] / src / Core / Process.php
1 <?php
2
3 namespace Friendica\Core;
4
5 use Friendica\App;
6 use Friendica\Core\Config\Configuration;
7 use Psr\Log\LoggerInterface;
8
9 /**
10  * Methods for interacting with the current process or create new process
11  *
12  * @todo 2019.12 Next release, this class holds all process relevant methods based on the big Worker class
13  *       - Starting new processes (including checks)
14  *       - Enabling multi-node processing (e.g. for docker service)
15  *         - Using an process-id per node
16  *         - Using memory locks for multi-node locking (redis, memcached, ..)
17  */
18 final class Process
19 {
20         /**
21          * @var LoggerInterface
22          */
23         private $logger;
24
25         /**
26          * @var App\Mode
27          */
28         private $mode;
29
30         /**
31          * @var Configuration
32          */
33         private $config;
34
35         /**
36          * @var string
37          */
38         private $basePath;
39
40         public function __construct(LoggerInterface $logger, App\Mode $mode, Configuration $config, string $basepath)
41         {
42                 $this->logger   = $logger;
43                 $this->mode     = $mode;
44                 $this->config   = $config;
45                 $this->basePath = $basepath;
46         }
47
48         /**
49          * @brief Checks if the maximum number of database processes is reached
50          *
51          * @return bool Is the limit reached?
52          */
53         public function isMaxProcessesReached()
54         {
55                 // Deactivated, needs more investigating if this check really makes sense
56                 return false;
57
58                 /*
59                  * Commented out to suppress static analyzer issues
60                  *
61                 if ($this->mode->isBackend()) {
62                         $process = 'backend';
63                         $max_processes = $this->config->get('system', 'max_processes_backend');
64                         if (intval($max_processes) == 0) {
65                                 $max_processes = 5;
66                         }
67                 } else {
68                         $process = 'frontend';
69                         $max_processes = $this->config->get('system', 'max_processes_frontend');
70                         if (intval($max_processes) == 0) {
71                                 $max_processes = 20;
72                         }
73                 }
74
75                 $processlist = DBA::processlist();
76                 if ($processlist['list'] != '') {
77                         $this->logger->debug('Processcheck: Processes: ' . $processlist['amount'] . ' - Processlist: ' . $processlist['list']);
78
79                         if ($processlist['amount'] > $max_processes) {
80                                 $this->logger->debug('Processcheck: Maximum number of processes for ' . $process . ' tasks (' . $max_processes . ') reached.');
81                                 return true;
82                         }
83                 }
84                 return false;
85                  */
86         }
87
88         /**
89          * @brief Checks if the minimal memory is reached
90          *
91          * @return bool Is the memory limit reached?
92          */
93         public function isMinMemoryReached()
94         {
95                 $min_memory = $this->config->get('system', 'min_memory', 0);
96                 if ($min_memory == 0) {
97                         return false;
98                 }
99
100                 if (!is_readable('/proc/meminfo')) {
101                         return false;
102                 }
103
104                 $memdata = explode("\n", file_get_contents('/proc/meminfo'));
105
106                 $meminfo = [];
107                 foreach ($memdata as $line) {
108                         $data = explode(':', $line);
109                         if (count($data) != 2) {
110                                 continue;
111                         }
112                         list($key, $val) = $data;
113                         $meminfo[$key] = (int)trim(str_replace('kB', '', $val));
114                         $meminfo[$key] = (int)($meminfo[$key] / 1024);
115                 }
116
117                 if (!isset($meminfo['MemFree'])) {
118                         return false;
119                 }
120
121                 $free = $meminfo['MemFree'];
122
123                 $reached = ($free < $min_memory);
124
125                 if ($reached) {
126                         $this->logger->debug('Minimal memory reached.', ['free' => $free, 'memtotal' => $meminfo['MemTotal'], 'limit' => $min_memory]);
127                 }
128
129                 return $reached;
130         }
131
132         /**
133          * @brief Checks if the maximum load is reached
134          *
135          * @return bool Is the load reached?
136          */
137         public function isMaxLoadReached()
138         {
139                 if ($this->mode->isBackend()) {
140                         $process    = 'backend';
141                         $maxsysload = intval($this->config->get('system', 'maxloadavg'));
142                         if ($maxsysload < 1) {
143                                 $maxsysload = 50;
144                         }
145                 } else {
146                         $process    = 'frontend';
147                         $maxsysload = intval($this->config->get('system', 'maxloadavg_frontend'));
148                         if ($maxsysload < 1) {
149                                 $maxsysload = 50;
150                         }
151                 }
152
153                 $load = System::currentLoad();
154                 if ($load) {
155                         if (intval($load) > $maxsysload) {
156                                 $this->logger->info('system load for process too high.', ['load' => $load, 'process' => $process, 'maxsysload' => $maxsysload]);
157                                 return true;
158                         }
159                 }
160                 return false;
161         }
162
163         /**
164          * Executes a child process with 'proc_open'
165          *
166          * @param string $command The command to execute
167          * @param array  $args    Arguments to pass to the command ( [ 'key' => value, 'key2' => value2, ... ]
168          */
169         public function run($command, $args)
170         {
171                 if (!function_exists('proc_open')) {
172                         return;
173                 }
174
175                 $cmdline = $this->config->get('config', 'php_path', 'php') . ' ' . escapeshellarg($command);
176
177                 foreach ($args as $key => $value) {
178                         if (!is_null($value) && is_bool($value) && !$value) {
179                                 continue;
180                         }
181
182                         $cmdline .= ' --' . $key;
183                         if (!is_null($value) && !is_bool($value)) {
184                                 $cmdline .= ' ' . $value;
185                         }
186                 }
187
188                 if ($this->isMinMemoryReached()) {
189                         return;
190                 }
191
192                 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
193                         $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->basePath);
194                 } else {
195                         $resource = proc_open($cmdline . ' &', [], $foo, $this->basePath);
196                 }
197                 if (!is_resource($resource)) {
198                         $this->logger->debug('We got no resource for command.', ['cmd' => $cmdline]);
199                         return;
200                 }
201                 proc_close($resource);
202         }
203 }