-renaming
[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 #if !WINDOWS
452   case SIGHUP:
453 #endif
454   case SIGINT:
455   case SIGKILL:
456   case SIGTERM:
457 #if WINDOWS && !defined(__CYGWIN__)
458     if (0 == TerminateProcess (proc->handle, 0))
459     {
460       /* FIXME: set 'errno' */
461       return -1;
462     }
463     return 0;
464 #else
465     return PLIBC_KILL (proc->pid, sig);
466 #endif
467   default:
468 #if WINDOWS
469     errno = EINVAL;
470     return -1;
471 #else
472     return kill (proc->pid, sig);
473 #endif    
474   }
475 }
476
477 /**
478  * Get the pid of the process in question
479  *
480  * @param proc the process to get the pid of
481  *
482  * @return the current process id
483  */
484 pid_t
485 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
486 {
487   return proc->pid;
488 }
489
490
491 void
492 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
493 {
494   if (NULL != proc->control_pipe)
495     GNUNET_DISK_file_close (proc->control_pipe);
496 // FIXME NILS
497 #ifdef WINDOWS
498   if (proc->handle != NULL)
499     CloseHandle (proc->handle);
500 #endif
501   if (NULL != proc->childpipename)
502   {
503 #if !WINDOWS
504     cleanup_npipe (proc->childpipename);
505 #endif
506     GNUNET_free (proc->childpipename);
507   }
508   GNUNET_free (proc);
509 }
510
511 // FIXME NILS
512 #if WINDOWS
513 #include "gnunet_signal_lib.h"
514
515 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
516
517 /**
518  * Make seaspider happy.
519  */
520 #define DWORD_WINAPI DWORD WINAPI
521
522 /**
523  * @brief Waits for a process to terminate and invokes the SIGCHLD handler
524  * @param proc pointer to process structure
525  */
526 static DWORD_WINAPI
527 child_wait_thread (void *arg)
528 {
529   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
530
531   WaitForSingleObject (proc->handle, INFINITE);
532
533   if (w32_sigchld_handler)
534     w32_sigchld_handler ();
535
536   return 0;
537 }
538 #endif
539
540 /**
541  * Set process priority
542  *
543  * @param proc pointer to process structure
544  * @param prio priority value
545  * @return GNUNET_OK on success, GNUNET_SYSERR on error
546  */
547 int
548 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
549                                 enum GNUNET_SCHEDULER_Priority prio)
550 {
551   int rprio;
552
553   GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
554   if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
555     return GNUNET_OK;
556
557   /* convert to MINGW/Unix values */
558   switch (prio)
559   {
560   case GNUNET_SCHEDULER_PRIORITY_UI:
561   case GNUNET_SCHEDULER_PRIORITY_URGENT:
562 #ifdef MINGW
563     rprio = HIGH_PRIORITY_CLASS;
564 #else
565     rprio = 0;
566 #endif
567     break;
568
569   case GNUNET_SCHEDULER_PRIORITY_HIGH:
570 #ifdef MINGW
571     rprio = ABOVE_NORMAL_PRIORITY_CLASS;
572 #else
573     rprio = 5;
574 #endif
575     break;
576
577   case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
578 #ifdef MINGW
579     rprio = NORMAL_PRIORITY_CLASS;
580 #else
581     rprio = 7;
582 #endif
583     break;
584
585   case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
586 #ifdef MINGW
587     rprio = BELOW_NORMAL_PRIORITY_CLASS;
588 #else
589     rprio = 10;
590 #endif
591     break;
592
593   case GNUNET_SCHEDULER_PRIORITY_IDLE:
594 #ifdef MINGW
595     rprio = IDLE_PRIORITY_CLASS;
596 #else
597     rprio = 19;
598 #endif
599     break;
600   default:
601     GNUNET_assert (0);
602     return GNUNET_SYSERR;
603   }
604
605   /* Set process priority */
606 #ifdef MINGW
607   {
608     HANDLE h = proc->handle;
609
610     GNUNET_assert (h != NULL);
611     SetPriorityClass (h, rprio);
612   }
613 #elif LINUX
614   pid_t pid;
615
616   pid = proc->pid;
617   if ((0 == pid) || (pid == getpid ()))
618   {
619     int have = nice (0);
620     int delta = rprio - have;
621
622     errno = 0;
623     if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
624     {
625       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
626       return GNUNET_SYSERR;
627     }
628   }
629   else
630   {
631     if (0 != setpriority (PRIO_PROCESS, pid, rprio))
632     {
633       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
634                     "setpriority");
635       return GNUNET_SYSERR;
636     }
637   }
638 #else
639   LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
640        "Priority management not availabe for this platform\n");
641 #endif
642   return GNUNET_OK;
643 }
644
645 #if MINGW
646 static char *
647 CreateCustomEnvTable (char **vars)
648 {
649   char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
650   size_t tablesize = 0;
651   size_t items_count = 0;
652   size_t n_found = 0, n_var;
653   char *index = NULL;
654   size_t c;
655   size_t var_len;
656   char *var;
657   char *val;
658
659   win32_env_table = GetEnvironmentStringsA ();
660   if (win32_env_table == NULL)
661     return NULL;
662   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
663   n_var = c;
664   index = GNUNET_malloc (sizeof (char *) * n_var);
665   for (c = 0; c < n_var; c++)
666     index[c] = 0;
667   for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
668   {
669     size_t len = strlen (ptr);
670     int found = 0;
671
672     for (var_ptr = vars; *var_ptr; var_ptr++)
673     {
674       var = *var_ptr++;
675       val = *var_ptr;
676       var_len = strlen (var);
677       if (strncmp (var, ptr, var_len) == 0)
678       {
679         found = 1;
680         index[c] = 1;
681         tablesize += var_len + strlen (val) + 1;
682         break;
683       }
684     }
685     if (!found)
686       tablesize += len + 1;
687     ptr += len + 1;
688   }
689   for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
690   {
691     var = *var_ptr++;
692     val = *var_ptr;
693     if (index[c] != 1)
694       n_found += strlen (var) + strlen (val) + 1;
695   }
696   result = GNUNET_malloc (tablesize + n_found + 1);
697   for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
698   {
699     size_t len = strlen (ptr);
700     int found = 0;
701
702     for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
703     {
704       var = *var_ptr++;
705       val = *var_ptr;
706       var_len = strlen (var);
707       if (strncmp (var, ptr, var_len) == 0)
708       {
709         found = 1;
710         break;
711       }
712     }
713     if (!found)
714     {
715       strcpy (result_ptr, ptr);
716       result_ptr += len + 1;
717     }
718     else
719     {
720       strcpy (result_ptr, var);
721       result_ptr += var_len;
722       strcpy (result_ptr, val);
723       result_ptr += strlen (val) + 1;
724     }
725     ptr += len + 1;
726   }
727   for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
728   {
729     var = *var_ptr++;
730     val = *var_ptr;
731     var_len = strlen (var);
732     if (index[c] != 1)
733     {
734       strcpy (result_ptr, var);
735       result_ptr += var_len;
736       strcpy (result_ptr, val);
737       result_ptr += strlen (val) + 1;
738     }
739   }
740   FreeEnvironmentStrings (win32_env_table);
741   GNUNET_free (index);
742   *result_ptr = 0;
743   return result;
744 }
745 #endif
746
747
748 /**
749  * Start a process.
750  *
751  * @param pipe_control should a pipe be used to send signals to the child?
752  * @param pipe_stdin pipe to use to send input to child process (or NULL)
753  * @param pipe_stdout pipe to use to get output from child process (or NULL)
754  * @param filename name of the binary
755  * @param argv NULL-terminated array of arguments to the process
756  * @return pointer to process structure of the new process, NULL on error
757  */
758 struct GNUNET_OS_Process *
759 GNUNET_OS_start_process_vap (int pipe_control,
760                              struct GNUNET_DISK_PipeHandle *pipe_stdin,
761                              struct GNUNET_DISK_PipeHandle *pipe_stdout,
762                              const char *filename, 
763                              char *const argv[])
764 {
765 #ifndef MINGW
766   char *childpipename = NULL;
767   struct GNUNET_OS_Process *gnunet_proc = NULL;
768   pid_t ret;
769   int fd_stdout_write;
770   int fd_stdout_read;
771   int fd_stdin_read;
772   int fd_stdin_write;
773
774   if ( (GNUNET_YES == pipe_control) &&
775        (GNUNET_OK != 
776         npipe_setup (&childpipename)) )
777     return NULL;  
778   if (pipe_stdout != NULL)
779   {
780     GNUNET_assert (GNUNET_OK ==
781                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
782                                                       (pipe_stdout,
783                                                        GNUNET_DISK_PIPE_END_WRITE),
784                                                       &fd_stdout_write, sizeof (int)));
785     GNUNET_assert (GNUNET_OK ==
786                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
787                                                       (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
788                                                       &fd_stdout_read, sizeof (int)));
789   }
790   if (pipe_stdin != NULL)
791   {
792     GNUNET_assert (GNUNET_OK ==
793                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
794                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
795                                                       &fd_stdin_read, sizeof (int)));
796     GNUNET_assert (GNUNET_OK ==
797                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
798                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
799                                                       &fd_stdin_write, sizeof (int)));
800   }
801
802   ret = fork ();
803   if (-1 == ret)
804   {
805     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
806     GNUNET_free_non_null (childpipename);
807     return NULL;
808   }
809   if (0 != ret)
810   {
811     gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
812     gnunet_proc->pid = ret;
813     gnunet_proc->childpipename = childpipename;
814     return gnunet_proc;
815   }
816   if (NULL != childpipename)
817   {
818     setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
819     GNUNET_free (childpipename);
820   }
821   if (pipe_stdout != NULL)
822   {
823     GNUNET_break (0 == close (fd_stdout_read));
824     if (-1 == dup2 (fd_stdout_write, 1))
825       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
826     GNUNET_break (0 == close (fd_stdout_write));
827   }
828
829   if (pipe_stdin != NULL)
830   {
831
832     GNUNET_break (0 == close (fd_stdin_write));
833     if (-1 == dup2 (fd_stdin_read, 0))
834       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
835     GNUNET_break (0 == close (fd_stdin_read));
836   }
837   execvp (filename, argv);
838   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
839   _exit (1);
840 #else
841   char *childpipename = NULL;
842   struct GNUNET_OS_Process *gnunet_proc = NULL;
843   char *arg;
844   unsigned int cmdlen;
845   char *cmd, *idx;
846   STARTUPINFOW start;
847   PROCESS_INFORMATION proc;
848   int argc, arg_count;
849   HANDLE stdin_handle;
850   HANDLE stdout_handle;
851   struct GNUNET_DISK_FileHandle *control_pipe;
852
853   char path[MAX_PATH + 1];
854
855   char *our_env[3] = { NULL, NULL, NULL };
856   char *env_block = NULL;
857   char *pathbuf;
858   DWORD pathbuf_len, alloc_len;
859   char *self_prefix;
860   char *bindir;
861   char *libdir;
862   char *ptr;
863   char *non_const_filename;
864   wchar_t wpath[MAX_PATH + 1], wcmd[32768];
865
866   /* Search in prefix dir (hopefully - the directory from which
867    * the current module was loaded), bindir and libdir, then in PATH
868    */
869   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
870   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
871   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
872
873   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
874
875   alloc_len =
876       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
877       strlen (libdir);
878
879   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
880
881   ptr = pathbuf;
882   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
883   GNUNET_free (self_prefix);
884   GNUNET_free (bindir);
885   GNUNET_free (libdir);
886
887   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
888   GNUNET_assert (alloc_len == (pathbuf_len - 1));
889
890   cmdlen = strlen (filename);
891   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
892     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
893   else
894     GNUNET_asprintf (&non_const_filename, "%s", filename);
895
896   /* Check that this is the full path. If it isn't, search. */
897   if (non_const_filename[1] == ':')
898     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
899   else if (!SearchPathA
900            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
901             path, NULL))
902   {
903     SetErrnoFromWinError (GetLastError ());
904     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
905                        non_const_filename);
906     GNUNET_free (non_const_filename);
907     GNUNET_free (pathbuf);
908     return NULL;
909   }
910   GNUNET_free (pathbuf);
911   GNUNET_free (non_const_filename);
912
913   cmdlen = 0;
914   argc = 0;
915   while (NULL != (arg = argv[argc++]))
916   {
917     if (cmdlen == 0)
918       cmdlen = cmdlen + strlen (path) + 4;
919     else
920       cmdlen = cmdlen + strlen (arg) + 4;
921   }
922   arg_count = argc;
923
924   cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
925   argc = 0;
926   while (NULL != (arg = argv[argc++]))
927   {
928     /* This is to escape trailing slash */
929     char arg_lastchar = arg[strlen (arg) - 1];
930     if (idx == cmd)
931       idx += sprintf (idx, "\"%s%s\"%s", path,
932           arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
933     else
934       idx += sprintf (idx, "\"%s%s\"%s", arg,
935           arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
936   }
937
938   memset (&start, 0, sizeof (start));
939   start.cb = sizeof (start);
940
941   if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
942     start.dwFlags |= STARTF_USESTDHANDLES;
943
944   if (pipe_stdin != NULL)
945   {
946     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
947                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
948                                        &stdin_handle, sizeof (HANDLE));
949     start.hStdInput = stdin_handle;
950   }
951
952   if (pipe_stdout != NULL)
953   {
954     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
955                                        (pipe_stdout,
956                                         GNUNET_DISK_PIPE_END_WRITE),
957                                        &stdout_handle, sizeof (HANDLE));
958     start.hStdOutput = stdout_handle;
959   }
960   if (GNUNET_YES == pipe_control)
961   {
962     control_pipe =
963       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
964                     GNUNET_DISK_PERM_USER_READ |
965                     GNUNET_DISK_PERM_USER_WRITE);
966     if (control_pipe == NULL)
967     {
968       GNUNET_free (cmd);
969       GNUNET_free (path);
970       return NULL;
971     }
972   }
973   else
974     control_pipe = NULL;
975   if (NULL != childpipename)
976   {
977     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
978          childpipename);
979     GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
980     GNUNET_asprintf (&our_env[1], "%s", childpipename);
981     our_env[2] = NULL;
982   }
983   else
984   {
985     our_env[0] = NULL;
986   }
987   env_block = CreateCustomEnvTable (our_env);
988   GNUNET_free_non_null (our_env[0]);
989   GNUNET_free_non_null (our_env[1]);
990
991   if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
992       || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
993       || !CreateProcessW
994       (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
995        env_block, NULL, &start, &proc))
996   {
997     SetErrnoFromWinError (GetLastError ());
998     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
999     GNUNET_free (env_block);
1000     GNUNET_free (cmd);
1001     return NULL;
1002   }
1003
1004   GNUNET_free (env_block);
1005
1006   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1007   gnunet_proc->pid = proc.dwProcessId;
1008   gnunet_proc->handle = proc.hProcess;
1009   gnunet_proc->control_pipe = control_pipe;
1010
1011   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1012
1013   ResumeThread (proc.hThread);
1014   CloseHandle (proc.hThread);
1015
1016   GNUNET_free (cmd);
1017
1018   return gnunet_proc;
1019 #endif
1020 }
1021
1022
1023 /**
1024  * Start a process.
1025  *
1026  * @param pipe_control should a pipe be used to send signals to the child?
1027  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1028  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1029  * @param filename name of the binary
1030  * @param va NULL-terminated list of arguments to the process
1031  * @return pointer to process structure of the new process, NULL on error
1032  */
1033 struct GNUNET_OS_Process *
1034 GNUNET_OS_start_process_va (int pipe_control,
1035                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1036                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1037                             const char *filename, va_list va)
1038 {
1039   struct GNUNET_OS_Process *ret;
1040   va_list ap;
1041   char **argv;
1042   int argc;
1043
1044   argc = 0;
1045   va_copy (ap, va);
1046   while (NULL != va_arg (ap, char *))
1047     argc++;
1048   va_end (ap);
1049   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1050   argc = 0;
1051   va_copy (ap, va);
1052   while (NULL != (argv[argc] = va_arg (ap, char *)))
1053     argc++;
1054   va_end (ap);
1055   ret = GNUNET_OS_start_process_vap (pipe_control,
1056                                      pipe_stdin,
1057                                      pipe_stdout,
1058                                      filename,
1059                                      argv);
1060   GNUNET_free (argv);
1061   return ret;
1062 }
1063
1064
1065
1066 /**
1067  * Start a process.
1068  *
1069  * @param pipe_control should a pipe be used to send signals to the child?
1070  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1071  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1072  * @param filename name of the binary
1073  * @param ... NULL-terminated list of arguments to the process
1074  *
1075  * @return pointer to process structure of the new process, NULL on error
1076  *
1077  */
1078 struct GNUNET_OS_Process *
1079 GNUNET_OS_start_process (int pipe_control,
1080                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1081                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1082                          const char *filename, ...)
1083 {
1084   struct GNUNET_OS_Process *ret;
1085   va_list ap;
1086
1087   va_start (ap, filename);
1088   ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1089   va_end (ap);
1090   return ret;
1091 }
1092
1093
1094 /**
1095  * Start a process.
1096  *
1097  * @param pipe_control should a pipe be used to send signals to the child?
1098  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1099  *         must be NULL on platforms where dup is not supported
1100  * @param filename name of the binary
1101  * @param argv NULL-terminated list of arguments to the process
1102  * @return process ID of the new process, -1 on error
1103  */
1104 struct GNUNET_OS_Process *
1105 GNUNET_OS_start_process_v (int pipe_control,
1106                            const SOCKTYPE *lsocks,
1107                            const char *filename,
1108                            char *const argv[])
1109 {
1110 #ifndef MINGW
1111   pid_t ret;
1112   char lpid[16];
1113   char fds[16];
1114   struct GNUNET_OS_Process *gnunet_proc = NULL;
1115   char *childpipename = NULL;
1116   int i;
1117   int j;
1118   int k;
1119   int tgt;
1120   int flags;
1121   int *lscp;
1122   unsigned int ls;
1123
1124   if ( (GNUNET_YES == pipe_control) &&
1125        (GNUNET_OK != npipe_setup (&childpipename)) )
1126     return NULL;  
1127   lscp = NULL;
1128   ls = 0;
1129   if (lsocks != NULL)
1130   {
1131     i = 0;
1132     while (-1 != (k = lsocks[i++]))
1133       GNUNET_array_append (lscp, ls, k);
1134     GNUNET_array_append (lscp, ls, -1);
1135   }
1136   ret = fork ();
1137   if (-1 == ret)
1138   {
1139     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1140     GNUNET_free_non_null (childpipename);
1141     GNUNET_array_grow (lscp, ls, 0);
1142     return NULL;
1143   }
1144   if (0 != ret)
1145   {
1146     gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1147     gnunet_proc->pid = ret;
1148     gnunet_proc->childpipename = childpipename;  
1149     GNUNET_array_grow (lscp, ls, 0);
1150     return gnunet_proc;
1151   }
1152   if (NULL != childpipename)
1153   {
1154     setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1155     GNUNET_free (childpipename);
1156   }
1157   if (lscp != NULL)
1158   {
1159     /* read systemd documentation... */
1160     GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1161     setenv ("LISTEN_PID", lpid, 1);
1162     i = 0;
1163     tgt = 3;
1164     while (-1 != lscp[i])
1165     {
1166       j = i + 1;
1167       while (-1 != lscp[j])
1168       {
1169         if (lscp[j] == tgt)
1170         {
1171           /* dup away */
1172           k = dup (lscp[j]);
1173           GNUNET_assert (-1 != k);
1174           GNUNET_assert (0 == close (lscp[j]));
1175           lscp[j] = k;
1176           break;
1177         }
1178         j++;
1179       }
1180       if (lscp[i] != tgt)
1181       {
1182         /* Bury any existing FD, no matter what; they should all be closed
1183          * on exec anyway and the important onces have been dup'ed away */
1184         (void) close (tgt);
1185         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1186       }
1187       /* unset close-on-exec flag */
1188       flags = fcntl (tgt, F_GETFD);
1189       GNUNET_assert (flags >= 0);
1190       flags &= ~FD_CLOEXEC;
1191       fflush (stderr);
1192       (void) fcntl (tgt, F_SETFD, flags);
1193       tgt++;
1194       i++;
1195     }
1196     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1197     setenv ("LISTEN_FDS", fds, 1);
1198   }
1199   GNUNET_array_grow (lscp, ls, 0);
1200   execvp (filename, argv);
1201   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1202   _exit (1);
1203 #else
1204   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1205   char *childpipename = NULL;
1206   char **arg, **non_const_argv;
1207   unsigned int cmdlen;
1208   char *cmd, *idx;
1209   STARTUPINFOW start;
1210   PROCESS_INFORMATION proc;
1211   int argcount = 0;
1212   struct GNUNET_OS_Process *gnunet_proc = NULL;
1213   char path[MAX_PATH + 1];
1214   char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1215   char *env_block = NULL;
1216   char *pathbuf;
1217   DWORD pathbuf_len, alloc_len;
1218   char *self_prefix;
1219   char *bindir;
1220   char *libdir;
1221   char *ptr;
1222   char *non_const_filename;
1223   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1224   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1225   HANDLE lsocks_read;
1226   HANDLE lsocks_write;
1227   wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1228   int env_off;
1229   int fail;
1230
1231   /* Search in prefix dir (hopefully - the directory from which
1232    * the current module was loaded), bindir and libdir, then in PATH
1233    */
1234   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1235   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1236   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1237
1238   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1239
1240   alloc_len =
1241       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1242       strlen (libdir);
1243
1244   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1245
1246   ptr = pathbuf;
1247   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1248   GNUNET_free (self_prefix);
1249   GNUNET_free (bindir);
1250   GNUNET_free (libdir);
1251
1252   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1253   if (alloc_len != pathbuf_len - 1)
1254   {
1255     GNUNET_free (pathbuf);
1256     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
1257     return NULL;
1258   }
1259
1260   cmdlen = strlen (filename);
1261   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1262     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1263   else
1264     GNUNET_asprintf (&non_const_filename, "%s", filename);
1265
1266   /* Check that this is the full path. If it isn't, search. */
1267   if (non_const_filename[1] == ':')
1268     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1269   else if (!SearchPathA
1270            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1271             path, NULL))
1272   {
1273     SetErrnoFromWinError (GetLastError ());
1274     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1275                        non_const_filename);
1276     GNUNET_free (non_const_filename);
1277     GNUNET_free (pathbuf);
1278     return NULL;
1279   }
1280   GNUNET_free (pathbuf);
1281   GNUNET_free (non_const_filename);
1282
1283   /* Count the number of arguments */
1284   arg = (char **) argv;
1285   while (*arg)
1286   {
1287     arg++;
1288     argcount++;
1289   }
1290
1291   /* Allocate a copy argv */
1292   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1293
1294   /* Copy all argv strings */
1295   argcount = 0;
1296   arg = (char **) argv;
1297   while (*arg)
1298   {
1299     if (arg == argv)
1300       non_const_argv[argcount] = GNUNET_strdup (path);
1301     else
1302       non_const_argv[argcount] = GNUNET_strdup (*arg);
1303     arg++;
1304     argcount++;
1305   }
1306   non_const_argv[argcount] = NULL;
1307
1308   /* Count cmd len */
1309   cmdlen = 1;
1310   arg = non_const_argv;
1311   while (*arg)
1312   {
1313     cmdlen = cmdlen + strlen (*arg) + 4;
1314     arg++;
1315   }
1316
1317   /* Allocate and create cmd */
1318   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1319   arg = non_const_argv;
1320   while (*arg)
1321   {
1322     char arg_last_char = (*arg)[strlen (*arg) - 1];
1323     idx += sprintf (idx, "\"%s%s\"%s", *arg,
1324         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1325     arg++;
1326   }
1327
1328   while (argcount > 0)
1329     GNUNET_free (non_const_argv[--argcount]);
1330   GNUNET_free (non_const_argv);
1331
1332   memset (&start, 0, sizeof (start));
1333   start.cb = sizeof (start);
1334
1335   if (GNUNET_YES == pipe_control)
1336   {
1337     control_pipe =
1338       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1339                     GNUNET_DISK_PERM_USER_READ |
1340                     GNUNET_DISK_PERM_USER_WRITE);
1341     if (control_pipe == NULL)
1342     {
1343       GNUNET_free (cmd);
1344       GNUNET_free (path);
1345       return NULL;
1346     }
1347   }
1348   else
1349     control_pipe = NULL;
1350   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1351   {
1352     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1353
1354     if (lsocks_pipe == NULL)
1355     {
1356       GNUNET_free (cmd);
1357       GNUNET_free (path);
1358       GNUNET_DISK_pipe_close (lsocks_pipe);
1359       return NULL;
1360     }
1361     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1362         GNUNET_DISK_PIPE_END_WRITE);
1363     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1364                                        &lsocks_write, sizeof (HANDLE));
1365     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1366                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1367                                        &lsocks_read, sizeof (HANDLE));
1368   }
1369
1370   env_off = 0;
1371   if (NULL != childpipename)
1372   {
1373     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1374          childpipename);
1375     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1376     GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1377     GNUNET_free (childpipename);
1378   }
1379   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1380   {
1381     /*This will tell the child that we're going to send lsocks over the pipe*/
1382     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1383     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1384   }
1385   our_env[env_off++] = NULL;
1386   env_block = CreateCustomEnvTable (our_env);
1387   while (0 > env_off)
1388     GNUNET_free_non_null (our_env[--env_off]);
1389   if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1390       || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1391       || !CreateProcessW
1392       (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1393        env_block, NULL, &start, &proc))
1394   {
1395     SetErrnoFromWinError (GetLastError ());
1396     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1397     if (NULL != control_pipe)
1398       GNUNET_DISK_file_close (control_pipe);
1399     if (NULL != lsocks)
1400       GNUNET_DISK_pipe_close (lsocks_pipe);
1401     GNUNET_free (env_block);
1402     GNUNET_free (cmd);
1403     return NULL;
1404   }
1405
1406   GNUNET_free (env_block);
1407
1408   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1409   gnunet_proc->pid = proc.dwProcessId;
1410   gnunet_proc->handle = proc.hProcess;
1411   gnunet_proc->control_pipe = control_pipe;
1412
1413   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1414
1415   ResumeThread (proc.hThread);
1416   CloseHandle (proc.hThread);
1417   GNUNET_free (cmd);
1418
1419   if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1420     return gnunet_proc;
1421
1422   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1423
1424   /* This is a replacement for "goto error" that doesn't use goto */
1425   fail = 1;
1426   do
1427   {
1428     int wrote;
1429     uint64_t size, count, i;
1430
1431     /* Tell the number of sockets */
1432     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1433
1434     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1435     if (wrote != sizeof (count))
1436     {
1437       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1438       break;
1439     }
1440     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1441     {
1442       WSAPROTOCOL_INFOA pi;
1443       /* Get a socket duplication info */
1444       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1445       {
1446         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1447         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1448         break;
1449       }
1450       /* Synchronous I/O is not nice, but we can't schedule this:
1451        * lsocks will be closed/freed by the caller soon, and until
1452        * the child creates a duplicate, closing a socket here will
1453        * close it for good.
1454        */
1455       /* Send the size of the structure
1456        * (the child might be built with different headers...)
1457        */
1458       size = sizeof (pi);
1459       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1460       if (wrote != sizeof (size))
1461       {
1462         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1463         break;
1464       }
1465       /* Finally! Send the data */
1466       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1467       if (wrote != sizeof (pi))
1468       {
1469         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1470         break;
1471       }
1472     }
1473     /* This will block us until the child makes a final read or closes
1474      * the pipe (hence no 'wrote' check), since we have to wait for it
1475      * to duplicate the last socket, before we return and start closing
1476      * our own copies)
1477      */
1478     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1479     fail = 0;
1480   }
1481   while (fail);
1482
1483   GNUNET_DISK_file_sync (lsocks_write_fd);
1484   GNUNET_DISK_pipe_close (lsocks_pipe);
1485
1486   if (fail)
1487   {
1488     /* If we can't pass on the socket(s), the child will block forever,
1489      * better put it out of its misery.
1490      */
1491     TerminateProcess (gnunet_proc->handle, 0);
1492     CloseHandle (gnunet_proc->handle);
1493     if (NULL != gnunet_proc->control_pipe)
1494       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1495     GNUNET_free (gnunet_proc);
1496     return NULL;
1497   }
1498   return gnunet_proc;
1499 #endif
1500 }
1501
1502
1503 /**
1504  * Retrieve the status of a process
1505  * @param proc process ID
1506  * @param type status type
1507  * @param code return code/signal number
1508  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1509  */
1510 int
1511 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1512                           enum GNUNET_OS_ProcessStatusType *type,
1513                           unsigned long *code)
1514 {
1515 #ifndef MINGW
1516   int status;
1517   int ret;
1518
1519   GNUNET_assert (0 != proc);
1520   ret = waitpid (proc->pid, &status, WNOHANG);
1521   if (ret < 0)
1522   {
1523     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1524     return GNUNET_SYSERR;
1525   }
1526   if (0 == ret)
1527   {
1528     *type = GNUNET_OS_PROCESS_RUNNING;
1529     *code = 0;
1530     return GNUNET_NO;
1531   }
1532   if (proc->pid != ret)
1533   {
1534     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1535     return GNUNET_SYSERR;
1536   }
1537   if (WIFEXITED (status))
1538   {
1539     *type = GNUNET_OS_PROCESS_EXITED;
1540     *code = WEXITSTATUS (status);
1541   }
1542   else if (WIFSIGNALED (status))
1543   {
1544     *type = GNUNET_OS_PROCESS_SIGNALED;
1545     *code = WTERMSIG (status);
1546   }
1547   else if (WIFSTOPPED (status))
1548   {
1549     *type = GNUNET_OS_PROCESS_SIGNALED;
1550     *code = WSTOPSIG (status);
1551   }
1552 #ifdef WIFCONTINUED
1553   else if (WIFCONTINUED (status))
1554   {
1555     *type = GNUNET_OS_PROCESS_RUNNING;
1556     *code = 0;
1557   }
1558 #endif
1559   else
1560   {
1561     *type = GNUNET_OS_PROCESS_UNKNOWN;
1562     *code = 0;
1563   }
1564 #else
1565   HANDLE h;
1566   DWORD c, error_code, ret;
1567
1568   h = proc->handle;
1569   ret = proc->pid;
1570   if (h == NULL || ret == 0)
1571   {
1572     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1573          ret, h);
1574     return GNUNET_SYSERR;
1575   }
1576   if (h == NULL)
1577     h = GetCurrentProcess ();
1578
1579   SetLastError (0);
1580   ret = GetExitCodeProcess (h, &c);
1581   error_code = GetLastError ();
1582   if (ret == 0 || error_code != NO_ERROR)
1583   {
1584     SetErrnoFromWinError (error_code);
1585     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1586     return GNUNET_SYSERR;
1587   }
1588   if (STILL_ACTIVE == c)
1589   {
1590     *type = GNUNET_OS_PROCESS_RUNNING;
1591     *code = 0;
1592     return GNUNET_NO;
1593   }
1594   *type = GNUNET_OS_PROCESS_EXITED;
1595   *code = c;
1596 #endif
1597
1598   return GNUNET_OK;
1599 }
1600
1601
1602 /**
1603  * Wait for a process
1604  * @param proc pointer to process structure
1605  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1606  */
1607 int
1608 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1609 {
1610
1611 #ifndef MINGW
1612   pid_t pid = proc->pid;
1613   pid_t ret;
1614
1615   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1616           (EINTR == errno) ) ;
1617   if (pid != ret) 
1618   {
1619     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1620     return GNUNET_SYSERR;
1621   }
1622   return GNUNET_OK;
1623 #else
1624   HANDLE h;
1625   int ret;
1626
1627   h = proc->handle;
1628   if (NULL == h)
1629   {
1630     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1631          proc->pid, h);
1632     return GNUNET_SYSERR;
1633   }
1634   if (h == NULL)
1635     h = GetCurrentProcess ();
1636
1637   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1638   {
1639     SetErrnoFromWinError (GetLastError ());
1640     ret = GNUNET_SYSERR;
1641   }
1642   else
1643     ret = GNUNET_OK;
1644
1645   return ret;
1646 #endif
1647 }
1648
1649
1650 /**
1651  * Handle to a command.
1652  */
1653 struct GNUNET_OS_CommandHandle
1654 {
1655
1656   /**
1657    * Process handle.
1658    */
1659   struct GNUNET_OS_Process *eip;
1660
1661   /**
1662    * Handle to the output pipe.
1663    */
1664   struct GNUNET_DISK_PipeHandle *opipe;
1665
1666   /**
1667    * Read-end of output pipe.
1668    */
1669   const struct GNUNET_DISK_FileHandle *r;
1670
1671   /**
1672    * Function to call on each line of output.
1673    */
1674   GNUNET_OS_LineProcessor proc;
1675
1676   /**
1677    * Closure for 'proc'.
1678    */
1679   void *proc_cls;
1680
1681   /**
1682    * Buffer for the output.
1683    */
1684   char buf[1024];
1685
1686   /**
1687    * Task reading from pipe.
1688    */
1689   GNUNET_SCHEDULER_TaskIdentifier rtask;
1690
1691   /**
1692    * When to time out.
1693    */
1694   struct GNUNET_TIME_Absolute timeout;
1695
1696   /**
1697    * Current read offset in buf.
1698    */
1699   size_t off;
1700 };
1701
1702
1703 /**
1704  * Stop/kill a command.  Must ONLY be called either from
1705  * the callback after 'NULL' was passed for 'line' *OR*
1706  * from an independent task (not within the line processor).
1707  *
1708  * @param cmd handle to the process
1709  */
1710 void
1711 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1712 {
1713
1714   if (cmd->proc != NULL)
1715   {
1716     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1717     GNUNET_SCHEDULER_cancel (cmd->rtask);
1718   }
1719   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1720   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1721   GNUNET_OS_process_close (cmd->eip);
1722   GNUNET_DISK_pipe_close (cmd->opipe);
1723   GNUNET_free (cmd);
1724 }
1725
1726
1727 /**
1728  * Read from the process and call the line processor.
1729  *
1730  * @param cls the 'struct GNUNET_OS_CommandHandle'
1731  * @param tc scheduler context
1732  */
1733 static void
1734 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1735 {
1736   struct GNUNET_OS_CommandHandle *cmd = cls;
1737   GNUNET_OS_LineProcessor proc;
1738   char *end;
1739   ssize_t ret;
1740
1741   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1742   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1743   {
1744     /* timeout, shutdown, etc. */
1745     proc = cmd->proc;
1746     cmd->proc = NULL;
1747     proc (cmd->proc_cls, NULL);
1748     return;
1749   }
1750   ret =
1751       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1752                              sizeof (cmd->buf) - cmd->off);
1753   if (ret <= 0)
1754   {
1755     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1756     {
1757       cmd->buf[cmd->off] = '\0';
1758       cmd->proc (cmd->proc_cls, cmd->buf);
1759     }
1760     proc = cmd->proc;
1761     cmd->proc = NULL;
1762     proc (cmd->proc_cls, NULL);
1763     return;
1764   }
1765   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1766   cmd->off += ret;
1767   while (end != NULL)
1768   {
1769     *end = '\0';
1770     cmd->proc (cmd->proc_cls, cmd->buf);
1771     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1772     cmd->off -= (end + 1 - cmd->buf);
1773     end = memchr (cmd->buf, '\n', cmd->off);
1774   }
1775   cmd->rtask =
1776       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1777                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1778 }
1779
1780
1781 /**
1782  * Run the given command line and call the given function
1783  * for each line of the output.
1784  *
1785  * @param proc function to call for each line of the output
1786  * @param proc_cls closure for proc
1787  * @param timeout when to time out
1788  * @param binary command to run
1789  * @param ... arguments to command
1790  * @return NULL on error
1791  */
1792 struct GNUNET_OS_CommandHandle *
1793 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1794                        struct GNUNET_TIME_Relative timeout, const char *binary,
1795                        ...)
1796 {
1797   struct GNUNET_OS_CommandHandle *cmd;
1798   struct GNUNET_OS_Process *eip;
1799   struct GNUNET_DISK_PipeHandle *opipe;
1800   va_list ap;
1801
1802   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1803   if (NULL == opipe)
1804     return NULL;
1805   va_start (ap, binary);
1806   eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1807   va_end (ap);
1808   if (NULL == eip)
1809   {
1810     GNUNET_DISK_pipe_close (opipe);
1811     return NULL;
1812   }
1813   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1814   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1815   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1816   cmd->eip = eip;
1817   cmd->opipe = opipe;
1818   cmd->proc = proc;
1819   cmd->proc_cls = proc_cls;
1820   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1821   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1822   return cmd;
1823 }
1824
1825
1826
1827
1828 /* end of os_priority.c */