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 ();
55 * Set process priority
57 * @param proc id of the process
58 * @param prio priority value
59 * @return GNUNET_OK on success, GNUNET_SYSERR on error
62 GNUNET_OS_set_process_priority (pid_t proc,
63 enum GNUNET_SCHEDULER_Priority prio)
67 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
68 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
70 /* convert to MINGW/Unix values */
73 case GNUNET_SCHEDULER_PRIORITY_UI:
74 case GNUNET_SCHEDULER_PRIORITY_URGENT:
76 rprio = HIGH_PRIORITY_CLASS;
82 case GNUNET_SCHEDULER_PRIORITY_HIGH:
84 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
90 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
92 rprio = NORMAL_PRIORITY_CLASS;
98 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
100 rprio = BELOW_NORMAL_PRIORITY_CLASS;
106 case GNUNET_SCHEDULER_PRIORITY_IDLE:
108 rprio = IDLE_PRIORITY_CLASS;
115 return GNUNET_SYSERR;
117 /* Set process priority */
119 SetPriorityClass (GetCurrentProcess (), rprio);
122 (proc == getpid () ) )
125 int delta = rprio - have;
128 (rprio == nice (delta)) &&
131 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
132 GNUNET_ERROR_TYPE_BULK, "nice");
133 return GNUNET_SYSERR;
138 if (0 != setpriority (PRIO_PROCESS, proc, rprio))
141 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
142 GNUNET_ERROR_TYPE_BULK, "setpriority");
143 return GNUNET_SYSERR;
147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
148 "Priority management not availabe for this platform\n");
156 * @param pipe_stdin pipe to use to send input to child process (or NULL)
157 * @param pipe_stdout pipe to use to get output from child process (or NULL)
158 * @param filename name of the binary
159 * @param ... NULL-terminated list of arguments to the process
160 * @return process ID of the new process, -1 on error
163 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
164 struct GNUNET_DISK_PipeHandle *pipe_stdout,
165 const char *filename, ...)
167 /* FIXME: Make this work on windows!!! */
180 va_start (ap, filename);
181 while (NULL != va_arg (ap, char *))
184 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
186 va_start (ap, filename);
187 while (NULL != (argv[argc] = va_arg (ap, char *)))
190 if (pipe_stdout != NULL)
192 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
193 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
195 if (pipe_stdin != NULL)
197 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
198 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
201 #if HAVE_WORKING_VFORK
210 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
215 #if HAVE_WORKING_VFORK
216 /* let's hope vfork actually works; for some extreme cases (including
217 a testcase) we need 'execvp' to have run before we return, since
218 we may send a signal to the process next and we don't want it
219 to be caught by OUR signal handler (but either by the default
220 handler or the actual handler as installed by the process itself). */
222 /* let's give the child process a chance to run execvp, 1s should
223 be plenty in practice */
224 if (pipe_stdout != NULL)
225 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
226 if (pipe_stdin != NULL)
227 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
235 if (pipe_stdout != NULL)
237 GNUNET_break (0 == close (fd_stdout_read));
238 if (-1 == dup2(fd_stdout_write, 1))
239 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
240 GNUNET_break (0 == close (fd_stdout_write));
243 if (pipe_stdin != NULL)
246 GNUNET_break (0 == close (fd_stdin_write));
247 if (-1 == dup2(fd_stdin_read, 0))
248 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
249 GNUNET_break (0 == close (fd_stdin_read));
251 execvp (filename, argv);
252 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
260 PROCESS_INFORMATION proc;
263 HANDLE stdout_handle;
265 char path[MAX_PATH + 1];
268 va_start (ap, filename);
269 while (NULL != (arg = va_arg (ap, char *)))
270 cmdlen = cmdlen + strlen (arg) + 3;
273 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
274 va_start (ap, filename);
275 while (NULL != (arg = va_arg (ap, char *)))
276 idx += sprintf (idx, "\"%s\" ", arg);
279 memset (&start, 0, sizeof (start));
280 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 findresult = (int) FindExecutableA (filename, NULL, path);
298 if (findresult <= 32)
300 SetErrnoFromWinError (GetLastError ());
301 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
306 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start,
309 SetErrnoFromWinError (GetLastError ());
310 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
314 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
316 CloseHandle (proc.hThread);
320 return proc.dwProcessId;
330 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
331 * must be NULL on platforms where dup is not supported
332 * @param filename name of the binary
333 * @param argv NULL-terminated list of arguments to the process
334 * @return process ID of the new process, -1 on error
337 GNUNET_OS_start_process_v (const int *lsocks,
338 const char *filename, char *const argv[])
357 while (-1 != (k = lsocks[i++]))
359 flags = fcntl (k, F_GETFD);
360 GNUNET_assert (flags >= 0);
361 flags &= ~FD_CLOEXEC;
362 (void) fcntl (k, F_SETFD, flags);
363 GNUNET_array_append (lscp, ls, k);
365 GNUNET_array_append (lscp, ls, -1);
367 #if HAVE_WORKING_VFORK
376 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
380 #if HAVE_WORKING_VFORK
381 /* let's hope vfork actually works; for some extreme cases (including
382 a testcase) we need 'execvp' to have run before we return, since
383 we may send a signal to the process next and we don't want it
384 to be caught by OUR signal handler (but either by the default
385 handler or the actual handler as installed by the process itself). */
387 /* let's give the child process a chance to run execvp, 1s should
388 be plenty in practice */
392 GNUNET_array_grow (lscp, ls, 0);
397 /* read systemd documentation... */
398 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
399 setenv ("LISTEN_PID", lpid, 1);
402 while (-1 != lscp[i])
405 while (-1 != lscp[j])
411 GNUNET_assert (-1 != k);
412 GNUNET_assert (0 == close (lscp[j]));
420 /* Bury any existing FD, no matter what; they should all be closed
421 on exec anyway and the important onces have been dup'ed away */
423 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
425 /* set close-on-exec flag */
426 flags = fcntl (tgt, F_GETFD);
427 GNUNET_assert (flags >= 0);
428 flags &= ~FD_CLOEXEC;
429 (void) fcntl (tgt, F_SETFD, flags);
433 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
434 setenv ("LISTEN_FDS", fds, 1);
436 GNUNET_array_grow (lscp, ls, 0);
437 execvp (filename, argv);
438 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
441 char **arg, **non_const_argv;
445 PROCESS_INFORMATION proc;
447 char *non_const_filename = NULL;
450 GNUNET_assert (lsocks == NULL);
451 /* Count the number of arguments */
452 arg = (char **) argv;
459 /* Allocate a copy argv */
460 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
462 /* Copy all argv strings */
464 arg = (char **) argv;
467 non_const_argv[argcount] = GNUNET_strdup (*arg);
471 non_const_argv[argcount] = NULL;
473 /* Fix .exe extension */
474 filenamelen = strlen (filename);
475 if (filenamelen <= 4 || stricmp (&filename[filenamelen - 4], ".exe") != 0)
477 non_const_filename = GNUNET_malloc (sizeof (char) * (filenamelen + 4 + 1));
478 non_const_filename = strcpy (non_const_filename, non_const_argv[0]);
479 strcat (non_const_filename, ".exe");
480 GNUNET_free (non_const_argv[0]);
481 non_const_argv[0] = non_const_filename;
484 non_const_filename = non_const_argv[0];
488 arg = non_const_argv;
491 cmdlen = cmdlen + strlen (*arg) + 3;
495 /* Allocate and create cmd */
496 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
497 arg = non_const_argv;
500 idx += sprintf (idx, "\"%s\" ", *arg);
504 memset (&start, 0, sizeof (start));
505 start.cb = sizeof (start);
508 (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
511 SetErrnoFromWinError (GetLastError ());
512 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
516 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
518 CloseHandle (proc.hThread);
522 GNUNET_free (non_const_argv[--argcount]);
523 GNUNET_free (non_const_argv);
525 return proc.dwProcessId;
530 * Retrieve the status of a process
531 * @param proc process ID
532 * @param type status type
533 * @param code return code/signal number
534 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
537 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
544 GNUNET_assert (0 != proc);
545 ret = waitpid (proc, &status, WNOHANG);
548 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
549 return GNUNET_SYSERR;
553 *type = GNUNET_OS_PROCESS_RUNNING;
559 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
560 return GNUNET_SYSERR;
562 if (WIFEXITED (status))
564 *type = GNUNET_OS_PROCESS_EXITED;
565 *code = WEXITSTATUS (status);
567 else if (WIFSIGNALED (status))
569 *type = GNUNET_OS_PROCESS_SIGNALED;
570 *code = WTERMSIG (status);
572 else if (WIFSTOPPED (status))
574 *type = GNUNET_OS_PROCESS_SIGNALED;
575 *code = WSTOPSIG (status);
578 else if (WIFCONTINUED (status))
580 *type = GNUNET_OS_PROCESS_RUNNING;
586 *type = GNUNET_OS_PROCESS_UNKNOWN;
593 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
594 if (INVALID_HANDLE_VALUE == h)
596 SetErrnoFromWinError (GetLastError ());
597 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
598 return GNUNET_SYSERR;
601 c = GetExitCodeProcess (h, &c);
602 if (STILL_ACTIVE == c)
604 *type = GNUNET_OS_PROCESS_RUNNING;
609 *type = GNUNET_OS_PROCESS_EXITED;
619 * @param proc process ID to wait for
620 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
623 GNUNET_OS_process_wait (pid_t proc)
626 if (proc != waitpid (proc, NULL, 0))
627 return GNUNET_SYSERR;
634 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
635 if (INVALID_HANDLE_VALUE == h)
637 SetErrnoFromWinError (GetLastError ());
638 return GNUNET_SYSERR;
641 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
643 SetErrnoFromWinError (GetLastError ());
656 /* end of os_priority.c */