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
23static const char* const kEnvLockName = "env lock";
24
25static mutex sEnvLock = MUTEX_INITIALIZER(kEnvLockName);
26static char **sManagedEnviron;
27
28char **environ = NULL;
29
30
31static inline void
32lock_variables(void)
33{
34	mutex_lock(&sEnvLock);
35}
36
37
38static inline void
39unlock_variables(void)
40{
41	mutex_unlock(&sEnvLock);
42}
43
44
45static void
46free_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
62static int32
63count_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
77static int32
78add_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
94static char *
95find_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*/
122static status_t
123copy_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
152static status_t
153update_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
190static void
191environ_fork_hook(void)
192{
193	mutex_init(&sEnvLock, kEnvLockName);
194}
195
196
197//	#pragma mark - libroot initializer
198
199
200void
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
211void
212__init_env_post_heap()
213{
214	atfork(environ_fork_hook);
215}
216
217
218//	#pragma mark - public API
219
220
221int
222clearenv(void)
223{
224	lock_variables();
225
226	free_variables();
227	environ = NULL;
228
229	unlock_variables();
230
231	return 0;
232}
233
234
235char *
236getenv(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
253int
254setenv(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
271int
272unsetenv(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
306int
307putenv(char *string)
308{
309	char *value = strchr(string, '=');
310	status_t status;
311
312	if (value == NULL) {
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
325ssize_t
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