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"
34 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
36 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
38 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
40 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
42 struct GNUNET_OS_Process
57 * Pipe we use to signal the process (if used).
59 struct GNUNET_DISK_FileHandle *control_pipe;
62 * Name of the pipe, NULL for none.
69 * Handle for 'this' process.
71 static struct GNUNET_OS_Process current_process;
74 /* MinGW version of named pipe API */
77 * Creates a named pipe/FIFO and opens it
79 * @param fn pointer to the name of the named pipe or to NULL
80 * @param flags open flags
81 * @param perm access permissions
82 * @return pipe handle on success, NULL on error
84 static struct GNUNET_DISK_FileHandle *
85 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
86 enum GNUNET_DISK_AccessPermissions perm)
88 struct GNUNET_DISK_FileHandle *ret;
94 if (flags & GNUNET_DISK_OPEN_READWRITE)
95 openMode = PIPE_ACCESS_DUPLEX;
96 else if (flags & GNUNET_DISK_OPEN_READ)
97 openMode = PIPE_ACCESS_INBOUND;
98 else if (flags & GNUNET_DISK_OPEN_WRITE)
99 openMode = PIPE_ACCESS_OUTBOUND;
100 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
101 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
110 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
111 LOG (GNUNET_ERROR_TYPE_DEBUG,
112 "Trying to create an instance of named pipe `%s'\n", name);
113 /* 1) This might work just fine with UTF-8 strings as it is.
114 * 2) This is only used by GNUnet itself, and only with latin names.
116 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
117 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
122 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
123 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
125 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
127 h = CreateNamedPipe (*fn,
128 openMode | FILE_FLAG_OVERLAPPED |
129 FILE_FLAG_FIRST_PIPE_INSTANCE,
130 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
133 error_code = GetLastError ();
136 /* don't re-set name to NULL yet */
137 if (h == INVALID_HANDLE_VALUE)
139 SetErrnoFromWinError (error_code);
140 LOG (GNUNET_ERROR_TYPE_DEBUG,
141 "Pipe creation have failed because of %d, errno is %d\n", error_code,
145 LOG (GNUNET_ERROR_TYPE_DEBUG,
146 "Pipe was to be unique, considering re-creation\n");
149 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
153 LOG (GNUNET_ERROR_TYPE_DEBUG,
154 "Pipe name was not unique, trying again\n");
163 ret = GNUNET_malloc (sizeof (*ret));
165 ret->type = GNUNET_PIPE;
166 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
167 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
168 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
169 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
175 * Opens already existing named pipe/FIFO
177 * @param fn name of an existing named pipe
178 * @param flags open flags
179 * @return pipe handle on success, NULL on error
181 static struct GNUNET_DISK_FileHandle *
182 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
184 struct GNUNET_DISK_FileHandle *ret;
189 if (flags & GNUNET_DISK_OPEN_READWRITE)
190 openMode = GENERIC_WRITE | GENERIC_READ;
191 else if (flags & GNUNET_DISK_OPEN_READ)
192 openMode = GENERIC_READ;
193 else if (flags & GNUNET_DISK_OPEN_WRITE)
194 openMode = GENERIC_WRITE;
196 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
197 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
198 if (h == INVALID_HANDLE_VALUE)
200 SetErrnoFromWinError (GetLastError ());
204 ret = GNUNET_malloc (sizeof (*ret));
206 ret->type = GNUNET_PIPE;
207 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
208 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
209 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
210 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
216 /* UNIX version of named-pipe API */
219 * Clean up a named pipe and the directory it was placed in.
221 * @param fn name of the pipe
224 cleanup_npipe (const char *fn)
229 if (0 != unlink (fn))
230 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
231 dn = GNUNET_strdup (fn);
234 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
240 * Setup a named pipe.
242 * @param fn where to store the name of the new pipe,
243 * if *fn is non-null, the name of the pipe to setup
244 * @return GNUNET_OK on success
247 npipe_setup (char **fn)
251 /* FIXME: hardwired '/tmp' path... is bad */
252 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
254 if (NULL == mkdtemp (dir))
256 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
257 return GNUNET_SYSERR;
259 GNUNET_asprintf (fn, "%s/child-control", dir);
261 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
262 return GNUNET_SYSERR;
268 * Open an existing named pipe.
270 * @param fn name of the file
271 * @param flags flags to use
272 * @return NULL on error
274 static struct GNUNET_DISK_FileHandle *
275 npipe_open (const char *fn,
276 enum GNUNET_DISK_OpenFlags flags)
278 struct GNUNET_DISK_FileHandle *ret;
283 /* 200 * 5ms = 1s at most */
286 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
287 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
289 /* as this is for killing a child process via pipe and it is conceivable that
290 the child process simply didn't finish starting yet, we do some sleeping
291 (which is obviously usually not allowed). We can't select on the FD as
292 'open' fails, and we probably shouldn't just "ignore" the error, so wait
293 and retry a few times is likely the best method; our process API doesn't
294 support continuations, so we need to sleep directly... */
296 req.tv_nsec = 5000000; /* 5ms */
297 (void) nanosleep (&req, NULL);
301 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
302 (flags == GNUNET_DISK_OPEN_READ)
303 ? _("Failed to open named pipe `%s' for reading: %s\n")
304 : _("Failed to open named pipe `%s' for writing: %s\n"),
309 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
317 * This handler is called when there are control data to be read on the pipe
319 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
320 * @param tc scheduler context
323 parent_control_handler (void *cls,
324 const struct GNUNET_SCHEDULER_TaskContext *tc)
326 struct GNUNET_DISK_FileHandle *control_pipe = cls;
329 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
332 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT |
333 GNUNET_SCHEDULER_REASON_PREREQ_DONE))
335 GNUNET_DISK_file_close (control_pipe);
338 if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) !=
341 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
342 GNUNET_DISK_file_close (control_pipe);
345 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
346 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
347 control_pipe, &parent_control_handler,
354 * Task that connects this process to its parent via pipe;
355 * essentially, the parent control handler will read signal numbers
356 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
357 * variable) and raise those signals.
359 * @param cls closure (unused)
360 * @param tc scheduler context (unused)
363 GNUNET_OS_install_parent_control_handler (void *cls,
365 GNUNET_SCHEDULER_TaskContext *tc)
368 struct GNUNET_DISK_FileHandle *control_pipe;
370 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
371 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
373 LOG (GNUNET_ERROR_TYPE_DEBUG,
374 "Not installing a handler because $%s is empty\n",
375 GNUNET_OS_CONTROL_PIPE);
376 putenv ("GNUNET_OS_CONTROL_PIPE=");
380 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
381 if (NULL == control_pipe)
383 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
384 putenv ("GNUNET_OS_CONTROL_PIPE=");
387 LOG (GNUNET_ERROR_TYPE_DEBUG,
388 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
389 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
390 &parent_control_handler, control_pipe);
391 putenv ("GNUNET_OS_CONTROL_PIPE=");
396 * Get process structure for current process
398 * The pointer it returns points to static memory location and must not be
401 * @return pointer to the process sturcutre for this process
403 struct GNUNET_OS_Process *
404 GNUNET_OS_process_current ()
407 current_process.pid = GetCurrentProcessId ();
408 current_process.handle = GetCurrentProcess ();
410 current_process.pid = 0;
412 return ¤t_process;
417 * Sends a signal to the process
419 * @param proc pointer to process structure
421 * @return 0 on success, -1 on error
424 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
429 if ( (NULL == proc->control_pipe) &&
430 (NULL != proc->childpipename) )
431 proc->control_pipe = npipe_open (proc->childpipename,
432 GNUNET_DISK_OPEN_WRITE);
434 if (NULL == proc->control_pipe)
437 /* no pipe and windows? can't do this */
441 return kill (proc->pid, sig);
444 ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof (sig));
445 if (ret == sizeof (sig))
447 /* pipe failed, try other methods */
454 #if WINDOWS && !defined(__CYGWIN__)
455 if (0 == TerminateProcess (proc->handle, 0))
457 /* FIXME: set 'errno' */
462 return PLIBC_KILL (proc->pid, sig);
469 return kill (proc->pid, sig);
475 * Get the pid of the process in question
477 * @param proc the process to get the pid of
479 * @return the current process id
482 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
489 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
491 #if ENABLE_WINDOWS_WORKAROUNDS
492 if (proc->control_pipe)
493 GNUNET_DISK_file_close (proc->control_pipe);
497 if (proc->handle != NULL)
498 CloseHandle (proc->handle);
500 if (NULL != proc->childpipename)
502 cleanup_npipe (proc->childpipename);
503 GNUNET_free (proc->childpipename);
510 #include "gnunet_signal_lib.h"
512 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
515 * Make seaspider happy.
517 #define DWORD_WINAPI DWORD WINAPI
520 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
521 * @param proc pointer to process structure
524 child_wait_thread (void *arg)
526 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
528 WaitForSingleObject (proc->handle, INFINITE);
530 if (w32_sigchld_handler)
531 w32_sigchld_handler ();
538 * Set process priority
540 * @param proc pointer to process structure
541 * @param prio priority value
542 * @return GNUNET_OK on success, GNUNET_SYSERR on error
545 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
546 enum GNUNET_SCHEDULER_Priority prio)
550 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
551 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
554 /* convert to MINGW/Unix values */
557 case GNUNET_SCHEDULER_PRIORITY_UI:
558 case GNUNET_SCHEDULER_PRIORITY_URGENT:
560 rprio = HIGH_PRIORITY_CLASS;
566 case GNUNET_SCHEDULER_PRIORITY_HIGH:
568 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
574 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
576 rprio = NORMAL_PRIORITY_CLASS;
582 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
584 rprio = BELOW_NORMAL_PRIORITY_CLASS;
590 case GNUNET_SCHEDULER_PRIORITY_IDLE:
592 rprio = IDLE_PRIORITY_CLASS;
599 return GNUNET_SYSERR;
602 /* Set process priority */
605 HANDLE h = proc->handle;
607 GNUNET_assert (h != NULL);
608 SetPriorityClass (h, rprio);
614 if ((0 == pid) || (pid == getpid ()))
617 int delta = rprio - have;
620 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
622 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
623 return GNUNET_SYSERR;
628 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
630 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
632 return GNUNET_SYSERR;
636 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
637 "Priority management not availabe for this platform\n");
644 CreateCustomEnvTable (char **vars)
646 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
647 size_t tablesize = 0;
648 size_t items_count = 0;
649 size_t n_found = 0, n_var;
656 win32_env_table = GetEnvironmentStringsA ();
657 if (win32_env_table == NULL)
659 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
661 index = GNUNET_malloc (sizeof (char *) * n_var);
662 for (c = 0; c < n_var; c++)
664 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
666 size_t len = strlen (ptr);
669 for (var_ptr = vars; *var_ptr; var_ptr++)
673 var_len = strlen (var);
674 if (strncmp (var, ptr, var_len) == 0)
678 tablesize += var_len + strlen (val) + 1;
683 tablesize += len + 1;
686 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
691 n_found += strlen (var) + strlen (val) + 1;
693 result = GNUNET_malloc (tablesize + n_found + 1);
694 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
696 size_t len = strlen (ptr);
699 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
703 var_len = strlen (var);
704 if (strncmp (var, ptr, var_len) == 0)
712 strcpy (result_ptr, ptr);
713 result_ptr += len + 1;
717 strcpy (result_ptr, var);
718 result_ptr += var_len;
719 strcpy (result_ptr, val);
720 result_ptr += strlen (val) + 1;
724 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
728 var_len = strlen (var);
731 strcpy (result_ptr, var);
732 result_ptr += var_len;
733 strcpy (result_ptr, val);
734 result_ptr += strlen (val) + 1;
737 FreeEnvironmentStrings (win32_env_table);
748 * @param pipe_control should a pipe be used to send signals to the child?
749 * @param pipe_stdin pipe to use to send input to child process (or NULL)
750 * @param pipe_stdout pipe to use to get output from child process (or NULL)
751 * @param filename name of the binary
752 * @param argv NULL-terminated array of arguments to the process
753 * @return pointer to process structure of the new process, NULL on error
755 struct GNUNET_OS_Process *
756 GNUNET_OS_start_process_vap (int pipe_control,
757 struct GNUNET_DISK_PipeHandle *pipe_stdin,
758 struct GNUNET_DISK_PipeHandle *pipe_stdout,
759 const char *filename,
763 char *childpipename = NULL;
764 struct GNUNET_OS_Process *gnunet_proc = NULL;
771 if ( (GNUNET_YES == pipe_control) &&
773 npipe_setup (&childpipename)) )
775 if (pipe_stdout != NULL)
777 GNUNET_assert (GNUNET_OK ==
778 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
780 GNUNET_DISK_PIPE_END_WRITE),
781 &fd_stdout_write, sizeof (int)));
782 GNUNET_assert (GNUNET_OK ==
783 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
784 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
785 &fd_stdout_read, sizeof (int)));
787 if (pipe_stdin != NULL)
789 GNUNET_assert (GNUNET_OK ==
790 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
791 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
792 &fd_stdin_read, sizeof (int)));
793 GNUNET_assert (GNUNET_OK ==
794 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
795 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
796 &fd_stdin_write, sizeof (int)));
802 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
803 GNUNET_free_non_null (childpipename);
808 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
809 gnunet_proc->pid = ret;
810 gnunet_proc->childpipename = childpipename;
813 if (NULL != childpipename)
815 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
816 GNUNET_free (childpipename);
818 if (pipe_stdout != NULL)
820 GNUNET_break (0 == close (fd_stdout_read));
821 if (-1 == dup2 (fd_stdout_write, 1))
822 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
823 GNUNET_break (0 == close (fd_stdout_write));
826 if (pipe_stdin != NULL)
829 GNUNET_break (0 == close (fd_stdin_write));
830 if (-1 == dup2 (fd_stdin_read, 0))
831 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
832 GNUNET_break (0 == close (fd_stdin_read));
834 execvp (filename, argv);
835 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
838 char *childpipename = NULL;
839 struct GNUNET_OS_Process *gnunet_proc = NULL;
844 PROCESS_INFORMATION proc;
847 HANDLE stdout_handle;
849 char path[MAX_PATH + 1];
851 char *our_env[3] = { NULL, NULL, NULL };
852 char *env_block = NULL;
854 DWORD pathbuf_len, alloc_len;
859 char *non_const_filename;
860 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
862 /* Search in prefix dir (hopefully - the directory from which
863 * the current module was loaded), bindir and libdir, then in PATH
865 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
866 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
867 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
869 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
872 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
875 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
878 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
879 GNUNET_free (self_prefix);
880 GNUNET_free (bindir);
881 GNUNET_free (libdir);
883 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
884 GNUNET_assert (alloc_len == (pathbuf_len - 1));
886 cmdlen = strlen (filename);
887 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
888 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
890 GNUNET_asprintf (&non_const_filename, "%s", filename);
892 /* Check that this is the full path. If it isn't, search. */
893 if (non_const_filename[1] == ':')
894 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
895 else if (!SearchPathA
896 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
899 SetErrnoFromWinError (GetLastError ());
900 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
902 GNUNET_free (non_const_filename);
903 GNUNET_free (pathbuf);
906 GNUNET_free (pathbuf);
907 GNUNET_free (non_const_filename);
911 while (NULL != (arg = argv[argc++]))
914 cmdlen = cmdlen + strlen (path) + 4;
916 cmdlen = cmdlen + strlen (arg) + 4;
920 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
922 while (NULL != (arg = argv[argc++]))
924 /* This is to escape trailing slash */
925 char arg_lastchar = arg[strlen (arg) - 1];
927 idx += sprintf (idx, "\"%s%s\"%s", path,
928 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
930 idx += sprintf (idx, "\"%s%s\"%s", arg,
931 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
934 memset (&start, 0, sizeof (start));
935 start.cb = sizeof (start);
937 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
938 start.dwFlags |= STARTF_USESTDHANDLES;
940 if (pipe_stdin != NULL)
942 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
943 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
944 &stdin_handle, sizeof (HANDLE));
945 start.hStdInput = stdin_handle;
948 if (pipe_stdout != NULL)
950 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
952 GNUNET_DISK_PIPE_END_WRITE),
953 &stdout_handle, sizeof (HANDLE));
954 start.hStdOutput = stdout_handle;
956 if (GNUNET_YES == pipe_control)
959 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
960 GNUNET_DISK_PERM_USER_READ |
961 GNUNET_DISK_PERM_USER_WRITE);
962 if (control_pipe == NULL)
969 if (NULL != childpipename)
971 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
973 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
974 GNUNET_asprintf (&our_env[1], "%s", childpipename);
981 env_block = CreateCustomEnvTable (our_env);
982 GNUNET_free (our_env[0]);
983 GNUNET_free (our_env[1]);
985 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
986 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
988 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
989 env_block, NULL, &start, &proc))
991 SetErrnoFromWinError (GetLastError ());
992 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
993 GNUNET_free (env_block);
998 GNUNET_free (env_block);
1000 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1001 gnunet_proc->pid = proc.dwProcessId;
1002 gnunet_proc->handle = proc.hProcess;
1003 gnunet_proc->control_pipe = control_pipe;
1005 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1007 ResumeThread (proc.hThread);
1008 CloseHandle (proc.hThread);
1020 * @param pipe_control should a pipe be used to send signals to the child?
1021 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1022 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1023 * @param filename name of the binary
1024 * @param va NULL-terminated list of arguments to the process
1025 * @return pointer to process structure of the new process, NULL on error
1027 struct GNUNET_OS_Process *
1028 GNUNET_OS_start_process_va (int pipe_control,
1029 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1030 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1031 const char *filename, va_list va)
1033 struct GNUNET_OS_Process *ret;
1040 while (NULL != va_arg (ap, char *))
1043 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1046 while (NULL != (argv[argc] = va_arg (ap, char *)))
1049 ret = GNUNET_OS_start_process_vap (pipe_control,
1063 * @param pipe_control should a pipe be used to send signals to the child?
1064 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1065 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1066 * @param filename name of the binary
1067 * @param ... NULL-terminated list of arguments to the process
1069 * @return pointer to process structure of the new process, NULL on error
1072 struct GNUNET_OS_Process *
1073 GNUNET_OS_start_process (int pipe_control,
1074 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1075 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1076 const char *filename, ...)
1078 struct GNUNET_OS_Process *ret;
1081 va_start (ap, filename);
1082 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1091 * @param pipe_control should a pipe be used to send signals to the child?
1092 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1093 * must be NULL on platforms where dup is not supported
1094 * @param filename name of the binary
1095 * @param argv NULL-terminated list of arguments to the process
1096 * @return process ID of the new process, -1 on error
1098 struct GNUNET_OS_Process *
1099 GNUNET_OS_start_process_v (int pipe_control,
1100 const SOCKTYPE *lsocks,
1101 const char *filename,
1108 struct GNUNET_OS_Process *gnunet_proc = NULL;
1109 char *childpipename = NULL;
1118 if ( (GNUNET_YES == pipe_control) &&
1119 (GNUNET_OK != npipe_setup (&childpipename)) )
1126 while (-1 != (k = lsocks[i++]))
1127 GNUNET_array_append (lscp, ls, k);
1128 GNUNET_array_append (lscp, ls, -1);
1133 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1134 GNUNET_free_non_null (childpipename);
1135 GNUNET_array_grow (lscp, ls, 0);
1140 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1141 gnunet_proc->pid = ret;
1142 gnunet_proc->childpipename = childpipename;
1143 GNUNET_array_grow (lscp, ls, 0);
1146 if (NULL != childpipename)
1148 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1149 GNUNET_free (childpipename);
1153 /* read systemd documentation... */
1154 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1155 setenv ("LISTEN_PID", lpid, 1);
1158 while (-1 != lscp[i])
1161 while (-1 != lscp[j])
1167 GNUNET_assert (-1 != k);
1168 GNUNET_assert (0 == close (lscp[j]));
1176 /* Bury any existing FD, no matter what; they should all be closed
1177 * on exec anyway and the important onces have been dup'ed away */
1179 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1181 /* unset close-on-exec flag */
1182 flags = fcntl (tgt, F_GETFD);
1183 GNUNET_assert (flags >= 0);
1184 flags &= ~FD_CLOEXEC;
1186 (void) fcntl (tgt, F_SETFD, flags);
1190 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1191 setenv ("LISTEN_FDS", fds, 1);
1193 GNUNET_array_grow (lscp, ls, 0);
1194 execvp (filename, argv);
1195 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1198 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1199 char *childpipename = NULL;
1200 char **arg, **non_const_argv;
1201 unsigned int cmdlen;
1204 PROCESS_INFORMATION proc;
1206 struct GNUNET_OS_Process *gnunet_proc = NULL;
1207 char path[MAX_PATH + 1];
1208 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1209 char *env_block = NULL;
1211 DWORD pathbuf_len, alloc_len;
1216 char *non_const_filename;
1217 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1218 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1220 HANDLE lsocks_write;
1221 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1225 /* Search in prefix dir (hopefully - the directory from which
1226 * the current module was loaded), bindir and libdir, then in PATH
1228 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1229 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1230 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1232 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1235 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1238 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1241 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1242 GNUNET_free (self_prefix);
1243 GNUNET_free (bindir);
1244 GNUNET_free (libdir);
1246 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1247 if (alloc_len != pathbuf_len - 1)
1249 GNUNET_free (pathbuf);
1250 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1254 cmdlen = strlen (filename);
1255 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1256 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1258 GNUNET_asprintf (&non_const_filename, "%s", filename);
1260 /* Check that this is the full path. If it isn't, search. */
1261 if (non_const_filename[1] == ':')
1262 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1263 else if (!SearchPathA
1264 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1267 SetErrnoFromWinError (GetLastError ());
1268 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1269 non_const_filename);
1270 GNUNET_free (non_const_filename);
1271 GNUNET_free (pathbuf);
1274 GNUNET_free (pathbuf);
1275 GNUNET_free (non_const_filename);
1277 /* Count the number of arguments */
1278 arg = (char **) argv;
1285 /* Allocate a copy argv */
1286 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1288 /* Copy all argv strings */
1290 arg = (char **) argv;
1294 non_const_argv[argcount] = GNUNET_strdup (path);
1296 non_const_argv[argcount] = GNUNET_strdup (*arg);
1300 non_const_argv[argcount] = NULL;
1304 arg = non_const_argv;
1307 cmdlen = cmdlen + strlen (*arg) + 4;
1311 /* Allocate and create cmd */
1312 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1313 arg = non_const_argv;
1316 char arg_last_char = (*arg)[strlen (*arg) - 1];
1317 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1318 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1322 while (argcount > 0)
1323 GNUNET_free (non_const_argv[--argcount]);
1324 GNUNET_free (non_const_argv);
1326 memset (&start, 0, sizeof (start));
1327 start.cb = sizeof (start);
1329 if (GNUNET_YES == pipe_control)
1332 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1333 GNUNET_DISK_PERM_USER_READ |
1334 GNUNET_DISK_PERM_USER_WRITE);
1335 if (control_pipe == NULL)
1342 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1344 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1346 if (lsocks_pipe == NULL)
1350 GNUNET_DISK_pipe_close (lsocks_pipe);
1353 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1354 GNUNET_DISK_PIPE_END_WRITE);
1355 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1356 &lsocks_write, sizeof (HANDLE));
1357 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1358 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1359 &lsocks_read, sizeof (HANDLE));
1363 if (NULL != childpipename)
1365 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1367 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1368 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1369 GNUNET_free (childpipename);
1371 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1373 /*This will tell the child that we're going to send lsocks over the pipe*/
1374 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1375 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1377 our_env[env_off++] = NULL;
1378 env_block = CreateCustomEnvTable (our_env);
1380 GNUNET_free_non_null (our_env[--env_off]);
1381 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1382 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1384 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1385 env_block, NULL, &start, &proc))
1387 SetErrnoFromWinError (GetLastError ());
1388 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1389 if (NULL != control_pipe)
1390 GNUNET_DISK_file_close (control_pipe);
1392 GNUNET_DISK_pipe_close (lsocks_pipe);
1393 GNUNET_free (env_block);
1398 GNUNET_free (env_block);
1400 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1401 gnunet_proc->pid = proc.dwProcessId;
1402 gnunet_proc->handle = proc.hProcess;
1403 gnunet_proc->control_pipe = control_pipe;
1405 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1407 ResumeThread (proc.hThread);
1408 CloseHandle (proc.hThread);
1411 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1414 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1416 /* This is a replacement for "goto error" that doesn't use goto */
1421 uint64_t size, count, i;
1423 /* Tell the number of sockets */
1424 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1426 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1427 if (wrote != sizeof (count))
1429 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1432 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1434 WSAPROTOCOL_INFOA pi;
1435 /* Get a socket duplication info */
1436 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1438 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1439 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1442 /* Synchronous I/O is not nice, but we can't schedule this:
1443 * lsocks will be closed/freed by the caller soon, and until
1444 * the child creates a duplicate, closing a socket here will
1445 * close it for good.
1447 /* Send the size of the structure
1448 * (the child might be built with different headers...)
1451 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1452 if (wrote != sizeof (size))
1454 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1457 /* Finally! Send the data */
1458 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1459 if (wrote != sizeof (pi))
1461 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1465 /* This will block us until the child makes a final read or closes
1466 * the pipe (hence no 'wrote' check), since we have to wait for it
1467 * to duplicate the last socket, before we return and start closing
1470 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1475 GNUNET_DISK_file_sync (lsocks_write_fd);
1476 GNUNET_DISK_pipe_close (lsocks_pipe);
1480 /* If we can't pass on the socket(s), the child will block forever,
1481 * better put it out of its misery.
1483 TerminateProcess (gnunet_proc->handle, 0);
1484 CloseHandle (gnunet_proc->handle);
1485 if (NULL != gnunet_proc->control_pipe)
1486 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1487 GNUNET_free (gnunet_proc);
1496 * Retrieve the status of a process
1497 * @param proc process ID
1498 * @param type status type
1499 * @param code return code/signal number
1500 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1503 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1504 enum GNUNET_OS_ProcessStatusType *type,
1505 unsigned long *code)
1511 GNUNET_assert (0 != proc);
1512 ret = waitpid (proc->pid, &status, WNOHANG);
1515 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1516 return GNUNET_SYSERR;
1520 *type = GNUNET_OS_PROCESS_RUNNING;
1524 if (proc->pid != ret)
1526 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1527 return GNUNET_SYSERR;
1529 if (WIFEXITED (status))
1531 *type = GNUNET_OS_PROCESS_EXITED;
1532 *code = WEXITSTATUS (status);
1534 else if (WIFSIGNALED (status))
1536 *type = GNUNET_OS_PROCESS_SIGNALED;
1537 *code = WTERMSIG (status);
1539 else if (WIFSTOPPED (status))
1541 *type = GNUNET_OS_PROCESS_SIGNALED;
1542 *code = WSTOPSIG (status);
1545 else if (WIFCONTINUED (status))
1547 *type = GNUNET_OS_PROCESS_RUNNING;
1553 *type = GNUNET_OS_PROCESS_UNKNOWN;
1558 DWORD c, error_code, ret;
1562 if (h == NULL || ret == 0)
1564 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1566 return GNUNET_SYSERR;
1569 h = GetCurrentProcess ();
1572 ret = GetExitCodeProcess (h, &c);
1573 error_code = GetLastError ();
1574 if (ret == 0 || error_code != NO_ERROR)
1576 SetErrnoFromWinError (error_code);
1577 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1578 return GNUNET_SYSERR;
1580 if (STILL_ACTIVE == c)
1582 *type = GNUNET_OS_PROCESS_RUNNING;
1586 *type = GNUNET_OS_PROCESS_EXITED;
1595 * Wait for a process
1596 * @param proc pointer to process structure
1597 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1600 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1604 pid_t pid = proc->pid;
1607 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1608 (EINTR == errno) ) ;
1611 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1612 return GNUNET_SYSERR;
1622 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1624 return GNUNET_SYSERR;
1627 h = GetCurrentProcess ();
1629 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1631 SetErrnoFromWinError (GetLastError ());
1632 ret = GNUNET_SYSERR;
1643 * Handle to a command.
1645 struct GNUNET_OS_CommandHandle
1651 struct GNUNET_OS_Process *eip;
1654 * Handle to the output pipe.
1656 struct GNUNET_DISK_PipeHandle *opipe;
1659 * Read-end of output pipe.
1661 const struct GNUNET_DISK_FileHandle *r;
1664 * Function to call on each line of output.
1666 GNUNET_OS_LineProcessor proc;
1669 * Closure for 'proc'.
1674 * Buffer for the output.
1679 * Task reading from pipe.
1681 GNUNET_SCHEDULER_TaskIdentifier rtask;
1686 struct GNUNET_TIME_Absolute timeout;
1689 * Current read offset in buf.
1696 * Stop/kill a command. Must ONLY be called either from
1697 * the callback after 'NULL' was passed for 'line' *OR*
1698 * from an independent task (not within the line processor).
1700 * @param cmd handle to the process
1703 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1706 if (cmd->proc != NULL)
1708 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1709 GNUNET_SCHEDULER_cancel (cmd->rtask);
1711 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1712 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1713 GNUNET_OS_process_close (cmd->eip);
1714 GNUNET_DISK_pipe_close (cmd->opipe);
1720 * Read from the process and call the line processor.
1722 * @param cls the 'struct GNUNET_OS_CommandHandle'
1723 * @param tc scheduler context
1726 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1728 struct GNUNET_OS_CommandHandle *cmd = cls;
1729 GNUNET_OS_LineProcessor proc;
1733 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1734 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1736 /* timeout, shutdown, etc. */
1739 proc (cmd->proc_cls, NULL);
1743 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1744 sizeof (cmd->buf) - cmd->off);
1747 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1749 cmd->buf[cmd->off] = '\0';
1750 cmd->proc (cmd->proc_cls, cmd->buf);
1754 proc (cmd->proc_cls, NULL);
1757 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1762 cmd->proc (cmd->proc_cls, cmd->buf);
1763 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1764 cmd->off -= (end + 1 - cmd->buf);
1765 end = memchr (cmd->buf, '\n', cmd->off);
1768 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1769 (cmd->timeout), cmd->r, &cmd_read, cmd);
1774 * Run the given command line and call the given function
1775 * for each line of the output.
1777 * @param proc function to call for each line of the output
1778 * @param proc_cls closure for proc
1779 * @param timeout when to time out
1780 * @param binary command to run
1781 * @param ... arguments to command
1782 * @return NULL on error
1784 struct GNUNET_OS_CommandHandle *
1785 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1786 struct GNUNET_TIME_Relative timeout, const char *binary,
1789 struct GNUNET_OS_CommandHandle *cmd;
1790 struct GNUNET_OS_Process *eip;
1791 struct GNUNET_DISK_PipeHandle *opipe;
1794 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1797 va_start (ap, binary);
1798 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1802 GNUNET_DISK_pipe_close (opipe);
1805 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1806 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1807 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1811 cmd->proc_cls = proc_cls;
1812 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1813 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1820 /* end of os_priority.c */