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