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 (-1 == 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;
154 * @param pipe_stdin pipe to use to send input to child process (or NULL)
155 * @param pipe_stdout pipe to use to get output from child process (or NULL)
156 * @param filename name of the binary
157 * @param ... NULL-terminated list of arguments to the process
158 * @return process ID of the new process, -1 on error
161 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
162 struct GNUNET_DISK_PipeHandle *pipe_stdout,
163 const char *filename, ...)
165 /* FIXME: Make this work on windows!!! */
178 va_start (ap, filename);
179 while (NULL != va_arg (ap, char *))
182 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
184 va_start (ap, filename);
185 while (NULL != (argv[argc] = va_arg (ap, char *)))
188 if (pipe_stdout != NULL)
190 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
191 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
193 if (pipe_stdin != NULL)
195 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
196 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
199 #if HAVE_WORKING_VFORK
208 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
213 #if HAVE_WORKING_VFORK
214 /* let's hope vfork actually works; for some extreme cases (including
215 a testcase) we need 'execvp' to have run before we return, since
216 we may send a signal to the process next and we don't want it
217 to be caught by OUR signal handler (but either by the default
218 handler or the actual handler as installed by the process itself). */
220 /* let's give the child process a chance to run execvp, 1s should
221 be plenty in practice */
222 if (pipe_stdout != NULL)
223 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
224 if (pipe_stdin != NULL)
225 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
233 if (pipe_stdout != NULL)
235 dup2(fd_stdout_write, 1);
236 close (fd_stdout_write);
237 close (fd_stdout_read);
240 if (pipe_stdin != NULL)
243 dup2(fd_stdin_read, 0);
244 close (fd_stdin_read);
245 close (fd_stdin_write);
248 execvp (filename, argv);
249 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
256 PROCESS_INFORMATION proc;
259 HANDLE stdout_handle;
263 char path[MAX_PATH + 1];
266 va_start (ap, filename);
267 while (NULL != (arg = va_arg (ap, char *)))
268 cmdlen = cmdlen + strlen (arg) + 3;
271 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
272 va_start (ap, filename);
273 while (NULL != (arg = va_arg (ap, char *)))
274 idx += sprintf (idx, "\"%s\" ", arg);
277 memset (&start, 0, sizeof (start));
278 start.cb = sizeof (start);
281 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
282 start.dwFlags |= STARTF_USESTDHANDLES;
284 if (pipe_stdin != NULL)
286 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
287 start.hStdInput = stdin_handle;
290 if (pipe_stdout != NULL)
292 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
293 start.hStdOutput = stdout_handle;
296 if (FindExecutable(filename, NULL, path) <= 32)
298 SetErrnoFromWinError (GetLastError ());
299 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", fn);
304 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
307 SetErrnoFromWinError (GetLastError ());
308 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", fn);
312 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
316 CloseHandle (proc.hThread);
320 return proc.dwProcessId;
330 * @param filename name of the binary
331 * @param argv NULL-terminated list of arguments to the process
332 * @return process ID of the new process, -1 on error
335 GNUNET_OS_start_process_v (const char *filename, char *const argv[])
340 #if HAVE_WORKING_VFORK
349 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
353 #if HAVE_WORKING_VFORK
354 /* let's hope vfork actually works; for some extreme cases (including
355 a testcase) we need 'execvp' to have run before we return, since
356 we may send a signal to the process next and we don't want it
357 to be caught by OUR signal handler (but either by the default
358 handler or the actual handler as installed by the process itself). */
360 /* let's give the child process a chance to run execvp, 1s should
361 be plenty in practice */
367 execvp (filename, argv);
368 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
375 PROCESS_INFORMATION proc;
381 cmdlen = cmdlen + strlen (*arg) + 3;
385 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
389 idx += sprintf (idx, "\"%s\" ", *arg);
393 memset (&start, 0, sizeof (start));
394 start.cb = sizeof (start);
397 (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
400 SetErrnoFromWinError (GetLastError ());
401 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
405 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
407 CloseHandle (proc.hThread);
410 return proc.dwProcessId;
415 * Retrieve the status of a process
416 * @param proc process ID
417 * @param type status type
418 * @param code return code/signal number
419 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
422 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
429 GNUNET_assert (0 != proc);
430 ret = waitpid (proc, &status, WNOHANG);
433 *type = GNUNET_OS_PROCESS_RUNNING;
439 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
440 return GNUNET_SYSERR;
442 if (WIFEXITED (status))
444 *type = GNUNET_OS_PROCESS_EXITED;
445 *code = WEXITSTATUS (status);
447 else if (WIFSIGNALED (status))
449 *type = GNUNET_OS_PROCESS_SIGNALED;
450 *code = WTERMSIG (status);
452 else if (WIFSTOPPED (status))
454 *type = GNUNET_OS_PROCESS_SIGNALED;
455 *code = WSTOPSIG (status);
458 else if (WIFCONTINUED (status))
460 *type = GNUNET_OS_PROCESS_RUNNING;
466 *type = GNUNET_OS_PROCESS_UNKNOWN;
473 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
474 if (INVALID_HANDLE_VALUE == h)
476 SetErrnoFromWinError (GetLastError ());
477 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
478 return GNUNET_SYSERR;
481 c = GetExitCodeProcess (proc, &c);
482 if (STILL_ACTIVE == c)
484 *type = GNUNET_OS_PROCESS_RUNNING;
489 *type = GNUNET_OS_PROCESS_EXITED;
499 * @param proc process ID to wait for
500 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
503 GNUNET_OS_process_wait (pid_t proc)
506 if (proc != waitpid (proc, NULL, 0))
507 return GNUNET_SYSERR;
515 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
516 if (INVALID_HANDLE_VALUE == h)
518 SetErrnoFromWinError (GetLastError ());
519 return GNUNET_SYSERR;
522 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
524 SetErrnoFromWinError (GetLastError ());
537 /* end of os_priority.c */