Passed
Push — master ( 6522b6...8c95d3 )
by
unknown
04:16 queued 01:06
created

grommunio-sync-top.php (5 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('GSYNC_CONFIG')) {
14
	define('GSYNC_CONFIG', 'config.php');
15
}
16
17
include_once GSYNC_CONFIG;
18
19
/*
20
 * MAIN
21
 */
22
	declare(ticks=1);
23
	define('BASE_PATH_CLI', dirname(__FILE__) . "/");
24
	set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH_CLI);
25
26
	if (!defined('GSYNC_CONFIG')) {
27
		define('GSYNC_CONFIG', BASE_PATH_CLI . 'config.php');
28
	}
29
30
	include_once GSYNC_CONFIG;
31
32
	try {
33
		GSync::CheckConfig();
34
		if (!function_exists("pcntl_signal")) {
35
			throw new FatalException("Function pcntl_signal() is not available. Please install package 'php5-pcntl' (or similar) on your system.");
36
		}
37
38
		if (php_sapi_name() != "cli") {
39
			throw new FatalException("This script can only be called from the CLI.");
40
		}
41
42
		$zpt = new GSyncTop();
43
44
		// check if help was requested from CLI
45
		if (in_array('-h', $argv) || in_array('--help', $argv)) {
46
			echo $zpt->UsageInstructions();
47
48
			exit(0);
49
		}
50
51
		if ($zpt->IsAvailable()) {
52
			pcntl_signal(SIGINT, [$zpt, "SignalHandler"]);
53
			$zpt->run();
54
			$zpt->scrClear();
55
			system("stty sane");
56
		}
57
		else {
58
			echo "grommunio-sync interprocess communication (IPC) is not available or TopCollector is disabled.\n";
59
		}
60
	}
61
	catch (GSyncException $zpe) {
62
		fwrite(STDERR, get_class($zpe) . ": " . $zpe->getMessage() . "\n");
63
64
		exit(1);
65
	}
66
67
	echo "terminated\n";
68
69
/*
70
 * grommunio-sync-top
71
 */
72
class GSyncTop {
73
	// show options
74
	public const SHOW_DEFAULT = 0;
75
	public const SHOW_ACTIVE_ONLY = 1;
76
	public const SHOW_UNKNOWN_ONLY = 2;
77
	public const SHOW_TERM_DEFAULT_TIME = 5; // 5 secs
78
79
	private $topCollector;
80
	private $starttime;
81
	private $currenttime;
82
	private $action;
83
	private $filter;
84
	private $status;
85
	private $statusexpire;
86
	private $helpexpire;
87
	private $doingTail;
88
	private $wide;
89
	private $wasEnabled;
90
	private $terminate;
91
	private $scrSize;
92
	private $pingInterval;
93
	private $showPush;
94
	private $showOption;
95
	private $showTermSec;
96
97
	private $linesActive = [];
98
	private $linesOpen = [];
99
	private $linesUnknown = [];
100
	private $linesTerm = [];
101
	private $pushConn = 0;
102
	private $activeConn = [];
103
	private $activeHosts = [];
104
	private $activeUsers = [];
105
	private $activeDevices = [];
106
107
	/**
108
	 * Constructor.
109
	 */
110
	public function __construct() {
111
		$this->starttime = time();
112
		$this->currenttime = time();
113
		$this->action = "";
114
		$this->filter = false;
115
		$this->status = false;
116
		$this->statusexpire = 0;
117
		$this->helpexpire = 0;
118
		$this->doingTail = false;
119
		$this->wide = false;
120
		$this->terminate = false;
121
		$this->showPush = true;
122
		$this->showOption = self::SHOW_DEFAULT;
123
		$this->showTermSec = self::SHOW_TERM_DEFAULT_TIME;
124
		$this->scrSize = ['width' => 80, 'height' => 24];
125
		$this->pingInterval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 12;
126
127
		// get a TopCollector
128
		$this->topCollector = new TopCollector();
129
	}
130
131
	/**
132
	 * Requests data from the running grommunio-sync processes.
133
	 *
134
	 * @return
135
	 */
136
	private function initialize() {
137
		// request feedback from active processes
138
		$this->wasEnabled = $this->topCollector->CollectData();
139
140
		// remove obsolete data
141
		$this->topCollector->ClearLatest(true);
142
143
		// start with default colours
144
		$this->scrDefaultColors();
145
	}
146
147
	/**
148
	 * Main loop of grommunio-sync-top
149
	 * Runs until termination is requested.
150
	 *
151
	 * @return
152
	 */
153
	public function run() {
154
		$this->initialize();
155
156
		do {
157
			$this->currenttime = time();
158
159
			// see if shared memory is active
160
			if (!$this->IsAvailable()) {
161
				$this->terminate = true;
162
			}
163
164
			// active processes should continue sending data
165
			$this->topCollector->CollectData();
166
167
			// get and process data from processes
168
			$this->topCollector->ClearLatest();
169
			$topdata = $this->topCollector->ReadLatest();
170
			$this->processData($topdata);
171
172
			// clear screen
173
			$this->scrClear();
174
175
			// check if screen size changed
176
			$s = $this->scrGetSize();
177
			if ($this->scrSize['width'] != $s['width']) {
178
				if ($s['width'] > 180) {
179
					$this->wide = true;
180
				}
181
				else {
182
					$this->wide = false;
183
				}
184
			}
185
			$this->scrSize = $s;
186
187
			// print overview
188
			$this->scrOverview();
189
190
			// wait for user input
191
			$this->readLineProcess();
192
		}
193
		while ($this->terminate !== true);
194
	}
195
196
	/**
197
	 * Indicates if TopCollector is available collecting data.
198
	 *
199
	 * @return bool
200
	 */
201
	public function IsAvailable() {
202
		if (defined('TOPCOLLECTOR_DISABLED') && constant('TOPCOLLECTOR_DISABLED') === true) {
203
			return false;
204
		}
205
206
		return $this->topCollector->IsActive();
207
	}
208
209
	/**
210
	 * Processes data written by the running processes.
211
	 *
212
	 * @param array $data
213
	 *
214
	 * @return
215
	 */
216
	private function processData($data) {
217
		$this->linesActive = [];
218
		$this->linesOpen = [];
219
		$this->linesUnknown = [];
220
		$this->linesTerm = [];
221
		$this->pushConn = 0;
222
		$this->activeConn = [];
223
		$this->activeHosts = [];
224
		$this->activeUsers = [];
225
		$this->activeDevices = [];
226
227
		if (!is_array($data)) {
0 ignored issues
show
The condition is_array($data) is always true.
Loading history...
228
			return;
229
		}
230
231
		foreach ($data as $devid => $users) {
232
			foreach ($users as $user => $pids) {
233
				foreach ($pids as $pid => $line) {
234
					if (!is_array($line)) {
235
						continue;
236
					}
237
238
					$line['command'] = Utils::GetCommandFromCode($line['command']);
239
240
					if ($line["ended"] == 0) {
241
						$this->activeDevices[$devid] = 1;
242
						$this->activeUsers[$user] = 1;
243
						$this->activeConn[$pid] = 1;
244
						$this->activeHosts[$line['ip']] = 1;
245
246
						$line["time"] = $this->currenttime - $line['start'];
247
						if ($line['push'] === true) {
248
							++$this->pushConn;
249
						}
250
251
						// ignore push connections
252
						if ($line['push'] === true && !$this->showPush) {
253
							continue;
254
						}
255
256
						if ($this->filter !== false) {
257
							$f = $this->filter;
258
							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

258
							if (!($line["pid"] == $f || $line["ip"] == $f || strtolower($line['command']) == strtolower(/** @scrutinizer ignore-type */ $f) || preg_match("/.*?{$f}.*?/i", $line['user']) ||
Loading history...
259
								preg_match("/.*?{$f}.*?/i", $line['devagent']) || preg_match("/.*?{$f}.*?/i", $line['devid']) || preg_match("/.*?{$f}.*?/i", $line['addinfo']))) {
260
								continue;
261
							}
262
						}
263
264
						$lastUpdate = $this->currenttime - $line["update"];
265
						if ($this->currenttime - $line["update"] < 2) {
266
							$this->linesActive[$line["update"] . $line["pid"]] = $line;
267
						}
268
						elseif (($line['push'] === true && $lastUpdate > ($this->pingInterval + 2)) || ($line['push'] !== true && $lastUpdate > 4)) {
269
							$this->linesUnknown[$line["update"] . $line["pid"]] = $line;
270
						}
271
						else {
272
							$this->linesOpen[$line["update"] . $line["pid"]] = $line;
273
						}
274
					}
275
					else {
276
						// do not show terminated + expired connections
277
						if ($this->currenttime > $line['ended'] + $this->showTermSec) {
278
							continue;
279
						}
280
281
						if ($this->filter !== false) {
282
							$f = $this->filter;
283
							if (
284
									!(
285
										$line['pid'] == $f ||
286
										$line['ip'] == $f ||
287
										strtolower($line['command']) == strtolower($f) ||
288
										preg_match("/.*?{$f}.*?/i", $line['user']) ||
289
										preg_match("/.*?{$f}.*?/i", $line['devagent']) ||
290
										preg_match("/.*?{$f}.*?/i", $line['devid']) ||
291
										preg_match("/.*?{$f}.*?/i", $line['addinfo'])
292
									)) {
293
								continue;
294
							}
295
						}
296
297
						$line['time'] = $line['ended'] - $line['start'];
298
						$this->linesTerm[$line['update'] . $line['pid']] = $line;
299
					}
300
				}
301
			}
302
		}
303
304
		// sort by execution time
305
		krsort($this->linesActive);
306
		krsort($this->linesOpen);
307
		krsort($this->linesUnknown);
308
		krsort($this->linesTerm);
309
	}
310
311
	/**
312
	 * Prints data to the terminal.
313
	 *
314
	 * @return
315
	 */
316
	private function scrOverview() {
317
		$linesAvail = $this->scrSize['height'] - 8;
318
		$lc = 1;
319
		$this->scrPrintAt($lc, 0, "\033[1mgrommunio-sync-top live statistics\033[0m\t\t\t\t\t" . @strftime("%d/%m/%Y %T") . "\n");
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

319
		$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");
Loading history...
320
		++$lc;
321
322
		$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()));
323
		++$lc;
324
		$this->scrPrintAt($lc, 0, sprintf("Push connections: %d\t\t\t\tDevices: %d\tPHP-MAPI: %s", $this->pushConn, count($this->activeDevices), phpversion("mapi")));
325
		++$lc;
326
		$this->scrPrintAt($lc, 0, sprintf("                                                Hosts:\t %d", count($this->activeHosts)));
327
		++$lc;
328
		++$lc;
329
330
		$this->scrPrintAt($lc, 0, "\033[4m" . $this->getLine(['pid' => 'PID', 'ip' => 'IP', 'user' => 'USER', 'command' => 'COMMAND', 'time' => 'TIME', 'devagent' => 'AGENT', 'devid' => 'DEVID', 'addinfo' => 'Additional Information']) . str_repeat(" ", 20) . "\033[0m");
331
		++$lc;
332
333
		// print help text if requested
334
		$hl = 0;
335
		if ($this->helpexpire > $this->currenttime) {
336
			$help = $this->scrHelp();
337
			$linesAvail -= count($help);
338
			$hl = $this->scrSize['height'] - count($help) - 1;
339
			foreach ($help as $h) {
340
				$this->scrPrintAt($hl, 0, $h);
341
				++$hl;
342
			}
343
		}
344
345
		$toPrintActive = $linesAvail;
346
		$toPrintOpen = $linesAvail;
347
		$toPrintUnknown = $linesAvail;
348
		$toPrintTerm = $linesAvail;
349
350
		// default view: show all unknown, no terminated and half active+open
351
		if (count($this->linesActive) + count($this->linesOpen) + count($this->linesUnknown) > $linesAvail) {
352
			$toPrintUnknown = count($this->linesUnknown);
353
			$toPrintActive = count($this->linesActive);
354
			$toPrintOpen = $linesAvail - $toPrintUnknown - $toPrintActive;
355
			$toPrintTerm = 0;
356
		}
357
358
		if ($this->showOption == self::SHOW_ACTIVE_ONLY) {
359
			$toPrintActive = $linesAvail;
360
			$toPrintOpen = 0;
361
			$toPrintUnknown = 0;
362
			$toPrintTerm = 0;
363
		}
364
365
		if ($this->showOption == self::SHOW_UNKNOWN_ONLY) {
366
			$toPrintActive = 0;
367
			$toPrintOpen = 0;
368
			$toPrintUnknown = $linesAvail;
369
			$toPrintTerm = 0;
370
		}
371
372
		$linesprinted = 0;
373
		foreach ($this->linesActive as $time => $l) {
374
			if ($linesprinted >= $toPrintActive) {
375
				break;
376
			}
377
378
			$this->scrPrintAt($lc, 0, "\033[01m" . $this->getLine($l) . "\033[0m");
379
			++$lc;
380
			++$linesprinted;
381
		}
382
383
		$linesprinted = 0;
384
		foreach ($this->linesOpen as $time => $l) {
385
			if ($linesprinted >= $toPrintOpen) {
386
				break;
387
			}
388
389
			$this->scrPrintAt($lc, 0, $this->getLine($l));
390
			++$lc;
391
			++$linesprinted;
392
		}
393
394
		$linesprinted = 0;
395
		foreach ($this->linesUnknown as $time => $l) {
396
			if ($linesprinted >= $toPrintUnknown) {
397
				break;
398
			}
399
400
			$color = "0;31m";
401
			if (!isset($l['start'])) {
402
				$l['start'] = $time;
403
			}
404
			if ((!isset($l['push']) || $l['push'] == false) && $time - $l["start"] > 30) {
405
				$color = "1;31m";
406
			}
407
			$this->scrPrintAt($lc, 0, "\033[0" . $color . $this->getLine($l) . "\033[0m");
408
			++$lc;
409
			++$linesprinted;
410
		}
411
412
		if ($toPrintTerm > 0) {
413
			$toPrintTerm = $linesAvail - $lc + 6;
414
		}
415
416
		$linesprinted = 0;
417
		foreach ($this->linesTerm as $time => $l) {
418
			if ($linesprinted >= $toPrintTerm) {
419
				break;
420
			}
421
422
			$this->scrPrintAt($lc, 0, "\033[01;30m" . $this->getLine($l) . "\033[0m");
423
			++$lc;
424
			++$linesprinted;
425
		}
426
427
		// add the lines used when displaying the help text
428
		$lc += $hl;
429
		$this->scrPrintAt($lc, 0, "\033[K");
430
		++$lc;
431
		$this->scrPrintAt($lc, 0, "Colorscheme: \033[01mActive  \033[0mOpen  \033[01;31mUnknown  \033[01;30mTerminated\033[0m");
432
433
		// remove old status
434
		if ($this->statusexpire < $this->currenttime) {
435
			$this->status = false;
436
		}
437
438
		// show request information and help command
439
		if ($this->starttime + 6 > $this->currenttime) {
440
			$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";
441
			$this->statusexpire = $this->currenttime + 1;
442
		}
443
444
		$str = "";
445
		if (!$this->showPush) {
446
			$str .= "\033[00;32mPush: \033[01;32mNo\033[0m   ";
447
		}
448
449
		if ($this->showOption == self::SHOW_ACTIVE_ONLY) {
450
			$str .= "\033[01;32mActive only\033[0m   ";
451
		}
452
453
		if ($this->showOption == self::SHOW_UNKNOWN_ONLY) {
454
			$str .= "\033[01;32mUnknown only\033[0m   ";
455
		}
456
457
		if ($this->showTermSec != self::SHOW_TERM_DEFAULT_TIME) {
458
			$str .= "\033[01;32mTerminated: " . $this->showTermSec . "s\033[0m   ";
459
		}
460
461
		if ($this->filter !== false || ($this->status !== false && $this->statusexpire > $this->currenttime)) {
462
			// print filter in green
463
			if ($this->filter !== false) {
464
				$str .= "\033[00;32mFilter: \033[01;32m{$this->filter}\033[0m   ";
465
			}
466
			// print status in red
467
			if ($this->status !== false) {
468
				$str .= "\033[00;31m{$this->status}\033[0m";
469
			}
470
		}
471
		$this->scrPrintAt(5, 0, $str);
472
473
		$this->scrPrintAt(4, 0, "Action: \033[01m" . $this->action . "\033[0m");
474
	}
475
476
	/**
477
	 * Waits for a keystroke and processes the requested command.
478
	 *
479
	 * @return
480
	 */
481
	private function readLineProcess() {
482
		$ans = explode("^^", `bash -c "read -n 1 -t 1 ANS ; echo \\\$?^^\\\$ANS;"`);
483
484
		if ($ans[0] < 128) {
485
			if (isset($ans[1]) && bin2hex(trim($ans[1])) == "7f") {
486
				$this->action = substr($this->action, 0, -1);
487
			}
488
489
			if (isset($ans[1]) && $ans[1] != "") {
490
				$this->action .= trim(preg_replace("/[^A-Za-z0-9:]/", "", $ans[1]));
491
			}
492
493
			if (bin2hex($ans[0]) == "30" && bin2hex($ans[1]) == "0a") {
494
				$cmds = explode(':', $this->action);
495
				if ($cmds[0] == "quit" || $cmds[0] == "q" || (isset($cmds[1]) && $cmds[0] == "" && $cmds[1] == "q")) {
496
					$this->topCollector->CollectData(true);
497
					$this->topCollector->ClearLatest(true);
498
499
					$this->terminate = true;
500
				}
501
				elseif ($cmds[0] == "clear") {
502
					$this->topCollector->ClearLatest(true);
503
					$this->topCollector->CollectData(true);
504
					$this->topCollector->ReInitIPC();
505
				}
506
				elseif ($cmds[0] == "filter" || $cmds[0] == "f") {
507
					if (!isset($cmds[1]) || $cmds[1] == "") {
508
						$this->filter = false;
509
						$this->status = "No filter";
510
						$this->statusexpire = $this->currenttime + 5;
511
					}
512
					else {
513
						$this->filter = $cmds[1];
514
						$this->status = false;
515
					}
516
				}
517
				elseif ($cmds[0] == "option" || $cmds[0] == "o") {
518
					if (!isset($cmds[1]) || $cmds[1] == "") {
519
						$this->status = "Option value needs to be specified. See 'help' or 'h' for instructions";
520
						$this->statusexpire = $this->currenttime + 5;
521
					}
522
					elseif ($cmds[1] == "p" || $cmds[1] == "push" || $cmds[1] == "ping") {
523
						$this->showPush = !$this->showPush;
524
					}
525
					elseif ($cmds[1] == "a" || $cmds[1] == "active") {
526
						$this->showOption = self::SHOW_ACTIVE_ONLY;
527
					}
528
					elseif ($cmds[1] == "u" || $cmds[1] == "unknown") {
529
						$this->showOption = self::SHOW_UNKNOWN_ONLY;
530
					}
531
					elseif ($cmds[1] == "d" || $cmds[1] == "default") {
532
						$this->showOption = self::SHOW_DEFAULT;
533
						$this->showTermSec = self::SHOW_TERM_DEFAULT_TIME;
534
						$this->showPush = true;
535
					}
536
					elseif (is_numeric($cmds[1])) {
537
						$this->showTermSec = $cmds[1];
538
					}
539
					else {
540
						$this->status = sprintf("Option '%s' unknown", $cmds[1]);
541
						$this->statusexpire = $this->currenttime + 5;
542
					}
543
				}
544
				elseif ($cmds[0] == "reset" || $cmds[0] == "r") {
545
					$this->filter = false;
546
					$this->wide = false;
547
					$this->helpexpire = 0;
548
					$this->status = "reset";
549
					$this->statusexpire = $this->currenttime + 2;
550
				}
551
				// enable/disable wide view
552
				elseif ($cmds[0] == "wide" || $cmds[0] == "w") {
553
					$this->wide = !$this->wide;
554
					$this->status = ($this->wide) ? "w i d e  view" : "normal view";
555
					$this->statusexpire = $this->currenttime + 2;
556
				}
557
				elseif ($cmds[0] == "help" || $cmds[0] == "h") {
558
					$this->helpexpire = $this->currenttime + 20;
559
				}
560
				// grep the log file
561
				elseif (($cmds[0] == "log" || $cmds[0] == "l") && isset($cmds[1])) {
562
					if (!file_exists(LOGFILE)) {
563
						$this->status = "Logfile can not be found: " . LOGFILE;
564
					}
565
					else {
566
						system('bash -c "fgrep -a ' . escapeshellarg($cmds[1]) . ' ' . LOGFILE . ' | less +G" > `tty`');
567
						$this->status = "Returning from log, updating data";
568
					}
569
					$this->statusexpire = time() + 5; // it might be much "later" now
570
				}
571
				// tail the log file
572
				elseif (($cmds[0] == "tail" || $cmds[0] == "t")) {
573
					if (!file_exists(LOGFILE)) {
574
						$this->status = "Logfile can not be found: " . LOGFILE;
575
					}
576
					else {
577
						$this->doingTail = true;
578
						$this->scrClear();
579
						$this->scrPrintAt(1, 0, $this->scrAsBold("Press CTRL+C to return to grommunio-sync-top\n\n"));
580
						$secondary = "";
581
						if (isset($cmds[1])) {
582
							$secondary = " -n 200 | grep " . escapeshellarg($cmds[1]);
583
						}
584
						system('bash -c "tail -f ' . LOGFILE . $secondary . '" > `tty`');
585
						$this->doingTail = false;
586
						$this->status = "Returning from tail, updating data";
587
					}
588
					$this->statusexpire = time() + 5; // it might be much "later" now
589
				}
590
				// tail the error log file
591
				elseif (($cmds[0] == "error" || $cmds[0] == "e")) {
592
					if (!file_exists(LOGERRORFILE)) {
593
						$this->status = "Error logfile can not be found: " . LOGERRORFILE;
594
					}
595
					else {
596
						$this->doingTail = true;
597
						$this->scrClear();
598
						$this->scrPrintAt(1, 0, $this->scrAsBold("Press CTRL+C to return to grommunio-sync-top\n\n"));
599
						$secondary = "";
600
						if (isset($cmds[1])) {
601
							$secondary = " -n 200 | grep " . escapeshellarg($cmds[1]);
602
						}
603
						system('bash -c "tail -f ' . LOGERRORFILE . $secondary . '" > `tty`');
604
						$this->doingTail = false;
605
						$this->status = "Returning from tail, updating data";
606
					}
607
					$this->statusexpire = time() + 5; // it might be much "later" now
608
				}
609
				elseif ($cmds[0] != "") {
610
					$this->status = sprintf("Command '%s' unknown", $cmds[0]);
611
					$this->statusexpire = $this->currenttime + 8;
612
				}
613
				$this->action = "";
614
			}
615
		}
616
	}
617
618
	/**
619
	 * Signal handler function.
620
	 *
621
	 * @param int $signo signal number
622
	 *
623
	 * @return
624
	 */
625
	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

625
	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...
626
		// don't terminate if the signal was sent by terminating tail
627
		if (!$this->doingTail) {
628
			$this->topCollector->CollectData(true);
629
			$this->topCollector->ClearLatest(true);
630
			$this->terminate = true;
631
		}
632
	}
633
634
	/**
635
	 * Returns usage instructions.
636
	 *
637
	 * @return string
638
	 */
639
	public function UsageInstructions() {
640
		$help = "Usage:\n\tgrommunio-sync-top.php\n\n" .
641
				"  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" .
642
				"  When grommunio-sync-top is running you can specify certain actions and options which can be executed (listed below).\n" .
643
				"  This help information can also be shown inside grommunio-sync-top by hitting 'help' or 'h'.\n\n";
644
		$scrhelp = $this->scrHelp();
645
		unset($scrhelp[0]);
646
647
		$help .= implode("\n", $scrhelp);
648
		$help .= "\n\n";
649
650
		return $help;
651
	}
652
653
	/**
654
	 * Prints a 'help' text at the end of the page.
655
	 *
656
	 * @return array with help lines
657
	 */
658
	private function scrHelp() {
659
		$h = [];
660
		$secs = $this->helpexpire - $this->currenttime;
661
		$h[] = "Actions supported by grommunio-sync-top (help page still displayed for " . $secs . "secs)";
662
		$h[] = "  " . $this->scrAsBold("Action") . "\t\t" . $this->scrAsBold("Comment");
663
		$h[] = "  " . $this->scrAsBold("h") . " or " . $this->scrAsBold("help") . "\t\tDisplays this information.";
664
		$h[] = "  " . $this->scrAsBold("q") . ", " . $this->scrAsBold("quit") . " or " . $this->scrAsBold(":q") . "\t\tExits grommunio-sync-top.";
665
		$h[] = "  " . $this->scrAsBold("w") . " or " . $this->scrAsBold("wide") . "\t\tTries not to truncate data. Automatically done if more than 180 columns available.";
666
		$h[] = "  " . $this->scrAsBold("f:VAL") . " or " . $this->scrAsBold("filter:VAL") . "\tOnly display connections which contain VAL. This value is case-insensitive.";
667
		$h[] = "  " . $this->scrAsBold("f:") . " or " . $this->scrAsBold("filter:") . "\t\tWithout a search word: resets the filter.";
668
		$h[] = "  " . $this->scrAsBold("l:STR") . " or " . $this->scrAsBold("log:STR") . "\tIssues 'less +G' on the logfile, after grepping on the optional STR.";
669
		$h[] = "  " . $this->scrAsBold("t:STR") . " or " . $this->scrAsBold("tail:STR") . "\tIssues 'tail -f' on the logfile, grepping for optional STR.";
670
		$h[] = "  " . $this->scrAsBold("e:STR") . " or " . $this->scrAsBold("error:STR") . "\tIssues 'tail -f' on the error logfile, grepping for optional STR.";
671
		$h[] = "  " . $this->scrAsBold("r") . " or " . $this->scrAsBold("reset") . "\t\tResets 'wide' or 'filter'.";
672
		$h[] = "  " . $this->scrAsBold("o:") . " or " . $this->scrAsBold("option:") . "\t\tSets display options. Valid options specified below";
673
		$h[] = "  " . $this->scrAsBold("  p") . " or " . $this->scrAsBold("push") . "\t\tLists/not lists active and open push connections.";
674
		$h[] = "  " . $this->scrAsBold("  a") . " or " . $this->scrAsBold("action") . "\t\tLists only active connections.";
675
		$h[] = "  " . $this->scrAsBold("  u") . " or " . $this->scrAsBold("unknown") . "\tLists only unknown connections.";
676
		$h[] = "  " . $this->scrAsBold("  10") . " or " . $this->scrAsBold("20") . "\t\tLists terminated connections for 10 or 20 seconds. Any other number can be used.";
677
		$h[] = "  " . $this->scrAsBold("  d") . " or " . $this->scrAsBold("default") . "\tUses default options";
678
679
		return $h;
680
	}
681
682
	/**
683
	 * Encapsulates string with different color escape characters.
684
	 *
685
	 * @param string $text
686
	 *
687
	 * @return string same text as bold
688
	 */
689
	private function scrAsBold($text) {
690
		return "\033[01m" . $text . "\033[0m";
691
	}
692
693
	/**
694
	 * Prints one line of precessed data.
695
	 *
696
	 * @param array $l line information
697
	 *
698
	 * @return string
699
	 */
700
	private function getLine($l) {
701
		if ($this->wide === true) {
702
			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']);
703
		}
704
705
		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']);
706
	}
707
708
	/**
709
	 * Pads and trims string.
710
	 *
711
	 * @param string $str       to be trimmed/padded
712
	 * @param int    $size      characters to be considered
713
	 * @param bool   $cutmiddle (optional) indicates where to long information should
714
	 *                          be trimmed of, false means at the end
715
	 *
716
	 * @return string
717
	 */
718
	private function ptStr($str, $size, $cutmiddle = false) {
719
		if (strlen($str) < $size) {
720
			return str_pad($str, $size);
721
		}
722
		if ($cutmiddle === true) {
723
			$cut = ($size - 2) / 2;
724
725
			return $this->ptStr(substr($str, 0, $cut) . ".." . substr($str, (-1) * ($cut - 1)), $size);
726
		}
727
728
		return substr($str, 0, $size - 3) . ".. ";
729
	}
730
731
	/**
732
	 * Tries to discover the size of the current terminal.
733
	 *
734
	 * @return array 'width' and 'height' as keys
735
	 */
736
	private function scrGetSize() {
737
		$tty = strtolower(exec('stty -a | fgrep columns'));
738
		if (preg_match_all("/rows.([0-9]+);.columns.([0-9]+);/", $tty, $output) ||
739
			preg_match_all("/([0-9]+).rows;.([0-9]+).columns;/", $tty, $output)) {
740
			return ['width' => $output[2][0], 'height' => $output[1][0]];
741
		}
742
743
		return ['width' => 80, 'height' => 24];
744
	}
745
746
	/**
747
	 * Returns the version of the current grommunio-sync installation.
748
	 *
749
	 * @return string
750
	 */
751
	private function getVersion() {
752
		return GROMMUNIOSYNC_VERSION;
753
	}
754
755
	/**
756
	 * Converts seconds in MM:SS.
757
	 *
758
	 * @param int $s seconds
759
	 *
760
	 * @return string
761
	 */
762
	private function sec2min($s) {
763
		if (!is_int($s)) {
0 ignored issues
show
The condition is_int($s) is always true.
Loading history...
764
			return $s;
765
		}
766
767
		return sprintf("%02.2d:%02.2d", floor($s / 60), $s % 60);
768
	}
769
770
	/**
771
	 * Resets the default colors of the terminal.
772
	 *
773
	 * @return
774
	 */
775
	private function scrDefaultColors() {
776
		echo "\033[0m";
777
	}
778
779
	/**
780
	 * Clears screen of the terminal.
781
	 *
782
	 * @param array $data
783
	 *
784
	 * @return
785
	 */
786
	public function scrClear() {
787
		echo "\033[2J";
788
	}
789
790
	/**
791
	 * Prints a text at a specific screen/terminal coordinates.
792
	 *
793
	 * @param int    $row  row number
794
	 * @param int    $col  column number
795
	 * @param string $text to be printed
796
	 *
797
	 * @return
798
	 */
799
	private function scrPrintAt($row, $col, $text = "") {
800
		echo "\033[" . $row . ";" . $col . "H" . $text;
801
	}
802
}
803