aboutsummaryrefslogtreecommitdiffstats
path: root/wsutil/ws_pipe.c
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2018-03-01 15:31:45 -0800
committerAnders Broman <a.broman58@gmail.com>2018-03-02 05:22:20 +0000
commit1a0987904fa571dc5abce03726e4ca3e17793574 (patch)
tree59b7bfe30517005b046bddb9fcae2315f48570fa /wsutil/ws_pipe.c
parentbf4c2fd82b30d8b088fcb1235263dce9173d4cd7 (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/ws_pipe.c')
-rw-r--r--wsutil/ws_pipe.c352
1 files changed, 352 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)
{