2009-01-21 11:30:02 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* win32env.c
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
* putenv(), setenv(), and unsetenv() for win32.
|
|
|
|
*
|
|
|
|
* These functions update both the process environment and caches in
|
|
|
|
* (potentially multiple) C run-time library (CRT) versions.
|
2009-01-21 11:30:02 +01:00
|
|
|
*
|
2024-01-04 02:49:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
2009-01-21 11:30:02 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/port/win32env.c
|
2009-01-21 11:30:02 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "c.h"
|
|
|
|
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that unlike POSIX putenv(), this doesn't use the passed-in string
|
|
|
|
* as permanent storage.
|
|
|
|
*/
|
2009-01-21 11:30:02 +01:00
|
|
|
int
|
|
|
|
pgwin32_putenv(const char *envval)
|
|
|
|
{
|
|
|
|
char *envcpy;
|
|
|
|
char *cp;
|
|
|
|
typedef int (_cdecl * PUTENVPROC) (const char *);
|
Make pgwin32_putenv() follow DLL loading and unloading.
Until now, the first putenv() call of a given postgres.exe process would
cache the set of loaded CRTs. If a CRT unloaded after that call, the
next putenv() would crash. That risk was largely theoretical, because
the first putenv() precedes all PostgreSQL-initiated module loading.
However, this might explain bad interactions with antivirus and other
software that injects threads asynchronously. If an additional CRT
loaded after the first putenv(), pgwin32_putenv() would not discover it.
That CRT would have all environment changes predating its load, but it
would not receive later PostgreSQL-initiated changes. An additional CRT
loading concurrently with the first putenv() might miss that change in
addition to missing later changes. Fix all those problems. This
removes the cache mechanism from pgwin32_putenv(); the cost, less than
100 μs per backend startup, is negligible.
No resulting misbehavior was known to be user-visible given the core
distribution alone, but one can readily construct an affected extension
module. No back-patch given the lack of complaints and the potential
for behavior changes in non-PostgreSQL code running in the backend.
Christian Ullrich, reviewed by Michael Paquier.
2016-12-03 21:46:36 +01:00
|
|
|
static const char *const modulenames[] = {
|
|
|
|
"msvcrt", /* Visual Studio 6.0 / MinGW */
|
|
|
|
"msvcrtd",
|
|
|
|
"msvcr70", /* Visual Studio 2002 */
|
|
|
|
"msvcr70d",
|
|
|
|
"msvcr71", /* Visual Studio 2003 */
|
|
|
|
"msvcr71d",
|
|
|
|
"msvcr80", /* Visual Studio 2005 */
|
|
|
|
"msvcr80d",
|
|
|
|
"msvcr90", /* Visual Studio 2008 */
|
|
|
|
"msvcr90d",
|
|
|
|
"msvcr100", /* Visual Studio 2010 */
|
|
|
|
"msvcr100d",
|
|
|
|
"msvcr110", /* Visual Studio 2012 */
|
|
|
|
"msvcr110d",
|
|
|
|
"msvcr120", /* Visual Studio 2013 */
|
|
|
|
"msvcr120d",
|
|
|
|
"ucrtbase", /* Visual Studio 2015 and later */
|
|
|
|
"ucrtbased",
|
|
|
|
NULL
|
2010-01-01 15:57:16 +01:00
|
|
|
};
|
|
|
|
int i;
|
2009-01-21 11:30:02 +01:00
|
|
|
|
|
|
|
/*
|
2016-12-03 21:46:35 +01:00
|
|
|
* Update process environment, making this change visible to child
|
Make pgwin32_putenv() follow DLL loading and unloading.
Until now, the first putenv() call of a given postgres.exe process would
cache the set of loaded CRTs. If a CRT unloaded after that call, the
next putenv() would crash. That risk was largely theoretical, because
the first putenv() precedes all PostgreSQL-initiated module loading.
However, this might explain bad interactions with antivirus and other
software that injects threads asynchronously. If an additional CRT
loaded after the first putenv(), pgwin32_putenv() would not discover it.
That CRT would have all environment changes predating its load, but it
would not receive later PostgreSQL-initiated changes. An additional CRT
loading concurrently with the first putenv() might miss that change in
addition to missing later changes. Fix all those problems. This
removes the cache mechanism from pgwin32_putenv(); the cost, less than
100 μs per backend startup, is negligible.
No resulting misbehavior was known to be user-visible given the core
distribution alone, but one can readily construct an affected extension
module. No back-patch given the lack of complaints and the potential
for behavior changes in non-PostgreSQL code running in the backend.
Christian Ullrich, reviewed by Michael Paquier.
2016-12-03 21:46:36 +01:00
|
|
|
* processes and to CRTs initializing in the future. Do this before the
|
|
|
|
* _putenv() loop, for the benefit of any CRT that initializes during this
|
|
|
|
* pgwin32_putenv() execution, after the loop checks that CRT.
|
2009-01-21 11:30:02 +01:00
|
|
|
*
|
|
|
|
* Need a copy of the string so we can modify it.
|
|
|
|
*/
|
|
|
|
envcpy = strdup(envval);
|
2010-12-16 16:22:08 +01:00
|
|
|
if (!envcpy)
|
|
|
|
return -1;
|
2009-01-21 11:30:02 +01:00
|
|
|
cp = strchr(envcpy, '=');
|
|
|
|
if (cp == NULL)
|
2010-12-16 16:22:08 +01:00
|
|
|
{
|
|
|
|
free(envcpy);
|
2009-01-21 11:30:02 +01:00
|
|
|
return -1;
|
2010-12-16 16:22:08 +01:00
|
|
|
}
|
2009-01-21 11:30:02 +01:00
|
|
|
*cp = '\0';
|
|
|
|
cp++;
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
if (*cp)
|
2009-01-21 11:30:02 +01:00
|
|
|
{
|
2009-02-12 13:53:34 +01:00
|
|
|
/*
|
|
|
|
* Only call SetEnvironmentVariable() when we are adding a variable,
|
|
|
|
* not when removing it. Calling it on both crashes on at least
|
2016-12-03 21:46:35 +01:00
|
|
|
* certain versions of MinGW.
|
2009-02-12 13:53:34 +01:00
|
|
|
*/
|
|
|
|
if (!SetEnvironmentVariable(envcpy, cp))
|
|
|
|
{
|
|
|
|
free(envcpy);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-01-21 11:30:02 +01:00
|
|
|
}
|
|
|
|
free(envcpy);
|
|
|
|
|
Make pgwin32_putenv() follow DLL loading and unloading.
Until now, the first putenv() call of a given postgres.exe process would
cache the set of loaded CRTs. If a CRT unloaded after that call, the
next putenv() would crash. That risk was largely theoretical, because
the first putenv() precedes all PostgreSQL-initiated module loading.
However, this might explain bad interactions with antivirus and other
software that injects threads asynchronously. If an additional CRT
loaded after the first putenv(), pgwin32_putenv() would not discover it.
That CRT would have all environment changes predating its load, but it
would not receive later PostgreSQL-initiated changes. An additional CRT
loading concurrently with the first putenv() might miss that change in
addition to missing later changes. Fix all those problems. This
removes the cache mechanism from pgwin32_putenv(); the cost, less than
100 μs per backend startup, is negligible.
No resulting misbehavior was known to be user-visible given the core
distribution alone, but one can readily construct an affected extension
module. No back-patch given the lack of complaints and the potential
for behavior changes in non-PostgreSQL code running in the backend.
Christian Ullrich, reviewed by Michael Paquier.
2016-12-03 21:46:36 +01:00
|
|
|
/*
|
|
|
|
* Each CRT has its own _putenv() symbol and copy of the environment.
|
|
|
|
* Update the environment in each CRT module currently loaded, so every
|
|
|
|
* third-party library sees this change regardless of the CRT it links
|
|
|
|
* against. Addresses within these modules may become invalid the moment
|
|
|
|
* we call FreeLibrary(), so don't cache them.
|
|
|
|
*/
|
|
|
|
for (i = 0; modulenames[i]; i++)
|
|
|
|
{
|
|
|
|
HMODULE hmodule = NULL;
|
|
|
|
BOOL res = GetModuleHandleEx(0, modulenames[i], &hmodule);
|
|
|
|
|
|
|
|
if (res != 0 && hmodule != NULL)
|
|
|
|
{
|
|
|
|
PUTENVPROC putenvFunc;
|
|
|
|
|
2020-10-21 08:17:51 +02:00
|
|
|
putenvFunc = (PUTENVPROC) (pg_funcptr_t) GetProcAddress(hmodule, "_putenv");
|
Make pgwin32_putenv() follow DLL loading and unloading.
Until now, the first putenv() call of a given postgres.exe process would
cache the set of loaded CRTs. If a CRT unloaded after that call, the
next putenv() would crash. That risk was largely theoretical, because
the first putenv() precedes all PostgreSQL-initiated module loading.
However, this might explain bad interactions with antivirus and other
software that injects threads asynchronously. If an additional CRT
loaded after the first putenv(), pgwin32_putenv() would not discover it.
That CRT would have all environment changes predating its load, but it
would not receive later PostgreSQL-initiated changes. An additional CRT
loading concurrently with the first putenv() might miss that change in
addition to missing later changes. Fix all those problems. This
removes the cache mechanism from pgwin32_putenv(); the cost, less than
100 μs per backend startup, is negligible.
No resulting misbehavior was known to be user-visible given the core
distribution alone, but one can readily construct an affected extension
module. No back-patch given the lack of complaints and the potential
for behavior changes in non-PostgreSQL code running in the backend.
Christian Ullrich, reviewed by Michael Paquier.
2016-12-03 21:46:36 +01:00
|
|
|
if (putenvFunc)
|
|
|
|
putenvFunc(envval);
|
|
|
|
FreeLibrary(hmodule);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-04 06:16:54 +01:00
|
|
|
/*
|
|
|
|
* Finally, update our "own" cache. This is redundant with the loop
|
|
|
|
* above, except when PostgreSQL itself links to a CRT not listed above.
|
|
|
|
* Ideally, the loop does visit all possible CRTs, making this redundant.
|
|
|
|
*/
|
2009-01-21 11:30:02 +01:00
|
|
|
return _putenv(envval);
|
|
|
|
}
|
|
|
|
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
int
|
|
|
|
pgwin32_setenv(const char *name, const char *value, int overwrite)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
char *envstr;
|
|
|
|
|
|
|
|
/* Error conditions, per POSIX */
|
|
|
|
if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL ||
|
|
|
|
value == NULL)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No work if variable exists and we're not to replace it */
|
|
|
|
if (overwrite == 0 && getenv(name) != NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
envstr = (char *) malloc(strlen(name) + strlen(value) + 2);
|
|
|
|
if (!envstr) /* not much we can do if no memory */
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
sprintf(envstr, "%s=%s", name, value);
|
|
|
|
|
|
|
|
res = pgwin32_putenv(envstr);
|
|
|
|
free(envstr);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-01-21 11:30:02 +01:00
|
|
|
pgwin32_unsetenv(const char *name)
|
|
|
|
{
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
int res;
|
2009-01-21 11:30:02 +01:00
|
|
|
char *envbuf;
|
|
|
|
|
|
|
|
envbuf = (char *) malloc(strlen(name) + 2);
|
|
|
|
if (!envbuf)
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
return -1;
|
2009-01-21 11:30:02 +01:00
|
|
|
|
|
|
|
sprintf(envbuf, "%s=", name);
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
res = pgwin32_putenv(envbuf);
|
2009-01-21 11:30:02 +01:00
|
|
|
free(envbuf);
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
return res;
|
2009-01-21 11:30:02 +01:00
|
|
|
}
|