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