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"
35 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
37 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
39 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
41 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
43 struct GNUNET_OS_Process
58 * Pipe we use to signal the process (if used).
60 struct GNUNET_DISK_FileHandle *control_pipe;
63 * Name of the pipe, NULL for none.
70 * Handle for 'this' process.
72 static struct GNUNET_OS_Process current_process;
75 /* MinGW version of named pipe API */
78 * Creates a named pipe/FIFO and opens it
80 * @param fn pointer to the name of the named pipe or to NULL
81 * @param flags open flags
82 * @param perm access permissions
83 * @return pipe handle on success, NULL on error
85 static struct GNUNET_DISK_FileHandle *
86 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
87 enum GNUNET_DISK_AccessPermissions perm)
89 struct GNUNET_DISK_FileHandle *ret;
95 if (flags & GNUNET_DISK_OPEN_READWRITE)
96 openMode = PIPE_ACCESS_DUPLEX;
97 else if (flags & GNUNET_DISK_OPEN_READ)
98 openMode = PIPE_ACCESS_INBOUND;
99 else if (flags & GNUNET_DISK_OPEN_WRITE)
100 openMode = PIPE_ACCESS_OUTBOUND;
101 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
102 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
111 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
112 LOG (GNUNET_ERROR_TYPE_DEBUG,
113 "Trying to create an instance of named pipe `%s'\n", name);
114 /* 1) This might work just fine with UTF-8 strings as it is.
115 * 2) This is only used by GNUnet itself, and only with latin names.
117 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
118 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
123 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
124 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
126 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
128 h = CreateNamedPipe (*fn,
129 openMode | FILE_FLAG_OVERLAPPED |
130 FILE_FLAG_FIRST_PIPE_INSTANCE,
131 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
134 error_code = GetLastError ();
137 /* don't re-set name to NULL yet */
138 if (h == INVALID_HANDLE_VALUE)
140 SetErrnoFromWinError (error_code);
141 LOG (GNUNET_ERROR_TYPE_DEBUG,
142 "Pipe creation have failed because of %d, errno is %d\n", error_code,
146 LOG (GNUNET_ERROR_TYPE_DEBUG,
147 "Pipe was to be unique, considering re-creation\n");
150 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
154 LOG (GNUNET_ERROR_TYPE_DEBUG,
155 "Pipe name was not unique, trying again\n");
164 ret = GNUNET_malloc (sizeof (*ret));
166 ret->type = GNUNET_PIPE;
167 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
168 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
169 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
170 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
176 * Opens already existing named pipe/FIFO
178 * @param fn name of an existing named pipe
179 * @param flags open flags
180 * @return pipe handle on success, NULL on error
182 static struct GNUNET_DISK_FileHandle *
183 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
185 struct GNUNET_DISK_FileHandle *ret;
190 if (flags & GNUNET_DISK_OPEN_READWRITE)
191 openMode = GENERIC_WRITE | GENERIC_READ;
192 else if (flags & GNUNET_DISK_OPEN_READ)
193 openMode = GENERIC_READ;
194 else if (flags & GNUNET_DISK_OPEN_WRITE)
195 openMode = GENERIC_WRITE;
197 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
198 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
199 if (h == INVALID_HANDLE_VALUE)
201 SetErrnoFromWinError (GetLastError ());
205 ret = GNUNET_malloc (sizeof (*ret));
207 ret->type = GNUNET_PIPE;
208 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
209 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
210 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
211 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
217 /* UNIX version of named-pipe API */
220 * Clean up a named pipe and the directory it was placed in.
222 * @param fn name of the pipe
225 cleanup_npipe (const char *fn)
230 if (0 != unlink (fn))
231 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
232 dn = GNUNET_strdup (fn);
235 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
241 * Setup a named pipe.
243 * @param fn where to store the name of the new pipe,
244 * if *fn is non-null, the name of the pipe to setup
245 * @return GNUNET_OK on success
248 npipe_setup (char **fn)
252 /* FIXME: hardwired '/tmp' path... is bad */
253 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
255 if (NULL == mkdtemp (dir))
257 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
258 return GNUNET_SYSERR;
260 GNUNET_asprintf (fn, "%s/child-control", dir);
262 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
263 return GNUNET_SYSERR;
269 * Open an existing named pipe.
271 * @param fn name of the file
272 * @param flags flags to use
273 * @return NULL on error
275 static struct GNUNET_DISK_FileHandle *
276 npipe_open (const char *fn,
277 enum GNUNET_DISK_OpenFlags flags)
279 struct GNUNET_DISK_FileHandle *ret;
284 /* 200 * 5ms = 1s at most */
287 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
288 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
290 /* as this is for killing a child process via pipe and it is conceivable that
291 the child process simply didn't finish starting yet, we do some sleeping
292 (which is obviously usually not allowed). We can't select on the FD as
293 'open' fails, and we probably shouldn't just "ignore" the error, so wait
294 and retry a few times is likely the best method; our process API doesn't
295 support continuations, so we need to sleep directly... */
297 req.tv_nsec = 5000000; /* 5ms */
298 (void) nanosleep (&req, NULL);
302 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
303 (flags == GNUNET_DISK_OPEN_READ)
304 ? _("Failed to open named pipe `%s' for reading: %s\n")
305 : _("Failed to open named pipe `%s' for writing: %s\n"),
310 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
318 * This handler is called when there are control data to be read on the pipe
320 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
321 * @param tc scheduler context
324 parent_control_handler (void *cls,
325 const struct GNUNET_SCHEDULER_TaskContext *tc)
327 struct GNUNET_DISK_FileHandle *control_pipe = cls;
330 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
333 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT |
334 GNUNET_SCHEDULER_REASON_PREREQ_DONE))
336 GNUNET_DISK_file_close (control_pipe);
339 if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) !=
342 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
343 GNUNET_DISK_file_close (control_pipe);
346 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
347 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
348 control_pipe, &parent_control_handler,
355 * Task that connects this process to its parent via pipe;
356 * essentially, the parent control handler will read signal numbers
357 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
358 * variable) and raise those signals.
360 * @param cls closure (unused)
361 * @param tc scheduler context (unused)
364 GNUNET_OS_install_parent_control_handler (void *cls,
366 GNUNET_SCHEDULER_TaskContext *tc)
369 struct GNUNET_DISK_FileHandle *control_pipe;
371 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
372 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
374 LOG (GNUNET_ERROR_TYPE_DEBUG,
375 "Not installing a handler because $%s is empty\n",
376 GNUNET_OS_CONTROL_PIPE);
377 putenv ("GNUNET_OS_CONTROL_PIPE=");
381 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
382 if (NULL == control_pipe)
384 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
385 putenv ("GNUNET_OS_CONTROL_PIPE=");
388 LOG (GNUNET_ERROR_TYPE_DEBUG,
389 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
390 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
391 &parent_control_handler, control_pipe);
392 putenv ("GNUNET_OS_CONTROL_PIPE=");
397 * Get process structure for current process
399 * The pointer it returns points to static memory location and must not be
402 * @return pointer to the process sturcutre for this process
404 struct GNUNET_OS_Process *
405 GNUNET_OS_process_current ()
408 current_process.pid = GetCurrentProcessId ();
409 current_process.handle = GetCurrentProcess ();
411 current_process.pid = 0;
413 return ¤t_process;
418 * Sends a signal to the process
420 * @param proc pointer to process structure
422 * @return 0 on success, -1 on error
425 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
430 if ( (NULL == proc->control_pipe) &&
431 (NULL != proc->childpipename) )
432 proc->control_pipe = npipe_open (proc->childpipename,
433 GNUNET_DISK_OPEN_WRITE);
435 if (NULL == proc->control_pipe)
438 /* no pipe and windows? can't do this */
442 return kill (proc->pid, sig);
445 ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof (sig));
446 if (ret == sizeof (sig))
448 /* pipe failed, try other methods */
457 #if WINDOWS && !defined(__CYGWIN__)
458 if (0 == TerminateProcess (proc->handle, 0))
460 /* FIXME: set 'errno' */
465 return PLIBC_KILL (proc->pid, sig);
472 return kill (proc->pid, sig);
478 * Get the pid of the process in question
480 * @param proc the process to get the pid of
482 * @return the current process id
485 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
492 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
494 #if ENABLE_WINDOWS_WORKAROUNDS
495 if (proc->control_pipe)
496 GNUNET_DISK_file_close (proc->control_pipe);
500 if (proc->handle != NULL)
501 CloseHandle (proc->handle);
503 if (NULL != proc->childpipename)
506 cleanup_npipe (proc->childpipename);
508 GNUNET_free (proc->childpipename);
515 #include "gnunet_signal_lib.h"
517 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
520 * Make seaspider happy.
522 #define DWORD_WINAPI DWORD WINAPI
525 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
526 * @param proc pointer to process structure
529 child_wait_thread (void *arg)
531 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
533 WaitForSingleObject (proc->handle, INFINITE);
535 if (w32_sigchld_handler)
536 w32_sigchld_handler ();
543 * Set process priority
545 * @param proc pointer to process structure
546 * @param prio priority value
547 * @return GNUNET_OK on success, GNUNET_SYSERR on error
550 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
551 enum GNUNET_SCHEDULER_Priority prio)
555 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
556 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
559 /* convert to MINGW/Unix values */
562 case GNUNET_SCHEDULER_PRIORITY_UI:
563 case GNUNET_SCHEDULER_PRIORITY_URGENT:
565 rprio = HIGH_PRIORITY_CLASS;
571 case GNUNET_SCHEDULER_PRIORITY_HIGH:
573 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
579 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
581 rprio = NORMAL_PRIORITY_CLASS;
587 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
589 rprio = BELOW_NORMAL_PRIORITY_CLASS;
595 case GNUNET_SCHEDULER_PRIORITY_IDLE:
597 rprio = IDLE_PRIORITY_CLASS;
604 return GNUNET_SYSERR;
607 /* Set process priority */
610 HANDLE h = proc->handle;
612 GNUNET_assert (h != NULL);
613 SetPriorityClass (h, rprio);
619 if ((0 == pid) || (pid == getpid ()))
622 int delta = rprio - have;
625 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
627 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
628 return GNUNET_SYSERR;
633 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
635 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
637 return GNUNET_SYSERR;
641 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
642 "Priority management not availabe for this platform\n");
649 CreateCustomEnvTable (char **vars)
651 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
652 size_t tablesize = 0;
653 size_t items_count = 0;
654 size_t n_found = 0, n_var;
661 win32_env_table = GetEnvironmentStringsA ();
662 if (win32_env_table == NULL)
664 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
666 index = GNUNET_malloc (sizeof (char *) * n_var);
667 for (c = 0; c < n_var; c++)
669 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
671 size_t len = strlen (ptr);
674 for (var_ptr = vars; *var_ptr; var_ptr++)
678 var_len = strlen (var);
679 if (strncmp (var, ptr, var_len) == 0)
683 tablesize += var_len + strlen (val) + 1;
688 tablesize += len + 1;
691 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
696 n_found += strlen (var) + strlen (val) + 1;
698 result = GNUNET_malloc (tablesize + n_found + 1);
699 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
701 size_t len = strlen (ptr);
704 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
708 var_len = strlen (var);
709 if (strncmp (var, ptr, var_len) == 0)
717 strcpy (result_ptr, ptr);
718 result_ptr += len + 1;
722 strcpy (result_ptr, var);
723 result_ptr += var_len;
724 strcpy (result_ptr, val);
725 result_ptr += strlen (val) + 1;
729 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
733 var_len = strlen (var);
736 strcpy (result_ptr, var);
737 result_ptr += var_len;
738 strcpy (result_ptr, val);
739 result_ptr += strlen (val) + 1;
742 FreeEnvironmentStrings (win32_env_table);
753 * @param pipe_control should a pipe be used to send signals to the child?
754 * @param pipe_stdin pipe to use to send input to child process (or NULL)
755 * @param pipe_stdout pipe to use to get output from child process (or NULL)
756 * @param filename name of the binary
757 * @param argv NULL-terminated array of arguments to the process
758 * @return pointer to process structure of the new process, NULL on error
760 struct GNUNET_OS_Process *
761 GNUNET_OS_start_process_vap (int pipe_control,
762 struct GNUNET_DISK_PipeHandle *pipe_stdin,
763 struct GNUNET_DISK_PipeHandle *pipe_stdout,
764 const char *filename,
768 char *childpipename = NULL;
769 struct GNUNET_OS_Process *gnunet_proc = NULL;
776 if ( (GNUNET_YES == pipe_control) &&
778 npipe_setup (&childpipename)) )
780 if (pipe_stdout != NULL)
782 GNUNET_assert (GNUNET_OK ==
783 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
785 GNUNET_DISK_PIPE_END_WRITE),
786 &fd_stdout_write, sizeof (int)));
787 GNUNET_assert (GNUNET_OK ==
788 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
789 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
790 &fd_stdout_read, sizeof (int)));
792 if (pipe_stdin != NULL)
794 GNUNET_assert (GNUNET_OK ==
795 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
796 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
797 &fd_stdin_read, sizeof (int)));
798 GNUNET_assert (GNUNET_OK ==
799 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
800 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
801 &fd_stdin_write, sizeof (int)));
807 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
808 GNUNET_free_non_null (childpipename);
813 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
814 gnunet_proc->pid = ret;
815 gnunet_proc->childpipename = childpipename;
818 if (NULL != childpipename)
820 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
821 GNUNET_free (childpipename);
823 if (pipe_stdout != NULL)
825 GNUNET_break (0 == close (fd_stdout_read));
826 if (-1 == dup2 (fd_stdout_write, 1))
827 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
828 GNUNET_break (0 == close (fd_stdout_write));
831 if (pipe_stdin != NULL)
834 GNUNET_break (0 == close (fd_stdin_write));
835 if (-1 == dup2 (fd_stdin_read, 0))
836 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
837 GNUNET_break (0 == close (fd_stdin_read));
839 execvp (filename, argv);
840 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
843 char *childpipename = NULL;
844 struct GNUNET_OS_Process *gnunet_proc = NULL;
849 PROCESS_INFORMATION proc;
852 HANDLE stdout_handle;
854 char path[MAX_PATH + 1];
856 char *our_env[3] = { NULL, NULL, NULL };
857 char *env_block = NULL;
859 DWORD pathbuf_len, alloc_len;
864 char *non_const_filename;
865 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
867 /* Search in prefix dir (hopefully - the directory from which
868 * the current module was loaded), bindir and libdir, then in PATH
870 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
871 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
872 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
874 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
877 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
880 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
883 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
884 GNUNET_free (self_prefix);
885 GNUNET_free (bindir);
886 GNUNET_free (libdir);
888 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
889 GNUNET_assert (alloc_len == (pathbuf_len - 1));
891 cmdlen = strlen (filename);
892 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
893 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
895 GNUNET_asprintf (&non_const_filename, "%s", filename);
897 /* Check that this is the full path. If it isn't, search. */
898 if (non_const_filename[1] == ':')
899 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
900 else if (!SearchPathA
901 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
904 SetErrnoFromWinError (GetLastError ());
905 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
907 GNUNET_free (non_const_filename);
908 GNUNET_free (pathbuf);
911 GNUNET_free (pathbuf);
912 GNUNET_free (non_const_filename);
916 while (NULL != (arg = argv[argc++]))
919 cmdlen = cmdlen + strlen (path) + 4;
921 cmdlen = cmdlen + strlen (arg) + 4;
925 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
927 while (NULL != (arg = argv[argc++]))
929 /* This is to escape trailing slash */
930 char arg_lastchar = arg[strlen (arg) - 1];
932 idx += sprintf (idx, "\"%s%s\"%s", path,
933 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
935 idx += sprintf (idx, "\"%s%s\"%s", arg,
936 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
939 memset (&start, 0, sizeof (start));
940 start.cb = sizeof (start);
942 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
943 start.dwFlags |= STARTF_USESTDHANDLES;
945 if (pipe_stdin != NULL)
947 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
948 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
949 &stdin_handle, sizeof (HANDLE));
950 start.hStdInput = stdin_handle;
953 if (pipe_stdout != NULL)
955 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
957 GNUNET_DISK_PIPE_END_WRITE),
958 &stdout_handle, sizeof (HANDLE));
959 start.hStdOutput = stdout_handle;
961 if (GNUNET_YES == pipe_control)
964 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
965 GNUNET_DISK_PERM_USER_READ |
966 GNUNET_DISK_PERM_USER_WRITE);
967 if (control_pipe == NULL)
974 if (NULL != childpipename)
976 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
978 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
979 GNUNET_asprintf (&our_env[1], "%s", childpipename);
986 env_block = CreateCustomEnvTable (our_env);
987 GNUNET_free (our_env[0]);
988 GNUNET_free (our_env[1]);
990 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
991 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
993 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
994 env_block, NULL, &start, &proc))
996 SetErrnoFromWinError (GetLastError ());
997 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
998 GNUNET_free (env_block);
1003 GNUNET_free (env_block);
1005 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1006 gnunet_proc->pid = proc.dwProcessId;
1007 gnunet_proc->handle = proc.hProcess;
1008 gnunet_proc->control_pipe = control_pipe;
1010 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1012 ResumeThread (proc.hThread);
1013 CloseHandle (proc.hThread);
1025 * @param pipe_control should a pipe be used to send signals to the child?
1026 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1027 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1028 * @param filename name of the binary
1029 * @param va NULL-terminated list of arguments to the process
1030 * @return pointer to process structure of the new process, NULL on error
1032 struct GNUNET_OS_Process *
1033 GNUNET_OS_start_process_va (int pipe_control,
1034 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1035 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1036 const char *filename, va_list va)
1038 struct GNUNET_OS_Process *ret;
1045 while (NULL != va_arg (ap, char *))
1048 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1051 while (NULL != (argv[argc] = va_arg (ap, char *)))
1054 ret = GNUNET_OS_start_process_vap (pipe_control,
1068 * @param pipe_control should a pipe be used to send signals to the child?
1069 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1070 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1071 * @param filename name of the binary
1072 * @param ... NULL-terminated list of arguments to the process
1074 * @return pointer to process structure of the new process, NULL on error
1077 struct GNUNET_OS_Process *
1078 GNUNET_OS_start_process (int pipe_control,
1079 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1080 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1081 const char *filename, ...)
1083 struct GNUNET_OS_Process *ret;
1086 va_start (ap, filename);
1087 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1096 * @param pipe_control should a pipe be used to send signals to the child?
1097 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1098 * must be NULL on platforms where dup is not supported
1099 * @param filename name of the binary
1100 * @param argv NULL-terminated list of arguments to the process
1101 * @return process ID of the new process, -1 on error
1103 struct GNUNET_OS_Process *
1104 GNUNET_OS_start_process_v (int pipe_control,
1105 const SOCKTYPE *lsocks,
1106 const char *filename,
1113 struct GNUNET_OS_Process *gnunet_proc = NULL;
1114 char *childpipename = NULL;
1123 if ( (GNUNET_YES == pipe_control) &&
1124 (GNUNET_OK != npipe_setup (&childpipename)) )
1131 while (-1 != (k = lsocks[i++]))
1132 GNUNET_array_append (lscp, ls, k);
1133 GNUNET_array_append (lscp, ls, -1);
1138 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1139 GNUNET_free_non_null (childpipename);
1140 GNUNET_array_grow (lscp, ls, 0);
1145 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1146 gnunet_proc->pid = ret;
1147 gnunet_proc->childpipename = childpipename;
1148 GNUNET_array_grow (lscp, ls, 0);
1151 if (NULL != childpipename)
1153 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1154 GNUNET_free (childpipename);
1158 /* read systemd documentation... */
1159 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1160 setenv ("LISTEN_PID", lpid, 1);
1163 while (-1 != lscp[i])
1166 while (-1 != lscp[j])
1172 GNUNET_assert (-1 != k);
1173 GNUNET_assert (0 == close (lscp[j]));
1181 /* Bury any existing FD, no matter what; they should all be closed
1182 * on exec anyway and the important onces have been dup'ed away */
1184 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1186 /* unset close-on-exec flag */
1187 flags = fcntl (tgt, F_GETFD);
1188 GNUNET_assert (flags >= 0);
1189 flags &= ~FD_CLOEXEC;
1191 (void) fcntl (tgt, F_SETFD, flags);
1195 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1196 setenv ("LISTEN_FDS", fds, 1);
1198 GNUNET_array_grow (lscp, ls, 0);
1199 execvp (filename, argv);
1200 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1203 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1204 char *childpipename = NULL;
1205 char **arg, **non_const_argv;
1206 unsigned int cmdlen;
1209 PROCESS_INFORMATION proc;
1211 struct GNUNET_OS_Process *gnunet_proc = NULL;
1212 char path[MAX_PATH + 1];
1213 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1214 char *env_block = NULL;
1216 DWORD pathbuf_len, alloc_len;
1221 char *non_const_filename;
1222 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1223 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1225 HANDLE lsocks_write;
1226 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1230 /* Search in prefix dir (hopefully - the directory from which
1231 * the current module was loaded), bindir and libdir, then in PATH
1233 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1234 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1235 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1237 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1240 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1243 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1246 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1247 GNUNET_free (self_prefix);
1248 GNUNET_free (bindir);
1249 GNUNET_free (libdir);
1251 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1252 if (alloc_len != pathbuf_len - 1)
1254 GNUNET_free (pathbuf);
1255 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1259 cmdlen = strlen (filename);
1260 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1261 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1263 GNUNET_asprintf (&non_const_filename, "%s", filename);
1265 /* Check that this is the full path. If it isn't, search. */
1266 if (non_const_filename[1] == ':')
1267 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1268 else if (!SearchPathA
1269 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1272 SetErrnoFromWinError (GetLastError ());
1273 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1274 non_const_filename);
1275 GNUNET_free (non_const_filename);
1276 GNUNET_free (pathbuf);
1279 GNUNET_free (pathbuf);
1280 GNUNET_free (non_const_filename);
1282 /* Count the number of arguments */
1283 arg = (char **) argv;
1290 /* Allocate a copy argv */
1291 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1293 /* Copy all argv strings */
1295 arg = (char **) argv;
1299 non_const_argv[argcount] = GNUNET_strdup (path);
1301 non_const_argv[argcount] = GNUNET_strdup (*arg);
1305 non_const_argv[argcount] = NULL;
1309 arg = non_const_argv;
1312 cmdlen = cmdlen + strlen (*arg) + 4;
1316 /* Allocate and create cmd */
1317 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1318 arg = non_const_argv;
1321 char arg_last_char = (*arg)[strlen (*arg) - 1];
1322 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1323 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1327 while (argcount > 0)
1328 GNUNET_free (non_const_argv[--argcount]);
1329 GNUNET_free (non_const_argv);
1331 memset (&start, 0, sizeof (start));
1332 start.cb = sizeof (start);
1334 if (GNUNET_YES == pipe_control)
1337 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1338 GNUNET_DISK_PERM_USER_READ |
1339 GNUNET_DISK_PERM_USER_WRITE);
1340 if (control_pipe == NULL)
1347 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1349 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1351 if (lsocks_pipe == NULL)
1355 GNUNET_DISK_pipe_close (lsocks_pipe);
1358 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1359 GNUNET_DISK_PIPE_END_WRITE);
1360 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1361 &lsocks_write, sizeof (HANDLE));
1362 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1363 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1364 &lsocks_read, sizeof (HANDLE));
1368 if (NULL != childpipename)
1370 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1372 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1373 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1374 GNUNET_free (childpipename);
1376 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1378 /*This will tell the child that we're going to send lsocks over the pipe*/
1379 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1380 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1382 our_env[env_off++] = NULL;
1383 env_block = CreateCustomEnvTable (our_env);
1385 GNUNET_free_non_null (our_env[--env_off]);
1386 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1387 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1389 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1390 env_block, NULL, &start, &proc))
1392 SetErrnoFromWinError (GetLastError ());
1393 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1394 if (NULL != control_pipe)
1395 GNUNET_DISK_file_close (control_pipe);
1397 GNUNET_DISK_pipe_close (lsocks_pipe);
1398 GNUNET_free (env_block);
1403 GNUNET_free (env_block);
1405 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1406 gnunet_proc->pid = proc.dwProcessId;
1407 gnunet_proc->handle = proc.hProcess;
1408 gnunet_proc->control_pipe = control_pipe;
1410 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1412 ResumeThread (proc.hThread);
1413 CloseHandle (proc.hThread);
1416 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1419 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1421 /* This is a replacement for "goto error" that doesn't use goto */
1426 uint64_t size, count, i;
1428 /* Tell the number of sockets */
1429 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1431 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1432 if (wrote != sizeof (count))
1434 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1437 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1439 WSAPROTOCOL_INFOA pi;
1440 /* Get a socket duplication info */
1441 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1444 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1447 /* Synchronous I/O is not nice, but we can't schedule this:
1448 * lsocks will be closed/freed by the caller soon, and until
1449 * the child creates a duplicate, closing a socket here will
1450 * close it for good.
1452 /* Send the size of the structure
1453 * (the child might be built with different headers...)
1456 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1457 if (wrote != sizeof (size))
1459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1462 /* Finally! Send the data */
1463 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1464 if (wrote != sizeof (pi))
1466 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1470 /* This will block us until the child makes a final read or closes
1471 * the pipe (hence no 'wrote' check), since we have to wait for it
1472 * to duplicate the last socket, before we return and start closing
1475 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1480 GNUNET_DISK_file_sync (lsocks_write_fd);
1481 GNUNET_DISK_pipe_close (lsocks_pipe);
1485 /* If we can't pass on the socket(s), the child will block forever,
1486 * better put it out of its misery.
1488 TerminateProcess (gnunet_proc->handle, 0);
1489 CloseHandle (gnunet_proc->handle);
1490 if (NULL != gnunet_proc->control_pipe)
1491 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1492 GNUNET_free (gnunet_proc);
1501 * Retrieve the status of a process
1502 * @param proc process ID
1503 * @param type status type
1504 * @param code return code/signal number
1505 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1508 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1509 enum GNUNET_OS_ProcessStatusType *type,
1510 unsigned long *code)
1516 GNUNET_assert (0 != proc);
1517 ret = waitpid (proc->pid, &status, WNOHANG);
1520 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1521 return GNUNET_SYSERR;
1525 *type = GNUNET_OS_PROCESS_RUNNING;
1529 if (proc->pid != ret)
1531 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1532 return GNUNET_SYSERR;
1534 if (WIFEXITED (status))
1536 *type = GNUNET_OS_PROCESS_EXITED;
1537 *code = WEXITSTATUS (status);
1539 else if (WIFSIGNALED (status))
1541 *type = GNUNET_OS_PROCESS_SIGNALED;
1542 *code = WTERMSIG (status);
1544 else if (WIFSTOPPED (status))
1546 *type = GNUNET_OS_PROCESS_SIGNALED;
1547 *code = WSTOPSIG (status);
1550 else if (WIFCONTINUED (status))
1552 *type = GNUNET_OS_PROCESS_RUNNING;
1558 *type = GNUNET_OS_PROCESS_UNKNOWN;
1563 DWORD c, error_code, ret;
1567 if (h == NULL || ret == 0)
1569 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1571 return GNUNET_SYSERR;
1574 h = GetCurrentProcess ();
1577 ret = GetExitCodeProcess (h, &c);
1578 error_code = GetLastError ();
1579 if (ret == 0 || error_code != NO_ERROR)
1581 SetErrnoFromWinError (error_code);
1582 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1583 return GNUNET_SYSERR;
1585 if (STILL_ACTIVE == c)
1587 *type = GNUNET_OS_PROCESS_RUNNING;
1591 *type = GNUNET_OS_PROCESS_EXITED;
1600 * Wait for a process
1601 * @param proc pointer to process structure
1602 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1605 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1609 pid_t pid = proc->pid;
1612 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1613 (EINTR == errno) ) ;
1616 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1617 return GNUNET_SYSERR;
1627 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1629 return GNUNET_SYSERR;
1632 h = GetCurrentProcess ();
1634 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1636 SetErrnoFromWinError (GetLastError ());
1637 ret = GNUNET_SYSERR;
1648 * Handle to a command.
1650 struct GNUNET_OS_CommandHandle
1656 struct GNUNET_OS_Process *eip;
1659 * Handle to the output pipe.
1661 struct GNUNET_DISK_PipeHandle *opipe;
1664 * Read-end of output pipe.
1666 const struct GNUNET_DISK_FileHandle *r;
1669 * Function to call on each line of output.
1671 GNUNET_OS_LineProcessor proc;
1674 * Closure for 'proc'.
1679 * Buffer for the output.
1684 * Task reading from pipe.
1686 GNUNET_SCHEDULER_TaskIdentifier rtask;
1691 struct GNUNET_TIME_Absolute timeout;
1694 * Current read offset in buf.
1701 * Stop/kill a command. Must ONLY be called either from
1702 * the callback after 'NULL' was passed for 'line' *OR*
1703 * from an independent task (not within the line processor).
1705 * @param cmd handle to the process
1708 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1711 if (cmd->proc != NULL)
1713 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1714 GNUNET_SCHEDULER_cancel (cmd->rtask);
1716 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1717 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1718 GNUNET_OS_process_close (cmd->eip);
1719 GNUNET_DISK_pipe_close (cmd->opipe);
1725 * Read from the process and call the line processor.
1727 * @param cls the 'struct GNUNET_OS_CommandHandle'
1728 * @param tc scheduler context
1731 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1733 struct GNUNET_OS_CommandHandle *cmd = cls;
1734 GNUNET_OS_LineProcessor proc;
1738 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1739 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1741 /* timeout, shutdown, etc. */
1744 proc (cmd->proc_cls, NULL);
1748 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1749 sizeof (cmd->buf) - cmd->off);
1752 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1754 cmd->buf[cmd->off] = '\0';
1755 cmd->proc (cmd->proc_cls, cmd->buf);
1759 proc (cmd->proc_cls, NULL);
1762 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1767 cmd->proc (cmd->proc_cls, cmd->buf);
1768 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1769 cmd->off -= (end + 1 - cmd->buf);
1770 end = memchr (cmd->buf, '\n', cmd->off);
1773 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1774 (cmd->timeout), cmd->r, &cmd_read, cmd);
1779 * Run the given command line and call the given function
1780 * for each line of the output.
1782 * @param proc function to call for each line of the output
1783 * @param proc_cls closure for proc
1784 * @param timeout when to time out
1785 * @param binary command to run
1786 * @param ... arguments to command
1787 * @return NULL on error
1789 struct GNUNET_OS_CommandHandle *
1790 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1791 struct GNUNET_TIME_Relative timeout, const char *binary,
1794 struct GNUNET_OS_CommandHandle *cmd;
1795 struct GNUNET_OS_Process *eip;
1796 struct GNUNET_DISK_PipeHandle *opipe;
1799 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1802 va_start (ap, binary);
1803 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1807 GNUNET_DISK_pipe_close (opipe);
1810 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1811 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1812 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1816 cmd->proc_cls = proc_cls;
1817 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1818 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1825 /* end of os_priority.c */