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