[tor-commits] [tor/master] Add process_environment_make and related utilities

nickm at torproject.org nickm at torproject.org
Fri Feb 17 16:46:48 UTC 2012


commit 98cec14982718d71e1a7b001f8a9a73c3bf0800b
Author: Robert Ransom <rransom.8774 at gmail.com>
Date:   Sun Feb 12 20:14:48 2012 -0800

    Add process_environment_make and related utilities
---
 src/common/util.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/common/util.h |   15 +++++++
 2 files changed, 126 insertions(+), 0 deletions(-)

diff --git a/src/common/util.c b/src/common/util.c
index 9040877..5c3a85e 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -3721,6 +3721,117 @@ tor_get_exit_code(const process_handle_t *process_handle,
   return PROCESS_EXIT_EXITED;
 }
 
+/** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b>
+ * to have the same name as strings in a process's environment. */
+int
+environment_variable_names_equal(const char *s1, const char *s2)
+{
+  size_t s1_name_len = strcspn(s1, "=");
+  size_t s2_name_len = strcspn(s2, "=");
+
+  return (s1_name_len == s2_name_len &&
+          tor_memeq(s1, s2, s1_name_len));
+}
+
+/** Free <b>env</b> (assuming it was produced by
+ * process_environment_make). */
+void
+process_environment_free(process_environment_t *env)
+{
+  if (env == NULL) return;
+
+  /* As both an optimization hack to reduce consing on Unixoid systems
+   * and a nice way to ensure that some otherwise-Windows-specific
+   * code will always get tested before changes to it get merged, the
+   * strings which env->unixoid_environment_block points to are packed
+   * into env->windows_environment_block. */
+  tor_free(env->unixoid_environment_block);
+  tor_free(env->windows_environment_block);
+
+  tor_free(env);
+}
+
+/** Make a process_environment_t containing the environment variables
+ * specified in <b>env_vars</b> (as C strings of the form
+ * "NAME=VALUE"). */
+process_environment_t *
+process_environment_make(struct smartlist_t *env_vars)
+{
+  process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t));
+  size_t n_env_vars = smartlist_len(env_vars);
+  size_t i;
+  size_t total_env_length;
+  smartlist_t *env_vars_sorted;
+
+  tor_assert(n_env_vars + 1 != 0);
+  env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *));
+  /* env->unixoid_environment_block is already NULL-terminated,
+   * because we assume that NULL == 0 (and check that during compilation). */
+
+  total_env_length = 1; /* terminating NUL of terminating empty string */
+  for (i = 0; i < n_env_vars; ++i) {
+    const char *s = smartlist_get(env_vars, i);
+    size_t slen = strlen(s);
+
+    tor_assert(slen + 1 != 0);
+    tor_assert(slen + 1 < SIZE_MAX - total_env_length);
+    total_env_length += slen + 1;
+  }
+
+  env->windows_environment_block = tor_malloc_zero(total_env_length);
+  /* env->windows_environment_block is already
+   * (NUL-terminated-empty-string)-terminated. */
+
+  /* Some versions of Windows supposedly require that environment
+   * blocks be sorted.  Or maybe some Windows programs (or their
+   * runtime libraries) fail to look up strings in non-sorted
+   * environment blocks.
+   *
+   * Also, sorting strings makes it easy to find duplicate environment
+   * variables and environment-variable strings without an '=' on all
+   * OSes, and they can cause badness.  Let's complain about those. */
+  env_vars_sorted = smartlist_new();
+  smartlist_add_all(env_vars_sorted, env_vars);
+  smartlist_sort_strings(env_vars_sorted);
+
+  /* Now copy the strings into the environment blocks. */
+  {
+    char *cp = env->windows_environment_block;
+    const char *prev_env_var = NULL;
+
+    for (i = 0; i < n_env_vars; ++i) {
+      const char *s = smartlist_get(env_vars_sorted, i);
+      size_t slen = strlen(s);
+      size_t s_name_len = strcspn(s, "=");
+
+      if (s_name_len == slen) {
+        log_warn(LD_GENERAL,
+                 "Preparing an environment containing a variable "
+                 "without a value: %s",
+                 s);
+      }
+      if (prev_env_var != NULL &&
+          environment_variable_names_equal(s, prev_env_var)) {
+        log_warn(LD_GENERAL,
+                 "Preparing an environment containing two variables "
+                 "with the same name: %s and %s",
+                 prev_env_var, s);
+      }
+
+      prev_env_var = s;
+
+      /* Actually copy the string into the environment. */
+      memcpy(cp, s, slen+1);
+      env->unixoid_environment_block[i] = cp;
+      cp += slen+1;
+    }
+
+    tor_assert(cp == env->windows_environment_block + total_env_length - 1);
+  }
+
+  return env;
+}
+
 #ifdef _WIN32
 /** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes.  If
  * <b>hProcess</b> is NULL, the function will return immediately if there is
diff --git a/src/common/util.h b/src/common/util.h
index 955a682..3989cb4 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -379,6 +379,21 @@ int tor_spawn_background(const char *const filename, const char **argv,
 HANDLE load_windows_system_library(const TCHAR *library_name);
 #endif
 
+int environment_variable_names_equal(const char *s1, const char *s2);
+
+struct process_environment_t {
+  /** A pointer to a sorted empty-string-terminated sequence of
+   * NUL-terminated strings of the form "NAME=VALUE". */
+  char *windows_environment_block;
+  /** A pointer to a NULL-terminated array of pointers to
+   * NUL-terminated strings of the form "NAME=VALUE". */
+  char **unixoid_environment_block;
+};
+typedef struct process_environment_t process_environment_t;
+
+process_environment_t *process_environment_make(struct smartlist_t *env_vars);
+void process_environment_free(process_environment_t *env);
+
 /* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be
  * 0 because tor_check_port_forwarding depends on this being the initial
  * statue of the static instance of process_handle_t */





More information about the tor-commits mailing list