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-2016, 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 
25 	/*
26 	 * Each CRT has its own _putenv() symbol and copy of the environment.
27 	 * Update the environment in each CRT module currently loaded, so every
28 	 * third-party library sees this change regardless of the CRT it links
29 	 * against.
30 	 */
31 #ifdef _MSC_VER
32 	typedef int (_cdecl * PUTENVPROC) (const char *);
33 	static struct
34 	{
35 		char	   *modulename;
36 		HMODULE		hmodule;
37 		PUTENVPROC	putenvFunc;
38 	}			rtmodules[] =
39 	{
40 		{
41 			"msvcrt", NULL, NULL
42 		},						/* Visual Studio 6.0 / MinGW */
43 		{
44 			"msvcrtd", NULL, NULL
45 		},
46 		{
47 			"msvcr70", NULL, NULL
48 		},						/* Visual Studio 2002 */
49 		{
50 			"msvcr70d", NULL, NULL
51 		},
52 		{
53 			"msvcr71", NULL, NULL
54 		},						/* Visual Studio 2003 */
55 		{
56 			"msvcr71d", NULL, NULL
57 		},
58 		{
59 			"msvcr80", NULL, NULL
60 		},						/* Visual Studio 2005 */
61 		{
62 			"msvcr80d", NULL, NULL
63 		},
64 		{
65 			"msvcr90", NULL, NULL
66 		},						/* Visual Studio 2008 */
67 		{
68 			"msvcr90d", NULL, NULL
69 		},
70 		{
71 			"msvcr100", NULL, NULL
72 		},						/* Visual Studio 2010 */
73 		{
74 			"msvcr100d", NULL, NULL
75 		},
76 		{
77 			"msvcr110", NULL, NULL
78 		},						/* Visual Studio 2012 */
79 		{
80 			"msvcr110d", NULL, NULL
81 		},
82 		{
83 			"msvcr120", NULL, NULL
84 		},						/* Visual Studio 2013 */
85 		{
86 			"msvcr120d", NULL, NULL
87 		},
88 		{
89 			"ucrtbase", NULL, NULL
90 		},						/* Visual Studio 2015 and later */
91 		{
92 			"ucrtbased", NULL, NULL
93 		},
94 		{
95 			NULL, NULL, NULL
96 		}
97 	};
98 	int			i;
99 
100 	for (i = 0; rtmodules[i].modulename; i++)
101 	{
102 		if (rtmodules[i].putenvFunc == NULL)
103 		{
104 			if (rtmodules[i].hmodule == NULL)
105 			{
106 				/* Not attempted before, so try to find this DLL */
107 				rtmodules[i].hmodule = GetModuleHandle(rtmodules[i].modulename);
108 				if (rtmodules[i].hmodule == NULL)
109 				{
110 					/*
111 					 * Set to INVALID_HANDLE_VALUE so we know we have tried
112 					 * this one before, and won't try again.
113 					 */
114 					rtmodules[i].hmodule = INVALID_HANDLE_VALUE;
115 					continue;
116 				}
117 				else
118 				{
119 					rtmodules[i].putenvFunc = (PUTENVPROC) GetProcAddress(rtmodules[i].hmodule, "_putenv");
120 					if (rtmodules[i].putenvFunc == NULL)
121 					{
122 						rtmodules[i].hmodule = INVALID_HANDLE_VALUE;
123 						continue;
124 					}
125 				}
126 			}
127 			else
128 			{
129 				/*
130 				 * Module loaded, but we did not find the function last time.
131 				 * We're not going to find it this time either...
132 				 */
133 				continue;
134 			}
135 		}
136 		/* At this point, putenvFunc is set or we have exited the loop */
137 		rtmodules[i].putenvFunc(envval);
138 	}
139 #endif   /* _MSC_VER */
140 
141 	/*
142 	 * Update process environment, making this change visible to child
143 	 * processes and to CRTs initializing in the future.
144 	 *
145 	 * Need a copy of the string so we can modify it.
146 	 */
147 	envcpy = strdup(envval);
148 	if (!envcpy)
149 		return -1;
150 	cp = strchr(envcpy, '=');
151 	if (cp == NULL)
152 	{
153 		free(envcpy);
154 		return -1;
155 	}
156 	*cp = '\0';
157 	cp++;
158 	if (strlen(cp))
159 	{
160 		/*
161 		 * Only call SetEnvironmentVariable() when we are adding a variable,
162 		 * not when removing it. Calling it on both crashes on at least
163 		 * certain versions of MinGW.
164 		 */
165 		if (!SetEnvironmentVariable(envcpy, cp))
166 		{
167 			free(envcpy);
168 			return -1;
169 		}
170 	}
171 	free(envcpy);
172 
173 	/* Finally, update our "own" cache */
174 	return _putenv(envval);
175 }
176 
177 void
pgwin32_unsetenv(const char * name)178 pgwin32_unsetenv(const char *name)
179 {
180 	char	   *envbuf;
181 
182 	envbuf = (char *) malloc(strlen(name) + 2);
183 	if (!envbuf)
184 		return;
185 
186 	sprintf(envbuf, "%s=", name);
187 	pgwin32_putenv(envbuf);
188 	free(envbuf);
189 }
190