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);
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);
460 * @param pipe_stdin pipe to use to send input to child process (or NULL)
461 * @param pipe_stdout pipe to use to get output from child process (or NULL)
462 * @param filename name of the binary
463 * @param va NULL-terminated list of arguments to the process
464 * @return pointer to process structure of the new process, NULL on error
466 struct GNUNET_OS_Process *
467 GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin,
468 struct GNUNET_DISK_PipeHandle *pipe_stdout,
469 const char *filename,
473 #if ENABLE_WINDOWS_WORKAROUNDS
474 char *childpipename = NULL;
475 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
477 struct GNUNET_OS_Process *gnunet_proc = NULL;
488 #if ENABLE_WINDOWS_WORKAROUNDS
489 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
490 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
491 GNUNET_DISK_PERM_USER_WRITE);
492 if (control_pipe == NULL)
498 while (NULL != va_arg (ap, char *))
501 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
504 while (NULL != (argv[argc] = va_arg (ap, char *)))
507 if (pipe_stdout != NULL)
509 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
510 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
512 if (pipe_stdin != NULL)
514 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
515 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
518 #if HAVE_WORKING_VFORK
527 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
528 #if ENABLE_WINDOWS_WORKAROUNDS
529 GNUNET_DISK_npipe_close (control_pipe);
535 #if HAVE_WORKING_VFORK
536 /* let's hope vfork actually works; for some extreme cases (including
537 a testcase) we need 'execvp' to have run before we return, since
538 we may send a signal to the process next and we don't want it
539 to be caught by OUR signal handler (but either by the default
540 handler or the actual handler as installed by the process itself). */
542 /* let's give the child process a chance to run execvp, 1s should
543 be plenty in practice */
544 if (pipe_stdout != NULL)
545 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
546 if (pipe_stdin != NULL)
547 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
550 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
551 gnunet_proc->pid = ret;
552 #if ENABLE_WINDOWS_WORKAROUNDS
553 gnunet_proc->control_pipe = control_pipe;
557 #if ENABLE_WINDOWS_WORKAROUNDS
558 GNUNET_free (childpipename);
563 #if ENABLE_WINDOWS_WORKAROUNDS
564 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
565 GNUNET_free (childpipename);
568 if (pipe_stdout != NULL)
570 GNUNET_break (0 == close (fd_stdout_read));
571 if (-1 == dup2(fd_stdout_write, 1))
572 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
573 GNUNET_break (0 == close (fd_stdout_write));
576 if (pipe_stdin != NULL)
579 GNUNET_break (0 == close (fd_stdin_write));
580 if (-1 == dup2(fd_stdin_read, 0))
581 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
582 GNUNET_break (0 == close (fd_stdin_read));
584 execvp (filename, argv);
585 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
592 PROCESS_INFORMATION proc;
595 HANDLE stdout_handle;
597 char path[MAX_PATH + 1];
599 char *our_env[3] = { NULL, NULL, NULL };
600 char *env_block = NULL;
602 DWORD pathbuf_len, alloc_len;
607 char *non_const_filename;
609 /* Search in prefix dir (hopefully - the directory from which
610 * the current module was loaded), bindir and libdir, then in PATH
612 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
613 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
614 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
616 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
618 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
620 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
623 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
624 GNUNET_free (self_prefix);
625 GNUNET_free (bindir);
626 GNUNET_free (libdir);
628 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
629 GNUNET_assert (alloc_len == (pathbuf_len - 1));
631 cmdlen = strlen (filename);
632 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
633 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
635 GNUNET_asprintf (&non_const_filename, "%s", filename);
637 /* Check that this is the full path. If it isn't, search. */
638 if (non_const_filename[1] == ':')
639 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
640 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
642 SetErrnoFromWinError (GetLastError ());
643 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
644 GNUNET_free (non_const_filename);
645 GNUNET_free (pathbuf);
648 GNUNET_free (pathbuf);
649 GNUNET_free (non_const_filename);
653 while (NULL != (arg = va_arg (ap, char *)))
656 cmdlen = cmdlen + strlen (path) + 3;
658 cmdlen = cmdlen + strlen (arg) + 3;
662 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
664 while (NULL != (arg = va_arg (ap, char *)))
667 idx += sprintf (idx, "\"%s\" ", path);
669 idx += sprintf (idx, "\"%s\" ", arg);
673 memset (&start, 0, sizeof (start));
674 start.cb = sizeof (start);
676 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
677 start.dwFlags |= STARTF_USESTDHANDLES;
679 if (pipe_stdin != NULL)
681 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
682 start.hStdInput = stdin_handle;
685 if (pipe_stdout != NULL)
687 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
688 start.hStdOutput = stdout_handle;
691 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
692 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
693 GNUNET_DISK_PERM_USER_WRITE);
694 if (control_pipe == NULL)
701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702 "Opened the parent end of the pipe `%s'\n",
705 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
706 GNUNET_asprintf (&our_env[1], "%s", childpipename);
708 env_block = CreateCustomEnvTable (our_env);
709 GNUNET_free (our_env[0]);
710 GNUNET_free (our_env[1]);
713 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
714 env_block, NULL, &start, &proc))
716 SetErrnoFromWinError (GetLastError ());
717 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
718 GNUNET_free (env_block);
723 GNUNET_free (env_block);
725 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
726 gnunet_proc->pid = proc.dwProcessId;
727 gnunet_proc->handle = proc.hProcess;
728 gnunet_proc->control_pipe = control_pipe;
730 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
732 ResumeThread (proc.hThread);
733 CloseHandle (proc.hThread);
745 * @param pipe_stdin pipe to use to send input to child process (or NULL)
746 * @param pipe_stdout pipe to use to get output from child process (or NULL)
747 * @param filename name of the binary
748 * @param ... NULL-terminated list of arguments to the process
750 * @return pointer to process structure of the new process, NULL on error
753 struct GNUNET_OS_Process *
754 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
755 struct GNUNET_DISK_PipeHandle *pipe_stdout,
756 const char *filename, ...)
758 struct GNUNET_OS_Process *ret;
761 va_start (ap, filename);
762 ret = GNUNET_OS_start_process_va (pipe_stdin,
774 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
775 * must be NULL on platforms where dup is not supported
776 * @param filename name of the binary
777 * @param argv NULL-terminated list of arguments to the process
778 * @return process ID of the new process, -1 on error
780 struct GNUNET_OS_Process *
781 GNUNET_OS_start_process_v (const int *lsocks,
782 const char *filename, char *const argv[])
784 #if ENABLE_WINDOWS_WORKAROUNDS
785 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
786 char *childpipename = NULL;
793 struct GNUNET_OS_Process *gnunet_proc = NULL;
802 #if ENABLE_WINDOWS_WORKAROUNDS
803 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
804 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
805 GNUNET_DISK_PERM_USER_WRITE);
806 if (control_pipe == NULL)
815 while (-1 != (k = lsocks[i++]))
816 GNUNET_array_append (lscp, ls, k);
817 GNUNET_array_append (lscp, ls, -1);
819 #if HAVE_WORKING_VFORK
828 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
829 #if ENABLE_WINDOWS_WORKAROUNDS
830 GNUNET_DISK_npipe_close (control_pipe);
835 #if HAVE_WORKING_VFORK
836 /* let's hope vfork actually works; for some extreme cases (including
837 a testcase) we need 'execvp' to have run before we return, since
838 we may send a signal to the process next and we don't want it
839 to be caught by OUR signal handler (but either by the default
840 handler or the actual handler as installed by the process itself). */
842 /* let's give the child process a chance to run execvp, 1s should
843 be plenty in practice */
846 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
847 gnunet_proc->pid = ret;
848 #if ENABLE_WINDOWS_WORKAROUNDS
849 gnunet_proc->control_pipe = control_pipe;
853 GNUNET_array_grow (lscp, ls, 0);
854 #if ENABLE_WINDOWS_WORKAROUNDS
855 GNUNET_free (childpipename);
860 #if ENABLE_WINDOWS_WORKAROUNDS
861 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
862 GNUNET_free (childpipename);
867 /* read systemd documentation... */
868 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
869 setenv ("LISTEN_PID", lpid, 1);
872 while (-1 != lscp[i])
875 while (-1 != lscp[j])
881 GNUNET_assert (-1 != k);
882 GNUNET_assert (0 == close (lscp[j]));
890 /* Bury any existing FD, no matter what; they should all be closed
891 on exec anyway and the important onces have been dup'ed away */
893 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
895 /* unset close-on-exec flag */
896 flags = fcntl (tgt, F_GETFD);
897 GNUNET_assert (flags >= 0);
898 flags &= ~FD_CLOEXEC;
900 (void) fcntl (tgt, F_SETFD, flags);
904 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
905 setenv ("LISTEN_FDS", fds, 1);
907 GNUNET_array_grow (lscp, ls, 0);
908 execvp (filename, argv);
909 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
912 char **arg, **non_const_argv;
916 PROCESS_INFORMATION proc;
918 struct GNUNET_OS_Process *gnunet_proc = NULL;
920 char path[MAX_PATH + 1];
922 char *our_env[3] = { NULL, NULL, NULL };
923 char *env_block = NULL;
925 DWORD pathbuf_len, alloc_len;
930 char *non_const_filename;
932 GNUNET_assert (lsocks == NULL);
934 /* Search in prefix dir (hopefully - the directory from which
935 * the current module was loaded), bindir and libdir, then in PATH
937 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
938 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
939 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
941 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
943 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
945 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
948 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
949 GNUNET_free (self_prefix);
950 GNUNET_free (bindir);
951 GNUNET_free (libdir);
953 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
954 if (alloc_len != pathbuf_len - 1)
956 GNUNET_free (pathbuf);
957 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
961 cmdlen = strlen (filename);
962 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
963 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
965 GNUNET_asprintf (&non_const_filename, "%s", filename);
967 /* Check that this is the full path. If it isn't, search. */
968 if (non_const_filename[1] == ':')
969 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
970 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
972 SetErrnoFromWinError (GetLastError ());
973 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
974 GNUNET_free (non_const_filename);
975 GNUNET_free (pathbuf);
978 GNUNET_free (pathbuf);
979 GNUNET_free (non_const_filename);
981 /* Count the number of arguments */
982 arg = (char **) argv;
989 /* Allocate a copy argv */
990 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
992 /* Copy all argv strings */
994 arg = (char **) argv;
998 non_const_argv[argcount] = GNUNET_strdup (path);
1000 non_const_argv[argcount] = GNUNET_strdup (*arg);
1004 non_const_argv[argcount] = NULL;
1008 arg = non_const_argv;
1011 cmdlen = cmdlen + strlen (*arg) + 3;
1015 /* Allocate and create cmd */
1016 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1017 arg = non_const_argv;
1020 idx += sprintf (idx, "\"%s\" ", *arg);
1024 while (argcount > 0)
1025 GNUNET_free (non_const_argv[--argcount]);
1026 GNUNET_free (non_const_argv);
1028 memset (&start, 0, sizeof (start));
1029 start.cb = sizeof (start);
1031 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1032 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1033 GNUNET_DISK_PERM_USER_WRITE);
1034 if (control_pipe == NULL)
1041 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1043 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1044 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1046 env_block = CreateCustomEnvTable (our_env);
1047 GNUNET_free (our_env[0]);
1048 GNUNET_free (our_env[1]);
1051 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1052 env_block, NULL, &start, &proc))
1054 SetErrnoFromWinError (GetLastError ());
1055 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1056 GNUNET_free (env_block);
1061 GNUNET_free (env_block);
1063 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1064 gnunet_proc->pid = proc.dwProcessId;
1065 gnunet_proc->handle = proc.hProcess;
1066 gnunet_proc->control_pipe = control_pipe;
1068 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1070 ResumeThread (proc.hThread);
1071 CloseHandle (proc.hThread);
1079 * Retrieve the status of a process
1080 * @param proc process ID
1081 * @param type status type
1082 * @param code return code/signal number
1083 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1086 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1087 enum GNUNET_OS_ProcessStatusType *type,
1088 unsigned long *code)
1094 GNUNET_assert (0 != proc);
1095 ret = waitpid (proc->pid, &status, WNOHANG);
1098 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1099 return GNUNET_SYSERR;
1103 *type = GNUNET_OS_PROCESS_RUNNING;
1107 if (proc->pid != ret)
1109 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1110 return GNUNET_SYSERR;
1112 if (WIFEXITED (status))
1114 *type = GNUNET_OS_PROCESS_EXITED;
1115 *code = WEXITSTATUS (status);
1117 else if (WIFSIGNALED (status))
1119 *type = GNUNET_OS_PROCESS_SIGNALED;
1120 *code = WTERMSIG (status);
1122 else if (WIFSTOPPED (status))
1124 *type = GNUNET_OS_PROCESS_SIGNALED;
1125 *code = WSTOPSIG (status);
1128 else if (WIFCONTINUED (status))
1130 *type = GNUNET_OS_PROCESS_RUNNING;
1136 *type = GNUNET_OS_PROCESS_UNKNOWN;
1141 DWORD c, error_code, ret;
1145 if (h == NULL || ret == 0)
1147 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1148 return GNUNET_SYSERR;
1151 h = GetCurrentProcess ();
1154 ret = GetExitCodeProcess (h, &c);
1155 error_code = GetLastError ();
1156 if (ret == 0 || error_code != NO_ERROR)
1158 SetErrnoFromWinError (error_code);
1159 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1160 return GNUNET_SYSERR;
1162 if (STILL_ACTIVE == c)
1164 *type = GNUNET_OS_PROCESS_RUNNING;
1168 *type = GNUNET_OS_PROCESS_EXITED;
1176 * Wait for a process
1177 * @param proc pointer to process structure
1178 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1181 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1185 pid_t pid = proc->pid;
1186 if (pid != waitpid (pid, NULL, 0))
1187 return GNUNET_SYSERR;
1196 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1197 "Invalid process information {%d, %08X}\n",
1200 return GNUNET_SYSERR;
1203 h = GetCurrentProcess ();
1205 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1207 SetErrnoFromWinError (GetLastError ());
1208 ret = GNUNET_SYSERR;
1219 * Handle to a command.
1221 struct GNUNET_OS_CommandHandle
1227 struct GNUNET_OS_Process *eip;
1230 * Handle to the output pipe.
1232 struct GNUNET_DISK_PipeHandle *opipe;
1235 * Read-end of output pipe.
1237 const struct GNUNET_DISK_FileHandle *r;
1240 * Function to call on each line of output.
1242 GNUNET_OS_LineProcessor proc;
1245 * Closure for 'proc'.
1250 * Buffer for the output.
1255 * Task reading from pipe.
1257 GNUNET_SCHEDULER_TaskIdentifier rtask;
1262 struct GNUNET_TIME_Absolute timeout;
1265 * Current read offset in buf.
1272 * Stop/kill a command. Must ONLY be called either from
1273 * the callback after 'NULL' was passed for 'line' *OR*
1274 * from an independent task (not within the line processor).
1276 * @param cmd handle to the process
1277 * @param type status type
1278 * @param code return code/signal number
1279 * @return GNUNET_OK on success, GNUNET_NO if we killed the process
1282 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd,
1283 enum GNUNET_OS_ProcessStatusType *type,
1284 unsigned long *code)
1288 if (cmd->proc != NULL)
1290 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1291 GNUNET_SCHEDULER_cancel (cmd->rtask);
1293 killed = GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1294 GNUNET_break (GNUNET_OK ==
1295 GNUNET_OS_process_status (cmd->eip,
1297 GNUNET_OS_process_close (cmd->eip);
1298 GNUNET_DISK_pipe_close (cmd->opipe);
1300 if (GNUNET_OK == killed)
1307 * Read from the process and call the line processor.
1309 * @param cls the 'struct GNUNET_OS_CommandHandle'
1310 * @param tc scheduler context
1313 cmd_read (void *cls,
1314 const struct GNUNET_SCHEDULER_TaskContext *tc)
1316 struct GNUNET_OS_CommandHandle *cmd = cls;
1317 GNUNET_OS_LineProcessor proc;
1321 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1323 GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1326 /* timeout, shutdown, etc. */
1329 proc (cmd->proc_cls, NULL);
1332 ret = GNUNET_DISK_file_read (cmd->r,
1333 &cmd->buf[cmd->off],
1334 sizeof (cmd->buf)-cmd->off);
1337 if ( (cmd->off > 0) && (cmd->off < sizeof (cmd->buf)) )
1339 cmd->buf[cmd->off] = '\0';
1340 cmd->proc (cmd->proc_cls, cmd->buf);
1344 proc (cmd->proc_cls, NULL);
1347 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1352 cmd->proc (cmd->proc_cls, cmd->buf);
1355 cmd->off - (end + 1 - cmd->buf));
1356 cmd->off -= (end + 1 - cmd->buf);
1357 end = memchr (cmd->buf, '\n', cmd->off);
1359 cmd->rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (cmd->timeout),
1367 * Run the given command line and call the given function
1368 * for each line of the output.
1370 * @param proc function to call for each line of the output
1371 * @param proc_cls closure for proc
1372 * @param timeout when to time out
1373 * @param binary command to run
1374 * @param ... arguments to command
1375 * @return NULL on error
1377 struct GNUNET_OS_CommandHandle *
1378 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1380 struct GNUNET_TIME_Relative timeout,
1384 struct GNUNET_OS_CommandHandle *cmd;
1385 struct GNUNET_OS_Process *eip;
1386 struct GNUNET_DISK_PipeHandle *opipe;
1389 opipe = GNUNET_DISK_pipe (GNUNET_YES,
1394 va_start (ap, binary);
1395 eip = GNUNET_OS_start_process_va (NULL, opipe,
1400 GNUNET_DISK_pipe_close (opipe);
1403 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1404 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1405 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1409 cmd->proc_cls = proc_cls;
1410 cmd->r = GNUNET_DISK_pipe_handle (opipe,
1411 GNUNET_DISK_PIPE_END_READ);
1412 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1422 /* end of os_priority.c */