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