PHP CLI errors from unix commands - php

I am writing php script, which will be used for making sites from "standart" site.
There's a lot of unix shell commands, and I've found the problem with displaying errors.
Example: I need to check that the site folder doesn't exist yet.
$ls_newsite = exec('ls /vhosts/'.$sitename, $output, $error_code);
if ($error_code == 0) {
Shell::error('This site already exists in /vhosts/');
}
Shell::output(sprintf("%'.-37s",$sitename).'OK!');
So, I can handle error, but it will be display anyway.
php shell.php testing.com
Checking site...
ls: cannot access /vhosts/testing.com: No such file or directory
testing.com.................................OK!
How can I prevent displaying? Thanks

You don't need the output from these CLI calls, just the error code. So direct your output to /dev/null (otherwise PHP will print whatever goes to stderr unless you use proc_open and create pipes for each of these - overkill).
$ls_newsite = exec('ls /vhosts/' . $sitename . ' > /dev/null 2>&1', $output, $error_code);
That will work without giving you any output.
Now, on to a few other issues:
Use escapeshellarg for anything you're passing to a shell command.
A better way to write this same code is:
$ls_newsite = exec(sprintf('ls %s > /dev/null 2>&1', escapeshellarg('/vhosts/' . $sitename)), $output, $error_code);
Be 100% sure that you need to use console commands. There are PHP equivalents for most file-based console commands (stat, file_exists, is_dir, etc) that would make your code both more secure and would allow it to be platform-independent.

Related

PHP System Call Always Fails

(Seemingly) no matter what command I try to run with PHP's system function, the command always does nothing and then fails with exit code 1. For example,
system("echo 'this should definitely work'", $retval);
sets $retval = 1. So does
system("dir", $retval);
as well as running an executable that I've written; when I run
vfmt -h cat.v
from cmd.exe the command works and returns with exit code 0, but running
system("vfmt -h cat.v", $retval);
again sets $retval = 1. This vfmt.exe file is in the same directory as the src.php script that is attempting these system calls.
I am nearly at my wit's end trying to figure out what's wrong. What could possibly be causing this issue?
You should check your php.ini for line like the next:
disable_functions =exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
^^^^ ^^^^^^^^^^^^^^^^^^
and such.
Also check your "safe mode" status, (php ver. < 5.4) if you have enabled it you can only execute files within the safe_mode_exec_dir and so on...
More information in the doc and for the execution of commands here and especially for system here.
echo is invariably a shell internal command, not a separate executable. You need something more like
system('/bin/sh -c "echo \'foo\'"'); // unix-ish
system('cmd.exe /c echo foo'); // windoze
Most likely issue is that the "working directory" is not what you expect.
This is hidden as you're checking return value and not screen output; they are different.
Firstly, to monitor: try using exec http://au2.php.net/manual/en/function.exec.php as this will help you debug by giving both screen output and return value.
Secondly, to fix:
1 - Specify full paths to both the executable and included files. Assuming this is Windows (you refer to cmd.exe) then run
system("c:\PathToPhpFile\vfmt -h c:\PathToCatVFile\cat.v, $retval);
or
2 - Change the working directory before calling system() or exec() by using chdir() first.
(Or you can do the cd in a batch file (windows) or concatenate the commands in Linux; but this is less portable.)
You need to redirect the output of the command you're running with system() in order for it to run in the background. Otherwise, PHP will wait for program execution to end before continuing.
system('mycommand.exe > output.log', $retval);
system('mycommand.exe 2>&1', $retval);
Not sure how you're invoking your script, but perhaps you're running into a PHP max_execution_time (or related) limit. Something to investigate. Also, since you're running on windows, you may need to use absolute paths with your commands when executing system calls as the file you're invoking may not be in any defined PATH.

PHP get what printed on command line to a variable

I have the following PHP code:
<?php
$video = "C:\Users\Administrator\myVideo\processing\video.mp4";
$cmd = 'ffmpeg -i "' . $video .'" 2>&1 | wtee buffer.txt'; // wtee is a Windows version of tee
exec($cmd);
echo($cmd);
?>
If I run aa.php, which contains the above code, it won't work.
But if I run the $cmd that is being echoed out on the command prompt directly, the file buffer.txt is created and works.
I want to get the output of this:
$cmd = 'ffmpeg -i "' . $video .'"'
Into a variable like, e.g. $output.
This code, so far, prints blank:
exec($cmd,$output,$result);
print_r($output);
Like you mentioned, that your code works directly from command line but not from PHP CLI, is most likely because of log file permissions, which means, you must create a log file with correct permissions first, and then write your output there!
For example, from command line (not PHP) make sure that you delete your log file buffer.txt and run your PHP code again. Most likely it will not work as your file doesn't exist. Remember, when you run that command from command line directly, your log file, which is buffer.txt is created with special permissions (not only read/write, but also group and owner (Linux)). When PHP creates a file, by default, it uses nobody nobody for owner/group and you already have a log file (as you ran that same command from command line), which is created by other user (I suppose administrator), as a result you can be sure that PHP will not be able to use it, just because of the wrong file permissions. Try to see what is on your current log file owner/group by executing ls -ls in Linux or DIR /Q in Windows to get the idea.
Besides everything, make sure that you're not running PHP in safe mode, as a result exec might not work as you expect it, because it's disabled if PHP is in safe mode.
shell_exec() (functional equivalent of backticks)
This function is disabled when PHP is running in safe mode.
exec()
You can only execute executables within the safe_mode_exec_dir. For practical reasons it's currently not allowed to have components in the path to the executable. escapeshellcmd() is executed on the argument of this function.
You can check your server's PHP settings with the phpinfo() function.
Your code should rather look like this:
exec("$cmd 2>&1", $output);
log_conversion($config['logs_path']. '/' .$video_id. '_log.txt', $cmd);
log_conversion($config['logs_path']. '/' .$video_id. '_log.txt', implode("\n", $output));
What does that mean? If you say 2>&1 then you are redirecting stderr to wherever stdout is currently redirected to. If stdout is going to the console then stderr is, too. If stdout is going to a file then stderr is as well.
Please read more about command redirections in articles Using command redirection operators for Windows or All about redirection for Linux.
Your log conversion function, could be something like this:
function log_conversion($file_path, $text)
{
$file_dir = dirname($file_path);
if( !file_exists($file_dir) || !is_dir($file_dir) || !is_writable($file_dir) )
return false;
$write_mode = 'w';
if( file_exists($file_path) && is_file($file_path) && is_writable($file_path) )
$write_mode = 'a';
$handle = fopen($file_path, $write_mode);
if( !$handle )
return false;
if( fwrite($handle, $text. "\n") == FALSE )
return false;
#fclose($handle);
}
It's very important to make sure that the log file you create is writable and has correct permissions!
I personally delete the log file in case it exists, to make sure that I have recent and reasonable log file.
I bet it will work either like this or with small tweaking!
If it doesn't work for you, please let me know and I will elaborate!
exec requires an array for the output variable.
So try:
$output = array();
exec($cmd, $output, $result);
That will give you an array where each element is a line returned by your command.

Starting a daemon from PHP

For a website, I need to be able to start and stop a daemon process. What I am currently doing is
exec("sudo /etc/init.d/daemonToStart start");
The daemon process is started, but Apache/PHP hangs. Doing a ps aux revealed that sudo itself changed into a zombie process, effectively killing all further progress. Is this normal behavior when trying to start a daeomon from PHP?
And yes, Apache has the right to execute the /etc/init.d/daemonToStart command. I altered the /etc/sudoers file to allow it to do so. No, I have not allowed Apache to be able to execute any kind of command, just a limited few to allow the website to work.
Anyway, going back to my question, is there a way to allow PHP to start daemons in a way that no zombie process is created? I ask this because when I do the reverse, stopping an already started daemon, works just fine.
Try appending > /dev/null 2>&1 & to the command.
So this:
exec("sudo /etc/init.d/daemonToStart > /dev/null 2>&1 &");
Just in case you want to know what it does/why:
> /dev/null - redirect STDOUT to /dev/null (blackhole it, in other words)
2>&1 - redirect STDERR to STDOUT (blackhole it as well)
& detach process and run in the background
I had the same problem.
I agree with DaveRandom, you have to suppress every output (stdout and stderr). But no need to launch in another process with the ending '&': the exec() function can't check the return code anymore, and returns ok even if there is an error...
And I prefer to store outputs in a temporary file, instead of 'blackhole'it.
Working solution:
$temp = tempnam(sys_get_temp_dir(), 'php');
exec('sudo /etc/init.d/daemonToStart >'.$temp.' 2>&1');
Just read file content after, and delete temporary file:
$output = explode("\n", file_get_contents($temp));
#unlink($temp);
I have never tried starting a daemon from PHP, but I have tried running other shell commands, with much trouble. Here are a few things I have tried, in the past:
As per DaveRandom's answer, append /dev/null 2>&1 & to the end of your command. This will redirect errors to standard output. You can then use this output to debug.
Make sure your webserver's user's PATH contains all referenced binaries inside your daemon script. You can do this by calling exec('echo $PATH; whoami;). This will tell you the user PHP is running under, and it's current PATH variable.

PHP: run "mkdir" and get errno

I need to execute mkdir command (e.g. via PHP's exec command). How do I access standard error (e.g. EACCES, see: http://pubs.opengroup.org/onlinepubs/009695399/functions/mkdir.html). Suggestions? Thanks.
UPDATE:
I added 2>&1 to my command:
$command = "sudo mkdir /home/test 2>&1";
$output = array();
$return = 0;
exec($command, $output, $return);
Without it, I used to get either 0 (success) or -1 (error). Now, I get a 1 during one of my tests -- and I think it's because the directory I am trying to create already exist. It would seem that 1 maps to EEXIST. How do I map the rest of the errno?
You cannot.
You want to catch the error of the mkdir function via errno, but your call to PHP's exec() means that you will instead deal with the exit code of the external program mkdir.
It is the difference between:
http://pubs.opengroup.org/onlinepubs/009695399/functions/mkdir.html (which you cite)
and
http://pubs.opengroup.org/onlinepubs/009695399/utilities/mkdir.html (which you exec)
You're probably looking for proc_open, which gives you the ability to work directly with stdin, stdout and stderr as PHP streams, meaning you can use the normal file reading and writing functions on them.
Please be sure to read more details about the proc_ family tree on the proc_close and proc_get_status pages.
You might have more luck working with the text of the error as provided to stderr instead of working with exit codes.

Problems when trying to exectue exec("unix2dos xxx") in PHP/Apache

In a previous post, I was trying to update the encoding for a download file from php. One of the suggestions was to run the unix2dos command before sending the file to the user. This works great when I run the command on the linux box, but when I try and run the command from php I get nothing. Here is what I tried:
$cmd = "unix2dos -n $fullPath $downloadFile";
echo exec($cmd, $out, $retVal);
This displays nothing to the screen, $retVal is 0, and $out is an empty string.
echo system($cmd, $retVal);
This displays nothing to the screen, $retVal is 0.
echo shell_exec($cmd);
This displays nothing to the screen.
I have also tried escaping the command and it parameters like:
$cmd = escapeshellcmd($cmd);
and
$cmd = "unix2dos ". escapeshellarg("-n \"$fullPath\" \"$downloadFile\"");
Please let me know if you see something that I am doing wrong.
Thanks!
Edit: Here is some info that may be helpful.
unix2dos version: 2.2 (1995.03.31)
php version 5.2.9
Running in apache 2 on in Redhat Enterprise Linux 4
Have you considered a pure PHP solution?
<?php
$unixfile = file_get_content('/location/of/file/');
$dosfile= str_replace("\n", "\r\n", $unixfile );
file_put_contents('/location/of/file/', $dosfile);
?>
Something like that should do it, although untested :)
Shadi
See which user the PHP exec command is running as:
<?php system('whoami'); ?>
If this command fails then you likely do not have permission to use exec() or system(), so check your INI files. But be sure to check the correct ones! On Debian systems there are separate Apache and CLI INI files stored at /etc/php5/apache/php.ini and /etc/php5/cli/php.ini respectively. Sorry I do not know the locations for RedHat.
If the whoami command succeeds, make sure that the unix2dos command can be run by the user that is shown, and that the same user is allowed to make changes to the files in question by using chmod or chown.
Are you using the full path to unix2dos? Perhaps the executable is in your path for your shell but not in the path that PHP is using.
My implementation of unix2dos produces no output. If the return value is 0 then the command succeeded and your file has been updated.
The only other thing I see is the -n option which my version doesn't seem to have. You should probably check your man page to see what options it supports
unix2dos does not display the file it converts. Therefor you must display it yourself. A very basic way to do it could be :
$cmd = "unix2dos -n $fullPath $downloadFile";
echo exec($cmd, $out, $retVal);
include "$fullPath."/".$downloadFile;
Using include is pretty dirty but quick and easy. A cleaner way would be to use fopen and read the file then display it.
You'd better create a function that enclose all the operation : conversion + display so you'll have everything at hands.
But, If I were you, I'd prefer to not use exec at all and use FileIterator with a trim on every line so you will not have to care about the carriage return nor deal with a hazardous shell binding.
Not sure about your exact problem, but debugging suggestion:
Try first setting $cmd to ls. See if that works. Then try using /bin/ls (use the full path.)
If those don't work, then there might be a problem with your PHP configuration - there might be a safemode parameter or something which disallows the use of exec(), shell_exec(), or system() functions.
I got the source code from here.
http://www.sfr-fresh.com/linux/misc/unix2dos-2.2.src.tar.gz
I compiled it and then ran the tool. This was my output:
rascher#danish:~/unix2dos$ ./a.out -n 1.txt 2.txt
unix2dos: converting file 1.txt to file 2.txt in DOS format ...
I think the problem is this: the program writes all of its output to stderr, rather than stdout. If you look at the source code, you can see "fprintf(stderr, ...)"
As far as I know, PHP will only read the part of your program's output that is sent to STDOUT. So to overcome this, it seems like you have to redirect the output of your program (unix2dos uses stderr) to stdout. To do this, try something like:
$cmd = "unix2dos -n $fullPath $downloadFile 2>&1"
The "2>" means "redirect stderr" and "&1" means "to stdout".
In either case, I would imagine that the file was converting properly, but since you weren't getting any of the expected output, you thought it was failing. Before making the change, check on the output file to see if it is in DOS or UNIX format.

Categories