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_osenv.h"
27 #include "erl_sys_driver.h"
28 #include "erl_alloc.h"
29 
30 static erts_osenv_t sysenv_global_env;
31 static erts_rwmtx_t sysenv_rwmtx;
32 
33 static void import_initial_env(void);
34 
erts_sys_env_init()35 void erts_sys_env_init() {
36     erts_rwmtx_init(&sysenv_rwmtx, "environ", NIL,
37         ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
38 
39     erts_osenv_init(&sysenv_global_env);
40     import_initial_env();
41 }
42 
erts_sys_rlock_global_osenv()43 const erts_osenv_t *erts_sys_rlock_global_osenv() {
44     erts_rwmtx_rlock(&sysenv_rwmtx);
45     return &sysenv_global_env;
46 }
47 
erts_sys_rwlock_global_osenv()48 erts_osenv_t *erts_sys_rwlock_global_osenv() {
49     erts_rwmtx_rwlock(&sysenv_rwmtx);
50     return &sysenv_global_env;
51 }
52 
erts_sys_runlock_global_osenv()53 void erts_sys_runlock_global_osenv() {
54     erts_rwmtx_runlock(&sysenv_rwmtx);
55 }
56 
erts_sys_rwunlock_global_osenv()57 void erts_sys_rwunlock_global_osenv() {
58     erts_rwmtx_rwunlock(&sysenv_rwmtx);
59 }
60 
erts_sys_explicit_host_getenv(char * key,char * value,size_t * size)61 int erts_sys_explicit_host_getenv(char *key, char *value, size_t *size) {
62     size_t new_size = GetEnvironmentVariableA(key, value, (DWORD)*size);
63 
64     if(new_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
65         return 0;
66     } else if(new_size > *size) {
67         return -1;
68     }
69 
70     *size = new_size;
71     return 1;
72 }
73 
erts_sys_explicit_8bit_putenv(char * key,char * value)74 int erts_sys_explicit_8bit_putenv(char *key, char *value) {
75     WCHAR *wide_key, *wide_value;
76     int key_length, value_length;
77     int result;
78 
79     /* Note that we do *NOT* honor the filename encoding flags (+fnu/+fnl)
80      * here; the previous implementation used SetEnvironmentVariableA and
81      * things may break if we step away from that. */
82 
83     key_length = MultiByteToWideChar(CP_ACP, 0, key, -1, NULL, 0);
84     value_length = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0);
85 
86     /* Report "not found" if either string isn't convertible. */
87     if(key_length == 0 || value_length == 0) {
88         return 0;
89     }
90 
91     wide_key = erts_alloc(ERTS_ALC_T_TMP, key_length * sizeof(WCHAR));
92     wide_value = erts_alloc(ERTS_ALC_T_TMP, value_length * sizeof(WCHAR));
93 
94     MultiByteToWideChar(CP_ACP, 0, key, -1, wide_key, key_length);
95     MultiByteToWideChar(CP_ACP, 0, value, -1, wide_value, value_length);
96 
97     {
98         erts_osenv_data_t env_key, env_value;
99         erts_osenv_t *env;
100 
101         env = erts_sys_rwlock_global_osenv();
102 
103         /* -1 to exclude the NUL terminator. */
104         env_key.length = (key_length - 1) * sizeof(WCHAR);
105         env_key.data = wide_key;
106 
107         env_value.length = (value_length - 1) * sizeof(WCHAR);
108         env_value.data = wide_value;
109 
110         result = erts_osenv_put_native(env, &env_key, &env_value);
111         erts_sys_rwunlock_global_osenv();
112     }
113 
114     erts_free(ERTS_ALC_T_TMP, wide_key);
115     erts_free(ERTS_ALC_T_TMP, wide_value);
116 
117     return result;
118 }
119 
erts_sys_explicit_8bit_getenv(char * key,char * value,size_t * size)120 int erts_sys_explicit_8bit_getenv(char *key, char *value, size_t *size) {
121     erts_osenv_data_t env_key, env_value;
122     int key_length, value_length, result;
123     WCHAR *wide_key, *wide_value;
124 
125     key_length = MultiByteToWideChar(CP_ACP, 0, key, -1, NULL, 0);
126 
127     /* Report "not found" if the string isn't convertible. */
128     if(key_length == 0) {
129         return 0;
130     }
131 
132     wide_key = erts_alloc(ERTS_ALC_T_TMP, key_length * sizeof(WCHAR));
133     MultiByteToWideChar(CP_ACP, 0, key, -1, wide_key, key_length);
134 
135     /* We assume that the worst possible size is twice the output buffer width,
136      * as we could theoretically be on a code page that requires surrogates. */
137     value_length = (*size) * 2;
138     wide_value = erts_alloc(ERTS_ALC_T_TMP, value_length * sizeof(WCHAR));
139 
140     {
141         const erts_osenv_t *env = erts_sys_rlock_global_osenv();
142 
143         /* -1 to exclude the NUL terminator. */
144         env_key.length = (key_length - 1) * sizeof(WCHAR);
145         env_key.data = wide_key;
146 
147         env_value.length = value_length * sizeof(WCHAR);
148         env_value.data = wide_value;
149 
150         result = erts_osenv_get_native(env, &env_key, &env_value);
151         erts_sys_runlock_global_osenv();
152     }
153 
154     if(result == 1 && env_value.length > 0) {
155         /* This function doesn't NUL-terminate if the provided size is >= 0,
156          * so we pass (*size - 1) to reserve space for it and then do it
157          * manually. */
158         *size = WideCharToMultiByte(CP_ACP, 0, env_value.data,
159             env_value.length / sizeof(WCHAR), value, *size - 1, NULL, NULL);
160 
161         if(*size == 0) {
162             if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
163                 result = -1;
164             } else {
165                 result = 0;
166             }
167         }
168     } else {
169         *size = 0;
170     }
171 
172     if(*size > 0) {
173         value[*size] = '\0';
174     }
175 
176     erts_free(ERTS_ALC_T_TMP, wide_key);
177     erts_free(ERTS_ALC_T_TMP, wide_value);
178 
179     return result;
180 }
181 
import_initial_env(void)182 static void import_initial_env(void) {
183     WCHAR *environment_block, *current_variable;
184 
185     environment_block = GetEnvironmentStringsW();
186     current_variable = environment_block;
187 
188     while(wcslen(current_variable) > 0) {
189         WCHAR *separator_index = wcschr(current_variable, L'=');
190 
191         /* We tolerate environment variables starting with '=' as the per-drive
192          * working directories are stored this way. */
193         if(separator_index == current_variable) {
194             separator_index = wcschr(separator_index + 1, L'=');
195         }
196 
197         if(separator_index != NULL && separator_index != current_variable) {
198             erts_osenv_data_t env_key, env_value;
199 
200             env_key.length = (separator_index - current_variable) * sizeof(WCHAR);
201             env_key.data = current_variable;
202 
203             env_value.length = (wcslen(separator_index) - 1) * sizeof(WCHAR);
204             env_value.data = separator_index + 1;
205 
206             erts_osenv_put_native(&sysenv_global_env, &env_key, &env_value);
207         }
208 
209         current_variable += wcslen(current_variable) + 1;
210     }
211 
212     FreeEnvironmentStringsW(environment_block);
213 }
214