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