[tor-commits] [tor/master] Complete logging of output from port forwarding helper

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


commit 7d015c886a3fc985ab767de01811d1591f0ac86a
Author: Steven Murdoch <Steven.Murdoch at cl.cam.ac.uk>
Date:   Thu Aug 18 18:41:23 2011 +0100

    Complete logging of output from port forwarding helper
---
 src/common/util.c    |  139 +++++++++++++++++++++++++++++++++++++-------------
 src/common/util.h    |    6 ++-
 src/test/test_util.c |    4 +-
 3 files changed, 110 insertions(+), 39 deletions(-)

diff --git a/src/common/util.c b/src/common/util.c
index 5a5f3b5..3769d06 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -3070,7 +3070,7 @@ tor_spawn_background(const char *const filename, const char **argv)
     // 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;
+    process_handle.status = 1;
   }
 
   // TODO: Close pipes on exit
@@ -3248,20 +3248,36 @@ tor_spawn_background(const char *const filename, const char **argv)
        needs to know about the pid in order to reap it later */
   }
 
-  process_handle.status = 0;
+  process_handle.status = 1;
   process_handle.pid = pid;
+  /* Set stdout/stderr pipes to be non-blocking */
+  fcntl(process_handle.stdout_pipe, F_SETFL, O_NONBLOCK);
+  fcntl(process_handle.stderr_pipe, F_SETFL, O_NONBLOCK);
+  /* Open the buffered IO streams */
+  process_handle.stdout_handle = fdopen(process_handle.stdout_pipe, "r");
+  process_handle.stderr_handle = fdopen(process_handle.stderr_pipe, "r");
+
   return process_handle;
 #endif // MS_WINDOWS
 }
 
 int
-tor_get_exit_code(const process_handle_t process_handle)
+tor_get_exit_code(const process_handle_t process_handle, int block)
 {
 #ifdef MS_WINDOWS
   DWORD exit_code;
   BOOL retval;
-  WaitForSingleObject(process_handle.pid.hProcess, INFINITE);
-  retval = GetExitCodeProcess(process_handle.pid.hProcess, &exit_code);
+  if (block) {
+    exit_code = WaitForSingleObject(process_handle.pid.hProcess, INFINITE);
+    retval = GetExitCodeProcess(process_handle.pid.hProcess, &exit_code);
+  } else {
+    exit_code = WaitForSingleObject(process_handle.pid.hProcess, 0);
+    if (WAIT_TIMEOUT == exit_code) {
+      // Process has not exited
+      return -2;
+    }
+    retval = GetExitCodeProcess(process_handle.pid.hProcess, &exit_code);
+  }
 
   if (!retval) {
     log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s",
@@ -3371,6 +3387,55 @@ tor_read_all_from_process_stderr(const process_handle_t process_handle,
 #endif
 }
 
+#ifdef MS_WINDOWS
+static int
+log_from_handle(HANDLE *pipe, int severity)
+{
+  char buf[256];
+  int pos;
+  int start, cur, next;
+
+  pos = tor_read_all_handle(pipe, buf, sizeof(buf) - 1, NULL);
+  if (pos < 0) {
+    // Error
+    log_warn(LD_GENERAL, "Failed to read data from subprocess");
+    return -1;
+  }
+
+  if (0 == pos) {
+    // There's nothing to read (process is busy or has exited)
+    log_debug(LD_GENERAL, "Subprocess had nothing to say");
+    return 0;
+  }
+
+  // End with a null even if there isn't a \r\n at the end
+  // TODO: What if this is a partial line?
+  buf[pos] = '\0';
+  log_debug(LD_GENERAL, "Subprocess had %d bytes to say", pos);
+
+  next = 0; // Start of the next line
+  while (next < pos) {
+    start = next; // Look for the end of this line
+    for (cur=start; cur<pos; cur++) {
+      if ('\r' == buf[cur]) {
+        buf[cur] = '\0';
+	next = cur + 1;
+        if ((cur + 1) < pos && buf[cur+1] < pos) {
+	  buf[cur + 1] = '\0';
+          next = cur + 2;
+	}
+	// Line starts at start and ends with a null (was \r\n)
+	break;
+      }
+      // Line starts at start and ends at the end of a string
+      // but we already added a null in earlier
+    }
+    log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf+start);
+  }
+  return pos;
+}    
+
+#else
 /** Read from stream, and send lines to log at the specified log level.
  * Returns 1 if stream is closed normally, -1 if there is a error reading, and
  * 0 otherwise. Handles lines from tor-fw-helper and
@@ -3446,28 +3511,23 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
   /* We should never get here */
   return -1;
 }
+#endif
 
 void
 tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
                           time_t now)
 {
-#ifdef MS_WINDOWS
-  (void) filename; (void) dir_port; (void) or_port; (void) now;
-  (void) tor_spawn_background;
-  (void) log_from_pipe;
-  log_warn(LD_GENERAL, "Sorry, port forwarding is not yet supported "
-           "on windows.");
-#else
 /* When fw-helper succeeds, how long do we wait until running it again */
 #define TIME_TO_EXEC_FWHELPER_SUCCESS 300
 /* When fw-helper fails, how long do we wait until running it again */
 #define TIME_TO_EXEC_FWHELPER_FAIL 60
 
-  // XXX: remove
-  static int child_pid = -1;
-  static process_handle_t child_handle = {0, 0, 0, 0};
-  static FILE *stdout_read = NULL;
-  static FILE *stderr_read = NULL;
+#ifdef MS_WINDOWS
+  static process_handle_t child_handle = {0, NULL, NULL, {NULL}};
+#else
+  static process_handle_t child_handle;
+#endif
+
   static time_t time_to_run_helper = 0;
   int stdout_status, stderr_status, retval;
   const char *argv[10];
@@ -3492,37 +3552,40 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
   argv[9] = NULL;
 
   /* Start the child, if it is not already running */
-  if (-1 == child_pid &&
-      time_to_run_helper < now) {
-    int fd_out=-1, fd_err=-1;
-
+  if (child_handle.status <= 0 && time_to_run_helper < now) {
     /* Assume tor-fw-helper will succeed, start it later*/
     time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS;
 
     child_handle = tor_spawn_background(filename, argv);
-    if (child_pid < 0) {
+    if (child_handle.status < 0) {
       log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
               filename);
-      child_pid = -1;
       return;
     }
-    /* Set stdout/stderr pipes to be non-blocking */
-    fcntl(fd_out, F_SETFL, O_NONBLOCK);
-    fcntl(fd_err, F_SETFL, O_NONBLOCK);
-    /* Open the buffered IO streams */
-    stdout_read = fdopen(fd_out, "r");
-    stderr_read = fdopen(fd_err, "r");
-
+#ifdef MS_WINDOWS
+    log_info(LD_GENERAL,
+      "Started port forwarding helper (%s)", filename);
+#else
     log_info(LD_GENERAL,
       "Started port forwarding helper (%s) with pid %d", filename, child_pid);
+#endif
   }
 
   /* If child is running, read from its stdout and stderr) */
-  if (child_pid > 0) {
+  if (child_handle.status > 0) {
     /* Read from stdout/stderr and log result */
     retval = 0;
-    stdout_status = log_from_pipe(stdout_read, LOG_INFO, filename, &retval);
-    stderr_status = log_from_pipe(stderr_read, LOG_WARN, filename, &retval);
+#ifdef MS_WINDOWS
+    stdout_status = log_from_handle(child_handle.stdout_pipe, LOG_INFO);
+    stderr_status = log_from_handle(child_handle.stderr_pipe, LOG_WARN);
+    // If we got this far (on Windows), the process started
+    retval = 0;
+#else
+    stdout_status = log_from_pipe(child_handle.stdout_handle,
+		    LOG_INFO, filename, &retval);
+    stderr_status = log_from_pipe(child_handle.stderr_handle,
+		    LOG_WARN, filename, &retval);
+#endif
     if (retval) {
       /* There was a problem in the child process */
       time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
@@ -3532,9 +3595,16 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
     if (-1 == stdout_status || -1 == stderr_status)
       /* There was a failure */
       retval = -1;
+#ifdef MS_WINDOWS
+    else if (tor_get_exit_code(child_handle, 0) >= 0) {
+      /* process has exited */
+      retval = 1;
+    }
+#else
     else if (1 == stdout_status || 1 == stderr_status)
       /* stdout or stderr was closed */
       retval = 1;
+#endif
     else
       /* Both are fine */
       retval = 0;
@@ -3549,9 +3619,8 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
 
       /* TODO: The child might not actually be finished (maybe it failed or
          closed stdout/stderr), so maybe we shouldn't start another? */
-      child_pid = -1;
+      child_handle.status = -1;
     }
   }
-#endif
 }
 
diff --git a/src/common/util.h b/src/common/util.h
index 2efe557..40fe305 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -349,7 +349,7 @@ HANDLE load_windows_system_library(const TCHAR *library_name);
 /* Prototypes for private functions only used by util.c (and unit tests) */
 
 typedef struct process_handle_s {
-  int status;
+  int status; // 0: not running; 1: running; -1: error
 #ifdef MS_WINDOWS
   HANDLE stdout_pipe;
   HANDLE stderr_pipe;
@@ -357,13 +357,15 @@ typedef struct process_handle_s {
 #else
   int stdout_pipe;
   int stderr_pipe;
+  FILE *stdout_handle;
+  FILE *stderr_handle;
   int pid;
 #endif // MS_WINDOWS
 } process_handle_t;
 
 process_handle_t tor_spawn_background(const char *const filename,
                                       const char **argv);
-int tor_get_exit_code(const process_handle_t pid);
+int tor_get_exit_code(const process_handle_t pid, int block);
 ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count, HANDLE hProcess);
 ssize_t tor_read_all_from_process_stdout(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 071fd04..6f87560 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -1408,7 +1408,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
   tt_int_op(pos, ==, strlen(expected_out));
 
   /* Check it terminated correctly */
-  retval = tor_get_exit_code(process_handle);
+  retval = tor_get_exit_code(process_handle, 1);
   tt_int_op(retval, ==, expected_exit);
   // TODO: Make test-child exit with something other than 0
 
@@ -1529,7 +1529,7 @@ test_util_spawn_background_partial_read(void *ptr)
 #endif
 
   /* Check it terminated correctly */
-  retval = tor_get_exit_code(process_handle);
+  retval = tor_get_exit_code(process_handle, 1);
   tt_int_op(retval, ==, expected_exit);
   // TODO: Make test-child exit with something other than 0
 





More information about the tor-commits mailing list