just use setpriority
[oweals/gnunet.git] / src / util / os_priority.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006 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 "disk.h"
32
33 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
34
35 struct GNUNET_OS_Process
36 {
37   pid_t pid;
38 #if WINDOWS
39   HANDLE handle;
40 #endif
41   int sig;
42   struct GNUNET_DISK_FileHandle *control_pipe;
43 };
44
45 static struct GNUNET_OS_Process current_process;
46
47
48 /**
49  * This handler is called when there are control data to be read on the pipe
50  */
51 void
52 GNUNET_OS_parent_control_handler (void *cls,
53                                   const struct
54                                   GNUNET_SCHEDULER_TaskContext * tc)
55 {
56   struct GNUNET_DISK_FileHandle *control_pipe = (struct GNUNET_DISK_FileHandle *) cls;
57   int sig;
58
59   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__, tc->reason);
60
61   if (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE))
62   {
63     GNUNET_DISK_npipe_close (control_pipe);
64   }
65   else
66   {
67     if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) != sizeof (sig))
68     {
69       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
70       GNUNET_DISK_npipe_close (control_pipe);
71     }
72     else
73     {
74       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
75       raise (sig);
76       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Re-scheduling the parent control handler pipe\n");
77       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe, GNUNET_OS_parent_control_handler, control_pipe);
78     }
79   }
80 }
81
82 /**
83  * Connects this process to its parent via pipe
84  */
85 void
86 GNUNET_OS_install_parent_control_handler (void *cls,
87                                           const struct
88                                           GNUNET_SCHEDULER_TaskContext * tc)
89 {
90   char *env_buf;
91   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
92
93   env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
94   if (env_buf == NULL || strlen (env_buf) <= 0)
95   {
96     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not installing a handler because %s=%s\n", GNUNET_OS_CONTROL_PIPE, env_buf);
97     return;
98   }
99
100   control_pipe = GNUNET_DISK_npipe_open (env_buf, GNUNET_DISK_OPEN_READ,
101         GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
102   if (control_pipe == NULL)
103   {
104     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to open the pipe `%s'\n", env_buf);
105     return;
106   }
107
108   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
109   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe, GNUNET_OS_parent_control_handler, control_pipe);
110 }
111
112 /**
113  * Get process structure for current process
114  *
115  * The pointer it returns points to static memory location and must not be
116  * deallocated/closed
117  *
118  * @return pointer to the process sturcutre for this process
119  */
120 struct GNUNET_OS_Process *
121 GNUNET_OS_process_current ()
122 {
123 #if WINDOWS
124   current_process.pid = GetCurrentProcessId ();
125   current_process.handle = GetCurrentProcess ();
126 #else
127   current_process.pid = 0;
128 #endif
129   return &current_process;
130 }
131
132 int
133 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
134 {
135 #if ENABLE_WINDOWS_WORKAROUNDS
136   int res;
137   int ret;
138
139   ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof(sig));
140   if (ret != sizeof(sig))
141   {
142     if (errno == ECOMM)
143       /* Child process is not controllable via pipe */
144       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145           "Child process is not controllable, will kill it directly\n");
146     else
147       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
148           "Failed to write into control pipe , errno is %d\n", errno);
149     res = PLIBC_KILL (proc->pid, sig);
150   }
151   else
152   {
153         struct GNUNET_NETWORK_FDSet *rfds;
154     struct GNUNET_NETWORK_FDSet *efds;
155
156     rfds = GNUNET_NETWORK_fdset_create ();
157     efds = GNUNET_NETWORK_fdset_create ();
158
159     GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe);
160     GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe);
161
162  read_next:
163         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164             "Wrote control code into control pipe, now waiting\n");
165
166         ret = GNUNET_NETWORK_socket_select (rfds, NULL, efds,
167             GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_unit (),
168                 5000));
169
170         if (ret < 1 || GNUNET_NETWORK_fdset_handle_isset (efds,
171             proc->control_pipe))
172           {
173             /* Just to be sure */
174             PLIBC_KILL (proc->pid, sig);
175             res = 0;
176           }
177         else
178           {
179             if (GNUNET_DISK_file_read (proc->control_pipe, &ret,
180                 sizeof(ret)) != GNUNET_OK)
181               res = PLIBC_KILL (proc->pid, sig);
182
183             /* Child signaled shutdown is in progress */
184             goto read_next;
185           }
186       }
187
188     return res;
189 #else
190   return kill (proc->pid, sig);
191 #endif
192 }
193
194 /**
195  * Get the pid of the process in question
196  *
197  * @param proc the process to get the pid of
198  *
199  * @return the current process id
200  */
201 pid_t
202 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
203 {
204   return proc->pid;
205 }
206
207 void
208 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
209 {
210 #if ENABLE_WINDOWS_WORKAROUNDS
211   if (proc->control_pipe)
212     GNUNET_DISK_npipe_close (proc->control_pipe);
213 #endif
214 // FIXME NILS
215 #ifdef WINDOWS
216   if (proc->handle != NULL)
217     CloseHandle (proc->handle);
218 #endif
219   GNUNET_free (proc);
220 }
221
222 // FIXME NILS
223 #if WINDOWS
224 #include "gnunet_signal_lib.h"
225
226 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
227
228 /**
229  * Make seaspider happy.
230  */
231 #define DWORD_WINAPI DWORD WINAPI
232
233 /**
234  * @brief Waits for a process to terminate and invokes the SIGCHLD handler
235  * @param proc pointer to process structure
236  */
237 static DWORD_WINAPI
238 ChildWaitThread (void *arg)
239 {
240   struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
241   WaitForSingleObject (proc->handle, INFINITE);
242
243   if (w32_sigchld_handler)
244     w32_sigchld_handler ();
245
246   return 0;
247 }
248 #endif
249
250 /**
251  * Set process priority
252  *
253  * @param proc pointer to process structure
254  * @param prio priority value
255  * @return GNUNET_OK on success, GNUNET_SYSERR on error
256  */
257 int
258 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
259                                 enum GNUNET_SCHEDULER_Priority prio)
260 {
261   int rprio;
262
263   GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
264   if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
265     return GNUNET_OK;
266
267   /* convert to MINGW/Unix values */
268   switch (prio)
269     {
270     case GNUNET_SCHEDULER_PRIORITY_UI:
271     case GNUNET_SCHEDULER_PRIORITY_URGENT:
272 #ifdef MINGW
273       rprio = HIGH_PRIORITY_CLASS;
274 #else
275       rprio = 0;
276 #endif
277       break;
278
279     case GNUNET_SCHEDULER_PRIORITY_HIGH:
280 #ifdef MINGW
281       rprio = ABOVE_NORMAL_PRIORITY_CLASS;
282 #else
283       rprio = 5;
284 #endif
285       break;
286
287     case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
288 #ifdef MINGW
289       rprio = NORMAL_PRIORITY_CLASS;
290 #else
291       rprio = 7;
292 #endif
293       break;
294
295     case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
296 #ifdef MINGW
297       rprio = BELOW_NORMAL_PRIORITY_CLASS;
298 #else
299       rprio = 10;
300 #endif
301       break;
302
303     case GNUNET_SCHEDULER_PRIORITY_IDLE:
304 #ifdef MINGW
305       rprio = IDLE_PRIORITY_CLASS;
306 #else
307       rprio = 19;
308 #endif
309       break;
310     default:
311       GNUNET_assert (0);
312       return GNUNET_SYSERR;
313     }
314
315   /* Set process priority */
316 #ifdef MINGW
317   {
318     HANDLE h = proc->handle;
319     GNUNET_assert (h != NULL);
320     SetPriorityClass (h, rprio);
321   }
322 #elif LINUX 
323   if (0 != setpriority (PRIO_PROCESS, proc->pid, rprio))
324     {
325       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
326                            GNUNET_ERROR_TYPE_BULK, "setpriority");
327       return GNUNET_SYSERR;
328     }
329 #else
330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
331               "Priority management not availabe for this platform\n");
332 #endif
333   return GNUNET_OK;
334 }
335
336 #if MINGW
337 static char *
338 CreateCustomEnvTable (char **vars)
339 {
340   char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
341   size_t tablesize = 0;
342   size_t items_count = 0;
343   size_t n_found = 0, n_var;
344   char *index = NULL;
345   size_t c;
346   size_t var_len;
347   char *var;
348   char *val;
349   win32_env_table = GetEnvironmentStringsA ();
350   if (win32_env_table == NULL)
351     return NULL;
352   for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
353   n_var = c;
354   index = GNUNET_malloc (n_var);
355   for (c = 0; c < n_var; c++)
356     index[c] = 0;
357   for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
358   {
359     size_t len = strlen (ptr);
360     int found = 0;
361     for (var_ptr = vars; *var_ptr; var_ptr++)
362     {
363       var = *var_ptr++;
364       val = *var_ptr;
365       var_len = strlen (var);
366       if (strncmp (var, ptr, var_len) == 0)
367       {
368         found = 1;
369         index[c] = 1;
370         tablesize += var_len + strlen (val) + 1;
371         break;
372       }
373     }
374     if (!found)
375       tablesize += len + 1;
376     ptr += len + 1; 
377   }
378   for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
379   {
380     var = *var_ptr++;
381     val = *var_ptr;
382     if (index[c] != 1)
383       n_found += strlen (var) + strlen (val) + 1;
384   }
385   result = GNUNET_malloc (tablesize + n_found + 1);
386   for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
387   {
388     size_t len = strlen (ptr);
389     int found = 0;
390     for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
391     {
392       var = *var_ptr++;
393       val = *var_ptr;
394       var_len = strlen (var);
395       if (strncmp (var, ptr, var_len) == 0)
396       {
397         found = 1;
398         break;
399       }
400     }
401     if (!found)
402     {
403       strcpy (result_ptr, ptr);
404       result_ptr += len + 1;
405     }
406     else
407     {
408       strcpy (result_ptr, var);
409       result_ptr += var_len;
410       strcpy (result_ptr, val);
411       result_ptr += strlen (val) + 1;
412     }
413     ptr += len + 1;
414   }
415   for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
416   {
417     var = *var_ptr++;
418     val = *var_ptr;
419     var_len = strlen (var);
420     if (index[c] != 1)
421     {
422       strcpy (result_ptr, var);
423       result_ptr += var_len;
424       strcpy (result_ptr, val);
425       result_ptr += strlen (val) + 1;
426     }
427   }
428   FreeEnvironmentStrings (win32_env_table);
429   GNUNET_free (index);
430   *result_ptr = 0;
431   return result;
432 }
433 #endif
434
435 /**
436  * Start a process.
437  *
438  * @param pipe_stdin pipe to use to send input to child process (or NULL)
439  * @param pipe_stdout pipe to use to get output from child process (or NULL)
440  * @param filename name of the binary
441  * @param ... NULL-terminated list of arguments to the process
442  * @return pointer to process structure of the new process, NULL on error
443  */
444 struct GNUNET_OS_Process *
445 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, 
446                          struct GNUNET_DISK_PipeHandle *pipe_stdout,
447                          const char *filename, ...)
448 {
449   va_list ap;
450 #if ENABLE_WINDOWS_WORKAROUNDS
451   char *childpipename = NULL;
452   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
453 #endif
454   struct GNUNET_OS_Process *gnunet_proc = NULL;
455
456 #ifndef MINGW
457   pid_t ret;
458   char **argv;
459   int argc;
460   int fd_stdout_write;
461   int fd_stdout_read;
462   int fd_stdin_read;
463   int fd_stdin_write;
464
465 #if ENABLE_WINDOWS_WORKAROUNDS
466   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
467       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
468       GNUNET_DISK_PERM_USER_WRITE);
469   if (control_pipe == NULL)
470     return NULL;
471 #endif
472
473   argc = 0;
474   va_start (ap, filename);
475   while (NULL != va_arg (ap, char *))
476       argc++;
477   va_end (ap);
478   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
479   argc = 0;
480   va_start (ap, filename);
481   while (NULL != (argv[argc] = va_arg (ap, char *)))
482     argc++;
483   va_end (ap);
484   if (pipe_stdout != NULL)
485     {
486       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
487       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
488     }
489   if (pipe_stdin != NULL)
490     {
491       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
492       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
493     }
494
495 #if HAVE_WORKING_VFORK
496   ret = vfork ();
497 #else
498   ret = fork ();
499 #endif
500   if (ret != 0)
501     {
502       if (ret == -1)
503         {
504           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
505 #if ENABLE_WINDOWS_WORKAROUNDS
506           GNUNET_DISK_npipe_close (control_pipe);
507 #endif
508         }
509       else
510         {
511
512 #if HAVE_WORKING_VFORK
513           /* let's hope vfork actually works; for some extreme cases (including
514              a testcase) we need 'execvp' to have run before we return, since
515              we may send a signal to the process next and we don't want it
516              to be caught by OUR signal handler (but either by the default
517              handler or the actual handler as installed by the process itself). */
518 #else
519           /* let's give the child process a chance to run execvp, 1s should
520              be plenty in practice */
521           if (pipe_stdout != NULL)
522             GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
523           if (pipe_stdin != NULL)
524             GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
525           sleep (1);
526 #endif
527           gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
528           gnunet_proc->pid = ret;
529 #if ENABLE_WINDOWS_WORKAROUNDS
530           gnunet_proc->control_pipe = control_pipe;
531 #endif
532         }
533       GNUNET_free (argv);
534 #if ENABLE_WINDOWS_WORKAROUNDS
535       GNUNET_free (childpipename);
536 #endif
537       return gnunet_proc;
538     }
539
540 #if ENABLE_WINDOWS_WORKAROUNDS
541   setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
542   GNUNET_free (childpipename);
543 #endif
544
545   if (pipe_stdout != NULL)
546     {
547       GNUNET_break (0 == close (fd_stdout_read));
548       if (-1 == dup2(fd_stdout_write, 1))
549         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");  
550       GNUNET_break (0 == close (fd_stdout_write));
551     }
552
553   if (pipe_stdin != NULL)
554     {
555
556       GNUNET_break (0 == close (fd_stdin_write));
557       if (-1 == dup2(fd_stdin_read, 0))
558         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");  
559       GNUNET_break (0 == close (fd_stdin_read));
560     }
561   execvp (filename, argv);
562   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
563   _exit (1);
564 #else
565   char *arg;
566   unsigned int cmdlen;
567   char *cmd, *idx;
568   STARTUPINFO start;
569   PROCESS_INFORMATION proc;
570
571   HANDLE stdin_handle;
572   HANDLE stdout_handle;
573
574   char path[MAX_PATH + 1];
575
576   char *our_env[3] = { NULL, NULL, NULL };
577   char *env_block = NULL;
578   char *pathbuf;
579   DWORD pathbuf_len, alloc_len;
580   char *self_prefix;
581   char *bindir;
582   char *libdir;
583   char *ptr;
584   char *non_const_filename;
585
586   /* Search in prefix dir (hopefully - the directory from which
587    * the current module was loaded), bindir and libdir, then in PATH
588    */
589   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
590   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
591   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
592
593   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
594
595   alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
596
597   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
598
599   ptr = pathbuf;
600   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
601   GNUNET_free (self_prefix);
602   GNUNET_free (bindir);
603   GNUNET_free (libdir);
604
605   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
606   GNUNET_assert (alloc_len == (pathbuf_len - 1));
607
608   cmdlen = strlen (filename);
609   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
610     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
611   else
612     GNUNET_asprintf (&non_const_filename, "%s", filename);
613
614   /* Check that this is the full path. If it isn't, search. */
615   if (non_const_filename[1] == ':')
616     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
617   else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
618     {
619       SetErrnoFromWinError (GetLastError ());
620       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
621       GNUNET_free (non_const_filename);
622       GNUNET_free (pathbuf);
623       return NULL;
624     }
625   GNUNET_free (pathbuf);
626   GNUNET_free (non_const_filename);
627  
628   cmdlen = 0;
629   va_start (ap, filename);
630   while (NULL != (arg = va_arg (ap, char *)))
631   {
632       if (cmdlen == 0)
633         cmdlen = cmdlen + strlen (path) + 3;
634       else
635         cmdlen = cmdlen + strlen (arg) + 3;
636   }
637   va_end (ap);
638
639   cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
640   va_start (ap, filename);
641   while (NULL != (arg = va_arg (ap, char *)))
642   {
643       if (idx == cmd)
644         idx += sprintf (idx, "\"%s\" ", path);
645       else
646         idx += sprintf (idx, "\"%s\" ", arg);
647   }
648   va_end (ap);
649
650   memset (&start, 0, sizeof (start));
651   start.cb = sizeof (start);
652
653   if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
654     start.dwFlags |= STARTF_USESTDHANDLES;
655
656   if (pipe_stdin != NULL)
657     {
658       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
659       start.hStdInput = stdin_handle;
660     }
661
662   if (pipe_stdout != NULL)
663     {
664       GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
665       start.hStdOutput = stdout_handle;
666     }
667
668   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
669       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
670       GNUNET_DISK_PERM_USER_WRITE);
671   if (control_pipe == NULL)
672   {
673     GNUNET_free (cmd);
674     GNUNET_free (path);
675     return NULL;
676   }
677
678   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
679
680   GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
681   GNUNET_asprintf (&our_env[1], "%s", childpipename);
682   our_env[2] = NULL;
683   env_block = CreateCustomEnvTable (our_env);
684   GNUNET_free (our_env[0]);
685   GNUNET_free (our_env[1]);
686
687   if (!CreateProcessA
688       (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
689        env_block, NULL, &start, &proc))
690     {
691       SetErrnoFromWinError (GetLastError ());
692       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
693       GNUNET_free (env_block);
694       GNUNET_free (cmd);
695       return NULL;
696     }
697
698   GNUNET_free (env_block);
699
700   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
701   gnunet_proc->pid = proc.dwProcessId;
702   gnunet_proc->handle = proc.hProcess;
703   gnunet_proc->control_pipe = control_pipe;
704
705   CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
706
707   ResumeThread (proc.hThread);
708   CloseHandle (proc.hThread);
709
710   GNUNET_free (cmd);
711
712   return gnunet_proc;
713 #endif
714
715 }
716
717
718
719 /**
720  * Start a process.
721  *
722  * @param lsocks array of listen sockets to dup systemd-style (or NULL);
723  *         must be NULL on platforms where dup is not supported
724  * @param filename name of the binary
725  * @param argv NULL-terminated list of arguments to the process
726  * @return process ID of the new process, -1 on error
727  */
728 struct GNUNET_OS_Process *
729 GNUNET_OS_start_process_v (const int *lsocks,
730                            const char *filename, char *const argv[])
731 {
732 #if ENABLE_WINDOWS_WORKAROUNDS
733   struct GNUNET_DISK_FileHandle *control_pipe = NULL;
734   char *childpipename = NULL;
735 #endif
736
737 #ifndef MINGW
738   pid_t ret;
739   char lpid[16];
740   char fds[16];
741   struct GNUNET_OS_Process *gnunet_proc = NULL;
742   int i;
743   int j;
744   int k;
745   int tgt;
746   int flags;
747   int *lscp;
748   unsigned int ls;    
749
750 #if ENABLE_WINDOWS_WORKAROUNDS
751   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
752       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
753       GNUNET_DISK_PERM_USER_WRITE);
754   if (control_pipe == NULL)
755     return NULL;
756 #endif
757
758   lscp = NULL;
759   ls = 0;
760   if (lsocks != NULL)
761     {
762       i = 0;
763       while (-1 != (k = lsocks[i++]))
764         GNUNET_array_append (lscp, ls, k);      
765       GNUNET_array_append (lscp, ls, -1);
766     }
767 #if HAVE_WORKING_VFORK
768   ret = vfork ();
769 #else
770   ret = fork ();
771 #endif
772   if (ret != 0)
773     {
774       if (ret == -1)
775         {
776           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
777 #if ENABLE_WINDOWS_WORKAROUNDS
778           GNUNET_DISK_npipe_close (control_pipe);
779 #endif
780         }
781       else
782         {
783 #if HAVE_WORKING_VFORK
784           /* let's hope vfork actually works; for some extreme cases (including
785              a testcase) we need 'execvp' to have run before we return, since
786              we may send a signal to the process next and we don't want it
787              to be caught by OUR signal handler (but either by the default
788              handler or the actual handler as installed by the process itself). */
789 #else
790           /* let's give the child process a chance to run execvp, 1s should
791              be plenty in practice */
792           sleep (1);
793 #endif
794           gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
795           gnunet_proc->pid = ret;
796 #if ENABLE_WINDOWS_WORKAROUNDS
797           gnunet_proc->control_pipe = control_pipe;
798
799 #endif
800         }
801       GNUNET_array_grow (lscp, ls, 0);
802 #if ENABLE_WINDOWS_WORKAROUNDS
803       GNUNET_free (childpipename);
804 #endif
805       return gnunet_proc;
806     }
807
808 #if ENABLE_WINDOWS_WORKAROUNDS
809         setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
810         GNUNET_free (childpipename);
811 #endif
812
813   if (lscp != NULL)
814     {
815       /* read systemd documentation... */
816       GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
817       setenv ("LISTEN_PID", lpid, 1);      
818       i = 0;
819       tgt = 3;
820       while (-1 != lscp[i])
821         {
822           j = i + 1;
823           while (-1 != lscp[j])
824             {
825               if (lscp[j] == tgt)
826                 {
827                   /* dup away */
828                   k = dup (lscp[j]);
829                   GNUNET_assert (-1 != k);
830                   GNUNET_assert (0 == close (lscp[j]));
831                   lscp[j] = k;
832                   break;
833                 }
834               j++;
835             }
836           if (lscp[i] != tgt)
837             {
838               /* Bury any existing FD, no matter what; they should all be closed
839                  on exec anyway and the important onces have been dup'ed away */
840               (void) close (tgt);             
841               GNUNET_assert (-1 != dup2 (lscp[i], tgt));
842             }
843           /* unset close-on-exec flag */
844           flags = fcntl (tgt, F_GETFD);
845           GNUNET_assert (flags >= 0);
846           flags &= ~FD_CLOEXEC;
847           fflush (stderr);
848           (void) fcntl (tgt, F_SETFD, flags);
849           tgt++;
850           i++;
851         }
852       GNUNET_snprintf (fds, sizeof (fds), "%u", i);
853       setenv ("LISTEN_FDS", fds, 1); 
854     }
855   GNUNET_array_grow (lscp, ls, 0);
856   execvp (filename, argv);
857   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
858   _exit (1);
859 #else
860   char **arg, **non_const_argv;
861   unsigned int cmdlen;
862   char *cmd, *idx;
863   STARTUPINFO start;
864   PROCESS_INFORMATION proc;
865   int argcount = 0;
866   struct GNUNET_OS_Process *gnunet_proc = NULL;
867
868   char path[MAX_PATH + 1];
869
870   char *our_env[3] = { NULL, NULL, NULL };
871   char *env_block = NULL;
872   char *pathbuf;
873   DWORD pathbuf_len, alloc_len;
874   char *self_prefix;
875   char *bindir;
876   char *libdir;
877   char *ptr;
878   char *non_const_filename;
879
880   GNUNET_assert (lsocks == NULL);
881
882   /* Search in prefix dir (hopefully - the directory from which
883    * the current module was loaded), bindir and libdir, then in PATH
884    */
885   self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
886   bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
887   libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
888
889   pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
890
891   alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
892
893   pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
894
895   ptr = pathbuf;
896   ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
897   GNUNET_free (self_prefix);
898   GNUNET_free (bindir);
899   GNUNET_free (libdir);
900
901   alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
902   if (alloc_len != pathbuf_len - 1)
903   {
904     GNUNET_free (pathbuf);
905     errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
906     return NULL;
907   }
908
909   cmdlen = strlen (filename);
910   if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
911     GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
912   else
913     GNUNET_asprintf (&non_const_filename, "%s", filename);
914
915   /* Check that this is the full path. If it isn't, search. */
916   if (non_const_filename[1] == ':')
917     snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
918   else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
919     {
920       SetErrnoFromWinError (GetLastError ());
921       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
922       GNUNET_free (non_const_filename);
923       GNUNET_free (pathbuf);
924       return NULL;
925     }
926   GNUNET_free (pathbuf);
927   GNUNET_free (non_const_filename);
928
929   /* Count the number of arguments */
930   arg = (char **) argv;
931   while (*arg)
932     {
933       arg++;
934       argcount++;
935     }
936
937   /* Allocate a copy argv */
938   non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
939
940   /* Copy all argv strings */
941   argcount = 0;
942   arg = (char **) argv;
943   while (*arg)
944     {
945       if (arg == argv)
946         non_const_argv[argcount] = GNUNET_strdup (path);
947       else
948         non_const_argv[argcount] = GNUNET_strdup (*arg);
949       arg++;
950       argcount++;
951     }
952   non_const_argv[argcount] = NULL;
953
954   /* Count cmd len */
955   cmdlen = 1;
956   arg = non_const_argv;
957   while (*arg)
958     {
959       cmdlen = cmdlen + strlen (*arg) + 3;
960       arg++;
961     }
962
963   /* Allocate and create cmd */
964   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
965   arg = non_const_argv;
966   while (*arg)
967     {
968       idx += sprintf (idx, "\"%s\" ", *arg);
969       arg++;
970     }
971
972   while (argcount > 0)
973     GNUNET_free (non_const_argv[--argcount]);
974   GNUNET_free (non_const_argv);
975
976   memset (&start, 0, sizeof (start));
977   start.cb = sizeof (start);
978
979   control_pipe = GNUNET_DISK_npipe_create (&childpipename,
980       GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
981       GNUNET_DISK_PERM_USER_WRITE);
982   if (control_pipe == NULL)
983   {
984     GNUNET_free (cmd);
985     GNUNET_free (path);
986     return NULL;
987   }
988
989   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
990
991   GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
992   GNUNET_asprintf (&our_env[1], "%s", childpipename);
993   our_env[2] = NULL;
994   env_block = CreateCustomEnvTable (our_env);
995   GNUNET_free (our_env[0]);
996   GNUNET_free (our_env[1]);
997
998   if (!CreateProcess
999       (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1000        env_block, NULL, &start, &proc))
1001     {
1002       SetErrnoFromWinError (GetLastError ());
1003       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1004       GNUNET_free (env_block);
1005       GNUNET_free (cmd);
1006       return NULL;
1007     }
1008
1009   GNUNET_free (env_block);
1010
1011   gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1012   gnunet_proc->pid = proc.dwProcessId;
1013   gnunet_proc->handle = proc.hProcess;
1014   gnunet_proc->control_pipe = control_pipe;
1015
1016   CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1017
1018   ResumeThread (proc.hThread);
1019   CloseHandle (proc.hThread);
1020   GNUNET_free (cmd);
1021
1022   return gnunet_proc;
1023 #endif
1024 }
1025
1026 /**
1027  * Retrieve the status of a process
1028  * @param proc process ID
1029  * @param type status type
1030  * @param code return code/signal number
1031  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1032  */
1033 int
1034 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc, 
1035                           enum GNUNET_OS_ProcessStatusType *type,
1036                           unsigned long *code)
1037 {
1038 #ifndef MINGW
1039   int status;
1040   int ret;
1041
1042   GNUNET_assert (0 != proc);
1043   ret = waitpid (proc->pid, &status, WNOHANG);
1044   if (ret < 0)
1045     {
1046       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1047       return GNUNET_SYSERR;
1048     }
1049   if (0 == ret)
1050     {
1051       *type = GNUNET_OS_PROCESS_RUNNING;
1052       *code = 0;
1053       return GNUNET_NO;
1054     }
1055   if (proc->pid != ret)
1056     {
1057       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1058       return GNUNET_SYSERR;
1059     }
1060   if (WIFEXITED (status))
1061     {
1062       *type = GNUNET_OS_PROCESS_EXITED;
1063       *code = WEXITSTATUS (status);
1064     }
1065   else if (WIFSIGNALED (status))
1066     {
1067       *type = GNUNET_OS_PROCESS_SIGNALED;
1068       *code = WTERMSIG (status);
1069     }
1070   else if (WIFSTOPPED (status))
1071     {
1072       *type = GNUNET_OS_PROCESS_SIGNALED;
1073       *code = WSTOPSIG (status);
1074     }
1075 #ifdef WIFCONTINUED
1076   else if (WIFCONTINUED (status))
1077     {
1078       *type = GNUNET_OS_PROCESS_RUNNING;
1079       *code = 0;
1080     }
1081 #endif
1082   else
1083     {
1084       *type = GNUNET_OS_PROCESS_UNKNOWN;
1085       *code = 0;
1086     }
1087 #else
1088   HANDLE h;
1089   DWORD c, error_code, ret;
1090
1091   h = proc->handle;
1092   ret = proc->pid;
1093   if (h == NULL || ret == 0)
1094     {
1095       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1096       return GNUNET_SYSERR;
1097     }
1098   if (h == NULL)
1099     h = GetCurrentProcess ();
1100
1101   SetLastError (0);
1102   ret = GetExitCodeProcess (h, &c);
1103   error_code = GetLastError ();
1104   if (ret == 0 || error_code != NO_ERROR)
1105   {
1106       SetErrnoFromWinError (error_code);
1107       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1108       return GNUNET_SYSERR;
1109   }
1110   if (STILL_ACTIVE == c)
1111     {
1112       *type = GNUNET_OS_PROCESS_RUNNING;
1113       *code = 0;
1114       return GNUNET_NO;
1115     }
1116   *type = GNUNET_OS_PROCESS_EXITED;
1117   *code = c;
1118 #endif
1119
1120   return GNUNET_OK;
1121 }
1122
1123 /**
1124  * Wait for a process
1125  * @param proc pointer to process structure
1126  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1127  */
1128 int
1129 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1130 {
1131
1132 #ifndef MINGW
1133   pid_t pid = proc->pid;
1134   if (pid != waitpid (pid, NULL, 0))
1135     return GNUNET_SYSERR;
1136   return GNUNET_OK;
1137 #else
1138   HANDLE h;
1139   int ret;
1140
1141   h = proc->handle;
1142   if (NULL == h)
1143     {
1144       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
1145                   "Invalid process information {%d, %08X}\n", 
1146                   proc->pid, 
1147                   h);
1148       return GNUNET_SYSERR;
1149     }
1150   if (h == NULL)
1151     h = GetCurrentProcess ();
1152
1153   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1154     {
1155       SetErrnoFromWinError (GetLastError ());
1156       ret = GNUNET_SYSERR;
1157     }
1158   else
1159     ret = GNUNET_OK;
1160
1161   return ret;
1162 #endif
1163 }
1164
1165
1166 /* end of os_priority.c */