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)
66 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
67 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
69 /* convert to MINGW/Unix values */
72 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
74 rprio = NORMAL_PRIORITY_CLASS;
79 case GNUNET_SCHEDULER_PRIORITY_HIGH:
81 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
86 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
88 rprio = BELOW_NORMAL_PRIORITY_CLASS;
93 case GNUNET_SCHEDULER_PRIORITY_UI:
94 case GNUNET_SCHEDULER_PRIORITY_URGENT:
96 rprio = HIGH_PRIORITY_CLASS;
101 case GNUNET_SCHEDULER_PRIORITY_IDLE:
103 rprio = IDLE_PRIORITY_CLASS;
110 return GNUNET_SYSERR;
112 /* Set process priority */
114 SetPriorityClass (GetCurrentProcess (), rprio);
116 if (proc == getpid ())
119 if ((-1 == nice (rprio)) && (errno != 0))
121 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
122 GNUNET_ERROR_TYPE_BULK, "nice");
123 return GNUNET_SYSERR;
128 if (0 != setpriority (PRIO_PROCESS, proc, rprio))
131 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
132 GNUNET_ERROR_TYPE_BULK, "setpriority");
133 return GNUNET_SYSERR;
143 * @param pipe_stdin pipe to use to send input to child process (or NULL)
144 * @param pipe_stdout pipe to use to get output from child process (or NULL)
145 * @param filename name of the binary
146 * @param ... NULL-terminated list of arguments to the process
147 * @return process ID of the new process, -1 on error
150 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
151 struct GNUNET_DISK_PipeHandle *pipe_stdout,
152 const char *filename, ...)
154 /* FIXME: Make this work on windows!!! */
167 va_start (ap, filename);
168 while (NULL != va_arg (ap, char *))
171 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
173 va_start (ap, filename);
174 while (NULL != (argv[argc] = va_arg (ap, char *)))
177 if (pipe_stdout != NULL)
179 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
180 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
182 if (pipe_stdin != NULL)
184 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
185 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
188 #if HAVE_WORKING_VFORK
197 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
202 #if HAVE_WORKING_VFORK
203 /* let's hope vfork actually works; for some extreme cases (including
204 a testcase) we need 'execvp' to have run before we return, since
205 we may send a signal to the process next and we don't want it
206 to be caught by OUR signal handler (but either by the default
207 handler or the actual handler as installed by the process itself). */
209 /* let's give the child process a chance to run execvp, 1s should
210 be plenty in practice */
211 if (pipe_stdout != NULL)
212 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
213 if (pipe_stdin != NULL)
214 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
222 if (pipe_stdout != NULL)
224 dup2(fd_stdout_write, 1);
225 close (fd_stdout_write);
226 close (fd_stdout_read);
229 if (pipe_stdin != NULL)
232 dup2(fd_stdin_read, 0);
233 close (fd_stdin_read);
234 close (fd_stdin_write);
237 execvp (filename, argv);
238 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
245 PROCESS_INFORMATION proc;
248 HANDLE stdout_handle;
252 char path[MAX_PATH + 1];
255 va_start (ap, filename);
256 while (NULL != (arg = va_arg (ap, char *)))
257 cmdlen = cmdlen + strlen (arg) + 3;
260 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
261 va_start (ap, filename);
262 while (NULL != (arg = va_arg (ap, char *)))
263 idx += sprintf (idx, "\"%s\" ", arg);
266 memset (&start, 0, sizeof (start));
267 start.cb = sizeof (start);
270 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
271 start.dwFlags |= STARTF_USESTDHANDLES;
273 if (pipe_stdin != NULL)
275 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
276 start.hStdInput = stdin_handle;
279 if (pipe_stdout != NULL)
281 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
282 start.hStdOutput = stdout_handle;
285 if (FindExecutable(filename, NULL, path) <= 32)
287 SetErrnoFromWinError (GetLastError ());
288 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", fn);
293 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
296 SetErrnoFromWinError (GetLastError ());
297 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", fn);
301 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
305 CloseHandle (proc.hThread);
309 return proc.dwProcessId;
319 * @param filename name of the binary
320 * @param argv NULL-terminated list of arguments to the process
321 * @return process ID of the new process, -1 on error
324 GNUNET_OS_start_process_v (const char *filename, char *const argv[])
329 #if HAVE_WORKING_VFORK
338 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
342 #if HAVE_WORKING_VFORK
343 /* let's hope vfork actually works; for some extreme cases (including
344 a testcase) we need 'execvp' to have run before we return, since
345 we may send a signal to the process next and we don't want it
346 to be caught by OUR signal handler (but either by the default
347 handler or the actual handler as installed by the process itself). */
349 /* let's give the child process a chance to run execvp, 1s should
350 be plenty in practice */
356 execvp (filename, argv);
357 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
364 PROCESS_INFORMATION proc;
370 cmdlen = cmdlen + strlen (*arg) + 3;
374 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
378 idx += sprintf (idx, "\"%s\" ", *arg);
382 memset (&start, 0, sizeof (start));
383 start.cb = sizeof (start);
386 (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
389 SetErrnoFromWinError (GetLastError ());
390 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
394 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
396 CloseHandle (proc.hThread);
399 return proc.dwProcessId;
404 * Retrieve the status of a process
405 * @param proc process ID
406 * @param type status type
407 * @param code return code/signal number
408 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
411 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
418 GNUNET_assert (0 != proc);
419 ret = waitpid (proc, &status, WNOHANG);
422 *type = GNUNET_OS_PROCESS_RUNNING;
428 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
429 return GNUNET_SYSERR;
431 if (WIFEXITED (status))
433 *type = GNUNET_OS_PROCESS_EXITED;
434 *code = WEXITSTATUS (status);
436 else if (WIFSIGNALED (status))
438 *type = GNUNET_OS_PROCESS_SIGNALED;
439 *code = WTERMSIG (status);
441 else if (WIFSTOPPED (status))
443 *type = GNUNET_OS_PROCESS_SIGNALED;
444 *code = WSTOPSIG (status);
447 else if (WIFCONTINUED (status))
449 *type = GNUNET_OS_PROCESS_RUNNING;
455 *type = GNUNET_OS_PROCESS_UNKNOWN;
462 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
463 if (INVALID_HANDLE_VALUE == h)
465 SetErrnoFromWinError (GetLastError ());
466 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
467 return GNUNET_SYSERR;
470 c = GetExitCodeProcess (proc, &c);
471 if (STILL_ACTIVE == c)
473 *type = GNUNET_OS_PROCESS_RUNNING;
478 *type = GNUNET_OS_PROCESS_EXITED;
488 * @param proc process ID to wait for
489 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
492 GNUNET_OS_process_wait (pid_t proc)
495 if (proc != waitpid (proc, NULL, 0))
496 return GNUNET_SYSERR;
504 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
505 if (INVALID_HANDLE_VALUE == h)
507 SetErrnoFromWinError (GetLastError ());
508 return GNUNET_SYSERR;
511 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
513 SetErrnoFromWinError (GetLastError ());
526 /* end of os_priority.c */