1#!/usr/bin/env php 2<?php 3/*********************************************** 4* File : z-push-top.php 5* Project : Z-Push 6* Descr : Shows realtime information about 7* connected devices and active 8* connections in a top-style format. 9* 10* Created : 07.09.2011 11* 12* Copyright 2007 - 2016 Zarafa Deutschland GmbH 13* 14* This program is free software: you can redistribute it and/or modify 15* it under the terms of the GNU Affero General Public License, version 3, 16* as published by the Free Software Foundation. 17* 18* This program is distributed in the hope that it will be useful, 19* but WITHOUT ANY WARRANTY; without even the implied warranty of 20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21* GNU Affero General Public License for more details. 22* 23* You should have received a copy of the GNU Affero General Public License 24* along with this program. If not, see <http://www.gnu.org/licenses/>. 25* 26* Consult LICENSE file for details 27************************************************/ 28 29require_once 'vendor/autoload.php'; 30 31/************************************************ 32 * MAIN 33 */ 34 declare(ticks = 1); 35 define('BASE_PATH_CLI', dirname(__FILE__) ."/"); 36 set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH_CLI); 37 38 if (!defined('ZPUSH_CONFIG')) define('ZPUSH_CONFIG', BASE_PATH_CLI . 'config.php'); 39 include_once(ZPUSH_CONFIG); 40 41 try { 42 ZPush::CheckConfig(); 43 if (!function_exists("pcntl_signal")) 44 throw new FatalException("Function pcntl_signal() is not available. Please install package 'php5-pcntl' (or similar) on your system."); 45 46 if (php_sapi_name() != "cli") 47 throw new FatalException("This script can only be called from the CLI."); 48 49 $zpt = new ZPushTop(); 50 51 // check if help was requested from CLI 52 if (in_array('-h', $argv) || in_array('--help', $argv)) { 53 echo $zpt->UsageInstructions(); 54 exit(0); 55 } 56 57 if ($zpt->IsAvailable()) { 58 pcntl_signal(SIGINT, array($zpt, "SignalHandler")); 59 $zpt->run(); 60 $zpt->scrClear(); 61 system("stty sane"); 62 } 63 else 64 echo "Z-Push interprocess communication (IPC) is not available or TopCollector is disabled.\n"; 65 } 66 catch (ZPushException $zpe) { 67 fwrite(STDERR, get_class($zpe) . ": ". $zpe->getMessage() . "\n"); 68 exit(1); 69 } 70 71 echo "terminated\n"; 72 73 74/************************************************ 75 * Z-Push-Top 76 */ 77class ZPushTop { 78 // show options 79 const SHOW_DEFAULT = 0; 80 const SHOW_ACTIVE_ONLY = 1; 81 const SHOW_UNKNOWN_ONLY = 2; 82 const SHOW_TERM_DEFAULT_TIME = 5; // 5 secs 83 84 private $topCollector; 85 private $starttime; 86 private $action; 87 private $filter; 88 private $status; 89 private $statusexpire; 90 private $wide; 91 private $wasEnabled; 92 private $terminate; 93 private $scrSize; 94 private $pingInterval; 95 private $showPush; 96 private $showTermSec; 97 98 private $linesActive = array(); 99 private $linesOpen = array(); 100 private $linesUnknown = array(); 101 private $linesTerm = array(); 102 private $pushConn = 0; 103 private $activeConn = array(); 104 private $activeHosts = array(); 105 private $activeUsers = array(); 106 private $activeDevices = array(); 107 108 /** 109 * Constructor 110 * 111 * @access public 112 */ 113 public function __construct() { 114 $this->starttime = time(); 115 $this->currenttime = time(); 116 $this->action = ""; 117 $this->filter = false; 118 $this->status = false; 119 $this->statusexpire = 0; 120 $this->helpexpire = 0; 121 $this->doingTail = false; 122 $this->wide = false; 123 $this->terminate = false; 124 $this->showPush = true; 125 $this->showOption = self::SHOW_DEFAULT; 126 $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME; 127 $this->scrSize = array('width' => 80, 'height' => 24); 128 $this->pingInterval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 12; 129 130 // get a TopCollector 131 $this->topCollector = new TopCollector(); 132 } 133 134 /** 135 * Requests data from the running Z-Push processes 136 * 137 * @access private 138 * @return 139 */ 140 private function initialize() { 141 // request feedback from active processes 142 $this->wasEnabled = $this->topCollector->CollectData(); 143 144 // remove obsolete data 145 $this->topCollector->ClearLatest(true); 146 147 // start with default colours 148 $this->scrDefaultColors(); 149 } 150 151 /** 152 * Main loop of Z-Push-top 153 * Runs until termination is requested 154 * 155 * @access public 156 * @return 157 */ 158 public function run() { 159 $this->initialize(); 160 161 do { 162 $this->currenttime = time(); 163 164 // see if shared memory is active 165 if (!$this->IsAvailable()) 166 $this->terminate = true; 167 168 // active processes should continue sending data 169 $this->topCollector->CollectData(); 170 171 // get and process data from processes 172 $this->topCollector->ClearLatest(); 173 $topdata = $this->topCollector->ReadLatest(); 174 $this->processData($topdata); 175 176 // clear screen 177 $this->scrClear(); 178 179 // check if screen size changed 180 $s = $this->scrGetSize(); 181 if ($this->scrSize['width'] != $s['width']) { 182 if ($s['width'] > 180) 183 $this->wide = true; 184 else 185 $this->wide = false; 186 } 187 $this->scrSize = $s; 188 189 // print overview 190 $this->scrOverview(); 191 192 // wait for user input 193 $this->readLineProcess(); 194 } 195 while($this->terminate != true); 196 } 197 198 /** 199 * Indicates if TopCollector is available collecting data 200 * 201 * @access public 202 * @return boolean 203 */ 204 public function IsAvailable() { 205 if (defined('TOPCOLLECTOR_DISABLED') && constant('TOPCOLLECTOR_DISABLED') === true) { 206 return false; 207 } 208 return $this->topCollector->IsActive(); 209 } 210 211 /** 212 * Processes data written by the running processes 213 * 214 * @param array $data 215 * 216 * @access private 217 * @return 218 */ 219 private function processData($data) { 220 $this->linesActive = array(); 221 $this->linesOpen = array(); 222 $this->linesUnknown = array(); 223 $this->linesTerm = array(); 224 $this->pushConn = 0; 225 $this->activeConn = array(); 226 $this->activeHosts = array(); 227 $this->activeUsers = array(); 228 $this->activeDevices = array(); 229 230 if (!is_array($data)) 231 return; 232 233 foreach ($data as $devid=>$users) { 234 foreach ($users as $user=>$pids) { 235 foreach ($pids as $pid=>$line) { 236 if (!is_array($line)) 237 continue; 238 239 $line['command'] = Utils::GetCommandFromCode($line['command']); 240 241 if ($line["ended"] == 0) { 242 $this->activeDevices[$devid] = 1; 243 $this->activeUsers[$user] = 1; 244 $this->activeConn[$pid] = 1; 245 $this->activeHosts[$line['ip']] = 1; 246 247 $line["time"] = $this->currenttime - $line['start']; 248 if ($line['push'] === true) $this->pushConn += 1; 249 250 // ignore push connections 251 if ($line['push'] === true && ! $this->showPush) 252 continue; 253 254 if ($this->filter !== false) { 255 $f = $this->filter; 256 if (!($line["pid"] == $f || $line["ip"] == $f || strtolower($line['command']) == strtolower($f) || preg_match("/.*?$f.*?/i", $line['user']) || 257 preg_match("/.*?$f.*?/i", $line['devagent']) || preg_match("/.*?$f.*?/i", $line['devid']) || preg_match("/.*?$f.*?/i", $line['addinfo']) )) 258 continue; 259 } 260 261 $lastUpdate = $this->currenttime - $line["update"]; 262 if ($this->currenttime - $line["update"] < 2) 263 $this->linesActive[$line["update"].$line["pid"]] = $line; 264 else if (($line['push'] === true && $lastUpdate > ($this->pingInterval+2)) || ($line['push'] !== true && $lastUpdate > 4)) 265 $this->linesUnknown[$line["update"].$line["pid"]] = $line; 266 else 267 $this->linesOpen[$line["update"].$line["pid"]] = $line; 268 } 269 else { 270 // do not show terminated + expired connections 271 if ($line['ended'] + $this->showTermSec < $this->currenttime) 272 continue; 273 274 if ($this->filter !== false) { 275 $f = $this->filter; 276 if (!($line['pid'] == $f || $line['ip'] == $f || strtolower($line['command']) == strtolower($f) || preg_match("/.*?$f.*?/i", $line['user']) || 277 preg_match("/.*?$f.*?/i", $line['devagent']) || preg_match("/.*?$f.*?/i", $line['devid']) || preg_match("/.*?$f.*?/i", $line['addinfo']) )) 278 continue; 279 } 280 281 $line['time'] = $line['ended'] - $line['start']; 282 $this->linesTerm[$line['update'].$line['pid']] = $line; 283 } 284 } 285 } 286 } 287 288 // sort by execution time 289 krsort($this->linesActive); 290 krsort($this->linesOpen); 291 krsort($this->linesUnknown); 292 krsort($this->linesTerm); 293 } 294 295 /** 296 * Prints data to the terminal 297 * 298 * @access private 299 * @return 300 */ 301 private function scrOverview() { 302 $linesAvail = $this->scrSize['height'] - 8; 303 $lc = 1; 304 $this->scrPrintAt($lc,0, "\033[1mZ-Push top live statistics\033[0m\t\t\t\t\t". @strftime("%d/%m/%Y %T")."\n"); $lc++; 305 306 $this->scrPrintAt($lc,0, sprintf("Open connections: %d\t\t\t\tUsers:\t %d\tZ-Push: %s ",count($this->activeConn),count($this->activeUsers), $this->getVersion())); $lc++; 307 $this->scrPrintAt($lc,0, sprintf("Push connections: %d\t\t\t\tDevices: %d\tPHP-MAPI: %s", $this->pushConn, count($this->activeDevices),phpversion("mapi"))); $lc++; 308 $this->scrPrintAt($lc,0, sprintf(" Hosts:\t %d", count($this->activeHosts))); $lc++; 309 $lc++; 310 311 $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++; 312 313 // print help text if requested 314 $hl = 0; 315 if ($this->helpexpire > $this->currenttime) { 316 $help = $this->scrHelp(); 317 $linesAvail -= count($help); 318 $hl = $this->scrSize['height'] - count($help) -1; 319 foreach ($help as $h) { 320 $this->scrPrintAt($hl,0, $h); 321 $hl++; 322 } 323 } 324 325 $toPrintActive = $linesAvail; 326 $toPrintOpen = $linesAvail; 327 $toPrintUnknown = $linesAvail; 328 $toPrintTerm = $linesAvail; 329 330 // default view: show all unknown, no terminated and half active+open 331 if (count($this->linesActive) + count($this->linesOpen) + count($this->linesUnknown) > $linesAvail) { 332 $toPrintUnknown = count($this->linesUnknown); 333 $toPrintActive = count($this->linesActive); 334 $toPrintOpen = $linesAvail-$toPrintUnknown-$toPrintActive; 335 $toPrintTerm = 0; 336 } 337 338 if ($this->showOption == self::SHOW_ACTIVE_ONLY) { 339 $toPrintActive = $linesAvail; 340 $toPrintOpen = 0; 341 $toPrintUnknown = 0; 342 $toPrintTerm = 0; 343 } 344 345 if ($this->showOption == self::SHOW_UNKNOWN_ONLY) { 346 $toPrintActive = 0; 347 $toPrintOpen = 0; 348 $toPrintUnknown = $linesAvail; 349 $toPrintTerm = 0; 350 } 351 352 $linesprinted = 0; 353 foreach ($this->linesActive as $time=>$l) { 354 if ($linesprinted >= $toPrintActive) 355 break; 356 357 $this->scrPrintAt($lc,0, "\033[01m" . $this->getLine($l) ."\033[0m"); 358 $lc++; 359 $linesprinted++; 360 } 361 362 $linesprinted = 0; 363 foreach ($this->linesOpen as $time=>$l) { 364 if ($linesprinted >= $toPrintOpen) 365 break; 366 367 $this->scrPrintAt($lc,0, $this->getLine($l)); 368 $lc++; 369 $linesprinted++; 370 } 371 372 $linesprinted = 0; 373 foreach ($this->linesUnknown as $time=>$l) { 374 if ($linesprinted >= $toPrintUnknown) 375 break; 376 377 $color = "0;31m"; 378 if ($l['push'] == false && $time - $l["start"] > 30) 379 $color = "1;31m"; 380 $this->scrPrintAt($lc,0, "\033[0". $color . $this->getLine($l) ."\033[0m"); 381 $lc++; 382 $linesprinted++; 383 } 384 385 if ($toPrintTerm > 0) 386 $toPrintTerm = $linesAvail - $lc +6; 387 388 $linesprinted = 0; 389 foreach ($this->linesTerm as $time=>$l){ 390 if ($linesprinted >= $toPrintTerm) 391 break; 392 393 $this->scrPrintAt($lc,0, "\033[01;30m" . $this->getLine($l) ."\033[0m"); 394 $lc++; 395 $linesprinted++; 396 } 397 398 // add the lines used when displaying the help text 399 $lc += $hl; 400 $this->scrPrintAt($lc,0, "\033[K"); $lc++; 401 $this->scrPrintAt($lc,0, "Colorscheme: \033[01mActive \033[0mOpen \033[01;31mUnknown \033[01;30mTerminated\033[0m"); 402 403 // remove old status 404 if ($this->statusexpire < $this->currenttime) 405 $this->status = false; 406 407 // show request information and help command 408 if ($this->starttime + 6 > $this->currenttime) { 409 $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"; 410 $this->statusexpire = $this->currenttime+1; 411 } 412 413 414 $str = ""; 415 if (! $this->showPush) 416 $str .= "\033[00;32mPush: \033[01;32mNo\033[0m "; 417 418 if ($this->showOption == self::SHOW_ACTIVE_ONLY) 419 $str .= "\033[01;32mActive only\033[0m "; 420 421 if ($this->showOption == self::SHOW_UNKNOWN_ONLY) 422 $str .= "\033[01;32mUnknown only\033[0m "; 423 424 if ($this->showTermSec != self::SHOW_TERM_DEFAULT_TIME) 425 $str .= "\033[01;32mTerminated: ". $this->showTermSec. "s\033[0m "; 426 427 if ($this->filter !== false || ($this->status !== false && $this->statusexpire > $this->currenttime)) { 428 // print filter in green 429 if ($this->filter !== false) 430 $str .= "\033[00;32mFilter: \033[01;32m$this->filter\033[0m "; 431 // print status in red 432 if ($this->status !== false) 433 $str .= "\033[00;31m$this->status\033[0m"; 434 } 435 $this->scrPrintAt(5,0, $str); 436 437 $this->scrPrintAt(4,0,"Action: \033[01m".$this->action . "\033[0m"); 438 } 439 440 /** 441 * Waits for a keystroke and processes the requested command 442 * 443 * @access private 444 * @return 445 */ 446 private function readLineProcess() { 447 $ans = explode("^^", `bash -c "read -n 1 -t 1 ANS ; echo \\\$?^^\\\$ANS;"`); 448 449 if ($ans[0] < 128) { 450 if (isset($ans[1]) && bin2hex(trim($ans[1])) == "7f") { 451 $this->action = substr($this->action,0,-1); 452 } 453 454 if (isset($ans[1]) && $ans[1] != "" ){ 455 $this->action .= trim(preg_replace("/[^A-Za-z0-9:]/","",$ans[1])); 456 } 457 458 if (bin2hex($ans[0]) == "30" && bin2hex($ans[1]) == "0a") { 459 $cmds = explode(':', $this->action); 460 if ($cmds[0] == "quit" || $cmds[0] == "q" || (isset($cmds[1]) && $cmds[0] == "" && $cmds[1] == "q")) { 461 $this->topCollector->CollectData(true); 462 $this->topCollector->ClearLatest(true); 463 464 $this->terminate = true; 465 } 466 else if ($cmds[0] == "clear" ) { 467 $this->topCollector->ClearLatest(true); 468 $this->topCollector->CollectData(true); 469 $this->topCollector->ReInitIPC(); 470 } 471 else if ($cmds[0] == "filter" || $cmds[0] == "f") { 472 if (!isset($cmds[1]) || $cmds[1] == "") { 473 $this->filter = false; 474 $this->status = "No filter"; 475 $this->statusexpire = $this->currenttime+5; 476 } 477 else { 478 $this->filter = $cmds[1]; 479 $this->status = false; 480 } 481 } 482 else if ($cmds[0] == "option" || $cmds[0] == "o") { 483 if (!isset($cmds[1]) || $cmds[1] == "") { 484 $this->status = "Option value needs to be specified. See 'help' or 'h' for instructions"; 485 $this->statusexpire = $this->currenttime+5; 486 } 487 else if ($cmds[1] == "p" || $cmds[1] == "push" || $cmds[1] == "ping") 488 $this->showPush = !$this->showPush; 489 else if ($cmds[1] == "a" || $cmds[1] == "active") 490 $this->showOption = self::SHOW_ACTIVE_ONLY; 491 else if ($cmds[1] == "u" || $cmds[1] == "unknown") 492 $this->showOption = self::SHOW_UNKNOWN_ONLY; 493 else if ($cmds[1] == "d" || $cmds[1] == "default") { 494 $this->showOption = self::SHOW_DEFAULT; 495 $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME; 496 $this->showPush = true; 497 } 498 else if (is_numeric($cmds[1])) 499 $this->showTermSec = $cmds[1]; 500 else { 501 $this->status = sprintf("Option '%s' unknown", $cmds[1]); 502 $this->statusexpire = $this->currenttime+5; 503 } 504 } 505 else if ($cmds[0] == "reset" || $cmds[0] == "r") { 506 $this->filter = false; 507 $this->wide = false; 508 $this->helpexpire = 0; 509 $this->status = "resetted"; 510 $this->statusexpire = $this->currenttime+2; 511 } 512 // enable/disable wide view 513 else if ($cmds[0] == "wide" || $cmds[0] == "w") { 514 $this->wide = ! $this->wide; 515 $this->status = ($this->wide)?"w i d e view" : "normal view"; 516 $this->statusexpire = $this->currenttime+2; 517 } 518 else if ($cmds[0] == "help" || $cmds[0] == "h") { 519 $this->helpexpire = $this->currenttime+20; 520 } 521 // grep the log file 522 else if (($cmds[0] == "log" || $cmds[0] == "l") && isset($cmds[1]) ) { 523 if (!file_exists(LOGFILE)) { 524 $this->status = "Logfile can not be found: ". LOGFILE; 525 } 526 else { 527 system('bash -c "fgrep -a '.escapeshellarg($cmds[1]).' '. LOGFILE .' | less +G" > `tty`'); 528 $this->status = "Returning from log, updating data"; 529 } 530 $this->statusexpire = time()+5; // it might be much "later" now 531 } 532 // tail the log file 533 else if (($cmds[0] == "tail" || $cmds[0] == "t")) { 534 if (!file_exists(LOGFILE)) { 535 $this->status = "Logfile can not be found: ". LOGFILE; 536 } 537 else { 538 $this->doingTail = true; 539 $this->scrClear(); 540 $this->scrPrintAt(1,0,$this->scrAsBold("Press CTRL+C to return to Z-Push-Top\n\n")); 541 $secondary = ""; 542 if (isset($cmds[1])) $secondary = " -n 200 | grep ".escapeshellarg($cmds[1]); 543 system('bash -c "tail -f '. LOGFILE . $secondary . '" > `tty`'); 544 $this->doingTail = false; 545 $this->status = "Returning from tail, updating data"; 546 } 547 $this->statusexpire = time()+5; // it might be much "later" now 548 } 549 // tail the error log file 550 else if (($cmds[0] == "error" || $cmds[0] == "e")) { 551 if (!file_exists(LOGERRORFILE)) { 552 $this->status = "Error logfile can not be found: ". LOGERRORFILE; 553 } 554 else { 555 $this->doingTail = true; 556 $this->scrClear(); 557 $this->scrPrintAt(1,0,$this->scrAsBold("Press CTRL+C to return to Z-Push-Top\n\n")); 558 $secondary = ""; 559 if (isset($cmds[1])) $secondary = " -n 200 | grep ".escapeshellarg($cmds[1]); 560 system('bash -c "tail -f '. LOGERRORFILE . $secondary . '" > `tty`'); 561 $this->doingTail = false; 562 $this->status = "Returning from tail, updating data"; 563 } 564 $this->statusexpire = time()+5; // it might be much "later" now 565 } 566 567 else if ($cmds[0] != "") { 568 $this->status = sprintf("Command '%s' unknown", $cmds[0]); 569 $this->statusexpire = $this->currenttime+8; 570 } 571 $this->action = ""; 572 } 573 } 574 } 575 576 /** 577 * Signal handler function 578 * 579 * @param int $signo signal number 580 * 581 * @access public 582 * @return 583 */ 584 public function SignalHandler($signo) { 585 // don't terminate if the signal was sent by terminating tail 586 if (!$this->doingTail) { 587 $this->topCollector->CollectData(true); 588 $this->topCollector->ClearLatest(true); 589 $this->terminate = true; 590 } 591 } 592 593 /** 594 * Returns usage instructions 595 * 596 * @return string 597 * @access public 598 */ 599 public function UsageInstructions() { 600 $help = "Usage:\n\tz-push-top.php\n\n" . 601 " Z-Push-Top is a live top-like overview of what Z-Push is doing. It does not have specific command line options.\n\n". 602 " When Z-Push-Top is running you can specify certain actions and options which can be executed (listed below).\n". 603 " This help information can also be shown inside Z-Push-Top by hitting 'help' or 'h'.\n\n"; 604 $scrhelp = $this->scrHelp(); 605 unset($scrhelp[0]); 606 607 $help .= implode("\n", $scrhelp); 608 $help .= "\n\n"; 609 return $help; 610 } 611 612 613 /** 614 * Prints a 'help' text at the end of the page 615 * 616 * @access private 617 * @return array with help lines 618 */ 619 private function scrHelp() { 620 $h = array(); 621 $secs = $this->helpexpire - $this->currenttime; 622 $h[] = "Actions supported by Z-Push-Top (help page still displayed for ".$secs."secs)"; 623 $h[] = " ".$this->scrAsBold("Action")."\t\t".$this->scrAsBold("Comment"); 624 $h[] = " ".$this->scrAsBold("h")." or ".$this->scrAsBold("help")."\t\tDisplays this information."; 625 $h[] = " ".$this->scrAsBold("q").", ".$this->scrAsBold("quit")." or ".$this->scrAsBold(":q")."\t\tExits Z-Push-Top."; 626 $h[] = " ".$this->scrAsBold("w")." or ".$this->scrAsBold("wide")."\t\tTries not to truncate data. Automatically done if more than 180 columns available."; 627 $h[] = " ".$this->scrAsBold("f:VAL")." or ".$this->scrAsBold("filter:VAL")."\tOnly display connections which contain VAL. This value is case-insensitive."; 628 $h[] = " ".$this->scrAsBold("f:")." or ".$this->scrAsBold("filter:")."\t\tWithout a search word: resets the filter."; 629 $h[] = " ".$this->scrAsBold("l:STR")." or ".$this->scrAsBold("log:STR")."\tIssues 'less +G' on the logfile, after grepping on the optional STR."; 630 $h[] = " ".$this->scrAsBold("t:STR")." or ".$this->scrAsBold("tail:STR")."\tIssues 'tail -f' on the logfile, grepping for optional STR."; 631 $h[] = " ".$this->scrAsBold("e:STR")." or ".$this->scrAsBold("error:STR")."\tIssues 'tail -f' on the error logfile, grepping for optional STR."; 632 $h[] = " ".$this->scrAsBold("r")." or ".$this->scrAsBold("reset")."\t\tResets 'wide' or 'filter'."; 633 $h[] = " ".$this->scrAsBold("o:")." or ".$this->scrAsBold("option:")."\t\tSets display options. Valid options specified below"; 634 $h[] = " ".$this->scrAsBold(" p")." or ".$this->scrAsBold("push")."\t\tLists/not lists active and open push connections."; 635 $h[] = " ".$this->scrAsBold(" a")." or ".$this->scrAsBold("action")."\t\tLists only active connections."; 636 $h[] = " ".$this->scrAsBold(" u")." or ".$this->scrAsBold("unknown")."\tLists only unknown connections."; 637 $h[] = " ".$this->scrAsBold(" 10")." or ".$this->scrAsBold("20")."\t\tLists terminated connections for 10 or 20 seconds. Any other number can be used."; 638 $h[] = " ".$this->scrAsBold(" d")." or ".$this->scrAsBold("default")."\tUses default options"; 639 640 return $h; 641 } 642 643 /** 644 * Encapsulates string with different color escape characters 645 * 646 * @param string $text 647 * 648 * @access private 649 * @return string same text as bold 650 */ 651 private function scrAsBold($text) { 652 return "\033[01m" . $text ."\033[0m"; 653 } 654 655 /** 656 * Prints one line of precessed data 657 * 658 * @param array $l line information 659 * 660 * @access private 661 * @return string 662 */ 663 private function getLine($l) { 664 if ($this->wide === true) 665 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']); 666 else 667 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']); 668 } 669 670 /** 671 * Pads and trims string 672 * 673 * @param string $str to be trimmed/padded 674 * @param int $size characters to be considered 675 * @param boolean $cutmiddle (optional) indicates where to long information should 676 * be trimmed of, false means at the end 677 * 678 * @access private 679 * @return string 680 */ 681 private function ptStr($str, $size, $cutmiddle = false) { 682 if (strlen($str) < $size) 683 return str_pad($str, $size); 684 else if ($cutmiddle == true) { 685 $cut = ($size-2)/2; 686 return $this->ptStr(substr($str,0,$cut) ."..". substr($str,(-1)*($cut-1)), $size); 687 } 688 else { 689 return substr($str,0,$size-3).".. "; 690 } 691 } 692 693 /** 694 * Tries to discover the size of the current terminal 695 * 696 * @access private 697 * @return array 'width' and 'height' as keys 698 */ 699 private function scrGetSize() { 700 $tty = strtolower(exec('stty -a | fgrep columns')); 701 if (preg_match_all("/rows.([0-9]+);.columns.([0-9]+);/", $tty, $output) || 702 preg_match_all("/([0-9]+).rows;.([0-9]+).columns;/", $tty, $output)) 703 return array('width' => $output[2][0], 'height' => $output[1][0]); 704 705 return array('width' => 80, 'height' => 24); 706 } 707 708 /** 709 * Returns the version of the current Z-Push installation 710 * 711 * @access private 712 * @return string 713 */ 714 private function getVersion() { 715 if (ZPUSH_VERSION == "SVN checkout" && file_exists(REAL_BASE_PATH.".svn/entries")) { 716 $svn = file(REAL_BASE_PATH.".svn/entries"); 717 return "SVN " . substr(trim($svn[4]),stripos($svn[4],"z-push")+7) ." r".trim($svn[3]); 718 } 719 return ZPUSH_VERSION; 720 } 721 722 /** 723 * Converts seconds in MM:SS 724 * 725 * @param int $s seconds 726 * 727 * @access private 728 * @return string 729 */ 730 private function sec2min($s) { 731 if (!is_int($s)) 732 return $s; 733 return sprintf("%02.2d:%02.2d", floor($s/60), $s%60); 734 } 735 736 /** 737 * Resets the default colors of the terminal 738 * 739 * @access private 740 * @return 741 */ 742 private function scrDefaultColors() { 743 echo "\033[0m"; 744 } 745 746 /** 747 * Clears screen of the terminal 748 * 749 * @param array $data 750 * 751 * @access private 752 * @return 753 */ 754 public function scrClear() { 755 echo "\033[2J"; 756 } 757 758 /** 759 * Prints a text at a specific screen/terminal coordinates 760 * 761 * @param int $row row number 762 * @param int $col column number 763 * @param string $text to be printed 764 * 765 * @access private 766 * @return 767 */ 768 private function scrPrintAt($row, $col, $text="") { 769 echo "\033[".$row.";".$col."H".$text; 770 } 771 772} 773