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