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