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