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);
90 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
91 "Re-scheduling the parent control handler pipe\n");
93 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
95 &parent_control_handler, control_pipe);
102 * Task that connects this process to its parent via pipe
105 GNUNET_OS_install_parent_control_handler (void *cls,
107 GNUNET_SCHEDULER_TaskContext * tc)
110 struct GNUNET_DISK_FileHandle *control_pipe;
112 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
113 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
115 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
116 _("Not installing a handler because $%s=%s\n"),
117 GNUNET_OS_CONTROL_PIPE,
121 control_pipe = GNUNET_DISK_npipe_open (env_buf,
122 GNUNET_DISK_OPEN_READ,
123 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
124 if (control_pipe == NULL)
126 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
132 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
133 "Adding parent control handler pipe `%s' to the scheduler\n",
136 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
138 &parent_control_handler,
144 * Get process structure for current process
146 * The pointer it returns points to static memory location and must not be
149 * @return pointer to the process sturcutre for this process
151 struct GNUNET_OS_Process *
152 GNUNET_OS_process_current ()
155 current_process.pid = GetCurrentProcessId ();
156 current_process.handle = GetCurrentProcess ();
158 current_process.pid = 0;
160 return ¤t_process;
165 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
167 #if ENABLE_WINDOWS_WORKAROUNDS
171 ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof(sig));
172 if (ret != sizeof(sig))
175 /* Child process is not controllable via pipe */
176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
177 "Child process is not controllable, will kill it directly\n");
179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 "Failed to write into control pipe , errno is %d\n", errno);
181 #if WINDOWS && !defined(__CYGWIN__)
182 TerminateProcess (proc->handle, 0);
184 PLIBC_KILL (proc->pid, sig);
189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
190 "Wrote control code into control pipe, now waiting\n");
193 /* Give it 3 seconds to die, then kill it in a nice Windows-specific way */
194 if (WaitForSingleObject (proc->handle, 3000) != WAIT_OBJECT_0)
195 TerminateProcess (proc->handle, 0);
198 struct GNUNET_NETWORK_FDSet *rfds;
199 struct GNUNET_NETWORK_FDSet *efds;
201 rfds = GNUNET_NETWORK_fdset_create ();
202 efds = GNUNET_NETWORK_fdset_create ();
204 GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe);
205 GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe);
207 /* Ndurner thought this up, and i have no idea what it does.
208 * There's have never been any code to answer the shutdown call
209 * (write a single int into the pipe, so that this function can read it).
210 * On *nix select() will probably tell that pipe is ready
211 * for reading, once the other process shuts down,
212 * but the read () call will fail, triggering a kill ()
213 * on the pid that is already dead. This will probably result in non-0
214 * return from kill(), and therefore from this function.
218 ret = GNUNET_NETWORK_socket_select (rfds, NULL, efds,
219 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_unit (),
222 if (ret < 1 || GNUNET_NETWORK_fdset_handle_isset (efds,
225 /* Just to be sure */
226 PLIBC_KILL (proc->pid, sig);
232 if (GNUNET_DISK_file_read (proc->control_pipe, &ret,
233 sizeof(ret)) != GNUNET_OK)
234 res = PLIBC_KILL (proc->pid, sig);
236 /* Child signaled shutdown is in progress */
245 return kill (proc->pid, sig);
250 * Get the pid of the process in question
252 * @param proc the process to get the pid of
254 * @return the current process id
257 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
264 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
266 #if ENABLE_WINDOWS_WORKAROUNDS
267 if (proc->control_pipe)
268 GNUNET_DISK_npipe_close (proc->control_pipe);
272 if (proc->handle != NULL)
273 CloseHandle (proc->handle);
280 #include "gnunet_signal_lib.h"
282 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
285 * Make seaspider happy.
287 #define DWORD_WINAPI DWORD WINAPI
290 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
291 * @param proc pointer to process structure
294 ChildWaitThread (void *arg)
296 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
297 WaitForSingleObject (proc->handle, INFINITE);
299 if (w32_sigchld_handler)
300 w32_sigchld_handler ();
307 * Set process priority
309 * @param proc pointer to process structure
310 * @param prio priority value
311 * @return GNUNET_OK on success, GNUNET_SYSERR on error
314 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
315 enum GNUNET_SCHEDULER_Priority prio)
319 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
320 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
323 /* convert to MINGW/Unix values */
326 case GNUNET_SCHEDULER_PRIORITY_UI:
327 case GNUNET_SCHEDULER_PRIORITY_URGENT:
329 rprio = HIGH_PRIORITY_CLASS;
335 case GNUNET_SCHEDULER_PRIORITY_HIGH:
337 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
343 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
345 rprio = NORMAL_PRIORITY_CLASS;
351 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
353 rprio = BELOW_NORMAL_PRIORITY_CLASS;
359 case GNUNET_SCHEDULER_PRIORITY_IDLE:
361 rprio = IDLE_PRIORITY_CLASS;
368 return GNUNET_SYSERR;
371 /* Set process priority */
374 HANDLE h = proc->handle;
375 GNUNET_assert (h != NULL);
376 SetPriorityClass (h, rprio);
383 (pid == getpid () ) )
386 int delta = rprio - have;
389 (rprio == nice (delta)) &&
392 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
393 GNUNET_ERROR_TYPE_BULK, "nice");
394 return GNUNET_SYSERR;
399 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
401 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
402 GNUNET_ERROR_TYPE_BULK, "setpriority");
403 return GNUNET_SYSERR;
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
408 "Priority management not availabe for this platform\n");
415 CreateCustomEnvTable (char **vars)
417 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
418 size_t tablesize = 0;
419 size_t items_count = 0;
420 size_t n_found = 0, n_var;
426 win32_env_table = GetEnvironmentStringsA ();
427 if (win32_env_table == NULL)
429 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
431 index = GNUNET_malloc (n_var);
432 for (c = 0; c < n_var; c++)
434 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
436 size_t len = strlen (ptr);
438 for (var_ptr = vars; *var_ptr; var_ptr++)
442 var_len = strlen (var);
443 if (strncmp (var, ptr, var_len) == 0)
447 tablesize += var_len + strlen (val) + 1;
452 tablesize += len + 1;
455 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
460 n_found += strlen (var) + strlen (val) + 1;
462 result = GNUNET_malloc (tablesize + n_found + 1);
463 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
465 size_t len = strlen (ptr);
467 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
471 var_len = strlen (var);
472 if (strncmp (var, ptr, var_len) == 0)
480 strcpy (result_ptr, ptr);
481 result_ptr += len + 1;
485 strcpy (result_ptr, var);
486 result_ptr += var_len;
487 strcpy (result_ptr, val);
488 result_ptr += strlen (val) + 1;
492 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
496 var_len = strlen (var);
499 strcpy (result_ptr, var);
500 result_ptr += var_len;
501 strcpy (result_ptr, val);
502 result_ptr += strlen (val) + 1;
505 FreeEnvironmentStrings (win32_env_table);
516 * @param pipe_stdin pipe to use to send input to child process (or NULL)
517 * @param pipe_stdout pipe to use to get output from child process (or NULL)
518 * @param filename name of the binary
519 * @param va NULL-terminated list of arguments to the process
520 * @return pointer to process structure of the new process, NULL on error
522 struct GNUNET_OS_Process *
523 GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin,
524 struct GNUNET_DISK_PipeHandle *pipe_stdout,
525 const char *filename,
529 #if ENABLE_WINDOWS_WORKAROUNDS
530 char *childpipename = NULL;
531 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
533 struct GNUNET_OS_Process *gnunet_proc = NULL;
544 #if ENABLE_WINDOWS_WORKAROUNDS
545 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
546 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
547 GNUNET_DISK_PERM_USER_WRITE);
548 if (control_pipe == NULL)
554 while (NULL != va_arg (ap, char *))
557 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
560 while (NULL != (argv[argc] = va_arg (ap, char *)))
563 if (pipe_stdout != NULL)
565 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
566 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
568 if (pipe_stdin != NULL)
570 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
571 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
574 #if HAVE_WORKING_VFORK
583 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
584 #if ENABLE_WINDOWS_WORKAROUNDS
585 GNUNET_DISK_npipe_close (control_pipe);
591 #if HAVE_WORKING_VFORK
592 /* let's hope vfork actually works; for some extreme cases (including
593 a testcase) we need 'execvp' to have run before we return, since
594 we may send a signal to the process next and we don't want it
595 to be caught by OUR signal handler (but either by the default
596 handler or the actual handler as installed by the process itself). */
598 /* let's give the child process a chance to run execvp, 1s should
599 be plenty in practice */
600 if (pipe_stdout != NULL)
601 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
602 if (pipe_stdin != NULL)
603 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
606 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
607 gnunet_proc->pid = ret;
608 #if ENABLE_WINDOWS_WORKAROUNDS
609 gnunet_proc->control_pipe = control_pipe;
613 #if ENABLE_WINDOWS_WORKAROUNDS
614 GNUNET_free (childpipename);
619 #if ENABLE_WINDOWS_WORKAROUNDS
620 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
621 GNUNET_free (childpipename);
624 if (pipe_stdout != NULL)
626 GNUNET_break (0 == close (fd_stdout_read));
627 if (-1 == dup2(fd_stdout_write, 1))
628 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
629 GNUNET_break (0 == close (fd_stdout_write));
632 if (pipe_stdin != NULL)
635 GNUNET_break (0 == close (fd_stdin_write));
636 if (-1 == dup2(fd_stdin_read, 0))
637 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
638 GNUNET_break (0 == close (fd_stdin_read));
640 execvp (filename, argv);
641 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
648 PROCESS_INFORMATION proc;
651 HANDLE stdout_handle;
653 char path[MAX_PATH + 1];
655 char *our_env[3] = { NULL, NULL, NULL };
656 char *env_block = NULL;
658 DWORD pathbuf_len, alloc_len;
663 char *non_const_filename;
665 /* Search in prefix dir (hopefully - the directory from which
666 * the current module was loaded), bindir and libdir, then in PATH
668 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
669 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
670 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
672 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
674 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
676 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
679 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
680 GNUNET_free (self_prefix);
681 GNUNET_free (bindir);
682 GNUNET_free (libdir);
684 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
685 GNUNET_assert (alloc_len == (pathbuf_len - 1));
687 cmdlen = strlen (filename);
688 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
689 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
691 GNUNET_asprintf (&non_const_filename, "%s", filename);
693 /* Check that this is the full path. If it isn't, search. */
694 if (non_const_filename[1] == ':')
695 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
696 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
698 SetErrnoFromWinError (GetLastError ());
699 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
700 GNUNET_free (non_const_filename);
701 GNUNET_free (pathbuf);
704 GNUNET_free (pathbuf);
705 GNUNET_free (non_const_filename);
709 while (NULL != (arg = va_arg (ap, char *)))
712 cmdlen = cmdlen + strlen (path) + 3;
714 cmdlen = cmdlen + strlen (arg) + 3;
718 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
720 while (NULL != (arg = va_arg (ap, char *)))
723 idx += sprintf (idx, "\"%s\" ", path);
725 idx += sprintf (idx, "\"%s\" ", arg);
729 memset (&start, 0, sizeof (start));
730 start.cb = sizeof (start);
732 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
733 start.dwFlags |= STARTF_USESTDHANDLES;
735 if (pipe_stdin != NULL)
737 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
738 start.hStdInput = stdin_handle;
741 if (pipe_stdout != NULL)
743 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
744 start.hStdOutput = stdout_handle;
747 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
748 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
749 GNUNET_DISK_PERM_USER_WRITE);
750 if (control_pipe == NULL)
757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
758 "Opened the parent end of the pipe `%s'\n",
761 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
762 GNUNET_asprintf (&our_env[1], "%s", childpipename);
764 env_block = CreateCustomEnvTable (our_env);
765 GNUNET_free (our_env[0]);
766 GNUNET_free (our_env[1]);
769 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
770 env_block, NULL, &start, &proc))
772 SetErrnoFromWinError (GetLastError ());
773 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
774 GNUNET_free (env_block);
779 GNUNET_free (env_block);
781 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
782 gnunet_proc->pid = proc.dwProcessId;
783 gnunet_proc->handle = proc.hProcess;
784 gnunet_proc->control_pipe = control_pipe;
786 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
788 ResumeThread (proc.hThread);
789 CloseHandle (proc.hThread);
801 * @param pipe_stdin pipe to use to send input to child process (or NULL)
802 * @param pipe_stdout pipe to use to get output from child process (or NULL)
803 * @param filename name of the binary
804 * @param ... NULL-terminated list of arguments to the process
806 * @return pointer to process structure of the new process, NULL on error
809 struct GNUNET_OS_Process *
810 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
811 struct GNUNET_DISK_PipeHandle *pipe_stdout,
812 const char *filename, ...)
814 struct GNUNET_OS_Process *ret;
817 va_start (ap, filename);
818 ret = GNUNET_OS_start_process_va (pipe_stdin,
830 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
831 * must be NULL on platforms where dup is not supported
832 * @param filename name of the binary
833 * @param argv NULL-terminated list of arguments to the process
834 * @return process ID of the new process, -1 on error
836 struct GNUNET_OS_Process *
837 GNUNET_OS_start_process_v (const int *lsocks,
838 const char *filename, char *const argv[])
840 #if ENABLE_WINDOWS_WORKAROUNDS
841 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
842 char *childpipename = NULL;
849 struct GNUNET_OS_Process *gnunet_proc = NULL;
858 #if ENABLE_WINDOWS_WORKAROUNDS
859 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
860 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
861 GNUNET_DISK_PERM_USER_WRITE);
862 if (control_pipe == NULL)
871 while (-1 != (k = lsocks[i++]))
872 GNUNET_array_append (lscp, ls, k);
873 GNUNET_array_append (lscp, ls, -1);
875 #if HAVE_WORKING_VFORK
884 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
885 #if ENABLE_WINDOWS_WORKAROUNDS
886 GNUNET_DISK_npipe_close (control_pipe);
891 #if HAVE_WORKING_VFORK
892 /* let's hope vfork actually works; for some extreme cases (including
893 a testcase) we need 'execvp' to have run before we return, since
894 we may send a signal to the process next and we don't want it
895 to be caught by OUR signal handler (but either by the default
896 handler or the actual handler as installed by the process itself). */
898 /* let's give the child process a chance to run execvp, 1s should
899 be plenty in practice */
902 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
903 gnunet_proc->pid = ret;
904 #if ENABLE_WINDOWS_WORKAROUNDS
905 gnunet_proc->control_pipe = control_pipe;
909 GNUNET_array_grow (lscp, ls, 0);
910 #if ENABLE_WINDOWS_WORKAROUNDS
911 GNUNET_free (childpipename);
916 #if ENABLE_WINDOWS_WORKAROUNDS
917 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
918 GNUNET_free (childpipename);
923 /* read systemd documentation... */
924 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
925 setenv ("LISTEN_PID", lpid, 1);
928 while (-1 != lscp[i])
931 while (-1 != lscp[j])
937 GNUNET_assert (-1 != k);
938 GNUNET_assert (0 == close (lscp[j]));
946 /* Bury any existing FD, no matter what; they should all be closed
947 on exec anyway and the important onces have been dup'ed away */
949 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
951 /* unset close-on-exec flag */
952 flags = fcntl (tgt, F_GETFD);
953 GNUNET_assert (flags >= 0);
954 flags &= ~FD_CLOEXEC;
956 (void) fcntl (tgt, F_SETFD, flags);
960 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
961 setenv ("LISTEN_FDS", fds, 1);
963 GNUNET_array_grow (lscp, ls, 0);
964 execvp (filename, argv);
965 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
968 char **arg, **non_const_argv;
972 PROCESS_INFORMATION proc;
974 struct GNUNET_OS_Process *gnunet_proc = NULL;
976 char path[MAX_PATH + 1];
978 char *our_env[3] = { NULL, NULL, NULL };
979 char *env_block = NULL;
981 DWORD pathbuf_len, alloc_len;
986 char *non_const_filename;
988 GNUNET_assert (lsocks == NULL);
990 /* Search in prefix dir (hopefully - the directory from which
991 * the current module was loaded), bindir and libdir, then in PATH
993 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
994 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
995 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
997 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
999 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
1001 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1004 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1005 GNUNET_free (self_prefix);
1006 GNUNET_free (bindir);
1007 GNUNET_free (libdir);
1009 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1010 if (alloc_len != pathbuf_len - 1)
1012 GNUNET_free (pathbuf);
1013 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1017 cmdlen = strlen (filename);
1018 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1019 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1021 GNUNET_asprintf (&non_const_filename, "%s", filename);
1023 /* Check that this is the full path. If it isn't, search. */
1024 if (non_const_filename[1] == ':')
1025 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1026 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
1028 SetErrnoFromWinError (GetLastError ());
1029 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
1030 GNUNET_free (non_const_filename);
1031 GNUNET_free (pathbuf);
1034 GNUNET_free (pathbuf);
1035 GNUNET_free (non_const_filename);
1037 /* Count the number of arguments */
1038 arg = (char **) argv;
1045 /* Allocate a copy argv */
1046 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1048 /* Copy all argv strings */
1050 arg = (char **) argv;
1054 non_const_argv[argcount] = GNUNET_strdup (path);
1056 non_const_argv[argcount] = GNUNET_strdup (*arg);
1060 non_const_argv[argcount] = NULL;
1064 arg = non_const_argv;
1067 cmdlen = cmdlen + strlen (*arg) + 3;
1071 /* Allocate and create cmd */
1072 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1073 arg = non_const_argv;
1076 idx += sprintf (idx, "\"%s\" ", *arg);
1080 while (argcount > 0)
1081 GNUNET_free (non_const_argv[--argcount]);
1082 GNUNET_free (non_const_argv);
1084 memset (&start, 0, sizeof (start));
1085 start.cb = sizeof (start);
1087 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1088 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1089 GNUNET_DISK_PERM_USER_WRITE);
1090 if (control_pipe == NULL)
1097 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1099 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1100 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1102 env_block = CreateCustomEnvTable (our_env);
1103 GNUNET_free (our_env[0]);
1104 GNUNET_free (our_env[1]);
1107 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1108 env_block, NULL, &start, &proc))
1110 SetErrnoFromWinError (GetLastError ());
1111 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1112 GNUNET_free (env_block);
1117 GNUNET_free (env_block);
1119 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1120 gnunet_proc->pid = proc.dwProcessId;
1121 gnunet_proc->handle = proc.hProcess;
1122 gnunet_proc->control_pipe = control_pipe;
1124 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1126 ResumeThread (proc.hThread);
1127 CloseHandle (proc.hThread);
1136 * Retrieve the status of a process
1137 * @param proc process ID
1138 * @param type status type
1139 * @param code return code/signal number
1140 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1143 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1144 enum GNUNET_OS_ProcessStatusType *type,
1145 unsigned long *code)
1151 GNUNET_assert (0 != proc);
1152 ret = waitpid (proc->pid, &status, WNOHANG);
1155 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1156 return GNUNET_SYSERR;
1160 *type = GNUNET_OS_PROCESS_RUNNING;
1164 if (proc->pid != ret)
1166 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1167 return GNUNET_SYSERR;
1169 if (WIFEXITED (status))
1171 *type = GNUNET_OS_PROCESS_EXITED;
1172 *code = WEXITSTATUS (status);
1174 else if (WIFSIGNALED (status))
1176 *type = GNUNET_OS_PROCESS_SIGNALED;
1177 *code = WTERMSIG (status);
1179 else if (WIFSTOPPED (status))
1181 *type = GNUNET_OS_PROCESS_SIGNALED;
1182 *code = WSTOPSIG (status);
1185 else if (WIFCONTINUED (status))
1187 *type = GNUNET_OS_PROCESS_RUNNING;
1193 *type = GNUNET_OS_PROCESS_UNKNOWN;
1198 DWORD c, error_code, ret;
1202 if (h == NULL || ret == 0)
1204 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1205 return GNUNET_SYSERR;
1208 h = GetCurrentProcess ();
1211 ret = GetExitCodeProcess (h, &c);
1212 error_code = GetLastError ();
1213 if (ret == 0 || error_code != NO_ERROR)
1215 SetErrnoFromWinError (error_code);
1216 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1217 return GNUNET_SYSERR;
1219 if (STILL_ACTIVE == c)
1221 *type = GNUNET_OS_PROCESS_RUNNING;
1225 *type = GNUNET_OS_PROCESS_EXITED;
1233 * Wait for a process
1234 * @param proc pointer to process structure
1235 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1238 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1242 pid_t pid = proc->pid;
1243 if (pid != waitpid (pid, NULL, 0))
1244 return GNUNET_SYSERR;
1253 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1254 "Invalid process information {%d, %08X}\n",
1257 return GNUNET_SYSERR;
1260 h = GetCurrentProcess ();
1262 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1264 SetErrnoFromWinError (GetLastError ());
1265 ret = GNUNET_SYSERR;
1276 * Handle to a command.
1278 struct GNUNET_OS_CommandHandle
1284 struct GNUNET_OS_Process *eip;
1287 * Handle to the output pipe.
1289 struct GNUNET_DISK_PipeHandle *opipe;
1292 * Read-end of output pipe.
1294 const struct GNUNET_DISK_FileHandle *r;
1297 * Function to call on each line of output.
1299 GNUNET_OS_LineProcessor proc;
1302 * Closure for 'proc'.
1307 * Buffer for the output.
1312 * Task reading from pipe.
1314 GNUNET_SCHEDULER_TaskIdentifier rtask;
1319 struct GNUNET_TIME_Absolute timeout;
1322 * Current read offset in buf.
1329 * Stop/kill a command. Must ONLY be called either from
1330 * the callback after 'NULL' was passed for 'line' *OR*
1331 * from an independent task (not within the line processor).
1333 * @param cmd handle to the process
1336 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1339 if (cmd->proc != NULL)
1341 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1342 GNUNET_SCHEDULER_cancel (cmd->rtask);
1344 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1345 GNUNET_break (GNUNET_OK ==
1346 GNUNET_OS_process_wait (cmd->eip));
1347 GNUNET_OS_process_close (cmd->eip);
1348 GNUNET_DISK_pipe_close (cmd->opipe);
1354 * Read from the process and call the line processor.
1356 * @param cls the 'struct GNUNET_OS_CommandHandle'
1357 * @param tc scheduler context
1360 cmd_read (void *cls,
1361 const struct GNUNET_SCHEDULER_TaskContext *tc)
1363 struct GNUNET_OS_CommandHandle *cmd = cls;
1364 GNUNET_OS_LineProcessor proc;
1368 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1370 GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1373 /* timeout, shutdown, etc. */
1376 proc (cmd->proc_cls, NULL);
1379 ret = GNUNET_DISK_file_read (cmd->r,
1380 &cmd->buf[cmd->off],
1381 sizeof (cmd->buf)-cmd->off);
1384 if ( (cmd->off > 0) && (cmd->off < sizeof (cmd->buf)) )
1386 cmd->buf[cmd->off] = '\0';
1387 cmd->proc (cmd->proc_cls, cmd->buf);
1391 proc (cmd->proc_cls, NULL);
1394 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1399 cmd->proc (cmd->proc_cls, cmd->buf);
1402 cmd->off - (end + 1 - cmd->buf));
1403 cmd->off -= (end + 1 - cmd->buf);
1404 end = memchr (cmd->buf, '\n', cmd->off);
1406 cmd->rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (cmd->timeout),
1414 * Run the given command line and call the given function
1415 * for each line of the output.
1417 * @param proc function to call for each line of the output
1418 * @param proc_cls closure for proc
1419 * @param timeout when to time out
1420 * @param binary command to run
1421 * @param ... arguments to command
1422 * @return NULL on error
1424 struct GNUNET_OS_CommandHandle *
1425 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1427 struct GNUNET_TIME_Relative timeout,
1431 struct GNUNET_OS_CommandHandle *cmd;
1432 struct GNUNET_OS_Process *eip;
1433 struct GNUNET_DISK_PipeHandle *opipe;
1436 opipe = GNUNET_DISK_pipe (GNUNET_YES,
1441 va_start (ap, binary);
1442 eip = GNUNET_OS_start_process_va (NULL, opipe,
1447 GNUNET_DISK_pipe_close (opipe);
1450 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1451 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1452 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1456 cmd->proc_cls = proc_cls;
1457 cmd->r = GNUNET_DISK_pipe_handle (opipe,
1458 GNUNET_DISK_PIPE_END_READ);
1459 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1469 /* end of os_priority.c */