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