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