[tor-commits] [tor/master] Add Windows version of tor_spawn_background and ancillary functions

nickm at torproject.org nickm at torproject.org
Tue Aug 30 19:58:37 UTC 2011


commit fec902dd6024dd170d151409387df5319b6e2ad0
Author: Steven Murdoch <Steven.Murdoch at cl.cam.ac.uk>
Date:   Thu Jul 21 19:26:19 2011 +0100

    Add Windows version of tor_spawn_background and ancillary functions
---
 src/common/util.c    |  141 ++++++++++++++++++++++++++++++++++++++++++++++----
 src/common/util.h    |    4 +-
 src/test/test_util.c |   14 ++++-
 3 files changed, 143 insertions(+), 16 deletions(-)

diff --git a/src/common/util.c b/src/common/util.c
index 5bc7a80..3d39f59 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -2976,18 +2976,107 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
  * and stderr, respectively, output of the child program can be read, and the
  * stdin of the child process shall be set to /dev/null.  Otherwise returns
  * -1.  Some parts of this code are based on the POSIX subprocess module from
- * Python.
+ * Python, and example code from
+ * http://msdn.microsoft.com/en-us/library/ms682499%28v=vs.85%29.aspx.
  */
 process_handle_t
 tor_spawn_background(const char *const filename, const char **argv)
 {
   process_handle_t process_handle;
 #ifdef MS_WINDOWS
-  (void) filename; (void) argv;
-  log_warn(LD_BUG, "not yet implemented on Windows.");
+  HANDLE stdout_pipe_read = NULL;
+  HANDLE stdout_pipe_write = NULL;
+  HANDLE stderr_pipe_read = NULL;
+  HANDLE stderr_pipe_write = NULL;
+
+  SECURITY_ATTRIBUTES saAttr;
+  smartlist_t *argv_list;
+  char *joined_argv;
+  int i;
+ 
+  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
+  saAttr.bInheritHandle = TRUE; 
+  saAttr.lpSecurityDescriptor = NULL; 
+
   process_handle.status = -1;
+
+  /* Set up pipe for stdout */
+  if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) {
+    log_warn(LD_GENERAL,
+      "Failed to create pipe for stdout communication with child process: %s",
+      format_win32_error(GetLastError()));
+    return process_handle;
+  }
+  if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
+    log_warn(LD_GENERAL,
+      "Failed to configure pipe for stdout communication with child process: %s",
+      format_win32_error(GetLastError()));
+  }
+
+  /* Set up pipe for stderr */
+  if (!CreatePipe(&stderr_pipe_read, &stderr_pipe_write, &saAttr, 0)) {
+    log_warn(LD_GENERAL,
+      "Failed to create pipe for stderr communication with child process: %s",
+      format_win32_error(GetLastError()));
+    return process_handle;
+  }
+  if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
+    log_warn(LD_GENERAL,
+      "Failed to configure pipe for stderr communication with child process: %s",
+      format_win32_error(GetLastError()));
+  }
+
+  /* Create the child process */
+
+  /* Windows expects argv to be a whitespace delimited string, so join argv up */
+  argv_list = smartlist_create();
+  for (i=0; argv[i] != NULL; i++) {
+    smartlist_add(argv_list, (void *)argv[i]);
+  }
+
+  joined_argv = smartlist_join_strings(argv_list, " ", 0, NULL);
+
+  STARTUPINFO siStartInfo;
+  BOOL retval = FALSE; 
+ 
+  ZeroMemory(&process_handle.pid, sizeof(PROCESS_INFORMATION));
+  ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+  siStartInfo.cb = sizeof(STARTUPINFO); 
+  siStartInfo.hStdError = stderr_pipe_write;
+  siStartInfo.hStdOutput = stdout_pipe_write;
+  siStartInfo.hStdInput = NULL;
+  siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+ 
+  /* Create the child process */
+
+  retval = CreateProcess(filename,       // module name
+                         joined_argv,    // command line 
+                         NULL,          // process security attributes 
+                         NULL,          // primary thread security attributes 
+                         TRUE,          // handles are inherited 
+                         0,             // creation flags (TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() work?)
+                         NULL,          // use parent's environment 
+                         NULL,          // use parent's current directory 
+                         &siStartInfo,  // STARTUPINFO pointer 
+                         &process_handle.pid);  // receives PROCESS_INFORMATION
+
+  tor_free(joined_argv); 
+   
+  if (!retval) {
+    log_warn(LD_GENERAL,
+      "Failed to create child process %s: %s", filename,
+      format_win32_error(GetLastError()));
+  } else  {
+    // TODO: Close hProcess and hThread in process_handle.pid?
+    process_handle.stdout_pipe = stdout_pipe_read;
+    process_handle.stderr_pipe = stderr_pipe_read;
+    process_handle.status = 0;
+  }
+
+  // TODO: Close pipes on exit
+
   return process_handle;
-#else
+#else // MS_WINDOWS
   pid_t pid;
   int stdout_pipe[2];
   int stderr_pipe[2];
@@ -3154,15 +3243,25 @@ tor_spawn_background(const char *const filename, const char **argv)
   process_handle.status = 0;
   process_handle.pid = pid;
   return process_handle;
-#endif
+#endif // MS_WINDOWS
 }
 
 int
 tor_get_exit_code(const process_handle_t process_handle)
 {
 #ifdef MS_WINDOWS
-  log_warn(LD_BUG, "not yet implemented on Windows.");
-  return -1;
+  DWORD exit_code;
+  BOOL retval;
+  WaitForSingleObject(process_handle.pid.hProcess, INFINITE);
+  retval = GetExitCodeProcess(process_handle.pid.hProcess, &exit_code);
+
+  if (!retval) {
+    log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s",
+             format_win32_error(GetLastError()));
+    return -1;
+  } else {
+    return exit_code;
+  }
 #else
   int stat_loc;
 
@@ -3183,13 +3282,23 @@ tor_get_exit_code(const process_handle_t process_handle)
 }
 
 ssize_t
-tor_read_all_from_process_stdin(const process_handle_t process_handle,
+tor_read_all_from_process_stdout(const process_handle_t process_handle,
                                 char *buf, size_t count)
 {
 #ifdef MS_WINDOWS
-  return -1;
+  BOOL retval;
+  DWORD bytes_read;
+  retval = ReadFile(process_handle.stdout_pipe, buf, count, &bytes_read, NULL);
+  if (!retval) {
+    log_warn(LD_GENERAL,
+      "Failed to read from stdin pipe: %s",
+      format_win32_error(GetLastError()));
+    return -1;
+  } else {
+    return bytes_read;
+  }
 #else
-  return read_all(process_handle.stdin_pipe, buf, count, 0);
+  return read_all(process_handle.stdout_pipe, buf, count, 0);
 #endif
 }
   
@@ -3198,7 +3307,17 @@ tor_read_all_from_process_stderr(const process_handle_t process_handle,
                                  char *buf, size_t count)
 {
 #ifdef MS_WINDOWS
-  return -1;
+  BOOL retval;
+  DWORD bytes_read;
+  retval = ReadFile(process_handle.stderr_pipe, buf, count, &bytes_read, NULL);
+  if (!retval) {
+    log_warn(LD_GENERAL,
+      "Failed to read from stderr pipe: %s",
+      format_win32_error(GetLastError()));
+    return -1;
+  } else {
+    return bytes_read;
+  }
 #else
   return read_all(process_handle.stderr_pipe, buf, count, 0);
 #endif
diff --git a/src/common/util.h b/src/common/util.h
index 2cf57a1..c111ba7 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -353,7 +353,7 @@ typedef struct process_handle_s {
 #ifdef MS_WINDOWS
   HANDLE stdout_pipe;
   HANDLE stderr_pipe;
-  HANDLE pid;
+  PROCESS_INFORMATION pid;
 #else
   int stdout_pipe;
   int stderr_pipe;
@@ -364,7 +364,7 @@ typedef struct process_handle_s {
 process_handle_t tor_spawn_background(const char *const filename,
                                       const char **argv);
 int tor_get_exit_code(const process_handle_t pid);
-ssize_t tor_read_all_from_process_stdin(const process_handle_t process_handle,
+ssize_t tor_read_all_from_process_stdout(const process_handle_t process_handle,
                                         char *buf, size_t count);
 ssize_t tor_read_all_from_process_stderr(const process_handle_t process_handle,
                                          char *buf, size_t count);
diff --git a/src/test/test_util.c b/src/test/test_util.c
index fce53b8..28030b7 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -1394,24 +1394,25 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
   tt_int_op(process_handle.stderr_pipe, >, 0);
 
   /* Check stdout */
-  pos = tor_read_all_from_process_stdin(process_handle, stdout_buf,
+  pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
 		                        sizeof(stdout_buf) - 1);
   tt_assert(pos >= 0);
   stdout_buf[pos] = '\0';
-  tt_int_op(pos, ==, strlen(expected_out));
   tt_str_op(stdout_buf, ==, expected_out);
+  tt_int_op(pos, ==, strlen(expected_out));
 
   /* Check it terminated correctly */
   retval = tor_get_exit_code(process_handle);
   tt_int_op(retval, ==, expected_exit);
+  // TODO: Make test-child exit with something other than 0
 
   /* Check stderr */
   pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
 		                         sizeof(stderr_buf) - 1);
   tt_assert(pos >= 0);
   stderr_buf[pos] = '\0';
-  tt_int_op(pos, ==, strlen(expected_err));
   tt_str_op(stderr_buf, ==, expected_err);
+  tt_int_op(pos, ==, strlen(expected_err));
 
  done:
   ;
@@ -1421,9 +1422,16 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
 static void
 test_util_spawn_background_ok(void *ptr)
 {
+#ifdef MS_WINDOWS
+  // TODO: Under MSYS, BUILDDIR in orconfig.h needs to be tweaked
+  const char *argv[] = {BUILDDIR "/src/test/test-child.exe", "--test", NULL};
+  const char *expected_out = "OUT\r\n--test\r\nDONE\r\n";
+  const char *expected_err = "ERR\r\n";
+#else
   const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
   const char *expected_out = "OUT\n--test\nDONE\n";
   const char *expected_err = "ERR\n";
+#endif
 
   (void)ptr;
 





More information about the tor-commits mailing list