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);
323 if (0 != setpriority (PRIO_PROCESS, proc->pid, rprio))
325 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
326 GNUNET_ERROR_TYPE_BULK, "setpriority");
327 return GNUNET_SYSERR;
330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
331 "Priority management not availabe for this platform\n");
338 CreateCustomEnvTable (char **vars)
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;
349 win32_env_table = GetEnvironmentStringsA ();
350 if (win32_env_table == NULL)
352 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
354 index = GNUNET_malloc (n_var);
355 for (c = 0; c < n_var; c++)
357 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
359 size_t len = strlen (ptr);
361 for (var_ptr = vars; *var_ptr; var_ptr++)
365 var_len = strlen (var);
366 if (strncmp (var, ptr, var_len) == 0)
370 tablesize += var_len + strlen (val) + 1;
375 tablesize += len + 1;
378 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
383 n_found += strlen (var) + strlen (val) + 1;
385 result = GNUNET_malloc (tablesize + n_found + 1);
386 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
388 size_t len = strlen (ptr);
390 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
394 var_len = strlen (var);
395 if (strncmp (var, ptr, var_len) == 0)
403 strcpy (result_ptr, ptr);
404 result_ptr += len + 1;
408 strcpy (result_ptr, var);
409 result_ptr += var_len;
410 strcpy (result_ptr, val);
411 result_ptr += strlen (val) + 1;
415 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
419 var_len = strlen (var);
422 strcpy (result_ptr, var);
423 result_ptr += var_len;
424 strcpy (result_ptr, val);
425 result_ptr += strlen (val) + 1;
428 FreeEnvironmentStrings (win32_env_table);
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
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, ...)
450 #if ENABLE_WINDOWS_WORKAROUNDS
451 char *childpipename = NULL;
452 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
454 struct GNUNET_OS_Process *gnunet_proc = NULL;
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)
474 va_start (ap, filename);
475 while (NULL != va_arg (ap, char *))
478 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
480 va_start (ap, filename);
481 while (NULL != (argv[argc] = va_arg (ap, char *)))
484 if (pipe_stdout != NULL)
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));
489 if (pipe_stdin != NULL)
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));
495 #if HAVE_WORKING_VFORK
504 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
505 #if ENABLE_WINDOWS_WORKAROUNDS
506 GNUNET_DISK_npipe_close (control_pipe);
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). */
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);
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;
534 #if ENABLE_WINDOWS_WORKAROUNDS
535 GNUNET_free (childpipename);
540 #if ENABLE_WINDOWS_WORKAROUNDS
541 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
542 GNUNET_free (childpipename);
545 if (pipe_stdout != NULL)
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));
553 if (pipe_stdin != NULL)
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));
561 execvp (filename, argv);
562 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
569 PROCESS_INFORMATION proc;
572 HANDLE stdout_handle;
574 char path[MAX_PATH + 1];
576 char *our_env[3] = { NULL, NULL, NULL };
577 char *env_block = NULL;
579 DWORD pathbuf_len, alloc_len;
584 char *non_const_filename;
586 /* Search in prefix dir (hopefully - the directory from which
587 * the current module was loaded), bindir and libdir, then in PATH
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);
593 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
595 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
597 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
600 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
601 GNUNET_free (self_prefix);
602 GNUNET_free (bindir);
603 GNUNET_free (libdir);
605 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
606 GNUNET_assert (alloc_len == (pathbuf_len - 1));
608 cmdlen = strlen (filename);
609 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
610 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
612 GNUNET_asprintf (&non_const_filename, "%s", filename);
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))
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);
625 GNUNET_free (pathbuf);
626 GNUNET_free (non_const_filename);
629 va_start (ap, filename);
630 while (NULL != (arg = va_arg (ap, char *)))
633 cmdlen = cmdlen + strlen (path) + 3;
635 cmdlen = cmdlen + strlen (arg) + 3;
639 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
640 va_start (ap, filename);
641 while (NULL != (arg = va_arg (ap, char *)))
644 idx += sprintf (idx, "\"%s\" ", path);
646 idx += sprintf (idx, "\"%s\" ", arg);
650 memset (&start, 0, sizeof (start));
651 start.cb = sizeof (start);
653 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
654 start.dwFlags |= STARTF_USESTDHANDLES;
656 if (pipe_stdin != NULL)
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;
662 if (pipe_stdout != NULL)
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;
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)
678 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
680 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
681 GNUNET_asprintf (&our_env[1], "%s", childpipename);
683 env_block = CreateCustomEnvTable (our_env);
684 GNUNET_free (our_env[0]);
685 GNUNET_free (our_env[1]);
688 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
689 env_block, NULL, &start, &proc))
691 SetErrnoFromWinError (GetLastError ());
692 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
693 GNUNET_free (env_block);
698 GNUNET_free (env_block);
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;
705 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
707 ResumeThread (proc.hThread);
708 CloseHandle (proc.hThread);
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
728 struct GNUNET_OS_Process *
729 GNUNET_OS_start_process_v (const int *lsocks,
730 const char *filename, char *const argv[])
732 #if ENABLE_WINDOWS_WORKAROUNDS
733 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
734 char *childpipename = NULL;
741 struct GNUNET_OS_Process *gnunet_proc = NULL;
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)
763 while (-1 != (k = lsocks[i++]))
764 GNUNET_array_append (lscp, ls, k);
765 GNUNET_array_append (lscp, ls, -1);
767 #if HAVE_WORKING_VFORK
776 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
777 #if ENABLE_WINDOWS_WORKAROUNDS
778 GNUNET_DISK_npipe_close (control_pipe);
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). */
790 /* let's give the child process a chance to run execvp, 1s should
791 be plenty in practice */
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;
801 GNUNET_array_grow (lscp, ls, 0);
802 #if ENABLE_WINDOWS_WORKAROUNDS
803 GNUNET_free (childpipename);
808 #if ENABLE_WINDOWS_WORKAROUNDS
809 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
810 GNUNET_free (childpipename);
815 /* read systemd documentation... */
816 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
817 setenv ("LISTEN_PID", lpid, 1);
820 while (-1 != lscp[i])
823 while (-1 != lscp[j])
829 GNUNET_assert (-1 != k);
830 GNUNET_assert (0 == close (lscp[j]));
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 */
841 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
843 /* unset close-on-exec flag */
844 flags = fcntl (tgt, F_GETFD);
845 GNUNET_assert (flags >= 0);
846 flags &= ~FD_CLOEXEC;
848 (void) fcntl (tgt, F_SETFD, flags);
852 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
853 setenv ("LISTEN_FDS", fds, 1);
855 GNUNET_array_grow (lscp, ls, 0);
856 execvp (filename, argv);
857 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
860 char **arg, **non_const_argv;
864 PROCESS_INFORMATION proc;
866 struct GNUNET_OS_Process *gnunet_proc = NULL;
868 char path[MAX_PATH + 1];
870 char *our_env[3] = { NULL, NULL, NULL };
871 char *env_block = NULL;
873 DWORD pathbuf_len, alloc_len;
878 char *non_const_filename;
880 GNUNET_assert (lsocks == NULL);
882 /* Search in prefix dir (hopefully - the directory from which
883 * the current module was loaded), bindir and libdir, then in PATH
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);
889 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
891 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
893 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
896 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
897 GNUNET_free (self_prefix);
898 GNUNET_free (bindir);
899 GNUNET_free (libdir);
901 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
902 if (alloc_len != pathbuf_len - 1)
904 GNUNET_free (pathbuf);
905 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
909 cmdlen = strlen (filename);
910 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
911 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
913 GNUNET_asprintf (&non_const_filename, "%s", filename);
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))
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);
926 GNUNET_free (pathbuf);
927 GNUNET_free (non_const_filename);
929 /* Count the number of arguments */
930 arg = (char **) argv;
937 /* Allocate a copy argv */
938 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
940 /* Copy all argv strings */
942 arg = (char **) argv;
946 non_const_argv[argcount] = GNUNET_strdup (path);
948 non_const_argv[argcount] = GNUNET_strdup (*arg);
952 non_const_argv[argcount] = NULL;
956 arg = non_const_argv;
959 cmdlen = cmdlen + strlen (*arg) + 3;
963 /* Allocate and create cmd */
964 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
965 arg = non_const_argv;
968 idx += sprintf (idx, "\"%s\" ", *arg);
973 GNUNET_free (non_const_argv[--argcount]);
974 GNUNET_free (non_const_argv);
976 memset (&start, 0, sizeof (start));
977 start.cb = sizeof (start);
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)
989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
991 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
992 GNUNET_asprintf (&our_env[1], "%s", childpipename);
994 env_block = CreateCustomEnvTable (our_env);
995 GNUNET_free (our_env[0]);
996 GNUNET_free (our_env[1]);
999 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1000 env_block, NULL, &start, &proc))
1002 SetErrnoFromWinError (GetLastError ());
1003 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1004 GNUNET_free (env_block);
1009 GNUNET_free (env_block);
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;
1016 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1018 ResumeThread (proc.hThread);
1019 CloseHandle (proc.hThread);
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
1034 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1035 enum GNUNET_OS_ProcessStatusType *type,
1036 unsigned long *code)
1042 GNUNET_assert (0 != proc);
1043 ret = waitpid (proc->pid, &status, WNOHANG);
1046 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1047 return GNUNET_SYSERR;
1051 *type = GNUNET_OS_PROCESS_RUNNING;
1055 if (proc->pid != ret)
1057 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1058 return GNUNET_SYSERR;
1060 if (WIFEXITED (status))
1062 *type = GNUNET_OS_PROCESS_EXITED;
1063 *code = WEXITSTATUS (status);
1065 else if (WIFSIGNALED (status))
1067 *type = GNUNET_OS_PROCESS_SIGNALED;
1068 *code = WTERMSIG (status);
1070 else if (WIFSTOPPED (status))
1072 *type = GNUNET_OS_PROCESS_SIGNALED;
1073 *code = WSTOPSIG (status);
1076 else if (WIFCONTINUED (status))
1078 *type = GNUNET_OS_PROCESS_RUNNING;
1084 *type = GNUNET_OS_PROCESS_UNKNOWN;
1089 DWORD c, error_code, ret;
1093 if (h == NULL || ret == 0)
1095 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1096 return GNUNET_SYSERR;
1099 h = GetCurrentProcess ();
1102 ret = GetExitCodeProcess (h, &c);
1103 error_code = GetLastError ();
1104 if (ret == 0 || error_code != NO_ERROR)
1106 SetErrnoFromWinError (error_code);
1107 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1108 return GNUNET_SYSERR;
1110 if (STILL_ACTIVE == c)
1112 *type = GNUNET_OS_PROCESS_RUNNING;
1116 *type = GNUNET_OS_PROCESS_EXITED;
1124 * Wait for a process
1125 * @param proc pointer to process structure
1126 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1129 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1133 pid_t pid = proc->pid;
1134 if (pid != waitpid (pid, NULL, 0))
1135 return GNUNET_SYSERR;
1144 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1145 "Invalid process information {%d, %08X}\n",
1148 return GNUNET_SYSERR;
1151 h = GetCurrentProcess ();
1153 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1155 SetErrnoFromWinError (GetLastError ());
1156 ret = GNUNET_SYSERR;
1166 /* end of os_priority.c */