popened process never dies - php

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!

Related

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

Remote Tailing Using ssh2_fetch_stream

I am trying to do remote tailing using phpseclib. I manged to do this using the following code:
<?php
include('Net/SSH2.php');
include('Crypt/RSA.php');
$server = $_POST['server'];
$ssh = new Net_SSH2($server);
$key = new Crypt_RSA();
$key->loadKey(file_get_contents('/home/{username}/.ssh/id_rsa'));
if (!$ssh->login('{username}', $key)) {
exit('Login Failed');
}
$tail="tail -n 1 {some lof file}";
while ($ssh->isConnected()) {
$ssh->exec(
$tail, function ($str) {
echo $str;
echo "<br>";
flush();
ob_flush();
}
);
}
?>
The problem with the code above is that it logs duplicate entries and I was told that it will not read the log file fast enough if we had to change our log file debug level. It was recommended that I look at ssh2_fetch_stream. I tried this but am honestly very confused. This is my code at the moment:
<?php
include('Net/SSH2.php');
include('Crypt/RSA.php');
$host = $_POST['server'];
$username = "{username}";
$publicKey = "/home/{username}/.ssh/id_rsa.pub";
$privateKey = "/home/{username}/.ssh/id_rsa";
$log = "{some log file}";
$conn = ssh2_connect($host);
if (ssh2_auth_pubkey_file($conn, $username, $publicKey, $privateKey)){
$stream = ssh2_exec($conn, 'tail -n 1 {some log file}');
$stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
if (ob_get_level() == 0)
ob_start();
while ($stream_out) {
$line = fgets($stream_out);
echo $line.'<br />';
ob_flush();
flush();
sleep(1);
}
fclose($stream_out);
ob_end_flush();
}
?>
The above code only prints one line since I am not sure how to do the loop since I can't use "while ($ssh->isConnected())" anymore. I think it is looping but not looping what it is supposed to. Unfortunately because of this I can't test if this would read the log file fast enough.
Any help or pointers will be very much appreciated. I hope this solution will work because I am not allowed to install anything on the remote server who's log file I am supposed to tail.
I was told that it will not read the log file fast enough if we had to
change our log file debug level
Whoever told you that is wrong. phpseclib reads what the SSH server sends, which is exactly what libssh2 (or any SSH client for that matter) does.
The problem with the code above is that it logs duplicate entries
That would make sense. tail -n 1 filename shows you the last entry in the log file. If there's a ten minute gap between entries being made and, in that ten minute, you run that command 100 times then you'll see 100 duplicate entries.
My recommendation: do this (with phpseclib):
$ssh->setTimeout(0);
$tail = 'tail -f /path/to/logfile';
$ssh->exec(
$tail, function ($str) {
echo $str;
echo "<br>";
flush();
ob_flush();
}
);
ie. no while loop, no running the same command a zillion times, etc. Just one command, ran once and into perpetuity.

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
}
}
...

php shell_exec with realtime updating

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();

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