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 * @param flags open flags
84 * @param perm access permissions
85 * @return pipe handle on success, NULL on error
87 static struct GNUNET_DISK_FileHandle *
88 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
89 enum GNUNET_DISK_AccessPermissions perm)
91 struct GNUNET_DISK_FileHandle *ret;
97 if (flags & GNUNET_DISK_OPEN_READWRITE)
98 openMode = PIPE_ACCESS_DUPLEX;
99 else if (flags & GNUNET_DISK_OPEN_READ)
100 openMode = PIPE_ACCESS_INBOUND;
101 else if (flags & GNUNET_DISK_OPEN_WRITE)
102 openMode = PIPE_ACCESS_OUTBOUND;
103 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
104 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
113 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
114 LOG (GNUNET_ERROR_TYPE_DEBUG,
115 "Trying to create an instance of named pipe `%s'\n", name);
116 /* 1) This might work just fine with UTF-8 strings as it is.
117 * 2) This is only used by GNUnet itself, and only with latin names.
119 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
120 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
125 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
126 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
128 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
130 h = CreateNamedPipe (*fn,
131 openMode | FILE_FLAG_OVERLAPPED |
132 FILE_FLAG_FIRST_PIPE_INSTANCE,
133 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
136 error_code = GetLastError ();
139 /* don't re-set name to NULL yet */
140 if (h == INVALID_HANDLE_VALUE)
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_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
156 LOG (GNUNET_ERROR_TYPE_DEBUG,
157 "Pipe name was not unique, trying again\n");
166 ret = GNUNET_malloc (sizeof (*ret));
168 ret->type = GNUNET_PIPE;
169 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
170 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
171 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
172 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
178 * Opens already existing named pipe/FIFO
180 * @param fn name of an existing named pipe
181 * @param flags open flags
182 * @return pipe handle on success, NULL on error
184 static struct GNUNET_DISK_FileHandle *
185 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
187 struct GNUNET_DISK_FileHandle *ret;
192 if (flags & GNUNET_DISK_OPEN_READWRITE)
193 openMode = GENERIC_WRITE | GENERIC_READ;
194 else if (flags & GNUNET_DISK_OPEN_READ)
195 openMode = GENERIC_READ;
196 else if (flags & GNUNET_DISK_OPEN_WRITE)
197 openMode = GENERIC_WRITE;
199 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
200 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
201 if (h == INVALID_HANDLE_VALUE)
203 SetErrnoFromWinError (GetLastError ());
207 ret = GNUNET_malloc (sizeof (*ret));
209 ret->type = GNUNET_PIPE;
210 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
211 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
212 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
213 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
219 /* UNIX version of named-pipe API */
222 * Clean up a named pipe and the directory it was placed in.
224 * @param fn name of the pipe
227 cleanup_npipe (const char *fn)
232 if (0 != unlink (fn))
233 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
234 dn = GNUNET_strdup (fn);
237 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
243 * Setup a named pipe.
245 * @param fn where to store the name of the new pipe,
246 * if *fn is non-null, the name of the pipe to setup
247 * @return GNUNET_OK on success
250 npipe_setup (char **fn)
254 /* FIXME: hardwired '/tmp' path... is bad */
255 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
257 if (NULL == mkdtemp (dir))
259 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
260 return GNUNET_SYSERR;
262 GNUNET_asprintf (fn, "%s/child-control", dir);
264 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
265 return GNUNET_SYSERR;
271 * Open an existing named pipe.
273 * @param fn name of the file
274 * @param flags flags to use
275 * @return NULL on error
277 static struct GNUNET_DISK_FileHandle *
278 npipe_open (const char *fn,
279 enum GNUNET_DISK_OpenFlags flags)
281 struct GNUNET_DISK_FileHandle *ret;
286 /* 200 * 5ms = 1s at most */
289 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
290 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
292 /* as this is for killing a child process via pipe and it is conceivable that
293 the child process simply didn't finish starting yet, we do some sleeping
294 (which is obviously usually not allowed). We can't select on the FD as
295 'open' fails, and we probably shouldn't just "ignore" the error, so wait
296 and retry a few times is likely the best method; our process API doesn't
297 support continuations, so we need to sleep directly... */
299 req.tv_nsec = 5000000; /* 5ms */
300 (void) nanosleep (&req, NULL);
304 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
305 (flags == GNUNET_DISK_OPEN_READ)
306 ? _("Failed to open named pipe `%s' for reading: %s\n")
307 : _("Failed to open named pipe `%s' for writing: %s\n"),
312 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
320 * This handler is called when there are control data to be read on the pipe
322 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
323 * @param tc scheduler context
326 parent_control_handler (void *cls,
327 const struct GNUNET_SCHEDULER_TaskContext *tc)
329 struct GNUNET_DISK_FileHandle *control_pipe = cls;
333 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
335 if (0 != (tc->reason &
336 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
338 GNUNET_DISK_file_close (control_pipe);
342 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
343 if (sizeof (sig) != ret)
346 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
347 GNUNET_DISK_file_close (control_pipe);
351 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
352 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
353 control_pipe, &parent_control_handler,
360 * Task that connects this process to its parent via pipe;
361 * essentially, the parent control handler will read signal numbers
362 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
363 * variable) and raise those signals.
365 * @param cls closure (unused)
366 * @param tc scheduler context (unused)
369 GNUNET_OS_install_parent_control_handler (void *cls,
371 GNUNET_SCHEDULER_TaskContext *tc)
374 struct GNUNET_DISK_FileHandle *control_pipe;
376 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
377 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
379 LOG (GNUNET_ERROR_TYPE_DEBUG,
380 "Not installing a handler because $%s is empty\n",
381 GNUNET_OS_CONTROL_PIPE);
382 putenv ("GNUNET_OS_CONTROL_PIPE=");
386 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
387 if (NULL == control_pipe)
389 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
390 putenv ("GNUNET_OS_CONTROL_PIPE=");
393 LOG (GNUNET_ERROR_TYPE_DEBUG,
394 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
395 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
396 &parent_control_handler, control_pipe);
397 putenv ("GNUNET_OS_CONTROL_PIPE=");
402 * Get process structure for current process
404 * The pointer it returns points to static memory location and must not be
407 * @return pointer to the process sturcutre for this process
409 struct GNUNET_OS_Process *
410 GNUNET_OS_process_current ()
413 current_process.pid = GetCurrentProcessId ();
414 current_process.handle = GetCurrentProcess ();
416 current_process.pid = 0;
418 return ¤t_process;
423 * Sends a signal to the process
425 * @param proc pointer to process structure
427 * @return 0 on success, -1 on error
430 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
437 if ( (NULL == proc->control_pipe) &&
438 (NULL != proc->childpipename) )
439 proc->control_pipe = npipe_open (proc->childpipename,
440 GNUNET_DISK_OPEN_WRITE);
442 if (NULL != proc->control_pipe)
444 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
445 if (ret == sizeof (csig))
448 /* pipe failed or non-existent, try other methods */
457 #if WINDOWS && !defined(__CYGWIN__)
460 int must_kill = GNUNET_YES;
461 if (0 != GetExitCodeProcess (proc->handle, &exitcode))
462 must_kill = (exitcode == STILL_ACTIVE) ? GNUNET_YES : GNUNET_NO;
463 if (GNUNET_YES == must_kill)
464 if (0 == TerminateProcess (proc->handle, 0))
466 SetErrnoFromWinError (GetLastError ());
472 return PLIBC_KILL (proc->pid, sig);
479 return PLIBC_KILL (proc->pid, sig);
485 * Get the pid of the process in question
487 * @param proc the process to get the pid of
489 * @return the current process id
492 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
499 * Cleans up process structure contents (OS-dependent) and deallocates it
501 * @param proc pointer to process structure
504 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
506 if (NULL != proc->control_pipe)
507 GNUNET_DISK_file_close (proc->control_pipe);
510 if (proc->handle != NULL)
511 CloseHandle (proc->handle);
513 if (NULL != proc->childpipename)
516 cleanup_npipe (proc->childpipename);
518 GNUNET_free (proc->childpipename);
525 #include "gnunet_signal_lib.h"
527 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
530 * Make seaspider happy.
532 #define DWORD_WINAPI DWORD WINAPI
535 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
536 * @param proc pointer to process structure
539 child_wait_thread (void *arg)
541 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
543 WaitForSingleObject (proc->handle, INFINITE);
545 if (w32_sigchld_handler)
546 w32_sigchld_handler ();
554 * Set process priority
556 * @param proc pointer to process structure
557 * @param prio priority value
558 * @return GNUNET_OK on success, GNUNET_SYSERR on error
561 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
562 enum GNUNET_SCHEDULER_Priority prio)
566 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
567 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
570 /* convert to MINGW/Unix values */
573 case GNUNET_SCHEDULER_PRIORITY_UI:
574 case GNUNET_SCHEDULER_PRIORITY_URGENT:
576 rprio = HIGH_PRIORITY_CLASS;
582 case GNUNET_SCHEDULER_PRIORITY_HIGH:
584 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
590 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
592 rprio = NORMAL_PRIORITY_CLASS;
598 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
600 rprio = BELOW_NORMAL_PRIORITY_CLASS;
606 case GNUNET_SCHEDULER_PRIORITY_IDLE:
608 rprio = IDLE_PRIORITY_CLASS;
615 return GNUNET_SYSERR;
618 /* Set process priority */
621 HANDLE h = proc->handle;
623 GNUNET_assert (h != NULL);
624 SetPriorityClass (h, rprio);
630 if ((0 == pid) || (pid == getpid ()))
633 int delta = rprio - have;
636 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
638 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
639 return GNUNET_SYSERR;
644 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
646 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
648 return GNUNET_SYSERR;
652 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
653 "Priority management not availabe for this platform\n");
661 CreateCustomEnvTable (char **vars)
663 char *win32_env_table;
668 size_t tablesize = 0;
669 size_t items_count = 0;
678 win32_env_table = GetEnvironmentStringsA ();
679 if (win32_env_table == NULL)
681 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
683 index = GNUNET_malloc (sizeof (char *) * n_var);
684 for (c = 0; c < n_var; c++)
686 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
688 size_t len = strlen (ptr);
691 for (var_ptr = vars; *var_ptr; var_ptr++)
695 var_len = strlen (var);
696 if (strncmp (var, ptr, var_len) == 0)
700 tablesize += var_len + strlen (val) + 1;
705 tablesize += len + 1;
708 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
713 n_found += strlen (var) + strlen (val) + 1;
715 result = GNUNET_malloc (tablesize + n_found + 1);
716 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
718 size_t len = strlen (ptr);
721 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
725 var_len = strlen (var);
726 if (strncmp (var, ptr, var_len) == 0)
734 strcpy (result_ptr, ptr);
735 result_ptr += len + 1;
739 strcpy (result_ptr, var);
740 result_ptr += var_len;
741 strcpy (result_ptr, val);
742 result_ptr += strlen (val) + 1;
746 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
750 var_len = strlen (var);
753 strcpy (result_ptr, var);
754 result_ptr += var_len;
755 strcpy (result_ptr, val);
756 result_ptr += strlen (val) + 1;
759 FreeEnvironmentStrings (win32_env_table);
768 * Open '/dev/null' and make the result the given
771 * @param target_fd desired FD to point to /dev/null
772 * @param flags open flags (O_RDONLY, O_WRONLY)
775 open_dev_null (int target_fd,
780 fd = open ("/dev/null", flags);
783 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null");
788 if (-1 == dup2 (fd, target_fd))
790 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
794 GNUNET_break (0 == close (fd));
802 * @param pipe_control should a pipe be used to send signals to the child?
803 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
804 * std handles of the parent are inherited by the child.
805 * pipe_stdin and pipe_stdout take priority over std_inheritance
806 * (when they are non-NULL).
807 * @param pipe_stdin pipe to use to send input to child process (or NULL)
808 * @param pipe_stdout pipe to use to get output from child process (or NULL)
809 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
810 * must be NULL on platforms where dup is not supported
811 * @param filename name of the binary
812 * @param argv NULL-terminated list of arguments to the process
813 * @return process ID of the new process, -1 on error
815 static struct GNUNET_OS_Process *
816 start_process (int pipe_control,
817 enum GNUNET_OS_InheritStdioFlags std_inheritance,
818 struct GNUNET_DISK_PipeHandle *pipe_stdin,
819 struct GNUNET_DISK_PipeHandle *pipe_stdout,
820 const SOCKTYPE *lsocks,
821 const char *filename,
828 struct GNUNET_OS_Process *gnunet_proc;
829 char *childpipename = NULL;
842 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
843 return NULL; /* not executable */
844 if ( (GNUNET_YES == pipe_control) &&
845 (GNUNET_OK != npipe_setup (&childpipename)) )
847 if (NULL != pipe_stdout)
849 GNUNET_assert (GNUNET_OK ==
850 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
852 GNUNET_DISK_PIPE_END_WRITE),
853 &fd_stdout_write, sizeof (int)));
854 GNUNET_assert (GNUNET_OK ==
855 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
856 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
857 &fd_stdout_read, sizeof (int)));
859 if (NULL != pipe_stdin)
861 GNUNET_assert (GNUNET_OK ==
862 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
863 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
864 &fd_stdin_read, sizeof (int)));
865 GNUNET_assert (GNUNET_OK ==
866 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
867 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
868 &fd_stdin_write, sizeof (int)));
875 while (-1 != (k = lsocks[i++]))
876 GNUNET_array_append (lscp, ls, k);
877 GNUNET_array_append (lscp, ls, -1);
884 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
885 GNUNET_free_non_null (childpipename);
886 GNUNET_array_grow (lscp, ls, 0);
892 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
893 gnunet_proc->pid = ret;
894 gnunet_proc->childpipename = childpipename;
895 GNUNET_array_grow (lscp, ls, 0);
898 if (NULL != childpipename)
900 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
901 GNUNET_free (childpipename);
903 if (NULL != pipe_stdin)
905 GNUNET_break (0 == close (fd_stdin_write));
906 if (-1 == dup2 (fd_stdin_read, 0))
907 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
908 GNUNET_break (0 == close (fd_stdin_read));
910 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
912 GNUNET_break (0 == close (0));
913 open_dev_null (0, O_RDONLY);
915 if (NULL != pipe_stdout)
917 GNUNET_break (0 == close (fd_stdout_read));
918 if (-1 == dup2 (fd_stdout_write, 1))
919 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
920 GNUNET_break (0 == close (fd_stdout_write));
922 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
924 GNUNET_break (0 == close (1));
925 open_dev_null (1, O_WRONLY);
927 if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
929 GNUNET_break (0 == close (2));
930 open_dev_null (2, O_WRONLY);
934 /* read systemd documentation... */
935 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
936 setenv ("LISTEN_PID", lpid, 1);
939 while (-1 != lscp[i])
942 while (-1 != lscp[j])
948 GNUNET_assert (-1 != k);
949 GNUNET_assert (0 == close (lscp[j]));
957 /* Bury any existing FD, no matter what; they should all be closed
958 * on exec anyway and the important onces have been dup'ed away */
960 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
962 /* unset close-on-exec flag */
963 flags = fcntl (tgt, F_GETFD);
964 GNUNET_assert (flags >= 0);
965 flags &= ~FD_CLOEXEC;
967 (void) fcntl (tgt, F_SETFD, flags);
971 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
972 setenv ("LISTEN_FDS", fds, 1);
974 GNUNET_array_grow (lscp, ls, 0);
975 execvp (filename, argv);
976 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
979 struct GNUNET_DISK_FileHandle *control_pipe;
980 char *childpipename = NULL;
982 char **non_const_argv;
987 PROCESS_INFORMATION proc;
989 struct GNUNET_OS_Process *gnunet_proc;
990 char path[MAX_PATH + 1];
991 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
992 char *env_block = NULL;
1000 char *non_const_filename;
1001 char win_path[MAX_PATH + 1];
1002 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1003 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1005 HANDLE lsocks_write;
1013 HANDLE stdin_handle;
1014 HANDLE stdout_handle;
1015 HANDLE stdih, stdoh, stdeh;
1016 DWORD stdif, stdof, stdef;
1020 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
1021 return NULL; /* not executable */
1023 /* Search in prefix dir (hopefully - the directory from which
1024 * the current module was loaded), bindir and libdir, then in PATH
1026 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1027 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1028 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1030 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1033 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1036 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1039 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1040 GNUNET_free (self_prefix);
1041 GNUNET_free (bindir);
1042 GNUNET_free (libdir);
1044 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1045 if (alloc_len != pathbuf_len - 1)
1047 GNUNET_free (pathbuf);
1048 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1052 cmdlen = strlen (filename);
1053 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1054 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1056 GNUNET_asprintf (&non_const_filename, "%s", filename);
1058 /* It could be in POSIX form, convert it to a DOS path early on */
1059 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1061 SetErrnoFromWinError (lRet);
1062 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1063 non_const_filename);
1064 GNUNET_free (non_const_filename);
1065 GNUNET_free (pathbuf);
1068 GNUNET_free (non_const_filename);
1069 non_const_filename = GNUNET_strdup (win_path);
1070 /* Check that this is the full path. If it isn't, search. */
1071 /* FIXME: convert it to wchar_t and use SearchPathW?
1072 * Remember: arguments to _start_process() are technically in UTF-8...
1074 if (non_const_filename[1] == ':')
1075 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1076 else if (!SearchPathA
1077 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1080 SetErrnoFromWinError (GetLastError ());
1081 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1082 non_const_filename);
1083 GNUNET_free (non_const_filename);
1084 GNUNET_free (pathbuf);
1087 GNUNET_free (pathbuf);
1088 GNUNET_free (non_const_filename);
1090 /* Count the number of arguments */
1091 arg = (char **) argv;
1098 /* Allocate a copy argv */
1099 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1101 /* Copy all argv strings */
1103 arg = (char **) argv;
1107 non_const_argv[argcount] = GNUNET_strdup (path);
1109 non_const_argv[argcount] = GNUNET_strdup (*arg);
1113 non_const_argv[argcount] = NULL;
1117 arg = non_const_argv;
1120 cmdlen = cmdlen + strlen (*arg) + 4;
1124 /* Allocate and create cmd */
1125 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1126 arg = non_const_argv;
1129 char arg_last_char = (*arg)[strlen (*arg) - 1];
1130 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1131 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1135 while (argcount > 0)
1136 GNUNET_free (non_const_argv[--argcount]);
1137 GNUNET_free (non_const_argv);
1139 memset (&start, 0, sizeof (start));
1140 start.cb = sizeof (start);
1141 if ((pipe_stdin != NULL) || (pipe_stdout != NULL) || (std_inheritance != 0))
1142 start.dwFlags |= STARTF_USESTDHANDLES;
1144 stdih = GetStdHandle (STD_INPUT_HANDLE);
1145 GetHandleInformation (stdih, &stdif);
1146 if (pipe_stdin != NULL)
1148 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1149 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1150 &stdin_handle, sizeof (HANDLE));
1151 start.hStdInput = stdin_handle;
1155 if (std_inheritance & GNUNET_OS_INHERIT_STD_IN)
1157 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 1);
1158 if (pipe_stdin == NULL)
1159 start.hStdInput = stdih;
1162 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
1166 stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
1167 GetHandleInformation (stdoh, &stdof);
1168 if (NULL != pipe_stdout)
1170 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1172 GNUNET_DISK_PIPE_END_WRITE),
1173 &stdout_handle, sizeof (HANDLE));
1174 start.hStdOutput = stdout_handle;
1178 if (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)
1180 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 1);
1181 if (pipe_stdout == NULL)
1182 start.hStdOutput = stdoh;
1185 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1188 stdeh = GetStdHandle (STD_ERROR_HANDLE);
1189 GetHandleInformation (stdeh, &stdef);
1192 if (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)
1194 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 1);
1195 start.hStdError = stdeh;
1198 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1201 if (GNUNET_YES == pipe_control)
1204 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1205 GNUNET_DISK_PERM_USER_READ |
1206 GNUNET_DISK_PERM_USER_WRITE);
1207 if (control_pipe == NULL)
1215 control_pipe = NULL;
1216 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1218 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1220 if (lsocks_pipe == NULL)
1224 GNUNET_DISK_pipe_close (lsocks_pipe);
1227 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1228 GNUNET_DISK_PIPE_END_WRITE);
1229 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1230 &lsocks_write, sizeof (HANDLE));
1231 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1232 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1233 &lsocks_read, sizeof (HANDLE));
1237 if (NULL != childpipename)
1239 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1241 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1242 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1243 GNUNET_free (childpipename);
1245 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1247 /*This will tell the child that we're going to send lsocks over the pipe*/
1248 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1249 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1251 our_env[env_off++] = NULL;
1252 env_block = CreateCustomEnvTable (our_env);
1254 GNUNET_free_non_null (our_env[--env_off]);
1257 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1259 LOG (GNUNET_ERROR_TYPE_DEBUG,
1260 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1261 GNUNET_free (env_block);
1267 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1269 LOG (GNUNET_ERROR_TYPE_DEBUG,
1270 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1271 GNUNET_free (env_block);
1277 bresult = CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1278 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1279 error_code = GetLastError ();
1281 if ((NULL == pipe_stdin) && (stdih))
1282 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, stdif);
1285 if ((NULL == pipe_stdout) && (stdoh))
1286 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, stdof);
1289 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, stdef);
1291 GNUNET_free (env_block);
1298 SetErrnoFromWinError (error_code);
1299 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1300 if (NULL != control_pipe)
1301 GNUNET_DISK_file_close (control_pipe);
1303 GNUNET_DISK_pipe_close (lsocks_pipe);
1307 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1308 gnunet_proc->pid = proc.dwProcessId;
1309 gnunet_proc->handle = proc.hProcess;
1310 gnunet_proc->control_pipe = control_pipe;
1312 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1314 ResumeThread (proc.hThread);
1315 CloseHandle (proc.hThread);
1317 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1320 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1322 /* This is a replacement for "goto error" that doesn't use goto */
1327 uint64_t size, count, i;
1329 /* Tell the number of sockets */
1330 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1332 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1333 if (wrote != sizeof (count))
1335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1338 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1340 WSAPROTOCOL_INFOA pi;
1341 /* Get a socket duplication info */
1342 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1344 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1345 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1348 /* Synchronous I/O is not nice, but we can't schedule this:
1349 * lsocks will be closed/freed by the caller soon, and until
1350 * the child creates a duplicate, closing a socket here will
1351 * close it for good.
1353 /* Send the size of the structure
1354 * (the child might be built with different headers...)
1357 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1358 if (wrote != sizeof (size))
1360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1363 /* Finally! Send the data */
1364 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1365 if (wrote != sizeof (pi))
1367 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1371 /* This will block us until the child makes a final read or closes
1372 * the pipe (hence no 'wrote' check), since we have to wait for it
1373 * to duplicate the last socket, before we return and start closing
1376 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1381 GNUNET_DISK_file_sync (lsocks_write_fd);
1382 GNUNET_DISK_pipe_close (lsocks_pipe);
1386 /* If we can't pass on the socket(s), the child will block forever,
1387 * better put it out of its misery.
1389 TerminateProcess (gnunet_proc->handle, 0);
1390 CloseHandle (gnunet_proc->handle);
1391 if (NULL != gnunet_proc->control_pipe)
1392 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1393 GNUNET_free (gnunet_proc);
1406 * @param pipe_control should a pipe be used to send signals to the child?
1407 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1408 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1409 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1410 * @param filename name of the binary
1411 * @param argv NULL-terminated array of arguments to the process
1412 * @return pointer to process structure of the new process, NULL on error
1414 struct GNUNET_OS_Process *
1415 GNUNET_OS_start_process_vap (int pipe_control,
1416 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1417 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1418 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1419 const char *filename,
1422 return start_process (pipe_control,
1435 * @param pipe_control should a pipe be used to send signals to the child?
1436 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1437 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1438 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1439 * @param filename name of the binary
1440 * @param va NULL-terminated list of arguments to the process
1441 * @return pointer to process structure of the new process, NULL on error
1443 struct GNUNET_OS_Process *
1444 GNUNET_OS_start_process_va (int pipe_control,
1445 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1446 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1447 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1448 const char *filename, va_list va)
1450 struct GNUNET_OS_Process *ret;
1457 while (NULL != va_arg (ap, char *))
1460 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1463 while (NULL != (argv[argc] = va_arg (ap, char *)))
1466 ret = GNUNET_OS_start_process_vap (pipe_control,
1481 * @param pipe_control should a pipe be used to send signals to the child?
1482 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags
1483 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1484 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1485 * @param filename name of the binary
1486 * @param ... NULL-terminated list of arguments to the process
1488 * @return pointer to process structure of the new process, NULL on error
1491 struct GNUNET_OS_Process *
1492 GNUNET_OS_start_process (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, ...)
1498 struct GNUNET_OS_Process *ret;
1501 va_start (ap, filename);
1502 ret = GNUNET_OS_start_process_va (pipe_control, std_inheritance, pipe_stdin,
1503 pipe_stdout, filename, ap);
1512 * @param pipe_control should a pipe be used to send signals to the child?
1513 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1514 * must be NULL on platforms where dup is not supported
1515 * @param filename name of the binary
1516 * @param argv NULL-terminated list of arguments to the process
1517 * @return process ID of the new process, -1 on error
1519 struct GNUNET_OS_Process *
1520 GNUNET_OS_start_process_v (int pipe_control,
1521 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1522 const SOCKTYPE *lsocks,
1523 const char *filename,
1526 return start_process (pipe_control,
1537 * Retrieve the status of a process, waiting on him if dead.
1538 * Nonblocking version.
1540 * @param proc process ID
1541 * @param type status type
1542 * @param code return code/signal number
1543 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1546 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1547 enum GNUNET_OS_ProcessStatusType *type,
1548 unsigned long *code)
1554 GNUNET_assert (0 != proc);
1555 ret = waitpid (proc->pid, &status, WNOHANG);
1558 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1559 return GNUNET_SYSERR;
1563 *type = GNUNET_OS_PROCESS_RUNNING;
1567 if (proc->pid != ret)
1569 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1570 return GNUNET_SYSERR;
1572 if (WIFEXITED (status))
1574 *type = GNUNET_OS_PROCESS_EXITED;
1575 *code = WEXITSTATUS (status);
1577 else if (WIFSIGNALED (status))
1579 *type = GNUNET_OS_PROCESS_SIGNALED;
1580 *code = WTERMSIG (status);
1582 else if (WIFSTOPPED (status))
1584 *type = GNUNET_OS_PROCESS_SIGNALED;
1585 *code = WSTOPSIG (status);
1588 else if (WIFCONTINUED (status))
1590 *type = GNUNET_OS_PROCESS_RUNNING;
1596 *type = GNUNET_OS_PROCESS_UNKNOWN;
1601 DWORD c, error_code, ret;
1605 if (h == NULL || ret == 0)
1607 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1609 return GNUNET_SYSERR;
1612 h = GetCurrentProcess ();
1615 ret = GetExitCodeProcess (h, &c);
1616 error_code = GetLastError ();
1617 if (ret == 0 || error_code != NO_ERROR)
1619 SetErrnoFromWinError (error_code);
1620 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1621 return GNUNET_SYSERR;
1623 if (STILL_ACTIVE == c)
1625 *type = GNUNET_OS_PROCESS_RUNNING;
1629 *type = GNUNET_OS_PROCESS_EXITED;
1638 * Wait for a process
1639 * @param proc pointer to process structure
1640 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1643 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1647 pid_t pid = proc->pid;
1650 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1651 (EINTR == errno) ) ;
1654 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1655 return GNUNET_SYSERR;
1665 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1667 return GNUNET_SYSERR;
1670 h = GetCurrentProcess ();
1672 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1674 SetErrnoFromWinError (GetLastError ());
1675 ret = GNUNET_SYSERR;
1686 * Handle to a command.
1688 struct GNUNET_OS_CommandHandle
1694 struct GNUNET_OS_Process *eip;
1697 * Handle to the output pipe.
1699 struct GNUNET_DISK_PipeHandle *opipe;
1702 * Read-end of output pipe.
1704 const struct GNUNET_DISK_FileHandle *r;
1707 * Function to call on each line of output.
1709 GNUNET_OS_LineProcessor proc;
1712 * Closure for 'proc'.
1717 * Buffer for the output.
1722 * Task reading from pipe.
1724 GNUNET_SCHEDULER_TaskIdentifier rtask;
1729 struct GNUNET_TIME_Absolute timeout;
1732 * Current read offset in buf.
1739 * Stop/kill a command. Must ONLY be called either from
1740 * the callback after 'NULL' was passed for 'line' *OR*
1741 * from an independent task (not within the line processor).
1743 * @param cmd handle to the process
1746 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1749 if (cmd->proc != NULL)
1751 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1752 GNUNET_SCHEDULER_cancel (cmd->rtask);
1754 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1755 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1756 GNUNET_OS_process_destroy (cmd->eip);
1757 GNUNET_DISK_pipe_close (cmd->opipe);
1763 * Read from the process and call the line processor.
1765 * @param cls the 'struct GNUNET_OS_CommandHandle'
1766 * @param tc scheduler context
1769 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1771 struct GNUNET_OS_CommandHandle *cmd = cls;
1772 GNUNET_OS_LineProcessor proc;
1776 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1777 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1779 /* timeout, shutdown, etc. */
1782 proc (cmd->proc_cls, NULL);
1786 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1787 sizeof (cmd->buf) - cmd->off);
1790 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1792 cmd->buf[cmd->off] = '\0';
1793 cmd->proc (cmd->proc_cls, cmd->buf);
1797 proc (cmd->proc_cls, NULL);
1800 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1805 cmd->proc (cmd->proc_cls, cmd->buf);
1806 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1807 cmd->off -= (end + 1 - cmd->buf);
1808 end = memchr (cmd->buf, '\n', cmd->off);
1811 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1812 (cmd->timeout), cmd->r, &cmd_read, cmd);
1817 * Run the given command line and call the given function
1818 * for each line of the output.
1820 * @param proc function to call for each line of the output
1821 * @param proc_cls closure for proc
1822 * @param timeout when to time out
1823 * @param binary command to run
1824 * @param ... arguments to command
1825 * @return NULL on error
1827 struct GNUNET_OS_CommandHandle *
1828 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1829 struct GNUNET_TIME_Relative timeout, const char *binary,
1832 struct GNUNET_OS_CommandHandle *cmd;
1833 struct GNUNET_OS_Process *eip;
1834 struct GNUNET_DISK_PipeHandle *opipe;
1837 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1840 va_start (ap, binary);
1841 /* redirect stdout, don't inherit stderr/stdin */
1842 eip = GNUNET_OS_start_process_va (GNUNET_NO, 0, NULL, opipe, binary, ap);
1846 GNUNET_DISK_pipe_close (opipe);
1849 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1850 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1851 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1855 cmd->proc_cls = proc_cls;
1856 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1857 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1864 /* end of os_priority.c */