2024-09-07 20:53:46 +10:00
< ? php
/**
2025-05-14 16:14:01 +01:00
* PS Plugin , which displays all running processes
2024-09-07 20:53:46 +10:00
* a simple tree view which is filled with the running processes which are determined by
* calling the " ps " command line utility , another way is to provide
* a file with the output of the ps utility , so there is no need to run a execute by the
* webserver , the format of the command is written down in the phpsysinfo . ini file , where also
* the method of getting the information is configured
*
* @ category PHP
* @ package PSI_Plugin_PS
* @ author Michael Cramer < BigMichi1 @ users . sourceforge . net >
* @ copyright 2009 phpSysInfo
2025-05-14 16:14:01 +01:00
* @ license http :// opensource . org / licenses / gpl - 2.0 . php GNU General Public License version 2 , or ( at your option ) any later version
2024-09-07 20:53:46 +10:00
* @ version Release : 3.0
* @ link http :// phpsysinfo . sourceforge . net
*/
class PS extends PSI_Plugin
{
/**
* variable , which holds the content of the command
* @ var array
*/
private $_filecontent = array ();
/**
* variable , which holds the result before the xml is generated out of this array
* @ var array
*/
private $_result = array ();
/**
* read the data into an internal array and also call the parent constructor
*
2025-05-14 16:14:01 +01:00
* @ param string $enc encoding
2024-09-07 20:53:46 +10:00
*/
public function __construct ( $enc )
{
parent :: __construct ( __CLASS__ , $enc );
2025-05-14 16:14:01 +01:00
$buffer = " " ;
2024-09-07 20:53:46 +10:00
switch ( strtolower ( PSI_PLUGIN_PS_ACCESS )) {
case 'command' :
2025-05-14 16:14:01 +01:00
if ((( PSI_OS == 'WINNT' ) && ! defined ( 'PSI_EMU_HOSTNAME' )) || ( defined ( 'PSI_EMU_HOSTNAME' ) && ! defined ( 'PSI_EMU_PORT' ))) {
2024-09-07 20:53:46 +10:00
try {
2025-05-14 16:14:01 +01:00
$os_wmi = WINNT :: _get_Win32_OperatingSystem ();
$memtotal = 0 ;
2024-09-07 20:53:46 +10:00
foreach ( $os_wmi as $os ) {
2025-05-14 16:14:01 +01:00
$memtotal = $os [ 'TotalVisibleMemorySize' ] * 1024 ;
break ;
}
$wmi = WINNT :: getcimv2wmi ();
$perf_wmi = WINNT :: getWMI ( $wmi , 'Win32_PerfFormattedData_PerfProc_Process' , array ( 'IDProcess' , 'CreatingProcessID' , 'PercentProcessorTime' ));
$proccpu = array ();
foreach ( $perf_wmi as $perf ) {
$proccpu [ trim ( $perf [ 'IDProcess' ])] = array ( 'ParentProcessId' => trim ( $perf [ 'CreatingProcessID' ]), 'PercentProcessorTime' => trim ( $perf [ 'PercentProcessorTime' ]));
2024-09-07 20:53:46 +10:00
}
2025-05-14 16:14:01 +01:00
$process_wmi = WINNT :: getWMI ( $wmi , 'Win32_Process' , array ( 'Caption' , 'CommandLine' , 'ProcessId' , 'ParentProcessId' , 'WorkingSetSize' ));
2024-09-07 20:53:46 +10:00
foreach ( $process_wmi as $process ) {
2025-05-14 16:14:01 +01:00
if ( isset ( $process [ 'CommandLine' ]) && strlen ( trim ( $process [ 'CommandLine' ])) > 0 ) {
$ps = trim ( $process [ 'CommandLine' ]);
2024-09-07 20:53:46 +10:00
} else {
2025-05-14 16:14:01 +01:00
$ps = trim ( $process [ 'Caption' ]);
2024-09-07 20:53:46 +10:00
}
2025-05-14 16:14:01 +01:00
if (( $procid = trim ( $process [ 'ProcessId' ])) != 0 ) {
$memusage = round ( trim ( $process [ 'WorkingSetSize' ]) * 100 / $memtotal , 1 );
$parentid = trim ( $process [ 'ParentProcessId' ]);
$cpu = 0 ;
if ( isset ( $proccpu [ $procid ]) && ( $proccpu [ $procid ][ 'ParentProcessId' ] == $parentid )) {
$cpu = $proccpu [ $procid ][ 'PercentProcessorTime' ];
}
2024-09-07 20:53:46 +10:00
//ParentProcessId
//Unique identifier of the process that creates a process. Process identifier numbers are reused, so they
//only identify a process for the lifetime of that process. It is possible that the process identified by
//ParentProcessId is terminated, so ParentProcessId may not refer to a running process. It is also
//possible that ParentProcessId incorrectly refers to a process that reuses a process identifier. You can
//use the CreationDate property to determine whether the specified parent was created after the process
//represented by this Win32_Process instance was created.
//=> subtrees of processes may be missing (WHAT TODO?!?)
2025-05-14 16:14:01 +01:00
$this -> _filecontent [] = $procid . " " . $parentid . " " . $memusage . " " . $cpu . " " . $ps ;
2024-09-07 20:53:46 +10:00
}
}
} catch ( Exception $e ) {
}
2025-05-14 16:14:01 +01:00
} elseif (( PSI_OS != 'WINNT' ) && ( ! defined ( 'PSI_EMU_HOSTNAME' ) || defined ( 'PSI_EMU_PORT' ))) {
CommonFunctions :: executeProgram ( " ps " , " axo pid,ppid,pmem,pcpu,args " , $buffer , PSI_DEBUG );
if ((( PSI_OS == 'Linux' ) || ( PSI_OS == 'Android' )) && ( ! preg_match ( " /^[^ \n ]+ \n \ s* \ d+ \ s+ \ d+ \ s+[ \ d \ .]+ \ s+[ \ d \ .]+ \ s+.+/ " , $buffer ))) { //alternative method if no data
2024-09-07 20:53:46 +10:00
if ( CommonFunctions :: rfts ( '/proc/meminfo' , $mbuf )) {
$bufe = preg_split ( " / \n / " , $mbuf , - 1 , PREG_SPLIT_NO_EMPTY );
$totalmem = 0 ;
foreach ( $bufe as $buf ) {
2025-05-14 16:14:01 +01:00
if ( preg_match ( '/^MemTotal:\s+(\d+)\s*kB/i' , $buf , $ar_buf )) {
2024-09-07 20:53:46 +10:00
$totalmem = $ar_buf [ 1 ];
break ;
}
}
2025-05-14 16:14:01 +01:00
$buffer = " PID PPID %MEM %CPU COMMAND \n " ;
2024-09-07 20:53:46 +10:00
2025-05-14 16:14:01 +01:00
$processlist = CommonFunctions :: findglob ( '/proc/*/status' , GLOB_NOSORT );
if ( is_array ( $processlist ) && (( $total = count ( $processlist )) > 0 )) {
2024-09-07 20:53:46 +10:00
natsort ( $processlist ); //first sort
2025-05-14 16:14:01 +01:00
$process = array ();
2024-09-07 20:53:46 +10:00
foreach ( $processlist as $processitem ) { //second sort
$process [] = $processitem ;
}
$buf = " " ;
for ( $i = 0 ; $i < $total ; $i ++ ) {
if ( CommonFunctions :: rfts ( $process [ $i ], $buf , 0 , 4096 , false )) {
if (( $totalmem != 0 ) && ( preg_match ( '/^VmRSS:\s+(\d+)\s+kB/m' , $buf , $tmppmem ))) {
$pmem = round ( 100 * $tmppmem [ 1 ] / $totalmem , 1 );
} else {
$pmem = 0 ;
}
$name = null ;
if ( CommonFunctions :: rfts ( substr ( $process [ $i ], 0 , strlen ( $process [ $i ]) - 6 ) . " cmdline " , $namebuf , 0 , 4096 , false )) {
$name = str_replace ( chr ( 0 ), ' ' , trim ( $namebuf ));
}
if ( preg_match ( '/^Pid:\s+(\d+)/m' , $buf , $tmppid ) &&
preg_match ( '/^PPid:\s+(\d+)/m' , $buf , $tmpppid ) &&
preg_match ( '/^Name:\s+(.+)/m' , $buf , $tmpargs )) {
$pid = $tmppid [ 1 ];
$ppid = $tmpppid [ 1 ];
$args = $tmpargs [ 1 ];
if ( $name !== null ) {
if ( $name !== " " ) {
$args = $name ;
} else {
$args = " [ " . $args . " ] " ;
}
}
2025-05-14 16:14:01 +01:00
$buffer .= $pid . " " . $ppid . " " . $pmem . " 0.0 " . $args . " \n " ;
2024-09-07 20:53:46 +10:00
}
}
}
}
}
}
}
break ;
case 'data' :
2025-05-14 16:14:01 +01:00
if ( ! defined ( 'PSI_EMU_HOSTNAME' )) {
CommonFunctions :: rftsdata ( " ps.tmp " , $buffer );
}
2024-09-07 20:53:46 +10:00
break ;
default :
2025-05-14 16:14:01 +01:00
$this -> global_error -> addConfigError ( " __construct() " , " [ps] ACCESS " );
2024-09-07 20:53:46 +10:00
}
2025-05-14 16:14:01 +01:00
if ( trim ( $buffer ) != " " ) {
$this -> _filecontent = preg_split ( " / \n / " , $buffer , - 1 , PREG_SPLIT_NO_EMPTY );
unset ( $this -> _filecontent [ 0 ]);
2024-09-07 20:53:46 +10:00
}
}
/**
* doing all tasks to get the required informations that the plugin needs
* result is stored in an internal array < br > the array is build like a tree ,
* so that it is possible to get only a specific process with the childs
*
* @ return void
*/
public function execute ()
{
if ( empty ( $this -> _filecontent )) {
return ;
}
2025-05-14 16:14:01 +01:00
$items = array ();
2024-09-07 20:53:46 +10:00
foreach ( $this -> _filecontent as $roworig ) {
2025-05-14 16:14:01 +01:00
$row = preg_split ( " /[ \ s]+/ " , trim ( $roworig ), 5 );
if ( count ( $row ) != 5 ) {
2024-09-07 20:53:46 +10:00
break ;
}
foreach ( $row as $key => $val ) {
$items [ $row [ 0 ]][ $key ] = $val ;
}
if ( $row [ 1 ] !== $row [ 0 ]) {
$items [ $row [ 1 ]][ 'childs' ][ $row [ 0 ]] = & $items [ $row [ 0 ]];
}
}
foreach ( $items as $item ) { //find zombie
if ( ! isset ( $item [ 0 ])) {
foreach ( $item [ " childs " ] as $subitem ) {
$zombie = $subitem [ 1 ];
if ( $zombie != 0 ) {
$items [ $zombie ][ " 0 " ] = $zombie ;
$items [ $zombie ][ " 1 " ] = " 0 " ;
$items [ $zombie ][ " 2 " ] = " 0 " ;
2025-05-14 16:14:01 +01:00
$items [ $zombie ][ " 3 " ] = " 0 " ;
$items [ $zombie ][ " 4 " ] = " unknown " ;
2024-09-07 20:53:46 +10:00
$items [ 0 ][ 'childs' ][ $zombie ] = & $items [ $zombie ];
}
break ; //first is sufficient
}
}
}
if ( isset ( $items [ 0 ])) {
$this -> _result = $items [ 0 ];
} else {
2025-05-14 16:14:01 +01:00
$this -> _result = array ();
2024-09-07 20:53:46 +10:00
}
}
/**
* generates the XML content for the plugin
*
* @ return SimpleXMLElement entire XML content for the plugin
*/
public function xml ()
{
if ( $this -> _result ) {
$positions = array ( 0 => 0 );
$this -> _addchild ( $this -> _result [ 'childs' ], $this -> xml , $positions );
}
return $this -> xml -> getSimpleXmlElement ();
}
/**
* recursive function to allow appending child processes to a parent process
*
2025-05-14 16:14:01 +01:00
* @ param array $child part of the array which should be appended to the XML
2024-09-07 20:53:46 +10:00
* @ param SimpleXMLExtended $xml XML - Object to which the array content is appended
2025-05-14 16:14:01 +01:00
* @ param array & $positions array with parent positions in xml structure
2024-09-07 20:53:46 +10:00
*
* @ return SimpleXMLExtended Object with the appended array content
*/
private function _addchild ( $child , SimpleXMLExtended $xml , & $positions )
{
foreach ( $child as $key => $value ) {
$xmlnode = $xml -> addChild ( " Process " );
if ( isset ( $value [ 0 ])) {
array_push ( $positions , $value [ 0 ]);
$xmlnode -> addAttribute ( 'PID' , $value [ 0 ]);
$parentid = array_search ( $value [ 1 ], $positions );
$xmlnode -> addAttribute ( 'ParentID' , $parentid );
$xmlnode -> addAttribute ( 'PPID' , $value [ 1 ]);
2025-05-14 16:14:01 +01:00
if ( ! defined ( 'PSI_PLUGIN_PS_MEMORY_USAGE' ) || ( PSI_PLUGIN_PS_MEMORY_USAGE !== false )) {
$xmlnode -> addAttribute ( 'MemoryUsage' , str_replace ( ',' , '.' , $value [ 2 ]));
}
if ( ! defined ( 'PSI_PLUGIN_PS_CPU_USAGE' ) || ( PSI_PLUGIN_PS_CPU_USAGE !== false )) {
$xmlnode -> addAttribute ( 'CPUUsage' , str_replace ( ',' , '.' , $value [ 3 ]));
}
$xmlnode -> addAttribute ( 'Name' , $value [ 4 ]);
if (( PSI_OS != 'WINNT' ) && ! defined ( 'PSI_EMU_HOSTNAME' ) &&
((( $parentid === 1 ) && ( ! defined ( 'PSI_PLUGIN_PS_SHOW_PID1CHILD_EXPANDED' ) || ( PSI_PLUGIN_PS_SHOW_PID1CHILD_EXPANDED === false )))
|| (( ! defined ( 'PSI_PLUGIN_PS_SHOW_KTHREADD_EXPANDED' ) || ( PSI_PLUGIN_PS_SHOW_KTHREADD_EXPANDED === false )) && ( $value [ 4 ] === " [kthreadd] " )))) {
$xmlnode -> addAttribute ( 'Expanded' , 0 );
2024-09-07 20:53:46 +10:00
}
}
if ( isset ( $value [ 'childs' ])) {
$this -> _addChild ( $value [ 'childs' ], $xml , $positions );
}
}
return $xml ;
}
}