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
51 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
52 * @param tc scheduler context
55 parent_control_handler (void *cls,
57 GNUNET_SCHEDULER_TaskContext * tc)
59 struct GNUNET_DISK_FileHandle *control_pipe = (struct GNUNET_DISK_FileHandle *) cls;
63 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
64 "`%s' invoked because of %d\n",
68 if (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE))
70 GNUNET_DISK_npipe_close (control_pipe);
74 if (GNUNET_DISK_file_read (control_pipe,
76 sizeof (sig)) != sizeof (sig))
78 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
79 "GNUNET_DISK_file_read");
80 GNUNET_DISK_npipe_close (control_pipe);
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Got control code %d from parent\n", sig);
88 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
90 &parent_control_handler, control_pipe);
98 * Task that connects this process to its parent via pipe
101 GNUNET_OS_install_parent_control_handler (void *cls,
103 GNUNET_SCHEDULER_TaskContext * tc)
106 struct GNUNET_DISK_FileHandle *control_pipe;
108 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
109 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
111 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
112 _("Not installing a handler because $%s=%s\n"),
113 GNUNET_OS_CONTROL_PIPE,
117 control_pipe = GNUNET_DISK_npipe_open (env_buf,
118 GNUNET_DISK_OPEN_READ,
119 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
120 if (control_pipe == NULL)
122 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
129 "Adding parent control handler pipe `%s' to the scheduler\n",
132 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
134 &parent_control_handler,
140 * Get process structure for current process
142 * The pointer it returns points to static memory location and must not be
145 * @return pointer to the process sturcutre for this process
147 struct GNUNET_OS_Process *
148 GNUNET_OS_process_current ()
151 current_process.pid = GetCurrentProcessId ();
152 current_process.handle = GetCurrentProcess ();
154 current_process.pid = 0;
156 return ¤t_process;
161 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
163 #if ENABLE_WINDOWS_WORKAROUNDS
167 ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof(sig));
168 if (ret != sizeof(sig))
172 /* Child process is not controllable via pipe */
174 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
175 "Child process is not controllable, will kill it directly\n");
178 else if (errno == EPIPE)
181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
182 "Failed to write into control pipe, because pipe is invalid (the child is most likely dead)\n");
186 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
187 "Failed to write into control pipe , errno is %d\n", errno);
188 #if WINDOWS && !defined(__CYGWIN__)
189 TerminateProcess (proc->handle, 0);
191 PLIBC_KILL (proc->pid, sig);
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Wrote control code into control pipe, now waiting\n");
202 /* Give it 3 seconds to die, then kill it in a nice Windows-specific way */
203 if (WaitForSingleObject (proc->handle, 3000) != WAIT_OBJECT_0)
204 TerminateProcess (proc->handle, 0);
207 struct GNUNET_NETWORK_FDSet *rfds;
208 struct GNUNET_NETWORK_FDSet *efds;
210 rfds = GNUNET_NETWORK_fdset_create ();
211 efds = GNUNET_NETWORK_fdset_create ();
213 GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe);
214 GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe);
216 /* Ndurner thought this up, and i have no idea what it does.
217 * There's have never been any code to answer the shutdown call
218 * (write a single int into the pipe, so that this function can read it).
219 * On *nix select() will probably tell that pipe is ready
220 * for reading, once the other process shuts down,
221 * but the read () call will fail, triggering a kill ()
222 * on the pid that is already dead. This will probably result in non-0
223 * return from kill(), and therefore from this function.
227 ret = GNUNET_NETWORK_socket_select (rfds, NULL, efds,
228 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_unit (),
231 if (ret < 1 || GNUNET_NETWORK_fdset_handle_isset (efds,
234 /* Just to be sure */
235 PLIBC_KILL (proc->pid, sig);
241 if (GNUNET_DISK_file_read (proc->control_pipe, &ret,
242 sizeof(ret)) != GNUNET_OK)
243 res = PLIBC_KILL (proc->pid, sig);
245 /* Child signaled shutdown is in progress */
254 return kill (proc->pid, sig);
259 * Get the pid of the process in question
261 * @param proc the process to get the pid of
263 * @return the current process id
266 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
273 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
275 #if ENABLE_WINDOWS_WORKAROUNDS
276 if (proc->control_pipe)
277 GNUNET_DISK_npipe_close (proc->control_pipe);
281 if (proc->handle != NULL)
282 CloseHandle (proc->handle);
289 #include "gnunet_signal_lib.h"
291 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
294 * Make seaspider happy.
296 #define DWORD_WINAPI DWORD WINAPI
299 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
300 * @param proc pointer to process structure
303 ChildWaitThread (void *arg)
305 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
306 WaitForSingleObject (proc->handle, INFINITE);
308 if (w32_sigchld_handler)
309 w32_sigchld_handler ();
316 * Set process priority
318 * @param proc pointer to process structure
319 * @param prio priority value
320 * @return GNUNET_OK on success, GNUNET_SYSERR on error
323 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
324 enum GNUNET_SCHEDULER_Priority prio)
328 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
329 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
332 /* convert to MINGW/Unix values */
335 case GNUNET_SCHEDULER_PRIORITY_UI:
336 case GNUNET_SCHEDULER_PRIORITY_URGENT:
338 rprio = HIGH_PRIORITY_CLASS;
344 case GNUNET_SCHEDULER_PRIORITY_HIGH:
346 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
352 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
354 rprio = NORMAL_PRIORITY_CLASS;
360 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
362 rprio = BELOW_NORMAL_PRIORITY_CLASS;
368 case GNUNET_SCHEDULER_PRIORITY_IDLE:
370 rprio = IDLE_PRIORITY_CLASS;
377 return GNUNET_SYSERR;
380 /* Set process priority */
383 HANDLE h = proc->handle;
384 GNUNET_assert (h != NULL);
385 SetPriorityClass (h, rprio);
392 (pid == getpid () ) )
395 int delta = rprio - have;
398 (rprio == nice (delta)) &&
401 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
402 GNUNET_ERROR_TYPE_BULK, "nice");
403 return GNUNET_SYSERR;
408 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
410 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
411 GNUNET_ERROR_TYPE_BULK, "setpriority");
412 return GNUNET_SYSERR;
417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
418 "Priority management not availabe for this platform\n");
426 CreateCustomEnvTable (char **vars)
428 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
429 size_t tablesize = 0;
430 size_t items_count = 0;
431 size_t n_found = 0, n_var;
437 win32_env_table = GetEnvironmentStringsA ();
438 if (win32_env_table == NULL)
440 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
442 index = GNUNET_malloc (sizeof (char *) * n_var);
443 for (c = 0; c < n_var; c++)
445 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
447 size_t len = strlen (ptr);
449 for (var_ptr = vars; *var_ptr; var_ptr++)
453 var_len = strlen (var);
454 if (strncmp (var, ptr, var_len) == 0)
458 tablesize += var_len + strlen (val) + 1;
463 tablesize += len + 1;
466 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
471 n_found += strlen (var) + strlen (val) + 1;
473 result = GNUNET_malloc (tablesize + n_found + 1);
474 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
476 size_t len = strlen (ptr);
478 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
482 var_len = strlen (var);
483 if (strncmp (var, ptr, var_len) == 0)
491 strcpy (result_ptr, ptr);
492 result_ptr += len + 1;
496 strcpy (result_ptr, var);
497 result_ptr += var_len;
498 strcpy (result_ptr, val);
499 result_ptr += strlen (val) + 1;
503 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
507 var_len = strlen (var);
510 strcpy (result_ptr, var);
511 result_ptr += var_len;
512 strcpy (result_ptr, val);
513 result_ptr += strlen (val) + 1;
516 FreeEnvironmentStrings (win32_env_table);
527 * @param pipe_stdin pipe to use to send input to child process (or NULL)
528 * @param pipe_stdout pipe to use to get output from child process (or NULL)
529 * @param filename name of the binary
530 * @param va NULL-terminated list of arguments to the process
531 * @return pointer to process structure of the new process, NULL on error
533 struct GNUNET_OS_Process *
534 GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin,
535 struct GNUNET_DISK_PipeHandle *pipe_stdout,
536 const char *filename,
540 #if ENABLE_WINDOWS_WORKAROUNDS
541 char *childpipename = NULL;
542 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
544 struct GNUNET_OS_Process *gnunet_proc = NULL;
555 #if ENABLE_WINDOWS_WORKAROUNDS
556 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
557 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
558 GNUNET_DISK_PERM_USER_WRITE);
559 if (control_pipe == NULL)
565 while (NULL != va_arg (ap, char *))
568 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
571 while (NULL != (argv[argc] = va_arg (ap, char *)))
574 if (pipe_stdout != NULL)
576 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
577 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
579 if (pipe_stdin != NULL)
581 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
582 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
585 #if HAVE_WORKING_VFORK
594 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
595 #if ENABLE_WINDOWS_WORKAROUNDS
596 GNUNET_DISK_npipe_close (control_pipe);
602 #if HAVE_WORKING_VFORK
603 /* let's hope vfork actually works; for some extreme cases (including
604 a testcase) we need 'execvp' to have run before we return, since
605 we may send a signal to the process next and we don't want it
606 to be caught by OUR signal handler (but either by the default
607 handler or the actual handler as installed by the process itself). */
609 /* let's give the child process a chance to run execvp, 1s should
610 be plenty in practice */
611 if (pipe_stdout != NULL)
612 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
613 if (pipe_stdin != NULL)
614 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
617 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
618 gnunet_proc->pid = ret;
619 #if ENABLE_WINDOWS_WORKAROUNDS
620 gnunet_proc->control_pipe = control_pipe;
624 #if ENABLE_WINDOWS_WORKAROUNDS
625 GNUNET_free (childpipename);
630 #if ENABLE_WINDOWS_WORKAROUNDS
631 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
632 GNUNET_free (childpipename);
635 if (pipe_stdout != NULL)
637 GNUNET_break (0 == close (fd_stdout_read));
638 if (-1 == dup2(fd_stdout_write, 1))
639 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
640 GNUNET_break (0 == close (fd_stdout_write));
643 if (pipe_stdin != NULL)
646 GNUNET_break (0 == close (fd_stdin_write));
647 if (-1 == dup2(fd_stdin_read, 0))
648 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
649 GNUNET_break (0 == close (fd_stdin_read));
651 execvp (filename, argv);
652 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
659 PROCESS_INFORMATION proc;
662 HANDLE stdout_handle;
664 char path[MAX_PATH + 1];
666 char *our_env[3] = { NULL, NULL, NULL };
667 char *env_block = NULL;
669 DWORD pathbuf_len, alloc_len;
674 char *non_const_filename;
676 /* Search in prefix dir (hopefully - the directory from which
677 * the current module was loaded), bindir and libdir, then in PATH
679 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
680 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
681 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
683 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
685 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
687 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
690 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
691 GNUNET_free (self_prefix);
692 GNUNET_free (bindir);
693 GNUNET_free (libdir);
695 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
696 GNUNET_assert (alloc_len == (pathbuf_len - 1));
698 cmdlen = strlen (filename);
699 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
700 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
702 GNUNET_asprintf (&non_const_filename, "%s", filename);
704 /* Check that this is the full path. If it isn't, search. */
705 if (non_const_filename[1] == ':')
706 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
707 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
709 SetErrnoFromWinError (GetLastError ());
710 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
711 GNUNET_free (non_const_filename);
712 GNUNET_free (pathbuf);
715 GNUNET_free (pathbuf);
716 GNUNET_free (non_const_filename);
720 while (NULL != (arg = va_arg (ap, char *)))
723 cmdlen = cmdlen + strlen (path) + 3;
725 cmdlen = cmdlen + strlen (arg) + 3;
729 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
731 while (NULL != (arg = va_arg (ap, char *)))
734 idx += sprintf (idx, "\"%s\" ", path);
736 idx += sprintf (idx, "\"%s\" ", arg);
740 memset (&start, 0, sizeof (start));
741 start.cb = sizeof (start);
743 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
744 start.dwFlags |= STARTF_USESTDHANDLES;
746 if (pipe_stdin != NULL)
748 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
749 start.hStdInput = stdin_handle;
752 if (pipe_stdout != NULL)
754 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
755 start.hStdOutput = stdout_handle;
758 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
759 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
760 GNUNET_DISK_PERM_USER_WRITE);
761 if (control_pipe == NULL)
769 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
770 "Opened the parent end of the pipe `%s'\n",
774 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
775 GNUNET_asprintf (&our_env[1], "%s", childpipename);
777 env_block = CreateCustomEnvTable (our_env);
778 GNUNET_free (our_env[0]);
779 GNUNET_free (our_env[1]);
782 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
783 env_block, NULL, &start, &proc))
785 SetErrnoFromWinError (GetLastError ());
786 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
787 GNUNET_free (env_block);
792 GNUNET_free (env_block);
794 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
795 gnunet_proc->pid = proc.dwProcessId;
796 gnunet_proc->handle = proc.hProcess;
797 gnunet_proc->control_pipe = control_pipe;
799 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
801 ResumeThread (proc.hThread);
802 CloseHandle (proc.hThread);
814 * @param pipe_stdin pipe to use to send input to child process (or NULL)
815 * @param pipe_stdout pipe to use to get output from child process (or NULL)
816 * @param filename name of the binary
817 * @param ... NULL-terminated list of arguments to the process
819 * @return pointer to process structure of the new process, NULL on error
822 struct GNUNET_OS_Process *
823 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
824 struct GNUNET_DISK_PipeHandle *pipe_stdout,
825 const char *filename, ...)
827 struct GNUNET_OS_Process *ret;
830 va_start (ap, filename);
831 ret = GNUNET_OS_start_process_va (pipe_stdin,
843 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
844 * must be NULL on platforms where dup is not supported
845 * @param filename name of the binary
846 * @param argv NULL-terminated list of arguments to the process
847 * @return process ID of the new process, -1 on error
849 struct GNUNET_OS_Process *
850 GNUNET_OS_start_process_v (const int *lsocks,
851 const char *filename, char *const argv[])
853 #if ENABLE_WINDOWS_WORKAROUNDS
854 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
855 char *childpipename = NULL;
862 struct GNUNET_OS_Process *gnunet_proc = NULL;
871 #if ENABLE_WINDOWS_WORKAROUNDS
872 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
873 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
874 GNUNET_DISK_PERM_USER_WRITE);
875 if (control_pipe == NULL)
884 while (-1 != (k = lsocks[i++]))
885 GNUNET_array_append (lscp, ls, k);
886 GNUNET_array_append (lscp, ls, -1);
888 #if HAVE_WORKING_VFORK
897 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
898 #if ENABLE_WINDOWS_WORKAROUNDS
899 GNUNET_DISK_npipe_close (control_pipe);
904 #if HAVE_WORKING_VFORK
905 /* let's hope vfork actually works; for some extreme cases (including
906 a testcase) we need 'execvp' to have run before we return, since
907 we may send a signal to the process next and we don't want it
908 to be caught by OUR signal handler (but either by the default
909 handler or the actual handler as installed by the process itself). */
911 /* let's give the child process a chance to run execvp, 1s should
912 be plenty in practice */
915 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
916 gnunet_proc->pid = ret;
917 #if ENABLE_WINDOWS_WORKAROUNDS
918 gnunet_proc->control_pipe = control_pipe;
922 GNUNET_array_grow (lscp, ls, 0);
923 #if ENABLE_WINDOWS_WORKAROUNDS
924 GNUNET_free (childpipename);
929 #if ENABLE_WINDOWS_WORKAROUNDS
930 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
931 GNUNET_free (childpipename);
936 /* read systemd documentation... */
937 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
938 setenv ("LISTEN_PID", lpid, 1);
941 while (-1 != lscp[i])
944 while (-1 != lscp[j])
950 GNUNET_assert (-1 != k);
951 GNUNET_assert (0 == close (lscp[j]));
959 /* Bury any existing FD, no matter what; they should all be closed
960 on exec anyway and the important onces have been dup'ed away */
962 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
964 /* unset close-on-exec flag */
965 flags = fcntl (tgt, F_GETFD);
966 GNUNET_assert (flags >= 0);
967 flags &= ~FD_CLOEXEC;
969 (void) fcntl (tgt, F_SETFD, flags);
973 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
974 setenv ("LISTEN_FDS", fds, 1);
976 GNUNET_array_grow (lscp, ls, 0);
977 execvp (filename, argv);
978 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
981 char **arg, **non_const_argv;
985 PROCESS_INFORMATION proc;
987 struct GNUNET_OS_Process *gnunet_proc = NULL;
989 char path[MAX_PATH + 1];
991 char *our_env[3] = { NULL, NULL, NULL };
992 char *env_block = NULL;
994 DWORD pathbuf_len, alloc_len;
999 char *non_const_filename;
1001 GNUNET_assert (lsocks == NULL);
1003 /* Search in prefix dir (hopefully - the directory from which
1004 * the current module was loaded), bindir and libdir, then in PATH
1006 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1007 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1008 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1010 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1012 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
1014 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1017 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1018 GNUNET_free (self_prefix);
1019 GNUNET_free (bindir);
1020 GNUNET_free (libdir);
1022 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1023 if (alloc_len != pathbuf_len - 1)
1025 GNUNET_free (pathbuf);
1026 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1030 cmdlen = strlen (filename);
1031 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1032 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1034 GNUNET_asprintf (&non_const_filename, "%s", filename);
1036 /* Check that this is the full path. If it isn't, search. */
1037 if (non_const_filename[1] == ':')
1038 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1039 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
1041 SetErrnoFromWinError (GetLastError ());
1042 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
1043 GNUNET_free (non_const_filename);
1044 GNUNET_free (pathbuf);
1047 GNUNET_free (pathbuf);
1048 GNUNET_free (non_const_filename);
1050 /* Count the number of arguments */
1051 arg = (char **) argv;
1058 /* Allocate a copy argv */
1059 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1061 /* Copy all argv strings */
1063 arg = (char **) argv;
1067 non_const_argv[argcount] = GNUNET_strdup (path);
1069 non_const_argv[argcount] = GNUNET_strdup (*arg);
1073 non_const_argv[argcount] = NULL;
1077 arg = non_const_argv;
1080 cmdlen = cmdlen + strlen (*arg) + 3;
1084 /* Allocate and create cmd */
1085 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1086 arg = non_const_argv;
1089 idx += sprintf (idx, "\"%s\" ", *arg);
1093 while (argcount > 0)
1094 GNUNET_free (non_const_argv[--argcount]);
1095 GNUNET_free (non_const_argv);
1097 memset (&start, 0, sizeof (start));
1098 start.cb = sizeof (start);
1100 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1101 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1102 GNUNET_DISK_PERM_USER_WRITE);
1103 if (control_pipe == NULL)
1111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1114 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1115 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1117 env_block = CreateCustomEnvTable (our_env);
1118 GNUNET_free (our_env[0]);
1119 GNUNET_free (our_env[1]);
1122 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1123 env_block, NULL, &start, &proc))
1125 SetErrnoFromWinError (GetLastError ());
1126 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1127 GNUNET_free (env_block);
1132 GNUNET_free (env_block);
1134 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1135 gnunet_proc->pid = proc.dwProcessId;
1136 gnunet_proc->handle = proc.hProcess;
1137 gnunet_proc->control_pipe = control_pipe;
1139 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1141 ResumeThread (proc.hThread);
1142 CloseHandle (proc.hThread);
1151 * Retrieve the status of a process
1152 * @param proc process ID
1153 * @param type status type
1154 * @param code return code/signal number
1155 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1158 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1159 enum GNUNET_OS_ProcessStatusType *type,
1160 unsigned long *code)
1166 GNUNET_assert (0 != proc);
1167 ret = waitpid (proc->pid, &status, WNOHANG);
1170 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1171 return GNUNET_SYSERR;
1175 *type = GNUNET_OS_PROCESS_RUNNING;
1179 if (proc->pid != ret)
1181 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1182 return GNUNET_SYSERR;
1184 if (WIFEXITED (status))
1186 *type = GNUNET_OS_PROCESS_EXITED;
1187 *code = WEXITSTATUS (status);
1189 else if (WIFSIGNALED (status))
1191 *type = GNUNET_OS_PROCESS_SIGNALED;
1192 *code = WTERMSIG (status);
1194 else if (WIFSTOPPED (status))
1196 *type = GNUNET_OS_PROCESS_SIGNALED;
1197 *code = WSTOPSIG (status);
1200 else if (WIFCONTINUED (status))
1202 *type = GNUNET_OS_PROCESS_RUNNING;
1208 *type = GNUNET_OS_PROCESS_UNKNOWN;
1213 DWORD c, error_code, ret;
1217 if (h == NULL || ret == 0)
1219 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1220 return GNUNET_SYSERR;
1223 h = GetCurrentProcess ();
1226 ret = GetExitCodeProcess (h, &c);
1227 error_code = GetLastError ();
1228 if (ret == 0 || error_code != NO_ERROR)
1230 SetErrnoFromWinError (error_code);
1231 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1232 return GNUNET_SYSERR;
1234 if (STILL_ACTIVE == c)
1236 *type = GNUNET_OS_PROCESS_RUNNING;
1240 *type = GNUNET_OS_PROCESS_EXITED;
1249 * Wait for a process
1250 * @param proc pointer to process structure
1251 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1254 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1258 pid_t pid = proc->pid;
1259 if (pid != waitpid (pid, NULL, 0))
1260 return GNUNET_SYSERR;
1269 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1270 "Invalid process information {%d, %08X}\n",
1273 return GNUNET_SYSERR;
1276 h = GetCurrentProcess ();
1278 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1280 SetErrnoFromWinError (GetLastError ());
1281 ret = GNUNET_SYSERR;
1292 * Handle to a command.
1294 struct GNUNET_OS_CommandHandle
1300 struct GNUNET_OS_Process *eip;
1303 * Handle to the output pipe.
1305 struct GNUNET_DISK_PipeHandle *opipe;
1308 * Read-end of output pipe.
1310 const struct GNUNET_DISK_FileHandle *r;
1313 * Function to call on each line of output.
1315 GNUNET_OS_LineProcessor proc;
1318 * Closure for 'proc'.
1323 * Buffer for the output.
1328 * Task reading from pipe.
1330 GNUNET_SCHEDULER_TaskIdentifier rtask;
1335 struct GNUNET_TIME_Absolute timeout;
1338 * Current read offset in buf.
1345 * Stop/kill a command. Must ONLY be called either from
1346 * the callback after 'NULL' was passed for 'line' *OR*
1347 * from an independent task (not within the line processor).
1349 * @param cmd handle to the process
1352 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1355 if (cmd->proc != NULL)
1357 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1358 GNUNET_SCHEDULER_cancel (cmd->rtask);
1360 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1361 GNUNET_break (GNUNET_OK ==
1362 GNUNET_OS_process_wait (cmd->eip));
1363 GNUNET_OS_process_close (cmd->eip);
1364 GNUNET_DISK_pipe_close (cmd->opipe);
1370 * Read from the process and call the line processor.
1372 * @param cls the 'struct GNUNET_OS_CommandHandle'
1373 * @param tc scheduler context
1376 cmd_read (void *cls,
1377 const struct GNUNET_SCHEDULER_TaskContext *tc)
1379 struct GNUNET_OS_CommandHandle *cmd = cls;
1380 GNUNET_OS_LineProcessor proc;
1384 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1386 GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1389 /* timeout, shutdown, etc. */
1392 proc (cmd->proc_cls, NULL);
1395 ret = GNUNET_DISK_file_read (cmd->r,
1396 &cmd->buf[cmd->off],
1397 sizeof (cmd->buf)-cmd->off);
1400 if ( (cmd->off > 0) && (cmd->off < sizeof (cmd->buf)) )
1402 cmd->buf[cmd->off] = '\0';
1403 cmd->proc (cmd->proc_cls, cmd->buf);
1407 proc (cmd->proc_cls, NULL);
1410 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1415 cmd->proc (cmd->proc_cls, cmd->buf);
1418 cmd->off - (end + 1 - cmd->buf));
1419 cmd->off -= (end + 1 - cmd->buf);
1420 end = memchr (cmd->buf, '\n', cmd->off);
1422 cmd->rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (cmd->timeout),
1430 * Run the given command line and call the given function
1431 * for each line of the output.
1433 * @param proc function to call for each line of the output
1434 * @param proc_cls closure for proc
1435 * @param timeout when to time out
1436 * @param binary command to run
1437 * @param ... arguments to command
1438 * @return NULL on error
1440 struct GNUNET_OS_CommandHandle *
1441 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1443 struct GNUNET_TIME_Relative timeout,
1447 struct GNUNET_OS_CommandHandle *cmd;
1448 struct GNUNET_OS_Process *eip;
1449 struct GNUNET_DISK_PipeHandle *opipe;
1452 opipe = GNUNET_DISK_pipe (GNUNET_YES,
1457 va_start (ap, binary);
1458 eip = GNUNET_OS_start_process_va (NULL, opipe,
1463 GNUNET_DISK_pipe_close (opipe);
1466 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1467 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1468 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1472 cmd->proc_cls = proc_cls;
1473 cmd->r = GNUNET_DISK_pipe_handle (opipe,
1474 GNUNET_DISK_PIPE_END_READ);
1475 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1485 /* end of os_priority.c */