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