fefb6a98ccb4aa9961d2e8916e792d4986d38d55
[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     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
815     GNUNET_free_non_null (childpipename);
816     return NULL;
817   }
818   if (0 != ret)
819   {
820     gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
821     gnunet_proc->pid = ret;
822     gnunet_proc->childpipename = childpipename;
823     return gnunet_proc;
824   }
825   if (NULL != childpipename)
826   {
827     setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
828     GNUNET_free (childpipename);
829   }
830   if (pipe_stdout != NULL)
831   {
832     GNUNET_break (0 == close (fd_stdout_read));
833     if (-1 == dup2 (fd_stdout_write, 1))
834       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
835     GNUNET_break (0 == close (fd_stdout_write));
836   }
837
838   if (pipe_stdin != NULL)
839   {
840
841     GNUNET_break (0 == close (fd_stdin_write));
842     if (-1 == dup2 (fd_stdin_read, 0))
843       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
844     GNUNET_break (0 == close (fd_stdin_read));
845   }
846   execvp (filename, argv);
847   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
848   _exit (1);
849 #else
850   char *childpipename = NULL;
851   struct GNUNET_OS_Process *gnunet_proc = NULL;
852   char *arg;
853   unsigned int cmdlen;
854   char *cmd, *idx;
855   STARTUPINFOW start;
856   PROCESS_INFORMATION proc;
857   int argc, arg_count;
858   HANDLE stdin_handle;
859   HANDLE stdout_handle;
860   struct GNUNET_DISK_FileHandle *control_pipe;
861
862   char path[MAX_PATH + 1];
863
864   char *our_env[3] = { NULL, NULL, NULL };
865   char *env_block = NULL;
866   char *pathbuf;
867   DWORD pathbuf_len, alloc_len;
868   char *self_prefix;
869   char *bindir;
870   char *libdir;
871   char *ptr;
872   char *non_const_filename;
873   char win_path[MAX_PATH + 1];
874   wchar_t *wpath, *wcmd;
875   size_t wpath_len, wcmd_len;
876   long lRet;
877
878   /* Search in prefix dir (hopefully - the directory from which
879    * the current module was loaded), bindir and libdir, then in PATH
880    */
881   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
882   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
883   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
884
885   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
886
887   alloc_len =
888       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
889       strlen (libdir);
890
891   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
892
893   ptr = pathbuf;
894   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
895   GNUNET_free (self_prefix);
896   GNUNET_free (bindir);
897   GNUNET_free (libdir);
898
899   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
900   GNUNET_assert (alloc_len == (pathbuf_len - 1));
901
902   cmdlen = strlen (filename);
903   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
904     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
905   else
906     GNUNET_asprintf (&non_const_filename, "%s", filename);
907
908   /* It could be in POSIX form, convert it to a DOS path early on */
909   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
910   {
911      SetErrnoFromWinError (lRet);
912      LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
913                         non_const_filename);
914      GNUNET_free (non_const_filename);
915      GNUNET_free (pathbuf);
916      return NULL;
917   }
918   GNUNET_free (non_const_filename);
919   non_const_filename = GNUNET_strdup (win_path);
920   /* Check that this is the full path. If it isn't, search. */
921   /* FIXME: convert it to wchar_t and use SearchPathW?
922    * Remember: arguments to _start_process() are technically in UTF-8...
923    */
924   if (non_const_filename[1] == ':')
925     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
926   else if (!SearchPathA
927            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
928             path, NULL))
929   {
930     SetErrnoFromWinError (GetLastError ());
931     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
932                        non_const_filename);
933     GNUNET_free (non_const_filename);
934     GNUNET_free (pathbuf);
935     return NULL;
936   }
937   GNUNET_free (pathbuf);
938   GNUNET_free (non_const_filename);
939
940   cmdlen = 0;
941   argc = 0;
942   while (NULL != (arg = argv[argc++]))
943   {
944     if (cmdlen == 0)
945       cmdlen = cmdlen + strlen (path) + 4;
946     else
947       cmdlen = cmdlen + strlen (arg) + 4;
948   }
949   arg_count = argc;
950
951   cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
952   argc = 0;
953   while (NULL != (arg = argv[argc++]))
954   {
955     /* This is to escape trailing slash */
956     char arg_lastchar = arg[strlen (arg) - 1];
957     if (idx == cmd)
958       idx += sprintf (idx, "\"%s%s\"%s", path,
959           arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
960     else
961       idx += sprintf (idx, "\"%s%s\"%s", arg,
962           arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
963   }
964
965   memset (&start, 0, sizeof (start));
966   start.cb = sizeof (start);
967
968   if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
969     start.dwFlags |= STARTF_USESTDHANDLES;
970
971   if (pipe_stdin != NULL)
972   {
973     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
974                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
975                                        &stdin_handle, sizeof (HANDLE));
976     start.hStdInput = stdin_handle;
977   }
978
979   if (pipe_stdout != NULL)
980   {
981     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
982                                        (pipe_stdout,
983                                         GNUNET_DISK_PIPE_END_WRITE),
984                                        &stdout_handle, sizeof (HANDLE));
985     start.hStdOutput = stdout_handle;
986   }
987   if (GNUNET_YES == pipe_control)
988   {
989     control_pipe =
990       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
991                     GNUNET_DISK_PERM_USER_READ |
992                     GNUNET_DISK_PERM_USER_WRITE);
993     if (control_pipe == NULL)
994     {
995       GNUNET_free (cmd);
996       GNUNET_free (path);
997       return NULL;
998     }
999   }
1000   else
1001     control_pipe = NULL;
1002   if (NULL != childpipename)
1003   {
1004     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1005          childpipename);
1006     GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1007     GNUNET_asprintf (&our_env[1], "%s", childpipename);
1008     our_env[2] = NULL;
1009   }
1010   else
1011   {
1012     our_env[0] = NULL;
1013   }
1014   env_block = CreateCustomEnvTable (our_env);
1015   GNUNET_free_non_null (our_env[0]);
1016   GNUNET_free_non_null (our_env[1]);
1017
1018   wpath_len = 0;
1019   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1020   {
1021     LOG (GNUNET_ERROR_TYPE_DEBUG,
1022         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1023     GNUNET_free (env_block);
1024     GNUNET_free (cmd);
1025     return NULL;
1026   }
1027
1028   wcmd_len = 0;
1029   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1030   {
1031     LOG (GNUNET_ERROR_TYPE_DEBUG,
1032         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1033     GNUNET_free (env_block);
1034     GNUNET_free (cmd);
1035     free (wpath);
1036     return NULL;
1037   }
1038
1039   if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1040       DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1041   {
1042     SetErrnoFromWinError (GetLastError ());
1043     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1044     GNUNET_free (env_block);
1045     GNUNET_free (cmd);
1046     free (wpath);
1047     free (wcmd);
1048     return NULL;
1049   }
1050
1051   GNUNET_free (env_block);
1052
1053   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1054   gnunet_proc->pid = proc.dwProcessId;
1055   gnunet_proc->handle = proc.hProcess;
1056   gnunet_proc->control_pipe = control_pipe;
1057
1058   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1059
1060   ResumeThread (proc.hThread);
1061   CloseHandle (proc.hThread);
1062
1063   GNUNET_free (cmd);
1064   free (wpath);
1065   free (wcmd);
1066   return gnunet_proc;
1067 #endif
1068 }
1069
1070
1071 /**
1072  * Start a process.
1073  *
1074  * @param pipe_control should a pipe be used to send signals to the child?
1075  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1076  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1077  * @param filename name of the binary
1078  * @param va NULL-terminated list of arguments to the process
1079  * @return pointer to process structure of the new process, NULL on error
1080  */
1081 struct GNUNET_OS_Process *
1082 GNUNET_OS_start_process_va (int pipe_control,
1083                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1084                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1085                             const char *filename, va_list va)
1086 {
1087   struct GNUNET_OS_Process *ret;
1088   va_list ap;
1089   char **argv;
1090   int argc;
1091
1092   argc = 0;
1093   va_copy (ap, va);
1094   while (NULL != va_arg (ap, char *))
1095     argc++;
1096   va_end (ap);
1097   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1098   argc = 0;
1099   va_copy (ap, va);
1100   while (NULL != (argv[argc] = va_arg (ap, char *)))
1101     argc++;
1102   va_end (ap);
1103   ret = GNUNET_OS_start_process_vap (pipe_control,
1104                                      pipe_stdin,
1105                                      pipe_stdout,
1106                                      filename,
1107                                      argv);
1108   GNUNET_free (argv);
1109   return ret;
1110 }
1111
1112
1113
1114 /**
1115  * Start a process.
1116  *
1117  * @param pipe_control should a pipe be used to send signals to the child?
1118  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1119  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1120  * @param filename name of the binary
1121  * @param ... NULL-terminated list of arguments to the process
1122  *
1123  * @return pointer to process structure of the new process, NULL on error
1124  *
1125  */
1126 struct GNUNET_OS_Process *
1127 GNUNET_OS_start_process (int pipe_control,
1128                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1129                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1130                          const char *filename, ...)
1131 {
1132   struct GNUNET_OS_Process *ret;
1133   va_list ap;
1134
1135   va_start (ap, filename);
1136   ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1137   va_end (ap);
1138   return ret;
1139 }
1140
1141
1142 /**
1143  * Start a process.
1144  *
1145  * @param pipe_control should a pipe be used to send signals to the child?
1146  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1147  *         must be NULL on platforms where dup is not supported
1148  * @param filename name of the binary
1149  * @param argv NULL-terminated list of arguments to the process
1150  * @return process ID of the new process, -1 on error
1151  */
1152 struct GNUNET_OS_Process *
1153 GNUNET_OS_start_process_v (int pipe_control,
1154                            const SOCKTYPE *lsocks,
1155                            const char *filename,
1156                            char *const argv[])
1157 {
1158 #ifndef MINGW
1159   pid_t ret;
1160   char lpid[16];
1161   char fds[16];
1162   struct GNUNET_OS_Process *gnunet_proc = NULL;
1163   char *childpipename = NULL;
1164   int i;
1165   int j;
1166   int k;
1167   int tgt;
1168   int flags;
1169   int *lscp;
1170   unsigned int ls;
1171
1172   if ( (GNUNET_YES == pipe_control) &&
1173        (GNUNET_OK != npipe_setup (&childpipename)) )
1174     return NULL;  
1175   lscp = NULL;
1176   ls = 0;
1177   if (lsocks != NULL)
1178   {
1179     i = 0;
1180     while (-1 != (k = lsocks[i++]))
1181       GNUNET_array_append (lscp, ls, k);
1182     GNUNET_array_append (lscp, ls, -1);
1183   }
1184   ret = fork ();
1185   if (-1 == ret)
1186   {
1187     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1188     GNUNET_free_non_null (childpipename);
1189     GNUNET_array_grow (lscp, ls, 0);
1190     return NULL;
1191   }
1192   if (0 != ret)
1193   {
1194     gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1195     gnunet_proc->pid = ret;
1196     gnunet_proc->childpipename = childpipename;  
1197     GNUNET_array_grow (lscp, ls, 0);
1198     return gnunet_proc;
1199   }
1200   if (NULL != childpipename)
1201   {
1202     setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1203     GNUNET_free (childpipename);
1204   }
1205   if (lscp != NULL)
1206   {
1207     /* read systemd documentation... */
1208     GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1209     setenv ("LISTEN_PID", lpid, 1);
1210     i = 0;
1211     tgt = 3;
1212     while (-1 != lscp[i])
1213     {
1214       j = i + 1;
1215       while (-1 != lscp[j])
1216       {
1217         if (lscp[j] == tgt)
1218         {
1219           /* dup away */
1220           k = dup (lscp[j]);
1221           GNUNET_assert (-1 != k);
1222           GNUNET_assert (0 == close (lscp[j]));
1223           lscp[j] = k;
1224           break;
1225         }
1226         j++;
1227       }
1228       if (lscp[i] != tgt)
1229       {
1230         /* Bury any existing FD, no matter what; they should all be closed
1231          * on exec anyway and the important onces have been dup'ed away */
1232         (void) close (tgt);
1233         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1234       }
1235       /* unset close-on-exec flag */
1236       flags = fcntl (tgt, F_GETFD);
1237       GNUNET_assert (flags >= 0);
1238       flags &= ~FD_CLOEXEC;
1239       fflush (stderr);
1240       (void) fcntl (tgt, F_SETFD, flags);
1241       tgt++;
1242       i++;
1243     }
1244     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1245     setenv ("LISTEN_FDS", fds, 1);
1246   }
1247   GNUNET_array_grow (lscp, ls, 0);
1248   execvp (filename, argv);
1249   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1250   _exit (1);
1251 #else
1252   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1253   char *childpipename = NULL;
1254   char **arg, **non_const_argv;
1255   unsigned int cmdlen;
1256   char *cmd, *idx;
1257   STARTUPINFOW start;
1258   PROCESS_INFORMATION proc;
1259   int argcount = 0;
1260   struct GNUNET_OS_Process *gnunet_proc = NULL;
1261   char path[MAX_PATH + 1];
1262   char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1263   char *env_block = NULL;
1264   char *pathbuf;
1265   DWORD pathbuf_len, alloc_len;
1266   char *self_prefix;
1267   char *bindir;
1268   char *libdir;
1269   char *ptr;
1270   char *non_const_filename;
1271   char win_path[MAX_PATH + 1];
1272   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1273   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1274   HANDLE lsocks_read;
1275   HANDLE lsocks_write;
1276   wchar_t *wpath, *wcmd;
1277   size_t wpath_len, wcmd_len;
1278   int env_off;
1279   int fail;
1280   long lRet;
1281
1282   /* Search in prefix dir (hopefully - the directory from which
1283    * the current module was loaded), bindir and libdir, then in PATH
1284    */
1285   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1286   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1287   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1288
1289   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1290
1291   alloc_len =
1292       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1293       strlen (libdir);
1294
1295   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1296
1297   ptr = pathbuf;
1298   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1299   GNUNET_free (self_prefix);
1300   GNUNET_free (bindir);
1301   GNUNET_free (libdir);
1302
1303   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1304   if (alloc_len != pathbuf_len - 1)
1305   {
1306     GNUNET_free (pathbuf);
1307     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
1308     return NULL;
1309   }
1310
1311   cmdlen = strlen (filename);
1312   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1313     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1314   else
1315     GNUNET_asprintf (&non_const_filename, "%s", filename);
1316
1317   /* It could be in POSIX form, convert it to a DOS path early on */
1318   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1319   {
1320     SetErrnoFromWinError (lRet);
1321     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1322                        non_const_filename);
1323     GNUNET_free (non_const_filename);
1324     GNUNET_free (pathbuf);
1325     return NULL;
1326   }
1327   GNUNET_free (non_const_filename);
1328   non_const_filename = GNUNET_strdup (win_path);
1329    /* Check that this is the full path. If it isn't, search. */
1330   /* FIXME: convert it to wchar_t and use SearchPathW?
1331    * Remember: arguments to _start_process() are technically in UTF-8...
1332    */
1333   if (non_const_filename[1] == ':')
1334     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1335   else if (!SearchPathA
1336            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1337             path, NULL))
1338   {
1339     SetErrnoFromWinError (GetLastError ());
1340     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1341                        non_const_filename);
1342     GNUNET_free (non_const_filename);
1343     GNUNET_free (pathbuf);
1344     return NULL;
1345   }
1346   GNUNET_free (pathbuf);
1347   GNUNET_free (non_const_filename);
1348
1349   /* Count the number of arguments */
1350   arg = (char **) argv;
1351   while (*arg)
1352   {
1353     arg++;
1354     argcount++;
1355   }
1356
1357   /* Allocate a copy argv */
1358   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1359
1360   /* Copy all argv strings */
1361   argcount = 0;
1362   arg = (char **) argv;
1363   while (*arg)
1364   {
1365     if (arg == argv)
1366       non_const_argv[argcount] = GNUNET_strdup (path);
1367     else
1368       non_const_argv[argcount] = GNUNET_strdup (*arg);
1369     arg++;
1370     argcount++;
1371   }
1372   non_const_argv[argcount] = NULL;
1373
1374   /* Count cmd len */
1375   cmdlen = 1;
1376   arg = non_const_argv;
1377   while (*arg)
1378   {
1379     cmdlen = cmdlen + strlen (*arg) + 4;
1380     arg++;
1381   }
1382
1383   /* Allocate and create cmd */
1384   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1385   arg = non_const_argv;
1386   while (*arg)
1387   {
1388     char arg_last_char = (*arg)[strlen (*arg) - 1];
1389     idx += sprintf (idx, "\"%s%s\"%s", *arg,
1390         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1391     arg++;
1392   }
1393
1394   while (argcount > 0)
1395     GNUNET_free (non_const_argv[--argcount]);
1396   GNUNET_free (non_const_argv);
1397
1398   memset (&start, 0, sizeof (start));
1399   start.cb = sizeof (start);
1400
1401   if (GNUNET_YES == pipe_control)
1402   {
1403     control_pipe =
1404       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1405                     GNUNET_DISK_PERM_USER_READ |
1406                     GNUNET_DISK_PERM_USER_WRITE);
1407     if (control_pipe == NULL)
1408     {
1409       GNUNET_free (cmd);
1410       GNUNET_free (path);
1411       return NULL;
1412     }
1413   }
1414   else
1415     control_pipe = NULL;
1416   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1417   {
1418     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1419
1420     if (lsocks_pipe == NULL)
1421     {
1422       GNUNET_free (cmd);
1423       GNUNET_free (path);
1424       GNUNET_DISK_pipe_close (lsocks_pipe);
1425       return NULL;
1426     }
1427     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1428         GNUNET_DISK_PIPE_END_WRITE);
1429     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1430                                        &lsocks_write, sizeof (HANDLE));
1431     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1432                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1433                                        &lsocks_read, sizeof (HANDLE));
1434   }
1435
1436   env_off = 0;
1437   if (NULL != childpipename)
1438   {
1439     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1440          childpipename);
1441     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1442     GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1443     GNUNET_free (childpipename);
1444   }
1445   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1446   {
1447     /*This will tell the child that we're going to send lsocks over the pipe*/
1448     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1449     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1450   }
1451   our_env[env_off++] = NULL;
1452   env_block = CreateCustomEnvTable (our_env);
1453   while (0 > env_off)
1454     GNUNET_free_non_null (our_env[--env_off]);
1455
1456   wpath_len = 0;
1457   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1458   {
1459     LOG (GNUNET_ERROR_TYPE_DEBUG,
1460         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1461     GNUNET_free (env_block);
1462     GNUNET_free (cmd);
1463     return NULL;
1464   }
1465
1466   wcmd_len = 0;
1467   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1468   {
1469     LOG (GNUNET_ERROR_TYPE_DEBUG,
1470         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1471     GNUNET_free (env_block);
1472     GNUNET_free (cmd);
1473     free (wpath);
1474     return NULL;
1475   }
1476
1477   if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1478        DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1479   {
1480     SetErrnoFromWinError (GetLastError ());
1481     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1482     if (NULL != control_pipe)
1483       GNUNET_DISK_file_close (control_pipe);
1484     if (NULL != lsocks)
1485       GNUNET_DISK_pipe_close (lsocks_pipe);
1486     GNUNET_free (env_block);
1487     GNUNET_free (cmd);
1488     free (wpath);
1489     free (wcmd);
1490     return NULL;
1491   }
1492
1493   GNUNET_free (env_block);
1494
1495   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1496   gnunet_proc->pid = proc.dwProcessId;
1497   gnunet_proc->handle = proc.hProcess;
1498   gnunet_proc->control_pipe = control_pipe;
1499
1500   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1501
1502   ResumeThread (proc.hThread);
1503   CloseHandle (proc.hThread);
1504   GNUNET_free (cmd);
1505   free (wpath);
1506   free (wcmd);
1507
1508   if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1509     return gnunet_proc;
1510
1511   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1512
1513   /* This is a replacement for "goto error" that doesn't use goto */
1514   fail = 1;
1515   do
1516   {
1517     int wrote;
1518     uint64_t size, count, i;
1519
1520     /* Tell the number of sockets */
1521     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1522
1523     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1524     if (wrote != sizeof (count))
1525     {
1526       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1527       break;
1528     }
1529     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1530     {
1531       WSAPROTOCOL_INFOA pi;
1532       /* Get a socket duplication info */
1533       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1534       {
1535         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1536         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1537         break;
1538       }
1539       /* Synchronous I/O is not nice, but we can't schedule this:
1540        * lsocks will be closed/freed by the caller soon, and until
1541        * the child creates a duplicate, closing a socket here will
1542        * close it for good.
1543        */
1544       /* Send the size of the structure
1545        * (the child might be built with different headers...)
1546        */
1547       size = sizeof (pi);
1548       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1549       if (wrote != sizeof (size))
1550       {
1551         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1552         break;
1553       }
1554       /* Finally! Send the data */
1555       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1556       if (wrote != sizeof (pi))
1557       {
1558         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1559         break;
1560       }
1561     }
1562     /* This will block us until the child makes a final read or closes
1563      * the pipe (hence no 'wrote' check), since we have to wait for it
1564      * to duplicate the last socket, before we return and start closing
1565      * our own copies)
1566      */
1567     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1568     fail = 0;
1569   }
1570   while (fail);
1571
1572   GNUNET_DISK_file_sync (lsocks_write_fd);
1573   GNUNET_DISK_pipe_close (lsocks_pipe);
1574
1575   if (fail)
1576   {
1577     /* If we can't pass on the socket(s), the child will block forever,
1578      * better put it out of its misery.
1579      */
1580     TerminateProcess (gnunet_proc->handle, 0);
1581     CloseHandle (gnunet_proc->handle);
1582     if (NULL != gnunet_proc->control_pipe)
1583       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1584     GNUNET_free (gnunet_proc);
1585     return NULL;
1586   }
1587   return gnunet_proc;
1588 #endif
1589 }
1590
1591
1592 /**
1593  * Retrieve the status of a process, waiting on him if dead.
1594  * Nonblocking version.
1595  * 
1596  * @param proc process ID
1597  * @param type status type
1598  * @param code return code/signal number
1599  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1600  */
1601 int
1602 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1603                           enum GNUNET_OS_ProcessStatusType *type,
1604                           unsigned long *code)
1605 {
1606 #ifndef MINGW
1607   int status;
1608   int ret;
1609
1610   GNUNET_assert (0 != proc);
1611   ret = waitpid (proc->pid, &status, WNOHANG);
1612   if (ret < 0)
1613   {
1614     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1615     return GNUNET_SYSERR;
1616   }
1617   if (0 == ret)
1618   {
1619     *type = GNUNET_OS_PROCESS_RUNNING;
1620     *code = 0;
1621     return GNUNET_NO;
1622   }
1623   if (proc->pid != ret)
1624   {
1625     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1626     return GNUNET_SYSERR;
1627   }
1628   if (WIFEXITED (status))
1629   {
1630     *type = GNUNET_OS_PROCESS_EXITED;
1631     *code = WEXITSTATUS (status);
1632   }
1633   else if (WIFSIGNALED (status))
1634   {
1635     *type = GNUNET_OS_PROCESS_SIGNALED;
1636     *code = WTERMSIG (status);
1637   }
1638   else if (WIFSTOPPED (status))
1639   {
1640     *type = GNUNET_OS_PROCESS_SIGNALED;
1641     *code = WSTOPSIG (status);
1642   }
1643 #ifdef WIFCONTINUED
1644   else if (WIFCONTINUED (status))
1645   {
1646     *type = GNUNET_OS_PROCESS_RUNNING;
1647     *code = 0;
1648   }
1649 #endif
1650   else
1651   {
1652     *type = GNUNET_OS_PROCESS_UNKNOWN;
1653     *code = 0;
1654   }
1655 #else
1656   HANDLE h;
1657   DWORD c, error_code, ret;
1658
1659   h = proc->handle;
1660   ret = proc->pid;
1661   if (h == NULL || ret == 0)
1662   {
1663     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1664          ret, h);
1665     return GNUNET_SYSERR;
1666   }
1667   if (h == NULL)
1668     h = GetCurrentProcess ();
1669
1670   SetLastError (0);
1671   ret = GetExitCodeProcess (h, &c);
1672   error_code = GetLastError ();
1673   if (ret == 0 || error_code != NO_ERROR)
1674   {
1675     SetErrnoFromWinError (error_code);
1676     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1677     return GNUNET_SYSERR;
1678   }
1679   if (STILL_ACTIVE == c)
1680   {
1681     *type = GNUNET_OS_PROCESS_RUNNING;
1682     *code = 0;
1683     return GNUNET_NO;
1684   }
1685   *type = GNUNET_OS_PROCESS_EXITED;
1686   *code = c;
1687 #endif
1688
1689   return GNUNET_OK;
1690 }
1691
1692
1693 /**
1694  * Wait for a process
1695  * @param proc pointer to process structure
1696  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1697  */
1698 int
1699 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1700 {
1701
1702 #ifndef MINGW
1703   pid_t pid = proc->pid;
1704   pid_t ret;
1705
1706   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1707           (EINTR == errno) ) ;
1708   if (pid != ret) 
1709   {
1710     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1711     return GNUNET_SYSERR;
1712   }
1713   return GNUNET_OK;
1714 #else
1715   HANDLE h;
1716   int ret;
1717
1718   h = proc->handle;
1719   if (NULL == h)
1720   {
1721     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1722          proc->pid, h);
1723     return GNUNET_SYSERR;
1724   }
1725   if (h == NULL)
1726     h = GetCurrentProcess ();
1727
1728   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1729   {
1730     SetErrnoFromWinError (GetLastError ());
1731     ret = GNUNET_SYSERR;
1732   }
1733   else
1734     ret = GNUNET_OK;
1735
1736   return ret;
1737 #endif
1738 }
1739
1740
1741 /**
1742  * Handle to a command.
1743  */
1744 struct GNUNET_OS_CommandHandle
1745 {
1746
1747   /**
1748    * Process handle.
1749    */
1750   struct GNUNET_OS_Process *eip;
1751
1752   /**
1753    * Handle to the output pipe.
1754    */
1755   struct GNUNET_DISK_PipeHandle *opipe;
1756
1757   /**
1758    * Read-end of output pipe.
1759    */
1760   const struct GNUNET_DISK_FileHandle *r;
1761
1762   /**
1763    * Function to call on each line of output.
1764    */
1765   GNUNET_OS_LineProcessor proc;
1766
1767   /**
1768    * Closure for 'proc'.
1769    */
1770   void *proc_cls;
1771
1772   /**
1773    * Buffer for the output.
1774    */
1775   char buf[1024];
1776
1777   /**
1778    * Task reading from pipe.
1779    */
1780   GNUNET_SCHEDULER_TaskIdentifier rtask;
1781
1782   /**
1783    * When to time out.
1784    */
1785   struct GNUNET_TIME_Absolute timeout;
1786
1787   /**
1788    * Current read offset in buf.
1789    */
1790   size_t off;
1791 };
1792
1793
1794 /**
1795  * Stop/kill a command.  Must ONLY be called either from
1796  * the callback after 'NULL' was passed for 'line' *OR*
1797  * from an independent task (not within the line processor).
1798  *
1799  * @param cmd handle to the process
1800  */
1801 void
1802 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1803 {
1804
1805   if (cmd->proc != NULL)
1806   {
1807     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1808     GNUNET_SCHEDULER_cancel (cmd->rtask);
1809   }
1810   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1811   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1812   GNUNET_OS_process_destroy (cmd->eip);
1813   GNUNET_DISK_pipe_close (cmd->opipe);
1814   GNUNET_free (cmd);
1815 }
1816
1817
1818 /**
1819  * Read from the process and call the line processor.
1820  *
1821  * @param cls the 'struct GNUNET_OS_CommandHandle'
1822  * @param tc scheduler context
1823  */
1824 static void
1825 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1826 {
1827   struct GNUNET_OS_CommandHandle *cmd = cls;
1828   GNUNET_OS_LineProcessor proc;
1829   char *end;
1830   ssize_t ret;
1831
1832   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1833   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1834   {
1835     /* timeout, shutdown, etc. */
1836     proc = cmd->proc;
1837     cmd->proc = NULL;
1838     proc (cmd->proc_cls, NULL);
1839     return;
1840   }
1841   ret =
1842       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1843                              sizeof (cmd->buf) - cmd->off);
1844   if (ret <= 0)
1845   {
1846     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1847     {
1848       cmd->buf[cmd->off] = '\0';
1849       cmd->proc (cmd->proc_cls, cmd->buf);
1850     }
1851     proc = cmd->proc;
1852     cmd->proc = NULL;
1853     proc (cmd->proc_cls, NULL);
1854     return;
1855   }
1856   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1857   cmd->off += ret;
1858   while (end != NULL)
1859   {
1860     *end = '\0';
1861     cmd->proc (cmd->proc_cls, cmd->buf);
1862     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1863     cmd->off -= (end + 1 - cmd->buf);
1864     end = memchr (cmd->buf, '\n', cmd->off);
1865   }
1866   cmd->rtask =
1867       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1868                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1869 }
1870
1871
1872 /**
1873  * Run the given command line and call the given function
1874  * for each line of the output.
1875  *
1876  * @param proc function to call for each line of the output
1877  * @param proc_cls closure for proc
1878  * @param timeout when to time out
1879  * @param binary command to run
1880  * @param ... arguments to command
1881  * @return NULL on error
1882  */
1883 struct GNUNET_OS_CommandHandle *
1884 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1885                        struct GNUNET_TIME_Relative timeout, const char *binary,
1886                        ...)
1887 {
1888   struct GNUNET_OS_CommandHandle *cmd;
1889   struct GNUNET_OS_Process *eip;
1890   struct GNUNET_DISK_PipeHandle *opipe;
1891   va_list ap;
1892
1893   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1894   if (NULL == opipe)
1895     return NULL;
1896   va_start (ap, binary);
1897   eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1898   va_end (ap);
1899   if (NULL == eip)
1900   {
1901     GNUNET_DISK_pipe_close (opipe);
1902     return NULL;
1903   }
1904   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1905   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1906   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1907   cmd->eip = eip;
1908   cmd->opipe = opipe;
1909   cmd->proc = proc;
1910   cmd->proc_cls = proc_cls;
1911   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1912   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1913   return cmd;
1914 }
1915
1916
1917
1918
1919 /* end of os_priority.c */