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"
32 struct GNUNET_OS_Process
40 static struct GNUNET_OS_Process current_process;
45 GNUNET_OS_process_set_handle(struct GNUNET_OS_Process *proc, HANDLE handle)
47 if (proc->handle != NULL)
48 CloseHandle (proc->handle);
49 proc->handle = handle;
55 * Get process structure for current process
57 * The pointer it returns points to static memory location and must not be
60 * @return pointer to the process sturcutre for this process
62 struct GNUNET_OS_Process *
63 GNUNET_OS_process_current ()
66 current_process.pid = GetCurrentProcessId ();
67 current_process.handle = GetCurrentProcess ();
69 current_process.pid = 0;
71 return ¤t_process;
75 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
78 if (sig == SIGKILL || sig == SIGTERM)
80 HANDLE h = proc->handle;
83 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
84 _("Invalid process information {%d, %08X}\n"),
89 if (!TerminateProcess (h, 0))
91 SetErrnoFromWinError (GetLastError ());
100 return kill (proc->pid, sig);
105 * Get the pid of the process in question
107 * @param proc the process to get the pid of
109 * @return the current process id
112 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
118 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
121 if (proc->handle != NULL)
122 CloseHandle (proc->handle);
128 #include "gnunet_signal_lib.h"
130 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
133 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
134 * @param proc pointer to process structure
137 ChildWaitThread (void *arg)
139 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
140 WaitForSingleObject (proc->handle, INFINITE);
142 if (w32_sigchld_handler)
143 w32_sigchld_handler ();
150 * Set process priority
152 * @param proc pointer to process structure
153 * @param prio priority value
154 * @return GNUNET_OK on success, GNUNET_SYSERR on error
157 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
158 enum GNUNET_SCHEDULER_Priority prio)
162 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
163 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
166 /* convert to MINGW/Unix values */
169 case GNUNET_SCHEDULER_PRIORITY_UI:
170 case GNUNET_SCHEDULER_PRIORITY_URGENT:
172 rprio = HIGH_PRIORITY_CLASS;
178 case GNUNET_SCHEDULER_PRIORITY_HIGH:
180 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
186 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
188 rprio = NORMAL_PRIORITY_CLASS;
194 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
196 rprio = BELOW_NORMAL_PRIORITY_CLASS;
202 case GNUNET_SCHEDULER_PRIORITY_IDLE:
204 rprio = IDLE_PRIORITY_CLASS;
211 return GNUNET_SYSERR;
214 /* Set process priority */
217 HANDLE h = proc->handle;
218 GNUNET_assert (h != NULL);
219 SetPriorityClass (h, rprio);
226 (pid == getpid () ) )
229 int delta = rprio - have;
232 (rprio == nice (delta)) &&
235 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
236 GNUNET_ERROR_TYPE_BULK, "nice");
237 return GNUNET_SYSERR;
242 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
244 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
245 GNUNET_ERROR_TYPE_BULK, "setpriority");
246 return GNUNET_SYSERR;
250 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
251 "Priority management not availabe for this platform\n");
259 * @param pipe_stdin pipe to use to send input to child process (or NULL)
260 * @param pipe_stdout pipe to use to get output from child process (or NULL)
261 * @param filename name of the binary
262 * @param ... NULL-terminated list of arguments to the process
263 * @return pointer to process structure of the new process, NULL on error
265 struct GNUNET_OS_Process *
266 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
267 struct GNUNET_DISK_PipeHandle *pipe_stdout,
268 const char *filename, ...)
274 struct GNUNET_OS_Process *gnunet_proc = NULL;
283 va_start (ap, filename);
284 while (NULL != va_arg (ap, char *))
287 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
289 va_start (ap, filename);
290 while (NULL != (argv[argc] = va_arg (ap, char *)))
293 if (pipe_stdout != NULL)
295 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
296 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
298 if (pipe_stdin != NULL)
300 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
301 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
304 #if HAVE_WORKING_VFORK
313 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
318 #if HAVE_WORKING_VFORK
319 /* let's hope vfork actually works; for some extreme cases (including
320 a testcase) we need 'execvp' to have run before we return, since
321 we may send a signal to the process next and we don't want it
322 to be caught by OUR signal handler (but either by the default
323 handler or the actual handler as installed by the process itself). */
325 /* let's give the child process a chance to run execvp, 1s should
326 be plenty in practice */
327 if (pipe_stdout != NULL)
328 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
329 if (pipe_stdin != NULL)
330 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
333 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
334 gnunet_proc->pid = ret;
340 if (pipe_stdout != NULL)
342 GNUNET_break (0 == close (fd_stdout_read));
343 if (-1 == dup2(fd_stdout_write, 1))
344 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
345 GNUNET_break (0 == close (fd_stdout_write));
348 if (pipe_stdin != NULL)
351 GNUNET_break (0 == close (fd_stdin_write));
352 if (-1 == dup2(fd_stdin_read, 0))
353 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
354 GNUNET_break (0 == close (fd_stdin_read));
356 execvp (filename, argv);
357 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
364 PROCESS_INFORMATION proc;
365 struct GNUNET_OS_Process *gnunet_proc = NULL;
368 HANDLE stdout_handle;
370 char path[MAX_PATH + 1];
373 va_start (ap, filename);
374 while (NULL != (arg = va_arg (ap, char *)))
375 cmdlen = cmdlen + strlen (arg) + 3;
378 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
379 va_start (ap, filename);
380 while (NULL != (arg = va_arg (ap, char *)))
381 idx += sprintf (idx, "\"%s\" ", arg);
384 memset (&start, 0, sizeof (start));
385 start.cb = sizeof (start);
387 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
388 start.dwFlags |= STARTF_USESTDHANDLES;
390 if (pipe_stdin != NULL)
392 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
393 start.hStdInput = stdin_handle;
396 if (pipe_stdout != NULL)
398 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
399 start.hStdOutput = stdout_handle;
402 if (32 >= (int) FindExecutableA (filename, NULL, path))
404 SetErrnoFromWinError (GetLastError ());
405 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
410 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start,
413 SetErrnoFromWinError (GetLastError ());
414 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
418 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
419 gnunet_proc->pid = proc.dwProcessId;
420 gnunet_proc->handle = proc.hProcess;
422 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
424 CloseHandle (proc.hThread);
438 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
439 * must be NULL on platforms where dup is not supported
440 * @param filename name of the binary
441 * @param argv NULL-terminated list of arguments to the process
442 * @return process ID of the new process, -1 on error
444 struct GNUNET_OS_Process *
445 GNUNET_OS_start_process_v (const int *lsocks,
446 const char *filename, char *const argv[])
452 struct GNUNET_OS_Process *gnunet_proc = NULL;
466 while (-1 != (k = lsocks[i++]))
467 GNUNET_array_append (lscp, ls, k);
468 GNUNET_array_append (lscp, ls, -1);
470 #if HAVE_WORKING_VFORK
479 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
483 #if HAVE_WORKING_VFORK
484 /* let's hope vfork actually works; for some extreme cases (including
485 a testcase) we need 'execvp' to have run before we return, since
486 we may send a signal to the process next and we don't want it
487 to be caught by OUR signal handler (but either by the default
488 handler or the actual handler as installed by the process itself). */
490 /* let's give the child process a chance to run execvp, 1s should
491 be plenty in practice */
494 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
495 gnunet_proc->pid = ret;
497 GNUNET_array_grow (lscp, ls, 0);
502 /* read systemd documentation... */
503 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
504 setenv ("LISTEN_PID", lpid, 1);
507 while (-1 != lscp[i])
510 while (-1 != lscp[j])
516 GNUNET_assert (-1 != k);
517 GNUNET_assert (0 == close (lscp[j]));
525 /* Bury any existing FD, no matter what; they should all be closed
526 on exec anyway and the important onces have been dup'ed away */
528 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
530 /* unset close-on-exec flag */
531 flags = fcntl (tgt, F_GETFD);
532 GNUNET_assert (flags >= 0);
533 flags &= ~FD_CLOEXEC;
535 (void) fcntl (tgt, F_SETFD, flags);
539 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
540 setenv ("LISTEN_FDS", fds, 1);
542 GNUNET_array_grow (lscp, ls, 0);
543 execvp (filename, argv);
544 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
547 char **arg, **non_const_argv;
551 PROCESS_INFORMATION proc;
553 char non_const_filename[MAX_PATH +1];
554 struct GNUNET_OS_Process *gnunet_proc = NULL;
556 GNUNET_assert (lsocks == NULL);
558 if (32 >= (int) FindExecutableA (filename, NULL, non_const_filename))
560 SetErrnoFromWinError (GetLastError ());
561 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
565 /* Count the number of arguments */
566 arg = (char **) argv;
573 /* Allocate a copy argv */
574 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
576 /* Copy all argv strings */
578 arg = (char **) argv;
581 non_const_argv[argcount] = GNUNET_strdup (*arg);
585 non_const_argv[argcount] = NULL;
589 arg = non_const_argv;
592 cmdlen = cmdlen + strlen (*arg) + 3;
596 /* Allocate and create cmd */
597 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
598 arg = non_const_argv;
601 idx += sprintf (idx, "\"%s\" ", *arg);
605 memset (&start, 0, sizeof (start));
606 start.cb = sizeof (start);
609 (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
612 SetErrnoFromWinError (GetLastError ());
613 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
617 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
618 gnunet_proc->pid = proc.dwProcessId;
619 gnunet_proc->handle = proc.hProcess;
621 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
623 CloseHandle (proc.hThread);
627 GNUNET_free (non_const_argv[--argcount]);
628 GNUNET_free (non_const_argv);
635 * Retrieve the status of a process
636 * @param proc process ID
637 * @param type status type
638 * @param code return code/signal number
639 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
642 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
643 enum GNUNET_OS_ProcessStatusType *type,
650 GNUNET_assert (0 != proc);
651 ret = waitpid (proc->pid, &status, WNOHANG);
654 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
655 return GNUNET_SYSERR;
659 *type = GNUNET_OS_PROCESS_RUNNING;
663 if (proc->pid != ret)
665 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
666 return GNUNET_SYSERR;
668 if (WIFEXITED (status))
670 *type = GNUNET_OS_PROCESS_EXITED;
671 *code = WEXITSTATUS (status);
673 else if (WIFSIGNALED (status))
675 *type = GNUNET_OS_PROCESS_SIGNALED;
676 *code = WTERMSIG (status);
678 else if (WIFSTOPPED (status))
680 *type = GNUNET_OS_PROCESS_SIGNALED;
681 *code = WSTOPSIG (status);
684 else if (WIFCONTINUED (status))
686 *type = GNUNET_OS_PROCESS_RUNNING;
692 *type = GNUNET_OS_PROCESS_UNKNOWN;
697 DWORD c, error_code, ret;
701 if (h == NULL || ret == 0)
703 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
704 return GNUNET_SYSERR;
707 h = GetCurrentProcess ();
710 ret = GetExitCodeProcess (h, &c);
711 error_code = GetLastError ();
712 if (ret == 0 || error_code != NO_ERROR)
714 SetErrnoFromWinError (error_code);
715 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
716 return GNUNET_SYSERR;
718 if (STILL_ACTIVE == c)
720 *type = GNUNET_OS_PROCESS_RUNNING;
724 *type = GNUNET_OS_PROCESS_EXITED;
733 * @param proc pointer to process structure
734 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
737 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
741 pid_t pid = proc->pid;
742 if (pid != waitpid (pid, NULL, 0))
743 return GNUNET_SYSERR;
752 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
753 "Invalid process information {%d, %08X}\n",
756 return GNUNET_SYSERR;
759 h = GetCurrentProcess ();
761 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
763 SetErrnoFromWinError (GetLastError ());
774 /* end of os_priority.c */