tags:

views:

262

answers:

5

Hello, I'm using php 5.2.9 on a production server, and it seems that the exec() function behaves "non-standard".

If i run exec("ls", $output, $return_var) then $output will contain the list of files in the current folder as expected, but $return_var will be set to -1 instead of 0, as expected. I'm using the $return_var to determine wherever the command finished successfully, and on every other server tested this works as expected:)

Anyone ever hit a situation like this?


edit:

<?php
$command = "asd";

$t1 = time();

$output = Array();
$result = -5;
$r = exec($command, $output, $result);
$t2 = time();

echo "<pre>";
var_export(Array(
    'command'=>$command,
    'result'=>$result,
    'output'=>implode("\n", $output),
    'r'=>$r,
    't2-t1'=>$t2-$t1,
));
echo "</pre>";

Whatever command i put in $command, $result will always be -1, even for nonexistent commands...this is very weird

A: 

Make sure you're not running in safe mode and that exec isn't listed in disable_functions in php.ini.

Either of these situations would cause exec() to fail, though I think a notice would be raised.

meagar
i'm not running in safe mode(you cant use exec in safe mode) and there no disabled function.
Quamis
@Quamis - I don't think it applies in your case, but you can use exec in safe mode. What happens then is that with safe mode enabled, the command string is escaped with escapeshellcmd(). - http://php.net/manual/en/function.exec.php
Peter Ajtai
A: 

Assuming the system returning $result == -1 is Unix-like based (I don't know how would behave Windows with the same code)

The PHP (5.2.9) exec() function does not call the C exec() primitive (which returns -1 if it could not replace/execute the process, which is not the case here). Instead it calls popen() that creates a pipe, performs a fork() and execute a shell with your command. The return_value, -1, is not the direct result from a C primitive, but rather is built by PHP internally, depending on the way your command was processed. In other terms, the "ls" command may have been well executed, while for instance PHP could not close properly the pipe.

Looking at the C code, in ext/standard/exec.c, there could be two reasons why the return code is -1, triggered by an error ; the 2nd one happens after the popen() call

  fp = VCWD_POPEN(cmd_p, "r");

  if (!fp) {
       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
       goto err;
  }
  // ...
  err:

  pclose_return = -1;
  goto done;

However in this case, you wouldn't see the result, and the log would show an error.

Later, the return_value is set via the line

  pclose_return = php_stream_close(stream);

Looking at _php_stream_free() (php_stream_close() is a macro replaced with _php_stream_free()), the most likely candidate that could return -1 is

  ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC);

Which in turn calls indirectly the C primitive pclose(). According to the manual

The pclose() function returns -1 if wait4(2) returns an error, or some other error is detected.

There seem to be an error detected during the closing of the pipe, that does not prevent the resulting data to be set. To find the reason rigorously, one need to check the operating system setup and logs, the PHP configuration and compilation parameters.

I would recommend

  • to apply patches for your OS, and maybe update to a more recent version (if applicable),
  • to update PHP to 5.3.3 (latest as of now) since the PHP exec() code changed significantly.

Be aware that there were changes related to the PHP suhosin module in the version 5.3 that enhance by default the security when running PHP files.

ring0
Yes it was "unix-like" (honest,ly i have no ideea if it was Linux or a unix flavor).nice answer. The problem got fixed by the server admin, but i don't know what he did to break or fix it...The thing was that the server was configured in a very very restrictive manner. I had access to very little commands(for example php-cli was not available through SSH, and lots of other commands.
Quamis
I'm accepting your answer as its the most complete and i assume that the server admin pathed the OS.
Quamis
A: 

Can we get the output of strace'ing the PHP process? That will likely contain the answer we're looking for.

Also 5.2.14 is the newest of the 5.2 series. Any chance you could try it there? If you're on a shared hosting provider, you can still likely get it to run locally to see if the behavior changes.

Rob Paisley
A: 

Hi -

I tried it out on two different Linux PC's (PHP 5.03 and PHP 5.2.10) - both worked just fine.

PHP 5.2.10 example:

array (
  'command' => 'ls',
  'result' => 0,
  'output' => 'atmail
...
vhosts',
  'r' => 'vhosts',
  't2-t1' => 0,
)

I'd double-check for any security-related directives in your php.ini file, check file permissions in the directory you're trying to search, and see if you have SELinux and/or AppArmor running.

You might also consider an altenrative, like opendir()/readdir ().

IMHO .. PSM

A: 

Seems like the problem got fixed by the server admin. I have no idea what he did, but it now works. The thing is that the server admin is pretty "strict" and maybe he got a little to restrictive with some system config. From the SSH shell for example i could not see where the php binaries were installed. I'm pretty sure that the SSH shell was chrooted, and also the webserver (either that or they were totally different servers, but i don't know how was that possible without using any kind of mount)...

Quamis