MAntis 1616, 0001-Temporary-fix-for-process_kill.patch
[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 "disk.h"
32
33 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
34
35 struct GNUNET_OS_Process
36 {
37   pid_t pid;
38 #if WINDOWS
39   HANDLE handle;
40 #endif
41   int sig;
42   struct GNUNET_DISK_FileHandle *control_pipe;
43 };
44
45 static struct GNUNET_OS_Process current_process;
46
47
48 /**
49  * This handler is called when there are control data to be read on the pipe
50  */
51 void
52 GNUNET_OS_parent_control_handler (void *cls,
53                                   const struct
54                                   GNUNET_SCHEDULER_TaskContext * tc)
55 {
56   struct GNUNET_DISK_FileHandle *control_pipe = (struct GNUNET_DISK_FileHandle *) cls;
57   int sig;
58
59   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__, tc->reason);
60
61   if (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE))
62   {
63     GNUNET_DISK_npipe_close (control_pipe);
64   }
65   else
66   {
67     if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) != sizeof (sig))
68     {
69       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
70       GNUNET_DISK_npipe_close (control_pipe);
71     }
72     else
73     {
74       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
75       raise (sig);
76       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Re-scheduling the parent control handler pipe\n");
77       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe, GNUNET_OS_parent_control_handler, control_pipe);
78     }
79   }
80 }
81
82 /**
83  * Connects this process to its parent via pipe
84  */
85 void
86 GNUNET_OS_install_parent_control_handler (void *cls,
87                                           const struct
88                                           GNUNET_SCHEDULER_TaskContext * tc)
89 {
90   char *env_buf;
91   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
92
93   env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
94   if (env_buf == NULL || strlen (env_buf) <= 0)
95   {
96     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not installing a handler because %s=%s\n", GNUNET_OS_CONTROL_PIPE, env_buf);
97     return;
98   }
99
100   control_pipe = GNUNET_DISK_npipe_open (env_buf, GNUNET_DISK_OPEN_READ,
101         GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
102   if (control_pipe == NULL)
103   {
104     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to open the pipe `%s'\n", env_buf);
105     return;
106   }
107
108   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
109   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe, GNUNET_OS_parent_control_handler, control_pipe);
110 }
111
112 /**
113  * Get process structure for current process
114  *
115  * The pointer it returns points to static memory location and must not be
116  * deallocated/closed
117  *
118  * @return pointer to the process sturcutre for this process
119  */
120 struct GNUNET_OS_Process *
121 GNUNET_OS_process_current ()
122 {
123 #if WINDOWS
124   current_process.pid = GetCurrentProcessId ();
125   current_process.handle = GetCurrentProcess ();
126 #else
127   current_process.pid = 0;
128 #endif
129   return &current_process;
130 }
131
132 int
133 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
134 {
135 #if ENABLE_WINDOWS_WORKAROUNDS
136   int res;
137   int ret;
138
139   ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof(sig));
140   if (ret != sizeof(sig))
141   {
142     if (errno == ECOMM)
143       /* Child process is not controllable via pipe */
144       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145           "Child process is not controllable, will kill it directly\n");
146     else
147       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
148           "Failed to write into control pipe , errno is %d\n", errno);
149 #if WINDOWS
150     res = 0;
151     TerminateProcess (proc->handle, 0);
152 #else
153     res = PLIBC_KILL (proc->pid, sig);
154 #endif
155   }
156   else
157   {
158     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159         "Wrote control code into control pipe, now waiting\n");
160     WaitForSingleObject (proc->handle, 5000);
161 #if WINDOWS
162     TerminateProcess (proc->handle, 0);
163 #else
164     PLIBC_KILL (proc->pid, sig);
165 #endif
166     res = 0;
167   }
168   return res;
169 #else
170   return kill (proc->pid, sig);
171 #endif
172 }
173
174 /**
175  * Get the pid of the process in question
176  *
177  * @param proc the process to get the pid of
178  *
179  * @return the current process id
180  */
181 pid_t
182 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
183 {
184   return proc->pid;
185 }
186
187 void
188 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
189 {
190 #if ENABLE_WINDOWS_WORKAROUNDS
191   if (proc->control_pipe)
192     GNUNET_DISK_npipe_close (proc->control_pipe);
193 #endif
194 // FIXME NILS
195 #ifdef WINDOWS
196   if (proc->handle != NULL)
197     CloseHandle (proc->handle);
198 #endif
199   GNUNET_free (proc);
200 }
201
202 // FIXME NILS
203 #if WINDOWS
204 #include "gnunet_signal_lib.h"
205
206 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
207
208 /**
209  * Make seaspider happy.
210  */
211 #define DWORD_WINAPI DWORD WINAPI
212
213 /**
214  * @brief Waits for a process to terminate and invokes the SIGCHLD handler
215  * @param proc pointer to process structure
216  */
217 static DWORD_WINAPI
218 ChildWaitThread (void *arg)
219 {
220   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
221   WaitForSingleObject (proc->handle, INFINITE);
222
223   if (w32_sigchld_handler)
224     w32_sigchld_handler ();
225
226   return 0;
227 }
228 #endif
229
230 /**
231  * Set process priority
232  *
233  * @param proc pointer to process structure
234  * @param prio priority value
235  * @return GNUNET_OK on success, GNUNET_SYSERR on error
236  */
237 int
238 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
239                                 enum GNUNET_SCHEDULER_Priority prio)
240 {
241   int rprio;
242
243   GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
244   if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
245     return GNUNET_OK;
246
247   /* convert to MINGW/Unix values */
248   switch (prio)
249     {
250     case GNUNET_SCHEDULER_PRIORITY_UI:
251     case GNUNET_SCHEDULER_PRIORITY_URGENT:
252 #ifdef MINGW
253       rprio = HIGH_PRIORITY_CLASS;
254 #else
255       rprio = 0;
256 #endif
257       break;
258
259     case GNUNET_SCHEDULER_PRIORITY_HIGH:
260 #ifdef MINGW
261       rprio = ABOVE_NORMAL_PRIORITY_CLASS;
262 #else
263       rprio = 5;
264 #endif
265       break;
266
267     case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
268 #ifdef MINGW
269       rprio = NORMAL_PRIORITY_CLASS;
270 #else
271       rprio = 7;
272 #endif
273       break;
274
275     case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
276 #ifdef MINGW
277       rprio = BELOW_NORMAL_PRIORITY_CLASS;
278 #else
279       rprio = 10;
280 #endif
281       break;
282
283     case GNUNET_SCHEDULER_PRIORITY_IDLE:
284 #ifdef MINGW
285       rprio = IDLE_PRIORITY_CLASS;
286 #else
287       rprio = 19;
288 #endif
289       break;
290     default:
291       GNUNET_assert (0);
292       return GNUNET_SYSERR;
293     }
294
295   /* Set process priority */
296 #ifdef MINGW
297   {
298     HANDLE h = proc->handle;
299     GNUNET_assert (h != NULL);
300     SetPriorityClass (h, rprio);
301   }
302 #elif LINUX 
303   pid_t pid;
304
305   pid = proc->pid;
306   if ( (0 == pid) ||
307        (pid == getpid () ) )
308     {
309       int have = nice (0);
310       int delta = rprio - have;
311       errno = 0;
312       if ( (delta != 0) &&
313            (rprio == nice (delta)) && 
314            (errno != 0) )
315         {
316           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
317                                GNUNET_ERROR_TYPE_BULK, "nice");
318           return GNUNET_SYSERR;
319         }
320     }
321   else
322     {
323       if (0 != setpriority (PRIO_PROCESS, pid, rprio))
324         {
325           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
326                                GNUNET_ERROR_TYPE_BULK, "setpriority");
327           return GNUNET_SYSERR;
328         }
329     }
330 #else
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
332               "Priority management not availabe for this platform\n");
333 #endif
334   return GNUNET_OK;
335 }
336
337 #if MINGW
338 static char *
339 CreateCustomEnvTable (char **vars)
340 {
341   char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
342   size_t tablesize = 0;
343   size_t items_count = 0;
344   size_t n_found = 0, n_var;
345   char *index = NULL;
346   size_t c;
347   size_t var_len;
348   char *var;
349   char *val;
350   win32_env_table = GetEnvironmentStringsA ();
351   if (win32_env_table == NULL)
352     return NULL;
353   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
354   n_var = c;
355   index = GNUNET_malloc (n_var);
356   for (c = 0; c < n_var; c++)
357     index[c] = 0;
358   for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
359   {
360     size_t len = strlen (ptr);
361     int found = 0;
362     for (var_ptr = vars; *var_ptr; var_ptr++)
363     {
364       var = *var_ptr++;
365       val = *var_ptr;
366       var_len = strlen (var);
367       if (strncmp (var, ptr, var_len) == 0)
368       {
369         found = 1;
370         index[c] = 1;
371         tablesize += var_len + strlen (val) + 1;
372         break;
373       }
374     }
375     if (!found)
376       tablesize += len + 1;
377     ptr += len + 1; 
378   }
379   for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
380   {
381     var = *var_ptr++;
382     val = *var_ptr;
383     if (index[c] != 1)
384       n_found += strlen (var) + strlen (val) + 1;
385   }
386   result = GNUNET_malloc (tablesize + n_found + 1);
387   for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
388   {
389     size_t len = strlen (ptr);
390     int found = 0;
391     for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
392     {
393       var = *var_ptr++;
394       val = *var_ptr;
395       var_len = strlen (var);
396       if (strncmp (var, ptr, var_len) == 0)
397       {
398         found = 1;
399         break;
400       }
401     }
402     if (!found)
403     {
404       strcpy (result_ptr, ptr);
405       result_ptr += len + 1;
406     }
407     else
408     {
409       strcpy (result_ptr, var);
410       result_ptr += var_len;
411       strcpy (result_ptr, val);
412       result_ptr += strlen (val) + 1;
413     }
414     ptr += len + 1;
415   }
416   for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
417   {
418     var = *var_ptr++;
419     val = *var_ptr;
420     var_len = strlen (var);
421     if (index[c] != 1)
422     {
423       strcpy (result_ptr, var);
424       result_ptr += var_len;
425       strcpy (result_ptr, val);
426       result_ptr += strlen (val) + 1;
427     }
428   }
429   FreeEnvironmentStrings (win32_env_table);
430   GNUNET_free (index);
431   *result_ptr = 0;
432   return result;
433 }
434 #endif
435
436
437 /**
438  * Start a process.
439  *
440  * @param pipe_stdin pipe to use to send input to child process (or NULL)
441  * @param pipe_stdout pipe to use to get output from child process (or NULL)
442  * @param filename name of the binary
443  * @param va NULL-terminated list of arguments to the process
444  * @return pointer to process structure of the new process, NULL on error
445  */
446 struct GNUNET_OS_Process *
447 GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin, 
448                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
449                             const char *filename, 
450                             va_list va)
451 {
452   va_list ap;
453 #if ENABLE_WINDOWS_WORKAROUNDS
454   char *childpipename = NULL;
455   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
456 #endif
457   struct GNUNET_OS_Process *gnunet_proc = NULL;
458
459 #ifndef MINGW
460   pid_t ret;
461   char **argv;
462   int argc;
463   int fd_stdout_write;
464   int fd_stdout_read;
465   int fd_stdin_read;
466   int fd_stdin_write;
467
468 #if ENABLE_WINDOWS_WORKAROUNDS
469   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
470       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
471       GNUNET_DISK_PERM_USER_WRITE);
472   if (control_pipe == NULL)
473     return NULL;
474 #endif
475
476   argc = 0;
477   va_copy (ap, va);
478   while (NULL != va_arg (ap, char *))
479       argc++;
480   va_end (ap);
481   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
482   argc = 0;
483   va_copy (ap, va);
484   while (NULL != (argv[argc] = va_arg (ap, char *)))
485     argc++;
486   va_end (ap);
487   if (pipe_stdout != NULL)
488     {
489       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
490       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
491     }
492   if (pipe_stdin != NULL)
493     {
494       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
495       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
496     }
497
498 #if HAVE_WORKING_VFORK
499   ret = vfork ();
500 #else
501   ret = fork ();
502 #endif
503   if (ret != 0)
504     {
505       if (ret == -1)
506         {
507           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
508 #if ENABLE_WINDOWS_WORKAROUNDS
509           GNUNET_DISK_npipe_close (control_pipe);
510 #endif
511         }
512       else
513         {
514
515 #if HAVE_WORKING_VFORK
516           /* let's hope vfork actually works; for some extreme cases (including
517              a testcase) we need 'execvp' to have run before we return, since
518              we may send a signal to the process next and we don't want it
519              to be caught by OUR signal handler (but either by the default
520              handler or the actual handler as installed by the process itself). */
521 #else
522           /* let's give the child process a chance to run execvp, 1s should
523              be plenty in practice */
524           if (pipe_stdout != NULL)
525             GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
526           if (pipe_stdin != NULL)
527             GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
528           sleep (1);
529 #endif
530           gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
531           gnunet_proc->pid = ret;
532 #if ENABLE_WINDOWS_WORKAROUNDS
533           gnunet_proc->control_pipe = control_pipe;
534 #endif
535         }
536       GNUNET_free (argv);
537 #if ENABLE_WINDOWS_WORKAROUNDS
538       GNUNET_free (childpipename);
539 #endif
540       return gnunet_proc;
541     }
542
543 #if ENABLE_WINDOWS_WORKAROUNDS
544   setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
545   GNUNET_free (childpipename);
546 #endif
547
548   if (pipe_stdout != NULL)
549     {
550       GNUNET_break (0 == close (fd_stdout_read));
551       if (-1 == dup2(fd_stdout_write, 1))
552         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");  
553       GNUNET_break (0 == close (fd_stdout_write));
554     }
555
556   if (pipe_stdin != NULL)
557     {
558
559       GNUNET_break (0 == close (fd_stdin_write));
560       if (-1 == dup2(fd_stdin_read, 0))
561         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");  
562       GNUNET_break (0 == close (fd_stdin_read));
563     }
564   execvp (filename, argv);
565   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
566   _exit (1);
567 #else
568   char *arg;
569   unsigned int cmdlen;
570   char *cmd, *idx;
571   STARTUPINFO start;
572   PROCESS_INFORMATION proc;
573
574   HANDLE stdin_handle;
575   HANDLE stdout_handle;
576
577   char path[MAX_PATH + 1];
578
579   char *our_env[3] = { NULL, NULL, NULL };
580   char *env_block = NULL;
581   char *pathbuf;
582   DWORD pathbuf_len, alloc_len;
583   char *self_prefix;
584   char *bindir;
585   char *libdir;
586   char *ptr;
587   char *non_const_filename;
588
589   /* Search in prefix dir (hopefully - the directory from which
590    * the current module was loaded), bindir and libdir, then in PATH
591    */
592   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
593   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
594   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
595
596   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
597
598   alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
599
600   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
601
602   ptr = pathbuf;
603   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
604   GNUNET_free (self_prefix);
605   GNUNET_free (bindir);
606   GNUNET_free (libdir);
607
608   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
609   GNUNET_assert (alloc_len == (pathbuf_len - 1));
610
611   cmdlen = strlen (filename);
612   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
613     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
614   else
615     GNUNET_asprintf (&non_const_filename, "%s", filename);
616
617   /* Check that this is the full path. If it isn't, search. */
618   if (non_const_filename[1] == ':')
619     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
620   else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
621     {
622       SetErrnoFromWinError (GetLastError ());
623       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
624       GNUNET_free (non_const_filename);
625       GNUNET_free (pathbuf);
626       return NULL;
627     }
628   GNUNET_free (pathbuf);
629   GNUNET_free (non_const_filename);
630  
631   cmdlen = 0;
632   va_copy (ap, va);
633   while (NULL != (arg = va_arg (ap, char *)))
634   {
635       if (cmdlen == 0)
636         cmdlen = cmdlen + strlen (path) + 3;
637       else
638         cmdlen = cmdlen + strlen (arg) + 3;
639   }
640   va_end (ap);
641
642   cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
643   va_copy (ap, va);
644   while (NULL != (arg = va_arg (ap, char *)))
645   {
646       if (idx == cmd)
647         idx += sprintf (idx, "\"%s\" ", path);
648       else
649         idx += sprintf (idx, "\"%s\" ", arg);
650   }
651   va_end (ap);
652
653   memset (&start, 0, sizeof (start));
654   start.cb = sizeof (start);
655
656   if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
657     start.dwFlags |= STARTF_USESTDHANDLES;
658
659   if (pipe_stdin != NULL)
660     {
661       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
662       start.hStdInput = stdin_handle;
663     }
664
665   if (pipe_stdout != NULL)
666     {
667       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
668       start.hStdOutput = stdout_handle;
669     }
670
671   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
672       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
673       GNUNET_DISK_PERM_USER_WRITE);
674   if (control_pipe == NULL)
675   {
676     GNUNET_free (cmd);
677     GNUNET_free (path);
678     return NULL;
679   }
680
681   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
682               "Opened the parent end of the pipe `%s'\n", 
683               childpipename);
684
685   GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
686   GNUNET_asprintf (&our_env[1], "%s", childpipename);
687   our_env[2] = NULL;
688   env_block = CreateCustomEnvTable (our_env);
689   GNUNET_free (our_env[0]);
690   GNUNET_free (our_env[1]);
691
692   if (!CreateProcessA
693       (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
694        env_block, NULL, &start, &proc))
695     {
696       SetErrnoFromWinError (GetLastError ());
697       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
698       GNUNET_free (env_block);
699       GNUNET_free (cmd);
700       return NULL;
701     }
702
703   GNUNET_free (env_block);
704
705   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
706   gnunet_proc->pid = proc.dwProcessId;
707   gnunet_proc->handle = proc.hProcess;
708   gnunet_proc->control_pipe = control_pipe;
709
710   CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
711
712   ResumeThread (proc.hThread);
713   CloseHandle (proc.hThread);
714
715   GNUNET_free (cmd);
716
717   return gnunet_proc;
718 #endif
719 }
720
721
722 /**
723  * Start a process.
724  *
725  * @param pipe_stdin pipe to use to send input to child process (or NULL)
726  * @param pipe_stdout pipe to use to get output from child process (or NULL)
727  * @param filename name of the binary
728  * @param ... NULL-terminated list of arguments to the process
729  *
730  * @return pointer to process structure of the new process, NULL on error
731  *
732  */
733 struct GNUNET_OS_Process *
734 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, 
735                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
736                          const char *filename, ...)
737 {
738   struct GNUNET_OS_Process *ret;
739   va_list ap;
740
741   va_start (ap, filename);
742   ret = GNUNET_OS_start_process_va (pipe_stdin,
743                                     pipe_stdout,
744                                     filename,
745                                     ap);
746   va_end (ap);
747   return ret;
748 }
749
750
751 /**
752  * Start a process.
753  *
754  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
755  *         must be NULL on platforms where dup is not supported
756  * @param filename name of the binary
757  * @param argv NULL-terminated list of arguments to the process
758  * @return process ID of the new process, -1 on error
759  */
760 struct GNUNET_OS_Process *
761 GNUNET_OS_start_process_v (const int *lsocks,
762                            const char *filename, char *const argv[])
763 {
764 #if ENABLE_WINDOWS_WORKAROUNDS
765   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
766   char *childpipename = NULL;
767 #endif
768
769 #ifndef MINGW
770   pid_t ret;
771   char lpid[16];
772   char fds[16];
773   struct GNUNET_OS_Process *gnunet_proc = NULL;
774   int i;
775   int j;
776   int k;
777   int tgt;
778   int flags;
779   int *lscp;
780   unsigned int ls;    
781
782 #if ENABLE_WINDOWS_WORKAROUNDS
783   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
784       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
785       GNUNET_DISK_PERM_USER_WRITE);
786   if (control_pipe == NULL)
787     return NULL;
788 #endif
789
790   lscp = NULL;
791   ls = 0;
792   if (lsocks != NULL)
793     {
794       i = 0;
795       while (-1 != (k = lsocks[i++]))
796         GNUNET_array_append (lscp, ls, k);      
797       GNUNET_array_append (lscp, ls, -1);
798     }
799 #if HAVE_WORKING_VFORK
800   ret = vfork ();
801 #else
802   ret = fork ();
803 #endif
804   if (ret != 0)
805     {
806       if (ret == -1)
807         {
808           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
809 #if ENABLE_WINDOWS_WORKAROUNDS
810           GNUNET_DISK_npipe_close (control_pipe);
811 #endif
812         }
813       else
814         {
815 #if HAVE_WORKING_VFORK
816           /* let's hope vfork actually works; for some extreme cases (including
817              a testcase) we need 'execvp' to have run before we return, since
818              we may send a signal to the process next and we don't want it
819              to be caught by OUR signal handler (but either by the default
820              handler or the actual handler as installed by the process itself). */
821 #else
822           /* let's give the child process a chance to run execvp, 1s should
823              be plenty in practice */
824           sleep (1);
825 #endif
826           gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
827           gnunet_proc->pid = ret;
828 #if ENABLE_WINDOWS_WORKAROUNDS
829           gnunet_proc->control_pipe = control_pipe;
830
831 #endif
832         }
833       GNUNET_array_grow (lscp, ls, 0);
834 #if ENABLE_WINDOWS_WORKAROUNDS
835       GNUNET_free (childpipename);
836 #endif
837       return gnunet_proc;
838     }
839
840 #if ENABLE_WINDOWS_WORKAROUNDS
841         setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
842         GNUNET_free (childpipename);
843 #endif
844
845   if (lscp != NULL)
846     {
847       /* read systemd documentation... */
848       GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
849       setenv ("LISTEN_PID", lpid, 1);      
850       i = 0;
851       tgt = 3;
852       while (-1 != lscp[i])
853         {
854           j = i + 1;
855           while (-1 != lscp[j])
856             {
857               if (lscp[j] == tgt)
858                 {
859                   /* dup away */
860                   k = dup (lscp[j]);
861                   GNUNET_assert (-1 != k);
862                   GNUNET_assert (0 == close (lscp[j]));
863                   lscp[j] = k;
864                   break;
865                 }
866               j++;
867             }
868           if (lscp[i] != tgt)
869             {
870               /* Bury any existing FD, no matter what; they should all be closed
871                  on exec anyway and the important onces have been dup'ed away */
872               (void) close (tgt);             
873               GNUNET_assert (-1 != dup2 (lscp[i], tgt));
874             }
875           /* unset close-on-exec flag */
876           flags = fcntl (tgt, F_GETFD);
877           GNUNET_assert (flags >= 0);
878           flags &= ~FD_CLOEXEC;
879           fflush (stderr);
880           (void) fcntl (tgt, F_SETFD, flags);
881           tgt++;
882           i++;
883         }
884       GNUNET_snprintf (fds, sizeof (fds), "%u", i);
885       setenv ("LISTEN_FDS", fds, 1); 
886     }
887   GNUNET_array_grow (lscp, ls, 0);
888   execvp (filename, argv);
889   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
890   _exit (1);
891 #else
892   char **arg, **non_const_argv;
893   unsigned int cmdlen;
894   char *cmd, *idx;
895   STARTUPINFO start;
896   PROCESS_INFORMATION proc;
897   int argcount = 0;
898   struct GNUNET_OS_Process *gnunet_proc = NULL;
899
900   char path[MAX_PATH + 1];
901
902   char *our_env[3] = { NULL, NULL, NULL };
903   char *env_block = NULL;
904   char *pathbuf;
905   DWORD pathbuf_len, alloc_len;
906   char *self_prefix;
907   char *bindir;
908   char *libdir;
909   char *ptr;
910   char *non_const_filename;
911
912   GNUNET_assert (lsocks == NULL);
913
914   /* Search in prefix dir (hopefully - the directory from which
915    * the current module was loaded), bindir and libdir, then in PATH
916    */
917   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
918   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
919   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
920
921   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
922
923   alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
924
925   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
926
927   ptr = pathbuf;
928   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
929   GNUNET_free (self_prefix);
930   GNUNET_free (bindir);
931   GNUNET_free (libdir);
932
933   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
934   if (alloc_len != pathbuf_len - 1)
935   {
936     GNUNET_free (pathbuf);
937     errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
938     return NULL;
939   }
940
941   cmdlen = strlen (filename);
942   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
943     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
944   else
945     GNUNET_asprintf (&non_const_filename, "%s", filename);
946
947   /* Check that this is the full path. If it isn't, search. */
948   if (non_const_filename[1] == ':')
949     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
950   else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
951     {
952       SetErrnoFromWinError (GetLastError ());
953       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
954       GNUNET_free (non_const_filename);
955       GNUNET_free (pathbuf);
956       return NULL;
957     }
958   GNUNET_free (pathbuf);
959   GNUNET_free (non_const_filename);
960
961   /* Count the number of arguments */
962   arg = (char **) argv;
963   while (*arg)
964     {
965       arg++;
966       argcount++;
967     }
968
969   /* Allocate a copy argv */
970   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
971
972   /* Copy all argv strings */
973   argcount = 0;
974   arg = (char **) argv;
975   while (*arg)
976     {
977       if (arg == argv)
978         non_const_argv[argcount] = GNUNET_strdup (path);
979       else
980         non_const_argv[argcount] = GNUNET_strdup (*arg);
981       arg++;
982       argcount++;
983     }
984   non_const_argv[argcount] = NULL;
985
986   /* Count cmd len */
987   cmdlen = 1;
988   arg = non_const_argv;
989   while (*arg)
990     {
991       cmdlen = cmdlen + strlen (*arg) + 3;
992       arg++;
993     }
994
995   /* Allocate and create cmd */
996   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
997   arg = non_const_argv;
998   while (*arg)
999     {
1000       idx += sprintf (idx, "\"%s\" ", *arg);
1001       arg++;
1002     }
1003
1004   while (argcount > 0)
1005     GNUNET_free (non_const_argv[--argcount]);
1006   GNUNET_free (non_const_argv);
1007
1008   memset (&start, 0, sizeof (start));
1009   start.cb = sizeof (start);
1010
1011   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1012       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1013       GNUNET_DISK_PERM_USER_WRITE);
1014   if (control_pipe == NULL)
1015   {
1016     GNUNET_free (cmd);
1017     GNUNET_free (path);
1018     return NULL;
1019   }
1020
1021   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1022
1023   GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1024   GNUNET_asprintf (&our_env[1], "%s", childpipename);
1025   our_env[2] = NULL;
1026   env_block = CreateCustomEnvTable (our_env);
1027   GNUNET_free (our_env[0]);
1028   GNUNET_free (our_env[1]);
1029
1030   if (!CreateProcess
1031       (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1032        env_block, NULL, &start, &proc))
1033     {
1034       SetErrnoFromWinError (GetLastError ());
1035       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1036       GNUNET_free (env_block);
1037       GNUNET_free (cmd);
1038       return NULL;
1039     }
1040
1041   GNUNET_free (env_block);
1042
1043   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1044   gnunet_proc->pid = proc.dwProcessId;
1045   gnunet_proc->handle = proc.hProcess;
1046   gnunet_proc->control_pipe = control_pipe;
1047
1048   CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1049
1050   ResumeThread (proc.hThread);
1051   CloseHandle (proc.hThread);
1052   GNUNET_free (cmd);
1053
1054   return gnunet_proc;
1055 #endif
1056 }
1057
1058
1059 /**
1060  * Retrieve the status of a process
1061  * @param proc process ID
1062  * @param type status type
1063  * @param code return code/signal number
1064  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1065  */
1066 int
1067 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc, 
1068                           enum GNUNET_OS_ProcessStatusType *type,
1069                           unsigned long *code)
1070 {
1071 #ifndef MINGW
1072   int status;
1073   int ret;
1074
1075   GNUNET_assert (0 != proc);
1076   ret = waitpid (proc->pid, &status, WNOHANG);
1077   if (ret < 0)
1078     {
1079       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1080       return GNUNET_SYSERR;
1081     }
1082   if (0 == ret)
1083     {
1084       *type = GNUNET_OS_PROCESS_RUNNING;
1085       *code = 0;
1086       return GNUNET_NO;
1087     }
1088   if (proc->pid != ret)
1089     {
1090       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1091       return GNUNET_SYSERR;
1092     }
1093   if (WIFEXITED (status))
1094     {
1095       *type = GNUNET_OS_PROCESS_EXITED;
1096       *code = WEXITSTATUS (status);
1097     }
1098   else if (WIFSIGNALED (status))
1099     {
1100       *type = GNUNET_OS_PROCESS_SIGNALED;
1101       *code = WTERMSIG (status);
1102     }
1103   else if (WIFSTOPPED (status))
1104     {
1105       *type = GNUNET_OS_PROCESS_SIGNALED;
1106       *code = WSTOPSIG (status);
1107     }
1108 #ifdef WIFCONTINUED
1109   else if (WIFCONTINUED (status))
1110     {
1111       *type = GNUNET_OS_PROCESS_RUNNING;
1112       *code = 0;
1113     }
1114 #endif
1115   else
1116     {
1117       *type = GNUNET_OS_PROCESS_UNKNOWN;
1118       *code = 0;
1119     }
1120 #else
1121   HANDLE h;
1122   DWORD c, error_code, ret;
1123
1124   h = proc->handle;
1125   ret = proc->pid;
1126   if (h == NULL || ret == 0)
1127     {
1128       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1129       return GNUNET_SYSERR;
1130     }
1131   if (h == NULL)
1132     h = GetCurrentProcess ();
1133
1134   SetLastError (0);
1135   ret = GetExitCodeProcess (h, &c);
1136   error_code = GetLastError ();
1137   if (ret == 0 || error_code != NO_ERROR)
1138   {
1139       SetErrnoFromWinError (error_code);
1140       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1141       return GNUNET_SYSERR;
1142   }
1143   if (STILL_ACTIVE == c)
1144     {
1145       *type = GNUNET_OS_PROCESS_RUNNING;
1146       *code = 0;
1147       return GNUNET_NO;
1148     }
1149   *type = GNUNET_OS_PROCESS_EXITED;
1150   *code = c;
1151 #endif
1152
1153   return GNUNET_OK;
1154 }
1155
1156 /**
1157  * Wait for a process
1158  * @param proc pointer to process structure
1159  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1160  */
1161 int
1162 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1163 {
1164
1165 #ifndef MINGW
1166   pid_t pid = proc->pid;
1167   if (pid != waitpid (pid, NULL, 0))
1168     return GNUNET_SYSERR;
1169   return GNUNET_OK;
1170 #else
1171   HANDLE h;
1172   int ret;
1173
1174   h = proc->handle;
1175   if (NULL == h)
1176     {
1177       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
1178                   "Invalid process information {%d, %08X}\n", 
1179                   proc->pid, 
1180                   h);
1181       return GNUNET_SYSERR;
1182     }
1183   if (h == NULL)
1184     h = GetCurrentProcess ();
1185
1186   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1187     {
1188       SetErrnoFromWinError (GetLastError ());
1189       ret = GNUNET_SYSERR;
1190     }
1191   else
1192     ret = GNUNET_OK;
1193
1194   return ret;
1195 #endif
1196 }
1197
1198
1199 /**
1200  * Handle to a command.
1201  */
1202 struct GNUNET_OS_CommandHandle
1203 {
1204
1205   /**
1206    * Process handle.
1207    */
1208   struct GNUNET_OS_Process *eip;
1209
1210   /**
1211    * Handle to the output pipe.
1212    */
1213   struct GNUNET_DISK_PipeHandle *opipe;
1214   
1215   /**
1216    * Read-end of output pipe.
1217    */
1218   const struct GNUNET_DISK_FileHandle *r;
1219
1220   /**
1221    * Function to call on each line of output.
1222    */
1223   GNUNET_OS_LineProcessor proc;
1224
1225   /**
1226    * Closure for 'proc'.
1227    */
1228   void *proc_cls;
1229                        
1230   /**
1231    * Buffer for the output.
1232    */
1233   char buf[1024];
1234   
1235   /**
1236    * Task reading from pipe.
1237    */
1238   GNUNET_SCHEDULER_TaskIdentifier rtask;
1239
1240   /**
1241    * When to time out.
1242    */
1243   struct GNUNET_TIME_Absolute timeout;
1244
1245   /**
1246    * Current read offset in buf.
1247    */
1248   size_t off;
1249 };
1250
1251
1252 /**
1253  * Stop/kill a command.  Must ONLY be called either from
1254  * the callback after 'NULL' was passed for 'line' *OR*
1255  * from an independent task (not within the line processor).
1256  *
1257  * @param cmd handle to the process
1258  */
1259 void
1260 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1261 {
1262
1263   if (cmd->proc != NULL)
1264     {
1265       GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1266       GNUNET_SCHEDULER_cancel (cmd->rtask);
1267     }
1268   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1269   GNUNET_break (GNUNET_OK ==
1270                 GNUNET_OS_process_wait (cmd->eip));
1271   GNUNET_OS_process_close (cmd->eip);
1272   GNUNET_DISK_pipe_close (cmd->opipe);
1273   GNUNET_free (cmd);
1274 }
1275
1276
1277 /**
1278  * Read from the process and call the line processor.
1279  *
1280  * @param cls the 'struct GNUNET_OS_CommandHandle'
1281  * @param tc scheduler context
1282  */
1283 static void
1284 cmd_read (void *cls,
1285           const struct GNUNET_SCHEDULER_TaskContext *tc)
1286 {
1287   struct GNUNET_OS_CommandHandle *cmd = cls;
1288   GNUNET_OS_LineProcessor proc;
1289   char *end;
1290   ssize_t ret;
1291
1292   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1293   if (GNUNET_YES !=
1294       GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1295                                          cmd->r))
1296     {
1297       /* timeout, shutdown, etc. */
1298       proc = cmd->proc;
1299       cmd->proc = NULL;
1300       proc (cmd->proc_cls, NULL);
1301       return;
1302     }                                    
1303   ret = GNUNET_DISK_file_read (cmd->r,
1304                                &cmd->buf[cmd->off], 
1305                                sizeof (cmd->buf)-cmd->off);
1306   if (ret <= 0)
1307     {
1308       if ( (cmd->off > 0) && (cmd->off < sizeof (cmd->buf)) )
1309         {
1310           cmd->buf[cmd->off] = '\0';
1311           cmd->proc (cmd->proc_cls, cmd->buf);
1312         }
1313       proc = cmd->proc;
1314       cmd->proc = NULL;
1315       proc (cmd->proc_cls, NULL);
1316       return;
1317     }    
1318   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1319   cmd->off += ret;
1320   while (end != NULL)
1321     {
1322       *end = '\0';
1323       cmd->proc (cmd->proc_cls, cmd->buf);
1324       memmove (cmd->buf, 
1325                end + 1, 
1326                cmd->off - (end + 1 - cmd->buf));
1327       cmd->off -= (end + 1 - cmd->buf);
1328       end = memchr (cmd->buf, '\n', cmd->off);
1329     }    
1330   cmd->rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (cmd->timeout),
1331                                                cmd->r,
1332                                                &cmd_read,
1333                                                cmd);
1334 }
1335
1336
1337 /**
1338  * Run the given command line and call the given function
1339  * for each line of the output.
1340  *
1341  * @param proc function to call for each line of the output
1342  * @param proc_cls closure for proc
1343  * @param timeout when to time out
1344  * @param binary command to run
1345  * @param ... arguments to command
1346  * @return NULL on error
1347  */
1348 struct GNUNET_OS_CommandHandle *
1349 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1350                        void *proc_cls,
1351                        struct GNUNET_TIME_Relative timeout,
1352                        const char *binary,
1353                        ...)
1354 {
1355   struct GNUNET_OS_CommandHandle *cmd;
1356   struct GNUNET_OS_Process *eip;
1357   struct GNUNET_DISK_PipeHandle *opipe;
1358   va_list ap;
1359
1360   opipe = GNUNET_DISK_pipe (GNUNET_YES,
1361                             GNUNET_NO,
1362                             GNUNET_YES);
1363   if (NULL == opipe)
1364     return NULL;
1365   va_start (ap, binary);
1366   eip = GNUNET_OS_start_process_va (NULL, opipe,
1367                                     binary, ap);
1368   va_end (ap);
1369   if (NULL == eip)
1370     {
1371       GNUNET_DISK_pipe_close (opipe);
1372       return NULL;
1373     }
1374   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1375   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1376   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1377   cmd->eip = eip;
1378   cmd->opipe = opipe;
1379   cmd->proc = proc;
1380   cmd->proc_cls = proc_cls;
1381   cmd->r = GNUNET_DISK_pipe_handle (opipe,
1382                                      GNUNET_DISK_PIPE_END_READ);
1383   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1384                                                cmd->r,
1385                                                &cmd_read,
1386                                                cmd);
1387   return cmd;
1388 }
1389
1390
1391
1392
1393 /* end of os_priority.c */