File: | builds/wireshark/wireshark/wsutil/ws_pipe.c |
Warning: | line 641, column 18 Potential leak of memory pointed to by 'argv' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* ws_pipe.c | |||
2 | * | |||
3 | * Routines for handling pipes. | |||
4 | * | |||
5 | * Wireshark - Network traffic analyzer | |||
6 | * By Gerald Combs <[email protected]> | |||
7 | * Copyright 1998 Gerald Combs | |||
8 | * | |||
9 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
10 | */ | |||
11 | ||||
12 | #include <config.h> | |||
13 | #define WS_LOG_DOMAIN"Capture" LOG_DOMAIN_CAPTURE"Capture" | |||
14 | #include "wsutil/ws_pipe.h" | |||
15 | ||||
16 | #include <stdio.h> | |||
17 | #include <stdlib.h> | |||
18 | #include <string.h> | |||
19 | ||||
20 | #ifdef _WIN32 | |||
21 | #include <windows.h> | |||
22 | #include <io.h> | |||
23 | #include <fcntl.h> /* for _O_BINARY */ | |||
24 | #include <wsutil/win32-utils.h> | |||
25 | #else | |||
26 | #include <unistd.h> | |||
27 | #ifdef HAVE_SYS_SELECT_H | |||
28 | #include <sys/select.h> | |||
29 | #endif | |||
30 | #endif | |||
31 | ||||
32 | #if !GLIB_CHECK_VERSION(2, 58, 2)(2 > (2) || (2 == (2) && 80 > (58)) || (2 == (2 ) && 80 == (58) && 0 >= (2))) | |||
33 | #ifdef __linux__1 | |||
34 | #define HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG | |||
35 | #include <fcntl.h> | |||
36 | #include <sys/syscall.h> /* for syscall and SYS_getdents64 */ | |||
37 | #include <wsutil/file_util.h> /* for ws_open -> open to pacify checkAPIs.pl */ | |||
38 | #endif | |||
39 | #endif | |||
40 | ||||
41 | #include "wsutil/filesystem.h" | |||
42 | #include "wsutil/wslog.h" | |||
43 | ||||
44 | #ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG | |||
45 | struct linux_dirent64 { | |||
46 | uint64_t d_ino; /* 64-bit inode number */ | |||
47 | uint64_t d_off; /* 64-bit offset to next structure */ | |||
48 | unsigned short d_reclen; /* Size of this dirent */ | |||
49 | unsigned char d_type; /* File type */ | |||
50 | char d_name[]; /* Filename (null-terminated) */ | |||
51 | }; | |||
52 | ||||
53 | /* Async-signal-safe string to integer conversion. */ | |||
54 | static int | |||
55 | filename_to_fd(const char *p) | |||
56 | { | |||
57 | char c; | |||
58 | int fd = 0; | |||
59 | const int cutoff = INT_MAX2147483647 / 10; | |||
60 | const int cutlim = INT_MAX2147483647 % 10; | |||
61 | ||||
62 | if (*p == '\0') | |||
63 | return -1; | |||
64 | ||||
65 | while ((c = *p++) != '\0') { | |||
66 | if (!g_ascii_isdigit(c)((g_ascii_table[(guchar) (c)] & G_ASCII_DIGIT) != 0)) | |||
67 | return -1; | |||
68 | c -= '0'; | |||
69 | ||||
70 | /* Check for overflow. */ | |||
71 | if (fd > cutoff || (fd == cutoff && c > cutlim)) | |||
72 | return -1; | |||
73 | ||||
74 | fd = fd * 10 + c; | |||
75 | } | |||
76 | ||||
77 | return fd; | |||
78 | } | |||
79 | ||||
80 | static void | |||
81 | close_non_standard_fds_linux(void * user_data _U___attribute__((unused))) | |||
82 | { | |||
83 | /* | |||
84 | * GLib 2.14.2 and newer (up to at least GLib 2.58.1) on Linux with multiple | |||
85 | * threads can deadlock in the child process due to use of opendir (which | |||
86 | * is not async-signal-safe). To avoid this, disable the broken code path | |||
87 | * and manually close file descriptors using async-signal-safe code only. | |||
88 | * Use CLOEXEC to allow reporting of execve errors to the parent via a pipe. | |||
89 | * https://gitlab.gnome.org/GNOME/glib/issues/1014 | |||
90 | * https://gitlab.gnome.org/GNOME/glib/merge_requests/490 | |||
91 | */ | |||
92 | int dir_fd = ws_open("/proc/self/fd", O_RDONLY | O_DIRECTORY); | |||
93 | if (dir_fd >= 0) { | |||
94 | char buf[4096]; | |||
95 | int nread, fd; | |||
96 | struct linux_dirent64 *de; | |||
97 | ||||
98 | while ((nread = (int) syscall(SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0) { | |||
99 | for (int pos = 0; pos < nread; pos += de->d_reclen) { | |||
100 | de = (struct linux_dirent64 *)(buf + pos); | |||
101 | fd = filename_to_fd(de->d_name); | |||
102 | if (fd > STDERR_FILENO2 && fd != dir_fd) { | |||
103 | /* Close all other (valid) file descriptors above stderr. */ | |||
104 | fcntl(fd, F_SETFD, FD_CLOEXEC); | |||
105 | } | |||
106 | } | |||
107 | } | |||
108 | ||||
109 | close(dir_fd); | |||
110 | } else { | |||
111 | /* Slow fallback in case /proc is not mounted */ | |||
112 | for (int fd = STDERR_FILENO2 + 1; fd < getdtablesize(); fd++) { | |||
113 | fcntl(fd, F_SETFD, FD_CLOEXEC); | |||
114 | } | |||
115 | } | |||
116 | } | |||
117 | #endif | |||
118 | ||||
119 | #ifdef _WIN32 | |||
120 | static ULONG pipe_serial_number; | |||
121 | ||||
122 | /* Alternative for CreatePipe() where read handle is opened with FILE_FLAG_OVERLAPPED */ | |||
123 | static bool_Bool | |||
124 | ws_pipe_create_overlapped_read(HANDLE *read_pipe_handle, HANDLE *write_pipe_handle, | |||
125 | SECURITY_ATTRIBUTES *sa, DWORD suggested_buffer_size) | |||
126 | { | |||
127 | HANDLE read_pipe, write_pipe; | |||
128 | unsigned char *name = ws_strdup_printf("\\\\.\\Pipe\\WiresharkWsPipe.%08lx.%08lx",wmem_strdup_printf(((void*)0), "\\\\.\\Pipe\\WiresharkWsPipe.%08lx.%08lx" , GetCurrentProcessId(), InterlockedIncrement(&pipe_serial_number )) | |||
129 | GetCurrentProcessId(),wmem_strdup_printf(((void*)0), "\\\\.\\Pipe\\WiresharkWsPipe.%08lx.%08lx" , GetCurrentProcessId(), InterlockedIncrement(&pipe_serial_number )) | |||
130 | InterlockedIncrement(&pipe_serial_number))wmem_strdup_printf(((void*)0), "\\\\.\\Pipe\\WiresharkWsPipe.%08lx.%08lx" , GetCurrentProcessId(), InterlockedIncrement(&pipe_serial_number )); | |||
131 | gunichar2 *wname = g_utf8_to_utf16(name, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
132 | ||||
133 | g_free(name); | |||
134 | ||||
135 | read_pipe = CreateNamedPipe(wname, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, | |||
136 | PIPE_TYPE_BYTE | PIPE_WAIT, 1, | |||
137 | suggested_buffer_size, suggested_buffer_size, | |||
138 | 0, sa); | |||
139 | if (INVALID_HANDLE_VALUE == read_pipe) | |||
140 | { | |||
141 | g_free(wname); | |||
142 | return false0; | |||
143 | } | |||
144 | ||||
145 | write_pipe = CreateFile(wname, GENERIC_WRITE, 0, sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL((void*)0)); | |||
146 | if (INVALID_HANDLE_VALUE == write_pipe) | |||
147 | { | |||
148 | DWORD error = GetLastError(); | |||
149 | CloseHandle(read_pipe); | |||
150 | SetLastError(error); | |||
151 | g_free(wname); | |||
152 | return false0; | |||
153 | } | |||
154 | ||||
155 | *read_pipe_handle = read_pipe; | |||
156 | *write_pipe_handle = write_pipe; | |||
157 | g_free(wname); | |||
158 | return true1; | |||
159 | } | |||
160 | #endif | |||
161 | ||||
162 | /** | |||
163 | * Helper to convert a command and argument list to an NULL-terminated 'argv' | |||
164 | * array, suitable for g_spawn_sync and friends. Free with g_strfreev. | |||
165 | */ | |||
166 | static char ** | |||
167 | convert_to_argv(const char *command, int args_count, char *const *args) | |||
168 | { | |||
169 | char **argv = g_new(char *, args_count + 2)((char * *) g_malloc_n ((args_count + 2), sizeof (char *))); | |||
170 | // The caller does not seem to modify this, but g_spawn_sync uses 'char **' | |||
171 | // as opposed to 'const char **', so just to be sure clone it. | |||
172 | argv[0] = g_strdup(command)g_strdup_inline (command); | |||
173 | for (int i = 0; i < args_count; i++) { | |||
174 | // Empty arguments may indicate a bug in Wireshark. Extcap for example | |||
175 | // omits arguments when their string value is empty. On Windows, empty | |||
176 | // arguments would silently be ignored because protect_arg returns an | |||
177 | // empty string, therefore we print a warning here. | |||
178 | if (!*args[i]) { | |||
179 | ws_warning("Empty argument %d in arguments list", i)do { if (1) { ws_log_full("Capture", LOG_LEVEL_WARNING, "wsutil/ws_pipe.c" , 179, __func__, "Empty argument %d in arguments list", i); } } while (0); | |||
180 | } | |||
181 | argv[1 + i] = g_strdup(args[i])g_strdup_inline (args[i]); | |||
182 | } | |||
183 | argv[args_count + 1] = NULL((void*)0); | |||
184 | return argv; | |||
185 | } | |||
186 | ||||
187 | /** | |||
188 | * Convert a non-empty NULL-terminated array of command and arguments to a | |||
189 | * string for displaying purposes. On Windows, the returned string is properly | |||
190 | * escaped and can be executed directly. | |||
191 | */ | |||
192 | static char * | |||
193 | convert_to_command_line(char **argv) | |||
194 | { | |||
195 | GString *command_line = g_string_sized_new(200); | |||
196 | #ifdef _WIN32 | |||
197 | // The first argument must always be quoted even if it does not contain | |||
198 | // special characters or else CreateProcess might consider arguments as part | |||
199 | // of the executable. | |||
200 | char *quoted_arg = protect_arg(argv[0]); | |||
201 | if (quoted_arg[0] != '"') { | |||
202 | g_string_append_c(command_line, '"')g_string_append_c_inline (command_line, '"'); | |||
203 | g_string_append(command_line, quoted_arg)(__builtin_constant_p (quoted_arg) ? __extension__ ({ const char * const __val = (quoted_arg); g_string_append_len_inline (command_line , __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + ! (__val))) : (gssize) -1); }) : g_string_append_len_inline (command_line , quoted_arg, (gssize) -1)); | |||
204 | g_string_append_c(command_line, '"')g_string_append_c_inline (command_line, '"'); | |||
205 | } else { | |||
206 | g_string_append(command_line, quoted_arg)(__builtin_constant_p (quoted_arg) ? __extension__ ({ const char * const __val = (quoted_arg); g_string_append_len_inline (command_line , __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + ! (__val))) : (gssize) -1); }) : g_string_append_len_inline (command_line , quoted_arg, (gssize) -1)); | |||
207 | } | |||
208 | g_free(quoted_arg); | |||
209 | ||||
210 | for (int i = 1; argv[i]; i++) { | |||
211 | quoted_arg = protect_arg(argv[i]); | |||
212 | g_string_append_c(command_line, ' ')g_string_append_c_inline (command_line, ' '); | |||
213 | g_string_append(command_line, quoted_arg)(__builtin_constant_p (quoted_arg) ? __extension__ ({ const char * const __val = (quoted_arg); g_string_append_len_inline (command_line , __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + ! (__val))) : (gssize) -1); }) : g_string_append_len_inline (command_line , quoted_arg, (gssize) -1)); | |||
214 | g_free(quoted_arg); | |||
215 | } | |||
216 | #else | |||
217 | for (int i = 0; argv[i]; i++) { | |||
218 | char *quoted_arg = g_shell_quote(argv[i]); | |||
219 | if (i != 0) { | |||
220 | g_string_append_c(command_line, ' ')g_string_append_c_inline (command_line, ' '); | |||
221 | } | |||
222 | g_string_append(command_line, quoted_arg)(__builtin_constant_p (quoted_arg) ? __extension__ ({ const char * const __val = (quoted_arg); g_string_append_len_inline (command_line , __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + ! (__val))) : (gssize) -1); }) : g_string_append_len_inline (command_line , quoted_arg, (gssize) -1)); | |||
223 | g_free(quoted_arg); | |||
224 | } | |||
225 | #endif | |||
226 | return g_string_free(command_line, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((command_line ), ((0))) : g_string_free_and_steal (command_line)) : (g_string_free ) ((command_line), ((0)))); | |||
227 | } | |||
228 | ||||
229 | bool_Bool ws_pipe_spawn_sync(const char *working_directory, const char *command, int argc, char **args, char **command_output) | |||
230 | { | |||
231 | bool_Bool status = false0; | |||
232 | bool_Bool result = false0; | |||
233 | char *local_output = NULL((void*)0); | |||
234 | #ifdef _WIN32 | |||
235 | ||||
236 | #define BUFFER_SIZE 16384 | |||
237 | ||||
238 | STARTUPINFO info; | |||
239 | PROCESS_INFORMATION processInfo; | |||
240 | ||||
241 | SECURITY_ATTRIBUTES sa; | |||
242 | HANDLE child_stdout_rd = NULL((void*)0); | |||
243 | HANDLE child_stdout_wr = NULL((void*)0); | |||
244 | HANDLE child_stderr_rd = NULL((void*)0); | |||
245 | HANDLE child_stderr_wr = NULL((void*)0); | |||
246 | HANDLE inherit_handles[2]; | |||
247 | ||||
248 | OVERLAPPED stdout_overlapped; | |||
249 | OVERLAPPED stderr_overlapped; | |||
250 | #else | |||
251 | int exit_status = 0; | |||
252 | #endif | |||
253 | ||||
254 | char **argv = convert_to_argv(command, argc, args); | |||
255 | char *command_line = convert_to_command_line(argv); | |||
256 | ||||
257 | ws_debug("command line: %s", command_line)do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 257, __func__, "command line: %s", command_line); } } while (0); | |||
258 | ||||
259 | uint64_t start_time = g_get_monotonic_time(); | |||
260 | ||||
261 | #ifdef _WIN32 | |||
262 | /* Setup overlapped structures. Create Manual Reset events, initially not signalled */ | |||
263 | memset(&stdout_overlapped, 0, sizeof(OVERLAPPED)); | |||
264 | memset(&stderr_overlapped, 0, sizeof(OVERLAPPED)); | |||
265 | stdout_overlapped.hEvent = CreateEvent(NULL((void*)0), true1, false0, NULL((void*)0)); | |||
266 | if (!stdout_overlapped.hEvent) | |||
267 | { | |||
268 | g_free(command_line); | |||
269 | g_strfreev(argv); | |||
270 | ws_debug("Could not create stdout overlapped event")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 270, __func__, "Could not create stdout overlapped event"); } } while (0); | |||
271 | return false0; | |||
272 | } | |||
273 | stderr_overlapped.hEvent = CreateEvent(NULL((void*)0), true1, false0, NULL((void*)0)); | |||
274 | if (!stderr_overlapped.hEvent) | |||
275 | { | |||
276 | CloseHandle(stdout_overlapped.hEvent); | |||
277 | g_free(command_line); | |||
278 | g_strfreev(argv); | |||
279 | ws_debug("Could not create stderr overlapped event")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 279, __func__, "Could not create stderr overlapped event"); } } while (0); | |||
280 | return false0; | |||
281 | } | |||
282 | ||||
283 | memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); | |||
284 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); | |||
285 | sa.bInheritHandle = false0; | |||
286 | sa.lpSecurityDescriptor = NULL((void*)0); | |||
287 | ||||
288 | if (!ws_pipe_create_overlapped_read(&child_stdout_rd, &child_stdout_wr, &sa, 0)) | |||
289 | { | |||
290 | CloseHandle(stdout_overlapped.hEvent); | |||
291 | CloseHandle(stderr_overlapped.hEvent); | |||
292 | g_free(command_line); | |||
293 | g_strfreev(argv); | |||
294 | ws_debug("Could not create stdout handle")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 294, __func__, "Could not create stdout handle"); } } while (0); | |||
295 | return false0; | |||
296 | } | |||
297 | ||||
298 | if (!ws_pipe_create_overlapped_read(&child_stderr_rd, &child_stderr_wr, &sa, 0)) | |||
299 | { | |||
300 | CloseHandle(stdout_overlapped.hEvent); | |||
301 | CloseHandle(stderr_overlapped.hEvent); | |||
302 | CloseHandle(child_stdout_rd); | |||
303 | CloseHandle(child_stdout_wr); | |||
304 | g_free(command_line); | |||
305 | g_strfreev(argv); | |||
306 | ws_debug("Could not create stderr handle")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 306, __func__, "Could not create stderr handle"); } } while (0); | |||
307 | return false0; | |||
308 | } | |||
309 | ||||
310 | inherit_handles[0] = child_stderr_wr; | |||
311 | inherit_handles[1] = child_stdout_wr; | |||
312 | ||||
313 | memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); | |||
314 | memset(&info, 0, sizeof(STARTUPINFO)); | |||
315 | ||||
316 | info.cb = sizeof(STARTUPINFO); | |||
317 | info.hStdError = child_stderr_wr; | |||
318 | info.hStdOutput = child_stdout_wr; | |||
319 | info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |||
320 | info.wShowWindow = SW_HIDE; | |||
321 | ||||
322 | if (win32_create_process(NULL((void*)0), command_line, NULL((void*)0), NULL((void*)0), G_N_ELEMENTS(inherit_handles)(sizeof (inherit_handles) / sizeof ((inherit_handles)[0])), inherit_handles, | |||
323 | CREATE_NEW_CONSOLE, NULL((void*)0), working_directory, &info, &processInfo)) | |||
324 | { | |||
325 | char* stdout_buffer = (char*)g_malloc(BUFFER_SIZE); | |||
326 | char* stderr_buffer = (char*)g_malloc(BUFFER_SIZE); | |||
327 | DWORD dw; | |||
328 | DWORD bytes_read; | |||
329 | GString *output_string = g_string_new(NULL((void*)0)); | |||
330 | bool_Bool process_finished = false0; | |||
331 | bool_Bool pending_stdout = true1; | |||
332 | bool_Bool pending_stderr = true1; | |||
333 | ||||
334 | /* Start asynchronous reads from child process stdout and stderr */ | |||
335 | if (!ReadFile(child_stdout_rd, stdout_buffer, BUFFER_SIZE, NULL((void*)0), &stdout_overlapped)) | |||
336 | { | |||
337 | if (GetLastError() != ERROR_IO_PENDING) | |||
338 | { | |||
339 | ws_debug("ReadFile on child stdout pipe failed. Error %ld", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 339, __func__, "ReadFile on child stdout pipe failed. Error %ld" , GetLastError()); } } while (0); | |||
340 | pending_stdout = false0; | |||
341 | } | |||
342 | } | |||
343 | ||||
344 | if (!ReadFile(child_stderr_rd, stderr_buffer, BUFFER_SIZE, NULL((void*)0), &stderr_overlapped)) | |||
345 | { | |||
346 | if (GetLastError() != ERROR_IO_PENDING) | |||
347 | { | |||
348 | ws_debug("ReadFile on child stderr pipe failed. Error %ld", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 348, __func__, "ReadFile on child stderr pipe failed. Error %ld" , GetLastError()); } } while (0); | |||
349 | pending_stderr = false0; | |||
350 | } | |||
351 | } | |||
352 | ||||
353 | for (;;) | |||
354 | { | |||
355 | HANDLE handles[3]; | |||
356 | DWORD n_handles = 0; | |||
357 | if (!process_finished) | |||
358 | { | |||
359 | handles[n_handles++] = processInfo.hProcess; | |||
360 | } | |||
361 | if (pending_stdout) | |||
362 | { | |||
363 | handles[n_handles++] = stdout_overlapped.hEvent; | |||
364 | } | |||
365 | if (pending_stderr) | |||
366 | { | |||
367 | handles[n_handles++] = stderr_overlapped.hEvent; | |||
368 | } | |||
369 | ||||
370 | if (!n_handles) | |||
371 | { | |||
372 | /* No more things to wait */ | |||
373 | break; | |||
374 | } | |||
375 | ||||
376 | dw = WaitForMultipleObjects(n_handles, handles, false0, INFINITE); | |||
377 | if (dw < (WAIT_OBJECT_0 + n_handles)) | |||
378 | { | |||
379 | int i = dw - WAIT_OBJECT_0; | |||
380 | if (handles[i] == processInfo.hProcess) | |||
381 | { | |||
382 | /* Process finished but there might still be unread data in the pipe. | |||
383 | * Close the write pipes, so ReadFile does not wait indefinitely. | |||
384 | */ | |||
385 | CloseHandle(child_stdout_wr); | |||
386 | CloseHandle(child_stderr_wr); | |||
387 | process_finished = true1; | |||
388 | } | |||
389 | else if (handles[i] == stdout_overlapped.hEvent) | |||
390 | { | |||
391 | bytes_read = 0; | |||
392 | if (!GetOverlappedResult(child_stdout_rd, &stdout_overlapped, &bytes_read, true1)) | |||
393 | { | |||
394 | if (GetLastError() == ERROR_BROKEN_PIPE) | |||
395 | { | |||
396 | pending_stdout = false0; | |||
397 | continue; | |||
398 | } | |||
399 | ws_debug("GetOverlappedResult on stdout failed. Error %ld", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 399, __func__, "GetOverlappedResult on stdout failed. Error %ld" , GetLastError()); } } while (0); | |||
400 | } | |||
401 | if (process_finished && (bytes_read == 0)) | |||
402 | { | |||
403 | /* We have drained the pipe and there isn't any process that holds active write handle to the pipe. */ | |||
404 | pending_stdout = false0; | |||
405 | continue; | |||
406 | } | |||
407 | g_string_append_len(output_string, stdout_buffer, bytes_read)g_string_append_len_inline (output_string, stdout_buffer, bytes_read ); | |||
408 | if (!ReadFile(child_stdout_rd, stdout_buffer, BUFFER_SIZE, NULL((void*)0), &stdout_overlapped)) | |||
409 | { | |||
410 | if (GetLastError() != ERROR_IO_PENDING) | |||
411 | { | |||
412 | ws_debug("ReadFile on child stdout pipe failed. Error %ld", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 412, __func__, "ReadFile on child stdout pipe failed. Error %ld" , GetLastError()); } } while (0); | |||
413 | pending_stdout = false0; | |||
414 | } | |||
415 | } | |||
416 | } | |||
417 | else if (handles[i] == stderr_overlapped.hEvent) | |||
418 | { | |||
419 | /* Discard the stderr data just like non-windows version of this function does. */ | |||
420 | bytes_read = 0; | |||
421 | if (!GetOverlappedResult(child_stderr_rd, &stderr_overlapped, &bytes_read, true1)) | |||
422 | { | |||
423 | if (GetLastError() == ERROR_BROKEN_PIPE) | |||
424 | { | |||
425 | pending_stderr = false0; | |||
426 | continue; | |||
427 | } | |||
428 | ws_debug("GetOverlappedResult on stderr failed. Error %ld", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 428, __func__, "GetOverlappedResult on stderr failed. Error %ld" , GetLastError()); } } while (0); | |||
429 | } | |||
430 | if (process_finished && (bytes_read == 0)) | |||
431 | { | |||
432 | pending_stderr = false0; | |||
433 | continue; | |||
434 | } | |||
435 | if (!ReadFile(child_stderr_rd, stderr_buffer, BUFFER_SIZE, NULL((void*)0), &stderr_overlapped)) | |||
436 | { | |||
437 | if (GetLastError() != ERROR_IO_PENDING) | |||
438 | { | |||
439 | ws_debug("ReadFile on child stderr pipe failed. Error %ld", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 439, __func__, "ReadFile on child stderr pipe failed. Error %ld" , GetLastError()); } } while (0); | |||
440 | pending_stderr = false0; | |||
441 | } | |||
442 | } | |||
443 | } | |||
444 | } | |||
445 | else | |||
446 | { | |||
447 | ws_debug("WaitForMultipleObjects returned 0x%08lX. Error %ld", dw, GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 447, __func__, "WaitForMultipleObjects returned 0x%08lX. Error %ld" , dw, GetLastError()); } } while (0); | |||
448 | } | |||
449 | } | |||
450 | ||||
451 | g_free(stdout_buffer); | |||
452 | g_free(stderr_buffer); | |||
453 | ||||
454 | status = GetExitCodeProcess(processInfo.hProcess, &dw); | |||
455 | if (status && dw != 0) | |||
456 | { | |||
457 | status = false0; | |||
458 | } | |||
459 | ||||
460 | local_output = g_string_free(output_string, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((output_string ), ((0))) : g_string_free_and_steal (output_string)) : (g_string_free ) ((output_string), ((0)))); | |||
461 | ||||
462 | CloseHandle(child_stdout_rd); | |||
463 | CloseHandle(child_stderr_rd); | |||
464 | ||||
465 | CloseHandle(processInfo.hProcess); | |||
466 | CloseHandle(processInfo.hThread); | |||
467 | } | |||
468 | else | |||
469 | { | |||
470 | status = false0; | |||
471 | ||||
472 | CloseHandle(child_stdout_rd); | |||
473 | CloseHandle(child_stdout_wr); | |||
474 | CloseHandle(child_stderr_rd); | |||
475 | CloseHandle(child_stderr_wr); | |||
476 | } | |||
477 | ||||
478 | CloseHandle(stdout_overlapped.hEvent); | |||
479 | CloseHandle(stderr_overlapped.hEvent); | |||
480 | #else | |||
481 | ||||
482 | GSpawnFlags flags = (GSpawnFlags)0; | |||
483 | GSpawnChildSetupFunc child_setup = NULL((void*)0); | |||
484 | #ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG | |||
485 | flags = (GSpawnFlags)(flags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN); | |||
486 | child_setup = close_non_standard_fds_linux; | |||
487 | #endif | |||
488 | status = g_spawn_sync(working_directory, argv, NULL((void*)0), | |||
489 | flags, child_setup, NULL((void*)0), &local_output, NULL((void*)0), &exit_status, NULL((void*)0)); | |||
490 | ||||
491 | if (status && exit_status != 0) | |||
492 | status = false0; | |||
493 | #endif | |||
494 | ||||
495 | ws_debug("%s finished in %.3fms", argv[0], (g_get_monotonic_time() - start_time) / 1000.0)do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 495, __func__, "%s finished in %.3fms", argv[0], (g_get_monotonic_time () - start_time) / 1000.0); } } while (0); | |||
496 | ||||
497 | if (status) | |||
498 | { | |||
499 | if (local_output != NULL((void*)0)) { | |||
500 | ws_noisy("spawn output: %s", local_output)do { if (1) { ws_log_full("Capture", LOG_LEVEL_NOISY, "wsutil/ws_pipe.c" , 500, __func__, "spawn output: %s", local_output); } } while (0); | |||
501 | if (command_output != NULL((void*)0)) | |||
502 | *command_output = g_strdup(local_output)g_strdup_inline (local_output); | |||
503 | } | |||
504 | result = true1; | |||
505 | } | |||
506 | ||||
507 | g_free(local_output); | |||
508 | g_free(command_line); | |||
509 | g_strfreev(argv); | |||
510 | ||||
511 | return result; | |||
512 | } | |||
513 | ||||
514 | void ws_pipe_init(ws_pipe_t *ws_pipe) | |||
515 | { | |||
516 | if (!ws_pipe) return; | |||
517 | memset(ws_pipe, 0, sizeof(ws_pipe_t)); | |||
518 | ws_pipe->pid = WS_INVALID_PID-1; | |||
519 | } | |||
520 | ||||
521 | GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args) | |||
522 | { | |||
523 | GPid pid = WS_INVALID_PID-1; | |||
524 | int stdin_fd, stdout_fd, stderr_fd; | |||
525 | #ifdef _WIN32 | |||
526 | STARTUPINFO info; | |||
527 | PROCESS_INFORMATION processInfo; | |||
528 | ||||
529 | SECURITY_ATTRIBUTES sa; | |||
530 | HANDLE child_stdin_rd = NULL((void*)0); | |||
531 | HANDLE child_stdin_wr = NULL((void*)0); | |||
532 | HANDLE child_stdout_rd = NULL((void*)0); | |||
533 | HANDLE child_stdout_wr = NULL((void*)0); | |||
534 | HANDLE child_stderr_rd = NULL((void*)0); | |||
535 | HANDLE child_stderr_wr = NULL((void*)0); | |||
536 | HANDLE inherit_handles[3]; | |||
537 | #endif | |||
538 | ||||
539 | // XXX harmonize handling of command arguments for the sync/async functions | |||
540 | // and make them const? This array ends with a trailing NULL by the way. | |||
541 | char **args_array = (char **)args->pdata; | |||
542 | char **argv = convert_to_argv(args_array[0], args->len - 2, args_array + 1); | |||
| ||||
543 | char *command_line = convert_to_command_line(argv); | |||
544 | ||||
545 | ws_debug("command line: %s", command_line)do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 545, __func__, "command line: %s", command_line); } } while (0); | |||
546 | ||||
547 | #ifdef _WIN32 | |||
548 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); | |||
549 | sa.bInheritHandle = false0; | |||
550 | sa.lpSecurityDescriptor = NULL((void*)0); | |||
551 | ||||
552 | if (!CreatePipe(&child_stdin_rd, &child_stdin_wr, &sa, 0)) | |||
553 | { | |||
554 | g_free(command_line); | |||
555 | g_strfreev(argv); | |||
556 | ws_debug("Could not create stdin handle")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 556, __func__, "Could not create stdin handle"); } } while ( 0); | |||
557 | return WS_INVALID_PID-1; | |||
558 | } | |||
559 | ||||
560 | if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) | |||
561 | { | |||
562 | CloseHandle(child_stdin_rd); | |||
563 | CloseHandle(child_stdin_wr); | |||
564 | g_free(command_line); | |||
565 | g_strfreev(argv); | |||
566 | ws_debug("Could not create stdout handle")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 566, __func__, "Could not create stdout handle"); } } while (0); | |||
567 | return WS_INVALID_PID-1; | |||
568 | } | |||
569 | ||||
570 | if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) | |||
571 | { | |||
572 | CloseHandle(child_stdin_rd); | |||
573 | CloseHandle(child_stdin_wr); | |||
574 | CloseHandle(child_stdout_rd); | |||
575 | CloseHandle(child_stdout_wr); | |||
576 | g_free(command_line); | |||
577 | g_strfreev(argv); | |||
578 | ws_debug("Could not create stderr handle")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 578, __func__, "Could not create stderr handle"); } } while (0); | |||
579 | return WS_INVALID_PID-1; | |||
580 | } | |||
581 | ||||
582 | inherit_handles[0] = child_stdin_rd; | |||
583 | inherit_handles[1] = child_stderr_wr; | |||
584 | inherit_handles[2] = child_stdout_wr; | |||
585 | ||||
586 | memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); | |||
587 | memset(&info, 0, sizeof(STARTUPINFO)); | |||
588 | ||||
589 | info.cb = sizeof(STARTUPINFO); | |||
590 | info.hStdInput = child_stdin_rd; | |||
591 | info.hStdError = child_stderr_wr; | |||
592 | info.hStdOutput = child_stdout_wr; | |||
593 | info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |||
594 | info.wShowWindow = SW_HIDE; | |||
595 | ||||
596 | if (win32_create_process(NULL((void*)0), command_line, NULL((void*)0), NULL((void*)0), G_N_ELEMENTS(inherit_handles)(sizeof (inherit_handles) / sizeof ((inherit_handles)[0])), inherit_handles, | |||
597 | CREATE_NEW_CONSOLE, NULL((void*)0), NULL((void*)0), &info, &processInfo)) | |||
598 | { | |||
599 | stdin_fd = _open_osfhandle((intptr_t)(child_stdin_wr), _O_BINARY); | |||
600 | stdout_fd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY); | |||
601 | stderr_fd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY); | |||
602 | pid = processInfo.hProcess; | |||
603 | CloseHandle(processInfo.hThread); | |||
604 | } | |||
605 | else | |||
606 | { | |||
607 | CloseHandle(child_stdin_wr); | |||
608 | CloseHandle(child_stdout_rd); | |||
609 | CloseHandle(child_stderr_rd); | |||
610 | } | |||
611 | ||||
612 | /* We no longer need other (child) end of pipes. The child process holds | |||
613 | * its own handles that will be closed on process exit. However, we have | |||
614 | * to close *our* handles as otherwise read() on stdout_fd and stderr_fd | |||
615 | * will block indefinitely after the process exits. | |||
616 | */ | |||
617 | CloseHandle(child_stdin_rd); | |||
618 | CloseHandle(child_stdout_wr); | |||
619 | CloseHandle(child_stderr_wr); | |||
620 | #else | |||
621 | ||||
622 | GError *error = NULL((void*)0); | |||
623 | GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD; | |||
624 | GSpawnChildSetupFunc child_setup = NULL((void*)0); | |||
625 | #ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG | |||
626 | flags = (GSpawnFlags)(flags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN); | |||
627 | child_setup = close_non_standard_fds_linux; | |||
628 | #endif | |||
629 | bool_Bool spawned = g_spawn_async_with_pipes(NULL((void*)0), argv, NULL((void*)0), | |||
630 | flags, child_setup, NULL((void*)0), | |||
631 | &pid, &stdin_fd, &stdout_fd, &stderr_fd, &error); | |||
632 | if (!spawned) { | |||
633 | ws_debug("Error creating async pipe: %s", error->message)do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 633, __func__, "Error creating async pipe: %s", error->message ); } } while (0); | |||
634 | g_free(error->message); | |||
635 | } | |||
636 | #endif | |||
637 | ||||
638 | g_free(command_line); | |||
639 | g_strfreev(argv); | |||
640 | ||||
641 | ws_pipe->pid = pid; | |||
| ||||
642 | ||||
643 | if (pid != WS_INVALID_PID-1) { | |||
644 | #ifdef _WIN32 | |||
645 | ws_pipe->stdin_io = g_io_channel_win32_new_fd(stdin_fd); | |||
646 | ws_pipe->stdout_io = g_io_channel_win32_new_fd(stdout_fd); | |||
647 | ws_pipe->stderr_io = g_io_channel_win32_new_fd(stderr_fd); | |||
648 | #else | |||
649 | ws_pipe->stdin_io = g_io_channel_unix_new(stdin_fd); | |||
650 | ws_pipe->stdout_io = g_io_channel_unix_new(stdout_fd); | |||
651 | ws_pipe->stderr_io = g_io_channel_unix_new(stderr_fd); | |||
652 | #endif | |||
653 | g_io_channel_set_encoding(ws_pipe->stdin_io, NULL((void*)0), NULL((void*)0)); | |||
654 | g_io_channel_set_encoding(ws_pipe->stdout_io, NULL((void*)0), NULL((void*)0)); | |||
655 | g_io_channel_set_encoding(ws_pipe->stderr_io, NULL((void*)0), NULL((void*)0)); | |||
656 | g_io_channel_set_buffered(ws_pipe->stdin_io, false0); | |||
657 | g_io_channel_set_buffered(ws_pipe->stdout_io, false0); | |||
658 | g_io_channel_set_buffered(ws_pipe->stderr_io, false0); | |||
659 | g_io_channel_set_close_on_unref(ws_pipe->stdin_io, true1); | |||
660 | g_io_channel_set_close_on_unref(ws_pipe->stdout_io, true1); | |||
661 | g_io_channel_set_close_on_unref(ws_pipe->stderr_io, true1); | |||
662 | } | |||
663 | ||||
664 | return pid; | |||
665 | } | |||
666 | ||||
667 | #ifdef _WIN32 | |||
668 | ||||
669 | typedef struct | |||
670 | { | |||
671 | HANDLE pipeHandle; | |||
672 | OVERLAPPED ol; | |||
673 | BOOL pendingIO; | |||
674 | } PIPEINTS; | |||
675 | ||||
676 | bool_Bool | |||
677 | ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid) | |||
678 | { | |||
679 | PIPEINTS pipeinsts[3]; | |||
680 | HANDLE handles[4]; | |||
681 | bool_Bool result = true1; | |||
682 | ||||
683 | SecureZeroMemory(pipeinsts, sizeof(pipeinsts)); | |||
684 | ||||
685 | if (num_pipe_handles == 0 || num_pipe_handles > 3) | |||
686 | { | |||
687 | ws_debug("Invalid number of pipes given as argument.")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 687, __func__, "Invalid number of pipes given as argument." ); } } while (0); | |||
688 | return false0; | |||
689 | } | |||
690 | ||||
691 | for (int i = 0; i < num_pipe_handles; ++i) | |||
692 | { | |||
693 | pipeinsts[i].ol.hEvent = CreateEvent(NULL((void*)0), true1, false0, NULL((void*)0)); | |||
694 | if (!pipeinsts[i].ol.hEvent) | |||
695 | { | |||
696 | ws_debug("Could not create overlapped event")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 696, __func__, "Could not create overlapped event"); } } while (0); | |||
697 | for (int j = 0; j < i; j++) | |||
698 | { | |||
699 | CloseHandle(pipeinsts[j].ol.hEvent); | |||
700 | } | |||
701 | return false0; | |||
702 | } | |||
703 | } | |||
704 | ||||
705 | for (int i = 0; i < num_pipe_handles; ++i) | |||
706 | { | |||
707 | pipeinsts[i].pipeHandle = pipe_handles[i]; | |||
708 | pipeinsts[i].ol.Pointer = 0; | |||
709 | pipeinsts[i].pendingIO = false0; | |||
710 | if (!ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol)) | |||
711 | { | |||
712 | DWORD error = GetLastError(); | |||
713 | switch (error) | |||
714 | { | |||
715 | case ERROR_IO_PENDING: | |||
716 | pipeinsts[i].pendingIO = true1; | |||
717 | break; | |||
718 | ||||
719 | case ERROR_PIPE_CONNECTED: | |||
720 | SetEvent(pipeinsts[i].ol.hEvent); | |||
721 | break; | |||
722 | ||||
723 | default: | |||
724 | ws_debug("ConnectNamedPipe failed with %ld\n.", error)do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 724, __func__, "ConnectNamedPipe failed with %ld\n.", error ); } } while (0); | |||
725 | result = false0; | |||
726 | } | |||
727 | } | |||
728 | } | |||
729 | ||||
730 | while (result) | |||
731 | { | |||
732 | DWORD dw; | |||
733 | int num_handles = 0; | |||
734 | for (int i = 0; i < num_pipe_handles; ++i) | |||
735 | { | |||
736 | if (pipeinsts[i].pendingIO) | |||
737 | { | |||
738 | handles[num_handles] = pipeinsts[i].ol.hEvent; | |||
739 | num_handles++; | |||
740 | } | |||
741 | } | |||
742 | if (num_handles == 0) | |||
743 | { | |||
744 | /* All pipes have been successfully connected */ | |||
745 | break; | |||
746 | } | |||
747 | /* Wait for process in case it exits before the pipes have connected */ | |||
748 | handles[num_handles] = pid; | |||
749 | num_handles++; | |||
750 | ||||
751 | dw = WaitForMultipleObjects(num_handles, handles, false0, 30000); | |||
752 | int handle_idx = dw - WAIT_OBJECT_0; | |||
753 | if (dw == WAIT_TIMEOUT) | |||
754 | { | |||
755 | ws_debug("extcap didn't connect to pipe within 30 seconds.")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 755, __func__, "extcap didn't connect to pipe within 30 seconds." ); } } while (0); | |||
756 | result = false0; | |||
757 | break; | |||
758 | } | |||
759 | // If index points to our handles array | |||
760 | else if (handle_idx >= 0 && handle_idx < num_handles) | |||
761 | { | |||
762 | if (handles[handle_idx] == pid) | |||
763 | { | |||
764 | ws_debug("extcap terminated without connecting to pipe.")do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 764, __func__, "extcap terminated without connecting to pipe." ); } } while (0); | |||
765 | result = false0; | |||
766 | } | |||
767 | for (int i = 0; i < num_pipe_handles; ++i) | |||
768 | { | |||
769 | if (handles[handle_idx] == pipeinsts[i].ol.hEvent) | |||
770 | { | |||
771 | DWORD cbRet; | |||
772 | BOOL success = GetOverlappedResult( | |||
773 | pipeinsts[i].pipeHandle, // handle to pipe | |||
774 | &pipeinsts[i].ol, // OVERLAPPED structure | |||
775 | &cbRet, // bytes transferred | |||
776 | true1); // wait | |||
777 | if (!success) | |||
778 | { | |||
779 | ws_debug("Error %ld \n.", GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 779, __func__, "Error %ld \n.", GetLastError()); } } while ( 0); | |||
780 | result = false0; | |||
781 | } | |||
782 | pipeinsts[i].pendingIO = false0; | |||
783 | } | |||
784 | } | |||
785 | } | |||
786 | else | |||
787 | { | |||
788 | ws_debug("WaitForMultipleObjects returned 0x%08lX. Error %ld", dw, GetLastError())do { if (1) { ws_log_full("Capture", LOG_LEVEL_DEBUG, "wsutil/ws_pipe.c" , 788, __func__, "WaitForMultipleObjects returned 0x%08lX. Error %ld" , dw, GetLastError()); } } while (0); | |||
789 | result = false0; | |||
790 | } | |||
791 | } | |||
792 | ||||
793 | for (int i = 0; i < num_pipe_handles; ++i) | |||
794 | { | |||
795 | if (pipeinsts[i].pendingIO) | |||
796 | { | |||
797 | CancelIoEx(pipeinsts[i].pipeHandle, &pipeinsts[i].ol); | |||
798 | WaitForSingleObject(pipeinsts[i].ol.hEvent, INFINITE); | |||
799 | } | |||
800 | CloseHandle(pipeinsts[i].ol.hEvent); | |||
801 | } | |||
802 | ||||
803 | return result; | |||
804 | } | |||
805 | #endif | |||
806 | ||||
807 | bool_Bool | |||
808 | ws_pipe_data_available(int pipe_fd) | |||
809 | { | |||
810 | #ifdef _WIN32 /* PeekNamedPipe */ | |||
811 | HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd); | |||
812 | DWORD bytes_avail; | |||
813 | ||||
814 | if (hPipe == INVALID_HANDLE_VALUE) | |||
815 | { | |||
816 | return false0; | |||
817 | } | |||
818 | ||||
819 | if (! PeekNamedPipe(hPipe, NULL((void*)0), 0, NULL((void*)0), &bytes_avail, NULL((void*)0))) | |||
820 | { | |||
821 | return false0; | |||
822 | } | |||
823 | ||||
824 | if (bytes_avail > 0) | |||
825 | { | |||
826 | return true1; | |||
827 | } | |||
828 | return false0; | |||
829 | #else /* select */ | |||
830 | fd_set rfds; | |||
831 | struct timeval timeout; | |||
832 | ||||
833 | FD_ZERO(&rfds)do { unsigned int __i; fd_set *__arr = (&rfds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr )->__fds_bits)[__i] = 0; } while (0); | |||
834 | FD_SET(pipe_fd, &rfds)((void) (((&rfds)->__fds_bits)[((pipe_fd) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((pipe_fd ) % (8 * (int) sizeof (__fd_mask))))))); | |||
835 | timeout.tv_sec = 0; | |||
836 | timeout.tv_usec = 0; | |||
837 | ||||
838 | if (select(pipe_fd + 1, &rfds, NULL((void*)0), NULL((void*)0), &timeout) > 0) | |||
839 | { | |||
840 | return true1; | |||
841 | } | |||
842 | ||||
843 | return false0; | |||
844 | #endif | |||
845 | } | |||
846 | ||||
847 | /* | |||
848 | * Editor modelines - https://www.wireshark.org/tools/modelines.html | |||
849 | * | |||
850 | * Local variables: | |||
851 | * c-basic-offset: 4 | |||
852 | * tab-width: 8 | |||
853 | * indent-tabs-mode: nil | |||
854 | * End: | |||
855 | * | |||
856 | * vi: set shiftwidth=4 tabstop=8 expandtab: | |||
857 | * :indentSize=4:tabSize=8:noTabs=true: | |||
858 | */ |