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