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 #include <sys/cdefs.h> 33 #if defined(LIBC_SCCS) && !defined(lint) 34 __RCSID("$NetBSD: _env.c,v 1.5 2010/11/17 13:25:53 tron Exp $"); 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 */ 56 typedef 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. */ 64 static signed int env_tree_compare_nodes(void *, const void *, const void *); 65 static signed int env_tree_compare_key(void *, const void *, const void *); 66 67 /* Operations for above tree. */ 68 static 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. */ 76 static rb_tree_t env_tree; 77 78 /* The allocated environment. */ 79 static char **allocated_environ; 80 static 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 86 static rwlock_t env_lock = RWLOCK_INITIALIZER; 87 #endif 88 89 /* Compatibility function. */ 90 char *__findenv(const char *name, int *offsetp); 91 92 __warn_references(__findenv, 93 "warning: __findenv is an internal obsolete function.") 94 95 /* Our initialization function. */ 96 void __libc_env_init(void); 97 98 /*ARGSUSED*/ 99 static signed int 100 env_tree_compare_nodes(void *ctx, const void *node_a, const void *node_b) 101 { 102 uintptr_t addr_a, addr_b; 103 104 addr_a = (uintptr_t)node_a; 105 addr_b = (uintptr_t)node_b; 106 107 if (addr_a < addr_b) 108 return -1; 109 110 if (addr_a > addr_b) 111 return 1; 112 113 return 0; 114 } 115 116 static signed int 117 env_tree_compare_key(void *ctx, const void *node, const void *key) 118 { 119 return env_tree_compare_nodes(ctx, node, 120 (const uint8_t *)key - offsetof(env_node_t, data)); 121 } 122 123 /* 124 * Determine the of the name in an environment string. Return 0 if the 125 * name is not valid. 126 */ 127 size_t 128 __envvarnamelen(const char *str, bool withequal) 129 { 130 size_t l_name; 131 132 if (str == NULL) 133 return 0; 134 135 l_name = strcspn(str, "="); 136 if (l_name == 0) 137 return 0; 138 139 if (withequal) { 140 if (str[l_name] != '=') 141 return 0; 142 } else { 143 if (str[l_name] == '=') 144 return 0; 145 } 146 147 return l_name; 148 } 149 150 /* 151 * Free memory occupied by environment variable if possible. This function 152 * must be called with the environment write locked. 153 */ 154 void 155 __freeenvvar(char *envvar) 156 { 157 env_node_t *node; 158 159 _DIAGASSERT(envvar != NULL); 160 node = rb_tree_find_node(&env_tree, envvar); 161 if (node != NULL) { 162 rb_tree_remove_node(&env_tree, node); 163 free(node); 164 } 165 } 166 167 /* 168 * Allocate memory for an environment variable. This function must be called 169 * with the environment write locked. 170 */ 171 char * 172 __allocenvvar(size_t length) 173 { 174 env_node_t *node; 175 176 node = malloc(sizeof(*node) + length); 177 if (node != NULL) { 178 node->length = length; 179 node->marker = 0; 180 rb_tree_insert_node(&env_tree, node); 181 return node->data; 182 } else { 183 return NULL; 184 } 185 } 186 187 /* 188 * Check whether an environment variable is writable. This function must be 189 * called with the environment write locked as the caller will probably 190 * overwrite the environment variable afterwards. 191 */ 192 bool 193 __canoverwriteenvvar(char *envvar, size_t length) 194 { 195 env_node_t *node; 196 197 _DIAGASSERT(envvar != NULL); 198 199 node = rb_tree_find_node(&env_tree, envvar); 200 return (node != NULL && length <= node->length); 201 } 202 203 /* Free all allocated environment variables that are no longer used. */ 204 static void 205 __scrubenv(void) 206 { 207 static uint8_t marker = 0; 208 size_t num_entries; 209 env_node_t *node, *next; 210 211 while (++marker == 0); 212 213 /* Mark all nodes which are currently used. */ 214 for (num_entries = 0; environ[num_entries] != NULL; num_entries++) { 215 node = rb_tree_find_node(&env_tree, environ[num_entries]); 216 if (node != NULL) 217 node->marker = marker; 218 } 219 220 /* Free all nodes which are currently not used. */ 221 for (node = RB_TREE_MIN(&env_tree); node != NULL; node = next) { 222 next = rb_tree_iterate(&env_tree, node, RB_DIR_RIGHT); 223 224 if (node->marker != marker) { 225 rb_tree_remove_node(&env_tree, node); 226 free(node); 227 } 228 } 229 230 /* Deal with the environment array itself. */ 231 if (environ == allocated_environ) { 232 /* Clear out spurious entries in the environment. */ 233 (void)memset(&environ[num_entries + 1], 0, 234 (allocated_environ_size - num_entries - 1) * 235 sizeof(*environ)); 236 } else { 237 /* 238 * The environment array was not allocated by "libc". 239 * Free our array if we allocated one. 240 */ 241 free(allocated_environ); 242 allocated_environ = NULL; 243 allocated_environ_size = 0; 244 } 245 } 246 247 /* 248 * Get a (new) slot in the environment. This function must be called with 249 * the environment write locked. 250 */ 251 ssize_t 252 __getenvslot(const char *name, size_t l_name, bool allocate) 253 { 254 size_t new_size, num_entries, required_size; 255 char **new_environ; 256 257 /* Does the environ need scrubbing? */ 258 if (environ != allocated_environ && allocated_environ != NULL) 259 __scrubenv(); 260 261 /* Search for an existing environment variable of the given name. */ 262 num_entries = 0; 263 while (environ[num_entries] != NULL) { 264 if (strncmp(environ[num_entries], name, l_name) == 0 && 265 environ[num_entries][l_name] == '=') { 266 /* We found a match. */ 267 return num_entries; 268 } 269 num_entries ++; 270 } 271 272 /* No match found, return if we don't want to allocate a new slot. */ 273 if (!allocate) 274 return -1; 275 276 /* Create a new slot in the environment. */ 277 required_size = num_entries + 1; 278 if (environ == allocated_environ && 279 required_size < allocated_environ_size) { 280 /* Does the environment need scrubbing? */ 281 if (required_size < allocated_environ_size && 282 allocated_environ[required_size] != NULL) { 283 __scrubenv(); 284 } 285 286 /* Return a free slot. */ 287 return num_entries; 288 } 289 290 /* Determine size of a new environment array. */ 291 new_size = ENV_ARRAY_SIZE_MIN; 292 while (new_size <= required_size) 293 new_size <<= 1; 294 295 /* Allocate a new environment array. */ 296 if (environ == allocated_environ) { 297 new_environ = realloc(environ, 298 new_size * sizeof(*new_environ)); 299 if (new_environ == NULL) 300 return -1; 301 } else { 302 free(allocated_environ); 303 allocated_environ = NULL; 304 allocated_environ_size = 0; 305 306 new_environ = malloc(new_size * sizeof(*new_environ)); 307 if (new_environ == NULL) 308 return -1; 309 (void)memcpy(new_environ, environ, 310 num_entries * sizeof(*new_environ)); 311 } 312 313 /* Clear remaining entries. */ 314 (void)memset(&new_environ[num_entries], 0, 315 (new_size - num_entries) * sizeof(*new_environ)); 316 317 /* Use the new environment array. */ 318 environ = allocated_environ = new_environ; 319 allocated_environ_size = new_size; 320 321 /* Return a free slot. */ 322 return num_entries; 323 } 324 325 /* Find a string in the environment. */ 326 char * 327 __findenvvar(const char *name, size_t l_name) 328 { 329 ssize_t offset; 330 331 offset = __getenvslot(name, l_name, false); 332 return (offset != -1) ? environ[offset] + l_name + 1 : NULL; 333 } 334 335 /* Compatibility interface, do *not* call this function. */ 336 char * 337 __findenv(const char *name, int *offsetp) 338 { 339 size_t l_name; 340 ssize_t offset; 341 342 l_name = __envvarnamelen(name, false); 343 if (l_name == 0) 344 return NULL; 345 346 offset = __getenvslot(name, l_name, false); 347 if (offset < 0 || offset > INT_MAX) 348 return NULL; 349 350 *offsetp = (int)offset; 351 return environ[offset] + l_name + 1; 352 } 353 354 #ifdef _REENTRANT 355 356 /* Lock the environment for read. */ 357 bool 358 __readlockenv(void) 359 { 360 int error; 361 362 error = rwlock_rdlock(&env_lock); 363 if (error == 0) 364 return true; 365 366 errno = error; 367 return false; 368 } 369 370 /* Lock the environment for write. */ 371 bool 372 __writelockenv(void) 373 { 374 int error; 375 376 error = rwlock_wrlock(&env_lock); 377 if (error == 0) 378 return true; 379 380 errno = error; 381 return false; 382 } 383 384 /* Unlock the environment for write. */ 385 bool 386 __unlockenv(void) 387 { 388 int error; 389 390 error = rwlock_unlock(&env_lock); 391 if (error == 0) 392 return true; 393 394 errno = error; 395 return false; 396 } 397 398 #endif 399 400 /* Initialize environment memory RB tree. */ 401 void 402 __libc_env_init(void) 403 { 404 rb_tree_init(&env_tree, &env_tree_ops); 405 } 406