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