3

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:

Real Time NET_SSH2 Log

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.

neubert
  • 15,947
  • 24
  • 120
  • 212
  • It is [effective to just look at the output](http://phpseclib.sourceforge.net/documentation/net.html) of `$ssh->getLastError()` (which returns a string) and `$ssh->getErrors()` (which returns an array). Post that output, if any, in your question. – WEBjuju Dec 09 '16 at 12:53
  • This can be done using using PHP with less resource footprint. See: http://stackoverflow.com/questions/19139434/php-move-a-file-into-a-different-folder-on-the-server#19139524 – Kitson88 Dec 09 '16 at 12:55
  • @WEBjuju There is no error output. Using `$ssh->getLog();` the only log I got is connection successful. – Murilo Komirchuk Dec 09 '16 at 13:09
  • @kitson88 the OP is trying to move files on a remote system – WEBjuju Dec 09 '16 at 13:12
  • @WEBjuju Ah whoops...then you can't do what I suggested. – Kitson88 Dec 09 '16 at 13:13
  • 1
    I'd say do what Murilo suggested and get the SSH2 logs. To enable logging you'll need to do `define('NET_SSH2_LOGGING', 2);` at the top of the file. Then do `$ssh->getLog()`. Copy / paste the output into a pastebin.com link and then add that link to your question. – neubert Dec 09 '16 at 16:30
  • @neubert I updated the question with the log link. Thanks for the suggestion. – Murilo Komirchuk Dec 09 '16 at 18:33
  • I think your question has more to do with bash than it does with ssh or phpseclib or whatever. I've tagged your question accordingly. I just say that because I try to adapt your command to my system and I had nothing but difficulty. With `'cat /tmp/ligacoes'` the loop ran once even tho /tmp/ligacoes had two files in it. Switching that out with `$(cat ~/tmp/ligacoes)` made the loop loop twice. I then tried `echo $i` instead of `echo x` and it didn't output the file names - it just outputted empty lines.. – neubert Dec 13 '16 at 02:59
  • Also, for the logging... try `define('NET_SSH2_LOGGING', 3);` instead of `2`. The `3` tells phpseclib to do real time logging that it'll output on the screen. I think that'll be more useful since your script is hanging. As is the log file that you did post looks truncated. It's like you sent the command and the server responded with "success!" and that's it. That's all the server did. But if the script is hanging it seems like you might have had to do something hackish to get the log anyway with `2` as the parameter.. – neubert Dec 13 '16 at 03:02
  • @neubert Yes, my bash commands were part of the problem. I have improved and corrected them. Anyway, they were not the main problem. I notice somehow that if I change the `$ssh->setTimeout()` to a lower value, like `3`, the problem is solved. In my understanding, the `$ssh->setTimeout()` objective was to set a value in seconds that PHP would wait for a command to be concluded. If not concluded, it would be cancelled. For what I have recently read on phpseclib documentation and have tested myself, `$ssh->setTimeout()` doesn't really works like that. – Murilo Komirchuk Dec 13 '16 at 11:44
  • I observed that if the time set is exceeded the command is not cancelled, it just keeps running in the background, while (in my case) a new command is sent to be executed. Anyway, I couldn't figure out the true purpose of `$ssh->setTimeout()` and why setting a higher value makes my script "crash". If you or someone could explain to me in details the `$ssh->setTimeout()` objective I would surely mark it as the answer. Maybe explaining why setting a high value broke the script too. – Murilo Komirchuk Dec 13 '16 at 11:52

2 Answers2

3

Things that may help:

  1. $ssh->exec echos both stdout and stderr. Check those.
  2. Try just echo $ssh->exec('echo hello');
  3. Connect manually first to be sure the "The authenticity of host...Are you sure you want to continue connecting?" has been accepted.
  4. Be certain to **check each of your commands manually to be sure they work before piping them through your script.
  5. Put one example of your commands in a bash file and try to execute only the bash file. If that works, you may be able to send the variables via 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]);
WEBjuju
  • 5,797
  • 4
  • 27
  • 36
  • I created a bash file like you sugested. I am able to execute it but I get the same results: my PHP script stops. I get no output in `echo $ssh->exec('grava1.sh');`, even the first file being copied successfully. – Murilo Komirchuk Dec 09 '16 at 14:02
  • Try just `echo $ssh->exec('echo hello');` – WEBjuju Dec 09 '16 at 14:05
  • also be sure you have accepted the man-in-the-middle warning you get when you first connect via ssh (see my new #3 above) – WEBjuju Dec 09 '16 at 14:06
  • OK, I get 'hello' when executing `echo $ssh->exec('echo hello');`. I connected manually and everything seems to be ready too. – Murilo Komirchuk Dec 09 '16 at 14:13
  • @MuriloKomirchuk well it sounds like you are getting closer. take it one step at a time. next i might add that `echo hello` into the shell script. try fiddling with the permissions until it works (i suggest `chmod 700` for starters). – WEBjuju Dec 09 '16 at 16:46
  • please, take a look at my update above. Any new clues? – Murilo Komirchuk Dec 09 '16 at 18:30
  • hmm. any luck working with that bash script? i do suspect that exec won't let you chain commands, but i have nothing to prove it. but if it won't, the bash script will be your only hope. can you get the bash script to echo hello? – WEBjuju Dec 09 '16 at 19:58
  • Yes, I can make the bash script echo hello and the php script continues after that. It does not die. I also can make it find and copy target file to the new directory, but after that the script stops, making everything after that not being processed. – Murilo Komirchuk Dec 09 '16 at 20:11
  • add `exit` to the end of the bash script to see if it will end your ssh session. or for that matter, put your original string of commands back and add `; exit` to it – WEBjuju Dec 09 '16 at 20:15
  • It does not end my session. Looks like after `done` it hangs and does not process the rest of commands. I really have no clue of what may be causing this behavior. The strange thing is that if I copy and paste this sequence of commands with the `exit` at the end directly to server it works perfectly. – Murilo Komirchuk Dec 10 '16 at 11:16
  • I tried to find the exact library you are using but was not certain that I had. If you will update your post with a link to it, I will test and see what I can find. – WEBjuju Dec 10 '16 at 13:59
  • The link is in the post now. I'm still making some research and tests, but I haven't found any solution yet. I'll post any update about this. Let me know if you have any too. Thanks for your time and help! – Murilo Komirchuk Dec 12 '16 at 13:34
0

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.

neubert
  • 15,947
  • 24
  • 120
  • 212