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