3 * Routines for extcap external capture
4 * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
24 /* Include for unlink */
28 #include <sys/types.h>
29 #ifdef HAVE_SYS_WAIT_H
36 #include <epan/prefs.h>
38 #include "ui/iface_toolbar.h"
40 #include <wsutil/file_util.h>
41 #include <wsutil/filesystem.h>
42 #include <wsutil/ws_pipe.h>
43 #include <wsutil/ws_printf.h>
44 #include <wsutil/tempfile.h>
46 #include "capture_opts.h"
49 #include "extcap_parser.h"
51 #include "version_info.h"
53 static void extcap_child_watch_cb(GPid pid
, gint status
, gpointer user_data
);
55 /* internal container, for all the extcap executables that have been found.
56 * Will be reset if extcap_clear_interfaces() is being explicitly called
57 * and is being used for printing information about all extcap interfaces found,
58 * as well as storing all sub-interfaces
60 static GHashTable
* _loaded_interfaces
= NULL
;
62 /* Internal container, which maps each ifname to the tool providing it, for faster
63 * lookup. The key and string value are owned by this table.
65 static GHashTable
* _tool_for_ifname
= NULL
;
67 /* internal container, for all the extcap executables that have been found
68 * and that provides a toolbar with controls to be added to a Interface Toolbar
70 static GHashTable
*_toolbars
= NULL
;
72 /* internal container, to map preference names to pointers that hold preference
73 * values. These ensure that preferences can survive extcap if garbage
74 * collection, and does not lead to dangling pointers in the prefs subsystem.
76 static GHashTable
*_extcap_prefs_dynamic_vals
= NULL
;
78 typedef struct _extcap_callback_info_t
85 } extcap_callback_info_t
;
87 /* Callback definition for extcap_foreach */
88 typedef gboolean(*extcap_cb_t
)(extcap_callback_info_t info_structure
);
90 /** GThreadPool does not support pushing new work from a thread while waiting
91 * for the thread pool to finish. This data structure tracks ongoing work.
92 * See https://gitlab.gnome.org/GNOME/glib/issues/1598 */
93 typedef struct thread_pool
{
95 gint count
; /**< Number of tasks that have not finished. */
101 * Callback definition for extcap_run_all, invoked with a thread pool (to
102 * schedule new tasks), an opaque data parameter, and the output from last task
103 * (or NULL if it failed). The output must be freed by the callback function.
104 * The implementation MUST be thread-safe.
106 typedef void (*extcap_run_cb_t
)(thread_pool_t
*pool
, void *data
, char *output
);
108 typedef struct extcap_run_task
{
109 const char *extcap_path
;
110 char **argv
; /**< NULL-terminated arguments list, freed when the task is completed. */
111 extcap_run_cb_t output_cb
;
112 void *data
; /** Parameter to be passed to output_cb. */
115 typedef struct extcap_iface_info
{
116 char *ifname
; /**< Interface name. */
117 char *output
; /**< Output of --extcap-config. */
118 } extcap_iface_info_t
;
120 typedef struct extcap_run_extcaps_info
{
121 char *extcap_path
; /**< Extcap program path, MUST be the first member. */
122 char *output
; /**< Output of --extcap-interfaces. */
123 guint num_interfaces
; /**< Number of discovered interfaces. */
124 extcap_iface_info_t
*iface_infos
; /**< Per-interface information. */
125 } extcap_run_extcaps_info_t
;
128 static void extcap_load_interface_list(void);
130 /* Used for lazily loading our interfaces. */
131 static void extcap_ensure_all_interfaces_loaded(void) {
132 if ( !_loaded_interfaces
|| g_hash_table_size(_loaded_interfaces
) == 0 )
133 extcap_load_interface_list();
137 thread_pool_push(thread_pool_t
*pool
, gpointer data
, GError
**error
)
139 g_mutex_lock(&pool
->data_mutex
);
141 g_mutex_unlock(&pool
->data_mutex
);
142 return g_thread_pool_push(pool
->pool
, data
, error
);
146 thread_pool_wait(thread_pool_t
*pool
)
148 g_mutex_lock(&pool
->data_mutex
);
149 while (pool
->count
!= 0) {
150 g_cond_wait(&pool
->cond
, &pool
->data_mutex
);
152 g_mutex_unlock(&pool
->data_mutex
);
156 extcap_loaded_interfaces(void)
158 if (prefs
.capture_no_extcap
)
161 extcap_ensure_all_interfaces_loaded();
163 return _loaded_interfaces
;
167 extcap_clear_interfaces(void)
169 if ( _loaded_interfaces
)
170 g_hash_table_destroy(_loaded_interfaces
);
171 _loaded_interfaces
= NULL
;
173 if ( _tool_for_ifname
)
174 g_hash_table_destroy(_tool_for_ifname
);
175 _tool_for_ifname
= NULL
;
179 compare_tools(gconstpointer a
, gconstpointer b
)
181 return g_strcmp0((*(extcap_info
*const *)a
)->basename
, (*(extcap_info
*const *)b
)->basename
);
185 extcap_get_descriptions(plugin_description_callback callback
, void *callback_data
)
187 extcap_ensure_all_interfaces_loaded();
189 GHashTable
* tools
= extcap_loaded_interfaces();
190 GPtrArray
*tools_array
= g_ptr_array_new();
192 if (tools
&& g_hash_table_size(tools
) > 0) {
193 GList
* keys
= g_hash_table_get_keys(tools
);
194 GList
* walker
= g_list_first(keys
);
195 while (walker
&& walker
->data
) {
196 extcap_info
* tool
= (extcap_info
*)g_hash_table_lookup(tools
, walker
->data
);
198 g_ptr_array_add(tools_array
, tool
);
200 walker
= g_list_next(walker
);
205 g_ptr_array_sort(tools_array
, compare_tools
);
207 for (guint i
= 0; i
< tools_array
->len
; i
++) {
208 extcap_info
*tool
= (extcap_info
*)tools_array
->pdata
[i
];
209 callback(tool
->basename
, tool
->version
, "extcap", tool
->full_path
, callback_data
);
212 g_ptr_array_free(tools_array
, TRUE
);
216 print_extcap_description(const char *basename
, const char *version
,
217 const char *description
, const char *filename
,
220 ws_debug_printf("%-16s\t%s\t%s\t%s\n", basename
, version
, description
, filename
);
224 extcap_dump_all(void)
226 extcap_get_descriptions(print_extcap_description
, NULL
);
230 extcap_get_extcap_paths_from_dir(GSList
* list
, const char * dirname
)
235 GSList
* paths
= list
;
237 if ((dir
= g_dir_open(dirname
, 0, NULL
)) != NULL
) {
238 while ((file
= g_dir_read_name(dir
)) != NULL
) {
239 /* full path to extcap binary */
240 gchar
*extcap_path
= g_strdup_printf("%s" G_DIR_SEPARATOR_S
"%s", dirname
, file
);
241 /* treat anything executable as an extcap binary */
242 if (g_file_test(extcap_path
, G_FILE_TEST_IS_REGULAR
) &&
243 g_file_test(extcap_path
, G_FILE_TEST_IS_EXECUTABLE
)) {
244 paths
= g_slist_append(paths
, extcap_path
);
257 * Obtains a list of extcap program paths. Use g_slist_free_full(paths, g_free)
258 * to destroy the list.
261 extcap_get_extcap_paths(void)
263 GSList
*paths
= NULL
;
265 char *persconffile_path
= get_persconffile_path("extcap", FALSE
);
266 paths
= extcap_get_extcap_paths_from_dir(paths
, persconffile_path
);
267 g_free(persconffile_path
);
269 paths
= extcap_get_extcap_paths_from_dir(paths
, get_extcap_dir());
274 static extcap_interface
*
275 extcap_find_interface_for_ifname(const gchar
*ifname
)
277 extcap_interface
* result
= NULL
;
279 if ( !ifname
|| ! _tool_for_ifname
|| ! _loaded_interfaces
)
282 gchar
* extcap_util
= (gchar
*)g_hash_table_lookup(_tool_for_ifname
, ifname
);
286 extcap_info
* element
= (extcap_info
*)g_hash_table_lookup(_loaded_interfaces
, extcap_util
);
290 GList
* walker
= element
->interfaces
;
291 while ( walker
&& walker
->data
&& ! result
)
293 extcap_interface
* interface
= (extcap_interface
*)walker
->data
;
294 if ( g_strcmp0(interface
->call
, ifname
) == 0 )
300 walker
= g_list_next ( walker
);
307 extcap_free_toolbar(gpointer data
)
314 iface_toolbar
*toolbar
= (iface_toolbar
*)data
;
316 g_free(toolbar
->menu_title
);
317 g_free(toolbar
->help
);
318 g_list_free_full(toolbar
->ifnames
, g_free
);
319 g_list_free_full(toolbar
->controls
, (GDestroyNotify
)extcap_free_toolbar_control
);
324 extcap_if_executable(const gchar
*ifname
)
326 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
327 return interface
!= NULL
? interface
->extcap_path
: NULL
;
331 extcap_iface_toolbar_add(const gchar
*extcap
, iface_toolbar
*toolbar_entry
)
334 gboolean ret
= FALSE
;
336 if (!extcap
|| !toolbar_entry
)
341 toolname
= g_path_get_basename(extcap
);
343 if (!g_hash_table_lookup(_toolbars
, toolname
))
345 g_hash_table_insert(_toolbars
, g_strdup(toolname
), toolbar_entry
);
354 extcap_convert_arguments_to_array(GList
* arguments
)
356 gchar
** result
= NULL
;
359 GList
* walker
= g_list_first(arguments
);
362 result
= (gchar
**) g_malloc0(sizeof(gchar
*) * (g_list_length(arguments
)));
366 result
[cnt
] = g_strdup((const gchar
*)walker
->data
);
367 walker
= g_list_next(walker
);
374 static void extcap_free_array(gchar
** args
, int argc
)
378 for ( cnt
= 0; cnt
< argc
; cnt
++ )
384 extcap_free_extcaps_info_array(extcap_run_extcaps_info_t
*infos
, guint count
)
386 for (guint i
= 0; i
< count
; i
++) {
387 g_free(infos
[i
].extcap_path
);
388 g_free(infos
[i
].output
);
389 for (guint j
= 0; j
< infos
[i
].num_interfaces
; j
++) {
390 extcap_iface_info_t
*iface_info
= &infos
[i
].iface_infos
[j
];
391 g_free(iface_info
->ifname
);
392 g_free(iface_info
->output
);
394 g_free(infos
[i
].iface_infos
);
400 extcap_run_one(const extcap_interface
*interface
, GList
*arguments
, extcap_cb_t cb
, void *user_data
, char **err_str
)
402 const char *dirname
= get_extcap_dir();
403 gchar
**args
= extcap_convert_arguments_to_array(arguments
);
404 int cnt
= g_list_length(arguments
);
405 gchar
*command_output
;
406 if (ws_pipe_spawn_sync(dirname
, interface
->extcap_path
, cnt
, args
, &command_output
)) {
407 extcap_callback_info_t cb_info
= {
408 .ifname
= interface
->call
,
409 .extcap
= interface
->extcap_path
,
410 .output
= command_output
,
415 g_free(command_output
);
417 extcap_free_array(args
, cnt
);
420 /** Thread callback to run an extcap program and pass its output. */
422 extcap_thread_callback(gpointer data
, gpointer user_data
)
424 extcap_run_task_t
*task
= (extcap_run_task_t
*)data
;
425 thread_pool_t
*pool
= (thread_pool_t
*)user_data
;
426 const char *dirname
= get_extcap_dir();
428 char *command_output
;
429 if (ws_pipe_spawn_sync(dirname
, task
->extcap_path
, g_strv_length(task
->argv
), task
->argv
, &command_output
)) {
430 task
->output_cb(pool
, task
->data
, command_output
);
432 task
->output_cb(pool
, task
->data
, NULL
);
434 g_strfreev(task
->argv
);
437 // Notify when all tasks are completed and no new subtasks were created.
438 g_mutex_lock(&pool
->data_mutex
);
439 if (--pool
->count
== 0) {
440 g_cond_signal(&pool
->cond
);
442 g_mutex_unlock(&pool
->data_mutex
);
446 * Run all extcap programs with the given arguments list, invoke the callback to
447 * do some processing and return the results.
449 * @param [IN] argv NULL-terminated arguments list.
450 * @param [IN] output_cb Thread callback function that receives the output.
451 * @param [IN] data_size Size of the per-program information that will be returned.
452 * @param [OUT] count Size of the returned array.
453 * @return Array of information or NULL if there are none. The first member of
454 * each element (char *extcap_path) must be freed.
457 extcap_run_all(const char *argv
[], extcap_run_cb_t output_cb
, gsize data_size
, guint
*count
)
459 /* Need enough space for at least 'extcap_path'. */
460 g_assert(data_size
>= sizeof(char *));
462 GSList
*paths
= extcap_get_extcap_paths();
464 #if GLIB_CHECK_VERSION(2,36,0)
465 int max_threads
= (int)g_get_num_processors();
467 // If the number of processors is unavailable, just use some sane maximum.
468 // extcap should not be CPU bound, so -1 could also be used for unlimited.
477 guint64 start_time
= g_get_monotonic_time();
478 guint paths_count
= g_slist_length(paths
);
479 /* GSList is not thread-safe, so pre-allocate an array instead. */
480 gpointer infos
= g_malloc0_n(paths_count
, data_size
);
483 pool
.pool
= g_thread_pool_new(extcap_thread_callback
, &pool
, max_threads
, FALSE
, NULL
);
485 g_cond_init(&pool
.cond
);
486 g_mutex_init(&pool
.data_mutex
);
488 for (GSList
*path
= paths
; path
; path
= g_slist_next(path
), i
++) {
489 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
491 task
->extcap_path
= (char *)path
->data
;
492 task
->argv
= g_strdupv((char **)argv
);
493 task
->output_cb
= output_cb
;
494 task
->data
= ((char *)infos
) + (i
* data_size
);
495 *((char **)task
->data
) = (char *)path
->data
;
497 thread_pool_push(&pool
, task
, NULL
);
499 g_slist_free(paths
); /* Note: the contents are transferred to 'infos'. */
501 /* Wait for all (sub)tasks to complete. */
502 thread_pool_wait(&pool
);
504 g_mutex_clear(&pool
.data_mutex
);
505 g_cond_clear(&pool
.cond
);
506 g_thread_pool_free(pool
.pool
, FALSE
, TRUE
);
508 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "extcap: completed discovery of %d tools in %.3fms",
509 paths_count
, (g_get_monotonic_time() - start_time
) / 1000.0);
510 *count
= paths_count
;
514 static void extcap_free_dlt(gpointer d
, gpointer user_data _U_
)
521 g_free(((extcap_dlt
*)d
)->name
);
522 g_free(((extcap_dlt
*)d
)->display
);
526 static void extcap_free_dlts(GList
*dlts
)
528 g_list_foreach(dlts
, extcap_free_dlt
, NULL
);
532 static gboolean
cb_dlt(extcap_callback_info_t cb_info
)
534 GList
*dlts
= NULL
, *temp
= NULL
;
536 if_capabilities_t
*caps
;
537 GList
*linktype_list
= NULL
;
538 data_link_info_t
*data_link_info
;
539 extcap_dlt
*dlt_item
;
541 dlts
= extcap_parse_dlts(cb_info
.output
);
544 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Extcap pipe %s ", cb_info
.extcap
);
547 * Allocate the interface capabilities structure.
549 caps
= (if_capabilities_t
*) g_malloc(sizeof * caps
);
550 caps
->can_set_rfmon
= FALSE
;
551 caps
->timestamp_types
= NULL
;
555 dlt_item
= (extcap_dlt
*)dlts
->data
;
558 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
559 " DLT %d name=\"%s\" display=\"%s\" ", dlt_item
->number
,
560 dlt_item
->name
, dlt_item
->display
);
562 data_link_info
= g_new(data_link_info_t
, 1);
563 data_link_info
->dlt
= dlt_item
->number
;
564 data_link_info
->name
= g_strdup(dlt_item
->name
);
565 data_link_info
->description
= g_strdup(dlt_item
->display
);
566 linktype_list
= g_list_append(linktype_list
, data_link_info
);
569 dlts
= g_list_next(dlts
);
572 /* Check to see if we built a list */
573 if (linktype_list
!= NULL
&& cb_info
.data
!= NULL
)
575 caps
->data_link_types
= linktype_list
;
576 *(if_capabilities_t
**) cb_info
.data
= caps
;
582 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, " returned no DLTs");
583 *(cb_info
.err_str
) = g_strdup("Extcap returned no DLTs");
588 extcap_free_dlts(temp
);
594 extcap_get_if_dlts(const gchar
*ifname
, char **err_str
)
596 GList
* arguments
= NULL
;
597 if_capabilities_t
*caps
= NULL
;
604 /* Update the extcap interfaces and get a list of their if_infos */
605 extcap_ensure_all_interfaces_loaded();
607 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
610 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_LIST_DLTS
));
611 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
612 arguments
= g_list_append(arguments
, g_strdup(ifname
));
614 extcap_run_one(interface
, arguments
, cb_dlt
, &caps
, err_str
);
616 g_list_free_full(arguments
, g_free
);
622 static void extcap_free_interface(gpointer i
)
625 extcap_interface
*interface
= (extcap_interface
*)i
;
632 g_free(interface
->call
);
633 g_free(interface
->display
);
634 g_free(interface
->version
);
635 g_free(interface
->help
);
636 g_free(interface
->extcap_path
);
640 static void extcap_free_interfaces(GList
*interfaces
)
642 if (interfaces
== NULL
)
647 g_list_free_full(interfaces
, extcap_free_interface
);
651 if_info_compare(gconstpointer a
, gconstpointer b
)
654 const if_info_t
*if_a
= (const if_info_t
*)a
;
655 const if_info_t
*if_b
= (const if_info_t
*)b
;
657 if ((comp
= g_strcmp0(if_a
->name
, if_b
->name
)) == 0)
659 return g_strcmp0(if_a
->friendly_name
, if_b
->friendly_name
);
666 extcap_get_help_for_ifname(const char *ifname
)
668 extcap_ensure_all_interfaces_loaded();
670 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
671 return interface
!= NULL
? interface
->help
: NULL
;
675 append_extcap_interface_list(GList
*list
, char **err_str _U_
)
677 GList
*interface_list
= NULL
;
678 extcap_interface
*data
= NULL
;
679 GList
*ifutilkeys_head
= NULL
, *ifutilkeys
= NULL
;
681 if (prefs
.capture_no_extcap
)
684 /* Update the extcap interfaces and get a list of their if_infos */
685 extcap_ensure_all_interfaces_loaded();
687 ifutilkeys_head
= g_hash_table_get_keys(_loaded_interfaces
);
688 ifutilkeys
= ifutilkeys_head
;
689 while ( ifutilkeys
&& ifutilkeys
->data
)
691 extcap_info
* extinfo
=
692 (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, (gchar
*)ifutilkeys
->data
);
693 GList
* walker
= extinfo
->interfaces
;
694 while ( walker
&& walker
->data
)
696 interface_list
= g_list_append(interface_list
, walker
->data
);
697 walker
= g_list_next(walker
);
700 ifutilkeys
= g_list_next(ifutilkeys
);
702 g_list_free(ifutilkeys_head
);
705 interface_list
= g_list_sort(interface_list
, if_info_compare
);
707 /* Append the interfaces in that list to the list we're handed. */
708 while (interface_list
!= NULL
)
710 GList
*entry
= g_list_first(interface_list
);
711 data
= (extcap_interface
*)entry
->data
;
712 interface_list
= g_list_delete_link(interface_list
, entry
);
714 if_info_t
* if_info
= g_new0(if_info_t
, 1);
715 if_info
->name
= g_strdup(data
->call
);
716 if_info
->friendly_name
= g_strdup(data
->display
);
718 if_info
->type
= IF_EXTCAP
;
720 if_info
->extcap
= g_strdup(data
->extcap_path
);
722 list
= g_list_append(list
, if_info
);
728 void extcap_register_preferences(void)
730 if (prefs
.capture_no_extcap
)
733 module_t
*dev_module
= prefs_find_module("extcap");
740 // Will load information about extcaps and their supported config.
741 extcap_ensure_all_interfaces_loaded();
745 * Releases the dynamic preference value pointers. Must not be called before
746 * prefs_cleanup since these pointers could still be in use.
748 void extcap_cleanup(void)
750 if (_extcap_prefs_dynamic_vals
)
751 g_hash_table_destroy(_extcap_prefs_dynamic_vals
);
753 if (_loaded_interfaces
)
754 g_hash_table_destroy(_loaded_interfaces
);
756 if (_tool_for_ifname
)
757 g_hash_table_destroy(_tool_for_ifname
);
761 * Obtains a pointer which can store a value for the given preference name.
762 * The preference name that can be passed to the prefs API is stored into
765 * Extcap interfaces (and their preferences) are dynamic, they can be created
766 * and destroyed at will. Thus their data structures are insufficient to pass to
767 * the preferences APIs which require pointers which are valid until the
768 * preferences are removed (at exit).
770 static gchar
**extcap_prefs_dynamic_valptr(const char *name
, char **pref_name
)
773 if (!_extcap_prefs_dynamic_vals
)
775 /* Initialize table only as needed, most preferences are not dynamic */
776 _extcap_prefs_dynamic_vals
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
779 if (!g_hash_table_lookup_extended(_extcap_prefs_dynamic_vals
, name
,
780 (gpointer
*)pref_name
, (gpointer
*)&valp
))
782 /* New dynamic pref, allocate, initialize and store. */
783 valp
= g_new0(gchar
*, 1);
784 *pref_name
= g_strdup(name
);
785 g_hash_table_insert(_extcap_prefs_dynamic_vals
, *pref_name
, valp
);
790 void extcap_free_if_configuration(GList
*list
, gboolean free_args
)
794 for (elem
= g_list_first(list
); elem
; elem
= elem
->next
)
796 if (elem
->data
!= NULL
)
798 sl
= g_list_first((GList
*)elem
->data
);
801 extcap_free_arg_list(sl
);
813 extcap_pref_for_argument(const gchar
*ifname
, struct _extcap_arg
*arg
)
815 struct preference
*pref
= NULL
;
817 extcap_ensure_all_interfaces_loaded();
819 GRegex
*regex_name
= g_regex_new("[-]+", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
820 GRegex
*regex_ifname
= g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
821 if (regex_name
&& regex_ifname
)
823 if (prefs_find_module("extcap"))
825 gchar
*pref_name
= g_regex_replace(regex_name
, arg
->call
, strlen(arg
->call
), 0, "", (GRegexMatchFlags
) 0, NULL
);
826 gchar
*ifname_underscore
= g_regex_replace(regex_ifname
, ifname
, strlen(ifname
), 0, "_", (GRegexMatchFlags
) 0, NULL
);
827 gchar
*ifname_lowercase
= g_ascii_strdown(ifname_underscore
, -1);
828 gchar
*pref_ifname
= g_strconcat(ifname_lowercase
, ".", pref_name
, NULL
);
830 pref
= prefs_find_preference(prefs_find_module("extcap"), pref_ifname
);
833 g_free(ifname_underscore
);
834 g_free(ifname_lowercase
);
840 g_regex_unref(regex_name
);
844 g_regex_unref(regex_ifname
);
850 static gboolean
cb_preference(extcap_callback_info_t cb_info
)
852 GList
*arguments
= NULL
;
853 GList
**il
= (GList
**) cb_info
.data
;
854 module_t
*dev_module
= NULL
;
856 arguments
= extcap_parse_args(cb_info
.output
);
858 dev_module
= prefs_find_module("extcap");
862 GList
*walker
= arguments
;
864 GRegex
*regex_name
= g_regex_new("[-]+", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
865 GRegex
*regex_ifname
= g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
866 if (regex_name
&& regex_ifname
)
868 while (walker
!= NULL
)
870 extcap_arg
*arg
= (extcap_arg
*)walker
->data
;
871 arg
->device_name
= g_strdup(cb_info
.ifname
);
875 struct preference
*pref
= NULL
;
877 gchar
*pref_name
= g_regex_replace(regex_name
, arg
->call
, strlen(arg
->call
), 0, "", (GRegexMatchFlags
) 0, NULL
);
878 gchar
*ifname_underscore
= g_regex_replace(regex_ifname
, cb_info
.ifname
, strlen(cb_info
.ifname
), 0, "_", (GRegexMatchFlags
) 0, NULL
);
879 gchar
*ifname_lowercase
= g_ascii_strdown(ifname_underscore
, -1);
880 gchar
*pref_ifname
= g_strconcat(ifname_lowercase
, ".", pref_name
, NULL
);
882 if ((pref
= prefs_find_preference(dev_module
, pref_ifname
)) == NULL
)
884 char *pref_name_for_prefs
;
885 char *pref_title
= wmem_strdup(wmem_epan_scope(), arg
->display
);
887 arg
->pref_valptr
= extcap_prefs_dynamic_valptr(pref_ifname
, &pref_name_for_prefs
);
888 /* Set an initial value if any (the string will be copied at registration) */
889 if (arg
->default_complex
)
891 *arg
->pref_valptr
= arg
->default_complex
->_val
;
894 prefs_register_string_preference(dev_module
, pref_name_for_prefs
,
895 pref_title
, pref_title
, (const char **)arg
->pref_valptr
);
899 /* Been here before, restore stored value */
900 if (arg
->pref_valptr
== NULL
)
902 arg
->pref_valptr
= (gchar
**)g_hash_table_lookup(_extcap_prefs_dynamic_vals
, pref_ifname
);
907 g_free(ifname_underscore
);
908 g_free(ifname_lowercase
);
912 walker
= g_list_next(walker
);
917 g_regex_unref(regex_name
);
921 g_regex_unref(regex_ifname
);
925 *il
= g_list_append(*il
, arguments
);
927 /* By returning false, extcap_foreach will break on first found */
932 extcap_get_if_configuration(const char *ifname
)
934 GList
* arguments
= NULL
;
937 extcap_ensure_all_interfaces_loaded();
939 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
942 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Extcap path %s",
945 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_CONFIG
));
946 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
947 arguments
= g_list_append(arguments
, g_strdup(ifname
));
949 extcap_run_one(interface
, arguments
, cb_preference
, &ret
, NULL
);
951 g_list_free_full(arguments
, g_free
);
957 static gboolean
cb_reload_preference(extcap_callback_info_t cb_info
)
959 GList
*arguments
= NULL
, * walker
= NULL
;
960 GList
**il
= (GList
**) cb_info
.data
;
962 arguments
= extcap_parse_values(cb_info
.output
);
964 walker
= g_list_first(arguments
);
965 while (walker
!= NULL
)
967 extcap_value
* val
= (extcap_value
*)walker
->data
;
968 *il
= g_list_append(*il
, val
);
969 walker
= g_list_next(walker
);
971 g_list_free(arguments
);
973 /* By returning false, extcap_foreach will break on first found */
978 extcap_get_if_configuration_values(const char * ifname
, const char * argname
, GHashTable
*arguments
)
983 extcap_ensure_all_interfaces_loaded();
985 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
988 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Extcap path %s",
991 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_CONFIG
));
992 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
993 args
= g_list_append(args
, g_strdup(ifname
));
994 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_RELOAD_OPTION
));
995 args
= g_list_append(args
, g_strdup(argname
));
999 GList
* keys
= g_hash_table_get_keys(arguments
);
1000 GList
* walker
= g_list_first(keys
);
1003 const gchar
* key_data
= (const gchar
*)walker
->data
;
1004 args
= g_list_append(args
, g_strdup(key_data
));
1005 args
= g_list_append(args
, g_strdup((const gchar
*)g_hash_table_lookup(arguments
, key_data
)));
1006 walker
= g_list_next(walker
);
1011 extcap_run_one(interface
, args
, cb_reload_preference
, &ret
, NULL
);
1013 g_list_free_full(args
, g_free
);
1020 extcap_has_configuration(const char *ifname
, gboolean is_required
)
1022 GList
*arguments
= 0;
1023 GList
*walker
= 0, * item
= 0;
1024 gboolean found
= FALSE
;
1026 extcap_ensure_all_interfaces_loaded();
1028 arguments
= extcap_get_if_configuration(ifname
);
1029 walker
= g_list_first(arguments
);
1031 while (walker
!= NULL
&& !found
)
1033 item
= g_list_first((GList
*)(walker
->data
));
1034 while (item
!= NULL
&& !found
)
1036 if ((extcap_arg
*)(item
->data
) != NULL
)
1038 extcap_arg
*arg
= (extcap_arg
*)(item
->data
);
1039 /* Should required options be present, or any kind of options */
1044 else if (arg
->is_required
)
1046 const gchar
*stored
= NULL
;
1047 const gchar
*defval
= NULL
;
1049 if (arg
->pref_valptr
!= NULL
)
1051 stored
= *arg
->pref_valptr
;
1054 if (arg
->default_complex
!= NULL
&& arg
->default_complex
->_val
!= NULL
)
1056 defval
= arg
->default_complex
->_val
;
1059 if (arg
->is_required
)
1061 /* If stored and defval is identical and the argument is required,
1062 * configuration is needed */
1063 if (defval
&& stored
&& g_strcmp0(stored
, defval
) == 0)
1067 else if (!defval
&& (!stored
|| !*stored
))
1073 if (arg
->arg_type
== EXTCAP_ARG_FILESELECT
)
1075 if (arg
->fileexists
&& !(file_exists(defval
) || file_exists(stored
)))
1085 walker
= walker
->next
;
1087 extcap_free_if_configuration(arguments
, TRUE
);
1092 static gboolean
cb_verify_filter(extcap_callback_info_t cb_info
)
1094 extcap_filter_status
*status
= (extcap_filter_status
*)cb_info
.data
;
1095 size_t output_size
, i
;
1097 output_size
= strlen(cb_info
.output
);
1098 if (output_size
== 0) {
1099 *status
= EXTCAP_FILTER_VALID
;
1101 *status
= EXTCAP_FILTER_INVALID
;
1102 for (i
= 0; i
< output_size
; i
++) {
1103 if (cb_info
.output
[i
] == '\n' || cb_info
.output
[i
] == '\r') {
1104 cb_info
.output
[i
] = '\0';
1108 *cb_info
.err_str
= g_strdup(cb_info
.output
);
1114 extcap_filter_status
1115 extcap_verify_capture_filter(const char *ifname
, const char *filter
, gchar
**err_str
)
1117 GList
* arguments
= NULL
;
1118 extcap_filter_status status
= EXTCAP_FILTER_UNKNOWN
;
1120 extcap_ensure_all_interfaces_loaded();
1122 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
1125 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Extcap path %s",
1128 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_CAPTURE_FILTER
));
1129 arguments
= g_list_append(arguments
, g_strdup(filter
));
1130 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
1131 arguments
= g_list_append(arguments
, g_strdup(ifname
));
1133 extcap_run_one(interface
, arguments
, cb_verify_filter
, &status
, err_str
);
1134 g_list_free_full(arguments
, g_free
);
1141 extcap_has_toolbar(const char *ifname
)
1143 if (!iface_toolbar_use())
1148 extcap_ensure_all_interfaces_loaded();
1150 GList
*toolbar_list
= g_hash_table_get_values (_toolbars
);
1151 for (GList
*walker
= toolbar_list
; walker
; walker
= walker
->next
)
1153 iface_toolbar
*toolbar
= (iface_toolbar
*) walker
->data
;
1154 if (g_list_find_custom(toolbar
->ifnames
, ifname
, (GCompareFunc
) g_strcmp0
))
1156 g_list_free(toolbar_list
);
1161 g_list_free(toolbar_list
);
1165 void extcap_if_cleanup(capture_options
*capture_opts
, gchar
**errormsg
)
1167 interface_options
*interface_opts
;
1168 ws_pipe_t
*pipedata
;
1170 gboolean overwrite_exitcode
;
1172 #define STDERR_BUFFER_SIZE 1024
1174 for (icnt
= 0; icnt
< capture_opts
->ifaces
->len
; icnt
++)
1176 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
,
1179 /* skip native interfaces */
1180 if (interface_opts
->if_type
!= IF_EXTCAP
)
1185 overwrite_exitcode
= FALSE
;
1187 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
1188 "Extcap [%s] - Cleaning up fifo: %s; PID: %d", interface_opts
->name
,
1189 interface_opts
->extcap_fifo
, interface_opts
->extcap_pid
);
1191 if (interface_opts
->extcap_pipe_h
!= INVALID_HANDLE_VALUE
)
1193 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
1194 "Extcap [%s] - Closing pipe", interface_opts
->name
);
1195 FlushFileBuffers(interface_opts
->extcap_pipe_h
);
1196 DisconnectNamedPipe(interface_opts
->extcap_pipe_h
);
1197 CloseHandle(interface_opts
->extcap_pipe_h
);
1198 interface_opts
->extcap_pipe_h
= INVALID_HANDLE_VALUE
;
1200 if (interface_opts
->extcap_control_in_h
!= INVALID_HANDLE_VALUE
)
1202 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
1203 "Extcap [%s] - Closing control_in pipe", interface_opts
->name
);
1204 FlushFileBuffers(interface_opts
->extcap_control_in_h
);
1205 DisconnectNamedPipe(interface_opts
->extcap_control_in_h
);
1206 CloseHandle(interface_opts
->extcap_control_in_h
);
1207 interface_opts
->extcap_control_in_h
= INVALID_HANDLE_VALUE
;
1209 if (interface_opts
->extcap_control_out_h
!= INVALID_HANDLE_VALUE
)
1211 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
1212 "Extcap [%s] - Closing control_out pipe", interface_opts
->name
);
1213 FlushFileBuffers(interface_opts
->extcap_control_out_h
);
1214 DisconnectNamedPipe(interface_opts
->extcap_control_out_h
);
1215 CloseHandle(interface_opts
->extcap_control_out_h
);
1216 interface_opts
->extcap_control_out_h
= INVALID_HANDLE_VALUE
;
1219 if (interface_opts
->extcap_fifo
!= NULL
&& file_exists(interface_opts
->extcap_fifo
))
1221 /* the fifo will not be freed here, but with the other capture_opts in capture_sync */
1222 ws_unlink(interface_opts
->extcap_fifo
);
1223 interface_opts
->extcap_fifo
= NULL
;
1225 if (interface_opts
->extcap_control_in
&& file_exists(interface_opts
->extcap_control_in
))
1227 ws_unlink(interface_opts
->extcap_control_in
);
1228 interface_opts
->extcap_control_in
= NULL
;
1230 if (interface_opts
->extcap_control_out
&& file_exists(interface_opts
->extcap_control_out
))
1232 ws_unlink(interface_opts
->extcap_control_out
);
1233 interface_opts
->extcap_control_out
= NULL
;
1235 /* Send termination signal to child. On Linux and OSX the child will not notice that the
1236 * pipe has been closed before writing to the pipe.
1238 if (interface_opts
->extcap_pid
!= WS_INVALID_PID
)
1240 kill(interface_opts
->extcap_pid
, SIGTERM
);
1243 /* Maybe the client closed and removed fifo, but ws should check if
1244 * pid should be closed */
1245 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
1246 "Extcap [%s] - Closing spawned PID: %d", interface_opts
->name
,
1247 interface_opts
->extcap_pid
);
1249 pipedata
= (ws_pipe_t
*) interface_opts
->extcap_pipedata
;
1252 if (pipedata
->stderr_fd
> 0)
1254 buffer
= (gchar
*)g_malloc0(STDERR_BUFFER_SIZE
+ 1);
1255 ws_read_string_from_pipe(ws_get_pipe_handle(pipedata
->stderr_fd
), buffer
, STDERR_BUFFER_SIZE
+ 1);
1256 if (strlen(buffer
) > 0)
1258 pipedata
->stderr_msg
= g_strdup(buffer
);
1259 pipedata
->exitcode
= 1;
1265 /* Final child watch may not have been called */
1266 if (interface_opts
->extcap_child_watch
> 0)
1268 extcap_child_watch_cb(pipedata
->pid
, 0, capture_opts
);
1269 /* it will have changed in extcap_child_watch_cb */
1270 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
,
1275 if (pipedata
->stderr_msg
!= NULL
)
1277 overwrite_exitcode
= TRUE
;
1280 if (overwrite_exitcode
|| pipedata
->exitcode
!= 0)
1282 if (pipedata
->stderr_msg
!= NULL
)
1284 if (*errormsg
== NULL
)
1286 *errormsg
= g_strdup_printf("Error by extcap pipe: %s", pipedata
->stderr_msg
);
1290 gchar
*temp
= g_strconcat(*errormsg
, "\nError by extcap pipe: " , pipedata
->stderr_msg
, NULL
);
1294 g_free(pipedata
->stderr_msg
);
1297 pipedata
->stderr_msg
= NULL
;
1298 pipedata
->exitcode
= 0;
1302 if (interface_opts
->extcap_child_watch
> 0)
1304 g_source_remove(interface_opts
->extcap_child_watch
);
1305 interface_opts
->extcap_child_watch
= 0;
1309 if (pipedata
->stdout_fd
> 0)
1311 ws_close(pipedata
->stdout_fd
);
1314 if (pipedata
->stderr_fd
> 0)
1316 ws_close(pipedata
->stderr_fd
);
1319 if (interface_opts
->extcap_pid
!= WS_INVALID_PID
)
1321 ws_pipe_close(pipedata
);
1322 interface_opts
->extcap_pid
= WS_INVALID_PID
;
1325 interface_opts
->extcap_pipedata
= NULL
;
1332 extcap_add_arg_and_remove_cb(gpointer key
, gpointer value
, gpointer data
)
1334 GPtrArray
*args
= (GPtrArray
*)data
;
1338 g_ptr_array_add(args
, g_strdup((const gchar
*)key
));
1342 g_ptr_array_add(args
, g_strdup((const gchar
*)value
));
1351 void extcap_child_watch_cb(GPid pid
, gint status
, gpointer user_data
)
1354 interface_options
*interface_opts
;
1355 ws_pipe_t
*pipedata
= NULL
;
1356 capture_options
*capture_opts
= (capture_options
*)(user_data
);
1358 if (capture_opts
== NULL
|| capture_opts
->ifaces
== NULL
|| capture_opts
->ifaces
->len
== 0)
1363 /* Close handle to child process. */
1364 g_spawn_close_pid(pid
);
1366 /* Update extcap_pid in interface options structure. */
1367 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1369 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1370 if (interface_opts
->extcap_pid
== pid
)
1372 pipedata
= (ws_pipe_t
*)interface_opts
->extcap_pipedata
;
1373 if (pipedata
!= NULL
)
1375 interface_opts
->extcap_pid
= WS_INVALID_PID
;
1376 pipedata
->exitcode
= 0;
1378 if (WIFEXITED(status
))
1380 if (WEXITSTATUS(status
) != 0)
1382 pipedata
->exitcode
= WEXITSTATUS(status
);
1387 pipedata
->exitcode
= G_SPAWN_ERROR_FAILED
;
1392 pipedata
->exitcode
= status
;
1395 if (status
== 0 && pipedata
->stderr_msg
!= NULL
)
1397 pipedata
->exitcode
= 1;
1400 g_source_remove(interface_opts
->extcap_child_watch
);
1401 interface_opts
->extcap_child_watch
= 0;
1408 GPtrArray
*extcap_prepare_arguments(interface_options
*interface_opts
)
1410 GPtrArray
*result
= NULL
;
1412 if (interface_opts
->if_type
== IF_EXTCAP
)
1414 result
= g_ptr_array_new();
1416 #define add_arg(X) g_ptr_array_add(result, g_strdup(X))
1418 add_arg(interface_opts
->extcap
);
1419 add_arg(EXTCAP_ARGUMENT_RUN_CAPTURE
);
1420 add_arg(EXTCAP_ARGUMENT_INTERFACE
);
1421 add_arg(interface_opts
->name
);
1422 if (interface_opts
->cfilter
&& strlen(interface_opts
->cfilter
) > 0)
1424 add_arg(EXTCAP_ARGUMENT_CAPTURE_FILTER
);
1425 add_arg(interface_opts
->cfilter
);
1427 add_arg(EXTCAP_ARGUMENT_RUN_PIPE
);
1428 add_arg(interface_opts
->extcap_fifo
);
1429 if (interface_opts
->extcap_control_in
)
1431 add_arg(EXTCAP_ARGUMENT_CONTROL_OUT
);
1432 add_arg(interface_opts
->extcap_control_in
);
1434 if (interface_opts
->extcap_control_out
)
1436 add_arg(EXTCAP_ARGUMENT_CONTROL_IN
);
1437 add_arg(interface_opts
->extcap_control_out
);
1439 if (interface_opts
->extcap_args
== NULL
|| g_hash_table_size(interface_opts
->extcap_args
) == 0)
1441 /* User did not perform interface configuration.
1443 * Check if there are any boolean flags that are set by default
1444 * and hence their argument should be added.
1449 arglist
= extcap_get_if_configuration(interface_opts
->name
);
1450 for (elem
= g_list_first(arglist
); elem
; elem
= elem
->next
)
1453 extcap_arg
*arg_iter
;
1455 if (elem
->data
== NULL
)
1460 arg_list
= g_list_first((GList
*)elem
->data
);
1461 while (arg_list
!= NULL
)
1463 const gchar
*stored
= NULL
;
1464 /* In case of boolflags only first element in arg_list is relevant. */
1465 arg_iter
= (extcap_arg
*)(arg_list
->data
);
1466 if (arg_iter
->pref_valptr
!= NULL
)
1468 stored
= *arg_iter
->pref_valptr
;
1471 if (arg_iter
->arg_type
== EXTCAP_ARG_BOOLFLAG
)
1473 if (!stored
&& extcap_complex_get_bool(arg_iter
->default_complex
))
1475 add_arg(arg_iter
->call
);
1477 else if (g_strcmp0(stored
, "true") == 0)
1479 add_arg(arg_iter
->call
);
1484 if (stored
&& strlen(stored
) > 0) {
1485 add_arg(arg_iter
->call
);
1490 arg_list
= arg_list
->next
;
1494 extcap_free_if_configuration(arglist
, TRUE
);
1498 g_hash_table_foreach_remove(interface_opts
->extcap_args
, extcap_add_arg_and_remove_cb
, result
);
1508 static void ptr_array_free(gpointer data
, gpointer user_data _U_
)
1514 static gboolean
extcap_create_pipe(const gchar
*ifname
, gchar
**fifo
, HANDLE
*handle_out
, const gchar
*pipe_prefix
)
1516 gchar timestr
[ 14 + 1 ];
1517 time_t current_time
;
1518 gchar
*pipename
= NULL
;
1519 SECURITY_ATTRIBUTES security
;
1521 /* create pipename */
1522 current_time
= time(NULL
);
1524 * XXX - we trust Windows not to return a time before the Epoch here,
1525 * so we won't get a null pointer back from localtime().
1527 strftime(timestr
, sizeof(timestr
), "%Y%m%d%H%M%S", localtime(¤t_time
));
1528 pipename
= g_strconcat("\\\\.\\pipe\\", pipe_prefix
, "_", ifname
, "_", timestr
, NULL
);
1530 /* Security struct to enable Inheritable HANDLE */
1531 memset(&security
, 0, sizeof(SECURITY_ATTRIBUTES
));
1532 security
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1533 security
.bInheritHandle
= TRUE
;
1534 security
.lpSecurityDescriptor
= NULL
;
1536 /* create a namedPipe */
1537 *handle_out
= CreateNamedPipe(
1538 utf_8to16(pipename
),
1539 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
1540 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
1545 if (*handle_out
== INVALID_HANDLE_VALUE
)
1547 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "\nError creating pipe => (%d)", GetLastError());
1553 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "\nWireshark Created pipe =>(%s) handle (%" G_GUINTPTR_FORMAT
")", pipename
, *handle_out
);
1554 *fifo
= g_strdup(pipename
);
1560 static gboolean
extcap_create_pipe(const gchar
*ifname
, gchar
**fifo
, const gchar
*pipe_prefix
)
1562 gchar
*temp_name
= NULL
;
1565 gchar
*pfx
= g_strconcat(pipe_prefix
, "_", ifname
, NULL
);
1566 if ((fd
= create_tempfile(&temp_name
, pfx
, NULL
, NULL
)) < 0)
1575 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
,
1576 "Extcap - Creating fifo: %s", temp_name
);
1578 if (file_exists(temp_name
))
1580 ws_unlink(temp_name
);
1583 if (mkfifo(temp_name
, 0600) == 0)
1595 /* call mkfifo for each extcap,
1596 * returns FALSE if there's an error creating a FIFO */
1598 extcap_init_interfaces(capture_options
*capture_opts
)
1601 interface_options
*interface_opts
;
1602 ws_pipe_t
*pipedata
;
1604 extcap_ensure_all_interfaces_loaded();
1606 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1608 GPtrArray
*args
= NULL
;
1609 GPid pid
= WS_INVALID_PID
;
1611 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1613 /* skip native interfaces */
1614 if (interface_opts
->if_type
!= IF_EXTCAP
)
1619 /* create control pipes if having toolbar */
1620 if (extcap_has_toolbar(interface_opts
->name
))
1622 extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_control_in
,
1624 &interface_opts
->extcap_control_in_h
,
1626 EXTCAP_CONTROL_IN_PREFIX
);
1627 extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_control_out
,
1629 &interface_opts
->extcap_control_out_h
,
1631 EXTCAP_CONTROL_OUT_PREFIX
);
1634 /* create pipe for fifo */
1635 if (!extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_fifo
,
1637 &interface_opts
->extcap_pipe_h
,
1639 EXTCAP_PIPE_PREFIX
))
1645 /* Create extcap call */
1646 args
= extcap_prepare_arguments(interface_opts
);
1648 pipedata
= g_new0(ws_pipe_t
, 1);
1650 pid
= ws_pipe_spawn_async(pipedata
, args
);
1652 g_ptr_array_foreach(args
, ptr_array_free
, NULL
);
1653 g_ptr_array_free(args
, TRUE
);
1655 if (pid
== WS_INVALID_PID
)
1661 ws_close(pipedata
->stdin_fd
);
1662 interface_opts
->extcap_pid
= pid
;
1664 interface_opts
->extcap_child_watch
=
1665 g_child_watch_add(pid
, extcap_child_watch_cb
, (gpointer
)capture_opts
);
1668 /* On Windows, wait for extcap to connect to named pipe.
1669 * Some extcaps will present UAC screen to user.
1670 * 30 second timeout should be reasonable timeout for extcap to
1671 * connect to named pipe (including user interaction).
1672 * Wait on multiple object in case of extcap termination
1673 * without opening pipe.
1675 if (pid
!= WS_INVALID_PID
)
1677 HANDLE pipe_handles
[3];
1678 int num_pipe_handles
= 1;
1679 pipe_handles
[0] = interface_opts
->extcap_pipe_h
;
1681 if (extcap_has_toolbar(interface_opts
->name
))
1683 pipe_handles
[1] = interface_opts
->extcap_control_in_h
;
1684 pipe_handles
[2] = interface_opts
->extcap_control_out_h
;
1685 num_pipe_handles
+= 2;
1688 ws_pipe_wait_for_pipe(pipe_handles
, num_pipe_handles
, pid
);
1692 interface_opts
->extcap_pipedata
= (gpointer
) pipedata
;
1698 /************* EXTCAP LOAD INTERFACE LIST ***************
1700 * The following code handles loading and reloading the interface list. It is explicitly
1701 * kept separate from the rest
1706 extcap_free_interface_info(gpointer data
)
1708 extcap_info
*info
= (extcap_info
*)data
;
1710 g_free(info
->basename
);
1711 g_free(info
->full_path
);
1712 g_free(info
->version
);
1715 extcap_free_interfaces(info
->interfaces
);
1720 static extcap_info
*
1721 extcap_ensure_interface(const gchar
* toolname
, gboolean create_if_nonexist
)
1723 extcap_info
* element
= 0;
1725 if ( prefs
.capture_no_extcap
)
1731 if ( ! _loaded_interfaces
)
1732 _loaded_interfaces
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_interface
);
1734 element
= (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, toolname
);
1738 if ( ! element
&& create_if_nonexist
)
1740 g_hash_table_insert(_loaded_interfaces
, g_strdup(toolname
), g_new0(extcap_info
, 1));
1741 element
= (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, toolname
);
1748 extcap_get_tool_by_ifname(const gchar
*ifname
)
1750 extcap_ensure_all_interfaces_loaded();
1752 if ( ifname
&& _tool_for_ifname
)
1754 gchar
* toolname
= (gchar
*)g_hash_table_lookup(_tool_for_ifname
, ifname
);
1756 return extcap_ensure_interface(toolname
, FALSE
);
1763 extcap_get_tool_info(const gchar
* toolname
)
1765 extcap_ensure_all_interfaces_loaded();
1767 return extcap_ensure_interface(toolname
, FALSE
);
1770 static void remove_extcap_entry(gpointer entry
, gpointer data _U_
)
1772 extcap_interface
*int_iter
= (extcap_interface
*)entry
;
1774 if (int_iter
->if_type
== EXTCAP_SENTENCE_EXTCAP
)
1775 extcap_free_interface(entry
);
1779 process_new_extcap(const char *extcap
, char *output
)
1781 GList
* interfaces
= NULL
, * control_items
= NULL
, * walker
= NULL
;
1782 extcap_interface
* int_iter
= NULL
;
1783 extcap_info
* element
= NULL
;
1784 iface_toolbar
* toolbar_entry
= NULL
;
1785 gchar
* toolname
= g_path_get_basename(extcap
);
1787 GList
* interface_keys
= g_hash_table_get_keys(_loaded_interfaces
);
1789 /* Load interfaces from utility */
1790 interfaces
= extcap_parse_interfaces(output
, &control_items
);
1792 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Loading interface list for %s ", extcap
);
1794 /* Seems, that there where no interfaces to be loaded */
1795 if ( ! interfaces
|| g_list_length(interfaces
) == 0 )
1797 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Cannot load interfaces for %s", extcap
);
1798 g_list_free(interface_keys
);
1803 /* Load or create the storage element for the tool */
1804 element
= extcap_ensure_interface(toolname
, TRUE
);
1805 if ( element
== NULL
)
1807 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_WARNING
,
1808 "Cannot store interface %s, already loaded as personal plugin", extcap
);
1809 g_list_foreach(interfaces
, remove_extcap_entry
, NULL
);
1810 g_list_free(interfaces
);
1811 g_list_free(interface_keys
);
1818 toolbar_entry
= g_new0(iface_toolbar
, 1);
1819 toolbar_entry
->controls
= control_items
;
1822 walker
= interfaces
;
1824 while (walker
!= NULL
)
1826 int_iter
= (extcap_interface
*)walker
->data
;
1828 if (int_iter
->call
!= NULL
)
1829 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, "Interface found %s\n", int_iter
->call
);
1831 /* Help is not necessarily stored with the interface, but rather with the version string.
1832 * As the version string allways comes in front of the interfaces, this ensures, that it get's
1833 * properly stored with the interface */
1834 if (int_iter
->if_type
== EXTCAP_SENTENCE_EXTCAP
)
1836 if (int_iter
->call
!= NULL
)
1837 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, " Extcap [%s] ", int_iter
->call
);
1839 /* Only initialize values if none are set. Need to check only one element here */
1840 if ( ! element
->version
)
1842 element
->version
= g_strdup(int_iter
->version
);
1843 element
->basename
= g_strdup(toolname
);
1844 element
->full_path
= g_strdup(extcap
);
1845 element
->help
= g_strdup(int_iter
->help
);
1848 help
= int_iter
->help
;
1851 toolbar_entry
->menu_title
= g_strdup(int_iter
->display
);
1852 toolbar_entry
->help
= g_strdup(int_iter
->help
);
1855 walker
= g_list_next(walker
);
1859 /* Only interface definitions will be parsed here. help is already set by the extcap element,
1860 * which makes it necessary to have version in the list before the interfaces. This is normally
1861 * the case by design, but could be changed by separating the information in extcap-base. */
1862 if ( int_iter
->if_type
== EXTCAP_SENTENCE_INTERFACE
)
1864 if ( g_list_find(interface_keys
, int_iter
->call
) )
1866 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_WARNING
, "Extcap interface \"%s\" is already provided by \"%s\" ",
1867 int_iter
->call
, extcap_if_executable(int_iter
->call
));
1868 walker
= g_list_next(walker
);
1872 if ((int_iter
->call
!= NULL
) && (int_iter
->display
))
1873 g_log(LOG_DOMAIN_CAPTURE
, G_LOG_LEVEL_DEBUG
, " Interface [%s] \"%s\" ", int_iter
->call
, int_iter
->display
);
1875 int_iter
->extcap_path
= g_strdup(extcap
);
1877 /* Only set the help, if it exists and no parsed help information is present */
1878 if ( ! int_iter
->help
&& help
)
1879 int_iter
->help
= g_strdup(help
);
1881 element
->interfaces
= g_list_append(element
->interfaces
, int_iter
);
1882 g_hash_table_insert(_tool_for_ifname
, g_strdup(int_iter
->call
), g_strdup(toolname
));
1886 if (!toolbar_entry
->menu_title
)
1888 toolbar_entry
->menu_title
= g_strdup(int_iter
->display
);
1890 toolbar_entry
->ifnames
= g_list_append(toolbar_entry
->ifnames
, g_strdup(int_iter
->call
));
1894 walker
= g_list_next(walker
);
1897 if (toolbar_entry
&& toolbar_entry
->menu_title
)
1899 iface_toolbar_add(toolbar_entry
);
1900 if (extcap_iface_toolbar_add(extcap
, toolbar_entry
))
1902 toolbar_entry
= NULL
;
1906 extcap_free_toolbar(toolbar_entry
);
1907 g_list_foreach(interfaces
, remove_extcap_entry
, NULL
);
1908 g_list_free(interfaces
);
1909 g_list_free(interface_keys
);
1914 /** Thread callback to save the output of a --extcap-config call. */
1916 extcap_process_config_cb(thread_pool_t
*pool _U_
, void *data
, char *output
)
1918 extcap_iface_info_t
*iface_info
= (extcap_iface_info_t
*)data
;
1919 iface_info
->output
= output
;
1923 * Thread callback to process discovered interfaces, scheduling more tasks to
1924 * retrieve the configuration for each interface. Called once for every extcap
1928 extcap_process_interfaces_cb(thread_pool_t
*pool
, void *data
, char *output
)
1930 extcap_run_extcaps_info_t
*info
= (extcap_run_extcaps_info_t
*)data
;
1932 guint num_interfaces
= 0;
1935 // No interfaces available, nothing to do.
1939 // Save output for process_new_extcap.
1940 info
->output
= output
;
1942 // Are there any interfaces to query information from?
1943 GList
*interfaces
= extcap_parse_interfaces(output
, NULL
);
1944 for (GList
*iface
= interfaces
; iface
; iface
= g_list_next(iface
)) {
1945 extcap_interface
*intf
= (extcap_interface
*)iface
->data
;
1946 if (intf
->if_type
== EXTCAP_SENTENCE_INTERFACE
) {
1950 if (num_interfaces
== 0) {
1952 g_list_free_full(interfaces
, extcap_free_interface
);
1956 /* GSList is not thread-safe, so pre-allocate an array instead. */
1957 info
->iface_infos
= g_new0(extcap_iface_info_t
, num_interfaces
);
1958 info
->num_interfaces
= num_interfaces
;
1960 // Schedule new commands to retrieve the configuration.
1961 for (GList
*iface
= interfaces
; iface
; iface
= g_list_next(iface
)) {
1962 extcap_interface
*intf
= (extcap_interface
*)iface
->data
;
1963 if (intf
->if_type
!= EXTCAP_SENTENCE_INTERFACE
) {
1967 const char *argv
[] = {
1968 EXTCAP_ARGUMENT_CONFIG
,
1969 EXTCAP_ARGUMENT_INTERFACE
,
1973 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
1974 extcap_iface_info_t
*iface_info
= &info
->iface_infos
[i
++];
1976 task
->extcap_path
= info
->extcap_path
;
1977 task
->argv
= g_strdupv((char **)argv
);
1978 task
->output_cb
= extcap_process_config_cb
;
1979 task
->data
= iface_info
;
1980 iface_info
->ifname
= g_strdup(intf
->call
);
1982 thread_pool_push(pool
, task
, NULL
);
1984 g_list_free_full(interfaces
, extcap_free_interface
);
1988 * Thread callback to check whether the new-style --list-interfaces call with an
1989 * explicit function succeeded. If not, schedule a call without the new version
1993 extcap_list_interfaces_cb(thread_pool_t
*pool
, void *data
, char *output
)
1995 extcap_run_extcaps_info_t
*info
= (extcap_run_extcaps_info_t
*)data
;
1998 /* No output available, schedule a fallback query. */
1999 const char *argv
[] = {
2000 EXTCAP_ARGUMENT_LIST_INTERFACES
,
2003 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
2005 task
->extcap_path
= info
->extcap_path
;
2006 task
->argv
= g_strdupv((char **)argv
);
2007 task
->output_cb
= extcap_process_interfaces_cb
;
2010 thread_pool_push(pool
, task
, NULL
);
2012 extcap_process_interfaces_cb(pool
, info
, output
);
2017 /* Handles loading of the interfaces. */
2019 extcap_load_interface_list(void)
2021 if (prefs
.capture_no_extcap
)
2026 // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
2027 // to avoid flicker in shown toolbars when refreshing interfaces.
2028 GList
*toolbar_list
= g_hash_table_get_values (_toolbars
);
2029 for (GList
*walker
= toolbar_list
; walker
; walker
= walker
->next
)
2031 iface_toolbar
*toolbar
= (iface_toolbar
*) walker
->data
;
2032 iface_toolbar_remove(toolbar
->menu_title
);
2034 g_list_free(toolbar_list
);
2035 g_hash_table_remove_all(_toolbars
);
2037 _toolbars
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_toolbar
);
2040 if (_loaded_interfaces
== NULL
)
2045 extcap_run_extcaps_info_t
*infos
;
2046 GList
*unused_arguments
= NULL
;
2048 _loaded_interfaces
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_interface_info
);
2049 /* Cleanup lookup table */
2050 if ( _tool_for_ifname
)
2052 g_hash_table_remove_all(_tool_for_ifname
);
2053 _tool_for_ifname
= 0;
2055 _tool_for_ifname
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2058 get_ws_version_number(&major
, &minor
, NULL
);
2059 char *arg_version
= g_strdup_printf("%s=%d.%d", EXTCAP_ARGUMENT_VERSION
, major
, minor
);
2060 const char *argv
[] = {
2061 EXTCAP_ARGUMENT_LIST_INTERFACES
,
2065 infos
= (extcap_run_extcaps_info_t
*)extcap_run_all(argv
,
2066 extcap_list_interfaces_cb
, sizeof(extcap_run_extcaps_info_t
),
2068 for (guint i
= 0; i
< count
; i
++) {
2069 if (!infos
[i
].output
) {
2073 // Save new extcap and each discovered interface.
2074 process_new_extcap(infos
[i
].extcap_path
, infos
[i
].output
);
2075 for (guint j
= 0; j
< infos
[i
].num_interfaces
; j
++) {
2076 extcap_iface_info_t
*iface_info
= &infos
[i
].iface_infos
[j
];
2078 if (!iface_info
->output
) {
2082 extcap_callback_info_t cb_info
= {
2083 .ifname
= iface_info
->ifname
,
2084 .output
= iface_info
->output
,
2085 .data
= &unused_arguments
,
2087 cb_preference(cb_info
);
2090 /* XXX rework cb_preference such that this unused list can be removed. */
2091 extcap_free_if_configuration(unused_arguments
, TRUE
);
2092 extcap_free_extcaps_info_array(infos
, count
);
2093 g_free(arg_version
);
2098 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2103 * indent-tabs-mode: nil
2106 * vi: set shiftwidth=4 tabstop=8 expandtab:
2107 * :indentSize=4:tabSize=8:noTabs=true: