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_INFO,
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;
331 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
333 if (0 != (tc->reason &
334 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
336 GNUNET_DISK_file_close (control_pipe);
340 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
341 if (sizeof (sig) != ret)
344 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
345 GNUNET_DISK_file_close (control_pipe);
349 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
350 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
351 control_pipe, &parent_control_handler,
358 * Task that connects this process to its parent via pipe;
359 * essentially, the parent control handler will read signal numbers
360 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
361 * variable) and raise those signals.
363 * @param cls closure (unused)
364 * @param tc scheduler context (unused)
367 GNUNET_OS_install_parent_control_handler (void *cls,
369 GNUNET_SCHEDULER_TaskContext *tc)
372 struct GNUNET_DISK_FileHandle *control_pipe;
374 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
375 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
377 LOG (GNUNET_ERROR_TYPE_DEBUG,
378 "Not installing a handler because $%s is empty\n",
379 GNUNET_OS_CONTROL_PIPE);
380 putenv ("GNUNET_OS_CONTROL_PIPE=");
384 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
385 if (NULL == control_pipe)
387 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
388 putenv ("GNUNET_OS_CONTROL_PIPE=");
391 LOG (GNUNET_ERROR_TYPE_DEBUG,
392 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
393 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
394 &parent_control_handler, control_pipe);
395 putenv ("GNUNET_OS_CONTROL_PIPE=");
400 * Get process structure for current process
402 * The pointer it returns points to static memory location and must not be
405 * @return pointer to the process sturcutre for this process
407 struct GNUNET_OS_Process *
408 GNUNET_OS_process_current ()
411 current_process.pid = GetCurrentProcessId ();
412 current_process.handle = GetCurrentProcess ();
414 current_process.pid = 0;
416 return ¤t_process;
421 * Sends a signal to the process
423 * @param proc pointer to process structure
425 * @return 0 on success, -1 on error
428 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
435 if ( (NULL == proc->control_pipe) &&
436 (NULL != proc->childpipename) )
437 proc->control_pipe = npipe_open (proc->childpipename,
438 GNUNET_DISK_OPEN_WRITE);
440 if (NULL != proc->control_pipe)
442 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
443 if (ret == sizeof (csig))
446 /* pipe failed or non-existent, try other methods */
455 #if WINDOWS && !defined(__CYGWIN__)
456 if (0 == TerminateProcess (proc->handle, 0))
458 /* FIXME: set 'errno' */
463 return PLIBC_KILL (proc->pid, sig);
470 return PLIBC_KILL (proc->pid, sig);
476 * Get the pid of the process in question
478 * @param proc the process to get the pid of
480 * @return the current process id
483 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
490 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
492 if (NULL != proc->control_pipe)
493 GNUNET_DISK_file_close (proc->control_pipe);
496 if (proc->handle != NULL)
497 CloseHandle (proc->handle);
499 if (NULL != proc->childpipename)
502 cleanup_npipe (proc->childpipename);
504 GNUNET_free (proc->childpipename);
511 #include "gnunet_signal_lib.h"
513 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
516 * Make seaspider happy.
518 #define DWORD_WINAPI DWORD WINAPI
521 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
522 * @param proc pointer to process structure
525 child_wait_thread (void *arg)
527 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
529 WaitForSingleObject (proc->handle, INFINITE);
531 if (w32_sigchld_handler)
532 w32_sigchld_handler ();
539 * Set process priority
541 * @param proc pointer to process structure
542 * @param prio priority value
543 * @return GNUNET_OK on success, GNUNET_SYSERR on error
546 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
547 enum GNUNET_SCHEDULER_Priority prio)
551 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
552 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
555 /* convert to MINGW/Unix values */
558 case GNUNET_SCHEDULER_PRIORITY_UI:
559 case GNUNET_SCHEDULER_PRIORITY_URGENT:
561 rprio = HIGH_PRIORITY_CLASS;
567 case GNUNET_SCHEDULER_PRIORITY_HIGH:
569 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
575 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
577 rprio = NORMAL_PRIORITY_CLASS;
583 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
585 rprio = BELOW_NORMAL_PRIORITY_CLASS;
591 case GNUNET_SCHEDULER_PRIORITY_IDLE:
593 rprio = IDLE_PRIORITY_CLASS;
600 return GNUNET_SYSERR;
603 /* Set process priority */
606 HANDLE h = proc->handle;
608 GNUNET_assert (h != NULL);
609 SetPriorityClass (h, rprio);
615 if ((0 == pid) || (pid == getpid ()))
618 int delta = rprio - have;
621 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
623 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
624 return GNUNET_SYSERR;
629 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
631 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
633 return GNUNET_SYSERR;
637 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
638 "Priority management not availabe for this platform\n");
645 CreateCustomEnvTable (char **vars)
647 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
648 size_t tablesize = 0;
649 size_t items_count = 0;
650 size_t n_found = 0, n_var;
657 win32_env_table = GetEnvironmentStringsA ();
658 if (win32_env_table == NULL)
660 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
662 index = GNUNET_malloc (sizeof (char *) * n_var);
663 for (c = 0; c < n_var; c++)
665 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
667 size_t len = strlen (ptr);
670 for (var_ptr = vars; *var_ptr; var_ptr++)
674 var_len = strlen (var);
675 if (strncmp (var, ptr, var_len) == 0)
679 tablesize += var_len + strlen (val) + 1;
684 tablesize += len + 1;
687 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
692 n_found += strlen (var) + strlen (val) + 1;
694 result = GNUNET_malloc (tablesize + n_found + 1);
695 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
697 size_t len = strlen (ptr);
700 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
704 var_len = strlen (var);
705 if (strncmp (var, ptr, var_len) == 0)
713 strcpy (result_ptr, ptr);
714 result_ptr += len + 1;
718 strcpy (result_ptr, var);
719 result_ptr += var_len;
720 strcpy (result_ptr, val);
721 result_ptr += strlen (val) + 1;
725 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
729 var_len = strlen (var);
732 strcpy (result_ptr, var);
733 result_ptr += var_len;
734 strcpy (result_ptr, val);
735 result_ptr += strlen (val) + 1;
738 FreeEnvironmentStrings (win32_env_table);
749 * @param pipe_control should a pipe be used to send signals to the child?
750 * @param pipe_stdin pipe to use to send input to child process (or NULL)
751 * @param pipe_stdout pipe to use to get output from child process (or NULL)
752 * @param filename name of the binary
753 * @param argv NULL-terminated array of arguments to the process
754 * @return pointer to process structure of the new process, NULL on error
756 struct GNUNET_OS_Process *
757 GNUNET_OS_start_process_vap (int pipe_control,
758 struct GNUNET_DISK_PipeHandle *pipe_stdin,
759 struct GNUNET_DISK_PipeHandle *pipe_stdout,
760 const char *filename,
764 char *childpipename = NULL;
765 struct GNUNET_OS_Process *gnunet_proc = NULL;
772 if ( (GNUNET_YES == pipe_control) &&
774 npipe_setup (&childpipename)) )
776 if (pipe_stdout != NULL)
778 GNUNET_assert (GNUNET_OK ==
779 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
781 GNUNET_DISK_PIPE_END_WRITE),
782 &fd_stdout_write, sizeof (int)));
783 GNUNET_assert (GNUNET_OK ==
784 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
785 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
786 &fd_stdout_read, sizeof (int)));
788 if (pipe_stdin != NULL)
790 GNUNET_assert (GNUNET_OK ==
791 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
792 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
793 &fd_stdin_read, sizeof (int)));
794 GNUNET_assert (GNUNET_OK ==
795 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
796 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
797 &fd_stdin_write, sizeof (int)));
803 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
804 GNUNET_free_non_null (childpipename);
809 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
810 gnunet_proc->pid = ret;
811 gnunet_proc->childpipename = childpipename;
814 if (NULL != childpipename)
816 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
817 GNUNET_free (childpipename);
819 if (pipe_stdout != NULL)
821 GNUNET_break (0 == close (fd_stdout_read));
822 if (-1 == dup2 (fd_stdout_write, 1))
823 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
824 GNUNET_break (0 == close (fd_stdout_write));
827 if (pipe_stdin != NULL)
830 GNUNET_break (0 == close (fd_stdin_write));
831 if (-1 == dup2 (fd_stdin_read, 0))
832 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
833 GNUNET_break (0 == close (fd_stdin_read));
835 execvp (filename, argv);
836 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
839 char *childpipename = NULL;
840 struct GNUNET_OS_Process *gnunet_proc = NULL;
845 PROCESS_INFORMATION proc;
848 HANDLE stdout_handle;
849 struct GNUNET_DISK_FileHandle *control_pipe;
851 char path[MAX_PATH + 1];
853 char *our_env[3] = { NULL, NULL, NULL };
854 char *env_block = NULL;
856 DWORD pathbuf_len, alloc_len;
861 char *non_const_filename;
862 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
864 /* Search in prefix dir (hopefully - the directory from which
865 * the current module was loaded), bindir and libdir, then in PATH
867 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
868 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
869 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
871 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
874 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
877 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
880 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
881 GNUNET_free (self_prefix);
882 GNUNET_free (bindir);
883 GNUNET_free (libdir);
885 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
886 GNUNET_assert (alloc_len == (pathbuf_len - 1));
888 cmdlen = strlen (filename);
889 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
890 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
892 GNUNET_asprintf (&non_const_filename, "%s", filename);
894 /* Check that this is the full path. If it isn't, search. */
895 if (non_const_filename[1] == ':')
896 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
897 else if (!SearchPathA
898 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
901 SetErrnoFromWinError (GetLastError ());
902 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
904 GNUNET_free (non_const_filename);
905 GNUNET_free (pathbuf);
908 GNUNET_free (pathbuf);
909 GNUNET_free (non_const_filename);
913 while (NULL != (arg = argv[argc++]))
916 cmdlen = cmdlen + strlen (path) + 4;
918 cmdlen = cmdlen + strlen (arg) + 4;
922 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
924 while (NULL != (arg = argv[argc++]))
926 /* This is to escape trailing slash */
927 char arg_lastchar = arg[strlen (arg) - 1];
929 idx += sprintf (idx, "\"%s%s\"%s", path,
930 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
932 idx += sprintf (idx, "\"%s%s\"%s", arg,
933 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
936 memset (&start, 0, sizeof (start));
937 start.cb = sizeof (start);
939 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
940 start.dwFlags |= STARTF_USESTDHANDLES;
942 if (pipe_stdin != NULL)
944 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
945 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
946 &stdin_handle, sizeof (HANDLE));
947 start.hStdInput = stdin_handle;
950 if (pipe_stdout != NULL)
952 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
954 GNUNET_DISK_PIPE_END_WRITE),
955 &stdout_handle, sizeof (HANDLE));
956 start.hStdOutput = stdout_handle;
958 if (GNUNET_YES == pipe_control)
961 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
962 GNUNET_DISK_PERM_USER_READ |
963 GNUNET_DISK_PERM_USER_WRITE);
964 if (control_pipe == NULL)
973 if (NULL != childpipename)
975 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
977 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
978 GNUNET_asprintf (&our_env[1], "%s", childpipename);
985 env_block = CreateCustomEnvTable (our_env);
986 GNUNET_free_non_null (our_env[0]);
987 GNUNET_free_non_null (our_env[1]);
989 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
990 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
992 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
993 env_block, NULL, &start, &proc))
995 SetErrnoFromWinError (GetLastError ());
996 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
997 GNUNET_free (env_block);
1002 GNUNET_free (env_block);
1004 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1005 gnunet_proc->pid = proc.dwProcessId;
1006 gnunet_proc->handle = proc.hProcess;
1007 gnunet_proc->control_pipe = control_pipe;
1009 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1011 ResumeThread (proc.hThread);
1012 CloseHandle (proc.hThread);
1024 * @param pipe_control should a pipe be used to send signals to the child?
1025 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1026 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1027 * @param filename name of the binary
1028 * @param va NULL-terminated list of arguments to the process
1029 * @return pointer to process structure of the new process, NULL on error
1031 struct GNUNET_OS_Process *
1032 GNUNET_OS_start_process_va (int pipe_control,
1033 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1034 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1035 const char *filename, va_list va)
1037 struct GNUNET_OS_Process *ret;
1044 while (NULL != va_arg (ap, char *))
1047 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1050 while (NULL != (argv[argc] = va_arg (ap, char *)))
1053 ret = GNUNET_OS_start_process_vap (pipe_control,
1067 * @param pipe_control should a pipe be used to send signals to the child?
1068 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1069 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1070 * @param filename name of the binary
1071 * @param ... NULL-terminated list of arguments to the process
1073 * @return pointer to process structure of the new process, NULL on error
1076 struct GNUNET_OS_Process *
1077 GNUNET_OS_start_process (int pipe_control,
1078 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1079 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1080 const char *filename, ...)
1082 struct GNUNET_OS_Process *ret;
1085 va_start (ap, filename);
1086 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1095 * @param pipe_control should a pipe be used to send signals to the child?
1096 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1097 * must be NULL on platforms where dup is not supported
1098 * @param filename name of the binary
1099 * @param argv NULL-terminated list of arguments to the process
1100 * @return process ID of the new process, -1 on error
1102 struct GNUNET_OS_Process *
1103 GNUNET_OS_start_process_v (int pipe_control,
1104 const SOCKTYPE *lsocks,
1105 const char *filename,
1112 struct GNUNET_OS_Process *gnunet_proc = NULL;
1113 char *childpipename = NULL;
1122 if ( (GNUNET_YES == pipe_control) &&
1123 (GNUNET_OK != npipe_setup (&childpipename)) )
1130 while (-1 != (k = lsocks[i++]))
1131 GNUNET_array_append (lscp, ls, k);
1132 GNUNET_array_append (lscp, ls, -1);
1137 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1138 GNUNET_free_non_null (childpipename);
1139 GNUNET_array_grow (lscp, ls, 0);
1144 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1145 gnunet_proc->pid = ret;
1146 gnunet_proc->childpipename = childpipename;
1147 GNUNET_array_grow (lscp, ls, 0);
1150 if (NULL != childpipename)
1152 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1153 GNUNET_free (childpipename);
1157 /* read systemd documentation... */
1158 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1159 setenv ("LISTEN_PID", lpid, 1);
1162 while (-1 != lscp[i])
1165 while (-1 != lscp[j])
1171 GNUNET_assert (-1 != k);
1172 GNUNET_assert (0 == close (lscp[j]));
1180 /* Bury any existing FD, no matter what; they should all be closed
1181 * on exec anyway and the important onces have been dup'ed away */
1183 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1185 /* unset close-on-exec flag */
1186 flags = fcntl (tgt, F_GETFD);
1187 GNUNET_assert (flags >= 0);
1188 flags &= ~FD_CLOEXEC;
1190 (void) fcntl (tgt, F_SETFD, flags);
1194 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1195 setenv ("LISTEN_FDS", fds, 1);
1197 GNUNET_array_grow (lscp, ls, 0);
1198 execvp (filename, argv);
1199 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1202 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1203 char *childpipename = NULL;
1204 char **arg, **non_const_argv;
1205 unsigned int cmdlen;
1208 PROCESS_INFORMATION proc;
1210 struct GNUNET_OS_Process *gnunet_proc = NULL;
1211 char path[MAX_PATH + 1];
1212 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1213 char *env_block = NULL;
1215 DWORD pathbuf_len, alloc_len;
1220 char *non_const_filename;
1221 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1222 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1224 HANDLE lsocks_write;
1225 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1229 /* Search in prefix dir (hopefully - the directory from which
1230 * the current module was loaded), bindir and libdir, then in PATH
1232 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1233 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1234 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1236 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1239 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1242 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1245 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1246 GNUNET_free (self_prefix);
1247 GNUNET_free (bindir);
1248 GNUNET_free (libdir);
1250 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1251 if (alloc_len != pathbuf_len - 1)
1253 GNUNET_free (pathbuf);
1254 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1258 cmdlen = strlen (filename);
1259 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1260 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1262 GNUNET_asprintf (&non_const_filename, "%s", filename);
1264 /* Check that this is the full path. If it isn't, search. */
1265 if (non_const_filename[1] == ':')
1266 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1267 else if (!SearchPathA
1268 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1271 SetErrnoFromWinError (GetLastError ());
1272 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1273 non_const_filename);
1274 GNUNET_free (non_const_filename);
1275 GNUNET_free (pathbuf);
1278 GNUNET_free (pathbuf);
1279 GNUNET_free (non_const_filename);
1281 /* Count the number of arguments */
1282 arg = (char **) argv;
1289 /* Allocate a copy argv */
1290 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1292 /* Copy all argv strings */
1294 arg = (char **) argv;
1298 non_const_argv[argcount] = GNUNET_strdup (path);
1300 non_const_argv[argcount] = GNUNET_strdup (*arg);
1304 non_const_argv[argcount] = NULL;
1308 arg = non_const_argv;
1311 cmdlen = cmdlen + strlen (*arg) + 4;
1315 /* Allocate and create cmd */
1316 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1317 arg = non_const_argv;
1320 char arg_last_char = (*arg)[strlen (*arg) - 1];
1321 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1322 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1326 while (argcount > 0)
1327 GNUNET_free (non_const_argv[--argcount]);
1328 GNUNET_free (non_const_argv);
1330 memset (&start, 0, sizeof (start));
1331 start.cb = sizeof (start);
1333 if (GNUNET_YES == pipe_control)
1336 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1337 GNUNET_DISK_PERM_USER_READ |
1338 GNUNET_DISK_PERM_USER_WRITE);
1339 if (control_pipe == NULL)
1347 control_pipe = NULL;
1348 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1350 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1352 if (lsocks_pipe == NULL)
1356 GNUNET_DISK_pipe_close (lsocks_pipe);
1359 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1360 GNUNET_DISK_PIPE_END_WRITE);
1361 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1362 &lsocks_write, sizeof (HANDLE));
1363 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1364 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1365 &lsocks_read, sizeof (HANDLE));
1369 if (NULL != childpipename)
1371 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1373 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1374 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1375 GNUNET_free (childpipename);
1377 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1379 /*This will tell the child that we're going to send lsocks over the pipe*/
1380 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1381 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1383 our_env[env_off++] = NULL;
1384 env_block = CreateCustomEnvTable (our_env);
1386 GNUNET_free_non_null (our_env[--env_off]);
1387 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1388 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1390 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1391 env_block, NULL, &start, &proc))
1393 SetErrnoFromWinError (GetLastError ());
1394 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1395 if (NULL != control_pipe)
1396 GNUNET_DISK_file_close (control_pipe);
1398 GNUNET_DISK_pipe_close (lsocks_pipe);
1399 GNUNET_free (env_block);
1404 GNUNET_free (env_block);
1406 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1407 gnunet_proc->pid = proc.dwProcessId;
1408 gnunet_proc->handle = proc.hProcess;
1409 gnunet_proc->control_pipe = control_pipe;
1411 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1413 ResumeThread (proc.hThread);
1414 CloseHandle (proc.hThread);
1417 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1420 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1422 /* This is a replacement for "goto error" that doesn't use goto */
1427 uint64_t size, count, i;
1429 /* Tell the number of sockets */
1430 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1432 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1433 if (wrote != sizeof (count))
1435 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1438 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1440 WSAPROTOCOL_INFOA pi;
1441 /* Get a socket duplication info */
1442 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1444 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1445 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1448 /* Synchronous I/O is not nice, but we can't schedule this:
1449 * lsocks will be closed/freed by the caller soon, and until
1450 * the child creates a duplicate, closing a socket here will
1451 * close it for good.
1453 /* Send the size of the structure
1454 * (the child might be built with different headers...)
1457 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1458 if (wrote != sizeof (size))
1460 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1463 /* Finally! Send the data */
1464 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1465 if (wrote != sizeof (pi))
1467 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1471 /* This will block us until the child makes a final read or closes
1472 * the pipe (hence no 'wrote' check), since we have to wait for it
1473 * to duplicate the last socket, before we return and start closing
1476 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1481 GNUNET_DISK_file_sync (lsocks_write_fd);
1482 GNUNET_DISK_pipe_close (lsocks_pipe);
1486 /* If we can't pass on the socket(s), the child will block forever,
1487 * better put it out of its misery.
1489 TerminateProcess (gnunet_proc->handle, 0);
1490 CloseHandle (gnunet_proc->handle);
1491 if (NULL != gnunet_proc->control_pipe)
1492 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1493 GNUNET_free (gnunet_proc);
1502 * Retrieve the status of a process
1503 * @param proc process ID
1504 * @param type status type
1505 * @param code return code/signal number
1506 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1509 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1510 enum GNUNET_OS_ProcessStatusType *type,
1511 unsigned long *code)
1517 GNUNET_assert (0 != proc);
1518 ret = waitpid (proc->pid, &status, WNOHANG);
1521 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1522 return GNUNET_SYSERR;
1526 *type = GNUNET_OS_PROCESS_RUNNING;
1530 if (proc->pid != ret)
1532 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1533 return GNUNET_SYSERR;
1535 if (WIFEXITED (status))
1537 *type = GNUNET_OS_PROCESS_EXITED;
1538 *code = WEXITSTATUS (status);
1540 else if (WIFSIGNALED (status))
1542 *type = GNUNET_OS_PROCESS_SIGNALED;
1543 *code = WTERMSIG (status);
1545 else if (WIFSTOPPED (status))
1547 *type = GNUNET_OS_PROCESS_SIGNALED;
1548 *code = WSTOPSIG (status);
1551 else if (WIFCONTINUED (status))
1553 *type = GNUNET_OS_PROCESS_RUNNING;
1559 *type = GNUNET_OS_PROCESS_UNKNOWN;
1564 DWORD c, error_code, ret;
1568 if (h == NULL || ret == 0)
1570 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1572 return GNUNET_SYSERR;
1575 h = GetCurrentProcess ();
1578 ret = GetExitCodeProcess (h, &c);
1579 error_code = GetLastError ();
1580 if (ret == 0 || error_code != NO_ERROR)
1582 SetErrnoFromWinError (error_code);
1583 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1584 return GNUNET_SYSERR;
1586 if (STILL_ACTIVE == c)
1588 *type = GNUNET_OS_PROCESS_RUNNING;
1592 *type = GNUNET_OS_PROCESS_EXITED;
1601 * Wait for a process
1602 * @param proc pointer to process structure
1603 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1606 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1610 pid_t pid = proc->pid;
1613 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1614 (EINTR == errno) ) ;
1617 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1618 return GNUNET_SYSERR;
1628 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1630 return GNUNET_SYSERR;
1633 h = GetCurrentProcess ();
1635 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1637 SetErrnoFromWinError (GetLastError ());
1638 ret = GNUNET_SYSERR;
1649 * Handle to a command.
1651 struct GNUNET_OS_CommandHandle
1657 struct GNUNET_OS_Process *eip;
1660 * Handle to the output pipe.
1662 struct GNUNET_DISK_PipeHandle *opipe;
1665 * Read-end of output pipe.
1667 const struct GNUNET_DISK_FileHandle *r;
1670 * Function to call on each line of output.
1672 GNUNET_OS_LineProcessor proc;
1675 * Closure for 'proc'.
1680 * Buffer for the output.
1685 * Task reading from pipe.
1687 GNUNET_SCHEDULER_TaskIdentifier rtask;
1692 struct GNUNET_TIME_Absolute timeout;
1695 * Current read offset in buf.
1702 * Stop/kill a command. Must ONLY be called either from
1703 * the callback after 'NULL' was passed for 'line' *OR*
1704 * from an independent task (not within the line processor).
1706 * @param cmd handle to the process
1709 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1712 if (cmd->proc != NULL)
1714 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1715 GNUNET_SCHEDULER_cancel (cmd->rtask);
1717 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1718 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1719 GNUNET_OS_process_close (cmd->eip);
1720 GNUNET_DISK_pipe_close (cmd->opipe);
1726 * Read from the process and call the line processor.
1728 * @param cls the 'struct GNUNET_OS_CommandHandle'
1729 * @param tc scheduler context
1732 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1734 struct GNUNET_OS_CommandHandle *cmd = cls;
1735 GNUNET_OS_LineProcessor proc;
1739 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1740 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1742 /* timeout, shutdown, etc. */
1745 proc (cmd->proc_cls, NULL);
1749 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1750 sizeof (cmd->buf) - cmd->off);
1753 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1755 cmd->buf[cmd->off] = '\0';
1756 cmd->proc (cmd->proc_cls, cmd->buf);
1760 proc (cmd->proc_cls, NULL);
1763 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1768 cmd->proc (cmd->proc_cls, cmd->buf);
1769 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1770 cmd->off -= (end + 1 - cmd->buf);
1771 end = memchr (cmd->buf, '\n', cmd->off);
1774 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1775 (cmd->timeout), cmd->r, &cmd_read, cmd);
1780 * Run the given command line and call the given function
1781 * for each line of the output.
1783 * @param proc function to call for each line of the output
1784 * @param proc_cls closure for proc
1785 * @param timeout when to time out
1786 * @param binary command to run
1787 * @param ... arguments to command
1788 * @return NULL on error
1790 struct GNUNET_OS_CommandHandle *
1791 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1792 struct GNUNET_TIME_Relative timeout, const char *binary,
1795 struct GNUNET_OS_CommandHandle *cmd;
1796 struct GNUNET_OS_Process *eip;
1797 struct GNUNET_DISK_PipeHandle *opipe;
1800 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1803 va_start (ap, binary);
1804 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1808 GNUNET_DISK_pipe_close (opipe);
1811 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1812 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1813 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1817 cmd->proc_cls = proc_cls;
1818 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1819 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1826 /* end of os_priority.c */