#include <signal.h>
#ifdef _WIN32
-#include "epan/unicode-utils.h"
+#include <wsutil/unicode-utils.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include "globals.h"
#include "file.h"
#include <epan/filesystem.h>
+#include <epan/report_err.h>
#include "capture.h"
#include "capture_sync.h"
-#include "simple_dialog.h"
#include "sync_pipe.h"
#include "capture-wpcap.h"
#endif
#include "ui_util.h"
-#include "file_util.h"
+#include <wsutil/file_util.h>
#include "log.h"
#ifdef _WIN32
static gboolean sync_pipe_input_cb(gint source, gpointer user_data);
-static void sync_pipe_wait_for_child(capture_options *capture_opts);
+static int sync_pipe_wait_for_child(int fork_child, gchar **msgp);
+static void pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len);
+static int pipe_read_block(int pipe_fd, char *indicator, int len, char *msg,
+ char **err_msg);
pp++;
if (*pp == '"')
len++;
- }
+ }
len++;
p++;
}
pp++;
if (*pp == '"')
*q++ = '\\';
- }
- *q++ = *p;
- p++;
+ }
+ *q++ = *p;
+ p++;
}
if (need_dblquotes)
return new_arg;
}
+
+/*
+ * Generate a string for a Win32 error.
+ */
+#define ERRBUF_SIZE 1024
+static const char *
+win32strerror(DWORD error)
+{
+ static char errbuf[ERRBUF_SIZE+1];
+ size_t errlen;
+ char *p;
+
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf,
+ ERRBUF_SIZE, NULL);
+
+ /*
+ * "FormatMessage()" "helpfully" sticks CR/LF at the end of the
+ * message. Get rid of it.
+ */
+ errlen = strlen(errbuf);
+ if (errlen >= 2) {
+ errbuf[errlen - 1] = '\0';
+ errbuf[errlen - 2] = '\0';
+ }
+ p = strchr(errbuf, '\0');
+ g_snprintf(p, (gulong)(sizeof errbuf - (p-errbuf)), " (%lu)", error);
+ return errbuf;
+}
+
+/*
+ * Generate a string for a Win32 exception code.
+ */
+static const char *
+win32strexception(DWORD exception)
+{
+ static char errbuf[ERRBUF_SIZE+1];
+ static const struct exception_msg {
+ int code;
+ char *msg;
+ } exceptions[] = {
+ { EXCEPTION_ACCESS_VIOLATION, "Access violation" },
+ { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" },
+ { EXCEPTION_BREAKPOINT, "Breakpoint" },
+ { EXCEPTION_DATATYPE_MISALIGNMENT, "Data type misalignment" },
+ { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal floating-point operand" },
+ { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-point divide by zero" },
+ { EXCEPTION_FLT_INEXACT_RESULT, "Floating-point inexact result" },
+ { EXCEPTION_FLT_INVALID_OPERATION, "Invalid floating-point operation" },
+ { EXCEPTION_FLT_OVERFLOW, "Floating-point overflow" },
+ { EXCEPTION_FLT_STACK_CHECK, "Floating-point stack check" },
+ { EXCEPTION_FLT_UNDERFLOW, "Floating-point underflow" },
+ { EXCEPTION_GUARD_PAGE, "Guard page violation" },
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" },
+ { EXCEPTION_IN_PAGE_ERROR, "Page-in error" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer divide by zero" },
+ { EXCEPTION_INT_OVERFLOW, "Integer overflow" },
+ { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" },
+ { EXCEPTION_INVALID_HANDLE, "Invalid handle" },
+ { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" },
+ { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" },
+ { EXCEPTION_SINGLE_STEP, "Single-step complete" },
+ { EXCEPTION_STACK_OVERFLOW, "Stack overflow" },
+ { 0, NULL }
+ };
+#define N_EXCEPTIONS (sizeof exceptions / sizeof exceptions[0])
+ int i;
+
+ for (i = 0; i < N_EXCEPTIONS; i++) {
+ if (exceptions[i].code == exception)
+ return exceptions[i].msg;
+ }
+ g_snprintf(errbuf, (gulong)sizeof errbuf, "Exception 0x%08x", exception);
+ return errbuf;
+}
#endif
/* Initialize an argument list and add dumpcap to it. */
gboolean
sync_pipe_start(capture_options *capture_opts) {
char ssnap[ARGV_NUMBER_LEN];
+ char sdlt[ARGV_NUMBER_LEN];
char scount[ARGV_NUMBER_LEN];
char sfilesize[ARGV_NUMBER_LEN];
char sfile_duration[ARGV_NUMBER_LEN];
char sautostop_files[ARGV_NUMBER_LEN];
char sautostop_filesize[ARGV_NUMBER_LEN];
char sautostop_duration[ARGV_NUMBER_LEN];
-#ifdef _WIN32
+#ifdef HAVE_PCAP_REMOTE
+ char sauth[256];
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+ char ssampling[ARGV_NUMBER_LEN];
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
char buffer_size[ARGV_NUMBER_LEN];
+#endif
+#ifdef _WIN32
HANDLE sync_pipe_read; /* pipe used to send messages from child to parent */
HANDLE sync_pipe_write; /* pipe used to send messages from child to parent */
- HANDLE signal_pipe_read; /* pipe used to send messages from parent to child (currently only stop) */
- HANDLE signal_pipe_write; /* pipe used to send messages from parent to child (currently only stop) */
+ HANDLE signal_pipe; /* named pipe used to send messages from parent to child (currently only stop) */
GString *args = g_string_sized_new(200);
gchar *quoted_arg;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
int i;
+ char control_id[ARGV_NUMBER_LEN];
+ gchar *signal_pipe_name;
#else
char errmsg[1024+1];
int sync_pipe[2]; /* pipe used to send messages from child to parent */
argv = init_pipe_args(&argc);
if (!argv) {
/* We don't know where to find dumpcap. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "We don't know where to find dumpcap.");
+ report_failure("We don't know where to find dumpcap.");
return FALSE;
}
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[0]: %s", argv[0]);
+
argv = sync_pipe_add_arg(argv, &argc, "-i");
argv = sync_pipe_add_arg(argv, &argc, capture_opts->iface);
if (capture_opts->linktype != -1) {
argv = sync_pipe_add_arg(argv, &argc, "-y");
-#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
- g_snprintf(ssnap, ARGV_NUMBER_LEN, "%s",linktype_val_to_name(capture_opts->linktype));
-#else
- /* we can't get the type name, just treat it as a number */
- g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d",capture_opts->linktype);
-#endif
- argv = sync_pipe_add_arg(argv, &argc, ssnap);
+ g_snprintf(sdlt, ARGV_NUMBER_LEN, "%s",linktype_val_to_name(capture_opts->linktype));
+ argv = sync_pipe_add_arg(argv, &argc, sdlt);
}
if(capture_opts->multi_files_on) {
if (!capture_opts->promisc_mode)
argv = sync_pipe_add_arg(argv, &argc, "-p");
+#ifdef HAVE_PCAP_CREATE
+ if (capture_opts->monitor_mode)
+ argv = sync_pipe_add_arg(argv, &argc, "-I");
+#endif
+ if (capture_opts->use_pcapng)
+ argv = sync_pipe_add_arg(argv, &argc, "-n");
+#ifdef HAVE_PCAP_REMOTE
+ if (capture_opts->datatx_udp)
+ argv = sync_pipe_add_arg(argv, &argc, "-u");
+
+ if (!capture_opts->nocap_rpcap)
+ argv = sync_pipe_add_arg(argv, &argc, "-r");
+
+ if (capture_opts->auth_type == CAPTURE_AUTH_PWD)
+ {
+ argv = sync_pipe_add_arg(argv, &argc, "-A");
+ g_snprintf(sauth, sizeof(sauth), "%s:%s", capture_opts->auth_username,
+ capture_opts->auth_password);
+ argv = sync_pipe_add_arg(argv, &argc, sauth);
+ }
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+ if (capture_opts->sampling_method != CAPTURE_SAMP_NONE)
+ {
+ argv = sync_pipe_add_arg(argv, &argc, "-m");
+ g_snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d",
+ capture_opts->sampling_method == CAPTURE_SAMP_BY_COUNT ? "count" :
+ capture_opts->sampling_method == CAPTURE_SAMP_BY_TIMER ? "timer" :
+ "undef",
+ capture_opts->sampling_param);
+ argv = sync_pipe_add_arg(argv, &argc, ssampling);
+ }
+#endif
/* dumpcap should be running in capture child mode (hidden feature) */
#ifndef DEBUG_CHILD
argv = sync_pipe_add_arg(argv, &argc, "-Z");
+#ifdef _WIN32
+ g_snprintf(control_id, ARGV_NUMBER_LEN, "%d", GetCurrentProcessId());
+ argv = sync_pipe_add_arg(argv, &argc, control_id);
+#else
+ argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
+#endif
#endif
-#ifdef _WIN32
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
argv = sync_pipe_add_arg(argv, &argc, "-B");
+#ifdef HAVE_PCAP_REMOTE
+ if (capture_opts->src_type == CAPTURE_IFREMOTE)
+ /* No buffer size when using remote interfaces */
+ g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", 1);
+ else
+#endif
g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d",capture_opts->buffer_size);
argv = sync_pipe_add_arg(argv, &argc, buffer_size);
#endif
sa.lpSecurityDescriptor = NULL;
/* Create a pipe for the child process */
- /* (inrease this value if you have trouble while fast capture file switches) */
+ /* (increase this value if you have trouble while fast capture file switches) */
if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) {
/* Couldn't create the pipe between parent and child. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create sync pipe: %s",
- strerror(errno));
+ report_failure("Couldn't create sync pipe: %s",
+ win32strerror(GetLastError()));
g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
return FALSE;
}
- /* Create a pipe for the parent process */
- if (! CreatePipe(&signal_pipe_read, &signal_pipe_write, &sa, 512)) {
+ /* Create the signal pipe */
+ signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, control_id);
+ signal_pipe = CreateNamedPipe(utf_8to16(signal_pipe_name),
+ PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL);
+ g_free(signal_pipe_name);
+
+ if (signal_pipe == INVALID_HANDLE_VALUE) {
/* Couldn't create the signal pipe between parent and child. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create signal pipe: %s",
- strerror(errno));
- CloseHandle(sync_pipe_read);
- CloseHandle(sync_pipe_write);
+ report_failure("Couldn't create signal pipe: %s",
+ win32strerror(GetLastError()));
g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
return FALSE;
#else
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; /* this hides the console window */
- si.hStdInput = signal_pipe_read;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = sync_pipe_write;
/*si.hStdError = (HANDLE) _get_osfhandle(2);*/
/* call dumpcap */
if(!CreateProcess(NULL, utf_8to16(args->str), NULL, NULL, TRUE,
CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Couldn't run %s in child process: error %u",
- args->str, GetLastError());
+ report_failure("Couldn't run %s in child process: %s",
+ args->str, win32strerror(GetLastError()));
CloseHandle(sync_pipe_read);
CloseHandle(sync_pipe_write);
g_free( (gpointer) argv[0]);
sync_pipe_read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
/* associate the operating system filehandle to a C run-time file handle */
- capture_opts->signal_pipe_write_fd = _open_osfhandle( (long) signal_pipe_write, _O_BINARY);
+ capture_opts->signal_pipe_write_fd = _open_osfhandle( (long) signal_pipe, _O_BINARY);
- /* child owns the read side now, close our handle */
- CloseHandle(signal_pipe_read);
#else /* _WIN32 */
if (pipe(sync_pipe) < 0) {
/* Couldn't create the pipe between parent and child. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create sync pipe: %s",
- strerror(errno));
+ report_failure("Couldn't create sync pipe: %s", strerror(errno));
g_free( (gpointer) argv[0]);
g_free(argv);
return FALSE;
* Child process - run dumpcap with the right arguments to make
* it just capture with the specified capture parameters
*/
- eth_close(2);
- dup(sync_pipe[PIPE_WRITE]);
- eth_close(sync_pipe[PIPE_READ]);
+ dup2(sync_pipe[PIPE_WRITE], 2);
+ ws_close(sync_pipe[PIPE_READ]);
execv(argv[0], (gpointer)argv);
g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
- argv[0], strerror(errno));
- sync_pipe_errmsg_to_parent(1, errmsg, "");
+ argv[0], strerror(errno));
+ sync_pipe_errmsg_to_parent(2, errmsg, "");
/* Exit with "_exit()", so that we don't close the connection
to the X server (and cause stuff buffered up by our parent but
- not yet sent to be sent, as that stuff should only be sent by
- our parent). */
- _exit(2);
+ not yet sent to be sent, as that stuff should only be sent by
+ our parent). We've sent an error message to the parent, so
+ we exit with an exit status of 1 (any exit status other than
+ 0 or 1 will cause an additional message to report that exit
+ status, over and above the error message we sent to the parent). */
+ _exit(1);
}
sync_pipe_read_fd = sync_pipe[PIPE_READ];
/* Parent process - read messages from the child process over the
sync pipe. */
- g_free( (gpointer) argv); /* free up arg array */
+ g_free( (gpointer) argv); /* free up arg array */
/* Close the write side of the pipe, so that only the child has it
open, and thus it completely closes, and thus returns to us
#ifdef _WIN32
CloseHandle(sync_pipe_write);
#else
- eth_close(sync_pipe[PIPE_WRITE]);
+ ws_close(sync_pipe[PIPE_WRITE]);
#endif
if (capture_opts->fork_child == -1) {
/* We couldn't even create the child process. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Couldn't create child process: %s", strerror(errno));
- eth_close(sync_pipe_read_fd);
+ report_failure("Couldn't create child process: %s", strerror(errno));
+ ws_close(sync_pipe_read_fd);
#ifdef _WIN32
- eth_close(capture_opts->signal_pipe_write_fd);
+ ws_close(capture_opts->signal_pipe_write_fd);
#endif
return FALSE;
}
+ capture_opts->fork_child_status = 0;
+
/* we might wait for a moment till child is ready, so update screen now */
main_window_update();
}
/*
- * Open dumpcap with the supplied arguments. On success, msg points to
- * a buffer containing the dumpcap output and returns 0. read_fd and
- * fork_child point to the pipe's file descriptor and child PID/handle,
- * respectively. On failure, msg points to the error message returned by
- * dumpcap, and returns dumpcap's exit value. In either case, msg must be
- * freed with g_free().
+ * Open two pipes to dumpcap with the supplied arguments, one for its
+ * standard output and one for its standard error.
+ *
+ * On success, *msg is unchanged and 0 is returned; data_read_fd,
+ * messsage_read_fd, and fork_child point to the standard output pipe's
+ * file descriptor, the standard error pipe's file descriptor, and
+ * the child's PID/handle, respectively.
+ *
+ * On failure, *msg points to an error message for the failure, and -1 is
+ * returned, in which case *msg must be freed with g_free().
*/
/* XXX - This duplicates a lot of code in sync_pipe_start() */
+/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */
#define PIPE_BUF_SIZE 5120
static int
-sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar **msg) {
+sync_pipe_open_command(const char** argv, int *data_read_fd,
+ int *message_read_fd, int *fork_child, gchar **msg)
+{
+ enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
#ifdef _WIN32
- HANDLE sync_pipe_read; /* pipe used to send messages from child to parent */
- HANDLE sync_pipe_write; /* pipe used to send messages from parent to child */
+ HANDLE sync_pipe[2]; /* pipe used to send messages from child to parent */
+ HANDLE data_pipe[2]; /* pipe used to send data from child to parent */
GString *args = g_string_sized_new(200);
gchar *quoted_arg;
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pi;
int i;
#else
+ char errmsg[1024+1];
int sync_pipe[2]; /* pipe used to send messages from child to parent */
- enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
+ int data_pipe[2]; /* pipe used to send data from child to parent */
#endif
*fork_child = -1;
- *read_fd = -1;
- g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_run_command");
+ *data_read_fd = -1;
+ *message_read_fd = -1;
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_open_command");
if (!msg) {
/* We can't return anything */
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
- /* Create a pipe for the child process */
- /* (inrease this value if you have trouble while fast capture file switches) */
- if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) {
- /* Couldn't create the pipe between parent and child. */
- *msg = g_strdup_printf("Couldn't create sync pipe: %s", strerror(errno));
+ /* Create a pipe for the child process to send us messages */
+ /* (increase this value if you have trouble while fast capture file switches) */
+ if (! CreatePipe(&sync_pipe[PIPE_READ], &sync_pipe[PIPE_WRITE], &sa, 5120)) {
+ /* Couldn't create the message pipe between parent and child. */
+ *msg = g_strdup_printf("Couldn't create sync pipe: %s",
+ win32strerror(GetLastError()));
+ g_free( (gpointer) argv[0]);
+ g_free( (gpointer) argv);
+ return -1;
+ }
+
+ /* Create a pipe for the child process to send us data */
+ /* (increase this value if you have trouble while fast capture file switches) */
+ if (! CreatePipe(&data_pipe[PIPE_READ], &data_pipe[PIPE_WRITE], &sa, 5120)) {
+ /* Couldn't create the message pipe between parent and child. */
+ *msg = g_strdup_printf("Couldn't create data pipe: %s",
+ win32strerror(GetLastError()));
+ CloseHandle(sync_pipe[PIPE_READ]);
+ CloseHandle(sync_pipe[PIPE_WRITE]);
g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
- return CANT_RUN_DUMPCAP;
+ return -1;
}
/* init STARTUPINFO */
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; /* this hides the console window */
si.hStdInput = NULL;
- si.hStdOutput = sync_pipe_write;
- si.hStdError = sync_pipe_write;
- /*si.hStdError = (HANDLE) _get_osfhandle(2);*/
+ si.hStdOutput = data_pipe[PIPE_WRITE];
+ si.hStdError = sync_pipe[PIPE_WRITE];
#endif
/* convert args array into a single string */
/* call dumpcap */
if(!CreateProcess(NULL, utf_8to16(args->str), NULL, NULL, TRUE,
CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
- *msg = g_strdup_printf("Couldn't run %s in child process: error %u",
- args->str, GetLastError());
- CloseHandle(sync_pipe_read);
- CloseHandle(sync_pipe_write);
+ *msg = g_strdup_printf("Couldn't run %s in child process: %s",
+ args->str, win32strerror(GetLastError()));
+ CloseHandle(data_pipe[PIPE_READ]);
+ CloseHandle(data_pipe[PIPE_WRITE]);
+ CloseHandle(sync_pipe[PIPE_READ]);
+ CloseHandle(sync_pipe[PIPE_WRITE]);
g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
- return CANT_RUN_DUMPCAP;
+ return -1;
}
*fork_child = (int) pi.hProcess;
g_string_free(args, TRUE);
- /* associate the operating system filehandle to a C run-time file handle */
+ /* associate the operating system filehandles to C run-time file handles */
/* (good file handle infos at: http://www.flounder.com/handles.htm) */
- *read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
-
+ *data_read_fd = _open_osfhandle( (long) data_pipe[PIPE_READ], _O_BINARY);
+ *message_read_fd = _open_osfhandle( (long) sync_pipe[PIPE_READ], _O_BINARY);
#else /* _WIN32 */
+ /* Create a pipe for the child process to send us messages */
if (pipe(sync_pipe) < 0) {
- /* Couldn't create the pipe between parent and child. */
+ /* Couldn't create the message pipe between parent and child. */
*msg = g_strdup_printf("Couldn't create sync pipe: %s", strerror(errno));
g_free( (gpointer) argv[0]);
g_free(argv);
- return CANT_RUN_DUMPCAP;
+ return -1;
+ }
+
+ /* Create a pipe for the child process to send us data */
+ if (pipe(data_pipe) < 0) {
+ /* Couldn't create the data pipe between parent and child. */
+ *msg = g_strdup_printf("Couldn't create data pipe: %s", strerror(errno));
+ ws_close(sync_pipe[PIPE_READ]);
+ ws_close(sync_pipe[PIPE_WRITE]);
+ g_free( (gpointer) argv[0]);
+ g_free(argv);
+ return -1;
}
if ((*fork_child = fork()) == 0) {
* Child process - run dumpcap with the right arguments to make
* it just capture with the specified capture parameters
*/
- eth_close(1);
- dup(sync_pipe[PIPE_WRITE]);
- eth_close(sync_pipe[PIPE_READ]);
+ dup2(data_pipe[PIPE_WRITE], 1);
+ ws_close(data_pipe[PIPE_READ]);
+ ws_close(data_pipe[PIPE_WRITE]);
+ dup2(sync_pipe[PIPE_WRITE], 2);
+ ws_close(sync_pipe[PIPE_READ]);
+ ws_close(sync_pipe[PIPE_WRITE]);
execv(argv[0], (gpointer)argv);
- *msg = g_strdup_printf("Couldn't run %s in child process: %s",
- argv[0], strerror(errno));
- return CANT_RUN_DUMPCAP;
+ g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
+ argv[0], strerror(errno));
+ sync_pipe_errmsg_to_parent(2, errmsg, "");
+
+ /* Exit with "_exit()", so that we don't close the connection
+ to the X server (and cause stuff buffered up by our parent but
+ not yet sent to be sent, as that stuff should only be sent by
+ our parent). We've sent an error message to the parent, so
+ we exit with an exit status of 1 (any exit status other than
+ 0 or 1 will cause an additional message to report that exit
+ status, over and above the error message we sent to the parent). */
+ _exit(1);
}
- *read_fd = sync_pipe[PIPE_READ];
+ *data_read_fd = data_pipe[PIPE_READ];
+ *message_read_fd = sync_pipe[PIPE_READ];
#endif
g_free( (gpointer) argv[0]); /* exename */
/* Parent process - read messages from the child process over the
sync pipe. */
- g_free( (gpointer) argv); /* free up arg array */
+ g_free( (gpointer) argv); /* free up arg array */
- /* Close the write side of the pipe, so that only the child has it
- open, and thus it completely closes, and thus returns to us
- an EOF indication, if the child closes it (either deliberately
+ /* Close the write sides of the pipes, so that only the child has them
+ open, and thus they completely close, and thus return to us
+ an EOF indication, if the child closes them (either deliberately
or by exiting abnormally). */
#ifdef _WIN32
- CloseHandle(sync_pipe_write);
+ CloseHandle(data_pipe[PIPE_WRITE]);
+ CloseHandle(sync_pipe[PIPE_WRITE]);
#else
- eth_close(sync_pipe[PIPE_WRITE]);
+ ws_close(data_pipe[PIPE_WRITE]);
+ ws_close(sync_pipe[PIPE_WRITE]);
#endif
if (*fork_child == -1) {
/* We couldn't even create the child process. */
*msg = g_strdup_printf("Couldn't create child process: %s", strerror(errno));
- eth_close(*read_fd);
- return CANT_RUN_DUMPCAP;
+ ws_close(*data_read_fd);
+ ws_close(*message_read_fd);
+ return -1;
}
/* we might wait for a moment till child is ready, so update screen now */
return 0;
}
+/*
+ * Wait for dumpcap to finish. On success, *msg is unchanged, and 0 is
+ * returned. On failure, *msg points to an error message for the
+ * failure, and -1 is returned. In the latter case, *msg must be
+ * freed with g_free().
+ */
static int
-#ifdef _WIN32
-sync_pipe_close_command(int *read_fd, int *fork_child, gchar **msg) {
-#else
-sync_pipe_close_command(int *read_fd, gchar **msg) {
-#endif
- int fork_child_status;
-
- eth_close(*read_fd);
-
- g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open: wait till child closed");
+sync_pipe_close_command(int *data_read_fd, int *message_read_fd,
+ int *fork_child, gchar **msg)
+{
+ ws_close(*data_read_fd);
+ if (message_read_fd != NULL)
+ ws_close(*message_read_fd);
#ifdef _WIN32
/* XXX - Should we signal the child somehow? */
sync_pipe_kill(*fork_child);
- if (_cwait(&fork_child_status, *fork_child, _WAIT_CHILD) == -1) {
- *msg = g_strdup_printf("Child capture process stopped unexpectedly "
- "(errno:%u)", errno);
- return CANT_RUN_DUMPCAP;
- }
-#else
- if (wait(&fork_child_status) != -1) {
- if (WIFEXITED(fork_child_status)) {
- /* The child exited. */
- fork_child_status = WEXITSTATUS(fork_child_status);
- } else {
- if (WIFSTOPPED(fork_child_status)) {
- /* It stopped, rather than exiting. "Should not happen." */
- *msg = g_strdup_printf("Child capture process stopped: %s",
- sync_pipe_signame(WSTOPSIG(fork_child_status)));
- } else if (WIFSIGNALED(fork_child_status)) {
- /* It died with a signal. */
- *msg = g_strdup_printf("Child capture process died: %s%s",
- sync_pipe_signame(WTERMSIG(fork_child_status)),
- WCOREDUMP(fork_child_status) ? " - core dumped" : "");
- } else {
- /* What? It had to either have exited, or stopped, or died with
- a signal; what happened here? */
- *msg = g_strdup_printf("Child capture process died: wait status %#o",
- fork_child_status);
- }
- return CANT_RUN_DUMPCAP;
- }
- } else {
- *msg = g_strdup_printf("Child capture process stopped unexpectedly "
- "(errno:%u)", errno);
- return CANT_RUN_DUMPCAP;
- }
#endif
- return 0;
+
+ return sync_pipe_wait_for_child(*fork_child, msg);
}
/*
- * Run dumpcap with the supplied arguments. On success, msg points to
- * a buffer containing the dumpcap output and returns 0. On failure, msg
- * points to the error message returned by dumpcap, and returns dumpcap's
- * exit value. In either case, msg must be freed with g_free().
+ * Run dumpcap with the supplied arguments.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_message are NULL, and 0 is returned; *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
*/
/* XXX - This duplicates a lot of code in sync_pipe_start() */
+/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */
#define PIPE_BUF_SIZE 5120
static int
-sync_pipe_run_command(const char** argv, gchar **msg) {
- int sync_pipe_read_fd, fork_child, ret;
- gchar buf[PIPE_BUF_SIZE+1];
- GString *msg_buf = NULL;
- int count;
-
- ret = sync_pipe_open_command(argv, &sync_pipe_read_fd, &fork_child, msg);
-
- if (ret)
- return ret;
-
- /* We were able to set up to read dumpcap's output. Do so and
- return its exit value. */
- msg_buf = g_string_new("");
- while ((count = eth_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) {
- buf[count] = '\0';
- g_string_append(msg_buf, buf);
+sync_pipe_run_command(const char** argv, gchar **data, gchar **primary_msg,
+ gchar **secondary_msg)
+{
+ gchar *msg;
+ int data_pipe_read_fd, sync_pipe_read_fd, fork_child, ret;
+ char *wait_msg;
+ gchar buffer[PIPE_BUF_SIZE+1];
+ int nread;
+ char indicator;
+ int primary_msg_len;
+ char *primary_msg_text;
+ int secondary_msg_len;
+ char *secondary_msg_text;
+ char *combined_msg;
+ GString *data_buf = NULL;
+ int count;
+
+ ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ *data = NULL;
+ return -1;
+ }
+
+ /*
+ * We were able to set up to read dumpcap's output. Do so.
+ *
+ * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message.
+ */
+ nread = pipe_read_block(sync_pipe_read_fd, &indicator, SP_MAX_MSG_LEN,
+ buffer, primary_msg);
+ if(nread <= 0) {
+ /* We got a read error from the sync pipe, or we got no data at
+ all from the sync pipe, so we're not going to be getting any
+ data or error message from the child process. Pick up its
+ exit status, and complain.
+
+ We don't have to worry about killing the child, if the sync pipe
+ returned an error. Usually this error is caused as the child killed
+ itself while going down. Even in the rare cases that this isn't the
+ case, the child will get an error when writing to the broken pipe
+ the next time, cleaning itself up then. */
+ ret = sync_pipe_wait_for_child(fork_child, &wait_msg);
+ if(nread == 0) {
+ /* We got an EOF from the sync pipe. That means that it exited
+ before giving us any data to read. If ret is -1, we report
+ that as a bad exit (e.g., exiting due to a signal); otherwise,
+ we report it as a premature exit. */
+ if (ret == -1)
+ *primary_msg = wait_msg;
+ else
+ *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+ } else {
+ /* We got an error from the sync pipe. If ret is -1, report
+ both the sync pipe I/O error and the wait error. */
+ if (ret == -1) {
+ combined_msg = g_strdup_printf("%s\n\n%s", *primary_msg, wait_msg);
+ g_free(*primary_msg);
+ g_free(wait_msg);
+ *primary_msg = combined_msg;
+ }
}
+ *secondary_msg = NULL;
-#ifdef _WIN32
- ret = sync_pipe_close_command(&sync_pipe_read_fd, &fork_child, msg);
-#else
- ret = sync_pipe_close_command(&sync_pipe_read_fd, msg);
-#endif
+ return -1;
+ }
- if (ret) {
- g_string_free(msg_buf, TRUE);
- return ret;
+ /* we got a valid message block from the child, process it */
+ switch(indicator) {
+
+ case SP_ERROR_MSG:
+ /*
+ * Error from dumpcap; there will be a primary message and a
+ * secondary message.
+ */
+
+ /* convert primary message */
+ pipe_convert_header(buffer, 4, &indicator, &primary_msg_len);
+ primary_msg_text = buffer+4;
+ /* convert secondary message */
+ pipe_convert_header(primary_msg_text + primary_msg_len, 4, &indicator,
+ &secondary_msg_len);
+ secondary_msg_text = primary_msg_text + primary_msg_len + 4;
+ /* the capture child will close the sync_pipe, nothing to do */
+
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ } else {
+ /*
+ * Child process failed, but returned the expected exit status.
+ * Return the messages it gave us, and indicate failure.
+ */
+ *primary_msg = g_strdup(primary_msg_text);
+ *secondary_msg = g_strdup(secondary_msg_text);
+ ret = -1;
}
+ *data = NULL;
+ break;
- *msg = msg_buf->str;
- g_string_free(msg_buf, FALSE);
- return 0;
+ case SP_SUCCESS:
+ /* read the output from the command */
+ data_buf = g_string_new("");
+ while ((count = ws_read(data_pipe_read_fd, buffer, PIPE_BUF_SIZE)) > 0) {
+ buffer[count] = '\0';
+ g_string_append(data_buf, buffer);
+ }
+
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ g_string_free(data_buf, TRUE);
+ *data = NULL;
+ } else {
+ /*
+ * Child process succeeded.
+ */
+ *primary_msg = NULL;
+ *secondary_msg = NULL;
+ *data = data_buf->str;
+ g_string_free(data_buf, FALSE);
+ }
+ break;
+
+ default:
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ } else {
+ /*
+ * Child process returned an unknown status.
+ */
+ *primary_msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x",
+ indicator);
+ *secondary_msg = NULL;
+ ret = -1;
+ }
+ *data = NULL;
+ break;
+ }
+ return ret;
}
/*
- * Get an interface list using dumpcap. On success, msg points to
- * a buffer containing the dumpcap output and returns 0. On failure, msg
- * points to the error message returned by dumpcap, and returns dumpcap's
- * exit value. In either case, msg must be freed with g_free().
+ * Get the list of interfaces using dumpcap.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
*/
int
-sync_interface_list_open(gchar **msg) {
+sync_interface_list_open(gchar **data, gchar **primary_msg,
+ gchar **secondary_msg)
+{
int argc;
const char **argv;
- if (!msg) {
- /* We can't return anything */
- return -1;
- }
-
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open");
argv = init_pipe_args(&argc);
if (!argv) {
- *msg = g_strdup_printf("We don't know where to find dumpcap.");
- return CANT_RUN_DUMPCAP;
+ *primary_msg = g_strdup("We don't know where to find dumpcap.");
+ *secondary_msg = NULL;
+ *data = NULL;
+ return -1;
}
/* Ask for the interface list */
argv = sync_pipe_add_arg(argv, &argc, "-D");
- argv = sync_pipe_add_arg(argv, &argc, "-M");
- /* dumpcap should be running in capture child mode (hidden feature) */
#ifndef DEBUG_CHILD
+ /* Run dumpcap in capture child mode */
argv = sync_pipe_add_arg(argv, &argc, "-Z");
+ argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
#endif
-
- return sync_pipe_run_command(argv, msg);
+ return sync_pipe_run_command(argv, data, primary_msg, secondary_msg);
}
/*
- * Get an linktype list using dumpcap. On success, msg points to
- * a buffer containing the dumpcap output and returns 0. On failure, msg
- * points to the error message returned by dumpcap, and returns dumpcap's
- * exit value. In either case, msg must be freed with g_free().
+ * Get the capabilities of an interface using dumpcap.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
*/
int
-sync_linktype_list_open(gchar *ifname, gchar **msg) {
+sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode,
+ gchar **data, gchar **primary_msg,
+ gchar **secondary_msg)
+{
int argc;
const char **argv;
- if (!msg) {
- /* We can't return anything */
- return -1;
- }
-
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_linktype_list_open");
argv = init_pipe_args(&argc);
if (!argv) {
- *msg = g_strdup_printf("We don't know where to find dumpcap.");
- return CANT_RUN_DUMPCAP;
+ *primary_msg = g_strdup("We don't know where to find dumpcap.");
+ *secondary_msg = NULL;
+ *data = NULL;
+ return -1;
}
- /* Ask for the linktype list */
+ /* Ask for the interface capabilities */
argv = sync_pipe_add_arg(argv, &argc, "-i");
argv = sync_pipe_add_arg(argv, &argc, ifname);
argv = sync_pipe_add_arg(argv, &argc, "-L");
- argv = sync_pipe_add_arg(argv, &argc, "-M");
+ if (monitor_mode)
+ argv = sync_pipe_add_arg(argv, &argc, "-I");
- /* dumpcap should be running in capture child mode (hidden feature) */
#ifndef DEBUG_CHILD
+ /* Run dumpcap in capture child mode */
argv = sync_pipe_add_arg(argv, &argc, "-Z");
+ argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
#endif
-
- return sync_pipe_run_command(argv, msg);
+ return sync_pipe_run_command(argv, data, primary_msg, secondary_msg);
}
/*
* Start getting interface statistics using dumpcap. On success, read_fd
- * contains the file descriptor for the pipe's stdout, msg is unchanged,
- * and zero is returned. On failure, msg will point to an error message
- * that must be g_free()d and a nonzero error value will be returned.
+ * contains the file descriptor for the pipe's stdout, *msg is unchanged,
+ * and zero is returned. On failure, *msg will point to an error message
+ * that must be g_free()d, and -1 will be returned.
*/
int
-sync_interface_stats_open(int *read_fd, int *fork_child, gchar **msg) {
- int argc;
- const char **argv;
-
- if (!msg) {
- /* We can't return anything */
- return -1;
- }
+sync_interface_stats_open(int *data_read_fd, int *fork_child, gchar **msg)
+{
+ int argc;
+ const char **argv;
+ int message_read_fd, ret;
+ char *wait_msg;
+ gchar buffer[PIPE_BUF_SIZE+1];
+ int nread;
+ char indicator;
+ int primary_msg_len;
+ char *primary_msg_text;
+ int secondary_msg_len;
+ char *secondary_msg_text;
+ char *combined_msg;
- g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_linktype_list_open");
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_stats_open");
- argv = init_pipe_args(&argc);
+ argv = init_pipe_args(&argc);
- if (!argv) {
- *msg = g_strdup_printf("We don't know where to find dumpcap.");
- return CANT_RUN_DUMPCAP;
- }
+ if (!argv) {
+ *msg = g_strdup("We don't know where to find dumpcap.");
+ return -1;
+ }
- /* Ask for the linktype list */
- argv = sync_pipe_add_arg(argv, &argc, "-S");
- argv = sync_pipe_add_arg(argv, &argc, "-M");
+ /* Ask for the interface statistics */
+ argv = sync_pipe_add_arg(argv, &argc, "-S");
- /* dumpcap should be running in capture child mode (hidden feature) */
#ifndef DEBUG_CHILD
- argv = sync_pipe_add_arg(argv, &argc, "-Z");
+ argv = sync_pipe_add_arg(argv, &argc, "-Z");
+ argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
#endif
+ ret = sync_pipe_open_command(argv, data_read_fd, &message_read_fd,
+ fork_child, msg);
+ if (ret == -1)
+ return -1;
+
+ /*
+ * We were able to set up to read dumpcap's output. Do so.
+ *
+ * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message.
+ */
+ nread = pipe_read_block(message_read_fd, &indicator, SP_MAX_MSG_LEN,
+ buffer, msg);
+ if(nread <= 0) {
+ /* We got a read error from the sync pipe, or we got no data at
+ all from the sync pipe, so we're not going to be getting any
+ data or error message from the child process. Pick up its
+ exit status, and complain.
+
+ We don't have to worry about killing the child, if the sync pipe
+ returned an error. Usually this error is caused as the child killed
+ itself while going down. Even in the rare cases that this isn't the
+ case, the child will get an error when writing to the broken pipe
+ the next time, cleaning itself up then. */
+ ret = sync_pipe_wait_for_child(*fork_child, &wait_msg);
+ if(nread == 0) {
+ /* We got an EOF from the sync pipe. That means that it exited
+ before giving us any data to read. If ret is -1, we report
+ that as a bad exit (e.g., exiting due to a signal); otherwise,
+ we report it as a premature exit. */
+ if (ret == -1)
+ *msg = wait_msg;
+ else
+ *msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+ } else {
+ /* We got an error from the sync pipe. If ret is -1, report
+ both the sync pipe I/O error and the wait error. */
+ if (ret == -1) {
+ combined_msg = g_strdup_printf("%s\n\n%s", *msg, wait_msg);
+ g_free(*msg);
+ g_free(wait_msg);
+ *msg = combined_msg;
+ }
+ }
+
+ return -1;
+ }
- return sync_pipe_open_command(argv, read_fd, fork_child, msg);
+ /* we got a valid message block from the child, process it */
+ switch(indicator) {
+
+ case SP_ERROR_MSG:
+ /*
+ * Error from dumpcap; there will be a primary message and a
+ * secondary message.
+ */
+
+ /* convert primary message */
+ pipe_convert_header(buffer, 4, &indicator, &primary_msg_len);
+ primary_msg_text = buffer+4;
+ /* convert secondary message */
+ pipe_convert_header(primary_msg_text + primary_msg_len, 4, &indicator,
+ &secondary_msg_len);
+ secondary_msg_text = primary_msg_text + primary_msg_len + 4;
+ /* the capture child will close the sync_pipe, nothing to do */
+
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(data_read_fd, &message_read_fd,
+ fork_child, msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ } else {
+ /*
+ * Child process failed, but returned the expected exit status.
+ * Return the messages it gave us, and indicate failure.
+ */
+ *msg = g_strdup(primary_msg_text);
+ ret = -1;
+ }
+ break;
+
+ case SP_SUCCESS:
+ /* Close the message pipe. */
+ ws_close(message_read_fd);
+ break;
+
+ default:
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(data_read_fd, &message_read_fd,
+ fork_child, msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ } else {
+ /*
+ * Child process returned an unknown status.
+ */
+ *msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x",
+ indicator);
+ ret = -1;
+ }
+ break;
+ }
+ return ret;
}
/* Close down the stats process */
int
-sync_interface_stats_close(int *read_fd, int *fork_child
-#ifndef _WIN32
-_U_
-#endif
-, gchar **msg) {
-#ifdef _WIN32
- return sync_pipe_close_command(read_fd, fork_child, msg);
-#else
- return sync_pipe_close_command(read_fd, msg);
-#endif
+sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg)
+{
+ return sync_pipe_close_command(read_fd, NULL, fork_child, msg);
}
/* read a number of bytes from a pipe */
/* (blocks until enough bytes read or an error occurs) */
static int
-pipe_read_bytes(int pipe, char *bytes, int required) {
+pipe_read_bytes(int pipe_fd, char *bytes, int required, char **msg)
+{
int newly;
int offset = 0;
+ int error;
while(required) {
- newly = read(pipe, &bytes[offset], required);
+ newly = read(pipe_fd, &bytes[offset], required);
if (newly == 0) {
/* EOF */
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "read from pipe %d: EOF (capture closed?)", pipe);
+ "read from pipe %d: EOF (capture closed?)", pipe_fd);
+ *msg = 0;
return offset;
}
if (newly < 0) {
/* error */
+ error = errno;
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "read from pipe %d: error(%u): %s", pipe, errno, strerror(errno));
+ "read from pipe %d: error(%u): %s", pipe_fd, error,
+ strerror(error));
+ *msg = g_strdup_printf("Error reading from sync pipe: %s",
+ strerror(error));
return newly;
}
offset += newly;
}
+ *msg = NULL;
return offset;
}
-static gboolean pipe_data_available(int pipe) {
+static gboolean pipe_data_available(int pipe_fd) {
#ifdef _WIN32 /* PeekNamedPipe */
- HANDLE hPipe = (HANDLE) _get_osfhandle(pipe);
+ HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
DWORD bytes_avail;
if (hPipe == INVALID_HANDLE_VALUE)
struct timeval timeout;
FD_ZERO(&rfds);
- FD_SET(pipe, &rfds);
+ FD_SET(pipe_fd, &rfds);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
- if (select(pipe+1, &rfds, NULL, NULL, &timeout) > 0)
+ if (select(pipe_fd+1, &rfds, NULL, NULL, &timeout) > 0)
return TRUE;
return FALSE;
/* Read a line from a pipe, similar to fgets */
int
-sync_pipe_gets_nonblock(int pipe, char *bytes, int max) {
+sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max) {
int newly;
int offset = -1;
while(offset < max - 1) {
offset++;
- if (! pipe_data_available(pipe))
+ if (! pipe_data_available(pipe_fd))
break;
- newly = read(pipe, &bytes[offset], 1);
+ newly = read(pipe_fd, &bytes[offset], 1);
if (newly == 0) {
/* EOF - not necessarily an error */
break;
} else if (newly < 0) {
/* error */
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "read from pipe %d: error(%u): %s", pipe, errno, strerror(errno));
+ "read from pipe %d: error(%u): %s", pipe_fd, errno, strerror(errno));
return newly;
} else if (bytes[offset] == '\n') {
break;
}
-/* convert header values (indicator and 4-byte length) */
+/* convert header values (indicator and 3-byte length) */
static void
pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {
(1-byte message indicator, 3-byte message length (excluding length
and indicator field), and the rest is the message) */
static int
-pipe_read_block(int pipe, char *indicator, int len, char *msg) {
+pipe_read_block(int pipe_fd, char *indicator, int len, char *msg,
+ char **err_msg)
+{
int required;
int newly;
guchar header[4];
-
/* read header (indicator and 3-byte length) */
- newly = pipe_read_bytes(pipe, header, 4);
+ newly = pipe_read_bytes(pipe_fd, header, 4, err_msg);
if(newly != 4) {
+ if (newly == 0) {
+ /*
+ * Immediate EOF; if the capture child exits normally, this
+ * is an "I'm done" indication, so don't report it as an
+ * error.
+ */
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "read %d got an EOF", pipe_fd);
+ return 0;
+ }
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "read %d failed to read header: %u", pipe, newly);
+ "read %d failed to read header: %u", pipe_fd, newly);
+ if (newly != -1) {
+ /*
+ * Short read, but not an immediate EOF.
+ */
+ *err_msg = g_strdup_printf("Premature EOF reading from sync pipe: got only %d bytes",
+ newly);
+ }
return -1;
}
/* only indicator with no value? */
if(required == 0) {
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "read %d indicator: %c empty value", pipe, *indicator);
+ "read %d indicator: %c empty value", pipe_fd, *indicator);
return 4;
}
if(required > len) {
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
"read %d length error, required %d > len %d, indicator: %u",
- pipe, required, len, *indicator);
+ pipe_fd, required, len, *indicator);
/* we have a problem here, try to read some more bytes from the pipe to debug where the problem really is */
memcpy(msg, header, sizeof(header));
- newly = read(pipe, &msg[sizeof(header)], len-sizeof(header));
- g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+ newly = read(pipe_fd, &msg[sizeof(header)], len-sizeof(header));
+ *err_msg = g_strdup_printf("Unknown message from dumpcap, try to show it as a string: %s",
+ msg);
return -1;
}
len = required;
/* read the actual block data */
- newly = pipe_read_bytes(pipe, msg, required);
+ newly = pipe_read_bytes(pipe_fd, msg, required, err_msg);
if(newly != required) {
- g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+ if (newly != -1) {
+ *err_msg = g_strdup_printf("Unknown message from dumpcap, try to show it as a string: %s",
+ msg);
+ }
return -1;
}
+ /* XXX If message is "2part", the msg probably won't be sent to debug log correctly */
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "read %d ok indicator: %c len: %u msg: %s", pipe, *indicator,
+ "read %d ok indicator: %c len: %u msg: %s", pipe_fd, *indicator,
len, msg);
+ *err_msg = NULL;
return newly + 4;
}
sync_pipe_input_cb(gint source, gpointer user_data)
{
capture_options *capture_opts = (capture_options *)user_data;
+ int ret;
char buffer[SP_MAX_MSG_LEN+1];
int nread;
char indicator;
int primary_len;
- char * primary_msg;
+ char *primary_msg;
int secondary_len;
- char * secondary_msg;
-
+ char *secondary_msg;
+ char *wait_msg, *combined_msg;
- nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer);
+ nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer,
+ &primary_msg);
if(nread <= 0) {
- if (nread == 0)
- g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "sync_pipe_input_cb: child has closed sync_pipe");
- else
- g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
- "sync_pipe_input_cb: error reading from sync pipe");
-
- /* The child has closed the sync pipe, meaning it's not going to be
- capturing any more packets. Pick up its exit status, and
- complain if it did anything other than exit with status 0.
+ /* We got a read error, or a bad message, or an EOF, from the sync pipe.
+
+ If we got a read error or a bad message, nread is -1 and
+ primary_msg is set to point to an error message. We don't
+ have to worry about killing the child; usually this error
+ is caused as the child killed itself while going down.
+ Even in the rare cases that this isn't the case, the child
+ will get an error when writing to the broken pipe the next time,
+ cleaning itself up then.
+
+ If we got an EOF, nread is 0 and primary_msg isn't set. This
+ is an indication that the capture is finished. */
+ ret = sync_pipe_wait_for_child(capture_opts->fork_child, &wait_msg);
+ if(nread == 0) {
+ /* We got an EOF from the sync pipe. That means that the capture
+ child exited, and not in the middle of a message; we treat
+ that as an indication that it's done, and only report an
+ error if ret is -1, in which case wait_msg is the error
+ message. */
+ if (ret == -1)
+ primary_msg = wait_msg;
+ } else {
+ /* We got an error from the sync pipe. If ret is -1, report
+ both the sync pipe I/O error and the wait error. */
+ if (ret == -1) {
+ combined_msg = g_strdup_printf("%s\n\n%s", primary_msg, wait_msg);
+ g_free(primary_msg);
+ g_free(wait_msg);
+ primary_msg = combined_msg;
+ }
+ }
- We don't have to worry about killing the child, if the sync pipe
- returned an error. Usually this error is caused as the child killed itself
- while going down. Even in the rare cases that this isn't the case,
- the child will get an error when writing to the broken pipe the next time,
- cleaning itself up then. */
- sync_pipe_wait_for_child(capture_opts);
+ /* No more child process. */
+ capture_opts->fork_child = -1;
+ capture_opts->fork_child_status = ret;
#ifdef _WIN32
- eth_close(capture_opts->signal_pipe_write_fd);
+ ws_close(capture_opts->signal_pipe_write_fd);
#endif
- capture_input_closed(capture_opts);
+ capture_input_closed(capture_opts, primary_msg);
+ g_free(primary_msg);
return FALSE;
}
/* We weren't able to open the new capture file; user has been
alerted. Close the sync pipe. */
- eth_close(source);
+ ws_close(source);
/* the child has send us a filename which we couldn't open.
this probably means, the child is creating files faster than we can handle it.
/* the capture child will close the sync_pipe, nothing to do for now */
break;
case SP_DROPS:
- capture_input_drops(capture_opts, atoi(buffer));
+ capture_input_drops(capture_opts, (guint32)strtoul(buffer, NULL, 10));
break;
default:
g_assert_not_reached();
/* the child process is going down, wait until it's completely terminated */
-static void
-sync_pipe_wait_for_child(capture_options *capture_opts)
+static int
+sync_pipe_wait_for_child(int fork_child, gchar **msgp)
{
- int wstatus;
-
+ int fork_child_status;
+ int ret;
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: wait till child closed");
- g_assert(capture_opts->fork_child != -1);
+ g_assert(fork_child != -1);
+ *msgp = NULL; /* assume no error */
+ ret = 0;
#ifdef _WIN32
- if (_cwait(&wstatus, capture_opts->fork_child, _WAIT_CHILD) == -1) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Child capture process stopped unexpectedly (errno:%u)", errno);
+ if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) {
+ *msgp = g_strdup_printf("Error from cwait(): %s", strerror(errno));
+ ret = -1;
+ } else {
+ /*
+ * The child exited; return its exit status. Do not treat this as
+ * an error.
+ */
+ ret = fork_child_status;
+ if ((fork_child_status & 0xC0000000) == ERROR_SEVERITY_ERROR) {
+ /* Probably an exception code */
+ *msgp = g_strdup_printf("Child dumpcap process died: %s",
+ win32strexception(fork_child_status));
+ ret = -1;
+ }
}
#else
- if (wait(&wstatus) != -1) {
- if (WIFEXITED(wstatus)) {
- /* The child exited; display its exit status, if it seems uncommon (0=ok, 1=error) */
- /* the child will inform us about errors through the sync_pipe, which will popup */
- /* an error message, so don't popup another one */
-
- /* If there are situations where the child won't send us such an error message, */
- /* this should be fixed in the child and not here! */
- if (WEXITSTATUS(wstatus) != 0 && WEXITSTATUS(wstatus) != 1) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Child capture process exited: exit status %d",
- WEXITSTATUS(wstatus));
- }
- } else if (WIFSTOPPED(wstatus)) {
+ if (waitpid(fork_child, &fork_child_status, 0) != -1) {
+ if (WIFEXITED(fork_child_status)) {
+ /*
+ * The child exited; return its exit status. Do not treat this as
+ * an error.
+ */
+ ret = WEXITSTATUS(fork_child_status);
+ } else if (WIFSTOPPED(fork_child_status)) {
/* It stopped, rather than exiting. "Should not happen." */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Child capture process stopped: %s",
- sync_pipe_signame(WSTOPSIG(wstatus)));
- } else if (WIFSIGNALED(wstatus)) {
+ *msgp = g_strdup_printf("Child dumpcap process stopped: %s",
+ sync_pipe_signame(WSTOPSIG(fork_child_status)));
+ ret = -1;
+ } else if (WIFSIGNALED(fork_child_status)) {
/* It died with a signal. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Child capture process died: %s%s",
- sync_pipe_signame(WTERMSIG(wstatus)),
- WCOREDUMP(wstatus) ? " - core dumped" : "");
+ *msgp = g_strdup_printf("Child dumpcap process died: %s%s",
+ sync_pipe_signame(WTERMSIG(fork_child_status)),
+ WCOREDUMP(fork_child_status) ? " - core dumped" : "");
+ ret = -1;
} else {
/* What? It had to either have exited, or stopped, or died with
a signal; what happened here? */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Child capture process died: wait status %#o", wstatus);
+ *msgp = g_strdup_printf("Bad status from waitpid(): %#o",
+ fork_child_status);
+ ret = -1;
}
+ } else {
+ *msgp = g_strdup_printf("Error from waitpid(): %s", strerror(errno));
+ ret = -1;
}
#endif
- /* No more child process. */
- capture_opts->fork_child = -1;
-
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: capture child closed");
+ return ret;
}
Linux is POSIX compliant. These are not POSIX-defined signals ---
ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez:
- ``The signals SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS
- were omitted from POSIX.1 because their behavior is
- implementation dependent and could not be adequately catego-
- rized. Conforming implementations may deliver these sig-
- nals, but must document the circumstances under which they
- are delivered and note any restrictions concerning their
- delivery.''
+ ``The signals SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS
+ were omitted from POSIX.1 because their behavior is
+ implementation dependent and could not be adequately catego-
+ rized. Conforming implementations may deliver these sig-
+ nals, but must document the circumstances under which they
+ are delivered and note any restrictions concerning their
+ delivery.''
So we only check for SIGSYS on those systems that happen to
implement them (a system can be POSIX-compliant and implement
break;
default:
- /* Returning a static buffer is ok in the context we use it here */
+ /* Returning a static buffer is ok in the context we use it here */
g_snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig);
sigmsg = sigmsg_buf;
break;
void
sync_pipe_stop(capture_options *capture_opts)
{
+#ifdef _WIN32
+ int count;
+ DWORD childstatus;
+ gboolean terminate = TRUE;
+#endif
+
if (capture_opts->fork_child != -1) {
#ifndef _WIN32
- /* send the SIGUSR1 signal to close the capture child gracefully. */
- kill(capture_opts->fork_child, SIGUSR1);
+ /* send the SIGINT signal to close the capture child gracefully. */
+ int sts = kill(capture_opts->fork_child, SIGINT);
+ if (sts != 0) {
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+ "Sending SIGINT to child failed: %s\n", strerror(errno));
+ }
#else
- /* Win32 doesn't have the kill() system call, use the special signal pipe
- instead to close the capture child gracefully. */
+#define STOP_SLEEP_TIME 500 /* ms */
+#define STOP_CHECK_TIME 50
+ /* First, use the special signal pipe to try to close the capture child
+ * gracefully.
+ */
signal_pipe_capquit_to_child(capture_opts);
+
+ /* Next, wait for the process to exit on its own */
+ for (count = 0; count < STOP_SLEEP_TIME / STOP_CHECK_TIME; count++) {
+ if (GetExitCodeProcess((HANDLE) capture_opts->fork_child, &childstatus) &&
+ childstatus != STILL_ACTIVE) {
+ terminate = FALSE;
+ break;
+ }
+ Sleep(STOP_CHECK_TIME);
+ }
+
+ /* Force the issue. */
+ if (terminate) {
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+ "sync_pipe_stop: forcing child to exit");
+ sync_pipe_kill(capture_opts->fork_child);
+ }
#endif
}
}
void
sync_pipe_kill(int fork_child)
{
- if (fork_child != -1) {
+ if (fork_child != -1) {
#ifndef _WIN32
- kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
+ int sts = kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
+ if (sts != 0) {
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+ "Sending SIGTERM to child failed: %s\n", strerror(errno));
+ }
#else
- /* Remark: This is not the preferred method of closing a process!
- * the clean way would be getting the process id of the child process,
- * then getting window handle hWnd of that process (using EnumChildWindows),
- * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0)
- *
- * Unfortunately, I don't know how to get the process id from the
- * handle. OpenProcess will get an handle (not a window handle)
- * from the process ID; it will not get a window handle from the
- * process ID. (How could it? A process can have more than one
- * window. For that matter, a process might have *no* windows,
- * as a process running dumpcap, the normal child process program,
- * probably does.)
- *
- * Hint: GenerateConsoleCtrlEvent() will only work if both processes are
- * running in the same console; that's not necessarily the case for
- * us, as we might not be running in a console.
- * And this also will require to have the process id.
- */
- TerminateProcess((HANDLE) (fork_child), 0);
+ /* Remark: This is not the preferred method of closing a process!
+ * the clean way would be getting the process id of the child process,
+ * then getting window handle hWnd of that process (using EnumChildWindows),
+ * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0)
+ *
+ * Unfortunately, I don't know how to get the process id from the
+ * handle. OpenProcess will get an handle (not a window handle)
+ * from the process ID; it will not get a window handle from the
+ * process ID. (How could it? A process can have more than one
+ * window. For that matter, a process might have *no* windows,
+ * as a process running dumpcap, the normal child process program,
+ * probably does.)
+ *
+ * Hint: GenerateConsoleCtrlEvent() will only work if both processes are
+ * running in the same console; that's not necessarily the case for
+ * us, as we might not be running in a console.
+ * And this also will require to have the process id.
+ */
+ TerminateProcess((HANDLE) (fork_child), 0);
#endif
- }
+ }
}
#endif /* HAVE_LIBPCAP */