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