So I'm writing a web server (I know, like the world needs another one!) And I want to call php through CGI. I've got a nice working CGI1.1 implementation good enough to run perl cgi scripts for example.
Now I want to run php. But when my cgi module calls the php program it returns a 404.
How do I get this working?
The details:
I'm running on ubuntu with php5. In my web server, I pass cgi requests to a handler that will find the program and call it with the sc ript as an argument. So here is the code that spawns the cgi process:
chdir( dir );
execve( argv[0], argv.data(), (char* const*) req.posixEnv() );
The command I'm running is equivalent to:
/usr/bin/php5-cgi /home/rafael/webserver/data/cgi-bin/test.php
This means I've set "dir" to:
/home/rafael/webserver/data/cgi-bin
And the cgi process should run in that directory. Argv[0] is "/usr/bin/php5-cgi", argv.data() returns a null terminated array with the following elements:
/usr/bin/php5-cgi
/home/rafael/webserver/data/cgi-bin/test.php
NULL
The process environment contains the CGI environment that includes a block of the usual CGI environment variables.
When I run the above command from a shell, that is by literally typing it into a shell, the script runs fine and php generates the test page. When I call it from CGI it returns:
Status: 404 Not Found
X-Powered-By: PHP/5.3.10-1ubuntu3.2
Content-type: text/html
No input file specified.
Now I know more or less what is happening. PHP is detecting if it is running in CLI or CGI mode, and searching for the script to run differently in each mode. In CGI mode it is probably using CGI environment variables like SCRIPT_FILENAME and DOCUMENT_ROOT to resolve the script name. I've tried a whole bunch of things - different settings for these variables. But none of it works.
What I do know: The whole spawning of PHP, and reading output works. For example if I remove certain CGI variables such that it thinks it is running in CLI mode it works great - generates the page and everything.
What I need to know is:
what is the right way to set up the CGI environment variables for the php-cgi to find my script.
what is the right way to call a php script from exec. I'm using full paths for everything as an effort to debug this. I'd prefer to use relative paths.
All the documentation I can find is about configuring php for cgi on particular web servers. I need to know this from the perspective of someone writing a server.
#EDIT: I have a simpler example that eliminates c++. I'm pretty sure that part is working correctly anyway. Say I make a script that calls php-cgi, and I try to emulate the cgi calling conventions in a shell.
This works:
#export GATEWAY_INTERFACE="CGI/1.1"
export REDIRECT_STATUS=200
export SERVER_PROTOCOL="HTTP/1.1"
export DOCUMENT_ROOT="/home/rafael/web"
#export SCRIPT_FILENAME="cgi-bin/test.php"
export SCRIPT_NAME="cgi-bin/test.php"
/usr/bin/php5-cgi test.php
But this doesn't:
export GATEWAY_INTERFACE="CGI/1.1"
export REDIRECT_STATUS=200
export SERVER_PROTOCOL="HTTP/1.1"
export DOCUMENT_ROOT="/home/rafael/web"
#export SCRIPT_FILENAME="cgi-bin/test.php"
export SCRIPT_NAME="cgi-bin/test.php"
/usr/bin/php5-cgi test.php
I think probably some combination of DOCUMENT_ROOT, SCRIPT_FILENAME and SCRIPT_NAME should work.
The thing that was messing it up was the setting of "doc_root" in php.ini. I unset that like so:
doc_root =
And now cgi environment variables work much more like the CGI spec says. Eg. The following thing now works:
export GATEWAY_INTERFACE="CGI/1.1"
export SERVER_PROTOCOL="HTTP/1.1"
export SCRIPT_FILENAME="/home/rafael/web/cgi-bin/test.php"
export SCRIPT_NAME="test.php"
export REDIRECT_STATUS="200"
/usr/bin/php5-cgi test.php
Related
I had developped a shell script and I wanted to create an UI with it. I decided to use a web interface with a local server because I have few knowledges in HTML/PHP, more than QT or Java. I simply want that my html can run a shell script on my computer.
I have an Apache php server which I start in localhost with apachectl.
In /var/www/html/ I have a shell script and a test_web.php file which execute this script with exec('/var/www/html/test.sh')
In my html I have this:
<form method="POST" action="test_web.php?">
<input type="submit" value="Execute" name="exec">
</form>
If I click on Execute, it opens a new page where I see the output of echo commands from the script but it is not executed on the terminal as a standard script (like if the script is not executed on server side).
I want this script to be executed as started from the terminal.
Thank you for your help
As suggested by some commentators, you could use the Apache cgi features to be able to execute directly your test.sh file (1), but since you say you are using a Php enabled Apache server, you have an alternative and easier option with Php which you partially already tried (2). In both cases, you will need to take care of the permissions and ownership of the bash script, and its execution environment for it to produce the same results as on the command line (3).
1. CGI
CGI stands for Common Gateway Interface. It allows web servers to execute programs that are executable on the host, and interface with the web server. That is a protocol per se, and CGI can be used not only to execute programs, but also for creating dynamic sites. You could program a dynamic website in any language, provided the executable is programmed to receive data via stdin from the server and interpret the headers, decode any contents properly, and that the program can return proper dynamic data to the server via stdout, with proper headers, Content-type, etc.
There are libraries for helping in this task for many languages (for C, Perl has a CGI module in is Core Modules, etc., and even for Bash).
On Linux, to use CGI with Apache 2, you would need to make sure that your Apache configuration loads the required module for supporting it, and select the directory where your web server will be allowed to execute the programs:
# uncomment or add the directive for the module
LoadModule cgid_module modules/mod_cgid.so
# one way to allow the server to execute cgi programs is to
# set a ScriptAlias directive to a directory dedicated to CGI programs
ScriptAlias "/cgi-bin/" "/var/www/html/cgi-bin/"
You put your test.sh script in /var/www/html/cgi-bin/, and browser can be pointed to http://localhost/cgi-bin/test.sh to directly execute the script. There are other configurations possible, see this Apache CGI tuturial.
If you need to execute the Bash program and want your CGI to send anything to the browser, the first echo sent by the Bash script shall be the following output:
Content-type: text/html
followed by whatever dynamic html you want to produce.
2. Php
Really all these complications may not be necessary since you say you use a Php enabled Apache server, and since you probably do not want the Bash script to send html directly to the browser.
In that case, the shell_exec() command or backticks operator, will allow you to run your Bash script and obtain its output in a Php variable, and do whatever you want to do with it in Php.
You say you have tried using the exec command, which should work also, except that you get a status code in response, and not the program output.
If your execution command does not work at all, this may be because of permissions, ownership or execution environment issues.
3. Permissions, ownership and execution environment
Have a look at your Apache logs for errors produced by the exec or shell_exec commands.
The script needs to have execution permissions, and needs to be readable and executable by the web server.
For instance, on Debian, Apache web server runs as a user www-data, so a script would need to be owned and executable by that user:
chown www-data:www-data test.sh
chmod u+x test.sh
You can find the location of the Apache logs and the user and group under which the web server runs in your Apache configuration file by inspecting (as root) the results of:
apachectl -S
Another possible issue is the environment in which Apache will be running the script. That environment, may or not include the same environment variables, the $PATH will be different and may not include all directories available to a "normal" user executing the same script on the console.
Again, check for errors in the logs.
The most frequent cause for failure is a Bash command not found because it is not in the $PATH of the Apache process. The quick fix is then to change the script to prefix the command with its full path.
So, for instance, if your test.sh uses a program called validjson, open your terminal as a user that can execute the script without errors, and check where it is:
$ which validjson
validjson: /usr/local/bin/validjson
and instead of calling validjson in the script assuming it is in the $PATH, call it instead with its full path /usr/local/bin/validjson reported by the which command.
If you are calling other scripts or programs, they may have the same issues, check that until you have debugged the execution of your script.
I am developing a php web application using Apache on CentOS6.
I have set a custom environment variable in CentOS on command line by using
export test_var=3
I cant figure out why I am unable to print this Linux shell variable in my php script on my web-application. Apache seems to pull up its own environment variables only and ignores Linux environment variables.
Things I have tried so far:
I have set variable variables_order='EGPCS' in /etc/php.ini.
This has allowed me to access Linux variables using PHP CLI but not in my web-application.
I tried adding line PassEnv test_var in my Apache configuration file (httpd.conf) but Apache returns a blank value in my application.
Passenv works when I add line export test_var=1 in /etc/init.d/httpd but I don't want to use this because I already have this variable declared and set in Linux and so I don't want to set it again.
I have written a shell script and have tried using exec to execute the shell script but when I try to print it using print_var, I get a blank array.
Any suggestions? I don't want to set the variable in any file because I have already set it in in Linux. I'm want to pull up the variable from the OS itself.
The Apache documentation says PassEnv should do the trick:
Specifies one or more native system environment variables to make available as internal environment variables, which are available to Apache >HTTP Server modules as well as propagated to CGI scripts and SSI pages. Values come from the native OS environment of the shell which >invoked the httpd process.
I'm trying to launch a perl script from within a PHP-based website. In that perl script, I'm using a package, that's installed on a non-standard location. So, to make all packages on that location available without launching perl as "perl -I /path/to/packages", or placing "lib /path/to/packages;" in every perl script I make, I added "/path/to/packages" to the PERL5LIB environmental system variable, and everything works fine when a regular user executes the script. However, when this perl-script is launched from the website, all environmental system variables seem to be inaccessible (tested with $ENV{"PERL5LIB"} and $ENV{"LOGNAME"} in perl, both are uninitialized), causing the perl script to crash because it can't find the required packages in #INC.
How can I make those perl packages available for launch from the website without lauching every perl script with "perl -I /path/to/packages script.pl" and without putting "lib /path/to/packages;" on top of every perl script?
There is two possible way to solve it:
If you own/access the web server, you could add variables globally.
(for apache)
http://www.perlmonks.org/?node_id=844715
<Directory /var/www/>
SetEnv PERL5LIB /your/lib/path
Another workaround that you could setup and maintain your libraries related to script then reach it from your perl script. Please check out FindBin module for this. http://www.perl.com/pub/2002/05/14/mod_perl.html
You could store your script ./bin/, and your libs at ./lib/
If you are using your libs more than one place, you could link it to ./lib/.
#!/usr/bin/perl
use FindBin ();
use lib "$FindBin::Bin../lib/";
use test;
print "test.pm => $INC{'test.pm'}\n";
You have to insure that PER5LIB is set in the environment that your web server launches from.
You'll need to consult the manual for your particular web server to find the configuration command that set environment variables, or you'll need to modify the launcher script (e.g. apachectl) an add the logic to properly set PERL5LIB.
aaaaannnd... somebody was posting examples at the same time I was posting this...
I asked the system admin for help and found out that we're not using apache (as expected) but nginx. That being said, the solution was to add the PERL5LIB definition to the /etc/nginx/fastcgi_params file like this:
sudo echo 'fastcgi_param PERL5LIB "custom/path/to/perl/modules";' >> /etc/nginx/fastcgi_params
sudo service nginx reload
Thank you #LenJaffe and #user1126070 for pointing me in the right direction!
I have access to a shared web host.
The website installed there uses exec(). About 4 months ago the function was enabled, but now they have put it on the disable_functions list. They put exec, passthru and shell_exec on that list but they forgot to put system:) This makes me think that the server admin is not very aware on what he's doing. Anyway, they now say that those functions should have never be enabled and they wont re-enable them.
The installed website uses exec() to start some php scripts that would do some background work. Right now i'm looking to see if there is any other "legal" way to start those php scripts in background(i expect system() to work, but maybe they will disable it in the future also).
And now my simple question:
In the perdefined file structure i see a cgi-bin folder. Whats its use? From what i read on the web it is used to generate "dynamic" pages when accessed through the browser, but the server has php anyway installed, so i dont see its use.
/cgi-bin/ is a directory where CGI script should be placed.
You can work around restrictions on exec in PHP by using CGI.
Create a file named somefile.sh with contents:
#!/bin/sh
printf "Content-Type: text/plain\n\n"
#your code here
This will only work if /bin/sh actually exists, and is executable (if you're not in a chroot without /bin/sh for example)
There are enough ways to get a command executed. If they disable CGI, you can continue with SSI.
I have a dev server with several virtual hosts on it.
Each one needs to be able to run the command: propel-gen ./ creole
That script executes some php that reverse-engineers the database...
BUT the php it executes needs to be included to do so.
There is no include_path set in the php.ini because it would be global to all virtual hosts.
I need the include_path to be unique for each vhost.
How can I have this work on the command-line?
(note: htaccess won't work, because command-line doesn't go through apache).
Is there another way around this problem?
Perhaps passing a command-line param to set an include path?
Perhaps setting something in .bashrc?
:(
P.S. - The script works fine on my mac, which has a hard-coded include path
When running PHP from the command line, you can use the -d switch to define configuration options, that would normally be defined in php.ini
For more informations about what can be done when running PHP in CLI, you can take a look at Using PHP from the command line
In your case, suppose you are running this, which only displays the current include_path :
$ php -r 'var_dump(get_include_path());'
Default output would be like this (on my machine, btw) :
string(20) ".:/usr/local/lib/php"
Now, using -d to override include_path, this way :
$ php -dinclude_path='.:/usr/local/lib/php:/var/my/directory/lib' -r 'var_dump(get_include_path());'
You're getting :
string(42) ".:/usr/local/lib/php:/var/my/directory/lib"
I admit, this is pretty boring/long to type, and you will sometimes forget the -dblahblah stuff...
So, you can define an alias, a bit like this :
$ alias myphp='php -dinclude_path=".:/usr/local/lib/php:/var/my/directory/lib"'
And now, if you are using myphp command instead of php, you don't need to specify the include_path anymore :
$ myphp -r 'var_dump(get_include_path());'
string(42) ".:/usr/local/lib/php:/var/my/directory/lib"
This won't persist between shell sessions... So you could put the alias command at the end of your ~/.bashrc, so it's executed each time you log in :-)