VirtualBox

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

Last change on this file since 8148 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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