diff options
author | Robert Ransom <rransom.8774@gmail.com> | 2012-02-12 20:14:48 -0800 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2012-02-17 11:42:19 -0500 |
commit | 98cec14982718d71e1a7b001f8a9a73c3bf0800b (patch) | |
tree | 72c0180cb4878a6702f57155520b24defebc5d10 /src/common/util.c | |
parent | 806e0f7e19ed10f91b1a7477486991611c9732de (diff) | |
download | tor-98cec14982718d71e1a7b001f8a9a73c3bf0800b.tar.gz tor-98cec14982718d71e1a7b001f8a9a73c3bf0800b.zip |
Add process_environment_make and related utilities
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/src/common/util.c b/src/common/util.c index 9040877667..5c3a85ec23 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 |