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_UI:
73 case GNUNET_SCHEDULER_PRIORITY_URGENT:
75 rprio = HIGH_PRIORITY_CLASS;
81 case GNUNET_SCHEDULER_PRIORITY_HIGH:
83 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
89 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
91 rprio = NORMAL_PRIORITY_CLASS;
97 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
99 rprio = BELOW_NORMAL_PRIORITY_CLASS;
105 case GNUNET_SCHEDULER_PRIORITY_IDLE:
107 rprio = IDLE_PRIORITY_CLASS;
114 return GNUNET_SYSERR;
116 /* Set process priority */
118 SetPriorityClass (GetCurrentProcess (), rprio);
121 (proc == getpid () ) )
124 int delta = rprio - have;
127 (rprio == nice (delta)) &&
130 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
131 GNUNET_ERROR_TYPE_BULK, "nice");
132 return GNUNET_SYSERR;
137 if (0 != setpriority (PRIO_PROCESS, proc, rprio))
140 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
141 GNUNET_ERROR_TYPE_BULK, "setpriority");
142 return GNUNET_SYSERR;
146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
147 "Priority management not availabe for this platform\n");
155 * @param pipe_stdin pipe to use to send input to child process (or NULL)
156 * @param pipe_stdout pipe to use to get output from child process (or NULL)
157 * @param filename name of the binary
158 * @param ... NULL-terminated list of arguments to the process
159 * @return process ID of the new process, -1 on error
162 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
163 struct GNUNET_DISK_PipeHandle *pipe_stdout,
164 const char *filename, ...)
166 /* FIXME: Make this work on windows!!! */
179 va_start (ap, filename);
180 while (NULL != va_arg (ap, char *))
183 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
185 va_start (ap, filename);
186 while (NULL != (argv[argc] = va_arg (ap, char *)))
189 if (pipe_stdout != NULL)
191 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
192 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
194 if (pipe_stdin != NULL)
196 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
197 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
200 #if HAVE_WORKING_VFORK
209 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
214 #if HAVE_WORKING_VFORK
215 /* let's hope vfork actually works; for some extreme cases (including
216 a testcase) we need 'execvp' to have run before we return, since
217 we may send a signal to the process next and we don't want it
218 to be caught by OUR signal handler (but either by the default
219 handler or the actual handler as installed by the process itself). */
221 /* let's give the child process a chance to run execvp, 1s should
222 be plenty in practice */
223 if (pipe_stdout != NULL)
224 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
225 if (pipe_stdin != NULL)
226 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
234 if (pipe_stdout != NULL)
236 dup2(fd_stdout_write, 1);
237 close (fd_stdout_write);
238 close (fd_stdout_read);
241 if (pipe_stdin != NULL)
244 dup2(fd_stdin_read, 0);
245 close (fd_stdin_read);
246 close (fd_stdin_write);
249 execvp (filename, argv);
250 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
257 PROCESS_INFORMATION proc;
260 HANDLE stdout_handle;
264 char path[MAX_PATH + 1];
267 va_start (ap, filename);
268 while (NULL != (arg = va_arg (ap, char *)))
269 cmdlen = cmdlen + strlen (arg) + 3;
272 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
273 va_start (ap, filename);
274 while (NULL != (arg = va_arg (ap, char *)))
275 idx += sprintf (idx, "\"%s\" ", arg);
278 memset (&start, 0, sizeof (start));
279 start.cb = sizeof (start);
282 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
283 start.dwFlags |= STARTF_USESTDHANDLES;
285 if (pipe_stdin != NULL)
287 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
288 start.hStdInput = stdin_handle;
291 if (pipe_stdout != NULL)
293 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
294 start.hStdOutput = stdout_handle;
297 if (FindExecutable(filename, NULL, path) <= 32)
299 SetErrnoFromWinError (GetLastError ());
300 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", fn);
305 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
308 SetErrnoFromWinError (GetLastError ());
309 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", fn);
313 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
317 CloseHandle (proc.hThread);
321 return proc.dwProcessId;
331 * @param filename name of the binary
332 * @param argv NULL-terminated list of arguments to the process
333 * @return process ID of the new process, -1 on error
336 GNUNET_OS_start_process_v (const char *filename, char *const argv[])
341 #if HAVE_WORKING_VFORK
350 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
354 #if HAVE_WORKING_VFORK
355 /* let's hope vfork actually works; for some extreme cases (including
356 a testcase) we need 'execvp' to have run before we return, since
357 we may send a signal to the process next and we don't want it
358 to be caught by OUR signal handler (but either by the default
359 handler or the actual handler as installed by the process itself). */
361 /* let's give the child process a chance to run execvp, 1s should
362 be plenty in practice */
368 execvp (filename, argv);
369 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
376 PROCESS_INFORMATION proc;
382 cmdlen = cmdlen + strlen (*arg) + 3;
386 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
390 idx += sprintf (idx, "\"%s\" ", *arg);
394 memset (&start, 0, sizeof (start));
395 start.cb = sizeof (start);
398 (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
401 SetErrnoFromWinError (GetLastError ());
402 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
406 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
408 CloseHandle (proc.hThread);
411 return proc.dwProcessId;
416 * Retrieve the status of a process
417 * @param proc process ID
418 * @param type status type
419 * @param code return code/signal number
420 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
423 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
430 GNUNET_assert (0 != proc);
431 ret = waitpid (proc, &status, WNOHANG);
434 *type = GNUNET_OS_PROCESS_RUNNING;
440 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
441 return GNUNET_SYSERR;
443 if (WIFEXITED (status))
445 *type = GNUNET_OS_PROCESS_EXITED;
446 *code = WEXITSTATUS (status);
448 else if (WIFSIGNALED (status))
450 *type = GNUNET_OS_PROCESS_SIGNALED;
451 *code = WTERMSIG (status);
453 else if (WIFSTOPPED (status))
455 *type = GNUNET_OS_PROCESS_SIGNALED;
456 *code = WSTOPSIG (status);
459 else if (WIFCONTINUED (status))
461 *type = GNUNET_OS_PROCESS_RUNNING;
467 *type = GNUNET_OS_PROCESS_UNKNOWN;
474 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
475 if (INVALID_HANDLE_VALUE == h)
477 SetErrnoFromWinError (GetLastError ());
478 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
479 return GNUNET_SYSERR;
482 c = GetExitCodeProcess (proc, &c);
483 if (STILL_ACTIVE == c)
485 *type = GNUNET_OS_PROCESS_RUNNING;
490 *type = GNUNET_OS_PROCESS_EXITED;
500 * @param proc process ID to wait for
501 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
504 GNUNET_OS_process_wait (pid_t proc)
507 if (proc != waitpid (proc, NULL, 0))
508 return GNUNET_SYSERR;
516 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
517 if (INVALID_HANDLE_VALUE == h)
519 SetErrnoFromWinError (GetLastError ());
520 return GNUNET_SYSERR;
523 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
525 SetErrnoFromWinError (GetLastError ());
538 /* end of os_priority.c */