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