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