env.c 7.1 KB

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