nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* extcap_spawn.c |
2 | * |
||
3 | * Routines to spawn extcap external capture programs |
||
4 | * Copyright 2016, Roland Knall <rknall@gmail.com> |
||
5 | * |
||
6 | * Wireshark - Network traffic analyzer |
||
7 | * By Gerald Combs <gerald@wireshark.org> |
||
8 | * Copyright 1998 Gerald Combs |
||
9 | * |
||
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. |
||
14 | * |
||
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. |
||
19 | * |
||
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. |
||
23 | */ |
||
24 | |||
25 | #include <config.h> |
||
26 | |||
27 | #include <stdio.h> |
||
28 | #include <glib.h> |
||
29 | #include <string.h> |
||
30 | |||
31 | #include <wsutil/file_util.h> |
||
32 | #include <wsutil/filesystem.h> |
||
33 | #ifdef _WIN32 |
||
34 | #include <wsutil/win32-utils.h> |
||
35 | #endif |
||
36 | |||
37 | #include <log.h> |
||
38 | |||
39 | #include "extcap.h" |
||
40 | #include "extcap_spawn.h" |
||
41 | |||
42 | #ifdef _WIN32 |
||
43 | |||
44 | void win32_readfrompipe(HANDLE read_pipe, gint32 max_buffer, gchar * buffer) |
||
45 | { |
||
46 | gboolean bSuccess = FALSE; |
||
47 | gint32 bytes_written = 0; |
||
48 | gint32 max_bytes = 0; |
||
49 | |||
50 | DWORD dwRead; |
||
51 | DWORD bytes_avail = 0; |
||
52 | |||
53 | for (;;) |
||
54 | { |
||
55 | if (!PeekNamedPipe(read_pipe, NULL, 0, NULL, &bytes_avail, NULL)) break; |
||
56 | if (bytes_avail <= 0) break; |
||
57 | |||
58 | max_bytes = max_buffer - bytes_written - 1; |
||
59 | |||
60 | bSuccess = ReadFile(read_pipe, &buffer[bytes_written], max_bytes, &dwRead, NULL); |
||
61 | if (!bSuccess || dwRead == 0) break; |
||
62 | |||
63 | bytes_written += dwRead; |
||
64 | if ((bytes_written + 1) >= max_buffer) break; |
||
65 | } |
||
66 | |||
67 | buffer[bytes_written] = '\0'; |
||
68 | } |
||
69 | #endif |
||
70 | |||
71 | gboolean extcap_spawn_sync ( gchar * dirname, gchar * command, gint argc, gchar ** args, gchar ** command_output ) |
||
72 | { |
||
73 | gboolean status = FALSE; |
||
74 | gboolean result = FALSE; |
||
75 | gchar ** argv = NULL; |
||
76 | gint cnt = 0; |
||
77 | gchar * local_output = NULL; |
||
78 | #ifdef _WIN32 |
||
79 | |||
80 | #define BUFFER_SIZE 4096 |
||
81 | gchar buffer[BUFFER_SIZE]; |
||
82 | |||
83 | GString *winargs = g_string_sized_new(200); |
||
84 | gchar *quoted_arg; |
||
85 | gunichar2 *wcommandline; |
||
86 | |||
87 | STARTUPINFO info; |
||
88 | PROCESS_INFORMATION processInfo; |
||
89 | |||
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; |
||
95 | |||
96 | const gchar * oldpath = g_getenv("PATH"); |
||
97 | gchar * newpath = NULL; |
||
98 | #else |
||
99 | gint exit_status = 0; |
||
100 | #endif |
||
101 | |||
102 | argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2)); |
||
103 | |||
104 | #ifdef _WIN32 |
||
105 | newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); |
||
106 | g_setenv("PATH", newpath, TRUE); |
||
107 | |||
108 | argv[0] = g_strescape(command, NULL); |
||
109 | #else |
||
110 | argv[0] = g_strdup(command); |
||
111 | #endif |
||
112 | |||
113 | for ( cnt = 0; cnt < argc; cnt++ ) |
||
114 | argv[cnt+1] = args[cnt]; |
||
115 | argv[argc+1] = NULL; |
||
116 | |||
117 | #ifdef _WIN32 |
||
118 | |||
119 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
||
120 | sa.bInheritHandle = TRUE; |
||
121 | sa.lpSecurityDescriptor = NULL; |
||
122 | |||
123 | if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) |
||
124 | { |
||
125 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); |
||
126 | return FALSE; |
||
127 | } |
||
128 | |||
129 | if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) |
||
130 | { |
||
131 | CloseHandle(child_stdout_rd); |
||
132 | CloseHandle(child_stdout_wr); |
||
133 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); |
||
134 | return FALSE; |
||
135 | } |
||
136 | |||
137 | /* convert args array into a single string */ |
||
138 | /* XXX - could change sync_pipe_add_arg() instead */ |
||
139 | /* there is a drawback here: the length is internally limited to 1024 bytes */ |
||
140 | for (cnt = 0; argv[cnt] != 0; cnt++) { |
||
141 | if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ |
||
142 | quoted_arg = protect_arg(argv[cnt]); |
||
143 | g_string_append(winargs, quoted_arg); |
||
144 | g_free(quoted_arg); |
||
145 | } |
||
146 | |||
147 | wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); |
||
148 | |||
149 | memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); |
||
150 | memset(&info, 0, sizeof(STARTUPINFO)); |
||
151 | |||
152 | info.cb = sizeof(STARTUPINFO); |
||
153 | info.hStdError = child_stderr_wr; |
||
154 | info.hStdOutput = child_stdout_wr; |
||
155 | info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; |
||
156 | info.wShowWindow = SW_HIDE; |
||
157 | |||
158 | if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) |
||
159 | { |
||
160 | WaitForSingleObject(processInfo.hProcess, INFINITE); |
||
161 | win32_readfrompipe(child_stdout_rd, BUFFER_SIZE, buffer); |
||
162 | local_output = g_strdup_printf("%s", buffer); |
||
163 | |||
164 | CloseHandle(child_stdout_rd); |
||
165 | CloseHandle(child_stdout_wr); |
||
166 | CloseHandle(child_stderr_rd); |
||
167 | CloseHandle(child_stderr_wr); |
||
168 | |||
169 | CloseHandle(processInfo.hProcess); |
||
170 | CloseHandle(processInfo.hThread); |
||
171 | status = TRUE; |
||
172 | } |
||
173 | else |
||
174 | status = FALSE; |
||
175 | |||
176 | g_setenv("PATH", oldpath, TRUE); |
||
177 | #else |
||
178 | |||
179 | status = g_spawn_sync(dirname, argv, NULL, |
||
180 | (GSpawnFlags) 0, NULL, NULL, &local_output, NULL, &exit_status, NULL); |
||
181 | |||
182 | if (status && exit_status != 0) |
||
183 | status = FALSE; |
||
184 | #endif |
||
185 | |||
186 | if (status) |
||
187 | { |
||
188 | if ( command_output != NULL && local_output != NULL ) |
||
189 | *command_output = g_strdup(local_output); |
||
190 | |||
191 | result = TRUE; |
||
192 | } |
||
193 | |||
194 | g_free(local_output); |
||
195 | g_free(argv); |
||
196 | |||
197 | return result; |
||
198 | } |
||
199 | |||
200 | GPid extcap_spawn_async(extcap_userdata * userdata, GPtrArray * args) |
||
201 | { |
||
202 | GPid pid = INVALID_EXTCAP_PID; |
||
203 | |||
204 | #ifdef _WIN32 |
||
205 | gint cnt = 0; |
||
206 | gchar ** tmp = NULL; |
||
207 | |||
208 | GString *winargs = g_string_sized_new(200); |
||
209 | gchar *quoted_arg; |
||
210 | gunichar2 *wcommandline; |
||
211 | |||
212 | STARTUPINFO info; |
||
213 | PROCESS_INFORMATION processInfo; |
||
214 | |||
215 | SECURITY_ATTRIBUTES sa; |
||
216 | HANDLE child_stdout_rd = NULL; |
||
217 | HANDLE child_stdout_wr = NULL; |
||
218 | HANDLE child_stderr_rd = NULL; |
||
219 | HANDLE child_stderr_wr = NULL; |
||
220 | |||
221 | const gchar * oldpath = g_getenv("PATH"); |
||
222 | gchar * newpath = NULL; |
||
223 | |||
224 | newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); |
||
225 | g_setenv("PATH", newpath, TRUE); |
||
226 | |||
227 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
||
228 | sa.bInheritHandle = TRUE; |
||
229 | sa.lpSecurityDescriptor = NULL; |
||
230 | |||
231 | if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) |
||
232 | { |
||
233 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); |
||
234 | return FALSE; |
||
235 | } |
||
236 | |||
237 | if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) |
||
238 | { |
||
239 | CloseHandle(child_stdout_rd); |
||
240 | CloseHandle(child_stdout_wr); |
||
241 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); |
||
242 | return FALSE; |
||
243 | } |
||
244 | |||
245 | /* convert args array into a single string */ |
||
246 | /* XXX - could change sync_pipe_add_arg() instead */ |
||
247 | /* there is a drawback here: the length is internally limited to 1024 bytes */ |
||
248 | for (tmp = (gchar **)args->pdata, cnt = 0; *tmp && **tmp; ++cnt, ++tmp) { |
||
249 | if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ |
||
250 | quoted_arg = protect_arg(*tmp); |
||
251 | g_string_append(winargs, quoted_arg); |
||
252 | g_free(quoted_arg); |
||
253 | } |
||
254 | |||
255 | wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); |
||
256 | |||
257 | memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); |
||
258 | memset(&info, 0, sizeof(STARTUPINFO)); |
||
259 | |||
260 | info.cb = sizeof(STARTUPINFO); |
||
261 | info.hStdError = child_stderr_wr; |
||
262 | info.hStdOutput = child_stdout_wr; |
||
263 | info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; |
||
264 | info.wShowWindow = SW_HIDE; |
||
265 | |||
266 | if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) |
||
267 | { |
||
268 | userdata->extcap_stderr_rd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY); |
||
269 | userdata->extcap_stdout_rd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY); |
||
270 | userdata->threadId = processInfo.hThread; |
||
271 | pid = processInfo.hProcess; |
||
272 | } |
||
273 | |||
274 | g_setenv("PATH", oldpath, TRUE); |
||
275 | #else |
||
276 | g_spawn_async_with_pipes(NULL, (gchar **)args->pdata, NULL, |
||
277 | (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, |
||
278 | &pid, NULL, &userdata->extcap_stdout_rd, &userdata->extcap_stderr_rd, NULL); |
||
279 | #endif |
||
280 | |||
281 | userdata->pid = pid; |
||
282 | |||
283 | return pid; |
||
284 | } |
||
285 | |||
286 | #ifdef _WIN32 |
||
287 | gboolean |
||
288 | extcap_wait_for_pipe(HANDLE pipe_h, HANDLE pid) |
||
289 | { |
||
290 | DWORD dw; |
||
291 | HANDLE handles[2]; |
||
292 | OVERLAPPED ov; |
||
293 | ov.Pointer = 0; |
||
294 | gboolean success = FALSE; |
||
295 | ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); |
||
296 | |||
297 | ConnectNamedPipe(pipe_h, &ov); |
||
298 | handles[0] = ov.hEvent; |
||
299 | handles[1] = pid; |
||
300 | |||
301 | if (GetLastError() == ERROR_PIPE_CONNECTED) |
||
302 | { |
||
303 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap connected to pipe"); |
||
304 | } |
||
305 | else |
||
306 | { |
||
307 | dw = WaitForMultipleObjects(2, handles, FALSE, 30000); |
||
308 | if (dw == WAIT_OBJECT_0) |
||
309 | { |
||
310 | /* ConnectNamedPipe finished. */ |
||
311 | DWORD code; |
||
312 | |||
313 | code = GetLastError(); |
||
314 | if (code == ERROR_IO_PENDING) |
||
315 | { |
||
316 | DWORD dummy; |
||
317 | if (!GetOverlappedResult(ov.hEvent, &ov, &dummy, TRUE)) |
||
318 | { |
||
319 | code = GetLastError(); |
||
320 | } |
||
321 | else |
||
322 | { |
||
323 | code = ERROR_SUCCESS; |
||
324 | success = TRUE; |
||
325 | } |
||
326 | } |
||
327 | |||
328 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe code: %d", code); |
||
329 | } |
||
330 | else if (dw == (WAIT_OBJECT_0 + 1)) |
||
331 | { |
||
332 | /* extcap process terminated. */ |
||
333 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe!"); |
||
334 | } |
||
335 | else if (dw == WAIT_TIMEOUT) |
||
336 | { |
||
337 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds!"); |
||
338 | } |
||
339 | else |
||
340 | { |
||
341 | g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError()); |
||
342 | } |
||
343 | } |
||
344 | |||
345 | CloseHandle(ov.hEvent); |
||
346 | |||
347 | return success; |
||
348 | } |
||
349 | #endif |
||
350 | |||
351 | /* |
||
352 | * Editor modelines - http://www.wireshark.org/tools/modelines.html |
||
353 | * |
||
354 | * Local variables: |
||
355 | * c-basic-offset: 4 |
||
356 | * tab-width: 8 |
||
357 | * indent-tabs-mode: nil |
||
358 | * End: |
||
359 | * |
||
360 | * vi: set shiftwidth=4 tabstop=8 expandtab: |
||
361 | * :indentSize=4:tabSize=8:noTabs=true: |
||
362 | */ |