Reputation: 43
I'm new to PHP and the phpseclib implementation of SSH.
I have the following code :
$ssh = new Net_SSH2($_SESSION['targetAddress']);
if (!$ssh->login(SSH_USER, SSH_PASSWORD)) {
exit('Login Failed');
}
$ssh->setTimeout(400);
$a = 0;
while(isset($file[$a])) {
$ssh->exec('cd '.$_SESSION['path'].'; find -L '.$file[$a].' > /tmp/ligacoes; for i in `cat /tmp/ligacoes`; do cp $i /var/tmp/; done');
$a++;
}
What I am trying to accomplish here is to copy files chosen by user on a remote server to a new directory on the same server. When executing the script, it successfully find and copy the first file to the new directory, but after that the script just stops. Even if the user choose just one item the script hangs and does not continue. It doesn't even increment $a
Any thoughts on what may be happening ?
UPDATE:
I also ran the command directly in the server and it works perfectly. I guess the issue is limited to $ssh->exec();
UPDATE 2:
I changed my $ssh->exec('cd '.$_SESSION['path'].'; find -L '.$file[$a].' > /tmp/ligacoes; for i in 'cat /tmp/ligacoes'; do cp $i /var/tmp/; done');
to $ssh->exec('cd '.$_SESSION['path'].'; cp '.$file[$a].' /var/tmp;');
and that solved part of the problem. Now I am able to copy one selected file to a new directory and the script does not hang. The issue keeps happening when two or more files are selected.
Upvotes: 3
Views: 1656
Reputation: 16792
Here's how timeout works w.r.t. exec().
So the first line in the exec() method is this:
$this->curTimeout = $this->timeout;
Later there's a while (true)
loop that has in it this line:
$temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
_get_channel_packet
has a while (true)
loop as well. It loops until either it times out or until it receives data on the appropriate channel. Here's the timeout code:
if ($this->curTimeout) {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return true;
}
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT parameters detected" error
if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
$this->is_timeout = true;
return true;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
stream_select
blocks until data is available to be read. Depending on the behavior of your SSH server it could very well block for 400s. And who knows... maybe on your system stream_select
will crash before it gets to 400s.
That said, error suppression is enabled on that function, as noted in the comment above the stream_select
call. You could remove the error suppression That might provide some insight.
Also, keep in mind that the timeout is only keeping track of how long it takes for data to be made available. The time it takes to decrypt, for example, does not count against the timeout.
For example...
$ssh->write("cat /dev/urandom\n");
$ssh->setTimeout(10);
$start = microtime(true);
echo $ssh->exec('ping google.com');
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds\n";
I do that and $ssh->exec()
takes 20s (despite the timeout). If I comment out the $ssh->write()
it takes 10s. The cat /dev/urandom\n
is just flooding the client. You can see this more clearly with real time logging (define('NET_SSH2_LOGGING', 3);
). The issue is that the bottleneck isn't the time spent blocking but the time spent receiving data / decrypting it.
Upvotes: 0
Reputation: 6581
Things that may help:
$ssh->exec
echos both stdout
and stderr
. Check those. echo $ssh->exec('echo hello');
exec()
to the bash file for processing. Something like:mybash.sh
cd /example/path/;
find -L example_file > /tmp/ligacoes;
for i in `cat /tmp/ligacoes`;
do cp $i /var/tmp/;
done
and in your php
$ssh->exec('mybash.sh');
If that works, then you can expand it to send variables
cd $1;
find -L $2 > /tmp/ligacoes;
for i in `cat /tmp/ligacoes`;
do cp $i /var/tmp/;
done
calling it like this where $_SESSION['path']
will be $1
:
$ssh->exec('mybash.sh '.$_SESSION['path'].' '.$file[$a]);
Upvotes: 3