Fix perf_crypto_rsa.c after various changes
[oweals/gnunet.git] / src / util / os_priority.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2002, 2003, 2004, 2005, 2006, 2011 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file util/os_priority.c
23  * @brief Methods to set process priority
24  * @author Nils Durner
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "disk.h"
30 #include <unistr.h>
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
35
36 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
37
38 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
39
40
41 struct GNUNET_OS_Process
42 {
43   /**
44    * PID of the process.
45    */
46   pid_t pid;
47
48 #if WINDOWS
49   /**
50    * Process handle.
51    */
52   HANDLE handle;
53 #endif
54
55   /**
56    * Pipe we use to signal the process.
57    * NULL if unused, or if process was deemed uncontrollable.
58    */
59   struct GNUNET_DISK_FileHandle *control_pipe;
60 };
61
62
63 /**
64  * Handle for 'this' process.
65  */
66 static struct GNUNET_OS_Process current_process;
67
68 /**
69  * Handle for the #parent_control_handler() Task.
70  */
71 static struct GNUNET_SCHEDULER_Task *pch;
72
73
74 /**
75  * This handler is called on shutdown to remove the #pch.
76  *
77  * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe
78  */
79 static void
80 shutdown_pch (void *cls)
81 {
82   struct GNUNET_DISK_FileHandle *control_pipe = cls;
83     
84   GNUNET_SCHEDULER_cancel (pch);
85   pch = NULL;
86   GNUNET_DISK_file_close (control_pipe);
87   control_pipe = NULL;
88 }
89
90
91 /**
92  * This handler is called when there are control data to be read on the pipe
93  *
94  * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe
95  */
96 static void
97 parent_control_handler (void *cls)
98 {
99   struct GNUNET_DISK_FileHandle *control_pipe = cls;
100   char sig;
101   char *pipe_fd;
102   ssize_t ret;
103
104   pch = NULL;
105   ret = GNUNET_DISK_file_read (control_pipe,
106                                &sig,
107                                sizeof (sig));
108   if (sizeof (sig) != ret)
109   {
110     if (-1 == ret)
111       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
112                     "GNUNET_DISK_file_read");
113     LOG (GNUNET_ERROR_TYPE_DEBUG,
114          "Closing control pipe\n");
115     GNUNET_DISK_file_close (control_pipe);
116     control_pipe = NULL;
117     return;
118   }
119   pipe_fd = getenv (GNUNET_OS_CONTROL_PIPE);
120   GNUNET_assert ( (NULL == pipe_fd) ||
121                   (strlen (pipe_fd) <= 0) );
122   LOG (GNUNET_ERROR_TYPE_DEBUG,
123        "Got control code %d from parent via pipe %s\n",
124        sig,
125        pipe_fd);
126   pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
127                                         control_pipe,
128                                         &parent_control_handler,
129                                         control_pipe);
130   GNUNET_SIGNAL_raise ((int) sig);
131 }
132
133
134 /**
135  * Task that connects this process to its parent via pipe;
136  * essentially, the parent control handler will read signal numbers
137  * from the #GNUNET_OS_CONTROL_PIPE (as given in an environment
138  * variable) and raise those signals.
139  *
140  * @param cls closure (unused)
141  */
142 void
143 GNUNET_OS_install_parent_control_handler (void *cls)
144 {
145   const char *env_buf;
146   char *env_buf_end;
147   struct GNUNET_DISK_FileHandle *control_pipe;
148   uint64_t pipe_fd;
149
150   if (NULL != pch)
151   {
152     /* already done, we've been called twice... */
153     GNUNET_break (0);
154     return;
155   }
156   env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
157   if ( (NULL == env_buf) || (strlen (env_buf) <= 0) )
158   {
159     LOG (GNUNET_ERROR_TYPE_DEBUG,
160          "Not installing a handler because $%s is empty\n",
161          GNUNET_OS_CONTROL_PIPE);
162     putenv (GNUNET_OS_CONTROL_PIPE "=");
163     return;
164   }
165   errno = 0;
166   pipe_fd = strtoull (env_buf, &env_buf_end, 16);
167   if ((0 != errno) || (env_buf == env_buf_end))
168   {
169     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
170                        "strtoull",
171                        env_buf);
172     putenv (GNUNET_OS_CONTROL_PIPE "=");
173     return;
174   }
175 #if !defined (WINDOWS)
176   if (pipe_fd >= FD_SETSIZE)
177 #else
178   if ((FILE_TYPE_UNKNOWN == GetFileType ((HANDLE) (uintptr_t) pipe_fd))
179       && (0 != GetLastError ()))
180 #endif
181   {
182     LOG (GNUNET_ERROR_TYPE_ERROR,
183          "GNUNET_OS_CONTROL_PIPE `%s' contains garbage?\n",
184          env_buf);
185     putenv (GNUNET_OS_CONTROL_PIPE "=");
186     return;
187   }
188 #if WINDOWS
189   control_pipe = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) (uintptr_t) pipe_fd);
190 #else
191   control_pipe = GNUNET_DISK_get_handle_from_int_fd ((int) pipe_fd);
192 #endif
193   if (NULL == control_pipe)
194   {
195     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
196                        "open",
197                        env_buf);
198     putenv (GNUNET_OS_CONTROL_PIPE "=");
199     return;
200   }
201   LOG (GNUNET_ERROR_TYPE_DEBUG,
202        "Adding parent control handler pipe `%s' to the scheduler\n",
203        env_buf);
204   pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
205                                         control_pipe,
206                                         &parent_control_handler,
207                                         control_pipe);
208   GNUNET_SCHEDULER_add_shutdown (&shutdown_pch,
209                                  control_pipe);
210   putenv (GNUNET_OS_CONTROL_PIPE "=");
211 }
212
213
214 /**
215  * Get process structure for current process
216  *
217  * The pointer it returns points to static memory location and must
218  * not be deallocated/closed.
219  *
220  * @return pointer to the process sturcutre for this process
221  */
222 struct GNUNET_OS_Process *
223 GNUNET_OS_process_current ()
224 {
225 #if WINDOWS
226   current_process.pid = GetCurrentProcessId ();
227   current_process.handle = GetCurrentProcess ();
228 #else
229   current_process.pid = 0;
230 #endif
231   return &current_process;
232 }
233
234
235 /**
236  * Sends a signal to the process
237  *
238  * @param proc pointer to process structure
239  * @param sig signal
240  * @return 0 on success, -1 on error
241  */
242 int
243 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc,
244                         int sig)
245 {
246   int ret;
247   char csig;
248
249   csig = (char) sig;
250   if (NULL != proc->control_pipe)
251   {
252     LOG (GNUNET_ERROR_TYPE_DEBUG,
253          "Sending signal %d to pid: %u via pipe\n",
254          sig,
255          proc->pid);
256     ret = GNUNET_DISK_file_write (proc->control_pipe,
257                                   &csig,
258                                   sizeof (csig));
259     if (sizeof (csig) == ret)
260       return 0;
261   }
262   /* pipe failed or non-existent, try other methods */
263   switch (sig)
264   {
265 #if !defined (WINDOWS)
266   case SIGHUP:
267 #endif
268   case SIGINT:
269   case SIGKILL:
270   case SIGTERM:
271 #if (SIGTERM != GNUNET_TERM_SIG)
272   case GNUNET_TERM_SIG:
273 #endif
274 #if defined(WINDOWS) && !defined(__CYGWIN__)
275     {
276       DWORD exitcode;
277       int must_kill = GNUNET_YES;
278       if (0 != GetExitCodeProcess (proc->handle, &exitcode))
279         must_kill = (exitcode == STILL_ACTIVE) ? GNUNET_YES : GNUNET_NO;
280       if (GNUNET_YES == must_kill)
281       {
282         if (0 == SafeTerminateProcess (proc->handle, 0, 0))
283         {
284           DWORD error_code = GetLastError ();
285           if ( (error_code != WAIT_TIMEOUT) &&
286                (error_code != ERROR_PROCESS_ABORTED) )
287           {
288             LOG ((error_code == ERROR_ACCESS_DENIED) ?
289                  GNUNET_ERROR_TYPE_INFO : GNUNET_ERROR_TYPE_WARNING,
290                  "SafeTermiateProcess failed with code %lu\n",
291                  error_code);
292             /* The problem here is that a process that is already dying
293              * might cause SafeTerminateProcess to fail with
294              * ERROR_ACCESS_DENIED, but the process WILL die eventually.
295              * If we really had a permissions problem, hanging up (which
296              * is what will happen in process_wait() in that case) is
297              * a valid option.
298              */
299             if (ERROR_ACCESS_DENIED == error_code)
300             {
301               errno = 0;
302             }
303             else
304             {
305               SetErrnoFromWinError (error_code);
306               return -1;
307             }
308           }
309         }
310       }
311     }
312     return 0;
313 #else
314     LOG (GNUNET_ERROR_TYPE_DEBUG,
315          "Sending signal %d to pid: %u via system call\n",
316          sig,
317          proc->pid);
318     return PLIBC_KILL (proc->pid, sig);
319 #endif
320   default:
321 #if defined (WINDOWS)
322     errno = EINVAL;
323     return -1;
324 #else
325     LOG (GNUNET_ERROR_TYPE_DEBUG,
326          "Sending signal %d to pid: %u via system call\n",
327          sig,
328          proc->pid);
329     return PLIBC_KILL (proc->pid, sig);
330 #endif
331   }
332 }
333
334
335 /**
336  * Get the pid of the process in question
337  *
338  * @param proc the process to get the pid of
339  *
340  * @return the current process id
341  */
342 pid_t
343 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
344 {
345   return proc->pid;
346 }
347
348
349 /**
350  * Cleans up process structure contents (OS-dependent) and deallocates
351  * it.
352  *
353  * @param proc pointer to process structure
354  */
355 void
356 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
357 {
358   if (NULL != proc->control_pipe)
359     GNUNET_DISK_file_close (proc->control_pipe);
360 #if defined (WINDOWS)
361   if (NULL != proc->handle)
362     CloseHandle (proc->handle);
363 #endif
364   GNUNET_free (proc);
365 }
366
367
368 #if WINDOWS
369 #include "gnunet_signal_lib.h"
370
371 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
372
373 /**
374  * Make seaspider happy.
375  */
376 #define DWORD_WINAPI DWORD WINAPI
377
378
379 /**
380  * @brief Waits for a process to terminate and invokes the SIGCHLD handler
381  * @param proc pointer to process structure
382  */
383 static DWORD_WINAPI
384 child_wait_thread (void *arg)
385 {
386   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
387
388   WaitForSingleObject (proc->handle, INFINITE);
389
390   if (w32_sigchld_handler)
391     w32_sigchld_handler ();
392
393   return 0;
394 }
395 #endif
396
397
398 #if MINGW
399 static char *
400 CreateCustomEnvTable (char **vars)
401 {
402   char *win32_env_table;
403   char *ptr;
404   char **var_ptr;
405   char *result;
406   char *result_ptr;
407   size_t tablesize = 0;
408   size_t items_count = 0;
409   size_t n_found = 0;
410   size_t n_var;
411   char *index = NULL;
412   size_t c;
413   size_t var_len;
414   char *var;
415   char *val;
416
417   win32_env_table = GetEnvironmentStringsA ();
418   if (NULL == win32_env_table)
419     return NULL;
420   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
421   n_var = c;
422   index = GNUNET_malloc (sizeof (char *) * n_var);
423   for (c = 0; c < n_var; c++)
424     index[c] = 0;
425   for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
426   {
427     size_t len = strlen (ptr);
428     int found = 0;
429
430     for (var_ptr = vars; *var_ptr; var_ptr++)
431     {
432       var = *var_ptr++;
433       val = *var_ptr;
434       var_len = strlen (var);
435       if (strncmp (var, ptr, var_len) == 0)
436       {
437         found = 1;
438         index[c] = 1;
439         tablesize += var_len + strlen (val) + 1;
440         break;
441       }
442     }
443     if (!found)
444       tablesize += len + 1;
445     ptr += len + 1;
446   }
447   for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
448   {
449     var = *var_ptr++;
450     val = *var_ptr;
451     if (index[c] != 1)
452       n_found += strlen (var) + strlen (val) + 1;
453   }
454   result = GNUNET_malloc (tablesize + n_found + 1);
455   for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
456   {
457     size_t len = strlen (ptr);
458     int found = 0;
459
460     for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
461     {
462       var = *var_ptr++;
463       val = *var_ptr;
464       var_len = strlen (var);
465       if (strncmp (var, ptr, var_len) == 0)
466       {
467         found = 1;
468         break;
469       }
470     }
471     if (!found)
472     {
473       strcpy (result_ptr, ptr);
474       result_ptr += len + 1;
475     }
476     else
477     {
478       strcpy (result_ptr, var);
479       result_ptr += var_len;
480       strcpy (result_ptr, val);
481       result_ptr += strlen (val) + 1;
482     }
483     ptr += len + 1;
484   }
485   for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
486   {
487     var = *var_ptr++;
488     val = *var_ptr;
489     var_len = strlen (var);
490     if (index[c] != 1)
491     {
492       strcpy (result_ptr, var);
493       result_ptr += var_len;
494       strcpy (result_ptr, val);
495       result_ptr += strlen (val) + 1;
496     }
497   }
498   FreeEnvironmentStrings (win32_env_table);
499   GNUNET_free (index);
500   *result_ptr = 0;
501   return result;
502 }
503
504 #else
505
506 /**
507  * Open '/dev/null' and make the result the given
508  * file descriptor.
509  *
510  * @param target_fd desired FD to point to /dev/null
511  * @param flags open flags (O_RDONLY, O_WRONLY)
512  */
513 static void
514 open_dev_null (int target_fd,
515                int flags)
516 {
517   int fd;
518
519   fd = open ("/dev/null", flags);
520   if (-1 == fd)
521   {
522     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
523                               "open",
524                               "/dev/null");
525     return;
526   }
527   if (fd == target_fd)
528     return;
529   if (-1 == dup2 (fd, target_fd))
530   {
531     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
532     (void) close (fd);
533     return;
534   }
535   GNUNET_break (0 == close (fd));
536 }
537 #endif
538
539
540 /**
541  * Start a process.
542  *
543  * @param pipe_control should a pipe be used to send signals to the child?
544  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
545  *        std handles of the parent are inherited by the child.
546  *        pipe_stdin and pipe_stdout take priority over std_inheritance
547  *        (when they are non-NULL).
548  * @param pipe_stdin pipe to use to send input to child process (or NULL)
549  * @param pipe_stdout pipe to use to get output from child process (or NULL)
550  * @param pipe_stderr pipe to use for stderr for child process (or NULL)
551  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
552  *         must be NULL on platforms where dup is not supported
553  * @param filename name of the binary
554  * @param argv NULL-terminated list of arguments to the process
555  * @return process ID of the new process, -1 on error
556  */
557 static struct GNUNET_OS_Process *
558 start_process (int pipe_control,
559                enum GNUNET_OS_InheritStdioFlags std_inheritance,
560                struct GNUNET_DISK_PipeHandle *pipe_stdin,
561                struct GNUNET_DISK_PipeHandle *pipe_stdout,
562                struct GNUNET_DISK_PipeHandle *pipe_stderr,
563                const SOCKTYPE *lsocks,
564                const char *filename,
565                char *const argv[])
566 {
567 #ifndef MINGW
568   pid_t ret;
569   char fds[16];
570   struct GNUNET_OS_Process *gnunet_proc;
571   struct GNUNET_DISK_FileHandle *childpipe_read;
572   struct GNUNET_DISK_FileHandle *childpipe_write;
573   int childpipe_read_fd;
574   int i;
575   int j;
576   int k;
577   int tgt;
578   int flags;
579   int *lscp;
580   unsigned int ls;
581   int fd_stdout_write;
582   int fd_stdout_read;
583   int fd_stderr_write;
584   int fd_stderr_read;
585   int fd_stdin_read;
586   int fd_stdin_write;
587
588   if (GNUNET_SYSERR ==
589       GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL))
590     return NULL; /* not executable */
591   if (GNUNET_YES == pipe_control)
592   {
593     struct GNUNET_DISK_PipeHandle *childpipe;
594     int dup_childpipe_read_fd = -1;
595
596     childpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
597                                   GNUNET_YES, GNUNET_NO);
598     if (NULL == childpipe)
599       return NULL;
600     childpipe_read = GNUNET_DISK_pipe_detach_end (childpipe,
601                                                   GNUNET_DISK_PIPE_END_READ);
602     childpipe_write = GNUNET_DISK_pipe_detach_end (childpipe,
603                                                    GNUNET_DISK_PIPE_END_WRITE);
604     GNUNET_DISK_pipe_close (childpipe);
605     if ( (NULL == childpipe_read) ||
606          (NULL == childpipe_write) ||
607          (GNUNET_OK !=
608           GNUNET_DISK_internal_file_handle_ (childpipe_read,
609                                              &childpipe_read_fd,
610                                              sizeof (int))) ||
611          (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd))))
612     {
613       if (NULL != childpipe_read)
614         GNUNET_DISK_file_close (childpipe_read);
615       if (NULL != childpipe_write)
616         GNUNET_DISK_file_close (childpipe_write);
617       if (0 <= dup_childpipe_read_fd)
618         close (dup_childpipe_read_fd);
619       return NULL;
620     }
621     childpipe_read_fd = dup_childpipe_read_fd;
622     GNUNET_DISK_file_close (childpipe_read);
623   }
624   else
625   {
626     childpipe_write = NULL;
627     childpipe_read_fd = -1;
628   }
629   if (NULL != pipe_stdin)
630   {
631     GNUNET_assert (GNUNET_OK ==
632                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
633                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
634                                                       &fd_stdin_read, sizeof (int)));
635     GNUNET_assert (GNUNET_OK ==
636                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
637                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
638                                                       &fd_stdin_write, sizeof (int)));
639   }
640   if (NULL != pipe_stdout)
641   {
642     GNUNET_assert (GNUNET_OK ==
643                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
644                                                       (pipe_stdout,
645                                                        GNUNET_DISK_PIPE_END_WRITE),
646                                                       &fd_stdout_write, sizeof (int)));
647     GNUNET_assert (GNUNET_OK ==
648                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
649                                                       (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
650                                                       &fd_stdout_read, sizeof (int)));
651   }
652   if (NULL != pipe_stderr)
653   {
654     GNUNET_assert (GNUNET_OK ==
655                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
656                                                       (pipe_stderr,
657                                                        GNUNET_DISK_PIPE_END_READ),
658                                                       &fd_stderr_read, sizeof (int)));
659     GNUNET_assert (GNUNET_OK ==
660                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
661                                                       (pipe_stderr,
662                                                        GNUNET_DISK_PIPE_END_WRITE),
663                                                       &fd_stderr_write, sizeof (int)));
664   }
665   lscp = NULL;
666   ls = 0;
667   if (NULL != lsocks)
668   {
669     i = 0;
670     while (-1 != (k = lsocks[i++]))
671       GNUNET_array_append (lscp, ls, k);
672     GNUNET_array_append (lscp, ls, -1);
673   }
674 #if DARWIN
675   /* see https://gnunet.org/vfork */
676   ret = vfork ();
677 #else
678   ret = fork ();
679 #endif
680   if (-1 == ret)
681   {
682     int eno = errno;
683     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
684     GNUNET_array_grow (lscp, ls, 0);
685     if (NULL != childpipe_write)
686       GNUNET_DISK_file_close (childpipe_write);
687     if (0 <= childpipe_read_fd)
688       close (childpipe_read_fd);
689     errno = eno;
690     return NULL;
691   }
692   if (0 != ret)
693   {
694     unsetenv (GNUNET_OS_CONTROL_PIPE);
695     gnunet_proc = GNUNET_new (struct GNUNET_OS_Process);
696     gnunet_proc->pid = ret;
697     gnunet_proc->control_pipe = childpipe_write;
698     if (GNUNET_YES == pipe_control)
699     {
700       close (childpipe_read_fd);
701     }
702     GNUNET_array_grow (lscp, ls, 0);
703     return gnunet_proc;
704   }
705   if (0 <= childpipe_read_fd)
706   {
707     char fdbuf[100];
708 #ifndef DARWIN
709     /* due to vfork, we must NOT free memory on DARWIN! */
710     GNUNET_DISK_file_close (childpipe_write);
711 #endif
712     snprintf (fdbuf, 100, "%x", childpipe_read_fd);
713     setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1);
714   }
715   else
716     unsetenv (GNUNET_OS_CONTROL_PIPE);
717   if (NULL != pipe_stdin)
718   {
719     GNUNET_break (0 == close (fd_stdin_write));
720     if (-1 == dup2 (fd_stdin_read, 0))
721       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
722     GNUNET_break (0 == close (fd_stdin_read));
723   }
724   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
725   {
726     GNUNET_break (0 == close (0));
727     open_dev_null (0, O_RDONLY);
728   }
729   if (NULL != pipe_stdout)
730   {
731     GNUNET_break (0 == close (fd_stdout_read));
732     if (-1 == dup2 (fd_stdout_write, 1))
733       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
734     GNUNET_break (0 == close (fd_stdout_write));
735   }
736   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
737   {
738     GNUNET_break (0 == close (1));
739     open_dev_null (1, O_WRONLY);
740   }
741   if (NULL != pipe_stderr)
742   {
743     GNUNET_break (0 == close (fd_stderr_read));
744     if (-1 == dup2 (fd_stderr_write, 2))
745       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
746     GNUNET_break (0 == close (fd_stderr_write));
747   }
748   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
749   {
750     GNUNET_break (0 == close (2));
751     open_dev_null (2, O_WRONLY);
752   }
753   if (NULL != lscp)
754   {
755     /* read systemd documentation... */
756     i = 0;
757     tgt = 3;
758     while (-1 != lscp[i])
759     {
760       j = i + 1;
761       while (-1 != lscp[j])
762       {
763         if (lscp[j] == tgt)
764         {
765           /* dup away */
766           k = dup (lscp[j]);
767           GNUNET_assert (-1 != k);
768           GNUNET_assert (0 == close (lscp[j]));
769           lscp[j] = k;
770           break;
771         }
772         j++;
773       }
774       if (lscp[i] != tgt)
775       {
776         /* Bury any existing FD, no matter what; they should all be closed
777          * on exec anyway and the important onces have been dup'ed away */
778         (void) close (tgt);
779         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
780       }
781       /* unset close-on-exec flag */
782       flags = fcntl (tgt, F_GETFD);
783       GNUNET_assert (flags >= 0);
784       flags &= ~FD_CLOEXEC;
785       fflush (stderr);
786       (void) fcntl (tgt, F_SETFD, flags);
787       tgt++;
788       i++;
789     }
790     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
791     setenv ("LISTEN_FDS", fds, 1);
792   }
793 #ifndef DARWIN
794   /* due to vfork, we must NOT free memory on DARWIN! */
795   GNUNET_array_grow (lscp, ls, 0);
796 #endif
797   execvp (filename, argv);
798   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
799   _exit (1);
800 #else
801   struct GNUNET_DISK_FileHandle *childpipe_read;
802   struct GNUNET_DISK_FileHandle *childpipe_write;
803   HANDLE childpipe_read_handle;
804   char **arg;
805   char **non_const_argv;
806   unsigned int cmdlen;
807   char *cmd;
808   char *idx;
809   STARTUPINFOW start;
810   PROCESS_INFORMATION proc;
811   int argcount = 0;
812   struct GNUNET_OS_Process *gnunet_proc;
813   char path[MAX_PATH + 1];
814   char *our_env[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
815   char *env_block = NULL;
816   char *pathbuf;
817   DWORD pathbuf_len;
818   DWORD alloc_len;
819   char *self_prefix;
820   char *bindir;
821   char *libdir;
822   char *ptr;
823   char *non_const_filename;
824   char win_path[MAX_PATH + 1];
825   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
826   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
827   HANDLE lsocks_read;
828   HANDLE lsocks_write;
829   wchar_t *wpath;
830   wchar_t *wcmd;
831   size_t wpath_len;
832   size_t wcmd_len;
833   int env_off;
834   int fail;
835   long lRet;
836   HANDLE stdin_handle;
837   HANDLE stdout_handle;
838   HANDLE stdih, stdoh, stdeh;
839   DWORD stdif, stdof, stdef;
840   BOOL bresult;
841   DWORD error_code;
842   DWORD create_no_window;
843
844   if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL))
845     return NULL; /* not executable */
846
847   /* Search in prefix dir (hopefully - the directory from which
848    * the current module was loaded), bindir and libdir, then in PATH
849    */
850   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
851   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
852   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
853
854   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
855
856   alloc_len =
857       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
858       strlen (libdir);
859
860   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
861
862   ptr = pathbuf;
863   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
864   GNUNET_free (self_prefix);
865   GNUNET_free (bindir);
866   GNUNET_free (libdir);
867
868   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
869   if (alloc_len != pathbuf_len - 1)
870   {
871     GNUNET_free (pathbuf);
872     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
873     return NULL;
874   }
875
876   cmdlen = strlen (filename);
877   if ( (cmdlen < 5) || (0 != strcmp (&filename[cmdlen - 4], ".exe")) )
878     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
879   else
880     GNUNET_asprintf (&non_const_filename, "%s", filename);
881
882   /* It could be in POSIX form, convert it to a DOS path early on */
883   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
884   {
885     SetErrnoFromWinError (lRet);
886     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
887                        non_const_filename);
888     GNUNET_free (non_const_filename);
889     GNUNET_free (pathbuf);
890     return NULL;
891   }
892   GNUNET_free (non_const_filename);
893   non_const_filename = GNUNET_strdup (win_path);
894    /* Check that this is the full path. If it isn't, search. */
895   /* FIXME: convert it to wchar_t and use SearchPathW?
896    * Remember: arguments to _start_process() are technically in UTF-8...
897    */
898   if (non_const_filename[1] == ':')
899   {
900     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
901     LOG (GNUNET_ERROR_TYPE_DEBUG,
902         "Using path `%s' as-is. PATH is %s\n", path, ptr);
903   }
904   else if (!SearchPathA
905            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
906             path, NULL))
907   {
908     SetErrnoFromWinError (GetLastError ());
909     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
910                        non_const_filename);
911     GNUNET_free (non_const_filename);
912     GNUNET_free (pathbuf);
913     return NULL;
914   }
915   else
916     LOG (GNUNET_ERROR_TYPE_DEBUG,
917         "Found `%s' in PATH `%s'\n", path, pathbuf);
918   GNUNET_free (pathbuf);
919   GNUNET_free (non_const_filename);
920
921   /* Count the number of arguments */
922   arg = (char **) argv;
923   while (*arg)
924   {
925     arg++;
926     argcount++;
927   }
928
929   /* Allocate a copy argv */
930   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
931
932   /* Copy all argv strings */
933   argcount = 0;
934   arg = (char **) argv;
935   while (*arg)
936   {
937     if (arg == argv)
938       non_const_argv[argcount] = GNUNET_strdup (path);
939     else
940       non_const_argv[argcount] = GNUNET_strdup (*arg);
941     arg++;
942     argcount++;
943   }
944   non_const_argv[argcount] = NULL;
945
946   /* Count cmd len */
947   cmdlen = 1;
948   arg = non_const_argv;
949   while (*arg)
950   {
951     cmdlen = cmdlen + strlen (*arg) + 4;
952     arg++;
953   }
954
955   /* Allocate and create cmd */
956   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
957   arg = non_const_argv;
958   while (*arg)
959   {
960     char arg_last_char = (*arg)[strlen (*arg) - 1];
961     idx += sprintf (idx, "\"%s%s\"%s", *arg,
962         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
963     arg++;
964   }
965
966   while (argcount > 0)
967     GNUNET_free (non_const_argv[--argcount]);
968   GNUNET_free (non_const_argv);
969
970   memset (&start, 0, sizeof (start));
971   start.cb = sizeof (start);
972   if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
973     start.dwFlags |= STARTF_USESTDHANDLES;
974
975   stdih = GetStdHandle (STD_INPUT_HANDLE);
976   GetHandleInformation (stdih, &stdif);
977   if (pipe_stdin != NULL)
978   {
979     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
980                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
981                                        &stdin_handle, sizeof (HANDLE));
982     start.hStdInput = stdin_handle;
983   }
984   else if (stdih)
985   {
986     if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
987     {
988       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
989       if (pipe_stdin == NULL)
990         start.hStdInput = stdih;
991     }
992     else
993       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
994   }
995
996
997   stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
998   GetHandleInformation (stdoh, &stdof);
999   if (NULL != pipe_stdout)
1000   {
1001     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1002                                        (pipe_stdout,
1003                                         GNUNET_DISK_PIPE_END_WRITE),
1004                                        &stdout_handle, sizeof (HANDLE));
1005     start.hStdOutput = stdout_handle;
1006   }
1007   else if (stdoh)
1008   {
1009     if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
1010     {
1011       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
1012       if (pipe_stdout == NULL)
1013         start.hStdOutput = stdoh;
1014     }
1015     else
1016       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1017   }
1018
1019   stdeh = GetStdHandle (STD_ERROR_HANDLE);
1020   GetHandleInformation (stdeh, &stdef);
1021   if (stdeh)
1022   {
1023     if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
1024     {
1025       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
1026       start.hStdError = stdeh;
1027     }
1028     else
1029       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1030   }
1031
1032   if (GNUNET_YES == pipe_control)
1033   {
1034     struct GNUNET_DISK_PipeHandle *childpipe;
1035     childpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
1036     if (NULL == childpipe)
1037       return NULL;
1038     childpipe_read = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ);
1039     childpipe_write = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE);
1040     GNUNET_DISK_pipe_close (childpipe);
1041     if ((NULL == childpipe_read) || (NULL == childpipe_write) ||
1042         (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read,
1043         &childpipe_read_handle, sizeof (HANDLE))))
1044     {
1045       if (childpipe_read)
1046         GNUNET_DISK_file_close (childpipe_read);
1047       if (childpipe_write)
1048         GNUNET_DISK_file_close (childpipe_write);
1049       GNUNET_free (cmd);
1050       return NULL;
1051     }
1052     /* Unlike *nix variant, we don't dup the handle, so can't close
1053      * filehandle right now.
1054      */
1055     SetHandleInformation (childpipe_read_handle, HANDLE_FLAG_INHERIT, 1);
1056   }
1057   else
1058   {
1059     childpipe_read = NULL;
1060     childpipe_write = NULL;
1061   }
1062
1063   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1064   {
1065     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1066
1067     if (lsocks_pipe == NULL)
1068     {
1069       GNUNET_free (cmd);
1070       GNUNET_DISK_pipe_close (lsocks_pipe);
1071       if (GNUNET_YES == pipe_control)
1072       {
1073         GNUNET_DISK_file_close (childpipe_write);
1074         GNUNET_DISK_file_close (childpipe_read);
1075       }
1076       return NULL;
1077     }
1078     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1079         GNUNET_DISK_PIPE_END_WRITE);
1080     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1081                                        &lsocks_write, sizeof (HANDLE));
1082     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1083                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1084                                        &lsocks_read, sizeof (HANDLE));
1085   }
1086   else
1087     lsocks_pipe = NULL;
1088
1089   env_off = 0;
1090   if (GNUNET_YES == pipe_control)
1091   {
1092     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1093     GNUNET_asprintf (&our_env[env_off++], "%p", childpipe_read_handle);
1094   }
1095   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1096   {
1097     /*This will tell the child that we're going to send lsocks over the pipe*/
1098     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1099     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1100   }
1101   our_env[env_off++] = NULL;
1102   env_block = CreateCustomEnvTable (our_env);
1103   while (0 > env_off)
1104     GNUNET_free_non_null (our_env[--env_off]);
1105
1106   wpath_len = 0;
1107   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1108   {
1109     LOG (GNUNET_ERROR_TYPE_DEBUG,
1110         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1111     GNUNET_free (env_block);
1112     GNUNET_free (cmd);
1113     if (lsocks_pipe)
1114       GNUNET_DISK_pipe_close (lsocks_pipe);
1115     if (GNUNET_YES == pipe_control)
1116     {
1117       GNUNET_DISK_file_close (childpipe_write);
1118       GNUNET_DISK_file_close (childpipe_read);
1119     }
1120     return NULL;
1121   }
1122
1123   wcmd_len = 0;
1124   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1125   {
1126     LOG (GNUNET_ERROR_TYPE_DEBUG,
1127          "Failed to convert `%s' from UTF-8 to UTF-16: %d\n",
1128          cmd,
1129          errno);
1130     GNUNET_free (env_block);
1131     GNUNET_free (cmd);
1132     free (wpath);
1133     if (lsocks_pipe)
1134       GNUNET_DISK_pipe_close (lsocks_pipe);
1135     if (GNUNET_YES == pipe_control)
1136     {
1137       GNUNET_DISK_file_close (childpipe_write);
1138       GNUNET_DISK_file_close (childpipe_read);
1139     }
1140     return NULL;
1141   }
1142
1143   create_no_window = 0;
1144   {
1145     HANDLE console_input = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1146     if (INVALID_HANDLE_VALUE == console_input)
1147       create_no_window = CREATE_NO_WINDOW;
1148     else
1149       CloseHandle (console_input);
1150   }
1151
1152   bresult = CreateProcessW (wpath, wcmd, NULL, NULL, GNUNET_YES,
1153        create_no_window | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1154   error_code = GetLastError ();
1155
1156   if ((NULL == pipe_stdin) && (stdih))
1157     SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
1158
1159
1160   if ((NULL == pipe_stdout) && (stdoh))
1161     SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
1162
1163   if (stdeh)
1164     SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
1165
1166   if (!bresult)
1167     LOG (GNUNET_ERROR_TYPE_ERROR,
1168          "CreateProcess(%s, %s) failed: %lu\n",
1169          path,
1170          cmd,
1171          error_code);
1172
1173   GNUNET_free (env_block);
1174   GNUNET_free (cmd);
1175   free (wpath);
1176   free (wcmd);
1177   if (GNUNET_YES == pipe_control)
1178   {
1179     GNUNET_DISK_file_close (childpipe_read);
1180   }
1181
1182   if (!bresult)
1183   {
1184     if (GNUNET_YES == pipe_control)
1185     {
1186       GNUNET_DISK_file_close (childpipe_write);
1187     }
1188     if (NULL != lsocks)
1189       GNUNET_DISK_pipe_close (lsocks_pipe);
1190     SetErrnoFromWinError (error_code);
1191     return NULL;
1192   }
1193
1194   gnunet_proc = GNUNET_new (struct GNUNET_OS_Process);
1195   gnunet_proc->pid = proc.dwProcessId;
1196   gnunet_proc->handle = proc.hProcess;
1197   gnunet_proc->control_pipe = childpipe_write;
1198
1199   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1200
1201   ResumeThread (proc.hThread);
1202   CloseHandle (proc.hThread);
1203
1204   if ( (NULL == lsocks) || (INVALID_SOCKET == lsocks[0]) )
1205     return gnunet_proc;
1206
1207   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1208
1209   /* This is a replacement for "goto error" that doesn't use goto */
1210   fail = 1;
1211   do
1212   {
1213     ssize_t wrote;
1214     uint64_t size;
1215     uint64_t count;
1216     unsigned int i;
1217
1218     /* Tell the number of sockets */
1219     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1220
1221     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1222     if (sizeof (count) != wrote)
1223     {
1224       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1225                   "Failed to write %u count bytes to the child: %u\n",
1226                   sizeof (count), GetLastError ());
1227       break;
1228     }
1229     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1230     {
1231       WSAPROTOCOL_INFOA pi;
1232       /* Get a socket duplication info */
1233       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1234       {
1235         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1236                     "Failed to duplicate an socket[%llu]: %u\n", i,
1237                     GetLastError ());
1238         break;
1239       }
1240       /* Synchronous I/O is not nice, but we can't schedule this:
1241        * lsocks will be closed/freed by the caller soon, and until
1242        * the child creates a duplicate, closing a socket here will
1243        * close it for good.
1244        */
1245       /* Send the size of the structure
1246        * (the child might be built with different headers...)
1247        */
1248       size = sizeof (pi);
1249       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1250       if (sizeof (size) != wrote)
1251       {
1252         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1253                     "Failed to write %u size[%llu] bytes to the child: %u\n",
1254                     sizeof (size), i, GetLastError ());
1255         break;
1256       }
1257       /* Finally! Send the data */
1258       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1259       if (sizeof (pi) != wrote)
1260       {
1261         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1262                     "Failed to write %u socket[%llu] bytes to the child: %u\n",
1263                     sizeof (pi), i, GetLastError ());
1264         break;
1265       }
1266     }
1267     /* This will block us until the child makes a final read or closes
1268      * the pipe (hence no 'wrote' check), since we have to wait for it
1269      * to duplicate the last socket, before we return and start closing
1270      * our own copies)
1271      */
1272     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1273     fail = 0;
1274   }
1275   while (fail);
1276
1277   GNUNET_DISK_file_sync (lsocks_write_fd);
1278   GNUNET_DISK_pipe_close (lsocks_pipe);
1279
1280   if (fail)
1281   {
1282     /* If we can't pass on the socket(s), the child will block forever,
1283      * better put it out of its misery.
1284      */
1285     SafeTerminateProcess (gnunet_proc->handle, 0, 0);
1286     CloseHandle (gnunet_proc->handle);
1287     if (NULL != gnunet_proc->control_pipe)
1288       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1289     GNUNET_free (gnunet_proc);
1290     return NULL;
1291   }
1292   return gnunet_proc;
1293 #endif
1294 }
1295
1296
1297 /**
1298  * Start a process.
1299  *
1300  * @param pipe_control should a pipe be used to send signals to the child?
1301  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1302  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1303  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1304  * @param pipe_stderr pipe to use to get output from child process (or NULL)
1305  * @param filename name of the binary
1306  * @param argv NULL-terminated array of arguments to the process
1307  * @return pointer to process structure of the new process, NULL on error
1308  */
1309 struct GNUNET_OS_Process *
1310 GNUNET_OS_start_process_vap (int pipe_control,
1311                              enum GNUNET_OS_InheritStdioFlags std_inheritance,
1312                              struct GNUNET_DISK_PipeHandle *pipe_stdin,
1313                              struct GNUNET_DISK_PipeHandle *pipe_stdout,
1314                              struct GNUNET_DISK_PipeHandle *pipe_stderr,
1315                              const char *filename,
1316                              char *const argv[])
1317 {
1318   return start_process (pipe_control,
1319                         std_inheritance,
1320                         pipe_stdin,
1321                         pipe_stdout,
1322                         pipe_stderr,
1323                         NULL,
1324                         filename,
1325                         argv);
1326 }
1327
1328
1329 /**
1330  * Start a process.
1331  *
1332  * @param pipe_control should a pipe be used to send signals to the child?
1333  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1334  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1335  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1336  * @param pipe_stderr pipe to use to get output from child process (or NULL)
1337  * @param filename name of the binary
1338  * @param va NULL-terminated list of arguments to the process
1339  * @return pointer to process structure of the new process, NULL on error
1340  */
1341 struct GNUNET_OS_Process *
1342 GNUNET_OS_start_process_va (int pipe_control,
1343                             enum GNUNET_OS_InheritStdioFlags std_inheritance,
1344                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1345                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1346                             struct GNUNET_DISK_PipeHandle *pipe_stderr,
1347                             const char *filename, va_list va)
1348 {
1349   struct GNUNET_OS_Process *ret;
1350   va_list ap;
1351   char **argv;
1352   int argc;
1353
1354   argc = 0;
1355   va_copy (ap, va);
1356   while (NULL != va_arg (ap, char *))
1357     argc++;
1358   va_end (ap);
1359   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1360   argc = 0;
1361   va_copy (ap, va);
1362   while (NULL != (argv[argc] = va_arg (ap, char *)))
1363     argc++;
1364   va_end (ap);
1365   ret = GNUNET_OS_start_process_vap (pipe_control,
1366                                      std_inheritance,
1367                                      pipe_stdin,
1368                                      pipe_stdout,
1369                                      pipe_stderr,
1370                                      filename,
1371                                      argv);
1372   GNUNET_free (argv);
1373   return ret;
1374 }
1375
1376
1377 /**
1378  * Start a process.
1379  *
1380  * @param pipe_control should a pipe be used to send signals to the child?
1381  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1382  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1383  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1384  * @param filename name of the binary
1385  * @param ... NULL-terminated list of arguments to the process
1386  * @return pointer to process structure of the new process, NULL on error
1387  */
1388 struct GNUNET_OS_Process *
1389 GNUNET_OS_start_process (int pipe_control,
1390                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
1391                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1392                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1393                          struct GNUNET_DISK_PipeHandle *pipe_stderr,
1394                          const char *filename, ...)
1395 {
1396   struct GNUNET_OS_Process *ret;
1397   va_list ap;
1398
1399   va_start (ap, filename);
1400   ret = GNUNET_OS_start_process_va (pipe_control,
1401                                     std_inheritance,
1402                                     pipe_stdin,
1403                                     pipe_stdout,
1404                                     pipe_stderr,
1405                                     filename,
1406                                     ap);
1407   va_end (ap);
1408   return ret;
1409 }
1410
1411
1412 /**
1413  * Start a process.
1414  *
1415  * @param pipe_control should a pipe be used to send signals to the child?
1416  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
1417  *        std handles of the parent are inherited by the child.
1418  *        pipe_stdin and pipe_stdout take priority over std_inheritance
1419  *        (when they are non-NULL).
1420  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1421  *         must be NULL on platforms where dup is not supported
1422  * @param filename name of the binary
1423  * @param argv NULL-terminated list of arguments to the process
1424  * @return process ID of the new process, -1 on error
1425  */
1426 struct GNUNET_OS_Process *
1427 GNUNET_OS_start_process_v (int pipe_control,
1428                            enum GNUNET_OS_InheritStdioFlags std_inheritance,
1429                            const SOCKTYPE *lsocks,
1430                            const char *filename,
1431                            char *const argv[])
1432 {
1433   return start_process (pipe_control,
1434                         std_inheritance,
1435                         NULL,
1436                         NULL,
1437                         NULL,
1438                         lsocks,
1439                         filename,
1440                         argv);
1441 }
1442
1443
1444 /**
1445  * Start a process.  This function is similar to the GNUNET_OS_start_process_*
1446  * except that the filename and arguments can have whole strings which contain
1447  * the arguments.  These arguments are to be separated by spaces and are parsed
1448  * in the order they appear.  Arguments containing spaces can be used by
1449  * quoting them with @em ".
1450  *
1451  * @param pipe_control should a pipe be used to send signals to the child?
1452  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1453  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1454  *         must be NULL on platforms where dup is not supported
1455  * @param filename name of the binary.  It is valid to have the arguments
1456  *         in this string when they are separated by spaces.
1457  * @param ... more arguments.  Should be of type `char *`.  It is valid
1458  *         to have the arguments in these strings when they are separated by
1459  *         spaces.  The last argument MUST be NULL.
1460  * @return pointer to process structure of the new process, NULL on error
1461  */
1462 struct GNUNET_OS_Process *
1463 GNUNET_OS_start_process_s (int pipe_control,
1464                            unsigned int std_inheritance,
1465                            const SOCKTYPE * lsocks,
1466                            const char *filename, ...)
1467 {
1468   va_list ap;
1469   char **argv;
1470   unsigned int argv_size;
1471   const char *arg;
1472   const char *rpos;
1473   char *pos;
1474   char *cp;
1475   const char *last;
1476   struct GNUNET_OS_Process *proc;
1477   char *binary_path;
1478   int quote_on;
1479   unsigned int i;
1480   size_t len;
1481
1482   argv_size = 1;
1483   va_start (ap, filename);
1484   arg = filename;
1485   last = NULL;
1486   do
1487   {
1488     rpos = arg;
1489     quote_on = 0;
1490     while ('\0' != *rpos)
1491     {
1492       if ('"' == *rpos)
1493       {
1494         if (1 == quote_on)
1495           quote_on = 0;
1496         else
1497           quote_on = 1;
1498       }
1499       if ( (' ' == *rpos) && (0 == quote_on) )
1500       {
1501         if (NULL != last)
1502           argv_size++;
1503         last = NULL;
1504         rpos++;
1505         while (' ' == *rpos)
1506           rpos++;
1507       }
1508       if ( (NULL == last) && ('\0' != *rpos) ) // FIXME: == or !=?
1509         last = rpos;
1510       if ('\0' != *rpos)
1511         rpos++;
1512     }
1513     if (NULL != last)
1514       argv_size++;
1515   }
1516   while (NULL != (arg = (va_arg (ap, const char*))));
1517   va_end (ap);
1518
1519   argv = GNUNET_malloc (argv_size * sizeof (char *));
1520   argv_size = 0;
1521   va_start (ap, filename);
1522   arg = filename;
1523   last = NULL;
1524   do
1525   {
1526     cp = GNUNET_strdup (arg);
1527     quote_on = 0;
1528     pos = cp;
1529     while ('\0' != *pos)
1530     {
1531       if ('"' == *pos)
1532       {
1533         if (1 == quote_on)
1534           quote_on = 0;
1535         else
1536           quote_on = 1;
1537       }
1538       if ( (' ' == *pos) && (0 == quote_on) )
1539       {
1540         *pos = '\0';
1541         if (NULL != last)
1542           argv[argv_size++] = GNUNET_strdup (last);
1543         last = NULL;
1544         pos++;
1545         while (' ' == *pos)
1546           pos++;
1547       }
1548       if ( (NULL == last) && ('\0' != *pos)) // FIXME: == or !=?
1549         last = pos;
1550       if ('\0' != *pos)
1551         pos++;
1552     }
1553     if (NULL != last)
1554       argv[argv_size++] = GNUNET_strdup (last);
1555     last = NULL;
1556     GNUNET_free (cp);
1557   }
1558   while (NULL != (arg = (va_arg (ap, const char*))));
1559   va_end (ap);
1560   argv[argv_size] = NULL;
1561
1562   for(i = 0; i < argv_size; i++)
1563   {
1564     len = strlen (argv[i]);
1565     if ( (argv[i][0] == '"') && (argv[i][len-1] == '"'))
1566     {
1567       memmove (&argv[i][0], &argv[i][1], len - 2);
1568       argv[i][len-2] = '\0';
1569     }
1570   }
1571   binary_path = argv[0];
1572   proc = GNUNET_OS_start_process_v (pipe_control, std_inheritance, lsocks,
1573                                     binary_path, argv);
1574   while (argv_size > 0)
1575     GNUNET_free (argv[--argv_size]);
1576   GNUNET_free (argv);
1577   return proc;
1578 }
1579
1580
1581 /**
1582  * Retrieve the status of a process, waiting on him if dead.
1583  * Nonblocking version.
1584  *
1585  * @param proc process ID
1586  * @param type status type
1587  * @param code return code/signal number
1588  * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
1589  */
1590 int
1591 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1592                           enum GNUNET_OS_ProcessStatusType *type,
1593                           unsigned long *code)
1594 {
1595 #ifndef MINGW
1596   int status;
1597   int ret;
1598
1599   GNUNET_assert (0 != proc);
1600   ret = waitpid (proc->pid, &status, WNOHANG);
1601   if (ret < 0)
1602   {
1603     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
1604                   "waitpid");
1605     return GNUNET_SYSERR;
1606   }
1607   if (0 == ret)
1608   {
1609     *type = GNUNET_OS_PROCESS_RUNNING;
1610     *code = 0;
1611     return GNUNET_NO;
1612   }
1613   if (proc->pid != ret)
1614   {
1615     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1616     return GNUNET_SYSERR;
1617   }
1618   if (WIFEXITED (status))
1619   {
1620     *type = GNUNET_OS_PROCESS_EXITED;
1621     *code = WEXITSTATUS (status);
1622   }
1623   else if (WIFSIGNALED (status))
1624   {
1625     *type = GNUNET_OS_PROCESS_SIGNALED;
1626     *code = WTERMSIG (status);
1627   }
1628   else if (WIFSTOPPED (status))
1629   {
1630     *type = GNUNET_OS_PROCESS_SIGNALED;
1631     *code = WSTOPSIG (status);
1632   }
1633 #ifdef WIFCONTINUED
1634   else if (WIFCONTINUED (status))
1635   {
1636     *type = GNUNET_OS_PROCESS_RUNNING;
1637     *code = 0;
1638   }
1639 #endif
1640   else
1641   {
1642     *type = GNUNET_OS_PROCESS_UNKNOWN;
1643     *code = 0;
1644   }
1645 #else
1646   HANDLE h;
1647   DWORD c, error_code, ret;
1648
1649   h = proc->handle;
1650   ret = proc->pid;
1651   if (h == NULL || ret == 0)
1652   {
1653     LOG (GNUNET_ERROR_TYPE_WARNING,
1654          "Invalid process information {%d, %08X}\n",
1655          ret, h);
1656     return GNUNET_SYSERR;
1657   }
1658   if (h == NULL)
1659     h = GetCurrentProcess ();
1660
1661   SetLastError (0);
1662   ret = GetExitCodeProcess (h, &c);
1663   error_code = GetLastError ();
1664   if (ret == 0 || error_code != NO_ERROR)
1665   {
1666     SetErrnoFromWinError (error_code);
1667     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1668     return GNUNET_SYSERR;
1669   }
1670   if (STILL_ACTIVE == c)
1671   {
1672     *type = GNUNET_OS_PROCESS_RUNNING;
1673     *code = 0;
1674     return GNUNET_NO;
1675   }
1676   *type = GNUNET_OS_PROCESS_EXITED;
1677   *code = c;
1678 #endif
1679
1680   return GNUNET_OK;
1681 }
1682
1683
1684 /**
1685  * Wait for a process to terminate. The return code is discarded.
1686  * You must not use #GNUNET_OS_process_status() on the same process
1687  * after calling this function!  This function is blocking and should
1688  * thus only be used if the child process is known to have terminated
1689  * or to terminate very soon.
1690  *
1691  * @param proc pointer to process structure
1692  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1693  */
1694 int
1695 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1696 {
1697 #ifndef MINGW
1698   pid_t pid = proc->pid;
1699   pid_t ret;
1700
1701   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1702           (EINTR == errno) ) ;
1703   if (pid != ret)
1704   {
1705     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
1706                   "waitpid");
1707     return GNUNET_SYSERR;
1708   }
1709   return GNUNET_OK;
1710 #else
1711   HANDLE h;
1712
1713   h = proc->handle;
1714   if (NULL == h)
1715   {
1716     LOG (GNUNET_ERROR_TYPE_WARNING,
1717          "Invalid process information {%d, %08X}\n",
1718          proc->pid, h);
1719     return GNUNET_SYSERR;
1720   }
1721   if (NULL == h)
1722     h = GetCurrentProcess ();
1723
1724   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1725   {
1726     SetErrnoFromWinError (GetLastError ());
1727     return GNUNET_SYSERR;
1728   }
1729   return GNUNET_OK;
1730 #endif
1731 }
1732
1733
1734 /**
1735  * Handle to a command.
1736  */
1737 struct GNUNET_OS_CommandHandle
1738 {
1739
1740   /**
1741    * Process handle.
1742    */
1743   struct GNUNET_OS_Process *eip;
1744
1745   /**
1746    * Handle to the output pipe.
1747    */
1748   struct GNUNET_DISK_PipeHandle *opipe;
1749
1750   /**
1751    * Read-end of output pipe.
1752    */
1753   const struct GNUNET_DISK_FileHandle *r;
1754
1755   /**
1756    * Function to call on each line of output.
1757    */
1758   GNUNET_OS_LineProcessor proc;
1759
1760   /**
1761    * Closure for @e proc.
1762    */
1763   void *proc_cls;
1764
1765   /**
1766    * Buffer for the output.
1767    */
1768   char buf[1024];
1769
1770   /**
1771    * Task reading from pipe.
1772    */
1773   struct GNUNET_SCHEDULER_Task *rtask;
1774
1775   /**
1776    * When to time out.
1777    */
1778   struct GNUNET_TIME_Absolute timeout;
1779
1780   /**
1781    * Current read offset in buf.
1782    */
1783   size_t off;
1784 };
1785
1786
1787 /**
1788  * Stop/kill a command.  Must ONLY be called either from
1789  * the callback after 'NULL' was passed for 'line' *OR*
1790  * from an independent task (not within the line processor).
1791  *
1792  * @param cmd handle to the process
1793  */
1794 void
1795 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1796 {
1797   if (NULL != cmd->proc)
1798   {
1799     GNUNET_assert (NULL != cmd->rtask);
1800     GNUNET_SCHEDULER_cancel (cmd->rtask);
1801   }
1802   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1803   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1804   GNUNET_OS_process_destroy (cmd->eip);
1805   GNUNET_DISK_pipe_close (cmd->opipe);
1806   GNUNET_free (cmd);
1807 }
1808
1809
1810 /**
1811  * Read from the process and call the line processor.
1812  *
1813  * @param cls the `struct GNUNET_OS_CommandHandle *`
1814  */
1815 static void
1816 cmd_read (void *cls)
1817 {
1818   struct GNUNET_OS_CommandHandle *cmd = cls;
1819   const struct GNUNET_SCHEDULER_TaskContext *tc;
1820   GNUNET_OS_LineProcessor proc;
1821   char *end;
1822   ssize_t ret;
1823
1824   cmd->rtask = NULL;
1825   tc = GNUNET_SCHEDULER_get_task_context ();
1826   if (GNUNET_YES !=
1827       GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1828                                          cmd->r))
1829   {
1830     /* timeout */
1831     proc = cmd->proc;
1832     cmd->proc = NULL;
1833     proc (cmd->proc_cls, NULL);
1834     return;
1835   }
1836   ret = GNUNET_DISK_file_read (cmd->r,
1837                                &cmd->buf[cmd->off],
1838                                sizeof (cmd->buf) - cmd->off);
1839   if (ret <= 0)
1840   {
1841     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1842     {
1843       cmd->buf[cmd->off] = '\0';
1844       cmd->proc (cmd->proc_cls, cmd->buf);
1845     }
1846     proc = cmd->proc;
1847     cmd->proc = NULL;
1848     proc (cmd->proc_cls, NULL);
1849     return;
1850   }
1851   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1852   cmd->off += ret;
1853   while (NULL != end)
1854   {
1855     *end = '\0';
1856     cmd->proc (cmd->proc_cls, cmd->buf);
1857     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1858     cmd->off -= (end + 1 - cmd->buf);
1859     end = memchr (cmd->buf, '\n', cmd->off);
1860   }
1861   cmd->rtask 
1862     = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1863                                       (cmd->timeout),
1864                                       cmd->r,
1865                                       &cmd_read, cmd);
1866 }
1867
1868
1869 /**
1870  * Run the given command line and call the given function
1871  * for each line of the output.
1872  *
1873  * @param proc function to call for each line of the output
1874  * @param proc_cls closure for @a proc
1875  * @param timeout when to time out
1876  * @param binary command to run
1877  * @param ... arguments to command
1878  * @return NULL on error
1879  */
1880 struct GNUNET_OS_CommandHandle *
1881 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1882                        void *proc_cls,
1883                        struct GNUNET_TIME_Relative timeout,
1884                        const char *binary,
1885                        ...)
1886 {
1887   struct GNUNET_OS_CommandHandle *cmd;
1888   struct GNUNET_OS_Process *eip;
1889   struct GNUNET_DISK_PipeHandle *opipe;
1890   va_list ap;
1891
1892   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES,
1893                             GNUNET_NO, GNUNET_YES);
1894   if (NULL == opipe)
1895     return NULL;
1896   va_start (ap, binary);
1897   /* redirect stdout, don't inherit stderr/stdin */
1898   eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL,
1899                                     opipe, NULL, binary,
1900                                     ap);
1901   va_end (ap);
1902   if (NULL == eip)
1903   {
1904     GNUNET_DISK_pipe_close (opipe);
1905     return NULL;
1906   }
1907   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1908   cmd = GNUNET_new (struct GNUNET_OS_CommandHandle);
1909   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1910   cmd->eip = eip;
1911   cmd->opipe = opipe;
1912   cmd->proc = proc;
1913   cmd->proc_cls = proc_cls;
1914   cmd->r = GNUNET_DISK_pipe_handle (opipe,
1915                                     GNUNET_DISK_PIPE_END_READ);
1916   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1917                                                cmd->r,
1918                                                &cmd_read,
1919                                                cmd);
1920   return cmd;
1921 }
1922
1923
1924 /* end of os_priority.c */