Why is putenv() needed on an already defined environment variable? - php

When php is used as an apache module, an environment variable coming from an apache SetEnv directive is available to php's getenv(), but it does not appear to be available to C extensions through stdlib's getenv(). At least it happens with the pgsql module.
If the variable is reinstantiated with the php code:
putenv("varname=".getenv("varname"));
then it becomes available to the extension's code.
The question: why is that reinstantiation necessary? How is the core php environment distinct from the "standard" (stdlib) environment?
This occurs with: PHP Version 5.3.10-1ubuntu3.17 in Ubuntu 12.04, as an apache module. When run from the command line, the above workaround is not necessary.
From this other question: Using .pgpass from Apache libphp5.so it appears that this workaround is also necessary for php-5.4 under FreeBSD so it's not just Ubuntu or php-5.3.
It doesn't depend on variables_order having E in it. I've tried both EGPCS and GPCS, and $_ENV is not populated when E is not there, as expected, but that doesn't change the result of getenv(), as documented, or apparently the result of stdlib's getenv() from inside extensions.
Demo of the problem with the pgsql module. It's built on top of the libpq shared library written in C, which calls getenv() on a handful of optional PG* environment variables.
In apache configuration file, under a <VirtualHost>, I'm setting this to make connection attempts fail:
SetEnv PGHOST doesnotexist
and not specifying a host in the pg_connect call, so PGHOST must be taken when present.
First try:
$v=getenv("PGHOST");
echo "PGHOST=$v\n";
$cnx=pg_connect("user=daniel");
if ($cnx) {
echo "Connection is successful.";
}
Result:
PGHOST=doesnotexist
Connection is successful.
So PGHOST is getting ignored, despite being in the environment.
Second try, now putting again PGHOST into the environment even though it's already there:
$v=getenv("PGHOST");
echo "PGHOST=$v\n";
putenv("PGHOST=".getenv("PGHOST"));
$cnx=pg_connect("user=daniel");
if ($cnx) {
echo "Connection is successful.";
}
Result (failure to connect to the specified host, as expected):
PGHOST=doesnotexist
Warning: pg_connect(): Unable to connect to PostgreSQL server:
could not translate host name "doesnotexist" to address:
Name or service not known in /var/www/test/pgtest2.php on line 8

The reason is this:
The environment values you get from getenv()[PHP] (the php function) are different than the environment you query with getenv()[C] (the C lib function). What getenv()[PHP] does, is checking with the registered sapi for a match (http://lxr.php.net/xref/PHP_5_6/ext/standard/basic_functions.c#3999).
The apache2 sapi does this through its own environment context (http://lxr.php.net/xref/PHP_5_6/sapi/apache2handler/sapi_apache2.c#253), not the standard OS environment from the apache process itself.
ONLY when there is no match found, it will check at the environment of the actual process. So this is why getenv()[PHP] returns a value, but getenv()[C] does not.
Now, the "hack" is a simple one as well: putenv()[PHP], stores the given key/value in the environment of the running process, which is why it can be found later on by getenv()[c].

Related

Can php-fpm service interpret environment variables inside www.conf to create unique log files?

I'm trying to set multiple cloud servers behind an ALB to write their log files to my common EFS. I have a single shared drive EFS, which I can access via a single development instance, while all my production instances are securely behind a VCN. I am trying to use system environment variables (specifically the HOSTNAME) so that each of the production instances, which use the same configuration file to start php-fpm (so that I can do auto-scaling), write a different log.
I've seen answers that say that the *nix way of using environment variables should work, but they are not working for me. Specifically ${HOSTNAME} returns blank (and I have checked it is set for the apache and root users; root starts the php-frm service). I also tried interpolations of that, and the bash flavor of things "%()". None of them work.
All of them return blank strings (but not errors).
I did create a single variable in my environment variable, and got that to work.
I also read that you can't assign a env value to a variable directly, but when I tried to create a separate variable, that actually returned an error when starting the service.
This is what I would expect to work:
slowlog = "/shared/logs/php-fpm/www-slow-${HOSTNAME}.log"
Tried all of the following:
slowlog = "/shared/logs/php-fpm/www-slow-".${HOSTNAME}.".log"
--> blank for hostname
slowlog = "/shared/logs/php-fpm/www-slow-${HOSTNAME}.log"
--> blank for hostname
slowlog = "/shared/logs/php-fpm/www-slow-%(HOSTNAME).log"
--> the file name includes %(HOSTNAME)
slowlog = "/shared/logs/php-fpm/www-slow-$HOSTNAME.log"
--> the file name includes $HOSTNAME
slowlog = "/shared/logs/php-fpm/www-slow-{$HOSTNAME}.log"
--> the file name includes {$HOSTNAME}
slowlog_name = "/shared/logs/php-fpm/www-slow-{$HOSTNAME}.log"
slowlog = slowlog_name
--> start error: unknown variable slowlog_name
Thank you for any tips/advice!
PHP 7.3 does support environment variable interpolation for log file names in config files - I just tested this. I suspect that where you're getting caught is that in bash, $HOSTNAME is a bash variable but not automatically an environment variable, and Linux systems vary in whether $HOSTNAME is set as an environment variable. RedHat distros do set it, Ubuntu does not:
jacob#nitrogen ~ $ cat /etc/redhat-release
Fedora release 29 (Twenty Nine)
jacob#nitrogen ~ $ echo $HOSTNAME
nitrogen
jacob#nitrogen ~ $ env | grep HOSTNAME
HOSTNAME=nitrogen
jacob#iad11:~$ grep PRETTY_NAME /etc/os-release
PRETTY_NAME="Ubuntu 18.04.2 LTS"
jacob#iad11:~$ echo $HOSTNAME
iad11
jacob#iad11:~$ env | grep HOSTNAME
You can test with a different environment variable, and then set $HOSTNAME in a custom startup script that then starts PHP.

Where does the environment lie that contains the PHP_MEMORY_LIMIT environment variable?

I'm using Windows 10 Home Single Language operating system on my machine. It's a 64-bit Operating System running on a x64-based processor.
I've installed the latest version ox XAMPP(Apache/2.4.33 (Win32) OpenSSL/1.1.0h PHP/7.2.5 ) on my machine.
In the PHP Manual I come across following code example saying php.ini Environment Variables
; PHP_MEMORY_LIMIT is taken from environment
memory_limit = ${PHP_MEMORY_LIMIT}
In the above example, I'm not understanding 'from environment' means exactly from where this PHP_MEMORY_LIMIT variable is taken. Where does this so called 'Environment and its Variables' lie?
Does this so called 'Environment' contain any other variables than PHP_MEMORY_LIMIT?
To know where the environment variables lie, you can run phpinfo() and see under the Environment section. Of course it contains more than one variable.

Why are Python environment variables different when executed from a web server?

I have the following executable python script:
#!/usr/bin/python
import os
print os.environ
when I execute it from the command line as root, I get the following:
{
'LANG': 'en_US.UTF-8',
'TERM': 'xterm-256color',
'SHELL': '/bin/bash',
'LESSCLOSE': '/usr/bin/lesspipe %s %s',
'LANGUAGE': 'en_US:en',
'SHLVL': '1',
'SSH_TTY': '/dev/pts/0',
'OLDPWD': '/var/www/bais-mordechai-laravel',
'COMP_WORDBREAKS': ' \t\n"\'><;|&(:',
'PWD': '/var/www/bais-mordechai-laravel/public',
'LESSOPEN': '| /usr/bin/lesspipe %s',
'SSH_CLIENT': '71.205.188.8 56489 22',
'LOGNAME': 'root',
'USER': 'root',
'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games',
'MAIL': '/var/mail/root',
'LS_COLORS':'...',
'HOME': '/root',
'_': './pythontest',
'SSH_CONNECTION': '...'
}
But when I run this from a php script (running on nginx server) and echo back the result I get:
{
'HOME': '/var/www',
'PWD': '/var/www/bais-mordechai-laravel/public',
'USER': 'www-data'
}
Why are they different? Specifically, how can I get the web version to include the 'PATH' variable?
For apache, see mod_setenv.
This module allows for control of internal environment variables that are used by various Apache HTTP Server modules. These variables are also provided to CGI scripts as native system environment variables, and available for use in SSI pages. Environment variables may be passed from the shell which invoked the httpd process. Alternatively, environment variables may be set or unset within the configuration process.
If you are using Apache's FCGId, see FcgidInitialEnv.
Use FcgidInitialEnv to define environment variables to pass to the FastCGI application. This directive can be used multiple times.
This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.
For Nginx CGI, see the env setting.
By default, nginx removes all environment variables inherited from its parent process except the TZ variable. This directive allows preserving some of the inherited variables, changing their values, or creating new environment variables.
If you are using NgxWSGIModule, see the wsgi_var setting.
Directive assigns the variable, which will be added to the
environment dictionary passed to the WSGI application.
It is possible to use strings, nginx variables and their
combination as values. Directives not set are inherited from the
outer level.
If you are running your application under (gunicorn|tornado|twisted|etc)+supervisord, see the environment directive.
A list of key/value pairs in the form KEY="val",KEY2="val2" that will be placed in the supervisord process’ environment (and as a result in all of its child process’ environments). This option can include the value %(here)s, which expands to the directory in which the supervisord configuration file was found. Values containing non-alphanumeric characters should be quoted (e.g. KEY="val:123",KEY2="val,456"). Otherwise, quoting the values is optional but recommended. Note that subprocesses will inherit the environment variables of the shell used to start supervisord except for the ones overridden here and within the program’s environment option.
If you are using something else, let me know.
You can always inject environment variables using os.environ - do it at the start of your (c|fc|ws)gi.py script. Se also sys.path:
sys.path.append('/usr/local/django')

Why does the connection to ASE fail when password encryption is required?

How can I password encrypt the database server connections from PHP to Sybase ASE?
The PHP connection to ASE fails when net password encryption reqd = 1.
How can we make a PHP DB connection to ASE with password encryption? Is there a PHP keyword or parameter that we need to set on the client side connection?
Below is a solution to this problem in term of able to connect from a Linux 64 bit base machine with PHP version 5.3.6 and Sybase driver from Sybase CTISQL Utility/15.7/P-EBF19975 ESD #3/DRV.15.7.0.3. While we got this to work, we need to have Apache to work in addition to this, I am hoping Sybase support is able to help us with that as well as they have with solution below to our question. I am posting this here for everyone benefit. Note: AFAICT, this is not a supported solution by Sybase. Please use it as your own risk.
Few things need to happen for this to work:
I've use the latest Debian Stable 6.05 squeeze 64 bit
I've install ebf19977(developer kit with open client, yes the number is off, that's not a mistake, you will see when you install it) downloaded from Sybase
Download PHP 5.3.6 from php.net website to be compile from source.
Step 1) I am assume one should easily set this up. Make sure you have sudo access
Step 2) To install the developer kit you need to type the whole path to where the setup.bin file is locate like so type in sudo /home/btran/ebf19977/setup.bin , this will start the setup, pretty much follow instructions there to to install OpenClient and its associate tools. Make sure that Open Client, and PHP library are install.
Once you done the install there are a few other steps to do:
Set all Sybase environments variables by executing the SYBASE.sh in the root directory at /opt/sybase
sudo cat /opt/sybase/SYBASE.sh >> /etc/profile , if necessary source /etc/profile
This will set most of the variables, but you need to add a couple variable that is not there:
PHPRC=/opt/sybase/OCS-15_0/config
LD_LIBRARY_PATH=/opt/sybase/OCS-15_0/php/php536_64/lib:/opt/sybase/OCS-15_0/lib
Your /ect/profile should look something like this:
# ------------------------------------
# Sybase Product Environment variables
# ------------------------------------
SYBASE="/opt/sybase"
export SYBASE
SYBASE_OCS="OCS-15_0"
export SYBASE_OCS
INCLUDE="/opt/sybase/OCS-15_0/include":$INCLUDE
export INCLUDE
LIB="/opt/sybase/OCS-15_0/lib":$LIB
export LIB
PATH="/opt/sybase/OCS-15_0/bin":$PATH
export PATH
LD_LIBRARY_PATH="/opt/sybase/OCS-15_0/php/php536_64/lib:/opt/sybase/OCS-15_0/lib:/opt/sybase/OCS-15_0/lib3p64:/opt/sybase/OCS-15_0/lib3p":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
PHPRC="/opt/sybase/OCS-15_0/config"
export PHPRC
JAVA_HOME="/opt/sybase/jre32"
export JAVA_HOME
RIBO_HOME="/opt/sybase/jutils-3_0/ribo"
export RIBO_HOME
# ------------------------------------
Then, you will also need to made modification to the interfaces file located at root directory /opt/sybase to look something like this, please do not use this, use your info:
dev
master tcp ether dev.company.com 4425
query tcp ether dev.company.com 4425
prod
master tcp ether prod.company.com 4425
query tcp ether prod.company.com 4425
ribo
master tcp ether testhost.company.com 5005
query tcp ether testhost.company.com 5005
You will also need to made modification to the ocs.cfg to get PHP to initiated correctly, this located in the folder /opt/sybase/OCS-15_0/config to look like this:
;;;;;;;;;;;
; This is the default external configuration definition file. Feel free
; to add your own customizations.
;
[DEFAULT]
; This is the default section loaded by applications that use
CS_SEC_ENCRYPTION = CS_TRUE
[ANSI_ESQL]
; This section defines configuration which an ANSI conforming
CS_CAP_RESPONSE = CS_RES_NOSTRIPBLANKS
CS_EXTRA_INF = CS_TRUE
CS_ANSI_BINDS = CS_TRUE
CS_OPT_ANSINULL = CS_TRUE
CS_OPT_ANSIPERM = CS_TRUE
CS_OPT_STR_RTRUNC = CS_TRUE
CS_OPT_ARITHABORT = CS_FALSE
CS_OPT_TRUNCIGNORE = CS_TRUE
CS_OPT_ARITHIGNORE = CS_FALSE
CS_OPT_ISOLATION = CS_OPT_LEVEL3
CS_OPT_CHAINXACTS = CS_TRUE
CS_OPT_CURCLOSEONXACT = CS_TRUE
CS_OPT_QUOTED_IDENT = CS_TRUE
; End of default sections
[isql]
CS_SEC_ENCRYPTION = CS_TRUE
[PHP]
CS_SEC_ENCRYPTION = CS_TRUE
; This is a sample section showing how you might alter configuration
; properties for you Embedded SQL application.
[SAMPLE_ESQL]
; use most of the ANSI properties defined above
include = ANSI_ESQL
; but override some default properties
CS_OPT_CHAINXACTS = CS_FALSE ; run in autocommit mode
;;;;;;;;;
Note: Very Critical to check for the existence of the php.ini file in the same directory as well /opt/sybase/OCS-15_0/config (this pointing the exention file, look at the environment variable LD_LIBRARY_PATH="/opt/sybase/OCS-15_0/php/php536_64/lib)
you php.ini should read like this:
extension=/opt/sybase/OCS-15_0/php/php536_64/lib/sybaseasephp.so
Step 3) compile PHP from source, again Sybase call for 64 bit.
I down load the source to /opt/php-5.3.6 for persistent with where most app install for Linux.
cd to /opt/php-5.3.6
./configure
VERY IMPORTANT to note, this is where we ran into a snag, if I choose any other configure option and not default ./configure, PHP will fail to load with error below:
btran#sls-baonix:/opt/php-5.3.6$ php ~/test-dev.php
PHP Warning: PHP Startup: Unable to load dynamic library
'/opt/sybase/OCS-15_0/php/php536_64/lib/sybaseasephp.so' -
/opt/sybase/OCS-15_0/php/php536_64/lib/sybaseasephp.so: undefined
symbol: executor_globals in Unknown on line 0
Fatal error: Call to undefined function sybase_connect() in
/home/btran/test-dev.php on line 10
]
Reason we need other options is we need it also to work with Apache. We got it now to work with PHP, but we need to be able to initiate PHP from website so the dependencies is that PHP beside support Sybase, it also need to support Apache. If any PHP expert out there has solution to this please jump in and help us out.

Session Start Failure

This is not your average session failed to start question, there is no whitespace, i have not called it in another file etc.
Im currently working on an application as I have started to build my session library, now when I call session_start I get the following error:
A session had already been started - ignoring session_start()
For those who wish to see the source: https://github.com/AdminSpot/ASFramework/blob/master/system/libraries/session.php
This usually means that the session.autostart directive is set to 1, but that's the thing.. it's not, it's set to 0 and I have verified this by doing the following:
Search my entire system for php.ini* files, checked them
Executed the following command php --ini amd validated the ini files
executed the following command php -i | grep session.auto_start. which responded with session.auto_start => Off => Off
Checked the PHPInfo page, see image below
Checked the php.ini files for cgi
There is no htaccess files on nginx
grep -lir "session_start" * only shows my library file
Restarting FastCGI, Nginx and the entire server
I have created a basic test script to test where i have just called session start on it's own.
The phpinfo() call stats the active php.ini is /etc/php5/cgi/php.ini so after running cat /etc/php5/cgi/php.ini | grep session.auto_start I get session.auto_start = 0, so it disabled, Could it be NGinx ?
Has anyone got any idea what's going on, some server information below:
PHP: PHP 5.3.5-1ubuntu7.2 with Suhosin-Patch
MySQL: Ver 14.14 Distrib 5.1.54, for debian-linux-gnu (i686) using readline 6.2
Nginx: Version: nginx/0.8.54
PHPInfo screen:
My first guess would be that you have an auto-prepend file or an .htaccess which is modifying the settings in the meanwhile.
You can use ini_get to retrieve the value of session.auto_start and auto_prepend_file to confirm. phpinfo() should work too.
Edit
Could it be that your session library is being instantiated twice? Since return $this->session_started is an instance variable, that could cause issues. What happens if you set that to a class-level variable?
Side note:
You also have this return $this->session_started = true; at the end of the start() method. It shouldn't matter, but it looks funny.
How about .htaccess containing a php_value session.auto_start 1? PHP on the command line would totally ignore settings overrides in .htaccess files. Remember that commandline PHP and web-based PHP have completely different .ini files in most standard configurations, so checking via command line is a waste of time.
I'd suggest having your script do a phpinfo() immediately before one of your session_start calls and check what the effective settings are there.
And anyway you can just verify if a session has already started or not.
if (!isset($_SESSION)) {
session_start();
}
http://php.net/manual/en/function.session-start.php#90007
Check and see if you are being passed a session cookie. May help you narrow it down.

Categories