Tor  0.4.7.0-alpha-dev
env.c
Go to the documentation of this file.
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"
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 **
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
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. */
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"). */
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 *
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
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 }
int tor_memeq(const void *a, const void *b, size_t sz)
Definition: di_ops.c:107
Headers for di_ops.c.
static size_t str_num_before(const char *s, char ch)
Definition: env.c:58
process_environment_t * process_environment_make(struct smartlist_t *env_vars)
Definition: env.c:101
int environment_variable_names_equal(const char *s1, const char *s2)
Definition: env.c:70
char ** get_environment(void)
Definition: env.c:42
struct smartlist_t * get_current_process_environment_variables(void)
Definition: env.c:189
void process_environment_free_(process_environment_t *env)
Definition: env.c:82
void set_environment_variable_in_smartlist(struct smartlist_t *env_vars, const char *new_var, void(*free_old)(void *), int free_p)
Definition: env.c:206
Header for env.c.
Headers for log.c.
#define LD_GENERAL
Definition: log.h:62
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:52
void smartlist_sort_strings(smartlist_t *sl)
Definition: smartlist.c:549
Header for smartlist.c.
void smartlist_add_all(smartlist_t *s1, const smartlist_t *s2)
smartlist_t * smartlist_new(void)
void smartlist_add_strdup(struct smartlist_t *sl, const char *string)
void smartlist_add(smartlist_t *sl, void *element)
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
#define SMARTLIST_DEL_CURRENT(sl, var)
char * windows_environment_block
Definition: env.h:24
char ** unixoid_environment_block
Definition: env.h:27
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:102