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;
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)
432 if ( (NULL == proc->control_pipe) &&
433 (NULL != proc->childpipename) )
434 proc->control_pipe = npipe_open (proc->childpipename,
435 GNUNET_DISK_OPEN_WRITE);
437 if (NULL == proc->control_pipe)
440 /* no pipe and windows? can't do this */
444 return kill (proc->pid, sig);
447 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
448 if (ret == sizeof (csig))
450 /* pipe failed, try other methods */
459 #if WINDOWS && !defined(__CYGWIN__)
460 if (0 == TerminateProcess (proc->handle, 0))
462 /* FIXME: set 'errno' */
467 return PLIBC_KILL (proc->pid, sig);
474 return kill (proc->pid, sig);
480 * Get the pid of the process in question
482 * @param proc the process to get the pid of
484 * @return the current process id
487 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
494 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
496 if (NULL != proc->control_pipe)
497 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;
853 struct GNUNET_DISK_FileHandle *control_pipe;
855 char path[MAX_PATH + 1];
857 char *our_env[3] = { NULL, NULL, NULL };
858 char *env_block = NULL;
860 DWORD pathbuf_len, alloc_len;
865 char *non_const_filename;
866 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
868 /* Search in prefix dir (hopefully - the directory from which
869 * the current module was loaded), bindir and libdir, then in PATH
871 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
872 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
873 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
875 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
878 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
881 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
884 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
885 GNUNET_free (self_prefix);
886 GNUNET_free (bindir);
887 GNUNET_free (libdir);
889 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
890 GNUNET_assert (alloc_len == (pathbuf_len - 1));
892 cmdlen = strlen (filename);
893 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
894 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
896 GNUNET_asprintf (&non_const_filename, "%s", filename);
898 /* Check that this is the full path. If it isn't, search. */
899 if (non_const_filename[1] == ':')
900 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
901 else if (!SearchPathA
902 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
905 SetErrnoFromWinError (GetLastError ());
906 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
908 GNUNET_free (non_const_filename);
909 GNUNET_free (pathbuf);
912 GNUNET_free (pathbuf);
913 GNUNET_free (non_const_filename);
917 while (NULL != (arg = argv[argc++]))
920 cmdlen = cmdlen + strlen (path) + 4;
922 cmdlen = cmdlen + strlen (arg) + 4;
926 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
928 while (NULL != (arg = argv[argc++]))
930 /* This is to escape trailing slash */
931 char arg_lastchar = arg[strlen (arg) - 1];
933 idx += sprintf (idx, "\"%s%s\"%s", path,
934 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
936 idx += sprintf (idx, "\"%s%s\"%s", arg,
937 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
940 memset (&start, 0, sizeof (start));
941 start.cb = sizeof (start);
943 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
944 start.dwFlags |= STARTF_USESTDHANDLES;
946 if (pipe_stdin != NULL)
948 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
949 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
950 &stdin_handle, sizeof (HANDLE));
951 start.hStdInput = stdin_handle;
954 if (pipe_stdout != NULL)
956 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
958 GNUNET_DISK_PIPE_END_WRITE),
959 &stdout_handle, sizeof (HANDLE));
960 start.hStdOutput = stdout_handle;
962 if (GNUNET_YES == pipe_control)
965 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
966 GNUNET_DISK_PERM_USER_READ |
967 GNUNET_DISK_PERM_USER_WRITE);
968 if (control_pipe == NULL)
977 if (NULL != childpipename)
979 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
981 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
982 GNUNET_asprintf (&our_env[1], "%s", childpipename);
989 env_block = CreateCustomEnvTable (our_env);
990 GNUNET_free_non_null (our_env[0]);
991 GNUNET_free_non_null (our_env[1]);
993 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
994 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
996 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
997 env_block, NULL, &start, &proc))
999 SetErrnoFromWinError (GetLastError ());
1000 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1001 GNUNET_free (env_block);
1006 GNUNET_free (env_block);
1008 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1009 gnunet_proc->pid = proc.dwProcessId;
1010 gnunet_proc->handle = proc.hProcess;
1011 gnunet_proc->control_pipe = control_pipe;
1013 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1015 ResumeThread (proc.hThread);
1016 CloseHandle (proc.hThread);
1028 * @param pipe_control should a pipe be used to send signals to the child?
1029 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1030 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1031 * @param filename name of the binary
1032 * @param va NULL-terminated list of arguments to the process
1033 * @return pointer to process structure of the new process, NULL on error
1035 struct GNUNET_OS_Process *
1036 GNUNET_OS_start_process_va (int pipe_control,
1037 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1038 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1039 const char *filename, va_list va)
1041 struct GNUNET_OS_Process *ret;
1048 while (NULL != va_arg (ap, char *))
1051 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1054 while (NULL != (argv[argc] = va_arg (ap, char *)))
1057 ret = GNUNET_OS_start_process_vap (pipe_control,
1071 * @param pipe_control should a pipe be used to send signals to the child?
1072 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1073 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1074 * @param filename name of the binary
1075 * @param ... NULL-terminated list of arguments to the process
1077 * @return pointer to process structure of the new process, NULL on error
1080 struct GNUNET_OS_Process *
1081 GNUNET_OS_start_process (int pipe_control,
1082 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1083 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1084 const char *filename, ...)
1086 struct GNUNET_OS_Process *ret;
1089 va_start (ap, filename);
1090 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1099 * @param pipe_control should a pipe be used to send signals to the child?
1100 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1101 * must be NULL on platforms where dup is not supported
1102 * @param filename name of the binary
1103 * @param argv NULL-terminated list of arguments to the process
1104 * @return process ID of the new process, -1 on error
1106 struct GNUNET_OS_Process *
1107 GNUNET_OS_start_process_v (int pipe_control,
1108 const SOCKTYPE *lsocks,
1109 const char *filename,
1116 struct GNUNET_OS_Process *gnunet_proc = NULL;
1117 char *childpipename = NULL;
1126 if ( (GNUNET_YES == pipe_control) &&
1127 (GNUNET_OK != npipe_setup (&childpipename)) )
1134 while (-1 != (k = lsocks[i++]))
1135 GNUNET_array_append (lscp, ls, k);
1136 GNUNET_array_append (lscp, ls, -1);
1141 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1142 GNUNET_free_non_null (childpipename);
1143 GNUNET_array_grow (lscp, ls, 0);
1148 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1149 gnunet_proc->pid = ret;
1150 gnunet_proc->childpipename = childpipename;
1151 GNUNET_array_grow (lscp, ls, 0);
1154 if (NULL != childpipename)
1156 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1157 GNUNET_free (childpipename);
1161 /* read systemd documentation... */
1162 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1163 setenv ("LISTEN_PID", lpid, 1);
1166 while (-1 != lscp[i])
1169 while (-1 != lscp[j])
1175 GNUNET_assert (-1 != k);
1176 GNUNET_assert (0 == close (lscp[j]));
1184 /* Bury any existing FD, no matter what; they should all be closed
1185 * on exec anyway and the important onces have been dup'ed away */
1187 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1189 /* unset close-on-exec flag */
1190 flags = fcntl (tgt, F_GETFD);
1191 GNUNET_assert (flags >= 0);
1192 flags &= ~FD_CLOEXEC;
1194 (void) fcntl (tgt, F_SETFD, flags);
1198 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1199 setenv ("LISTEN_FDS", fds, 1);
1201 GNUNET_array_grow (lscp, ls, 0);
1202 execvp (filename, argv);
1203 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1206 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1207 char *childpipename = NULL;
1208 char **arg, **non_const_argv;
1209 unsigned int cmdlen;
1212 PROCESS_INFORMATION proc;
1214 struct GNUNET_OS_Process *gnunet_proc = NULL;
1215 char path[MAX_PATH + 1];
1216 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1217 char *env_block = NULL;
1219 DWORD pathbuf_len, alloc_len;
1224 char *non_const_filename;
1225 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1226 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1228 HANDLE lsocks_write;
1229 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1233 /* Search in prefix dir (hopefully - the directory from which
1234 * the current module was loaded), bindir and libdir, then in PATH
1236 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1237 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1238 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1240 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1243 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1246 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1249 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1250 GNUNET_free (self_prefix);
1251 GNUNET_free (bindir);
1252 GNUNET_free (libdir);
1254 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1255 if (alloc_len != pathbuf_len - 1)
1257 GNUNET_free (pathbuf);
1258 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1262 cmdlen = strlen (filename);
1263 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1264 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1266 GNUNET_asprintf (&non_const_filename, "%s", filename);
1268 /* Check that this is the full path. If it isn't, search. */
1269 if (non_const_filename[1] == ':')
1270 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1271 else if (!SearchPathA
1272 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1275 SetErrnoFromWinError (GetLastError ());
1276 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1277 non_const_filename);
1278 GNUNET_free (non_const_filename);
1279 GNUNET_free (pathbuf);
1282 GNUNET_free (pathbuf);
1283 GNUNET_free (non_const_filename);
1285 /* Count the number of arguments */
1286 arg = (char **) argv;
1293 /* Allocate a copy argv */
1294 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1296 /* Copy all argv strings */
1298 arg = (char **) argv;
1302 non_const_argv[argcount] = GNUNET_strdup (path);
1304 non_const_argv[argcount] = GNUNET_strdup (*arg);
1308 non_const_argv[argcount] = NULL;
1312 arg = non_const_argv;
1315 cmdlen = cmdlen + strlen (*arg) + 4;
1319 /* Allocate and create cmd */
1320 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1321 arg = non_const_argv;
1324 char arg_last_char = (*arg)[strlen (*arg) - 1];
1325 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1326 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1330 while (argcount > 0)
1331 GNUNET_free (non_const_argv[--argcount]);
1332 GNUNET_free (non_const_argv);
1334 memset (&start, 0, sizeof (start));
1335 start.cb = sizeof (start);
1337 if (GNUNET_YES == pipe_control)
1340 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1341 GNUNET_DISK_PERM_USER_READ |
1342 GNUNET_DISK_PERM_USER_WRITE);
1343 if (control_pipe == NULL)
1351 control_pipe = NULL;
1352 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1354 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1356 if (lsocks_pipe == NULL)
1360 GNUNET_DISK_pipe_close (lsocks_pipe);
1363 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1364 GNUNET_DISK_PIPE_END_WRITE);
1365 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1366 &lsocks_write, sizeof (HANDLE));
1367 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1368 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1369 &lsocks_read, sizeof (HANDLE));
1373 if (NULL != childpipename)
1375 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1377 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1378 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1379 GNUNET_free (childpipename);
1381 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1383 /*This will tell the child that we're going to send lsocks over the pipe*/
1384 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1385 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1387 our_env[env_off++] = NULL;
1388 env_block = CreateCustomEnvTable (our_env);
1390 GNUNET_free_non_null (our_env[--env_off]);
1391 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1392 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1394 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1395 env_block, NULL, &start, &proc))
1397 SetErrnoFromWinError (GetLastError ());
1398 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1399 if (NULL != control_pipe)
1400 GNUNET_DISK_file_close (control_pipe);
1402 GNUNET_DISK_pipe_close (lsocks_pipe);
1403 GNUNET_free (env_block);
1408 GNUNET_free (env_block);
1410 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1411 gnunet_proc->pid = proc.dwProcessId;
1412 gnunet_proc->handle = proc.hProcess;
1413 gnunet_proc->control_pipe = control_pipe;
1415 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1417 ResumeThread (proc.hThread);
1418 CloseHandle (proc.hThread);
1421 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1424 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1426 /* This is a replacement for "goto error" that doesn't use goto */
1431 uint64_t size, count, i;
1433 /* Tell the number of sockets */
1434 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1436 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1437 if (wrote != sizeof (count))
1439 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1442 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1444 WSAPROTOCOL_INFOA pi;
1445 /* Get a socket duplication info */
1446 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1449 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1452 /* Synchronous I/O is not nice, but we can't schedule this:
1453 * lsocks will be closed/freed by the caller soon, and until
1454 * the child creates a duplicate, closing a socket here will
1455 * close it for good.
1457 /* Send the size of the structure
1458 * (the child might be built with different headers...)
1461 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1462 if (wrote != sizeof (size))
1464 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1467 /* Finally! Send the data */
1468 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1469 if (wrote != sizeof (pi))
1471 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1475 /* This will block us until the child makes a final read or closes
1476 * the pipe (hence no 'wrote' check), since we have to wait for it
1477 * to duplicate the last socket, before we return and start closing
1480 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1485 GNUNET_DISK_file_sync (lsocks_write_fd);
1486 GNUNET_DISK_pipe_close (lsocks_pipe);
1490 /* If we can't pass on the socket(s), the child will block forever,
1491 * better put it out of its misery.
1493 TerminateProcess (gnunet_proc->handle, 0);
1494 CloseHandle (gnunet_proc->handle);
1495 if (NULL != gnunet_proc->control_pipe)
1496 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1497 GNUNET_free (gnunet_proc);
1506 * Retrieve the status of a process
1507 * @param proc process ID
1508 * @param type status type
1509 * @param code return code/signal number
1510 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1513 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1514 enum GNUNET_OS_ProcessStatusType *type,
1515 unsigned long *code)
1521 GNUNET_assert (0 != proc);
1522 ret = waitpid (proc->pid, &status, WNOHANG);
1525 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1526 return GNUNET_SYSERR;
1530 *type = GNUNET_OS_PROCESS_RUNNING;
1534 if (proc->pid != ret)
1536 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1537 return GNUNET_SYSERR;
1539 if (WIFEXITED (status))
1541 *type = GNUNET_OS_PROCESS_EXITED;
1542 *code = WEXITSTATUS (status);
1544 else if (WIFSIGNALED (status))
1546 *type = GNUNET_OS_PROCESS_SIGNALED;
1547 *code = WTERMSIG (status);
1549 else if (WIFSTOPPED (status))
1551 *type = GNUNET_OS_PROCESS_SIGNALED;
1552 *code = WSTOPSIG (status);
1555 else if (WIFCONTINUED (status))
1557 *type = GNUNET_OS_PROCESS_RUNNING;
1563 *type = GNUNET_OS_PROCESS_UNKNOWN;
1568 DWORD c, error_code, ret;
1572 if (h == NULL || ret == 0)
1574 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1576 return GNUNET_SYSERR;
1579 h = GetCurrentProcess ();
1582 ret = GetExitCodeProcess (h, &c);
1583 error_code = GetLastError ();
1584 if (ret == 0 || error_code != NO_ERROR)
1586 SetErrnoFromWinError (error_code);
1587 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1588 return GNUNET_SYSERR;
1590 if (STILL_ACTIVE == c)
1592 *type = GNUNET_OS_PROCESS_RUNNING;
1596 *type = GNUNET_OS_PROCESS_EXITED;
1605 * Wait for a process
1606 * @param proc pointer to process structure
1607 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1610 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1614 pid_t pid = proc->pid;
1617 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1618 (EINTR == errno) ) ;
1621 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1622 return GNUNET_SYSERR;
1632 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1634 return GNUNET_SYSERR;
1637 h = GetCurrentProcess ();
1639 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1641 SetErrnoFromWinError (GetLastError ());
1642 ret = GNUNET_SYSERR;
1653 * Handle to a command.
1655 struct GNUNET_OS_CommandHandle
1661 struct GNUNET_OS_Process *eip;
1664 * Handle to the output pipe.
1666 struct GNUNET_DISK_PipeHandle *opipe;
1669 * Read-end of output pipe.
1671 const struct GNUNET_DISK_FileHandle *r;
1674 * Function to call on each line of output.
1676 GNUNET_OS_LineProcessor proc;
1679 * Closure for 'proc'.
1684 * Buffer for the output.
1689 * Task reading from pipe.
1691 GNUNET_SCHEDULER_TaskIdentifier rtask;
1696 struct GNUNET_TIME_Absolute timeout;
1699 * Current read offset in buf.
1706 * Stop/kill a command. Must ONLY be called either from
1707 * the callback after 'NULL' was passed for 'line' *OR*
1708 * from an independent task (not within the line processor).
1710 * @param cmd handle to the process
1713 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1716 if (cmd->proc != NULL)
1718 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1719 GNUNET_SCHEDULER_cancel (cmd->rtask);
1721 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1722 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1723 GNUNET_OS_process_close (cmd->eip);
1724 GNUNET_DISK_pipe_close (cmd->opipe);
1730 * Read from the process and call the line processor.
1732 * @param cls the 'struct GNUNET_OS_CommandHandle'
1733 * @param tc scheduler context
1736 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1738 struct GNUNET_OS_CommandHandle *cmd = cls;
1739 GNUNET_OS_LineProcessor proc;
1743 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1744 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1746 /* timeout, shutdown, etc. */
1749 proc (cmd->proc_cls, NULL);
1753 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1754 sizeof (cmd->buf) - cmd->off);
1757 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1759 cmd->buf[cmd->off] = '\0';
1760 cmd->proc (cmd->proc_cls, cmd->buf);
1764 proc (cmd->proc_cls, NULL);
1767 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1772 cmd->proc (cmd->proc_cls, cmd->buf);
1773 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1774 cmd->off -= (end + 1 - cmd->buf);
1775 end = memchr (cmd->buf, '\n', cmd->off);
1778 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1779 (cmd->timeout), cmd->r, &cmd_read, cmd);
1784 * Run the given command line and call the given function
1785 * for each line of the output.
1787 * @param proc function to call for each line of the output
1788 * @param proc_cls closure for proc
1789 * @param timeout when to time out
1790 * @param binary command to run
1791 * @param ... arguments to command
1792 * @return NULL on error
1794 struct GNUNET_OS_CommandHandle *
1795 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1796 struct GNUNET_TIME_Relative timeout, const char *binary,
1799 struct GNUNET_OS_CommandHandle *cmd;
1800 struct GNUNET_OS_Process *eip;
1801 struct GNUNET_DISK_PipeHandle *opipe;
1804 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1807 va_start (ap, binary);
1808 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1812 GNUNET_DISK_pipe_close (opipe);
1815 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1816 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1817 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1821 cmd->proc_cls = proc_cls;
1822 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1823 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1830 /* end of os_priority.c */