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 */
8 * \brief Inspect and manipulate the environment variables.
12 #include "lib/process/env.h"
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"
25 #ifdef HAVE_CRT_EXTERNS_H
26 /* For _NSGetEnviron on macOS */
27 #include <crt_externs.h>
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
;
36 #endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */
37 #endif /* !defined(HAVE__NSGETENVIRON) */
39 /** Return the current environment. This is a portable replacement for
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) */
51 #endif /* defined(HAVE__NSGETENVIRON) */
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"). */
58 str_num_before(const char *s
, char ch
)
60 const char *cp
= strchr(s
, ch
);
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. */
70 environment_variable_names_equal(const char *s1
, const char *s2
)
72 size_t s1_name_len
= str_num_before(s1
, '=');
73 size_t s2_name_len
= str_num_before(s2
, '=');
75 return (s1_name_len
== s2_name_len
&&
76 tor_memeq(s1
, s2
, s1_name_len
));
79 /** Free <b>env</b> (assuming it was produced by
80 * process_environment_make). */
82 process_environment_free_(process_environment_t
*env
)
84 if (env
== NULL
) return;
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
);
97 /** Make a process_environment_t containing the environment variables
98 * specified in <b>env_vars</b> (as C strings of the form
100 process_environment_t
*
101 process_environment_make(struct smartlist_t
*env_vars
)
103 process_environment_t
*env
= tor_malloc_zero(sizeof(process_environment_t
));
104 int n_env_vars
= smartlist_len(env_vars
);
106 size_t total_env_length
;
107 smartlist_t
*env_vars_sorted
;
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). */
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
);
119 tor_assert(slen
+ 1 != 0);
120 tor_assert(slen
+ 1 < SIZE_MAX
- total_env_length
);
121 total_env_length
+= slen
+ 1;
124 env
->windows_environment_block
= tor_malloc_zero(total_env_length
);
125 /* env->windows_environment_block is already
126 * (NUL-terminated-empty-string)-terminated. */
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.
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
);
140 /* Now copy the strings into the environment blocks. */
142 char *cp
= env
->windows_environment_block
;
143 const char *prev_env_var
= NULL
;
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
, '=');
150 if (s_name_len
== slen
) {
152 "Preparing an environment containing a variable "
153 "without a value: %s",
156 if (prev_env_var
!= NULL
&&
157 environment_variable_names_equal(s
, prev_env_var
)) {
159 "Preparing an environment containing two variables "
160 "with the same name: %s and %s",
166 /* Actually copy the string into the environment. */
167 memcpy(cp
, s
, slen
+1);
168 env
->unixoid_environment_block
[i
] = cp
;
172 tor_assert(cp
== env
->windows_environment_block
+ total_env_length
- 1);
175 smartlist_free(env_vars_sorted
);
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.
186 * The returned strings are heap-allocated, and must be freed by the
189 get_current_process_environment_variables(void)
191 smartlist_t
*sl
= smartlist_new();
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
);
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>. */
206 set_environment_variable_in_smartlist(struct smartlist_t
*env_vars
,
208 void (*free_old
)(void*),
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
);
218 } SMARTLIST_FOREACH_END(s
);
220 if (strchr(new_var
, '=') != NULL
) {
221 smartlist_add(env_vars
, (void *)new_var
);