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