1/* $NetBSD$ */ 2 3/*- 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matthias Scheler. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#if defined(LIBC_SCCS) && !defined(lint) 34__RCSID("$NetBSD$"); 35#endif /* LIBC_SCCS and not lint */ 36 37#include "namespace.h" 38 39#include <sys/rbtree.h> 40 41#include <assert.h> 42#include <errno.h> 43#include <limits.h> 44#include <stdlib.h> 45#include <stddef.h> 46#include <string.h> 47 48#include "env.h" 49#include "reentrant.h" 50#include "local.h" 51 52/* 53 * Red-Black tree node for tracking memory used by environment variables. 54 * The tree is sorted by the address of the nodes themselves. 55 */ 56typedef struct { 57 rb_node_t rb_node; 58 size_t length; 59 uint8_t marker; 60 char data[]; 61} env_node_t; 62 63/* Compare functions for above tree. */ 64static signed int env_tree_compare_nodes(void *, const void *, const void *); 65static signed int env_tree_compare_key(void *, const void *, const void *); 66 67/* Operations for above tree. */ 68static const rb_tree_ops_t env_tree_ops = { 69 .rbto_compare_nodes = env_tree_compare_nodes, 70 .rbto_compare_key = env_tree_compare_key, 71 .rbto_node_offset = offsetof(env_node_t, rb_node), 72 .rbto_context = NULL 73}; 74 75/* The single instance of above tree. */ 76static rb_tree_t env_tree; 77 78/* The allocated environment. */ 79static char **allocated_environ; 80static size_t allocated_environ_size; 81 82#define ENV_ARRAY_SIZE_MIN 16 83 84/* The lock protecting access to the environment. */ 85#ifdef _REENTRANT 86static rwlock_t env_lock = RWLOCK_INITIALIZER; 87#endif 88 89/* Compatibility function. */ 90char *__findenv(const char *name, int *offsetp); 91 92__warn_references(__findenv, 93 "warning: __findenv is an internal obsolete function.") 94 95/* Our initialization function. */ 96void __libc_env_init(void); 97 98char **environ; 99 100/*ARGSUSED*/ 101static signed int 102env_tree_compare_nodes(void *ctx, const void *node_a, const void *node_b) 103{ 104 uintptr_t addr_a, addr_b; 105 106 addr_a = (uintptr_t)node_a; 107 addr_b = (uintptr_t)node_b; 108 109 if (addr_a < addr_b) 110 return -1; 111 112 if (addr_a > addr_b) 113 return 1; 114 115 return 0; 116} 117 118static signed int 119env_tree_compare_key(void *ctx, const void *node, const void *key) 120{ 121 return env_tree_compare_nodes(ctx, node, 122 (const uint8_t *)key - offsetof(env_node_t, data)); 123} 124 125/* 126 * Determine the of the name in an environment string. Return 0 if the 127 * name is not valid. 128 */ 129size_t 130__envvarnamelen(const char *str, bool withequal) 131{ 132 size_t l_name; 133 134 if (str == NULL) 135 return 0; 136 137 l_name = strcspn(str, "="); 138 if (l_name == 0) 139 return 0; 140 141 if (withequal) { 142 if (str[l_name] != '=') 143 return 0; 144 } else { 145 if (str[l_name] == '=') 146 return 0; 147 } 148 149 return l_name; 150} 151 152/* 153 * Free memory occupied by environment variable if possible. This function 154 * must be called with the environment write locked. 155 */ 156void 157__freeenvvar(char *envvar) 158{ 159 env_node_t *node; 160 161 _DIAGASSERT(envvar != NULL); 162 node = rb_tree_find_node(&env_tree, envvar); 163 if (node != NULL) { 164 rb_tree_remove_node(&env_tree, node); 165 free(node); 166 } 167} 168 169/* 170 * Allocate memory for an environment variable. This function must be called 171 * with the environment write locked. 172 */ 173char * 174__allocenvvar(size_t length) 175{ 176 env_node_t *node; 177 178 node = malloc(sizeof(*node) + length); 179 if (node != NULL) { 180 node->length = length; 181 node->marker = 0; 182 rb_tree_insert_node(&env_tree, node); 183 return node->data; 184 } else { 185 return NULL; 186 } 187} 188 189/* 190 * Check whether an environment variable is writable. This function must be 191 * called with the environment write locked as the caller will probably 192 * overwrite the environment variable afterwards. 193 */ 194bool 195__canoverwriteenvvar(char *envvar, size_t length) 196{ 197 env_node_t *node; 198 199 _DIAGASSERT(envvar != NULL); 200 201 node = rb_tree_find_node(&env_tree, envvar); 202 return (node != NULL && length <= node->length); 203} 204 205/* Free all allocated environment variables that are no longer used. */ 206static void 207__scrubenv(void) 208{ 209 static uint8_t marker = 0; 210 size_t num_entries; 211 env_node_t *node, *next; 212 213 while (++marker == 0); 214 215 /* Mark all nodes which are currently used. */ 216 for (num_entries = 0; environ[num_entries] != NULL; num_entries++) { 217 node = rb_tree_find_node(&env_tree, environ[num_entries]); 218 if (node != NULL) 219 node->marker = marker; 220 } 221 222 /* Free all nodes which are currently not used. */ 223 for (node = RB_TREE_MIN(&env_tree); node != NULL; node = next) { 224 next = rb_tree_iterate(&env_tree, node, RB_DIR_RIGHT); 225 226 if (node->marker != marker) { 227 rb_tree_remove_node(&env_tree, node); 228 free(node); 229 } 230 } 231 232 /* Deal with the environment array itself. */ 233 if (environ == allocated_environ) { 234 /* Clear out spurious entries in the environment. */ 235 (void)memset(&environ[num_entries + 1], 0, 236 (allocated_environ_size - num_entries - 1) * 237 sizeof(*environ)); 238 } else { 239 /* 240 * The environment array was not allocated by "libc". 241 * Free our array if we allocated one. 242 */ 243 free(allocated_environ); 244 allocated_environ = NULL; 245 allocated_environ_size = 0; 246 } 247} 248 249/* 250 * Get a (new) slot in the environment. This function must be called with 251 * the environment write locked. 252 */ 253ssize_t 254__getenvslot(const char *name, size_t l_name, bool allocate) 255{ 256 size_t new_size, num_entries, required_size; 257 char **new_environ; 258 259 /* Search for an existing environment variable of the given name. */ 260 num_entries = 0; 261 while (environ[num_entries] != NULL) { 262 if (strncmp(environ[num_entries], name, l_name) == 0 && 263 environ[num_entries][l_name] == '=') { 264 /* We found a match. */ 265 return num_entries; 266 } 267 num_entries ++; 268 } 269 270 /* No match found, return if we don't want to allocate a new slot. */ 271 if (!allocate) 272 return -1; 273 274 /* Does the environ need scrubbing? */ 275 if (environ != allocated_environ && allocated_environ != NULL) 276 __scrubenv(); 277 278 /* Create a new slot in the environment. */ 279 required_size = num_entries + 1; 280 if (environ == allocated_environ && 281 required_size < allocated_environ_size) { 282 /* Does the environment need scrubbing? */ 283 if (required_size < allocated_environ_size && 284 allocated_environ[required_size] != NULL) { 285 __scrubenv(); 286 } 287 288 /* Return a free slot. */ 289 return num_entries; 290 } 291 292 /* Determine size of a new environment array. */ 293 new_size = ENV_ARRAY_SIZE_MIN; 294 while (new_size <= required_size) 295 new_size <<= 1; 296 297 /* Allocate a new environment array. */ 298 if (environ == allocated_environ) { 299 new_environ = realloc(environ, 300 new_size * sizeof(*new_environ)); 301 if (new_environ == NULL) 302 return -1; 303 } else { 304 free(allocated_environ); 305 allocated_environ = NULL; 306 allocated_environ_size = 0; 307 308 new_environ = malloc(new_size * sizeof(*new_environ)); 309 if (new_environ == NULL) 310 return -1; 311 (void)memcpy(new_environ, environ, 312 num_entries * sizeof(*new_environ)); 313 } 314 315 /* Clear remaining entries. */ 316 (void)memset(&new_environ[num_entries], 0, 317 (new_size - num_entries) * sizeof(*new_environ)); 318 319 /* Use the new environment array. */ 320 environ = allocated_environ = new_environ; 321 allocated_environ_size = new_size; 322 323 /* Return a free slot. */ 324 return num_entries; 325} 326 327/* Find a string in the environment. */ 328char * 329__findenvvar(const char *name, size_t l_name) 330{ 331 ssize_t offset; 332 333 offset = __getenvslot(name, l_name, false); 334 return (offset != -1) ? environ[offset] + l_name + 1 : NULL; 335} 336 337/* Compatibility interface, do *not* call this function. */ 338char * 339__findenv(const char *name, int *offsetp) 340{ 341 size_t l_name; 342 ssize_t offset; 343 344 l_name = __envvarnamelen(name, false); 345 if (l_name == 0) 346 return NULL; 347 348 offset = __getenvslot(name, l_name, false); 349 if (offset < 0 || offset > INT_MAX) 350 return NULL; 351 352 *offsetp = (int)offset; 353 return environ[offset] + l_name + 1; 354} 355 356#ifdef _REENTRANT 357 358/* Lock the environment for read. */ 359bool 360__readlockenv(void) 361{ 362 int error; 363 364 error = rwlock_rdlock(&env_lock); 365 if (error == 0) 366 return true; 367 368 errno = error; 369 return false; 370} 371 372/* Lock the environment for write. */ 373bool 374__writelockenv(void) 375{ 376 int error; 377 378 error = rwlock_wrlock(&env_lock); 379 if (error == 0) 380 return true; 381 382 errno = error; 383 return false; 384} 385 386/* Unlock the environment for write. */ 387bool 388__unlockenv(void) 389{ 390 int error; 391 392 error = rwlock_unlock(&env_lock); 393 if (error == 0) 394 return true; 395 396 errno = error; 397 return false; 398} 399 400#endif 401 402/* Initialize environment memory RB tree. */ 403void 404__libc_env_init(void) 405{ 406 rb_tree_init(&env_tree, &env_tree_ops); 407} 408