2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2011 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file util/os_priority.c
23 * @brief Methods to set process priority
28 #include "gnunet_common.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_scheduler_lib.h"
31 #include "gnunet_strings_lib.h"
32 #include "gnunet_crypto_lib.h"
35 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
37 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
39 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
41 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
43 struct GNUNET_OS_Process
58 * Pipe we use to signal the process (if used).
60 struct GNUNET_DISK_FileHandle *control_pipe;
63 * Name of the pipe, NULL for none.
70 * Handle for 'this' process.
72 static struct GNUNET_OS_Process current_process;
75 /* MinGW version of named pipe API */
78 * Creates a named pipe/FIFO and opens it
80 * @param fn pointer to the name of the named pipe or to NULL
81 * @param flags open flags
82 * @param perm access permissions
83 * @return pipe handle on success, NULL on error
85 static struct GNUNET_DISK_FileHandle *
86 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
87 enum GNUNET_DISK_AccessPermissions perm)
89 struct GNUNET_DISK_FileHandle *ret;
95 if (flags & GNUNET_DISK_OPEN_READWRITE)
96 openMode = PIPE_ACCESS_DUPLEX;
97 else if (flags & GNUNET_DISK_OPEN_READ)
98 openMode = PIPE_ACCESS_INBOUND;
99 else if (flags & GNUNET_DISK_OPEN_WRITE)
100 openMode = PIPE_ACCESS_OUTBOUND;
101 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
102 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
111 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
112 LOG (GNUNET_ERROR_TYPE_DEBUG,
113 "Trying to create an instance of named pipe `%s'\n", name);
114 /* 1) This might work just fine with UTF-8 strings as it is.
115 * 2) This is only used by GNUnet itself, and only with latin names.
117 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
118 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
123 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
124 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
126 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
128 h = CreateNamedPipe (*fn,
129 openMode | FILE_FLAG_OVERLAPPED |
130 FILE_FLAG_FIRST_PIPE_INSTANCE,
131 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
134 error_code = GetLastError ();
137 /* don't re-set name to NULL yet */
138 if (h == INVALID_HANDLE_VALUE)
140 SetErrnoFromWinError (error_code);
141 LOG (GNUNET_ERROR_TYPE_DEBUG,
142 "Pipe creation have failed because of %d, errno is %d\n", error_code,
146 LOG (GNUNET_ERROR_TYPE_DEBUG,
147 "Pipe was to be unique, considering re-creation\n");
150 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
154 LOG (GNUNET_ERROR_TYPE_DEBUG,
155 "Pipe name was not unique, trying again\n");
164 ret = GNUNET_malloc (sizeof (*ret));
166 ret->type = GNUNET_PIPE;
167 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
168 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
169 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
170 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
176 * Opens already existing named pipe/FIFO
178 * @param fn name of an existing named pipe
179 * @param flags open flags
180 * @return pipe handle on success, NULL on error
182 static struct GNUNET_DISK_FileHandle *
183 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
185 struct GNUNET_DISK_FileHandle *ret;
190 if (flags & GNUNET_DISK_OPEN_READWRITE)
191 openMode = GENERIC_WRITE | GENERIC_READ;
192 else if (flags & GNUNET_DISK_OPEN_READ)
193 openMode = GENERIC_READ;
194 else if (flags & GNUNET_DISK_OPEN_WRITE)
195 openMode = GENERIC_WRITE;
197 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
198 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
199 if (h == INVALID_HANDLE_VALUE)
201 SetErrnoFromWinError (GetLastError ());
205 ret = GNUNET_malloc (sizeof (*ret));
207 ret->type = GNUNET_PIPE;
208 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
209 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
210 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
211 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
217 /* UNIX version of named-pipe API */
220 * Clean up a named pipe and the directory it was placed in.
222 * @param fn name of the pipe
225 cleanup_npipe (const char *fn)
230 if (0 != unlink (fn))
231 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
232 dn = GNUNET_strdup (fn);
235 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
241 * Setup a named pipe.
243 * @param fn where to store the name of the new pipe,
244 * if *fn is non-null, the name of the pipe to setup
245 * @return GNUNET_OK on success
248 npipe_setup (char **fn)
252 /* FIXME: hardwired '/tmp' path... is bad */
253 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
255 if (NULL == mkdtemp (dir))
257 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
258 return GNUNET_SYSERR;
260 GNUNET_asprintf (fn, "%s/child-control", dir);
262 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
263 return GNUNET_SYSERR;
269 * Open an existing named pipe.
271 * @param fn name of the file
272 * @param flags flags to use
273 * @return NULL on error
275 static struct GNUNET_DISK_FileHandle *
276 npipe_open (const char *fn,
277 enum GNUNET_DISK_OpenFlags flags)
279 struct GNUNET_DISK_FileHandle *ret;
284 /* 200 * 5ms = 1s at most */
287 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
288 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
290 /* as this is for killing a child process via pipe and it is conceivable that
291 the child process simply didn't finish starting yet, we do some sleeping
292 (which is obviously usually not allowed). We can't select on the FD as
293 'open' fails, and we probably shouldn't just "ignore" the error, so wait
294 and retry a few times is likely the best method; our process API doesn't
295 support continuations, so we need to sleep directly... */
297 req.tv_nsec = 5000000; /* 5ms */
298 (void) nanosleep (&req, NULL);
302 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
303 (flags == GNUNET_DISK_OPEN_READ)
304 ? _("Failed to open named pipe `%s' for reading: %s\n")
305 : _("Failed to open named pipe `%s' for writing: %s\n"),
310 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
318 * This handler is called when there are control data to be read on the pipe
320 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
321 * @param tc scheduler context
324 parent_control_handler (void *cls,
325 const struct GNUNET_SCHEDULER_TaskContext *tc)
327 struct GNUNET_DISK_FileHandle *control_pipe = cls;
331 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
334 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT |
335 GNUNET_SCHEDULER_REASON_PREREQ_DONE))
337 GNUNET_DISK_file_close (control_pipe);
340 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
341 if (sizeof (sig) != ret)
344 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
345 GNUNET_DISK_file_close (control_pipe);
348 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
349 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
350 control_pipe, &parent_control_handler,
357 * Task that connects this process to its parent via pipe;
358 * essentially, the parent control handler will read signal numbers
359 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
360 * variable) and raise those signals.
362 * @param cls closure (unused)
363 * @param tc scheduler context (unused)
366 GNUNET_OS_install_parent_control_handler (void *cls,
368 GNUNET_SCHEDULER_TaskContext *tc)
371 struct GNUNET_DISK_FileHandle *control_pipe;
373 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
374 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
376 LOG (GNUNET_ERROR_TYPE_DEBUG,
377 "Not installing a handler because $%s is empty\n",
378 GNUNET_OS_CONTROL_PIPE);
379 putenv ("GNUNET_OS_CONTROL_PIPE=");
383 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
384 if (NULL == control_pipe)
386 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
387 putenv ("GNUNET_OS_CONTROL_PIPE=");
390 LOG (GNUNET_ERROR_TYPE_DEBUG,
391 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
392 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
393 &parent_control_handler, control_pipe);
394 putenv ("GNUNET_OS_CONTROL_PIPE=");
399 * Get process structure for current process
401 * The pointer it returns points to static memory location and must not be
404 * @return pointer to the process sturcutre for this process
406 struct GNUNET_OS_Process *
407 GNUNET_OS_process_current ()
410 current_process.pid = GetCurrentProcessId ();
411 current_process.handle = GetCurrentProcess ();
413 current_process.pid = 0;
415 return ¤t_process;
420 * Sends a signal to the process
422 * @param proc pointer to process structure
424 * @return 0 on success, -1 on error
427 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
434 if ( (NULL == proc->control_pipe) &&
435 (NULL != proc->childpipename) )
436 proc->control_pipe = npipe_open (proc->childpipename,
437 GNUNET_DISK_OPEN_WRITE);
439 if (NULL == proc->control_pipe)
442 /* no pipe and windows? can't do this */
446 return kill (proc->pid, sig);
449 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
450 if (ret == sizeof (csig))
452 /* pipe failed, try other methods */
461 #if WINDOWS && !defined(__CYGWIN__)
462 if (0 == TerminateProcess (proc->handle, 0))
464 /* FIXME: set 'errno' */
469 return PLIBC_KILL (proc->pid, sig);
476 return kill (proc->pid, sig);
482 * Get the pid of the process in question
484 * @param proc the process to get the pid of
486 * @return the current process id
489 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
496 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
498 if (NULL != proc->control_pipe)
499 GNUNET_DISK_file_close (proc->control_pipe);
502 if (proc->handle != NULL)
503 CloseHandle (proc->handle);
505 if (NULL != proc->childpipename)
508 cleanup_npipe (proc->childpipename);
510 GNUNET_free (proc->childpipename);
517 #include "gnunet_signal_lib.h"
519 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
522 * Make seaspider happy.
524 #define DWORD_WINAPI DWORD WINAPI
527 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
528 * @param proc pointer to process structure
531 child_wait_thread (void *arg)
533 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
535 WaitForSingleObject (proc->handle, INFINITE);
537 if (w32_sigchld_handler)
538 w32_sigchld_handler ();
545 * Set process priority
547 * @param proc pointer to process structure
548 * @param prio priority value
549 * @return GNUNET_OK on success, GNUNET_SYSERR on error
552 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
553 enum GNUNET_SCHEDULER_Priority prio)
557 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
558 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
561 /* convert to MINGW/Unix values */
564 case GNUNET_SCHEDULER_PRIORITY_UI:
565 case GNUNET_SCHEDULER_PRIORITY_URGENT:
567 rprio = HIGH_PRIORITY_CLASS;
573 case GNUNET_SCHEDULER_PRIORITY_HIGH:
575 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
581 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
583 rprio = NORMAL_PRIORITY_CLASS;
589 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
591 rprio = BELOW_NORMAL_PRIORITY_CLASS;
597 case GNUNET_SCHEDULER_PRIORITY_IDLE:
599 rprio = IDLE_PRIORITY_CLASS;
606 return GNUNET_SYSERR;
609 /* Set process priority */
612 HANDLE h = proc->handle;
614 GNUNET_assert (h != NULL);
615 SetPriorityClass (h, rprio);
621 if ((0 == pid) || (pid == getpid ()))
624 int delta = rprio - have;
627 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
629 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
630 return GNUNET_SYSERR;
635 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
637 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
639 return GNUNET_SYSERR;
643 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
644 "Priority management not availabe for this platform\n");
651 CreateCustomEnvTable (char **vars)
653 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
654 size_t tablesize = 0;
655 size_t items_count = 0;
656 size_t n_found = 0, n_var;
663 win32_env_table = GetEnvironmentStringsA ();
664 if (win32_env_table == NULL)
666 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
668 index = GNUNET_malloc (sizeof (char *) * n_var);
669 for (c = 0; c < n_var; c++)
671 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
673 size_t len = strlen (ptr);
676 for (var_ptr = vars; *var_ptr; var_ptr++)
680 var_len = strlen (var);
681 if (strncmp (var, ptr, var_len) == 0)
685 tablesize += var_len + strlen (val) + 1;
690 tablesize += len + 1;
693 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
698 n_found += strlen (var) + strlen (val) + 1;
700 result = GNUNET_malloc (tablesize + n_found + 1);
701 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
703 size_t len = strlen (ptr);
706 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
710 var_len = strlen (var);
711 if (strncmp (var, ptr, var_len) == 0)
719 strcpy (result_ptr, ptr);
720 result_ptr += len + 1;
724 strcpy (result_ptr, var);
725 result_ptr += var_len;
726 strcpy (result_ptr, val);
727 result_ptr += strlen (val) + 1;
731 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
735 var_len = strlen (var);
738 strcpy (result_ptr, var);
739 result_ptr += var_len;
740 strcpy (result_ptr, val);
741 result_ptr += strlen (val) + 1;
744 FreeEnvironmentStrings (win32_env_table);
755 * @param pipe_control should a pipe be used to send signals to the child?
756 * @param pipe_stdin pipe to use to send input to child process (or NULL)
757 * @param pipe_stdout pipe to use to get output from child process (or NULL)
758 * @param filename name of the binary
759 * @param argv NULL-terminated array of arguments to the process
760 * @return pointer to process structure of the new process, NULL on error
762 struct GNUNET_OS_Process *
763 GNUNET_OS_start_process_vap (int pipe_control,
764 struct GNUNET_DISK_PipeHandle *pipe_stdin,
765 struct GNUNET_DISK_PipeHandle *pipe_stdout,
766 const char *filename,
770 char *childpipename = NULL;
771 struct GNUNET_OS_Process *gnunet_proc = NULL;
778 if ( (GNUNET_YES == pipe_control) &&
780 npipe_setup (&childpipename)) )
782 if (pipe_stdout != NULL)
784 GNUNET_assert (GNUNET_OK ==
785 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
787 GNUNET_DISK_PIPE_END_WRITE),
788 &fd_stdout_write, sizeof (int)));
789 GNUNET_assert (GNUNET_OK ==
790 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
791 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
792 &fd_stdout_read, sizeof (int)));
794 if (pipe_stdin != NULL)
796 GNUNET_assert (GNUNET_OK ==
797 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
798 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
799 &fd_stdin_read, sizeof (int)));
800 GNUNET_assert (GNUNET_OK ==
801 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
802 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
803 &fd_stdin_write, sizeof (int)));
809 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
810 GNUNET_free_non_null (childpipename);
815 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
816 gnunet_proc->pid = ret;
817 gnunet_proc->childpipename = childpipename;
820 if (NULL != childpipename)
822 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
823 GNUNET_free (childpipename);
825 if (pipe_stdout != NULL)
827 GNUNET_break (0 == close (fd_stdout_read));
828 if (-1 == dup2 (fd_stdout_write, 1))
829 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
830 GNUNET_break (0 == close (fd_stdout_write));
833 if (pipe_stdin != NULL)
836 GNUNET_break (0 == close (fd_stdin_write));
837 if (-1 == dup2 (fd_stdin_read, 0))
838 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
839 GNUNET_break (0 == close (fd_stdin_read));
841 execvp (filename, argv);
842 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
845 char *childpipename = NULL;
846 struct GNUNET_OS_Process *gnunet_proc = NULL;
851 PROCESS_INFORMATION proc;
854 HANDLE stdout_handle;
855 struct GNUNET_DISK_FileHandle *control_pipe;
857 char path[MAX_PATH + 1];
859 char *our_env[3] = { NULL, NULL, NULL };
860 char *env_block = NULL;
862 DWORD pathbuf_len, alloc_len;
867 char *non_const_filename;
868 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
870 /* Search in prefix dir (hopefully - the directory from which
871 * the current module was loaded), bindir and libdir, then in PATH
873 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
874 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
875 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
877 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
880 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
883 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
886 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
887 GNUNET_free (self_prefix);
888 GNUNET_free (bindir);
889 GNUNET_free (libdir);
891 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
892 GNUNET_assert (alloc_len == (pathbuf_len - 1));
894 cmdlen = strlen (filename);
895 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
896 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
898 GNUNET_asprintf (&non_const_filename, "%s", filename);
900 /* Check that this is the full path. If it isn't, search. */
901 if (non_const_filename[1] == ':')
902 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
903 else if (!SearchPathA
904 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
907 SetErrnoFromWinError (GetLastError ());
908 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
910 GNUNET_free (non_const_filename);
911 GNUNET_free (pathbuf);
914 GNUNET_free (pathbuf);
915 GNUNET_free (non_const_filename);
919 while (NULL != (arg = argv[argc++]))
922 cmdlen = cmdlen + strlen (path) + 4;
924 cmdlen = cmdlen + strlen (arg) + 4;
928 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
930 while (NULL != (arg = argv[argc++]))
932 /* This is to escape trailing slash */
933 char arg_lastchar = arg[strlen (arg) - 1];
935 idx += sprintf (idx, "\"%s%s\"%s", path,
936 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
938 idx += sprintf (idx, "\"%s%s\"%s", arg,
939 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
942 memset (&start, 0, sizeof (start));
943 start.cb = sizeof (start);
945 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
946 start.dwFlags |= STARTF_USESTDHANDLES;
948 if (pipe_stdin != NULL)
950 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
951 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
952 &stdin_handle, sizeof (HANDLE));
953 start.hStdInput = stdin_handle;
956 if (pipe_stdout != NULL)
958 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
960 GNUNET_DISK_PIPE_END_WRITE),
961 &stdout_handle, sizeof (HANDLE));
962 start.hStdOutput = stdout_handle;
964 if (GNUNET_YES == pipe_control)
967 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
968 GNUNET_DISK_PERM_USER_READ |
969 GNUNET_DISK_PERM_USER_WRITE);
970 if (control_pipe == NULL)
979 if (NULL != childpipename)
981 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
983 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
984 GNUNET_asprintf (&our_env[1], "%s", childpipename);
991 env_block = CreateCustomEnvTable (our_env);
992 GNUNET_free_non_null (our_env[0]);
993 GNUNET_free_non_null (our_env[1]);
995 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
996 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
998 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
999 env_block, NULL, &start, &proc))
1001 SetErrnoFromWinError (GetLastError ());
1002 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1003 GNUNET_free (env_block);
1008 GNUNET_free (env_block);
1010 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1011 gnunet_proc->pid = proc.dwProcessId;
1012 gnunet_proc->handle = proc.hProcess;
1013 gnunet_proc->control_pipe = control_pipe;
1015 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1017 ResumeThread (proc.hThread);
1018 CloseHandle (proc.hThread);
1030 * @param pipe_control should a pipe be used to send signals to the child?
1031 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1032 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1033 * @param filename name of the binary
1034 * @param va NULL-terminated list of arguments to the process
1035 * @return pointer to process structure of the new process, NULL on error
1037 struct GNUNET_OS_Process *
1038 GNUNET_OS_start_process_va (int pipe_control,
1039 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1040 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1041 const char *filename, va_list va)
1043 struct GNUNET_OS_Process *ret;
1050 while (NULL != va_arg (ap, char *))
1053 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1056 while (NULL != (argv[argc] = va_arg (ap, char *)))
1059 ret = GNUNET_OS_start_process_vap (pipe_control,
1073 * @param pipe_control should a pipe be used to send signals to the child?
1074 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1075 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1076 * @param filename name of the binary
1077 * @param ... NULL-terminated list of arguments to the process
1079 * @return pointer to process structure of the new process, NULL on error
1082 struct GNUNET_OS_Process *
1083 GNUNET_OS_start_process (int pipe_control,
1084 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1085 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1086 const char *filename, ...)
1088 struct GNUNET_OS_Process *ret;
1091 va_start (ap, filename);
1092 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1101 * @param pipe_control should a pipe be used to send signals to the child?
1102 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1103 * must be NULL on platforms where dup is not supported
1104 * @param filename name of the binary
1105 * @param argv NULL-terminated list of arguments to the process
1106 * @return process ID of the new process, -1 on error
1108 struct GNUNET_OS_Process *
1109 GNUNET_OS_start_process_v (int pipe_control,
1110 const SOCKTYPE *lsocks,
1111 const char *filename,
1118 struct GNUNET_OS_Process *gnunet_proc = NULL;
1119 char *childpipename = NULL;
1128 if ( (GNUNET_YES == pipe_control) &&
1129 (GNUNET_OK != npipe_setup (&childpipename)) )
1136 while (-1 != (k = lsocks[i++]))
1137 GNUNET_array_append (lscp, ls, k);
1138 GNUNET_array_append (lscp, ls, -1);
1143 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1144 GNUNET_free_non_null (childpipename);
1145 GNUNET_array_grow (lscp, ls, 0);
1150 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1151 gnunet_proc->pid = ret;
1152 gnunet_proc->childpipename = childpipename;
1153 GNUNET_array_grow (lscp, ls, 0);
1156 if (NULL != childpipename)
1158 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1159 GNUNET_free (childpipename);
1163 /* read systemd documentation... */
1164 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1165 setenv ("LISTEN_PID", lpid, 1);
1168 while (-1 != lscp[i])
1171 while (-1 != lscp[j])
1177 GNUNET_assert (-1 != k);
1178 GNUNET_assert (0 == close (lscp[j]));
1186 /* Bury any existing FD, no matter what; they should all be closed
1187 * on exec anyway and the important onces have been dup'ed away */
1189 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1191 /* unset close-on-exec flag */
1192 flags = fcntl (tgt, F_GETFD);
1193 GNUNET_assert (flags >= 0);
1194 flags &= ~FD_CLOEXEC;
1196 (void) fcntl (tgt, F_SETFD, flags);
1200 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1201 setenv ("LISTEN_FDS", fds, 1);
1203 GNUNET_array_grow (lscp, ls, 0);
1204 execvp (filename, argv);
1205 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1208 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1209 char *childpipename = NULL;
1210 char **arg, **non_const_argv;
1211 unsigned int cmdlen;
1214 PROCESS_INFORMATION proc;
1216 struct GNUNET_OS_Process *gnunet_proc = NULL;
1217 char path[MAX_PATH + 1];
1218 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1219 char *env_block = NULL;
1221 DWORD pathbuf_len, alloc_len;
1226 char *non_const_filename;
1227 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1228 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1230 HANDLE lsocks_write;
1231 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1235 /* Search in prefix dir (hopefully - the directory from which
1236 * the current module was loaded), bindir and libdir, then in PATH
1238 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1239 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1240 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1242 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1245 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1248 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1251 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1252 GNUNET_free (self_prefix);
1253 GNUNET_free (bindir);
1254 GNUNET_free (libdir);
1256 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1257 if (alloc_len != pathbuf_len - 1)
1259 GNUNET_free (pathbuf);
1260 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1264 cmdlen = strlen (filename);
1265 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1266 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1268 GNUNET_asprintf (&non_const_filename, "%s", filename);
1270 /* Check that this is the full path. If it isn't, search. */
1271 if (non_const_filename[1] == ':')
1272 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1273 else if (!SearchPathA
1274 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1277 SetErrnoFromWinError (GetLastError ());
1278 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1279 non_const_filename);
1280 GNUNET_free (non_const_filename);
1281 GNUNET_free (pathbuf);
1284 GNUNET_free (pathbuf);
1285 GNUNET_free (non_const_filename);
1287 /* Count the number of arguments */
1288 arg = (char **) argv;
1295 /* Allocate a copy argv */
1296 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1298 /* Copy all argv strings */
1300 arg = (char **) argv;
1304 non_const_argv[argcount] = GNUNET_strdup (path);
1306 non_const_argv[argcount] = GNUNET_strdup (*arg);
1310 non_const_argv[argcount] = NULL;
1314 arg = non_const_argv;
1317 cmdlen = cmdlen + strlen (*arg) + 4;
1321 /* Allocate and create cmd */
1322 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1323 arg = non_const_argv;
1326 char arg_last_char = (*arg)[strlen (*arg) - 1];
1327 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1328 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1332 while (argcount > 0)
1333 GNUNET_free (non_const_argv[--argcount]);
1334 GNUNET_free (non_const_argv);
1336 memset (&start, 0, sizeof (start));
1337 start.cb = sizeof (start);
1339 if (GNUNET_YES == pipe_control)
1342 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1343 GNUNET_DISK_PERM_USER_READ |
1344 GNUNET_DISK_PERM_USER_WRITE);
1345 if (control_pipe == NULL)
1353 control_pipe = NULL;
1354 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1356 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1358 if (lsocks_pipe == NULL)
1362 GNUNET_DISK_pipe_close (lsocks_pipe);
1365 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1366 GNUNET_DISK_PIPE_END_WRITE);
1367 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1368 &lsocks_write, sizeof (HANDLE));
1369 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1370 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1371 &lsocks_read, sizeof (HANDLE));
1375 if (NULL != childpipename)
1377 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1379 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1380 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1381 GNUNET_free (childpipename);
1383 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1385 /*This will tell the child that we're going to send lsocks over the pipe*/
1386 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1387 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1389 our_env[env_off++] = NULL;
1390 env_block = CreateCustomEnvTable (our_env);
1392 GNUNET_free_non_null (our_env[--env_off]);
1393 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1394 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1396 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1397 env_block, NULL, &start, &proc))
1399 SetErrnoFromWinError (GetLastError ());
1400 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1401 if (NULL != control_pipe)
1402 GNUNET_DISK_file_close (control_pipe);
1404 GNUNET_DISK_pipe_close (lsocks_pipe);
1405 GNUNET_free (env_block);
1410 GNUNET_free (env_block);
1412 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1413 gnunet_proc->pid = proc.dwProcessId;
1414 gnunet_proc->handle = proc.hProcess;
1415 gnunet_proc->control_pipe = control_pipe;
1417 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1419 ResumeThread (proc.hThread);
1420 CloseHandle (proc.hThread);
1423 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1426 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1428 /* This is a replacement for "goto error" that doesn't use goto */
1433 uint64_t size, count, i;
1435 /* Tell the number of sockets */
1436 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1438 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1439 if (wrote != sizeof (count))
1441 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1444 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1446 WSAPROTOCOL_INFOA pi;
1447 /* Get a socket duplication info */
1448 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1451 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1454 /* Synchronous I/O is not nice, but we can't schedule this:
1455 * lsocks will be closed/freed by the caller soon, and until
1456 * the child creates a duplicate, closing a socket here will
1457 * close it for good.
1459 /* Send the size of the structure
1460 * (the child might be built with different headers...)
1463 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1464 if (wrote != sizeof (size))
1466 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1469 /* Finally! Send the data */
1470 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1471 if (wrote != sizeof (pi))
1473 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1477 /* This will block us until the child makes a final read or closes
1478 * the pipe (hence no 'wrote' check), since we have to wait for it
1479 * to duplicate the last socket, before we return and start closing
1482 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1487 GNUNET_DISK_file_sync (lsocks_write_fd);
1488 GNUNET_DISK_pipe_close (lsocks_pipe);
1492 /* If we can't pass on the socket(s), the child will block forever,
1493 * better put it out of its misery.
1495 TerminateProcess (gnunet_proc->handle, 0);
1496 CloseHandle (gnunet_proc->handle);
1497 if (NULL != gnunet_proc->control_pipe)
1498 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1499 GNUNET_free (gnunet_proc);
1508 * Retrieve the status of a process
1509 * @param proc process ID
1510 * @param type status type
1511 * @param code return code/signal number
1512 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1515 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1516 enum GNUNET_OS_ProcessStatusType *type,
1517 unsigned long *code)
1523 GNUNET_assert (0 != proc);
1524 ret = waitpid (proc->pid, &status, WNOHANG);
1527 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1528 return GNUNET_SYSERR;
1532 *type = GNUNET_OS_PROCESS_RUNNING;
1536 if (proc->pid != ret)
1538 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1539 return GNUNET_SYSERR;
1541 if (WIFEXITED (status))
1543 *type = GNUNET_OS_PROCESS_EXITED;
1544 *code = WEXITSTATUS (status);
1546 else if (WIFSIGNALED (status))
1548 *type = GNUNET_OS_PROCESS_SIGNALED;
1549 *code = WTERMSIG (status);
1551 else if (WIFSTOPPED (status))
1553 *type = GNUNET_OS_PROCESS_SIGNALED;
1554 *code = WSTOPSIG (status);
1557 else if (WIFCONTINUED (status))
1559 *type = GNUNET_OS_PROCESS_RUNNING;
1565 *type = GNUNET_OS_PROCESS_UNKNOWN;
1570 DWORD c, error_code, ret;
1574 if (h == NULL || ret == 0)
1576 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1578 return GNUNET_SYSERR;
1581 h = GetCurrentProcess ();
1584 ret = GetExitCodeProcess (h, &c);
1585 error_code = GetLastError ();
1586 if (ret == 0 || error_code != NO_ERROR)
1588 SetErrnoFromWinError (error_code);
1589 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1590 return GNUNET_SYSERR;
1592 if (STILL_ACTIVE == c)
1594 *type = GNUNET_OS_PROCESS_RUNNING;
1598 *type = GNUNET_OS_PROCESS_EXITED;
1607 * Wait for a process
1608 * @param proc pointer to process structure
1609 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1612 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1616 pid_t pid = proc->pid;
1619 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1620 (EINTR == errno) ) ;
1623 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1624 return GNUNET_SYSERR;
1634 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1636 return GNUNET_SYSERR;
1639 h = GetCurrentProcess ();
1641 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1643 SetErrnoFromWinError (GetLastError ());
1644 ret = GNUNET_SYSERR;
1655 * Handle to a command.
1657 struct GNUNET_OS_CommandHandle
1663 struct GNUNET_OS_Process *eip;
1666 * Handle to the output pipe.
1668 struct GNUNET_DISK_PipeHandle *opipe;
1671 * Read-end of output pipe.
1673 const struct GNUNET_DISK_FileHandle *r;
1676 * Function to call on each line of output.
1678 GNUNET_OS_LineProcessor proc;
1681 * Closure for 'proc'.
1686 * Buffer for the output.
1691 * Task reading from pipe.
1693 GNUNET_SCHEDULER_TaskIdentifier rtask;
1698 struct GNUNET_TIME_Absolute timeout;
1701 * Current read offset in buf.
1708 * Stop/kill a command. Must ONLY be called either from
1709 * the callback after 'NULL' was passed for 'line' *OR*
1710 * from an independent task (not within the line processor).
1712 * @param cmd handle to the process
1715 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1718 if (cmd->proc != NULL)
1720 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1721 GNUNET_SCHEDULER_cancel (cmd->rtask);
1723 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1724 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1725 GNUNET_OS_process_close (cmd->eip);
1726 GNUNET_DISK_pipe_close (cmd->opipe);
1732 * Read from the process and call the line processor.
1734 * @param cls the 'struct GNUNET_OS_CommandHandle'
1735 * @param tc scheduler context
1738 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1740 struct GNUNET_OS_CommandHandle *cmd = cls;
1741 GNUNET_OS_LineProcessor proc;
1745 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1746 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1748 /* timeout, shutdown, etc. */
1751 proc (cmd->proc_cls, NULL);
1755 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1756 sizeof (cmd->buf) - cmd->off);
1759 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1761 cmd->buf[cmd->off] = '\0';
1762 cmd->proc (cmd->proc_cls, cmd->buf);
1766 proc (cmd->proc_cls, NULL);
1769 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1774 cmd->proc (cmd->proc_cls, cmd->buf);
1775 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1776 cmd->off -= (end + 1 - cmd->buf);
1777 end = memchr (cmd->buf, '\n', cmd->off);
1780 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1781 (cmd->timeout), cmd->r, &cmd_read, cmd);
1786 * Run the given command line and call the given function
1787 * for each line of the output.
1789 * @param proc function to call for each line of the output
1790 * @param proc_cls closure for proc
1791 * @param timeout when to time out
1792 * @param binary command to run
1793 * @param ... arguments to command
1794 * @return NULL on error
1796 struct GNUNET_OS_CommandHandle *
1797 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1798 struct GNUNET_TIME_Relative timeout, const char *binary,
1801 struct GNUNET_OS_CommandHandle *cmd;
1802 struct GNUNET_OS_Process *eip;
1803 struct GNUNET_DISK_PipeHandle *opipe;
1806 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1809 va_start (ap, binary);
1810 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1814 GNUNET_DISK_pipe_close (opipe);
1817 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1818 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1819 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1823 cmd->proc_cls = proc_cls;
1824 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1825 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1832 /* end of os_priority.c */