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