diff options
author | Gerald Combs <gerald@wireshark.org> | 2018-03-01 15:31:45 -0800 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2018-03-02 05:22:20 +0000 |
commit | 1a0987904fa571dc5abce03726e4ca3e17793574 (patch) | |
tree | 59b7bfe30517005b046bddb9fcae2315f48570fa /wsutil | |
parent | bf4c2fd82b30d8b088fcb1235263dce9173d4cd7 (diff) |
Generalize our process spawning code.
Move the contents of extcap_spawn to ws_pipe. Rename various extcap_*
prefixes to ws_pipe_*. Open stdin when we spawn processes.
Change-Id: I9286295443ee955bb6328b0ed6f945ee0bb2a798
Reviewed-on: https://code.wireshark.org/review/26216
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'wsutil')
-rw-r--r-- | wsutil/ws_pipe.c | 352 | ||||
-rw-r--r-- | wsutil/ws_pipe.h | 26 |
2 files changed, 378 insertions, 0 deletions
diff --git a/wsutil/ws_pipe.c b/wsutil/ws_pipe.c index 2c0f28b89e..3649276864 100644 --- a/wsutil/ws_pipe.c +++ b/wsutil/ws_pipe.c @@ -18,6 +18,8 @@ #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 @@ -28,8 +30,358 @@ #include <glib.h> #include <log.h> +#include <wsutil/filesystem.h> #include "wsutil/ws_pipe.h" +gboolean ws_pipe_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 16384 + + 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_free(argv[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_free(argv[0]); + 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)) + { + gchar* buffer; + + WaitForSingleObject(processInfo.hProcess, INFINITE); + buffer = (gchar*)g_malloc(BUFFER_SIZE); + status = ws_read_string_from_pipe(child_stdout_rd, buffer, BUFFER_SIZE); + if (status) + { + local_output = g_strdup_printf("%s", buffer); + } + g_free(buffer); + + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + CloseHandle(child_stderr_rd); + CloseHandle(child_stderr_wr); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } + 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[0]); + g_free(argv); + + return result; +} + +GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, 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_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; + + const gchar *oldpath = g_getenv("PATH"); + gchar *newpath = NULL; + + 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_stdin_rd, &child_stdin_wr, &sa, 0)) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdin handle"); + return FALSE; + } + + 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.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 (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) + { + ws_pipe->stdin_fd = _open_osfhandle((intptr_t)(child_stdin_wr), _O_BINARY); + ws_pipe->stdout_fd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY); + ws_pipe->stderr_fd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY); + ws_pipe->threadId = processInfo.hThread; + pid = processInfo.hProcess; + } + + g_setenv("PATH", oldpath, TRUE); +#else + g_spawn_async_with_pipes(NULL, (gchar **)args->pdata, NULL, + (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, + &pid, &ws_pipe->stdin_fd, &ws_pipe->stdout_fd, &ws_pipe->stderr_fd, NULL); +#endif + + ws_pipe->pid = pid; + + return pid; +} + +#ifdef _WIN32 + +typedef struct +{ + HANDLE pipeHandle; + OVERLAPPED ol; + BOOL pendingIO; +} PIPEINTS; + +gboolean +ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid) +{ + PIPEINTS pipeinsts[3]; + DWORD dw, cbRet; + HANDLE handles[4]; + int error_code; + int num_waiting_to_connect = 0; + int num_handles = num_pipe_handles + 1; // PID handle is also added to list of handles. + + if (num_pipe_handles == 0 || num_pipe_handles > 3) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Invalid number of pipes given as argument."); + return FALSE; + } + + for (int i = 0; i < num_pipe_handles; ++i) + { + pipeinsts[i].pipeHandle = pipe_handles[i]; + pipeinsts[i].ol.Pointer = 0; + pipeinsts[i].ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + pipeinsts[i].pendingIO = FALSE; + handles[i] = pipeinsts[i].ol.hEvent; + BOOL connected = ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol); + if (connected) + { + error_code = GetLastError(); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code); + return FALSE; + } + + switch (GetLastError()) + { + case ERROR_IO_PENDING: + num_waiting_to_connect++; + pipeinsts[i].pendingIO = TRUE; + break; + + case ERROR_PIPE_CONNECTED: + if (SetEvent(pipeinsts[i].ol.hEvent)) + { + break; + } // Fallthrough if this fails. + + default: + error_code = GetLastError(); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code); + return FALSE; + } + } + + // Store pid of extcap process so it can be monitored in case it fails before the pipes has connceted. + handles[num_pipe_handles] = pid; + + while(num_waiting_to_connect > 0) + { + dw = WaitForMultipleObjects(num_handles, handles, FALSE, 30000); + int idx = dw - WAIT_OBJECT_0; + if (dw == WAIT_TIMEOUT) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds."); + return FALSE; + } + // If index points to our handles array + else if (idx >= 0 && idx < num_handles) + { + if (idx < num_pipe_handles) // Index of pipe handle + { + if (pipeinsts[idx].pendingIO) + { + BOOL success = GetOverlappedResult( + pipeinsts[idx].pipeHandle, // handle to pipe + &pipeinsts[idx].ol, // OVERLAPPED structure + &cbRet, // bytes transferred + FALSE); // do not wait + + if (!success) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Error %d \n.", GetLastError()); + return FALSE; + } + else + { + pipeinsts[idx].pendingIO = FALSE; + CloseHandle(pipeinsts[idx].ol.hEvent); + num_waiting_to_connect--; + } + } + } + else // Index of PID + { + // Fail since index of 'pid' indicates that the pid of the extcap process has terminated. + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe."); + return FALSE; + } + } + else + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError()); + return FALSE; + } + } + + return TRUE; +} +#endif + gboolean ws_pipe_data_available(int pipe_fd) { diff --git a/wsutil/ws_pipe.h b/wsutil/ws_pipe.h index 8a4870f2cc..e69873c449 100644 --- a/wsutil/ws_pipe.h +++ b/wsutil/ws_pipe.h @@ -17,6 +17,12 @@ #include <glib.h> #ifdef _WIN32 +#define INVALID_EXTCAP_PID INVALID_HANDLE_VALUE +#else +#define INVALID_EXTCAP_PID (GPid)-1 +#endif + +#ifdef _WIN32 #include <windows.h> #include <io.h> #define ws_pipe_handle HANDLE @@ -26,6 +32,26 @@ #define ws_get_pipe_handle(pipe_fd) (pipe_fd) #endif +typedef struct _ws_pipe_t { + GPid pid; + gchar *stderr_msg; + gint exitcode; + gint stdin_fd; + gint stdout_fd; + gint stderr_fd; +#ifdef _WIN32 + HANDLE threadId; +#endif +} ws_pipe_t; + +WS_DLL_PUBLIC gboolean ws_pipe_spawn_sync ( gchar * dirname, gchar * command, gint argc, gchar ** argv, gchar ** command_output ); + +WS_DLL_PUBLIC GPid ws_pipe_spawn_async (ws_pipe_t * ws_pipe, GPtrArray * args ); + +#ifdef _WIN32 +WS_DLL_PUBLIC gboolean ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid); +#endif + WS_DLL_PUBLIC gboolean ws_pipe_data_available(int pipe_fd); WS_DLL_PUBLIC gboolean ws_read_string_from_pipe(ws_pipe_handle read_pipe, |