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 = GNUNET_OS_process_get_handle (proc);
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);
365 PROCESS_INFORMATION proc;
366 struct GNUNET_OS_Process *gnunet_proc = NULL;
369 HANDLE stdout_handle;
371 char path[MAX_PATH + 1];
374 va_start (ap, filename);
375 while (NULL != (arg = va_arg (ap, char *)))
376 cmdlen = cmdlen + strlen (arg) + 3;
379 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
380 va_start (ap, filename);
381 while (NULL != (arg = va_arg (ap, char *)))
382 idx += sprintf (idx, "\"%s\" ", arg);
385 memset (&start, 0, sizeof (start));
386 start.cb = sizeof (start);
388 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
389 start.dwFlags |= STARTF_USESTDHANDLES;
391 if (pipe_stdin != NULL)
393 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
394 start.hStdInput = stdin_handle;
397 if (pipe_stdout != NULL)
399 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
400 start.hStdOutput = stdout_handle;
403 findresult = (int) FindExecutableA (filename, NULL, path);
404 if (findresult <= 32)
406 SetErrnoFromWinError (GetLastError ());
407 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
412 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start,
415 SetErrnoFromWinError (GetLastError ());
416 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
420 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
421 gnunet_proc->pid = proc.dwProcessId;
422 gnunet_proc->handle = proc.hProcess;
424 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
426 CloseHandle (proc.hThread);
440 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
441 * must be NULL on platforms where dup is not supported
442 * @param filename name of the binary
443 * @param argv NULL-terminated list of arguments to the process
444 * @return process ID of the new process, -1 on error
446 struct GNUNET_OS_Process *
447 GNUNET_OS_start_process_v (const int *lsocks,
448 const char *filename, char *const argv[])
454 struct GNUNET_OS_Process *gnunet_proc = NULL;
468 while (-1 != (k = lsocks[i++]))
469 GNUNET_array_append (lscp, ls, k);
470 GNUNET_array_append (lscp, ls, -1);
472 #if HAVE_WORKING_VFORK
481 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
485 #if HAVE_WORKING_VFORK
486 /* let's hope vfork actually works; for some extreme cases (including
487 a testcase) we need 'execvp' to have run before we return, since
488 we may send a signal to the process next and we don't want it
489 to be caught by OUR signal handler (but either by the default
490 handler or the actual handler as installed by the process itself). */
492 /* let's give the child process a chance to run execvp, 1s should
493 be plenty in practice */
496 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
497 gnunet_proc->pid = ret;
499 GNUNET_array_grow (lscp, ls, 0);
504 /* read systemd documentation... */
505 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
506 setenv ("LISTEN_PID", lpid, 1);
509 while (-1 != lscp[i])
512 while (-1 != lscp[j])
518 GNUNET_assert (-1 != k);
519 GNUNET_assert (0 == close (lscp[j]));
527 /* Bury any existing FD, no matter what; they should all be closed
528 on exec anyway and the important onces have been dup'ed away */
530 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
532 /* unset close-on-exec flag */
533 flags = fcntl (tgt, F_GETFD);
534 GNUNET_assert (flags >= 0);
535 flags &= ~FD_CLOEXEC;
537 (void) fcntl (tgt, F_SETFD, flags);
541 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
542 setenv ("LISTEN_FDS", fds, 1);
544 GNUNET_array_grow (lscp, ls, 0);
545 execvp (filename, argv);
546 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
549 char **arg, **non_const_argv;
553 PROCESS_INFORMATION proc;
555 char *non_const_filename = NULL;
557 struct GNUNET_OS_Process *gnunet_proc = NULL;
559 GNUNET_assert (lsocks == NULL);
560 /* Count the number of arguments */
561 arg = (char **) argv;
568 /* Allocate a copy argv */
569 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
571 /* Copy all argv strings */
573 arg = (char **) argv;
576 non_const_argv[argcount] = GNUNET_strdup (*arg);
580 non_const_argv[argcount] = NULL;
582 /* Fix .exe extension */
583 filenamelen = strlen (filename);
584 if (filenamelen <= 4 || stricmp (&filename[filenamelen - 4], ".exe") != 0)
586 non_const_filename = GNUNET_malloc (sizeof (char) * (filenamelen + 4 + 1));
587 non_const_filename = strcpy (non_const_filename, non_const_argv[0]);
588 strcat (non_const_filename, ".exe");
589 GNUNET_free (non_const_argv[0]);
590 non_const_argv[0] = non_const_filename;
593 non_const_filename = non_const_argv[0];
597 arg = non_const_argv;
600 cmdlen = cmdlen + strlen (*arg) + 3;
604 /* Allocate and create cmd */
605 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
606 arg = non_const_argv;
609 idx += sprintf (idx, "\"%s\" ", *arg);
613 memset (&start, 0, sizeof (start));
614 start.cb = sizeof (start);
617 (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
620 SetErrnoFromWinError (GetLastError ());
621 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
625 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
626 gnunet_proc->pid = proc.dwProcessId;
627 gnunet_proc->handle = proc.hProcess;
629 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
631 CloseHandle (proc.hThread);
635 GNUNET_free (non_const_argv[--argcount]);
636 GNUNET_free (non_const_argv);
643 * Retrieve the status of a process
644 * @param proc process ID
645 * @param type status type
646 * @param code return code/signal number
647 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
650 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
651 enum GNUNET_OS_ProcessStatusType *type,
658 GNUNET_assert (0 != proc);
659 ret = waitpid (proc->pid, &status, WNOHANG);
662 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
663 return GNUNET_SYSERR;
667 *type = GNUNET_OS_PROCESS_RUNNING;
671 if (proc->pid != ret)
673 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
674 return GNUNET_SYSERR;
676 if (WIFEXITED (status))
678 *type = GNUNET_OS_PROCESS_EXITED;
679 *code = WEXITSTATUS (status);
681 else if (WIFSIGNALED (status))
683 *type = GNUNET_OS_PROCESS_SIGNALED;
684 *code = WTERMSIG (status);
686 else if (WIFSTOPPED (status))
688 *type = GNUNET_OS_PROCESS_SIGNALED;
689 *code = WSTOPSIG (status);
692 else if (WIFCONTINUED (status))
694 *type = GNUNET_OS_PROCESS_RUNNING;
700 *type = GNUNET_OS_PROCESS_UNKNOWN;
705 DWORD c, error_code, ret;
707 h = GNUNET_OS_process_get_handle (proc);
709 if (h == NULL || ret == 0)
711 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
712 return GNUNET_SYSERR;
715 h = GetCurrentProcess ();
718 ret = GetExitCodeProcess (h, &c);
719 error_code = GetLastError ();
720 if (ret == 0 || error_code != NO_ERROR)
722 SetErrnoFromWinError (error_code);
723 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
724 return GNUNET_SYSERR;
726 if (STILL_ACTIVE == c)
728 *type = GNUNET_OS_PROCESS_RUNNING;
732 *type = GNUNET_OS_PROCESS_EXITED;
741 * @param proc pointer to process structure
742 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
745 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
749 pid_t pid = proc->pid;
750 if (pid != waitpid (pid, NULL, 0))
751 return GNUNET_SYSERR;
760 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
761 "Invalid process information {%d, %08X}\n",
764 return GNUNET_SYSERR;
767 h = GetCurrentProcess ();
769 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
771 SetErrnoFromWinError (GetLastError ());
782 /* end of os_priority.c */