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 *filter_cm, *filter_te, *linktype_combo_box;
+ gchar *filter_text;
+ gpointer dlt_ptr;
- GtkWidget *linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+ if (!prefs.capture_syntax_check_filter)
+ return;
- if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
+ 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 ((dlt = GPOINTER_TO_INT(ptr)) == -1) {
+ if ((cfc_data.dlt = GPOINTER_TO_INT(dlt_ptr)) == -1) {
g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
}
- if (!prefs.capture_syntax_check_filter)
- return;
-
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
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 {
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)) {