868268b10a2392bb6035d2382d2c853ab99b36d7
[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
1503  * @param proc process ID
1504  * @param type status type
1505  * @param code return code/signal number
1506  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1507  */
1508 int
1509 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1510                           enum GNUNET_OS_ProcessStatusType *type,
1511                           unsigned long *code)
1512 {
1513 #ifndef MINGW
1514   int status;
1515   int ret;
1516
1517   GNUNET_assert (0 != proc);
1518   ret = waitpid (proc->pid, &status, WNOHANG);
1519   if (ret < 0)
1520   {
1521     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1522     return GNUNET_SYSERR;
1523   }
1524   if (0 == ret)
1525   {
1526     *type = GNUNET_OS_PROCESS_RUNNING;
1527     *code = 0;
1528     return GNUNET_NO;
1529   }
1530   if (proc->pid != ret)
1531   {
1532     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1533     return GNUNET_SYSERR;
1534   }
1535   if (WIFEXITED (status))
1536   {
1537     *type = GNUNET_OS_PROCESS_EXITED;
1538     *code = WEXITSTATUS (status);
1539   }
1540   else if (WIFSIGNALED (status))
1541   {
1542     *type = GNUNET_OS_PROCESS_SIGNALED;
1543     *code = WTERMSIG (status);
1544   }
1545   else if (WIFSTOPPED (status))
1546   {
1547     *type = GNUNET_OS_PROCESS_SIGNALED;
1548     *code = WSTOPSIG (status);
1549   }
1550 #ifdef WIFCONTINUED
1551   else if (WIFCONTINUED (status))
1552   {
1553     *type = GNUNET_OS_PROCESS_RUNNING;
1554     *code = 0;
1555   }
1556 #endif
1557   else
1558   {
1559     *type = GNUNET_OS_PROCESS_UNKNOWN;
1560     *code = 0;
1561   }
1562 #else
1563   HANDLE h;
1564   DWORD c, error_code, ret;
1565
1566   h = proc->handle;
1567   ret = proc->pid;
1568   if (h == NULL || ret == 0)
1569   {
1570     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1571          ret, h);
1572     return GNUNET_SYSERR;
1573   }
1574   if (h == NULL)
1575     h = GetCurrentProcess ();
1576
1577   SetLastError (0);
1578   ret = GetExitCodeProcess (h, &c);
1579   error_code = GetLastError ();
1580   if (ret == 0 || error_code != NO_ERROR)
1581   {
1582     SetErrnoFromWinError (error_code);
1583     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1584     return GNUNET_SYSERR;
1585   }
1586   if (STILL_ACTIVE == c)
1587   {
1588     *type = GNUNET_OS_PROCESS_RUNNING;
1589     *code = 0;
1590     return GNUNET_NO;
1591   }
1592   *type = GNUNET_OS_PROCESS_EXITED;
1593   *code = c;
1594 #endif
1595
1596   return GNUNET_OK;
1597 }
1598
1599
1600 /**
1601  * Wait for a process
1602  * @param proc pointer to process structure
1603  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1604  */
1605 int
1606 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1607 {
1608
1609 #ifndef MINGW
1610   pid_t pid = proc->pid;
1611   pid_t ret;
1612
1613   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1614           (EINTR == errno) ) ;
1615   if (pid != ret) 
1616   {
1617     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1618     return GNUNET_SYSERR;
1619   }
1620   return GNUNET_OK;
1621 #else
1622   HANDLE h;
1623   int ret;
1624
1625   h = proc->handle;
1626   if (NULL == h)
1627   {
1628     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1629          proc->pid, h);
1630     return GNUNET_SYSERR;
1631   }
1632   if (h == NULL)
1633     h = GetCurrentProcess ();
1634
1635   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1636   {
1637     SetErrnoFromWinError (GetLastError ());
1638     ret = GNUNET_SYSERR;
1639   }
1640   else
1641     ret = GNUNET_OK;
1642
1643   return ret;
1644 #endif
1645 }
1646
1647
1648 /**
1649  * Handle to a command.
1650  */
1651 struct GNUNET_OS_CommandHandle
1652 {
1653
1654   /**
1655    * Process handle.
1656    */
1657   struct GNUNET_OS_Process *eip;
1658
1659   /**
1660    * Handle to the output pipe.
1661    */
1662   struct GNUNET_DISK_PipeHandle *opipe;
1663
1664   /**
1665    * Read-end of output pipe.
1666    */
1667   const struct GNUNET_DISK_FileHandle *r;
1668
1669   /**
1670    * Function to call on each line of output.
1671    */
1672   GNUNET_OS_LineProcessor proc;
1673
1674   /**
1675    * Closure for 'proc'.
1676    */
1677   void *proc_cls;
1678
1679   /**
1680    * Buffer for the output.
1681    */
1682   char buf[1024];
1683
1684   /**
1685    * Task reading from pipe.
1686    */
1687   GNUNET_SCHEDULER_TaskIdentifier rtask;
1688
1689   /**
1690    * When to time out.
1691    */
1692   struct GNUNET_TIME_Absolute timeout;
1693
1694   /**
1695    * Current read offset in buf.
1696    */
1697   size_t off;
1698 };
1699
1700
1701 /**
1702  * Stop/kill a command.  Must ONLY be called either from
1703  * the callback after 'NULL' was passed for 'line' *OR*
1704  * from an independent task (not within the line processor).
1705  *
1706  * @param cmd handle to the process
1707  */
1708 void
1709 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1710 {
1711
1712   if (cmd->proc != NULL)
1713   {
1714     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1715     GNUNET_SCHEDULER_cancel (cmd->rtask);
1716   }
1717   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1718   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1719   GNUNET_OS_process_close (cmd->eip);
1720   GNUNET_DISK_pipe_close (cmd->opipe);
1721   GNUNET_free (cmd);
1722 }
1723
1724
1725 /**
1726  * Read from the process and call the line processor.
1727  *
1728  * @param cls the 'struct GNUNET_OS_CommandHandle'
1729  * @param tc scheduler context
1730  */
1731 static void
1732 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1733 {
1734   struct GNUNET_OS_CommandHandle *cmd = cls;
1735   GNUNET_OS_LineProcessor proc;
1736   char *end;
1737   ssize_t ret;
1738
1739   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1740   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1741   {
1742     /* timeout, shutdown, etc. */
1743     proc = cmd->proc;
1744     cmd->proc = NULL;
1745     proc (cmd->proc_cls, NULL);
1746     return;
1747   }
1748   ret =
1749       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1750                              sizeof (cmd->buf) - cmd->off);
1751   if (ret <= 0)
1752   {
1753     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1754     {
1755       cmd->buf[cmd->off] = '\0';
1756       cmd->proc (cmd->proc_cls, cmd->buf);
1757     }
1758     proc = cmd->proc;
1759     cmd->proc = NULL;
1760     proc (cmd->proc_cls, NULL);
1761     return;
1762   }
1763   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1764   cmd->off += ret;
1765   while (end != NULL)
1766   {
1767     *end = '\0';
1768     cmd->proc (cmd->proc_cls, cmd->buf);
1769     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1770     cmd->off -= (end + 1 - cmd->buf);
1771     end = memchr (cmd->buf, '\n', cmd->off);
1772   }
1773   cmd->rtask =
1774       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1775                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1776 }
1777
1778
1779 /**
1780  * Run the given command line and call the given function
1781  * for each line of the output.
1782  *
1783  * @param proc function to call for each line of the output
1784  * @param proc_cls closure for proc
1785  * @param timeout when to time out
1786  * @param binary command to run
1787  * @param ... arguments to command
1788  * @return NULL on error
1789  */
1790 struct GNUNET_OS_CommandHandle *
1791 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1792                        struct GNUNET_TIME_Relative timeout, const char *binary,
1793                        ...)
1794 {
1795   struct GNUNET_OS_CommandHandle *cmd;
1796   struct GNUNET_OS_Process *eip;
1797   struct GNUNET_DISK_PipeHandle *opipe;
1798   va_list ap;
1799
1800   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1801   if (NULL == opipe)
1802     return NULL;
1803   va_start (ap, binary);
1804   eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1805   va_end (ap);
1806   if (NULL == eip)
1807   {
1808     GNUNET_DISK_pipe_close (opipe);
1809     return NULL;
1810   }
1811   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1812   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1813   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1814   cmd->eip = eip;
1815   cmd->opipe = opipe;
1816   cmd->proc = proc;
1817   cmd->proc_cls = proc_cls;
1818   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1819   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1820   return cmd;
1821 }
1822
1823
1824
1825
1826 /* end of os_priority.c */