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 * SPDX-License-Identifier: GPL-2.0-or-later
19 #include <wsutil/file_util.h>
20 #include <wsutil/filesystem.h>
21 #include <wsutil/ws_pipe.h>
23 #include <wsutil/win32-utils.h>
29 #include "extcap_spawn.h"
31 gboolean extcap_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **args, gchar **command_output)
33 gboolean status = FALSE;
34 gboolean result = FALSE;
37 gchar *local_output = NULL;
40 #define BUFFER_SIZE 16384
42 GString *winargs = g_string_sized_new(200);
44 gunichar2 *wcommandline;
47 PROCESS_INFORMATION processInfo;
49 SECURITY_ATTRIBUTES sa;
50 HANDLE child_stdout_rd = NULL;
51 HANDLE child_stdout_wr = NULL;
52 HANDLE child_stderr_rd = NULL;
53 HANDLE child_stderr_wr = NULL;
55 const gchar *oldpath = g_getenv("PATH");
56 gchar *newpath = NULL;
61 argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2));
64 newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath);
65 g_setenv("PATH", newpath, TRUE);
67 argv[0] = g_strescape(command, NULL);
69 argv[0] = g_strdup(command);
72 for (cnt = 0; cnt < argc; cnt++)
73 argv[cnt + 1] = args[cnt];
74 argv[argc + 1] = NULL;
78 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
79 sa.bInheritHandle = TRUE;
80 sa.lpSecurityDescriptor = NULL;
82 if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0))
85 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle");
89 if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0))
91 CloseHandle(child_stdout_rd);
92 CloseHandle(child_stdout_wr);
94 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle");
98 /* convert args array into a single string */
99 /* XXX - could change sync_pipe_add_arg() instead */
100 /* there is a drawback here: the length is internally limited to 1024 bytes */
101 for (cnt = 0; argv[cnt] != 0; cnt++) {
102 if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */
103 quoted_arg = protect_arg(argv[cnt]);
104 g_string_append(winargs, quoted_arg);
108 wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL);
110 memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
111 memset(&info, 0, sizeof(STARTUPINFO));
113 info.cb = sizeof(STARTUPINFO);
114 info.hStdError = child_stderr_wr;
115 info.hStdOutput = child_stdout_wr;
116 info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
117 info.wShowWindow = SW_HIDE;
119 if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
123 WaitForSingleObject(processInfo.hProcess, INFINITE);
124 buffer = (gchar*)g_malloc(BUFFER_SIZE);
125 status = ws_read_string_from_pipe(child_stdout_rd, buffer, BUFFER_SIZE);
128 local_output = g_strdup_printf("%s", buffer);
132 CloseHandle(child_stdout_rd);
133 CloseHandle(child_stdout_wr);
134 CloseHandle(child_stderr_rd);
135 CloseHandle(child_stderr_wr);
137 CloseHandle(processInfo.hProcess);
138 CloseHandle(processInfo.hThread);
143 g_setenv("PATH", oldpath, TRUE);
146 status = g_spawn_sync(dirname, argv, NULL,
147 (GSpawnFlags) 0, NULL, NULL, &local_output, NULL, &exit_status, NULL);
149 if (status && exit_status != 0)
155 if (command_output != NULL && local_output != NULL)
156 *command_output = g_strdup(local_output);
161 g_free(local_output);
168 GPid extcap_spawn_async(extcap_userdata *userdata, GPtrArray *args)
170 GPid pid = INVALID_EXTCAP_PID;
176 GString *winargs = g_string_sized_new(200);
178 gunichar2 *wcommandline;
181 PROCESS_INFORMATION processInfo;
183 SECURITY_ATTRIBUTES sa;
184 HANDLE child_stdout_rd = NULL;
185 HANDLE child_stdout_wr = NULL;
186 HANDLE child_stderr_rd = NULL;
187 HANDLE child_stderr_wr = NULL;
189 const gchar *oldpath = g_getenv("PATH");
190 gchar *newpath = NULL;
192 newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath);
193 g_setenv("PATH", newpath, TRUE);
195 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
196 sa.bInheritHandle = TRUE;
197 sa.lpSecurityDescriptor = NULL;
199 if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0))
201 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle");
205 if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0))
207 CloseHandle(child_stdout_rd);
208 CloseHandle(child_stdout_wr);
209 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle");
213 /* convert args array into a single string */
214 /* XXX - could change sync_pipe_add_arg() instead */
215 /* there is a drawback here: the length is internally limited to 1024 bytes */
216 for (tmp = (gchar **)args->pdata, cnt = 0; *tmp && **tmp; ++cnt, ++tmp) {
217 if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */
218 quoted_arg = protect_arg(*tmp);
219 g_string_append(winargs, quoted_arg);
223 wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL);
225 memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
226 memset(&info, 0, sizeof(STARTUPINFO));
228 info.cb = sizeof(STARTUPINFO);
229 info.hStdError = child_stderr_wr;
230 info.hStdOutput = child_stdout_wr;
231 info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
232 info.wShowWindow = SW_HIDE;
234 if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
236 userdata->extcap_stderr_rd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY);
237 userdata->extcap_stdout_rd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY);
238 userdata->threadId = processInfo.hThread;
239 pid = processInfo.hProcess;
242 g_setenv("PATH", oldpath, TRUE);
244 g_spawn_async_with_pipes(NULL, (gchar **)args->pdata, NULL,
245 (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
246 &pid, NULL, &userdata->extcap_stdout_rd, &userdata->extcap_stderr_rd, NULL);
264 extcap_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid)
266 PIPEINTS pipeinsts[3];
270 int num_waiting_to_connect = 0;
271 int num_handles = num_pipe_handles + 1; // PID handle is also added to list of handles.
273 if (num_pipe_handles == 0 || num_pipe_handles > 3)
275 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Invalid number of pipes given as argument.");
279 for (int i = 0; i < num_pipe_handles; ++i)
281 pipeinsts[i].pipeHandle = pipe_handles[i];
282 pipeinsts[i].ol.Pointer = 0;
283 pipeinsts[i].ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
284 pipeinsts[i].pendingIO = FALSE;
285 handles[i] = pipeinsts[i].ol.hEvent;
286 BOOL connected = ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol);
289 error_code = GetLastError();
290 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code);
294 switch (GetLastError())
296 case ERROR_IO_PENDING:
297 num_waiting_to_connect++;
298 pipeinsts[i].pendingIO = TRUE;
301 case ERROR_PIPE_CONNECTED:
302 if (SetEvent(pipeinsts[i].ol.hEvent))
305 } // Fallthrough if this fails.
308 error_code = GetLastError();
309 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code);
314 // Store pid of extcap process so it can be monitored in case it fails before the pipes has connceted.
315 handles[num_pipe_handles] = pid;
317 while(num_waiting_to_connect > 0)
319 dw = WaitForMultipleObjects(num_handles, handles, FALSE, 30000);
320 int idx = dw - WAIT_OBJECT_0;
321 if (dw == WAIT_TIMEOUT)
323 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds.");
326 // If index points to our handles array
327 else if (idx >= 0 && idx < num_handles)
329 if (idx < num_pipe_handles) // Index of pipe handle
331 if (pipeinsts[idx].pendingIO)
333 BOOL success = GetOverlappedResult(
334 pipeinsts[idx].pipeHandle, // handle to pipe
335 &pipeinsts[idx].ol, // OVERLAPPED structure
336 &cbRet, // bytes transferred
337 FALSE); // do not wait
341 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Error %d \n.", GetLastError());
346 pipeinsts[idx].pendingIO = FALSE;
347 CloseHandle(pipeinsts[idx].ol.hEvent);
348 num_waiting_to_connect--;
354 // Fail since index of 'pid' indicates that the pid of the extcap process has terminated.
355 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe.");
361 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError());
371 * Editor modelines - http://www.wireshark.org/tools/modelines.html
376 * indent-tabs-mode: nil
379 * vi: set shiftwidth=4 tabstop=8 expandtab:
380 * :indentSize=4:tabSize=8:noTabs=true: