2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2011 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"
30 #include "gnunet_scheduler_lib.h"
31 #include "gnunet_strings_lib.h"
32 #include "gnunet_crypto_lib.h"
36 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
38 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
40 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
42 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
45 struct GNUNET_OS_Process
60 * Pipe we use to signal the process (if used).
62 struct GNUNET_DISK_FileHandle *control_pipe;
65 * Name of the pipe, NULL for none.
72 * Handle for 'this' process.
74 static struct GNUNET_OS_Process current_process;
77 /* MinGW version of named pipe API */
80 * Creates a named pipe/FIFO and opens it
82 * @param fn pointer to the name of the named pipe or to NULL,
83 * possibly updated to the new name (or free'd)
84 * @param flags open flags
85 * @param perm access permissions
86 * @return pipe handle on success, NULL on error
88 static struct GNUNET_DISK_FileHandle *
89 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
90 enum GNUNET_DISK_AccessPermissions perm)
92 struct GNUNET_DISK_FileHandle *ret;
98 if (flags & GNUNET_DISK_OPEN_READWRITE)
99 openMode = PIPE_ACCESS_DUPLEX;
100 else if (flags & GNUNET_DISK_OPEN_READ)
101 openMode = PIPE_ACCESS_INBOUND;
102 else if (flags & GNUNET_DISK_OPEN_WRITE)
103 openMode = PIPE_ACCESS_OUTBOUND;
104 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
105 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
114 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
115 LOG (GNUNET_ERROR_TYPE_DEBUG,
116 "Trying to create an instance of named pipe `%s'\n", name);
117 /* 1) This might work just fine with UTF-8 strings as it is.
118 * 2) This is only used by GNUnet itself, and only with latin names.
120 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
121 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
126 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
127 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
129 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
131 h = CreateNamedPipe (*fn,
132 openMode | FILE_FLAG_OVERLAPPED |
133 FILE_FLAG_FIRST_PIPE_INSTANCE,
134 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
137 error_code = GetLastError ();
138 GNUNET_free_non_null (name);
139 /* don't re-set name to NULL yet */
140 if (INVALID_HANDLE_VALUE == h)
142 SetErrnoFromWinError (error_code);
143 LOG (GNUNET_ERROR_TYPE_DEBUG,
144 "Pipe creation have failed because of %d, errno is %d\n", error_code,
148 LOG (GNUNET_ERROR_TYPE_DEBUG,
149 "Pipe was to be unique, considering re-creation\n");
152 if ( (ERROR_ACCESS_DENIED != error_code) && (ERROR_PIPE_BUSY != error_code) )
154 LOG (GNUNET_ERROR_TYPE_DEBUG,
155 "Pipe name was not unique, trying again\n");
162 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
164 ret->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
165 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
166 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
167 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
168 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
174 * Opens already existing named pipe/FIFO
176 * @param fn name of an existing named pipe
177 * @param flags open flags
178 * @return pipe handle on success, NULL on error
180 static struct GNUNET_DISK_FileHandle *
181 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
183 struct GNUNET_DISK_FileHandle *ret;
188 if (flags & GNUNET_DISK_OPEN_READWRITE)
189 openMode = GENERIC_WRITE | GENERIC_READ;
190 else if (flags & GNUNET_DISK_OPEN_READ)
191 openMode = GENERIC_READ;
192 else if (flags & GNUNET_DISK_OPEN_WRITE)
193 openMode = GENERIC_WRITE;
195 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
196 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
197 if (INVALID_HANDLE_VALUE == h)
199 SetErrnoFromWinError (GetLastError ());
203 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
205 ret->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
206 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
207 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
208 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
209 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
215 /* UNIX version of named-pipe API */
218 * Clean up a named pipe and the directory it was placed in.
220 * @param fn name of the pipe
223 cleanup_npipe (const char *fn)
228 if (0 != unlink (fn))
229 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
230 dn = GNUNET_strdup (fn);
233 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
239 * Setup a named pipe.
241 * @param fn where to store the name of the new pipe,
242 * if *fn is non-null, the name of the pipe to setup
243 * @return GNUNET_OK on success
246 npipe_setup (char **fn)
250 /* FIXME: hardwired '/tmp' path... is bad */
251 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
253 if (NULL == mkdtemp (dir))
255 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
256 return GNUNET_SYSERR;
258 GNUNET_asprintf (fn, "%s/child-control", dir);
260 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
261 return GNUNET_SYSERR;
267 * Open an existing named pipe.
269 * @param fn name of the file
270 * @param flags flags to use
271 * @return NULL on error
273 static struct GNUNET_DISK_FileHandle *
274 npipe_open (const char *fn,
275 enum GNUNET_DISK_OpenFlags flags)
277 struct GNUNET_DISK_FileHandle *ret;
282 /* 200 * 5ms = 1s at most */
285 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
286 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
288 /* as this is for killing a child process via pipe and it is conceivable that
289 the child process simply didn't finish starting yet, we do some sleeping
290 (which is obviously usually not allowed). We can't select on the FD as
291 'open' fails, and we probably shouldn't just "ignore" the error, so wait
292 and retry a few times is likely the best method; our process API doesn't
293 support continuations, so we need to sleep directly... */
295 req.tv_nsec = 5000000; /* 5ms */
296 (void) nanosleep (&req, NULL);
300 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
301 (flags == GNUNET_DISK_OPEN_READ)
302 ? _("Failed to open named pipe `%s' for reading: %s\n")
303 : _("Failed to open named pipe `%s' for writing: %s\n"),
308 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
316 * This handler is called when there are control data to be read on the pipe
318 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
319 * @param tc scheduler context
322 parent_control_handler (void *cls,
323 const struct GNUNET_SCHEDULER_TaskContext *tc)
325 struct GNUNET_DISK_FileHandle *control_pipe = cls;
330 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
332 if (0 != (tc->reason &
333 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
335 GNUNET_DISK_file_close (control_pipe);
339 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
340 if (sizeof (sig) != ret)
343 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
344 LOG (GNUNET_ERROR_TYPE_WARNING, "Closing control pipe\n");
345 GNUNET_DISK_file_close (control_pipe);
349 pipe_name = getenv (GNUNET_OS_CONTROL_PIPE);
350 GNUNET_assert ( (NULL == pipe_name) || (strlen (pipe_name) <= 0) );
351 LOG (GNUNET_ERROR_TYPE_DEBUG,
352 "Got control code %d from parent via pipe %s\n", sig, pipe_name);
353 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
354 control_pipe, &parent_control_handler,
361 * Task that connects this process to its parent via pipe;
362 * essentially, the parent control handler will read signal numbers
363 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
364 * variable) and raise those signals.
366 * @param cls closure (unused)
367 * @param tc scheduler context (unused)
370 GNUNET_OS_install_parent_control_handler (void *cls,
372 GNUNET_SCHEDULER_TaskContext *tc)
375 struct GNUNET_DISK_FileHandle *control_pipe;
377 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
378 if ( (NULL == env_buf) || (strlen (env_buf) <= 0) )
380 LOG (GNUNET_ERROR_TYPE_DEBUG,
381 "Not installing a handler because $%s is empty\n",
382 GNUNET_OS_CONTROL_PIPE);
383 putenv ("GNUNET_OS_CONTROL_PIPE=");
387 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
388 if (NULL == control_pipe)
390 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
391 putenv ("GNUNET_OS_CONTROL_PIPE=");
394 LOG (GNUNET_ERROR_TYPE_DEBUG,
395 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
396 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
397 &parent_control_handler, control_pipe);
398 putenv ("GNUNET_OS_CONTROL_PIPE=");
403 * Get process structure for current process
405 * The pointer it returns points to static memory location and must not be
408 * @return pointer to the process sturcutre for this process
410 struct GNUNET_OS_Process *
411 GNUNET_OS_process_current ()
414 current_process.pid = GetCurrentProcessId ();
415 current_process.handle = GetCurrentProcess ();
417 current_process.pid = 0;
419 return ¤t_process;
424 * Sends a signal to the process
426 * @param proc pointer to process structure
428 * @return 0 on success, -1 on error
431 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
438 if ( (NULL == proc->control_pipe) &&
439 (NULL != proc->childpipename) )
440 proc->control_pipe = npipe_open (proc->childpipename,
441 GNUNET_DISK_OPEN_WRITE);
443 if (NULL != proc->control_pipe)
445 LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending signal %d to pid: %u via pipe\n", sig, proc->pid);
446 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
447 if (sizeof (csig) == ret)
450 /* pipe failed or non-existent, try other methods */
459 #if WINDOWS && !defined(__CYGWIN__)
462 int must_kill = GNUNET_YES;
463 if (0 != GetExitCodeProcess (proc->handle, &exitcode))
464 must_kill = (exitcode == STILL_ACTIVE) ? GNUNET_YES : GNUNET_NO;
465 if (GNUNET_YES == must_kill)
466 if (0 == SafeTerminateProcess (proc->handle, 0, 0))
468 DWORD error_code = GetLastError ();
469 if ((error_code != WAIT_TIMEOUT) && (error_code != ERROR_PROCESS_ABORTED))
471 LOG ((error_code == ERROR_ACCESS_DENIED) ?
472 GNUNET_ERROR_TYPE_INFO : GNUNET_ERROR_TYPE_WARNING,
473 "SafeTermiateProcess failed with code %lu\n", error_code);
474 /* The problem here is that a process that is already dying
475 * might cause SafeTerminateProcess to fail with
476 * ERROR_ACCESS_DENIED, but the process WILL die eventually.
477 * If we really had a permissions problem, hanging up (which
478 * is what will happen in process_wait() in that case) is
481 if (ERROR_ACCESS_DENIED == error_code)
487 SetErrnoFromWinError (error_code);
495 LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending signal %d to pid: %u via system call\n", sig, proc->pid);
496 return PLIBC_KILL (proc->pid, sig);
503 LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending signal %d to pid: %u via system call\n", sig, proc->pid);
504 return PLIBC_KILL (proc->pid, sig);
510 * Get the pid of the process in question
512 * @param proc the process to get the pid of
514 * @return the current process id
517 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
524 * Cleans up process structure contents (OS-dependent) and deallocates it
526 * @param proc pointer to process structure
529 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
531 if (NULL != proc->control_pipe)
532 GNUNET_DISK_file_close (proc->control_pipe);
535 if (proc->handle != NULL)
536 CloseHandle (proc->handle);
538 if (NULL != proc->childpipename)
541 cleanup_npipe (proc->childpipename);
543 GNUNET_free (proc->childpipename);
550 #include "gnunet_signal_lib.h"
552 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
555 * Make seaspider happy.
557 #define DWORD_WINAPI DWORD WINAPI
560 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
561 * @param proc pointer to process structure
564 child_wait_thread (void *arg)
566 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
568 WaitForSingleObject (proc->handle, INFINITE);
570 if (w32_sigchld_handler)
571 w32_sigchld_handler ();
579 * Set process priority
581 * @param proc pointer to process structure
582 * @param prio priority value
583 * @return GNUNET_OK on success, GNUNET_SYSERR on error
586 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
587 enum GNUNET_SCHEDULER_Priority prio)
591 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
592 if (GNUNET_SCHEDULER_PRIORITY_KEEP == prio)
595 /* convert to MINGW/Unix values */
598 case GNUNET_SCHEDULER_PRIORITY_UI:
599 case GNUNET_SCHEDULER_PRIORITY_URGENT:
601 rprio = HIGH_PRIORITY_CLASS;
607 case GNUNET_SCHEDULER_PRIORITY_HIGH:
609 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
615 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
617 rprio = NORMAL_PRIORITY_CLASS;
623 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
625 rprio = BELOW_NORMAL_PRIORITY_CLASS;
631 case GNUNET_SCHEDULER_PRIORITY_IDLE:
633 rprio = IDLE_PRIORITY_CLASS;
640 return GNUNET_SYSERR;
643 /* Set process priority */
646 HANDLE h = proc->handle;
648 GNUNET_assert (h != NULL);
649 SetPriorityClass (h, rprio);
655 if ((0 == pid) || (pid == getpid ()))
658 int delta = rprio - have;
661 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
663 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
664 return GNUNET_SYSERR;
669 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
671 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
673 return GNUNET_SYSERR;
677 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
678 "Priority management not availabe for this platform\n");
686 CreateCustomEnvTable (char **vars)
688 char *win32_env_table;
693 size_t tablesize = 0;
694 size_t items_count = 0;
703 win32_env_table = GetEnvironmentStringsA ();
704 if (NULL == win32_env_table)
706 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
708 index = GNUNET_malloc (sizeof (char *) * n_var);
709 for (c = 0; c < n_var; c++)
711 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
713 size_t len = strlen (ptr);
716 for (var_ptr = vars; *var_ptr; var_ptr++)
720 var_len = strlen (var);
721 if (strncmp (var, ptr, var_len) == 0)
725 tablesize += var_len + strlen (val) + 1;
730 tablesize += len + 1;
733 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
738 n_found += strlen (var) + strlen (val) + 1;
740 result = GNUNET_malloc (tablesize + n_found + 1);
741 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
743 size_t len = strlen (ptr);
746 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
750 var_len = strlen (var);
751 if (strncmp (var, ptr, var_len) == 0)
759 strcpy (result_ptr, ptr);
760 result_ptr += len + 1;
764 strcpy (result_ptr, var);
765 result_ptr += var_len;
766 strcpy (result_ptr, val);
767 result_ptr += strlen (val) + 1;
771 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
775 var_len = strlen (var);
778 strcpy (result_ptr, var);
779 result_ptr += var_len;
780 strcpy (result_ptr, val);
781 result_ptr += strlen (val) + 1;
784 FreeEnvironmentStrings (win32_env_table);
793 * Open '/dev/null' and make the result the given
796 * @param target_fd desired FD to point to /dev/null
797 * @param flags open flags (O_RDONLY, O_WRONLY)
800 open_dev_null (int target_fd,
805 fd = open ("/dev/null", flags);
808 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null");
813 if (-1 == dup2 (fd, target_fd))
815 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
819 GNUNET_break (0 == close (fd));
827 * @param pipe_control should a pipe be used to send signals to the child?
828 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
829 * std handles of the parent are inherited by the child.
830 * pipe_stdin and pipe_stdout take priority over std_inheritance
831 * (when they are non-NULL).
832 * @param pipe_stdin pipe to use to send input to child process (or NULL)
833 * @param pipe_stdout pipe to use to get output from child process (or NULL)
834 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
835 * must be NULL on platforms where dup is not supported
836 * @param filename name of the binary
837 * @param argv NULL-terminated list of arguments to the process
838 * @return process ID of the new process, -1 on error
840 static struct GNUNET_OS_Process *
841 start_process (int pipe_control,
842 enum GNUNET_OS_InheritStdioFlags std_inheritance,
843 struct GNUNET_DISK_PipeHandle *pipe_stdin,
844 struct GNUNET_DISK_PipeHandle *pipe_stdout,
845 const SOCKTYPE *lsocks,
846 const char *filename,
853 struct GNUNET_OS_Process *gnunet_proc;
854 char *childpipename = NULL;
867 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
868 return NULL; /* not executable */
869 if ( (GNUNET_YES == pipe_control) &&
870 (GNUNET_OK != npipe_setup (&childpipename)) )
872 GNUNET_free (childpipename);
875 if (NULL != pipe_stdout)
877 GNUNET_assert (GNUNET_OK ==
878 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
880 GNUNET_DISK_PIPE_END_WRITE),
881 &fd_stdout_write, sizeof (int)));
882 GNUNET_assert (GNUNET_OK ==
883 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
884 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
885 &fd_stdout_read, sizeof (int)));
887 if (NULL != pipe_stdin)
889 GNUNET_assert (GNUNET_OK ==
890 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
891 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
892 &fd_stdin_read, sizeof (int)));
893 GNUNET_assert (GNUNET_OK ==
894 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
895 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
896 &fd_stdin_write, sizeof (int)));
903 while (-1 != (k = lsocks[i++]))
904 GNUNET_array_append (lscp, ls, k);
905 GNUNET_array_append (lscp, ls, -1);
908 /* see https://gnunet.org/vfork */
917 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
918 GNUNET_free_non_null (childpipename);
919 GNUNET_array_grow (lscp, ls, 0);
925 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
926 gnunet_proc->pid = ret;
927 gnunet_proc->childpipename = childpipename;
928 GNUNET_array_grow (lscp, ls, 0);
931 if (NULL != childpipename)
933 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
935 /* due to vfork, we must NOT free memory on DARWIN! */
936 GNUNET_free (childpipename);
939 if (NULL != pipe_stdin)
941 GNUNET_break (0 == close (fd_stdin_write));
942 if (-1 == dup2 (fd_stdin_read, 0))
943 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
944 GNUNET_break (0 == close (fd_stdin_read));
946 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
948 GNUNET_break (0 == close (0));
949 open_dev_null (0, O_RDONLY);
951 if (NULL != pipe_stdout)
953 GNUNET_break (0 == close (fd_stdout_read));
954 if (-1 == dup2 (fd_stdout_write, 1))
955 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
956 GNUNET_break (0 == close (fd_stdout_write));
958 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
960 GNUNET_break (0 == close (1));
961 open_dev_null (1, O_WRONLY);
963 if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
965 GNUNET_break (0 == close (2));
966 open_dev_null (2, O_WRONLY);
970 /* read systemd documentation... */
971 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
972 setenv ("LISTEN_PID", lpid, 1);
975 while (-1 != lscp[i])
978 while (-1 != lscp[j])
984 GNUNET_assert (-1 != k);
985 GNUNET_assert (0 == close (lscp[j]));
993 /* Bury any existing FD, no matter what; they should all be closed
994 * on exec anyway and the important onces have been dup'ed away */
996 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
998 /* unset close-on-exec flag */
999 flags = fcntl (tgt, F_GETFD);
1000 GNUNET_assert (flags >= 0);
1001 flags &= ~FD_CLOEXEC;
1003 (void) fcntl (tgt, F_SETFD, flags);
1007 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1008 setenv ("LISTEN_FDS", fds, 1);
1011 /* due to vfork, we must NOT free memory on DARWIN! */
1012 GNUNET_array_grow (lscp, ls, 0);
1014 execvp (filename, argv);
1015 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1018 struct GNUNET_DISK_FileHandle *control_pipe;
1019 char *childpipename = NULL;
1021 char **non_const_argv;
1022 unsigned int cmdlen;
1026 PROCESS_INFORMATION proc;
1028 struct GNUNET_OS_Process *gnunet_proc;
1029 char path[MAX_PATH + 1];
1030 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1031 char *env_block = NULL;
1039 char *non_const_filename;
1040 char win_path[MAX_PATH + 1];
1041 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1042 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1044 HANDLE lsocks_write;
1052 HANDLE stdin_handle;
1053 HANDLE stdout_handle;
1054 HANDLE stdih, stdoh, stdeh;
1055 DWORD stdif, stdof, stdef;
1059 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
1060 return NULL; /* not executable */
1062 /* Search in prefix dir (hopefully - the directory from which
1063 * the current module was loaded), bindir and libdir, then in PATH
1065 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1066 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1067 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1069 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1072 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1075 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1078 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1079 GNUNET_free (self_prefix);
1080 GNUNET_free (bindir);
1081 GNUNET_free (libdir);
1083 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1084 if (alloc_len != pathbuf_len - 1)
1086 GNUNET_free (pathbuf);
1087 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1091 cmdlen = strlen (filename);
1092 if ( (cmdlen < 5) || (0 != strcmp (&filename[cmdlen - 4], ".exe")) )
1093 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1095 GNUNET_asprintf (&non_const_filename, "%s", filename);
1097 /* It could be in POSIX form, convert it to a DOS path early on */
1098 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1100 SetErrnoFromWinError (lRet);
1101 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1102 non_const_filename);
1103 GNUNET_free (non_const_filename);
1104 GNUNET_free (pathbuf);
1107 GNUNET_free (non_const_filename);
1108 non_const_filename = GNUNET_strdup (win_path);
1109 /* Check that this is the full path. If it isn't, search. */
1110 /* FIXME: convert it to wchar_t and use SearchPathW?
1111 * Remember: arguments to _start_process() are technically in UTF-8...
1113 if (non_const_filename[1] == ':')
1114 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1115 else if (!SearchPathA
1116 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1119 SetErrnoFromWinError (GetLastError ());
1120 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1121 non_const_filename);
1122 GNUNET_free (non_const_filename);
1123 GNUNET_free (pathbuf);
1126 GNUNET_free (pathbuf);
1127 GNUNET_free (non_const_filename);
1129 /* Count the number of arguments */
1130 arg = (char **) argv;
1137 /* Allocate a copy argv */
1138 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1140 /* Copy all argv strings */
1142 arg = (char **) argv;
1146 non_const_argv[argcount] = GNUNET_strdup (path);
1148 non_const_argv[argcount] = GNUNET_strdup (*arg);
1152 non_const_argv[argcount] = NULL;
1156 arg = non_const_argv;
1159 cmdlen = cmdlen + strlen (*arg) + 4;
1163 /* Allocate and create cmd */
1164 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1165 arg = non_const_argv;
1168 char arg_last_char = (*arg)[strlen (*arg) - 1];
1169 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1170 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1174 while (argcount > 0)
1175 GNUNET_free (non_const_argv[--argcount]);
1176 GNUNET_free (non_const_argv);
1178 memset (&start, 0, sizeof (start));
1179 start.cb = sizeof (start);
1180 if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
1181 start.dwFlags |= STARTF_USESTDHANDLES;
1183 stdih = GetStdHandle (STD_INPUT_HANDLE);
1184 GetHandleInformation (stdih, &stdif);
1185 if (pipe_stdin != NULL)
1187 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1188 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1189 &stdin_handle, sizeof (HANDLE));
1190 start.hStdInput = stdin_handle;
1194 if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
1196 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
1197 if (pipe_stdin == NULL)
1198 start.hStdInput = stdih;
1201 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
1205 stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
1206 GetHandleInformation (stdoh, &stdof);
1207 if (NULL != pipe_stdout)
1209 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1211 GNUNET_DISK_PIPE_END_WRITE),
1212 &stdout_handle, sizeof (HANDLE));
1213 start.hStdOutput = stdout_handle;
1217 if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
1219 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
1220 if (pipe_stdout == NULL)
1221 start.hStdOutput = stdoh;
1224 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1227 stdeh = GetStdHandle (STD_ERROR_HANDLE);
1228 GetHandleInformation (stdeh, &stdef);
1231 if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
1233 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
1234 start.hStdError = stdeh;
1237 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1240 if (GNUNET_YES == pipe_control)
1243 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1244 GNUNET_DISK_PERM_USER_READ |
1245 GNUNET_DISK_PERM_USER_WRITE);
1246 if (control_pipe == NULL)
1254 control_pipe = NULL;
1255 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1257 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1259 if (lsocks_pipe == NULL)
1263 GNUNET_DISK_pipe_close (lsocks_pipe);
1266 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1267 GNUNET_DISK_PIPE_END_WRITE);
1268 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1269 &lsocks_write, sizeof (HANDLE));
1270 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1271 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1272 &lsocks_read, sizeof (HANDLE));
1276 if (NULL != childpipename)
1278 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1280 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1281 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1282 GNUNET_free (childpipename);
1284 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1286 /*This will tell the child that we're going to send lsocks over the pipe*/
1287 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1288 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1290 our_env[env_off++] = NULL;
1291 env_block = CreateCustomEnvTable (our_env);
1293 GNUNET_free_non_null (our_env[--env_off]);
1296 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1298 LOG (GNUNET_ERROR_TYPE_DEBUG,
1299 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1300 GNUNET_free (env_block);
1306 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1308 LOG (GNUNET_ERROR_TYPE_DEBUG,
1309 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1310 GNUNET_free (env_block);
1316 bresult = CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1317 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1318 error_code = GetLastError ();
1320 if ((NULL == pipe_stdin) && (stdih))
1321 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
1324 if ((NULL == pipe_stdout) && (stdoh))
1325 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
1328 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
1330 GNUNET_free (env_block);
1337 SetErrnoFromWinError (error_code);
1338 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1339 if (NULL != control_pipe)
1340 GNUNET_DISK_file_close (control_pipe);
1342 GNUNET_DISK_pipe_close (lsocks_pipe);
1346 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1347 gnunet_proc->pid = proc.dwProcessId;
1348 gnunet_proc->handle = proc.hProcess;
1349 gnunet_proc->control_pipe = control_pipe;
1351 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1353 ResumeThread (proc.hThread);
1354 CloseHandle (proc.hThread);
1356 if ( (NULL == lsocks) || (INVALID_SOCKET == lsocks[0]) )
1359 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1361 /* This is a replacement for "goto error" that doesn't use goto */
1370 /* Tell the number of sockets */
1371 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1373 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1374 if (sizeof (count) != wrote)
1376 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1377 "Failed to write %u count bytes to the child: %u\n",
1378 sizeof (count), GetLastError ());
1381 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1383 WSAPROTOCOL_INFOA pi;
1384 /* Get a socket duplication info */
1385 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1387 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1388 "Failed to duplicate an socket[%llu]: %u\n", i,
1392 /* Synchronous I/O is not nice, but we can't schedule this:
1393 * lsocks will be closed/freed by the caller soon, and until
1394 * the child creates a duplicate, closing a socket here will
1395 * close it for good.
1397 /* Send the size of the structure
1398 * (the child might be built with different headers...)
1401 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1402 if (sizeof (size) != wrote)
1404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1405 "Failed to write %u size[%llu] bytes to the child: %u\n",
1406 sizeof (size), i, GetLastError ());
1409 /* Finally! Send the data */
1410 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1411 if (sizeof (pi) != wrote)
1413 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1414 "Failed to write %u socket[%llu] bytes to the child: %u\n",
1415 sizeof (pi), i, GetLastError ());
1419 /* This will block us until the child makes a final read or closes
1420 * the pipe (hence no 'wrote' check), since we have to wait for it
1421 * to duplicate the last socket, before we return and start closing
1424 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1429 GNUNET_DISK_file_sync (lsocks_write_fd);
1430 GNUNET_DISK_pipe_close (lsocks_pipe);
1434 /* If we can't pass on the socket(s), the child will block forever,
1435 * better put it out of its misery.
1437 SafeTerminateProcess (gnunet_proc->handle, 0, 0);
1438 CloseHandle (gnunet_proc->handle);
1439 if (NULL != gnunet_proc->control_pipe)
1440 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1441 GNUNET_free (gnunet_proc);
1454 * @param pipe_control should a pipe be used to send signals to the child?
1455 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1456 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1457 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1458 * @param filename name of the binary
1459 * @param argv NULL-terminated array of arguments to the process
1460 * @return pointer to process structure of the new process, NULL on error
1462 struct GNUNET_OS_Process *
1463 GNUNET_OS_start_process_vap (int pipe_control,
1464 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1465 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1466 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1467 const char *filename,
1470 return start_process (pipe_control,
1483 * @param pipe_control should a pipe be used to send signals to the child?
1484 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1485 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1486 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1487 * @param filename name of the binary
1488 * @param va NULL-terminated list of arguments to the process
1489 * @return pointer to process structure of the new process, NULL on error
1491 struct GNUNET_OS_Process *
1492 GNUNET_OS_start_process_va (int pipe_control,
1493 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1494 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1495 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1496 const char *filename, va_list va)
1498 struct GNUNET_OS_Process *ret;
1505 while (NULL != va_arg (ap, char *))
1508 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1511 while (NULL != (argv[argc] = va_arg (ap, char *)))
1514 ret = GNUNET_OS_start_process_vap (pipe_control,
1528 * @param pipe_control should a pipe be used to send signals to the child?
1529 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1530 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1531 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1532 * @param filename name of the binary
1533 * @param ... NULL-terminated list of arguments to the process
1534 * @return pointer to process structure of the new process, NULL on error
1536 struct GNUNET_OS_Process *
1537 GNUNET_OS_start_process (int pipe_control,
1538 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1539 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1540 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1541 const char *filename, ...)
1543 struct GNUNET_OS_Process *ret;
1546 va_start (ap, filename);
1547 ret = GNUNET_OS_start_process_va (pipe_control, std_inheritance, pipe_stdin,
1548 pipe_stdout, filename, ap);
1557 * @param pipe_control should a pipe be used to send signals to the child?
1558 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
1559 * std handles of the parent are inherited by the child.
1560 * pipe_stdin and pipe_stdout take priority over std_inheritance
1561 * (when they are non-NULL).
1562 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1563 * must be NULL on platforms where dup is not supported
1564 * @param filename name of the binary
1565 * @param argv NULL-terminated list of arguments to the process
1566 * @return process ID of the new process, -1 on error
1568 struct GNUNET_OS_Process *
1569 GNUNET_OS_start_process_v (int pipe_control,
1570 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1571 const SOCKTYPE *lsocks,
1572 const char *filename,
1575 return start_process (pipe_control,
1586 * Retrieve the status of a process, waiting on him if dead.
1587 * Nonblocking version.
1589 * @param proc process ID
1590 * @param type status type
1591 * @param code return code/signal number
1592 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1595 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1596 enum GNUNET_OS_ProcessStatusType *type,
1597 unsigned long *code)
1603 GNUNET_assert (0 != proc);
1604 ret = waitpid (proc->pid, &status, WNOHANG);
1607 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1608 return GNUNET_SYSERR;
1612 *type = GNUNET_OS_PROCESS_RUNNING;
1616 if (proc->pid != ret)
1618 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1619 return GNUNET_SYSERR;
1621 if (WIFEXITED (status))
1623 *type = GNUNET_OS_PROCESS_EXITED;
1624 *code = WEXITSTATUS (status);
1626 else if (WIFSIGNALED (status))
1628 *type = GNUNET_OS_PROCESS_SIGNALED;
1629 *code = WTERMSIG (status);
1631 else if (WIFSTOPPED (status))
1633 *type = GNUNET_OS_PROCESS_SIGNALED;
1634 *code = WSTOPSIG (status);
1637 else if (WIFCONTINUED (status))
1639 *type = GNUNET_OS_PROCESS_RUNNING;
1645 *type = GNUNET_OS_PROCESS_UNKNOWN;
1650 DWORD c, error_code, ret;
1654 if (h == NULL || ret == 0)
1656 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1658 return GNUNET_SYSERR;
1661 h = GetCurrentProcess ();
1664 ret = GetExitCodeProcess (h, &c);
1665 error_code = GetLastError ();
1666 if (ret == 0 || error_code != NO_ERROR)
1668 SetErrnoFromWinError (error_code);
1669 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1670 return GNUNET_SYSERR;
1672 if (STILL_ACTIVE == c)
1674 *type = GNUNET_OS_PROCESS_RUNNING;
1678 *type = GNUNET_OS_PROCESS_EXITED;
1687 * Wait for a process
1689 * @param proc pointer to process structure
1690 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1693 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1696 pid_t pid = proc->pid;
1699 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1700 (EINTR == errno) ) ;
1703 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1704 return GNUNET_SYSERR;
1713 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1715 return GNUNET_SYSERR;
1718 h = GetCurrentProcess ();
1720 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1722 SetErrnoFromWinError (GetLastError ());
1723 return GNUNET_SYSERR;
1731 * Handle to a command.
1733 struct GNUNET_OS_CommandHandle
1739 struct GNUNET_OS_Process *eip;
1742 * Handle to the output pipe.
1744 struct GNUNET_DISK_PipeHandle *opipe;
1747 * Read-end of output pipe.
1749 const struct GNUNET_DISK_FileHandle *r;
1752 * Function to call on each line of output.
1754 GNUNET_OS_LineProcessor proc;
1757 * Closure for 'proc'.
1762 * Buffer for the output.
1767 * Task reading from pipe.
1769 GNUNET_SCHEDULER_TaskIdentifier rtask;
1774 struct GNUNET_TIME_Absolute timeout;
1777 * Current read offset in buf.
1784 * Stop/kill a command. Must ONLY be called either from
1785 * the callback after 'NULL' was passed for 'line' *OR*
1786 * from an independent task (not within the line processor).
1788 * @param cmd handle to the process
1791 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1793 if (NULL != cmd->proc)
1795 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1796 GNUNET_SCHEDULER_cancel (cmd->rtask);
1798 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1799 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1800 GNUNET_OS_process_destroy (cmd->eip);
1801 GNUNET_DISK_pipe_close (cmd->opipe);
1807 * Read from the process and call the line processor.
1809 * @param cls the 'struct GNUNET_OS_CommandHandle'
1810 * @param tc scheduler context
1813 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1815 struct GNUNET_OS_CommandHandle *cmd = cls;
1816 GNUNET_OS_LineProcessor proc;
1820 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1821 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1823 /* timeout, shutdown, etc. */
1826 proc (cmd->proc_cls, NULL);
1830 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1831 sizeof (cmd->buf) - cmd->off);
1834 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1836 cmd->buf[cmd->off] = '\0';
1837 cmd->proc (cmd->proc_cls, cmd->buf);
1841 proc (cmd->proc_cls, NULL);
1844 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1849 cmd->proc (cmd->proc_cls, cmd->buf);
1850 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1851 cmd->off -= (end + 1 - cmd->buf);
1852 end = memchr (cmd->buf, '\n', cmd->off);
1855 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1856 (cmd->timeout), cmd->r, &cmd_read, cmd);
1861 * Run the given command line and call the given function
1862 * for each line of the output.
1864 * @param proc function to call for each line of the output
1865 * @param proc_cls closure for proc
1866 * @param timeout when to time out
1867 * @param binary command to run
1868 * @param ... arguments to command
1869 * @return NULL on error
1871 struct GNUNET_OS_CommandHandle *
1872 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1873 struct GNUNET_TIME_Relative timeout, const char *binary,
1876 struct GNUNET_OS_CommandHandle *cmd;
1877 struct GNUNET_OS_Process *eip;
1878 struct GNUNET_DISK_PipeHandle *opipe;
1881 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1884 va_start (ap, binary);
1885 /* redirect stdout, don't inherit stderr/stdin */
1886 eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, binary, ap);
1890 GNUNET_DISK_pipe_close (opipe);
1893 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1894 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1895 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1899 cmd->proc_cls = proc_cls;
1900 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1901 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1906 /* end of os_priority.c */