Completed
Push — typo3v9 ( 337a37...73c725 )
by Tomas Norre
05:37
created

ProcessCleanUpHook::isOsWindows()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 6
1
<?php
2
namespace AOE\Crawler\Hooks;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2019 AOE GmbH <[email protected]>
8
 *
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use AOE\Crawler\Controller\CrawlerController;
29
use AOE\Crawler\Domain\Repository\ProcessRepository;
30
use AOE\Crawler\Domain\Repository\QueueRepository;
31
use TYPO3\CMS\Core\Core\Environment;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
33
use TYPO3\CMS\Extbase\Object\ObjectManager;
34
35
/**
36
 * Class ProcessCleanUpHook
37
 * @package AOE\Crawler\Hooks
38
 */
39
class ProcessCleanUpHook
40
{
41
    /**
42
     * @var CrawlerController
43
     */
44
    private $crawlerController;
45
46
    /**
47
     * @var array
48
     */
49
    private $extensionSettings;
50
51
    /**
52
     * @var ProcessRepository
53
     */
54
    protected $processRepository;
55
56
    /**
57
     * @var QueueRepository
58
     */
59
    protected $queueRepository;
60
61 5
    public function __construct()
62
    {
63 5
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
64 5
        $this->processRepository = $objectManager->get(ProcessRepository::class);
65 5
        $this->queueRepository = $objectManager->get(QueueRepository::class);
66 5
    }
67
68
    /**
69
     * Main function of process CleanUp Hook.
70
     *
71
     * @param CrawlerController $crawlerController Crawler Lib class
72
     *
73
     * @return void
74
     */
75
    public function crawler_init(CrawlerController $crawlerController)
76
    {
77
        $this->crawlerController = $crawlerController;
78
        $this->extensionSettings = $this->crawlerController->extensionSettings;
79
80
        // Clean Up
81
        $this->removeActiveOrphanProcesses();
82
        $this->removeActiveProcessesOlderThanOneHour();
83
    }
84
85
    /**
86
     * Remove active processes older than one hour
87
     *
88
     * @return void
89
     */
90
    private function removeActiveProcessesOlderThanOneHour()
91
    {
92
        $results = $this->processRepository->getActiveProcessesOlderThanOneHour();
93
94
        if (!is_array($results)) {
95
            return;
96
        }
97
        foreach ($results as $result) {
98
            $systemProcessId = (int)$result['system_process_id'];
99
            $processId = $result['process_id'];
100
            if ($systemProcessId > 1) {
101
                if ($this->doProcessStillExists($systemProcessId)) {
102
                    $this->killProcess($systemProcessId);
103
                }
104
                $this->removeProcessFromProcesslist($processId);
105
            }
106
        }
107
    }
108
109
    /**
110
     * Removes active orphan processes from process list
111
     *
112
     * @return void
113
     */
114
    private function removeActiveOrphanProcesses()
115
    {
116
        $results = $this->processRepository->getActiveOrphanProcesses();
117
118
        if (!is_array($results)) {
119
            return;
120
        }
121
        foreach ($results as $result) {
122
            $processExists = false;
123
            $systemProcessId = (int)$result['system_process_id'];
124
            $processId = $result['process_id'];
125
            if ($systemProcessId > 1) {
126
                $dispatcherProcesses = $this->findDispatcherProcesses();
127
                if (!is_array($dispatcherProcesses) || empty($dispatcherProcesses)) {
128
                    $this->removeProcessFromProcesslist($processId);
129
                    return;
130
                }
131
                foreach ($dispatcherProcesses as $process) {
132
                    $responseArray = $this->createResponseArray($process);
133
                    if ($systemProcessId === (int)$responseArray[1]) {
134
                        $processExists = true;
135
                    };
136
                }
137
                if (!$processExists) {
138
                    $this->removeProcessFromProcesslist($processId);
139
                }
140
            }
141
        }
142
    }
143
144
    /**
145
     * Remove a process from processlist
146
     *
147
     * @param string $processId Unique process Id.
148
     *
149
     * @return void
150
     */
151 3
    private function removeProcessFromProcesslist($processId)
152
    {
153 3
        $this->processRepository->removeByProcessId($processId);
154 3
        $this->queueRepository->unsetQueueProcessId($processId);
155 3
    }
156
157
    /**
158
     * Create response array
159
     * Convert string to array with space character as delimiter,
160
     * removes all empty records to have a cleaner array
161
     *
162
     * @param string $string String to create array from
163
     *
164
     * @return array
165
     *
166
     */
167 2
    private function createResponseArray($string)
168
    {
169 2
        $responseArray = GeneralUtility::trimExplode(' ', $string, true);
170 2
        $responseArray = array_values($responseArray);
171 2
        return $responseArray;
172
    }
173
174
    /**
175
     * Check if the process still exists
176
     *
177
     * @param int $pid Process id to be checked.
178
     *
179
     * @return bool
180
     * @codeCoverageIgnore
181
     */
182
    private function doProcessStillExists($pid)
183
    {
184
        $doProcessStillExists = false;
185
        if (!Environment::isWindows()) {
186
            // Not windows
187
            if (file_exists('/proc/' . $pid)) {
188
                $doProcessStillExists = true;
189
            }
190
        } else {
191
            // Windows
192
            exec('tasklist | find "' . $pid . '"', $returnArray, $returnValue);
193
            if (count($returnArray) > 0 && preg_match('/php/i', $returnValue[0])) {
194
                $doProcessStillExists = true;
195
            }
196
        }
197
        return $doProcessStillExists;
198
    }
199
200
    /**
201
     * Kills a process
202
     *
203
     * @param int $pid Process id to kill
204
     *
205
     * @return void
206
     * @codeCoverageIgnore
207
     */
208
    private function killProcess($pid)
209
    {
210
        if (!Environment::isWindows()) {
211
            // Not windows
212
            posix_kill($pid, 9);
213
        } else {
214
            // Windows
215
            exec('taskkill /PID ' . $pid);
216
        }
217
    }
218
219
    /**
220
     * Find dispatcher processes
221
     *
222
     * @return array
223
     * @codeCoverageIgnore
224
     */
225
    private function findDispatcherProcesses()
226
    {
227
        $returnArray = [];
228
        if (!Environment::isWindows()) {
229
            // Not windows
230
            exec('ps aux | grep \'cli_dispatcher\'', $returnArray, $returnValue);
231
        } else {
232
            // Windows
233
            exec('tasklist | find \'cli_dispatcher\'', $returnArray, $returnValue);
234
        }
235
        return $returnArray;
236
    }
237
}
238