From f2786bc8ff5ea25fd1c36d3ebf83a29277ee662b Mon Sep 17 00:00:00 2001 From: Roland Knall Date: Tue, 28 Jun 2016 21:34:46 +0200 Subject: extcap: Remove g_spawn_helper support Move g_spawn to separate file and implement functions to use Windows based method of spawning, instead of the glib based version Change-Id: Ibae03d834ec86531eba37dc8768fbf17ddadf57f Reviewed-on: https://code.wireshark.org/review/16049 Petri-Dish: Roland Knall Tested-by: Petri Dish Buildbot Reviewed-by: Graham Bloice Reviewed-by: Roland Knall --- extcap_spawn.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 extcap_spawn.c (limited to 'extcap_spawn.c') diff --git a/extcap_spawn.c b/extcap_spawn.c new file mode 100644 index 0000000000..555958c687 --- /dev/null +++ b/extcap_spawn.c @@ -0,0 +1,368 @@ +/* extcap_spawn.c + * + * Routines to spawn extcap external capture programs + * Copyright 2016, Roland Knall + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include +#include + +#include +#include +#ifdef _WIN32 +#include +#endif + +#include + +#include "extcap.h" +#include "extcap_spawn.h" + +#ifdef _WIN32 + +void win32_readfrompipe(HANDLE read_pipe, gint32 max_buffer, gchar * buffer) +{ + gboolean bSuccess = FALSE; + gint32 bytes_written = 0; + gint32 max_bytes = 0; + + DWORD dwRead; + DWORD bytes_avail = 0; + + for (;;) + { + if (!PeekNamedPipe(read_pipe, NULL, 0, NULL, &bytes_avail, NULL)) break; + if (bytes_avail <= 0) break; + + max_bytes = max_buffer - bytes_written - 1; + + bSuccess = ReadFile(read_pipe, &buffer[bytes_written], max_bytes, &dwRead, NULL); + if (!bSuccess || dwRead == 0) break; + + bytes_written += dwRead; + if ((bytes_written + 1) >= max_buffer) break; + } + + buffer[bytes_written] = '\0'; +} +#endif + +gboolean extcap_spawn_sync ( gchar * dirname, gchar * command, gint argc, gchar ** args, gchar ** command_output ) +{ + gboolean status = FALSE; + gboolean result = FALSE; + gchar ** argv = NULL; + gint cnt = 0; + gchar * local_output = NULL; +#ifdef _WIN32 + +#define BUFFER_SIZE 4096 + gchar buffer[BUFFER_SIZE]; + + GString *winargs = g_string_sized_new(200); + gchar *quoted_arg; + gunichar2 *wcommandline; + + 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; + + const gchar * oldpath = g_getenv("PATH"); + gchar * newpath = NULL; +#else + gint exit_status = 0; +#endif + + argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2)); + +#ifdef _WIN32 + newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); + g_setenv("PATH", newpath, TRUE); + + argv[0] = g_strescape(command, NULL); +#else + argv[0] = g_strdup(command); +#endif + + for ( cnt = 0; cnt < argc; cnt++ ) + argv[cnt+1] = args[cnt]; + argv[argc+1] = NULL; + +#ifdef _WIN32 + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); + return FALSE; + } + + if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) + { + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); + return FALSE; + } + + /* convert args array into a single string */ + /* XXX - could change sync_pipe_add_arg() instead */ + /* there is a drawback here: the length is internally limited to 1024 bytes */ + for (cnt = 0; argv[cnt] != 0; cnt++) { + if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(argv[cnt]); + g_string_append(winargs, quoted_arg); + g_free(quoted_arg); + } + + wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); + + 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 (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) + { + WaitForSingleObject(processInfo.hProcess, INFINITE); + win32_readfrompipe(child_stdout_rd, BUFFER_SIZE, buffer); + local_output = g_strdup_printf("%s", buffer); + + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + CloseHandle(child_stderr_rd); + CloseHandle(child_stderr_wr); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + status = TRUE; + } + else + status = FALSE; + + g_setenv("PATH", oldpath, TRUE); +#else + + status = g_spawn_sync(dirname, argv, NULL, + (GSpawnFlags) 0, NULL, NULL, &local_output, NULL, &exit_status, NULL); + + if (status && exit_status != 0) + status = FALSE; +#endif + + if (status) + { + if ( command_output != NULL && local_output != NULL ) + *command_output = g_strdup(local_output); + + result = TRUE; + } + + g_free(local_output); + g_free(argv); + + return result; +} + +GPid extcap_spawn_async(interface_options * interface, GPtrArray * args) +{ + GPid pid = INVALID_EXTCAP_PID; + +#ifdef _WIN32 + gint cnt = 0; + gchar ** tmp = NULL; + + GString *winargs = g_string_sized_new(200); + gchar *quoted_arg; + gunichar2 *wcommandline; + + 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; + + const gchar * oldpath = g_getenv("PATH"); + gchar * newpath = NULL; + +#endif + + extcap_userdata * userdata = NULL; + userdata = (extcap_userdata *) g_malloc0(sizeof(extcap_userdata)); + +#ifdef _WIN32 + newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); + g_setenv("PATH", newpath, TRUE); + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); + return FALSE; + } + + if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) + { + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); + return FALSE; + } + + /* convert args array into a single string */ + /* XXX - could change sync_pipe_add_arg() instead */ + /* there is a drawback here: the length is internally limited to 1024 bytes */ + for (tmp = (gchar **)args->pdata, cnt = 0; *tmp && **tmp; ++cnt, ++tmp) { + if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(*tmp); + g_string_append(winargs, quoted_arg); + g_free(quoted_arg); + } + + wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); + + 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 (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) + { + userdata->extcap_stderr_rd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY); + userdata->extcap_stdout_rd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY); + userdata->threadId = processInfo.hThread; + pid = processInfo.hProcess; + } + + g_setenv("PATH", oldpath, TRUE); +#else + g_spawn_async(NULL, (gchar **)args->pdata, NULL, (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, + &pid, NULL); +#endif + + userdata->pid = pid; + interface->extcap_userdata = userdata; + + return pid; +} + +#ifdef _WIN32 +gboolean +extcap_wait_for_pipe(HANDLE pipe_h, HANDLE pid) +{ + DWORD dw; + HANDLE handles[2]; + OVERLAPPED ov; + ov.Pointer = 0; + gboolean success = FALSE; + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + ConnectNamedPipe(pipe_h, &ov); + handles[0] = ov.hEvent; + handles[1] = pid; + + if (GetLastError() == ERROR_PIPE_CONNECTED) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap connected to pipe"); + } + else + { + dw = WaitForMultipleObjects(2, handles, FALSE, 30000); + if (dw == WAIT_OBJECT_0) + { + /* ConnectNamedPipe finished. */ + DWORD code; + + code = GetLastError(); + if (code == ERROR_IO_PENDING) + { + DWORD dummy; + if (!GetOverlappedResult(ov.hEvent, &ov, &dummy, TRUE)) + { + code = GetLastError(); + } + else + { + code = ERROR_SUCCESS; + success = TRUE; + } + } + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe code: %d", code); + } + else if (dw == (WAIT_OBJECT_0 + 1)) + { + /* extcap process terminated. */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe!"); + } + else if (dw == WAIT_TIMEOUT) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds!"); + } + else + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError()); + } + } + + CloseHandle(ov.hEvent); + + return success; +} +#endif + +/* + * Editor modelines - http://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: + */ -- cgit v1.2.1