3 * Routines to spawn extcap external capture programs
4 * Copyright 2016, Roland Knall <rknall@gmail.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <wsutil/file_util.h>
32 #include <wsutil/filesystem.h>
34 #include <wsutil/win32-utils.h>
40 #include "extcap_spawn.h"
44 void win32_readfrompipe(HANDLE read_pipe, gint32 max_buffer, gchar *buffer)
46 gboolean bSuccess = FALSE;
47 gint32 bytes_written = 0;
51 DWORD bytes_avail = 0;
55 if (!PeekNamedPipe(read_pipe, NULL, 0, NULL, &bytes_avail, NULL)) break;
56 if (bytes_avail <= 0) break;
58 max_bytes = max_buffer - bytes_written - 1;
60 bSuccess = ReadFile(read_pipe, &buffer[bytes_written], max_bytes, &dwRead, NULL);
61 if (!bSuccess || dwRead == 0) break;
63 bytes_written += dwRead;
64 if ((bytes_written + 1) >= max_buffer) break;
67 buffer[bytes_written] = '\0';
71 gboolean extcap_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **args, gchar **command_output)
73 gboolean status = FALSE;
74 gboolean result = FALSE;
77 gchar *local_output = NULL;
80 #define BUFFER_SIZE 4096
81 gchar buffer[BUFFER_SIZE];
83 GString *winargs = g_string_sized_new(200);
85 gunichar2 *wcommandline;
88 PROCESS_INFORMATION processInfo;
90 SECURITY_ATTRIBUTES sa;
91 HANDLE child_stdout_rd = NULL;
92 HANDLE child_stdout_wr = NULL;
93 HANDLE child_stderr_rd = NULL;
94 HANDLE child_stderr_wr = NULL;
96 const gchar *oldpath = g_getenv("PATH");
97 gchar *newpath = NULL;
102 argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2));
105 newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath);
106 g_setenv("PATH", newpath, TRUE);
108 argv[0] = g_strescape(command, NULL);
110 argv[0] = g_strdup(command);
113 for (cnt = 0; cnt < argc; cnt++)
114 argv[cnt + 1] = args[cnt];
115 argv[argc + 1] = NULL;
119 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
120 sa.bInheritHandle = TRUE;
121 sa.lpSecurityDescriptor = NULL;
123 if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0))
126 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle");
130 if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0))
132 CloseHandle(child_stdout_rd);
133 CloseHandle(child_stdout_wr);
135 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle");
139 /* convert args array into a single string */
140 /* XXX - could change sync_pipe_add_arg() instead */
141 /* there is a drawback here: the length is internally limited to 1024 bytes */
142 for (cnt = 0; argv[cnt] != 0; cnt++) {
143 if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */
144 quoted_arg = protect_arg(argv[cnt]);
145 g_string_append(winargs, quoted_arg);
149 wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL);
151 memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
152 memset(&info, 0, sizeof(STARTUPINFO));
154 info.cb = sizeof(STARTUPINFO);
155 info.hStdError = child_stderr_wr;
156 info.hStdOutput = child_stdout_wr;
157 info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
158 info.wShowWindow = SW_HIDE;
160 if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
162 WaitForSingleObject(processInfo.hProcess, INFINITE);
163 win32_readfrompipe(child_stdout_rd, BUFFER_SIZE, buffer);
164 local_output = g_strdup_printf("%s", buffer);
166 CloseHandle(child_stdout_rd);
167 CloseHandle(child_stdout_wr);
168 CloseHandle(child_stderr_rd);
169 CloseHandle(child_stderr_wr);
171 CloseHandle(processInfo.hProcess);
172 CloseHandle(processInfo.hThread);
178 g_setenv("PATH", oldpath, TRUE);
181 status = g_spawn_sync(dirname, argv, NULL,
182 (GSpawnFlags) 0, NULL, NULL, &local_output, NULL, &exit_status, NULL);
184 if (status && exit_status != 0)
190 if (command_output != NULL && local_output != NULL)
191 *command_output = g_strdup(local_output);
196 g_free(local_output);
203 GPid extcap_spawn_async(extcap_userdata *userdata, GPtrArray *args)
205 GPid pid = INVALID_EXTCAP_PID;
211 GString *winargs = g_string_sized_new(200);
213 gunichar2 *wcommandline;
216 PROCESS_INFORMATION processInfo;
218 SECURITY_ATTRIBUTES sa;
219 HANDLE child_stdout_rd = NULL;
220 HANDLE child_stdout_wr = NULL;
221 HANDLE child_stderr_rd = NULL;
222 HANDLE child_stderr_wr = NULL;
224 const gchar *oldpath = g_getenv("PATH");
225 gchar *newpath = NULL;
227 newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath);
228 g_setenv("PATH", newpath, TRUE);
230 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
231 sa.bInheritHandle = TRUE;
232 sa.lpSecurityDescriptor = NULL;
234 if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0))
236 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle");
240 if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0))
242 CloseHandle(child_stdout_rd);
243 CloseHandle(child_stdout_wr);
244 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle");
248 /* convert args array into a single string */
249 /* XXX - could change sync_pipe_add_arg() instead */
250 /* there is a drawback here: the length is internally limited to 1024 bytes */
251 for (tmp = (gchar **)args->pdata, cnt = 0; *tmp && **tmp; ++cnt, ++tmp) {
252 if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */
253 quoted_arg = protect_arg(*tmp);
254 g_string_append(winargs, quoted_arg);
258 wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL);
260 memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
261 memset(&info, 0, sizeof(STARTUPINFO));
263 info.cb = sizeof(STARTUPINFO);
264 info.hStdError = child_stderr_wr;
265 info.hStdOutput = child_stdout_wr;
266 info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
267 info.wShowWindow = SW_HIDE;
269 if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
271 userdata->extcap_stderr_rd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY);
272 userdata->extcap_stdout_rd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY);
273 userdata->threadId = processInfo.hThread;
274 pid = processInfo.hProcess;
277 g_setenv("PATH", oldpath, TRUE);
279 g_spawn_async_with_pipes(NULL, (gchar **)args->pdata, NULL,
280 (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
281 &pid, NULL, &userdata->extcap_stdout_rd, &userdata->extcap_stderr_rd, NULL);
291 extcap_wait_for_pipe(HANDLE pipe_h, HANDLE pid)
296 gboolean success = FALSE;
298 ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
300 ConnectNamedPipe(pipe_h, &ov);
301 handles[0] = ov.hEvent;
304 if (GetLastError() == ERROR_PIPE_CONNECTED)
306 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap connected to pipe");
310 dw = WaitForMultipleObjects(2, handles, FALSE, 30000);
311 if (dw == WAIT_OBJECT_0)
313 /* ConnectNamedPipe finished. */
316 code = GetLastError();
317 if (code == ERROR_IO_PENDING)
320 if (!GetOverlappedResult(ov.hEvent, &ov, &dummy, TRUE))
322 code = GetLastError();
326 code = ERROR_SUCCESS;
331 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe code: %d", code);
333 else if (dw == (WAIT_OBJECT_0 + 1))
335 /* extcap process terminated. */
336 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe.");
338 else if (dw == WAIT_TIMEOUT)
340 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds.");
344 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError());
348 CloseHandle(ov.hEvent);
355 * Editor modelines - http://www.wireshark.org/tools/modelines.html
360 * indent-tabs-mode: nil
363 * vi: set shiftwidth=4 tabstop=8 expandtab:
364 * :indentSize=4:tabSize=8:noTabs=true: