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