+#endif
+
+/************* EXTCAP LOAD INTERFACE LIST ***************
+ *
+ * The following code handles loading and reloading the interface list. It is explicitly
+ * kept separate from the rest
+ */
+
+
+static void
+extcap_free_interface_info(gpointer data)
+{
+ extcap_info *info = (extcap_info *)data;
+
+ g_free(info->basename);
+ g_free(info->full_path);
+ g_free(info->version);
+ g_free(info->help);
+
+ extcap_free_interfaces(info->interfaces);
+
+ g_free(info);
+}
+
+static extcap_info *
+extcap_ensure_interface(const gchar * toolname, gboolean create_if_nonexist)
+{
+ extcap_info * element = 0;
+
+ if ( prefs.capture_no_extcap )
+ return NULL;
+
+ if ( ! toolname )
+ return element;
+
+ if ( ! _loaded_interfaces )
+ _loaded_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_interface);
+
+ element = (extcap_info *) g_hash_table_lookup(_loaded_interfaces, toolname );
+ if ( ! element && create_if_nonexist )
+ {
+ g_hash_table_insert(_loaded_interfaces, g_strdup(toolname), g_new0(extcap_info, 1));
+ element = (extcap_info *) g_hash_table_lookup(_loaded_interfaces, toolname );
+ }
+
+ return element;
+}
+
+extcap_info *
+extcap_get_tool_by_ifname(const gchar *ifname)
+{
+ if ( ifname && _tool_for_ifname )
+ {
+ gchar * toolname = (gchar *)g_hash_table_lookup(_tool_for_ifname, ifname);
+ if ( toolname )
+ return extcap_ensure_interface(toolname, FALSE);
+ }
+
+ return NULL;
+}
+
+extcap_info *
+extcap_get_tool_info(const gchar * toolname)
+{
+ return extcap_ensure_interface(toolname, FALSE);
+}
+
+static void remove_extcap_entry(gpointer entry, gpointer data _U_)
+{
+ extcap_interface *int_iter = (extcap_interface*)entry;
+
+ if (int_iter->if_type == EXTCAP_SENTENCE_EXTCAP)
+ extcap_free_interface(entry);
+}
+
+static void
+process_new_extcap(const char *extcap, char *output)
+{
+ GList * interfaces = NULL, * control_items = NULL, * walker = NULL;
+ extcap_interface * int_iter = NULL;
+ extcap_info * element = NULL;
+ iface_toolbar * toolbar_entry = NULL;
+ gchar * toolname = g_path_get_basename(extcap);
+
+ GList * interface_keys = g_hash_table_get_keys(_loaded_interfaces);
+
+ /* Load interfaces from utility */
+ interfaces = extcap_parse_interfaces(output, &control_items);
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Loading interface list for %s ", extcap);
+
+ /* Seems, that there where no interfaces to be loaded */
+ if ( ! interfaces || g_list_length(interfaces) == 0 )
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Cannot load interfaces for %s", extcap );
+ g_list_free(interface_keys);
+ g_free(toolname);
+ return;
+ }
+
+ /* Load or create the storage element for the tool */
+ element = extcap_ensure_interface(toolname, TRUE);
+ if ( element == NULL )
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR, "Cannot store interface %s, maybe duplicate?", extcap );
+ g_list_foreach(interfaces, remove_extcap_entry, NULL);
+ g_list_free(interfaces);
+ g_list_free(interface_keys);
+ g_free(toolname);
+ return;
+ }
+
+ if (control_items)
+ {
+ toolbar_entry = g_new0(iface_toolbar, 1);
+ toolbar_entry->controls = control_items;
+ }
+
+ walker = interfaces;
+ gchar* help = NULL;
+ while (walker != NULL)
+ {
+ int_iter = (extcap_interface *)walker->data;
+
+ if (int_iter->call != NULL)
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Interface found %s\n", int_iter->call);
+
+ /* Help is not necessarily stored with the interface, but rather with the version string.
+ * As the version string allways comes in front of the interfaces, this ensures, that it get's
+ * properly stored with the interface */
+ if (int_iter->if_type == EXTCAP_SENTENCE_EXTCAP)
+ {
+ if (int_iter->call != NULL)
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " Extcap [%s] ", int_iter->call);
+
+ /* Only initialize values if none are set. Need to check only one element here */
+ if ( ! element->version )
+ {
+ element->version = g_strdup(int_iter->version);
+ element->basename = g_strdup(toolname);
+ element->full_path = g_strdup(extcap);
+ element->help = g_strdup(int_iter->help);
+ }
+
+ help = int_iter->help;
+ if (toolbar_entry)
+ {
+ toolbar_entry->menu_title = g_strdup(int_iter->display);
+ toolbar_entry->help = g_strdup(int_iter->help);
+ }
+
+ walker = g_list_next(walker);
+ continue;
+ }
+
+ /* Only interface definitions will be parsed here. help is already set by the extcap element,
+ * which makes it necessary to have version in the list before the interfaces. This is normally
+ * the case by design, but could be changed by separating the information in extcap-base. */
+ if ( int_iter->if_type == EXTCAP_SENTENCE_INTERFACE )
+ {
+ if ( g_list_find(interface_keys, int_iter->call) )
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "Extcap interface \"%s\" is already provided by \"%s\" ",
+ int_iter->call, extcap_if_executable(int_iter->call));
+ walker = g_list_next(walker);
+ continue;
+ }
+
+ if ((int_iter->call != NULL) && (int_iter->display))
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " Interface [%s] \"%s\" ", int_iter->call, int_iter->display);
+
+ int_iter->extcap_path = g_strdup(extcap);
+
+ /* Only set the help, if it exists and no parsed help information is present */
+ if ( ! int_iter->help && help )
+ int_iter->help = g_strdup(help);
+
+ element->interfaces = g_list_append(element->interfaces, int_iter);
+ g_hash_table_insert(_tool_for_ifname, g_strdup(int_iter->call), g_strdup(toolname));
+
+ if (toolbar_entry)
+ {
+ if (!toolbar_entry->menu_title)
+ {
+ toolbar_entry->menu_title = g_strdup(int_iter->display);
+ }
+ toolbar_entry->ifnames = g_list_append(toolbar_entry->ifnames, g_strdup(int_iter->call));
+ }
+ }
+
+ walker = g_list_next(walker);
+ }
+
+ if (toolbar_entry && toolbar_entry->menu_title)
+ {
+ iface_toolbar_add(toolbar_entry);
+ extcap_iface_toolbar_add(extcap, toolbar_entry);
+ }
+
+ g_list_foreach(interfaces, remove_extcap_entry, NULL);
+ g_list_free(interfaces);
+ g_list_free(interface_keys);
+ g_free(toolname);
+}
+
+
+/** Thread callback to save the output of a --extcap-config call. */
+static void
+extcap_process_config_cb(thread_pool_t *pool _U_, void *data, char *output)
+{
+ extcap_iface_info_t *iface_info = (extcap_iface_info_t *)data;
+ iface_info->output = output;
+}
+
+/**
+ * Thread callback to process discovered interfaces, scheduling more tasks to
+ * retrieve the configuration for each interface. Called once for every extcap
+ * program.
+ */
+static void
+extcap_process_interfaces_cb(thread_pool_t *pool, void *data, char *output)
+{
+ extcap_run_extcaps_info_t *info = (extcap_run_extcaps_info_t *)data;
+ guint i = 0;
+ guint num_interfaces = 0;
+
+ if (!output) {
+ // No interfaces available, nothing to do.
+ return;
+ }
+
+ // Save output for process_new_extcap.
+ info->output = output;
+
+ // Are there any interfaces to query information from?
+ GList *interfaces = extcap_parse_interfaces(output, NULL);
+ for (GList *iface = interfaces; iface; iface = g_list_next(iface)) {
+ extcap_interface *intf = (extcap_interface *)iface->data;
+ if (intf->if_type == EXTCAP_SENTENCE_INTERFACE) {
+ ++num_interfaces;
+ }
+ }
+ if (num_interfaces == 0) {
+ // nothing to do.
+ g_list_free_full(interfaces, extcap_free_interface);
+ return;
+ }
+
+ /* GSList is not thread-safe, so pre-allocate an array instead. */
+ info->iface_infos = g_new0(extcap_iface_info_t, num_interfaces);
+ info->num_interfaces = num_interfaces;
+
+ // Schedule new commands to retrieve the configuration.
+ for (GList *iface = interfaces; iface; iface = g_list_next(iface)) {
+ extcap_interface *intf = (extcap_interface *)iface->data;
+ if (intf->if_type != EXTCAP_SENTENCE_INTERFACE) {
+ continue;
+ }
+
+ const char *argv[] = {
+ EXTCAP_ARGUMENT_CONFIG,
+ EXTCAP_ARGUMENT_INTERFACE,
+ intf->call,
+ NULL
+ };
+ extcap_run_task_t *task = g_new0(extcap_run_task_t, 1);
+ extcap_iface_info_t *iface_info = &info->iface_infos[i++];
+
+ task->extcap_path = info->extcap_path;
+ task->argv = g_strdupv((char **)argv);
+ task->output_cb = extcap_process_config_cb;
+ task->data = iface_info;
+ iface_info->ifname = g_strdup(intf->call);
+
+ thread_pool_push(pool, task, NULL);
+ }
+ g_list_free_full(interfaces, extcap_free_interface);
+}
+
+/**
+ * Thread callback to check whether the new-style --list-interfaces call with an
+ * explicit function succeeded. If not, schedule a call without the new version
+ * argument.
+ */
+static void
+extcap_list_interfaces_cb(thread_pool_t *pool, void *data, char *output)
+{
+ extcap_run_extcaps_info_t *info = (extcap_run_extcaps_info_t *)data;
+
+ if (!output) {
+ /* No output available, schedule a fallback query. */
+ const char *argv[] = {
+ EXTCAP_ARGUMENT_LIST_INTERFACES,
+ NULL
+ };
+ extcap_run_task_t *task = g_new0(extcap_run_task_t, 1);
+
+ task->extcap_path = info->extcap_path;
+ task->argv = g_strdupv((char **)argv);
+ task->output_cb = extcap_process_interfaces_cb;
+ task->data = info;
+
+ thread_pool_push(pool, task, NULL);
+ } else {
+ extcap_process_interfaces_cb(pool, info, output);
+ }
+}
+
+
+/* Handles loading of the interfaces.
+ *
+ * A list of interfaces can be obtained by calling \ref extcap_loaded_interfaces
+ */
+static void
+extcap_load_interface_list(void)
+{
+ if (prefs.capture_no_extcap)
+ return;
+
+ if (_toolbars)
+ {
+ // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
+ // to avoid flicker in shown toolbars when refreshing interfaces.
+ GList *toolbar_list = g_hash_table_get_values (_toolbars);
+ for (GList *walker = toolbar_list; walker; walker = walker->next)
+ {
+ iface_toolbar *toolbar = (iface_toolbar *) walker->data;
+ iface_toolbar_remove(toolbar->menu_title);
+ }
+ g_hash_table_remove_all(_toolbars);
+ } else {
+ _toolbars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_toolbar);
+ }
+
+ if (_loaded_interfaces == NULL)
+ {
+ int major = 0;
+ int minor = 0;
+ guint count = 0;
+ extcap_run_extcaps_info_t *infos;
+ GList *unused_arguments = NULL;
+
+ _loaded_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_interface_info);
+ /* Cleanup lookup table */
+ if ( _tool_for_ifname )
+ {
+ g_hash_table_remove_all(_tool_for_ifname);
+ _tool_for_ifname = 0;
+ } else {
+ _tool_for_ifname = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ }
+
+ get_ws_version_number(&major, &minor, NULL);
+ char *arg_version = g_strdup_printf("%s=%d.%d", EXTCAP_ARGUMENT_VERSION, major, minor);
+ const char *argv[] = {
+ EXTCAP_ARGUMENT_LIST_INTERFACES,
+ arg_version,
+ NULL
+ };
+ infos = (extcap_run_extcaps_info_t *)extcap_run_all(argv,
+ extcap_list_interfaces_cb, sizeof(extcap_run_extcaps_info_t),
+ &count);
+ for (guint i = 0; i < count; i++) {
+ if (!infos[i].output) {
+ continue;
+ }
+
+ // Save new extcap and each discovered interface.
+ process_new_extcap(infos[i].extcap_path, infos[i].output);
+ for (guint j = 0; j < infos[i].num_interfaces; j++) {
+ extcap_iface_info_t *iface_info = &infos[i].iface_infos[j];
+
+ if (!iface_info->output) {
+ continue;
+ }
+
+ extcap_callback_info_t cb_info = {
+ .ifname = iface_info->ifname,
+ .output = iface_info->output,
+ .data = &unused_arguments,
+ };
+ cb_preference(cb_info);
+ }
+ }
+ /* XXX rework cb_preference such that this unused list can be removed. */
+ extcap_free_if_configuration(unused_arguments, TRUE);
+ extcap_free_extcaps_info_array(infos, count);
+ g_free(arg_version);
+ }
+}