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