1 /*
2 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <errno.h>
8 #include <pthread.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <OS.h>
13
14 #include <errno_private.h>
15 #include <libroot_private.h>
16 #include <locks.h>
17 #include <runtime_loader.h>
18 #include <stdlib_private.h>
19 #include <syscall_utils.h>
20 #include <user_runtime.h>
21
22
23 static const char* const kEnvLockName = "env lock";
24
25 static mutex sEnvLock = MUTEX_INITIALIZER(kEnvLockName);
26 static char **sManagedEnviron;
27
28 char **environ = NULL;
29
30
31 static inline void
lock_variables(void)32 lock_variables(void)
33 {
34 mutex_lock(&sEnvLock);
35 }
36
37
38 static inline void
unlock_variables(void)39 unlock_variables(void)
40 {
41 mutex_unlock(&sEnvLock);
42 }
43
44
45 static void
free_variables(void)46 free_variables(void)
47 {
48 int32 i;
49
50 if (sManagedEnviron == NULL)
51 return;
52
53 for (i = 0; sManagedEnviron[i] != NULL; i++) {
54 free(sManagedEnviron[i]);
55 }
56
57 free(sManagedEnviron);
58 sManagedEnviron = NULL;
59 }
60
61
62 static int32
count_variables(void)63 count_variables(void)
64 {
65 int32 i = 0;
66
67 if (environ == NULL)
68 return 0;
69
70 while (environ[i])
71 i++;
72
73 return i;
74 }
75
76
77 static int32
add_variable(void)78 add_variable(void)
79 {
80 int32 count = count_variables() + 1;
81 char **newEnviron = (char**)realloc(environ, (count + 1) * sizeof(char *));
82 if (newEnviron == NULL)
83 return B_NO_MEMORY;
84
85 newEnviron[count] = NULL;
86 // null terminate the array
87
88 environ = sManagedEnviron = newEnviron;
89
90 return count - 1;
91 }
92
93
94 static char *
find_variable(const char * name,int32 length,int32 * _index)95 find_variable(const char *name, int32 length, int32 *_index)
96 {
97 int32 i;
98
99 if (environ == NULL)
100 return NULL;
101
102 for (i = 0; environ[i] != NULL; i++) {
103 if (!strncmp(name, environ[i], length) && environ[i][length] == '=') {
104 if (_index != NULL)
105 *_index = i;
106 return environ[i];
107 }
108 }
109
110 return NULL;
111 }
112
113
114 /*! Copies the environment from its current location into a heap managed
115 environment, if it's not already there.
116
117 This is needed whenever the environment is changed, that is, when one
118 of the POSIX *env() functions is called, and we either used the environment
119 provided by the kernel, or by an application that changed \c environ
120 directly.
121 */
122 static status_t
copy_environ_to_heap_if_needed(void)123 copy_environ_to_heap_if_needed(void)
124 {
125 int32 i = 0;
126
127 if (environ == sManagedEnviron)
128 return B_OK;
129
130 // free previously used "environ" if it has been changed by an application
131 free_variables();
132
133 sManagedEnviron = (char**)malloc((count_variables() + 1) * sizeof(char *));
134 if (sManagedEnviron == NULL)
135 return B_NO_MEMORY;
136
137 if (environ != NULL) {
138 // copy from previous
139 for (; environ[i]; i++) {
140 sManagedEnviron[i] = strdup(environ[i]);
141 }
142 }
143
144 sManagedEnviron[i] = NULL;
145 // null terminate the array
146
147 environ = sManagedEnviron;
148 return B_OK;
149 }
150
151
152 static status_t
update_variable(const char * name,int32 length,const char * value,bool overwrite)153 update_variable(const char *name, int32 length, const char *value,
154 bool overwrite)
155 {
156 bool update = false;
157 int32 index;
158 char *env;
159
160 copy_environ_to_heap_if_needed();
161
162 env = find_variable(name, length, &index);
163 if (env != NULL && overwrite) {
164 // change variable
165 free(environ[index]);
166 update = true;
167 } else if (env == NULL) {
168 // add variable
169 index = add_variable();
170 if (index < 0)
171 return B_NO_MEMORY;
172
173 update = true;
174 }
175
176 if (update) {
177 environ[index] = (char*)malloc(length + 2 + strlen(value));
178 if (environ[index] == NULL)
179 return B_NO_MEMORY;
180
181 memcpy(environ[index], name, length);
182 environ[index][length] = '=';
183 strcpy(environ[index] + length + 1, value);
184 }
185
186 return B_OK;
187 }
188
189
190 static void
environ_fork_hook(void)191 environ_fork_hook(void)
192 {
193 mutex_init(&sEnvLock, kEnvLockName);
194 }
195
196
197 // #pragma mark - libroot initializer
198
199
200 void
__init_env(const struct user_space_program_args * args)201 __init_env(const struct user_space_program_args *args)
202 {
203 // Following POSIX, there is no need to make any of the environment
204 // functions thread-safe - but we do it anyway as much as possible to
205 // protect our implementation
206 environ = args->env;
207 sManagedEnviron = NULL;
208 }
209
210
211 void
__init_env_post_heap()212 __init_env_post_heap()
213 {
214 atfork(environ_fork_hook);
215 }
216
217
218 // #pragma mark - public API
219
220
221 int
clearenv(void)222 clearenv(void)
223 {
224 lock_variables();
225
226 free_variables();
227 environ = NULL;
228
229 unlock_variables();
230
231 return 0;
232 }
233
234
235 char *
getenv(const char * name)236 getenv(const char *name)
237 {
238 int32 length = strlen(name);
239 char *value;
240
241 lock_variables();
242
243 value = find_variable(name, length, NULL);
244 unlock_variables();
245
246 if (value == NULL)
247 return NULL;
248
249 return value + length + 1;
250 }
251
252
253 int
setenv(const char * name,const char * value,int overwrite)254 setenv(const char *name, const char *value, int overwrite)
255 {
256 status_t status;
257
258 if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) {
259 __set_errno(B_BAD_VALUE);
260 return -1;
261 }
262
263 lock_variables();
264 status = update_variable(name, strlen(name), value, overwrite);
265 unlock_variables();
266
267 RETURN_AND_SET_ERRNO(status);
268 }
269
270
271 int
unsetenv(const char * name)272 unsetenv(const char *name)
273 {
274 int32 index, length;
275 char *env;
276
277 if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) {
278 __set_errno(B_BAD_VALUE);
279 return -1;
280 }
281
282 length = strlen(name);
283
284 lock_variables();
285
286 copy_environ_to_heap_if_needed();
287
288 env = find_variable(name, length, &index);
289 while (env != NULL) {
290 // we don't free the memory for the slot, we just move the array
291 // contents
292 free(env);
293 memmove(environ + index, environ + index + 1,
294 sizeof(char *) * (count_variables() - index));
295
296 // search possible another occurence, introduced via putenv()
297 // and renamed since
298 env = find_variable(name, length, &index);
299 }
300
301 unlock_variables();
302 return 0;
303 }
304
305
306 int
putenv(char * string)307 putenv(char *string)
308 {
309 char *value = strchr(string, '=');
310 status_t status;
311
312 if (value == NULL || value == string) {
313 __set_errno(B_BAD_VALUE);
314 return -1;
315 }
316
317 lock_variables();
318 status = update_variable(string, value - string, value + 1, true);
319 unlock_variables();
320
321 RETURN_AND_SET_ERRNO(status);
322 }
323
324
325 ssize_t
__getenv_reentrant(const char * name,char * buffer,size_t bufferSize)326 __getenv_reentrant(const char* name, char* buffer, size_t bufferSize)
327 {
328 size_t nameLength = strlen(name);
329
330 lock_variables();
331
332 char* value = find_variable(name, nameLength, NULL);
333 ssize_t result = value != NULL
334 ? strlcpy(buffer, value + nameLength + 1, bufferSize)
335 : B_NAME_NOT_FOUND;
336
337 unlock_variables();
338
339 return result;
340 }
341
342