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