xref: /haiku/src/system/libroot/posix/stdlib/env.cpp (revision 7b3e89c0944ae1efa9a8fc66c7303874b7a344b2)
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
32 lock_variables(void)
33 {
34 	mutex_lock(&sEnvLock);
35 }
36 
37 
38 static inline void
39 unlock_variables(void)
40 {
41 	mutex_unlock(&sEnvLock);
42 }
43 
44 
45 static 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
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
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 *
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
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
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
191 environ_fork_hook(void)
192 {
193 	mutex_init(&sEnvLock, kEnvLockName);
194 }
195 
196 
197 //	#pragma mark - libroot initializer
198 
199 
200 void
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
212 __init_env_post_heap()
213 {
214 	atfork(environ_fork_hook);
215 }
216 
217 
218 //	#pragma mark - public API
219 
220 
221 int
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 *
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
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
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
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
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