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 GNUNET_break (0 == close (fd_stdout_read));
237 if (-1 == dup2(fd_stdout_write, 1))
238 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
239 GNUNET_break (0 == close (fd_stdout_write));
242 if (pipe_stdin != NULL)
245 GNUNET_break (0 == close (fd_stdin_write));
246 if (-1 == dup2(fd_stdin_read, 0))
247 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
248 GNUNET_break (0 == close (fd_stdin_read));
250 execvp (filename, argv);
251 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
258 PROCESS_INFORMATION proc;
261 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);
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);
283 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
284 start.dwFlags |= STARTF_USESTDHANDLES;
286 if (pipe_stdin != NULL)
288 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
289 start.hStdInput = stdin_handle;
292 if (pipe_stdout != NULL)
294 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
295 start.hStdOutput = stdout_handle;
298 if (FindExecutable(filename, NULL, path) <= 32)
300 SetErrnoFromWinError (GetLastError ());
301 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", fn);
306 (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
309 SetErrnoFromWinError (GetLastError ());
310 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", fn);
314 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
318 CloseHandle (proc.hThread);
322 return proc.dwProcessId;
332 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
333 * must be NULL on platforms where dup is not supported
334 * @param filename name of the binary
335 * @param argv NULL-terminated list of arguments to the process
336 * @return process ID of the new process, -1 on error
339 GNUNET_OS_start_process_v (const int *lsocks,
340 const char *filename, char *const argv[])
359 while (-1 != (k = lsocks[i++]))
361 flags = fcntl (k, F_GETFD);
362 GNUNET_assert (flags >= 0);
363 flags &= ~FD_CLOEXEC;
364 (void) fcntl (k, F_SETFD, flags);
365 GNUNET_array_append (lscp, ls, k);
367 GNUNET_array_append (lscp, ls, -1);
369 #if HAVE_WORKING_VFORK
378 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
382 #if HAVE_WORKING_VFORK
383 /* let's hope vfork actually works; for some extreme cases (including
384 a testcase) we need 'execvp' to have run before we return, since
385 we may send a signal to the process next and we don't want it
386 to be caught by OUR signal handler (but either by the default
387 handler or the actual handler as installed by the process itself). */
389 /* let's give the child process a chance to run execvp, 1s should
390 be plenty in practice */
398 /* read systemd documentation... */
399 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
400 setenv ("LISTEN_PID", lpid, 1);
403 while (-1 != lscp[i])
406 while (-1 != lscp[j])
412 GNUNET_assert (-1 != k);
413 GNUNET_assert (0 == close (lscp[j]));
421 /* Bury any existing FD, no matter what; they should all be closed
422 on exec anyway and the important onces have been dup'ed away */
424 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
426 /* set close-on-exec flag */
427 flags = fcntl (tgt, F_GETFD);
428 GNUNET_assert (flags >= 0);
429 flags &= ~FD_CLOEXEC;
430 (void) fcntl (tgt, F_SETFD, flags);
434 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
435 setenv ("LISTEN_FDS", fds, 1);
437 GNUNET_array_grow (lscp, ls, 0);
438 execvp (filename, argv);
439 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
442 char **arg, **non_const_argv;
446 PROCESS_INFORMATION proc;
448 char *non_const_filename = NULL;
451 GNUNET_assert (lsocks == NULL);
452 /* Count the number of arguments */
460 /* Allocate a copy argv */
461 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
463 /* Copy all argv strings */
468 non_const_argv[argcount] = GNUNET_strdup (*arg);
472 non_const_argv[argcount] = NULL;
474 /* Fix .exe extension */
475 filenamelen = strlen (filename);
476 if (filenamelen <= 4 || stricmp (&filename[filenamelen - 4], ".exe") != 0)
478 non_const_filename = GNUNET_malloc (sizeof (char) * (filenamelen + 4 + 1));
479 non_const_filename = strcpy (non_const_filename, non_const_argv[0]);
480 strcat (non_const_filename, ".exe");
481 GNUNET_free (non_const_argv[0]);
482 non_const_argv[0] = non_const_filename;
485 non_const_filename = non_const_argv[0];
489 arg = non_const_argv;
492 cmdlen = cmdlen + strlen (*arg) + 3;
496 /* Allocate and create cmd */
497 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
498 arg = non_const_argv;
501 idx += sprintf (idx, "\"%s\" ", *arg);
505 memset (&start, 0, sizeof (start));
506 start.cb = sizeof (start);
509 (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
512 SetErrnoFromWinError (GetLastError ());
513 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
517 CreateThread (NULL, 64000, ChildWaitThread, proc.hProcess, 0, NULL);
519 CloseHandle (proc.hThread);
523 GNUNET_free (non_const_argv[--argcount]);
524 GNUNET_free (non_const_argv);
526 return proc.dwProcessId;
531 * Retrieve the status of a process
532 * @param proc process ID
533 * @param type status type
534 * @param code return code/signal number
535 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
538 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
545 GNUNET_assert (0 != proc);
546 ret = waitpid (proc, &status, WNOHANG);
549 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
550 return GNUNET_SYSERR;
554 *type = GNUNET_OS_PROCESS_RUNNING;
560 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
561 return GNUNET_SYSERR;
563 if (WIFEXITED (status))
565 *type = GNUNET_OS_PROCESS_EXITED;
566 *code = WEXITSTATUS (status);
568 else if (WIFSIGNALED (status))
570 *type = GNUNET_OS_PROCESS_SIGNALED;
571 *code = WTERMSIG (status);
573 else if (WIFSTOPPED (status))
575 *type = GNUNET_OS_PROCESS_SIGNALED;
576 *code = WSTOPSIG (status);
579 else if (WIFCONTINUED (status))
581 *type = GNUNET_OS_PROCESS_RUNNING;
587 *type = GNUNET_OS_PROCESS_UNKNOWN;
594 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
595 if (INVALID_HANDLE_VALUE == h)
597 SetErrnoFromWinError (GetLastError ());
598 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
599 return GNUNET_SYSERR;
602 c = GetExitCodeProcess (proc, &c);
603 if (STILL_ACTIVE == c)
605 *type = GNUNET_OS_PROCESS_RUNNING;
610 *type = GNUNET_OS_PROCESS_EXITED;
620 * @param proc process ID to wait for
621 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
624 GNUNET_OS_process_wait (pid_t proc)
627 if (proc != waitpid (proc, NULL, 0))
628 return GNUNET_SYSERR;
636 h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
637 if (INVALID_HANDLE_VALUE == h)
639 SetErrnoFromWinError (GetLastError ());
640 return GNUNET_SYSERR;
643 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
645 SetErrnoFromWinError (GetLastError ());
658 /* end of os_priority.c */