Passed
Push — master ( 8dcc68...34e8da )
by
unknown
23:44 queued 20:40
created

grommunio-sync-top.php (15 issues)

1
#!/usr/bin/env php
2
<?php
3
/*
4
 * SPDX-License-Identifier: AGPL-3.0-only
5
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
6
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
7
 *
8
 * Shows realtime information about connected devices and active connections
9
 * in a top-style format.
10
 */
11
12
require_once 'vendor/autoload.php';
13
if (!defined('ZPUSH_CONFIG')) define('ZPUSH_CONFIG', 'config.php');
14
include_once(ZPUSH_CONFIG);
15
16
/************************************************
17
 * MAIN
18
 */
19
    declare(ticks = 1);
20
    define('BASE_PATH_CLI',  dirname(__FILE__) ."/");
21
    set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH_CLI);
22
23
    if (!defined('ZPUSH_CONFIG')) define('ZPUSH_CONFIG', BASE_PATH_CLI . 'config.php');
24
    include_once(ZPUSH_CONFIG);
25
26
    try {
27
        ZPush::CheckConfig();
28
        if (!function_exists("pcntl_signal"))
29
            throw new FatalException("Function pcntl_signal() is not available. Please install package 'php5-pcntl' (or similar) on your system.");
30
31
        if (php_sapi_name() != "cli")
32
            throw new FatalException("This script can only be called from the CLI.");
33
34
        $zpt = new ZPushTop();
35
36
        // check if help was requested from CLI
37
        if (in_array('-h', $argv) || in_array('--help', $argv)) {
38
            echo $zpt->UsageInstructions();
39
            exit(0);
40
        }
41
42
        if ($zpt->IsAvailable()) {
43
            pcntl_signal(SIGINT, array($zpt, "SignalHandler"));
44
            $zpt->run();
45
            $zpt->scrClear();
46
            system("stty sane");
47
        }
48
        else
49
            echo "grommunio-sync interprocess communication (IPC) is not available or TopCollector is disabled.\n";
50
    }
51
    catch (ZPushException $zpe) {
52
        fwrite(STDERR, get_class($zpe) . ": ". $zpe->getMessage() . "\n");
53
        exit(1);
54
    }
55
56
    echo "terminated\n";
57
58
59
/************************************************
60
 * grommunio-sync-top
61
 */
62
class ZPushTop {
63
    // show options
64
    const SHOW_DEFAULT = 0;
65
    const SHOW_ACTIVE_ONLY = 1;
66
    const SHOW_UNKNOWN_ONLY = 2;
67
    const SHOW_TERM_DEFAULT_TIME = 5; // 5 secs
68
69
    private $topCollector;
70
    private $starttime;
71
    private $action;
72
    private $filter;
73
    private $status;
74
    private $statusexpire;
75
    private $wide;
76
    private $wasEnabled;
77
    private $terminate;
78
    private $scrSize;
79
    private $pingInterval;
80
    private $showPush;
81
    private $showTermSec;
82
83
    private $linesActive = array();
84
    private $linesOpen = array();
85
    private $linesUnknown = array();
86
    private $linesTerm = array();
87
    private $pushConn = 0;
88
    private $activeConn = array();
89
    private $activeHosts = array();
90
    private $activeUsers = array();
91
    private $activeDevices = array();
92
93
    /**
94
     * Constructor
95
     *
96
     * @access public
97
     */
98
    public function __construct() {
99
        $this->starttime = time();
100
        $this->currenttime = time();
0 ignored issues
show
Bug Best Practice introduced by
The property currenttime does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
101
        $this->action = "";
102
        $this->filter = false;
103
        $this->status = false;
104
        $this->statusexpire = 0;
105
        $this->helpexpire = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property helpexpire does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
106
        $this->doingTail = false;
0 ignored issues
show
Bug Best Practice introduced by
The property doingTail does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
107
        $this->wide = false;
108
        $this->terminate = false;
109
        $this->showPush = true;
110
        $this->showOption = self::SHOW_DEFAULT;
0 ignored issues
show
Bug Best Practice introduced by
The property showOption does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
111
        $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME;
112
        $this->scrSize = array('width' => 80, 'height' => 24);
113
        $this->pingInterval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 12;
114
115
        // get a TopCollector
116
        $this->topCollector = new TopCollector();
117
    }
118
119
    /**
120
     * Requests data from the running grommunio-sync processes
121
     *
122
     * @access private
123
     * @return
124
     */
125
    private function initialize() {
126
        // request feedback from active processes
127
        $this->wasEnabled = $this->topCollector->CollectData();
128
129
        // remove obsolete data
130
        $this->topCollector->ClearLatest(true);
131
132
        // start with default colours
133
        $this->scrDefaultColors();
134
    }
135
136
    /**
137
     * Main loop of grommunio-sync-top
138
     * Runs until termination is requested
139
     *
140
     * @access public
141
     * @return
142
     */
143
    public function run() {
144
        $this->initialize();
145
146
        do {
147
            $this->currenttime = time();
0 ignored issues
show
Bug Best Practice introduced by
The property currenttime does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
148
149
            // see if shared memory is active
150
            if (!$this->IsAvailable())
151
                $this->terminate = true;
152
153
            // active processes should continue sending data
154
            $this->topCollector->CollectData();
155
156
            // get and process data from processes
157
            $this->topCollector->ClearLatest();
158
            $topdata = $this->topCollector->ReadLatest();
159
            $this->processData($topdata);
160
161
            // clear screen
162
            $this->scrClear();
163
164
            // check if screen size changed
165
            $s = $this->scrGetSize();
166
            if ($this->scrSize['width'] != $s['width']) {
167
                if ($s['width'] > 180)
168
                    $this->wide = true;
169
                else
170
                    $this->wide = false;
171
            }
172
            $this->scrSize = $s;
173
174
            // print overview
175
            $this->scrOverview();
176
177
            // wait for user input
178
            $this->readLineProcess();
179
        }
180
        while($this->terminate != true);
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
181
    }
182
183
    /**
184
     * Indicates if TopCollector is available collecting data
185
     *
186
     * @access public
187
     * @return boolean
188
     */
189
    public function IsAvailable() {
190
        if (defined('TOPCOLLECTOR_DISABLED') && constant('TOPCOLLECTOR_DISABLED') === true) {
191
            return false;
192
        }
193
        return $this->topCollector->IsActive();
194
    }
195
196
    /**
197
     * Processes data written by the running processes
198
     *
199
     * @param array $data
200
     *
201
     * @access private
202
     * @return
203
     */
204
    private function processData($data) {
205
        $this->linesActive = array();
206
        $this->linesOpen = array();
207
        $this->linesUnknown = array();
208
        $this->linesTerm = array();
209
        $this->pushConn = 0;
210
        $this->activeConn = array();
211
        $this->activeHosts = array();
212
        $this->activeUsers = array();
213
        $this->activeDevices = array();
214
215
        if (!is_array($data))
0 ignored issues
show
The condition is_array($data) is always true.
Loading history...
216
            return;
217
218
        foreach ($data as $devid=>$users) {
219
            foreach ($users as $user=>$pids) {
220
                foreach ($pids as $pid=>$line) {
221
                    if (!is_array($line))
222
                        continue;
223
224
                    $line['command'] = Utils::GetCommandFromCode($line['command']);
225
226
                    if ($line["ended"] == 0) {
227
                        $this->activeDevices[$devid] = 1;
228
                        $this->activeUsers[$user] = 1;
229
                        $this->activeConn[$pid] = 1;
230
                        $this->activeHosts[$line['ip']] = 1;
231
232
                        $line["time"] = $this->currenttime - $line['start'];
233
                        if ($line['push'] === true) $this->pushConn += 1;
234
235
                        // ignore push connections
236
                        if ($line['push'] === true && ! $this->showPush)
237
                            continue;
238
239
                        if ($this->filter !== false) {
240
                            $f = $this->filter;
241
                            if (!($line["pid"] == $f || $line["ip"] == $f || strtolower($line['command']) == strtolower($f) || preg_match("/.*?$f.*?/i", $line['user']) ||
0 ignored issues
show
$f of type true is incompatible with the type string expected by parameter $string of strtolower(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

241
                            if (!($line["pid"] == $f || $line["ip"] == $f || strtolower($line['command']) == strtolower(/** @scrutinizer ignore-type */ $f) || preg_match("/.*?$f.*?/i", $line['user']) ||
Loading history...
242
                                preg_match("/.*?$f.*?/i", $line['devagent']) || preg_match("/.*?$f.*?/i", $line['devid']) || preg_match("/.*?$f.*?/i", $line['addinfo']) ))
243
                                continue;
244
                        }
245
246
                        $lastUpdate = $this->currenttime - $line["update"];
247
                        if ($this->currenttime - $line["update"] < 2)
248
                            $this->linesActive[$line["update"].$line["pid"]] = $line;
249
                        else if (($line['push'] === true  && $lastUpdate > ($this->pingInterval+2)) || ($line['push'] !== true  && $lastUpdate > 4))
250
                            $this->linesUnknown[$line["update"].$line["pid"]] = $line;
251
                        else
252
                            $this->linesOpen[$line["update"].$line["pid"]] = $line;
253
                    }
254
                    else {
255
                        // do not show terminated + expired connections
256
                        if ($line['ended'] + $this->showTermSec < $this->currenttime)
257
                            continue;
258
259
                        if ($this->filter !== false) {
260
                            $f = $this->filter;
261
                            if (!($line['pid'] == $f || $line['ip'] == $f || strtolower($line['command']) == strtolower($f) || preg_match("/.*?$f.*?/i", $line['user']) ||
262
                                preg_match("/.*?$f.*?/i", $line['devagent']) || preg_match("/.*?$f.*?/i", $line['devid']) || preg_match("/.*?$f.*?/i", $line['addinfo']) ))
263
                                continue;
264
                        }
265
266
                        $line['time'] = $line['ended'] - $line['start'];
267
                        $this->linesTerm[$line['update'].$line['pid']] = $line;
268
                    }
269
                }
270
            }
271
        }
272
273
        // sort by execution time
274
        krsort($this->linesActive);
275
        krsort($this->linesOpen);
276
        krsort($this->linesUnknown);
277
        krsort($this->linesTerm);
278
    }
279
280
    /**
281
     * Prints data to the terminal
282
     *
283
     * @access private
284
     * @return
285
     */
286
    private function scrOverview() {
287
        $linesAvail = $this->scrSize['height'] - 8;
288
        $lc = 1;
289
        $this->scrPrintAt($lc,0, "\033[1mgrommunio-sync-top live statistics\033[0m\t\t\t\t\t". @strftime("%d/%m/%Y %T")."\n"); $lc++;
0 ignored issues
show
Are you sure @strftime('%d/%m/%Y %T') of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
        $this->scrPrintAt($lc,0, "\033[1mgrommunio-sync-top live statistics\033[0m\t\t\t\t\t". /** @scrutinizer ignore-type */ @strftime("%d/%m/%Y %T")."\n"); $lc++;
Loading history...
290
291
        $this->scrPrintAt($lc,0, sprintf("Open connections: %d\t\t\t\tUsers:\t %d\tgrommunio-sync:   %s ",count($this->activeConn),count($this->activeUsers), $this->getVersion())); $lc++;
292
        $this->scrPrintAt($lc,0, sprintf("Push connections: %d\t\t\t\tDevices: %d\tPHP-MAPI: %s", $this->pushConn, count($this->activeDevices),phpversion("mapi"))); $lc++;
293
        $this->scrPrintAt($lc,0, sprintf("                                                Hosts:\t %d", count($this->activeHosts))); $lc++;
294
        $lc++;
295
296
        $this->scrPrintAt($lc,0, "\033[4m". $this->getLine(array('pid'=>'PID', 'ip'=>'IP', 'user'=>'USER', 'command'=>'COMMAND', 'time'=>'TIME', 'devagent'=>'AGENT', 'devid'=>'DEVID', 'addinfo'=>'Additional Information')). str_repeat(" ",20)."\033[0m"); $lc++;
297
298
        // print help text if requested
299
        $hl = 0;
300
        if ($this->helpexpire > $this->currenttime) {
301
            $help = $this->scrHelp();
302
            $linesAvail -= count($help);
303
            $hl = $this->scrSize['height'] - count($help) -1;
304
            foreach ($help as $h) {
305
                $this->scrPrintAt($hl,0, $h);
306
                $hl++;
307
            }
308
        }
309
310
        $toPrintActive = $linesAvail;
311
        $toPrintOpen = $linesAvail;
312
        $toPrintUnknown = $linesAvail;
313
        $toPrintTerm = $linesAvail;
314
315
        // default view: show all unknown, no terminated and half active+open
316
        if (count($this->linesActive) + count($this->linesOpen) + count($this->linesUnknown) > $linesAvail) {
317
            $toPrintUnknown = count($this->linesUnknown);
318
            $toPrintActive = count($this->linesActive);
319
            $toPrintOpen = $linesAvail-$toPrintUnknown-$toPrintActive;
320
            $toPrintTerm = 0;
321
        }
322
323
        if ($this->showOption == self::SHOW_ACTIVE_ONLY) {
324
            $toPrintActive = $linesAvail;
325
            $toPrintOpen = 0;
326
            $toPrintUnknown = 0;
327
            $toPrintTerm = 0;
328
        }
329
330
        if ($this->showOption == self::SHOW_UNKNOWN_ONLY) {
331
            $toPrintActive = 0;
332
            $toPrintOpen = 0;
333
            $toPrintUnknown = $linesAvail;
334
            $toPrintTerm = 0;
335
        }
336
337
        $linesprinted = 0;
338
        foreach ($this->linesActive as $time=>$l) {
339
            if ($linesprinted >= $toPrintActive)
340
                break;
341
342
            $this->scrPrintAt($lc,0, "\033[01m" . $this->getLine($l)  ."\033[0m");
343
            $lc++;
344
            $linesprinted++;
345
        }
346
347
        $linesprinted = 0;
348
        foreach ($this->linesOpen as $time=>$l) {
349
            if ($linesprinted >= $toPrintOpen)
350
                break;
351
352
            $this->scrPrintAt($lc,0, $this->getLine($l));
353
            $lc++;
354
            $linesprinted++;
355
        }
356
357
        $linesprinted = 0;
358
        foreach ($this->linesUnknown as $time=>$l) {
359
            if ($linesprinted >= $toPrintUnknown)
360
                break;
361
362
            $color = "0;31m";
363
            if ($l['push'] == false && $time - $l["start"] > 30)
364
                $color = "1;31m";
365
            $this->scrPrintAt($lc,0, "\033[0". $color . $this->getLine($l)  ."\033[0m");
366
            $lc++;
367
            $linesprinted++;
368
        }
369
370
        if ($toPrintTerm > 0)
371
            $toPrintTerm = $linesAvail - $lc +6;
372
373
        $linesprinted = 0;
374
        foreach ($this->linesTerm as $time=>$l){
375
            if ($linesprinted >= $toPrintTerm)
376
                break;
377
378
            $this->scrPrintAt($lc,0, "\033[01;30m" . $this->getLine($l)  ."\033[0m");
379
            $lc++;
380
            $linesprinted++;
381
        }
382
383
        // add the lines used when displaying the help text
384
        $lc += $hl;
385
        $this->scrPrintAt($lc,0, "\033[K"); $lc++;
386
        $this->scrPrintAt($lc,0, "Colorscheme: \033[01mActive  \033[0mOpen  \033[01;31mUnknown  \033[01;30mTerminated\033[0m");
387
388
        // remove old status
389
        if ($this->statusexpire < $this->currenttime)
390
            $this->status = false;
391
392
        // show request information and help command
393
        if ($this->starttime + 6 > $this->currenttime) {
394
            $this->status = sprintf("Requesting information (takes up to %dsecs)", $this->pingInterval). str_repeat(".", ($this->currenttime-$this->starttime)) . "  type \033[01;31mh\033[00;31m or \033[01;31mhelp\033[00;31m for usage instructions";
395
            $this->statusexpire = $this->currenttime+1;
396
        }
397
398
399
        $str = "";
400
        if (! $this->showPush)
401
            $str .= "\033[00;32mPush: \033[01;32mNo\033[0m   ";
402
403
        if ($this->showOption == self::SHOW_ACTIVE_ONLY)
404
            $str .= "\033[01;32mActive only\033[0m   ";
405
406
        if ($this->showOption == self::SHOW_UNKNOWN_ONLY)
407
            $str .= "\033[01;32mUnknown only\033[0m   ";
408
409
        if ($this->showTermSec != self::SHOW_TERM_DEFAULT_TIME)
410
            $str .= "\033[01;32mTerminated: ". $this->showTermSec. "s\033[0m   ";
411
412
        if ($this->filter !== false || ($this->status !== false && $this->statusexpire > $this->currenttime)) {
413
            // print filter in green
414
            if ($this->filter !== false)
415
                $str .= "\033[00;32mFilter: \033[01;32m$this->filter\033[0m   ";
416
            // print status in red
417
            if ($this->status !== false)
418
                $str .= "\033[00;31m$this->status\033[0m";
419
        }
420
        $this->scrPrintAt(5,0, $str);
421
422
        $this->scrPrintAt(4,0,"Action: \033[01m".$this->action . "\033[0m");
423
    }
424
425
    /**
426
     * Waits for a keystroke and processes the requested command
427
     *
428
     * @access private
429
     * @return
430
     */
431
    private function readLineProcess() {
432
        $ans = explode("^^", `bash -c "read -n 1 -t 1 ANS ; echo \\\$?^^\\\$ANS;"`);
433
434
        if ($ans[0] < 128) {
435
            if (isset($ans[1]) && bin2hex(trim($ans[1])) == "7f") {
436
                $this->action = substr($this->action,0,-1);
437
            }
438
439
            if (isset($ans[1]) && $ans[1] != "" ){
440
                $this->action .= trim(preg_replace("/[^A-Za-z0-9:]/","",$ans[1]));
441
            }
442
443
            if (bin2hex($ans[0]) == "30" && bin2hex($ans[1]) == "0a")  {
444
                $cmds = explode(':', $this->action);
445
                if ($cmds[0] == "quit" || $cmds[0] == "q" || (isset($cmds[1]) && $cmds[0] == "" && $cmds[1] == "q")) {
446
                    $this->topCollector->CollectData(true);
447
                    $this->topCollector->ClearLatest(true);
448
449
                    $this->terminate = true;
450
                }
451
                else if ($cmds[0] == "clear" ) {
452
                    $this->topCollector->ClearLatest(true);
453
                    $this->topCollector->CollectData(true);
454
                    $this->topCollector->ReInitIPC();
455
                }
456
                else if ($cmds[0] == "filter" || $cmds[0] == "f") {
457
                    if (!isset($cmds[1]) || $cmds[1] == "") {
458
                        $this->filter = false;
459
                        $this->status = "No filter";
460
                        $this->statusexpire = $this->currenttime+5;
461
                    }
462
                    else {
463
                        $this->filter = $cmds[1];
464
                        $this->status = false;
465
                    }
466
                }
467
                else if ($cmds[0] == "option" || $cmds[0] == "o") {
468
                    if (!isset($cmds[1]) || $cmds[1] == "") {
469
                        $this->status = "Option value needs to be specified. See 'help' or 'h' for instructions";
470
                        $this->statusexpire = $this->currenttime+5;
471
                    }
472
                    else if ($cmds[1] == "p" || $cmds[1] == "push" || $cmds[1] == "ping")
473
                        $this->showPush = !$this->showPush;
474
                    else if ($cmds[1] == "a" || $cmds[1] == "active")
475
                        $this->showOption = self::SHOW_ACTIVE_ONLY;
0 ignored issues
show
Bug Best Practice introduced by
The property showOption does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
476
                    else if ($cmds[1] == "u" || $cmds[1] == "unknown")
477
                        $this->showOption = self::SHOW_UNKNOWN_ONLY;
478
                    else if ($cmds[1] == "d" || $cmds[1] == "default") {
479
                        $this->showOption = self::SHOW_DEFAULT;
480
                        $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME;
481
                        $this->showPush = true;
482
                    }
483
                    else if (is_numeric($cmds[1]))
484
                        $this->showTermSec = $cmds[1];
485
                    else {
486
                        $this->status = sprintf("Option '%s' unknown", $cmds[1]);
487
                        $this->statusexpire = $this->currenttime+5;
488
                    }
489
                }
490
                else if ($cmds[0] == "reset" || $cmds[0] == "r") {
491
                    $this->filter = false;
492
                    $this->wide = false;
493
                    $this->helpexpire = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property helpexpire does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
494
                    $this->status = "reset";
495
                    $this->statusexpire = $this->currenttime+2;
496
                }
497
                // enable/disable wide view
498
                else if ($cmds[0] == "wide" || $cmds[0] == "w") {
499
                    $this->wide = ! $this->wide;
500
                    $this->status = ($this->wide)?"w i d e  view" : "normal view";
501
                    $this->statusexpire = $this->currenttime+2;
502
                }
503
                else if ($cmds[0] == "help" || $cmds[0] == "h") {
504
                    $this->helpexpire = $this->currenttime+20;
505
                }
506
                // grep the log file
507
                else if (($cmds[0] == "log" || $cmds[0] == "l") && isset($cmds[1]) ) {
508
                    if (!file_exists(LOGFILE)) {
509
                        $this->status = "Logfile can not be found: ". LOGFILE;
510
                    }
511
                    else {
512
                        system('bash -c "fgrep -a '.escapeshellarg($cmds[1]).' '. LOGFILE .' | less +G" > `tty`');
513
                        $this->status = "Returning from log, updating data";
514
                    }
515
                    $this->statusexpire = time()+5; // it might be much "later" now
516
                }
517
                // tail the log file
518
                else if (($cmds[0] == "tail" || $cmds[0] == "t")) {
519
                    if (!file_exists(LOGFILE)) {
520
                        $this->status = "Logfile can not be found: ". LOGFILE;
521
                    }
522
                    else {
523
                        $this->doingTail = true;
0 ignored issues
show
Bug Best Practice introduced by
The property doingTail does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
524
                        $this->scrClear();
525
                        $this->scrPrintAt(1,0,$this->scrAsBold("Press CTRL+C to return to grommunio-sync-top\n\n"));
526
                        $secondary = "";
527
                        if (isset($cmds[1])) $secondary =  " -n 200 | grep ".escapeshellarg($cmds[1]);
528
                        system('bash -c "tail -f '. LOGFILE . $secondary . '" > `tty`');
529
                        $this->doingTail = false;
530
                        $this->status = "Returning from tail, updating data";
531
                    }
532
                    $this->statusexpire = time()+5; // it might be much "later" now
533
                }
534
                // tail the error log file
535
                else if (($cmds[0] == "error" || $cmds[0] == "e")) {
536
                    if (!file_exists(LOGERRORFILE)) {
537
                        $this->status = "Error logfile can not be found: ". LOGERRORFILE;
538
                    }
539
                    else {
540
                        $this->doingTail = true;
541
                        $this->scrClear();
542
                        $this->scrPrintAt(1,0,$this->scrAsBold("Press CTRL+C to return to grommunio-sync-top\n\n"));
543
                        $secondary = "";
544
                        if (isset($cmds[1])) $secondary =  " -n 200 | grep ".escapeshellarg($cmds[1]);
545
                        system('bash -c "tail -f '. LOGERRORFILE . $secondary . '" > `tty`');
546
                        $this->doingTail = false;
547
                        $this->status = "Returning from tail, updating data";
548
                    }
549
                    $this->statusexpire = time()+5; // it might be much "later" now
550
                }
551
552
                else if ($cmds[0] != "") {
553
                    $this->status = sprintf("Command '%s' unknown", $cmds[0]);
554
                    $this->statusexpire = $this->currenttime+8;
555
                }
556
                $this->action = "";
557
            }
558
        }
559
    }
560
561
    /**
562
     * Signal handler function
563
     *
564
     * @param int   $signo      signal number
565
     *
566
     * @access public
567
     * @return
568
     */
569
    public function SignalHandler($signo) {
0 ignored issues
show
The parameter $signo is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

569
    public function SignalHandler(/** @scrutinizer ignore-unused */ $signo) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
570
        // don't terminate if the signal was sent by terminating tail
571
        if (!$this->doingTail) {
572
            $this->topCollector->CollectData(true);
573
            $this->topCollector->ClearLatest(true);
574
            $this->terminate = true;
575
        }
576
    }
577
578
    /**
579
     * Returns usage instructions
580
     *
581
     * @return string
582
     * @access public
583
     */
584
    public function UsageInstructions() {
585
        $help = "Usage:\n\tgrommunio-sync-top.php\n\n" .
586
                "  grommunio-sync-top is a live top-like overview of what grommunio-sync is doing. It does not have specific command line options.\n\n".
587
                "  When grommunio-sync-top is running you can specify certain actions and options which can be executed (listed below).\n".
588
                "  This help information can also be shown inside grommunio-sync-top by hitting 'help' or 'h'.\n\n";
589
        $scrhelp = $this->scrHelp();
590
        unset($scrhelp[0]);
591
592
        $help .= implode("\n", $scrhelp);
593
        $help .= "\n\n";
594
        return $help;
595
    }
596
597
598
    /**
599
     * Prints a 'help' text at the end of the page
600
     *
601
     * @access private
602
     * @return array        with help lines
603
     */
604
    private function scrHelp() {
605
        $h = array();
606
        $secs = $this->helpexpire - $this->currenttime;
607
        $h[] = "Actions supported by grommunio-sync-top (help page still displayed for ".$secs."secs)";
608
        $h[] = "  ".$this->scrAsBold("Action")."\t\t".$this->scrAsBold("Comment");
609
        $h[] = "  ".$this->scrAsBold("h")." or ".$this->scrAsBold("help")."\t\tDisplays this information.";
610
        $h[] = "  ".$this->scrAsBold("q").", ".$this->scrAsBold("quit")." or ".$this->scrAsBold(":q")."\t\tExits grommunio-sync-top.";
611
        $h[] = "  ".$this->scrAsBold("w")." or ".$this->scrAsBold("wide")."\t\tTries not to truncate data. Automatically done if more than 180 columns available.";
612
        $h[] = "  ".$this->scrAsBold("f:VAL")." or ".$this->scrAsBold("filter:VAL")."\tOnly display connections which contain VAL. This value is case-insensitive.";
613
        $h[] = "  ".$this->scrAsBold("f:")." or ".$this->scrAsBold("filter:")."\t\tWithout a search word: resets the filter.";
614
        $h[] = "  ".$this->scrAsBold("l:STR")." or ".$this->scrAsBold("log:STR")."\tIssues 'less +G' on the logfile, after grepping on the optional STR.";
615
        $h[] = "  ".$this->scrAsBold("t:STR")." or ".$this->scrAsBold("tail:STR")."\tIssues 'tail -f' on the logfile, grepping for optional STR.";
616
        $h[] = "  ".$this->scrAsBold("e:STR")." or ".$this->scrAsBold("error:STR")."\tIssues 'tail -f' on the error logfile, grepping for optional STR.";
617
        $h[] = "  ".$this->scrAsBold("r")." or ".$this->scrAsBold("reset")."\t\tResets 'wide' or 'filter'.";
618
        $h[] = "  ".$this->scrAsBold("o:")." or ".$this->scrAsBold("option:")."\t\tSets display options. Valid options specified below";
619
        $h[] = "  ".$this->scrAsBold("  p")." or ".$this->scrAsBold("push")."\t\tLists/not lists active and open push connections.";
620
        $h[] = "  ".$this->scrAsBold("  a")." or ".$this->scrAsBold("action")."\t\tLists only active connections.";
621
        $h[] = "  ".$this->scrAsBold("  u")." or ".$this->scrAsBold("unknown")."\tLists only unknown connections.";
622
        $h[] = "  ".$this->scrAsBold("  10")." or ".$this->scrAsBold("20")."\t\tLists terminated connections for 10 or 20 seconds. Any other number can be used.";
623
        $h[] = "  ".$this->scrAsBold("  d")." or ".$this->scrAsBold("default")."\tUses default options";
624
625
        return $h;
626
    }
627
628
    /**
629
     * Encapsulates string with different color escape characters
630
     *
631
     * @param string        $text
632
     *
633
     * @access private
634
     * @return string       same text as bold
635
     */
636
    private function scrAsBold($text) {
637
        return "\033[01m" . $text  ."\033[0m";
638
    }
639
640
    /**
641
     * Prints one line of precessed data
642
     *
643
     * @param array     $l      line information
644
     *
645
     * @access private
646
     * @return string
647
     */
648
    private function getLine($l) {
649
        if ($this->wide === true)
650
            return sprintf("%s%s%s%s%s%s%s%s", $this->ptStr($l['pid'],6), $this->ptStr($l['ip'],16), $this->ptStr($l['user'],24), $this->ptStr($l['command'],16), $this->ptStr($this->sec2min($l['time']),8), $this->ptStr($l['devagent'],28), $this->ptStr($l['devid'],33, true), $l['addinfo']);
651
        else
652
            return sprintf("%s%s%s%s%s%s%s%s", $this->ptStr($l['pid'],6), $this->ptStr($l['ip'],16), $this->ptStr($l['user'],8), $this->ptStr($l['command'],8), $this->ptStr($this->sec2min($l['time']),6), $this->ptStr($l['devagent'],20), $this->ptStr($l['devid'],12, true), $l['addinfo']);
653
    }
654
655
    /**
656
     * Pads and trims string
657
     *
658
     * @param string    $str        to be trimmed/padded
659
     * @param int       $size       characters to be considered
660
     * @param boolean   $cutmiddle  (optional) indicates where to long information should
661
     *                              be trimmed of, false means at the end
662
     *
663
     * @access private
664
     * @return string
665
     */
666
    private function ptStr($str, $size, $cutmiddle = false) {
667
        if (strlen($str) < $size)
668
            return str_pad($str, $size);
669
        else if ($cutmiddle == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
670
            $cut = ($size-2)/2;
671
            return $this->ptStr(substr($str,0,$cut) ."..". substr($str,(-1)*($cut-1)), $size);
672
        }
673
        else {
674
            return substr($str,0,$size-3).".. ";
675
        }
676
    }
677
678
    /**
679
     * Tries to discover the size of the current terminal
680
     *
681
     * @access private
682
     * @return array        'width' and 'height' as keys
683
     */
684
    private function scrGetSize() {
685
        $tty = strtolower(exec('stty -a | fgrep columns'));
686
        if (preg_match_all("/rows.([0-9]+);.columns.([0-9]+);/", $tty, $output) ||
687
            preg_match_all("/([0-9]+).rows;.([0-9]+).columns;/", $tty, $output))
688
            return array('width' => $output[2][0], 'height' => $output[1][0]);
689
690
        return array('width' => 80, 'height' => 24);
691
    }
692
693
    /**
694
     * Returns the version of the current grommunio-sync installation
695
     *
696
     * @access private
697
     * @return string
698
     */
699
    private function getVersion() {
700
        return GROMMUNIOSYNC_VERSION;
701
    }
702
703
    /**
704
     * Converts seconds in MM:SS
705
     *
706
     * @param int   $s      seconds
707
     *
708
     * @access private
709
     * @return string
710
     */
711
    private function sec2min($s) {
712
        if (!is_int($s))
0 ignored issues
show
The condition is_int($s) is always true.
Loading history...
713
            return $s;
714
        return sprintf("%02.2d:%02.2d", floor($s/60), $s%60);
715
    }
716
717
    /**
718
     * Resets the default colors of the terminal
719
     *
720
     * @access private
721
     * @return
722
     */
723
    private function scrDefaultColors() {
724
        echo "\033[0m";
725
    }
726
727
    /**
728
     * Clears screen of the terminal
729
     *
730
     * @param array $data
731
     *
732
     * @access private
733
     * @return
734
     */
735
    public function scrClear() {
736
        echo "\033[2J";
737
    }
738
739
    /**
740
     * Prints a text at a specific screen/terminal coordinates
741
     *
742
     * @param int       $row        row number
743
     * @param int       $col        column number
744
     * @param string    $text       to be printed
745
     *
746
     * @access private
747
     * @return
748
     */
749
    private function scrPrintAt($row, $col, $text="") {
750
        echo "\033[".$row.";".$col."H".$text;
751
    }
752
753
}
754