stream_set_timeout not working for ssh2_exec in PHP - php

I am having trouble when try to set timeout for my ssh2_exec connection in PHP, so that when there is something wrong when running the command, it will release the executing thread and will not jam the whole web site. I have tried stream_set_timeout($stream,$seconds) but it seems not to work as expected.
Is there any ideas on this?
//run specified command (ssh)
function runCMD($cmd){
if (!($stream = ssh2_exec($this->conn, $cmd )))
{
echo "fail: unable to execute command\n";
fclose($stream);
return ERROR_SSH_EXEC_COMM;
}
sleep(1);
stream_set_timeout($stream,5);//>>not work
stream_set_blocking($stream, true);
$res = stream_get_contents($stream);
return $res;
}

For anyone wondering how to set the timeout, there's a default_socket_timeout variable in php.ini that can be used to set the timeout for any socket-based connections in your current session.
You can check it by simply running
$timeStart = time();
ini_set("default_socket_timeout", 5);
$ssh = ssh2_connect('123.123.123.123');
echo (time() - $timeStart), PHP_EOL;

Here's how you could do that with phpseclib, a pure PHP SSH2 implementation:
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
$ssh->setTimeout(5);
echo $ssh->exec('whatever');
?>
If you want to execute subsequent commands after that one you'll need to do $ssh->reset().

AFAIK, There are no true 'timeout' implementations in the php ssh2 specs.
The only solution that I have found for this problem can be found in this post here:
PHP ssh2_connect() Implement A Timeout

Related

Real-time SSH output in Laravel with phpseclib

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

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.

ssh2_scp_send() and ssh2_scp_recv() functions are not working in php. Why?

ssh2_scp_send() function is hanging in php. Here's the code:
$debug_line_feed = "\n";
$conn = ssh2_connect($sftp_server, 22);
$return = ssh2_auth_password($conn, $sftp_user_name, $sftp_user_pass);
if ($return===true) echo "successfull connection".$debug_line_feed;
echo "uploading file".$debug_line_feed;
$local_filename = $product_feed_file_with_path;
$remote_filename = 'product_feed.txt';
ssh2_scp_send($conn, $local_filename, $remote_filename);
echo "successful".$debug_line_feed;
When i run it, it outputs "successful connection", "uploading file" then hangs. Any idea how to fix this?
I have tried a download as well with ssh2_scp_recv, and it hangs as well, with the local file being created as a 0 byte file.
My guess is that the server has a jail shell installed. At that point SCP wouldn't work but SFTP would.
recently,i use sftp to send file,linux to windows,ssh2_scp_send not work,i solve the problems use
$sftp = ssh2_sftp($conn);
$contents = file_get_contents($localPath);
$result = file_put_contents("ssh2.sftp://{$sftp}/{$remotePath}", $contents);
then works

Waiting for command to finish using SSH2_Shell in PHP

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]');
?>

Empty PHP SSH2 stream contents, even with stream_set_blocking?

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

Categories