|
@@ -172,6 +172,35 @@ _tor_malloc_zero(size_t size DMALLOC_PARAMS)
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill
|
|
|
|
+ * the memory with zero bytes, and return a pointer to the result.
|
|
|
|
+ * Log and terminate the process on error. (Same as
|
|
|
|
+ * calloc(<b>nmemb</b>,<b>size</b>), but never returns NULL.)
|
|
|
|
+ *
|
|
|
|
+ * XXXX This implementation probably asserts in cases where it could
|
|
|
|
+ * work, because it only tries dividing SIZE_MAX by size (according to
|
|
|
|
+ * the calloc(3) man page, the size of an element of the nmemb-element
|
|
|
|
+ * array to be allocated), not by nmemb (which could in theory be
|
|
|
|
+ * smaller than size). Don't do that then.
|
|
|
|
+ */
|
|
|
|
+void *
|
|
|
|
+_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS)
|
|
|
|
+{
|
|
|
|
+ /* You may ask yourself, "wouldn't it be smart to use calloc instead of
|
|
|
|
+ * malloc+memset? Perhaps libc's calloc knows some nifty optimization trick
|
|
|
|
+ * we don't!" Indeed it does, but its optimizations are only a big win when
|
|
|
|
+ * we're allocating something very big (it knows if it just got the memory
|
|
|
|
+ * from the OS in a pre-zeroed state). We don't want to use tor_malloc_zero
|
|
|
|
+ * for big stuff, so we don't bother with calloc. */
|
|
|
|
+ void *result;
|
|
|
|
+ size_t max_nmemb = (size == 0) ? SIZE_MAX : SIZE_MAX/size;
|
|
|
|
+
|
|
|
|
+ tor_assert(nmemb < max_nmemb);
|
|
|
|
+
|
|
|
|
+ result = _tor_malloc_zero((nmemb * size) DMALLOC_FN_ARGS);
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
+
|
|
/** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b>
|
|
/** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b>
|
|
* bytes long; return the new memory block. On error, log and
|
|
* bytes long; return the new memory block. On error, log and
|
|
* terminate. (Like realloc(ptr,size), but never returns NULL.)
|
|
* terminate. (Like realloc(ptr,size), but never returns NULL.)
|
|
@@ -3284,11 +3313,7 @@ process_handle_new(void)
|
|
*/
|
|
*/
|
|
int
|
|
int
|
|
tor_spawn_background(const char *const filename, const char **argv,
|
|
tor_spawn_background(const char *const filename, const char **argv,
|
|
-#ifdef _WIN32
|
|
|
|
- LPVOID envp,
|
|
|
|
-#else
|
|
|
|
- const char **envp,
|
|
|
|
-#endif
|
|
|
|
|
|
+ process_environment_t *env,
|
|
process_handle_t **process_handle_out)
|
|
process_handle_t **process_handle_out)
|
|
{
|
|
{
|
|
#ifdef _WIN32
|
|
#ifdef _WIN32
|
|
@@ -3305,8 +3330,6 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
char *joined_argv;
|
|
char *joined_argv;
|
|
|
|
|
|
- (void)envp; // Unused on Windows
|
|
|
|
-
|
|
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = TRUE;
|
|
saAttr.bInheritHandle = TRUE;
|
|
/* TODO: should we set explicit security attributes? (#2046, comment 5) */
|
|
/* TODO: should we set explicit security attributes? (#2046, comment 5) */
|
|
@@ -3371,7 +3394,7 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
|
|
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
|
|
* work?) */
|
|
* work?) */
|
|
0, // creation flags
|
|
0, // creation flags
|
|
- envp, // use parent's environment
|
|
|
|
|
|
+ (env==NULL) ? NULL : env->windows_environment_block,
|
|
NULL, // use parent's current directory
|
|
NULL, // use parent's current directory
|
|
&siStartInfo, // STARTUPINFO pointer
|
|
&siStartInfo, // STARTUPINFO pointer
|
|
&(process_handle->pid)); // receives PROCESS_INFORMATION
|
|
&(process_handle->pid)); // receives PROCESS_INFORMATION
|
|
@@ -3501,8 +3524,8 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|
/* Call the requested program. We need the cast because
|
|
/* Call the requested program. We need the cast because
|
|
execvp doesn't define argv as const, even though it
|
|
execvp doesn't define argv as const, even though it
|
|
does not modify the arguments */
|
|
does not modify the arguments */
|
|
- if (envp)
|
|
|
|
- execve(filename, (char *const *) argv, (char*const*)envp);
|
|
|
|
|
|
+ if (env)
|
|
|
|
+ execve(filename, (char *const *) argv, env->unixoid_environment_block);
|
|
else
|
|
else
|
|
execvp(filename, (char *const *) argv);
|
|
execvp(filename, (char *const *) argv);
|
|
|
|
|
|
@@ -3692,6 +3715,162 @@ tor_get_exit_code(const process_handle_t *process_handle,
|
|
return PROCESS_EXIT_EXITED;
|
|
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;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** Return a newly allocated smartlist containing every variable in
|
|
|
|
+ * this process's environment, as a NUL-terminated string of the form
|
|
|
|
+ * "NAME=VALUE". Note that on some/many/most/all OSes, the parent
|
|
|
|
+ * process can put strings not of that form in our environment;
|
|
|
|
+ * callers should try to not get crashed by that.
|
|
|
|
+ *
|
|
|
|
+ * The returned strings are heap-allocated, and must be freed by the
|
|
|
|
+ * caller. */
|
|
|
|
+struct smartlist_t *
|
|
|
|
+get_current_process_environment_variables(void)
|
|
|
|
+{
|
|
|
|
+ smartlist_t *sl = smartlist_new();
|
|
|
|
+
|
|
|
|
+ char **environ_tmp; /* Not const char ** ? Really? */
|
|
|
|
+ for (environ_tmp = environ; *environ_tmp; ++environ_tmp) {
|
|
|
|
+ smartlist_add(sl, tor_strdup(*environ_tmp));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return sl;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** For each string s in <b>env_vars</b> such that
|
|
|
|
+ * environment_variable_names_equal(s, <b>new_var</b>), remove it; if
|
|
|
|
+ * <b>free_p</b> is non-zero, call <b>free_old</b>(s). If
|
|
|
|
+ * <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */
|
|
|
|
+void
|
|
|
|
+set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
|
|
|
|
+ const char *new_var,
|
|
|
|
+ void (*free_old)(void*),
|
|
|
|
+ int free_p)
|
|
|
|
+{
|
|
|
|
+ SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) {
|
|
|
|
+ if (environment_variable_names_equal(s, new_var)) {
|
|
|
|
+ SMARTLIST_DEL_CURRENT(env_vars, s);
|
|
|
|
+ if (free_p) {
|
|
|
|
+ free_old((void *)s);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } SMARTLIST_FOREACH_END(s);
|
|
|
|
+
|
|
|
|
+ if (strchr(new_var, '=') != NULL) {
|
|
|
|
+ smartlist_add(env_vars, (void *)new_var);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
#ifdef _WIN32
|
|
#ifdef _WIN32
|
|
/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If
|
|
/** 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
|
|
* <b>hProcess</b> is NULL, the function will return immediately if there is
|