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__,
333 if (0 != (tc->reason &
334 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
336 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);
349 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
350 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
351 control_pipe, &parent_control_handler,
358 * Task that connects this process to its parent via pipe;
359 * essentially, the parent control handler will read signal numbers
360 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
361 * variable) and raise those signals.
363 * @param cls closure (unused)
364 * @param tc scheduler context (unused)
367 GNUNET_OS_install_parent_control_handler (void *cls,
369 GNUNET_SCHEDULER_TaskContext *tc)
372 struct GNUNET_DISK_FileHandle *control_pipe;
374 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
375 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
377 LOG (GNUNET_ERROR_TYPE_DEBUG,
378 "Not installing a handler because $%s is empty\n",
379 GNUNET_OS_CONTROL_PIPE);
380 putenv ("GNUNET_OS_CONTROL_PIPE=");
384 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
385 if (NULL == control_pipe)
387 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
388 putenv ("GNUNET_OS_CONTROL_PIPE=");
391 LOG (GNUNET_ERROR_TYPE_DEBUG,
392 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
393 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
394 &parent_control_handler, control_pipe);
395 putenv ("GNUNET_OS_CONTROL_PIPE=");
400 * Get process structure for current process
402 * The pointer it returns points to static memory location and must not be
405 * @return pointer to the process sturcutre for this process
407 struct GNUNET_OS_Process *
408 GNUNET_OS_process_current ()
411 current_process.pid = GetCurrentProcessId ();
412 current_process.handle = GetCurrentProcess ();
414 current_process.pid = 0;
416 return ¤t_process;
421 * Sends a signal to the process
423 * @param proc pointer to process structure
425 * @return 0 on success, -1 on error
428 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
435 if ( (NULL == proc->control_pipe) &&
436 (NULL != proc->childpipename) )
437 proc->control_pipe = npipe_open (proc->childpipename,
438 GNUNET_DISK_OPEN_WRITE);
440 if (NULL != proc->control_pipe)
442 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
443 if (ret == sizeof (csig))
446 /* pipe failed or non-existent, try other methods */
455 #if WINDOWS && !defined(__CYGWIN__)
456 if (0 == TerminateProcess (proc->handle, 0))
458 /* FIXME: set 'errno' */
463 return PLIBC_KILL (proc->pid, sig);
470 return PLIBC_KILL (proc->pid, sig);
476 * Get the pid of the process in question
478 * @param proc the process to get the pid of
480 * @return the current process id
483 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
490 * Cleans up process structure contents (OS-dependent) and deallocates it
492 * @param proc pointer to process structure
495 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
497 if (NULL != proc->control_pipe)
498 GNUNET_DISK_file_close (proc->control_pipe);
501 if (proc->handle != NULL)
502 CloseHandle (proc->handle);
504 if (NULL != proc->childpipename)
507 cleanup_npipe (proc->childpipename);
509 GNUNET_free (proc->childpipename);
516 #include "gnunet_signal_lib.h"
518 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
521 * Make seaspider happy.
523 #define DWORD_WINAPI DWORD WINAPI
526 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
527 * @param proc pointer to process structure
530 child_wait_thread (void *arg)
532 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
534 WaitForSingleObject (proc->handle, INFINITE);
536 if (w32_sigchld_handler)
537 w32_sigchld_handler ();
544 * Set process priority
546 * @param proc pointer to process structure
547 * @param prio priority value
548 * @return GNUNET_OK on success, GNUNET_SYSERR on error
551 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
552 enum GNUNET_SCHEDULER_Priority prio)
556 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
557 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
560 /* convert to MINGW/Unix values */
563 case GNUNET_SCHEDULER_PRIORITY_UI:
564 case GNUNET_SCHEDULER_PRIORITY_URGENT:
566 rprio = HIGH_PRIORITY_CLASS;
572 case GNUNET_SCHEDULER_PRIORITY_HIGH:
574 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
580 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
582 rprio = NORMAL_PRIORITY_CLASS;
588 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
590 rprio = BELOW_NORMAL_PRIORITY_CLASS;
596 case GNUNET_SCHEDULER_PRIORITY_IDLE:
598 rprio = IDLE_PRIORITY_CLASS;
605 return GNUNET_SYSERR;
608 /* Set process priority */
611 HANDLE h = proc->handle;
613 GNUNET_assert (h != NULL);
614 SetPriorityClass (h, rprio);
620 if ((0 == pid) || (pid == getpid ()))
623 int delta = rprio - have;
626 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
628 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
629 return GNUNET_SYSERR;
634 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
636 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
638 return GNUNET_SYSERR;
642 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
643 "Priority management not availabe for this platform\n");
650 CreateCustomEnvTable (char **vars)
652 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
653 size_t tablesize = 0;
654 size_t items_count = 0;
655 size_t n_found = 0, n_var;
662 win32_env_table = GetEnvironmentStringsA ();
663 if (win32_env_table == NULL)
665 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
667 index = GNUNET_malloc (sizeof (char *) * n_var);
668 for (c = 0; c < n_var; c++)
670 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
672 size_t len = strlen (ptr);
675 for (var_ptr = vars; *var_ptr; var_ptr++)
679 var_len = strlen (var);
680 if (strncmp (var, ptr, var_len) == 0)
684 tablesize += var_len + strlen (val) + 1;
689 tablesize += len + 1;
692 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
697 n_found += strlen (var) + strlen (val) + 1;
699 result = GNUNET_malloc (tablesize + n_found + 1);
700 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
702 size_t len = strlen (ptr);
705 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
709 var_len = strlen (var);
710 if (strncmp (var, ptr, var_len) == 0)
718 strcpy (result_ptr, ptr);
719 result_ptr += len + 1;
723 strcpy (result_ptr, var);
724 result_ptr += var_len;
725 strcpy (result_ptr, val);
726 result_ptr += strlen (val) + 1;
730 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
734 var_len = strlen (var);
737 strcpy (result_ptr, var);
738 result_ptr += var_len;
739 strcpy (result_ptr, val);
740 result_ptr += strlen (val) + 1;
743 FreeEnvironmentStrings (win32_env_table);
754 * @param pipe_control should a pipe be used to send signals to the child?
755 * @param pipe_stdin pipe to use to send input to child process (or NULL)
756 * @param pipe_stdout pipe to use to get output from child process (or NULL)
757 * @param filename name of the binary
758 * @param argv NULL-terminated array of arguments to the process
759 * @return pointer to process structure of the new process, NULL on error
761 struct GNUNET_OS_Process *
762 GNUNET_OS_start_process_vap (int pipe_control,
763 struct GNUNET_DISK_PipeHandle *pipe_stdin,
764 struct GNUNET_DISK_PipeHandle *pipe_stdout,
765 const char *filename,
769 char *childpipename = NULL;
770 struct GNUNET_OS_Process *gnunet_proc = NULL;
777 if ( (GNUNET_YES == pipe_control) &&
779 npipe_setup (&childpipename)) )
781 if (pipe_stdout != NULL)
783 GNUNET_assert (GNUNET_OK ==
784 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
786 GNUNET_DISK_PIPE_END_WRITE),
787 &fd_stdout_write, sizeof (int)));
788 GNUNET_assert (GNUNET_OK ==
789 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
790 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
791 &fd_stdout_read, sizeof (int)));
793 if (pipe_stdin != NULL)
795 GNUNET_assert (GNUNET_OK ==
796 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
797 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
798 &fd_stdin_read, sizeof (int)));
799 GNUNET_assert (GNUNET_OK ==
800 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
801 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
802 &fd_stdin_write, sizeof (int)));
808 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
809 GNUNET_free_non_null (childpipename);
814 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
815 gnunet_proc->pid = ret;
816 gnunet_proc->childpipename = childpipename;
819 if (NULL != childpipename)
821 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
822 GNUNET_free (childpipename);
824 if (pipe_stdout != NULL)
826 GNUNET_break (0 == close (fd_stdout_read));
827 if (-1 == dup2 (fd_stdout_write, 1))
828 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
829 GNUNET_break (0 == close (fd_stdout_write));
832 if (pipe_stdin != NULL)
835 GNUNET_break (0 == close (fd_stdin_write));
836 if (-1 == dup2 (fd_stdin_read, 0))
837 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
838 GNUNET_break (0 == close (fd_stdin_read));
840 execvp (filename, argv);
841 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
844 char *childpipename = NULL;
845 struct GNUNET_OS_Process *gnunet_proc = NULL;
850 PROCESS_INFORMATION proc;
853 HANDLE stdout_handle;
854 struct GNUNET_DISK_FileHandle *control_pipe;
856 char path[MAX_PATH + 1];
858 char *our_env[3] = { NULL, NULL, NULL };
859 char *env_block = NULL;
861 DWORD pathbuf_len, alloc_len;
866 char *non_const_filename;
867 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
869 /* Search in prefix dir (hopefully - the directory from which
870 * the current module was loaded), bindir and libdir, then in PATH
872 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
873 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
874 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
876 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
879 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
882 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
885 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
886 GNUNET_free (self_prefix);
887 GNUNET_free (bindir);
888 GNUNET_free (libdir);
890 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
891 GNUNET_assert (alloc_len == (pathbuf_len - 1));
893 cmdlen = strlen (filename);
894 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
895 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
897 GNUNET_asprintf (&non_const_filename, "%s", filename);
899 /* Check that this is the full path. If it isn't, search. */
900 if (non_const_filename[1] == ':')
901 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
902 else if (!SearchPathA
903 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
906 SetErrnoFromWinError (GetLastError ());
907 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
909 GNUNET_free (non_const_filename);
910 GNUNET_free (pathbuf);
913 GNUNET_free (pathbuf);
914 GNUNET_free (non_const_filename);
918 while (NULL != (arg = argv[argc++]))
921 cmdlen = cmdlen + strlen (path) + 4;
923 cmdlen = cmdlen + strlen (arg) + 4;
927 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
929 while (NULL != (arg = argv[argc++]))
931 /* This is to escape trailing slash */
932 char arg_lastchar = arg[strlen (arg) - 1];
934 idx += sprintf (idx, "\"%s%s\"%s", path,
935 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
937 idx += sprintf (idx, "\"%s%s\"%s", arg,
938 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
941 memset (&start, 0, sizeof (start));
942 start.cb = sizeof (start);
944 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
945 start.dwFlags |= STARTF_USESTDHANDLES;
947 if (pipe_stdin != NULL)
949 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
950 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
951 &stdin_handle, sizeof (HANDLE));
952 start.hStdInput = stdin_handle;
955 if (pipe_stdout != NULL)
957 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
959 GNUNET_DISK_PIPE_END_WRITE),
960 &stdout_handle, sizeof (HANDLE));
961 start.hStdOutput = stdout_handle;
963 if (GNUNET_YES == pipe_control)
966 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
967 GNUNET_DISK_PERM_USER_READ |
968 GNUNET_DISK_PERM_USER_WRITE);
969 if (control_pipe == NULL)
978 if (NULL != childpipename)
980 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
982 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
983 GNUNET_asprintf (&our_env[1], "%s", childpipename);
990 env_block = CreateCustomEnvTable (our_env);
991 GNUNET_free_non_null (our_env[0]);
992 GNUNET_free_non_null (our_env[1]);
994 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
995 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
997 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
998 env_block, NULL, &start, &proc))
1000 SetErrnoFromWinError (GetLastError ());
1001 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1002 GNUNET_free (env_block);
1007 GNUNET_free (env_block);
1009 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1010 gnunet_proc->pid = proc.dwProcessId;
1011 gnunet_proc->handle = proc.hProcess;
1012 gnunet_proc->control_pipe = control_pipe;
1014 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1016 ResumeThread (proc.hThread);
1017 CloseHandle (proc.hThread);
1029 * @param pipe_control should a pipe be used to send signals to the child?
1030 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1031 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1032 * @param filename name of the binary
1033 * @param va NULL-terminated list of arguments to the process
1034 * @return pointer to process structure of the new process, NULL on error
1036 struct GNUNET_OS_Process *
1037 GNUNET_OS_start_process_va (int pipe_control,
1038 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1039 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1040 const char *filename, va_list va)
1042 struct GNUNET_OS_Process *ret;
1049 while (NULL != va_arg (ap, char *))
1052 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1055 while (NULL != (argv[argc] = va_arg (ap, char *)))
1058 ret = GNUNET_OS_start_process_vap (pipe_control,
1072 * @param pipe_control should a pipe be used to send signals to the child?
1073 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1074 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1075 * @param filename name of the binary
1076 * @param ... NULL-terminated list of arguments to the process
1078 * @return pointer to process structure of the new process, NULL on error
1081 struct GNUNET_OS_Process *
1082 GNUNET_OS_start_process (int pipe_control,
1083 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1084 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1085 const char *filename, ...)
1087 struct GNUNET_OS_Process *ret;
1090 va_start (ap, filename);
1091 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1100 * @param pipe_control should a pipe be used to send signals to the child?
1101 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1102 * must be NULL on platforms where dup is not supported
1103 * @param filename name of the binary
1104 * @param argv NULL-terminated list of arguments to the process
1105 * @return process ID of the new process, -1 on error
1107 struct GNUNET_OS_Process *
1108 GNUNET_OS_start_process_v (int pipe_control,
1109 const SOCKTYPE *lsocks,
1110 const char *filename,
1117 struct GNUNET_OS_Process *gnunet_proc = NULL;
1118 char *childpipename = NULL;
1127 if ( (GNUNET_YES == pipe_control) &&
1128 (GNUNET_OK != npipe_setup (&childpipename)) )
1135 while (-1 != (k = lsocks[i++]))
1136 GNUNET_array_append (lscp, ls, k);
1137 GNUNET_array_append (lscp, ls, -1);
1142 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1143 GNUNET_free_non_null (childpipename);
1144 GNUNET_array_grow (lscp, ls, 0);
1149 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1150 gnunet_proc->pid = ret;
1151 gnunet_proc->childpipename = childpipename;
1152 GNUNET_array_grow (lscp, ls, 0);
1155 if (NULL != childpipename)
1157 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1158 GNUNET_free (childpipename);
1162 /* read systemd documentation... */
1163 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1164 setenv ("LISTEN_PID", lpid, 1);
1167 while (-1 != lscp[i])
1170 while (-1 != lscp[j])
1176 GNUNET_assert (-1 != k);
1177 GNUNET_assert (0 == close (lscp[j]));
1185 /* Bury any existing FD, no matter what; they should all be closed
1186 * on exec anyway and the important onces have been dup'ed away */
1188 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1190 /* unset close-on-exec flag */
1191 flags = fcntl (tgt, F_GETFD);
1192 GNUNET_assert (flags >= 0);
1193 flags &= ~FD_CLOEXEC;
1195 (void) fcntl (tgt, F_SETFD, flags);
1199 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1200 setenv ("LISTEN_FDS", fds, 1);
1202 GNUNET_array_grow (lscp, ls, 0);
1203 execvp (filename, argv);
1204 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1207 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1208 char *childpipename = NULL;
1209 char **arg, **non_const_argv;
1210 unsigned int cmdlen;
1213 PROCESS_INFORMATION proc;
1215 struct GNUNET_OS_Process *gnunet_proc = NULL;
1216 char path[MAX_PATH + 1];
1217 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1218 char *env_block = NULL;
1220 DWORD pathbuf_len, alloc_len;
1225 char *non_const_filename;
1226 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1227 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1229 HANDLE lsocks_write;
1230 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1234 /* Search in prefix dir (hopefully - the directory from which
1235 * the current module was loaded), bindir and libdir, then in PATH
1237 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1238 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1239 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1241 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1244 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1247 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1250 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1251 GNUNET_free (self_prefix);
1252 GNUNET_free (bindir);
1253 GNUNET_free (libdir);
1255 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1256 if (alloc_len != pathbuf_len - 1)
1258 GNUNET_free (pathbuf);
1259 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1263 cmdlen = strlen (filename);
1264 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1265 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1267 GNUNET_asprintf (&non_const_filename, "%s", filename);
1269 /* Check that this is the full path. If it isn't, search. */
1270 if (non_const_filename[1] == ':')
1271 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1272 else if (!SearchPathA
1273 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1276 SetErrnoFromWinError (GetLastError ());
1277 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1278 non_const_filename);
1279 GNUNET_free (non_const_filename);
1280 GNUNET_free (pathbuf);
1283 GNUNET_free (pathbuf);
1284 GNUNET_free (non_const_filename);
1286 /* Count the number of arguments */
1287 arg = (char **) argv;
1294 /* Allocate a copy argv */
1295 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1297 /* Copy all argv strings */
1299 arg = (char **) argv;
1303 non_const_argv[argcount] = GNUNET_strdup (path);
1305 non_const_argv[argcount] = GNUNET_strdup (*arg);
1309 non_const_argv[argcount] = NULL;
1313 arg = non_const_argv;
1316 cmdlen = cmdlen + strlen (*arg) + 4;
1320 /* Allocate and create cmd */
1321 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1322 arg = non_const_argv;
1325 char arg_last_char = (*arg)[strlen (*arg) - 1];
1326 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1327 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1331 while (argcount > 0)
1332 GNUNET_free (non_const_argv[--argcount]);
1333 GNUNET_free (non_const_argv);
1335 memset (&start, 0, sizeof (start));
1336 start.cb = sizeof (start);
1338 if (GNUNET_YES == pipe_control)
1341 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1342 GNUNET_DISK_PERM_USER_READ |
1343 GNUNET_DISK_PERM_USER_WRITE);
1344 if (control_pipe == NULL)
1352 control_pipe = NULL;
1353 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1355 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1357 if (lsocks_pipe == NULL)
1361 GNUNET_DISK_pipe_close (lsocks_pipe);
1364 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1365 GNUNET_DISK_PIPE_END_WRITE);
1366 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1367 &lsocks_write, sizeof (HANDLE));
1368 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1369 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1370 &lsocks_read, sizeof (HANDLE));
1374 if (NULL != childpipename)
1376 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1378 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1379 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1380 GNUNET_free (childpipename);
1382 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1384 /*This will tell the child that we're going to send lsocks over the pipe*/
1385 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1386 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1388 our_env[env_off++] = NULL;
1389 env_block = CreateCustomEnvTable (our_env);
1391 GNUNET_free_non_null (our_env[--env_off]);
1392 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1393 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1395 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1396 env_block, NULL, &start, &proc))
1398 SetErrnoFromWinError (GetLastError ());
1399 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1400 if (NULL != control_pipe)
1401 GNUNET_DISK_file_close (control_pipe);
1403 GNUNET_DISK_pipe_close (lsocks_pipe);
1404 GNUNET_free (env_block);
1409 GNUNET_free (env_block);
1411 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1412 gnunet_proc->pid = proc.dwProcessId;
1413 gnunet_proc->handle = proc.hProcess;
1414 gnunet_proc->control_pipe = control_pipe;
1416 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1418 ResumeThread (proc.hThread);
1419 CloseHandle (proc.hThread);
1422 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1425 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1427 /* This is a replacement for "goto error" that doesn't use goto */
1432 uint64_t size, count, i;
1434 /* Tell the number of sockets */
1435 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1437 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1438 if (wrote != sizeof (count))
1440 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1443 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1445 WSAPROTOCOL_INFOA pi;
1446 /* Get a socket duplication info */
1447 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1449 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1450 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1453 /* Synchronous I/O is not nice, but we can't schedule this:
1454 * lsocks will be closed/freed by the caller soon, and until
1455 * the child creates a duplicate, closing a socket here will
1456 * close it for good.
1458 /* Send the size of the structure
1459 * (the child might be built with different headers...)
1462 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1463 if (wrote != sizeof (size))
1465 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1468 /* Finally! Send the data */
1469 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1470 if (wrote != sizeof (pi))
1472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1476 /* This will block us until the child makes a final read or closes
1477 * the pipe (hence no 'wrote' check), since we have to wait for it
1478 * to duplicate the last socket, before we return and start closing
1481 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1486 GNUNET_DISK_file_sync (lsocks_write_fd);
1487 GNUNET_DISK_pipe_close (lsocks_pipe);
1491 /* If we can't pass on the socket(s), the child will block forever,
1492 * better put it out of its misery.
1494 TerminateProcess (gnunet_proc->handle, 0);
1495 CloseHandle (gnunet_proc->handle);
1496 if (NULL != gnunet_proc->control_pipe)
1497 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1498 GNUNET_free (gnunet_proc);
1507 * Retrieve the status of a process, waiting on him if dead.
1508 * Nonblocking version.
1510 * @param proc process ID
1511 * @param type status type
1512 * @param code return code/signal number
1513 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1516 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1517 enum GNUNET_OS_ProcessStatusType *type,
1518 unsigned long *code)
1524 GNUNET_assert (0 != proc);
1525 ret = waitpid (proc->pid, &status, WNOHANG);
1528 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1529 return GNUNET_SYSERR;
1533 *type = GNUNET_OS_PROCESS_RUNNING;
1537 if (proc->pid != ret)
1539 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1540 return GNUNET_SYSERR;
1542 if (WIFEXITED (status))
1544 *type = GNUNET_OS_PROCESS_EXITED;
1545 *code = WEXITSTATUS (status);
1547 else if (WIFSIGNALED (status))
1549 *type = GNUNET_OS_PROCESS_SIGNALED;
1550 *code = WTERMSIG (status);
1552 else if (WIFSTOPPED (status))
1554 *type = GNUNET_OS_PROCESS_SIGNALED;
1555 *code = WSTOPSIG (status);
1558 else if (WIFCONTINUED (status))
1560 *type = GNUNET_OS_PROCESS_RUNNING;
1566 *type = GNUNET_OS_PROCESS_UNKNOWN;
1571 DWORD c, error_code, ret;
1575 if (h == NULL || ret == 0)
1577 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1579 return GNUNET_SYSERR;
1582 h = GetCurrentProcess ();
1585 ret = GetExitCodeProcess (h, &c);
1586 error_code = GetLastError ();
1587 if (ret == 0 || error_code != NO_ERROR)
1589 SetErrnoFromWinError (error_code);
1590 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1591 return GNUNET_SYSERR;
1593 if (STILL_ACTIVE == c)
1595 *type = GNUNET_OS_PROCESS_RUNNING;
1599 *type = GNUNET_OS_PROCESS_EXITED;
1608 * Wait for a process
1609 * @param proc pointer to process structure
1610 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1613 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1617 pid_t pid = proc->pid;
1620 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1621 (EINTR == errno) ) ;
1624 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1625 return GNUNET_SYSERR;
1635 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1637 return GNUNET_SYSERR;
1640 h = GetCurrentProcess ();
1642 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1644 SetErrnoFromWinError (GetLastError ());
1645 ret = GNUNET_SYSERR;
1656 * Handle to a command.
1658 struct GNUNET_OS_CommandHandle
1664 struct GNUNET_OS_Process *eip;
1667 * Handle to the output pipe.
1669 struct GNUNET_DISK_PipeHandle *opipe;
1672 * Read-end of output pipe.
1674 const struct GNUNET_DISK_FileHandle *r;
1677 * Function to call on each line of output.
1679 GNUNET_OS_LineProcessor proc;
1682 * Closure for 'proc'.
1687 * Buffer for the output.
1692 * Task reading from pipe.
1694 GNUNET_SCHEDULER_TaskIdentifier rtask;
1699 struct GNUNET_TIME_Absolute timeout;
1702 * Current read offset in buf.
1709 * Stop/kill a command. Must ONLY be called either from
1710 * the callback after 'NULL' was passed for 'line' *OR*
1711 * from an independent task (not within the line processor).
1713 * @param cmd handle to the process
1716 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1719 if (cmd->proc != NULL)
1721 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1722 GNUNET_SCHEDULER_cancel (cmd->rtask);
1724 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1725 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1726 GNUNET_OS_process_close (cmd->eip);
1727 GNUNET_DISK_pipe_close (cmd->opipe);
1733 * Read from the process and call the line processor.
1735 * @param cls the 'struct GNUNET_OS_CommandHandle'
1736 * @param tc scheduler context
1739 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1741 struct GNUNET_OS_CommandHandle *cmd = cls;
1742 GNUNET_OS_LineProcessor proc;
1746 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1747 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1749 /* timeout, shutdown, etc. */
1752 proc (cmd->proc_cls, NULL);
1756 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1757 sizeof (cmd->buf) - cmd->off);
1760 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1762 cmd->buf[cmd->off] = '\0';
1763 cmd->proc (cmd->proc_cls, cmd->buf);
1767 proc (cmd->proc_cls, NULL);
1770 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1775 cmd->proc (cmd->proc_cls, cmd->buf);
1776 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1777 cmd->off -= (end + 1 - cmd->buf);
1778 end = memchr (cmd->buf, '\n', cmd->off);
1781 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1782 (cmd->timeout), cmd->r, &cmd_read, cmd);
1787 * Run the given command line and call the given function
1788 * for each line of the output.
1790 * @param proc function to call for each line of the output
1791 * @param proc_cls closure for proc
1792 * @param timeout when to time out
1793 * @param binary command to run
1794 * @param ... arguments to command
1795 * @return NULL on error
1797 struct GNUNET_OS_CommandHandle *
1798 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1799 struct GNUNET_TIME_Relative timeout, const char *binary,
1802 struct GNUNET_OS_CommandHandle *cmd;
1803 struct GNUNET_OS_Process *eip;
1804 struct GNUNET_DISK_PipeHandle *opipe;
1807 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1810 va_start (ap, binary);
1811 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1815 GNUNET_DISK_pipe_close (opipe);
1818 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1819 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1820 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1824 cmd->proc_cls = proc_cls;
1825 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1826 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1833 /* end of os_priority.c */