2 This file is part of GNUnet
3 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2011 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file util/os_priority.c
23 * @brief Methods to set process priority
28 #include "gnunet_util_lib.h"
32 #define LOG(kind, ...) GNUNET_log_from (kind, "util-os-priority", __VA_ARGS__)
34 #define LOG_STRERROR(kind, syscall) \
35 GNUNET_log_from_strerror (kind, "util-os-priority", syscall)
37 #define LOG_STRERROR_FILE(kind, syscall, filename) \
38 GNUNET_log_from_strerror_file (kind, "util-os-priority", syscall, filename)
40 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
43 struct GNUNET_OS_Process
52 * Pipe we use to signal the process.
53 * NULL if unused, or if process was deemed uncontrollable.
55 struct GNUNET_DISK_FileHandle *control_pipe;
60 * Handle for 'this' process.
62 static struct GNUNET_OS_Process current_process;
65 * Handle for the #parent_control_handler() Task.
67 static struct GNUNET_SCHEDULER_Task *pch;
70 * Handle for the #shutdown_pch() Task.
72 static struct GNUNET_SCHEDULER_Task *spch;
76 * This handler is called on shutdown to remove the #pch.
78 * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe
81 shutdown_pch (void *cls)
83 struct GNUNET_DISK_FileHandle *control_pipe = cls;
85 GNUNET_SCHEDULER_cancel (pch);
87 GNUNET_DISK_file_close (control_pipe);
93 * This handler is called when there are control data to be read on the pipe
95 * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe
98 parent_control_handler (void *cls)
100 struct GNUNET_DISK_FileHandle *control_pipe = cls;
106 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof(sig));
107 if (sizeof(sig) != ret)
110 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
111 LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing control pipe\n");
112 GNUNET_DISK_file_close (control_pipe);
114 GNUNET_SCHEDULER_cancel (spch);
118 pipe_fd = getenv (GNUNET_OS_CONTROL_PIPE);
119 GNUNET_assert ((NULL == pipe_fd) || (strlen (pipe_fd) <= 0));
120 LOG (GNUNET_ERROR_TYPE_DEBUG,
121 "Got control code %d from parent via pipe %s\n",
124 pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
126 &parent_control_handler,
128 GNUNET_SIGNAL_raise ((int) sig);
133 * Task that connects this process to its parent via pipe;
134 * essentially, the parent control handler will read signal numbers
135 * from the #GNUNET_OS_CONTROL_PIPE (as given in an environment
136 * variable) and raise those signals.
138 * @param cls closure (unused)
141 GNUNET_OS_install_parent_control_handler (void *cls)
145 struct GNUNET_DISK_FileHandle *control_pipe;
151 /* already done, we've been called twice... */
155 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
156 if ((NULL == env_buf) || (strlen (env_buf) <= 0))
158 LOG (GNUNET_ERROR_TYPE_DEBUG,
159 "Not installing a handler because $%s is empty\n",
160 GNUNET_OS_CONTROL_PIPE);
161 putenv (GNUNET_OS_CONTROL_PIPE "=");
165 pipe_fd = strtoull (env_buf, &env_buf_end, 16);
166 if ((0 != errno) || (env_buf == env_buf_end))
168 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "strtoull", env_buf);
169 putenv (GNUNET_OS_CONTROL_PIPE "=");
172 if (pipe_fd >= FD_SETSIZE)
174 LOG (GNUNET_ERROR_TYPE_ERROR,
175 "GNUNET_OS_CONTROL_PIPE `%s' contains garbage?\n",
177 putenv (GNUNET_OS_CONTROL_PIPE "=");
181 control_pipe = GNUNET_DISK_get_handle_from_int_fd ((int) pipe_fd);
183 if (NULL == control_pipe)
185 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
186 putenv (GNUNET_OS_CONTROL_PIPE "=");
189 LOG (GNUNET_ERROR_TYPE_DEBUG,
190 "Adding parent control handler pipe `%s' to the scheduler\n",
192 pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
194 &parent_control_handler,
196 spch = GNUNET_SCHEDULER_add_shutdown (&shutdown_pch, control_pipe);
197 putenv (GNUNET_OS_CONTROL_PIPE "=");
202 * Get process structure for current process
204 * The pointer it returns points to static memory location and must
205 * not be deallocated/closed.
207 * @return pointer to the process sturcutre for this process
209 struct GNUNET_OS_Process *
210 GNUNET_OS_process_current ()
212 current_process.pid = 0;
213 return ¤t_process;
218 * Sends a signal to the process
220 * @param proc pointer to process structure
222 * @return 0 on success, -1 on error
225 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
231 if (NULL != proc->control_pipe)
233 LOG (GNUNET_ERROR_TYPE_DEBUG,
234 "Sending signal %d to pid: %u via pipe\n",
237 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof(csig));
238 if (sizeof(csig) == ret)
241 /* pipe failed or non-existent, try other methods */
248 #if (SIGTERM != GNUNET_TERM_SIG)
249 case GNUNET_TERM_SIG:
251 LOG (GNUNET_ERROR_TYPE_DEBUG,
252 "Sending signal %d to pid: %u via system call\n",
255 return kill (proc->pid, sig);
257 LOG (GNUNET_ERROR_TYPE_DEBUG,
258 "Sending signal %d to pid: %u via system call\n",
261 return kill (proc->pid, sig);
267 * Get the pid of the process in question
269 * @param proc the process to get the pid of
271 * @return the current process id
274 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
281 * Cleans up process structure contents (OS-dependent) and deallocates
284 * @param proc pointer to process structure
287 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
289 if (NULL != proc->control_pipe)
290 GNUNET_DISK_file_close (proc->control_pipe);
297 * Open '/dev/null' and make the result the given
300 * @param target_fd desired FD to point to /dev/null
301 * @param flags open flags (O_RDONLY, O_WRONLY)
304 open_dev_null (int target_fd, int flags)
308 fd = open ("/dev/null", flags);
311 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null");
316 if (-1 == dup2 (fd, target_fd))
318 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
322 GNUNET_break (0 == close (fd));
329 * @param pipe_control should a pipe be used to send signals to the child?
330 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
331 * std handles of the parent are inherited by the child.
332 * pipe_stdin and pipe_stdout take priority over std_inheritance
333 * (when they are non-NULL).
334 * @param pipe_stdin pipe to use to send input to child process (or NULL)
335 * @param pipe_stdout pipe to use to get output from child process (or NULL)
336 * @param pipe_stderr pipe to use for stderr for child process (or NULL)
337 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
338 * must be NULL on platforms where dup is not supported
339 * @param filename name of the binary
340 * @param argv NULL-terminated list of arguments to the process
341 * @return process ID of the new process, -1 on error
343 static struct GNUNET_OS_Process *
344 start_process (int pipe_control,
345 enum GNUNET_OS_InheritStdioFlags std_inheritance,
346 struct GNUNET_DISK_PipeHandle *pipe_stdin,
347 struct GNUNET_DISK_PipeHandle *pipe_stdout,
348 struct GNUNET_DISK_PipeHandle *pipe_stderr,
349 const SOCKTYPE *lsocks,
350 const char *filename,
355 struct GNUNET_OS_Process *gnunet_proc;
356 struct GNUNET_DISK_FileHandle *childpipe_read;
357 struct GNUNET_DISK_FileHandle *childpipe_write;
358 int childpipe_read_fd;
374 GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL))
375 return NULL; /* not executable */
376 if (GNUNET_YES == pipe_control)
378 struct GNUNET_DISK_PipeHandle *childpipe;
379 int dup_childpipe_read_fd = -1;
381 childpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
382 if (NULL == childpipe)
385 GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ);
387 GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE);
388 GNUNET_DISK_pipe_close (childpipe);
389 if ((NULL == childpipe_read) || (NULL == childpipe_write) ||
390 (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read,
393 (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd))))
395 if (NULL != childpipe_read)
396 GNUNET_DISK_file_close (childpipe_read);
397 if (NULL != childpipe_write)
398 GNUNET_DISK_file_close (childpipe_write);
399 if (0 <= dup_childpipe_read_fd)
400 close (dup_childpipe_read_fd);
403 childpipe_read_fd = dup_childpipe_read_fd;
404 GNUNET_DISK_file_close (childpipe_read);
408 childpipe_write = NULL;
409 childpipe_read_fd = -1;
411 if (NULL != pipe_stdin)
415 GNUNET_DISK_internal_file_handle_ (
416 GNUNET_DISK_pipe_handle (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
421 GNUNET_DISK_internal_file_handle_ (
422 GNUNET_DISK_pipe_handle (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
426 if (NULL != pipe_stdout)
430 GNUNET_DISK_internal_file_handle_ (
431 GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_WRITE),
436 GNUNET_DISK_internal_file_handle_ (
437 GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
441 if (NULL != pipe_stderr)
445 GNUNET_DISK_internal_file_handle_ (
446 GNUNET_DISK_pipe_handle (pipe_stderr, GNUNET_DISK_PIPE_END_READ),
451 GNUNET_DISK_internal_file_handle_ (
452 GNUNET_DISK_pipe_handle (pipe_stderr, GNUNET_DISK_PIPE_END_WRITE),
461 while (-1 != (k = lsocks[i++]))
462 GNUNET_array_append (lscp, ls, k);
463 GNUNET_array_append (lscp, ls, -1);
466 /* see https://web.archive.org/web/20150924082249/gnunet.org/vfork */
474 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
475 GNUNET_array_grow (lscp, ls, 0);
476 if (NULL != childpipe_write)
477 GNUNET_DISK_file_close (childpipe_write);
478 if (0 <= childpipe_read_fd)
479 close (childpipe_read_fd);
485 unsetenv (GNUNET_OS_CONTROL_PIPE);
486 gnunet_proc = GNUNET_new (struct GNUNET_OS_Process);
487 gnunet_proc->pid = ret;
488 gnunet_proc->control_pipe = childpipe_write;
489 if (GNUNET_YES == pipe_control)
491 close (childpipe_read_fd);
493 GNUNET_array_grow (lscp, ls, 0);
496 if (0 <= childpipe_read_fd)
500 /* due to vfork, we must NOT free memory on DARWIN! */
501 GNUNET_DISK_file_close (childpipe_write);
503 snprintf (fdbuf, 100, "%x", childpipe_read_fd);
504 setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1);
507 unsetenv (GNUNET_OS_CONTROL_PIPE);
508 if (NULL != pipe_stdin)
510 GNUNET_break (0 == close (fd_stdin_write));
511 if (-1 == dup2 (fd_stdin_read, 0))
512 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
513 GNUNET_break (0 == close (fd_stdin_read));
515 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
517 GNUNET_break (0 == close (0));
518 open_dev_null (0, O_RDONLY);
520 if (NULL != pipe_stdout)
522 GNUNET_break (0 == close (fd_stdout_read));
523 if (-1 == dup2 (fd_stdout_write, 1))
524 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
525 GNUNET_break (0 == close (fd_stdout_write));
527 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
529 GNUNET_break (0 == close (1));
530 open_dev_null (1, O_WRONLY);
532 if (NULL != pipe_stderr)
534 GNUNET_break (0 == close (fd_stderr_read));
535 if (-1 == dup2 (fd_stderr_write, 2))
536 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
537 GNUNET_break (0 == close (fd_stderr_write));
539 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
541 GNUNET_break (0 == close (2));
542 open_dev_null (2, O_WRONLY);
546 /* read systemd documentation... */
549 while (-1 != lscp[i])
552 while (-1 != lscp[j])
558 GNUNET_assert (-1 != k);
559 GNUNET_assert (0 == close (lscp[j]));
567 /* Bury any existing FD, no matter what; they should all be closed
568 * on exec anyway and the important onces have been dup'ed away */
570 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
572 /* unset close-on-exec flag */
573 flags = fcntl (tgt, F_GETFD);
574 GNUNET_assert (flags >= 0);
575 flags &= ~FD_CLOEXEC;
577 (void) fcntl (tgt, F_SETFD, flags);
581 GNUNET_snprintf (fds, sizeof(fds), "%u", i);
582 setenv ("LISTEN_FDS", fds, 1);
585 /* due to vfork, we must NOT free memory on DARWIN! */
586 GNUNET_array_grow (lscp, ls, 0);
588 execvp (filename, argv);
589 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
597 * @param pipe_control should a pipe be used to send signals to the child?
598 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
599 * @param pipe_stdin pipe to use to send input to child process (or NULL)
600 * @param pipe_stdout pipe to use to get output from child process (or NULL)
601 * @param pipe_stderr pipe to use to get output from child process (or NULL)
602 * @param filename name of the binary
603 * @param argv NULL-terminated array of arguments to the process
604 * @return pointer to process structure of the new process, NULL on error
606 struct GNUNET_OS_Process *
607 GNUNET_OS_start_process_vap (int pipe_control,
608 enum GNUNET_OS_InheritStdioFlags std_inheritance,
609 struct GNUNET_DISK_PipeHandle *pipe_stdin,
610 struct GNUNET_DISK_PipeHandle *pipe_stdout,
611 struct GNUNET_DISK_PipeHandle *pipe_stderr,
612 const char *filename,
615 return start_process (pipe_control,
629 * @param pipe_control should a pipe be used to send signals to the child?
630 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
631 * @param pipe_stdin pipe to use to send input to child process (or NULL)
632 * @param pipe_stdout pipe to use to get output from child process (or NULL)
633 * @param pipe_stderr pipe to use to get output from child process (or NULL)
634 * @param filename name of the binary
635 * @param va NULL-terminated list of arguments to the process
636 * @return pointer to process structure of the new process, NULL on error
638 struct GNUNET_OS_Process *
639 GNUNET_OS_start_process_va (int pipe_control,
640 enum GNUNET_OS_InheritStdioFlags std_inheritance,
641 struct GNUNET_DISK_PipeHandle *pipe_stdin,
642 struct GNUNET_DISK_PipeHandle *pipe_stdout,
643 struct GNUNET_DISK_PipeHandle *pipe_stderr,
644 const char *filename,
647 struct GNUNET_OS_Process *ret;
654 while (NULL != va_arg (ap, char *))
657 argv = GNUNET_malloc (sizeof(char *) * (argc + 1));
660 while (NULL != (argv[argc] = va_arg (ap, char *)))
663 ret = GNUNET_OS_start_process_vap (pipe_control,
678 * @param pipe_control should a pipe be used to send signals to the child?
679 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
680 * @param pipe_stdin pipe to use to send input to child process (or NULL)
681 * @param pipe_stdout pipe to use to get output from child process (or NULL)
682 * @param filename name of the binary
683 * @param ... NULL-terminated list of arguments to the process
684 * @return pointer to process structure of the new process, NULL on error
686 struct GNUNET_OS_Process *
687 GNUNET_OS_start_process (int pipe_control,
688 enum GNUNET_OS_InheritStdioFlags std_inheritance,
689 struct GNUNET_DISK_PipeHandle *pipe_stdin,
690 struct GNUNET_DISK_PipeHandle *pipe_stdout,
691 struct GNUNET_DISK_PipeHandle *pipe_stderr,
692 const char *filename,
695 struct GNUNET_OS_Process *ret;
698 va_start (ap, filename);
699 ret = GNUNET_OS_start_process_va (pipe_control,
714 * @param pipe_control should a pipe be used to send signals to the child?
715 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
716 * std handles of the parent are inherited by the child.
717 * pipe_stdin and pipe_stdout take priority over std_inheritance
718 * (when they are non-NULL).
719 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
720 * must be NULL on platforms where dup is not supported
721 * @param filename name of the binary
722 * @param argv NULL-terminated list of arguments to the process
723 * @return process ID of the new process, -1 on error
725 struct GNUNET_OS_Process *
726 GNUNET_OS_start_process_v (int pipe_control,
727 enum GNUNET_OS_InheritStdioFlags std_inheritance,
728 const SOCKTYPE *lsocks,
729 const char *filename,
732 return start_process (pipe_control,
744 * Start a process. This function is similar to the GNUNET_OS_start_process_*
745 * except that the filename and arguments can have whole strings which contain
746 * the arguments. These arguments are to be separated by spaces and are parsed
747 * in the order they appear. Arguments containing spaces can be used by
748 * quoting them with @em ".
750 * @param pipe_control should a pipe be used to send signals to the child?
751 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
752 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
753 * must be NULL on platforms where dup is not supported
754 * @param filename name of the binary. It is valid to have the arguments
755 * in this string when they are separated by spaces.
756 * @param ... more arguments. Should be of type `char *`. It is valid
757 * to have the arguments in these strings when they are separated by
758 * spaces. The last argument MUST be NULL.
759 * @return pointer to process structure of the new process, NULL on error
761 struct GNUNET_OS_Process *
762 GNUNET_OS_start_process_s (int pipe_control,
763 unsigned int std_inheritance,
764 const SOCKTYPE *lsocks,
765 const char *filename,
770 unsigned int argv_size;
776 struct GNUNET_OS_Process *proc;
783 va_start (ap, filename);
790 while ('\0' != *rpos)
799 if ((' ' == *rpos) && (0 == quote_on))
808 if ((NULL == last) && ('\0' != *rpos)) // FIXME: == or !=?
816 while (NULL != (arg = (va_arg (ap, const char *))));
819 argv = GNUNET_malloc (argv_size * sizeof(char *));
821 va_start (ap, filename);
826 cp = GNUNET_strdup (arg);
838 if ((' ' == *pos) && (0 == quote_on))
842 argv[argv_size++] = GNUNET_strdup (last);
848 if ((NULL == last) && ('\0' != *pos)) // FIXME: == or !=?
854 argv[argv_size++] = GNUNET_strdup (last);
858 while (NULL != (arg = (va_arg (ap, const char *))));
860 argv[argv_size] = NULL;
862 for (i = 0; i < argv_size; i++)
864 len = strlen (argv[i]);
865 if ((argv[i][0] == '"') && (argv[i][len - 1] == '"'))
867 memmove (&argv[i][0], &argv[i][1], len - 2);
868 argv[i][len - 2] = '\0';
871 binary_path = argv[0];
872 proc = GNUNET_OS_start_process_v (pipe_control,
877 while (argv_size > 0)
878 GNUNET_free (argv[--argv_size]);
885 * Retrieve the status of a process, waiting on it if dead.
886 * Nonblocking version.
888 * @param proc process ID
889 * @param type status type
890 * @param code return code/signal number
891 * @param options WNOHANG if non-blocking is desired
892 * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
895 process_status (struct GNUNET_OS_Process *proc,
896 enum GNUNET_OS_ProcessStatusType *type,
903 GNUNET_assert (0 != proc);
904 ret = waitpid (proc->pid, &status, options);
907 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
908 return GNUNET_SYSERR;
912 *type = GNUNET_OS_PROCESS_RUNNING;
916 if (proc->pid != ret)
918 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
919 return GNUNET_SYSERR;
921 if (WIFEXITED (status))
923 *type = GNUNET_OS_PROCESS_EXITED;
924 *code = WEXITSTATUS (status);
926 else if (WIFSIGNALED (status))
928 *type = GNUNET_OS_PROCESS_SIGNALED;
929 *code = WTERMSIG (status);
931 else if (WIFSTOPPED (status))
933 *type = GNUNET_OS_PROCESS_SIGNALED;
934 *code = WSTOPSIG (status);
937 else if (WIFCONTINUED (status))
939 *type = GNUNET_OS_PROCESS_RUNNING;
945 *type = GNUNET_OS_PROCESS_UNKNOWN;
954 * Retrieve the status of a process, waiting on it if dead.
955 * Nonblocking version.
957 * @param proc process ID
958 * @param type status type
959 * @param code return code/signal number
960 * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
963 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
964 enum GNUNET_OS_ProcessStatusType *type,
967 return process_status (proc, type, code, WNOHANG);
972 * Retrieve the status of a process, waiting on it if dead.
975 * @param proc pointer to process structure
976 * @param type status type
977 * @param code return code/signal number
978 * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
981 GNUNET_OS_process_wait_status (struct GNUNET_OS_Process *proc,
982 enum GNUNET_OS_ProcessStatusType *type,
985 return process_status (proc, type, code, 0);
990 * Wait for a process to terminate. The return code is discarded.
991 * You must not use #GNUNET_OS_process_status() on the same process
992 * after calling this function! This function is blocking and should
993 * thus only be used if the child process is known to have terminated
994 * or to terminate very soon.
996 * @param proc pointer to process structure
997 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1000 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1002 pid_t pid = proc->pid;
1005 while ((pid != (ret = waitpid (pid, NULL, 0))) && (EINTR == errno))
1009 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1010 return GNUNET_SYSERR;
1017 * Handle to a command.
1019 struct GNUNET_OS_CommandHandle
1024 struct GNUNET_OS_Process *eip;
1027 * Handle to the output pipe.
1029 struct GNUNET_DISK_PipeHandle *opipe;
1032 * Read-end of output pipe.
1034 const struct GNUNET_DISK_FileHandle *r;
1037 * Function to call on each line of output.
1039 GNUNET_OS_LineProcessor proc;
1042 * Closure for @e proc.
1047 * Buffer for the output.
1052 * Task reading from pipe.
1054 struct GNUNET_SCHEDULER_Task *rtask;
1059 struct GNUNET_TIME_Absolute timeout;
1062 * Current read offset in buf.
1069 * Stop/kill a command. Must ONLY be called either from
1070 * the callback after 'NULL' was passed for 'line' *OR*
1071 * from an independent task (not within the line processor).
1073 * @param cmd handle to the process
1076 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1078 if (NULL != cmd->proc)
1080 GNUNET_assert (NULL != cmd->rtask);
1081 GNUNET_SCHEDULER_cancel (cmd->rtask);
1083 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1084 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1085 GNUNET_OS_process_destroy (cmd->eip);
1086 GNUNET_DISK_pipe_close (cmd->opipe);
1092 * Read from the process and call the line processor.
1094 * @param cls the `struct GNUNET_OS_CommandHandle *`
1097 cmd_read (void *cls)
1099 struct GNUNET_OS_CommandHandle *cmd = cls;
1100 const struct GNUNET_SCHEDULER_TaskContext *tc;
1101 GNUNET_OS_LineProcessor proc;
1106 tc = GNUNET_SCHEDULER_get_task_context ();
1107 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1112 proc (cmd->proc_cls, NULL);
1115 ret = GNUNET_DISK_file_read (cmd->r,
1116 &cmd->buf[cmd->off],
1117 sizeof(cmd->buf) - cmd->off);
1120 if ((cmd->off > 0) && (cmd->off < sizeof(cmd->buf)))
1122 cmd->buf[cmd->off] = '\0';
1123 cmd->proc (cmd->proc_cls, cmd->buf);
1127 proc (cmd->proc_cls, NULL);
1130 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1135 cmd->proc (cmd->proc_cls, cmd->buf);
1136 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1137 cmd->off -= (end + 1 - cmd->buf);
1138 end = memchr (cmd->buf, '\n', cmd->off);
1141 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (
1150 * Run the given command line and call the given function
1151 * for each line of the output.
1153 * @param proc function to call for each line of the output
1154 * @param proc_cls closure for @a proc
1155 * @param timeout when to time out
1156 * @param binary command to run
1157 * @param ... arguments to command
1158 * @return NULL on error
1160 struct GNUNET_OS_CommandHandle *
1161 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1163 struct GNUNET_TIME_Relative timeout,
1167 struct GNUNET_OS_CommandHandle *cmd;
1168 struct GNUNET_OS_Process *eip;
1169 struct GNUNET_DISK_PipeHandle *opipe;
1172 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1175 va_start (ap, binary);
1176 /* redirect stdout, don't inherit stderr/stdin */
1178 GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, NULL, binary, ap);
1182 GNUNET_DISK_pipe_close (opipe);
1185 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1186 cmd = GNUNET_new (struct GNUNET_OS_CommandHandle);
1187 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1191 cmd->proc_cls = proc_cls;
1192 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1193 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1198 /* end of os_priority.c */