b0668e20d67d5f50258422d68c0d2e33660704ef
[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 == SafeTerminateProcess (proc->handle, 0, 0))
465         {
466           DWORD error_code = GetLastError ();
467           if (error_code != WAIT_TIMEOUT) /* OK, since timeout is 0 */
468           {
469             LOG (GNUNET_ERROR_TYPE_WARNING,
470                 "SafeTermiateProcess failed with code %lu\n", error_code);
471             SetErrnoFromWinError (error_code);
472             return -1;
473           }
474         }
475     }
476     return 0;
477 #else
478     return PLIBC_KILL (proc->pid, sig);
479 #endif
480   default:
481 #if WINDOWS
482     errno = EINVAL;
483     return -1;
484 #else
485     return PLIBC_KILL (proc->pid, sig);
486 #endif    
487   }
488 }
489
490 /**
491  * Get the pid of the process in question
492  *
493  * @param proc the process to get the pid of
494  *
495  * @return the current process id
496  */
497 pid_t
498 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
499 {
500   return proc->pid;
501 }
502
503
504 /**
505  * Cleans up process structure contents (OS-dependent) and deallocates it
506  *
507  * @param proc pointer to process structure
508  */
509 void
510 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
511 {
512   if (NULL != proc->control_pipe)
513     GNUNET_DISK_file_close (proc->control_pipe);
514 // FIXME NILS
515 #ifdef WINDOWS
516   if (proc->handle != NULL)
517     CloseHandle (proc->handle);
518 #endif
519   if (NULL != proc->childpipename)
520   {
521 #if !WINDOWS
522     cleanup_npipe (proc->childpipename);
523 #endif
524     GNUNET_free (proc->childpipename);
525   }
526   GNUNET_free (proc);
527 }
528
529 // FIXME NILS
530 #if WINDOWS
531 #include "gnunet_signal_lib.h"
532
533 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
534
535 /**
536  * Make seaspider happy.
537  */
538 #define DWORD_WINAPI DWORD WINAPI
539
540 /**
541  * @brief Waits for a process to terminate and invokes the SIGCHLD handler
542  * @param proc pointer to process structure
543  */
544 static DWORD_WINAPI
545 child_wait_thread (void *arg)
546 {
547   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
548
549   WaitForSingleObject (proc->handle, INFINITE);
550
551   if (w32_sigchld_handler)
552     w32_sigchld_handler ();
553
554   return 0;
555 }
556 #endif
557
558
559 /**
560  * Set process priority
561  *
562  * @param proc pointer to process structure
563  * @param prio priority value
564  * @return GNUNET_OK on success, GNUNET_SYSERR on error
565  */
566 int
567 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
568                                 enum GNUNET_SCHEDULER_Priority prio)
569 {
570   int rprio;
571
572   GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
573   if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
574     return GNUNET_OK;
575
576   /* convert to MINGW/Unix values */
577   switch (prio)
578   {
579   case GNUNET_SCHEDULER_PRIORITY_UI:
580   case GNUNET_SCHEDULER_PRIORITY_URGENT:
581 #ifdef MINGW
582     rprio = HIGH_PRIORITY_CLASS;
583 #else
584     rprio = 0;
585 #endif
586     break;
587
588   case GNUNET_SCHEDULER_PRIORITY_HIGH:
589 #ifdef MINGW
590     rprio = ABOVE_NORMAL_PRIORITY_CLASS;
591 #else
592     rprio = 5;
593 #endif
594     break;
595
596   case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
597 #ifdef MINGW
598     rprio = NORMAL_PRIORITY_CLASS;
599 #else
600     rprio = 7;
601 #endif
602     break;
603
604   case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
605 #ifdef MINGW
606     rprio = BELOW_NORMAL_PRIORITY_CLASS;
607 #else
608     rprio = 10;
609 #endif
610     break;
611
612   case GNUNET_SCHEDULER_PRIORITY_IDLE:
613 #ifdef MINGW
614     rprio = IDLE_PRIORITY_CLASS;
615 #else
616     rprio = 19;
617 #endif
618     break;
619   default:
620     GNUNET_assert (0);
621     return GNUNET_SYSERR;
622   }
623
624   /* Set process priority */
625 #ifdef MINGW
626   {
627     HANDLE h = proc->handle;
628
629     GNUNET_assert (h != NULL);
630     SetPriorityClass (h, rprio);
631   }
632 #elif LINUX
633   pid_t pid;
634
635   pid = proc->pid;
636   if ((0 == pid) || (pid == getpid ()))
637   {
638     int have = nice (0);
639     int delta = rprio - have;
640
641     errno = 0;
642     if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
643     {
644       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
645       return GNUNET_SYSERR;
646     }
647   }
648   else
649   {
650     if (0 != setpriority (PRIO_PROCESS, pid, rprio))
651     {
652       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
653                     "setpriority");
654       return GNUNET_SYSERR;
655     }
656   }
657 #else
658   LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
659        "Priority management not availabe for this platform\n");
660 #endif
661   return GNUNET_OK;
662 }
663
664
665 #if MINGW
666 static char *
667 CreateCustomEnvTable (char **vars)
668 {
669   char *win32_env_table;
670   char *ptr;
671   char **var_ptr;
672   char *result;
673   char *result_ptr;
674   size_t tablesize = 0;
675   size_t items_count = 0;
676   size_t n_found = 0;
677   size_t n_var;
678   char *index = NULL;
679   size_t c;
680   size_t var_len;
681   char *var;
682   char *val;
683
684   win32_env_table = GetEnvironmentStringsA ();
685   if (win32_env_table == NULL)
686     return NULL;
687   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
688   n_var = c;
689   index = GNUNET_malloc (sizeof (char *) * n_var);
690   for (c = 0; c < n_var; c++)
691     index[c] = 0;
692   for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
693   {
694     size_t len = strlen (ptr);
695     int found = 0;
696
697     for (var_ptr = vars; *var_ptr; var_ptr++)
698     {
699       var = *var_ptr++;
700       val = *var_ptr;
701       var_len = strlen (var);
702       if (strncmp (var, ptr, var_len) == 0)
703       {
704         found = 1;
705         index[c] = 1;
706         tablesize += var_len + strlen (val) + 1;
707         break;
708       }
709     }
710     if (!found)
711       tablesize += len + 1;
712     ptr += len + 1;
713   }
714   for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
715   {
716     var = *var_ptr++;
717     val = *var_ptr;
718     if (index[c] != 1)
719       n_found += strlen (var) + strlen (val) + 1;
720   }
721   result = GNUNET_malloc (tablesize + n_found + 1);
722   for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
723   {
724     size_t len = strlen (ptr);
725     int found = 0;
726
727     for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
728     {
729       var = *var_ptr++;
730       val = *var_ptr;
731       var_len = strlen (var);
732       if (strncmp (var, ptr, var_len) == 0)
733       {
734         found = 1;
735         break;
736       }
737     }
738     if (!found)
739     {
740       strcpy (result_ptr, ptr);
741       result_ptr += len + 1;
742     }
743     else
744     {
745       strcpy (result_ptr, var);
746       result_ptr += var_len;
747       strcpy (result_ptr, val);
748       result_ptr += strlen (val) + 1;
749     }
750     ptr += len + 1;
751   }
752   for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
753   {
754     var = *var_ptr++;
755     val = *var_ptr;
756     var_len = strlen (var);
757     if (index[c] != 1)
758     {
759       strcpy (result_ptr, var);
760       result_ptr += var_len;
761       strcpy (result_ptr, val);
762       result_ptr += strlen (val) + 1;
763     }
764   }
765   FreeEnvironmentStrings (win32_env_table);
766   GNUNET_free (index);
767   *result_ptr = 0;
768   return result;
769 }
770
771 #else
772
773 /**
774  * Open '/dev/null' and make the result the given
775  * file descriptor.
776  *
777  * @param target_fd desired FD to point to /dev/null
778  * @param flags open flags (O_RDONLY, O_WRONLY)
779  */
780 static void
781 open_dev_null (int target_fd,
782                int flags)
783 {
784   int fd;
785
786   fd = open ("/dev/null", flags);
787   if (-1 == fd)
788   {
789     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null");
790     return;
791   }
792   if (fd == target_fd)
793     return;
794   if (-1 == dup2 (fd, target_fd))
795   {
796     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
797     (void) close (fd);
798     return;
799   }
800   GNUNET_break (0 == close (fd));
801 }
802 #endif
803
804
805 /**
806  * Start a process.
807  *
808  * @param pipe_control should a pipe be used to send signals to the child?
809  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
810  *        std handles of the parent are inherited by the child.
811  *        pipe_stdin and pipe_stdout take priority over std_inheritance
812  *        (when they are non-NULL).
813  * @param pipe_stdin pipe to use to send input to child process (or NULL)
814  * @param pipe_stdout pipe to use to get output from child process (or NULL)
815  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
816  *         must be NULL on platforms where dup is not supported
817  * @param filename name of the binary
818  * @param argv NULL-terminated list of arguments to the process
819  * @return process ID of the new process, -1 on error
820  */
821 static struct GNUNET_OS_Process *
822 start_process (int pipe_control,
823                enum GNUNET_OS_InheritStdioFlags std_inheritance,
824                struct GNUNET_DISK_PipeHandle *pipe_stdin,
825                struct GNUNET_DISK_PipeHandle *pipe_stdout,
826                const SOCKTYPE *lsocks,
827                const char *filename,
828                char *const argv[])
829 {
830 #ifndef MINGW
831   pid_t ret;
832   char lpid[16];
833   char fds[16];
834   struct GNUNET_OS_Process *gnunet_proc;
835   char *childpipename = NULL;
836   int i;
837   int j;
838   int k;
839   int tgt;
840   int flags;
841   int *lscp;
842   unsigned int ls;
843   int fd_stdout_write;
844   int fd_stdout_read;
845   int fd_stdin_read;
846   int fd_stdin_write;
847
848   if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
849     return NULL; /* not executable */
850   if ( (GNUNET_YES == pipe_control) &&
851        (GNUNET_OK != npipe_setup (&childpipename)) )
852     return NULL;  
853   if (NULL != pipe_stdout)
854   {
855     GNUNET_assert (GNUNET_OK ==
856                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
857                                                       (pipe_stdout,
858                                                        GNUNET_DISK_PIPE_END_WRITE),
859                                                       &fd_stdout_write, sizeof (int)));
860     GNUNET_assert (GNUNET_OK ==
861                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
862                                                       (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
863                                                       &fd_stdout_read, sizeof (int)));
864   }
865   if (NULL != pipe_stdin)
866   {
867     GNUNET_assert (GNUNET_OK ==
868                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
869                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
870                                                       &fd_stdin_read, sizeof (int)));
871     GNUNET_assert (GNUNET_OK ==
872                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
873                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
874                                                       &fd_stdin_write, sizeof (int)));
875   }
876   lscp = NULL;
877   ls = 0;
878   if (NULL != lsocks)
879   {
880     i = 0;
881     while (-1 != (k = lsocks[i++]))
882       GNUNET_array_append (lscp, ls, k);
883     GNUNET_array_append (lscp, ls, -1);
884   }
885   ret = fork ();
886   if (-1 == ret)
887   {
888     int eno = errno;
889
890     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
891     GNUNET_free_non_null (childpipename);
892     GNUNET_array_grow (lscp, ls, 0);
893     errno = eno;
894     return NULL;
895   }
896   if (0 != ret)
897   {
898     gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
899     gnunet_proc->pid = ret;
900     gnunet_proc->childpipename = childpipename;  
901     GNUNET_array_grow (lscp, ls, 0);
902     return gnunet_proc;
903   }
904   if (NULL != childpipename)
905   {
906     setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
907     GNUNET_free (childpipename);
908   }
909   if (NULL != pipe_stdin)
910   {
911     GNUNET_break (0 == close (fd_stdin_write));
912     if (-1 == dup2 (fd_stdin_read, 0))
913       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
914     GNUNET_break (0 == close (fd_stdin_read));
915   }
916   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
917   {
918     GNUNET_break (0 == close (0));
919     open_dev_null (0, O_RDONLY);
920   }
921   if (NULL != pipe_stdout)
922   {
923     GNUNET_break (0 == close (fd_stdout_read));
924     if (-1 == dup2 (fd_stdout_write, 1))
925       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
926     GNUNET_break (0 == close (fd_stdout_write));
927   }
928   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
929   {
930     GNUNET_break (0 == close (1));
931     open_dev_null (1, O_WRONLY);
932   }
933   if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
934   {
935     GNUNET_break (0 == close (2));
936     open_dev_null (2, O_WRONLY);
937   }
938   if (NULL != lscp)
939   {
940     /* read systemd documentation... */
941     GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
942     setenv ("LISTEN_PID", lpid, 1);
943     i = 0;
944     tgt = 3;
945     while (-1 != lscp[i])
946     {
947       j = i + 1;
948       while (-1 != lscp[j])
949       {
950         if (lscp[j] == tgt)
951         {
952           /* dup away */
953           k = dup (lscp[j]);
954           GNUNET_assert (-1 != k);
955           GNUNET_assert (0 == close (lscp[j]));
956           lscp[j] = k;
957           break;
958         }
959         j++;
960       }
961       if (lscp[i] != tgt)
962       {
963         /* Bury any existing FD, no matter what; they should all be closed
964          * on exec anyway and the important onces have been dup'ed away */
965         (void) close (tgt);
966         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
967       }
968       /* unset close-on-exec flag */
969       flags = fcntl (tgt, F_GETFD);
970       GNUNET_assert (flags >= 0);
971       flags &= ~FD_CLOEXEC;
972       fflush (stderr);
973       (void) fcntl (tgt, F_SETFD, flags);
974       tgt++;
975       i++;
976     }
977     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
978     setenv ("LISTEN_FDS", fds, 1);
979   }
980   GNUNET_array_grow (lscp, ls, 0);
981   execvp (filename, argv);
982   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
983   _exit (1);
984 #else
985   struct GNUNET_DISK_FileHandle *control_pipe;
986   char *childpipename = NULL;
987   char **arg;
988   char **non_const_argv;
989   unsigned int cmdlen;
990   char *cmd;
991   char *idx;
992   STARTUPINFOW start;
993   PROCESS_INFORMATION proc;
994   int argcount = 0;
995   struct GNUNET_OS_Process *gnunet_proc;
996   char path[MAX_PATH + 1];
997   char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
998   char *env_block = NULL;
999   char *pathbuf;
1000   DWORD pathbuf_len;
1001   DWORD alloc_len;
1002   char *self_prefix;
1003   char *bindir;
1004   char *libdir;
1005   char *ptr;
1006   char *non_const_filename;
1007   char win_path[MAX_PATH + 1];
1008   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1009   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1010   HANDLE lsocks_read;
1011   HANDLE lsocks_write;
1012   wchar_t *wpath;
1013   wchar_t *wcmd;
1014   size_t wpath_len;
1015   size_t wcmd_len;
1016   int env_off;
1017   int fail;
1018   long lRet;
1019   HANDLE stdin_handle;
1020   HANDLE stdout_handle;
1021   HANDLE stdih, stdoh, stdeh;
1022   DWORD stdif, stdof, stdef;
1023   BOOL bresult;
1024   DWORD error_code;
1025
1026   if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
1027     return NULL; /* not executable */
1028  
1029   /* Search in prefix dir (hopefully - the directory from which
1030    * the current module was loaded), bindir and libdir, then in PATH
1031    */
1032   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1033   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1034   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1035
1036   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1037
1038   alloc_len =
1039       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1040       strlen (libdir);
1041
1042   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1043
1044   ptr = pathbuf;
1045   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1046   GNUNET_free (self_prefix);
1047   GNUNET_free (bindir);
1048   GNUNET_free (libdir);
1049
1050   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1051   if (alloc_len != pathbuf_len - 1)
1052   {
1053     GNUNET_free (pathbuf);
1054     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
1055     return NULL;
1056   }
1057
1058   cmdlen = strlen (filename);
1059   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1060     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1061   else
1062     GNUNET_asprintf (&non_const_filename, "%s", filename);
1063
1064   /* It could be in POSIX form, convert it to a DOS path early on */
1065   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1066   {
1067     SetErrnoFromWinError (lRet);
1068     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1069                        non_const_filename);
1070     GNUNET_free (non_const_filename);
1071     GNUNET_free (pathbuf);
1072     return NULL;
1073   }
1074   GNUNET_free (non_const_filename);
1075   non_const_filename = GNUNET_strdup (win_path);
1076    /* Check that this is the full path. If it isn't, search. */
1077   /* FIXME: convert it to wchar_t and use SearchPathW?
1078    * Remember: arguments to _start_process() are technically in UTF-8...
1079    */
1080   if (non_const_filename[1] == ':')
1081     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1082   else if (!SearchPathA
1083            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1084             path, NULL))
1085   {
1086     SetErrnoFromWinError (GetLastError ());
1087     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1088                        non_const_filename);
1089     GNUNET_free (non_const_filename);
1090     GNUNET_free (pathbuf);
1091     return NULL;
1092   }
1093   GNUNET_free (pathbuf);
1094   GNUNET_free (non_const_filename);
1095
1096   /* Count the number of arguments */
1097   arg = (char **) argv;
1098   while (*arg)
1099   {
1100     arg++;
1101     argcount++;
1102   }
1103
1104   /* Allocate a copy argv */
1105   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1106
1107   /* Copy all argv strings */
1108   argcount = 0;
1109   arg = (char **) argv;
1110   while (*arg)
1111   {
1112     if (arg == argv)
1113       non_const_argv[argcount] = GNUNET_strdup (path);
1114     else
1115       non_const_argv[argcount] = GNUNET_strdup (*arg);
1116     arg++;
1117     argcount++;
1118   }
1119   non_const_argv[argcount] = NULL;
1120
1121   /* Count cmd len */
1122   cmdlen = 1;
1123   arg = non_const_argv;
1124   while (*arg)
1125   {
1126     cmdlen = cmdlen + strlen (*arg) + 4;
1127     arg++;
1128   }
1129
1130   /* Allocate and create cmd */
1131   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1132   arg = non_const_argv;
1133   while (*arg)
1134   {
1135     char arg_last_char = (*arg)[strlen (*arg) - 1];
1136     idx += sprintf (idx, "\"%s%s\"%s", *arg,
1137         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1138     arg++;
1139   }
1140
1141   while (argcount > 0)
1142     GNUNET_free (non_const_argv[--argcount]);
1143   GNUNET_free (non_const_argv);
1144
1145   memset (&start, 0, sizeof (start));
1146   start.cb = sizeof (start);
1147   if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
1148     start.dwFlags |= STARTF_USESTDHANDLES;
1149
1150   stdih = GetStdHandle (STD_INPUT_HANDLE);
1151   GetHandleInformation (stdih, &stdif);
1152   if (pipe_stdin != NULL)
1153   {
1154     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1155                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1156                                        &stdin_handle, sizeof (HANDLE));
1157     start.hStdInput = stdin_handle;
1158   }
1159   if (stdih)
1160   {
1161     if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
1162     {
1163       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
1164       if (pipe_stdin == NULL)
1165         start.hStdInput = stdih;
1166     }
1167     else
1168       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
1169   }
1170     
1171
1172   stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
1173   GetHandleInformation (stdoh, &stdof);
1174   if (NULL != pipe_stdout)
1175   {
1176     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1177                                        (pipe_stdout,
1178                                         GNUNET_DISK_PIPE_END_WRITE),
1179                                        &stdout_handle, sizeof (HANDLE));
1180     start.hStdOutput = stdout_handle;
1181   }
1182   if (stdoh)
1183   {
1184     if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
1185     {
1186       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
1187       if (pipe_stdout == NULL)
1188         start.hStdOutput = stdoh;
1189     }
1190     else
1191       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1192   }
1193
1194   stdeh = GetStdHandle (STD_ERROR_HANDLE);
1195   GetHandleInformation (stdeh, &stdef);
1196   if (stdeh)
1197   {
1198     if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
1199     {
1200       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
1201       start.hStdError = stdeh;
1202     }
1203     else
1204       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1205   }
1206
1207   if (GNUNET_YES == pipe_control)
1208   {
1209     control_pipe =
1210       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1211                     GNUNET_DISK_PERM_USER_READ |
1212                     GNUNET_DISK_PERM_USER_WRITE);
1213     if (control_pipe == NULL)
1214     {
1215       GNUNET_free (cmd);
1216       GNUNET_free (path);
1217       return NULL;
1218     }
1219   }
1220   else
1221     control_pipe = NULL;
1222   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1223   {
1224     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1225
1226     if (lsocks_pipe == NULL)
1227     {
1228       GNUNET_free (cmd);
1229       GNUNET_free (path);
1230       GNUNET_DISK_pipe_close (lsocks_pipe);
1231       return NULL;
1232     }
1233     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1234         GNUNET_DISK_PIPE_END_WRITE);
1235     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1236                                        &lsocks_write, sizeof (HANDLE));
1237     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1238                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1239                                        &lsocks_read, sizeof (HANDLE));
1240   }
1241
1242   env_off = 0;
1243   if (NULL != childpipename)
1244   {
1245     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1246          childpipename);
1247     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1248     GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1249     GNUNET_free (childpipename);
1250   }
1251   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1252   {
1253     /*This will tell the child that we're going to send lsocks over the pipe*/
1254     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1255     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1256   }
1257   our_env[env_off++] = NULL;
1258   env_block = CreateCustomEnvTable (our_env);
1259   while (0 > env_off)
1260     GNUNET_free_non_null (our_env[--env_off]);
1261
1262   wpath_len = 0;
1263   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1264   {
1265     LOG (GNUNET_ERROR_TYPE_DEBUG,
1266         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1267     GNUNET_free (env_block);
1268     GNUNET_free (cmd);
1269     return NULL;
1270   }
1271
1272   wcmd_len = 0;
1273   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1274   {
1275     LOG (GNUNET_ERROR_TYPE_DEBUG,
1276         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1277     GNUNET_free (env_block);
1278     GNUNET_free (cmd);
1279     free (wpath);
1280     return NULL;
1281   }
1282
1283   bresult = CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1284        DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1285   error_code = GetLastError ();
1286
1287   if ((NULL == pipe_stdin) && (stdih))
1288     SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
1289     
1290
1291   if ((NULL == pipe_stdout) && (stdoh))
1292     SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
1293
1294   if (stdeh)
1295     SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
1296
1297   GNUNET_free (env_block);
1298   GNUNET_free (cmd);
1299   free (wpath);
1300   free (wcmd);
1301
1302   if (!bresult)
1303   {
1304     SetErrnoFromWinError (error_code);
1305     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1306     if (NULL != control_pipe)
1307       GNUNET_DISK_file_close (control_pipe);
1308     if (NULL != lsocks)
1309       GNUNET_DISK_pipe_close (lsocks_pipe);
1310     return NULL;
1311   }
1312
1313   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1314   gnunet_proc->pid = proc.dwProcessId;
1315   gnunet_proc->handle = proc.hProcess;
1316   gnunet_proc->control_pipe = control_pipe;
1317
1318   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1319
1320   ResumeThread (proc.hThread);
1321   CloseHandle (proc.hThread);
1322
1323   if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1324     return gnunet_proc;
1325
1326   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1327
1328   /* This is a replacement for "goto error" that doesn't use goto */
1329   fail = 1;
1330   do
1331   {
1332     int wrote;
1333     uint64_t size, count, i;
1334
1335     /* Tell the number of sockets */
1336     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1337
1338     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1339     if (wrote != sizeof (count))
1340     {
1341       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1342       break;
1343     }
1344     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1345     {
1346       WSAPROTOCOL_INFOA pi;
1347       /* Get a socket duplication info */
1348       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1349       {
1350         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1351         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1352         break;
1353       }
1354       /* Synchronous I/O is not nice, but we can't schedule this:
1355        * lsocks will be closed/freed by the caller soon, and until
1356        * the child creates a duplicate, closing a socket here will
1357        * close it for good.
1358        */
1359       /* Send the size of the structure
1360        * (the child might be built with different headers...)
1361        */
1362       size = sizeof (pi);
1363       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1364       if (wrote != sizeof (size))
1365       {
1366         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1367         break;
1368       }
1369       /* Finally! Send the data */
1370       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1371       if (wrote != sizeof (pi))
1372       {
1373         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1374         break;
1375       }
1376     }
1377     /* This will block us until the child makes a final read or closes
1378      * the pipe (hence no 'wrote' check), since we have to wait for it
1379      * to duplicate the last socket, before we return and start closing
1380      * our own copies)
1381      */
1382     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1383     fail = 0;
1384   }
1385   while (fail);
1386
1387   GNUNET_DISK_file_sync (lsocks_write_fd);
1388   GNUNET_DISK_pipe_close (lsocks_pipe);
1389
1390   if (fail)
1391   {
1392     /* If we can't pass on the socket(s), the child will block forever,
1393      * better put it out of its misery.
1394      */
1395     SafeTerminateProcess (gnunet_proc->handle, 0, 0);
1396     CloseHandle (gnunet_proc->handle);
1397     if (NULL != gnunet_proc->control_pipe)
1398       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1399     GNUNET_free (gnunet_proc);
1400     return NULL;
1401   }
1402   return gnunet_proc;
1403 #endif
1404 }
1405
1406
1407
1408
1409 /**
1410  * Start a process.
1411  *
1412  * @param pipe_control should a pipe be used to send signals to the child?
1413  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1414  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1415  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1416  * @param filename name of the binary
1417  * @param argv NULL-terminated array of arguments to the process
1418  * @return pointer to process structure of the new process, NULL on error
1419  */
1420 struct GNUNET_OS_Process *
1421 GNUNET_OS_start_process_vap (int pipe_control,
1422                              enum GNUNET_OS_InheritStdioFlags std_inheritance,
1423                              struct GNUNET_DISK_PipeHandle *pipe_stdin,
1424                              struct GNUNET_DISK_PipeHandle *pipe_stdout,
1425                              const char *filename, 
1426                              char *const argv[])
1427 {
1428   return start_process (pipe_control,
1429                         std_inheritance,
1430                         pipe_stdin,
1431                         pipe_stdout,
1432                         NULL,
1433                         filename,
1434                         argv);
1435 }
1436
1437
1438 /**
1439  * Start a process.
1440  *
1441  * @param pipe_control should a pipe be used to send signals to the child?
1442  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1443  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1444  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1445  * @param filename name of the binary
1446  * @param va NULL-terminated list of arguments to the process
1447  * @return pointer to process structure of the new process, NULL on error
1448  */
1449 struct GNUNET_OS_Process *
1450 GNUNET_OS_start_process_va (int pipe_control,
1451                             enum GNUNET_OS_InheritStdioFlags std_inheritance,
1452                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1453                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1454                             const char *filename, va_list va)
1455 {
1456   struct GNUNET_OS_Process *ret;
1457   va_list ap;
1458   char **argv;
1459   int argc;
1460
1461   argc = 0;
1462   va_copy (ap, va);
1463   while (NULL != va_arg (ap, char *))
1464     argc++;
1465   va_end (ap);
1466   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1467   argc = 0;
1468   va_copy (ap, va);
1469   while (NULL != (argv[argc] = va_arg (ap, char *)))
1470     argc++;
1471   va_end (ap);
1472   ret = GNUNET_OS_start_process_vap (pipe_control,
1473                                      std_inheritance,
1474                                      pipe_stdin,
1475                                      pipe_stdout,
1476                                      filename,
1477                                      argv);
1478   GNUNET_free (argv);
1479   return ret;
1480 }
1481
1482
1483
1484 /**
1485  * Start a process.
1486  *
1487  * @param pipe_control should a pipe be used to send signals to the child?
1488  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1489  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1490  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1491  * @param filename name of the binary
1492  * @param ... NULL-terminated list of arguments to the process
1493  *
1494  * @return pointer to process structure of the new process, NULL on error
1495  *
1496  */
1497 struct GNUNET_OS_Process *
1498 GNUNET_OS_start_process (int pipe_control,
1499                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
1500                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1501                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1502                          const char *filename, ...)
1503 {
1504   struct GNUNET_OS_Process *ret;
1505   va_list ap;
1506
1507   va_start (ap, filename);
1508   ret = GNUNET_OS_start_process_va (pipe_control, std_inheritance, pipe_stdin,
1509       pipe_stdout, filename, ap);
1510   va_end (ap);
1511   return ret;
1512 }
1513
1514
1515 /**
1516  * Start a process.
1517  *
1518  * @param pipe_control should a pipe be used to send signals to the child?
1519  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1520  *         must be NULL on platforms where dup is not supported
1521  * @param filename name of the binary
1522  * @param argv NULL-terminated list of arguments to the process
1523  * @return process ID of the new process, -1 on error
1524  */
1525 struct GNUNET_OS_Process *
1526 GNUNET_OS_start_process_v (int pipe_control,
1527                            enum GNUNET_OS_InheritStdioFlags std_inheritance,
1528                            const SOCKTYPE *lsocks,
1529                            const char *filename,
1530                            char *const argv[])
1531 {
1532   return start_process (pipe_control,
1533                         std_inheritance,
1534                         NULL,
1535                         NULL,
1536                         lsocks,
1537                         filename,
1538                         argv);
1539 }
1540
1541
1542 /**
1543  * Retrieve the status of a process, waiting on him if dead.
1544  * Nonblocking version.
1545  * 
1546  * @param proc process ID
1547  * @param type status type
1548  * @param code return code/signal number
1549  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1550  */
1551 int
1552 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1553                           enum GNUNET_OS_ProcessStatusType *type,
1554                           unsigned long *code)
1555 {
1556 #ifndef MINGW
1557   int status;
1558   int ret;
1559
1560   GNUNET_assert (0 != proc);
1561   ret = waitpid (proc->pid, &status, WNOHANG);
1562   if (ret < 0)
1563   {
1564     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1565     return GNUNET_SYSERR;
1566   }
1567   if (0 == ret)
1568   {
1569     *type = GNUNET_OS_PROCESS_RUNNING;
1570     *code = 0;
1571     return GNUNET_NO;
1572   }
1573   if (proc->pid != ret)
1574   {
1575     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1576     return GNUNET_SYSERR;
1577   }
1578   if (WIFEXITED (status))
1579   {
1580     *type = GNUNET_OS_PROCESS_EXITED;
1581     *code = WEXITSTATUS (status);
1582   }
1583   else if (WIFSIGNALED (status))
1584   {
1585     *type = GNUNET_OS_PROCESS_SIGNALED;
1586     *code = WTERMSIG (status);
1587   }
1588   else if (WIFSTOPPED (status))
1589   {
1590     *type = GNUNET_OS_PROCESS_SIGNALED;
1591     *code = WSTOPSIG (status);
1592   }
1593 #ifdef WIFCONTINUED
1594   else if (WIFCONTINUED (status))
1595   {
1596     *type = GNUNET_OS_PROCESS_RUNNING;
1597     *code = 0;
1598   }
1599 #endif
1600   else
1601   {
1602     *type = GNUNET_OS_PROCESS_UNKNOWN;
1603     *code = 0;
1604   }
1605 #else
1606   HANDLE h;
1607   DWORD c, error_code, ret;
1608
1609   h = proc->handle;
1610   ret = proc->pid;
1611   if (h == NULL || ret == 0)
1612   {
1613     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1614          ret, h);
1615     return GNUNET_SYSERR;
1616   }
1617   if (h == NULL)
1618     h = GetCurrentProcess ();
1619
1620   SetLastError (0);
1621   ret = GetExitCodeProcess (h, &c);
1622   error_code = GetLastError ();
1623   if (ret == 0 || error_code != NO_ERROR)
1624   {
1625     SetErrnoFromWinError (error_code);
1626     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1627     return GNUNET_SYSERR;
1628   }
1629   if (STILL_ACTIVE == c)
1630   {
1631     *type = GNUNET_OS_PROCESS_RUNNING;
1632     *code = 0;
1633     return GNUNET_NO;
1634   }
1635   *type = GNUNET_OS_PROCESS_EXITED;
1636   *code = c;
1637 #endif
1638
1639   return GNUNET_OK;
1640 }
1641
1642
1643 /**
1644  * Wait for a process
1645  * @param proc pointer to process structure
1646  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1647  */
1648 int
1649 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1650 {
1651
1652 #ifndef MINGW
1653   pid_t pid = proc->pid;
1654   pid_t ret;
1655
1656   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1657           (EINTR == errno) ) ;
1658   if (pid != ret) 
1659   {
1660     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1661     return GNUNET_SYSERR;
1662   }
1663   return GNUNET_OK;
1664 #else
1665   HANDLE h;
1666   int ret;
1667
1668   h = proc->handle;
1669   if (NULL == h)
1670   {
1671     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1672          proc->pid, h);
1673     return GNUNET_SYSERR;
1674   }
1675   if (h == NULL)
1676     h = GetCurrentProcess ();
1677
1678   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1679   {
1680     SetErrnoFromWinError (GetLastError ());
1681     ret = GNUNET_SYSERR;
1682   }
1683   else
1684     ret = GNUNET_OK;
1685
1686   return ret;
1687 #endif
1688 }
1689
1690
1691 /**
1692  * Handle to a command.
1693  */
1694 struct GNUNET_OS_CommandHandle
1695 {
1696
1697   /**
1698    * Process handle.
1699    */
1700   struct GNUNET_OS_Process *eip;
1701
1702   /**
1703    * Handle to the output pipe.
1704    */
1705   struct GNUNET_DISK_PipeHandle *opipe;
1706
1707   /**
1708    * Read-end of output pipe.
1709    */
1710   const struct GNUNET_DISK_FileHandle *r;
1711
1712   /**
1713    * Function to call on each line of output.
1714    */
1715   GNUNET_OS_LineProcessor proc;
1716
1717   /**
1718    * Closure for 'proc'.
1719    */
1720   void *proc_cls;
1721
1722   /**
1723    * Buffer for the output.
1724    */
1725   char buf[1024];
1726
1727   /**
1728    * Task reading from pipe.
1729    */
1730   GNUNET_SCHEDULER_TaskIdentifier rtask;
1731
1732   /**
1733    * When to time out.
1734    */
1735   struct GNUNET_TIME_Absolute timeout;
1736
1737   /**
1738    * Current read offset in buf.
1739    */
1740   size_t off;
1741 };
1742
1743
1744 /**
1745  * Stop/kill a command.  Must ONLY be called either from
1746  * the callback after 'NULL' was passed for 'line' *OR*
1747  * from an independent task (not within the line processor).
1748  *
1749  * @param cmd handle to the process
1750  */
1751 void
1752 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1753 {
1754
1755   if (cmd->proc != NULL)
1756   {
1757     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1758     GNUNET_SCHEDULER_cancel (cmd->rtask);
1759   }
1760   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1761   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1762   GNUNET_OS_process_destroy (cmd->eip);
1763   GNUNET_DISK_pipe_close (cmd->opipe);
1764   GNUNET_free (cmd);
1765 }
1766
1767
1768 /**
1769  * Read from the process and call the line processor.
1770  *
1771  * @param cls the 'struct GNUNET_OS_CommandHandle'
1772  * @param tc scheduler context
1773  */
1774 static void
1775 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1776 {
1777   struct GNUNET_OS_CommandHandle *cmd = cls;
1778   GNUNET_OS_LineProcessor proc;
1779   char *end;
1780   ssize_t ret;
1781
1782   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1783   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1784   {
1785     /* timeout, shutdown, etc. */
1786     proc = cmd->proc;
1787     cmd->proc = NULL;
1788     proc (cmd->proc_cls, NULL);
1789     return;
1790   }
1791   ret =
1792       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1793                              sizeof (cmd->buf) - cmd->off);
1794   if (ret <= 0)
1795   {
1796     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1797     {
1798       cmd->buf[cmd->off] = '\0';
1799       cmd->proc (cmd->proc_cls, cmd->buf);
1800     }
1801     proc = cmd->proc;
1802     cmd->proc = NULL;
1803     proc (cmd->proc_cls, NULL);
1804     return;
1805   }
1806   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1807   cmd->off += ret;
1808   while (end != NULL)
1809   {
1810     *end = '\0';
1811     cmd->proc (cmd->proc_cls, cmd->buf);
1812     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1813     cmd->off -= (end + 1 - cmd->buf);
1814     end = memchr (cmd->buf, '\n', cmd->off);
1815   }
1816   cmd->rtask =
1817       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1818                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1819 }
1820
1821
1822 /**
1823  * Run the given command line and call the given function
1824  * for each line of the output.
1825  *
1826  * @param proc function to call for each line of the output
1827  * @param proc_cls closure for proc
1828  * @param timeout when to time out
1829  * @param binary command to run
1830  * @param ... arguments to command
1831  * @return NULL on error
1832  */
1833 struct GNUNET_OS_CommandHandle *
1834 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1835                        struct GNUNET_TIME_Relative timeout, const char *binary,
1836                        ...)
1837 {
1838   struct GNUNET_OS_CommandHandle *cmd;
1839   struct GNUNET_OS_Process *eip;
1840   struct GNUNET_DISK_PipeHandle *opipe;
1841   va_list ap;
1842
1843   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1844   if (NULL == opipe)
1845     return NULL;
1846   va_start (ap, binary);
1847   /* redirect stdout, don't inherit stderr/stdin */
1848   eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, binary, ap);
1849   va_end (ap);
1850   if (NULL == eip)
1851   {
1852     GNUNET_DISK_pipe_close (opipe);
1853     return NULL;
1854   }
1855   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1856   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1857   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1858   cmd->eip = eip;
1859   cmd->opipe = opipe;
1860   cmd->proc = proc;
1861   cmd->proc_cls = proc_cls;
1862   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1863   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1864   return cmd;
1865 }
1866
1867
1868
1869
1870 /* end of os_priority.c */