exec always returns -1 (or 127) - php

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

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.

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.

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.

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

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

Related

In PHP shell_exec function with command "node welcome.js" does not return value

The welcome.js code is given below
console.log('Wellcome');
and the php file code is given below
$op = shell_exec('node welcome.js').PHP_EOL;
echo $op
If I run the php file in command line it print Wellcome but when I run from browser it does not print any output.
There are most likely errors here that you're not seeing.
Set 'error_reporting' to -1 and 'display_errors' to 1 in your php.ini and be sure to restart your webserver/fastcgi-listeners. This is more reliable than using ini_set() and error_reporting() in the script, which will fail if there are parse errors...see php:errorfunc.configuration for more detail.
Check the appropriate error log (depending on your settings and the Server API, this can be the httpd's error log, syslog, some independent file, or even going nowhere right now). Again, php:errorfunc.configuration can help you get things configured correctly, or suss out the current configuration.
The $PATH (or %PATH% on Win32) for an interactive login session is usually dramatically different than that of a running daemon. Try specifying the full path to the node binary.
I don't know off-hand which file-handle node's "console.log()" goes out to. Assuming you're using a bourne-style shell (such as bash) for the subshell here, you can try piping stderr to stdout, using something like: $op = shell_exec('/foo/bin/node welcome.js 2>&1').PHP_EOL; echo $op;
Make sure that 'welcome.js' is where you think it is in relation to the current working directory of your PHP process (although it's likely that node would warn you via one of the previous suggestions if this were not the case, it seemed worth pointing out as a potential pitfall.)

Cant get php exec to work

I've been fighting with this for a few hours now, and I can't seem to work it out.
tried exec(), shell_exec(), and system(). Nothing works.
I have this:
exec("/usr/bin/php /var/www/vhosts/domain.com/httpdocs/shell/send.php >> /var/www/vhosts/domain.com/httpdocs/shell/paging.log &");
send.php simply has:
echo 'works';
But nothing shows up in the log. I've googled and read stuff on here, but I can't find anything to help.
I'm running php v.5.3.8.
safe mode is on
I'm pretty sure that is the path to php, but can't really find out how to find it, so I'm going on phpinfo().
exec('whoami'); does nothing. Is it suppose to show in the browser? or email you something?
any ideas?
According to PHP Manual for exec function:
When safe mode is enabled, you can only execute files within the
safe_mode_exec_dir. For practical reasons, it is currently not allowed
to have .. components in the path to the executable.
Check http://php.net/manual/en/ini.sect.safe-mode.php#ini.safe-mode-exec-dir
Also, be aware that the web server user must have permission to write in the log file.
EDIT: To turn safe mode off, check not only php.ini file but also virtual hosts specific configurations in your web server, whether it is Apache, NginX or other. If you use Plesk, look in vhosts for httpd.include, and make sure that safe_mode is set to off there as well.
> The last line from the result of the command. If you need to execute
> a command and have all the data from the command passed directly back
> without any interference, use the passthru() function. To get the output
> of the executed command, be sure to set and use the output parameter.
I am not looking for output, and the last two arguments of exec() are optional. I my case what I really need is to be able to open a folder on the desktop. This exact syntax worked very well in MAMP_PRO_1.9.6, but no longer works in MAMP_PRO_2.0.5 (it's broken)
<?php
exec("open /path/to/any/folder"); // BROKEN in Mamp Pro 2.0.5
?>
To get the output, you need to pass a second parameter, or you can get the last line of output by echo'ing it.
From the PHP manual:
string exec ( string $command [, array &$output [, int &$return_var ]] )
Return Values:
The last line from the result of the command. If you need to execute a command and have all the data from the command passed directly back without any interference, use the passthru() function.
To get the output of the executed command, be sure to set and use the output parameter.

how to test if PHP system() function is allowed? and not turned off for security reasons

I would like to know how to test if system() or exec() is allowed on a server. I keep getting this error "Warning: exec() has been disabled for security reasons in ..."
I understand that the safe_mode function is depreciated in the php version my provider runs (5.3.3) so i cant use a get_ini('safe_mode') check.
What else to do?
I use this for a backup script. if the provider allows system, the script makes a tar file and mails it to me whenever a user logs in.
Thanks in advance.
Well, there's only two ways it can be disabled: safe_mode or disable_functions.
So you can do a check like:
function isAvailable($func) {
if (ini_get('safe_mode')) return false;
$disabled = ini_get('disable_functions');
if ($disabled) {
$disabled = explode(',', $disabled);
$disabled = array_map('trim', $disabled);
return !in_array($func, $disabled);
}
return true;
}
Oh, and function_exists should return true, since it's a core function (otherwise you could forge a core function and cause some real havoc on a host)... Therefore is_callable should also return true (since the function does exist). So the only ways to tell, are to check the ini settings, or to actually call it...
Edit: One other thing to note, there are several of ways to execute shell commands. Check out:
Program Execution Functions
Backtick Operator
Testing for disabled functions and the presence of safe mode as shown by #ircmaxell is arguably the easiest way to go.
If you want to find out 1000% reliably whether execution of system commands is possible - there may be security patches like Suhosin that block this on another level - try to exec() an external command that is bound to work on all systems (including Windows), and is extremely unlikely to fail even if user rights are very tight.
Say
cd .
this should work (i.e. not return false, and return an error level code of 0) at least on all Linux, Windows and Unix flavours including OS X.
function_exists() doesn't works for this situation ?
http://fr.php.net/function_exists
exec() returns false if it fails, or a success message string if it succeeds... so the following should work:
if(!exec('cd .')){ die('ERROR: Exec is not available!!!'); }
Replacement for 'cd .' can be any function you know to work on the system.

Running lame from php

I am trying to run lame from a php script.
I have tried these, but no luck, I don't get anything returned! Any ideas?
system('lame', $returnarr);
system('lame --help', $returnarr);
exec('lame', $returnarr);
passthru('lame', $returnarr);
even this one returns nothing:
exec('which lame', $returnarr);
I am on OSX and final deployment will be on Linux. Do you have better suggestions for an automated wav->mp3 conversion?
From php, should I execute a bash script that executes Lame?
Try something like this:
$output = array();
$result = -1;
exec('`/usr/bin/which lame` --help 2>&1', $output, $result);
var_dump($output, $result);
$output should be an array of lines contained in the output
$result should be an integer result code. 0 is typically success, >=1 is an error (specific codes are application dependant).
The 2>&1 part will redirect STDERR to STDOUT ($output) which would normally be dropped. So if it's erroring out, you should be able to see the error (hopefully).
If you get -1 for the dump of $result, there's a fundimental problem, because that's not a valid result code (it likely means that exec is disabled, or the process you're trying to run is restricted because of permissions errors or the such)...
If you feel a need for more convenient way to work with lame, I would recommend to use phplame wrapper. Install PHP LAME wrapper using Composer:
{
"require": {
"b-b3rn4rd/phplame": "dev-master"
}
}
set error reporting on and check if you can do exec's. By default most systems wont allow it, it's a serious security liability. You've got to explicitly allow execs in php.ini.
Might be a $PATH problem. Try specifying the full path to lame, ie. /usr/local/bin/lame.

PHP #exec is failing silently

This is driving me crazy. I'm trying to execute a command line statement on a windows box for my PHP web app. It's running on windows XP, IIS5.1. The web app is running fine, but I cannot get #exec() to work with a specific contactenated variable. My command construction looks like this:
$cmd = ($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.quote($filename));
This command does not work as is above, when it generates the following string:
svn --non-interactive --config-dir /tmp cat "file:///c:/temp/test/acccount/dbo_sproctest.sql" -r 1 > "C:\Inetpub\sites\websvn\temp\wsv5B45.tmp"
If I copy/paste this to my own command line, it works fine.
If I hard code that very same path instead of adding it with the variable, it works! I've tried with and without quotes around the file name. I've tried with and without quotes around the entire command. I've tried other directories. I've tried passing an output paramter to exec(), and it comes back empty (Array () ). I've tried redirecting the output of the error stream of the command to a file, and that error output file never gets created.
The only thing I can possibly concieve of is that exec() is failing silently. What on earth am I doing wrong here? If I hard code the file path, using the same dir structure and filename, it works fine. If I don't, it doesn't.
Maybe the slashes () in the file path aren't being escaped properly, but when I do it manually with single quotes they are not considered escape sequences??
UPDATE:
I took the # off of exec, and still not seeing any errors.
I gave the full path to SVN, still no luck. It should be noted that the command worked fine before with the non-full path SVN so long as I manually specify the file destination for cat.
Update 2: RE: Kieth
I'm calling exec by trying both:
exec($cmd);
or
exec($cmd, $out);
My php.ini already had safe_mode = 0.
I added error_reporting(E_ALL); and didn't see anything new
If I echo (or print_r) my exec call, I am not actually seing anything
If I echo (or print_r) my exec call when included an output var, I get an empty arr
Update 3
I tried both escapeshellcmd and escapeshellarg to no avail (good idea though).
I should add that the file is being created through invoking
tempnam("temp", "wbsn");
The fact that it works just fine if I manually specify the string instead of letting it be generated by tempname seems to suggests that the source of the problem, but I can't figure out how. I did a comparison of the manual string with the one generated, and it came back as a match.
#exec will always fail silently, because # is PHP's error suppression operator.
Not sure if this will help you since you're on Windows and I'm on Linux, but I ran into this same problem of silent errors from PHP exec(). I figured out that the command I was attempting to issue (nconvert) sends its error messages to the standard error stream, not standard out. So I added
2>&1
at the end of the command line to redirect my error back to the standard stream. Then I could see that nconvert was giving me a permission denied error.
It could be that the PATH isn't the same from your php script vs your user account. Try removing the # and see if it's trying to throw an error.
In addition, you may want to try putting the full filesystem path to the SVN executable.
Well, if removing # isn't working, try including this at the start of the piece of code you're running:
error_reporting(E_ALL);
That'll turn on full error reporting, just in case you have it turned down or disabled in your php.ini file.
I don't suppose you could show us how exactly you're calling exec()? Could you also check to make sure you're not accidentally running the script in safe mode? Are you echoing out what exec() is returning, and if so, what is it returning?
I'm not a big fan of adding another response, but if I just edit my previous response, you may not see it.
PHP has some special escaping commands for shell scripts: escapeshellcmd and escapeshellarg.
I think you should be able to use escapeshellcmd around your entire $cmd, but I'm not sure.
Have you tried echo exec("dir") or something simple to see if exec() is working at all?
I don't know what going on, but I at least have a workaround.
This works:
$tmp = tempnam("./", "wbsn");
$filename = dirname($tmp).'\\temp\\'.basename($tmp);
This, however, does not, but I would have expected it to generate the same path (diff file name since it's a new tempnam()).
$tmp = tempnam("temp", "wbsn");
Also, this does not work, which I also would expect to generate the same thing:
$tmp = tempnam("temp", "wbsn");
$filename = dirname($tmp).'\\'.basename($tmp);
All 3 of these solutions appear to generate the same file paths, but only the first one actually works when used in my exec. I have no clue why it does not.
Visual inspection (echo) of all 3 of these appear to generate the same paths (with the exception of filenames differing, of course). A string comparison of dirname() of each of these 3 shows as a match. I have no clue what the deal is, but the first one is a workaround.
A very useful trick when debugging shell exec problems is to place an "echo" at the beginning of the command. Make sure that you can view the standard output somewhere.
This will let you examine the command for any obvious problems. Perhaps it has a wildcard that is expanding unexpectedly, or perhaps the shell quoting is not exactly right. The echo will let you see this, and you can cut and paste the echoed command into another shell to see if it is working properly.
My advice will be to switch from WIN, IIS to linux, but as alternative you can try this:
function exec_alt($cmd) {
exec($cmd, $output);
if (!$output) {
/**
* FIXME: for some reason exec() returns empty output array #mine,'s machine.
* Somehow proc_open() approach (below) works, but doesn't work at
* test machines - same empty output with both pipes and temporary
* files (not we bypass shell wrapper). So use it as a fallback.
*/
$output = array();
$handle = proc_open($cmd, array(1 => array('pipe', 'w')), $pipes, null, null, array('bypass_shell' => true));
if (is_resource($handle)) {
$output = explode("\n", stream_get_contents($pipes[1]));
fclose($pipes[1]);
proc_close($handle);
}
}
return $output; }

Categories