LRN's patch argument order
[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 SOCKTYPE *lsocks,
856                            const char *filename,
857                            char *const argv[])
858 {
859 #if ENABLE_WINDOWS_WORKAROUNDS
860   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
861   char *childpipename = NULL;
862 #endif
863
864 #ifndef MINGW
865   pid_t ret;
866   char lpid[16];
867   char fds[16];
868   struct GNUNET_OS_Process *gnunet_proc = NULL;
869   int i;
870   int j;
871   int k;
872   int tgt;
873   int flags;
874   int *lscp;
875   unsigned int ls;
876
877 #if ENABLE_WINDOWS_WORKAROUNDS
878   control_pipe =
879       GNUNET_DISK_npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
880                                 GNUNET_DISK_PERM_USER_READ |
881                                 GNUNET_DISK_PERM_USER_WRITE);
882   if (control_pipe == NULL)
883     return NULL;
884 #endif
885
886   lscp = NULL;
887   ls = 0;
888   if (lsocks != NULL)
889   {
890     i = 0;
891     while (-1 != (k = lsocks[i++]))
892       GNUNET_array_append (lscp, ls, k);
893     GNUNET_array_append (lscp, ls, -1);
894   }
895   ret = fork ();
896   if (ret != 0)
897   {
898     if (ret == -1)
899     {
900       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
901 #if ENABLE_WINDOWS_WORKAROUNDS
902       GNUNET_DISK_npipe_close (control_pipe);
903 #endif
904     }
905     else
906     {
907       gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
908       gnunet_proc->pid = ret;
909 #if ENABLE_WINDOWS_WORKAROUNDS
910       gnunet_proc->control_pipe = control_pipe;
911
912 #endif
913     }
914     GNUNET_array_grow (lscp, ls, 0);
915 #if ENABLE_WINDOWS_WORKAROUNDS
916     GNUNET_free (childpipename);
917 #endif
918     return gnunet_proc;
919   }
920
921 #if ENABLE_WINDOWS_WORKAROUNDS
922   setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
923   GNUNET_free (childpipename);
924 #endif
925
926   if (lscp != NULL)
927   {
928     /* read systemd documentation... */
929     GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
930     setenv ("LISTEN_PID", lpid, 1);
931     i = 0;
932     tgt = 3;
933     while (-1 != lscp[i])
934     {
935       j = i + 1;
936       while (-1 != lscp[j])
937       {
938         if (lscp[j] == tgt)
939         {
940           /* dup away */
941           k = dup (lscp[j]);
942           GNUNET_assert (-1 != k);
943           GNUNET_assert (0 == close (lscp[j]));
944           lscp[j] = k;
945           break;
946         }
947         j++;
948       }
949       if (lscp[i] != tgt)
950       {
951         /* Bury any existing FD, no matter what; they should all be closed
952          * on exec anyway and the important onces have been dup'ed away */
953         (void) close (tgt);
954         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
955       }
956       /* unset close-on-exec flag */
957       flags = fcntl (tgt, F_GETFD);
958       GNUNET_assert (flags >= 0);
959       flags &= ~FD_CLOEXEC;
960       fflush (stderr);
961       (void) fcntl (tgt, F_SETFD, flags);
962       tgt++;
963       i++;
964     }
965     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
966     setenv ("LISTEN_FDS", fds, 1);
967   }
968   GNUNET_array_grow (lscp, ls, 0);
969   execvp (filename, argv);
970   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
971   _exit (1);
972 #else
973   char **arg, **non_const_argv;
974   unsigned int cmdlen;
975   char *cmd, *idx;
976   STARTUPINFO start;
977   PROCESS_INFORMATION proc;
978   int argcount = 0;
979   struct GNUNET_OS_Process *gnunet_proc = NULL;
980
981   char path[MAX_PATH + 1];
982
983   char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
984   char *env_block = NULL;
985   char *pathbuf;
986   DWORD pathbuf_len, alloc_len;
987   char *self_prefix;
988   char *bindir;
989   char *libdir;
990   char *ptr;
991   char *non_const_filename;
992   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
993   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
994   HANDLE lsocks_read;
995   HANDLE lsocks_write;
996
997   int fail;
998
999   /* Search in prefix dir (hopefully - the directory from which
1000    * the current module was loaded), bindir and libdir, then in PATH
1001    */
1002   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1003   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1004   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1005
1006   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1007
1008   alloc_len =
1009       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1010       strlen (libdir);
1011
1012   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1013
1014   ptr = pathbuf;
1015   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1016   GNUNET_free (self_prefix);
1017   GNUNET_free (bindir);
1018   GNUNET_free (libdir);
1019
1020   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1021   if (alloc_len != pathbuf_len - 1)
1022   {
1023     GNUNET_free (pathbuf);
1024     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
1025     return NULL;
1026   }
1027
1028   cmdlen = strlen (filename);
1029   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1030     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1031   else
1032     GNUNET_asprintf (&non_const_filename, "%s", filename);
1033
1034   /* Check that this is the full path. If it isn't, search. */
1035   if (non_const_filename[1] == ':')
1036     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1037   else if (!SearchPathA
1038            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1039             path, NULL))
1040   {
1041     SetErrnoFromWinError (GetLastError ());
1042     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1043                        non_const_filename);
1044     GNUNET_free (non_const_filename);
1045     GNUNET_free (pathbuf);
1046     return NULL;
1047   }
1048   GNUNET_free (pathbuf);
1049   GNUNET_free (non_const_filename);
1050
1051   /* Count the number of arguments */
1052   arg = (char **) argv;
1053   while (*arg)
1054   {
1055     arg++;
1056     argcount++;
1057   }
1058
1059   /* Allocate a copy argv */
1060   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1061
1062   /* Copy all argv strings */
1063   argcount = 0;
1064   arg = (char **) argv;
1065   while (*arg)
1066   {
1067     if (arg == argv)
1068       non_const_argv[argcount] = GNUNET_strdup (path);
1069     else
1070       non_const_argv[argcount] = GNUNET_strdup (*arg);
1071     arg++;
1072     argcount++;
1073   }
1074   non_const_argv[argcount] = NULL;
1075
1076   /* Count cmd len */
1077   cmdlen = 1;
1078   arg = non_const_argv;
1079   while (*arg)
1080   {
1081     cmdlen = cmdlen + strlen (*arg) + 3;
1082     arg++;
1083   }
1084
1085   /* Allocate and create cmd */
1086   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1087   arg = non_const_argv;
1088   while (*arg)
1089   {
1090     idx += sprintf (idx, "\"%s\" ", *arg);
1091     arg++;
1092   }
1093
1094   while (argcount > 0)
1095     GNUNET_free (non_const_argv[--argcount]);
1096   GNUNET_free (non_const_argv);
1097
1098   memset (&start, 0, sizeof (start));
1099   start.cb = sizeof (start);
1100
1101   control_pipe =
1102       GNUNET_DISK_npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1103                                 GNUNET_DISK_PERM_USER_READ |
1104                                 GNUNET_DISK_PERM_USER_WRITE);
1105   if (control_pipe == NULL)
1106   {
1107     GNUNET_free (cmd);
1108     GNUNET_free (path);
1109     return NULL;
1110   }
1111   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1112   {
1113     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO);
1114
1115     if (lsocks_pipe == NULL)
1116     {
1117       GNUNET_free (cmd);
1118       GNUNET_free (path);
1119       GNUNET_DISK_pipe_close (lsocks_pipe);
1120       return NULL;
1121     }
1122     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1123         GNUNET_DISK_PIPE_END_WRITE);
1124     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1125                                        &lsocks_write, sizeof (HANDLE));
1126     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1127                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1128                                        &lsocks_read, sizeof (HANDLE));
1129   }
1130
1131 #if DEBUG_OS
1132   LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1133        childpipename);
1134 #endif
1135
1136   GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1137   GNUNET_asprintf (&our_env[1], "%s", childpipename);
1138   GNUNET_free (childpipename);
1139   if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1140     our_env[2] = NULL;
1141   else
1142   {
1143     /*This will tell the child that we're going to send lsocks over the pipe*/
1144     GNUNET_asprintf (&our_env[2], "%s=", "GNUNET_OS_READ_LSOCKS");
1145     GNUNET_asprintf (&our_env[3], "%lu", lsocks_read);
1146     our_env[4] = NULL;
1147   }
1148   env_block = CreateCustomEnvTable (our_env);
1149   GNUNET_free_non_null (our_env[0]);
1150   GNUNET_free_non_null (our_env[1]);
1151   GNUNET_free_non_null (our_env[2]);
1152   GNUNET_free_non_null (our_env[3]);
1153
1154   if (!CreateProcessA
1155       (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1156        env_block, NULL, &start, &proc))
1157   {
1158     SetErrnoFromWinError (GetLastError ());
1159     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1160     GNUNET_DISK_npipe_close (control_pipe);
1161     if (lsocks != NULL)
1162       GNUNET_DISK_pipe_close (lsocks_pipe);
1163     GNUNET_free (env_block);
1164     GNUNET_free (cmd);
1165     return NULL;
1166   }
1167
1168   GNUNET_free (env_block);
1169
1170   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1171   gnunet_proc->pid = proc.dwProcessId;
1172   gnunet_proc->handle = proc.hProcess;
1173   gnunet_proc->control_pipe = control_pipe;
1174
1175   CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1176
1177   ResumeThread (proc.hThread);
1178   CloseHandle (proc.hThread);
1179   GNUNET_free (cmd);
1180
1181   if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1182     return gnunet_proc;
1183
1184   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1185
1186   /* This is a replacement for "goto error" that doesn't use goto */
1187   fail = 1;
1188   do
1189   {
1190     int wrote;
1191     uint64_t size, count, i;
1192
1193     /* Tell the number of sockets */
1194     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1195
1196     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1197     if (wrote != sizeof (count))
1198     {
1199       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1200       break;
1201     }
1202     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1203     {
1204       WSAPROTOCOL_INFOA pi;
1205       /* Get a socket duplication info */
1206       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1207       {
1208         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1209         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1210         break;
1211       }
1212       /* Synchronous I/O is not nice, but we can't schedule this:
1213        * lsocks will be closed/freed by the caller soon, and until
1214        * the child creates a duplicate, closing a socket here will
1215        * close it for good.
1216        */
1217       /* Send the size of the structure
1218        * (the child might be built with different headers...)
1219        */
1220       size = sizeof (pi);
1221       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1222       if (wrote != sizeof (size))
1223       {
1224         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1225         break;
1226       }
1227       /* Finally! Send the data */
1228       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1229       if (wrote != sizeof (pi))
1230       {
1231         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1232         break;
1233       }
1234     }
1235     /* This will block us until the child makes a final read or closes
1236      * the pipe (hence no 'wrote' check), since we have to wait for it
1237      * to duplicate the last socket, before we return and start closing
1238      * our own copies)
1239      */
1240     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1241     fail = 0;
1242   }
1243   while (fail);
1244
1245   GNUNET_DISK_file_sync (lsocks_write_fd);
1246   GNUNET_DISK_pipe_close (lsocks_pipe);
1247
1248   if (fail)
1249   {
1250     /* If we can't pass on the socket(s), the child will block forever,
1251      * better put it out of its misery.
1252      */
1253     TerminateProcess (gnunet_proc->handle, 0);
1254     CloseHandle (gnunet_proc->handle);
1255     GNUNET_DISK_npipe_close (gnunet_proc->control_pipe);
1256     GNUNET_free (gnunet_proc);
1257     return NULL;
1258   }
1259
1260   return gnunet_proc;
1261 #endif
1262 }
1263
1264
1265 /**
1266  * Retrieve the status of a process
1267  * @param proc process ID
1268  * @param type status type
1269  * @param code return code/signal number
1270  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1271  */
1272 int
1273 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1274                           enum GNUNET_OS_ProcessStatusType *type,
1275                           unsigned long *code)
1276 {
1277 #ifndef MINGW
1278   int status;
1279   int ret;
1280
1281   GNUNET_assert (0 != proc);
1282   ret = waitpid (proc->pid, &status, WNOHANG);
1283   if (ret < 0)
1284   {
1285     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1286     return GNUNET_SYSERR;
1287   }
1288   if (0 == ret)
1289   {
1290     *type = GNUNET_OS_PROCESS_RUNNING;
1291     *code = 0;
1292     return GNUNET_NO;
1293   }
1294   if (proc->pid != ret)
1295   {
1296     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1297     return GNUNET_SYSERR;
1298   }
1299   if (WIFEXITED (status))
1300   {
1301     *type = GNUNET_OS_PROCESS_EXITED;
1302     *code = WEXITSTATUS (status);
1303   }
1304   else if (WIFSIGNALED (status))
1305   {
1306     *type = GNUNET_OS_PROCESS_SIGNALED;
1307     *code = WTERMSIG (status);
1308   }
1309   else if (WIFSTOPPED (status))
1310   {
1311     *type = GNUNET_OS_PROCESS_SIGNALED;
1312     *code = WSTOPSIG (status);
1313   }
1314 #ifdef WIFCONTINUED
1315   else if (WIFCONTINUED (status))
1316   {
1317     *type = GNUNET_OS_PROCESS_RUNNING;
1318     *code = 0;
1319   }
1320 #endif
1321   else
1322   {
1323     *type = GNUNET_OS_PROCESS_UNKNOWN;
1324     *code = 0;
1325   }
1326 #else
1327   HANDLE h;
1328   DWORD c, error_code, ret;
1329
1330   h = proc->handle;
1331   ret = proc->pid;
1332   if (h == NULL || ret == 0)
1333   {
1334     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1335          ret, h);
1336     return GNUNET_SYSERR;
1337   }
1338   if (h == NULL)
1339     h = GetCurrentProcess ();
1340
1341   SetLastError (0);
1342   ret = GetExitCodeProcess (h, &c);
1343   error_code = GetLastError ();
1344   if (ret == 0 || error_code != NO_ERROR)
1345   {
1346     SetErrnoFromWinError (error_code);
1347     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1348     return GNUNET_SYSERR;
1349   }
1350   if (STILL_ACTIVE == c)
1351   {
1352     *type = GNUNET_OS_PROCESS_RUNNING;
1353     *code = 0;
1354     return GNUNET_NO;
1355   }
1356   *type = GNUNET_OS_PROCESS_EXITED;
1357   *code = c;
1358 #endif
1359
1360   return GNUNET_OK;
1361 }
1362
1363
1364 /**
1365  * Wait for a process
1366  * @param proc pointer to process structure
1367  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1368  */
1369 int
1370 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1371 {
1372
1373 #ifndef MINGW
1374   pid_t pid = proc->pid;
1375
1376   if (pid != waitpid (pid, NULL, 0))
1377     return GNUNET_SYSERR;
1378   return GNUNET_OK;
1379 #else
1380   HANDLE h;
1381   int ret;
1382
1383   h = proc->handle;
1384   if (NULL == h)
1385   {
1386     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1387          proc->pid, h);
1388     return GNUNET_SYSERR;
1389   }
1390   if (h == NULL)
1391     h = GetCurrentProcess ();
1392
1393   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1394   {
1395     SetErrnoFromWinError (GetLastError ());
1396     ret = GNUNET_SYSERR;
1397   }
1398   else
1399     ret = GNUNET_OK;
1400
1401   return ret;
1402 #endif
1403 }
1404
1405
1406 /**
1407  * Handle to a command.
1408  */
1409 struct GNUNET_OS_CommandHandle
1410 {
1411
1412   /**
1413    * Process handle.
1414    */
1415   struct GNUNET_OS_Process *eip;
1416
1417   /**
1418    * Handle to the output pipe.
1419    */
1420   struct GNUNET_DISK_PipeHandle *opipe;
1421
1422   /**
1423    * Read-end of output pipe.
1424    */
1425   const struct GNUNET_DISK_FileHandle *r;
1426
1427   /**
1428    * Function to call on each line of output.
1429    */
1430   GNUNET_OS_LineProcessor proc;
1431
1432   /**
1433    * Closure for 'proc'.
1434    */
1435   void *proc_cls;
1436
1437   /**
1438    * Buffer for the output.
1439    */
1440   char buf[1024];
1441
1442   /**
1443    * Task reading from pipe.
1444    */
1445   GNUNET_SCHEDULER_TaskIdentifier rtask;
1446
1447   /**
1448    * When to time out.
1449    */
1450   struct GNUNET_TIME_Absolute timeout;
1451
1452   /**
1453    * Current read offset in buf.
1454    */
1455   size_t off;
1456 };
1457
1458
1459 /**
1460  * Stop/kill a command.  Must ONLY be called either from
1461  * the callback after 'NULL' was passed for 'line' *OR*
1462  * from an independent task (not within the line processor).
1463  *
1464  * @param cmd handle to the process
1465  */
1466 void
1467 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1468 {
1469
1470   if (cmd->proc != NULL)
1471   {
1472     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1473     GNUNET_SCHEDULER_cancel (cmd->rtask);
1474   }
1475   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1476   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1477   GNUNET_OS_process_close (cmd->eip);
1478   GNUNET_DISK_pipe_close (cmd->opipe);
1479   GNUNET_free (cmd);
1480 }
1481
1482
1483 /**
1484  * Read from the process and call the line processor.
1485  *
1486  * @param cls the 'struct GNUNET_OS_CommandHandle'
1487  * @param tc scheduler context
1488  */
1489 static void
1490 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1491 {
1492   struct GNUNET_OS_CommandHandle *cmd = cls;
1493   GNUNET_OS_LineProcessor proc;
1494   char *end;
1495   ssize_t ret;
1496
1497   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1498   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1499   {
1500     /* timeout, shutdown, etc. */
1501     proc = cmd->proc;
1502     cmd->proc = NULL;
1503     proc (cmd->proc_cls, NULL);
1504     return;
1505   }
1506   ret =
1507       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1508                              sizeof (cmd->buf) - cmd->off);
1509   if (ret <= 0)
1510   {
1511     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1512     {
1513       cmd->buf[cmd->off] = '\0';
1514       cmd->proc (cmd->proc_cls, cmd->buf);
1515     }
1516     proc = cmd->proc;
1517     cmd->proc = NULL;
1518     proc (cmd->proc_cls, NULL);
1519     return;
1520   }
1521   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1522   cmd->off += ret;
1523   while (end != NULL)
1524   {
1525     *end = '\0';
1526     cmd->proc (cmd->proc_cls, cmd->buf);
1527     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1528     cmd->off -= (end + 1 - cmd->buf);
1529     end = memchr (cmd->buf, '\n', cmd->off);
1530   }
1531   cmd->rtask =
1532       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1533                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1534 }
1535
1536
1537 /**
1538  * Run the given command line and call the given function
1539  * for each line of the output.
1540  *
1541  * @param proc function to call for each line of the output
1542  * @param proc_cls closure for proc
1543  * @param timeout when to time out
1544  * @param binary command to run
1545  * @param ... arguments to command
1546  * @return NULL on error
1547  */
1548 struct GNUNET_OS_CommandHandle *
1549 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1550                        struct GNUNET_TIME_Relative timeout, const char *binary,
1551                        ...)
1552 {
1553   struct GNUNET_OS_CommandHandle *cmd;
1554   struct GNUNET_OS_Process *eip;
1555   struct GNUNET_DISK_PipeHandle *opipe;
1556   va_list ap;
1557
1558   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_NO, GNUNET_YES);
1559   if (NULL == opipe)
1560     return NULL;
1561   va_start (ap, binary);
1562   eip = GNUNET_OS_start_process_va (NULL, opipe, binary, ap);
1563   va_end (ap);
1564   if (NULL == eip)
1565   {
1566     GNUNET_DISK_pipe_close (opipe);
1567     return NULL;
1568   }
1569   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1570   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1571   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1572   cmd->eip = eip;
1573   cmd->opipe = opipe;
1574   cmd->proc = proc;
1575   cmd->proc_cls = proc_cls;
1576   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1577   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1578   return cmd;
1579 }
1580
1581
1582
1583
1584 /* end of os_priority.c */