env.c (7205B)
1 /* Copyright (c) 2003-2004, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file env.c 8 * \brief Inspect and manipulate the environment variables. 9 **/ 10 11 #include "orconfig.h" 12 #include "lib/process/env.h" 13 14 #include "lib/malloc/malloc.h" 15 #include "lib/ctime/di_ops.h" 16 #include "lib/container/smartlist.h" 17 #include "lib/log/util_bug.h" 18 #include "lib/log/log.h" 19 20 #ifdef HAVE_UNISTD_H 21 #include <unistd.h> 22 #endif 23 #include <stdlib.h> 24 #include <string.h> 25 #ifdef HAVE_CRT_EXTERNS_H 26 /* For _NSGetEnviron on macOS */ 27 #include <crt_externs.h> 28 #endif 29 30 #ifndef HAVE__NSGETENVIRON 31 #ifndef HAVE_EXTERN_ENVIRON_DECLARED 32 /* Some platforms declare environ under some circumstances, others don't. */ 33 #ifndef RUNNING_DOXYGEN 34 extern char **environ; 35 #endif 36 #endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */ 37 #endif /* !defined(HAVE__NSGETENVIRON) */ 38 39 /** Return the current environment. This is a portable replacement for 40 * 'environ'. */ 41 char ** 42 get_environment(void) 43 { 44 #ifdef HAVE__NSGETENVIRON 45 /* This is for compatibility between OSX versions. Otherwise (for example) 46 * when we do a mostly-static build on OSX 10.7, the resulting binary won't 47 * work on OSX 10.6. */ 48 return *_NSGetEnviron(); 49 #else /* !defined(HAVE__NSGETENVIRON) */ 50 return environ; 51 #endif /* defined(HAVE__NSGETENVIRON) */ 52 } 53 54 /** Helper: return the number of characters in <b>s</b> preceding the first 55 * occurrence of <b>ch</b>. If <b>ch</b> does not occur in <b>s</b>, return 56 * the length of <b>s</b>. Should be equivalent to strspn(s, "ch"). */ 57 static inline size_t 58 str_num_before(const char *s, char ch) 59 { 60 const char *cp = strchr(s, ch); 61 if (cp) 62 return cp - s; 63 else 64 return strlen(s); 65 } 66 67 /** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b> 68 * to have the same name as strings in a process's environment. */ 69 int 70 environment_variable_names_equal(const char *s1, const char *s2) 71 { 72 size_t s1_name_len = str_num_before(s1, '='); 73 size_t s2_name_len = str_num_before(s2, '='); 74 75 return (s1_name_len == s2_name_len && 76 tor_memeq(s1, s2, s1_name_len)); 77 } 78 79 /** Free <b>env</b> (assuming it was produced by 80 * process_environment_make). */ 81 void 82 process_environment_free_(process_environment_t *env) 83 { 84 if (env == NULL) return; 85 86 /* As both an optimization hack to reduce consing on Unixoid systems 87 * and a nice way to ensure that some otherwise-Windows-specific 88 * code will always get tested before changes to it get merged, the 89 * strings which env->unixoid_environment_block points to are packed 90 * into env->windows_environment_block. */ 91 tor_free(env->unixoid_environment_block); 92 tor_free(env->windows_environment_block); 93 94 tor_free(env); 95 } 96 97 /** Make a process_environment_t containing the environment variables 98 * specified in <b>env_vars</b> (as C strings of the form 99 * "NAME=VALUE"). */ 100 process_environment_t * 101 process_environment_make(struct smartlist_t *env_vars) 102 { 103 process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t)); 104 int n_env_vars = smartlist_len(env_vars); 105 int i; 106 size_t total_env_length; 107 smartlist_t *env_vars_sorted; 108 109 tor_assert(n_env_vars + 1 != 0); 110 env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *)); 111 /* env->unixoid_environment_block is already NULL-terminated, 112 * because we assume that NULL == 0 (and check that during compilation). */ 113 114 total_env_length = 1; /* terminating NUL of terminating empty string */ 115 for (i = 0; i < n_env_vars; ++i) { 116 const char *s = smartlist_get(env_vars, (int)i); 117 size_t slen = strlen(s); 118 119 tor_assert(slen + 1 != 0); 120 tor_assert(slen + 1 < SIZE_MAX - total_env_length); 121 total_env_length += slen + 1; 122 } 123 124 env->windows_environment_block = tor_malloc_zero(total_env_length); 125 /* env->windows_environment_block is already 126 * (NUL-terminated-empty-string)-terminated. */ 127 128 /* Some versions of Windows supposedly require that environment 129 * blocks be sorted. Or maybe some Windows programs (or their 130 * runtime libraries) fail to look up strings in non-sorted 131 * environment blocks. 132 * 133 * Also, sorting strings makes it easy to find duplicate environment 134 * variables and environment-variable strings without an '=' on all 135 * OSes, and they can cause badness. Let's complain about those. */ 136 env_vars_sorted = smartlist_new(); 137 smartlist_add_all(env_vars_sorted, env_vars); 138 smartlist_sort_strings(env_vars_sorted); 139 140 /* Now copy the strings into the environment blocks. */ 141 { 142 char *cp = env->windows_environment_block; 143 const char *prev_env_var = NULL; 144 145 for (i = 0; i < n_env_vars; ++i) { 146 const char *s = smartlist_get(env_vars_sorted, (int)i); 147 size_t slen = strlen(s); 148 size_t s_name_len = str_num_before(s, '='); 149 150 if (s_name_len == slen) { 151 log_warn(LD_GENERAL, 152 "Preparing an environment containing a variable " 153 "without a value: %s", 154 s); 155 } 156 if (prev_env_var != NULL && 157 environment_variable_names_equal(s, prev_env_var)) { 158 log_warn(LD_GENERAL, 159 "Preparing an environment containing two variables " 160 "with the same name: %s and %s", 161 prev_env_var, s); 162 } 163 164 prev_env_var = s; 165 166 /* Actually copy the string into the environment. */ 167 memcpy(cp, s, slen+1); 168 env->unixoid_environment_block[i] = cp; 169 cp += slen+1; 170 } 171 172 tor_assert(cp == env->windows_environment_block + total_env_length - 1); 173 } 174 175 smartlist_free(env_vars_sorted); 176 177 return env; 178 } 179 180 /** Return a newly allocated smartlist containing every variable in 181 * this process's environment, as a NUL-terminated string of the form 182 * "NAME=VALUE". Note that on some/many/most/all OSes, the parent 183 * process can put strings not of that form in our environment; 184 * callers should try to not get crashed by that. 185 * 186 * The returned strings are heap-allocated, and must be freed by the 187 * caller. */ 188 struct smartlist_t * 189 get_current_process_environment_variables(void) 190 { 191 smartlist_t *sl = smartlist_new(); 192 193 char **environ_tmp; /* Not const char ** ? Really? */ 194 for (environ_tmp = get_environment(); *environ_tmp; ++environ_tmp) { 195 smartlist_add_strdup(sl, *environ_tmp); 196 } 197 198 return sl; 199 } 200 201 /** For each string s in <b>env_vars</b> such that 202 * environment_variable_names_equal(s, <b>new_var</b>), remove it; if 203 * <b>free_p</b> is non-zero, call <b>free_old</b>(s). If 204 * <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */ 205 void 206 set_environment_variable_in_smartlist(struct smartlist_t *env_vars, 207 const char *new_var, 208 void (*free_old)(void*), 209 int free_p) 210 { 211 SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) { 212 if (environment_variable_names_equal(s, new_var)) { 213 SMARTLIST_DEL_CURRENT(env_vars, s); 214 if (free_p) { 215 free_old((void *)s); 216 } 217 } 218 } SMARTLIST_FOREACH_END(s); 219 220 if (strchr(new_var, '=') != NULL) { 221 smartlist_add(env_vars, (void *)new_var); 222 } 223 }