expand GNUNET_OS_ProjectData API to also enable de-duplcation of logic for --help
[oweals/gnunet.git] / src / util / os_priority.c
index b344cd86235eab02db3e0a8ad34e7f95fa35fce8..52e2f6d0e88d7106c19d03787c246ab554904dcc 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet
 /*
      This file is part of GNUnet
-     (C) 2002, 2003, 2004, 2005, 2006, 2011 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2002, 2003, 2004, 2005, 2006, 2011 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -14,8 +14,8 @@
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
 */
 
 /**
  */
 
 #include "platform.h"
  */
 
 #include "platform.h"
-#include "gnunet_common.h"
-#include "gnunet_os_lib.h"
-#include "gnunet_scheduler_lib.h"
-#include "gnunet_strings_lib.h"
+#include "gnunet_util_lib.h"
 #include "disk.h"
 #include "disk.h"
+#include <unistr.h>
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
 
 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
 
 
 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
 
-#define DEBUG_OS GNUNET_EXTRA_LOGGING
 
 struct GNUNET_OS_Process
 {
 
 struct GNUNET_OS_Process
 {
+  /**
+   * PID of the process.
+   */
   pid_t pid;
   pid_t pid;
+
 #if WINDOWS
 #if WINDOWS
+  /**
+   * Process handle.
+   */
   HANDLE handle;
 #endif
   HANDLE handle;
 #endif
-  int sig;
+
+  /**
+   * Pipe we use to signal the process.
+   * NULL if unused, or if process was deemed uncontrollable.
+   */
   struct GNUNET_DISK_FileHandle *control_pipe;
 };
 
   struct GNUNET_DISK_FileHandle *control_pipe;
 };
 
+
+/**
+ * Handle for 'this' process.
+ */
 static struct GNUNET_OS_Process current_process;
 
 
 static struct GNUNET_OS_Process current_process;
 
 
@@ -64,44 +76,49 @@ static void
 parent_control_handler (void *cls,
                         const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 parent_control_handler (void *cls,
                         const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct GNUNET_DISK_FileHandle *control_pipe =
-      (struct GNUNET_DISK_FileHandle *) cls;
-  int sig;
+  struct GNUNET_DISK_FileHandle *control_pipe = cls;
+  char sig;
+  char *pipe_fd;
+  ssize_t ret;
 
 
-#if DEBUG_OS
   LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
        tc->reason);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
        tc->reason);
-#endif
-  if (tc->reason &
-      (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT |
-       GNUNET_SCHEDULER_REASON_PREREQ_DONE))
+  if (0 != (tc->reason &
+           (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
   {
   {
-    GNUNET_DISK_npipe_close (control_pipe);
+    GNUNET_DISK_file_close (control_pipe);
+    control_pipe = NULL;
+    return;
   }
   }
-  else
+  ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
+  if (sizeof (sig) != ret)
   {
   {
-    if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) !=
-        sizeof (sig))
-    {
+    if (-1 == ret)
       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
-      GNUNET_DISK_npipe_close (control_pipe);
-    }
-    else
-    {
-#if DEBUG_OS
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
-#endif
-      GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                      control_pipe, &parent_control_handler,
-                                      control_pipe);
-      raise (sig);
-    }
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing control pipe\n");
+    GNUNET_DISK_file_close (control_pipe);
+    control_pipe = NULL;
+    return;
   }
   }
+  pipe_fd = getenv (GNUNET_OS_CONTROL_PIPE);
+  GNUNET_assert ( (NULL == pipe_fd) || (strlen (pipe_fd) <= 0) );
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Got control code %d from parent via pipe %s\n", sig, pipe_fd);
+  GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                 control_pipe, &parent_control_handler,
+                                 control_pipe);
+  GNUNET_SIGNAL_raise ((int) sig);
 }
 
 
 /**
 }
 
 
 /**
- * Task that connects this process to its parent via pipe
+ * Task that connects this process to its parent via pipe;
+ * essentially, the parent control handler will read signal numbers
+ * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
+ * variable) and raise those signals.
+ *
+ * @param cls closure (unused)
+ * @param tc scheduler context (unused)
  */
 void
 GNUNET_OS_install_parent_control_handler (void *cls,
  */
 void
 GNUNET_OS_install_parent_control_handler (void *cls,
@@ -109,33 +126,55 @@ GNUNET_OS_install_parent_control_handler (void *cls,
                                           GNUNET_SCHEDULER_TaskContext *tc)
 {
   const char *env_buf;
                                           GNUNET_SCHEDULER_TaskContext *tc)
 {
   const char *env_buf;
+  char *env_buf_end;
   struct GNUNET_DISK_FileHandle *control_pipe;
   struct GNUNET_DISK_FileHandle *control_pipe;
+  uint64_t pipe_fd;
 
   env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
 
   env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
-  if ((env_buf == NULL) || (strlen (env_buf) <= 0))
+  if ( (NULL == env_buf) || (strlen (env_buf) <= 0) )
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Not installing a handler because $%s is empty\n",
+         GNUNET_OS_CONTROL_PIPE);
+    putenv (GNUNET_OS_CONTROL_PIPE "=");
+    return;
+  }
+  errno = 0;
+  pipe_fd = strtoull (env_buf, &env_buf_end, 16);
+  if ((0 != errno) || (env_buf == env_buf_end))
   {
   {
-    LOG (GNUNET_ERROR_TYPE_INFO, _("Not installing a handler because $%s=%s\n"),
-         GNUNET_OS_CONTROL_PIPE, env_buf);
-    putenv ("GNUNET_OS_CONTROL_PIPE=");
+    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "strtoull", env_buf);
+    putenv (GNUNET_OS_CONTROL_PIPE "=");
     return;
   }
     return;
   }
-  control_pipe =
-      GNUNET_DISK_npipe_open (env_buf, GNUNET_DISK_OPEN_READ,
-                              GNUNET_DISK_PERM_USER_READ |
-                              GNUNET_DISK_PERM_USER_WRITE);
-  if (control_pipe == NULL)
+#if !defined (WINDOWS)
+  if (pipe_fd >= FD_SETSIZE)
+#else
+  if ((FILE_TYPE_UNKNOWN == GetFileType ((HANDLE) (uintptr_t) pipe_fd))
+      && (0 != GetLastError ()))
+#endif
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "GNUNET_OS_CONTROL_PIPE `%s' contains garbage?\n", env_buf);
+    putenv (GNUNET_OS_CONTROL_PIPE "=");
+    return;
+  }
+#if WINDOWS
+  control_pipe = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) (uintptr_t) pipe_fd);
+#else
+  control_pipe = GNUNET_DISK_get_handle_from_int_fd ((int) pipe_fd);
+#endif
+  if (NULL == control_pipe)
   {
     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
   {
     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
-    putenv ("GNUNET_OS_CONTROL_PIPE=");
+    putenv (GNUNET_OS_CONTROL_PIPE "=");
     return;
   }
     return;
   }
-#if DEBUG_OS
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
-#endif
   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
                                   &parent_control_handler, control_pipe);
   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
                                   &parent_control_handler, control_pipe);
-  putenv ("GNUNET_OS_CONTROL_PIPE=");
+  putenv (GNUNET_OS_CONTROL_PIPE "=");
 }
 
 
 }
 
 
@@ -160,104 +199,90 @@ GNUNET_OS_process_current ()
 }
 
 
 }
 
 
+/**
+ * Sends a signal to the process
+ *
+ * @param proc pointer to process structure
+ * @param sig signal
+ * @return 0 on success, -1 on error
+ */
 int
 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
 {
 int
 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
 {
-#if ENABLE_WINDOWS_WORKAROUNDS
-  int res = 0;
-  int ret = 0;
+  int ret;
+  char csig;
 
 
-  ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof (sig));
-  if (ret != sizeof (sig))
+  csig = (char) sig;
+  if (NULL != proc->control_pipe)
   {
   {
-    if (errno == ECOMM)
-    {
-      /* Child process is not controllable via pipe */
-#if DEBUG_OS
-      LOG (GNUNET_ERROR_TYPE_DEBUG,
-           "Child process is not controllable, will kill it directly\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending signal %d to pid: %u via pipe\n", sig, proc->pid);
+    ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
+    if (sizeof (csig) == ret)
+      return 0;
+  }
+  /* pipe failed or non-existent, try other methods */
+  switch (sig)
+  {
+#if !defined (WINDOWS)
+  case SIGHUP:
 #endif
 #endif
-    }
-    else if (errno == EPIPE)
-    {
-#if DEBUG_OS
-      LOG (GNUNET_ERROR_TYPE_DEBUG,
-           "Failed to write into control pipe, because pipe is invalid (the child is most likely dead)\n");
+  case SIGINT:
+  case SIGKILL:
+  case SIGTERM:
+#if (SIGTERM != GNUNET_TERM_SIG)
+  case GNUNET_TERM_SIG:
 #endif
 #endif
+#if defined(WINDOWS) && !defined(__CYGWIN__)
+    {
+      DWORD exitcode;
+      int must_kill = GNUNET_YES;
+      if (0 != GetExitCodeProcess (proc->handle, &exitcode))
+        must_kill = (exitcode == STILL_ACTIVE) ? GNUNET_YES : GNUNET_NO;
+      if (GNUNET_YES == must_kill)
+        if (0 == SafeTerminateProcess (proc->handle, 0, 0))
+        {
+          DWORD error_code = GetLastError ();
+          if ((error_code != WAIT_TIMEOUT) && (error_code != ERROR_PROCESS_ABORTED))
+          {
+            LOG ((error_code == ERROR_ACCESS_DENIED) ?
+                GNUNET_ERROR_TYPE_INFO : GNUNET_ERROR_TYPE_WARNING,
+                "SafeTermiateProcess failed with code %lu\n", error_code);
+            /* The problem here is that a process that is already dying
+             * might cause SafeTerminateProcess to fail with
+             * ERROR_ACCESS_DENIED, but the process WILL die eventually.
+             * If we really had a permissions problem, hanging up (which
+             * is what will happen in process_wait() in that case) is
+             * a valid option.
+             */
+            if (ERROR_ACCESS_DENIED == error_code)
+            {
+              errno = 0;
+            }
+            else
+            {
+              SetErrnoFromWinError (error_code);
+              return -1;
+            }
+          }
+        }
     }
     }
-    else
-      LOG (GNUNET_ERROR_TYPE_WARNING,
-           "Failed to write into control pipe , errno is %d\n", errno);
-#if WINDOWS && !defined(__CYGWIN__)
-    TerminateProcess (proc->handle, 0);
+    return 0;
 #else
 #else
-    PLIBC_KILL (proc->pid, sig);
-#endif
-  }
-  else
-  {
-#if DEBUG_OS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Wrote control code into control pipe, now waiting\n");
+         "Sending signal %d to pid: %u via system call\n",
+         sig,
+         proc->pid);
+    return PLIBC_KILL (proc->pid, sig);
 #endif
 #endif
-
-#if WINDOWS
-    /* Give it 3 seconds to die, then kill it in a nice Windows-specific way */
-    if (WaitForSingleObject (proc->handle, 3000) != WAIT_OBJECT_0)
-      TerminateProcess (proc->handle, 0);
-    res = 0;
+  default:
+#if defined (WINDOWS)
+    errno = EINVAL;
+    return -1;
 #else
 #else
-    struct GNUNET_NETWORK_FDSet *rfds;
-    struct GNUNET_NETWORK_FDSet *efds;
-
-    rfds = GNUNET_NETWORK_fdset_create ();
-    efds = GNUNET_NETWORK_fdset_create ();
-
-    GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe);
-    GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe);
-
-    /* Ndurner thought this up, and i have no idea what it does.
-     * There's have never been any code to answer the shutdown call
-     * (write a single int into the pipe, so that this function can read it).
-     * On *nix select() will probably tell that pipe is ready
-     * for reading, once the other process shuts down,
-     * but the read () call will fail, triggering a kill ()
-     * on the pid that is already dead. This will probably result in non-0
-     * return from kill(), and therefore from this function.
-     */
-    while (1)
-    {
-      ret =
-          GNUNET_NETWORK_socket_select (rfds, NULL, efds,
-                                        GNUNET_TIME_relative_multiply
-                                        (GNUNET_TIME_relative_get_unit (),
-                                         5000));
-
-      if (ret < 1 ||
-          GNUNET_NETWORK_fdset_handle_isset (efds, proc->control_pipe))
-      {
-        /* Just to be sure */
-        PLIBC_KILL (proc->pid, sig);
-        res = 0;
-        break;
-      }
-      else
-      {
-        if (GNUNET_DISK_file_read (proc->control_pipe, &ret, sizeof (ret)) !=
-            GNUNET_OK)
-          res = PLIBC_KILL (proc->pid, sig);
-
-        /* Child signaled shutdown is in progress */
-        continue;
-      }
-    }
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending signal %d to pid: %u via system call\n", sig, proc->pid);
+    return PLIBC_KILL (proc->pid, sig);
 #endif
   }
 #endif
   }
-
-  return res;
-#else
-  return kill (proc->pid, sig);
-#endif
 }
 
 /**
 }
 
 /**
@@ -274,22 +299,23 @@ GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
 }
 
 
 }
 
 
+/**
+ * Cleans up process structure contents (OS-dependent) and deallocates it
+ *
+ * @param proc pointer to process structure
+ */
 void
 void
-GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
+GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
 {
 {
-#if ENABLE_WINDOWS_WORKAROUNDS
-  if (proc->control_pipe)
-    GNUNET_DISK_npipe_close (proc->control_pipe);
-#endif
-// FIXME NILS
-#ifdef WINDOWS
-  if (proc->handle != NULL)
+  if (NULL != proc->control_pipe)
+    GNUNET_DISK_file_close (proc->control_pipe);
+#if defined (WINDOWS)
+  if (NULL != proc->handle)
     CloseHandle (proc->handle);
 #endif
   GNUNET_free (proc);
 }
 
     CloseHandle (proc->handle);
 #endif
   GNUNET_free (proc);
 }
 
-// FIXME NILS
 #if WINDOWS
 #include "gnunet_signal_lib.h"
 
 #if WINDOWS
 #include "gnunet_signal_lib.h"
 
@@ -305,7 +331,7 @@ extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
  * @param proc pointer to process structure
  */
 static DWORD_WINAPI
  * @param proc pointer to process structure
  */
 static DWORD_WINAPI
-ChildWaitThread (void *arg)
+child_wait_thread (void *arg)
 {
   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
 
 {
   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
 
@@ -318,121 +344,20 @@ ChildWaitThread (void *arg)
 }
 #endif
 
 }
 #endif
 
-/**
- * Set process priority
- *
- * @param proc pointer to process structure
- * @param prio priority value
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
- */
-int
-GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
-                                enum GNUNET_SCHEDULER_Priority prio)
-{
-  int rprio;
-
-  GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
-  if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
-    return GNUNET_OK;
-
-  /* convert to MINGW/Unix values */
-  switch (prio)
-  {
-  case GNUNET_SCHEDULER_PRIORITY_UI:
-  case GNUNET_SCHEDULER_PRIORITY_URGENT:
-#ifdef MINGW
-    rprio = HIGH_PRIORITY_CLASS;
-#else
-    rprio = 0;
-#endif
-    break;
-
-  case GNUNET_SCHEDULER_PRIORITY_HIGH:
-#ifdef MINGW
-    rprio = ABOVE_NORMAL_PRIORITY_CLASS;
-#else
-    rprio = 5;
-#endif
-    break;
-
-  case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
-#ifdef MINGW
-    rprio = NORMAL_PRIORITY_CLASS;
-#else
-    rprio = 7;
-#endif
-    break;
-
-  case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
-#ifdef MINGW
-    rprio = BELOW_NORMAL_PRIORITY_CLASS;
-#else
-    rprio = 10;
-#endif
-    break;
-
-  case GNUNET_SCHEDULER_PRIORITY_IDLE:
-#ifdef MINGW
-    rprio = IDLE_PRIORITY_CLASS;
-#else
-    rprio = 19;
-#endif
-    break;
-  default:
-    GNUNET_assert (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* Set process priority */
-#ifdef MINGW
-  {
-    HANDLE h = proc->handle;
-
-    GNUNET_assert (h != NULL);
-    SetPriorityClass (h, rprio);
-  }
-#elif LINUX
-  pid_t pid;
-
-  pid = proc->pid;
-  if ((0 == pid) || (pid == getpid ()))
-  {
-    int have = nice (0);
-    int delta = rprio - have;
-
-    errno = 0;
-    if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
-    {
-      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
-      return GNUNET_SYSERR;
-    }
-  }
-  else
-  {
-    if (0 != setpriority (PRIO_PROCESS, pid, rprio))
-    {
-      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
-                    "setpriority");
-      return GNUNET_SYSERR;
-    }
-  }
-#else
-#if DEBUG_OS
-  LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-       "Priority management not availabe for this platform\n");
-#endif
-#endif
-  return GNUNET_OK;
-}
 
 #if MINGW
 static char *
 CreateCustomEnvTable (char **vars)
 {
 
 #if MINGW
 static char *
 CreateCustomEnvTable (char **vars)
 {
-  char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
+  char *win32_env_table;
+  char *ptr;
+  char **var_ptr;
+  char *result;
+  char *result_ptr;
   size_t tablesize = 0;
   size_t items_count = 0;
   size_t tablesize = 0;
   size_t items_count = 0;
-  size_t n_found = 0, n_var;
+  size_t n_found = 0;
+  size_t n_var;
   char *index = NULL;
   size_t c;
   size_t var_len;
   char *index = NULL;
   size_t c;
   size_t var_len;
@@ -440,7 +365,7 @@ CreateCustomEnvTable (char **vars)
   char *val;
 
   win32_env_table = GetEnvironmentStringsA ();
   char *val;
 
   win32_env_table = GetEnvironmentStringsA ();
-  if (win32_env_table == NULL)
+  if (NULL == win32_env_table)
     return NULL;
   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
   n_var = c;
     return NULL;
   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
   n_var = c;
@@ -525,371 +450,75 @@ CreateCustomEnvTable (char **vars)
   *result_ptr = 0;
   return result;
 }
   *result_ptr = 0;
   return result;
 }
-#endif
 
 
+#else
 
 /**
 
 /**
- * Start a process.
+ * Open '/dev/null' and make the result the given
+ * file descriptor.
  *
  *
- * @param pipe_stdin pipe to use to send input to child process (or NULL)
- * @param pipe_stdout pipe to use to get output from child process (or NULL)
- * @param filename name of the binary
- * @param argv NULL-terminated array of arguments to the process
- * @return pointer to process structure of the new process, NULL on error
+ * @param target_fd desired FD to point to /dev/null
+ * @param flags open flags (O_RDONLY, O_WRONLY)
  */
  */
-struct GNUNET_OS_Process *
-GNUNET_OS_start_process_vap (struct GNUNET_DISK_PipeHandle *pipe_stdin,
-                            struct GNUNET_DISK_PipeHandle *pipe_stdout,
-                            const char *filename, 
-                            char *const argv[])
+static void
+open_dev_null (int target_fd,
+              int flags)
 {
 {
-#if ENABLE_WINDOWS_WORKAROUNDS
-  char *childpipename = NULL;
-  struct GNUNET_DISK_FileHandle *control_pipe = NULL;
-#endif
-  struct GNUNET_OS_Process *gnunet_proc = NULL;
-
-#ifndef MINGW
-  pid_t ret;
-  int fd_stdout_write;
-  int fd_stdout_read;
-  int fd_stdin_read;
-  int fd_stdin_write;
-
-#if ENABLE_WINDOWS_WORKAROUNDS
-  control_pipe =
-      GNUNET_DISK_npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
-                                GNUNET_DISK_PERM_USER_READ |
-                                GNUNET_DISK_PERM_USER_WRITE);
-  if (control_pipe == NULL)
-    return NULL;
-#endif
-  if (pipe_stdout != NULL)
-  {
-    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
-                                       (pipe_stdout,
-                                        GNUNET_DISK_PIPE_END_WRITE),
-                                       &fd_stdout_write, sizeof (int));
-    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
-                                       (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
-                                       &fd_stdout_read, sizeof (int));
-  }
-  if (pipe_stdin != NULL)
-  {
-    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
-                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
-                                       &fd_stdin_read, sizeof (int));
-    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
-                                       (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
-                                       &fd_stdin_write, sizeof (int));
-  }
-
-  ret = fork ();
-  if (ret != 0)
-  {
-    if (ret == -1)
-    {
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
-#if ENABLE_WINDOWS_WORKAROUNDS
-      GNUNET_DISK_npipe_close (control_pipe);
-#endif
-    }
-    else
-    {
-      gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
-      gnunet_proc->pid = ret;
-#if ENABLE_WINDOWS_WORKAROUNDS
-      gnunet_proc->control_pipe = control_pipe;
-#endif
-    }
-#if ENABLE_WINDOWS_WORKAROUNDS
-    GNUNET_free (childpipename);
-#endif
-    return gnunet_proc;
-  }
-
-#if ENABLE_WINDOWS_WORKAROUNDS
-  setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
-  GNUNET_free (childpipename);
-#endif
-
-  if (pipe_stdout != NULL)
-  {
-    GNUNET_break (0 == close (fd_stdout_read));
-    if (-1 == dup2 (fd_stdout_write, 1))
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
-    GNUNET_break (0 == close (fd_stdout_write));
-  }
-
-  if (pipe_stdin != NULL)
-  {
-
-    GNUNET_break (0 == close (fd_stdin_write));
-    if (-1 == dup2 (fd_stdin_read, 0))
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
-    GNUNET_break (0 == close (fd_stdin_read));
-  }
-  execvp (filename, argv);
-  LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
-  _exit (1);
-#else
-  char *arg;
-  unsigned int cmdlen;
-  char *cmd, *idx;
-  STARTUPINFOW start;
-  PROCESS_INFORMATION proc;
-  int argc;
-  HANDLE stdin_handle;
-  HANDLE stdout_handle;
-
-  char path[MAX_PATH + 1];
-
-  char *our_env[3] = { NULL, NULL, NULL };
-  char *env_block = NULL;
-  char *pathbuf;
-  DWORD pathbuf_len, alloc_len;
-  char *self_prefix;
-  char *bindir;
-  char *libdir;
-  char *ptr;
-  char *non_const_filename;
-  wchar_t wpath[MAX_PATH + 1], wcmd[32768];
-
-  /* Search in prefix dir (hopefully - the directory from which
-   * the current module was loaded), bindir and libdir, then in PATH
-   */
-  self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
-  bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
-  libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
-
-  pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
-
-  alloc_len =
-      pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
-      strlen (libdir);
+  int fd;
 
 
-  pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
-
-  ptr = pathbuf;
-  ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
-  GNUNET_free (self_prefix);
-  GNUNET_free (bindir);
-  GNUNET_free (libdir);
-
-  alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
-  GNUNET_assert (alloc_len == (pathbuf_len - 1));
-
-  cmdlen = strlen (filename);
-  if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
-    GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
-  else
-    GNUNET_asprintf (&non_const_filename, "%s", filename);
-
-  /* Check that this is the full path. If it isn't, search. */
-  if (non_const_filename[1] == ':')
-    snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
-  else if (!SearchPathA
-           (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
-            path, NULL))
-  {
-    SetErrnoFromWinError (GetLastError ());
-    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
-                       non_const_filename);
-    GNUNET_free (non_const_filename);
-    GNUNET_free (pathbuf);
-    return NULL;
-  }
-  GNUNET_free (pathbuf);
-  GNUNET_free (non_const_filename);
-
-  cmdlen = 0;
-  argc = 0;
-  while (NULL != (arg = argv[argc++]))
-  {
-    if (cmdlen == 0)
-      cmdlen = cmdlen + strlen (path) + 3;
-    else
-      cmdlen = cmdlen + strlen (arg) + 3;
-  }
-
-  cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
-  argc = 0;
-  while (NULL != (arg = argv[argc++]))
-  {
-    if (idx == cmd)
-      idx += sprintf (idx, "\"%s\" ", path);
-    else
-      idx += sprintf (idx, "\"%s\" ", arg);
-  }
-
-  memset (&start, 0, sizeof (start));
-  start.cb = sizeof (start);
-
-  if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
-    start.dwFlags |= STARTF_USESTDHANDLES;
-
-  if (pipe_stdin != NULL)
-  {
-    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
-                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
-                                       &stdin_handle, sizeof (HANDLE));
-    start.hStdInput = stdin_handle;
-  }
-
-  if (pipe_stdout != NULL)
-  {
-    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
-                                       (pipe_stdout,
-                                        GNUNET_DISK_PIPE_END_WRITE),
-                                       &stdout_handle, sizeof (HANDLE));
-    start.hStdOutput = stdout_handle;
-  }
-
-  control_pipe =
-      GNUNET_DISK_npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
-                                GNUNET_DISK_PERM_USER_READ |
-                                GNUNET_DISK_PERM_USER_WRITE);
-  if (control_pipe == NULL)
+  fd = open ("/dev/null", flags);
+  if (-1 == fd)
   {
   {
-    GNUNET_free (cmd);
-    GNUNET_free (path);
-    return NULL;
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null");
+    return;
   }
   }
-
-#if DEBUG_OS
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
-       childpipename);
-#endif
-
-  GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
-  GNUNET_asprintf (&our_env[1], "%s", childpipename);
-  our_env[2] = NULL;
-  env_block = CreateCustomEnvTable (our_env);
-  GNUNET_free (our_env[0]);
-  GNUNET_free (our_env[1]);
-
-  if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
-      || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
-      || !CreateProcessW
-      (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
-       env_block, NULL, &start, &proc))
+  if (fd == target_fd)
+    return;
+  if (-1 == dup2 (fd, target_fd))
   {
   {
-    SetErrnoFromWinError (GetLastError ());
-    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
-    GNUNET_free (env_block);
-    GNUNET_free (cmd);
-    return NULL;
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
+    (void) close (fd);
+    return;
   }
   }
-
-  GNUNET_free (env_block);
-
-  gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
-  gnunet_proc->pid = proc.dwProcessId;
-  gnunet_proc->handle = proc.hProcess;
-  gnunet_proc->control_pipe = control_pipe;
-
-  CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
-
-  ResumeThread (proc.hThread);
-  CloseHandle (proc.hThread);
-
-  GNUNET_free (cmd);
-
-  return gnunet_proc;
-#endif
+  GNUNET_break (0 == close (fd));
 }
 }
+#endif
 
 
 /**
  * Start a process.
  *
 
 
 /**
  * Start a process.
  *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
+ *        std handles of the parent are inherited by the child.
+ *        pipe_stdin and pipe_stdout take priority over std_inheritance
+ *        (when they are non-NULL).
  * @param pipe_stdin pipe to use to send input to child process (or NULL)
  * @param pipe_stdout pipe to use to get output from child process (or NULL)
  * @param pipe_stdin pipe to use to send input to child process (or NULL)
  * @param pipe_stdout pipe to use to get output from child process (or NULL)
- * @param filename name of the binary
- * @param va NULL-terminated list of arguments to the process
- * @return pointer to process structure of the new process, NULL on error
- */
-struct GNUNET_OS_Process *
-GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin,
-                            struct GNUNET_DISK_PipeHandle *pipe_stdout,
-                            const char *filename, va_list va)
-{
-  struct GNUNET_OS_Process *ret;
-  va_list ap;
-  char **argv;
-  int argc;
-
-  argc = 0;
-  va_copy (ap, va);
-  while (NULL != va_arg (ap, char *))
-    argc++;
-  va_end (ap);
-  argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
-  argc = 0;
-  va_copy (ap, va);
-  while (NULL != (argv[argc] = va_arg (ap, char *)))
-    argc++;
-  va_end (ap);
-  ret = GNUNET_OS_start_process_vap (pipe_stdin,
-                                    pipe_stdout,
-                                    filename,
-                                    argv);
-  GNUNET_free (argv);
-  return ret;
-}
-
-
-
-/**
- * Start a process.
- *
- * @param pipe_stdin pipe to use to send input to child process (or NULL)
- * @param pipe_stdout pipe to use to get output from child process (or NULL)
- * @param filename name of the binary
- * @param ... NULL-terminated list of arguments to the process
- *
- * @return pointer to process structure of the new process, NULL on error
- *
- */
-struct GNUNET_OS_Process *
-GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
-                         struct GNUNET_DISK_PipeHandle *pipe_stdout,
-                         const char *filename, ...)
-{
-  struct GNUNET_OS_Process *ret;
-  va_list ap;
-
-  va_start (ap, filename);
-  ret = GNUNET_OS_start_process_va (pipe_stdin, pipe_stdout, filename, ap);
-  va_end (ap);
-  return ret;
-}
-
-
-/**
- * Start a process.
- *
+ * @param pipe_stderr pipe to use for stderr for child process (or NULL)
  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
  *         must be NULL on platforms where dup is not supported
  * @param filename name of the binary
  * @param argv NULL-terminated list of arguments to the process
  * @return process ID of the new process, -1 on error
  */
  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
  *         must be NULL on platforms where dup is not supported
  * @param filename name of the binary
  * @param argv NULL-terminated list of arguments to the process
  * @return process ID of the new process, -1 on error
  */
-struct GNUNET_OS_Process *
-GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
-                           const char *filename,
-                           char *const argv[])
+static struct GNUNET_OS_Process *
+start_process (int pipe_control,
+               enum GNUNET_OS_InheritStdioFlags std_inheritance,
+              struct GNUNET_DISK_PipeHandle *pipe_stdin,
+              struct GNUNET_DISK_PipeHandle *pipe_stdout,
+              struct GNUNET_DISK_PipeHandle *pipe_stderr,
+              const SOCKTYPE *lsocks,
+              const char *filename,
+              char *const argv[])
 {
 {
-#if ENABLE_WINDOWS_WORKAROUNDS
-  struct GNUNET_DISK_FileHandle *control_pipe = NULL;
-  char *childpipename = NULL;
-#endif
-
 #ifndef MINGW
   pid_t ret;
 #ifndef MINGW
   pid_t ret;
-  char lpid[16];
   char fds[16];
   char fds[16];
-  struct GNUNET_OS_Process *gnunet_proc = NULL;
+  struct GNUNET_OS_Process *gnunet_proc;
+  struct GNUNET_DISK_FileHandle *childpipe_read;
+  struct GNUNET_DISK_FileHandle *childpipe_write;
+  int childpipe_read_fd;
   int i;
   int j;
   int k;
   int i;
   int j;
   int k;
@@ -897,61 +526,174 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
   int flags;
   int *lscp;
   unsigned int ls;
   int flags;
   int *lscp;
   unsigned int ls;
+  int fd_stdout_write;
+  int fd_stdout_read;
+  int fd_stderr_write;
+  int fd_stderr_read;
+  int fd_stdin_read;
+  int fd_stdin_write;
 
 
-#if ENABLE_WINDOWS_WORKAROUNDS
-  control_pipe =
-      GNUNET_DISK_npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
-                                GNUNET_DISK_PERM_USER_READ |
-                                GNUNET_DISK_PERM_USER_WRITE);
-  if (control_pipe == NULL)
-    return NULL;
-#endif
+  if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL))
+    return NULL; /* not executable */
+  if (GNUNET_YES == pipe_control)
+  {
+    struct GNUNET_DISK_PipeHandle *childpipe;
+    int dup_childpipe_read_fd = -1;
 
 
+    childpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
+    if (NULL == childpipe)
+      return NULL;
+    childpipe_read = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ);
+    childpipe_write = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE);
+    GNUNET_DISK_pipe_close (childpipe);
+    if ((NULL == childpipe_read) || (NULL == childpipe_write) ||
+        (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read,
+        &childpipe_read_fd, sizeof (int))) ||
+        (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd))))
+    {
+      if (NULL != childpipe_read)
+        GNUNET_DISK_file_close (childpipe_read);
+      if (NULL != childpipe_write)
+        GNUNET_DISK_file_close (childpipe_write);
+      if (0 <= dup_childpipe_read_fd)
+        close (dup_childpipe_read_fd);
+      return NULL;
+    }
+    childpipe_read_fd = dup_childpipe_read_fd;
+    GNUNET_DISK_file_close (childpipe_read);
+  }
+  else
+  {
+    childpipe_write = NULL;
+    childpipe_read_fd = -1;
+  }
+  if (NULL != pipe_stdin)
+  {
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                                     (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
+                                                     &fd_stdin_read, sizeof (int)));
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                                     (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
+                                                     &fd_stdin_write, sizeof (int)));
+  }
+  if (NULL != pipe_stdout)
+  {
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                                     (pipe_stdout,
+                                                      GNUNET_DISK_PIPE_END_WRITE),
+                                                     &fd_stdout_write, sizeof (int)));
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                                     (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
+                                                     &fd_stdout_read, sizeof (int)));
+  }
+  if (NULL != pipe_stderr)
+  {
+    GNUNET_assert (GNUNET_OK ==
+                   GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                                      (pipe_stderr,
+                                                       GNUNET_DISK_PIPE_END_READ),
+                                                      &fd_stderr_read, sizeof (int)));
+    GNUNET_assert (GNUNET_OK ==
+                   GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                                      (pipe_stderr,
+                                                       GNUNET_DISK_PIPE_END_WRITE),
+                                                      &fd_stderr_write, sizeof (int)));
+  }
   lscp = NULL;
   ls = 0;
   lscp = NULL;
   ls = 0;
-  if (lsocks != NULL)
+  if (NULL != lsocks)
   {
     i = 0;
     while (-1 != (k = lsocks[i++]))
       GNUNET_array_append (lscp, ls, k);
     GNUNET_array_append (lscp, ls, -1);
   }
   {
     i = 0;
     while (-1 != (k = lsocks[i++]))
       GNUNET_array_append (lscp, ls, k);
     GNUNET_array_append (lscp, ls, -1);
   }
+#if DARWIN
+  /* see https://gnunet.org/vfork */
+  ret = vfork ();
+#else
   ret = fork ();
   ret = fork ();
-  if (ret != 0)
-  {
-    if (ret == -1)
-    {
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
-#if ENABLE_WINDOWS_WORKAROUNDS
-      GNUNET_DISK_npipe_close (control_pipe);
 #endif
 #endif
-    }
-    else
+  if (-1 == ret)
+  {
+    int eno = errno;
+    LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
+    GNUNET_array_grow (lscp, ls, 0);
+    if (NULL != childpipe_write)
+      GNUNET_DISK_file_close (childpipe_write);
+    if (0 <= childpipe_read_fd)
+      close (childpipe_read_fd);
+    errno = eno;
+    return NULL;
+  }
+  if (0 != ret)
+  {
+    unsetenv (GNUNET_OS_CONTROL_PIPE);
+    gnunet_proc = GNUNET_new (struct GNUNET_OS_Process);
+    gnunet_proc->pid = ret;
+    gnunet_proc->control_pipe = childpipe_write;
+    if (GNUNET_YES == pipe_control)
     {
     {
-      gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
-      gnunet_proc->pid = ret;
-#if ENABLE_WINDOWS_WORKAROUNDS
-      gnunet_proc->control_pipe = control_pipe;
-
-#endif
+      close (childpipe_read_fd);
     }
     GNUNET_array_grow (lscp, ls, 0);
     }
     GNUNET_array_grow (lscp, ls, 0);
-#if ENABLE_WINDOWS_WORKAROUNDS
-    GNUNET_free (childpipename);
-#endif
     return gnunet_proc;
   }
     return gnunet_proc;
   }
-
-#if ENABLE_WINDOWS_WORKAROUNDS
-  setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
-  GNUNET_free (childpipename);
+  if (0 <= childpipe_read_fd)
+  {
+    char fdbuf[100];
+#ifndef DARWIN
+    /* due to vfork, we must NOT free memory on DARWIN! */
+    GNUNET_DISK_file_close (childpipe_write);
 #endif
 #endif
-
-  if (lscp != NULL)
+    snprintf (fdbuf, 100, "%x", childpipe_read_fd);
+    setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1);
+  }
+  else
+    unsetenv (GNUNET_OS_CONTROL_PIPE);
+  if (NULL != pipe_stdin)
+  {
+    GNUNET_break (0 == close (fd_stdin_write));
+    if (-1 == dup2 (fd_stdin_read, 0))
+      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+    GNUNET_break (0 == close (fd_stdin_read));
+  }
+  else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
+  {
+    GNUNET_break (0 == close (0));
+    open_dev_null (0, O_RDONLY);
+  }
+  if (NULL != pipe_stdout)
+  {
+    GNUNET_break (0 == close (fd_stdout_read));
+    if (-1 == dup2 (fd_stdout_write, 1))
+      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+    GNUNET_break (0 == close (fd_stdout_write));
+  }
+  else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
+  {
+    GNUNET_break (0 == close (1));
+    open_dev_null (1, O_WRONLY);
+  }
+  if (NULL != pipe_stderr)
+  {
+    GNUNET_break (0 == close (fd_stderr_read));
+    if (-1 == dup2 (fd_stderr_write, 2))
+      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+    GNUNET_break (0 == close (fd_stderr_write));
+  }
+  else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
+  {
+    GNUNET_break (0 == close (2));
+    open_dev_null (2, O_WRONLY);
+  }
+  if (NULL != lscp)
   {
     /* read systemd documentation... */
   {
     /* read systemd documentation... */
-    GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
-    setenv ("LISTEN_PID", lpid, 1);
     i = 0;
     tgt = 3;
     while (-1 != lscp[i])
     i = 0;
     tgt = 3;
     while (-1 != lscp[i])
@@ -989,37 +731,59 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
     setenv ("LISTEN_FDS", fds, 1);
   }
     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
     setenv ("LISTEN_FDS", fds, 1);
   }
+#ifndef DARWIN
+  /* due to vfork, we must NOT free memory on DARWIN! */
   GNUNET_array_grow (lscp, ls, 0);
   GNUNET_array_grow (lscp, ls, 0);
+#endif
   execvp (filename, argv);
   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
   _exit (1);
 #else
   execvp (filename, argv);
   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
   _exit (1);
 #else
-  char **arg, **non_const_argv;
+  struct GNUNET_DISK_FileHandle *childpipe_read;
+  struct GNUNET_DISK_FileHandle *childpipe_write;
+  HANDLE childpipe_read_handle;
+  char **arg;
+  char **non_const_argv;
   unsigned int cmdlen;
   unsigned int cmdlen;
-  char *cmd, *idx;
+  char *cmd;
+  char *idx;
   STARTUPINFOW start;
   PROCESS_INFORMATION proc;
   int argcount = 0;
   STARTUPINFOW start;
   PROCESS_INFORMATION proc;
   int argcount = 0;
-  struct GNUNET_OS_Process *gnunet_proc = NULL;
-
+  struct GNUNET_OS_Process *gnunet_proc;
   char path[MAX_PATH + 1];
   char path[MAX_PATH + 1];
-
-  char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
+  char *our_env[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
   char *env_block = NULL;
   char *pathbuf;
   char *env_block = NULL;
   char *pathbuf;
-  DWORD pathbuf_len, alloc_len;
+  DWORD pathbuf_len;
+  DWORD alloc_len;
   char *self_prefix;
   char *bindir;
   char *libdir;
   char *ptr;
   char *non_const_filename;
   char *self_prefix;
   char *bindir;
   char *libdir;
   char *ptr;
   char *non_const_filename;
+  char win_path[MAX_PATH + 1];
   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
   HANDLE lsocks_read;
   HANDLE lsocks_write;
   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
   HANDLE lsocks_read;
   HANDLE lsocks_write;
-  wchar_t wpath[MAX_PATH + 1], wcmd[32768];
-
+  wchar_t *wpath;
+  wchar_t *wcmd;
+  size_t wpath_len;
+  size_t wcmd_len;
+  int env_off;
   int fail;
   int fail;
+  long lRet;
+  HANDLE stdin_handle;
+  HANDLE stdout_handle;
+  HANDLE stdih, stdoh, stdeh;
+  DWORD stdif, stdof, stdef;
+  BOOL bresult;
+  DWORD error_code;
+  DWORD create_no_window;
+
+  if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL))
+    return NULL; /* not executable */
 
   /* Search in prefix dir (hopefully - the directory from which
    * the current module was loaded), bindir and libdir, then in PATH
 
   /* Search in prefix dir (hopefully - the directory from which
    * the current module was loaded), bindir and libdir, then in PATH
@@ -1051,14 +815,33 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
   }
 
   cmdlen = strlen (filename);
   }
 
   cmdlen = strlen (filename);
-  if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
+  if ( (cmdlen < 5) || (0 != strcmp (&filename[cmdlen - 4], ".exe")) )
     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
   else
     GNUNET_asprintf (&non_const_filename, "%s", filename);
 
     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
   else
     GNUNET_asprintf (&non_const_filename, "%s", filename);
 
-  /* Check that this is the full path. If it isn't, search. */
+  /* It could be in POSIX form, convert it to a DOS path early on */
+  if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
+  {
+    SetErrnoFromWinError (lRet);
+    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
+                       non_const_filename);
+    GNUNET_free (non_const_filename);
+    GNUNET_free (pathbuf);
+    return NULL;
+  }
+  GNUNET_free (non_const_filename);
+  non_const_filename = GNUNET_strdup (win_path);
+   /* Check that this is the full path. If it isn't, search. */
+  /* FIXME: convert it to wchar_t and use SearchPathW?
+   * Remember: arguments to _start_process() are technically in UTF-8...
+   */
   if (non_const_filename[1] == ':')
   if (non_const_filename[1] == ':')
+  {
     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Using path `%s' as-is. PATH is %s\n", path, ptr);
+  }
   else if (!SearchPathA
            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
             path, NULL))
   else if (!SearchPathA
            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
             path, NULL))
@@ -1070,6 +853,9 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
     GNUNET_free (pathbuf);
     return NULL;
   }
     GNUNET_free (pathbuf);
     return NULL;
   }
+  else
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Found `%s' in PATH `%s'\n", path, pathbuf);
   GNUNET_free (pathbuf);
   GNUNET_free (non_const_filename);
 
   GNUNET_free (pathbuf);
   GNUNET_free (non_const_filename);
 
@@ -1103,7 +889,7 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
   arg = non_const_argv;
   while (*arg)
   {
   arg = non_const_argv;
   while (*arg)
   {
-    cmdlen = cmdlen + strlen (*arg) + 3;
+    cmdlen = cmdlen + strlen (*arg) + 4;
     arg++;
   }
 
     arg++;
   }
 
@@ -1112,7 +898,9 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
   arg = non_const_argv;
   while (*arg)
   {
   arg = non_const_argv;
   while (*arg)
   {
-    idx += sprintf (idx, "\"%s\" ", *arg);
+    char arg_last_char = (*arg)[strlen (*arg) - 1];
+    idx += sprintf (idx, "\"%s%s\"%s", *arg,
+        arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
     arg++;
   }
 
     arg++;
   }
 
@@ -1122,17 +910,97 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
 
   memset (&start, 0, sizeof (start));
   start.cb = sizeof (start);
 
   memset (&start, 0, sizeof (start));
   start.cb = sizeof (start);
+  if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
+    start.dwFlags |= STARTF_USESTDHANDLES;
 
 
-  control_pipe =
-      GNUNET_DISK_npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
-                                GNUNET_DISK_PERM_USER_READ |
-                                GNUNET_DISK_PERM_USER_WRITE);
-  if (control_pipe == NULL)
+  stdih = GetStdHandle (STD_INPUT_HANDLE);
+  GetHandleInformation (stdih, &stdif);
+  if (pipe_stdin != NULL)
   {
   {
-    GNUNET_free (cmd);
-    GNUNET_free (path);
-    return NULL;
+    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
+                                       &stdin_handle, sizeof (HANDLE));
+    start.hStdInput = stdin_handle;
+  }
+  else if (stdih)
+  {
+    if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
+    {
+      SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
+      if (pipe_stdin == NULL)
+        start.hStdInput = stdih;
+    }
+    else
+      SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
+  }
+
+
+  stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
+  GetHandleInformation (stdoh, &stdof);
+  if (NULL != pipe_stdout)
+  {
+    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+                                       (pipe_stdout,
+                                        GNUNET_DISK_PIPE_END_WRITE),
+                                       &stdout_handle, sizeof (HANDLE));
+    start.hStdOutput = stdout_handle;
+  }
+  else if (stdoh)
+  {
+    if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
+    {
+      SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
+      if (pipe_stdout == NULL)
+        start.hStdOutput = stdoh;
+    }
+    else
+      SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
+  }
+
+  stdeh = GetStdHandle (STD_ERROR_HANDLE);
+  GetHandleInformation (stdeh, &stdef);
+  if (stdeh)
+  {
+    if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
+    {
+      SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
+      start.hStdError = stdeh;
+    }
+    else
+      SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
+  }
+
+  if (GNUNET_YES == pipe_control)
+  {
+    struct GNUNET_DISK_PipeHandle *childpipe;
+    childpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
+    if (NULL == childpipe)
+      return NULL;
+    childpipe_read = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ);
+    childpipe_write = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE);
+    GNUNET_DISK_pipe_close (childpipe);
+    if ((NULL == childpipe_read) || (NULL == childpipe_write) ||
+        (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read,
+        &childpipe_read_handle, sizeof (HANDLE))))
+    {
+      if (childpipe_read)
+        GNUNET_DISK_file_close (childpipe_read);
+      if (childpipe_write)
+        GNUNET_DISK_file_close (childpipe_write);
+      GNUNET_free (cmd);
+      return NULL;
+    }
+    /* Unlike *nix variant, we don't dup the handle, so can't close
+     * filehandle right now.
+     */
+    SetHandleInformation (childpipe_read_handle, HANDLE_FLAG_INHERIT, 1);
+  }
+  else
+  {
+    childpipe_read = NULL;
+    childpipe_write = NULL;
   }
   }
+
   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
   {
     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
   {
     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
@@ -1140,8 +1008,12 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
     if (lsocks_pipe == NULL)
     {
       GNUNET_free (cmd);
     if (lsocks_pipe == NULL)
     {
       GNUNET_free (cmd);
-      GNUNET_free (path);
       GNUNET_DISK_pipe_close (lsocks_pipe);
       GNUNET_DISK_pipe_close (lsocks_pipe);
+      if (GNUNET_YES == pipe_control)
+      {
+        GNUNET_DISK_file_close (childpipe_write);
+        GNUNET_DISK_file_close (childpipe_read);
+      }
       return NULL;
     }
     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
       return NULL;
     }
     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
@@ -1152,60 +1024,125 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
                                        &lsocks_read, sizeof (HANDLE));
   }
                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
                                        &lsocks_read, sizeof (HANDLE));
   }
-
-#if DEBUG_OS
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
-       childpipename);
-#endif
-
-  GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
-  GNUNET_asprintf (&our_env[1], "%s", childpipename);
-  GNUNET_free (childpipename);
-  if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
-    our_env[2] = NULL;
   else
   else
+    lsocks_pipe = NULL;
+
+  env_off = 0;
+  if (GNUNET_YES == pipe_control)
+  {
+    GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
+    GNUNET_asprintf (&our_env[env_off++], "%p", childpipe_read_handle);
+  }
+  if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
   {
     /*This will tell the child that we're going to send lsocks over the pipe*/
   {
     /*This will tell the child that we're going to send lsocks over the pipe*/
-    GNUNET_asprintf (&our_env[2], "%s=", "GNUNET_OS_READ_LSOCKS");
-    GNUNET_asprintf (&our_env[3], "%lu", lsocks_read);
-    our_env[4] = NULL;
+    GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
+    GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
   }
   }
+  our_env[env_off++] = NULL;
   env_block = CreateCustomEnvTable (our_env);
   env_block = CreateCustomEnvTable (our_env);
-  GNUNET_free_non_null (our_env[0]);
-  GNUNET_free_non_null (our_env[1]);
-  GNUNET_free_non_null (our_env[2]);
-  GNUNET_free_non_null (our_env[3]);
+  while (0 > env_off)
+    GNUNET_free_non_null (our_env[--env_off]);
 
 
-  if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
-      || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
-      || !CreateProcessW
-      (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
-       env_block, NULL, &start, &proc))
+  wpath_len = 0;
+  if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
   {
   {
-    SetErrnoFromWinError (GetLastError ());
-    LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
-    GNUNET_DISK_npipe_close (control_pipe);
-    if (lsocks != NULL)
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
+    GNUNET_free (env_block);
+    GNUNET_free (cmd);
+    if (lsocks_pipe)
       GNUNET_DISK_pipe_close (lsocks_pipe);
       GNUNET_DISK_pipe_close (lsocks_pipe);
+    if (GNUNET_YES == pipe_control)
+    {
+      GNUNET_DISK_file_close (childpipe_write);
+      GNUNET_DISK_file_close (childpipe_read);
+    }
+    return NULL;
+  }
+
+  wcmd_len = 0;
+  if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n",
+         cmd,
+         errno);
     GNUNET_free (env_block);
     GNUNET_free (cmd);
     GNUNET_free (env_block);
     GNUNET_free (cmd);
+    free (wpath);
+    if (lsocks_pipe)
+      GNUNET_DISK_pipe_close (lsocks_pipe);
+    if (GNUNET_YES == pipe_control)
+    {
+      GNUNET_DISK_file_close (childpipe_write);
+      GNUNET_DISK_file_close (childpipe_read);
+    }
     return NULL;
   }
 
     return NULL;
   }
 
+  create_no_window = 0;
+  {
+    HANDLE console_input = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+    if (INVALID_HANDLE_VALUE == console_input)
+      create_no_window = CREATE_NO_WINDOW;
+    else
+      CloseHandle (console_input);
+  }
+
+  bresult = CreateProcessW (wpath, wcmd, NULL, NULL, GNUNET_YES,
+       create_no_window | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
+  error_code = GetLastError ();
+
+  if ((NULL == pipe_stdin) && (stdih))
+    SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
+
+
+  if ((NULL == pipe_stdout) && (stdoh))
+    SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
+
+  if (stdeh)
+    SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
+
+  if (!bresult)
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "CreateProcess(%s, %s) failed: %lu\n",
+         path,
+         cmd,
+         error_code);
+
   GNUNET_free (env_block);
   GNUNET_free (env_block);
+  GNUNET_free (cmd);
+  free (wpath);
+  free (wcmd);
+  if (GNUNET_YES == pipe_control)
+  {
+    GNUNET_DISK_file_close (childpipe_read);
+  }
+
+  if (!bresult)
+  {
+    if (GNUNET_YES == pipe_control)
+    {
+      GNUNET_DISK_file_close (childpipe_write);
+    }
+    if (NULL != lsocks)
+      GNUNET_DISK_pipe_close (lsocks_pipe);
+    SetErrnoFromWinError (error_code);
+    return NULL;
+  }
 
 
-  gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
+  gnunet_proc = GNUNET_new (struct GNUNET_OS_Process);
   gnunet_proc->pid = proc.dwProcessId;
   gnunet_proc->handle = proc.hProcess;
   gnunet_proc->pid = proc.dwProcessId;
   gnunet_proc->handle = proc.hProcess;
-  gnunet_proc->control_pipe = control_pipe;
+  gnunet_proc->control_pipe = childpipe_write;
 
 
-  CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
+  CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
 
   ResumeThread (proc.hThread);
   CloseHandle (proc.hThread);
 
   ResumeThread (proc.hThread);
   CloseHandle (proc.hThread);
-  GNUNET_free (cmd);
 
 
-  if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
+  if ( (NULL == lsocks) || (INVALID_SOCKET == lsocks[0]) )
     return gnunet_proc;
 
   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
     return gnunet_proc;
 
   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
@@ -1214,16 +1151,20 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
   fail = 1;
   do
   {
   fail = 1;
   do
   {
-    int wrote;
-    uint64_t size, count, i;
+    ssize_t wrote;
+    uint64_t size;
+    uint64_t count;
+    unsigned int i;
 
     /* Tell the number of sockets */
     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
 
     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
 
     /* Tell the number of sockets */
     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
 
     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
-    if (wrote != sizeof (count))
+    if (sizeof (count) != wrote)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "Failed to write %u count bytes to the child: %u\n",
+                 sizeof (count), GetLastError ());
       break;
     }
     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
       break;
     }
     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
@@ -1232,8 +1173,9 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
       /* Get a socket duplication info */
       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
       {
       /* Get a socket duplication info */
       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
-        LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   "Failed to duplicate an socket[%llu]: %u\n", i,
+                   GetLastError ());
         break;
       }
       /* Synchronous I/O is not nice, but we can't schedule this:
         break;
       }
       /* Synchronous I/O is not nice, but we can't schedule this:
@@ -1246,16 +1188,20 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
        */
       size = sizeof (pi);
       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
        */
       size = sizeof (pi);
       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
-      if (wrote != sizeof (size))
+      if (sizeof (size) != wrote)
       {
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   "Failed to write %u size[%llu] bytes to the child: %u\n",
+                   sizeof (size), i, GetLastError ());
         break;
       }
       /* Finally! Send the data */
       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
         break;
       }
       /* Finally! Send the data */
       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
-      if (wrote != sizeof (pi))
+      if (sizeof (pi) != wrote)
       {
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   "Failed to write %u socket[%llu] bytes to the child: %u\n",
+                   sizeof (pi), i, GetLastError ());
         break;
       }
     }
         break;
       }
     }
@@ -1277,24 +1223,310 @@ GNUNET_OS_start_process_v (const SOCKTYPE *lsocks,
     /* If we can't pass on the socket(s), the child will block forever,
      * better put it out of its misery.
      */
     /* If we can't pass on the socket(s), the child will block forever,
      * better put it out of its misery.
      */
-    TerminateProcess (gnunet_proc->handle, 0);
+    SafeTerminateProcess (gnunet_proc->handle, 0, 0);
     CloseHandle (gnunet_proc->handle);
     CloseHandle (gnunet_proc->handle);
-    GNUNET_DISK_npipe_close (gnunet_proc->control_pipe);
+    if (NULL != gnunet_proc->control_pipe)
+      GNUNET_DISK_file_close (gnunet_proc->control_pipe);
     GNUNET_free (gnunet_proc);
     return NULL;
   }
     GNUNET_free (gnunet_proc);
     return NULL;
   }
-
   return gnunet_proc;
 #endif
 }
 
 
 /**
   return gnunet_proc;
 #endif
 }
 
 
 /**
- * Retrieve the status of a process
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
+ * @param pipe_stdin pipe to use to send input to child process (or NULL)
+ * @param pipe_stdout pipe to use to get output from child process (or NULL)
+ * @param pipe_stderr pipe to use to get output from child process (or NULL)
+ * @param filename name of the binary
+ * @param argv NULL-terminated array of arguments to the process
+ * @return pointer to process structure of the new process, NULL on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_vap (int pipe_control,
+                             enum GNUNET_OS_InheritStdioFlags std_inheritance,
+                            struct GNUNET_DISK_PipeHandle *pipe_stdin,
+                            struct GNUNET_DISK_PipeHandle *pipe_stdout,
+                             struct GNUNET_DISK_PipeHandle *pipe_stderr,
+                            const char *filename,
+                            char *const argv[])
+{
+  return start_process (pipe_control,
+                        std_inheritance,
+                       pipe_stdin,
+                       pipe_stdout,
+                        pipe_stderr,
+                       NULL,
+                       filename,
+                       argv);
+}
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
+ * @param pipe_stdin pipe to use to send input to child process (or NULL)
+ * @param pipe_stdout pipe to use to get output from child process (or NULL)
+ * @param pipe_stderr pipe to use to get output from child process (or NULL)
+ * @param filename name of the binary
+ * @param va NULL-terminated list of arguments to the process
+ * @return pointer to process structure of the new process, NULL on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_va (int pipe_control,
+                            enum GNUNET_OS_InheritStdioFlags std_inheritance,
+                           struct GNUNET_DISK_PipeHandle *pipe_stdin,
+                            struct GNUNET_DISK_PipeHandle *pipe_stdout,
+                            struct GNUNET_DISK_PipeHandle *pipe_stderr,
+                            const char *filename, va_list va)
+{
+  struct GNUNET_OS_Process *ret;
+  va_list ap;
+  char **argv;
+  int argc;
+
+  argc = 0;
+  va_copy (ap, va);
+  while (NULL != va_arg (ap, char *))
+    argc++;
+  va_end (ap);
+  argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
+  argc = 0;
+  va_copy (ap, va);
+  while (NULL != (argv[argc] = va_arg (ap, char *)))
+    argc++;
+  va_end (ap);
+  ret = GNUNET_OS_start_process_vap (pipe_control,
+                                     std_inheritance,
+                                    pipe_stdin,
+                                    pipe_stdout,
+                                     pipe_stderr,
+                                    filename,
+                                    argv);
+  GNUNET_free (argv);
+  return ret;
+}
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
+ * @param pipe_stdin pipe to use to send input to child process (or NULL)
+ * @param pipe_stdout pipe to use to get output from child process (or NULL)
+ * @param filename name of the binary
+ * @param ... NULL-terminated list of arguments to the process
+ * @return pointer to process structure of the new process, NULL on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process (int pipe_control,
+                         enum GNUNET_OS_InheritStdioFlags std_inheritance,
+                        struct GNUNET_DISK_PipeHandle *pipe_stdin,
+                         struct GNUNET_DISK_PipeHandle *pipe_stdout,
+                         struct GNUNET_DISK_PipeHandle *pipe_stderr,
+                         const char *filename, ...)
+{
+  struct GNUNET_OS_Process *ret;
+  va_list ap;
+
+  va_start (ap, filename);
+  ret = GNUNET_OS_start_process_va (pipe_control,
+                                    std_inheritance,
+                                    pipe_stdin,
+                                   pipe_stdout,
+                                    pipe_stderr,
+                                    filename,
+                                    ap);
+  va_end (ap);
+  return ret;
+}
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
+ *        std handles of the parent are inherited by the child.
+ *        pipe_stdin and pipe_stdout take priority over std_inheritance
+ *        (when they are non-NULL).
+ * @param lsocks array of listen sockets to dup systemd-style (or NULL);
+ *         must be NULL on platforms where dup is not supported
+ * @param filename name of the binary
+ * @param argv NULL-terminated list of arguments to the process
+ * @return process ID of the new process, -1 on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_v (int pipe_control,
+                           enum GNUNET_OS_InheritStdioFlags std_inheritance,
+                          const SOCKTYPE *lsocks,
+                           const char *filename,
+                           char *const argv[])
+{
+  return start_process (pipe_control,
+                        std_inheritance,
+                       NULL,
+                       NULL,
+                        NULL,
+                       lsocks,
+                       filename,
+                       argv);
+}
+
+
+/**
+ * Start a process.  This function is similar to the GNUNET_OS_start_process_*
+ * except that the filename and arguments can have whole strings which contain
+ * the arguments.  These arguments are to be separated by spaces and are parsed
+ * in the order they appear.  Arguments containing spaces can be used by
+ * quoting them with @em ".
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
+ * @param lsocks array of listen sockets to dup systemd-style (or NULL);
+ *         must be NULL on platforms where dup is not supported
+ * @param filename name of the binary.  It is valid to have the arguments
+ *         in this string when they are separated by spaces.
+ * @param ... more arguments.  Should be of type `char *`.  It is valid
+ *         to have the arguments in these strings when they are separated by
+ *         spaces.  The last argument MUST be NULL.
+ * @return pointer to process structure of the new process, NULL on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_s (int pipe_control,
+                           unsigned int std_inheritance,
+                           const SOCKTYPE * lsocks,
+                           const char *filename, ...)
+{
+  va_list ap;
+  char **argv;
+  unsigned int argv_size;
+  const char *arg;
+  const char *rpos;
+  char *pos;
+  char *cp;
+  const char *last;
+  struct GNUNET_OS_Process *proc;
+  char *binary_path;
+  int quote_on;
+  unsigned int i;
+  size_t len;
+
+  argv_size = 1;
+  va_start (ap, filename);
+  arg = filename;
+  last = NULL;
+  do
+  {
+    rpos = arg;
+    quote_on = 0;
+    while ('\0' != *rpos)
+    {
+      if ('"' == *rpos)
+      {
+       if (1 == quote_on)
+         quote_on = 0;
+       else
+         quote_on = 1;
+      }
+      if ( (' ' == *rpos) && (0 == quote_on) )
+      {
+       if (NULL != last)
+         argv_size++;
+       last = NULL;
+       rpos++;
+       while (' ' == *rpos)
+         rpos++;
+      }
+      if ( (NULL == last) && ('\0' != *rpos) ) // FIXME: == or !=?
+       last = rpos;
+      if ('\0' != *rpos)
+       rpos++;
+    }
+    if (NULL != last)
+      argv_size++;
+  }
+  while (NULL != (arg = (va_arg (ap, const char*))));
+  va_end (ap);
+
+  argv = GNUNET_malloc (argv_size * sizeof (char *));
+  argv_size = 0;
+  va_start (ap, filename);
+  arg = filename;
+  last = NULL;
+  do
+  {
+    cp = GNUNET_strdup (arg);
+    quote_on = 0;
+    pos = cp;
+    while ('\0' != *pos)
+    {
+      if ('"' == *pos)
+      {
+       if (1 == quote_on)
+         quote_on = 0;
+       else
+         quote_on = 1;
+      }
+      if ( (' ' == *pos) && (0 == quote_on) )
+      {
+       *pos = '\0';
+       if (NULL != last)
+         argv[argv_size++] = GNUNET_strdup (last);
+       last = NULL;
+       pos++;
+       while (' ' == *pos)
+         pos++;
+      }
+      if ( (NULL == last) && ('\0' != *pos)) // FIXME: == or !=?
+       last = pos;
+      if ('\0' != *pos)
+       pos++;
+    }
+    if (NULL != last)
+      argv[argv_size++] = GNUNET_strdup (last);
+    last = NULL;
+    GNUNET_free (cp);
+  }
+  while (NULL != (arg = (va_arg (ap, const char*))));
+  va_end (ap);
+  argv[argv_size] = NULL;
+
+  for(i = 0; i < argv_size; i++)
+  {
+    len = strlen (argv[i]);
+    if ( (argv[i][0] == '"') && (argv[i][len-1] == '"'))
+    {
+      memmove (&argv[i][0], &argv[i][1], len - 2);
+      argv[i][len-2] = '\0';
+    }
+  }
+  binary_path = argv[0];
+  proc = GNUNET_OS_start_process_v (pipe_control, std_inheritance, lsocks,
+                                   binary_path, argv);
+  while (argv_size > 0)
+    GNUNET_free (argv[--argv_size]);
+  GNUNET_free (argv);
+  return proc;
+}
+
+
+/**
+ * Retrieve the status of a process, waiting on him if dead.
+ * Nonblocking version.
+ *
  * @param proc process ID
  * @param type status type
  * @param code return code/signal number
  * @param proc process ID
  * @param type status type
  * @param code return code/signal number
- * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
+ * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
  */
 int
 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
  */
 int
 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
@@ -1309,7 +1541,8 @@ GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
   ret = waitpid (proc->pid, &status, WNOHANG);
   if (ret < 0)
   {
   ret = waitpid (proc->pid, &status, WNOHANG);
   if (ret < 0)
   {
-    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
+    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
+                  "waitpid");
     return GNUNET_SYSERR;
   }
   if (0 == ret)
     return GNUNET_SYSERR;
   }
   if (0 == ret)
@@ -1358,7 +1591,8 @@ GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
   ret = proc->pid;
   if (h == NULL || ret == 0)
   {
   ret = proc->pid;
   if (h == NULL || ret == 0)
   {
-    LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Invalid process information {%d, %08X}\n",
          ret, h);
     return GNUNET_SYSERR;
   }
          ret, h);
     return GNUNET_SYSERR;
   }
@@ -1389,43 +1623,51 @@ GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
 
 
 /**
 
 
 /**
- * Wait for a process
+ * Wait for a process to terminate. The return code is discarded.
+ * You must not use #GNUNET_OS_process_status() on the same process
+ * after calling this function!  This function is blocking and should
+ * thus only be used if the child process is known to have terminated
+ * or to terminate very soon.
+ *
  * @param proc pointer to process structure
  * @param proc pointer to process structure
- * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
  */
 int
 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
 {
  */
 int
 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
 {
-
 #ifndef MINGW
   pid_t pid = proc->pid;
 #ifndef MINGW
   pid_t pid = proc->pid;
+  pid_t ret;
 
 
-  if (pid != waitpid (pid, NULL, 0))
+  while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
+         (EINTR == errno) ) ;
+  if (pid != ret)
+  {
+    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
+                  "waitpid");
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
+  }
   return GNUNET_OK;
 #else
   HANDLE h;
   return GNUNET_OK;
 #else
   HANDLE h;
-  int ret;
 
   h = proc->handle;
   if (NULL == h)
   {
 
   h = proc->handle;
   if (NULL == h)
   {
-    LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Invalid process information {%d, %08X}\n",
          proc->pid, h);
     return GNUNET_SYSERR;
   }
          proc->pid, h);
     return GNUNET_SYSERR;
   }
-  if (h == NULL)
+  if (NULL == h)
     h = GetCurrentProcess ();
 
   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
   {
     SetErrnoFromWinError (GetLastError ());
     h = GetCurrentProcess ();
 
   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
   {
     SetErrnoFromWinError (GetLastError ());
-    ret = GNUNET_SYSERR;
+    return GNUNET_SYSERR;
   }
   }
-  else
-    ret = GNUNET_OK;
-
-  return ret;
+  return GNUNET_OK;
 #endif
 }
 
 #endif
 }
 
@@ -1457,7 +1699,7 @@ struct GNUNET_OS_CommandHandle
   GNUNET_OS_LineProcessor proc;
 
   /**
   GNUNET_OS_LineProcessor proc;
 
   /**
-   * Closure for 'proc'.
+   * Closure for @e proc.
    */
   void *proc_cls;
 
    */
   void *proc_cls;
 
@@ -1469,7 +1711,7 @@ struct GNUNET_OS_CommandHandle
   /**
    * Task reading from pipe.
    */
   /**
    * Task reading from pipe.
    */
-  GNUNET_SCHEDULER_TaskIdentifier rtask;
+  struct GNUNET_SCHEDULER_Task *rtask;
 
   /**
    * When to time out.
 
   /**
    * When to time out.
@@ -1493,15 +1735,14 @@ struct GNUNET_OS_CommandHandle
 void
 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
 {
 void
 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
 {
-
-  if (cmd->proc != NULL)
+  if (NULL != cmd->proc)
   {
   {
-    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
+    GNUNET_assert (NULL != cmd->rtask);
     GNUNET_SCHEDULER_cancel (cmd->rtask);
   }
   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
     GNUNET_SCHEDULER_cancel (cmd->rtask);
   }
   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
-  GNUNET_OS_process_close (cmd->eip);
+  GNUNET_OS_process_destroy (cmd->eip);
   GNUNET_DISK_pipe_close (cmd->opipe);
   GNUNET_free (cmd);
 }
   GNUNET_DISK_pipe_close (cmd->opipe);
   GNUNET_free (cmd);
 }
@@ -1521,7 +1762,7 @@ cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   char *end;
   ssize_t ret;
 
   char *end;
   ssize_t ret;
 
-  cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
+  cmd->rtask = NULL;
   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
   {
     /* timeout, shutdown, etc. */
   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
   {
     /* timeout, shutdown, etc. */
@@ -1547,7 +1788,7 @@ cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   }
   end = memchr (&cmd->buf[cmd->off], '\n', ret);
   cmd->off += ret;
   }
   end = memchr (&cmd->buf[cmd->off], '\n', ret);
   cmd->off += ret;
-  while (end != NULL)
+  while (NULL != end)
   {
     *end = '\0';
     cmd->proc (cmd->proc_cls, cmd->buf);
   {
     *end = '\0';
     cmd->proc (cmd->proc_cls, cmd->buf);
@@ -1566,15 +1807,17 @@ cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * for each line of the output.
  *
  * @param proc function to call for each line of the output
  * for each line of the output.
  *
  * @param proc function to call for each line of the output
- * @param proc_cls closure for proc
+ * @param proc_cls closure for @a proc
  * @param timeout when to time out
  * @param binary command to run
  * @param ... arguments to command
  * @return NULL on error
  */
 struct GNUNET_OS_CommandHandle *
  * @param timeout when to time out
  * @param binary command to run
  * @param ... arguments to command
  * @return NULL on error
  */
 struct GNUNET_OS_CommandHandle *
-GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
-                       struct GNUNET_TIME_Relative timeout, const char *binary,
+GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
+                       void *proc_cls,
+                       struct GNUNET_TIME_Relative timeout,
+                       const char *binary,
                        ...)
 {
   struct GNUNET_OS_CommandHandle *cmd;
                        ...)
 {
   struct GNUNET_OS_CommandHandle *cmd;
@@ -1586,7 +1829,8 @@ GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
   if (NULL == opipe)
     return NULL;
   va_start (ap, binary);
   if (NULL == opipe)
     return NULL;
   va_start (ap, binary);
-  eip = GNUNET_OS_start_process_va (NULL, opipe, binary, ap);
+  /* redirect stdout, don't inherit stderr/stdin */
+  eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, NULL, binary, ap);
   va_end (ap);
   if (NULL == eip)
   {
   va_end (ap);
   if (NULL == eip)
   {
@@ -1594,7 +1838,7 @@ GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
     return NULL;
   }
   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
     return NULL;
   }
   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
-  cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
+  cmd = GNUNET_new (struct GNUNET_OS_CommandHandle);
   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   cmd->eip = eip;
   cmd->opipe = opipe;
   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   cmd->eip = eip;
   cmd->opipe = opipe;
@@ -1606,6 +1850,4 @@ GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
 }
 
 
 }
 
 
-
-
 /* end of os_priority.c */
 /* end of os_priority.c */