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