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