php shell_exec with realtime updating - php

I have this shell program that I want to execute by php. The problem is that it can potentially take a long time, and as of that I need it to have real-time updating to the user's browser.
I read that I may need to use popen() to do that, but I am sort of (ok, I really am :P) a PHP noob and can't figure out how I may be able to do it.
Would appreciate any help!

if( ($fp = popen("your command", "r")) ) {
while( !feof($fp) ){
echo fread($fp, 1024);
flush(); // you have to flush buffer
}
fclose($fp);
}

there is a dirty easy option
`yourcommand 1>&2`;
redirecting the stdout to the stderr.

there are two possible behaviors:
Non Block, where you need to do something else between flushs (#GameBit show how to do it).
With Block, where you wait until the called command finish, in this case look passthru function

I used this solution. It works fine for me.
$commandString = "myexe";
# Uncomment this line if you want to execute the command in background on Windows
# $commandString = "start /b $commandString";
$exec = popen($commandString, "r");
# echo "Async Code Test";
while($output = fgets($exec, 2048))
{
echo "$output <br>\n";
ob_flush();
flush();
}
pclose($exec);

try this code (tested on Windows machine + wamp server)
header('Content-Encoding: none;');
set_time_limit(0);
$handle = popen("<<< Your Shell Command >>>", "r");
if (ob_get_level() == 0)
ob_start();
while(!feof($handle)) {
$buffer = fgets($handle);
$buffer = trim(htmlspecialchars($buffer));
echo $buffer . "<br />";
echo str_pad('', 4096);
ob_flush();
flush();
sleep(1);
}
pclose($handle);
ob_end_flush();

Related

popened process never dies

I am using this neat code that tails a logfile live on the browser (thanks SO!)
The problem is that tail remains running even after user closes the tab/browser/pc
any ideas?
logs are still being written while window is closed, so I know that the server is trying to send something and fails, but the if connection_status()​!=0 seems to never fire
<?php
$files = scandir('somefile', SCANDIR_SORT_DESCENDING);
$newestfile = $files[0];
echo "$newestfile<br/>\n";
$handle = popen("tail -fn +1 ./somedir/" . $newestfile . " 2>&1", 'r');
while(!feof($handle)) {
if (connection_status()​!=0){
exec("killall tail");
pclose($handle);
}
$buffer = fgets($handle);
echo "$buffer<br/>\n";
ob_flush();
flush();
}
pclose($handle);
?>
Update:
I ended up using a modified version of this:
https://github.com/richardvk/web_file_tail
KUDOS to this guy!

Tail -f live output process still running

I am trying to do a live output of a file called fail2ban.log this log is on my linux server and i try to proccess it using. The tail process stay opened so it uses loads of cpu performance after some pepoles open the page since the process stay opened
I tried some solution of killing it with
while(true)
{
if($flag === false) die(); // Or exit if you prefer
}
The server is on Apache2
My code :
<?php
echo "Number of banned ip (live) : ";
$hand = popen("grep 'Ban' /var/log/fail2ban.log | wc -l 2>&1", 'r');
while(!feof($hand)) {
$buff = fgets($hand);
echo "$buff<br/>\n";
ob_flush();
flush();
}
pclose($hand);
echo " ";
echo "Current Log (go at the bottom of the page for the live log)";
echo " ";
$output = shell_exec('cat /var/log/fail2ban.log 2>&1');
echo "<pre>$output</pre>";
echo "Live Logs";
echo "<h1> </h1> ";
echo " ";
$handle = popen("tail -f /var/log/fail2ban.log 2>&1", 'r');
while(!feof($handle)) {
$buffer = fgets($handle);
echo "$buffer<br/>\n";
ob_flush();
flush();
}
pclose($handle);
?>
I want it to kill the process when the user quit the page.
No #jhnc In this case, popen is guilty, which does not end the process when the program is closed.
In general, PHP is one of the worst choices to implement tail -f. It's better to use node + websocket.
In this case, you need to check if something has been added to the file by another method. From http://php.net/manual/en/function.inotify-init.php#101093
<?php
/**
* Tail a file (UNIX only!)
* Watch a file for changes using inotify and return the changed data
*
* #param string $file - filename of the file to be watched
* #param integer $pos - actual position in the file
* #return string
*/
function tail($file,&$pos) {
// get the size of the file
if(!$pos) $pos = filesize($file);
// Open an inotify instance
$fd = inotify_init();
// Watch $file for changes.
$watch_descriptor = inotify_add_watch($fd, $file, IN_ALL_EVENTS);
// Loop forever (breaks are below)
while (true) {
// Read events (inotify_read is blocking!)
$events = inotify_read($fd);
// Loop though the events which occured
foreach ($events as $event=>$evdetails) {
// React on the event type
switch (true) {
// File was modified
case ($evdetails['mask'] & IN_MODIFY):
// Stop watching $file for changes
inotify_rm_watch($fd, $watch_descriptor);
// Close the inotify instance
fclose($fd);
// open the file
$fp = fopen($file,'r');
if (!$fp) return false;
// seek to the last EOF position
fseek($fp,$pos);
// read until EOF
while (!feof($fp)) {
$buf .= fread($fp,8192);
}
// save the new EOF to $pos
$pos = ftell($fp); // (remember: $pos is called by reference)
// close the file pointer
fclose($fp);
// return the new data and leave the function
return $buf;
// be a nice guy and program good code ;-)
break;
// File was moved or deleted
case ($evdetails['mask'] & IN_MOVE):
case ($evdetails['mask'] & IN_MOVE_SELF):
case ($evdetails['mask'] & IN_DELETE):
case ($evdetails['mask'] & IN_DELETE_SELF):
// Stop watching $file for changes
inotify_rm_watch($fd, $watch_descriptor);
// Close the inotify instance
fclose($fd);
// Return a failure
return false;
break;
}
}
}
}
// Use it like that:
$lastpos = 0;
$file = '/var/log/fail2ban.log'l
while (true) {
echo tail($file,$lastpos);
ob_flush();
flush();
}
?>
And you can't forget about max_execution_time and Apache limits

How to have web page stop long running process

I have the following code, which runs a process that runs forever (until killed) and display the results (using tail -F below just as an illustration):
<?php
$cmd = "tail -F /var/log/whatever.log";
set_time_limit(0);
$handle = popen($cmd, "r");
if (ob_get_level() == 0)
ob_start();
while(!feof($handle)) {
$buffer = fgets($handle);
echo $buffer . "<br />";
ob_flush();
flush();
sleep(1);
}
pclose($handle);
ob_end_flush();
?>
This sort of works, my process runs, but it continues to run when I stop loading page, close tab, etc.
Is there some mechanism I can take advantage of to stop the process when I am not showing the page? I'm sure there is, but not clear.
...
header("Content-type:text/html");// start the content serving
echo("\n"); // connection with the browser
while(!feof($handle)) {
$buffer = fgets($handle);
echo $buffer . "<br />";
ob_flush();
flush();
sleep(1);
if (connection_status()!=0){ // check the connection
die; // or whatever you want to stop the work
}
}
...

Is it possible to run two commands and deal with both outputs on same page

I am running two linux commands with popen() in php. If I run 1, I can gather the output and process it fine. If I run two at the same time while funneling to the page with 2>&1, output gets jumbled. Is it possible to run two commands and deal with both outputs on same page?
I basically duplicated the bottom code for each command
$handle = popen ("-some long command 2>&1");
while (false !== ($char = fgetc($handle)))
{
if ($char == "\r")
{
// You could now parse the $line for status information.
$line= "$line\n";
if (preg_match("/Duration: (.*?),/", $line, $matches)){
//do stuff
}
$line = "";
} else {
$line .= $char;
}
ob_flush();
flush();
}
pclose ($handle);
}
proc_open allows multiple, asynchronous commands execution; you would get the output through the stdout pipe.
PS: never duplicate code; use functions instead!

How To watch a file write in PHP?

I want to make movement such as the tail command with PHP,
but how may watch append to the file?
I don't believe that there's some magical way to do it. You just have to continuously poll the file size and output any new data. This is actually quite easy, and the only real thing to watch out for is that file sizes and other stat data is cached in php. The solution to this is to call clearstatcache() before outputting any data.
Here's a quick sample, that doesn't include any error handling:
function follow($file)
{
$size = 0;
while (true) {
clearstatcache();
$currentSize = filesize($file);
if ($size == $currentSize) {
usleep(100);
continue;
}
$fh = fopen($file, "r");
fseek($fh, $size);
while ($d = fgets($fh)) {
echo $d;
}
fclose($fh);
$size = $currentSize;
}
}
follow("file.txt");
$handle = popen("tail -f /var/log/your_file.log 2>&1", 'r');
while(!feof($handle)) {
$buffer = fgets($handle);
echo "$buffer\n";
flush();
}
pclose($handle);
Checkout php-tail on Google code. It's a 2 file implementation with PHP and Javascript and it has very little overhead in my testing.
It even supports filtering with a grep keyword (useful for ffmpeg which spits out frame rate etc every second).
$handler = fopen('somefile.txt', 'r');
// move you at the end of file
fseek($handler, filesize( ));
// move you at the begining of file
fseek($handler, 0);
And probably you will want to consider a use of stream_get_line
Instead of polling filesize you regular checking the file modification time: filemtime
Below is what I adapted from above. Call it periodically with an ajax call and append to your 'holder' (textarea)... Hope this helps... thank you to all of you who contribute to stackoverflow and other such forums!
/* Used by the programming module to output debug.txt */
session_start();
$_SESSION['tailSize'] = filesize("./debugLog.txt");
if($_SESSION['tailPrevSize'] == '' || $_SESSION['tailPrevSize'] > $_SESSION['tailSize'])
{
$_SESSION['tailPrevSize'] = $_SESSION['tailSize'];
}
$tailDiff = $_SESSION['tailSize'] - $_SESSION['tailPrevSize'];
$_SESSION['tailPrevSize'] = $_SESSION['tailSize'];
/* Include your own security checks (valid user, etc) if required here */
if(!$valid_user) {
echo "Invalid system mode for this page.";
}
$handle = popen("tail -c ".$tailDiff." ./debugLog.txt 2>&1", 'r');
while(!feof($handle)) {
$buffer = fgets($handle);
echo "$buffer";
flush();
}
pclose($handle);

Categories