|
@@ -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
|