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