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 */
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 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 ENABLE_WINDOWS_WORKAROUNDS
493 if (proc->control_pipe)
494 GNUNET_DISK_file_close (proc->control_pipe);
498 if (proc->handle != NULL)
499 CloseHandle (proc->handle);
501 if (NULL != proc->childpipename)
503 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;
850 char path[MAX_PATH + 1];
852 char *our_env[3] = { NULL, NULL, NULL };
853 char *env_block = NULL;
855 DWORD pathbuf_len, alloc_len;
860 char *non_const_filename;
861 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
863 /* Search in prefix dir (hopefully - the directory from which
864 * the current module was loaded), bindir and libdir, then in PATH
866 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
867 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
868 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
870 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
873 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
876 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
879 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
880 GNUNET_free (self_prefix);
881 GNUNET_free (bindir);
882 GNUNET_free (libdir);
884 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
885 GNUNET_assert (alloc_len == (pathbuf_len - 1));
887 cmdlen = strlen (filename);
888 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
889 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
891 GNUNET_asprintf (&non_const_filename, "%s", filename);
893 /* Check that this is the full path. If it isn't, search. */
894 if (non_const_filename[1] == ':')
895 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
896 else if (!SearchPathA
897 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
900 SetErrnoFromWinError (GetLastError ());
901 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
903 GNUNET_free (non_const_filename);
904 GNUNET_free (pathbuf);
907 GNUNET_free (pathbuf);
908 GNUNET_free (non_const_filename);
912 while (NULL != (arg = argv[argc++]))
915 cmdlen = cmdlen + strlen (path) + 4;
917 cmdlen = cmdlen + strlen (arg) + 4;
921 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
923 while (NULL != (arg = argv[argc++]))
925 /* This is to escape trailing slash */
926 char arg_lastchar = arg[strlen (arg) - 1];
928 idx += sprintf (idx, "\"%s%s\"%s", path,
929 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
931 idx += sprintf (idx, "\"%s%s\"%s", arg,
932 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
935 memset (&start, 0, sizeof (start));
936 start.cb = sizeof (start);
938 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
939 start.dwFlags |= STARTF_USESTDHANDLES;
941 if (pipe_stdin != NULL)
943 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
944 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
945 &stdin_handle, sizeof (HANDLE));
946 start.hStdInput = stdin_handle;
949 if (pipe_stdout != NULL)
951 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
953 GNUNET_DISK_PIPE_END_WRITE),
954 &stdout_handle, sizeof (HANDLE));
955 start.hStdOutput = stdout_handle;
957 if (GNUNET_YES == pipe_control)
960 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
961 GNUNET_DISK_PERM_USER_READ |
962 GNUNET_DISK_PERM_USER_WRITE);
963 if (control_pipe == NULL)
970 if (NULL != childpipename)
972 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
974 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
975 GNUNET_asprintf (&our_env[1], "%s", childpipename);
982 env_block = CreateCustomEnvTable (our_env);
983 GNUNET_free (our_env[0]);
984 GNUNET_free (our_env[1]);
986 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
987 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
989 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
990 env_block, NULL, &start, &proc))
992 SetErrnoFromWinError (GetLastError ());
993 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
994 GNUNET_free (env_block);
999 GNUNET_free (env_block);
1001 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1002 gnunet_proc->pid = proc.dwProcessId;
1003 gnunet_proc->handle = proc.hProcess;
1004 gnunet_proc->control_pipe = control_pipe;
1006 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1008 ResumeThread (proc.hThread);
1009 CloseHandle (proc.hThread);
1021 * @param pipe_control should a pipe be used to send signals to the child?
1022 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1023 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1024 * @param filename name of the binary
1025 * @param va NULL-terminated list of arguments to the process
1026 * @return pointer to process structure of the new process, NULL on error
1028 struct GNUNET_OS_Process *
1029 GNUNET_OS_start_process_va (int pipe_control,
1030 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1031 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1032 const char *filename, va_list va)
1034 struct GNUNET_OS_Process *ret;
1041 while (NULL != va_arg (ap, char *))
1044 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1047 while (NULL != (argv[argc] = va_arg (ap, char *)))
1050 ret = GNUNET_OS_start_process_vap (pipe_control,
1064 * @param pipe_control should a pipe be used to send signals to the child?
1065 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1066 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1067 * @param filename name of the binary
1068 * @param ... NULL-terminated list of arguments to the process
1070 * @return pointer to process structure of the new process, NULL on error
1073 struct GNUNET_OS_Process *
1074 GNUNET_OS_start_process (int pipe_control,
1075 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1076 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1077 const char *filename, ...)
1079 struct GNUNET_OS_Process *ret;
1082 va_start (ap, filename);
1083 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1092 * @param pipe_control should a pipe be used to send signals to the child?
1093 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1094 * must be NULL on platforms where dup is not supported
1095 * @param filename name of the binary
1096 * @param argv NULL-terminated list of arguments to the process
1097 * @return process ID of the new process, -1 on error
1099 struct GNUNET_OS_Process *
1100 GNUNET_OS_start_process_v (int pipe_control,
1101 const SOCKTYPE *lsocks,
1102 const char *filename,
1109 struct GNUNET_OS_Process *gnunet_proc = NULL;
1110 char *childpipename = NULL;
1119 if ( (GNUNET_YES == pipe_control) &&
1120 (GNUNET_OK != npipe_setup (&childpipename)) )
1127 while (-1 != (k = lsocks[i++]))
1128 GNUNET_array_append (lscp, ls, k);
1129 GNUNET_array_append (lscp, ls, -1);
1134 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1135 GNUNET_free_non_null (childpipename);
1136 GNUNET_array_grow (lscp, ls, 0);
1141 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1142 gnunet_proc->pid = ret;
1143 gnunet_proc->childpipename = childpipename;
1144 GNUNET_array_grow (lscp, ls, 0);
1147 if (NULL != childpipename)
1149 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1150 GNUNET_free (childpipename);
1154 /* read systemd documentation... */
1155 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1156 setenv ("LISTEN_PID", lpid, 1);
1159 while (-1 != lscp[i])
1162 while (-1 != lscp[j])
1168 GNUNET_assert (-1 != k);
1169 GNUNET_assert (0 == close (lscp[j]));
1177 /* Bury any existing FD, no matter what; they should all be closed
1178 * on exec anyway and the important onces have been dup'ed away */
1180 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1182 /* unset close-on-exec flag */
1183 flags = fcntl (tgt, F_GETFD);
1184 GNUNET_assert (flags >= 0);
1185 flags &= ~FD_CLOEXEC;
1187 (void) fcntl (tgt, F_SETFD, flags);
1191 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1192 setenv ("LISTEN_FDS", fds, 1);
1194 GNUNET_array_grow (lscp, ls, 0);
1195 execvp (filename, argv);
1196 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1199 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1200 char *childpipename = NULL;
1201 char **arg, **non_const_argv;
1202 unsigned int cmdlen;
1205 PROCESS_INFORMATION proc;
1207 struct GNUNET_OS_Process *gnunet_proc = NULL;
1208 char path[MAX_PATH + 1];
1209 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1210 char *env_block = NULL;
1212 DWORD pathbuf_len, alloc_len;
1217 char *non_const_filename;
1218 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1219 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1221 HANDLE lsocks_write;
1222 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1226 /* Search in prefix dir (hopefully - the directory from which
1227 * the current module was loaded), bindir and libdir, then in PATH
1229 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1230 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1231 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1233 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1236 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1239 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1242 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1243 GNUNET_free (self_prefix);
1244 GNUNET_free (bindir);
1245 GNUNET_free (libdir);
1247 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1248 if (alloc_len != pathbuf_len - 1)
1250 GNUNET_free (pathbuf);
1251 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1255 cmdlen = strlen (filename);
1256 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1257 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1259 GNUNET_asprintf (&non_const_filename, "%s", filename);
1261 /* Check that this is the full path. If it isn't, search. */
1262 if (non_const_filename[1] == ':')
1263 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1264 else if (!SearchPathA
1265 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1268 SetErrnoFromWinError (GetLastError ());
1269 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1270 non_const_filename);
1271 GNUNET_free (non_const_filename);
1272 GNUNET_free (pathbuf);
1275 GNUNET_free (pathbuf);
1276 GNUNET_free (non_const_filename);
1278 /* Count the number of arguments */
1279 arg = (char **) argv;
1286 /* Allocate a copy argv */
1287 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1289 /* Copy all argv strings */
1291 arg = (char **) argv;
1295 non_const_argv[argcount] = GNUNET_strdup (path);
1297 non_const_argv[argcount] = GNUNET_strdup (*arg);
1301 non_const_argv[argcount] = NULL;
1305 arg = non_const_argv;
1308 cmdlen = cmdlen + strlen (*arg) + 4;
1312 /* Allocate and create cmd */
1313 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1314 arg = non_const_argv;
1317 char arg_last_char = (*arg)[strlen (*arg) - 1];
1318 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1319 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1323 while (argcount > 0)
1324 GNUNET_free (non_const_argv[--argcount]);
1325 GNUNET_free (non_const_argv);
1327 memset (&start, 0, sizeof (start));
1328 start.cb = sizeof (start);
1330 if (GNUNET_YES == pipe_control)
1333 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1334 GNUNET_DISK_PERM_USER_READ |
1335 GNUNET_DISK_PERM_USER_WRITE);
1336 if (control_pipe == NULL)
1343 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1345 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1347 if (lsocks_pipe == NULL)
1351 GNUNET_DISK_pipe_close (lsocks_pipe);
1354 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1355 GNUNET_DISK_PIPE_END_WRITE);
1356 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1357 &lsocks_write, sizeof (HANDLE));
1358 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1359 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1360 &lsocks_read, sizeof (HANDLE));
1364 if (NULL != childpipename)
1366 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1368 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1369 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1370 GNUNET_free (childpipename);
1372 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1374 /*This will tell the child that we're going to send lsocks over the pipe*/
1375 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1376 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1378 our_env[env_off++] = NULL;
1379 env_block = CreateCustomEnvTable (our_env);
1381 GNUNET_free_non_null (our_env[--env_off]);
1382 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1383 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1385 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1386 env_block, NULL, &start, &proc))
1388 SetErrnoFromWinError (GetLastError ());
1389 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1390 if (NULL != control_pipe)
1391 GNUNET_DISK_file_close (control_pipe);
1393 GNUNET_DISK_pipe_close (lsocks_pipe);
1394 GNUNET_free (env_block);
1399 GNUNET_free (env_block);
1401 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1402 gnunet_proc->pid = proc.dwProcessId;
1403 gnunet_proc->handle = proc.hProcess;
1404 gnunet_proc->control_pipe = control_pipe;
1406 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1408 ResumeThread (proc.hThread);
1409 CloseHandle (proc.hThread);
1412 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1415 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1417 /* This is a replacement for "goto error" that doesn't use goto */
1422 uint64_t size, count, i;
1424 /* Tell the number of sockets */
1425 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1427 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1428 if (wrote != sizeof (count))
1430 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1433 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1435 WSAPROTOCOL_INFOA pi;
1436 /* Get a socket duplication info */
1437 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1439 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1440 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1443 /* Synchronous I/O is not nice, but we can't schedule this:
1444 * lsocks will be closed/freed by the caller soon, and until
1445 * the child creates a duplicate, closing a socket here will
1446 * close it for good.
1448 /* Send the size of the structure
1449 * (the child might be built with different headers...)
1452 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1453 if (wrote != sizeof (size))
1455 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1458 /* Finally! Send the data */
1459 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1460 if (wrote != sizeof (pi))
1462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1466 /* This will block us until the child makes a final read or closes
1467 * the pipe (hence no 'wrote' check), since we have to wait for it
1468 * to duplicate the last socket, before we return and start closing
1471 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1476 GNUNET_DISK_file_sync (lsocks_write_fd);
1477 GNUNET_DISK_pipe_close (lsocks_pipe);
1481 /* If we can't pass on the socket(s), the child will block forever,
1482 * better put it out of its misery.
1484 TerminateProcess (gnunet_proc->handle, 0);
1485 CloseHandle (gnunet_proc->handle);
1486 if (NULL != gnunet_proc->control_pipe)
1487 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1488 GNUNET_free (gnunet_proc);
1497 * Retrieve the status of a process
1498 * @param proc process ID
1499 * @param type status type
1500 * @param code return code/signal number
1501 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1504 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1505 enum GNUNET_OS_ProcessStatusType *type,
1506 unsigned long *code)
1512 GNUNET_assert (0 != proc);
1513 ret = waitpid (proc->pid, &status, WNOHANG);
1516 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1517 return GNUNET_SYSERR;
1521 *type = GNUNET_OS_PROCESS_RUNNING;
1525 if (proc->pid != ret)
1527 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1528 return GNUNET_SYSERR;
1530 if (WIFEXITED (status))
1532 *type = GNUNET_OS_PROCESS_EXITED;
1533 *code = WEXITSTATUS (status);
1535 else if (WIFSIGNALED (status))
1537 *type = GNUNET_OS_PROCESS_SIGNALED;
1538 *code = WTERMSIG (status);
1540 else if (WIFSTOPPED (status))
1542 *type = GNUNET_OS_PROCESS_SIGNALED;
1543 *code = WSTOPSIG (status);
1546 else if (WIFCONTINUED (status))
1548 *type = GNUNET_OS_PROCESS_RUNNING;
1554 *type = GNUNET_OS_PROCESS_UNKNOWN;
1559 DWORD c, error_code, ret;
1563 if (h == NULL || ret == 0)
1565 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1567 return GNUNET_SYSERR;
1570 h = GetCurrentProcess ();
1573 ret = GetExitCodeProcess (h, &c);
1574 error_code = GetLastError ();
1575 if (ret == 0 || error_code != NO_ERROR)
1577 SetErrnoFromWinError (error_code);
1578 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1579 return GNUNET_SYSERR;
1581 if (STILL_ACTIVE == c)
1583 *type = GNUNET_OS_PROCESS_RUNNING;
1587 *type = GNUNET_OS_PROCESS_EXITED;
1596 * Wait for a process
1597 * @param proc pointer to process structure
1598 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1601 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1605 pid_t pid = proc->pid;
1608 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1609 (EINTR == errno) ) ;
1612 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1613 return GNUNET_SYSERR;
1623 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1625 return GNUNET_SYSERR;
1628 h = GetCurrentProcess ();
1630 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1632 SetErrnoFromWinError (GetLastError ());
1633 ret = GNUNET_SYSERR;
1644 * Handle to a command.
1646 struct GNUNET_OS_CommandHandle
1652 struct GNUNET_OS_Process *eip;
1655 * Handle to the output pipe.
1657 struct GNUNET_DISK_PipeHandle *opipe;
1660 * Read-end of output pipe.
1662 const struct GNUNET_DISK_FileHandle *r;
1665 * Function to call on each line of output.
1667 GNUNET_OS_LineProcessor proc;
1670 * Closure for 'proc'.
1675 * Buffer for the output.
1680 * Task reading from pipe.
1682 GNUNET_SCHEDULER_TaskIdentifier rtask;
1687 struct GNUNET_TIME_Absolute timeout;
1690 * Current read offset in buf.
1697 * Stop/kill a command. Must ONLY be called either from
1698 * the callback after 'NULL' was passed for 'line' *OR*
1699 * from an independent task (not within the line processor).
1701 * @param cmd handle to the process
1704 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1707 if (cmd->proc != NULL)
1709 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1710 GNUNET_SCHEDULER_cancel (cmd->rtask);
1712 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1713 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1714 GNUNET_OS_process_close (cmd->eip);
1715 GNUNET_DISK_pipe_close (cmd->opipe);
1721 * Read from the process and call the line processor.
1723 * @param cls the 'struct GNUNET_OS_CommandHandle'
1724 * @param tc scheduler context
1727 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1729 struct GNUNET_OS_CommandHandle *cmd = cls;
1730 GNUNET_OS_LineProcessor proc;
1734 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1735 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1737 /* timeout, shutdown, etc. */
1740 proc (cmd->proc_cls, NULL);
1744 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1745 sizeof (cmd->buf) - cmd->off);
1748 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1750 cmd->buf[cmd->off] = '\0';
1751 cmd->proc (cmd->proc_cls, cmd->buf);
1755 proc (cmd->proc_cls, NULL);
1758 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1763 cmd->proc (cmd->proc_cls, cmd->buf);
1764 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1765 cmd->off -= (end + 1 - cmd->buf);
1766 end = memchr (cmd->buf, '\n', cmd->off);
1769 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1770 (cmd->timeout), cmd->r, &cmd_read, cmd);
1775 * Run the given command line and call the given function
1776 * for each line of the output.
1778 * @param proc function to call for each line of the output
1779 * @param proc_cls closure for proc
1780 * @param timeout when to time out
1781 * @param binary command to run
1782 * @param ... arguments to command
1783 * @return NULL on error
1785 struct GNUNET_OS_CommandHandle *
1786 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1787 struct GNUNET_TIME_Relative timeout, const char *binary,
1790 struct GNUNET_OS_CommandHandle *cmd;
1791 struct GNUNET_OS_Process *eip;
1792 struct GNUNET_DISK_PipeHandle *opipe;
1795 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1798 va_start (ap, binary);
1799 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1803 GNUNET_DISK_pipe_close (opipe);
1806 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1807 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1808 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1812 cmd->proc_cls = proc_cls;
1813 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1814 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1821 /* end of os_priority.c */