2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2011 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);
151 TerminateProcess (proc->handle, 0);
153 res = PLIBC_KILL (proc->pid, sig);
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159 "Wrote control code into control pipe, now waiting\n");
160 WaitForSingleObject (proc->handle, 5000);
162 TerminateProcess (proc->handle, 0);
164 PLIBC_KILL (proc->pid, sig);
170 return kill (proc->pid, sig);
175 * Get the pid of the process in question
177 * @param proc the process to get the pid of
179 * @return the current process id
182 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
188 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
190 #if ENABLE_WINDOWS_WORKAROUNDS
191 if (proc->control_pipe)
192 GNUNET_DISK_npipe_close (proc->control_pipe);
196 if (proc->handle != NULL)
197 CloseHandle (proc->handle);
204 #include "gnunet_signal_lib.h"
206 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
209 * Make seaspider happy.
211 #define DWORD_WINAPI DWORD WINAPI
214 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
215 * @param proc pointer to process structure
218 ChildWaitThread (void *arg)
220 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
221 WaitForSingleObject (proc->handle, INFINITE);
223 if (w32_sigchld_handler)
224 w32_sigchld_handler ();
231 * Set process priority
233 * @param proc pointer to process structure
234 * @param prio priority value
235 * @return GNUNET_OK on success, GNUNET_SYSERR on error
238 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
239 enum GNUNET_SCHEDULER_Priority prio)
243 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
244 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
247 /* convert to MINGW/Unix values */
250 case GNUNET_SCHEDULER_PRIORITY_UI:
251 case GNUNET_SCHEDULER_PRIORITY_URGENT:
253 rprio = HIGH_PRIORITY_CLASS;
259 case GNUNET_SCHEDULER_PRIORITY_HIGH:
261 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
267 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
269 rprio = NORMAL_PRIORITY_CLASS;
275 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
277 rprio = BELOW_NORMAL_PRIORITY_CLASS;
283 case GNUNET_SCHEDULER_PRIORITY_IDLE:
285 rprio = IDLE_PRIORITY_CLASS;
292 return GNUNET_SYSERR;
295 /* Set process priority */
298 HANDLE h = proc->handle;
299 GNUNET_assert (h != NULL);
300 SetPriorityClass (h, rprio);
307 (pid == getpid () ) )
310 int delta = rprio - have;
313 (rprio == nice (delta)) &&
316 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
317 GNUNET_ERROR_TYPE_BULK, "nice");
318 return GNUNET_SYSERR;
323 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
325 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
326 GNUNET_ERROR_TYPE_BULK, "setpriority");
327 return GNUNET_SYSERR;
331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
332 "Priority management not availabe for this platform\n");
339 CreateCustomEnvTable (char **vars)
341 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
342 size_t tablesize = 0;
343 size_t items_count = 0;
344 size_t n_found = 0, n_var;
350 win32_env_table = GetEnvironmentStringsA ();
351 if (win32_env_table == NULL)
353 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
355 index = GNUNET_malloc (n_var);
356 for (c = 0; c < n_var; c++)
358 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
360 size_t len = strlen (ptr);
362 for (var_ptr = vars; *var_ptr; var_ptr++)
366 var_len = strlen (var);
367 if (strncmp (var, ptr, var_len) == 0)
371 tablesize += var_len + strlen (val) + 1;
376 tablesize += len + 1;
379 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
384 n_found += strlen (var) + strlen (val) + 1;
386 result = GNUNET_malloc (tablesize + n_found + 1);
387 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
389 size_t len = strlen (ptr);
391 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
395 var_len = strlen (var);
396 if (strncmp (var, ptr, var_len) == 0)
404 strcpy (result_ptr, ptr);
405 result_ptr += len + 1;
409 strcpy (result_ptr, var);
410 result_ptr += var_len;
411 strcpy (result_ptr, val);
412 result_ptr += strlen (val) + 1;
416 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
420 var_len = strlen (var);
423 strcpy (result_ptr, var);
424 result_ptr += var_len;
425 strcpy (result_ptr, val);
426 result_ptr += strlen (val) + 1;
429 FreeEnvironmentStrings (win32_env_table);
440 * @param pipe_stdin pipe to use to send input to child process (or NULL)
441 * @param pipe_stdout pipe to use to get output from child process (or NULL)
442 * @param filename name of the binary
443 * @param va NULL-terminated list of arguments to the process
444 * @return pointer to process structure of the new process, NULL on error
446 struct GNUNET_OS_Process *
447 GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin,
448 struct GNUNET_DISK_PipeHandle *pipe_stdout,
449 const char *filename,
453 #if ENABLE_WINDOWS_WORKAROUNDS
454 char *childpipename = NULL;
455 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
457 struct GNUNET_OS_Process *gnunet_proc = NULL;
468 #if ENABLE_WINDOWS_WORKAROUNDS
469 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
470 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
471 GNUNET_DISK_PERM_USER_WRITE);
472 if (control_pipe == NULL)
478 while (NULL != va_arg (ap, char *))
481 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
484 while (NULL != (argv[argc] = va_arg (ap, char *)))
487 if (pipe_stdout != NULL)
489 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
490 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
492 if (pipe_stdin != NULL)
494 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
495 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
498 #if HAVE_WORKING_VFORK
507 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
508 #if ENABLE_WINDOWS_WORKAROUNDS
509 GNUNET_DISK_npipe_close (control_pipe);
515 #if HAVE_WORKING_VFORK
516 /* let's hope vfork actually works; for some extreme cases (including
517 a testcase) we need 'execvp' to have run before we return, since
518 we may send a signal to the process next and we don't want it
519 to be caught by OUR signal handler (but either by the default
520 handler or the actual handler as installed by the process itself). */
522 /* let's give the child process a chance to run execvp, 1s should
523 be plenty in practice */
524 if (pipe_stdout != NULL)
525 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
526 if (pipe_stdin != NULL)
527 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
530 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
531 gnunet_proc->pid = ret;
532 #if ENABLE_WINDOWS_WORKAROUNDS
533 gnunet_proc->control_pipe = control_pipe;
537 #if ENABLE_WINDOWS_WORKAROUNDS
538 GNUNET_free (childpipename);
543 #if ENABLE_WINDOWS_WORKAROUNDS
544 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
545 GNUNET_free (childpipename);
548 if (pipe_stdout != NULL)
550 GNUNET_break (0 == close (fd_stdout_read));
551 if (-1 == dup2(fd_stdout_write, 1))
552 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
553 GNUNET_break (0 == close (fd_stdout_write));
556 if (pipe_stdin != NULL)
559 GNUNET_break (0 == close (fd_stdin_write));
560 if (-1 == dup2(fd_stdin_read, 0))
561 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
562 GNUNET_break (0 == close (fd_stdin_read));
564 execvp (filename, argv);
565 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
572 PROCESS_INFORMATION proc;
575 HANDLE stdout_handle;
577 char path[MAX_PATH + 1];
579 char *our_env[3] = { NULL, NULL, NULL };
580 char *env_block = NULL;
582 DWORD pathbuf_len, alloc_len;
587 char *non_const_filename;
589 /* Search in prefix dir (hopefully - the directory from which
590 * the current module was loaded), bindir and libdir, then in PATH
592 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
593 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
594 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
596 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
598 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
600 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
603 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
604 GNUNET_free (self_prefix);
605 GNUNET_free (bindir);
606 GNUNET_free (libdir);
608 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
609 GNUNET_assert (alloc_len == (pathbuf_len - 1));
611 cmdlen = strlen (filename);
612 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
613 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
615 GNUNET_asprintf (&non_const_filename, "%s", filename);
617 /* Check that this is the full path. If it isn't, search. */
618 if (non_const_filename[1] == ':')
619 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
620 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
622 SetErrnoFromWinError (GetLastError ());
623 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
624 GNUNET_free (non_const_filename);
625 GNUNET_free (pathbuf);
628 GNUNET_free (pathbuf);
629 GNUNET_free (non_const_filename);
633 while (NULL != (arg = va_arg (ap, char *)))
636 cmdlen = cmdlen + strlen (path) + 3;
638 cmdlen = cmdlen + strlen (arg) + 3;
642 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
644 while (NULL != (arg = va_arg (ap, char *)))
647 idx += sprintf (idx, "\"%s\" ", path);
649 idx += sprintf (idx, "\"%s\" ", arg);
653 memset (&start, 0, sizeof (start));
654 start.cb = sizeof (start);
656 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
657 start.dwFlags |= STARTF_USESTDHANDLES;
659 if (pipe_stdin != NULL)
661 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
662 start.hStdInput = stdin_handle;
665 if (pipe_stdout != NULL)
667 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
668 start.hStdOutput = stdout_handle;
671 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
672 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
673 GNUNET_DISK_PERM_USER_WRITE);
674 if (control_pipe == NULL)
681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682 "Opened the parent end of the pipe `%s'\n",
685 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
686 GNUNET_asprintf (&our_env[1], "%s", childpipename);
688 env_block = CreateCustomEnvTable (our_env);
689 GNUNET_free (our_env[0]);
690 GNUNET_free (our_env[1]);
693 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
694 env_block, NULL, &start, &proc))
696 SetErrnoFromWinError (GetLastError ());
697 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
698 GNUNET_free (env_block);
703 GNUNET_free (env_block);
705 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
706 gnunet_proc->pid = proc.dwProcessId;
707 gnunet_proc->handle = proc.hProcess;
708 gnunet_proc->control_pipe = control_pipe;
710 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
712 ResumeThread (proc.hThread);
713 CloseHandle (proc.hThread);
725 * @param pipe_stdin pipe to use to send input to child process (or NULL)
726 * @param pipe_stdout pipe to use to get output from child process (or NULL)
727 * @param filename name of the binary
728 * @param ... NULL-terminated list of arguments to the process
730 * @return pointer to process structure of the new process, NULL on error
733 struct GNUNET_OS_Process *
734 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
735 struct GNUNET_DISK_PipeHandle *pipe_stdout,
736 const char *filename, ...)
738 struct GNUNET_OS_Process *ret;
741 va_start (ap, filename);
742 ret = GNUNET_OS_start_process_va (pipe_stdin,
754 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
755 * must be NULL on platforms where dup is not supported
756 * @param filename name of the binary
757 * @param argv NULL-terminated list of arguments to the process
758 * @return process ID of the new process, -1 on error
760 struct GNUNET_OS_Process *
761 GNUNET_OS_start_process_v (const int *lsocks,
762 const char *filename, char *const argv[])
764 #if ENABLE_WINDOWS_WORKAROUNDS
765 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
766 char *childpipename = NULL;
773 struct GNUNET_OS_Process *gnunet_proc = NULL;
782 #if ENABLE_WINDOWS_WORKAROUNDS
783 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
784 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
785 GNUNET_DISK_PERM_USER_WRITE);
786 if (control_pipe == NULL)
795 while (-1 != (k = lsocks[i++]))
796 GNUNET_array_append (lscp, ls, k);
797 GNUNET_array_append (lscp, ls, -1);
799 #if HAVE_WORKING_VFORK
808 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
809 #if ENABLE_WINDOWS_WORKAROUNDS
810 GNUNET_DISK_npipe_close (control_pipe);
815 #if HAVE_WORKING_VFORK
816 /* let's hope vfork actually works; for some extreme cases (including
817 a testcase) we need 'execvp' to have run before we return, since
818 we may send a signal to the process next and we don't want it
819 to be caught by OUR signal handler (but either by the default
820 handler or the actual handler as installed by the process itself). */
822 /* let's give the child process a chance to run execvp, 1s should
823 be plenty in practice */
826 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
827 gnunet_proc->pid = ret;
828 #if ENABLE_WINDOWS_WORKAROUNDS
829 gnunet_proc->control_pipe = control_pipe;
833 GNUNET_array_grow (lscp, ls, 0);
834 #if ENABLE_WINDOWS_WORKAROUNDS
835 GNUNET_free (childpipename);
840 #if ENABLE_WINDOWS_WORKAROUNDS
841 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
842 GNUNET_free (childpipename);
847 /* read systemd documentation... */
848 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
849 setenv ("LISTEN_PID", lpid, 1);
852 while (-1 != lscp[i])
855 while (-1 != lscp[j])
861 GNUNET_assert (-1 != k);
862 GNUNET_assert (0 == close (lscp[j]));
870 /* Bury any existing FD, no matter what; they should all be closed
871 on exec anyway and the important onces have been dup'ed away */
873 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
875 /* unset close-on-exec flag */
876 flags = fcntl (tgt, F_GETFD);
877 GNUNET_assert (flags >= 0);
878 flags &= ~FD_CLOEXEC;
880 (void) fcntl (tgt, F_SETFD, flags);
884 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
885 setenv ("LISTEN_FDS", fds, 1);
887 GNUNET_array_grow (lscp, ls, 0);
888 execvp (filename, argv);
889 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
892 char **arg, **non_const_argv;
896 PROCESS_INFORMATION proc;
898 struct GNUNET_OS_Process *gnunet_proc = NULL;
900 char path[MAX_PATH + 1];
902 char *our_env[3] = { NULL, NULL, NULL };
903 char *env_block = NULL;
905 DWORD pathbuf_len, alloc_len;
910 char *non_const_filename;
912 GNUNET_assert (lsocks == NULL);
914 /* Search in prefix dir (hopefully - the directory from which
915 * the current module was loaded), bindir and libdir, then in PATH
917 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
918 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
919 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
921 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
923 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
925 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
928 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
929 GNUNET_free (self_prefix);
930 GNUNET_free (bindir);
931 GNUNET_free (libdir);
933 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
934 if (alloc_len != pathbuf_len - 1)
936 GNUNET_free (pathbuf);
937 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
941 cmdlen = strlen (filename);
942 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
943 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
945 GNUNET_asprintf (&non_const_filename, "%s", filename);
947 /* Check that this is the full path. If it isn't, search. */
948 if (non_const_filename[1] == ':')
949 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
950 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
952 SetErrnoFromWinError (GetLastError ());
953 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
954 GNUNET_free (non_const_filename);
955 GNUNET_free (pathbuf);
958 GNUNET_free (pathbuf);
959 GNUNET_free (non_const_filename);
961 /* Count the number of arguments */
962 arg = (char **) argv;
969 /* Allocate a copy argv */
970 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
972 /* Copy all argv strings */
974 arg = (char **) argv;
978 non_const_argv[argcount] = GNUNET_strdup (path);
980 non_const_argv[argcount] = GNUNET_strdup (*arg);
984 non_const_argv[argcount] = NULL;
988 arg = non_const_argv;
991 cmdlen = cmdlen + strlen (*arg) + 3;
995 /* Allocate and create cmd */
996 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
997 arg = non_const_argv;
1000 idx += sprintf (idx, "\"%s\" ", *arg);
1004 while (argcount > 0)
1005 GNUNET_free (non_const_argv[--argcount]);
1006 GNUNET_free (non_const_argv);
1008 memset (&start, 0, sizeof (start));
1009 start.cb = sizeof (start);
1011 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1012 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1013 GNUNET_DISK_PERM_USER_WRITE);
1014 if (control_pipe == NULL)
1021 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1023 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1024 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1026 env_block = CreateCustomEnvTable (our_env);
1027 GNUNET_free (our_env[0]);
1028 GNUNET_free (our_env[1]);
1031 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1032 env_block, NULL, &start, &proc))
1034 SetErrnoFromWinError (GetLastError ());
1035 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1036 GNUNET_free (env_block);
1041 GNUNET_free (env_block);
1043 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1044 gnunet_proc->pid = proc.dwProcessId;
1045 gnunet_proc->handle = proc.hProcess;
1046 gnunet_proc->control_pipe = control_pipe;
1048 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1050 ResumeThread (proc.hThread);
1051 CloseHandle (proc.hThread);
1060 * Retrieve the status of a process
1061 * @param proc process ID
1062 * @param type status type
1063 * @param code return code/signal number
1064 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1067 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1068 enum GNUNET_OS_ProcessStatusType *type,
1069 unsigned long *code)
1075 GNUNET_assert (0 != proc);
1076 ret = waitpid (proc->pid, &status, WNOHANG);
1079 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1080 return GNUNET_SYSERR;
1084 *type = GNUNET_OS_PROCESS_RUNNING;
1088 if (proc->pid != ret)
1090 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1091 return GNUNET_SYSERR;
1093 if (WIFEXITED (status))
1095 *type = GNUNET_OS_PROCESS_EXITED;
1096 *code = WEXITSTATUS (status);
1098 else if (WIFSIGNALED (status))
1100 *type = GNUNET_OS_PROCESS_SIGNALED;
1101 *code = WTERMSIG (status);
1103 else if (WIFSTOPPED (status))
1105 *type = GNUNET_OS_PROCESS_SIGNALED;
1106 *code = WSTOPSIG (status);
1109 else if (WIFCONTINUED (status))
1111 *type = GNUNET_OS_PROCESS_RUNNING;
1117 *type = GNUNET_OS_PROCESS_UNKNOWN;
1122 DWORD c, error_code, ret;
1126 if (h == NULL || ret == 0)
1128 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1129 return GNUNET_SYSERR;
1132 h = GetCurrentProcess ();
1135 ret = GetExitCodeProcess (h, &c);
1136 error_code = GetLastError ();
1137 if (ret == 0 || error_code != NO_ERROR)
1139 SetErrnoFromWinError (error_code);
1140 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1141 return GNUNET_SYSERR;
1143 if (STILL_ACTIVE == c)
1145 *type = GNUNET_OS_PROCESS_RUNNING;
1149 *type = GNUNET_OS_PROCESS_EXITED;
1157 * Wait for a process
1158 * @param proc pointer to process structure
1159 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1162 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1166 pid_t pid = proc->pid;
1167 if (pid != waitpid (pid, NULL, 0))
1168 return GNUNET_SYSERR;
1177 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1178 "Invalid process information {%d, %08X}\n",
1181 return GNUNET_SYSERR;
1184 h = GetCurrentProcess ();
1186 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1188 SetErrnoFromWinError (GetLastError ());
1189 ret = GNUNET_SYSERR;
1200 * Handle to a command.
1202 struct GNUNET_OS_CommandHandle
1208 struct GNUNET_OS_Process *eip;
1211 * Handle to the output pipe.
1213 struct GNUNET_DISK_PipeHandle *opipe;
1216 * Read-end of output pipe.
1218 const struct GNUNET_DISK_FileHandle *r;
1221 * Function to call on each line of output.
1223 GNUNET_OS_LineProcessor proc;
1226 * Closure for 'proc'.
1231 * Buffer for the output.
1236 * Task reading from pipe.
1238 GNUNET_SCHEDULER_TaskIdentifier rtask;
1243 struct GNUNET_TIME_Absolute timeout;
1246 * Current read offset in buf.
1253 * Stop/kill a command. Must ONLY be called either from
1254 * the callback after 'NULL' was passed for 'line' *OR*
1255 * from an independent task (not within the line processor).
1257 * @param cmd handle to the process
1260 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1263 if (cmd->proc != NULL)
1265 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1266 GNUNET_SCHEDULER_cancel (cmd->rtask);
1268 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1269 GNUNET_break (GNUNET_OK ==
1270 GNUNET_OS_process_wait (cmd->eip));
1271 GNUNET_OS_process_close (cmd->eip);
1272 GNUNET_DISK_pipe_close (cmd->opipe);
1278 * Read from the process and call the line processor.
1280 * @param cls the 'struct GNUNET_OS_CommandHandle'
1281 * @param tc scheduler context
1284 cmd_read (void *cls,
1285 const struct GNUNET_SCHEDULER_TaskContext *tc)
1287 struct GNUNET_OS_CommandHandle *cmd = cls;
1288 GNUNET_OS_LineProcessor proc;
1292 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1294 GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1297 /* timeout, shutdown, etc. */
1300 proc (cmd->proc_cls, NULL);
1303 ret = GNUNET_DISK_file_read (cmd->r,
1304 &cmd->buf[cmd->off],
1305 sizeof (cmd->buf)-cmd->off);
1308 if ( (cmd->off > 0) && (cmd->off < sizeof (cmd->buf)) )
1310 cmd->buf[cmd->off] = '\0';
1311 cmd->proc (cmd->proc_cls, cmd->buf);
1315 proc (cmd->proc_cls, NULL);
1318 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1323 cmd->proc (cmd->proc_cls, cmd->buf);
1326 cmd->off - (end + 1 - cmd->buf));
1327 cmd->off -= (end + 1 - cmd->buf);
1328 end = memchr (cmd->buf, '\n', cmd->off);
1330 cmd->rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (cmd->timeout),
1338 * Run the given command line and call the given function
1339 * for each line of the output.
1341 * @param proc function to call for each line of the output
1342 * @param proc_cls closure for proc
1343 * @param timeout when to time out
1344 * @param binary command to run
1345 * @param ... arguments to command
1346 * @return NULL on error
1348 struct GNUNET_OS_CommandHandle *
1349 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1351 struct GNUNET_TIME_Relative timeout,
1355 struct GNUNET_OS_CommandHandle *cmd;
1356 struct GNUNET_OS_Process *eip;
1357 struct GNUNET_DISK_PipeHandle *opipe;
1360 opipe = GNUNET_DISK_pipe (GNUNET_YES,
1365 va_start (ap, binary);
1366 eip = GNUNET_OS_start_process_va (NULL, opipe,
1371 GNUNET_DISK_pipe_close (opipe);
1374 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1375 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1376 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1380 cmd->proc_cls = proc_cls;
1381 cmd->r = GNUNET_DISK_pipe_handle (opipe,
1382 GNUNET_DISK_PIPE_END_READ);
1383 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1393 /* end of os_priority.c */