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 * Make seaspider happy.
135 #define DWORD_WINAPI DWORD WINAPI
138 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
139 * @param proc pointer to process structure
142 ChildWaitThread (void *arg)
144 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
145 WaitForSingleObject (proc->handle, INFINITE);
147 if (w32_sigchld_handler)
148 w32_sigchld_handler ();
155 * Set process priority
157 * @param proc pointer to process structure
158 * @param prio priority value
159 * @return GNUNET_OK on success, GNUNET_SYSERR on error
162 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
163 enum GNUNET_SCHEDULER_Priority prio)
167 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
168 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
171 /* convert to MINGW/Unix values */
174 case GNUNET_SCHEDULER_PRIORITY_UI:
175 case GNUNET_SCHEDULER_PRIORITY_URGENT:
177 rprio = HIGH_PRIORITY_CLASS;
183 case GNUNET_SCHEDULER_PRIORITY_HIGH:
185 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
191 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
193 rprio = NORMAL_PRIORITY_CLASS;
199 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
201 rprio = BELOW_NORMAL_PRIORITY_CLASS;
207 case GNUNET_SCHEDULER_PRIORITY_IDLE:
209 rprio = IDLE_PRIORITY_CLASS;
216 return GNUNET_SYSERR;
219 /* Set process priority */
222 HANDLE h = proc->handle;
223 GNUNET_assert (h != NULL);
224 SetPriorityClass (h, rprio);
231 (pid == getpid () ) )
234 int delta = rprio - have;
237 (rprio == nice (delta)) &&
240 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
241 GNUNET_ERROR_TYPE_BULK, "nice");
242 return GNUNET_SYSERR;
247 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
249 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
250 GNUNET_ERROR_TYPE_BULK, "setpriority");
251 return GNUNET_SYSERR;
255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
256 "Priority management not availabe for this platform\n");
264 * @param pipe_stdin pipe to use to send input to child process (or NULL)
265 * @param pipe_stdout pipe to use to get output from child process (or NULL)
266 * @param filename name of the binary
267 * @param ... NULL-terminated list of arguments to the process
268 * @return pointer to process structure of the new process, NULL on error
270 struct GNUNET_OS_Process *
271 GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin,
272 struct GNUNET_DISK_PipeHandle *pipe_stdout,
273 const char *filename, ...)
279 struct GNUNET_OS_Process *gnunet_proc = NULL;
288 va_start (ap, filename);
289 while (NULL != va_arg (ap, char *))
292 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
294 va_start (ap, filename);
295 while (NULL != (argv[argc] = va_arg (ap, char *)))
298 if (pipe_stdout != NULL)
300 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &fd_stdout_write, sizeof (int));
301 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_READ), &fd_stdout_read, sizeof (int));
303 if (pipe_stdin != NULL)
305 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &fd_stdin_read, sizeof (int));
306 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), &fd_stdin_write, sizeof (int));
309 #if HAVE_WORKING_VFORK
318 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
323 #if HAVE_WORKING_VFORK
324 /* let's hope vfork actually works; for some extreme cases (including
325 a testcase) we need 'execvp' to have run before we return, since
326 we may send a signal to the process next and we don't want it
327 to be caught by OUR signal handler (but either by the default
328 handler or the actual handler as installed by the process itself). */
330 /* let's give the child process a chance to run execvp, 1s should
331 be plenty in practice */
332 if (pipe_stdout != NULL)
333 GNUNET_DISK_pipe_close_end(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
334 if (pipe_stdin != NULL)
335 GNUNET_DISK_pipe_close_end(pipe_stdin, GNUNET_DISK_PIPE_END_READ);
338 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
339 gnunet_proc->pid = ret;
345 if (pipe_stdout != NULL)
347 GNUNET_break (0 == close (fd_stdout_read));
348 if (-1 == dup2(fd_stdout_write, 1))
349 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
350 GNUNET_break (0 == close (fd_stdout_write));
353 if (pipe_stdin != NULL)
356 GNUNET_break (0 == close (fd_stdin_write));
357 if (-1 == dup2(fd_stdin_read, 0))
358 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
359 GNUNET_break (0 == close (fd_stdin_read));
361 execvp (filename, argv);
362 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
369 PROCESS_INFORMATION proc;
370 struct GNUNET_OS_Process *gnunet_proc = NULL;
373 HANDLE stdout_handle;
375 char path[MAX_PATH + 1];
378 va_start (ap, filename);
379 while (NULL != (arg = va_arg (ap, char *)))
380 cmdlen = cmdlen + strlen (arg) + 3;
383 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
384 va_start (ap, filename);
385 while (NULL != (arg = va_arg (ap, char *)))
386 idx += sprintf (idx, "\"%s\" ", arg);
389 memset (&start, 0, sizeof (start));
390 start.cb = sizeof (start);
392 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
393 start.dwFlags |= STARTF_USESTDHANDLES;
395 if (pipe_stdin != NULL)
397 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdin, GNUNET_DISK_PIPE_END_READ), &stdin_handle, sizeof (HANDLE));
398 start.hStdInput = stdin_handle;
401 if (pipe_stdout != NULL)
403 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle(pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), &stdout_handle, sizeof (HANDLE));
404 start.hStdOutput = stdout_handle;
407 if (32 >= (int) FindExecutableA (filename, NULL, path))
409 SetErrnoFromWinError (GetLastError ());
410 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
415 (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start,
418 SetErrnoFromWinError (GetLastError ());
419 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
423 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
424 gnunet_proc->pid = proc.dwProcessId;
425 gnunet_proc->handle = proc.hProcess;
427 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
429 CloseHandle (proc.hThread);
443 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
444 * must be NULL on platforms where dup is not supported
445 * @param filename name of the binary
446 * @param argv NULL-terminated list of arguments to the process
447 * @return process ID of the new process, -1 on error
449 struct GNUNET_OS_Process *
450 GNUNET_OS_start_process_v (const int *lsocks,
451 const char *filename, char *const argv[])
457 struct GNUNET_OS_Process *gnunet_proc = NULL;
471 while (-1 != (k = lsocks[i++]))
472 GNUNET_array_append (lscp, ls, k);
473 GNUNET_array_append (lscp, ls, -1);
475 #if HAVE_WORKING_VFORK
484 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
488 #if HAVE_WORKING_VFORK
489 /* let's hope vfork actually works; for some extreme cases (including
490 a testcase) we need 'execvp' to have run before we return, since
491 we may send a signal to the process next and we don't want it
492 to be caught by OUR signal handler (but either by the default
493 handler or the actual handler as installed by the process itself). */
495 /* let's give the child process a chance to run execvp, 1s should
496 be plenty in practice */
499 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
500 gnunet_proc->pid = ret;
502 GNUNET_array_grow (lscp, ls, 0);
507 /* read systemd documentation... */
508 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid());
509 setenv ("LISTEN_PID", lpid, 1);
512 while (-1 != lscp[i])
515 while (-1 != lscp[j])
521 GNUNET_assert (-1 != k);
522 GNUNET_assert (0 == close (lscp[j]));
530 /* Bury any existing FD, no matter what; they should all be closed
531 on exec anyway and the important onces have been dup'ed away */
533 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
535 /* unset close-on-exec flag */
536 flags = fcntl (tgt, F_GETFD);
537 GNUNET_assert (flags >= 0);
538 flags &= ~FD_CLOEXEC;
540 (void) fcntl (tgt, F_SETFD, flags);
544 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
545 setenv ("LISTEN_FDS", fds, 1);
547 GNUNET_array_grow (lscp, ls, 0);
548 execvp (filename, argv);
549 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
552 char **arg, **non_const_argv;
556 PROCESS_INFORMATION proc;
558 char non_const_filename[MAX_PATH +1];
559 struct GNUNET_OS_Process *gnunet_proc = NULL;
561 GNUNET_assert (lsocks == NULL);
563 if (32 >= (int) FindExecutableA (filename, NULL, non_const_filename))
565 SetErrnoFromWinError (GetLastError ());
566 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename);
570 /* Count the number of arguments */
571 arg = (char **) argv;
578 /* Allocate a copy argv */
579 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
581 /* Copy all argv strings */
583 arg = (char **) argv;
586 non_const_argv[argcount] = GNUNET_strdup (*arg);
590 non_const_argv[argcount] = NULL;
594 arg = non_const_argv;
597 cmdlen = cmdlen + strlen (*arg) + 3;
601 /* Allocate and create cmd */
602 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
603 arg = non_const_argv;
606 idx += sprintf (idx, "\"%s\" ", *arg);
610 memset (&start, 0, sizeof (start));
611 start.cb = sizeof (start);
614 (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
617 SetErrnoFromWinError (GetLastError ());
618 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
622 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
623 gnunet_proc->pid = proc.dwProcessId;
624 gnunet_proc->handle = proc.hProcess;
626 CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL);
628 CloseHandle (proc.hThread);
632 GNUNET_free (non_const_argv[--argcount]);
633 GNUNET_free (non_const_argv);
640 * Retrieve the status of a process
641 * @param proc process ID
642 * @param type status type
643 * @param code return code/signal number
644 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
647 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
648 enum GNUNET_OS_ProcessStatusType *type,
655 GNUNET_assert (0 != proc);
656 ret = waitpid (proc->pid, &status, WNOHANG);
659 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
660 return GNUNET_SYSERR;
664 *type = GNUNET_OS_PROCESS_RUNNING;
668 if (proc->pid != ret)
670 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
671 return GNUNET_SYSERR;
673 if (WIFEXITED (status))
675 *type = GNUNET_OS_PROCESS_EXITED;
676 *code = WEXITSTATUS (status);
678 else if (WIFSIGNALED (status))
680 *type = GNUNET_OS_PROCESS_SIGNALED;
681 *code = WTERMSIG (status);
683 else if (WIFSTOPPED (status))
685 *type = GNUNET_OS_PROCESS_SIGNALED;
686 *code = WSTOPSIG (status);
689 else if (WIFCONTINUED (status))
691 *type = GNUNET_OS_PROCESS_RUNNING;
697 *type = GNUNET_OS_PROCESS_UNKNOWN;
702 DWORD c, error_code, ret;
706 if (h == NULL || ret == 0)
708 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n", ret, h);
709 return GNUNET_SYSERR;
712 h = GetCurrentProcess ();
715 ret = GetExitCodeProcess (h, &c);
716 error_code = GetLastError ();
717 if (ret == 0 || error_code != NO_ERROR)
719 SetErrnoFromWinError (error_code);
720 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
721 return GNUNET_SYSERR;
723 if (STILL_ACTIVE == c)
725 *type = GNUNET_OS_PROCESS_RUNNING;
729 *type = GNUNET_OS_PROCESS_EXITED;
738 * @param proc pointer to process structure
739 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
742 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
746 pid_t pid = proc->pid;
747 if (pid != waitpid (pid, NULL, 0))
748 return GNUNET_SYSERR;
757 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
758 "Invalid process information {%d, %08X}\n",
761 return GNUNET_SYSERR;
764 h = GetCurrentProcess ();
766 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
768 SetErrnoFromWinError (GetLastError ());
779 /* end of os_priority.c */