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