2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file util/os_priority.c
23 * @brief Methods to set process priority
28 #include "gnunet_common.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_scheduler_lib.h"
33 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
35 struct GNUNET_OS_Process
42 struct GNUNET_DISK_FileHandle *control_pipe;
45 static struct GNUNET_OS_Process current_process;
49 * This handler is called when there are control data to be read on the pipe
52 GNUNET_OS_parent_control_handler (void *cls,
54 GNUNET_SCHEDULER_TaskContext * tc)
56 struct GNUNET_DISK_FileHandle *control_pipe = (struct GNUNET_DISK_FileHandle *) cls;
59 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__, tc->reason);
61 if (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE))
63 GNUNET_DISK_npipe_close (control_pipe);
67 if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) != sizeof (sig))
69 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
70 GNUNET_DISK_npipe_close (control_pipe);
74 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", 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);
83 * Connects this process to its parent via pipe
86 GNUNET_OS_install_parent_control_handler (void *cls,
88 GNUNET_SCHEDULER_TaskContext * tc)
91 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
93 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
94 if (env_buf == NULL || strlen (env_buf) <= 0)
96 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not installing a handler because %s=%s\n", GNUNET_OS_CONTROL_PIPE, env_buf);
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)
104 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to open the pipe `%s'\n", env_buf);
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);
113 * Get process structure for current process
115 * The pointer it returns points to static memory location and must not be
118 * @return pointer to the process sturcutre for this process
120 struct GNUNET_OS_Process *
121 GNUNET_OS_process_current ()
124 current_process.pid = GetCurrentProcessId ();
125 current_process.handle = GetCurrentProcess ();
127 current_process.pid = 0;
129 return ¤t_process;
133 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
135 #if ENABLE_WINDOWS_WORKAROUNDS
139 ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof(sig));
140 if (ret != sizeof(sig))
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");
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);
153 struct GNUNET_NETWORK_FDSet *rfds;
154 struct GNUNET_NETWORK_FDSet *efds;
156 rfds = GNUNET_NETWORK_fdset_create ();
157 efds = GNUNET_NETWORK_fdset_create ();
159 GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe);
160 GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe);
163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164 "Wrote control code into control pipe, now waiting\n");
166 ret = GNUNET_NETWORK_socket_select (rfds, NULL, efds,
167 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_unit (),
170 if (ret < 1 || GNUNET_NETWORK_fdset_handle_isset (efds,
173 /* Just to be sure */
174 PLIBC_KILL (proc->pid, sig);
179 if (GNUNET_DISK_file_read (proc->control_pipe, &ret,
180 sizeof(ret)) != GNUNET_OK)
181 res = PLIBC_KILL (proc->pid, sig);
183 /* Child signaled shutdown is in progress */
190 return kill (proc->pid, sig);
195 * Get the pid of the process in question
197 * @param proc the process to get the pid of
199 * @return the current process id
202 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
208 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
210 #if ENABLE_WINDOWS_WORKAROUNDS
211 if (proc->control_pipe)
212 GNUNET_DISK_npipe_close (proc->control_pipe);
216 if (proc->handle != NULL)
217 CloseHandle (proc->handle);
224 #include "gnunet_signal_lib.h"
226 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
229 * Make seaspider happy.
231 #define DWORD_WINAPI DWORD WINAPI
234 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
235 * @param proc pointer to process structure
238 ChildWaitThread (void *arg)
240 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
241 WaitForSingleObject (proc->handle, INFINITE);
243 if (w32_sigchld_handler)
244 w32_sigchld_handler ();
251 * Set process priority
253 * @param proc pointer to process structure
254 * @param prio priority value
255 * @return GNUNET_OK on success, GNUNET_SYSERR on error
258 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
259 enum GNUNET_SCHEDULER_Priority prio)
263 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
264 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
267 /* convert to MINGW/Unix values */
270 case GNUNET_SCHEDULER_PRIORITY_UI:
271 case GNUNET_SCHEDULER_PRIORITY_URGENT:
273 rprio = HIGH_PRIORITY_CLASS;
279 case GNUNET_SCHEDULER_PRIORITY_HIGH:
281 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
287 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
289 rprio = NORMAL_PRIORITY_CLASS;
295 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
297 rprio = BELOW_NORMAL_PRIORITY_CLASS;
303 case GNUNET_SCHEDULER_PRIORITY_IDLE:
305 rprio = IDLE_PRIORITY_CLASS;
312 return GNUNET_SYSERR;
315 /* Set process priority */
318 HANDLE h = proc->handle;
319 GNUNET_assert (h != NULL);
320 SetPriorityClass (h, rprio);
327 (pid == getpid () ) )
330 int delta = rprio - have;
333 (rprio == nice (delta)) &&
336 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
337 GNUNET_ERROR_TYPE_BULK, "nice");
338 return GNUNET_SYSERR;
343 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
345 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
346 GNUNET_ERROR_TYPE_BULK, "setpriority");
347 return GNUNET_SYSERR;
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
352 "Priority management not availabe for this platform\n");
359 CreateCustomEnvTable (char **vars)
361 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
362 size_t tablesize = 0;
363 size_t items_count = 0;
364 size_t n_found = 0, n_var;
370 win32_env_table = GetEnvironmentStringsA ();
371 if (win32_env_table == NULL)
373 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
375 index = GNUNET_malloc (n_var);
376 for (c = 0; c < n_var; c++)
378 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
380 size_t len = strlen (ptr);
382 for (var_ptr = vars; *var_ptr; var_ptr++)
386 var_len = strlen (var);
387 if (strncmp (var, ptr, var_len) == 0)
391 tablesize += var_len + strlen (val) + 1;
396 tablesize += len + 1;
399 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
404 n_found += strlen (var) + strlen (val) + 1;
406 result = GNUNET_malloc (tablesize + n_found + 1);
407 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
409 size_t len = strlen (ptr);
411 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
415 var_len = strlen (var);
416 if (strncmp (var, ptr, var_len) == 0)
424 strcpy (result_ptr, ptr);
425 result_ptr += len + 1;
429 strcpy (result_ptr, var);
430 result_ptr += var_len;
431 strcpy (result_ptr, val);
432 result_ptr += strlen (val) + 1;
436 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
440 var_len = strlen (var);
443 strcpy (result_ptr, var);
444 result_ptr += var_len;
445 strcpy (result_ptr, val);
446 result_ptr += strlen (val) + 1;
449 FreeEnvironmentStrings (win32_env_table);
459 * @param pipe_stdin pipe to use to send input to child process (or NULL)
460 * @param pipe_stdout pipe to use to get output from child process (or NULL)
461 * @param filename name of the binary
462 * @param ... NULL-terminated list of arguments to the process
463 * @return pointer to process structure of the new process, NULL on error
465 struct GNUNET_OS_Process *
466 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
467 struct GNUNET_DISK_PipeHandle *pipe_stdout,
468 const char *filename, ...)
471 #if ENABLE_WINDOWS_WORKAROUNDS
472 char *childpipename = NULL;
473 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
475 struct GNUNET_OS_Process *gnunet_proc = NULL;
486 #if ENABLE_WINDOWS_WORKAROUNDS
487 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
488 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
489 GNUNET_DISK_PERM_USER_WRITE);
490 if (control_pipe == NULL)
495 va_start (ap, filename);
496 while (NULL != va_arg (ap, char *))
499 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
501 va_start (ap, filename);
502 while (NULL != (argv[argc] = va_arg (ap, char *)))
505 if (pipe_stdout != NULL)
507 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
508 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
510 if (pipe_stdin != NULL)
512 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
513 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
516 #if HAVE_WORKING_VFORK
525 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
526 #if ENABLE_WINDOWS_WORKAROUNDS
527 GNUNET_DISK_npipe_close (control_pipe);
533 #if HAVE_WORKING_VFORK
534 /* let's hope vfork actually works; for some extreme cases (including
535 a testcase) we need 'execvp' to have run before we return, since
536 we may send a signal to the process next and we don't want it
537 to be caught by OUR signal handler (but either by the default
538 handler or the actual handler as installed by the process itself). */
540 /* let's give the child process a chance to run execvp, 1s should
541 be plenty in practice */
542 if (pipe_stdout != NULL)
543 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
544 if (pipe_stdin != NULL)
545 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
548 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
549 gnunet_proc->pid = ret;
550 #if ENABLE_WINDOWS_WORKAROUNDS
551 gnunet_proc->control_pipe = control_pipe;
555 #if ENABLE_WINDOWS_WORKAROUNDS
556 GNUNET_free (childpipename);
561 #if ENABLE_WINDOWS_WORKAROUNDS
562 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
563 GNUNET_free (childpipename);
566 if (pipe_stdout != NULL)
568 GNUNET_break (0 == close (fd_stdout_read));
569 if (-1 == dup2(fd_stdout_write, 1))
570 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
571 GNUNET_break (0 == close (fd_stdout_write));
574 if (pipe_stdin != NULL)
577 GNUNET_break (0 == close (fd_stdin_write));
578 if (-1 == dup2(fd_stdin_read, 0))
579 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
580 GNUNET_break (0 == close (fd_stdin_read));
582 execvp (filename, argv);
583 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
590 PROCESS_INFORMATION proc;
593 HANDLE stdout_handle;
595 char path[MAX_PATH + 1];
597 char *our_env[3] = { NULL, NULL, NULL };
598 char *env_block = NULL;
600 DWORD pathbuf_len, alloc_len;
605 char *non_const_filename;
607 /* Search in prefix dir (hopefully - the directory from which
608 * the current module was loaded), bindir and libdir, then in PATH
610 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
611 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
612 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
614 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
616 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
618 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
621 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
622 GNUNET_free (self_prefix);
623 GNUNET_free (bindir);
624 GNUNET_free (libdir);
626 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
627 GNUNET_assert (alloc_len == (pathbuf_len - 1));
629 cmdlen = strlen (filename);
630 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
631 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
633 GNUNET_asprintf (&non_const_filename, "%s", filename);
635 /* Check that this is the full path. If it isn't, search. */
636 if (non_const_filename[1] == ':')
637 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
638 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
640 SetErrnoFromWinError (GetLastError ());
641 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
642 GNUNET_free (non_const_filename);
643 GNUNET_free (pathbuf);
646 GNUNET_free (pathbuf);
647 GNUNET_free (non_const_filename);
650 va_start (ap, filename);
651 while (NULL != (arg = va_arg (ap, char *)))
654 cmdlen = cmdlen + strlen (path) + 3;
656 cmdlen = cmdlen + strlen (arg) + 3;
660 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
661 va_start (ap, filename);
662 while (NULL != (arg = va_arg (ap, char *)))
665 idx += sprintf (idx, "\"%s\" ", path);
667 idx += sprintf (idx, "\"%s\" ", arg);
671 memset (&start, 0, sizeof (start));
672 start.cb = sizeof (start);
674 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
675 start.dwFlags |= STARTF_USESTDHANDLES;
677 if (pipe_stdin != NULL)
679 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
680 start.hStdInput = stdin_handle;
683 if (pipe_stdout != NULL)
685 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
686 start.hStdOutput = stdout_handle;
689 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
690 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
691 GNUNET_DISK_PERM_USER_WRITE);
692 if (control_pipe == NULL)
699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
701 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
702 GNUNET_asprintf (&our_env[1], "%s", childpipename);
704 env_block = CreateCustomEnvTable (our_env);
705 GNUNET_free (our_env[0]);
706 GNUNET_free (our_env[1]);
709 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
710 env_block, NULL, &start, &proc))
712 SetErrnoFromWinError (GetLastError ());
713 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
714 GNUNET_free (env_block);
719 GNUNET_free (env_block);
721 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
722 gnunet_proc->pid = proc.dwProcessId;
723 gnunet_proc->handle = proc.hProcess;
724 gnunet_proc->control_pipe = control_pipe;
726 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
728 ResumeThread (proc.hThread);
729 CloseHandle (proc.hThread);
743 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
744 * must be NULL on platforms where dup is not supported
745 * @param filename name of the binary
746 * @param argv NULL-terminated list of arguments to the process
747 * @return process ID of the new process, -1 on error
749 struct GNUNET_OS_Process *
750 GNUNET_OS_start_process_v (const int *lsocks,
751 const char *filename, char *const argv[])
753 #if ENABLE_WINDOWS_WORKAROUNDS
754 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
755 char *childpipename = NULL;
762 struct GNUNET_OS_Process *gnunet_proc = NULL;
771 #if ENABLE_WINDOWS_WORKAROUNDS
772 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
773 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
774 GNUNET_DISK_PERM_USER_WRITE);
775 if (control_pipe == NULL)
784 while (-1 != (k = lsocks[i++]))
785 GNUNET_array_append (lscp, ls, k);
786 GNUNET_array_append (lscp, ls, -1);
788 #if HAVE_WORKING_VFORK
797 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
798 #if ENABLE_WINDOWS_WORKAROUNDS
799 GNUNET_DISK_npipe_close (control_pipe);
804 #if HAVE_WORKING_VFORK
805 /* let's hope vfork actually works; for some extreme cases (including
806 a testcase) we need 'execvp' to have run before we return, since
807 we may send a signal to the process next and we don't want it
808 to be caught by OUR signal handler (but either by the default
809 handler or the actual handler as installed by the process itself). */
811 /* let's give the child process a chance to run execvp, 1s should
812 be plenty in practice */
815 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
816 gnunet_proc->pid = ret;
817 #if ENABLE_WINDOWS_WORKAROUNDS
818 gnunet_proc->control_pipe = control_pipe;
822 GNUNET_array_grow (lscp, ls, 0);
823 #if ENABLE_WINDOWS_WORKAROUNDS
824 GNUNET_free (childpipename);
829 #if ENABLE_WINDOWS_WORKAROUNDS
830 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
831 GNUNET_free (childpipename);
836 /* read systemd documentation... */
837 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
838 setenv ("LISTEN_PID", lpid, 1);
841 while (-1 != lscp[i])
844 while (-1 != lscp[j])
850 GNUNET_assert (-1 != k);
851 GNUNET_assert (0 == close (lscp[j]));
859 /* Bury any existing FD, no matter what; they should all be closed
860 on exec anyway and the important onces have been dup'ed away */
862 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
864 /* unset close-on-exec flag */
865 flags = fcntl (tgt, F_GETFD);
866 GNUNET_assert (flags >= 0);
867 flags &= ~FD_CLOEXEC;
869 (void) fcntl (tgt, F_SETFD, flags);
873 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
874 setenv ("LISTEN_FDS", fds, 1);
876 GNUNET_array_grow (lscp, ls, 0);
877 execvp (filename, argv);
878 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
881 char **arg, **non_const_argv;
885 PROCESS_INFORMATION proc;
887 struct GNUNET_OS_Process *gnunet_proc = NULL;
889 char path[MAX_PATH + 1];
891 char *our_env[3] = { NULL, NULL, NULL };
892 char *env_block = NULL;
894 DWORD pathbuf_len, alloc_len;
899 char *non_const_filename;
901 GNUNET_assert (lsocks == NULL);
903 /* Search in prefix dir (hopefully - the directory from which
904 * the current module was loaded), bindir and libdir, then in PATH
906 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
907 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
908 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
910 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
912 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
914 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
917 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
918 GNUNET_free (self_prefix);
919 GNUNET_free (bindir);
920 GNUNET_free (libdir);
922 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
923 if (alloc_len != pathbuf_len - 1)
925 GNUNET_free (pathbuf);
926 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
930 cmdlen = strlen (filename);
931 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
932 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
934 GNUNET_asprintf (&non_const_filename, "%s", filename);
936 /* Check that this is the full path. If it isn't, search. */
937 if (non_const_filename[1] == ':')
938 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
939 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
941 SetErrnoFromWinError (GetLastError ());
942 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
943 GNUNET_free (non_const_filename);
944 GNUNET_free (pathbuf);
947 GNUNET_free (pathbuf);
948 GNUNET_free (non_const_filename);
950 /* Count the number of arguments */
951 arg = (char **) argv;
958 /* Allocate a copy argv */
959 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
961 /* Copy all argv strings */
963 arg = (char **) argv;
967 non_const_argv[argcount] = GNUNET_strdup (path);
969 non_const_argv[argcount] = GNUNET_strdup (*arg);
973 non_const_argv[argcount] = NULL;
977 arg = non_const_argv;
980 cmdlen = cmdlen + strlen (*arg) + 3;
984 /* Allocate and create cmd */
985 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
986 arg = non_const_argv;
989 idx += sprintf (idx, "\"%s\" ", *arg);
994 GNUNET_free (non_const_argv[--argcount]);
995 GNUNET_free (non_const_argv);
997 memset (&start, 0, sizeof (start));
998 start.cb = sizeof (start);
1000 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1001 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1002 GNUNET_DISK_PERM_USER_WRITE);
1003 if (control_pipe == NULL)
1010 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1012 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1013 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1015 env_block = CreateCustomEnvTable (our_env);
1016 GNUNET_free (our_env[0]);
1017 GNUNET_free (our_env[1]);
1020 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1021 env_block, NULL, &start, &proc))
1023 SetErrnoFromWinError (GetLastError ());
1024 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1025 GNUNET_free (env_block);
1030 GNUNET_free (env_block);
1032 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1033 gnunet_proc->pid = proc.dwProcessId;
1034 gnunet_proc->handle = proc.hProcess;
1035 gnunet_proc->control_pipe = control_pipe;
1037 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1039 ResumeThread (proc.hThread);
1040 CloseHandle (proc.hThread);
1048 * Retrieve the status of a process
1049 * @param proc process ID
1050 * @param type status type
1051 * @param code return code/signal number
1052 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1055 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1056 enum GNUNET_OS_ProcessStatusType *type,
1057 unsigned long *code)
1063 GNUNET_assert (0 != proc);
1064 ret = waitpid (proc->pid, &status, WNOHANG);
1067 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1068 return GNUNET_SYSERR;
1072 *type = GNUNET_OS_PROCESS_RUNNING;
1076 if (proc->pid != ret)
1078 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1079 return GNUNET_SYSERR;
1081 if (WIFEXITED (status))
1083 *type = GNUNET_OS_PROCESS_EXITED;
1084 *code = WEXITSTATUS (status);
1086 else if (WIFSIGNALED (status))
1088 *type = GNUNET_OS_PROCESS_SIGNALED;
1089 *code = WTERMSIG (status);
1091 else if (WIFSTOPPED (status))
1093 *type = GNUNET_OS_PROCESS_SIGNALED;
1094 *code = WSTOPSIG (status);
1097 else if (WIFCONTINUED (status))
1099 *type = GNUNET_OS_PROCESS_RUNNING;
1105 *type = GNUNET_OS_PROCESS_UNKNOWN;
1110 DWORD c, error_code, ret;
1114 if (h == NULL || ret == 0)
1116 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1117 return GNUNET_SYSERR;
1120 h = GetCurrentProcess ();
1123 ret = GetExitCodeProcess (h, &c);
1124 error_code = GetLastError ();
1125 if (ret == 0 || error_code != NO_ERROR)
1127 SetErrnoFromWinError (error_code);
1128 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1129 return GNUNET_SYSERR;
1131 if (STILL_ACTIVE == c)
1133 *type = GNUNET_OS_PROCESS_RUNNING;
1137 *type = GNUNET_OS_PROCESS_EXITED;
1145 * Wait for a process
1146 * @param proc pointer to process structure
1147 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1150 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1154 pid_t pid = proc->pid;
1155 if (pid != waitpid (pid, NULL, 0))
1156 return GNUNET_SYSERR;
1165 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1166 "Invalid process information {%d, %08X}\n",
1169 return GNUNET_SYSERR;
1172 h = GetCurrentProcess ();
1174 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1176 SetErrnoFromWinError (GetLastError ());
1177 ret = GNUNET_SYSERR;
1187 /* end of os_priority.c */