5

So I do one simple thing, firstly I exec command via ssh2_exec (after success authenticate) and then read answer in variable. My code below (without authenticate)

try {
        $stdout_stream = ssh2_exec($this->connection, $_cmd);
        $stderr_stream = ssh2_fetch_stream($stdout_stream, \SSH2_STREAM_STDERR);
    } catch (Exception $e) {
        $std_output = $e->getMessage();
        return false;
    }

    $output = "";

    while (!feof($stdout_stream)) {
        $output .= fgets($stdout_stream, 4096);
    }

    while (!feof($stderr_stream)) {
        $output .= fgets($stderr_stream, 4096);
    }

    fclose($stdout_stream);
    fclose($stderr_stream);     

    return $output;

For example I try to execute such cmd:

sudo service httpd stop && sudo service httpd start

So when command executes well everything is good, response is

Shutting down httpd: [ OK ]Starting httpd: [ OK ]

But when I try for instance to execute such command without sudo

service httpd stop && service httpd start

I know server says something like "command not found" or like this, but I can't get this error, this script executes infinitely.

I tried to rewrite my code in this manner (or another similiar to this)

    $dataString = fgets($stdout_stream);
        if($dataString == "\n" || $dataString == "\r\n" || $dataString == "") {
            //var_dump("Empty line found.");
        }

        if($dataString === false && !feof($stdout_stream)) {
            //var_dump("not string");
        } elseif($dataString === false && feof($stdout_stream)) {
            //var_dump("We are at the end of the file.\n");
            break;
        } else {
            //else all is good, process line read in
            $output .= $dataString;
        }
    }

but result is the same.

So the problem is we cannot say in advance what cause infinite loop $stdout_stream or $stderr_stream.

I am using PHP 5.3.

  • In the rewritten version of your code, do you always get in the "$output.=" case or others happen ? If it's the sudo which does the difference, could you do a permissions checking ? In a general manner, infinite loops can be caused by a bad "stop criterion"("stop case", "cas d'arrêt") or by a bad incrementation/step to next iteration. Maybe answering those questions will help you. Good luck – Answers_Seeker Apr 23 '15 at 13:22
  • Thank you for response. Sometimes `fgets($stdout_stream)` returns false, even when command executes successfully ultimately, so that's why I cannot count on it when I know command will not execute well. Auth_User has permissions for restart httpd service. – Alexandr Suleymanov Apr 24 '15 at 07:20
  • Is "www-data" part of "Auth_User" group ? If not, maybe giving www-data the rights to execute httpd would do the trick.(chown) – Answers_Seeker Apr 24 '15 at 08:33
  • Thank you, I'll check this option, But It's not problem for me to add **sudo** to my command, interest is why **ssh2_exec** returns, as it seems, good response (resource of type stream), but script cannot do with it anything acceptable. I guess it can't reach the end of file, but actually don't know what problem is. – Alexandr Suleymanov Apr 24 '15 at 09:27
  • Could you join a var_dump() of your stream ? Is there any way you can concatenate the EOF (chr(26)) character to the end of your stream before looping on it ? – Answers_Seeker Apr 24 '15 at 09:37
  • var_dump is very big. I really don't know how to concatenate the EOF character to the end of stream, do you know any way? – Alexandr Suleymanov Apr 24 '15 at 12:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76170/discussion-between-answers-seeker-and--). – Answers_Seeker Apr 24 '15 at 12:22

2 Answers2

0

I've decided to believe it would be enough ~2 sec to my server to check if errors present. And just in case set max time for secod loop. So my code below. It executes more than I'd like...

    try {
        $stdout_stream = ssh2_exec($this->connection, $_cmd);
        $stderr_stream = ssh2_fetch_stream($stdout_stream, \SSH2_STREAM_STDERR);
    } catch (Exception $e) {
        $std_output = $e->getMessage();
        return false;
    }

    $output = "";

    $start_time = time();
    $max_time = 2; //time in seconds

    while(((time()-$start_time) < $max_time)) {
        $output .= fgets($stderr_stream, 4096);
    }

    if(empty($output)) {
        $start_time = time();
        $max_time = 10; //time in seconds

        while (!feof($stdout_stream)) {
            $output .= fgets($stdout_stream, 4096);
            if((time()-$start_time) > $max_time) break;
        }
    }

    fclose($stdout_stream);
    fclose($stderr_stream);     

    return $output;
0

Boy, am I glad I found this! I was having the same problem. I eventually solved it this way.

Instead of:

while (!feof($stdout_stream)) {
    $output .= fgets($stdout_stream, 4096);
}

while (!feof($stderr_stream)) {
    $output .= fgets($stderr_stream, 4096);
}

Do this:

while (!feof($stdout_stream) || !feof($stderr_stream)) {
    $output_stdout .= fgets($stdout_stream, 4096);
    $output_stderr .= fgets($stderr_stream, 4096);
}

The technical details are beyond me, but it does appear that both descriptors close simultaneously, rather than one at a time.

Jared Brandt
  • 203
  • 1
  • 7