/* capture_sync.c
- * Synchronisation between Ethereal capture parent and child instances
+ * Synchronisation between Wireshark capture parent and child instances
*
* $Id$
*
#include <unistd.h>
#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
#include <signal.h>
#ifdef _WIN32
-#include <fcntl.h>
-#include "epan/strutil.h"
+#include "epan/unicode-utils.h"
#endif
#ifdef HAVE_SYS_WAIT_H
}
#endif
+/* Initialize an argument list and add dumpcap to it. */
+static const char **
+init_pipe_args(int *argc) {
+ const char **argv;
+ const char *progfile_dir;
+ char *exename;
+ progfile_dir = get_progfile_dir();
+ if (progfile_dir == NULL) {
+ return NULL;
+ }
-#define ARGV_NUMBER_LEN 24
+ /* Allocate the string pointer array with enough space for the
+ terminating NULL pointer. */
+ *argc = 0;
+ argv = g_malloc(sizeof (char *));
+ *argv = NULL;
+ /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */
+ exename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "dumpcap", progfile_dir);
+
+ /* Make that the first argument in the argument list (argv[0]). */
+ argv = sync_pipe_add_arg(argv, argc, exename);
+
+ return argv;
+}
+
+#define ARGV_NUMBER_LEN 24
/* a new capture run: start a new dumpcap task and hand over parameters through command line */
gboolean
sync_pipe_start(capture_options *capture_opts) {
enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
#endif
int sync_pipe_read_fd;
- const char *progfile_dir;
- char *exename;
int argc;
const char **argv;
capture_opts->fork_child = -1;
- progfile_dir = get_progfile_dir();
- if (progfile_dir == NULL) {
- /* We don't know where to find dumpcap. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "We don't know where to find dumpcap.");
- return FALSE;
+ 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.");
+ return FALSE;
}
- /* Allocate the string pointer array with enough space for the
- terminating NULL pointer. */
- argc = 0;
- argv = g_malloc(sizeof (char *));
- *argv = NULL;
-
- /* take ethereal's absolute program path and replace "ethereal" with "dumpcap" */
- exename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "dumpcap", progfile_dir);
-
- /* Make that the first argument in the argument list (argv[0]). */
- argv = sync_pipe_add_arg(argv, &argc, exename);
-
argv = sync_pipe_add_arg(argv, &argc, "-i");
argv = sync_pipe_add_arg(argv, &argc, capture_opts->iface);
/* 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));
+ g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
return FALSE;
}
strerror(errno));
CloseHandle(sync_pipe_read);
CloseHandle(sync_pipe_write);
+ g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
return FALSE;
}
args->str, GetLastError());
CloseHandle(sync_pipe_read);
CloseHandle(sync_pipe_write);
+ g_free( (gpointer) argv[0]);
g_free( (gpointer) argv);
return FALSE;
}
/* 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);
- /* child own's the read side now, close our handle */
+ /* 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));
+ g_free( (gpointer) argv[0]);
g_free(argv);
return FALSE;
}
if ((capture_opts->fork_child = fork()) == 0) {
/*
- * Child process - run Ethereal with the right arguments to make
- * it just pop up the live capture dialog box and capture with
- * the specified capture parameters, writing to the specified file.
+ * 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]);
- execv(exename, argv);
+ execv(argv[0], (gpointer)argv);
g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
- exename, strerror(errno));
+ argv[0], strerror(errno));
sync_pipe_errmsg_to_parent(errmsg, "");
/* Exit with "_exit()", so that we don't close the connection
sync_pipe_read_fd = sync_pipe[PIPE_READ];
#endif
- g_free(exename);
+ g_free( (gpointer) argv[0]); /* exename */
/* Parent process - read messages from the child process over the
sync pipe. */
return TRUE;
}
+/*
+ * 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().
+ */
+/* XXX - This duplicates a lot of code in sync_pipe_start() */
+#define PIPE_BUF_SIZE 5120
+static int
+sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar **msg) {
+#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 */
+ GString *args = g_string_sized_new(200);
+ gchar *quoted_arg;
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ int i;
+#else
+ 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 */
+#endif
+
+ *fork_child = -1;
+ *read_fd = -1;
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_run_command");
+
+ if (!msg) {
+ /* We can't return anything */
+#ifdef _WIN32
+ g_string_free(args, TRUE);
+#endif
+ return -1;
+ }
+#ifdef _WIN32
+ /* init SECURITY_ATTRIBUTES */
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ 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));
+ g_free( (gpointer) argv[0]);
+ g_free( (gpointer) argv);
+ return CANT_RUN_DUMPCAP;
+ }
+
+ /* init STARTUPINFO */
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+#ifdef DEBUG_CHILD
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOW;
+#else
+ 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);*/
+#endif
+
+ /* convert args array into a single string */
+ /* XXX - could change sync_pipe_add_arg() instead */
+ /* there is a drawback here: the length is internally limited to 1024 bytes */
+ for(i=0; argv[i] != 0; i++) {
+ if(i != 0) g_string_append_c(args, ' '); /* don't prepend a space before the path!!! */
+ quoted_arg = protect_arg(argv[i]);
+ g_string_append(args, quoted_arg);
+ g_free(quoted_arg);
+ }
+
+ /* 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);
+ g_free( (gpointer) argv[0]);
+ g_free( (gpointer) argv);
+ return CANT_RUN_DUMPCAP;
+ }
+ *fork_child = (int) pi.hProcess;
+ g_string_free(args, TRUE);
+
+ /* associate the operating system filehandle to a C run-time file handle */
+ /* (good file handle infos at: http://www.flounder.com/handles.htm) */
+ *read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
+
+#else /* _WIN32 */
+ if (pipe(sync_pipe) < 0) {
+ /* Couldn't create the 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;
+ }
+
+ 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]);
+ 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;
+ }
+
+ *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 */
+
+ /* 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
+ or by exiting abnormally). */
+#ifdef _WIN32
+ CloseHandle(sync_pipe_write);
+#else
+ eth_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;
+ }
+
+ /* we might wait for a moment till child is ready, so update screen now */
+ main_window_update();
+ return 0;
+}
+
+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");
+
+#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;
+}
+
+/*
+ * 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().
+ */
+/* XXX - This duplicates a lot of code in sync_pipe_start() */
+#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);
+ }
+
+#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
+
+ if (ret) {
+ g_string_free(msg_buf, TRUE);
+ return ret;
+ }
+
+ *msg = msg_buf->str;
+ g_string_free(msg_buf, FALSE);
+ return 0;
+}
+
+/*
+ * 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().
+ */
+int
+sync_interface_list_open(gchar **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;
+ }
+
+ /* 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
+ argv = sync_pipe_add_arg(argv, &argc, "-Z");
+#endif
+
+ return sync_pipe_run_command(argv, 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().
+ */
+int
+sync_linktype_list_open(gchar *ifname, gchar **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;
+ }
+
+ /* Ask for the linktype list */
+ 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");
+
+ /* dumpcap should be running in capture child mode (hidden feature) */
+#ifndef DEBUG_CHILD
+ argv = sync_pipe_add_arg(argv, &argc, "-Z");
+#endif
+
+ return sync_pipe_run_command(argv, 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.
+ */
+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;
+ }
+
+ 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;
+ }
+
+ /* Ask for the linktype list */
+ argv = sync_pipe_add_arg(argv, &argc, "-S");
+ argv = sync_pipe_add_arg(argv, &argc, "-M");
+
+ /* dumpcap should be running in capture child mode (hidden feature) */
+#ifndef DEBUG_CHILD
+ argv = sync_pipe_add_arg(argv, &argc, "-Z");
+#endif
+
+ return sync_pipe_open_command(argv, read_fd, fork_child, msg);
+}
+
+/* Close down the stats process */
+int
+sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg) {
+#ifdef _WIN32
+ return sync_pipe_close_command(read_fd, fork_child, msg);
+#else
+ return sync_pipe_close_command(read_fd, msg);
+#endif
+}
/* read a number of bytes from a pipe */
/* (blocks until enough bytes read or an error occurs) */
int newly;
int offset = 0;
-
while(required) {
newly = read(pipe, &bytes[offset], required);
if (newly == 0) {
return offset;
}
+static gboolean pipe_data_available(int pipe) {
+#ifdef _WIN32 /* PeekNamedPipe */
+ HANDLE hPipe = (HANDLE) _get_osfhandle(pipe);
+ DWORD bytes_avail;
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL))
+ return FALSE;
+
+ if (bytes_avail > 0)
+ return TRUE;
+ return FALSE;
+#else /* select */
+ fd_set rfds;
+ struct timeval timeout;
+
+ FD_ZERO(&rfds);
+ FD_SET(pipe, &rfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if (select(pipe+1, &rfds, NULL, NULL, &timeout) > 0)
+ return TRUE;
+
+ return FALSE;
+#endif
+}
+
+/* Read a line from a pipe, similar to fgets */
+int
+sync_pipe_gets_nonblock(int pipe, char *bytes, int max) {
+ int newly;
+ int offset = -1;
+
+ while(offset < max - 1) {
+ offset++;
+ if (! pipe_data_available(pipe))
+ break;
+ newly = read(pipe, &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));
+ return newly;
+ } else if (bytes[offset] == '\n') {
+ break;
+ }
+ }
+
+ if (offset >= 0)
+ bytes[offset] = '\0';
+
+ return offset;
+}
+
+
/* convert header values (indicator and 4-byte length) */
static void
-pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {
+pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {
g_assert(header_len == 4);
capturing any more packets. Pick up its exit status, and
complain if it did anything other than exit with status 0.
- 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,
+ 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);
}
-/* Ethereal has to exit, force the capture child to close */
+/* Wireshark has to exit, force the capture child to close */
void
-sync_pipe_kill(capture_options *capture_opts)
+sync_pipe_kill(int fork_child)
{
- if (capture_opts->fork_child != -1) {
+ if (fork_child != -1) {
#ifndef _WIN32
- kill(capture_opts->fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
+ kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
#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,
* us, as we might not be running in a console.
* And this also will require to have the process id.
*/
- TerminateProcess((HANDLE) (capture_opts->fork_child), 0);
+ TerminateProcess((HANDLE) (fork_child), 0);
#endif
}
}