Browse Source

Implement "TOR_PT_EXIT_ON_STDIN_CLOSE".

Background processes spawned by Tor now will have a valid stdin.
Pluggable transports can detect this behavior with the aformentioned
enviornment variable, and exit if stdin ever gets closed.
Yawning Angel 9 years ago
parent
commit
fda61e030e
5 changed files with 82 additions and 14 deletions
  1. 5 0
      changes/feature15435
  2. 67 14
      src/common/util.c
  3. 3 0
      src/common/util.h
  4. 5 0
      src/or/transports.c
  5. 2 0
      src/test/test_util_slow.c

+ 5 - 0
changes/feature15435

@@ -0,0 +1,5 @@
+  o Minor features (pluggable Transports):
+    - When launching managed pluggable transports, setup a valid open stdin
+      in the child process that can be used to detect if tor has terminated.
+      The "TOR_PT_EXIT_ON_STDIN_CLOSE" enviornment variable can be used by
+      implementations to detect this new behavior. Resolves ticket 15435.

+ 67 - 14
src/common/util.c

@@ -3939,9 +3939,11 @@ process_handle_new(void)
   process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t));
 
 #ifdef _WIN32
+  out->stdin_pipe = INVALID_HANDLE_VALUE;
   out->stdout_pipe = INVALID_HANDLE_VALUE;
   out->stderr_pipe = INVALID_HANDLE_VALUE;
 #else
+  out->stdin_pipe = -1;
   out->stdout_pipe = -1;
   out->stderr_pipe = -1;
 #endif
@@ -3981,7 +3983,7 @@ process_handle_waitpid_cb(int status, void *arg)
 #define CHILD_STATE_FORK 3
 #define CHILD_STATE_DUPOUT 4
 #define CHILD_STATE_DUPERR 5
-#define CHILD_STATE_REDIRECT 6
+#define CHILD_STATE_DUPIN 6
 #define CHILD_STATE_CLOSEFD 7
 #define CHILD_STATE_EXEC 8
 #define CHILD_STATE_FAILEXEC 9
@@ -4015,6 +4017,8 @@ tor_spawn_background(const char *const filename, const char **argv,
   HANDLE stdout_pipe_write = NULL;
   HANDLE stderr_pipe_read = NULL;
   HANDLE stderr_pipe_write = NULL;
+  HANDLE stdin_pipe_read = NULL;
+  HANDLE stdin_pipe_write = NULL;
   process_handle_t *process_handle;
   int status;
 
@@ -4060,6 +4064,20 @@ tor_spawn_background(const char *const filename, const char **argv,
     return status;
   }
 
+  /* Set up pipe for stdin */
+  if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, &saAttr, 0)) {
+    log_warn(LD_GENERAL,
+      "Failed to create pipe for stdin communication with child process: %s",
+      format_win32_error(GetLastError()));
+    return status;
+  }
+  if (!SetHandleInformation(stderr_pipe_write, HANDLE_FLAG_INHERIT, 0)) {
+    log_warn(LD_GENERAL,
+      "Failed to configure pipe for stdin communication with child "
+      "process: %s", format_win32_error(GetLastError()));
+    return status;
+  }
+
   /* Create the child process */
 
   /* Windows expects argv to be a whitespace delimited string, so join argv up
@@ -4074,7 +4092,7 @@ tor_spawn_background(const char *const filename, const char **argv,
   siStartInfo.cb = sizeof(STARTUPINFO);
   siStartInfo.hStdError = stderr_pipe_write;
   siStartInfo.hStdOutput = stdout_pipe_write;
-  siStartInfo.hStdInput = NULL;
+  siStartInfo.hStdInput = stdin_pipe_read;
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
 
   /* Create the child process */
@@ -4104,6 +4122,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->stdin_pipe = stdin_pipe_write;
     status = process_handle->status = PROCESS_STATUS_RUNNING;
   }
 
@@ -4114,6 +4133,7 @@ tor_spawn_background(const char *const filename, const char **argv,
   pid_t pid;
   int stdout_pipe[2];
   int stderr_pipe[2];
+  int stdin_pipe[2];
   int fd, retval;
   ssize_t nbytes;
   process_handle_t *process_handle;
@@ -4138,7 +4158,7 @@ tor_spawn_background(const char *const filename, const char **argv,
 
   child_state = CHILD_STATE_PIPE;
 
-  /* Set up pipe for redirecting stdout and stderr of child */
+  /* Set up pipe for redirecting stdout, stderr, and stdin of child */
   retval = pipe(stdout_pipe);
   if (-1 == retval) {
     log_warn(LD_GENERAL,
@@ -4159,6 +4179,20 @@ tor_spawn_background(const char *const filename, const char **argv,
     return status;
   }
 
+  retval = pipe(stdin_pipe);
+  if (-1 == retval) {
+    log_warn(LD_GENERAL,
+      "Failed to set up pipe for stdin communication with child process: %s",
+       strerror(errno));
+
+    close(stdout_pipe[0]);
+    close(stdout_pipe[1]);
+    close(stderr_pipe[0]);
+    close(stderr_pipe[1]);
+
+    return status;
+  }
+
   child_state = CHILD_STATE_MAXFD;
 
 #ifdef _SC_OPEN_MAX
@@ -4194,13 +4228,11 @@ tor_spawn_background(const char *const filename, const char **argv,
     if (-1 == retval)
         goto error;
 
-    child_state = CHILD_STATE_REDIRECT;
+    child_state = CHILD_STATE_DUPIN;
 
-    /* Link stdin to /dev/null */
-    fd = open("/dev/null", O_RDONLY); /* NOT cloexec, obviously. */
-    if (fd != -1)
-      dup2(fd, STDIN_FILENO);
-    else
+    /* Link child stdin to the read end of the pipe */
+    retval = dup2(stdin_pipe[0], STDIN_FILENO);
+    if (-1 == retval)
       goto error;
 
     child_state = CHILD_STATE_CLOSEFD;
@@ -4209,7 +4241,8 @@ tor_spawn_background(const char *const filename, const char **argv,
     close(stderr_pipe[1]);
     close(stdout_pipe[0]);
     close(stdout_pipe[1]);
-    close(fd);
+    close(stdin_pipe[0]);
+    close(stdin_pipe[1]);
 
     /* Close all other fds, including the read end of the pipe */
     /* XXX: We should now be doing enough FD_CLOEXEC setting to make
@@ -4259,6 +4292,8 @@ tor_spawn_background(const char *const filename, const char **argv,
 
   if (-1 == pid) {
     log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
+    close(stdin_pipe[0]);
+    close(stdin_pipe[1]);
     close(stdout_pipe[0]);
     close(stdout_pipe[1]);
     close(stderr_pipe[0]);
@@ -4295,16 +4330,28 @@ tor_spawn_background(const char *const filename, const char **argv,
             strerror(errno));
   }
 
+  /* Return write end of the stdin pipe to caller, and close the read end */
+  process_handle->stdin_pipe = stdin_pipe[1];
+  retval = close(stdin_pipe[0]);
+
+  if (-1 == retval) {
+    log_warn(LD_GENERAL,
+            "Failed to close read end of stdin pipe in parent process: %s",
+            strerror(errno));
+  }
+
   status = process_handle->status = PROCESS_STATUS_RUNNING;
-  /* Set stdout/stderr pipes to be non-blocking */
+  /* Set stdin/stdout/stderr pipes to be non-blocking */
   if (fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK) < 0 ||
-      fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0) {
-    log_warn(LD_GENERAL, "Failed to set stderror/stdout pipes nonblocking "
-             "in parent process: %s", strerror(errno));
+      fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0 ||
+      fcntl(process_handle->stdin_pipe, F_SETFL, O_NONBLOCK) < 0) {
+    log_warn(LD_GENERAL, "Failed to set stderror/stdout/stdin pipes "
+             "nonblocking in parent process: %s", strerror(errno));
   }
   /* 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");
+  process_handle->stdin_handle = fdopen(process_handle->stdin_pipe, "r");
 
   *process_handle_out = process_handle;
   return process_handle->status;
@@ -4347,6 +4394,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
 
   if (process_handle->stderr_pipe)
     CloseHandle(process_handle->stderr_pipe);
+
+  if (process_handle->stdin_pipe)
+    CloseHandle(process_handle->stdin_pipe);
 #else
   if (process_handle->stdout_handle)
     fclose(process_handle->stdout_handle);
@@ -4354,6 +4404,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
   if (process_handle->stderr_handle)
     fclose(process_handle->stderr_handle);
 
+  if (process_handle->stdin_handle)
+    fclose(process_handle->stdin_handle);
+
   clear_waitpid_callback(process_handle->waitpid_cb);
 #endif
 

+ 3 - 0
src/common/util.h

@@ -474,12 +474,15 @@ struct process_handle_t {
   /** One of the PROCESS_STATUS_* values */
   int status;
 #ifdef _WIN32
+  HANDLE stdin_pipe;
   HANDLE stdout_pipe;
   HANDLE stderr_pipe;
   PROCESS_INFORMATION pid;
 #else
+  int stdin_pipe;
   int stdout_pipe;
   int stderr_pipe;
+  FILE *stdin_handle;
   FILE *stdout_handle;
   FILE *stderr_handle;
   pid_t pid;

+ 5 - 0
src/or/transports.c

@@ -1388,6 +1388,11 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
     } else {
       smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
     }
+
+    /* All new versions of tor will keep stdin open, so PTs can use it
+     * as a reliable termination detection mechanism.
+     */
+    smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1");
   } else {
     /* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the
      * TOR_PT_PROXY line.

+ 2 - 0
src/test/test_util_slow.c

@@ -107,9 +107,11 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
 #ifdef _WIN32
   tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
   tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
+  tt_assert(process_handle->stdin_pipe != INVALID_HANDLE_VALUE);
 #else
   tt_assert(process_handle->stdout_pipe >= 0);
   tt_assert(process_handle->stderr_pipe >= 0);
+  tt_assert(process_handle->stdin_pipe >= 0);
 #endif
 
   /* Check stdout */