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