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