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))
171 /* Child process is not controllable via pipe */
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173 "Child process is not controllable, will kill it directly\n");
175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
176 "Failed to write into control pipe , errno is %d\n", errno);
177 #if WINDOWS && !defined(__CYGWIN__)
178 TerminateProcess (proc->handle, 0);
180 PLIBC_KILL (proc->pid, sig);
185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 "Wrote control code into control pipe, now waiting\n");
189 /* Give it 3 seconds to die, then kill it in a nice Windows-specific way */
190 if (WaitForSingleObject (proc->handle, 3000) != WAIT_OBJECT_0)
191 TerminateProcess (proc->handle, 0);
194 struct GNUNET_NETWORK_FDSet *rfds;
195 struct GNUNET_NETWORK_FDSet *efds;
197 rfds = GNUNET_NETWORK_fdset_create ();
198 efds = GNUNET_NETWORK_fdset_create ();
200 GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe);
201 GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe);
203 /* Ndurner thought this up, and i have no idea what it does.
204 * There's have never been any code to answer the shutdown call
205 * (write a single int into the pipe, so that this function can read it).
206 * On *nix select() will probably tell that pipe is ready
207 * for reading, once the other process shuts down,
208 * but the read () call will fail, triggering a kill ()
209 * on the pid that is already dead. This will probably result in non-0
210 * return from kill(), and therefore from this function.
214 ret = GNUNET_NETWORK_socket_select (rfds, NULL, efds,
215 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_unit (),
218 if (ret < 1 || GNUNET_NETWORK_fdset_handle_isset (efds,
221 /* Just to be sure */
222 PLIBC_KILL (proc->pid, sig);
228 if (GNUNET_DISK_file_read (proc->control_pipe, &ret,
229 sizeof(ret)) != GNUNET_OK)
230 res = PLIBC_KILL (proc->pid, sig);
232 /* Child signaled shutdown is in progress */
241 return kill (proc->pid, sig);
246 * Get the pid of the process in question
248 * @param proc the process to get the pid of
250 * @return the current process id
253 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
260 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
262 #if ENABLE_WINDOWS_WORKAROUNDS
263 if (proc->control_pipe)
264 GNUNET_DISK_npipe_close (proc->control_pipe);
268 if (proc->handle != NULL)
269 CloseHandle (proc->handle);
276 #include "gnunet_signal_lib.h"
278 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
281 * Make seaspider happy.
283 #define DWORD_WINAPI DWORD WINAPI
286 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
287 * @param proc pointer to process structure
290 ChildWaitThread (void *arg)
292 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
293 WaitForSingleObject (proc->handle, INFINITE);
295 if (w32_sigchld_handler)
296 w32_sigchld_handler ();
303 * Set process priority
305 * @param proc pointer to process structure
306 * @param prio priority value
307 * @return GNUNET_OK on success, GNUNET_SYSERR on error
310 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
311 enum GNUNET_SCHEDULER_Priority prio)
315 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
316 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
319 /* convert to MINGW/Unix values */
322 case GNUNET_SCHEDULER_PRIORITY_UI:
323 case GNUNET_SCHEDULER_PRIORITY_URGENT:
325 rprio = HIGH_PRIORITY_CLASS;
331 case GNUNET_SCHEDULER_PRIORITY_HIGH:
333 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
339 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
341 rprio = NORMAL_PRIORITY_CLASS;
347 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
349 rprio = BELOW_NORMAL_PRIORITY_CLASS;
355 case GNUNET_SCHEDULER_PRIORITY_IDLE:
357 rprio = IDLE_PRIORITY_CLASS;
364 return GNUNET_SYSERR;
367 /* Set process priority */
370 HANDLE h = proc->handle;
371 GNUNET_assert (h != NULL);
372 SetPriorityClass (h, rprio);
379 (pid == getpid () ) )
382 int delta = rprio - have;
385 (rprio == nice (delta)) &&
388 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
389 GNUNET_ERROR_TYPE_BULK, "nice");
390 return GNUNET_SYSERR;
395 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
397 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
398 GNUNET_ERROR_TYPE_BULK, "setpriority");
399 return GNUNET_SYSERR;
403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
404 "Priority management not availabe for this platform\n");
411 CreateCustomEnvTable (char **vars)
413 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
414 size_t tablesize = 0;
415 size_t items_count = 0;
416 size_t n_found = 0, n_var;
422 win32_env_table = GetEnvironmentStringsA ();
423 if (win32_env_table == NULL)
425 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++);
427 index = GNUNET_malloc (sizeof (char *) * n_var);
428 for (c = 0; c < n_var; c++)
430 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
432 size_t len = strlen (ptr);
434 for (var_ptr = vars; *var_ptr; var_ptr++)
438 var_len = strlen (var);
439 if (strncmp (var, ptr, var_len) == 0)
443 tablesize += var_len + strlen (val) + 1;
448 tablesize += len + 1;
451 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
456 n_found += strlen (var) + strlen (val) + 1;
458 result = GNUNET_malloc (tablesize + n_found + 1);
459 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
461 size_t len = strlen (ptr);
463 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
467 var_len = strlen (var);
468 if (strncmp (var, ptr, var_len) == 0)
476 strcpy (result_ptr, ptr);
477 result_ptr += len + 1;
481 strcpy (result_ptr, var);
482 result_ptr += var_len;
483 strcpy (result_ptr, val);
484 result_ptr += strlen (val) + 1;
488 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
492 var_len = strlen (var);
495 strcpy (result_ptr, var);
496 result_ptr += var_len;
497 strcpy (result_ptr, val);
498 result_ptr += strlen (val) + 1;
501 FreeEnvironmentStrings (win32_env_table);
512 * @param pipe_stdin pipe to use to send input to child process (or NULL)
513 * @param pipe_stdout pipe to use to get output from child process (or NULL)
514 * @param filename name of the binary
515 * @param va NULL-terminated list of arguments to the process
516 * @return pointer to process structure of the new process, NULL on error
518 struct GNUNET_OS_Process *
519 GNUNET_OS_start_process_va (struct GNUNET_DISK_PipeHandle *pipe_stdin,
520 struct GNUNET_DISK_PipeHandle *pipe_stdout,
521 const char *filename,
525 #if ENABLE_WINDOWS_WORKAROUNDS
526 char *childpipename = NULL;
527 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
529 struct GNUNET_OS_Process *gnunet_proc = NULL;
540 #if ENABLE_WINDOWS_WORKAROUNDS
541 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
542 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
543 GNUNET_DISK_PERM_USER_WRITE);
544 if (control_pipe == NULL)
550 while (NULL != va_arg (ap, char *))
553 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
556 while (NULL != (argv[argc] = va_arg (ap, char *)))
559 if (pipe_stdout != NULL)
561 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
562 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
564 if (pipe_stdin != NULL)
566 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
567 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
570 #if HAVE_WORKING_VFORK
579 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
580 #if ENABLE_WINDOWS_WORKAROUNDS
581 GNUNET_DISK_npipe_close (control_pipe);
587 #if HAVE_WORKING_VFORK
588 /* let's hope vfork actually works; for some extreme cases (including
589 a testcase) we need 'execvp' to have run before we return, since
590 we may send a signal to the process next and we don't want it
591 to be caught by OUR signal handler (but either by the default
592 handler or the actual handler as installed by the process itself). */
594 /* let's give the child process a chance to run execvp, 1s should
595 be plenty in practice */
596 if (pipe_stdout != NULL)
597 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
598 if (pipe_stdin != NULL)
599 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
602 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
603 gnunet_proc->pid = ret;
604 #if ENABLE_WINDOWS_WORKAROUNDS
605 gnunet_proc->control_pipe = control_pipe;
609 #if ENABLE_WINDOWS_WORKAROUNDS
610 GNUNET_free (childpipename);
615 #if ENABLE_WINDOWS_WORKAROUNDS
616 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
617 GNUNET_free (childpipename);
620 if (pipe_stdout != NULL)
622 GNUNET_break (0 == close (fd_stdout_read));
623 if (-1 == dup2(fd_stdout_write, 1))
624 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
625 GNUNET_break (0 == close (fd_stdout_write));
628 if (pipe_stdin != NULL)
631 GNUNET_break (0 == close (fd_stdin_write));
632 if (-1 == dup2(fd_stdin_read, 0))
633 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
634 GNUNET_break (0 == close (fd_stdin_read));
636 execvp (filename, argv);
637 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
644 PROCESS_INFORMATION proc;
647 HANDLE stdout_handle;
649 char path[MAX_PATH + 1];
651 char *our_env[3] = { NULL, NULL, NULL };
652 char *env_block = NULL;
654 DWORD pathbuf_len, alloc_len;
659 char *non_const_filename;
661 /* Search in prefix dir (hopefully - the directory from which
662 * the current module was loaded), bindir and libdir, then in PATH
664 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
665 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
666 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
668 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
670 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
672 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
675 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
676 GNUNET_free (self_prefix);
677 GNUNET_free (bindir);
678 GNUNET_free (libdir);
680 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
681 GNUNET_assert (alloc_len == (pathbuf_len - 1));
683 cmdlen = strlen (filename);
684 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
685 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
687 GNUNET_asprintf (&non_const_filename, "%s", filename);
689 /* Check that this is the full path. If it isn't, search. */
690 if (non_const_filename[1] == ':')
691 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
692 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
694 SetErrnoFromWinError (GetLastError ());
695 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
696 GNUNET_free (non_const_filename);
697 GNUNET_free (pathbuf);
700 GNUNET_free (pathbuf);
701 GNUNET_free (non_const_filename);
705 while (NULL != (arg = va_arg (ap, char *)))
708 cmdlen = cmdlen + strlen (path) + 3;
710 cmdlen = cmdlen + strlen (arg) + 3;
714 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
716 while (NULL != (arg = va_arg (ap, char *)))
719 idx += sprintf (idx, "\"%s\" ", path);
721 idx += sprintf (idx, "\"%s\" ", arg);
725 memset (&start, 0, sizeof (start));
726 start.cb = sizeof (start);
728 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
729 start.dwFlags |= STARTF_USESTDHANDLES;
731 if (pipe_stdin != NULL)
733 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
734 start.hStdInput = stdin_handle;
737 if (pipe_stdout != NULL)
739 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
740 start.hStdOutput = stdout_handle;
743 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
744 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
745 GNUNET_DISK_PERM_USER_WRITE);
746 if (control_pipe == NULL)
753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
754 "Opened the parent end of the pipe `%s'\n",
757 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
758 GNUNET_asprintf (&our_env[1], "%s", childpipename);
760 env_block = CreateCustomEnvTable (our_env);
761 GNUNET_free (our_env[0]);
762 GNUNET_free (our_env[1]);
765 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
766 env_block, NULL, &start, &proc))
768 SetErrnoFromWinError (GetLastError ());
769 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
770 GNUNET_free (env_block);
775 GNUNET_free (env_block);
777 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
778 gnunet_proc->pid = proc.dwProcessId;
779 gnunet_proc->handle = proc.hProcess;
780 gnunet_proc->control_pipe = control_pipe;
782 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
784 ResumeThread (proc.hThread);
785 CloseHandle (proc.hThread);
797 * @param pipe_stdin pipe to use to send input to child process (or NULL)
798 * @param pipe_stdout pipe to use to get output from child process (or NULL)
799 * @param filename name of the binary
800 * @param ... NULL-terminated list of arguments to the process
802 * @return pointer to process structure of the new process, NULL on error
805 struct GNUNET_OS_Process *
806 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
807 struct GNUNET_DISK_PipeHandle *pipe_stdout,
808 const char *filename, ...)
810 struct GNUNET_OS_Process *ret;
813 va_start (ap, filename);
814 ret = GNUNET_OS_start_process_va (pipe_stdin,
826 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
827 * must be NULL on platforms where dup is not supported
828 * @param filename name of the binary
829 * @param argv NULL-terminated list of arguments to the process
830 * @return process ID of the new process, -1 on error
832 struct GNUNET_OS_Process *
833 GNUNET_OS_start_process_v (const int *lsocks,
834 const char *filename, char *const argv[])
836 #if ENABLE_WINDOWS_WORKAROUNDS
837 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
838 char *childpipename = NULL;
845 struct GNUNET_OS_Process *gnunet_proc = NULL;
854 #if ENABLE_WINDOWS_WORKAROUNDS
855 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
856 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
857 GNUNET_DISK_PERM_USER_WRITE);
858 if (control_pipe == NULL)
867 while (-1 != (k = lsocks[i++]))
868 GNUNET_array_append (lscp, ls, k);
869 GNUNET_array_append (lscp, ls, -1);
871 #if HAVE_WORKING_VFORK
880 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
881 #if ENABLE_WINDOWS_WORKAROUNDS
882 GNUNET_DISK_npipe_close (control_pipe);
887 #if HAVE_WORKING_VFORK
888 /* let's hope vfork actually works; for some extreme cases (including
889 a testcase) we need 'execvp' to have run before we return, since
890 we may send a signal to the process next and we don't want it
891 to be caught by OUR signal handler (but either by the default
892 handler or the actual handler as installed by the process itself). */
894 /* let's give the child process a chance to run execvp, 1s should
895 be plenty in practice */
898 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
899 gnunet_proc->pid = ret;
900 #if ENABLE_WINDOWS_WORKAROUNDS
901 gnunet_proc->control_pipe = control_pipe;
905 GNUNET_array_grow (lscp, ls, 0);
906 #if ENABLE_WINDOWS_WORKAROUNDS
907 GNUNET_free (childpipename);
912 #if ENABLE_WINDOWS_WORKAROUNDS
913 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
914 GNUNET_free (childpipename);
919 /* read systemd documentation... */
920 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
921 setenv ("LISTEN_PID", lpid, 1);
924 while (-1 != lscp[i])
927 while (-1 != lscp[j])
933 GNUNET_assert (-1 != k);
934 GNUNET_assert (0 == close (lscp[j]));
942 /* Bury any existing FD, no matter what; they should all be closed
943 on exec anyway and the important onces have been dup'ed away */
945 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
947 /* unset close-on-exec flag */
948 flags = fcntl (tgt, F_GETFD);
949 GNUNET_assert (flags >= 0);
950 flags &= ~FD_CLOEXEC;
952 (void) fcntl (tgt, F_SETFD, flags);
956 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
957 setenv ("LISTEN_FDS", fds, 1);
959 GNUNET_array_grow (lscp, ls, 0);
960 execvp (filename, argv);
961 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
964 char **arg, **non_const_argv;
968 PROCESS_INFORMATION proc;
970 struct GNUNET_OS_Process *gnunet_proc = NULL;
972 char path[MAX_PATH + 1];
974 char *our_env[3] = { NULL, NULL, NULL };
975 char *env_block = NULL;
977 DWORD pathbuf_len, alloc_len;
982 char *non_const_filename;
984 GNUNET_assert (lsocks == NULL);
986 /* Search in prefix dir (hopefully - the directory from which
987 * the current module was loaded), bindir and libdir, then in PATH
989 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
990 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
991 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
993 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
995 alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir);
997 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1000 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1001 GNUNET_free (self_prefix);
1002 GNUNET_free (bindir);
1003 GNUNET_free (libdir);
1005 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1006 if (alloc_len != pathbuf_len - 1)
1008 GNUNET_free (pathbuf);
1009 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1013 cmdlen = strlen (filename);
1014 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1015 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1017 GNUNET_asprintf (&non_const_filename, "%s", filename);
1019 /* Check that this is the full path. If it isn't, search. */
1020 if (non_const_filename[1] == ':')
1021 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1022 else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL))
1024 SetErrnoFromWinError (GetLastError ());
1025 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename);
1026 GNUNET_free (non_const_filename);
1027 GNUNET_free (pathbuf);
1030 GNUNET_free (pathbuf);
1031 GNUNET_free (non_const_filename);
1033 /* Count the number of arguments */
1034 arg = (char **) argv;
1041 /* Allocate a copy argv */
1042 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1044 /* Copy all argv strings */
1046 arg = (char **) argv;
1050 non_const_argv[argcount] = GNUNET_strdup (path);
1052 non_const_argv[argcount] = GNUNET_strdup (*arg);
1056 non_const_argv[argcount] = NULL;
1060 arg = non_const_argv;
1063 cmdlen = cmdlen + strlen (*arg) + 3;
1067 /* Allocate and create cmd */
1068 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1069 arg = non_const_argv;
1072 idx += sprintf (idx, "\"%s\" ", *arg);
1076 while (argcount > 0)
1077 GNUNET_free (non_const_argv[--argcount]);
1078 GNUNET_free (non_const_argv);
1080 memset (&start, 0, sizeof (start));
1081 start.cb = sizeof (start);
1083 control_pipe = GNUNET_DISK_npipe_create (&childpipename,
1084 GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ |
1085 GNUNET_DISK_PERM_USER_WRITE);
1086 if (control_pipe == NULL)
1093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename);
1095 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1096 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1098 env_block = CreateCustomEnvTable (our_env);
1099 GNUNET_free (our_env[0]);
1100 GNUNET_free (our_env[1]);
1103 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED,
1104 env_block, NULL, &start, &proc))
1106 SetErrnoFromWinError (GetLastError ());
1107 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1108 GNUNET_free (env_block);
1113 GNUNET_free (env_block);
1115 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1116 gnunet_proc->pid = proc.dwProcessId;
1117 gnunet_proc->handle = proc.hProcess;
1118 gnunet_proc->control_pipe = control_pipe;
1120 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
1122 ResumeThread (proc.hThread);
1123 CloseHandle (proc.hThread);
1132 * Retrieve the status of a process
1133 * @param proc process ID
1134 * @param type status type
1135 * @param code return code/signal number
1136 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1139 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1140 enum GNUNET_OS_ProcessStatusType *type,
1141 unsigned long *code)
1147 GNUNET_assert (0 != proc);
1148 ret = waitpid (proc->pid, &status, WNOHANG);
1151 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1152 return GNUNET_SYSERR;
1156 *type = GNUNET_OS_PROCESS_RUNNING;
1160 if (proc->pid != ret)
1162 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1163 return GNUNET_SYSERR;
1165 if (WIFEXITED (status))
1167 *type = GNUNET_OS_PROCESS_EXITED;
1168 *code = WEXITSTATUS (status);
1170 else if (WIFSIGNALED (status))
1172 *type = GNUNET_OS_PROCESS_SIGNALED;
1173 *code = WTERMSIG (status);
1175 else if (WIFSTOPPED (status))
1177 *type = GNUNET_OS_PROCESS_SIGNALED;
1178 *code = WSTOPSIG (status);
1181 else if (WIFCONTINUED (status))
1183 *type = GNUNET_OS_PROCESS_RUNNING;
1189 *type = GNUNET_OS_PROCESS_UNKNOWN;
1194 DWORD c, error_code, ret;
1198 if (h == NULL || ret == 0)
1200 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
1201 return GNUNET_SYSERR;
1204 h = GetCurrentProcess ();
1207 ret = GetExitCodeProcess (h, &c);
1208 error_code = GetLastError ();
1209 if (ret == 0 || error_code != NO_ERROR)
1211 SetErrnoFromWinError (error_code);
1212 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1213 return GNUNET_SYSERR;
1215 if (STILL_ACTIVE == c)
1217 *type = GNUNET_OS_PROCESS_RUNNING;
1221 *type = GNUNET_OS_PROCESS_EXITED;
1229 * Wait for a process
1230 * @param proc pointer to process structure
1231 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1234 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1238 pid_t pid = proc->pid;
1239 if (pid != waitpid (pid, NULL, 0))
1240 return GNUNET_SYSERR;
1249 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1250 "Invalid process information {%d, %08X}\n",
1253 return GNUNET_SYSERR;
1256 h = GetCurrentProcess ();
1258 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1260 SetErrnoFromWinError (GetLastError ());
1261 ret = GNUNET_SYSERR;
1272 * Handle to a command.
1274 struct GNUNET_OS_CommandHandle
1280 struct GNUNET_OS_Process *eip;
1283 * Handle to the output pipe.
1285 struct GNUNET_DISK_PipeHandle *opipe;
1288 * Read-end of output pipe.
1290 const struct GNUNET_DISK_FileHandle *r;
1293 * Function to call on each line of output.
1295 GNUNET_OS_LineProcessor proc;
1298 * Closure for 'proc'.
1303 * Buffer for the output.
1308 * Task reading from pipe.
1310 GNUNET_SCHEDULER_TaskIdentifier rtask;
1315 struct GNUNET_TIME_Absolute timeout;
1318 * Current read offset in buf.
1325 * Stop/kill a command. Must ONLY be called either from
1326 * the callback after 'NULL' was passed for 'line' *OR*
1327 * from an independent task (not within the line processor).
1329 * @param cmd handle to the process
1332 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1335 if (cmd->proc != NULL)
1337 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1338 GNUNET_SCHEDULER_cancel (cmd->rtask);
1340 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1341 GNUNET_break (GNUNET_OK ==
1342 GNUNET_OS_process_wait (cmd->eip));
1343 GNUNET_OS_process_close (cmd->eip);
1344 GNUNET_DISK_pipe_close (cmd->opipe);
1350 * Read from the process and call the line processor.
1352 * @param cls the 'struct GNUNET_OS_CommandHandle'
1353 * @param tc scheduler context
1356 cmd_read (void *cls,
1357 const struct GNUNET_SCHEDULER_TaskContext *tc)
1359 struct GNUNET_OS_CommandHandle *cmd = cls;
1360 GNUNET_OS_LineProcessor proc;
1364 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1366 GNUNET_NETWORK_fdset_handle_isset (tc->read_ready,
1369 /* timeout, shutdown, etc. */
1372 proc (cmd->proc_cls, NULL);
1375 ret = GNUNET_DISK_file_read (cmd->r,
1376 &cmd->buf[cmd->off],
1377 sizeof (cmd->buf)-cmd->off);
1380 if ( (cmd->off > 0) && (cmd->off < sizeof (cmd->buf)) )
1382 cmd->buf[cmd->off] = '\0';
1383 cmd->proc (cmd->proc_cls, cmd->buf);
1387 proc (cmd->proc_cls, NULL);
1390 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1395 cmd->proc (cmd->proc_cls, cmd->buf);
1398 cmd->off - (end + 1 - cmd->buf));
1399 cmd->off -= (end + 1 - cmd->buf);
1400 end = memchr (cmd->buf, '\n', cmd->off);
1402 cmd->rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (cmd->timeout),
1410 * Run the given command line and call the given function
1411 * for each line of the output.
1413 * @param proc function to call for each line of the output
1414 * @param proc_cls closure for proc
1415 * @param timeout when to time out
1416 * @param binary command to run
1417 * @param ... arguments to command
1418 * @return NULL on error
1420 struct GNUNET_OS_CommandHandle *
1421 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1423 struct GNUNET_TIME_Relative timeout,
1427 struct GNUNET_OS_CommandHandle *cmd;
1428 struct GNUNET_OS_Process *eip;
1429 struct GNUNET_DISK_PipeHandle *opipe;
1432 opipe = GNUNET_DISK_pipe (GNUNET_YES,
1437 va_start (ap, binary);
1438 eip = GNUNET_OS_start_process_va (NULL, opipe,
1443 GNUNET_DISK_pipe_close (opipe);
1446 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1447 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1448 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1452 cmd->proc_cls = proc_cls;
1453 cmd->r = GNUNET_DISK_pipe_handle (opipe,
1454 GNUNET_DISK_PIPE_END_READ);
1455 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout,
1465 /* end of os_priority.c */