xref: /haiku/src/system/libroot/posix/stdlib/env.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
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 	atfork(environ_fork_hook);
210 }
211 
212 
213 //	#pragma mark - public API
214 
215 
216 int
217 clearenv(void)
218 {
219 	lock_variables();
220 
221 	free_variables();
222 	environ = NULL;
223 
224 	unlock_variables();
225 
226 	return 0;
227 }
228 
229 
230 char *
231 getenv(const char *name)
232 {
233 	int32 length = strlen(name);
234 	char *value;
235 
236 	lock_variables();
237 
238 	value = find_variable(name, length, NULL);
239 	unlock_variables();
240 
241 	if (value == NULL)
242 		return NULL;
243 
244 	return value + length + 1;
245 }
246 
247 
248 int
249 setenv(const char *name, const char *value, int overwrite)
250 {
251 	status_t status;
252 
253 	if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) {
254 		__set_errno(B_BAD_VALUE);
255 		return -1;
256 	}
257 
258 	lock_variables();
259 	status = update_variable(name, strlen(name), value, overwrite);
260 	unlock_variables();
261 
262 	RETURN_AND_SET_ERRNO(status);
263 }
264 
265 
266 int
267 unsetenv(const char *name)
268 {
269 	int32 index, length;
270 	char *env;
271 
272 	if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) {
273 		__set_errno(B_BAD_VALUE);
274 		return -1;
275 	}
276 
277 	length = strlen(name);
278 
279 	lock_variables();
280 
281 	copy_environ_to_heap_if_needed();
282 
283 	env = find_variable(name, length, &index);
284 	while (env != NULL) {
285 		// we don't free the memory for the slot, we just move the array
286 		// contents
287 		free(env);
288 		memmove(environ + index, environ + index + 1,
289 			sizeof(char *) * (count_variables() - index));
290 
291 		// search possible another occurence, introduced via putenv()
292 		// and renamed since
293 		env = find_variable(name, length, &index);
294 	}
295 
296 	unlock_variables();
297 	return 0;
298 }
299 
300 
301 int
302 putenv(const char *string)
303 {
304 	char *value = strchr(string, '=');
305 	status_t status;
306 
307 	if (value == NULL) {
308 		__set_errno(B_BAD_VALUE);
309 		return -1;
310 	}
311 
312 	lock_variables();
313 	status = update_variable(string, value - string, value + 1, true);
314 	unlock_variables();
315 
316 	RETURN_AND_SET_ERRNO(status);
317 }
318 
319 
320 ssize_t
321 __getenv_reentrant(const char* name, char* buffer, size_t bufferSize)
322 {
323 	size_t nameLength = strlen(name);
324 
325 	lock_variables();
326 
327 	char* value = find_variable(name, nameLength, NULL);
328 	ssize_t result = value != NULL
329 		? strlcpy(buffer, value + nameLength + 1, bufferSize)
330 		: B_NAME_NOT_FOUND;
331 
332 	unlock_variables();
333 
334 	return result;
335 }
336 
337