diff options
Diffstat (limited to 'capchild/capture_sync.c')
-rw-r--r-- | capchild/capture_sync.c | 2077 |
1 files changed, 0 insertions, 2077 deletions
diff --git a/capchild/capture_sync.c b/capchild/capture_sync.c deleted file mode 100644 index ca59d9711e..0000000000 --- a/capchild/capture_sync.c +++ /dev/null @@ -1,2077 +0,0 @@ -/* capture_sync.c - * Synchronisation between Wireshark capture parent and child instances - * - * Wireshark - Network traffic analyzer - * By Gerald Combs <gerald@wireshark.org> - * Copyright 1998 Gerald Combs - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "config.h" - -#ifdef HAVE_LIBPCAP - -#include <glib.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <signal.h> - -#include <wsutil/strtoi.h> - -#ifdef _WIN32 -#include <wsutil/unicode-utils.h> -#include <wsutil/win32-utils.h> -#include <wsutil/ws_pipe.h> -#endif - -#ifdef HAVE_SYS_WAIT_H -# include <sys/wait.h> -#endif - -#include "caputils/capture-pcap-util.h" - -#ifndef _WIN32 -/* - * Define various POSIX macros (and, in the case of WCOREDUMP, non-POSIX - * macros) on UNIX systems that don't have them. - */ -#ifndef WIFEXITED -# define WIFEXITED(status) (((status) & 0177) == 0) -#endif -#ifndef WIFSTOPPED -# define WIFSTOPPED(status) (((status) & 0177) == 0177) -#endif -#ifndef WIFSIGNALED -# define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) -#endif -#ifndef WEXITSTATUS -# define WEXITSTATUS(status) ((status) >> 8) -#endif -#ifndef WTERMSIG -# define WTERMSIG(status) ((status) & 0177) -#endif -#ifndef WCOREDUMP -# define WCOREDUMP(status) ((status) & 0200) -#endif -#ifndef WSTOPSIG -# define WSTOPSIG(status) ((status) >> 8) -#endif -#endif /* _WIN32 */ - -#include <epan/packet.h> -#include <epan/prefs.h> - -#include "file.h" - -#include "ui/capture.h" -#include <capchild/capture_sync.h> - -#include "sync_pipe.h" - -#ifdef _WIN32 -#include "caputils/capture-wpcap.h" -#endif - -#include "ui/ws_ui_util.h" - -#include <wsutil/filesystem.h> -#include <wsutil/file_util.h> -#include <wsutil/report_message.h> -#include "extcap.h" -#include "log.h" - -#ifdef _WIN32 -#include <process.h> /* For spawning child process */ -#endif - -#include <wsutil/ws_pipe.h> - -#ifdef _WIN32 -static void create_dummy_signal_pipe(); -static HANDLE dummy_signal_pipe; /* Dummy named pipe which lets the child check for a dropped connection */ -static gchar *dummy_control_id; -#else -static const char *sync_pipe_signame(int); -#endif - - -static gboolean sync_pipe_input_cb(gint source, gpointer user_data); -static int sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp); -static void pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len); -static ssize_t pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, - char **err_msg); - -static void (*fetch_dumpcap_pid)(ws_process_id) = NULL; - -static void free_argv(char** argv, int argc) -{ - int i; - for (i = 0; i < argc; i++) - g_free(argv[i]); - g_free(argv); -} - -void -capture_session_init(capture_session *cap_session, capture_file *cf) -{ - cap_session->cf = cf; - cap_session->fork_child = WS_INVALID_PID; /* invalid process handle */ -#ifdef _WIN32 - cap_session->signal_pipe_write_fd = -1; -#endif - cap_session->state = CAPTURE_STOPPED; -#ifndef _WIN32 - cap_session->owner = getuid(); - cap_session->group = getgid(); -#endif - cap_session->count = 0; - cap_session->session_will_restart = FALSE; -} - -/* Append an arg (realloc) to an argc/argv array */ -/* (add a string pointer to a NULL-terminated array of string pointers) */ -static char ** -sync_pipe_add_arg(char **args, int *argc, const char *arg) -{ - /* Grow the array; "*argc" currently contains the number of string - pointers, *not* counting the NULL pointer at the end, so we have - to add 2 in order to get the new size of the array, including the - new pointer and the terminating NULL pointer. */ - args = (char **)g_realloc( (gpointer) args, (*argc + 2) * sizeof (char *)); - - /* Stuff the pointer into the penultimate element of the array, which - is the one at the index specified by "*argc". */ - args[*argc] = g_strdup(arg); - /* Now bump the count. */ - (*argc)++; - - /* We overwrite the NULL pointer; put it back right after the - element we added. */ - args[*argc] = NULL; - - return args; -} - -/* Initialize an argument list and add dumpcap to it. */ -static char ** -init_pipe_args(int *argc) { - char **argv; - const char *progfile_dir; - char *exename; - - progfile_dir = get_progfile_dir(); - if (progfile_dir == NULL) { - return NULL; - } - - /* Allocate the string pointer array with enough space for the - terminating NULL pointer. */ - *argc = 0; - argv = (char **)g_malloc(sizeof (char *)); - *argv = NULL; - - /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */ -#ifdef _WIN32 - exename = g_strdup_printf("%s\\dumpcap.exe", progfile_dir); -#else - exename = g_strdup_printf("%s/dumpcap", progfile_dir); -#endif - - /* Make that the first argument in the argument list (argv[0]). */ - argv = sync_pipe_add_arg(argv, argc, exename); - - /* sync_pipe_add_arg strdupes exename, so we should free our copy */ - g_free(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, capture_session *cap_session, info_data_t* cap_data, void (*update_cb)(void)) -{ -#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 */ - int signal_pipe_write_fd; - 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; - 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 */ - enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ -#endif - int sync_pipe_read_fd; - int argc; - char **argv; - int i; - guint j; - interface_options *interface_opts; - - if (capture_opts->ifaces->len > 1) - capture_opts->use_pcapng = TRUE; - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_start"); - capture_opts_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, capture_opts); - - cap_session->fork_child = WS_INVALID_PID; - - if (!extcap_init_interfaces(capture_opts)) { - report_failure("Unable to init extcaps. (tmp fifo already exists?)"); - return FALSE; - } - - argv = init_pipe_args(&argc); - if (!argv) { - /* We don't know where to find dumpcap. */ - report_failure("We don't know where to find dumpcap."); - return FALSE; - } - - if (capture_opts->ifaces->len > 1) - argv = sync_pipe_add_arg(argv, &argc, "-t"); - - if (capture_opts->use_pcapng) - argv = sync_pipe_add_arg(argv, &argc, "-n"); - else - argv = sync_pipe_add_arg(argv, &argc, "-P"); - - if (capture_opts->capture_comment) { - argv = sync_pipe_add_arg(argv, &argc, "--capture-comment"); - argv = sync_pipe_add_arg(argv, &argc, capture_opts->capture_comment); - } - - if (capture_opts->multi_files_on) { - if (capture_opts->has_autostop_filesize) { - char sfilesize[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfilesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); - argv = sync_pipe_add_arg(argv, &argc, sfilesize); - } - - if (capture_opts->has_file_duration) { - char sfile_duration[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfile_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->file_duration); - argv = sync_pipe_add_arg(argv, &argc, sfile_duration); - } - - if (capture_opts->has_file_interval) { - char sfile_interval[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfile_interval, ARGV_NUMBER_LEN, "interval:%d",capture_opts->file_interval); - argv = sync_pipe_add_arg(argv, &argc, sfile_interval); - } - - if (capture_opts->has_file_packets) { - char sfile_packets[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfile_packets, ARGV_NUMBER_LEN, "packets:%d",capture_opts->file_packets); - argv = sync_pipe_add_arg(argv, &argc, sfile_packets); - } - - if (capture_opts->has_ring_num_files) { - char sring_num_files[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sring_num_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->ring_num_files); - argv = sync_pipe_add_arg(argv, &argc, sring_num_files); - } - - if (capture_opts->has_autostop_files) { - char sautostop_files[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-a"); - g_snprintf(sautostop_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->autostop_files); - argv = sync_pipe_add_arg(argv, &argc, sautostop_files); - } - } else { - if (capture_opts->has_autostop_filesize) { - char sautostop_filesize[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-a"); - g_snprintf(sautostop_filesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); - argv = sync_pipe_add_arg(argv, &argc, sautostop_filesize); - } - } - - if (capture_opts->has_autostop_packets) { - char scount[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-c"); - g_snprintf(scount, ARGV_NUMBER_LEN, "%d",capture_opts->autostop_packets); - argv = sync_pipe_add_arg(argv, &argc, scount); - } - - if (capture_opts->has_autostop_duration) { - char sautostop_duration[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-a"); - g_snprintf(sautostop_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->autostop_duration); - argv = sync_pipe_add_arg(argv, &argc, sautostop_duration); - } - - if (capture_opts->group_read_access) { - argv = sync_pipe_add_arg(argv, &argc, "-g"); - } - - for (j = 0; j < capture_opts->ifaces->len; j++) { - interface_opts = &g_array_index(capture_opts->ifaces, interface_options, j); - - argv = sync_pipe_add_arg(argv, &argc, "-i"); - if (interface_opts->extcap_fifo != NULL) - { -#ifdef _WIN32 - char *pipe = g_strdup_printf("%s%" G_GUINTPTR_FORMAT, EXTCAP_PIPE_PREFIX, interface_opts->extcap_pipe_h); - argv = sync_pipe_add_arg(argv, &argc, pipe); - g_free(pipe); -#else - argv = sync_pipe_add_arg(argv, &argc, interface_opts->extcap_fifo); -#endif - } - else - argv = sync_pipe_add_arg(argv, &argc, interface_opts->name); - - if (interface_opts->cfilter != NULL && strlen(interface_opts->cfilter) != 0) { - argv = sync_pipe_add_arg(argv, &argc, "-f"); - argv = sync_pipe_add_arg(argv, &argc, interface_opts->cfilter); - } - if (interface_opts->has_snaplen) { - char ssnap[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-s"); - g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d", interface_opts->snaplen); - argv = sync_pipe_add_arg(argv, &argc, ssnap); - } - - if (interface_opts->linktype != -1) { - const char *linktype = linktype_val_to_name(interface_opts->linktype); - if ( linktype != NULL ) - { - argv = sync_pipe_add_arg(argv, &argc, "-y"); - argv = sync_pipe_add_arg(argv, &argc, linktype); - } - } - - if (!interface_opts->promisc_mode) { - argv = sync_pipe_add_arg(argv, &argc, "-p"); - } - -#ifdef CAN_SET_CAPTURE_BUFFER_SIZE - if (interface_opts->buffer_size != DEFAULT_CAPTURE_BUFFER_SIZE) { - char buffer_size[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-B"); - if(interface_opts->buffer_size == 0x00) - interface_opts->buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE; - g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts->buffer_size); - argv = sync_pipe_add_arg(argv, &argc, buffer_size); - } -#endif - -#ifdef HAVE_PCAP_CREATE - if (interface_opts->monitor_mode) { - argv = sync_pipe_add_arg(argv, &argc, "-I"); - } -#endif - -#ifdef HAVE_PCAP_REMOTE - if (interface_opts->datatx_udp) - argv = sync_pipe_add_arg(argv, &argc, "-u"); - - if (!interface_opts->nocap_rpcap) - argv = sync_pipe_add_arg(argv, &argc, "-r"); - - if (interface_opts->auth_type == CAPTURE_AUTH_PWD) { - char sauth[256]; - argv = sync_pipe_add_arg(argv, &argc, "-A"); - g_snprintf(sauth, sizeof(sauth), "%s:%s", - interface_opts->auth_username, - interface_opts->auth_password); - argv = sync_pipe_add_arg(argv, &argc, sauth); - } -#endif - -#ifdef HAVE_PCAP_SETSAMPLING - if (interface_opts->sampling_method != CAPTURE_SAMP_NONE) { - char ssampling[ARGV_NUMBER_LEN]; - argv = sync_pipe_add_arg(argv, &argc, "-m"); - g_snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d", - interface_opts->sampling_method == CAPTURE_SAMP_BY_COUNT ? "count" : - interface_opts->sampling_method == CAPTURE_SAMP_BY_TIMER ? "timer" : - "undef", - interface_opts->sampling_param); - argv = sync_pipe_add_arg(argv, &argc, ssampling); - } -#endif - if (interface_opts->timestamp_type) { - argv = sync_pipe_add_arg(argv, &argc, "--time-stamp-type"); - argv = sync_pipe_add_arg(argv, &argc, interface_opts->timestamp_type); - } - } - - /* 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 - - if (capture_opts->save_file) { - argv = sync_pipe_add_arg(argv, &argc, "-w"); - argv = sync_pipe_add_arg(argv, &argc, capture_opts->save_file); - } - for (i = 0; i < argc; i++) { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[%d]: %s", i, argv[i]); - } - -#ifdef _WIN32 - /* init SECURITY_ATTRIBUTES */ - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - /* Create a pipe for the child process */ - /* (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. */ - report_failure("Couldn't create sync pipe: %s", - win32strerror(GetLastError())); - free_argv(argv, argc); - return FALSE; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the message pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - sync_pipe_read_fd = _open_osfhandle( (intptr_t) sync_pipe_read, _O_BINARY); - if (sync_pipe_read_fd == -1) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); - CloseHandle(sync_pipe_read); - CloseHandle(sync_pipe_write); - free_argv(argv, argc); - return FALSE; - } - - /* 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. */ - report_failure("Couldn't create signal pipe: %s", - win32strerror(GetLastError())); - ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ - CloseHandle(sync_pipe_write); - free_argv(argv, argc); - return FALSE; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the message pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - signal_pipe_write_fd = _open_osfhandle( (intptr_t) signal_pipe, _O_BINARY); - if (sync_pipe_read_fd == -1) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); - ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ - CloseHandle(sync_pipe_write); - CloseHandle(signal_pipe); - free_argv(argv, argc); - return FALSE; - } - - /* init STARTUPINFO & PROCESS_INFORMATION */ - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - memset(&pi, 0, sizeof(pi)); -#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 = GetStdHandle(STD_INPUT_HANDLE); - si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - 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(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE, - CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { - report_failure("Couldn't run %s in child process: %s", - args->str, win32strerror(GetLastError())); - ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ - CloseHandle(sync_pipe_write); - CloseHandle(signal_pipe); - free_argv(argv, argc); - g_string_free(args, TRUE); - return FALSE; - } - cap_session->fork_child = pi.hProcess; - /* We may need to store this and close it later */ - CloseHandle(pi.hThread); - g_string_free(args, TRUE); - - cap_session->signal_pipe_write_fd = signal_pipe_write_fd; - -#else /* _WIN32 */ - if (pipe(sync_pipe) < 0) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't create sync pipe: %s", g_strerror(errno)); - free_argv(argv, argc); - return FALSE; - } - - if ((cap_session->fork_child = fork()) == 0) { - /* - * Child process - run dumpcap with the right arguments to make - * it just capture with the specified capture parameters - */ - dup2(sync_pipe[PIPE_WRITE], 2); - ws_close(sync_pipe[PIPE_READ]); - execv(argv[0], argv); - g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s", - argv[0], g_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); - } - - if (fetch_dumpcap_pid && cap_session->fork_child > 0) - fetch_dumpcap_pid(cap_session->fork_child); - - sync_pipe_read_fd = sync_pipe[PIPE_READ]; -#endif - - /* Parent process - read messages from the child process over the - sync pipe. */ - free_argv(argv, argc); - - /* 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 - ws_close(sync_pipe[PIPE_WRITE]); -#endif - - if (cap_session->fork_child == WS_INVALID_PID) { - /* We couldn't even create the child process. */ - report_failure("Couldn't create child process: %s", g_strerror(errno)); - ws_close(sync_pipe_read_fd); -#ifdef _WIN32 - ws_close(cap_session->signal_pipe_write_fd); -#endif - return FALSE; - } - - cap_session->fork_child_status = 0; - cap_session->capture_opts = capture_opts; - cap_session->cap_data_info = cap_data; - - /* we might wait for a moment till child is ready, so update screen now */ - if (update_cb) update_cb(); - - /* We were able to set up to read the capture file; - arrange that our callback be called whenever it's possible - to read from the sync pipe, so that it's called when - the child process wants to tell us something. */ - - /* we have a running capture, now wait for the real capture filename */ - pipe_input_set_handler(sync_pipe_read_fd, (gpointer) cap_session, - &cap_session->fork_child, sync_pipe_input_cb); - - return TRUE; -} - -/* - * 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, - * message_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(char* const argv[], int *data_read_fd, - int *message_read_fd, ws_process_id *fork_child, gchar **msg, void(*update_cb)(void)) -{ - enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ -#ifdef _WIN32 - 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; - STARTUPINFO si; - PROCESS_INFORMATION pi; - int i; -#else - char errmsg[1024+1]; - int sync_pipe[2]; /* pipe used to send messages from child to parent */ - int data_pipe[2]; /* pipe used to send data from child to parent */ -#endif - *fork_child = WS_INVALID_PID; - *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 */ -#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 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())); - return -1; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the message pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - *message_read_fd = _open_osfhandle( (intptr_t) sync_pipe[PIPE_READ], _O_BINARY); - if (*message_read_fd == -1) { - *msg = g_strdup_printf("Couldn't get C file handle for message read pipe: %s", g_strerror(errno)); - CloseHandle(sync_pipe[PIPE_READ]); - CloseHandle(sync_pipe[PIPE_WRITE]); - 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())); - ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ - CloseHandle(sync_pipe[PIPE_WRITE]); - return -1; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the data pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - *data_read_fd = _open_osfhandle( (intptr_t) data_pipe[PIPE_READ], _O_BINARY); - if (*data_read_fd == -1) { - *msg = g_strdup_printf("Couldn't get C file handle for data read pipe: %s", g_strerror(errno)); - CloseHandle(data_pipe[PIPE_READ]); - CloseHandle(data_pipe[PIPE_WRITE]); - ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ - CloseHandle(sync_pipe[PIPE_WRITE]); - return -1; - } - - /* init STARTUPINFO & PROCESS_INFORMATION */ - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - memset(&pi, 0, sizeof(pi)); -#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; /* handle for named pipe*/ - - si.hStdOutput = data_pipe[PIPE_WRITE]; - si.hStdError = sync_pipe[PIPE_WRITE]; -#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(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE, - CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { - *msg = g_strdup_printf("Couldn't run %s in child process: %s", - args->str, win32strerror(GetLastError())); - ws_close(*data_read_fd); /* Should close data_pipe[PIPE_READ] */ - CloseHandle(data_pipe[PIPE_WRITE]); - ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ - CloseHandle(sync_pipe[PIPE_WRITE]); - g_string_free(args, TRUE); - return -1; - } - *fork_child = pi.hProcess; - /* We may need to store this and close it later */ - CloseHandle(pi.hThread); - g_string_free(args, TRUE); -#else /* _WIN32 */ - /* Create a pipe for the child process to send us messages */ - if (pipe(sync_pipe) < 0) { - /* Couldn't create the message pipe between parent and child. */ - *msg = g_strdup_printf("Couldn't create sync pipe: %s", g_strerror(errno)); - 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", g_strerror(errno)); - ws_close(sync_pipe[PIPE_READ]); - ws_close(sync_pipe[PIPE_WRITE]); - 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 - */ - 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], argv); - g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s", - argv[0], g_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); - } - - if (fetch_dumpcap_pid && *fork_child > 0) - fetch_dumpcap_pid(*fork_child); - - *data_read_fd = data_pipe[PIPE_READ]; - *message_read_fd = sync_pipe[PIPE_READ]; -#endif - - /* Parent process - read messages from the child process over the - sync pipe. */ - - /* 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(data_pipe[PIPE_WRITE]); - CloseHandle(sync_pipe[PIPE_WRITE]); -#else - ws_close(data_pipe[PIPE_WRITE]); - ws_close(sync_pipe[PIPE_WRITE]); -#endif - - if (*fork_child == WS_INVALID_PID) { - /* We couldn't even create the child process. */ - *msg = g_strdup_printf("Couldn't create child process: %s", g_strerror(errno)); - 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 */ - if (update_cb) update_cb(); - return 0; -} - -/* - * Close the pipes we're using to read from dumpcap, and wait for it - * to exit. On success, *msgp is unchanged, and the exit status of - * dumpcap is returned. On failure (which includes "dumpcap exited - * due to being killed by a signal or an exception"), *msgp points - * to an error message for the failure, and -1 is returned. In the - * latter case, *msgp must be freed with g_free(). - */ -static int -sync_pipe_close_command(int *data_read_fd, int *message_read_fd, - ws_process_id *fork_child, gchar **msgp) -{ - 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); -#endif - - return sync_pipe_wait_for_child(*fork_child, msgp); -} - -/* - * 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_actual(char* const argv[], gchar **data, gchar **primary_msg, - gchar **secondary_msg, void(*update_cb)(void)) -{ - gchar *msg; - int data_pipe_read_fd, sync_pipe_read_fd, ret; - ws_process_id fork_child; - char *wait_msg; - gchar buffer[PIPE_BUF_SIZE+1] = {0}; - ssize_t 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; - ssize_t count; - - ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd, - &fork_child, &msg, update_cb); - 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; - *data = NULL; - - return -1; - } - - /* 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((guchar*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((guchar*)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; - - 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 = 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; -} - -/* centralised logging and timing for sync_pipe_run_command_actual(), -* redirects to sync_pipe_run_command_actual() -*/ -static int -sync_pipe_run_command(char* const argv[], gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) -{ - int ret, i; - gint64 start_time; - double elapsed; - int logging_enabled; - - /* check if logging is actually enabled, otherwise don't expend the CPU generating logging */ - logging_enabled=( (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO) & G_LOG_LEVEL_MASK & prefs.console_log_level); - if(logging_enabled){ - start_time = g_get_monotonic_time(); - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "sync_pipe_run_command() starts"); - for(i=0; argv[i] != 0; i++) { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " argv[%d]: %s", i, argv[i]); - } - } - /* do the actual sync pipe run command */ - ret=sync_pipe_run_command_actual(argv, data, primary_msg, secondary_msg, update_cb); - - if(logging_enabled){ - elapsed = (g_get_monotonic_time() - start_time) / 1e6; - - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "sync_pipe_run_command() ends, taking %.3fs, result=%d", elapsed, ret); - - } - return ret; -} - - -int -sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type, - const gchar *center_freq1, const gchar *center_freq2, - gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) -{ - int argc, ret; - char **argv; - gchar *opt; - - argv = init_pipe_args(&argc); - - if (!argv) { - *primary_msg = g_strdup("We don't know where to find dumpcap."); - *secondary_msg = NULL; - *data = NULL; - return -1; - } - - argv = sync_pipe_add_arg(argv, &argc, "-i"); - argv = sync_pipe_add_arg(argv, &argc, iface); - - if (center_freq2) - opt = g_strdup_printf("%s,%s,%s,%s", freq, type, center_freq1, center_freq2); - else if (center_freq1) - opt = g_strdup_printf("%s,%s,%s", freq, type, center_freq1); - else if (type) - opt = g_strdup_printf("%s,%s", freq, type); - else - opt = g_strdup(freq); - - if (!opt) { - *primary_msg = g_strdup("Out of mem."); - *secondary_msg = NULL; - *data = NULL; - free_argv(argv, argc); - return -1; - } - - argv = sync_pipe_add_arg(argv, &argc, "-k"); - argv = sync_pipe_add_arg(argv, &argc, opt); - -#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 - - ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); - g_free(opt); - free_argv(argv, argc); - return ret; -} - -/* - * 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 **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) -{ - int argc; - char **argv; - int ret; - - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open"); - - argv = init_pipe_args(&argc); - - if (!argv) { - *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"); - -#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 - ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); - free_argv(argv, argc); - return ret; -} - -/* - * 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_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, const gchar* auth, - gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) -{ - int argc; - char **argv; - int ret; - - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_if_capabilities_open"); - - argv = init_pipe_args(&argc); - - if (!argv) { - *primary_msg = g_strdup("We don't know where to find dumpcap."); - *secondary_msg = NULL; - *data = NULL; - return -1; - } - - /* 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, "--list-time-stamp-types"); - if (monitor_mode) - argv = sync_pipe_add_arg(argv, &argc, "-I"); - if (auth) { - argv = sync_pipe_add_arg(argv, &argc, "-A"); - argv = sync_pipe_add_arg(argv, &argc, auth); - } - -#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 - ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); - free_argv(argv, argc); - return ret; -} - -/* - * 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 -1 will be returned. - */ -int -sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, gchar **msg, void (*update_cb)(void)) -{ - int argc; - char **argv; - int message_read_fd, ret; - char *wait_msg; - gchar buffer[PIPE_BUF_SIZE+1] = {0}; - ssize_t 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_interface_stats_open"); - - argv = init_pipe_args(&argc); - - if (!argv) { - *msg = g_strdup("We don't know where to find dumpcap."); - return -1; - } - - /* Ask for the interface statistics */ - argv = sync_pipe_add_arg(argv, &argc, "-S"); - -#ifndef DEBUG_CHILD - argv = sync_pipe_add_arg(argv, &argc, "-Z"); -#ifdef _WIN32 - create_dummy_signal_pipe(); - argv = sync_pipe_add_arg(argv, &argc, dummy_control_id); -#else - argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); -#endif -#endif - ret = sync_pipe_open_command(argv, data_read_fd, &message_read_fd, - fork_child, msg, update_cb); - free_argv(argv, argc); - 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); - ws_close(message_read_fd); - ws_close(*data_read_fd); - 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; - } - - /* 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((guchar*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((guchar*)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, ws_process_id *fork_child, gchar **msg) -{ -#ifndef _WIN32 - /* - * Don't bother waiting for the child. sync_pipe_close_command - * does this for us on Windows. - */ - sync_pipe_kill(*fork_child); -#endif - 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 ssize_t -pipe_read_bytes(int pipe_fd, char *bytes, int required, char **msg) -{ - ssize_t newly; - ssize_t offset = 0; - int error; - - while(required) { - newly = ws_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_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_fd, error, - g_strerror(error)); - *msg = g_strdup_printf("Error reading from sync pipe: %s", - g_strerror(error)); - return newly; - } - - required -= (int)newly; - offset += newly; - } - - *msg = NULL; - return offset; -} - -/* - * Read a line from a pipe; similar to fgets, but doesn't block. - * - * XXX - just stops reading if there's nothing to be read right now; - * that could conceivably mean that you don't get a complete line. - */ -int -sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max) { - ssize_t newly; - int offset = -1; - - while(offset < max - 1) { - offset++; - if (! ws_pipe_data_available(pipe_fd)) - break; - newly = ws_read(pipe_fd, &bytes[offset], 1); - if (newly == 0) { - /* EOF - not necessarily an error */ - break; - } else if (newly == -1) { - /* error */ - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, - "read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); - return -1; - } else if (bytes[offset] == '\n') { - break; - } - } - - if (offset >= 0) - bytes[offset] = '\0'; - - return offset; -} - - -/* convert header values (indicator and 3-byte length) */ -static void -pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) { - - g_assert(header_len == 4); - - /* convert header values */ - *indicator = header[0]; - *block_len = (header[1]&0xFF)<<16 | (header[2]&0xFF)<<8 | (header[3]&0xFF); -} - -/* read a message from the sending pipe in the standard format - (1-byte message indicator, 3-byte message length (excluding length - and indicator field), and the rest is the message) */ -static ssize_t -pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, - char **err_msg) -{ - int required; - ssize_t newly; - gchar header[4]; - - /* read header (indicator and 3-byte length) */ - 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: %lu", pipe_fd, (long)newly); - if (newly != -1) { - /* - * Short read, but not an immediate EOF. - */ - *err_msg = g_strdup_printf("Premature EOF reading from sync pipe: got only %ld bytes", - (long)newly); - } - return -1; - } - - /* convert header values */ - pipe_convert_header((guchar*)header, 4, indicator, &required); - - /* only indicator with no value? */ - if(required == 0) { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, - "read %d indicator: %c empty value", pipe_fd, *indicator); - return 4; - } - - /* does the data fit into the given buffer? */ - if(required > len) { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, - "read %d length error, required %d > len %d, header: 0x%02x 0x%02x 0x%02x 0x%02x", - pipe_fd, required, len, - header[0], header[1], header[2], header[3]); - - /* 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 = ws_read(pipe_fd, &msg[sizeof(header)], len-sizeof(header)); - if (newly < 0) { /* error */ - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, - "read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); - } - *err_msg = g_strdup_printf("Unknown message from dumpcap reading header, try to show it as a string: %s", - msg); - return -1; - } - len = required; - - /* read the actual block data */ - newly = pipe_read_bytes(pipe_fd, msg, required, err_msg); - if(newly != required) { - if (newly != -1) { - *err_msg = g_strdup_printf("Unknown message from dumpcap reading data, 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_fd, *indicator, - len, msg); - *err_msg = NULL; - return newly + 4; -} - - -/* There's stuff to read from the sync pipe, meaning the child has sent - us a message, or the sync pipe has closed, meaning the child has - closed it (perhaps because it exited). */ -static gboolean -sync_pipe_input_cb(gint source, gpointer user_data) -{ - capture_session *cap_session = (capture_session *)user_data; - int ret; - char buffer[SP_MAX_MSG_LEN+1] = {0}; - ssize_t nread; - char indicator; - int primary_len; - char *primary_msg; - int secondary_len; - char *secondary_msg; - char *wait_msg, *combined_msg; - guint32 npackets = 0; - - nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer, - &primary_msg); - if(nread <= 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(cap_session->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; - } - } - - /* No more child process. */ - cap_session->fork_child = WS_INVALID_PID; - cap_session->fork_child_status = ret; - -#ifdef _WIN32 - ws_close(cap_session->signal_pipe_write_fd); -#endif - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: cleaning extcap pipe"); - extcap_if_cleanup(cap_session->capture_opts, &primary_msg); - capture_input_closed(cap_session, primary_msg); - g_free(primary_msg); - return FALSE; - } - - /* we got a valid message block from the child, process it */ - switch(indicator) { - case SP_FILE: - if(!capture_input_new_file(cap_session, buffer)) { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: file failed, closing capture"); - - /* We weren't able to open the new capture file; user has been - alerted. Close the sync pipe. */ - ws_close(source); - - /* The child has sent us a filename which we couldn't open. - - This could mean that the child is creating and deleting files - (ring buffer mode) faster than we can handle it. - - That should only be the case for very fast file switches; - We can't do much more than telling the child to stop. - (This is the "emergency brake" if the user e.g. wants to - switch files every second). - - This can also happen if the user specified "-", meaning - "standard output", as the capture file. */ - sync_pipe_stop(cap_session); - capture_input_closed(cap_session, NULL); - return FALSE; - } - break; - case SP_PACKET_COUNT: - if (!ws_strtou32(buffer, NULL, &npackets)) { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "Invalid packets number: %s", buffer); - } - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: new packets %u", npackets); - cap_session->count += npackets; - capture_input_new_packets(cap_session, npackets); - break; - case SP_ERROR_MSG: - /* convert primary message */ - pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_len); - primary_msg = buffer+4; - /* convert secondary message */ - pipe_convert_header((guchar*)primary_msg + primary_len, 4, &indicator, &secondary_len); - secondary_msg = primary_msg + primary_len + 4; - /* message output */ - capture_input_error_message(cap_session, primary_msg, secondary_msg); - /* the capture child will close the sync_pipe, nothing to do for now */ - /* (an error message doesn't mean we have to stop capturing) */ - break; - case SP_BAD_FILTER: { - const char *message=NULL; - guint32 indx = 0; - const gchar* end; - - if (ws_strtou32(buffer, &end, &indx) && end[0] == ':') { - message = end + 1; - } - - capture_input_cfilter_error_message(cap_session, indx, message); - /* the capture child will close the sync_pipe, nothing to do for now */ - break; - } - case SP_DROPS: { - const char *name = NULL; - const gchar* end; - guint32 num = 0; - - if (ws_strtou32(buffer, &end, &num) && end[0] == ':') { - name = end + 1; - } - - capture_input_drops(cap_session, num, name); - break; - } - default: - g_assert_not_reached(); - } - - return TRUE; -} - - - -/* - * dumpcap is exiting; wait for it to exit. On success, *msgp is - * unchanged, and the exit status of dumpcap is returned. On - * failure (which includes "dumpcap exited due to being killed by - * a signal or an exception"), *msgp points to an error message - * for the failure, and -1 is returned. In the latter case, *msgp - * must be freed with g_free(). - */ -static int -sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) -{ - int fork_child_status; -#ifndef _WIN32 - int retry_waitpid = 3; -#endif - int ret = -1; - gint64 start_time; - double elapsed; - - start_time = g_get_monotonic_time(); - - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: wait till child closed"); - g_assert(fork_child != WS_INVALID_PID); - - *msgp = NULL; /* assume no error */ -#ifdef _WIN32 - if (_cwait(&fork_child_status, (intptr_t) fork_child, _WAIT_CHILD) == -1) { - *msgp = g_strdup_printf("Error from cwait(): %s", g_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 - while (--retry_waitpid >= 0) { - if (waitpid(fork_child, &fork_child_status, 0) != -1) { - /* waitpid() succeeded */ - 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." */ - *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. */ - *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? */ - *msgp = g_strdup_printf("Bad status from waitpid(): %#o", - fork_child_status); - ret = -1; - } - } else { - /* waitpid() failed */ - if (errno == EINTR) { - /* - * Signal interrupted waitpid(). - * - * If it's SIGALRM, we just want to keep waiting, in case - * there's some timer using it (e.g., in a GUI toolkit). - * - * If you ^C TShark (or Wireshark), that should deliver - * SIGINT to dumpcap as well. dumpcap catches SIGINT, - * and should clean up and exit, so we should eventually - * see that and clean up and terminate. - * - * If we're sent a SIGTERM, we should (and do) catch it, - * and TShark, at least, calls sync_pipe_stop(). which - * kills dumpcap, so we should eventually see that and - * clean up and terminate. - */ - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "sync_pipe_wait_for_child: waitpid returned EINTR. retrying."); - continue; - } else if (errno == ECHILD) { - /* - * The process identified by fork_child either doesn't - * exist any more or isn't our child process (anymore?). - * - * echld might have already reaped the child. - */ - ret = fetch_dumpcap_pid ? 0 : -1; - } else { - /* Unknown error. */ - *msgp = g_strdup_printf("Error from waitpid(): %s", g_strerror(errno)); - ret = -1; - } - } - break; - } -#endif - - elapsed = (g_get_monotonic_time() - start_time) / 1e6; - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: capture child closed after %.3fs", elapsed); - return ret; -} - - -#ifndef _WIN32 -/* convert signal to corresponding name */ -static const char * -sync_pipe_signame(int sig) -{ - const char *sigmsg; - static char sigmsg_buf[6+1+3+1]; - - switch (sig) { - - case SIGHUP: - sigmsg = "Hangup"; - break; - - case SIGINT: - sigmsg = "Interrupted"; - break; - - case SIGQUIT: - sigmsg = "Quit"; - break; - - case SIGILL: - sigmsg = "Illegal instruction"; - break; - - case SIGTRAP: - sigmsg = "Trace trap"; - break; - - case SIGABRT: - sigmsg = "Abort"; - break; - - case SIGFPE: - sigmsg = "Arithmetic exception"; - break; - - case SIGKILL: - sigmsg = "Killed"; - break; - - case SIGBUS: - sigmsg = "Bus error"; - break; - - case SIGSEGV: - sigmsg = "Segmentation violation"; - break; - - /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO - 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.'' - - So we only check for SIGSYS on those systems that happen to - implement them (a system can be POSIX-compliant and implement - them, it's just that POSIX doesn't *require* a POSIX-compliant - system to implement them). - */ - -#ifdef SIGSYS - case SIGSYS: - sigmsg = "Bad system call"; - break; -#endif - - case SIGPIPE: - sigmsg = "Broken pipe"; - break; - - case SIGALRM: - sigmsg = "Alarm clock"; - break; - - case SIGTERM: - sigmsg = "Terminated"; - break; - - default: - /* 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; - } - return sigmsg; -} -#endif - - -#ifdef _WIN32 - -static void create_dummy_signal_pipe() { - gchar *dummy_signal_pipe_name; - - if (dummy_signal_pipe != NULL) return; - - if (!dummy_control_id) { - dummy_control_id = g_strdup_printf("%d.dummy", GetCurrentProcessId()); - } - - /* Create the signal pipe */ - dummy_signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, dummy_control_id); - dummy_signal_pipe = CreateNamedPipe(utf_8to16(dummy_signal_pipe_name), - PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL); - g_free(dummy_signal_pipe_name); -} - -/* tell the child through the signal pipe that we want to quit the capture */ -static void -signal_pipe_capquit_to_child(capture_session *cap_session) -{ - const char quit_msg[] = "QUIT"; - int ret; - - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "signal_pipe_capquit_to_child"); - - /* it doesn't matter *what* we send here, the first byte will stop the capture */ - /* simply sending a "QUIT" string */ - /*pipe_write_block(cap_session->signal_pipe_write_fd, SP_QUIT, quit_msg);*/ - ret = ws_write(cap_session->signal_pipe_write_fd, quit_msg, sizeof quit_msg); - if(ret == -1) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, - "signal_pipe_capquit_to_child: %d header: error %s", cap_session->signal_pipe_write_fd, g_strerror(errno)); - } -} -#endif - - -/* user wants to stop the capture run */ -void -sync_pipe_stop(capture_session *cap_session) -{ -#ifdef _WIN32 - int count; - DWORD childstatus; - gboolean terminate = TRUE; -#endif - if (cap_session->fork_child != WS_INVALID_PID) { -#ifndef _WIN32 - /* send the SIGINT signal to close the capture child gracefully. */ - int sts = kill(cap_session->fork_child, SIGINT); - if (sts != 0) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, - "Sending SIGINT to child failed: %s\n", g_strerror(errno)); - } -#else -#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(cap_session); - - /* Next, wait for the process to exit on its own */ - for (count = 0; count < STOP_SLEEP_TIME / STOP_CHECK_TIME; count++) { - if (GetExitCodeProcess((HANDLE) cap_session->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(cap_session->fork_child); - } -#endif - } -} - - -/* Wireshark has to exit, force the capture child to close */ -void -sync_pipe_kill(ws_process_id fork_child) -{ - if (fork_child != WS_INVALID_PID) { -#ifndef _WIN32 - 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", g_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); - -#endif - } -} - -void capture_sync_set_fetch_dumpcap_pid_cb(void(*cb)(ws_process_id pid)) { - fetch_dumpcap_pid = cb; -} - -#endif /* HAVE_LIBPCAP */ |