Starting shell process under www-data (apache2, php) - php

I need to start php process from shell on remove server with some arguments, so i thought that it should be a nice idea to make REST API, that executes some function when user performs GET request.
I wrote a simple bash script for testing and figured out that command-line argument is not being specified, when calling this script from website:
shell_exec('/var/www/test.sh 123')
Bash script source:
#!/bin/sh
echo $1;
When calling this bash script from root (or other existing user) it correctly shows argument it has received. When i call this script from website (that is running under user www-data under apache2), it returns nothing:
Also, if i execute this bash script in my console under www-data user, it also returns nothing:
su -c '/var/www/test.sh 123' www-data
Also i've tried to start process from different user from php (is supposed that this will not work for security reasons, but just in case):
$result = system("su -c '/var/www/test.sh 123' sexyuser", $data);
// var_dump($result): string(0) ""
// var_dump($data): int(1)
So, what privileges should i give to www-data user to run process under php?

You should let php run the script and handle the results
check php.net on exec for example http://www.php.net/manual/en/function.exec.php
//called by example.com/myshell.php?day=today&k=y&whatever=youwant
$arguments = implode(" ", $_GET);
$lastline_of_exec_result = exec ( "/your/command.sh ".$arguments); //sh called with today y youwant
echo $lastline_of_exec;
Where $arguments are the stringified list of ALL information your script got from GET arguments
if you want a ore precise in and output, try this:
//called by example.com/myshell.php?day=today&k=y&whatever=youwant
$argument = $_GET['whatever'];
$output = array();
$last_line = exec("your/command.sh ".$argument, &$output); //sh called with youwant
foreach($output as $line)
echo $line."<br/>".PHP_EOL;
or of course (with shell_exec)
$argument = $_GET['whatever'];
$output = shell_exec("your/command.sh ".$argument);
echo "<pre>".$output."</pre>";
make sure (shell_)exec is not listed under disable_functions in your php.ini

Related

PHP - Get amount of open files of current process

For monitoring purposes, I want to forward the amount of open files of the current process to our monitoring tool. In shell, the following command can be executed to get the desired information: ls /proc/PROCES_ID/fd | wc -l where PROCES_ID is the current process id. Is there a way to get this information natively in PHP?
To run any shell commands from within php script and get the output:
From PHP manual on exec() command
exec(string $command, array &$output = null, int &$result_code = null): string|false
For your command:
$output = []; // this array will hold whatever the output from the bash command
$result_code = null; // this will give you the result code returned, if needed
$command = '/proc/PROCES_ID/fd | wc -l'; // command to be run in the bash
exec($command, &$output, &$result_code);
//now you can look into $output array variable for the values returned from the above command
print_r($output);
But as mentioned in the comment, using bash script over php should be preferred if feasible.

php exec in the background with WAMP on Windows

with the following code i can call a php script and pass some variables into it
$cmd = 'php -f C:/wamp/www/np/myphpscript.php '.$var1;
exec($cmd);
this way my called script works, but , i need that process to be in the background , i dont want to wait for the script to finish, is there any way of doing that using wamp on windows ?
been doing some reading and some add a & at the end of the command, or a > NUL , now i noticed some of them are for linux , is there such a command for wamp on windows ? if there is please share it
EDIT: Due to the way the exec() command waits for the program to finish executing, it's very difficult to do this with vanilla exec(). I came across these solutions, and this one should work:
$rshell = new COM("WScript.Shell");
$rexec = $rshell->Run("php -f C:/wamp/www/np/myphpscript.php ".$var1, 0, false);
The WScript.Shell->Run command takes 3 arguments: the command (you can optionally add output redirection), window mode (0 = hidden), and wait it should wait to finish. Because the 3rd argument is false, this PHP should return immediately.
Original Solution: As this post suggests, you should try START /B cmd. It is virtually the Linux equivalent of cmd & in that it runs the command asynchronously, in the background, without user interaction or opening a new shell.
Because this will return immediately, PHP won't wait for it to finish, and the exec() command will not receive any output. Instead, try using shell output redirection. Your PHP given code would look like this:
$cmd = 'start /b "" php -f C:/wamp/www/np/myphpscript.php '.$var1.' >C:/wamp/www/np/output.txt';
exec($cmd);
Don't know what you are running and if you get a response to your command. But maybe it helps if you open a tab for each command. So you can see responses of each running script and at the end you can call javascript to close the tab.
You must set the variable php on windows environment !
If you have already done so skip the tutorials steps:
1. Open:
My Computer => Properties => Change Settings
2. Select the tab: Advanced
3. Click Environment Variables: Variable system
4. Click the button New
Add the name of the environment variable. Example = php
Add the path to executable php.exe. Example = D:\xampp\php\php.exe
Create a file myscript.php
The variariaveis $argc and $argv are native php.
You will notice that $ argc always carries the same value as the
result of calling count ($argv) in any case $argc is the standard
used and is a few milliseconds faster by being in memory (if that
makes any difference in performance your script).
//\n skip line
echo "\n\n";
//echo test debug
echo "Print Total Args : ";
//Print return variavel $argc
print_r($argc);
//\n skip line
echo "\n\n";
//echo test debug
echo "Print Array Args : \n\n";
//Print return variavel $argv
print_r($argv);
echo "\n";
// You can retrieve the arguments in the normal way.
$myvar_count = $argc;
$myvar_array_args = $argv;
Or if you want to set is not the environment variable, simply can call the path
Example: D:\xampp\php\php.exe myscript.php argument1 2 3 4 5
Retorn the Prompt in Windows
Total Args : 5
Array Args :
Array
(
[0] => test.php
[1] => argumento1
[2] => 2
[3] => 3
[4] => 4
)
I hope this helps! See you later!

How to run a linux command to compile a c program, from php script

I am trying to make an online judge for c programming. When user enters the c code and submits it, my form redirects to judge.php which is the action file for the form.
Here is what I have written in judge.php
<?php
$text=$_POST['code'];
//echo $text;
$var_str = var_export($text, true);
file_put_contents('code.c', $text);
$ans=exec('pwd');
$ans= exec('gcc code.c');
echo $ans;
?>
I have captured user input in $text and wrote it to a c file(code.c). Till now, it is fine.
But exec(gcc code.c) is not working and not giving any output. I tried other linux commnads like pwd, date, etc. They are working fine. What may be the reason for this and how to fix it?
It is not a directory issue i tried exec(pwd) and it gave the output as the same directory in which code is present.
I tried to run same code.c file from terminal and it is running fine. So, it is also not a 'permission' problem.
One more thing, how to echo the error message generated if any exec() command is not working properly?
After getting suggestion from the answer below, i tried
$cmd="gcc -std=c99 code.c -g -Wall mysql_config --libs --cflags -o db_obj.o --pedantic";
exec($cmd,$out,$status);
But it is also not working. The status returned is 1
Most probably it is permission issue. "whoami" says nobody. Please tell how to change the owner from nobody to root or how to assign the permission to execute gcc from nobody
Three main aspects to my answer
improper use of the exec function.
Look at the man pages. First, the exec function's signature is:
string exec ( string $command [, array &$output [, int &$return_var ]] )
So exec can take up to 3 arguments. It returns the last line of the command's output, like the docs state quite clearly:
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.
So in your case:
$lastLine = exec($command, $fullOutput, $status);
Is what you're looking for. If $status is anything else than 0, your command was unsuccessful. That's what you should check to react accordingly.
The full output of any command can be found in $fullOutput as a line-per-line array.
Output like:
all went well
except for this
Will look like this in the $fullOutput array:
array('all went well', 'except for this');
permissions can be an issue, still.
You say permissions aren't likely to be the cause of the problem, because you can run gcc from the command-line. All fine and dandy, but what user is running the PHP script on the server?
In the case of web-servers, that user is often called nobody, apache or something, and that user is very likely not permitted to run gcc. It's PHP that runs a new instance of whatever default shell it has set up (bash, probably), and it's PHP's user that logs in to that shell, and it's that user that is calling gcc...
Know who you are, and what groups you belong to. Try adding this to your script:
echo 'Script is running under user: ', exec('whoami'), '<br>', PHP_EOL;
echo 'member of the following groups: ', exec('groups'), '<br>', PHP_EOL;
And before you ask: yes, those are comma's... no need to concatenate, you can pass multiple variables/values to echo, separated by a comma. It's actually faster (think of it as C++'s std::cout << some_var << another_var;)
general issues + security
This all said and done: compiling C code from a php script isn't as simple as you seem to think it is. Suppose I were to write this:
#include <stdio.h>
#include <time.h>
int main ( void )
{
time_t t = time(NULL);
if (t%2)
{
float val = (float) t/2.0;
//do stuff with float...
}
else
{
unsigned long long val = t/2;
//do stuff with unsigned long long...
}
}
Your gcc test.c command would fail, because you failed to pass the argument -std=c99, for example.
If I wanted a script to compile a given file, I'd also expect that script to allow me to choose which arguments I compiled my code with, too -g, -Wall and, not to mention: cflags and libs (the output of pkg-config or mysql_config --cflags --libs, to name a specific example I recently used).
Basically, your script simply cannot deal with my wanting to compile something with a commind like
gcc -std=c99 code.c -g -Wall `mysql_config --libs --cflags` -o db_obj.o --pedantic
Which still is a simplified version of what many compilation commands look like, especially when debugging code under development. For stable releases, you'd probably drop -g and --pedantic, but you get my point...
Just think of what it means, allowing the user to pass a set of cli arguments, along with the code. They might pass an argument like -DSOME_MACRO or -O0, which means they might also pass -O0 && rm -Rf *. That means you'll have to call escapeshellcmd or escapeshellarg. Both of which will prohibit me from passing a valid argument, being:
`mysql_config --libs --cflags`
Which contains back-ticks, and thus will be escaped.
To be frank, I struggle to see the point of this exercise... and I'm leaving a lot out, still: the dangers of compiling (let alone running) user-provided code on your machine, for example, are not to be overlooked. You can't just compile code, and run it on your server: memory leaks, segfaults... heck, pure evil code is all getting compiled on your server unchecked if this is the code you have. Really, save yourself a lot of tears, and include an iframe that loads codepad or some similar service...
Recap:
always check the man for a function, see if you're getting all information it returns
check the permissions and runtime for the user that is actually executing the commands
Never trust the network, don't blindly assume people will submit valid, harmless code for you to compile.
Don't reinvent the wheel: compilation services exist, just forward those (but ask for permission first)
Try this code to execute c program from PHP file
<?php
// used to compile the c file using exec() in php
exec('gcc helloworld.c -o helloworld', $out, $status);
if (0 === $status) {
var_dump($out);
// used to execute the c file using exec() in php
exec('./helloworld', $out, $status);
if (0 === $status) {
var_dump($out);
} else {
echo "Command failed with status: $status";
}
} else {
echo "Command failed with status: $status";
}
?>

PHP passing $_GET in the Linux command prompt

Say we usually it access via
http://localhost/index.php?a=1&b=2&c=3
How do we execute the same on a Linux command prompt?
php -e index.php
But what about passing the $_GET variables? Maybe something like php -e index.php --a 1 --b 2 --c 3? I doubt that'll work.
From this answer on Server Fault:
Use the php-cgi binary instead of just php, and pass the arguments on the command line, like this:
php-cgi -f index.php left=1058 right=1067 class=A language=English
Which puts this in $_GET:
Array
(
[left] => 1058
[right] => 1067
[class] => A
[language] => English
)
You can also set environment variables that would be set by the web server, like this:
REQUEST_URI='/index.php' SCRIPT_NAME='/index.php' php-cgi -f index.php left=1058 right=1067 class=A language=English
Typically, for passing arguments to a command line script, you will use either the argv global variable or getopt:
// Bash command:
// php -e myscript.php hello
echo $argv[1]; // Prints "hello"
// Bash command:
// php -e myscript.php -f=world
$opts = getopt('f:');
echo $opts['f']; // Prints "world"
$_GET refers to the HTTP GET method parameters, which are unavailable on the command line, since they require a web server to populate.
If you really want to populate $_GET anyway, you can do this:
// Bash command:
// export QUERY_STRING="var=value&arg=value" ; php -e myscript.php
parse_str($_SERVER['QUERY_STRING'], $_GET);
print_r($_GET);
/* Outputs:
Array(
[var] => value
[arg] => value
)
*/
You can also execute a given script, populate $_GET from the command line, without having to modify said script:
export QUERY_STRING="var=value&arg=value" ; \
php -e -r 'parse_str($_SERVER["QUERY_STRING"], $_GET); include "index.php";'
Note that you can do the same with $_POST and $_COOKIE as well.
Sometimes you don't have the option of editing the PHP file to set $_GET to the parameters passed in, and sometimes you can't or don't want to install php-cgi.
I found this to be the best solution for that case:
php -r '$_GET["key"]="value"; require_once("script.php");'
This avoids altering your PHP file and lets you use the plain php command. If you have php-cgi installed, by all means use that, but this is the next best thing. I thought this options was worthy of mention.
The -r means run the PHP code in the string following. You set the $_GET value manually there, and then reference the file you want to run.
It's worth noting you should run this in the right folder, often, but not always, the folder the PHP file is in. 'Requires' statements will use the location of your command to resolve relative URLs, not the location of the file.
I don't have a php-cgi binary on Ubuntu, so I did this:
% alias php-cgi="php -r '"'parse_str(implode("&", array_slice($argv, 2)), $_GET); include($argv[1]);'"' --"
% php-cgi test1.php foo=123
<html>
You set foo to 123.
</html>
%cat test1.php
<html>You set foo to <?php print $_GET['foo']?>.</html>
Use:
php file_name.php var1 var2 varN
Then set your $_GET variables on your first line in PHP, although this is not the desired way of setting a $_GET variable and you may experience problems depending on what you do later with that variable.
if (isset($argv[1])) {
$_GET['variable_name'] = $argv[1];
}
The variables you launch the script with will be accessible from the $argv array in your PHP application. The first entry will the name of the script they came from, so you may want to do an array_shift($argv) to drop that first entry if you want to process a bunch of variables. Or just load into a local variable.
Try using WGET:
WGET 'http://localhost/index.php?a=1&b=2&c=3'
Option 1: php-cgi
Use 'php-cgi' in place of 'php' to run your script. This is the simplest way as you won't need to specially modify your PHP code to work with it:
php-cgi -f /my/script/file.php a=1 b=2 c=3
Option 2: If you have a web server
If the PHP file is on a web server you can use 'wget' on the command line:
wget 'http://localhost/my/script/file.php?a=1&b=2&c=3'
Or:
wget -q -O - "http://localhost/my/script/file.php?a=1&b=2&c=3"
Accessing the variables in PHP
In both option 1 & 2, you access these parameters like this:
$a = $_GET["a"];
$b = $_GET["b"];
$c = $_GET["c"];
If you have the possibility to edit the PHP script, you can artificially populate the $_GET array using the following code at the beginning of the script and then call the script with the syntax: php -f script.php name1=value1 name2=value2
// When invoking the script via CLI like
// "php -f script.php name1=value1 name2=value2",
// this code will populate $_GET variables called
// "name1" and "name2", so a script designed to
// be called by a web server will work even
// when called by CLI
if (php_sapi_name() == "cli") {
for ($c = 1; $c < $argc; $c++) {
$param = explode("=", $argv[$c], 2);
$_GET[$param[0]] = $param[1]; // $_GET['name1'] = 'value1'
}
}
If you need to pass $_GET, $_REQUEST, $_POST, or anything else you can also use PHP interactive mode:
php -a
Then type:
<?php
$_GET['a'] = 1;
$_POST['b'] = 2;
include("/somefolder/some_file_path.php");
This will manually set any variables you want and then run your PHP file with those variables set.
At the command line, paste the following:
export QUERY_STRING="param1=abc&param2=xyz" ;
POST_STRING="name=John&lastname=Doe" ; php -e -r
'parse_str($_SERVER["QUERY_STRING"], $_GET); parse_str($_SERVER["POST_STRING"],
$_POST); include "index.php";'
I just pass them like this:
php5 script.php param1=blabla param2=yadayada
It works just fine. The $_GET array is:
array(3) {
["script_php"]=>
string(0) ""
["param1"]=>
string(6) "blabla"
["param2"]=>
string(8) "yadayada"
}
Use:
php -r 'parse_str($argv[2],$_GET);include $argv[1];' index.php 'a=1&b=2'
You could make the first part as an alias:
alias php-get='php -r '\''parse_str($argv[2],$_GET);include $argv[1];'\'
Then simply use:
php-get some_script.php 'a=1&b=2&c=3'
Or just (if you have Lynx):
lynx 'http://localhost/index.php?a=1&b=2&c=3'

PHP exec() return value for background process (linux)

Using PHP on Linux, I'd like to determine whether a shell command run using exec() was successfully executed. I'm using the return_var parameter to check for a successful return value of 0. This works fine until I need to do the same thing for a process that has to run in the background. For example, in the following command $result returns 0:
exec('badcommand > /dev/null 2>&1 &', $output, $result);
I have put the redirect in there on purpose, I do not want to capture any output. I just want to know that the command was executed successfully. Is that possible to do?
Thanks, Brian
My guess is that what you are trying to do is not directly possible. By backgrounding the process, you are letting your PHP script continue (and potentially exit) before a result exists.
A work around is to have a second PHP (or Bash/etc) script that just does the command execution and writes the result to a temp file.
The main script would be something like:
$resultFile = '/tmp/result001';
touch($resultFile);
exec('php command_runner.php '.escapeshellarg($resultFile).' > /dev/null 2>&1 &');
// do other stuff...
// Sometime later when you want to check the result...
while (!strlen(file_get_contents($resultFile))) {
sleep(5);
}
$result = intval(file_get_contents($resultFile));
unlink($resultFile);
And the command_runner.php would look like:
$outputFile = $argv[0];
exec('badcommand > /dev/null 2>&1', $output, $result);
file_put_contents($outputFile, $result);
Its not pretty, and there is certainly room for adding robustness and handling concurrent executions, but the general idea should work.
Not using the exec() method. When you send a process to the background, it will return 0 to the exec call and php will continue execution, there's no way to retrieve the final result.
pcntl_fork() however will fork your application, so you can run exec() in the child process and leave it waiting until it finishes. Then exit() with the status the exec call returned.
In the parent process you can access that return code with pcntl_waitpid()
Just my 2 cents, how about using the || or && bash operator?
exec('ls && touch /tmp/res_ok || touch /tmp/res_bad');
And then check for file existence.

Categories