2d5dba4a311ff8fd47387c64fd498ea352825163
[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_crt_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   }
659   if (NULL != pipe_stdout)
660   {
661     GNUNET_assert (GNUNET_OK ==
662                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
663                                                       (pipe_stdout,
664                                                        GNUNET_DISK_PIPE_END_WRITE),
665                                                       &fd_stdout_write, sizeof (int)));
666     GNUNET_assert (GNUNET_OK ==
667                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
668                                                       (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
669                                                       &fd_stdout_read, sizeof (int)));
670   }
671   if (NULL != pipe_stdin)
672   {
673     GNUNET_assert (GNUNET_OK ==
674                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
675                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
676                                                       &fd_stdin_read, sizeof (int)));
677     GNUNET_assert (GNUNET_OK ==
678                    GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
679                                                       (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
680                                                       &fd_stdin_write, sizeof (int)));
681   }
682   lscp = NULL;
683   ls = 0;
684   if (NULL != lsocks)
685   {
686     i = 0;
687     while (-1 != (k = lsocks[i++]))
688       GNUNET_array_append (lscp, ls, k);
689     GNUNET_array_append (lscp, ls, -1);
690   }
691 #if DARWIN
692   /* see https://gnunet.org/vfork */
693   ret = vfork ();
694 #else
695   ret = fork ();
696 #endif
697   if (-1 == ret)
698   {
699     int eno = errno;
700     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
701     GNUNET_array_grow (lscp, ls, 0);
702     if (childpipe_read)
703       GNUNET_DISK_file_close (childpipe_read);
704     if (childpipe_write)
705       GNUNET_DISK_file_close (childpipe_write);
706     if (0 <= childpipe_read_fd)
707       close (childpipe_read_fd);
708     errno = eno;
709     return NULL;
710   }
711   if (0 != ret)
712   {
713     gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
714     gnunet_proc->pid = ret;
715     gnunet_proc->control_pipe = childpipe_write;
716     if (GNUNET_YES == pipe_control)
717     {
718       close (childpipe_read_fd);
719     }
720     GNUNET_array_grow (lscp, ls, 0);
721     return gnunet_proc;
722   }
723   if (0 <= childpipe_read_fd)
724   {
725     char fdbuf[100];
726 #ifndef DARWIN
727     /* due to vfork, we must NOT free memory on DARWIN! */
728     GNUNET_DISK_file_close (childpipe_write);
729 #endif
730     snprintf (fdbuf, 100, "%x", childpipe_read_fd);
731     setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1);
732   }
733   if (NULL != pipe_stdin)
734   {
735     GNUNET_break (0 == close (fd_stdin_write));
736     if (-1 == dup2 (fd_stdin_read, 0))
737       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
738     GNUNET_break (0 == close (fd_stdin_read));
739   }
740   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
741   {
742     GNUNET_break (0 == close (0));
743     open_dev_null (0, O_RDONLY);
744   }
745   if (NULL != pipe_stdout)
746   {
747     GNUNET_break (0 == close (fd_stdout_read));
748     if (-1 == dup2 (fd_stdout_write, 1))
749       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
750     GNUNET_break (0 == close (fd_stdout_write));
751   }
752   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
753   {
754     GNUNET_break (0 == close (1));
755     open_dev_null (1, O_WRONLY);
756   }
757   if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
758   {
759     GNUNET_break (0 == close (2));
760     open_dev_null (2, O_WRONLY);
761   }
762   if (NULL != lscp)
763   {
764     /* read systemd documentation... */
765     GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
766     setenv ("LISTEN_PID", lpid, 1);
767     i = 0;
768     tgt = 3;
769     while (-1 != lscp[i])
770     {
771       j = i + 1;
772       while (-1 != lscp[j])
773       {
774         if (lscp[j] == tgt)
775         {
776           /* dup away */
777           k = dup (lscp[j]);
778           GNUNET_assert (-1 != k);
779           GNUNET_assert (0 == close (lscp[j]));
780           lscp[j] = k;
781           break;
782         }
783         j++;
784       }
785       if (lscp[i] != tgt)
786       {
787         /* Bury any existing FD, no matter what; they should all be closed
788          * on exec anyway and the important onces have been dup'ed away */
789         (void) close (tgt);
790         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
791       }
792       /* unset close-on-exec flag */
793       flags = fcntl (tgt, F_GETFD);
794       GNUNET_assert (flags >= 0);
795       flags &= ~FD_CLOEXEC;
796       fflush (stderr);
797       (void) fcntl (tgt, F_SETFD, flags);
798       tgt++;
799       i++;
800     }
801     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
802     setenv ("LISTEN_FDS", fds, 1);
803   }
804 #ifndef DARWIN
805   /* due to vfork, we must NOT free memory on DARWIN! */
806   GNUNET_array_grow (lscp, ls, 0);
807 #endif
808   execvp (filename, argv);
809   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
810   _exit (1);
811 #else
812   struct GNUNET_DISK_FileHandle *childpipe_read;
813   struct GNUNET_DISK_FileHandle *childpipe_write;
814   HANDLE childpipe_read_handle;
815   char **arg;
816   char **non_const_argv;
817   unsigned int cmdlen;
818   char *cmd;
819   char *idx;
820   STARTUPINFOW start;
821   PROCESS_INFORMATION proc;
822   int argcount = 0;
823   struct GNUNET_OS_Process *gnunet_proc;
824   char path[MAX_PATH + 1];
825   char *our_env[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
826   char *env_block = NULL;
827   char *pathbuf;
828   DWORD pathbuf_len;
829   DWORD alloc_len;
830   char *self_prefix;
831   char *bindir;
832   char *libdir;
833   char *ptr;
834   char *non_const_filename;
835   char win_path[MAX_PATH + 1];
836   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
837   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
838   HANDLE lsocks_read;
839   HANDLE lsocks_write;
840   wchar_t *wpath;
841   wchar_t *wcmd;
842   size_t wpath_len;
843   size_t wcmd_len;
844   int env_off;
845   int fail;
846   long lRet;
847   HANDLE stdin_handle;
848   HANDLE stdout_handle;
849   HANDLE stdih, stdoh, stdeh;
850   DWORD stdif, stdof, stdef;
851   BOOL bresult;
852   DWORD error_code;
853
854   if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
855     return NULL; /* not executable */
856  
857   /* Search in prefix dir (hopefully - the directory from which
858    * the current module was loaded), bindir and libdir, then in PATH
859    */
860   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
861   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
862   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
863
864   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
865
866   alloc_len =
867       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
868       strlen (libdir);
869
870   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
871
872   ptr = pathbuf;
873   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
874   GNUNET_free (self_prefix);
875   GNUNET_free (bindir);
876   GNUNET_free (libdir);
877
878   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
879   if (alloc_len != pathbuf_len - 1)
880   {
881     GNUNET_free (pathbuf);
882     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
883     return NULL;
884   }
885
886   cmdlen = strlen (filename);
887   if ( (cmdlen < 5) || (0 != strcmp (&filename[cmdlen - 4], ".exe")) )
888     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
889   else
890     GNUNET_asprintf (&non_const_filename, "%s", filename);
891
892   /* It could be in POSIX form, convert it to a DOS path early on */
893   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
894   {
895     SetErrnoFromWinError (lRet);
896     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
897                        non_const_filename);
898     GNUNET_free (non_const_filename);
899     GNUNET_free (pathbuf);
900     return NULL;
901   }
902   GNUNET_free (non_const_filename);
903   non_const_filename = GNUNET_strdup (win_path);
904    /* Check that this is the full path. If it isn't, search. */
905   /* FIXME: convert it to wchar_t and use SearchPathW?
906    * Remember: arguments to _start_process() are technically in UTF-8...
907    */
908   if (non_const_filename[1] == ':')
909   {
910     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
911     LOG (GNUNET_ERROR_TYPE_DEBUG,
912         "Using path `%s' as-is. PATH is %s\n", path, ptr);
913   }
914   else if (!SearchPathA
915            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
916             path, NULL))
917   {
918     SetErrnoFromWinError (GetLastError ());
919     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
920                        non_const_filename);
921     GNUNET_free (non_const_filename);
922     GNUNET_free (pathbuf);
923     return NULL;
924   }
925   else
926     LOG (GNUNET_ERROR_TYPE_DEBUG,
927         "Found `%s' in PATH `%s'\n", path, pathbuf);
928   GNUNET_free (pathbuf);
929   GNUNET_free (non_const_filename);
930
931   /* Count the number of arguments */
932   arg = (char **) argv;
933   while (*arg)
934   {
935     arg++;
936     argcount++;
937   }
938
939   /* Allocate a copy argv */
940   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
941
942   /* Copy all argv strings */
943   argcount = 0;
944   arg = (char **) argv;
945   while (*arg)
946   {
947     if (arg == argv)
948       non_const_argv[argcount] = GNUNET_strdup (path);
949     else
950       non_const_argv[argcount] = GNUNET_strdup (*arg);
951     arg++;
952     argcount++;
953   }
954   non_const_argv[argcount] = NULL;
955
956   /* Count cmd len */
957   cmdlen = 1;
958   arg = non_const_argv;
959   while (*arg)
960   {
961     cmdlen = cmdlen + strlen (*arg) + 4;
962     arg++;
963   }
964
965   /* Allocate and create cmd */
966   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
967   arg = non_const_argv;
968   while (*arg)
969   {
970     char arg_last_char = (*arg)[strlen (*arg) - 1];
971     idx += sprintf (idx, "\"%s%s\"%s", *arg,
972         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
973     arg++;
974   }
975
976   while (argcount > 0)
977     GNUNET_free (non_const_argv[--argcount]);
978   GNUNET_free (non_const_argv);
979
980   memset (&start, 0, sizeof (start));
981   start.cb = sizeof (start);
982   if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
983     start.dwFlags |= STARTF_USESTDHANDLES;
984
985   stdih = GetStdHandle (STD_INPUT_HANDLE);
986   GetHandleInformation (stdih, &stdif);
987   if (pipe_stdin != NULL)
988   {
989     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
990                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
991                                        &stdin_handle, sizeof (HANDLE));
992     start.hStdInput = stdin_handle;
993   }
994   else if (stdih)
995   {
996     if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
997     {
998       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
999       if (pipe_stdin == NULL)
1000         start.hStdInput = stdih;
1001     }
1002     else
1003       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
1004   }
1005     
1006
1007   stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
1008   GetHandleInformation (stdoh, &stdof);
1009   if (NULL != pipe_stdout)
1010   {
1011     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1012                                        (pipe_stdout,
1013                                         GNUNET_DISK_PIPE_END_WRITE),
1014                                        &stdout_handle, sizeof (HANDLE));
1015     start.hStdOutput = stdout_handle;
1016   }
1017   else if (stdoh)
1018   {
1019     if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
1020     {
1021       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
1022       if (pipe_stdout == NULL)
1023         start.hStdOutput = stdoh;
1024     }
1025     else
1026       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1027   }
1028
1029   stdeh = GetStdHandle (STD_ERROR_HANDLE);
1030   GetHandleInformation (stdeh, &stdef);
1031   if (stdeh)
1032   {
1033     if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
1034     {
1035       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
1036       start.hStdError = stdeh;
1037     }
1038     else
1039       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1040   }
1041
1042   if (GNUNET_YES == pipe_control)
1043   {
1044     struct GNUNET_DISK_PipeHandle *childpipe;
1045     childpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
1046     if (NULL == childpipe)
1047       return NULL;
1048     childpipe_read = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ);
1049     childpipe_write = GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE);
1050     GNUNET_DISK_pipe_close (childpipe);
1051     if ((NULL == childpipe_read) || (NULL == childpipe_write) ||
1052         (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read,
1053         &childpipe_read_handle, sizeof (HANDLE))))
1054     {
1055       if (childpipe_read)
1056         GNUNET_DISK_file_close (childpipe_read);
1057       if (childpipe_write)
1058         GNUNET_DISK_file_close (childpipe_write);
1059       GNUNET_free (cmd);
1060       GNUNET_free (path);
1061       return NULL;
1062     }
1063     /* Unlike *nix variant, we don't dup the handle, so can't close
1064      * filehandle right now.
1065      */
1066     SetHandleInformation (childpipe_read_handle, HANDLE_FLAG_INHERIT, 1);
1067   }
1068   else
1069   {
1070     childpipe_read = NULL;
1071     childpipe_write = NULL;
1072   }
1073
1074   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1075   {
1076     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1077
1078     if (lsocks_pipe == NULL)
1079     {
1080       GNUNET_free (cmd);
1081       GNUNET_free (path);
1082       GNUNET_DISK_pipe_close (lsocks_pipe);
1083       if (GNUNET_YES == pipe_control)
1084       {
1085         GNUNET_DISK_file_close (childpipe_write);
1086         GNUNET_DISK_file_close (childpipe_read);
1087       }
1088       return NULL;
1089     }
1090     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1091         GNUNET_DISK_PIPE_END_WRITE);
1092     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1093                                        &lsocks_write, sizeof (HANDLE));
1094     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1095                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1096                                        &lsocks_read, sizeof (HANDLE));
1097   }
1098   else
1099     lsocks_pipe = NULL;
1100
1101   env_off = 0;
1102   if (GNUNET_YES == pipe_control)
1103   {
1104     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1105     GNUNET_asprintf (&our_env[env_off++], "%p", childpipe_read_handle);
1106   }
1107   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1108   {
1109     /*This will tell the child that we're going to send lsocks over the pipe*/
1110     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1111     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1112   }
1113   our_env[env_off++] = NULL;
1114   env_block = CreateCustomEnvTable (our_env);
1115   while (0 > env_off)
1116     GNUNET_free_non_null (our_env[--env_off]);
1117
1118   wpath_len = 0;
1119   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1120   {
1121     LOG (GNUNET_ERROR_TYPE_DEBUG,
1122         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1123     GNUNET_free (env_block);
1124     GNUNET_free (cmd);
1125     if (lsocks_pipe)
1126       GNUNET_DISK_pipe_close (lsocks_pipe);
1127     if (GNUNET_YES == pipe_control)
1128     {
1129       GNUNET_DISK_file_close (childpipe_write);
1130       GNUNET_DISK_file_close (childpipe_read);
1131     }
1132     return NULL;
1133   }
1134
1135   wcmd_len = 0;
1136   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1137   {
1138     LOG (GNUNET_ERROR_TYPE_DEBUG,
1139         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1140     GNUNET_free (env_block);
1141     GNUNET_free (cmd);
1142     free (wpath);
1143     if (lsocks_pipe)
1144       GNUNET_DISK_pipe_close (lsocks_pipe);
1145     if (GNUNET_YES == pipe_control)
1146     {
1147       GNUNET_DISK_file_close (childpipe_write);
1148       GNUNET_DISK_file_close (childpipe_read);
1149     }
1150     return NULL;
1151   }
1152
1153   bresult = CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1154        DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1155   error_code = GetLastError ();
1156
1157   if ((NULL == pipe_stdin) && (stdih))
1158     SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
1159     
1160
1161   if ((NULL == pipe_stdout) && (stdoh))
1162     SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
1163
1164   if (stdeh)
1165     SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
1166
1167   if (!bresult)
1168     LOG (GNUNET_ERROR_TYPE_ERROR, "CreateProcess(%s, %s) failed: %lu\n", path, cmd, error_code);
1169
1170   GNUNET_free (env_block);
1171   GNUNET_free (cmd);
1172   GNUNET_free (path);
1173   free (wpath);
1174   free (wcmd);
1175   if (GNUNET_YES == pipe_control)
1176   {
1177     GNUNET_DISK_file_close (childpipe_read);
1178   }
1179
1180   if (!bresult)
1181   {
1182     if (GNUNET_YES == pipe_control)
1183     {
1184       GNUNET_DISK_file_close (childpipe_write);
1185     }
1186     if (NULL != lsocks)
1187       GNUNET_DISK_pipe_close (lsocks_pipe);
1188     SetErrnoFromWinError (error_code);
1189     return NULL;
1190   }
1191
1192   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1193   gnunet_proc->pid = proc.dwProcessId;
1194   gnunet_proc->handle = proc.hProcess;
1195   gnunet_proc->control_pipe = childpipe_write;
1196
1197   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1198
1199   ResumeThread (proc.hThread);
1200   CloseHandle (proc.hThread);
1201
1202   if ( (NULL == lsocks) || (INVALID_SOCKET == lsocks[0]) )
1203     return gnunet_proc;
1204
1205   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1206
1207   /* This is a replacement for "goto error" that doesn't use goto */
1208   fail = 1;
1209   do
1210   {
1211     ssize_t wrote;
1212     uint64_t size;
1213     uint64_t count;
1214     unsigned int i;
1215
1216     /* Tell the number of sockets */
1217     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1218
1219     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1220     if (sizeof (count) != wrote)
1221     {
1222       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1223                   "Failed to write %u count bytes to the child: %u\n",
1224                   sizeof (count), GetLastError ());
1225       break;
1226     }
1227     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1228     {
1229       WSAPROTOCOL_INFOA pi;
1230       /* Get a socket duplication info */
1231       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1232       {
1233         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1234                     "Failed to duplicate an socket[%llu]: %u\n", i, 
1235                     GetLastError ());
1236         break;
1237       }
1238       /* Synchronous I/O is not nice, but we can't schedule this:
1239        * lsocks will be closed/freed by the caller soon, and until
1240        * the child creates a duplicate, closing a socket here will
1241        * close it for good.
1242        */
1243       /* Send the size of the structure
1244        * (the child might be built with different headers...)
1245        */
1246       size = sizeof (pi);
1247       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1248       if (sizeof (size) != wrote)
1249       {
1250         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1251                     "Failed to write %u size[%llu] bytes to the child: %u\n", 
1252                     sizeof (size), i, GetLastError ());
1253         break;
1254       }
1255       /* Finally! Send the data */
1256       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1257       if (sizeof (pi) != wrote)
1258       {
1259         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1260                     "Failed to write %u socket[%llu] bytes to the child: %u\n", 
1261                     sizeof (pi), i, GetLastError ());
1262         break;
1263       }
1264     }
1265     /* This will block us until the child makes a final read or closes
1266      * the pipe (hence no 'wrote' check), since we have to wait for it
1267      * to duplicate the last socket, before we return and start closing
1268      * our own copies)
1269      */
1270     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1271     fail = 0;
1272   }
1273   while (fail);
1274
1275   GNUNET_DISK_file_sync (lsocks_write_fd);
1276   GNUNET_DISK_pipe_close (lsocks_pipe);
1277
1278   if (fail)
1279   {
1280     /* If we can't pass on the socket(s), the child will block forever,
1281      * better put it out of its misery.
1282      */
1283     SafeTerminateProcess (gnunet_proc->handle, 0, 0);
1284     CloseHandle (gnunet_proc->handle);
1285     if (NULL != gnunet_proc->control_pipe)
1286       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1287     GNUNET_free (gnunet_proc);
1288     return NULL;
1289   }
1290   return gnunet_proc;
1291 #endif
1292 }
1293
1294
1295
1296
1297 /**
1298  * Start a process.
1299  *
1300  * @param pipe_control should a pipe be used to send signals to the child?
1301  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1302  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1303  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1304  * @param filename name of the binary
1305  * @param argv NULL-terminated array of arguments to the process
1306  * @return pointer to process structure of the new process, NULL on error
1307  */
1308 struct GNUNET_OS_Process *
1309 GNUNET_OS_start_process_vap (int pipe_control,
1310                              enum GNUNET_OS_InheritStdioFlags std_inheritance,
1311                              struct GNUNET_DISK_PipeHandle *pipe_stdin,
1312                              struct GNUNET_DISK_PipeHandle *pipe_stdout,
1313                              const char *filename, 
1314                              char *const argv[])
1315 {
1316   return start_process (pipe_control,
1317                         std_inheritance,
1318                         pipe_stdin,
1319                         pipe_stdout,
1320                         NULL,
1321                         filename,
1322                         argv);
1323 }
1324
1325
1326 /**
1327  * Start a process.
1328  *
1329  * @param pipe_control should a pipe be used to send signals to the child?
1330  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1331  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1332  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1333  * @param filename name of the binary
1334  * @param va NULL-terminated list of arguments to the process
1335  * @return pointer to process structure of the new process, NULL on error
1336  */
1337 struct GNUNET_OS_Process *
1338 GNUNET_OS_start_process_va (int pipe_control,
1339                             enum GNUNET_OS_InheritStdioFlags std_inheritance,
1340                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1341                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1342                             const char *filename, va_list va)
1343 {
1344   struct GNUNET_OS_Process *ret;
1345   va_list ap;
1346   char **argv;
1347   int argc;
1348
1349   argc = 0;
1350   va_copy (ap, va);
1351   while (NULL != va_arg (ap, char *))
1352     argc++;
1353   va_end (ap);
1354   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1355   argc = 0;
1356   va_copy (ap, va);
1357   while (NULL != (argv[argc] = va_arg (ap, char *)))
1358     argc++;
1359   va_end (ap);
1360   ret = GNUNET_OS_start_process_vap (pipe_control,
1361                                      std_inheritance,
1362                                      pipe_stdin,
1363                                      pipe_stdout,
1364                                      filename,
1365                                      argv);
1366   GNUNET_free (argv);
1367   return ret;
1368 }
1369
1370
1371 /**
1372  * Start a process.
1373  *
1374  * @param pipe_control should a pipe be used to send signals to the child?
1375  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1376  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1377  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1378  * @param filename name of the binary
1379  * @param ... NULL-terminated list of arguments to the process
1380  * @return pointer to process structure of the new process, NULL on error
1381  */
1382 struct GNUNET_OS_Process *
1383 GNUNET_OS_start_process (int pipe_control,
1384                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
1385                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1386                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1387                          const char *filename, ...)
1388 {
1389   struct GNUNET_OS_Process *ret;
1390   va_list ap;
1391
1392   va_start (ap, filename);
1393   ret = GNUNET_OS_start_process_va (pipe_control, std_inheritance, pipe_stdin,
1394                                     pipe_stdout, filename, ap);
1395   va_end (ap);
1396   return ret;
1397 }
1398
1399
1400 /**
1401  * Start a process.
1402  *
1403  * @param pipe_control should a pipe be used to send signals to the child?
1404  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
1405  *        std handles of the parent are inherited by the child.
1406  *        pipe_stdin and pipe_stdout take priority over std_inheritance
1407  *        (when they are non-NULL).
1408  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1409  *         must be NULL on platforms where dup is not supported
1410  * @param filename name of the binary
1411  * @param argv NULL-terminated list of arguments to the process
1412  * @return process ID of the new process, -1 on error
1413  */
1414 struct GNUNET_OS_Process *
1415 GNUNET_OS_start_process_v (int pipe_control,
1416                            enum GNUNET_OS_InheritStdioFlags std_inheritance,
1417                            const SOCKTYPE *lsocks,
1418                            const char *filename,
1419                            char *const argv[])
1420 {
1421   return start_process (pipe_control,
1422                         std_inheritance,
1423                         NULL,
1424                         NULL,
1425                         lsocks,
1426                         filename,
1427                         argv);
1428 }
1429
1430
1431 /**
1432  * Retrieve the status of a process, waiting on him if dead.
1433  * Nonblocking version.
1434  * 
1435  * @param proc process ID
1436  * @param type status type
1437  * @param code return code/signal number
1438  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1439  */
1440 int
1441 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1442                           enum GNUNET_OS_ProcessStatusType *type,
1443                           unsigned long *code)
1444 {
1445 #ifndef MINGW
1446   int status;
1447   int ret;
1448
1449   GNUNET_assert (0 != proc);
1450   ret = waitpid (proc->pid, &status, WNOHANG);
1451   if (ret < 0)
1452   {
1453     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1454     return GNUNET_SYSERR;
1455   }
1456   if (0 == ret)
1457   {
1458     *type = GNUNET_OS_PROCESS_RUNNING;
1459     *code = 0;
1460     return GNUNET_NO;
1461   }
1462   if (proc->pid != ret)
1463   {
1464     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1465     return GNUNET_SYSERR;
1466   }
1467   if (WIFEXITED (status))
1468   {
1469     *type = GNUNET_OS_PROCESS_EXITED;
1470     *code = WEXITSTATUS (status);
1471   }
1472   else if (WIFSIGNALED (status))
1473   {
1474     *type = GNUNET_OS_PROCESS_SIGNALED;
1475     *code = WTERMSIG (status);
1476   }
1477   else if (WIFSTOPPED (status))
1478   {
1479     *type = GNUNET_OS_PROCESS_SIGNALED;
1480     *code = WSTOPSIG (status);
1481   }
1482 #ifdef WIFCONTINUED
1483   else if (WIFCONTINUED (status))
1484   {
1485     *type = GNUNET_OS_PROCESS_RUNNING;
1486     *code = 0;
1487   }
1488 #endif
1489   else
1490   {
1491     *type = GNUNET_OS_PROCESS_UNKNOWN;
1492     *code = 0;
1493   }
1494 #else
1495   HANDLE h;
1496   DWORD c, error_code, ret;
1497
1498   h = proc->handle;
1499   ret = proc->pid;
1500   if (h == NULL || ret == 0)
1501   {
1502     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1503          ret, h);
1504     return GNUNET_SYSERR;
1505   }
1506   if (h == NULL)
1507     h = GetCurrentProcess ();
1508
1509   SetLastError (0);
1510   ret = GetExitCodeProcess (h, &c);
1511   error_code = GetLastError ();
1512   if (ret == 0 || error_code != NO_ERROR)
1513   {
1514     SetErrnoFromWinError (error_code);
1515     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1516     return GNUNET_SYSERR;
1517   }
1518   if (STILL_ACTIVE == c)
1519   {
1520     *type = GNUNET_OS_PROCESS_RUNNING;
1521     *code = 0;
1522     return GNUNET_NO;
1523   }
1524   *type = GNUNET_OS_PROCESS_EXITED;
1525   *code = c;
1526 #endif
1527
1528   return GNUNET_OK;
1529 }
1530
1531
1532 /**
1533  * Wait for a process
1534  *
1535  * @param proc pointer to process structure
1536  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1537  */
1538 int
1539 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1540 {
1541 #ifndef MINGW
1542   pid_t pid = proc->pid;
1543   pid_t ret;
1544
1545   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1546           (EINTR == errno) ) ;
1547   if (pid != ret) 
1548   {
1549     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1550     return GNUNET_SYSERR;
1551   }
1552   return GNUNET_OK;
1553 #else
1554   HANDLE h;
1555
1556   h = proc->handle;
1557   if (NULL == h)
1558   {
1559     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1560          proc->pid, h);
1561     return GNUNET_SYSERR;
1562   }
1563   if (NULL == h)
1564     h = GetCurrentProcess ();
1565
1566   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1567   {
1568     SetErrnoFromWinError (GetLastError ());
1569     return GNUNET_SYSERR;
1570   }
1571   return GNUNET_OK;
1572 #endif
1573 }
1574
1575
1576 /**
1577  * Handle to a command.
1578  */
1579 struct GNUNET_OS_CommandHandle
1580 {
1581
1582   /**
1583    * Process handle.
1584    */
1585   struct GNUNET_OS_Process *eip;
1586
1587   /**
1588    * Handle to the output pipe.
1589    */
1590   struct GNUNET_DISK_PipeHandle *opipe;
1591
1592   /**
1593    * Read-end of output pipe.
1594    */
1595   const struct GNUNET_DISK_FileHandle *r;
1596
1597   /**
1598    * Function to call on each line of output.
1599    */
1600   GNUNET_OS_LineProcessor proc;
1601
1602   /**
1603    * Closure for 'proc'.
1604    */
1605   void *proc_cls;
1606
1607   /**
1608    * Buffer for the output.
1609    */
1610   char buf[1024];
1611
1612   /**
1613    * Task reading from pipe.
1614    */
1615   GNUNET_SCHEDULER_TaskIdentifier rtask;
1616
1617   /**
1618    * When to time out.
1619    */
1620   struct GNUNET_TIME_Absolute timeout;
1621
1622   /**
1623    * Current read offset in buf.
1624    */
1625   size_t off;
1626 };
1627
1628
1629 /**
1630  * Stop/kill a command.  Must ONLY be called either from
1631  * the callback after 'NULL' was passed for 'line' *OR*
1632  * from an independent task (not within the line processor).
1633  *
1634  * @param cmd handle to the process
1635  */
1636 void
1637 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1638 {
1639   if (NULL != cmd->proc)
1640   {
1641     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1642     GNUNET_SCHEDULER_cancel (cmd->rtask);
1643   }
1644   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1645   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1646   GNUNET_OS_process_destroy (cmd->eip);
1647   GNUNET_DISK_pipe_close (cmd->opipe);
1648   GNUNET_free (cmd);
1649 }
1650
1651
1652 /**
1653  * Read from the process and call the line processor.
1654  *
1655  * @param cls the 'struct GNUNET_OS_CommandHandle'
1656  * @param tc scheduler context
1657  */
1658 static void
1659 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1660 {
1661   struct GNUNET_OS_CommandHandle *cmd = cls;
1662   GNUNET_OS_LineProcessor proc;
1663   char *end;
1664   ssize_t ret;
1665
1666   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1667   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1668   {
1669     /* timeout, shutdown, etc. */
1670     proc = cmd->proc;
1671     cmd->proc = NULL;
1672     proc (cmd->proc_cls, NULL);
1673     return;
1674   }
1675   ret =
1676       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1677                              sizeof (cmd->buf) - cmd->off);
1678   if (ret <= 0)
1679   {
1680     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1681     {
1682       cmd->buf[cmd->off] = '\0';
1683       cmd->proc (cmd->proc_cls, cmd->buf);
1684     }
1685     proc = cmd->proc;
1686     cmd->proc = NULL;
1687     proc (cmd->proc_cls, NULL);
1688     return;
1689   }
1690   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1691   cmd->off += ret;
1692   while (NULL != end)
1693   {
1694     *end = '\0';
1695     cmd->proc (cmd->proc_cls, cmd->buf);
1696     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1697     cmd->off -= (end + 1 - cmd->buf);
1698     end = memchr (cmd->buf, '\n', cmd->off);
1699   }
1700   cmd->rtask =
1701       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1702                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1703 }
1704
1705
1706 /**
1707  * Run the given command line and call the given function
1708  * for each line of the output.
1709  *
1710  * @param proc function to call for each line of the output
1711  * @param proc_cls closure for proc
1712  * @param timeout when to time out
1713  * @param binary command to run
1714  * @param ... arguments to command
1715  * @return NULL on error
1716  */
1717 struct GNUNET_OS_CommandHandle *
1718 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1719                        struct GNUNET_TIME_Relative timeout, const char *binary,
1720                        ...)
1721 {
1722   struct GNUNET_OS_CommandHandle *cmd;
1723   struct GNUNET_OS_Process *eip;
1724   struct GNUNET_DISK_PipeHandle *opipe;
1725   va_list ap;
1726
1727   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1728   if (NULL == opipe)
1729     return NULL;
1730   va_start (ap, binary);
1731   /* redirect stdout, don't inherit stderr/stdin */
1732   eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, binary, ap);
1733   va_end (ap);
1734   if (NULL == eip)
1735   {
1736     GNUNET_DISK_pipe_close (opipe);
1737     return NULL;
1738   }
1739   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1740   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1741   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1742   cmd->eip = eip;
1743   cmd->opipe = opipe;
1744   cmd->proc = proc;
1745   cmd->proc_cls = proc_cls;
1746   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1747   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1748   return cmd;
1749 }
1750
1751
1752 /* end of os_priority.c */