1 /*-------------------------------------------------------------------------
2 *
3 * win32env.c
4 * putenv() and unsetenv() for win32, which update both process environment
5 * and caches in (potentially multiple) C run-time library (CRT) versions.
6 *
7 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 *
11 * IDENTIFICATION
12 * src/port/win32env.c
13 *
14 *-------------------------------------------------------------------------
15 */
16
17 #include "c.h"
18
19 int
pgwin32_putenv(const char * envval)20 pgwin32_putenv(const char *envval)
21 {
22 char *envcpy;
23 char *cp;
24 typedef int (_cdecl * PUTENVPROC) (const char *);
25 static const char *const modulenames[] = {
26 "msvcrt", /* Visual Studio 6.0 / MinGW */
27 "msvcrtd",
28 "msvcr70", /* Visual Studio 2002 */
29 "msvcr70d",
30 "msvcr71", /* Visual Studio 2003 */
31 "msvcr71d",
32 "msvcr80", /* Visual Studio 2005 */
33 "msvcr80d",
34 "msvcr90", /* Visual Studio 2008 */
35 "msvcr90d",
36 "msvcr100", /* Visual Studio 2010 */
37 "msvcr100d",
38 "msvcr110", /* Visual Studio 2012 */
39 "msvcr110d",
40 "msvcr120", /* Visual Studio 2013 */
41 "msvcr120d",
42 "ucrtbase", /* Visual Studio 2015 and later */
43 "ucrtbased",
44 NULL
45 };
46 int i;
47
48 /*
49 * Update process environment, making this change visible to child
50 * processes and to CRTs initializing in the future. Do this before the
51 * _putenv() loop, for the benefit of any CRT that initializes during this
52 * pgwin32_putenv() execution, after the loop checks that CRT.
53 *
54 * Need a copy of the string so we can modify it.
55 */
56 envcpy = strdup(envval);
57 if (!envcpy)
58 return -1;
59 cp = strchr(envcpy, '=');
60 if (cp == NULL)
61 {
62 free(envcpy);
63 return -1;
64 }
65 *cp = '\0';
66 cp++;
67 if (strlen(cp))
68 {
69 /*
70 * Only call SetEnvironmentVariable() when we are adding a variable,
71 * not when removing it. Calling it on both crashes on at least
72 * certain versions of MinGW.
73 */
74 if (!SetEnvironmentVariable(envcpy, cp))
75 {
76 free(envcpy);
77 return -1;
78 }
79 }
80 free(envcpy);
81
82 /*
83 * Each CRT has its own _putenv() symbol and copy of the environment.
84 * Update the environment in each CRT module currently loaded, so every
85 * third-party library sees this change regardless of the CRT it links
86 * against. Addresses within these modules may become invalid the moment
87 * we call FreeLibrary(), so don't cache them.
88 */
89 for (i = 0; modulenames[i]; i++)
90 {
91 HMODULE hmodule = NULL;
92 BOOL res = GetModuleHandleEx(0, modulenames[i], &hmodule);
93
94 if (res != 0 && hmodule != NULL)
95 {
96 PUTENVPROC putenvFunc;
97
98 putenvFunc = (PUTENVPROC) GetProcAddress(hmodule, "_putenv");
99 if (putenvFunc)
100 putenvFunc(envval);
101 FreeLibrary(hmodule);
102 }
103 }
104
105 /*
106 * Finally, update our "own" cache. This is redundant with the loop
107 * above, except when PostgreSQL itself links to a CRT not listed above.
108 * Ideally, the loop does visit all possible CRTs, making this redundant.
109 */
110 return _putenv(envval);
111 }
112
113 void
pgwin32_unsetenv(const char * name)114 pgwin32_unsetenv(const char *name)
115 {
116 char *envbuf;
117
118 envbuf = (char *) malloc(strlen(name) + 2);
119 if (!envbuf)
120 return;
121
122 sprintf(envbuf, "%s=", name);
123 pgwin32_putenv(envbuf);
124 free(envbuf);
125 }
126