c773c23995aadf4d9daee3ce891b0fa22df34e2a
[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     GNUNET_free (childpipename);
935   }
936   if (NULL != pipe_stdin)
937   {
938     GNUNET_break (0 == close (fd_stdin_write));
939     if (-1 == dup2 (fd_stdin_read, 0))
940       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
941     GNUNET_break (0 == close (fd_stdin_read));
942   }
943   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
944   {
945     GNUNET_break (0 == close (0));
946     open_dev_null (0, O_RDONLY);
947   }
948   if (NULL != pipe_stdout)
949   {
950     GNUNET_break (0 == close (fd_stdout_read));
951     if (-1 == dup2 (fd_stdout_write, 1))
952       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
953     GNUNET_break (0 == close (fd_stdout_write));
954   }
955   else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
956   {
957     GNUNET_break (0 == close (1));
958     open_dev_null (1, O_WRONLY);
959   }
960   if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
961   {
962     GNUNET_break (0 == close (2));
963     open_dev_null (2, O_WRONLY);
964   }
965   if (NULL != lscp)
966   {
967     /* read systemd documentation... */
968     GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
969     setenv ("LISTEN_PID", lpid, 1);
970     i = 0;
971     tgt = 3;
972     while (-1 != lscp[i])
973     {
974       j = i + 1;
975       while (-1 != lscp[j])
976       {
977         if (lscp[j] == tgt)
978         {
979           /* dup away */
980           k = dup (lscp[j]);
981           GNUNET_assert (-1 != k);
982           GNUNET_assert (0 == close (lscp[j]));
983           lscp[j] = k;
984           break;
985         }
986         j++;
987       }
988       if (lscp[i] != tgt)
989       {
990         /* Bury any existing FD, no matter what; they should all be closed
991          * on exec anyway and the important onces have been dup'ed away */
992         (void) close (tgt);
993         GNUNET_assert (-1 != dup2 (lscp[i], tgt));
994       }
995       /* unset close-on-exec flag */
996       flags = fcntl (tgt, F_GETFD);
997       GNUNET_assert (flags >= 0);
998       flags &= ~FD_CLOEXEC;
999       fflush (stderr);
1000       (void) fcntl (tgt, F_SETFD, flags);
1001       tgt++;
1002       i++;
1003     }
1004     GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1005     setenv ("LISTEN_FDS", fds, 1);
1006   }
1007   GNUNET_array_grow (lscp, ls, 0);
1008   execvp (filename, argv);
1009   LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1010   _exit (1);
1011 #else
1012   struct GNUNET_DISK_FileHandle *control_pipe;
1013   char *childpipename = NULL;
1014   char **arg;
1015   char **non_const_argv;
1016   unsigned int cmdlen;
1017   char *cmd;
1018   char *idx;
1019   STARTUPINFOW start;
1020   PROCESS_INFORMATION proc;
1021   int argcount = 0;
1022   struct GNUNET_OS_Process *gnunet_proc;
1023   char path[MAX_PATH + 1];
1024   char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1025   char *env_block = NULL;
1026   char *pathbuf;
1027   DWORD pathbuf_len;
1028   DWORD alloc_len;
1029   char *self_prefix;
1030   char *bindir;
1031   char *libdir;
1032   char *ptr;
1033   char *non_const_filename;
1034   char win_path[MAX_PATH + 1];
1035   struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1036   const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1037   HANDLE lsocks_read;
1038   HANDLE lsocks_write;
1039   wchar_t *wpath;
1040   wchar_t *wcmd;
1041   size_t wpath_len;
1042   size_t wcmd_len;
1043   int env_off;
1044   int fail;
1045   long lRet;
1046   HANDLE stdin_handle;
1047   HANDLE stdout_handle;
1048   HANDLE stdih, stdoh, stdeh;
1049   DWORD stdif, stdof, stdef;
1050   BOOL bresult;
1051   DWORD error_code;
1052
1053   if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
1054     return NULL; /* not executable */
1055  
1056   /* Search in prefix dir (hopefully - the directory from which
1057    * the current module was loaded), bindir and libdir, then in PATH
1058    */
1059   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1060   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1061   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1062
1063   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1064
1065   alloc_len =
1066       pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1067       strlen (libdir);
1068
1069   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1070
1071   ptr = pathbuf;
1072   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1073   GNUNET_free (self_prefix);
1074   GNUNET_free (bindir);
1075   GNUNET_free (libdir);
1076
1077   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1078   if (alloc_len != pathbuf_len - 1)
1079   {
1080     GNUNET_free (pathbuf);
1081     errno = ENOSYS;             /* PATH changed on the fly. What kind of error is that? */
1082     return NULL;
1083   }
1084
1085   cmdlen = strlen (filename);
1086   if ( (cmdlen < 5) || (0 != strcmp (&filename[cmdlen - 4], ".exe")) )
1087     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1088   else
1089     GNUNET_asprintf (&non_const_filename, "%s", filename);
1090
1091   /* It could be in POSIX form, convert it to a DOS path early on */
1092   if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1093   {
1094     SetErrnoFromWinError (lRet);
1095     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1096                        non_const_filename);
1097     GNUNET_free (non_const_filename);
1098     GNUNET_free (pathbuf);
1099     return NULL;
1100   }
1101   GNUNET_free (non_const_filename);
1102   non_const_filename = GNUNET_strdup (win_path);
1103    /* Check that this is the full path. If it isn't, search. */
1104   /* FIXME: convert it to wchar_t and use SearchPathW?
1105    * Remember: arguments to _start_process() are technically in UTF-8...
1106    */
1107   if (non_const_filename[1] == ':')
1108     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1109   else if (!SearchPathA
1110            (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1111             path, NULL))
1112   {
1113     SetErrnoFromWinError (GetLastError ());
1114     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1115                        non_const_filename);
1116     GNUNET_free (non_const_filename);
1117     GNUNET_free (pathbuf);
1118     return NULL;
1119   }
1120   GNUNET_free (pathbuf);
1121   GNUNET_free (non_const_filename);
1122
1123   /* Count the number of arguments */
1124   arg = (char **) argv;
1125   while (*arg)
1126   {
1127     arg++;
1128     argcount++;
1129   }
1130
1131   /* Allocate a copy argv */
1132   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1133
1134   /* Copy all argv strings */
1135   argcount = 0;
1136   arg = (char **) argv;
1137   while (*arg)
1138   {
1139     if (arg == argv)
1140       non_const_argv[argcount] = GNUNET_strdup (path);
1141     else
1142       non_const_argv[argcount] = GNUNET_strdup (*arg);
1143     arg++;
1144     argcount++;
1145   }
1146   non_const_argv[argcount] = NULL;
1147
1148   /* Count cmd len */
1149   cmdlen = 1;
1150   arg = non_const_argv;
1151   while (*arg)
1152   {
1153     cmdlen = cmdlen + strlen (*arg) + 4;
1154     arg++;
1155   }
1156
1157   /* Allocate and create cmd */
1158   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1159   arg = non_const_argv;
1160   while (*arg)
1161   {
1162     char arg_last_char = (*arg)[strlen (*arg) - 1];
1163     idx += sprintf (idx, "\"%s%s\"%s", *arg,
1164         arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1165     arg++;
1166   }
1167
1168   while (argcount > 0)
1169     GNUNET_free (non_const_argv[--argcount]);
1170   GNUNET_free (non_const_argv);
1171
1172   memset (&start, 0, sizeof (start));
1173   start.cb = sizeof (start);
1174   if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
1175     start.dwFlags |= STARTF_USESTDHANDLES;
1176
1177   stdih = GetStdHandle (STD_INPUT_HANDLE);
1178   GetHandleInformation (stdih, &stdif);
1179   if (pipe_stdin != NULL)
1180   {
1181     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1182                                        (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1183                                        &stdin_handle, sizeof (HANDLE));
1184     start.hStdInput = stdin_handle;
1185   }
1186   if (stdih)
1187   {
1188     if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
1189     {
1190       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
1191       if (pipe_stdin == NULL)
1192         start.hStdInput = stdih;
1193     }
1194     else
1195       SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
1196   }
1197     
1198
1199   stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
1200   GetHandleInformation (stdoh, &stdof);
1201   if (NULL != pipe_stdout)
1202   {
1203     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1204                                        (pipe_stdout,
1205                                         GNUNET_DISK_PIPE_END_WRITE),
1206                                        &stdout_handle, sizeof (HANDLE));
1207     start.hStdOutput = stdout_handle;
1208   }
1209   if (stdoh)
1210   {
1211     if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
1212     {
1213       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
1214       if (pipe_stdout == NULL)
1215         start.hStdOutput = stdoh;
1216     }
1217     else
1218       SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1219   }
1220
1221   stdeh = GetStdHandle (STD_ERROR_HANDLE);
1222   GetHandleInformation (stdeh, &stdef);
1223   if (stdeh)
1224   {
1225     if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
1226     {
1227       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
1228       start.hStdError = stdeh;
1229     }
1230     else
1231       SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1232   }
1233
1234   if (GNUNET_YES == pipe_control)
1235   {
1236     control_pipe =
1237       npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1238                     GNUNET_DISK_PERM_USER_READ |
1239                     GNUNET_DISK_PERM_USER_WRITE);
1240     if (control_pipe == NULL)
1241     {
1242       GNUNET_free (cmd);
1243       GNUNET_free (path);
1244       return NULL;
1245     }
1246   }
1247   else
1248     control_pipe = NULL;
1249   if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1250   {
1251     lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1252
1253     if (lsocks_pipe == NULL)
1254     {
1255       GNUNET_free (cmd);
1256       GNUNET_free (path);
1257       GNUNET_DISK_pipe_close (lsocks_pipe);
1258       return NULL;
1259     }
1260     lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1261         GNUNET_DISK_PIPE_END_WRITE);
1262     GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1263                                        &lsocks_write, sizeof (HANDLE));
1264     GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1265                                        (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1266                                        &lsocks_read, sizeof (HANDLE));
1267   }
1268
1269   env_off = 0;
1270   if (NULL != childpipename)
1271   {
1272     LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1273          childpipename);
1274     GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1275     GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1276     GNUNET_free (childpipename);
1277   }
1278   if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1279   {
1280     /*This will tell the child that we're going to send lsocks over the pipe*/
1281     GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1282     GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1283   }
1284   our_env[env_off++] = NULL;
1285   env_block = CreateCustomEnvTable (our_env);
1286   while (0 > env_off)
1287     GNUNET_free_non_null (our_env[--env_off]);
1288
1289   wpath_len = 0;
1290   if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1291   {
1292     LOG (GNUNET_ERROR_TYPE_DEBUG,
1293         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1294     GNUNET_free (env_block);
1295     GNUNET_free (cmd);
1296     return NULL;
1297   }
1298
1299   wcmd_len = 0;
1300   if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1301   {
1302     LOG (GNUNET_ERROR_TYPE_DEBUG,
1303         "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1304     GNUNET_free (env_block);
1305     GNUNET_free (cmd);
1306     free (wpath);
1307     return NULL;
1308   }
1309
1310   bresult = CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1311        DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1312   error_code = GetLastError ();
1313
1314   if ((NULL == pipe_stdin) && (stdih))
1315     SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
1316     
1317
1318   if ((NULL == pipe_stdout) && (stdoh))
1319     SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
1320
1321   if (stdeh)
1322     SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
1323
1324   GNUNET_free (env_block);
1325   GNUNET_free (cmd);
1326   free (wpath);
1327   free (wcmd);
1328
1329   if (!bresult)
1330   {
1331     SetErrnoFromWinError (error_code);
1332     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1333     if (NULL != control_pipe)
1334       GNUNET_DISK_file_close (control_pipe);
1335     if (NULL != lsocks)
1336       GNUNET_DISK_pipe_close (lsocks_pipe);
1337     return NULL;
1338   }
1339
1340   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1341   gnunet_proc->pid = proc.dwProcessId;
1342   gnunet_proc->handle = proc.hProcess;
1343   gnunet_proc->control_pipe = control_pipe;
1344
1345   CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1346
1347   ResumeThread (proc.hThread);
1348   CloseHandle (proc.hThread);
1349
1350   if ( (NULL == lsocks) || (INVALID_SOCKET == lsocks[0]) )
1351     return gnunet_proc;
1352
1353   GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1354
1355   /* This is a replacement for "goto error" that doesn't use goto */
1356   fail = 1;
1357   do
1358   {
1359     ssize_t wrote;
1360     uint64_t size;
1361     uint64_t count;
1362     unsigned int i;
1363
1364     /* Tell the number of sockets */
1365     for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1366
1367     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1368     if (sizeof (count) != wrote)
1369     {
1370       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1371                   "Failed to write %u count bytes to the child: %u\n",
1372                   sizeof (count), GetLastError ());
1373       break;
1374     }
1375     for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1376     {
1377       WSAPROTOCOL_INFOA pi;
1378       /* Get a socket duplication info */
1379       if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1380       {
1381         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1382                     "Failed to duplicate an socket[%llu]: %u\n", i, 
1383                     GetLastError ());
1384         break;
1385       }
1386       /* Synchronous I/O is not nice, but we can't schedule this:
1387        * lsocks will be closed/freed by the caller soon, and until
1388        * the child creates a duplicate, closing a socket here will
1389        * close it for good.
1390        */
1391       /* Send the size of the structure
1392        * (the child might be built with different headers...)
1393        */
1394       size = sizeof (pi);
1395       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1396       if (sizeof (size) != wrote)
1397       {
1398         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1399                     "Failed to write %u size[%llu] bytes to the child: %u\n", 
1400                     sizeof (size), i, GetLastError ());
1401         break;
1402       }
1403       /* Finally! Send the data */
1404       wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1405       if (sizeof (pi) != wrote)
1406       {
1407         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
1408                     "Failed to write %u socket[%llu] bytes to the child: %u\n", 
1409                     sizeof (pi), i, GetLastError ());
1410         break;
1411       }
1412     }
1413     /* This will block us until the child makes a final read or closes
1414      * the pipe (hence no 'wrote' check), since we have to wait for it
1415      * to duplicate the last socket, before we return and start closing
1416      * our own copies)
1417      */
1418     wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1419     fail = 0;
1420   }
1421   while (fail);
1422
1423   GNUNET_DISK_file_sync (lsocks_write_fd);
1424   GNUNET_DISK_pipe_close (lsocks_pipe);
1425
1426   if (fail)
1427   {
1428     /* If we can't pass on the socket(s), the child will block forever,
1429      * better put it out of its misery.
1430      */
1431     SafeTerminateProcess (gnunet_proc->handle, 0, 0);
1432     CloseHandle (gnunet_proc->handle);
1433     if (NULL != gnunet_proc->control_pipe)
1434       GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1435     GNUNET_free (gnunet_proc);
1436     return NULL;
1437   }
1438   return gnunet_proc;
1439 #endif
1440 }
1441
1442
1443
1444
1445 /**
1446  * Start a process.
1447  *
1448  * @param pipe_control should a pipe be used to send signals to the child?
1449  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1450  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1451  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1452  * @param filename name of the binary
1453  * @param argv NULL-terminated array of arguments to the process
1454  * @return pointer to process structure of the new process, NULL on error
1455  */
1456 struct GNUNET_OS_Process *
1457 GNUNET_OS_start_process_vap (int pipe_control,
1458                              enum GNUNET_OS_InheritStdioFlags std_inheritance,
1459                              struct GNUNET_DISK_PipeHandle *pipe_stdin,
1460                              struct GNUNET_DISK_PipeHandle *pipe_stdout,
1461                              const char *filename, 
1462                              char *const argv[])
1463 {
1464   return start_process (pipe_control,
1465                         std_inheritance,
1466                         pipe_stdin,
1467                         pipe_stdout,
1468                         NULL,
1469                         filename,
1470                         argv);
1471 }
1472
1473
1474 /**
1475  * Start a process.
1476  *
1477  * @param pipe_control should a pipe be used to send signals to the child?
1478  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1479  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1480  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1481  * @param filename name of the binary
1482  * @param va NULL-terminated list of arguments to the process
1483  * @return pointer to process structure of the new process, NULL on error
1484  */
1485 struct GNUNET_OS_Process *
1486 GNUNET_OS_start_process_va (int pipe_control,
1487                             enum GNUNET_OS_InheritStdioFlags std_inheritance,
1488                             struct GNUNET_DISK_PipeHandle *pipe_stdin,
1489                             struct GNUNET_DISK_PipeHandle *pipe_stdout,
1490                             const char *filename, va_list va)
1491 {
1492   struct GNUNET_OS_Process *ret;
1493   va_list ap;
1494   char **argv;
1495   int argc;
1496
1497   argc = 0;
1498   va_copy (ap, va);
1499   while (NULL != va_arg (ap, char *))
1500     argc++;
1501   va_end (ap);
1502   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1503   argc = 0;
1504   va_copy (ap, va);
1505   while (NULL != (argv[argc] = va_arg (ap, char *)))
1506     argc++;
1507   va_end (ap);
1508   ret = GNUNET_OS_start_process_vap (pipe_control,
1509                                      std_inheritance,
1510                                      pipe_stdin,
1511                                      pipe_stdout,
1512                                      filename,
1513                                      argv);
1514   GNUNET_free (argv);
1515   return ret;
1516 }
1517
1518
1519 /**
1520  * Start a process.
1521  *
1522  * @param pipe_control should a pipe be used to send signals to the child?
1523  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1524  * @param pipe_stdin pipe to use to send input to child process (or NULL)
1525  * @param pipe_stdout pipe to use to get output from child process (or NULL)
1526  * @param filename name of the binary
1527  * @param ... NULL-terminated list of arguments to the process
1528  * @return pointer to process structure of the new process, NULL on error
1529  */
1530 struct GNUNET_OS_Process *
1531 GNUNET_OS_start_process (int pipe_control,
1532                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
1533                          struct GNUNET_DISK_PipeHandle *pipe_stdin,
1534                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
1535                          const char *filename, ...)
1536 {
1537   struct GNUNET_OS_Process *ret;
1538   va_list ap;
1539
1540   va_start (ap, filename);
1541   ret = GNUNET_OS_start_process_va (pipe_control, std_inheritance, pipe_stdin,
1542                                     pipe_stdout, filename, ap);
1543   va_end (ap);
1544   return ret;
1545 }
1546
1547
1548 /**
1549  * Start a process.
1550  *
1551  * @param pipe_control should a pipe be used to send signals to the child?
1552  * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
1553  *        std handles of the parent are inherited by the child.
1554  *        pipe_stdin and pipe_stdout take priority over std_inheritance
1555  *        (when they are non-NULL).
1556  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1557  *         must be NULL on platforms where dup is not supported
1558  * @param filename name of the binary
1559  * @param argv NULL-terminated list of arguments to the process
1560  * @return process ID of the new process, -1 on error
1561  */
1562 struct GNUNET_OS_Process *
1563 GNUNET_OS_start_process_v (int pipe_control,
1564                            enum GNUNET_OS_InheritStdioFlags std_inheritance,
1565                            const SOCKTYPE *lsocks,
1566                            const char *filename,
1567                            char *const argv[])
1568 {
1569   return start_process (pipe_control,
1570                         std_inheritance,
1571                         NULL,
1572                         NULL,
1573                         lsocks,
1574                         filename,
1575                         argv);
1576 }
1577
1578
1579 /**
1580  * Retrieve the status of a process, waiting on him if dead.
1581  * Nonblocking version.
1582  * 
1583  * @param proc process ID
1584  * @param type status type
1585  * @param code return code/signal number
1586  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1587  */
1588 int
1589 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1590                           enum GNUNET_OS_ProcessStatusType *type,
1591                           unsigned long *code)
1592 {
1593 #ifndef MINGW
1594   int status;
1595   int ret;
1596
1597   GNUNET_assert (0 != proc);
1598   ret = waitpid (proc->pid, &status, WNOHANG);
1599   if (ret < 0)
1600   {
1601     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1602     return GNUNET_SYSERR;
1603   }
1604   if (0 == ret)
1605   {
1606     *type = GNUNET_OS_PROCESS_RUNNING;
1607     *code = 0;
1608     return GNUNET_NO;
1609   }
1610   if (proc->pid != ret)
1611   {
1612     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1613     return GNUNET_SYSERR;
1614   }
1615   if (WIFEXITED (status))
1616   {
1617     *type = GNUNET_OS_PROCESS_EXITED;
1618     *code = WEXITSTATUS (status);
1619   }
1620   else if (WIFSIGNALED (status))
1621   {
1622     *type = GNUNET_OS_PROCESS_SIGNALED;
1623     *code = WTERMSIG (status);
1624   }
1625   else if (WIFSTOPPED (status))
1626   {
1627     *type = GNUNET_OS_PROCESS_SIGNALED;
1628     *code = WSTOPSIG (status);
1629   }
1630 #ifdef WIFCONTINUED
1631   else if (WIFCONTINUED (status))
1632   {
1633     *type = GNUNET_OS_PROCESS_RUNNING;
1634     *code = 0;
1635   }
1636 #endif
1637   else
1638   {
1639     *type = GNUNET_OS_PROCESS_UNKNOWN;
1640     *code = 0;
1641   }
1642 #else
1643   HANDLE h;
1644   DWORD c, error_code, ret;
1645
1646   h = proc->handle;
1647   ret = proc->pid;
1648   if (h == NULL || ret == 0)
1649   {
1650     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1651          ret, h);
1652     return GNUNET_SYSERR;
1653   }
1654   if (h == NULL)
1655     h = GetCurrentProcess ();
1656
1657   SetLastError (0);
1658   ret = GetExitCodeProcess (h, &c);
1659   error_code = GetLastError ();
1660   if (ret == 0 || error_code != NO_ERROR)
1661   {
1662     SetErrnoFromWinError (error_code);
1663     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1664     return GNUNET_SYSERR;
1665   }
1666   if (STILL_ACTIVE == c)
1667   {
1668     *type = GNUNET_OS_PROCESS_RUNNING;
1669     *code = 0;
1670     return GNUNET_NO;
1671   }
1672   *type = GNUNET_OS_PROCESS_EXITED;
1673   *code = c;
1674 #endif
1675
1676   return GNUNET_OK;
1677 }
1678
1679
1680 /**
1681  * Wait for a process
1682  *
1683  * @param proc pointer to process structure
1684  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1685  */
1686 int
1687 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1688 {
1689 #ifndef MINGW
1690   pid_t pid = proc->pid;
1691   pid_t ret;
1692
1693   while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1694           (EINTR == errno) ) ;
1695   if (pid != ret) 
1696   {
1697     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1698     return GNUNET_SYSERR;
1699   }
1700   return GNUNET_OK;
1701 #else
1702   HANDLE h;
1703
1704   h = proc->handle;
1705   if (NULL == h)
1706   {
1707     LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1708          proc->pid, h);
1709     return GNUNET_SYSERR;
1710   }
1711   if (NULL == h)
1712     h = GetCurrentProcess ();
1713
1714   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1715   {
1716     SetErrnoFromWinError (GetLastError ());
1717     return GNUNET_SYSERR;
1718   }
1719   return GNUNET_OK;
1720 #endif
1721 }
1722
1723
1724 /**
1725  * Handle to a command.
1726  */
1727 struct GNUNET_OS_CommandHandle
1728 {
1729
1730   /**
1731    * Process handle.
1732    */
1733   struct GNUNET_OS_Process *eip;
1734
1735   /**
1736    * Handle to the output pipe.
1737    */
1738   struct GNUNET_DISK_PipeHandle *opipe;
1739
1740   /**
1741    * Read-end of output pipe.
1742    */
1743   const struct GNUNET_DISK_FileHandle *r;
1744
1745   /**
1746    * Function to call on each line of output.
1747    */
1748   GNUNET_OS_LineProcessor proc;
1749
1750   /**
1751    * Closure for 'proc'.
1752    */
1753   void *proc_cls;
1754
1755   /**
1756    * Buffer for the output.
1757    */
1758   char buf[1024];
1759
1760   /**
1761    * Task reading from pipe.
1762    */
1763   GNUNET_SCHEDULER_TaskIdentifier rtask;
1764
1765   /**
1766    * When to time out.
1767    */
1768   struct GNUNET_TIME_Absolute timeout;
1769
1770   /**
1771    * Current read offset in buf.
1772    */
1773   size_t off;
1774 };
1775
1776
1777 /**
1778  * Stop/kill a command.  Must ONLY be called either from
1779  * the callback after 'NULL' was passed for 'line' *OR*
1780  * from an independent task (not within the line processor).
1781  *
1782  * @param cmd handle to the process
1783  */
1784 void
1785 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1786 {
1787   if (NULL != cmd->proc)
1788   {
1789     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1790     GNUNET_SCHEDULER_cancel (cmd->rtask);
1791   }
1792   (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1793   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1794   GNUNET_OS_process_destroy (cmd->eip);
1795   GNUNET_DISK_pipe_close (cmd->opipe);
1796   GNUNET_free (cmd);
1797 }
1798
1799
1800 /**
1801  * Read from the process and call the line processor.
1802  *
1803  * @param cls the 'struct GNUNET_OS_CommandHandle'
1804  * @param tc scheduler context
1805  */
1806 static void
1807 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1808 {
1809   struct GNUNET_OS_CommandHandle *cmd = cls;
1810   GNUNET_OS_LineProcessor proc;
1811   char *end;
1812   ssize_t ret;
1813
1814   cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1815   if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1816   {
1817     /* timeout, shutdown, etc. */
1818     proc = cmd->proc;
1819     cmd->proc = NULL;
1820     proc (cmd->proc_cls, NULL);
1821     return;
1822   }
1823   ret =
1824       GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1825                              sizeof (cmd->buf) - cmd->off);
1826   if (ret <= 0)
1827   {
1828     if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1829     {
1830       cmd->buf[cmd->off] = '\0';
1831       cmd->proc (cmd->proc_cls, cmd->buf);
1832     }
1833     proc = cmd->proc;
1834     cmd->proc = NULL;
1835     proc (cmd->proc_cls, NULL);
1836     return;
1837   }
1838   end = memchr (&cmd->buf[cmd->off], '\n', ret);
1839   cmd->off += ret;
1840   while (NULL != end)
1841   {
1842     *end = '\0';
1843     cmd->proc (cmd->proc_cls, cmd->buf);
1844     memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1845     cmd->off -= (end + 1 - cmd->buf);
1846     end = memchr (cmd->buf, '\n', cmd->off);
1847   }
1848   cmd->rtask =
1849       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1850                                       (cmd->timeout), cmd->r, &cmd_read, cmd);
1851 }
1852
1853
1854 /**
1855  * Run the given command line and call the given function
1856  * for each line of the output.
1857  *
1858  * @param proc function to call for each line of the output
1859  * @param proc_cls closure for proc
1860  * @param timeout when to time out
1861  * @param binary command to run
1862  * @param ... arguments to command
1863  * @return NULL on error
1864  */
1865 struct GNUNET_OS_CommandHandle *
1866 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1867                        struct GNUNET_TIME_Relative timeout, const char *binary,
1868                        ...)
1869 {
1870   struct GNUNET_OS_CommandHandle *cmd;
1871   struct GNUNET_OS_Process *eip;
1872   struct GNUNET_DISK_PipeHandle *opipe;
1873   va_list ap;
1874
1875   opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1876   if (NULL == opipe)
1877     return NULL;
1878   va_start (ap, binary);
1879   /* redirect stdout, don't inherit stderr/stdin */
1880   eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, binary, ap);
1881   va_end (ap);
1882   if (NULL == eip)
1883   {
1884     GNUNET_DISK_pipe_close (opipe);
1885     return NULL;
1886   }
1887   GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1888   cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1889   cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1890   cmd->eip = eip;
1891   cmd->opipe = opipe;
1892   cmd->proc = proc;
1893   cmd->proc_cls = proc_cls;
1894   cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1895   cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1896   return cmd;
1897 }
1898
1899
1900 /* end of os_priority.c */