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_destroy (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;
657 size_t tablesize = 0;
658 size_t items_count = 0;
667 win32_env_table = GetEnvironmentStringsA ();
668 if (win32_env_table == NULL)
670 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
672 index = GNUNET_malloc (sizeof (char *) * n_var);
673 for (c = 0; c < n_var; c++)
675 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
677 size_t len = strlen (ptr);
680 for (var_ptr = vars; *var_ptr; var_ptr++)
684 var_len = strlen (var);
685 if (strncmp (var, ptr, var_len) == 0)
689 tablesize += var_len + strlen (val) + 1;
694 tablesize += len + 1;
697 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
702 n_found += strlen (var) + strlen (val) + 1;
704 result = GNUNET_malloc (tablesize + n_found + 1);
705 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
707 size_t len = strlen (ptr);
710 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
714 var_len = strlen (var);
715 if (strncmp (var, ptr, var_len) == 0)
723 strcpy (result_ptr, ptr);
724 result_ptr += len + 1;
728 strcpy (result_ptr, var);
729 result_ptr += var_len;
730 strcpy (result_ptr, val);
731 result_ptr += strlen (val) + 1;
735 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
739 var_len = strlen (var);
742 strcpy (result_ptr, var);
743 result_ptr += var_len;
744 strcpy (result_ptr, val);
745 result_ptr += strlen (val) + 1;
748 FreeEnvironmentStrings (win32_env_table);
759 * @param pipe_control should a pipe be used to send signals to the child?
760 * @param pipe_stdin pipe to use to send input to child process (or NULL)
761 * @param pipe_stdout pipe to use to get output from child process (or NULL)
762 * @param filename name of the binary
763 * @param argv NULL-terminated array of arguments to the process
764 * @return pointer to process structure of the new process, NULL on error
766 struct GNUNET_OS_Process *
767 GNUNET_OS_start_process_vap (int pipe_control,
768 struct GNUNET_DISK_PipeHandle *pipe_stdin,
769 struct GNUNET_DISK_PipeHandle *pipe_stdout,
770 const char *filename,
774 char *childpipename = NULL;
775 struct GNUNET_OS_Process *gnunet_proc = NULL;
782 if ( (GNUNET_YES == pipe_control) &&
784 npipe_setup (&childpipename)) )
786 if (pipe_stdout != NULL)
788 GNUNET_assert (GNUNET_OK ==
789 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
791 GNUNET_DISK_PIPE_END_WRITE),
792 &fd_stdout_write, sizeof (int)));
793 GNUNET_assert (GNUNET_OK ==
794 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
795 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
796 &fd_stdout_read, sizeof (int)));
798 if (pipe_stdin != NULL)
800 GNUNET_assert (GNUNET_OK ==
801 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
802 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
803 &fd_stdin_read, sizeof (int)));
804 GNUNET_assert (GNUNET_OK ==
805 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
806 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
807 &fd_stdin_write, sizeof (int)));
813 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
814 GNUNET_free_non_null (childpipename);
819 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
820 gnunet_proc->pid = ret;
821 gnunet_proc->childpipename = childpipename;
824 if (NULL != childpipename)
826 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
827 GNUNET_free (childpipename);
829 if (pipe_stdout != NULL)
831 GNUNET_break (0 == close (fd_stdout_read));
832 if (-1 == dup2 (fd_stdout_write, 1))
833 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
834 GNUNET_break (0 == close (fd_stdout_write));
837 if (pipe_stdin != NULL)
840 GNUNET_break (0 == close (fd_stdin_write));
841 if (-1 == dup2 (fd_stdin_read, 0))
842 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
843 GNUNET_break (0 == close (fd_stdin_read));
845 execvp (filename, argv);
846 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
849 char *childpipename = NULL;
850 struct GNUNET_OS_Process *gnunet_proc = NULL;
855 PROCESS_INFORMATION proc;
858 HANDLE stdout_handle;
859 struct GNUNET_DISK_FileHandle *control_pipe;
861 char path[MAX_PATH + 1];
863 char *our_env[3] = { NULL, NULL, NULL };
864 char *env_block = NULL;
866 DWORD pathbuf_len, alloc_len;
871 char *non_const_filename;
872 char win_path[MAX_PATH + 1];
873 wchar_t *wpath, *wcmd;
874 size_t wpath_len, wcmd_len;
877 /* Search in prefix dir (hopefully - the directory from which
878 * the current module was loaded), bindir and libdir, then in PATH
880 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
881 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
882 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
884 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
887 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
890 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
893 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
894 GNUNET_free (self_prefix);
895 GNUNET_free (bindir);
896 GNUNET_free (libdir);
898 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
899 GNUNET_assert (alloc_len == (pathbuf_len - 1));
901 cmdlen = strlen (filename);
902 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
903 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
905 GNUNET_asprintf (&non_const_filename, "%s", filename);
907 /* It could be in POSIX form, convert it to a DOS path early on */
908 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
910 SetErrnoFromWinError (lRet);
911 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
913 GNUNET_free (non_const_filename);
914 GNUNET_free (pathbuf);
917 GNUNET_free (non_const_filename);
918 non_const_filename = GNUNET_strdup (win_path);
919 /* Check that this is the full path. If it isn't, search. */
920 /* FIXME: convert it to wchar_t and use SearchPathW?
921 * Remember: arguments to _start_process() are technically in UTF-8...
923 if (non_const_filename[1] == ':')
924 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
925 else if (!SearchPathA
926 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
929 SetErrnoFromWinError (GetLastError ());
930 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
932 GNUNET_free (non_const_filename);
933 GNUNET_free (pathbuf);
936 GNUNET_free (pathbuf);
937 GNUNET_free (non_const_filename);
941 while (NULL != (arg = argv[argc++]))
944 cmdlen = cmdlen + strlen (path) + 4;
946 cmdlen = cmdlen + strlen (arg) + 4;
950 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
952 while (NULL != (arg = argv[argc++]))
954 /* This is to escape trailing slash */
955 char arg_lastchar = arg[strlen (arg) - 1];
957 idx += sprintf (idx, "\"%s%s\"%s", path,
958 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
960 idx += sprintf (idx, "\"%s%s\"%s", arg,
961 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
964 memset (&start, 0, sizeof (start));
965 start.cb = sizeof (start);
967 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
968 start.dwFlags |= STARTF_USESTDHANDLES;
970 if (pipe_stdin != NULL)
972 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
973 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
974 &stdin_handle, sizeof (HANDLE));
975 start.hStdInput = stdin_handle;
978 if (pipe_stdout != NULL)
980 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
982 GNUNET_DISK_PIPE_END_WRITE),
983 &stdout_handle, sizeof (HANDLE));
984 start.hStdOutput = stdout_handle;
986 if (GNUNET_YES == pipe_control)
989 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
990 GNUNET_DISK_PERM_USER_READ |
991 GNUNET_DISK_PERM_USER_WRITE);
992 if (control_pipe == NULL)
1000 control_pipe = NULL;
1001 if (NULL != childpipename)
1003 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1005 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1006 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1013 env_block = CreateCustomEnvTable (our_env);
1014 GNUNET_free_non_null (our_env[0]);
1015 GNUNET_free_non_null (our_env[1]);
1018 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1020 LOG (GNUNET_ERROR_TYPE_DEBUG,
1021 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1022 GNUNET_free (env_block);
1028 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1030 LOG (GNUNET_ERROR_TYPE_DEBUG,
1031 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1032 GNUNET_free (env_block);
1038 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1039 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1041 SetErrnoFromWinError (GetLastError ());
1042 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1043 GNUNET_free (env_block);
1050 GNUNET_free (env_block);
1052 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1053 gnunet_proc->pid = proc.dwProcessId;
1054 gnunet_proc->handle = proc.hProcess;
1055 gnunet_proc->control_pipe = control_pipe;
1057 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1059 ResumeThread (proc.hThread);
1060 CloseHandle (proc.hThread);
1073 * @param pipe_control should a pipe be used to send signals to the child?
1074 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1075 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1076 * @param filename name of the binary
1077 * @param va NULL-terminated list of arguments to the process
1078 * @return pointer to process structure of the new process, NULL on error
1080 struct GNUNET_OS_Process *
1081 GNUNET_OS_start_process_va (int pipe_control,
1082 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1083 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1084 const char *filename, va_list va)
1086 struct GNUNET_OS_Process *ret;
1093 while (NULL != va_arg (ap, char *))
1096 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1099 while (NULL != (argv[argc] = va_arg (ap, char *)))
1102 ret = GNUNET_OS_start_process_vap (pipe_control,
1116 * @param pipe_control should a pipe be used to send signals to the child?
1117 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1118 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1119 * @param filename name of the binary
1120 * @param ... NULL-terminated list of arguments to the process
1122 * @return pointer to process structure of the new process, NULL on error
1125 struct GNUNET_OS_Process *
1126 GNUNET_OS_start_process (int pipe_control,
1127 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1128 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1129 const char *filename, ...)
1131 struct GNUNET_OS_Process *ret;
1134 va_start (ap, filename);
1135 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1144 * @param pipe_control should a pipe be used to send signals to the child?
1145 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1146 * must be NULL on platforms where dup is not supported
1147 * @param filename name of the binary
1148 * @param argv NULL-terminated list of arguments to the process
1149 * @return process ID of the new process, -1 on error
1151 struct GNUNET_OS_Process *
1152 GNUNET_OS_start_process_v (int pipe_control,
1153 const SOCKTYPE *lsocks,
1154 const char *filename,
1161 struct GNUNET_OS_Process *gnunet_proc = NULL;
1162 char *childpipename = NULL;
1171 if ( (GNUNET_YES == pipe_control) &&
1172 (GNUNET_OK != npipe_setup (&childpipename)) )
1179 while (-1 != (k = lsocks[i++]))
1180 GNUNET_array_append (lscp, ls, k);
1181 GNUNET_array_append (lscp, ls, -1);
1186 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1187 GNUNET_free_non_null (childpipename);
1188 GNUNET_array_grow (lscp, ls, 0);
1193 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1194 gnunet_proc->pid = ret;
1195 gnunet_proc->childpipename = childpipename;
1196 GNUNET_array_grow (lscp, ls, 0);
1199 if (NULL != childpipename)
1201 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1202 GNUNET_free (childpipename);
1206 /* read systemd documentation... */
1207 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1208 setenv ("LISTEN_PID", lpid, 1);
1211 while (-1 != lscp[i])
1214 while (-1 != lscp[j])
1220 GNUNET_assert (-1 != k);
1221 GNUNET_assert (0 == close (lscp[j]));
1229 /* Bury any existing FD, no matter what; they should all be closed
1230 * on exec anyway and the important onces have been dup'ed away */
1232 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1234 /* unset close-on-exec flag */
1235 flags = fcntl (tgt, F_GETFD);
1236 GNUNET_assert (flags >= 0);
1237 flags &= ~FD_CLOEXEC;
1239 (void) fcntl (tgt, F_SETFD, flags);
1243 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1244 setenv ("LISTEN_FDS", fds, 1);
1246 GNUNET_array_grow (lscp, ls, 0);
1247 execvp (filename, argv);
1248 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1251 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1252 char *childpipename = NULL;
1253 char **arg, **non_const_argv;
1254 unsigned int cmdlen;
1257 PROCESS_INFORMATION proc;
1259 struct GNUNET_OS_Process *gnunet_proc = NULL;
1260 char path[MAX_PATH + 1];
1261 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1262 char *env_block = NULL;
1264 DWORD pathbuf_len, alloc_len;
1269 char *non_const_filename;
1270 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1271 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1273 HANDLE lsocks_write;
1274 wchar_t *wpath, *wcmd;
1275 size_t wpath_len, wcmd_len;
1280 /* Search in prefix dir (hopefully - the directory from which
1281 * the current module was loaded), bindir and libdir, then in PATH
1283 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1284 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1285 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1287 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1290 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1293 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1296 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1297 GNUNET_free (self_prefix);
1298 GNUNET_free (bindir);
1299 GNUNET_free (libdir);
1301 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1302 if (alloc_len != pathbuf_len - 1)
1304 GNUNET_free (pathbuf);
1305 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1309 cmdlen = strlen (filename);
1310 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1311 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1313 GNUNET_asprintf (&non_const_filename, "%s", filename);
1315 /* It could be in POSIX form, convert it to a DOS path early on */
1316 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1318 SetErrnoFromWinError (lRet);
1319 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1320 non_const_filename);
1321 GNUNET_free (non_const_filename);
1322 GNUNET_free (pathbuf);
1325 GNUNET_free (non_const_filename);
1326 non_const_filename = GNUNET_strdup (win_path);
1327 /* Check that this is the full path. If it isn't, search. */
1328 /* FIXME: convert it to wchar_t and use SearchPathW?
1329 * Remember: arguments to _start_process() are technically in UTF-8...
1331 if (non_const_filename[1] == ':')
1332 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1333 else if (!SearchPathA
1334 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1337 SetErrnoFromWinError (GetLastError ());
1338 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1339 non_const_filename);
1340 GNUNET_free (non_const_filename);
1341 GNUNET_free (pathbuf);
1344 GNUNET_free (pathbuf);
1345 GNUNET_free (non_const_filename);
1347 /* Count the number of arguments */
1348 arg = (char **) argv;
1355 /* Allocate a copy argv */
1356 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1358 /* Copy all argv strings */
1360 arg = (char **) argv;
1364 non_const_argv[argcount] = GNUNET_strdup (path);
1366 non_const_argv[argcount] = GNUNET_strdup (*arg);
1370 non_const_argv[argcount] = NULL;
1374 arg = non_const_argv;
1377 cmdlen = cmdlen + strlen (*arg) + 4;
1381 /* Allocate and create cmd */
1382 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1383 arg = non_const_argv;
1386 char arg_last_char = (*arg)[strlen (*arg) - 1];
1387 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1388 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1392 while (argcount > 0)
1393 GNUNET_free (non_const_argv[--argcount]);
1394 GNUNET_free (non_const_argv);
1396 memset (&start, 0, sizeof (start));
1397 start.cb = sizeof (start);
1399 if (GNUNET_YES == pipe_control)
1402 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1403 GNUNET_DISK_PERM_USER_READ |
1404 GNUNET_DISK_PERM_USER_WRITE);
1405 if (control_pipe == NULL)
1413 control_pipe = NULL;
1414 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1416 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1418 if (lsocks_pipe == NULL)
1422 GNUNET_DISK_pipe_close (lsocks_pipe);
1425 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1426 GNUNET_DISK_PIPE_END_WRITE);
1427 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1428 &lsocks_write, sizeof (HANDLE));
1429 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1430 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1431 &lsocks_read, sizeof (HANDLE));
1435 if (NULL != childpipename)
1437 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1439 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1440 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1441 GNUNET_free (childpipename);
1443 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1445 /*This will tell the child that we're going to send lsocks over the pipe*/
1446 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1447 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1449 our_env[env_off++] = NULL;
1450 env_block = CreateCustomEnvTable (our_env);
1452 GNUNET_free_non_null (our_env[--env_off]);
1455 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1457 LOG (GNUNET_ERROR_TYPE_DEBUG,
1458 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1459 GNUNET_free (env_block);
1465 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1467 LOG (GNUNET_ERROR_TYPE_DEBUG,
1468 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1469 GNUNET_free (env_block);
1475 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1476 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1478 SetErrnoFromWinError (GetLastError ());
1479 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1480 if (NULL != control_pipe)
1481 GNUNET_DISK_file_close (control_pipe);
1483 GNUNET_DISK_pipe_close (lsocks_pipe);
1484 GNUNET_free (env_block);
1491 GNUNET_free (env_block);
1493 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1494 gnunet_proc->pid = proc.dwProcessId;
1495 gnunet_proc->handle = proc.hProcess;
1496 gnunet_proc->control_pipe = control_pipe;
1498 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1500 ResumeThread (proc.hThread);
1501 CloseHandle (proc.hThread);
1506 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1509 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1511 /* This is a replacement for "goto error" that doesn't use goto */
1516 uint64_t size, count, i;
1518 /* Tell the number of sockets */
1519 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1521 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1522 if (wrote != sizeof (count))
1524 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1527 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1529 WSAPROTOCOL_INFOA pi;
1530 /* Get a socket duplication info */
1531 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1533 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1534 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1537 /* Synchronous I/O is not nice, but we can't schedule this:
1538 * lsocks will be closed/freed by the caller soon, and until
1539 * the child creates a duplicate, closing a socket here will
1540 * close it for good.
1542 /* Send the size of the structure
1543 * (the child might be built with different headers...)
1546 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1547 if (wrote != sizeof (size))
1549 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1552 /* Finally! Send the data */
1553 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1554 if (wrote != sizeof (pi))
1556 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1560 /* This will block us until the child makes a final read or closes
1561 * the pipe (hence no 'wrote' check), since we have to wait for it
1562 * to duplicate the last socket, before we return and start closing
1565 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1570 GNUNET_DISK_file_sync (lsocks_write_fd);
1571 GNUNET_DISK_pipe_close (lsocks_pipe);
1575 /* If we can't pass on the socket(s), the child will block forever,
1576 * better put it out of its misery.
1578 TerminateProcess (gnunet_proc->handle, 0);
1579 CloseHandle (gnunet_proc->handle);
1580 if (NULL != gnunet_proc->control_pipe)
1581 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1582 GNUNET_free (gnunet_proc);
1591 * Retrieve the status of a process, waiting on him if dead.
1592 * Nonblocking version.
1594 * @param proc process ID
1595 * @param type status type
1596 * @param code return code/signal number
1597 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1600 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1601 enum GNUNET_OS_ProcessStatusType *type,
1602 unsigned long *code)
1608 GNUNET_assert (0 != proc);
1609 ret = waitpid (proc->pid, &status, WNOHANG);
1612 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1613 return GNUNET_SYSERR;
1617 *type = GNUNET_OS_PROCESS_RUNNING;
1621 if (proc->pid != ret)
1623 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1624 return GNUNET_SYSERR;
1626 if (WIFEXITED (status))
1628 *type = GNUNET_OS_PROCESS_EXITED;
1629 *code = WEXITSTATUS (status);
1631 else if (WIFSIGNALED (status))
1633 *type = GNUNET_OS_PROCESS_SIGNALED;
1634 *code = WTERMSIG (status);
1636 else if (WIFSTOPPED (status))
1638 *type = GNUNET_OS_PROCESS_SIGNALED;
1639 *code = WSTOPSIG (status);
1642 else if (WIFCONTINUED (status))
1644 *type = GNUNET_OS_PROCESS_RUNNING;
1650 *type = GNUNET_OS_PROCESS_UNKNOWN;
1655 DWORD c, error_code, ret;
1659 if (h == NULL || ret == 0)
1661 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1663 return GNUNET_SYSERR;
1666 h = GetCurrentProcess ();
1669 ret = GetExitCodeProcess (h, &c);
1670 error_code = GetLastError ();
1671 if (ret == 0 || error_code != NO_ERROR)
1673 SetErrnoFromWinError (error_code);
1674 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1675 return GNUNET_SYSERR;
1677 if (STILL_ACTIVE == c)
1679 *type = GNUNET_OS_PROCESS_RUNNING;
1683 *type = GNUNET_OS_PROCESS_EXITED;
1692 * Wait for a process
1693 * @param proc pointer to process structure
1694 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1697 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1701 pid_t pid = proc->pid;
1704 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1705 (EINTR == errno) ) ;
1708 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1709 return GNUNET_SYSERR;
1719 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1721 return GNUNET_SYSERR;
1724 h = GetCurrentProcess ();
1726 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1728 SetErrnoFromWinError (GetLastError ());
1729 ret = GNUNET_SYSERR;
1740 * Handle to a command.
1742 struct GNUNET_OS_CommandHandle
1748 struct GNUNET_OS_Process *eip;
1751 * Handle to the output pipe.
1753 struct GNUNET_DISK_PipeHandle *opipe;
1756 * Read-end of output pipe.
1758 const struct GNUNET_DISK_FileHandle *r;
1761 * Function to call on each line of output.
1763 GNUNET_OS_LineProcessor proc;
1766 * Closure for 'proc'.
1771 * Buffer for the output.
1776 * Task reading from pipe.
1778 GNUNET_SCHEDULER_TaskIdentifier rtask;
1783 struct GNUNET_TIME_Absolute timeout;
1786 * Current read offset in buf.
1793 * Stop/kill a command. Must ONLY be called either from
1794 * the callback after 'NULL' was passed for 'line' *OR*
1795 * from an independent task (not within the line processor).
1797 * @param cmd handle to the process
1800 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1803 if (cmd->proc != NULL)
1805 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1806 GNUNET_SCHEDULER_cancel (cmd->rtask);
1808 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1809 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1810 GNUNET_OS_process_destroy (cmd->eip);
1811 GNUNET_DISK_pipe_close (cmd->opipe);
1817 * Read from the process and call the line processor.
1819 * @param cls the 'struct GNUNET_OS_CommandHandle'
1820 * @param tc scheduler context
1823 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1825 struct GNUNET_OS_CommandHandle *cmd = cls;
1826 GNUNET_OS_LineProcessor proc;
1830 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1831 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1833 /* timeout, shutdown, etc. */
1836 proc (cmd->proc_cls, NULL);
1840 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1841 sizeof (cmd->buf) - cmd->off);
1844 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1846 cmd->buf[cmd->off] = '\0';
1847 cmd->proc (cmd->proc_cls, cmd->buf);
1851 proc (cmd->proc_cls, NULL);
1854 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1859 cmd->proc (cmd->proc_cls, cmd->buf);
1860 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1861 cmd->off -= (end + 1 - cmd->buf);
1862 end = memchr (cmd->buf, '\n', cmd->off);
1865 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1866 (cmd->timeout), cmd->r, &cmd_read, cmd);
1871 * Run the given command line and call the given function
1872 * for each line of the output.
1874 * @param proc function to call for each line of the output
1875 * @param proc_cls closure for proc
1876 * @param timeout when to time out
1877 * @param binary command to run
1878 * @param ... arguments to command
1879 * @return NULL on error
1881 struct GNUNET_OS_CommandHandle *
1882 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1883 struct GNUNET_TIME_Relative timeout, const char *binary,
1886 struct GNUNET_OS_CommandHandle *cmd;
1887 struct GNUNET_OS_Process *eip;
1888 struct GNUNET_DISK_PipeHandle *opipe;
1891 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1894 va_start (ap, binary);
1895 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1899 GNUNET_DISK_pipe_close (opipe);
1902 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1903 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1904 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1908 cmd->proc_cls = proc_cls;
1909 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1910 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1917 /* end of os_priority.c */