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