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