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