2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006 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"
33 #include "gnunet_signal_lib.h"
35 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
38 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
39 * @param h handle to the process
42 ChildWaitThread (HANDLE h)
44 WaitForSingleObject (h, INFINITE);
46 if (w32_sigchld_handler)
47 w32_sigchld_handler ();
54 * Set process priority
56 * @param proc id of the process
57 * @param prio priority value
58 * @return GNUNET_OK on success, GNUNET_SYSERR on error
61 GNUNET_OS_set_process_priority (pid_t proc,
62 enum GNUNET_SCHEDULER_Priority prio)
68 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
69 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
71 /* convert to MINGW/Unix values */
74 case GNUNET_SCHEDULER_PRIORITY_UI:
75 case GNUNET_SCHEDULER_PRIORITY_URGENT:
77 rprio = HIGH_PRIORITY_CLASS;
83 case GNUNET_SCHEDULER_PRIORITY_HIGH:
85 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
91 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
93 rprio = NORMAL_PRIORITY_CLASS;
99 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
101 rprio = BELOW_NORMAL_PRIORITY_CLASS;
107 case GNUNET_SCHEDULER_PRIORITY_IDLE:
109 rprio = IDLE_PRIORITY_CLASS;
116 return GNUNET_SYSERR;
118 /* Set process priority */
120 SetPriorityClass (GetCurrentProcess (), rprio);
123 (proc == getpid () ) )
126 delta = rprio - have;
129 (rprio == nice (delta)) &&
132 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
133 GNUNET_ERROR_TYPE_BULK, "nice");
134 return GNUNET_SYSERR;
139 if (0 != setpriority (PRIO_PROCESS, proc, rprio))
142 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
143 GNUNET_ERROR_TYPE_BULK, "setpriority");
144 return GNUNET_SYSERR;
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
149 "Priority management not availabe for this platform\n");
157 * @param pipe_stdin pipe to use to send input to child process (or NULL)
158 * @param pipe_stdout pipe to use to get output from child process (or NULL)
159 * @param filename name of the binary
160 * @param ... NULL-terminated list of arguments to the process
161 * @return process ID of the new process, -1 on error
164 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
165 struct GNUNET_DISK_PipeHandle *pipe_stdout,
166 const char *filename, ...)
168 /* FIXME: Make this work on windows!!! */
181 va_start (ap, filename);
182 while (NULL != va_arg (ap, char *))
185 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
187 va_start (ap, filename);
188 while (NULL != (argv[argc] = va_arg (ap, char *)))
191 if (pipe_stdout != NULL)
193 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
194 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
196 if (pipe_stdin != NULL)
198 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
199 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
202 #if HAVE_WORKING_VFORK
211 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
216 #if HAVE_WORKING_VFORK
217 /* let's hope vfork actually works; for some extreme cases (including
218 a testcase) we need 'execvp' to have run before we return, since
219 we may send a signal to the process next and we don't want it
220 to be caught by OUR signal handler (but either by the default
221 handler or the actual handler as installed by the process itself). */
223 /* let's give the child process a chance to run execvp, 1s should
224 be plenty in practice */
225 if (pipe_stdout != NULL)
226 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
227 if (pipe_stdin != NULL)
228 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
236 if (pipe_stdout != NULL)
238 dup2(fd_stdout_write, 1);
239 close (fd_stdout_write);
240 close (fd_stdout_read);
243 if (pipe_stdin != NULL)
246 dup2(fd_stdin_read, 0);
247 close (fd_stdin_read);
248 close (fd_stdin_write);
251 execvp (filename, argv);
252 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
259 PROCESS_INFORMATION proc;
262 HANDLE stdout_handle;
266 char path[MAX_PATH + 1];
269 va_start (ap, filename);
270 while (NULL != (arg = va_arg (ap, char *)))
271 cmdlen = cmdlen + strlen (arg) + 3;
274 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
275 va_start (ap, filename);
276 while (NULL != (arg = va_arg (ap, char *)))
277 idx += sprintf (idx, "\"%s\" ", arg);
280 memset (&start, 0, sizeof (start));
281 start.cb = sizeof (start);
284 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
285 start.dwFlags |= STARTF_USESTDHANDLES;
287 if (pipe_stdin != NULL)
289 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
290 start.hStdInput = stdin_handle;
293 if (pipe_stdout != NULL)
295 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
296 start.hStdOutput = stdout_handle;
299 if (FindExecutable(filename, NULL, path) <= 32)
301 SetErrnoFromWinError (GetLastError ());
302 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", fn);
307 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
310 SetErrnoFromWinError (GetLastError ());
311 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", fn);
315 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
319 CloseHandle (proc.hThread);
323 return proc.dwProcessId;
333 * @param filename name of the binary
334 * @param argv NULL-terminated list of arguments to the process
335 * @return process ID of the new process, -1 on error
338 GNUNET_OS_start_process_v (const char *filename, char *const argv[])
343 #if HAVE_WORKING_VFORK
352 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
356 #if HAVE_WORKING_VFORK
357 /* let's hope vfork actually works; for some extreme cases (including
358 a testcase) we need 'execvp' to have run before we return, since
359 we may send a signal to the process next and we don't want it
360 to be caught by OUR signal handler (but either by the default
361 handler or the actual handler as installed by the process itself). */
363 /* let's give the child process a chance to run execvp, 1s should
364 be plenty in practice */
370 execvp (filename, argv);
371 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
378 PROCESS_INFORMATION proc;
384 cmdlen = cmdlen + strlen (*arg) + 3;
388 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
392 idx += sprintf (idx, "\"%s\" ", *arg);
396 memset (&start, 0, sizeof (start));
397 start.cb = sizeof (start);
400 (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
403 SetErrnoFromWinError (GetLastError ());
404 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
408 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
410 CloseHandle (proc.hThread);
413 return proc.dwProcessId;
418 * Retrieve the status of a process
419 * @param proc process ID
420 * @param type status type
421 * @param code return code/signal number
422 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
425 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
432 GNUNET_assert (0 != proc);
433 ret = waitpid (proc, &status, WNOHANG);
436 *type = GNUNET_OS_PROCESS_RUNNING;
442 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
443 return GNUNET_SYSERR;
445 if (WIFEXITED (status))
447 *type = GNUNET_OS_PROCESS_EXITED;
448 *code = WEXITSTATUS (status);
450 else if (WIFSIGNALED (status))
452 *type = GNUNET_OS_PROCESS_SIGNALED;
453 *code = WTERMSIG (status);
455 else if (WIFSTOPPED (status))
457 *type = GNUNET_OS_PROCESS_SIGNALED;
458 *code = WSTOPSIG (status);
461 else if (WIFCONTINUED (status))
463 *type = GNUNET_OS_PROCESS_RUNNING;
469 *type = GNUNET_OS_PROCESS_UNKNOWN;
476 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
477 if (INVALID_HANDLE_VALUE == h)
479 SetErrnoFromWinError (GetLastError ());
480 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
481 return GNUNET_SYSERR;
484 c = GetExitCodeProcess (proc, &c);
485 if (STILL_ACTIVE == c)
487 *type = GNUNET_OS_PROCESS_RUNNING;
492 *type = GNUNET_OS_PROCESS_EXITED;
502 * @param proc process ID to wait for
503 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
506 GNUNET_OS_process_wait (pid_t proc)
509 if (proc != waitpid (proc, NULL, 0))
510 return GNUNET_SYSERR;
518 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
519 if (INVALID_HANDLE_VALUE == h)
521 SetErrnoFromWinError (GetLastError ());
522 return GNUNET_SYSERR;
525 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
527 SetErrnoFromWinError (GetLastError ());
540 /* end of os_priority.c */