Passed
Push — develop ( b7c00c...da1f69 )
by Nikolay
08:07 queued 01:43
created

Processes::killByName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
/*
3
 * Copyright (C) MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Nikolay Beketov, 11 2020
7
 *
8
 */
9
10
namespace MikoPBX\Core\System;
11
12
13
use MikoPBX\Core\Workers\Cron\WorkerSafeScriptsCore;
14
use Phalcon\Di;
15
16
class Processes
17
{
18
19
    /**
20
     * Kills process/daemon by name
21
     *
22
     * @param $procName
23
     *
24
     * @return int|null
25
     */
26
    public static function killByName($procName): ?int
27
    {
28
        $killallPath = Util::which('killall');
29
30
        return self::mwExec($killallPath . ' ' . escapeshellarg($procName));
31
    }
32
33
    /**
34
     * Executes command exec().
35
     *
36
     * @param $command
37
     * @param $outArr
38
     * @param $retVal
39
     *
40
     * @return int
41
     */
42
    public static function mwExec($command, &$outArr = null, &$retVal = null): int
43
    {
44
        $retVal = 0;
45
        $outArr   = [];
46
        $di     = Di::getDefault();
47
48
        if ($di !== null && $di->getShared('config')->path('core.debugMode')) {
49
            echo "mwExec(): $command\n";
50
        } else {
51
            exec("$command 2>&1", $outArr, $retVal);
52
        }
53
54
        return $retVal;
55
    }
56
57
    /**
58
     * Executes command exec() as background process.
59
     *
60
     * @param $command
61
     * @param $out_file
62
     * @param $sleep_time
63
     */
64
    public static function mwExecBg($command, $out_file = '/dev/null', $sleep_time = 0): void
65
    {
66
        $nohupPath = Util::which('nohup');
67
        $shPath    = Util::which('sh');
68
        $rmPath    = Util::which('rm');
69
        $sleepPath = Util::which('sleep');
70
        if ($sleep_time > 0) {
71
            $filename = '/tmp/' . time() . '_noop.sh';
72
            file_put_contents($filename, "{$sleepPath} {$sleep_time}; {$command}; {$rmPath} -rf {$filename}");
73
            $noop_command = "{$nohupPath} {$shPath} {$filename} > {$out_file} 2>&1 &";
74
        } else {
75
            $noop_command = "{$nohupPath} {$command} > {$out_file} 2>&1 &";
76
        }
77
        exec($noop_command);
78
    }
79
80
    /**
81
     * Executes command exec() as background process with an execution timeout.
82
     *
83
     * @param        $command
84
     * @param int    $timeout
85
     * @param string $logname
86
     */
87
    public static function mwExecBgWithTimeout($command, $timeout = 4, $logname = '/dev/null'): void
88
    {
89
        $di = Di::getDefault();
90
91
        if ($di !== null && $di->getShared('config')->path('core.debugMode')) {
92
            echo "mwExecBg(): $command\n";
93
94
            return;
95
        }
96
        $nohupPath   = Util::which('nohup');
97
        $timeoutPath = Util::which('timeout');
98
        exec("{$nohupPath} {$timeoutPath} -t {$timeout} {$command} > {$logname} 2>&1 &");
99
    }
100
101
    /**
102
     * Executes multiple commands.
103
     *
104
     * @param        $arr_cmds
105
     * @param array  $out
106
     * @param string $logname
107
     */
108
    public static function mwExecCommands($arr_cmds, &$out = [], $logname = ''): void
109
    {
110
        $out = [];
111
        foreach ($arr_cmds as $cmd) {
112
            $out[]   = "$cmd;";
113
            $out_cmd = [];
114
            self::mwExec($cmd, $out_cmd);
115
            $out = array_merge($out, $out_cmd);
116
        }
117
118
        if ($logname !== '') {
119
            $result = implode("\n", $out);
120
            file_put_contents("/tmp/{$logname}_commands.log", $result);
121
        }
122
    }
123
124
125
    /**
126
     * Restart all workers in separate process,
127
     * we use this method after module install or delete
128
     */
129
    public static function restartAllWorkers(): void
130
    {
131
        $workerSafeScriptsPath = Util::getFilePathByClassName(WorkerSafeScriptsCore::class);
132
        $phpPath               = Util::which('php');
133
        $WorkerSafeScripts     = "{$phpPath} -f {$workerSafeScriptsPath} restart > /dev/null 2> /dev/null";
134
        self::mwExecBg($WorkerSafeScripts, '/dev/null', 1);
135
    }
136
137
138
    /**
139
     * Process PHP workers
140
     *
141
     * @param string $className
142
     * @param string $param
143
     * @param string $action
144
     */
145
    public static function processPHPWorker(string $className, string $param = 'start', string $action='restart'): void
146
    {
147
        $workerPath = Util::getFilePathByClassName($className);
148
        if ( ! empty($workerPath)) {
149
            $command = "php -f {$workerPath}";
150
            $path_kill  = Util::which('kill');
151
            $path_nohup = Util::which('nohup');
152
            $WorkerPID = self::getPidOfProcess($className);
153
            switch ($action) {
154
                case 'restart':
155
                    // Firstly start new process
156
                    self::mwExec("{$path_nohup} {$command} {$param}  > /dev/null 2>&1 &");
157
                    // Then kill the old one
158
                    if ($WorkerPID !== '') {
159
                        self::mwExec("{$path_kill} SIGUSR1 {$WorkerPID}  > /dev/null 2>&1 &");
160
                    }
161
                    break;
162
                case 'stop':
163
                    if ($WorkerPID !== '') {
164
                        self::mwExec("{$path_kill} SIGUSR1 {$WorkerPID}  > /dev/null 2>&1 &");
165
                    }
166
                    break;
167
                case 'start':
168
                    if ($WorkerPID === '') {
169
                        self::mwExec("{$path_nohup} {$command} {$param}  > /dev/null 2>&1 &");
170
                    }
171
                    break;
172
                case 'multiStart':
173
                    self::mwExec("{$path_nohup} {$command} {$param}  > /dev/null 2>&1 &");
174
                    break;
175
                default:
176
            }
177
        }
178
    }
179
180
    /**
181
     * Manages a daemon/worker process
182
     * Returns process statuses by name of it
183
     *
184
     * @param $cmd
185
     * @param $param
186
     * @param $proc_name
187
     * @param $action
188
     * @param $out_file
189
     *
190
     * @return array | bool
191
     */
192
    public static function processWorker($cmd, $param, $proc_name, $action, $out_file = '/dev/null')
193
    {
194
        $path_kill  = Util::which('kill');
195
        $path_nohup = Util::which('nohup');
196
197
        $WorkerPID = self::getPidOfProcess($proc_name);
198
199
        switch ($action) {
200
            case 'status':
201
                $status = ($WorkerPID !== '') ? 'Started' : 'Stoped';
202
                return ['status' => $status, 'app' => $proc_name, 'PID' => $WorkerPID];
203
            case 'restart':
204
                if ($WorkerPID !== '') {
205
                    self::mwExec("{$path_kill} -9 {$WorkerPID}  > /dev/null 2>&1 &");
206
                }
207
                self::mwExec("{$path_nohup} {$cmd} {$param}  > {$out_file} 2>&1 &");
208
                break;
209
            case 'stop':
210
                if ($WorkerPID !== '') {
211
                    self::mwExec("{$path_kill} -9 {$WorkerPID}  > /dev/null 2>&1 &");
212
                }
213
                break;
214
            case 'start':
215
                if ($WorkerPID === '') {
216
                    self::mwExec("{$path_nohup} {$cmd} {$param}  > {$out_file} 2>&1 &");
217
                }
218
                break;
219
            default:
220
        }
221
222
        return true;
223
    }
224
225
    /**
226
     * Возвращает PID процесса по его имени.
227
     *
228
     * @param        $name
229
     * @param string $exclude
230
     *
231
     * @return string
232
     */
233
    public static function getPidOfProcess($name, $exclude = ''): string
234
    {
235
        $path_ps   = Util::which('ps');
236
        $path_grep = Util::which('grep');
237
        $path_awk  = Util::which('awk');
238
239
        $name       = addslashes($name);
240
        $filter_cmd = '';
241
        if ( ! empty($exclude)) {
242
            $filter_cmd = "| $path_grep -v " . escapeshellarg($exclude);
243
        }
244
        $out = [];
245
        self::mwExec(
246
            "{$path_ps} -A -o 'pid,args' {$filter_cmd} | {$path_grep} '{$name}' | {$path_grep} -v grep | {$path_awk} ' {print $1} '",
247
            $out
248
        );
249
250
        return trim(implode(' ', $out));
251
    }
252
253
}