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.
Related
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!
I've tried different SSH libraries but they don't seem to work too well in Laravel. Since I got phpseclib to work well, I'm trying to stick with it. But, I'm not seeing a way to have a live output from the SSH connection.
Some things that I plan on running can take up to a few hours to finish but the software being accessed always prints out a percentage of completion that I'd like to utilize to display in the browser.
Currently, I have this:
<?php
use phpseclib\Net\SSH2;
use phpseclib\Crypt\RSA;
$key = new RSA();
$key->loadKey(file_get_contents('key.pem'));
$command = 'ping google.com';
$ssh = new SSH2('awesomeserver.com');
if (!$ssh->login('username', $key))
{
echo 'Login Failed';
}
else
{
echo $ssh->exec($command);
}
?>
This just waits until the command is complete and then just barfs it all onto the screen.
Previously, in the else bracket, I had
while(# ob_end_flush());
$proc = $ssh->exec($command);
while(!feof($proc))
{
echo fread($proc, 4096);
# flush();
}
But, $proc was only recognized as a string instead of a resource, so it didn't work.
Do you all have any other suggestions, other than using a different library?
Thanks!
EDIT (Partial solution):
I eventually got this script to work by utilizing the read() function with this:
while(# ob_end_flush());
$ssh->write($command.'\n');
while($read = $ssh->read())
{
echo $read;
# flush();
}
You could employee a callback. eg.
$ssh->exec('ping 127.0.0.1', function($output) {
echo $output;
});
I have a php code which will ssh to a remote machine and execute a shell script to get list of folders. The remote machine contain more than 300 folders in the specified path in the shell script.Shell script executes well and return the list of all folders.But while I retrieve this output in php, I'am getting only around 150, 200 number of folders.
Here is my php code,
<?php
if (!function_exists("ssh2_connect")) die("function ssh2_connect doesn't exist");
if(!($con = ssh2_connect("ip.add.re.ss", "port")))
{
echo "fail: unable to establish connection";
}
else
{
if(!ssh2_auth_password($con, "username", "password"))
{
echo "fail: unable to authenticate";
}
else
{
$stream = ssh2_exec($con, "/usr/local/listdomain/listproject.sh");
stream_set_blocking($stream, true);
$item = fread($stream,4096);
$items = explode(" ", $item);
print_r($items);
}
}
?>
And this is my shell script.
#!/bin/bash
var=$(ls /home);
echo $var;
What is the issue with php here. Is there any limit in array size in php while getting data dynamically like here.Please advise as I am very beginner to PHP.
Thanks.
You're only reading one block of 4096 characters from your stream. if your folder list is longer than this you'll lose the rest. You need something like this:
stream_set_blocking($stream, true);
$item = "";
// continue reading while there's more data
while ($input = fread($stream,4096)) {
$item .= $input;
}
$items = explode(" ", $item);
print_r($items);
You asked fread() to only read 4096 bytes. In the examples portion of fread()’s documentation, it is suggested that stream_get_contents() be used for reading a file handle out to its end. Otherwise, you have to use a loop and keep on reading data until feof($stream) returns FALSE.
I have an SSH2_Shell session working in PHP. my issue is that i need a command to completely finish before moving onto the next command. Here is my code so far:
$command_capture = "cd /mnt/NADS/scripts/";
$command_capture2 = "./tcpdump.sh $capture_name $sleep";
if (!($connection = ssh2_connect("172.20.1.18", 22))) {
echo "fail: unable to establish connection";
}
if (!ssh2_auth_password($connection, "root", "Hideandseek")) {
echo "fail: unable to authenticate";
}
$stream = ssh2_shell($connection);
fwrite($stream, $command_capture. PHP_EOL);
sleep(1);
fwrite($stream, $command_capture2 . PHP_EOL);
sleep(5);
$data="";
while($buf = stream_get_contents($stream)){
$data.=$buf;
}
echo $data;
fclose($stream);
the tcpdump.sh script is running a lftp command but is not being given anough time to complete. I cant use sleep as it may take longer then the specified time and i dont want to make the user wait if it only needs a few seconds. I have not had luck implementing stream_set_blocking as when i do, it seems to freeze up my browser.
overall, I need a way to detect when a command has finished and move into the next command.
Thanks in advance!
Thanks for the ideas but I think I got it solved. I did an echo of a few special characters when the command finished and then I would search for the characters using strpos(). Another thing I think may have helped was adjusting the max_execution_time setting in the php.ini file. I found that answer here:
http://verysimple.com/2006/03/30/fatal-error-maximum-execution-time-of-30-seconds-exceeded/
And here is my new code
$data="";
while (true){
$data .= stream_get_contents($stream);
if (strpos($data,"XOXO") !== false) {
echo "okay: command finished\n";
break;
}
}
echo $data;
fclose($stream);
I have an SSH2 class that implements the Secure Shell2 over on github .. check out https://github.com/bubba-h57/PHP-SSH2 and see if that helps.
I continue to search for a better way to handle finding the prompts though, so any advice or contribution to that would be welcomed. Hopefully it will help, if nothing more than giving you some ideas.
You could use phpseclib, a pure PHP SSH implementation. eg.
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2();
$ssh->login('username', 'password');
echo $ssh->exec('command');
echo $ssh->exec('command2');
?>
If you really need shell support you could do the following (with the latest SVN):
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2();
$ssh->login('username', 'password');
$ssh->read('[prompt]');
$ssh->write("command\n");
echo $ssh->read('[prompt]');
$ssh->write("command\n");
echo $ssh->read('[prompt]');
?>
I am working on a tool that reads an iptables configuration from a remote host over SSH2 using the PECL SSH2 extension. I am able to successfully make the connection to the host, authenticate, and execute commands. The trouble I am having is sometimes the stream doesn't contain any data.
/**
* Load the current firewall configuration
* #return bool
*/
public function loadRules() {
$stream = ssh2_exec($this->connection,"~/iptsave;");
stream_set_blocking($stream,true);
$iptablesSave = stream_get_contents($stream);
if(empty($iptablesSave)) {
return false;
}
parent::restore($iptablesSave);
return true;
}
About 25% of the time, loadRules() returns false, even when connecting to locahost instead of the remote system. I was able to work around the problem by changing the ssh2_exec call to
$stream = ssh2_exec($this->connection,"~/iptsave; sleep .5");
but I am concerned that something is wrong.
phpSecLib may be able to help:
According to this post, it always returns the output, unlike ssh2.so.
I've got the same issue here. Somehow you need to set a delay for getting the result of the stream.
The way you've done it is possible, but you could also set a sleep(1) after the stream_set_block($stream, true) function.
You could try the usleep() function. Haven't tried it yet
May be this will solve the issue:
$stream = ssh2_exec($this->connection,"~/iptsave;");
stream_set_blocking($stream,true);
$stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
$iptablesSave = stream_get_contents($stream);
With some severs you have to use 'interactive shell'. And sometimes you have to set the delay / sleep manually. A working example:
$connection = ssh2_connect($IP, 22);
$auth = ssh2_auth_password($connection, $User, $Pass);
$cmd = "help" . PHP_EOL;
if (!$auth) {
echo "Login Failed;
exit(1);
}
$shell = ssh2_shell($connection);
stream_set_blocking($shell, false); // we will use manual sleep
sleep(1); // This sleep to make sure that you get the prompt back
fwrite ($shell, $cmd . ";" . PHP_EOL);
sleep(1); // This to make sure that the command executes and we get the prompt back again!
while($output = fgets($shell)){
echo $output;
}
fwrite ($shell, "exit;" . PHP_EOL); // If needed
sleep(1);
ssh2_disconnect($connection);
unset($shell);
unset($connection);