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 GNUNET_OS_Process current_process;
43 GNUNET_OS_process_alloc ()
45 GNUNET_OS_Process *ret = GNUNET_malloc (sizeof (GNUNET_OS_Process));
54 * Get process structure for current process
56 * The pointer it returns points to static memory location and must not be
59 * @return pointer to the process sturcutre for this process
62 GNUNET_OS_process_current ()
65 current_process.pid = GetCurrentProcessId ();
66 current_process.handle = GetCurrentProcess ();
68 current_process.pid = 0;
70 return ¤t_process;
74 GNUNET_OS_process_kill (GNUNET_OS_Process *proc, int sig)
77 if (sig == SIGKILL || sig == SIGTERM)
79 HANDLE h = GNUNET_OS_process_get_handle (proc);
82 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", GNUNET_OS_process_get_pid (proc), h);
85 if (!TerminateProcess (h, 0))
87 SetErrnoFromWinError (GetLastError ());
96 return kill (GNUNET_OS_process_get_pid (proc), sig);
101 GNUNET_OS_process_get_pid (GNUNET_OS_Process *proc)
107 GNUNET_OS_process_set_pid (GNUNET_OS_Process *proc, pid_t pid)
114 GNUNET_OS_process_get_handle (GNUNET_OS_Process *proc)
120 GNUNET_OS_process_set_handle(GNUNET_OS_Process *proc, HANDLE handle)
122 if (proc->handle != NULL)
123 CloseHandle (proc->handle);
124 proc->handle = handle;
129 GNUNET_OS_process_close (GNUNET_OS_Process *proc)
132 if (proc->handle != NULL)
133 CloseHandle (proc->handle);
139 #include "gnunet_signal_lib.h"
141 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
144 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
145 * @param proc pointer to process structure
148 ChildWaitThread (void *arg)
150 GNUNET_OS_Process *proc = (GNUNET_OS_Process *) arg;
151 WaitForSingleObject (proc->handle, INFINITE);
153 if (w32_sigchld_handler)
154 w32_sigchld_handler ();
161 * Set process priority
163 * @param proc pointer to process structure
164 * @param prio priority value
165 * @return GNUNET_OK on success, GNUNET_SYSERR on error
168 GNUNET_OS_set_process_priority (GNUNET_OS_Process *proc,
169 enum GNUNET_SCHEDULER_Priority prio)
174 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
175 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
178 /* convert to MINGW/Unix values */
181 case GNUNET_SCHEDULER_PRIORITY_UI:
182 case GNUNET_SCHEDULER_PRIORITY_URGENT:
184 rprio = HIGH_PRIORITY_CLASS;
190 case GNUNET_SCHEDULER_PRIORITY_HIGH:
192 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
198 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
200 rprio = NORMAL_PRIORITY_CLASS;
206 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
208 rprio = BELOW_NORMAL_PRIORITY_CLASS;
214 case GNUNET_SCHEDULER_PRIORITY_IDLE:
216 rprio = IDLE_PRIORITY_CLASS;
223 return GNUNET_SYSERR;
226 pid = GNUNET_OS_process_get_pid (proc);
228 /* Set process priority */
231 HANDLE h = GNUNET_OS_process_get_handle (proc);
232 GNUNET_assert (h != NULL);
233 SetPriorityClass (h, rprio);
237 (pid == getpid () ) )
240 int delta = rprio - have;
243 (rprio == nice (delta)) &&
246 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
247 GNUNET_ERROR_TYPE_BULK, "nice");
248 return GNUNET_SYSERR;
253 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
256 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
257 GNUNET_ERROR_TYPE_BULK, "setpriority");
258 return GNUNET_SYSERR;
262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
263 "Priority management not availabe for this platform\n");
271 * @param pipe_stdin pipe to use to send input to child process (or NULL)
272 * @param pipe_stdout pipe to use to get output from child process (or NULL)
273 * @param filename name of the binary
274 * @param ... NULL-terminated list of arguments to the process
275 * @return pointer to process structure of the new process, NULL on error
278 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
279 struct GNUNET_DISK_PipeHandle *pipe_stdout,
280 const char *filename, ...)
286 GNUNET_OS_Process *gnunet_proc = NULL;
295 va_start (ap, filename);
296 while (NULL != va_arg (ap, char *))
299 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
301 va_start (ap, filename);
302 while (NULL != (argv[argc] = va_arg (ap, char *)))
305 if (pipe_stdout != NULL)
307 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
308 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
310 if (pipe_stdin != NULL)
312 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
313 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
316 #if HAVE_WORKING_VFORK
325 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
330 #if HAVE_WORKING_VFORK
331 /* let's hope vfork actually works; for some extreme cases (including
332 a testcase) we need 'execvp' to have run before we return, since
333 we may send a signal to the process next and we don't want it
334 to be caught by OUR signal handler (but either by the default
335 handler or the actual handler as installed by the process itself). */
337 /* let's give the child process a chance to run execvp, 1s should
338 be plenty in practice */
339 if (pipe_stdout != NULL)
340 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
341 if (pipe_stdin != NULL)
342 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
345 gnunet_proc = GNUNET_OS_process_alloc ();
346 GNUNET_OS_process_set_pid (gnunet_proc, ret);
352 if (pipe_stdout != NULL)
354 GNUNET_break (0 == close (fd_stdout_read));
355 if (-1 == dup2(fd_stdout_write, 1))
356 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
357 GNUNET_break (0 == close (fd_stdout_write));
360 if (pipe_stdin != NULL)
363 GNUNET_break (0 == close (fd_stdin_write));
364 if (-1 == dup2(fd_stdin_read, 0))
365 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
366 GNUNET_break (0 == close (fd_stdin_read));
368 execvp (filename, argv);
369 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
377 PROCESS_INFORMATION proc;
378 GNUNET_OS_Process *gnunet_proc = NULL;
381 HANDLE stdout_handle;
383 char path[MAX_PATH + 1];
386 va_start (ap, filename);
387 while (NULL != (arg = va_arg (ap, char *)))
388 cmdlen = cmdlen + strlen (arg) + 3;
391 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
392 va_start (ap, filename);
393 while (NULL != (arg = va_arg (ap, char *)))
394 idx += sprintf (idx, "\"%s\" ", arg);
397 memset (&start, 0, sizeof (start));
398 start.cb = sizeof (start);
400 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
401 start.dwFlags |= STARTF_USESTDHANDLES;
403 if (pipe_stdin != NULL)
405 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
406 start.hStdInput = stdin_handle;
409 if (pipe_stdout != NULL)
411 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
412 start.hStdOutput = stdout_handle;
415 findresult = (int) FindExecutableA (filename, NULL, path);
416 if (findresult <= 32)
418 SetErrnoFromWinError (GetLastError ());
419 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
424 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start,
427 SetErrnoFromWinError (GetLastError ());
428 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
432 gnunet_proc = GNUNET_OS_process_alloc ();
433 GNUNET_OS_process_set_pid (gnunet_proc, proc.dwProcessId);
434 GNUNET_OS_process_set_handle (gnunet_proc, proc.hProcess);
436 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
438 CloseHandle (proc.hThread);
452 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
453 * must be NULL on platforms where dup is not supported
454 * @param filename name of the binary
455 * @param argv NULL-terminated list of arguments to the process
456 * @return process ID of the new process, -1 on error
459 GNUNET_OS_start_process_v (const int *lsocks,
460 const char *filename, char *const argv[])
466 GNUNET_OS_Process *gnunet_proc = NULL;
480 while (-1 != (k = lsocks[i++]))
481 GNUNET_array_append (lscp, ls, k);
482 GNUNET_array_append (lscp, ls, -1);
484 #if HAVE_WORKING_VFORK
493 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
497 #if HAVE_WORKING_VFORK
498 /* let's hope vfork actually works; for some extreme cases (including
499 a testcase) we need 'execvp' to have run before we return, since
500 we may send a signal to the process next and we don't want it
501 to be caught by OUR signal handler (but either by the default
502 handler or the actual handler as installed by the process itself). */
504 /* let's give the child process a chance to run execvp, 1s should
505 be plenty in practice */
508 gnunet_proc = GNUNET_OS_process_alloc ();
509 GNUNET_OS_process_set_pid (gnunet_proc, ret);
511 GNUNET_array_grow (lscp, ls, 0);
516 /* read systemd documentation... */
517 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
518 setenv ("LISTEN_PID", lpid, 1);
521 while (-1 != lscp[i])
524 while (-1 != lscp[j])
530 GNUNET_assert (-1 != k);
531 GNUNET_assert (0 == close (lscp[j]));
539 /* Bury any existing FD, no matter what; they should all be closed
540 on exec anyway and the important onces have been dup'ed away */
542 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
544 /* unset close-on-exec flag */
545 flags = fcntl (tgt, F_GETFD);
546 GNUNET_assert (flags >= 0);
547 flags &= ~FD_CLOEXEC;
549 (void) fcntl (tgt, F_SETFD, flags);
553 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
554 setenv ("LISTEN_FDS", fds, 1);
556 GNUNET_array_grow (lscp, ls, 0);
557 execvp (filename, argv);
558 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
561 char **arg, **non_const_argv;
565 PROCESS_INFORMATION proc;
567 char *non_const_filename = NULL;
569 GNUNET_OS_Process *gnunet_proc = NULL;
571 GNUNET_assert (lsocks == NULL);
572 /* Count the number of arguments */
573 arg = (char **) argv;
580 /* Allocate a copy argv */
581 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
583 /* Copy all argv strings */
585 arg = (char **) argv;
588 non_const_argv[argcount] = GNUNET_strdup (*arg);
592 non_const_argv[argcount] = NULL;
594 /* Fix .exe extension */
595 filenamelen = strlen (filename);
596 if (filenamelen <= 4 || stricmp (&filename[filenamelen - 4], ".exe") != 0)
598 non_const_filename = GNUNET_malloc (sizeof (char) * (filenamelen + 4 + 1));
599 non_const_filename = strcpy (non_const_filename, non_const_argv[0]);
600 strcat (non_const_filename, ".exe");
601 GNUNET_free (non_const_argv[0]);
602 non_const_argv[0] = non_const_filename;
605 non_const_filename = non_const_argv[0];
609 arg = non_const_argv;
612 cmdlen = cmdlen + strlen (*arg) + 3;
616 /* Allocate and create cmd */
617 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
618 arg = non_const_argv;
621 idx += sprintf (idx, "\"%s\" ", *arg);
625 memset (&start, 0, sizeof (start));
626 start.cb = sizeof (start);
629 (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
632 SetErrnoFromWinError (GetLastError ());
633 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
637 gnunet_proc = GNUNET_OS_process_alloc ();
638 GNUNET_OS_process_set_pid (gnunet_proc, proc.dwProcessId);
639 GNUNET_OS_process_set_handle (gnunet_proc, proc.hProcess);
641 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
643 CloseHandle (proc.hThread);
647 GNUNET_free (non_const_argv[--argcount]);
648 GNUNET_free (non_const_argv);
655 * Retrieve the status of a process
656 * @param proc process ID
657 * @param type status type
658 * @param code return code/signal number
659 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
662 GNUNET_OS_process_status (GNUNET_OS_Process *proc, enum GNUNET_OS_ProcessStatusType *type,
669 GNUNET_assert (0 != proc);
670 ret = waitpid (GNUNET_OS_process_get_pid (proc), &status, WNOHANG);
673 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
674 return GNUNET_SYSERR;
678 *type = GNUNET_OS_PROCESS_RUNNING;
682 if (GNUNET_OS_process_get_pid (proc) != ret)
684 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
685 return GNUNET_SYSERR;
687 if (WIFEXITED (status))
689 *type = GNUNET_OS_PROCESS_EXITED;
690 *code = WEXITSTATUS (status);
692 else if (WIFSIGNALED (status))
694 *type = GNUNET_OS_PROCESS_SIGNALED;
695 *code = WTERMSIG (status);
697 else if (WIFSTOPPED (status))
699 *type = GNUNET_OS_PROCESS_SIGNALED;
700 *code = WSTOPSIG (status);
703 else if (WIFCONTINUED (status))
705 *type = GNUNET_OS_PROCESS_RUNNING;
711 *type = GNUNET_OS_PROCESS_UNKNOWN;
716 DWORD c, error_code, ret;
718 h = GNUNET_OS_process_get_handle (proc);
719 ret = GNUNET_OS_process_get_pid (proc);
720 if (h == NULL || ret == 0)
722 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
723 return GNUNET_SYSERR;
726 h = GetCurrentProcess ();
729 ret = GetExitCodeProcess (h, &c);
730 error_code = GetLastError ();
731 if (ret == 0 || error_code != NO_ERROR)
733 SetErrnoFromWinError (error_code);
734 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
735 return GNUNET_SYSERR;
737 if (STILL_ACTIVE == c)
739 *type = GNUNET_OS_PROCESS_RUNNING;
743 *type = GNUNET_OS_PROCESS_EXITED;
752 * @param proc pointer to process structure
753 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
756 GNUNET_OS_process_wait (GNUNET_OS_Process *proc)
758 pid_t pid = GNUNET_OS_process_get_pid (proc);
760 if (pid != waitpid (pid, NULL, 0))
761 return GNUNET_SYSERR;
768 h = GNUNET_OS_process_get_handle (proc);
771 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", pid, h);
772 return GNUNET_SYSERR;
775 h = GetCurrentProcess ();
777 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
779 SetErrnoFromWinError (GetLastError ());
790 /* end of os_priority.c */