Check our capture filter syntax in a separate thread.

svn path=/trunk/; revision=39349
This commit is contained in:
Gerald Combs 2011-10-10 22:39:35 +00:00
parent 29e823dfba
commit 8e5c7e2da9
3 changed files with 203 additions and 27 deletions

View File

@ -266,43 +266,207 @@ capture_get_cap_settings (gchar *if_name)
return cap_settings;
}
enum cfc_state_t {
CFC_PENDING,
CFC_UNKNOWN,
CFC_VALID,
CFC_INVALID
};
typedef struct capture_filter_check {
enum cfc_state_t state;
gchar *filter_text;
GtkWidget *filter_te;
int dlt;
} capture_filter_check_t;
/* Valid states:
*
* Idle: filter_text = NULL, state = ?
* Pending: filter_text != NULL, state = CFC_PENDING
* Unknown: filter_text != NULL, state = CFC_UNKNOWN
* Known: filter_text != NULL, state = CFC_VALID || CFC_INVALID
*
* We assume that only one text entry is active at a time.
*/
/* We could make this smarter by caching results */
capture_filter_check_t cfc_data;
#ifdef USE_THREADS
GMutex *pcap_compile_mtx = NULL;
GCond *cfc_data_cond = NULL;
GMutex *cfc_data_mtx = NULL;
GThread *cfc_thread = NULL;
#endif
#if 0
#define DEBUG_SYNTAX_CHECK(state1, state2) g_warning("CF state %s -> %s : %s", state1, state2, cfc_data.filter_text)
#else
#define DEBUG_SYNTAX_CHECK(state1, state2)
#endif
static void *
check_capture_filter_syntax(void *data _U_) {
struct bpf_program fcode;
int pc_err;
#ifdef USE_THREADS
while (1) {
g_mutex_lock(cfc_data_mtx);
while (!cfc_data.filter_text || cfc_data.state != CFC_PENDING) {
/* Do we really need to use a mutex here? We only have one thread... */
g_cond_wait(cfc_data_cond, cfc_data_mtx);
}
#endif
cfc_data.state = CFC_UNKNOWN;
DEBUG_SYNTAX_CHECK("pending", "unknown");
#ifdef USE_THREADS
g_mutex_unlock(cfc_data_mtx);
g_mutex_lock(pcap_compile_mtx);
#endif
/* pcap_compile_nopcap will not alter the filter string, so the (char *) cast is "safe" */
pc_err = pcap_compile_nopcap(DUMMY_SNAPLENGTH /* use a dummy snaplength for syntax-checking */,
cfc_data.dlt, &fcode, cfc_data.filter_text, 1 /* Do optimize */,
DUMMY_NETMASK /* use a dummy netmask for syntax-checking */);
#ifdef USE_THREADS
g_mutex_unlock(pcap_compile_mtx);
g_mutex_lock(cfc_data_mtx);
#endif
if (cfc_data.state == CFC_UNKNOWN) { /* No more input came in */
if (pc_err) {
DEBUG_SYNTAX_CHECK("unknown", "known bad");
cfc_data.state = CFC_INVALID;
} else {
DEBUG_SYNTAX_CHECK("unknown", "known good");
cfc_data.state = CFC_VALID;
}
}
#ifdef USE_THREADS
g_mutex_unlock(cfc_data_mtx);
}
#endif
return NULL;
}
static gboolean
update_capture_filter_te(gpointer data _U_) {
if (!prefs.capture_syntax_check_filter)
return TRUE;
#ifdef USE_THREADS
g_mutex_lock(cfc_data_mtx);
#endif
if (cfc_data.filter_text && cfc_data.filter_te) {
if (cfc_data.state == CFC_VALID) {
colorize_filter_te_as_valid(cfc_data.filter_te);
} else if (cfc_data.state == CFC_INVALID) {
colorize_filter_te_as_invalid(cfc_data.filter_te);
} else {
colorize_filter_te_as_empty(cfc_data.filter_te);
}
if (cfc_data.state == CFC_VALID || cfc_data.state == CFC_INVALID) {
DEBUG_SYNTAX_CHECK("known", "idle");
/* Reset the current state to idle. */
if (cfc_data.filter_text != NULL) {
g_free(cfc_data.filter_text);
}
cfc_data.filter_text = NULL;
cfc_data.state = CFC_PENDING;
}
}
#ifdef USE_THREADS
g_mutex_unlock(cfc_data_mtx);
#endif
return TRUE;
}
/** Initialize background capture filter syntax checking
*/
void capture_filter_init(void) {
cfc_data.filter_text = NULL;
cfc_data.filter_te = NULL;
cfc_data.state = CFC_PENDING;
#ifdef USE_THREADS
pcap_compile_mtx = g_mutex_new();
cfc_data_cond = g_cond_new();
cfc_data_mtx = g_mutex_new();
g_timeout_add(200, update_capture_filter_te, NULL);
g_thread_create(check_capture_filter_syntax, NULL, FALSE, NULL);
#endif
}
static void
capture_filter_check_syntax_cb(GtkWidget *w _U_, gpointer user_data _U_)
{
struct bpf_program fcode;
GtkWidget *filter_cm, *filter_te;
const gchar *filter_text;
gpointer ptr;
int dlt;
GtkWidget *linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
g_assert_not_reached(); /* Programming error: somehow nothing is active */
}
if ((dlt = GPOINTER_TO_INT(ptr)) == -1) {
g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
}
GtkWidget *filter_cm, *filter_te, *linktype_combo_box;
gchar *filter_text;
gpointer dlt_ptr;
if (!prefs.capture_syntax_check_filter)
return;
linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &dlt_ptr)) {
g_assert_not_reached(); /* Programming error: somehow nothing is active */
}
if ((cfc_data.dlt = GPOINTER_TO_INT(dlt_ptr)) == -1) {
g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
}
filter_cm = g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
if (!filter_cm)
return;
filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
if (!filter_te)
return;
filter_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(filter_cm));
if (strlen(filter_text) == 0) {
colorize_filter_te_as_empty(filter_te);
return;
}
/* pcap_compile_nopcap will not alter the filter string, so the (char *) cast is "safe" */
if (pcap_compile_nopcap(DUMMY_SNAPLENGTH /* use a dummy snaplength for syntax-checking */,
dlt, &fcode, (char *)filter_text, 1 /* Do optimize */,
DUMMY_NETMASK /* use a dummy netmask for syntax-checking */) < 0) {
colorize_filter_te_as_invalid(filter_te);
} else {
colorize_filter_te_as_valid(filter_te);
#ifdef USE_THREADS
g_mutex_lock(cfc_data_mtx);
#endif
/* Ruthlessly clobber the current state. */
if (cfc_data.filter_text != NULL) {
g_free(cfc_data.filter_text);
}
cfc_data.filter_text = filter_text;
cfc_data.filter_te = filter_te;
cfc_data.state = CFC_PENDING;
DEBUG_SYNTAX_CHECK("?", "pending");
#ifdef USE_THREADS
g_cond_signal(cfc_data_cond);
g_mutex_unlock(cfc_data_mtx);
#else
check_capture_filter_syntax(NULL);
update_capture_filter_te(NULL);
#endif
}
static void
capture_filter_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
{
#ifdef USE_THREADS
g_mutex_lock(cfc_data_mtx);
#endif
/* Reset the current state to idle. */
if (cfc_data.filter_text != NULL) {
g_free(cfc_data.filter_text);
}
cfc_data.filter_text = NULL;
cfc_data.filter_te = NULL;
cfc_data.state = CFC_PENDING;
#ifdef USE_THREADS
g_mutex_unlock(cfc_data_mtx);
#endif
}
#define TIME_UNIT_SECOND 0
@ -1474,11 +1638,17 @@ capture_filter_compile_cb(GtkWidget *w _U_, gpointer user_data _U_)
pd = pcap_open_dead(dlt, DUMMY_SNAPLENGTH);
filter_cm = g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
filter_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(filter_cm));
#ifdef USE_THREADS
g_mutex_lock(pcap_compile_mtx);
#endif
/* pcap_compile will not alter the filter string, so the (char *) cast is "safe" */
#ifdef PCAP_NETMASK_UNKNOWN
if (pcap_compile(pd, &fcode, (char *)filter_text, 1 /* Do optimize */, PCAP_NETMASK_UNKNOWN) < 0) {
#else
if (pcap_compile(pd, &fcode, (char *)filter_text, 1 /* Do optimize */, 0) < 0) {
#endif
#ifdef USE_THREADS
g_mutex_unlock(pcap_compile_mtx);
#endif
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", pcap_geterr(pd));
} else {
@ -2052,6 +2222,7 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum
filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
colorize_filter_te_as_empty(filter_te);
g_signal_connect(filter_te, "changed", G_CALLBACK(capture_filter_check_syntax_cb), NULL);
g_signal_connect(filter_te, "destroy", G_CALLBACK(capture_filter_destroy_cb), NULL);
for (cf_entry = cfilter_list; cf_entry != NULL; cf_entry = g_list_next(cf_entry)) {
if (cf_entry->data && (strlen(cf_entry->data) > 0)) {

View File

@ -100,6 +100,10 @@ enum
NUM_COLUMNS
};
/** Initialize background capture filter syntax checking
*/
void capture_filter_init(void);
/** User requested the "Capture Options" dialog box by menu or toolbar.
*
* @param widget parent widget (unused)
@ -160,10 +164,10 @@ typedef struct {
cap_settings_t
capture_get_cap_settings (gchar *if_name);
GtkTreeModel*
GtkTreeModel*
create_and_fill_model (GtkTreeView *view);
gboolean
gboolean
query_tooltip_tree_view_cb (GtkWidget *widget,
gint x,
gint y,
@ -171,7 +175,7 @@ query_tooltip_tree_view_cb (GtkWidget *widget,
GtkTooltip *tooltip,
gpointer data);
void
void
activate_monitor (GtkTreeViewColumn *tree_column, GtkCellRenderer *renderer,
GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
@ -190,7 +194,7 @@ capture_remote_combo_recent_write_all(FILE *rf);
* @param s string with hostname,port,auth_type
* @return TRUE if correctly added
*/
gboolean
gboolean
capture_remote_combo_add_recent(gchar *s);
#endif

View File

@ -1008,7 +1008,7 @@ main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer
if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
gtk_window_present(GTK_WINDOW(top_level));
/* user didn't saved his current file, ask him */
dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
((cfile.state == FILE_READ_IN_PROGRESS) ? ESD_BTNS_QUIT_DONTSAVE_CANCEL : ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL),
"%sSave capture file before program quit?%s\n\n"
"If you quit the program without saving, your capture data will be discarded.",
@ -1115,7 +1115,7 @@ file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
/* user didn't saved his current file, ask him */
dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
((cfile.state == FILE_READ_IN_PROGRESS) ? ESD_BTNS_QUIT_DONTSAVE_CANCEL : ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL),
"%sSave capture file before program quit?%s\n\n"
"If you quit the program without saving, your capture data will be discarded.",
@ -2917,6 +2917,7 @@ main(int argc, char *argv[])
color_filters_init();
decode_as_init();
capture_filter_init();
/* the window can be sized only, if it's not already shown, so do it now! */
main_load_window_geometry(top_level);