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