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