VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/env-generic.cpp@ 4968

Last change on this file since 4968 was 4534, checked in by vboxsync, 17 years ago

FreeBSD needs extern char environ; Assume the other BSD needs this too.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 19.1 KB
Line 
1/* $Id: env-generic.cpp 4534 2007-09-05 14:02:30Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Environment, Generic.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/env.h>
23#include <iprt/assert.h>
24#include <iprt/alloc.h>
25#include <iprt/alloca.h>
26#include <iprt/string.h>
27#include <iprt/err.h>
28#include "internal/magics.h"
29
30#include <stdlib.h>
31#if !defined(RT_OS_WINDOWS)
32# include <unistd.h>
33#endif
34#ifdef RT_OS_DARWIN
35# include <crt_externs.h>
36#endif
37#if defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD)
38__BEGIN_DECLS
39extern char **environ;
40__END_DECLS
41#endif
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46/** Macro that unlocks the specified environment block. */
47#define RTENV_LOCK(pEnvInt) do { } while (0)
48/** Macro that unlocks the specified environment block. */
49#define RTENV_UNLOCK(pEnvInt) do { } while (0)
50
51
52/*******************************************************************************
53* Structures and Typedefs *
54*******************************************************************************/
55/**
56 * The internal representation of a (non-default) environment.
57 */
58typedef struct RTENVINTERNAL
59{
60 /** Magic value . */
61 uint32_t u32Magic;
62 /** Number of variables in the array.
63 * This does not include the terminating NULL entry. */
64 size_t cVars;
65 /** Capacity (allocated size) of the array.
66 * This includes space for the terminating NULL element (for compatibility
67 * with the C library), so that c <= cCapacity - 1. */
68 size_t cAllocated;
69 /** Array of environment variables. */
70 char **papszEnv;
71 /** Array of environment variables in the process CP.
72 * This get (re-)constructed when RTEnvGetExecEnvP method is called. */
73 char **papszEnvOtherCP;
74} RTENVINTERNAL, *PRTENVINTERNAL;
75
76/** The allocation granularity of the RTENVINTERNAL::papszEnv memory. */
77#define RTENV_GROW_SIZE 16
78
79
80/**
81 * Internal worker that resolves the pointer to the default
82 * process environment. (environ)
83 *
84 * @returns Pointer to the default environment.
85 * This may be NULL.
86 */
87static const char * const *rtEnvDefault(void)
88{
89#ifdef RT_OS_DARWIN
90 return *(_NSGetEnviron());
91#elif defined(RT_OS_L4)
92 /* So far, our L4 libraries do not include environment support. */
93 return NULL;
94#else
95 return environ;
96#endif
97}
98
99
100/**
101 * Internal worker that creates an environment handle with a specified capacity.
102 *
103 * @returns IPRT status code.
104 * @param ppIntEnv Where to store the result.
105 * @param cAllocated The initial array size.
106 */
107static int rtEnvCreate(PRTENVINTERNAL *ppIntEnv, size_t cAllocated)
108{
109 /*
110 * Allocate environment handle.
111 */
112 PRTENVINTERNAL pIntEnv = (PRTENVINTERNAL)RTMemAlloc(sizeof(*pIntEnv));
113 if (pIntEnv)
114 {
115 /*
116 * Pre-allocate the variable array.
117 */
118 pIntEnv->u32Magic = RTENV_MAGIC;
119 pIntEnv->papszEnvOtherCP = NULL;
120 pIntEnv->cVars = 0;
121 pIntEnv->cAllocated = RT_ALIGN_Z(RT_MAX(cAllocated, RTENV_GROW_SIZE), RTENV_GROW_SIZE);
122 pIntEnv->papszEnv = (char **)RTMemAllocZ(sizeof(pIntEnv->papszEnv[0]) * pIntEnv->cAllocated);
123 if (pIntEnv->papszEnv)
124 {
125 *ppIntEnv = pIntEnv;
126 return VINF_SUCCESS;
127 }
128
129 RTMemFree(pIntEnv);
130 }
131
132 return VERR_NO_MEMORY;
133}
134
135
136RTDECL(int) RTEnvCreate(PRTENV pEnv)
137{
138 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
139 return rtEnvCreate(pEnv, RTENV_GROW_SIZE);
140}
141
142
143RTDECL(int) RTEnvDestroy(RTENV Env)
144{
145 /*
146 * Ignore NIL_RTENV and validate input.
147 */
148 if ( Env == NIL_RTENV
149 || Env == RTENV_DEFAULT)
150 return VINF_SUCCESS;
151
152 PRTENVINTERNAL pIntEnv = Env;
153 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
154 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
155
156 /*
157 * Do the cleanup.
158 */
159 RTENV_LOCK(pIntEnv);
160 pIntEnv->u32Magic++;
161 size_t iVar = pIntEnv->cVars;
162 while (iVar-- > 0)
163 RTStrFree(pIntEnv->papszEnv[iVar]);
164 RTMemFree(pIntEnv->papszEnv);
165 pIntEnv->papszEnv = NULL;
166
167 if (pIntEnv->papszEnvOtherCP)
168 {
169 for (iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
170 {
171 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
172 pIntEnv->papszEnvOtherCP[iVar] = NULL;
173 }
174 RTMemFree(pIntEnv->papszEnvOtherCP);
175 pIntEnv->papszEnvOtherCP = NULL;
176 }
177
178 RTENV_UNLOCK(pIntEnv);
179 /*RTCritSectDelete(&pIntEnv->CritSect) */
180 RTMemFree(pIntEnv);
181
182 return VINF_SUCCESS;
183}
184
185
186RTDECL(int) RTEnvClone(PRTENV pEnv, RTENV EnvToClone)
187{
188 /*
189 * Validate input and figure out how many variable to clone and where to get them.
190 */
191 size_t cVars;
192 const char * const *papszEnv;
193 PRTENVINTERNAL pIntEnvToClone;
194 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
195 if (EnvToClone == RTENV_DEFAULT)
196 {
197 pIntEnvToClone = NULL;
198 papszEnv = rtEnvDefault();
199 cVars = 0;
200 if (papszEnv)
201 while (papszEnv[cVars])
202 cVars++;
203 }
204 else
205 {
206 pIntEnvToClone = EnvToClone;
207 AssertPtrReturn(pIntEnvToClone, VERR_INVALID_HANDLE);
208 AssertReturn(pIntEnvToClone->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
209 RTENV_LOCK(pIntEnvToClone);
210
211 papszEnv = pIntEnvToClone->papszEnv;
212 cVars = pIntEnvToClone->cVars;
213 }
214
215 /*
216 * Create the duplicate.
217 */
218 PRTENVINTERNAL pIntEnv;
219 int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */);
220 if (RT_SUCCESS(rc))
221 {
222 pIntEnv->cVars = cVars;
223 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
224 if (EnvToClone == RTENV_DEFAULT)
225 {
226 /* ASSUMES the default environment is in the current codepage. */
227 for (size_t iVar = 0; iVar < cVars; iVar++)
228 {
229 int rc = RTStrCurrentCPToUtf8(&pIntEnv->papszEnv[iVar], papszEnv[iVar]);
230 if (RT_FAILURE(rc))
231 {
232 pIntEnv->cVars = iVar;
233 RTEnvDestroy(pIntEnv);
234 return rc;
235 }
236 }
237 }
238 else
239 {
240 for (size_t iVar = 0; iVar < cVars; iVar++)
241 {
242 char *pszVar = RTStrDup(papszEnv[iVar]);
243 if (RT_UNLIKELY(!pszVar))
244 {
245 RTENV_UNLOCK(pIntEnvToClone);
246
247 pIntEnv->cVars = iVar;
248 RTEnvDestroy(pIntEnv);
249 return rc;
250 }
251 pIntEnv->papszEnv[iVar] = pszVar;
252 }
253 }
254
255 /* done */
256 *pEnv = pIntEnv;
257 }
258
259 if (pIntEnvToClone)
260 RTENV_UNLOCK(pIntEnvToClone);
261 return rc;
262}
263
264
265RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
266{
267 int rc;
268 AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
269 const char *pszEq = strchr(pszVarEqualValue, '=');
270 if (!pszEq)
271 rc = RTEnvUnsetEx(Env, pszVarEqualValue);
272 else
273 {
274 /*
275 * Make a copy of the variable name so we can terminate it
276 * properly and then pass the request on to RTEnvSetEx.
277 */
278 const char *pszValue = pszEq + 1;
279
280 size_t cchVar = pszEq - pszVarEqualValue;
281 Assert(cchVar < 1024);
282 char *pszVar = (char *)alloca(cchVar + 1);
283 memcpy(pszVar, pszVarEqualValue, cchVar);
284 pszVar[cchVar] = '\0';
285
286 rc = RTEnvSetEx(Env, pszVar, pszValue);
287 }
288 return rc;
289}
290
291
292RTDECL(int) RTEnvSetEx(RTENV Env, const char *pszVar, const char *pszValue)
293{
294 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
295 AssertReturn(*pszVar, VERR_INVALID_PARAMETER);
296 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
297
298 int rc;
299 if (Env == RTENV_DEFAULT)
300 {
301 /*
302 * Since RTEnvPut isn't UTF-8 clean and actually expects the strings
303 * to be in the current code page (codeset), we'll do the necessary
304 * conversions here.
305 */
306 char *pszVarOtherCP;
307 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
308 if (RT_SUCCESS(rc))
309 {
310 char *pszValueOtherCP;
311 rc = RTStrUtf8ToCurrentCP(&pszValueOtherCP, pszValue);
312 if (RT_SUCCESS(rc))
313 {
314 rc = RTEnvSet(pszVarOtherCP, pszValueOtherCP);
315 RTStrFree(pszValueOtherCP);
316 }
317 RTStrFree(pszVarOtherCP);
318 }
319 }
320 else
321 {
322 PRTENVINTERNAL pIntEnv = Env;
323 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
324 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
325
326 /*
327 * Create the variable string.
328 */
329 const size_t cchVar = strlen(pszVar);
330 const size_t cchValue = strlen(pszValue);
331 char *pszEntry = (char *)RTMemAlloc(cchVar + cchValue + 2);
332 if (pszEntry)
333 {
334 memcpy(pszEntry, pszVar, cchVar);
335 pszEntry[cchVar] = '=';
336 memcpy(&pszEntry[cchVar + 1], pszValue, cchValue + 1);
337
338 RTENV_LOCK(pIntEnv);
339
340 /*
341 * Find the location of the variable. (iVar = cVars if new)
342 */
343 rc = VINF_SUCCESS;
344 size_t iVar;
345 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
346 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
347 && pIntEnv->papszEnv[iVar][cchVar] == '=')
348 break;
349 if (iVar < pIntEnv->cVars)
350 {
351 /*
352 * Replace the current entry. Simple.
353 */
354 RTMemFree(pIntEnv->papszEnv[iVar]);
355 pIntEnv->papszEnv[iVar] = pszEntry;
356 }
357 else
358 {
359 /*
360 * Adding a new variable. Resize the array if required
361 * and then insert the new value at the end.
362 */
363 if (pIntEnv->cVars + 2 > pIntEnv->cAllocated)
364 {
365 void *pvNew = RTMemRealloc(pIntEnv->papszEnv, sizeof(char *) * (pIntEnv->cAllocated + RTENV_GROW_SIZE));
366 if (!pvNew)
367 rc = VERR_NO_MEMORY;
368 else
369 {
370 pIntEnv->papszEnv = (char **)pvNew;
371 pIntEnv->cAllocated += RTENV_GROW_SIZE;
372 for (size_t iNewVar = pIntEnv->cVars; iNewVar < pIntEnv->cAllocated; iNewVar++)
373 pIntEnv->papszEnv[iNewVar] = NULL;
374 }
375 }
376 if (RT_SUCCESS(rc))
377 {
378 pIntEnv->papszEnv[iVar] = pszEntry;
379 pIntEnv->papszEnv[iVar + 1] = NULL; /* this isn't really necessary, but doesn't hurt. */
380 pIntEnv->cVars++;
381 Assert(pIntEnv->cVars == iVar + 1);
382 }
383 }
384
385 RTENV_UNLOCK(pIntEnv);
386
387 if (RT_FAILURE(rc))
388 RTMemFree(pszEntry);
389 }
390 else
391 rc = VERR_NO_MEMORY;
392 }
393 return rc;
394}
395
396
397RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
398{
399 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
400 AssertReturn(*pszVar, VERR_INVALID_PARAMETER);
401
402 int rc;
403 if (Env == RTENV_DEFAULT)
404 {
405 /*
406 * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
407 * to be in the current code page (codeset), we'll do the necessary
408 * conversions here.
409 */
410 char *pszVarOtherCP;
411 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
412 if (RT_SUCCESS(rc))
413 {
414 rc = RTEnvUnset(pszVarOtherCP);
415 RTStrFree(pszVarOtherCP);
416 }
417 }
418 else
419 {
420 PRTENVINTERNAL pIntEnv = Env;
421 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
422 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
423
424 RTENV_LOCK(pIntEnv);
425
426 /*
427 * Remove all variable by the given name.
428 */
429 rc = VINF_ENV_VAR_NOT_FOUND;
430 const size_t cchVar = strlen(pszVar);
431 size_t iVar;
432 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
433 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
434 && pIntEnv->papszEnv[iVar][cchVar] == '=')
435 {
436 RTMemFree(pIntEnv->papszEnv[iVar]);
437 pIntEnv->cVars--;
438 if (pIntEnv->cVars > 0)
439 pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
440 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
441 rc = VINF_SUCCESS;
442 /* no break, there could be more. */
443 }
444
445 RTENV_UNLOCK(pIntEnv);
446 }
447 return rc;
448
449}
450
451
452RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
453{
454 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
455 AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
456 AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
457 AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
458
459 if (pcchActual)
460 *pcchActual = 0;
461 int rc;
462 if (Env == RTENV_DEFAULT)
463 {
464 /*
465 * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
466 * to be in the current code page (codeset), we'll do the necessary
467 * conversions here.
468 */
469 char *pszVarOtherCP;
470 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
471 if (RT_SUCCESS(rc))
472 {
473 const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
474 RTStrFree(pszVarOtherCP);
475 if (pszValueOtherCP)
476 {
477 char *pszValueUtf8;
478 rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
479 if (RT_SUCCESS(rc))
480 {
481 rc = VINF_SUCCESS;
482 size_t cch = strlen(pszValueUtf8);
483 if (pcchActual)
484 *pcchActual = cch;
485 if (pszValue && cbValue)
486 {
487 if (cch < cbValue)
488 memcpy(pszValue, pszValueUtf8, cch + 1);
489 else
490 rc = VERR_BUFFER_OVERFLOW;
491 }
492 }
493 }
494 else
495 rc = VERR_ENV_VAR_NOT_FOUND;
496 }
497 }
498 else
499 {
500 PRTENVINTERNAL pIntEnv = Env;
501 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
502 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
503
504 RTENV_LOCK(pIntEnv);
505
506 /*
507 * Locate the first variable and return it to the caller.
508 */
509 rc = VERR_ENV_VAR_NOT_FOUND;
510 const size_t cchVar = strlen(pszVar);
511 size_t iVar;
512 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
513 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
514 && pIntEnv->papszEnv[iVar][cchVar] == '=')
515 {
516 rc = VINF_SUCCESS;
517 const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
518 size_t cch = strlen(pszValueOrg);
519 if (pcchActual)
520 *pcchActual = cch;
521 if (pszValue && cbValue)
522 {
523 if (cch < cbValue)
524 memcpy(pszValue, pszValueOrg, cch + 1);
525 else
526 rc = VERR_BUFFER_OVERFLOW;
527 }
528 break;
529 }
530
531 RTENV_UNLOCK(pIntEnv);
532 }
533 return rc;
534
535}
536
537
538RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
539{
540 AssertPtrReturn(pszVar, false);
541
542 bool fExist = false;
543 if (Env == RTENV_DEFAULT)
544 {
545 /*
546 * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
547 * to be in the current code page (codeset), we'll do the necessary
548 * conversions here.
549 */
550 char *pszVarOtherCP;
551 int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
552 if (RT_SUCCESS(rc))
553 {
554 fExist = RTEnvExist(pszVarOtherCP);
555 RTStrFree(pszVarOtherCP);
556 }
557 }
558 else
559 {
560 PRTENVINTERNAL pIntEnv = Env;
561 AssertPtrReturn(pIntEnv, false);
562 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
563
564 RTENV_LOCK(pIntEnv);
565
566 /*
567 * Simple search.
568 */
569 const size_t cchVar = strlen(pszVar);
570 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
571 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
572 && pIntEnv->papszEnv[iVar][cchVar] == '=')
573 {
574 fExist = true;
575 break;
576 }
577
578 RTENV_UNLOCK(pIntEnv);
579 }
580 return fExist;
581}
582
583
584RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
585{
586 const char * const *papszRet;
587 if (Env == RTENV_DEFAULT)
588 {
589 papszRet = rtEnvDefault();
590 if (!papszRet)
591 {
592 static const char * const s_papszDummy[2] = { NULL, NULL };
593 papszRet = &s_papszDummy[0];
594 }
595 }
596 else
597 {
598 PRTENVINTERNAL pIntEnv = Env;
599 AssertPtrReturn(pIntEnv, NULL);
600 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
601
602 RTENV_LOCK(pIntEnv);
603
604 /*
605 * Free any old envp.
606 */
607 if (pIntEnv->papszEnvOtherCP)
608 {
609 for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
610 {
611 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
612 pIntEnv->papszEnvOtherCP[iVar] = NULL;
613 }
614 RTMemFree(pIntEnv->papszEnvOtherCP);
615 pIntEnv->papszEnvOtherCP = NULL;
616 }
617
618 /*
619 * Construct a new envp with the strings in the process code set.
620 */
621 char **papsz;
622 papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
623 if (papsz)
624 {
625 papsz[pIntEnv->cVars] = NULL;
626 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
627 {
628 int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
629 if (RT_FAILURE(rc))
630 {
631 /* RTEnvDestroy / we cleans up later. */
632 papsz[iVar] = NULL;
633 AssertRC(rc);
634 papszRet = NULL;
635 break;
636 }
637 }
638 }
639
640 RTENV_UNLOCK(pIntEnv);
641 }
642 return papszRet;
643}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette