summaryrefslogtreecommitdiffstats
path: root/wsutil/ws_pipe.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--wsutil/ws_pipe.c856
1 files changed, 856 insertions, 0 deletions
diff --git a/wsutil/ws_pipe.c b/wsutil/ws_pipe.c
new file mode 100644
index 0000000..7689f93
--- /dev/null
+++ b/wsutil/ws_pipe.c
@@ -0,0 +1,856 @@
+/* ws_pipe.c
+ *
+ * Routines for handling pipes.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <config.h>
+#define WS_LOG_DOMAIN LOG_DOMAIN_CAPTURE
+#include "wsutil/ws_pipe.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h> /* for _O_BINARY */
+#include <wsutil/win32-utils.h>
+#else
+#include <unistd.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+
+#ifdef __linux__
+#define HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+#include <fcntl.h>
+#include <sys/syscall.h> /* for syscall and SYS_getdents64 */
+#include <wsutil/file_util.h> /* for ws_open -> open to pacify checkAPIs.pl */
+#endif
+
+#include "wsutil/filesystem.h"
+#include "wsutil/wslog.h"
+
+#ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+struct linux_dirent64 {
+ uint64_t d_ino; /* 64-bit inode number */
+ uint64_t d_off; /* 64-bit offset to next structure */
+ unsigned short d_reclen; /* Size of this dirent */
+ unsigned char d_type; /* File type */
+ char d_name[]; /* Filename (null-terminated) */
+};
+
+/* Async-signal-safe string to integer conversion. */
+static int
+filename_to_fd(const char *p)
+{
+ char c;
+ int fd = 0;
+ const int cutoff = INT_MAX / 10;
+ const int cutlim = INT_MAX % 10;
+
+ if (*p == '\0')
+ return -1;
+
+ while ((c = *p++) != '\0') {
+ if (!g_ascii_isdigit(c))
+ return -1;
+ c -= '0';
+
+ /* Check for overflow. */
+ if (fd > cutoff || (fd == cutoff && c > cutlim))
+ return -1;
+
+ fd = fd * 10 + c;
+ }
+
+ return fd;
+}
+
+static void
+close_non_standard_fds_linux(void * user_data _U_)
+{
+ /*
+ * GLib 2.14.2 and newer (up to at least GLib 2.58.1) on Linux with multiple
+ * threads can deadlock in the child process due to use of opendir (which
+ * is not async-signal-safe). To avoid this, disable the broken code path
+ * and manually close file descriptors using async-signal-safe code only.
+ * Use CLOEXEC to allow reporting of execve errors to the parent via a pipe.
+ * https://gitlab.gnome.org/GNOME/glib/issues/1014
+ * https://gitlab.gnome.org/GNOME/glib/merge_requests/490
+ */
+ int dir_fd = ws_open("/proc/self/fd", O_RDONLY | O_DIRECTORY);
+ if (dir_fd >= 0) {
+ char buf[4096];
+ int nread, fd;
+ struct linux_dirent64 *de;
+
+ while ((nread = (int) syscall(SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0) {
+ for (int pos = 0; pos < nread; pos += de->d_reclen) {
+ de = (struct linux_dirent64 *)(buf + pos);
+ fd = filename_to_fd(de->d_name);
+ if (fd > STDERR_FILENO && fd != dir_fd) {
+ /* Close all other (valid) file descriptors above stderr. */
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ }
+ }
+ }
+
+ close(dir_fd);
+ } else {
+ /* Slow fallback in case /proc is not mounted */
+ for (int fd = STDERR_FILENO + 1; fd < getdtablesize(); fd++) {
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ }
+ }
+}
+#endif
+
+#ifdef _WIN32
+static ULONG pipe_serial_number;
+
+/* Alternative for CreatePipe() where read handle is opened with FILE_FLAG_OVERLAPPED */
+static bool
+ws_pipe_create_overlapped_read(HANDLE *read_pipe_handle, HANDLE *write_pipe_handle,
+ SECURITY_ATTRIBUTES *sa, DWORD suggested_buffer_size)
+{
+ HANDLE read_pipe, write_pipe;
+ unsigned char *name = ws_strdup_printf("\\\\.\\Pipe\\WiresharkWsPipe.%08lx.%08lx",
+ GetCurrentProcessId(),
+ InterlockedIncrement(&pipe_serial_number));
+ gunichar2 *wname = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
+
+ g_free(name);
+
+ read_pipe = CreateNamedPipe(wname, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_WAIT, 1,
+ suggested_buffer_size, suggested_buffer_size,
+ 0, sa);
+ if (INVALID_HANDLE_VALUE == read_pipe)
+ {
+ g_free(wname);
+ return false;
+ }
+
+ write_pipe = CreateFile(wname, GENERIC_WRITE, 0, sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (INVALID_HANDLE_VALUE == write_pipe)
+ {
+ DWORD error = GetLastError();
+ CloseHandle(read_pipe);
+ SetLastError(error);
+ g_free(wname);
+ return false;
+ }
+
+ *read_pipe_handle = read_pipe;
+ *write_pipe_handle = write_pipe;
+ g_free(wname);
+ return(true);
+}
+#endif
+
+/**
+ * Helper to convert a command and argument list to an NULL-terminated 'argv'
+ * array, suitable for g_spawn_sync and friends. Free with g_strfreev.
+ */
+static char **
+convert_to_argv(const char *command, int args_count, char *const *args)
+{
+ char **argv = g_new(char *, args_count + 2);
+ // The caller does not seem to modify this, but g_spawn_sync uses 'char **'
+ // as opposed to 'const char **', so just to be sure clone it.
+ argv[0] = g_strdup(command);
+ for (int i = 0; i < args_count; i++) {
+ // Empty arguments may indicate a bug in Wireshark. Extcap for example
+ // omits arguments when their string value is empty. On Windows, empty
+ // arguments would silently be ignored because protect_arg returns an
+ // empty string, therefore we print a warning here.
+ if (!*args[i]) {
+ ws_warning("Empty argument %d in arguments list", i);
+ }
+ argv[1 + i] = g_strdup(args[i]);
+ }
+ argv[args_count + 1] = NULL;
+ return argv;
+}
+
+/**
+ * Convert a non-empty NULL-terminated array of command and arguments to a
+ * string for displaying purposes. On Windows, the returned string is properly
+ * escaped and can be executed directly.
+ */
+static char *
+convert_to_command_line(char **argv)
+{
+ GString *command_line = g_string_sized_new(200);
+#ifdef _WIN32
+ // The first argument must always be quoted even if it does not contain
+ // special characters or else CreateProcess might consider arguments as part
+ // of the executable.
+ char *quoted_arg = protect_arg(argv[0]);
+ if (quoted_arg[0] != '"') {
+ g_string_append_c(command_line, '"');
+ g_string_append(command_line, quoted_arg);
+ g_string_append_c(command_line, '"');
+ } else {
+ g_string_append(command_line, quoted_arg);
+ }
+ g_free(quoted_arg);
+
+ for (int i = 1; argv[i]; i++) {
+ quoted_arg = protect_arg(argv[i]);
+ g_string_append_c(command_line, ' ');
+ g_string_append(command_line, quoted_arg);
+ g_free(quoted_arg);
+ }
+#else
+ for (int i = 0; argv[i]; i++) {
+ char *quoted_arg = g_shell_quote(argv[i]);
+ if (i != 0) {
+ g_string_append_c(command_line, ' ');
+ }
+ g_string_append(command_line, quoted_arg);
+ g_free(quoted_arg);
+ }
+#endif
+ return g_string_free(command_line, false);
+}
+
+bool ws_pipe_spawn_sync(const char *working_directory, const char *command, int argc, char **args, char **command_output)
+{
+ bool status = false;
+ bool result = false;
+ char *local_output = NULL;
+#ifdef _WIN32
+
+#define BUFFER_SIZE 16384
+
+ STARTUPINFO info;
+ PROCESS_INFORMATION processInfo;
+
+ SECURITY_ATTRIBUTES sa;
+ HANDLE child_stdout_rd = NULL;
+ HANDLE child_stdout_wr = NULL;
+ HANDLE child_stderr_rd = NULL;
+ HANDLE child_stderr_wr = NULL;
+ HANDLE inherit_handles[2];
+
+ OVERLAPPED stdout_overlapped;
+ OVERLAPPED stderr_overlapped;
+#else
+ int exit_status = 0;
+#endif
+
+ char **argv = convert_to_argv(command, argc, args);
+ char *command_line = convert_to_command_line(argv);
+
+ ws_debug("command line: %s", command_line);
+
+ uint64_t start_time = g_get_monotonic_time();
+
+#ifdef _WIN32
+ /* Setup overlapped structures. Create Manual Reset events, initially not signalled */
+ memset(&stdout_overlapped, 0, sizeof(OVERLAPPED));
+ memset(&stderr_overlapped, 0, sizeof(OVERLAPPED));
+ stdout_overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
+ if (!stdout_overlapped.hEvent)
+ {
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdout overlapped event");
+ return false;
+ }
+ stderr_overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
+ if (!stderr_overlapped.hEvent)
+ {
+ CloseHandle(stdout_overlapped.hEvent);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stderr overlapped event");
+ return false;
+ }
+
+ memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = false;
+ sa.lpSecurityDescriptor = NULL;
+
+ if (!ws_pipe_create_overlapped_read(&child_stdout_rd, &child_stdout_wr, &sa, 0))
+ {
+ CloseHandle(stdout_overlapped.hEvent);
+ CloseHandle(stderr_overlapped.hEvent);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdout handle");
+ return false;
+ }
+
+ if (!ws_pipe_create_overlapped_read(&child_stderr_rd, &child_stderr_wr, &sa, 0))
+ {
+ CloseHandle(stdout_overlapped.hEvent);
+ CloseHandle(stderr_overlapped.hEvent);
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stdout_wr);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stderr handle");
+ return false;
+ }
+
+ inherit_handles[0] = child_stderr_wr;
+ inherit_handles[1] = child_stdout_wr;
+
+ memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
+ memset(&info, 0, sizeof(STARTUPINFO));
+
+ info.cb = sizeof(STARTUPINFO);
+ info.hStdError = child_stderr_wr;
+ info.hStdOutput = child_stdout_wr;
+ info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ info.wShowWindow = SW_HIDE;
+
+ if (win32_create_process(NULL, command_line, NULL, NULL, G_N_ELEMENTS(inherit_handles), inherit_handles,
+ CREATE_NEW_CONSOLE, NULL, working_directory, &info, &processInfo))
+ {
+ char* stdout_buffer = (char*)g_malloc(BUFFER_SIZE);
+ char* stderr_buffer = (char*)g_malloc(BUFFER_SIZE);
+ DWORD dw;
+ DWORD bytes_read;
+ GString *output_string = g_string_new(NULL);
+ bool process_finished = false;
+ bool pending_stdout = true;
+ bool pending_stderr = true;
+
+ /* Start asynchronous reads from child process stdout and stderr */
+ if (!ReadFile(child_stdout_rd, stdout_buffer, BUFFER_SIZE, NULL, &stdout_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stdout pipe failed. Error %ld", GetLastError());
+ pending_stdout = false;
+ }
+ }
+
+ if (!ReadFile(child_stderr_rd, stderr_buffer, BUFFER_SIZE, NULL, &stderr_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stderr pipe failed. Error %ld", GetLastError());
+ pending_stderr = false;
+ }
+ }
+
+ for (;;)
+ {
+ HANDLE handles[3];
+ DWORD n_handles = 0;
+ if (!process_finished)
+ {
+ handles[n_handles++] = processInfo.hProcess;
+ }
+ if (pending_stdout)
+ {
+ handles[n_handles++] = stdout_overlapped.hEvent;
+ }
+ if (pending_stderr)
+ {
+ handles[n_handles++] = stderr_overlapped.hEvent;
+ }
+
+ if (!n_handles)
+ {
+ /* No more things to wait */
+ break;
+ }
+
+ dw = WaitForMultipleObjects(n_handles, handles, false, INFINITE);
+ if (dw < (WAIT_OBJECT_0 + n_handles))
+ {
+ int i = dw - WAIT_OBJECT_0;
+ if (handles[i] == processInfo.hProcess)
+ {
+ /* Process finished but there might still be unread data in the pipe.
+ * Close the write pipes, so ReadFile does not wait indefinitely.
+ */
+ CloseHandle(child_stdout_wr);
+ CloseHandle(child_stderr_wr);
+ process_finished = true;
+ }
+ else if (handles[i] == stdout_overlapped.hEvent)
+ {
+ bytes_read = 0;
+ if (!GetOverlappedResult(child_stdout_rd, &stdout_overlapped, &bytes_read, true))
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ pending_stdout = false;
+ continue;
+ }
+ ws_debug("GetOverlappedResult on stdout failed. Error %ld", GetLastError());
+ }
+ if (process_finished && (bytes_read == 0))
+ {
+ /* We have drained the pipe and there isn't any process that holds active write handle to the pipe. */
+ pending_stdout = false;
+ continue;
+ }
+ g_string_append_len(output_string, stdout_buffer, bytes_read);
+ if (!ReadFile(child_stdout_rd, stdout_buffer, BUFFER_SIZE, NULL, &stdout_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stdout pipe failed. Error %ld", GetLastError());
+ pending_stdout = false;
+ }
+ }
+ }
+ else if (handles[i] == stderr_overlapped.hEvent)
+ {
+ /* Discard the stderr data just like non-windows version of this function does. */
+ bytes_read = 0;
+ if (!GetOverlappedResult(child_stderr_rd, &stderr_overlapped, &bytes_read, true))
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ pending_stderr = false;
+ continue;
+ }
+ ws_debug("GetOverlappedResult on stderr failed. Error %ld", GetLastError());
+ }
+ if (process_finished && (bytes_read == 0))
+ {
+ pending_stderr = false;
+ continue;
+ }
+ if (!ReadFile(child_stderr_rd, stderr_buffer, BUFFER_SIZE, NULL, &stderr_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stderr pipe failed. Error %ld", GetLastError());
+ pending_stderr = false;
+ }
+ }
+ }
+ }
+ else
+ {
+ ws_debug("WaitForMultipleObjects returned 0x%08lX. Error %ld", dw, GetLastError());
+ }
+ }
+
+ g_free(stdout_buffer);
+ g_free(stderr_buffer);
+
+ status = GetExitCodeProcess(processInfo.hProcess, &dw);
+ if (status && dw != 0)
+ {
+ status = false;
+ }
+
+ local_output = g_string_free(output_string, false);
+
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stderr_rd);
+
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ }
+ else
+ {
+ status = false;
+
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stdout_wr);
+ CloseHandle(child_stderr_rd);
+ CloseHandle(child_stderr_wr);
+ }
+
+ CloseHandle(stdout_overlapped.hEvent);
+ CloseHandle(stderr_overlapped.hEvent);
+#else
+
+ GSpawnFlags flags = (GSpawnFlags)0;
+ GSpawnChildSetupFunc child_setup = NULL;
+#ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+ flags = (GSpawnFlags)(flags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN);
+ child_setup = close_non_standard_fds_linux;
+#endif
+ status = g_spawn_sync(working_directory, argv, NULL,
+ flags, child_setup, NULL, &local_output, NULL, &exit_status, NULL);
+
+ if (status && exit_status != 0)
+ status = false;
+#endif
+
+ ws_debug("%s finished in %.3fms", argv[0], (g_get_monotonic_time() - start_time) / 1000.0);
+
+ if (status)
+ {
+ if (local_output != NULL) {
+ ws_noisy("spawn output: %s", local_output);
+ if (command_output != NULL)
+ *command_output = g_strdup(local_output);
+ }
+ result = true;
+ }
+
+ g_free(local_output);
+ g_free(command_line);
+ g_strfreev(argv);
+
+ return result;
+}
+
+void ws_pipe_init(ws_pipe_t *ws_pipe)
+{
+ if (!ws_pipe) return;
+ memset(ws_pipe, 0, sizeof(ws_pipe_t));
+ ws_pipe->pid = WS_INVALID_PID;
+}
+
+GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args)
+{
+ GPid pid = WS_INVALID_PID;
+ int stdin_fd, stdout_fd, stderr_fd;
+#ifdef _WIN32
+ STARTUPINFO info;
+ PROCESS_INFORMATION processInfo;
+
+ SECURITY_ATTRIBUTES sa;
+ HANDLE child_stdin_rd = NULL;
+ HANDLE child_stdin_wr = NULL;
+ HANDLE child_stdout_rd = NULL;
+ HANDLE child_stdout_wr = NULL;
+ HANDLE child_stderr_rd = NULL;
+ HANDLE child_stderr_wr = NULL;
+ HANDLE inherit_handles[3];
+#endif
+
+ // XXX harmonize handling of command arguments for the sync/async functions
+ // and make them const? This array ends with a trailing NULL by the way.
+ char **args_array = (char **)args->pdata;
+ char **argv = convert_to_argv(args_array[0], args->len - 2, args_array + 1);
+ char *command_line = convert_to_command_line(argv);
+
+ ws_debug("command line: %s", command_line);
+
+#ifdef _WIN32
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = false;
+ sa.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&child_stdin_rd, &child_stdin_wr, &sa, 0))
+ {
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdin handle");
+ return WS_INVALID_PID;
+ }
+
+ if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0))
+ {
+ CloseHandle(child_stdin_rd);
+ CloseHandle(child_stdin_wr);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdout handle");
+ return WS_INVALID_PID;
+ }
+
+ if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0))
+ {
+ CloseHandle(child_stdin_rd);
+ CloseHandle(child_stdin_wr);
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stdout_wr);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stderr handle");
+ return WS_INVALID_PID;
+ }
+
+ inherit_handles[0] = child_stdin_rd;
+ inherit_handles[1] = child_stderr_wr;
+ inherit_handles[2] = child_stdout_wr;
+
+ memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
+ memset(&info, 0, sizeof(STARTUPINFO));
+
+ info.cb = sizeof(STARTUPINFO);
+ info.hStdInput = child_stdin_rd;
+ info.hStdError = child_stderr_wr;
+ info.hStdOutput = child_stdout_wr;
+ info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ info.wShowWindow = SW_HIDE;
+
+ if (win32_create_process(NULL, command_line, NULL, NULL, G_N_ELEMENTS(inherit_handles), inherit_handles,
+ CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
+ {
+ stdin_fd = _open_osfhandle((intptr_t)(child_stdin_wr), _O_BINARY);
+ stdout_fd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY);
+ stderr_fd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY);
+ pid = processInfo.hProcess;
+ CloseHandle(processInfo.hThread);
+ }
+ else
+ {
+ CloseHandle(child_stdin_wr);
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stderr_rd);
+ }
+
+ /* We no longer need other (child) end of pipes. The child process holds
+ * its own handles that will be closed on process exit. However, we have
+ * to close *our* handles as otherwise read() on stdout_fd and stderr_fd
+ * will block indefinitely after the process exits.
+ */
+ CloseHandle(child_stdin_rd);
+ CloseHandle(child_stdout_wr);
+ CloseHandle(child_stderr_wr);
+#else
+
+ GError *error = NULL;
+ GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD;
+ GSpawnChildSetupFunc child_setup = NULL;
+#ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+ flags = (GSpawnFlags)(flags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN);
+ child_setup = close_non_standard_fds_linux;
+#endif
+ bool spawned = g_spawn_async_with_pipes(NULL, argv, NULL,
+ flags, child_setup, NULL,
+ &pid, &stdin_fd, &stdout_fd, &stderr_fd, &error);
+ if (!spawned) {
+ ws_debug("Error creating async pipe: %s", error->message);
+ g_free(error->message);
+ }
+#endif
+
+ g_free(command_line);
+ g_strfreev(argv);
+
+ ws_pipe->pid = pid;
+
+ if (pid != WS_INVALID_PID) {
+#ifdef _WIN32
+ ws_pipe->stdin_io = g_io_channel_win32_new_fd(stdin_fd);
+ ws_pipe->stdout_io = g_io_channel_win32_new_fd(stdout_fd);
+ ws_pipe->stderr_io = g_io_channel_win32_new_fd(stderr_fd);
+#else
+ ws_pipe->stdin_io = g_io_channel_unix_new(stdin_fd);
+ ws_pipe->stdout_io = g_io_channel_unix_new(stdout_fd);
+ ws_pipe->stderr_io = g_io_channel_unix_new(stderr_fd);
+#endif
+ g_io_channel_set_encoding(ws_pipe->stdin_io, NULL, NULL);
+ g_io_channel_set_encoding(ws_pipe->stdout_io, NULL, NULL);
+ g_io_channel_set_encoding(ws_pipe->stderr_io, NULL, NULL);
+ g_io_channel_set_buffered(ws_pipe->stdin_io, false);
+ g_io_channel_set_buffered(ws_pipe->stdout_io, false);
+ g_io_channel_set_buffered(ws_pipe->stderr_io, false);
+ g_io_channel_set_close_on_unref(ws_pipe->stdin_io, true);
+ g_io_channel_set_close_on_unref(ws_pipe->stdout_io, true);
+ g_io_channel_set_close_on_unref(ws_pipe->stderr_io, true);
+ }
+
+ return pid;
+}
+
+#ifdef _WIN32
+
+typedef struct
+{
+ HANDLE pipeHandle;
+ OVERLAPPED ol;
+ BOOL pendingIO;
+} PIPEINTS;
+
+bool
+ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid)
+{
+ PIPEINTS pipeinsts[3];
+ HANDLE handles[4];
+ bool result = true;
+
+ SecureZeroMemory(pipeinsts, sizeof(pipeinsts));
+
+ if (num_pipe_handles == 0 || num_pipe_handles > 3)
+ {
+ ws_debug("Invalid number of pipes given as argument.");
+ return false;
+ }
+
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ pipeinsts[i].ol.hEvent = CreateEvent(NULL, true, false, NULL);
+ if (!pipeinsts[i].ol.hEvent)
+ {
+ ws_debug("Could not create overlapped event");
+ for (int j = 0; j < i; j++)
+ {
+ CloseHandle(pipeinsts[j].ol.hEvent);
+ }
+ return false;
+ }
+ }
+
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ pipeinsts[i].pipeHandle = pipe_handles[i];
+ pipeinsts[i].ol.Pointer = 0;
+ pipeinsts[i].pendingIO = false;
+ if (!ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol))
+ {
+ DWORD error = GetLastError();
+ switch (error)
+ {
+ case ERROR_IO_PENDING:
+ pipeinsts[i].pendingIO = true;
+ break;
+
+ case ERROR_PIPE_CONNECTED:
+ SetEvent(pipeinsts[i].ol.hEvent);
+ break;
+
+ default:
+ ws_debug("ConnectNamedPipe failed with %ld\n.", error);
+ result = false;
+ }
+ }
+ }
+
+ while (result)
+ {
+ DWORD dw;
+ int num_handles = 0;
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ if (pipeinsts[i].pendingIO)
+ {
+ handles[num_handles] = pipeinsts[i].ol.hEvent;
+ num_handles++;
+ }
+ }
+ if (num_handles == 0)
+ {
+ /* All pipes have been successfully connected */
+ break;
+ }
+ /* Wait for process in case it exits before the pipes have connected */
+ handles[num_handles] = pid;
+ num_handles++;
+
+ dw = WaitForMultipleObjects(num_handles, handles, false, 30000);
+ int handle_idx = dw - WAIT_OBJECT_0;
+ if (dw == WAIT_TIMEOUT)
+ {
+ ws_debug("extcap didn't connect to pipe within 30 seconds.");
+ result = false;
+ break;
+ }
+ // If index points to our handles array
+ else if (handle_idx >= 0 && handle_idx < num_handles)
+ {
+ if (handles[handle_idx] == pid)
+ {
+ ws_debug("extcap terminated without connecting to pipe.");
+ result = false;
+ }
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ if (handles[handle_idx] == pipeinsts[i].ol.hEvent)
+ {
+ DWORD cbRet;
+ BOOL success = GetOverlappedResult(
+ pipeinsts[i].pipeHandle, // handle to pipe
+ &pipeinsts[i].ol, // OVERLAPPED structure
+ &cbRet, // bytes transferred
+ true); // wait
+ if (!success)
+ {
+ ws_debug("Error %ld \n.", GetLastError());
+ result = false;
+ }
+ pipeinsts[i].pendingIO = false;
+ }
+ }
+ }
+ else
+ {
+ ws_debug("WaitForMultipleObjects returned 0x%08lX. Error %ld", dw, GetLastError());
+ result = false;
+ }
+ }
+
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ if (pipeinsts[i].pendingIO)
+ {
+ CancelIoEx(pipeinsts[i].pipeHandle, &pipeinsts[i].ol);
+ WaitForSingleObject(pipeinsts[i].ol.hEvent, INFINITE);
+ }
+ CloseHandle(pipeinsts[i].ol.hEvent);
+ }
+
+ return result;
+}
+#endif
+
+bool
+ws_pipe_data_available(int pipe_fd)
+{
+#ifdef _WIN32 /* PeekNamedPipe */
+ HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
+ DWORD bytes_avail;
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+
+ if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL))
+ {
+ return false;
+ }
+
+ if (bytes_avail > 0)
+ {
+ return true;
+ }
+ return false;
+#else /* select */
+ fd_set rfds;
+ struct timeval timeout;
+
+ FD_ZERO(&rfds);
+ FD_SET(pipe_fd, &rfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if (select(pipe_fd + 1, &rfds, NULL, NULL, &timeout) > 0)
+ {
+ return true;
+ }
+
+ return false;
+#endif
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */