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)
441 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
442 if (ret == sizeof (csig))
445 /* pipe failed or non-existent, try other methods */
454 #if WINDOWS && !defined(__CYGWIN__)
455 if (0 == TerminateProcess (proc->handle, 0))
457 /* FIXME: set 'errno' */
462 return PLIBC_KILL (proc->pid, sig);
469 return PLIBC_KILL (proc->pid, sig);
475 * Get the pid of the process in question
477 * @param proc the process to get the pid of
479 * @return the current process id
482 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
489 GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
491 if (NULL != proc->control_pipe)
492 GNUNET_DISK_file_close (proc->control_pipe);
495 if (proc->handle != NULL)
496 CloseHandle (proc->handle);
498 if (NULL != proc->childpipename)
501 cleanup_npipe (proc->childpipename);
503 GNUNET_free (proc->childpipename);
510 #include "gnunet_signal_lib.h"
512 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
515 * Make seaspider happy.
517 #define DWORD_WINAPI DWORD WINAPI
520 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
521 * @param proc pointer to process structure
524 child_wait_thread (void *arg)
526 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
528 WaitForSingleObject (proc->handle, INFINITE);
530 if (w32_sigchld_handler)
531 w32_sigchld_handler ();
538 * Set process priority
540 * @param proc pointer to process structure
541 * @param prio priority value
542 * @return GNUNET_OK on success, GNUNET_SYSERR on error
545 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
546 enum GNUNET_SCHEDULER_Priority prio)
550 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
551 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
554 /* convert to MINGW/Unix values */
557 case GNUNET_SCHEDULER_PRIORITY_UI:
558 case GNUNET_SCHEDULER_PRIORITY_URGENT:
560 rprio = HIGH_PRIORITY_CLASS;
566 case GNUNET_SCHEDULER_PRIORITY_HIGH:
568 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
574 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
576 rprio = NORMAL_PRIORITY_CLASS;
582 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
584 rprio = BELOW_NORMAL_PRIORITY_CLASS;
590 case GNUNET_SCHEDULER_PRIORITY_IDLE:
592 rprio = IDLE_PRIORITY_CLASS;
599 return GNUNET_SYSERR;
602 /* Set process priority */
605 HANDLE h = proc->handle;
607 GNUNET_assert (h != NULL);
608 SetPriorityClass (h, rprio);
614 if ((0 == pid) || (pid == getpid ()))
617 int delta = rprio - have;
620 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
622 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
623 return GNUNET_SYSERR;
628 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
630 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
632 return GNUNET_SYSERR;
636 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
637 "Priority management not availabe for this platform\n");
644 CreateCustomEnvTable (char **vars)
646 char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
647 size_t tablesize = 0;
648 size_t items_count = 0;
649 size_t n_found = 0, n_var;
656 win32_env_table = GetEnvironmentStringsA ();
657 if (win32_env_table == NULL)
659 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
661 index = GNUNET_malloc (sizeof (char *) * n_var);
662 for (c = 0; c < n_var; c++)
664 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
666 size_t len = strlen (ptr);
669 for (var_ptr = vars; *var_ptr; var_ptr++)
673 var_len = strlen (var);
674 if (strncmp (var, ptr, var_len) == 0)
678 tablesize += var_len + strlen (val) + 1;
683 tablesize += len + 1;
686 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
691 n_found += strlen (var) + strlen (val) + 1;
693 result = GNUNET_malloc (tablesize + n_found + 1);
694 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
696 size_t len = strlen (ptr);
699 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
703 var_len = strlen (var);
704 if (strncmp (var, ptr, var_len) == 0)
712 strcpy (result_ptr, ptr);
713 result_ptr += len + 1;
717 strcpy (result_ptr, var);
718 result_ptr += var_len;
719 strcpy (result_ptr, val);
720 result_ptr += strlen (val) + 1;
724 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
728 var_len = strlen (var);
731 strcpy (result_ptr, var);
732 result_ptr += var_len;
733 strcpy (result_ptr, val);
734 result_ptr += strlen (val) + 1;
737 FreeEnvironmentStrings (win32_env_table);
748 * @param pipe_control should a pipe be used to send signals to the child?
749 * @param pipe_stdin pipe to use to send input to child process (or NULL)
750 * @param pipe_stdout pipe to use to get output from child process (or NULL)
751 * @param filename name of the binary
752 * @param argv NULL-terminated array of arguments to the process
753 * @return pointer to process structure of the new process, NULL on error
755 struct GNUNET_OS_Process *
756 GNUNET_OS_start_process_vap (int pipe_control,
757 struct GNUNET_DISK_PipeHandle *pipe_stdin,
758 struct GNUNET_DISK_PipeHandle *pipe_stdout,
759 const char *filename,
763 char *childpipename = NULL;
764 struct GNUNET_OS_Process *gnunet_proc = NULL;
771 if ( (GNUNET_YES == pipe_control) &&
773 npipe_setup (&childpipename)) )
775 if (pipe_stdout != NULL)
777 GNUNET_assert (GNUNET_OK ==
778 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
780 GNUNET_DISK_PIPE_END_WRITE),
781 &fd_stdout_write, sizeof (int)));
782 GNUNET_assert (GNUNET_OK ==
783 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
784 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
785 &fd_stdout_read, sizeof (int)));
787 if (pipe_stdin != NULL)
789 GNUNET_assert (GNUNET_OK ==
790 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
791 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
792 &fd_stdin_read, sizeof (int)));
793 GNUNET_assert (GNUNET_OK ==
794 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
795 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
796 &fd_stdin_write, sizeof (int)));
802 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
803 GNUNET_free_non_null (childpipename);
808 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
809 gnunet_proc->pid = ret;
810 gnunet_proc->childpipename = childpipename;
813 if (NULL != childpipename)
815 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
816 GNUNET_free (childpipename);
818 if (pipe_stdout != NULL)
820 GNUNET_break (0 == close (fd_stdout_read));
821 if (-1 == dup2 (fd_stdout_write, 1))
822 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
823 GNUNET_break (0 == close (fd_stdout_write));
826 if (pipe_stdin != NULL)
829 GNUNET_break (0 == close (fd_stdin_write));
830 if (-1 == dup2 (fd_stdin_read, 0))
831 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
832 GNUNET_break (0 == close (fd_stdin_read));
834 execvp (filename, argv);
835 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
838 char *childpipename = NULL;
839 struct GNUNET_OS_Process *gnunet_proc = NULL;
844 PROCESS_INFORMATION proc;
847 HANDLE stdout_handle;
848 struct GNUNET_DISK_FileHandle *control_pipe;
850 char path[MAX_PATH + 1];
852 char *our_env[3] = { NULL, NULL, NULL };
853 char *env_block = NULL;
855 DWORD pathbuf_len, alloc_len;
860 char *non_const_filename;
861 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
863 /* Search in prefix dir (hopefully - the directory from which
864 * the current module was loaded), bindir and libdir, then in PATH
866 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
867 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
868 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
870 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
873 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
876 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
879 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
880 GNUNET_free (self_prefix);
881 GNUNET_free (bindir);
882 GNUNET_free (libdir);
884 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
885 GNUNET_assert (alloc_len == (pathbuf_len - 1));
887 cmdlen = strlen (filename);
888 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
889 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
891 GNUNET_asprintf (&non_const_filename, "%s", filename);
893 /* Check that this is the full path. If it isn't, search. */
894 if (non_const_filename[1] == ':')
895 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
896 else if (!SearchPathA
897 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
900 SetErrnoFromWinError (GetLastError ());
901 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
903 GNUNET_free (non_const_filename);
904 GNUNET_free (pathbuf);
907 GNUNET_free (pathbuf);
908 GNUNET_free (non_const_filename);
912 while (NULL != (arg = argv[argc++]))
915 cmdlen = cmdlen + strlen (path) + 4;
917 cmdlen = cmdlen + strlen (arg) + 4;
921 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
923 while (NULL != (arg = argv[argc++]))
925 /* This is to escape trailing slash */
926 char arg_lastchar = arg[strlen (arg) - 1];
928 idx += sprintf (idx, "\"%s%s\"%s", path,
929 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
931 idx += sprintf (idx, "\"%s%s\"%s", arg,
932 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
935 memset (&start, 0, sizeof (start));
936 start.cb = sizeof (start);
938 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
939 start.dwFlags |= STARTF_USESTDHANDLES;
941 if (pipe_stdin != NULL)
943 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
944 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
945 &stdin_handle, sizeof (HANDLE));
946 start.hStdInput = stdin_handle;
949 if (pipe_stdout != NULL)
951 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
953 GNUNET_DISK_PIPE_END_WRITE),
954 &stdout_handle, sizeof (HANDLE));
955 start.hStdOutput = stdout_handle;
957 if (GNUNET_YES == pipe_control)
960 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
961 GNUNET_DISK_PERM_USER_READ |
962 GNUNET_DISK_PERM_USER_WRITE);
963 if (control_pipe == NULL)
972 if (NULL != childpipename)
974 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
976 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
977 GNUNET_asprintf (&our_env[1], "%s", childpipename);
984 env_block = CreateCustomEnvTable (our_env);
985 GNUNET_free_non_null (our_env[0]);
986 GNUNET_free_non_null (our_env[1]);
988 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
989 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
991 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
992 env_block, NULL, &start, &proc))
994 SetErrnoFromWinError (GetLastError ());
995 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
996 GNUNET_free (env_block);
1001 GNUNET_free (env_block);
1003 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1004 gnunet_proc->pid = proc.dwProcessId;
1005 gnunet_proc->handle = proc.hProcess;
1006 gnunet_proc->control_pipe = control_pipe;
1008 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1010 ResumeThread (proc.hThread);
1011 CloseHandle (proc.hThread);
1023 * @param pipe_control should a pipe be used to send signals to the child?
1024 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1025 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1026 * @param filename name of the binary
1027 * @param va NULL-terminated list of arguments to the process
1028 * @return pointer to process structure of the new process, NULL on error
1030 struct GNUNET_OS_Process *
1031 GNUNET_OS_start_process_va (int pipe_control,
1032 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1033 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1034 const char *filename, va_list va)
1036 struct GNUNET_OS_Process *ret;
1043 while (NULL != va_arg (ap, char *))
1046 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1049 while (NULL != (argv[argc] = va_arg (ap, char *)))
1052 ret = GNUNET_OS_start_process_vap (pipe_control,
1066 * @param pipe_control should a pipe be used to send signals to the child?
1067 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1068 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1069 * @param filename name of the binary
1070 * @param ... NULL-terminated list of arguments to the process
1072 * @return pointer to process structure of the new process, NULL on error
1075 struct GNUNET_OS_Process *
1076 GNUNET_OS_start_process (int pipe_control,
1077 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1078 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1079 const char *filename, ...)
1081 struct GNUNET_OS_Process *ret;
1084 va_start (ap, filename);
1085 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1094 * @param pipe_control should a pipe be used to send signals to the child?
1095 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1096 * must be NULL on platforms where dup is not supported
1097 * @param filename name of the binary
1098 * @param argv NULL-terminated list of arguments to the process
1099 * @return process ID of the new process, -1 on error
1101 struct GNUNET_OS_Process *
1102 GNUNET_OS_start_process_v (int pipe_control,
1103 const SOCKTYPE *lsocks,
1104 const char *filename,
1111 struct GNUNET_OS_Process *gnunet_proc = NULL;
1112 char *childpipename = NULL;
1121 if ( (GNUNET_YES == pipe_control) &&
1122 (GNUNET_OK != npipe_setup (&childpipename)) )
1129 while (-1 != (k = lsocks[i++]))
1130 GNUNET_array_append (lscp, ls, k);
1131 GNUNET_array_append (lscp, ls, -1);
1136 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1137 GNUNET_free_non_null (childpipename);
1138 GNUNET_array_grow (lscp, ls, 0);
1143 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1144 gnunet_proc->pid = ret;
1145 gnunet_proc->childpipename = childpipename;
1146 GNUNET_array_grow (lscp, ls, 0);
1149 if (NULL != childpipename)
1151 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1152 GNUNET_free (childpipename);
1156 /* read systemd documentation... */
1157 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1158 setenv ("LISTEN_PID", lpid, 1);
1161 while (-1 != lscp[i])
1164 while (-1 != lscp[j])
1170 GNUNET_assert (-1 != k);
1171 GNUNET_assert (0 == close (lscp[j]));
1179 /* Bury any existing FD, no matter what; they should all be closed
1180 * on exec anyway and the important onces have been dup'ed away */
1182 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1184 /* unset close-on-exec flag */
1185 flags = fcntl (tgt, F_GETFD);
1186 GNUNET_assert (flags >= 0);
1187 flags &= ~FD_CLOEXEC;
1189 (void) fcntl (tgt, F_SETFD, flags);
1193 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1194 setenv ("LISTEN_FDS", fds, 1);
1196 GNUNET_array_grow (lscp, ls, 0);
1197 execvp (filename, argv);
1198 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1201 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1202 char *childpipename = NULL;
1203 char **arg, **non_const_argv;
1204 unsigned int cmdlen;
1207 PROCESS_INFORMATION proc;
1209 struct GNUNET_OS_Process *gnunet_proc = NULL;
1210 char path[MAX_PATH + 1];
1211 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1212 char *env_block = NULL;
1214 DWORD pathbuf_len, alloc_len;
1219 char *non_const_filename;
1220 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1221 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1223 HANDLE lsocks_write;
1224 wchar_t wpath[MAX_PATH + 1], wcmd[32768];
1228 /* Search in prefix dir (hopefully - the directory from which
1229 * the current module was loaded), bindir and libdir, then in PATH
1231 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1232 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1233 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1235 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1238 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1241 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1244 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1245 GNUNET_free (self_prefix);
1246 GNUNET_free (bindir);
1247 GNUNET_free (libdir);
1249 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1250 if (alloc_len != pathbuf_len - 1)
1252 GNUNET_free (pathbuf);
1253 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1257 cmdlen = strlen (filename);
1258 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1259 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1261 GNUNET_asprintf (&non_const_filename, "%s", filename);
1263 /* Check that this is the full path. If it isn't, search. */
1264 if (non_const_filename[1] == ':')
1265 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1266 else if (!SearchPathA
1267 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1270 SetErrnoFromWinError (GetLastError ());
1271 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1272 non_const_filename);
1273 GNUNET_free (non_const_filename);
1274 GNUNET_free (pathbuf);
1277 GNUNET_free (pathbuf);
1278 GNUNET_free (non_const_filename);
1280 /* Count the number of arguments */
1281 arg = (char **) argv;
1288 /* Allocate a copy argv */
1289 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1291 /* Copy all argv strings */
1293 arg = (char **) argv;
1297 non_const_argv[argcount] = GNUNET_strdup (path);
1299 non_const_argv[argcount] = GNUNET_strdup (*arg);
1303 non_const_argv[argcount] = NULL;
1307 arg = non_const_argv;
1310 cmdlen = cmdlen + strlen (*arg) + 4;
1314 /* Allocate and create cmd */
1315 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1316 arg = non_const_argv;
1319 char arg_last_char = (*arg)[strlen (*arg) - 1];
1320 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1321 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1325 while (argcount > 0)
1326 GNUNET_free (non_const_argv[--argcount]);
1327 GNUNET_free (non_const_argv);
1329 memset (&start, 0, sizeof (start));
1330 start.cb = sizeof (start);
1332 if (GNUNET_YES == pipe_control)
1335 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1336 GNUNET_DISK_PERM_USER_READ |
1337 GNUNET_DISK_PERM_USER_WRITE);
1338 if (control_pipe == NULL)
1346 control_pipe = NULL;
1347 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1349 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1351 if (lsocks_pipe == NULL)
1355 GNUNET_DISK_pipe_close (lsocks_pipe);
1358 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1359 GNUNET_DISK_PIPE_END_WRITE);
1360 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1361 &lsocks_write, sizeof (HANDLE));
1362 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1363 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1364 &lsocks_read, sizeof (HANDLE));
1368 if (NULL != childpipename)
1370 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1372 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1373 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1374 GNUNET_free (childpipename);
1376 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1378 /*This will tell the child that we're going to send lsocks over the pipe*/
1379 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1380 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1382 our_env[env_off++] = NULL;
1383 env_block = CreateCustomEnvTable (our_env);
1385 GNUNET_free_non_null (our_env[--env_off]);
1386 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
1387 || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
1389 (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
1390 env_block, NULL, &start, &proc))
1392 SetErrnoFromWinError (GetLastError ());
1393 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1394 if (NULL != control_pipe)
1395 GNUNET_DISK_file_close (control_pipe);
1397 GNUNET_DISK_pipe_close (lsocks_pipe);
1398 GNUNET_free (env_block);
1403 GNUNET_free (env_block);
1405 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1406 gnunet_proc->pid = proc.dwProcessId;
1407 gnunet_proc->handle = proc.hProcess;
1408 gnunet_proc->control_pipe = control_pipe;
1410 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1412 ResumeThread (proc.hThread);
1413 CloseHandle (proc.hThread);
1416 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1419 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1421 /* This is a replacement for "goto error" that doesn't use goto */
1426 uint64_t size, count, i;
1428 /* Tell the number of sockets */
1429 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1431 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1432 if (wrote != sizeof (count))
1434 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1437 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1439 WSAPROTOCOL_INFOA pi;
1440 /* Get a socket duplication info */
1441 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1444 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1447 /* Synchronous I/O is not nice, but we can't schedule this:
1448 * lsocks will be closed/freed by the caller soon, and until
1449 * the child creates a duplicate, closing a socket here will
1450 * close it for good.
1452 /* Send the size of the structure
1453 * (the child might be built with different headers...)
1456 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1457 if (wrote != sizeof (size))
1459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1462 /* Finally! Send the data */
1463 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1464 if (wrote != sizeof (pi))
1466 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1470 /* This will block us until the child makes a final read or closes
1471 * the pipe (hence no 'wrote' check), since we have to wait for it
1472 * to duplicate the last socket, before we return and start closing
1475 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1480 GNUNET_DISK_file_sync (lsocks_write_fd);
1481 GNUNET_DISK_pipe_close (lsocks_pipe);
1485 /* If we can't pass on the socket(s), the child will block forever,
1486 * better put it out of its misery.
1488 TerminateProcess (gnunet_proc->handle, 0);
1489 CloseHandle (gnunet_proc->handle);
1490 if (NULL != gnunet_proc->control_pipe)
1491 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1492 GNUNET_free (gnunet_proc);
1501 * Retrieve the status of a process
1502 * @param proc process ID
1503 * @param type status type
1504 * @param code return code/signal number
1505 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1508 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1509 enum GNUNET_OS_ProcessStatusType *type,
1510 unsigned long *code)
1516 GNUNET_assert (0 != proc);
1517 ret = waitpid (proc->pid, &status, WNOHANG);
1520 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1521 return GNUNET_SYSERR;
1525 *type = GNUNET_OS_PROCESS_RUNNING;
1529 if (proc->pid != ret)
1531 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1532 return GNUNET_SYSERR;
1534 if (WIFEXITED (status))
1536 *type = GNUNET_OS_PROCESS_EXITED;
1537 *code = WEXITSTATUS (status);
1539 else if (WIFSIGNALED (status))
1541 *type = GNUNET_OS_PROCESS_SIGNALED;
1542 *code = WTERMSIG (status);
1544 else if (WIFSTOPPED (status))
1546 *type = GNUNET_OS_PROCESS_SIGNALED;
1547 *code = WSTOPSIG (status);
1550 else if (WIFCONTINUED (status))
1552 *type = GNUNET_OS_PROCESS_RUNNING;
1558 *type = GNUNET_OS_PROCESS_UNKNOWN;
1563 DWORD c, error_code, ret;
1567 if (h == NULL || ret == 0)
1569 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1571 return GNUNET_SYSERR;
1574 h = GetCurrentProcess ();
1577 ret = GetExitCodeProcess (h, &c);
1578 error_code = GetLastError ();
1579 if (ret == 0 || error_code != NO_ERROR)
1581 SetErrnoFromWinError (error_code);
1582 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1583 return GNUNET_SYSERR;
1585 if (STILL_ACTIVE == c)
1587 *type = GNUNET_OS_PROCESS_RUNNING;
1591 *type = GNUNET_OS_PROCESS_EXITED;
1600 * Wait for a process
1601 * @param proc pointer to process structure
1602 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1605 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1609 pid_t pid = proc->pid;
1612 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1613 (EINTR == errno) ) ;
1616 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1617 return GNUNET_SYSERR;
1627 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1629 return GNUNET_SYSERR;
1632 h = GetCurrentProcess ();
1634 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1636 SetErrnoFromWinError (GetLastError ());
1637 ret = GNUNET_SYSERR;
1648 * Handle to a command.
1650 struct GNUNET_OS_CommandHandle
1656 struct GNUNET_OS_Process *eip;
1659 * Handle to the output pipe.
1661 struct GNUNET_DISK_PipeHandle *opipe;
1664 * Read-end of output pipe.
1666 const struct GNUNET_DISK_FileHandle *r;
1669 * Function to call on each line of output.
1671 GNUNET_OS_LineProcessor proc;
1674 * Closure for 'proc'.
1679 * Buffer for the output.
1684 * Task reading from pipe.
1686 GNUNET_SCHEDULER_TaskIdentifier rtask;
1691 struct GNUNET_TIME_Absolute timeout;
1694 * Current read offset in buf.
1701 * Stop/kill a command. Must ONLY be called either from
1702 * the callback after 'NULL' was passed for 'line' *OR*
1703 * from an independent task (not within the line processor).
1705 * @param cmd handle to the process
1708 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1711 if (cmd->proc != NULL)
1713 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1714 GNUNET_SCHEDULER_cancel (cmd->rtask);
1716 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1717 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1718 GNUNET_OS_process_close (cmd->eip);
1719 GNUNET_DISK_pipe_close (cmd->opipe);
1725 * Read from the process and call the line processor.
1727 * @param cls the 'struct GNUNET_OS_CommandHandle'
1728 * @param tc scheduler context
1731 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1733 struct GNUNET_OS_CommandHandle *cmd = cls;
1734 GNUNET_OS_LineProcessor proc;
1738 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1739 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1741 /* timeout, shutdown, etc. */
1744 proc (cmd->proc_cls, NULL);
1748 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1749 sizeof (cmd->buf) - cmd->off);
1752 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1754 cmd->buf[cmd->off] = '\0';
1755 cmd->proc (cmd->proc_cls, cmd->buf);
1759 proc (cmd->proc_cls, NULL);
1762 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1767 cmd->proc (cmd->proc_cls, cmd->buf);
1768 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1769 cmd->off -= (end + 1 - cmd->buf);
1770 end = memchr (cmd->buf, '\n', cmd->off);
1773 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1774 (cmd->timeout), cmd->r, &cmd_read, cmd);
1779 * Run the given command line and call the given function
1780 * for each line of the output.
1782 * @param proc function to call for each line of the output
1783 * @param proc_cls closure for proc
1784 * @param timeout when to time out
1785 * @param binary command to run
1786 * @param ... arguments to command
1787 * @return NULL on error
1789 struct GNUNET_OS_CommandHandle *
1790 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1791 struct GNUNET_TIME_Relative timeout, const char *binary,
1794 struct GNUNET_OS_CommandHandle *cmd;
1795 struct GNUNET_OS_Process *eip;
1796 struct GNUNET_DISK_PipeHandle *opipe;
1799 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1802 va_start (ap, binary);
1803 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1807 GNUNET_DISK_pipe_close (opipe);
1810 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1811 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1812 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1816 cmd->proc_cls = proc_cls;
1817 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1818 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1825 /* end of os_priority.c */