1 /* $NetBSD: _env.c,v 1.5 2010/11/17 13:25:53 tron Exp $ */ 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 static int inited = 0; 33 34 #include <sys/cdefs.h> 35 #if defined(LIBC_SCCS) && !defined(lint) 36 __RCSID("$NetBSD: _env.c,v 1.5 2010/11/17 13:25:53 tron Exp $"); 37 #endif /* LIBC_SCCS and not lint */ 38 39 #include "namespace.h" 40 41 #include <sys/rbtree.h> 42 43 #include <assert.h> 44 #include <errno.h> 45 #include <limits.h> 46 #include <stdlib.h> 47 #include <stddef.h> 48 #include <string.h> 49 50 #include "env.h" 51 #include "reentrant.h" 52 #include "local.h" 53 54 /* 55 * Red-Black tree node for tracking memory used by environment variables. 56 * The tree is sorted by the address of the nodes themselves. 57 */ 58 typedef struct { 59 rb_node_t rb_node; 60 size_t length; 61 uint8_t marker; 62 char data[]; 63 } env_node_t; 64 65 /* Compare functions for above tree. */ 66 static signed int env_tree_compare_nodes(void *, const void *, const void *); 67 static signed int env_tree_compare_key(void *, const void *, const void *); 68 69 /* Operations for above tree. */ 70 static const rb_tree_ops_t env_tree_ops = { 71 .rbto_compare_nodes = env_tree_compare_nodes, 72 .rbto_compare_key = env_tree_compare_key, 73 .rbto_node_offset = offsetof(env_node_t, rb_node), 74 .rbto_context = NULL 75 }; 76 77 /* The single instance of above tree. */ 78 static rb_tree_t env_tree; 79 80 /* The allocated environment. */ 81 static char **allocated_environ; 82 static size_t allocated_environ_size; 83 84 #define ENV_ARRAY_SIZE_MIN 16 85 86 /* The lock protecting access to the environment. */ 87 #ifdef _REENTRANT 88 static rwlock_t env_lock = RWLOCK_INITIALIZER; 89 #endif 90 91 /* Compatibility function. */ 92 char *__findenv(const char *name, int *offsetp); 93 94 __warn_references(__findenv, 95 "warning: __findenv is an internal obsolete function.") 96 97 /* Our initialization function. */ 98 void __libc_env_init(void); 99 100 /*ARGSUSED*/ 101 static signed int 102 env_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 118 static signed int 119 env_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 */ 129 size_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 */ 156 void 157 __freeenvvar(char *envvar) 158 { 159 env_node_t *node; 160 161 _DIAGASSERT(envvar != NULL); 162 assert(inited); 163 node = rb_tree_find_node(&env_tree, envvar); 164 if (node != NULL) { 165 rb_tree_remove_node(&env_tree, node); 166 free(node); 167 } 168 } 169 170 /* 171 * Allocate memory for an environment variable. This function must be called 172 * with the environment write locked. 173 */ 174 char * 175 __allocenvvar(size_t length) 176 { 177 env_node_t *node; 178 179 assert(inited); 180 node = malloc(sizeof(*node) + length); 181 if (node != NULL) { 182 node->length = length; 183 node->marker = 0; 184 rb_tree_insert_node(&env_tree, node); 185 return node->data; 186 } else { 187 return NULL; 188 } 189 } 190 191 /* 192 * Check whether an environment variable is writable. This function must be 193 * called with the environment write locked as the caller will probably 194 * overwrite the environment variable afterwards. 195 */ 196 bool 197 __canoverwriteenvvar(char *envvar, size_t length) 198 { 199 env_node_t *node; 200 201 assert(inited); 202 203 _DIAGASSERT(envvar != NULL); 204 205 node = rb_tree_find_node(&env_tree, envvar); 206 return (node != NULL && length <= node->length); 207 } 208 209 /* Free all allocated environment variables that are no longer used. */ 210 static void 211 __scrubenv(void) 212 { 213 static uint8_t marker = 0; 214 size_t num_entries; 215 env_node_t *node, *next; 216 217 assert(inited); 218 while (++marker == 0); 219 220 /* Mark all nodes which are currently used. */ 221 for (num_entries = 0; environ[num_entries] != NULL; num_entries++) { 222 node = rb_tree_find_node(&env_tree, environ[num_entries]); 223 if (node != NULL) 224 node->marker = marker; 225 } 226 227 /* Free all nodes which are currently not used. */ 228 for (node = RB_TREE_MIN(&env_tree); node != NULL; node = next) { 229 next = rb_tree_iterate(&env_tree, node, RB_DIR_RIGHT); 230 231 if (node->marker != marker) { 232 rb_tree_remove_node(&env_tree, node); 233 free(node); 234 } 235 } 236 237 /* Deal with the environment array itself. */ 238 if (environ == allocated_environ) { 239 /* Clear out spurious entries in the environment. */ 240 (void)memset(&environ[num_entries + 1], 0, 241 (allocated_environ_size - num_entries - 1) * 242 sizeof(*environ)); 243 } else { 244 /* 245 * The environment array was not allocated by "libc". 246 * Free our array if we allocated one. 247 */ 248 free(allocated_environ); 249 allocated_environ = NULL; 250 allocated_environ_size = 0; 251 } 252 } 253 254 /* 255 * Get a (new) slot in the environment. This function must be called with 256 * the environment write locked. 257 */ 258 ssize_t 259 __getenvslot(const char *name, size_t l_name, bool allocate) 260 { 261 size_t new_size, num_entries, required_size; 262 char **new_environ; 263 264 /* Does the environ need scrubbing? */ 265 if (environ != allocated_environ && allocated_environ != NULL) 266 __scrubenv(); 267 268 /* Search for an existing environment variable of the given name. */ 269 num_entries = 0; 270 while (environ[num_entries] != NULL) { 271 if (strncmp(environ[num_entries], name, l_name) == 0 && 272 environ[num_entries][l_name] == '=') { 273 /* We found a match. */ 274 return num_entries; 275 } 276 num_entries ++; 277 } 278 279 /* No match found, return if we don't want to allocate a new slot. */ 280 if (!allocate) 281 return -1; 282 283 /* Create a new slot in the environment. */ 284 required_size = num_entries + 1; 285 if (environ == allocated_environ && 286 required_size < allocated_environ_size) { 287 /* Does the environment need scrubbing? */ 288 if (required_size < allocated_environ_size && 289 allocated_environ[required_size] != NULL) { 290 __scrubenv(); 291 } 292 293 /* Return a free slot. */ 294 return num_entries; 295 } 296 297 /* Determine size of a new environment array. */ 298 new_size = ENV_ARRAY_SIZE_MIN; 299 while (new_size <= required_size) 300 new_size <<= 1; 301 302 /* Allocate a new environment array. */ 303 if (environ == allocated_environ) { 304 new_environ = realloc(environ, 305 new_size * sizeof(*new_environ)); 306 if (new_environ == NULL) 307 return -1; 308 } else { 309 free(allocated_environ); 310 allocated_environ = NULL; 311 allocated_environ_size = 0; 312 313 new_environ = malloc(new_size * sizeof(*new_environ)); 314 if (new_environ == NULL) 315 return -1; 316 (void)memcpy(new_environ, environ, 317 num_entries * sizeof(*new_environ)); 318 } 319 320 /* Clear remaining entries. */ 321 (void)memset(&new_environ[num_entries], 0, 322 (new_size - num_entries) * sizeof(*new_environ)); 323 324 /* Use the new environment array. */ 325 environ = allocated_environ = new_environ; 326 allocated_environ_size = new_size; 327 328 /* Return a free slot. */ 329 return num_entries; 330 } 331 332 /* Find a string in the environment. */ 333 char * 334 __findenvvar(const char *name, size_t l_name) 335 { 336 ssize_t offset; 337 338 offset = __getenvslot(name, l_name, false); 339 return (offset != -1) ? environ[offset] + l_name + 1 : NULL; 340 } 341 342 /* Compatibility interface, do *not* call this function. */ 343 char * 344 __findenv(const char *name, int *offsetp) 345 { 346 size_t l_name; 347 ssize_t offset; 348 349 l_name = __envvarnamelen(name, false); 350 if (l_name == 0) 351 return NULL; 352 353 offset = __getenvslot(name, l_name, false); 354 if (offset < 0 || offset > INT_MAX) 355 return NULL; 356 357 *offsetp = (int)offset; 358 return environ[offset] + l_name + 1; 359 } 360 361 #ifdef _REENTRANT 362 363 /* Lock the environment for read. */ 364 bool 365 __readlockenv(void) 366 { 367 int error; 368 369 error = rwlock_rdlock(&env_lock); 370 if (error == 0) 371 return true; 372 373 errno = error; 374 return false; 375 } 376 377 /* Lock the environment for write. */ 378 bool 379 __writelockenv(void) 380 { 381 int error; 382 383 error = rwlock_wrlock(&env_lock); 384 if (error == 0) 385 return true; 386 387 errno = error; 388 return false; 389 } 390 391 /* Unlock the environment for write. */ 392 bool 393 __unlockenv(void) 394 { 395 int error; 396 397 error = rwlock_unlock(&env_lock); 398 if (error == 0) 399 return true; 400 401 errno = error; 402 return false; 403 } 404 405 #endif 406 407 /* Initialize environment memory RB tree. */ 408 void 409 __libc_env_init(void) 410 { 411 assert(!inited); 412 rb_tree_init(&env_tree, &env_tree_ops); 413 inited = 1; 414 } 415