1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2002-2017. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "sys.h"
26 #include "erl_sys_driver.h"
27 #include "erl_alloc.h"
28
29 static erts_osenv_t sysenv_global_env;
30 static erts_rwmtx_t sysenv_rwmtx;
31
32 static void import_initial_env(void);
33
erts_sys_env_init()34 void erts_sys_env_init() {
35 erts_rwmtx_init(&sysenv_rwmtx, "environ", NIL,
36 ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
37
38 erts_osenv_init(&sysenv_global_env);
39 import_initial_env();
40 }
41
erts_sys_rlock_global_osenv()42 const erts_osenv_t *erts_sys_rlock_global_osenv() {
43 erts_rwmtx_rlock(&sysenv_rwmtx);
44 return &sysenv_global_env;
45 }
46
erts_sys_rwlock_global_osenv()47 erts_osenv_t *erts_sys_rwlock_global_osenv() {
48 erts_rwmtx_rwlock(&sysenv_rwmtx);
49 return &sysenv_global_env;
50 }
51
erts_sys_runlock_global_osenv()52 void erts_sys_runlock_global_osenv() {
53 erts_rwmtx_runlock(&sysenv_rwmtx);
54 }
55
erts_sys_rwunlock_global_osenv()56 void erts_sys_rwunlock_global_osenv() {
57 erts_rwmtx_rwunlock(&sysenv_rwmtx);
58 }
59
erts_sys_explicit_host_getenv(char * key,char * value,size_t * size)60 int erts_sys_explicit_host_getenv(char *key, char *value, size_t *size) {
61 size_t new_size = GetEnvironmentVariableA(key, value, (DWORD)*size);
62
63 if(new_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
64 return 0;
65 } else if(new_size > *size) {
66 return -1;
67 }
68
69 *size = new_size;
70 return 1;
71 }
72
erts_sys_explicit_8bit_putenv(char * key,char * value)73 int erts_sys_explicit_8bit_putenv(char *key, char *value) {
74 WCHAR *wide_key, *wide_value;
75 int key_length, value_length;
76 int result;
77
78 /* Note that we do *NOT* honor the filename encoding flags (+fnu/+fnl)
79 * here; the previous implementation used SetEnvironmentVariableA and
80 * things may break if we step away from that. */
81
82 key_length = MultiByteToWideChar(CP_ACP, 0, key, -1, NULL, 0);
83 value_length = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0);
84
85 /* Report "not found" if either string isn't convertible. */
86 if(key_length == 0 || value_length == 0) {
87 return 0;
88 }
89
90 wide_key = erts_alloc(ERTS_ALC_T_TMP, key_length * sizeof(WCHAR));
91 wide_value = erts_alloc(ERTS_ALC_T_TMP, value_length * sizeof(WCHAR));
92
93 MultiByteToWideChar(CP_ACP, 0, key, -1, wide_key, key_length);
94 MultiByteToWideChar(CP_ACP, 0, value, -1, wide_value, value_length);
95
96 {
97 erts_osenv_data_t env_key, env_value;
98 erts_osenv_t *env;
99
100 env = erts_sys_rwlock_global_osenv();
101
102 /* -1 to exclude the NUL terminator. */
103 env_key.length = (key_length - 1) * sizeof(WCHAR);
104 env_key.data = wide_key;
105
106 env_value.length = (value_length - 1) * sizeof(WCHAR);
107 env_value.data = wide_value;
108
109 result = erts_osenv_put_native(env, &env_key, &env_value);
110 erts_sys_rwunlock_global_osenv();
111 }
112
113 erts_free(ERTS_ALC_T_TMP, wide_key);
114 erts_free(ERTS_ALC_T_TMP, wide_value);
115
116 return result;
117 }
118
erts_sys_explicit_8bit_getenv(char * key,char * value,size_t * size)119 int erts_sys_explicit_8bit_getenv(char *key, char *value, size_t *size) {
120 erts_osenv_data_t env_key, env_value;
121 int key_length, value_length, result;
122 WCHAR *wide_key, *wide_value;
123
124 key_length = MultiByteToWideChar(CP_ACP, 0, key, -1, NULL, 0);
125
126 /* Report "not found" if the string isn't convertible. */
127 if(key_length == 0) {
128 return 0;
129 }
130
131 wide_key = erts_alloc(ERTS_ALC_T_TMP, key_length * sizeof(WCHAR));
132 MultiByteToWideChar(CP_ACP, 0, key, -1, wide_key, key_length);
133
134 /* We assume that the worst possible size is twice the output buffer width,
135 * as we could theoretically be on a code page that requires surrogates. */
136 value_length = (*size) * 2;
137 wide_value = erts_alloc(ERTS_ALC_T_TMP, value_length * sizeof(WCHAR));
138
139 {
140 const erts_osenv_t *env = erts_sys_rlock_global_osenv();
141
142 /* -1 to exclude the NUL terminator. */
143 env_key.length = (key_length - 1) * sizeof(WCHAR);
144 env_key.data = wide_key;
145
146 env_value.length = value_length * sizeof(WCHAR);
147 env_value.data = wide_value;
148
149 result = erts_osenv_get_native(env, &env_key, &env_value);
150 erts_sys_runlock_global_osenv();
151 }
152
153 if(result == 1 && env_value.length > 0) {
154 /* This function doesn't NUL-terminate if the provided size is >= 0,
155 * so we pass (*size - 1) to reserve space for it and then do it
156 * manually. */
157 *size = WideCharToMultiByte(CP_ACP, 0, env_value.data,
158 env_value.length / sizeof(WCHAR), value, *size - 1, NULL, NULL);
159
160 if(*size == 0) {
161 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
162 result = -1;
163 } else {
164 result = 0;
165 }
166 }
167 } else {
168 *size = 0;
169 }
170
171 if(*size > 0) {
172 value[*size] = '\0';
173 }
174
175 erts_free(ERTS_ALC_T_TMP, wide_key);
176 erts_free(ERTS_ALC_T_TMP, wide_value);
177
178 return result;
179 }
180
import_initial_env(void)181 static void import_initial_env(void) {
182 WCHAR *environment_block, *current_variable;
183
184 environment_block = GetEnvironmentStringsW();
185 current_variable = environment_block;
186
187 while(wcslen(current_variable) > 0) {
188 WCHAR *separator_index = wcschr(current_variable, L'=');
189
190 /* We tolerate environment variables starting with '=' as the per-drive
191 * working directories are stored this way. */
192 if(separator_index == current_variable) {
193 separator_index = wcschr(separator_index + 1, L'=');
194 }
195
196 if(separator_index != NULL && separator_index != current_variable) {
197 erts_osenv_data_t env_key, env_value;
198
199 env_key.length = (separator_index - current_variable) * sizeof(WCHAR);
200 env_key.data = current_variable;
201
202 env_value.length = (wcslen(separator_index) - 1) * sizeof(WCHAR);
203 env_value.data = separator_index + 1;
204
205 erts_osenv_put_native(&sysenv_global_env, &env_key, &env_value);
206 }
207
208 current_variable += wcslen(current_variable) + 1;
209 }
210
211 FreeEnvironmentStringsW(environment_block);
212 }
213