2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2011 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file util/os_priority.c
23 * @brief Methods to set process priority
28 #include "gnunet_common.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_scheduler_lib.h"
31 #include "gnunet_strings_lib.h"
32 #include "gnunet_crypto_lib.h"
35 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
37 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
39 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
41 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
43 struct GNUNET_OS_Process
58 * Pipe we use to signal the process (if used).
60 struct GNUNET_DISK_FileHandle *control_pipe;
63 * Name of the pipe, NULL for none.
70 * Handle for 'this' process.
72 static struct GNUNET_OS_Process current_process;
75 /* MinGW version of named pipe API */
78 * Creates a named pipe/FIFO and opens it
80 * @param fn pointer to the name of the named pipe or to NULL
81 * @param flags open flags
82 * @param perm access permissions
83 * @return pipe handle on success, NULL on error
85 static struct GNUNET_DISK_FileHandle *
86 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
87 enum GNUNET_DISK_AccessPermissions perm)
89 struct GNUNET_DISK_FileHandle *ret;
95 if (flags & GNUNET_DISK_OPEN_READWRITE)
96 openMode = PIPE_ACCESS_DUPLEX;
97 else if (flags & GNUNET_DISK_OPEN_READ)
98 openMode = PIPE_ACCESS_INBOUND;
99 else if (flags & GNUNET_DISK_OPEN_WRITE)
100 openMode = PIPE_ACCESS_OUTBOUND;
101 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
102 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
111 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
112 LOG (GNUNET_ERROR_TYPE_DEBUG,
113 "Trying to create an instance of named pipe `%s'\n", name);
114 /* 1) This might work just fine with UTF-8 strings as it is.
115 * 2) This is only used by GNUnet itself, and only with latin names.
117 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
118 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
123 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
124 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
126 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
128 h = CreateNamedPipe (*fn,
129 openMode | FILE_FLAG_OVERLAPPED |
130 FILE_FLAG_FIRST_PIPE_INSTANCE,
131 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
134 error_code = GetLastError ();
137 /* don't re-set name to NULL yet */
138 if (h == INVALID_HANDLE_VALUE)
140 SetErrnoFromWinError (error_code);
141 LOG (GNUNET_ERROR_TYPE_DEBUG,
142 "Pipe creation have failed because of %d, errno is %d\n", error_code,
146 LOG (GNUNET_ERROR_TYPE_DEBUG,
147 "Pipe was to be unique, considering re-creation\n");
150 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
154 LOG (GNUNET_ERROR_TYPE_DEBUG,
155 "Pipe name was not unique, trying again\n");
164 ret = GNUNET_malloc (sizeof (*ret));
166 ret->type = GNUNET_PIPE;
167 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
168 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
169 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
170 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
176 * Opens already existing named pipe/FIFO
178 * @param fn name of an existing named pipe
179 * @param flags open flags
180 * @return pipe handle on success, NULL on error
182 static struct GNUNET_DISK_FileHandle *
183 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
185 struct GNUNET_DISK_FileHandle *ret;
190 if (flags & GNUNET_DISK_OPEN_READWRITE)
191 openMode = GENERIC_WRITE | GENERIC_READ;
192 else if (flags & GNUNET_DISK_OPEN_READ)
193 openMode = GENERIC_READ;
194 else if (flags & GNUNET_DISK_OPEN_WRITE)
195 openMode = GENERIC_WRITE;
197 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
198 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
199 if (h == INVALID_HANDLE_VALUE)
201 SetErrnoFromWinError (GetLastError ());
205 ret = GNUNET_malloc (sizeof (*ret));
207 ret->type = GNUNET_PIPE;
208 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
209 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
210 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
211 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
217 /* UNIX version of named-pipe API */
220 * Clean up a named pipe and the directory it was placed in.
222 * @param fn name of the pipe
225 cleanup_npipe (const char *fn)
230 if (0 != unlink (fn))
231 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
232 dn = GNUNET_strdup (fn);
235 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
241 * Setup a named pipe.
243 * @param fn where to store the name of the new pipe,
244 * if *fn is non-null, the name of the pipe to setup
245 * @return GNUNET_OK on success
248 npipe_setup (char **fn)
252 /* FIXME: hardwired '/tmp' path... is bad */
253 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
255 if (NULL == mkdtemp (dir))
257 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
258 return GNUNET_SYSERR;
260 GNUNET_asprintf (fn, "%s/child-control", dir);
262 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
263 return GNUNET_SYSERR;
269 * Open an existing named pipe.
271 * @param fn name of the file
272 * @param flags flags to use
273 * @return NULL on error
275 static struct GNUNET_DISK_FileHandle *
276 npipe_open (const char *fn,
277 enum GNUNET_DISK_OpenFlags flags)
279 struct GNUNET_DISK_FileHandle *ret;
284 /* 200 * 5ms = 1s at most */
287 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
288 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
290 /* as this is for killing a child process via pipe and it is conceivable that
291 the child process simply didn't finish starting yet, we do some sleeping
292 (which is obviously usually not allowed). We can't select on the FD as
293 'open' fails, and we probably shouldn't just "ignore" the error, so wait
294 and retry a few times is likely the best method; our process API doesn't
295 support continuations, so we need to sleep directly... */
297 req.tv_nsec = 5000000; /* 5ms */
298 (void) nanosleep (&req, NULL);
302 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
303 (flags == GNUNET_DISK_OPEN_READ)
304 ? _("Failed to open named pipe `%s' for reading: %s\n")
305 : _("Failed to open named pipe `%s' for writing: %s\n"),
310 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
318 * This handler is called when there are control data to be read on the pipe
320 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
321 * @param tc scheduler context
324 parent_control_handler (void *cls,
325 const struct GNUNET_SCHEDULER_TaskContext *tc)
327 struct GNUNET_DISK_FileHandle *control_pipe = cls;
330 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
333 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT |
334 GNUNET_SCHEDULER_REASON_PREREQ_DONE))
336 GNUNET_DISK_file_close (control_pipe);
339 if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) !=
342 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
343 GNUNET_DISK_file_close (control_pipe);
346 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
347 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
348 control_pipe, &parent_control_handler,
355 * Task that connects this process to its parent via pipe;
356 * essentially, the parent control handler will read signal numbers
357 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
358 * variable) and raise those signals.
360 * @param cls closure (unused)
361 * @param tc scheduler context (unused)
364 GNUNET_OS_install_parent_control_handler (void *cls,
366 GNUNET_SCHEDULER_TaskContext *tc)
369 struct GNUNET_DISK_FileHandle *control_pipe;
371 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
372 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
374 LOG (GNUNET_ERROR_TYPE_DEBUG,
375 "Not installing a handler because $%s is empty\n",
376 GNUNET_OS_CONTROL_PIPE);
377 putenv ("GNUNET_OS_CONTROL_PIPE=");
381 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
382 if (NULL == control_pipe)
384 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
385 putenv ("GNUNET_OS_CONTROL_PIPE=");
388 LOG (GNUNET_ERROR_TYPE_DEBUG,
389 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
390 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
391 &parent_control_handler, control_pipe);
392 putenv ("GNUNET_OS_CONTROL_PIPE=");
397 * Get process structure for current process
399 * The pointer it returns points to static memory location and must not be
402 * @return pointer to the process sturcutre for this process
404 struct GNUNET_OS_Process *
405 GNUNET_OS_process_current ()
408 current_process.pid = GetCurrentProcessId ();
409 current_process.handle = GetCurrentProcess ();
411 current_process.pid = 0;
413 return ¤t_process;
418 * Sends a signal to the process
420 * @param proc pointer to process structure
422 * @return 0 on success, -1 on error
425 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
430 if ( (NULL == proc->control_pipe) &&
431 (NULL != proc->childpipename) )
432 proc->control_pipe = npipe_open (proc->childpipename,
433 GNUNET_DISK_OPEN_WRITE);
435 if (NULL == proc->control_pipe)
438 /* no pipe and windows? can't do this */
442 return kill (proc->pid, sig);
445 ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof (sig));
446 if (ret == sizeof (sig))
448 /* pipe failed, try other methods */
457 #if WINDOWS && !defined(__CYGWIN__)
458 if (0 == TerminateProcess (proc->handle, 0))
460 /* FIXME: set 'errno' */
465 return PLIBC_KILL (proc->pid, sig);
472 return kill (proc->pid, sig);
478 * Get the pid of the process in question
480 * @param proc the process to get the pid of
482 * @return the current process id
485 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
492 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
494 if (NULL != proc->control_pipe)
495 GNUNET_DISK_file_close (proc->control_pipe);
498 if (proc->handle != NULL)
499 CloseHandle (proc->handle);
501 if (NULL != proc->childpipename)
504 cleanup_npipe (proc->childpipename);
506 GNUNET_free (proc->childpipename);
513 #include "gnunet_signal_lib.h"
515 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
518 * Make seaspider happy.
520 #define DWORD_WINAPI DWORD WINAPI
523 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
524 * @param proc pointer to process structure
527 child_wait_thread (void *arg)
529 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
531 WaitForSingleObject (proc->handle, INFINITE);
533 if (w32_sigchld_handler)
534 w32_sigchld_handler ();
541 * Set process priority
543 * @param proc pointer to process structure
544 * @param prio priority value
545 * @return GNUNET_OK on success, GNUNET_SYSERR on error
548 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
549 enum GNUNET_SCHEDULER_Priority prio)
553 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
554 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
557 /* convert to MINGW/Unix values */
560 case GNUNET_SCHEDULER_PRIORITY_UI:
561 case GNUNET_SCHEDULER_PRIORITY_URGENT:
563 rprio = HIGH_PRIORITY_CLASS;
569 case GNUNET_SCHEDULER_PRIORITY_HIGH:
571 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
577 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
579 rprio = NORMAL_PRIORITY_CLASS;
585 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
587 rprio = BELOW_NORMAL_PRIORITY_CLASS;
593 case GNUNET_SCHEDULER_PRIORITY_IDLE:
595 rprio = IDLE_PRIORITY_CLASS;
602 return GNUNET_SYSERR;
605 /* Set process priority */
608 HANDLE h = proc->handle;
610 GNUNET_assert (h != NULL);
611 SetPriorityClass (h, rprio);
617 if ((0 == pid) || (pid == getpid ()))
620 int delta = rprio - have;
623 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
625 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
626 return GNUNET_SYSERR;
631 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
633 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
635 return GNUNET_SYSERR;
639 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
640 "Priority management not availabe for this platform\n");
647 CreateCustomEnvTable (char **vars)
649 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
650 size_t tablesize = 0;
651 size_t items_count = 0;
652 size_t n_found = 0, n_var;
659 win32_env_table = GetEnvironmentStringsA ();
660 if (win32_env_table == NULL)
662 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
664 index = GNUNET_malloc (sizeof (char *) * n_var);
665 for (c = 0; c < n_var; c++)
667 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
669 size_t len = strlen (ptr);
672 for (var_ptr = vars; *var_ptr; var_ptr++)
676 var_len = strlen (var);
677 if (strncmp (var, ptr, var_len) == 0)
681 tablesize += var_len + strlen (val) + 1;
686 tablesize += len + 1;
689 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
694 n_found += strlen (var) + strlen (val) + 1;
696 result = GNUNET_malloc (tablesize + n_found + 1);
697 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
699 size_t len = strlen (ptr);
702 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
706 var_len = strlen (var);
707 if (strncmp (var, ptr, var_len) == 0)
715 strcpy (result_ptr, ptr);
716 result_ptr += len + 1;
720 strcpy (result_ptr, var);
721 result_ptr += var_len;
722 strcpy (result_ptr, val);
723 result_ptr += strlen (val) + 1;
727 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
731 var_len = strlen (var);
734 strcpy (result_ptr, var);
735 result_ptr += var_len;
736 strcpy (result_ptr, val);
737 result_ptr += strlen (val) + 1;
740 FreeEnvironmentStrings (win32_env_table);
751 * @param pipe_control should a pipe be used to send signals to the child?
752 * @param pipe_stdin pipe to use to send input to child process (or NULL)
753 * @param pipe_stdout pipe to use to get output from child process (or NULL)
754 * @param filename name of the binary
755 * @param argv NULL-terminated array of arguments to the process
756 * @return pointer to process structure of the new process, NULL on error
758 struct GNUNET_OS_Process *
759 GNUNET_OS_start_process_vap (int pipe_control,
760 struct GNUNET_DISK_PipeHandle *pipe_stdin,
761 struct GNUNET_DISK_PipeHandle *pipe_stdout,
762 const char *filename,
766 char *childpipename = NULL;
767 struct GNUNET_OS_Process *gnunet_proc = NULL;
774 if ( (GNUNET_YES == pipe_control) &&
776 npipe_setup (&childpipename)) )
778 if (pipe_stdout != NULL)
780 GNUNET_assert (GNUNET_OK ==
781 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
783 GNUNET_DISK_PIPE_END_WRITE),
784 &fd_stdout_write, sizeof (int)));
785 GNUNET_assert (GNUNET_OK ==
786 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
787 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
788 &fd_stdout_read, sizeof (int)));
790 if (pipe_stdin != NULL)
792 GNUNET_assert (GNUNET_OK ==
793 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
794 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
795 &fd_stdin_read, sizeof (int)));
796 GNUNET_assert (GNUNET_OK ==
797 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
798 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
799 &fd_stdin_write, sizeof (int)));
805 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
806 GNUNET_free_non_null (childpipename);
811 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
812 gnunet_proc->pid = ret;
813 gnunet_proc->childpipename = childpipename;
816 if (NULL != childpipename)
818 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
819 GNUNET_free (childpipename);
821 if (pipe_stdout != NULL)
823 GNUNET_break (0 == close (fd_stdout_read));
824 if (-1 == dup2 (fd_stdout_write, 1))
825 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
826 GNUNET_break (0 == close (fd_stdout_write));
829 if (pipe_stdin != NULL)
832 GNUNET_break (0 == close (fd_stdin_write));
833 if (-1 == dup2 (fd_stdin_read, 0))
834 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
835 GNUNET_break (0 == close (fd_stdin_read));
837 execvp (filename, argv);
838 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
841 char *childpipename = NULL;
842 struct GNUNET_OS_Process *gnunet_proc = NULL;
847 PROCESS_INFORMATION proc;
850 HANDLE stdout_handle;
851 struct GNUNET_DISK_FileHandle *control_pipe;
853 char path[MAX_PATH + 1];
855 char *our_env[3] = { NULL, NULL, NULL };
856 char *env_block = NULL;
858 DWORD pathbuf_len, alloc_len;
863 char *non_const_filename;
864 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
866 /* Search in prefix dir (hopefully - the directory from which
867 * the current module was loaded), bindir and libdir, then in PATH
869 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
870 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
871 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
873 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
876 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
879 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
882 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
883 GNUNET_free (self_prefix);
884 GNUNET_free (bindir);
885 GNUNET_free (libdir);
887 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
888 GNUNET_assert (alloc_len == (pathbuf_len - 1));
890 cmdlen = strlen (filename);
891 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
892 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
894 GNUNET_asprintf (&non_const_filename, "%s", filename);
896 /* Check that this is the full path. If it isn't, search. */
897 if (non_const_filename[1] == ':')
898 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
899 else if (!SearchPathA
900 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
903 SetErrnoFromWinError (GetLastError ());
904 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
906 GNUNET_free (non_const_filename);
907 GNUNET_free (pathbuf);
910 GNUNET_free (pathbuf);
911 GNUNET_free (non_const_filename);
915 while (NULL != (arg = argv[argc++]))
918 cmdlen = cmdlen + strlen (path) + 4;
920 cmdlen = cmdlen + strlen (arg) + 4;
924 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
926 while (NULL != (arg = argv[argc++]))
928 /* This is to escape trailing slash */
929 char arg_lastchar = arg[strlen (arg) - 1];
931 idx += sprintf (idx, "\"%s%s\"%s", path,
932 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
934 idx += sprintf (idx, "\"%s%s\"%s", arg,
935 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
938 memset (&start, 0, sizeof (start));
939 start.cb = sizeof (start);
941 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
942 start.dwFlags |= STARTF_USESTDHANDLES;
944 if (pipe_stdin != NULL)
946 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
947 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
948 &stdin_handle, sizeof (HANDLE));
949 start.hStdInput = stdin_handle;
952 if (pipe_stdout != NULL)
954 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
956 GNUNET_DISK_PIPE_END_WRITE),
957 &stdout_handle, sizeof (HANDLE));
958 start.hStdOutput = stdout_handle;
960 if (GNUNET_YES == pipe_control)
963 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
964 GNUNET_DISK_PERM_USER_READ |
965 GNUNET_DISK_PERM_USER_WRITE);
966 if (control_pipe == NULL)
975 if (NULL != childpipename)
977 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
979 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
980 GNUNET_asprintf (&our_env[1], "%s", childpipename);
987 env_block = CreateCustomEnvTable (our_env);
988 GNUNET_free_non_null (our_env[0]);
989 GNUNET_free_non_null (our_env[1]);
991 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
992 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
994 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
995 env_block, NULL, &start, &proc))
997 SetErrnoFromWinError (GetLastError ());
998 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
999 GNUNET_free (env_block);
1004 GNUNET_free (env_block);
1006 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1007 gnunet_proc->pid = proc.dwProcessId;
1008 gnunet_proc->handle = proc.hProcess;
1009 gnunet_proc->control_pipe = control_pipe;
1011 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1013 ResumeThread (proc.hThread);
1014 CloseHandle (proc.hThread);
1026 * @param pipe_control should a pipe be used to send signals to the child?
1027 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1028 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1029 * @param filename name of the binary
1030 * @param va NULL-terminated list of arguments to the process
1031 * @return pointer to process structure of the new process, NULL on error
1033 struct GNUNET_OS_Process *
1034 GNUNET_OS_start_process_va (int pipe_control,
1035 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1036 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1037 const char *filename, va_list va)
1039 struct GNUNET_OS_Process *ret;
1046 while (NULL != va_arg (ap, char *))
1049 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1052 while (NULL != (argv[argc] = va_arg (ap, char *)))
1055 ret = GNUNET_OS_start_process_vap (pipe_control,
1069 * @param pipe_control should a pipe be used to send signals to the child?
1070 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1071 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1072 * @param filename name of the binary
1073 * @param ... NULL-terminated list of arguments to the process
1075 * @return pointer to process structure of the new process, NULL on error
1078 struct GNUNET_OS_Process *
1079 GNUNET_OS_start_process (int pipe_control,
1080 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1081 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1082 const char *filename, ...)
1084 struct GNUNET_OS_Process *ret;
1087 va_start (ap, filename);
1088 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1097 * @param pipe_control should a pipe be used to send signals to the child?
1098 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1099 * must be NULL on platforms where dup is not supported
1100 * @param filename name of the binary
1101 * @param argv NULL-terminated list of arguments to the process
1102 * @return process ID of the new process, -1 on error
1104 struct GNUNET_OS_Process *
1105 GNUNET_OS_start_process_v (int pipe_control,
1106 const SOCKTYPE *lsocks,
1107 const char *filename,
1114 struct GNUNET_OS_Process *gnunet_proc = NULL;
1115 char *childpipename = NULL;
1124 if ( (GNUNET_YES == pipe_control) &&
1125 (GNUNET_OK != npipe_setup (&childpipename)) )
1132 while (-1 != (k = lsocks[i++]))
1133 GNUNET_array_append (lscp, ls, k);
1134 GNUNET_array_append (lscp, ls, -1);
1139 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1140 GNUNET_free_non_null (childpipename);
1141 GNUNET_array_grow (lscp, ls, 0);
1146 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1147 gnunet_proc->pid = ret;
1148 gnunet_proc->childpipename = childpipename;
1149 GNUNET_array_grow (lscp, ls, 0);
1152 if (NULL != childpipename)
1154 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1155 GNUNET_free (childpipename);
1159 /* read systemd documentation... */
1160 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1161 setenv ("LISTEN_PID", lpid, 1);
1164 while (-1 != lscp[i])
1167 while (-1 != lscp[j])
1173 GNUNET_assert (-1 != k);
1174 GNUNET_assert (0 == close (lscp[j]));
1182 /* Bury any existing FD, no matter what; they should all be closed
1183 * on exec anyway and the important onces have been dup'ed away */
1185 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1187 /* unset close-on-exec flag */
1188 flags = fcntl (tgt, F_GETFD);
1189 GNUNET_assert (flags >= 0);
1190 flags &= ~FD_CLOEXEC;
1192 (void) fcntl (tgt, F_SETFD, flags);
1196 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1197 setenv ("LISTEN_FDS", fds, 1);
1199 GNUNET_array_grow (lscp, ls, 0);
1200 execvp (filename, argv);
1201 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1204 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1205 char *childpipename = NULL;
1206 char **arg, **non_const_argv;
1207 unsigned int cmdlen;
1210 PROCESS_INFORMATION proc;
1212 struct GNUNET_OS_Process *gnunet_proc = NULL;
1213 char path[MAX_PATH + 1];
1214 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1215 char *env_block = NULL;
1217 DWORD pathbuf_len, alloc_len;
1222 char *non_const_filename;
1223 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1224 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1226 HANDLE lsocks_write;
1227 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1231 /* Search in prefix dir (hopefully - the directory from which
1232 * the current module was loaded), bindir and libdir, then in PATH
1234 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1235 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1236 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1238 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1241 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1244 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1247 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1248 GNUNET_free (self_prefix);
1249 GNUNET_free (bindir);
1250 GNUNET_free (libdir);
1252 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1253 if (alloc_len != pathbuf_len - 1)
1255 GNUNET_free (pathbuf);
1256 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1260 cmdlen = strlen (filename);
1261 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1262 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1264 GNUNET_asprintf (&non_const_filename, "%s", filename);
1266 /* Check that this is the full path. If it isn't, search. */
1267 if (non_const_filename[1] == ':')
1268 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1269 else if (!SearchPathA
1270 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1273 SetErrnoFromWinError (GetLastError ());
1274 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1275 non_const_filename);
1276 GNUNET_free (non_const_filename);
1277 GNUNET_free (pathbuf);
1280 GNUNET_free (pathbuf);
1281 GNUNET_free (non_const_filename);
1283 /* Count the number of arguments */
1284 arg = (char **) argv;
1291 /* Allocate a copy argv */
1292 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1294 /* Copy all argv strings */
1296 arg = (char **) argv;
1300 non_const_argv[argcount] = GNUNET_strdup (path);
1302 non_const_argv[argcount] = GNUNET_strdup (*arg);
1306 non_const_argv[argcount] = NULL;
1310 arg = non_const_argv;
1313 cmdlen = cmdlen + strlen (*arg) + 4;
1317 /* Allocate and create cmd */
1318 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1319 arg = non_const_argv;
1322 char arg_last_char = (*arg)[strlen (*arg) - 1];
1323 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1324 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1328 while (argcount > 0)
1329 GNUNET_free (non_const_argv[--argcount]);
1330 GNUNET_free (non_const_argv);
1332 memset (&start, 0, sizeof (start));
1333 start.cb = sizeof (start);
1335 if (GNUNET_YES == pipe_control)
1338 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1339 GNUNET_DISK_PERM_USER_READ |
1340 GNUNET_DISK_PERM_USER_WRITE);
1341 if (control_pipe == NULL)
1349 control_pipe = NULL;
1350 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1352 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1354 if (lsocks_pipe == NULL)
1358 GNUNET_DISK_pipe_close (lsocks_pipe);
1361 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1362 GNUNET_DISK_PIPE_END_WRITE);
1363 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1364 &lsocks_write, sizeof (HANDLE));
1365 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1366 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1367 &lsocks_read, sizeof (HANDLE));
1371 if (NULL != childpipename)
1373 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1375 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1376 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1377 GNUNET_free (childpipename);
1379 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1381 /*This will tell the child that we're going to send lsocks over the pipe*/
1382 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1383 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1385 our_env[env_off++] = NULL;
1386 env_block = CreateCustomEnvTable (our_env);
1388 GNUNET_free_non_null (our_env[--env_off]);
1389 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1390 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1392 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1393 env_block, NULL, &start, &proc))
1395 SetErrnoFromWinError (GetLastError ());
1396 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1397 if (NULL != control_pipe)
1398 GNUNET_DISK_file_close (control_pipe);
1400 GNUNET_DISK_pipe_close (lsocks_pipe);
1401 GNUNET_free (env_block);
1406 GNUNET_free (env_block);
1408 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1409 gnunet_proc->pid = proc.dwProcessId;
1410 gnunet_proc->handle = proc.hProcess;
1411 gnunet_proc->control_pipe = control_pipe;
1413 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1415 ResumeThread (proc.hThread);
1416 CloseHandle (proc.hThread);
1419 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1422 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1424 /* This is a replacement for "goto error" that doesn't use goto */
1429 uint64_t size, count, i;
1431 /* Tell the number of sockets */
1432 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1434 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1435 if (wrote != sizeof (count))
1437 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1440 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1442 WSAPROTOCOL_INFOA pi;
1443 /* Get a socket duplication info */
1444 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1446 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1447 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1450 /* Synchronous I/O is not nice, but we can't schedule this:
1451 * lsocks will be closed/freed by the caller soon, and until
1452 * the child creates a duplicate, closing a socket here will
1453 * close it for good.
1455 /* Send the size of the structure
1456 * (the child might be built with different headers...)
1459 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1460 if (wrote != sizeof (size))
1462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1465 /* Finally! Send the data */
1466 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1467 if (wrote != sizeof (pi))
1469 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1473 /* This will block us until the child makes a final read or closes
1474 * the pipe (hence no 'wrote' check), since we have to wait for it
1475 * to duplicate the last socket, before we return and start closing
1478 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1483 GNUNET_DISK_file_sync (lsocks_write_fd);
1484 GNUNET_DISK_pipe_close (lsocks_pipe);
1488 /* If we can't pass on the socket(s), the child will block forever,
1489 * better put it out of its misery.
1491 TerminateProcess (gnunet_proc->handle, 0);
1492 CloseHandle (gnunet_proc->handle);
1493 if (NULL != gnunet_proc->control_pipe)
1494 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1495 GNUNET_free (gnunet_proc);
1504 * Retrieve the status of a process
1505 * @param proc process ID
1506 * @param type status type
1507 * @param code return code/signal number
1508 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1511 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1512 enum GNUNET_OS_ProcessStatusType *type,
1513 unsigned long *code)
1519 GNUNET_assert (0 != proc);
1520 ret = waitpid (proc->pid, &status, WNOHANG);
1523 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1524 return GNUNET_SYSERR;
1528 *type = GNUNET_OS_PROCESS_RUNNING;
1532 if (proc->pid != ret)
1534 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1535 return GNUNET_SYSERR;
1537 if (WIFEXITED (status))
1539 *type = GNUNET_OS_PROCESS_EXITED;
1540 *code = WEXITSTATUS (status);
1542 else if (WIFSIGNALED (status))
1544 *type = GNUNET_OS_PROCESS_SIGNALED;
1545 *code = WTERMSIG (status);
1547 else if (WIFSTOPPED (status))
1549 *type = GNUNET_OS_PROCESS_SIGNALED;
1550 *code = WSTOPSIG (status);
1553 else if (WIFCONTINUED (status))
1555 *type = GNUNET_OS_PROCESS_RUNNING;
1561 *type = GNUNET_OS_PROCESS_UNKNOWN;
1566 DWORD c, error_code, ret;
1570 if (h == NULL || ret == 0)
1572 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1574 return GNUNET_SYSERR;
1577 h = GetCurrentProcess ();
1580 ret = GetExitCodeProcess (h, &c);
1581 error_code = GetLastError ();
1582 if (ret == 0 || error_code != NO_ERROR)
1584 SetErrnoFromWinError (error_code);
1585 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1586 return GNUNET_SYSERR;
1588 if (STILL_ACTIVE == c)
1590 *type = GNUNET_OS_PROCESS_RUNNING;
1594 *type = GNUNET_OS_PROCESS_EXITED;
1603 * Wait for a process
1604 * @param proc pointer to process structure
1605 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1608 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1612 pid_t pid = proc->pid;
1615 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1616 (EINTR == errno) ) ;
1619 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1620 return GNUNET_SYSERR;
1630 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1632 return GNUNET_SYSERR;
1635 h = GetCurrentProcess ();
1637 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1639 SetErrnoFromWinError (GetLastError ());
1640 ret = GNUNET_SYSERR;
1651 * Handle to a command.
1653 struct GNUNET_OS_CommandHandle
1659 struct GNUNET_OS_Process *eip;
1662 * Handle to the output pipe.
1664 struct GNUNET_DISK_PipeHandle *opipe;
1667 * Read-end of output pipe.
1669 const struct GNUNET_DISK_FileHandle *r;
1672 * Function to call on each line of output.
1674 GNUNET_OS_LineProcessor proc;
1677 * Closure for 'proc'.
1682 * Buffer for the output.
1687 * Task reading from pipe.
1689 GNUNET_SCHEDULER_TaskIdentifier rtask;
1694 struct GNUNET_TIME_Absolute timeout;
1697 * Current read offset in buf.
1704 * Stop/kill a command. Must ONLY be called either from
1705 * the callback after 'NULL' was passed for 'line' *OR*
1706 * from an independent task (not within the line processor).
1708 * @param cmd handle to the process
1711 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1714 if (cmd->proc != NULL)
1716 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1717 GNUNET_SCHEDULER_cancel (cmd->rtask);
1719 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1720 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1721 GNUNET_OS_process_close (cmd->eip);
1722 GNUNET_DISK_pipe_close (cmd->opipe);
1728 * Read from the process and call the line processor.
1730 * @param cls the 'struct GNUNET_OS_CommandHandle'
1731 * @param tc scheduler context
1734 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1736 struct GNUNET_OS_CommandHandle *cmd = cls;
1737 GNUNET_OS_LineProcessor proc;
1741 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1742 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1744 /* timeout, shutdown, etc. */
1747 proc (cmd->proc_cls, NULL);
1751 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1752 sizeof (cmd->buf) - cmd->off);
1755 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1757 cmd->buf[cmd->off] = '\0';
1758 cmd->proc (cmd->proc_cls, cmd->buf);
1762 proc (cmd->proc_cls, NULL);
1765 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1770 cmd->proc (cmd->proc_cls, cmd->buf);
1771 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1772 cmd->off -= (end + 1 - cmd->buf);
1773 end = memchr (cmd->buf, '\n', cmd->off);
1776 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1777 (cmd->timeout), cmd->r, &cmd_read, cmd);
1782 * Run the given command line and call the given function
1783 * for each line of the output.
1785 * @param proc function to call for each line of the output
1786 * @param proc_cls closure for proc
1787 * @param timeout when to time out
1788 * @param binary command to run
1789 * @param ... arguments to command
1790 * @return NULL on error
1792 struct GNUNET_OS_CommandHandle *
1793 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1794 struct GNUNET_TIME_Relative timeout, const char *binary,
1797 struct GNUNET_OS_CommandHandle *cmd;
1798 struct GNUNET_OS_Process *eip;
1799 struct GNUNET_DISK_PipeHandle *opipe;
1802 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1805 va_start (ap, binary);
1806 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1810 GNUNET_DISK_pipe_close (opipe);
1813 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1814 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1815 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1819 cmd->proc_cls = proc_cls;
1820 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1821 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1828 /* end of os_priority.c */