c9174dee9917155efc363d4ffdccb1d28dad2123
[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
965   if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
966     return NULL; /* not executable */
967  
968   /* Search in prefix dir (hopefully - the directory from which
969    * the current module was loaded), bindir and libdir, then in PATH
970    */
971   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
972   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
973   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
974
975   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
976
977   alloc_len =
978       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
979       strlen (libdir);
980
981   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
982
983   ptr = pathbuf;
984   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
985   GNUNET_free (self_prefix);
986   GNUNET_free (bindir);
987   GNUNET_free (libdir);
988
989   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
990   if (alloc_len != pathbuf_len - 1)
991   {
992     GNUNET_free (pathbuf);
993     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
994     return NULL;
995   }
996
997   cmdlen = strlen (filename);
998   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
999     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1000   else
1001     GNUNET_asprintf (&non_const_filename, "%s", filename);
1002
1003   /* It could be in POSIX form, convert it to a DOS path early on */
1004   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1005   {
1006     SetErrnoFromWinError (lRet);
1007     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1008                        non_const_filename);
1009     GNUNET_free (non_const_filename);
1010     GNUNET_free (pathbuf);
1011     return NULL;
1012   }
1013   GNUNET_free (non_const_filename);
1014   non_const_filename = GNUNET_strdup (win_path);
1015    /* Check that this is the full path. If it isn't, search. */
1016   /* FIXME: convert it to wchar_t and use SearchPathW?
1017    * Remember: arguments to _start_process() are technically in UTF-8...
1018    */
1019   if (non_const_filename[1] == ':')
1020     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1021   else if (!SearchPathA
1022            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1023             path, NULL))
1024   {
1025     SetErrnoFromWinError (GetLastError ());
1026     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1027                        non_const_filename);
1028     GNUNET_free (non_const_filename);
1029     GNUNET_free (pathbuf);
1030     return NULL;
1031   }
1032   GNUNET_free (pathbuf);
1033   GNUNET_free (non_const_filename);
1034
1035   /* Count the number of arguments */
1036   arg = (char **) argv;
1037   while (*arg)
1038   {
1039     arg++;
1040     argcount++;
1041   }
1042
1043   /* Allocate a copy argv */
1044   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1045
1046   /* Copy all argv strings */
1047   argcount = 0;
1048   arg = (char **) argv;
1049   while (*arg)
1050   {
1051     if (arg == argv)
1052       non_const_argv[argcount] = GNUNET_strdup (path);
1053     else
1054       non_const_argv[argcount] = GNUNET_strdup (*arg);
1055     arg++;
1056     argcount++;
1057   }
1058   non_const_argv[argcount] = NULL;
1059
1060   /* Count cmd len */
1061   cmdlen = 1;
1062   arg = non_const_argv;
1063   while (*arg)
1064   {
1065     cmdlen = cmdlen + strlen (*arg) + 4;
1066     arg++;
1067   }
1068
1069   /* Allocate and create cmd */
1070   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1071   arg = non_const_argv;
1072   while (*arg)
1073   {
1074     char arg_last_char = (*arg)[strlen (*arg) - 1];
1075     idx += sprintf (idx, "\"%s%s\"%s", *arg,
1076         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1077     arg++;
1078   }
1079
1080   while (argcount > 0)
1081     GNUNET_free (non_const_argv[--argcount]);
1082   GNUNET_free (non_const_argv);
1083
1084   memset (&start, 0, sizeof (start));
1085   start.cb = sizeof (start);
1086   if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
1087     start.dwFlags |= STARTF_USESTDHANDLES;
1088
1089   if (pipe_stdin != NULL)
1090   {
1091     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1092                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1093                                        &stdin_handle, sizeof (HANDLE));
1094     start.hStdInput = stdin_handle;
1095   }
1096
1097   if (pipe_stdout != NULL)
1098   {
1099     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1100                                        (pipe_stdout,
1101                                         GNUNET_DISK_PIPE_END_WRITE),
1102                                        &stdout_handle, sizeof (HANDLE));
1103     start.hStdOutput = stdout_handle;
1104   }
1105
1106   if (GNUNET_YES == pipe_control)
1107   {
1108     control_pipe =
1109       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1110                     GNUNET_DISK_PERM_USER_READ |
1111                     GNUNET_DISK_PERM_USER_WRITE);
1112     if (control_pipe == NULL)
1113     {
1114       GNUNET_free (cmd);
1115       GNUNET_free (path);
1116       return NULL;
1117     }
1118   }
1119   else
1120     control_pipe = NULL;
1121   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1122   {
1123     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1124
1125     if (lsocks_pipe == NULL)
1126     {
1127       GNUNET_free (cmd);
1128       GNUNET_free (path);
1129       GNUNET_DISK_pipe_close (lsocks_pipe);
1130       return NULL;
1131     }
1132     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1133         GNUNET_DISK_PIPE_END_WRITE);
1134     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1135                                        &lsocks_write, sizeof (HANDLE));
1136     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1137                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1138                                        &lsocks_read, sizeof (HANDLE));
1139   }
1140
1141   env_off = 0;
1142   if (NULL != childpipename)
1143   {
1144     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1145          childpipename);
1146     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1147     GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1148     GNUNET_free (childpipename);
1149   }
1150   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1151   {
1152     /*This will tell the child that we're going to send lsocks over the pipe*/
1153     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1154     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1155   }
1156   our_env[env_off++] = NULL;
1157   env_block = CreateCustomEnvTable (our_env);
1158   while (0 > env_off)
1159     GNUNET_free_non_null (our_env[--env_off]);
1160
1161   wpath_len = 0;
1162   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1163   {
1164     LOG (GNUNET_ERROR_TYPE_DEBUG,
1165         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1166     GNUNET_free (env_block);
1167     GNUNET_free (cmd);
1168     return NULL;
1169   }
1170
1171   wcmd_len = 0;
1172   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1173   {
1174     LOG (GNUNET_ERROR_TYPE_DEBUG,
1175         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1176     GNUNET_free (env_block);
1177     GNUNET_free (cmd);
1178     free (wpath);
1179     return NULL;
1180   }
1181
1182   if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1183        DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1184   {
1185     SetErrnoFromWinError (GetLastError ());
1186     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1187     if (NULL != control_pipe)
1188       GNUNET_DISK_file_close (control_pipe);
1189     if (NULL != lsocks)
1190       GNUNET_DISK_pipe_close (lsocks_pipe);
1191     GNUNET_free (env_block);
1192     GNUNET_free (cmd);
1193     free (wpath);
1194     free (wcmd);
1195     return NULL;
1196   }
1197
1198   GNUNET_free (env_block);
1199
1200   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1201   gnunet_proc->pid = proc.dwProcessId;
1202   gnunet_proc->handle = proc.hProcess;
1203   gnunet_proc->control_pipe = control_pipe;
1204
1205   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1206
1207   ResumeThread (proc.hThread);
1208   CloseHandle (proc.hThread);
1209   GNUNET_free (cmd);
1210   free (wpath);
1211   free (wcmd);
1212
1213   if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1214     return gnunet_proc;
1215
1216   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1217
1218   /* This is a replacement for "goto error" that doesn't use goto */
1219   fail = 1;
1220   do
1221   {
1222     int wrote;
1223     uint64_t size, count, i;
1224
1225     /* Tell the number of sockets */
1226     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1227
1228     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1229     if (wrote != sizeof (count))
1230     {
1231       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1232       break;
1233     }
1234     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1235     {
1236       WSAPROTOCOL_INFOA pi;
1237       /* Get a socket duplication info */
1238       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1239       {
1240         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1241         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1242         break;
1243       }
1244       /* Synchronous I/O is not nice, but we can't schedule this:
1245        * lsocks will be closed/freed by the caller soon, and until
1246        * the child creates a duplicate, closing a socket here will
1247        * close it for good.
1248        */
1249       /* Send the size of the structure
1250        * (the child might be built with different headers...)
1251        */
1252       size = sizeof (pi);
1253       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1254       if (wrote != sizeof (size))
1255       {
1256         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1257         break;
1258       }
1259       /* Finally! Send the data */
1260       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1261       if (wrote != sizeof (pi))
1262       {
1263         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1264         break;
1265       }
1266     }
1267     /* This will block us until the child makes a final read or closes
1268      * the pipe (hence no 'wrote' check), since we have to wait for it
1269      * to duplicate the last socket, before we return and start closing
1270      * our own copies)
1271      */
1272     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1273     fail = 0;
1274   }
1275   while (fail);
1276
1277   GNUNET_DISK_file_sync (lsocks_write_fd);
1278   GNUNET_DISK_pipe_close (lsocks_pipe);
1279
1280   if (fail)
1281   {
1282     /* If we can't pass on the socket(s), the child will block forever,
1283      * better put it out of its misery.
1284      */
1285     TerminateProcess (gnunet_proc->handle, 0);
1286     CloseHandle (gnunet_proc->handle);
1287     if (NULL != gnunet_proc->control_pipe)
1288       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1289     GNUNET_free (gnunet_proc);
1290     return NULL;
1291   }
1292   return gnunet_proc;
1293 #endif
1294 }
1295
1296
1297
1298
1299 /**
1300  * Start a process.
1301  *
1302  * @param pipe_control should a pipe be used to send signals to the child?
1303  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1304  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1305  * @param filename name of the binary
1306  * @param argv NULL-terminated array of arguments to the process
1307  * @return pointer to process structure of the new process, NULL on error
1308  */
1309 struct GNUNET_OS_Process *
1310 GNUNET_OS_start_process_vap (int pipe_control,
1311                              struct GNUNET_DISK_PipeHandle *pipe_stdin,
1312                              struct GNUNET_DISK_PipeHandle *pipe_stdout,
1313                              const char *filename, 
1314                              char *const argv[])
1315 {
1316   return start_process (pipe_control,
1317                         pipe_stdin,
1318                         pipe_stdout,
1319                         NULL,
1320                         filename,
1321                         argv);
1322 }
1323
1324
1325 /**
1326  * Start a process.
1327  *
1328  * @param pipe_control should a pipe be used to send signals to the child?
1329  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1330  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1331  * @param filename name of the binary
1332  * @param va NULL-terminated list of arguments to the process
1333  * @return pointer to process structure of the new process, NULL on error
1334  */
1335 struct GNUNET_OS_Process *
1336 GNUNET_OS_start_process_va (int pipe_control,
1337                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1338                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1339                             const char *filename, va_list va)
1340 {
1341   struct GNUNET_OS_Process *ret;
1342   va_list ap;
1343   char **argv;
1344   int argc;
1345
1346   argc = 0;
1347   va_copy (ap, va);
1348   while (NULL != va_arg (ap, char *))
1349     argc++;
1350   va_end (ap);
1351   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1352   argc = 0;
1353   va_copy (ap, va);
1354   while (NULL != (argv[argc] = va_arg (ap, char *)))
1355     argc++;
1356   va_end (ap);
1357   ret = GNUNET_OS_start_process_vap (pipe_control,
1358                                      pipe_stdin,
1359                                      pipe_stdout,
1360                                      filename,
1361                                      argv);
1362   GNUNET_free (argv);
1363   return ret;
1364 }
1365
1366
1367
1368 /**
1369  * Start a process.
1370  *
1371  * @param pipe_control should a pipe be used to send signals to the child?
1372  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1373  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1374  * @param filename name of the binary
1375  * @param ... NULL-terminated list of arguments to the process
1376  *
1377  * @return pointer to process structure of the new process, NULL on error
1378  *
1379  */
1380 struct GNUNET_OS_Process *
1381 GNUNET_OS_start_process (int pipe_control,
1382                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1383                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1384                          const char *filename, ...)
1385 {
1386   struct GNUNET_OS_Process *ret;
1387   va_list ap;
1388
1389   va_start (ap, filename);
1390   ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1391   va_end (ap);
1392   return ret;
1393 }
1394
1395
1396 /**
1397  * Start a process.
1398  *
1399  * @param pipe_control should a pipe be used to send signals to the child?
1400  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1401  *         must be NULL on platforms where dup is not supported
1402  * @param filename name of the binary
1403  * @param argv NULL-terminated list of arguments to the process
1404  * @return process ID of the new process, -1 on error
1405  */
1406 struct GNUNET_OS_Process *
1407 GNUNET_OS_start_process_v (int pipe_control,
1408                            const SOCKTYPE *lsocks,
1409                            const char *filename,
1410                            char *const argv[])
1411 {
1412   return start_process (pipe_control,
1413                         NULL,
1414                         NULL,
1415                         lsocks,
1416                         filename,
1417                         argv);
1418 }
1419
1420
1421 /**
1422  * Retrieve the status of a process, waiting on him if dead.
1423  * Nonblocking version.
1424  * 
1425  * @param proc process ID
1426  * @param type status type
1427  * @param code return code/signal number
1428  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1429  */
1430 int
1431 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1432                           enum GNUNET_OS_ProcessStatusType *type,
1433                           unsigned long *code)
1434 {
1435 #ifndef MINGW
1436   int status;
1437   int ret;
1438
1439   GNUNET_assert (0 != proc);
1440   ret = waitpid (proc->pid, &status, WNOHANG);
1441   if (ret < 0)
1442   {
1443     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1444     return GNUNET_SYSERR;
1445   }
1446   if (0 == ret)
1447   {
1448     *type = GNUNET_OS_PROCESS_RUNNING;
1449     *code = 0;
1450     return GNUNET_NO;
1451   }
1452   if (proc->pid != ret)
1453   {
1454     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1455     return GNUNET_SYSERR;
1456   }
1457   if (WIFEXITED (status))
1458   {
1459     *type = GNUNET_OS_PROCESS_EXITED;
1460     *code = WEXITSTATUS (status);
1461   }
1462   else if (WIFSIGNALED (status))
1463   {
1464     *type = GNUNET_OS_PROCESS_SIGNALED;
1465     *code = WTERMSIG (status);
1466   }
1467   else if (WIFSTOPPED (status))
1468   {
1469     *type = GNUNET_OS_PROCESS_SIGNALED;
1470     *code = WSTOPSIG (status);
1471   }
1472 #ifdef WIFCONTINUED
1473   else if (WIFCONTINUED (status))
1474   {
1475     *type = GNUNET_OS_PROCESS_RUNNING;
1476     *code = 0;
1477   }
1478 #endif
1479   else
1480   {
1481     *type = GNUNET_OS_PROCESS_UNKNOWN;
1482     *code = 0;
1483   }
1484 #else
1485   HANDLE h;
1486   DWORD c, error_code, ret;
1487
1488   h = proc->handle;
1489   ret = proc->pid;
1490   if (h == NULL || ret == 0)
1491   {
1492     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1493          ret, h);
1494     return GNUNET_SYSERR;
1495   }
1496   if (h == NULL)
1497     h = GetCurrentProcess ();
1498
1499   SetLastError (0);
1500   ret = GetExitCodeProcess (h, &c);
1501   error_code = GetLastError ();
1502   if (ret == 0 || error_code != NO_ERROR)
1503   {
1504     SetErrnoFromWinError (error_code);
1505     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1506     return GNUNET_SYSERR;
1507   }
1508   if (STILL_ACTIVE == c)
1509   {
1510     *type = GNUNET_OS_PROCESS_RUNNING;
1511     *code = 0;
1512     return GNUNET_NO;
1513   }
1514   *type = GNUNET_OS_PROCESS_EXITED;
1515   *code = c;
1516 #endif
1517
1518   return GNUNET_OK;
1519 }
1520
1521
1522 /**
1523  * Wait for a process
1524  * @param proc pointer to process structure
1525  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1526  */
1527 int
1528 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1529 {
1530
1531 #ifndef MINGW
1532   pid_t pid = proc->pid;
1533   pid_t ret;
1534
1535   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1536           (EINTR == errno) ) ;
1537   if (pid != ret) 
1538   {
1539     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1540     return GNUNET_SYSERR;
1541   }
1542   return GNUNET_OK;
1543 #else
1544   HANDLE h;
1545   int ret;
1546
1547   h = proc->handle;
1548   if (NULL == h)
1549   {
1550     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1551          proc->pid, h);
1552     return GNUNET_SYSERR;
1553   }
1554   if (h == NULL)
1555     h = GetCurrentProcess ();
1556
1557   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1558   {
1559     SetErrnoFromWinError (GetLastError ());
1560     ret = GNUNET_SYSERR;
1561   }
1562   else
1563     ret = GNUNET_OK;
1564
1565   return ret;
1566 #endif
1567 }
1568
1569
1570 /**
1571  * Handle to a command.
1572  */
1573 struct GNUNET_OS_CommandHandle
1574 {
1575
1576   /**
1577    * Process handle.
1578    */
1579   struct GNUNET_OS_Process *eip;
1580
1581   /**
1582    * Handle to the output pipe.
1583    */
1584   struct GNUNET_DISK_PipeHandle *opipe;
1585
1586   /**
1587    * Read-end of output pipe.
1588    */
1589   const struct GNUNET_DISK_FileHandle *r;
1590
1591   /**
1592    * Function to call on each line of output.
1593    */
1594   GNUNET_OS_LineProcessor proc;
1595
1596   /**
1597    * Closure for 'proc'.
1598    */
1599   void *proc_cls;
1600
1601   /**
1602    * Buffer for the output.
1603    */
1604   char buf[1024];
1605
1606   /**
1607    * Task reading from pipe.
1608    */
1609   GNUNET_SCHEDULER_TaskIdentifier rtask;
1610
1611   /**
1612    * When to time out.
1613    */
1614   struct GNUNET_TIME_Absolute timeout;
1615
1616   /**
1617    * Current read offset in buf.
1618    */
1619   size_t off;
1620 };
1621
1622
1623 /**
1624  * Stop/kill a command.  Must ONLY be called either from
1625  * the callback after 'NULL' was passed for 'line' *OR*
1626  * from an independent task (not within the line processor).
1627  *
1628  * @param cmd handle to the process
1629  */
1630 void
1631 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1632 {
1633
1634   if (cmd->proc != NULL)
1635   {
1636     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1637     GNUNET_SCHEDULER_cancel (cmd->rtask);
1638   }
1639   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1640   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1641   GNUNET_OS_process_destroy (cmd->eip);
1642   GNUNET_DISK_pipe_close (cmd->opipe);
1643   GNUNET_free (cmd);
1644 }
1645
1646
1647 /**
1648  * Read from the process and call the line processor.
1649  *
1650  * @param cls the 'struct GNUNET_OS_CommandHandle'
1651  * @param tc scheduler context
1652  */
1653 static void
1654 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1655 {
1656   struct GNUNET_OS_CommandHandle *cmd = cls;
1657   GNUNET_OS_LineProcessor proc;
1658   char *end;
1659   ssize_t ret;
1660
1661   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1662   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1663   {
1664     /* timeout, shutdown, etc. */
1665     proc = cmd->proc;
1666     cmd->proc = NULL;
1667     proc (cmd->proc_cls, NULL);
1668     return;
1669   }
1670   ret =
1671       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1672                              sizeof (cmd->buf) - cmd->off);
1673   if (ret <= 0)
1674   {
1675     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1676     {
1677       cmd->buf[cmd->off] = '\0';
1678       cmd->proc (cmd->proc_cls, cmd->buf);
1679     }
1680     proc = cmd->proc;
1681     cmd->proc = NULL;
1682     proc (cmd->proc_cls, NULL);
1683     return;
1684   }
1685   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1686   cmd->off += ret;
1687   while (end != NULL)
1688   {
1689     *end = '\0';
1690     cmd->proc (cmd->proc_cls, cmd->buf);
1691     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1692     cmd->off -= (end + 1 - cmd->buf);
1693     end = memchr (cmd->buf, '\n', cmd->off);
1694   }
1695   cmd->rtask =
1696       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1697                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1698 }
1699
1700
1701 /**
1702  * Run the given command line and call the given function
1703  * for each line of the output.
1704  *
1705  * @param proc function to call for each line of the output
1706  * @param proc_cls closure for proc
1707  * @param timeout when to time out
1708  * @param binary command to run
1709  * @param ... arguments to command
1710  * @return NULL on error
1711  */
1712 struct GNUNET_OS_CommandHandle *
1713 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1714                        struct GNUNET_TIME_Relative timeout, const char *binary,
1715                        ...)
1716 {
1717   struct GNUNET_OS_CommandHandle *cmd;
1718   struct GNUNET_OS_Process *eip;
1719   struct GNUNET_DISK_PipeHandle *opipe;
1720   va_list ap;
1721
1722   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1723   if (NULL == opipe)
1724     return NULL;
1725   va_start (ap, binary);
1726   eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1727   va_end (ap);
1728   if (NULL == eip)
1729   {
1730     GNUNET_DISK_pipe_close (opipe);
1731     return NULL;
1732   }
1733   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1734   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1735   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1736   cmd->eip = eip;
1737   cmd->opipe = opipe;
1738   cmd->proc = proc;
1739   cmd->proc_cls = proc_cls;
1740   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1741   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1742   return cmd;
1743 }
1744
1745
1746
1747
1748 /* end of os_priority.c */