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