plugins: Add a codecs API level

Add a minimum and maximum API level. Backward-compatible changes to
the API only bump the maximum API level. Backward incompatible
changes bump the maximum API level and the mininum, to the
new (maximum) level.

This may allow codec plugins to continue working without recompilation,
possibly with reduced functionality.

The API level is only defined for codecs because it is a small
and easy to define API, and very stable.

Maybe we could do the same for wiretap (file type) plugins. For the
various epan plugin types it seems pointless and futile. I cannot
see a scenario where a new Wireshark minor release does not increase
the minimum API level.
This commit is contained in:
João Valverde 2023-12-01 18:45:48 +00:00
parent b52d9173f8
commit 7f32c90ab9
23 changed files with 174 additions and 66 deletions

View File

@ -1,5 +1,5 @@
#
function(register_plugin_files _outputfile _registertype _blurb)
function(make_plugin_register _outputfile _registertype _api_level _blurb)
file(RELATIVE_PATH output "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/${_outputfile}")
add_custom_command(
OUTPUT
@ -9,6 +9,7 @@ function(register_plugin_files _outputfile _registertype _blurb)
${CMAKE_CURRENT_SOURCE_DIR}
${_registertype}
${_blurb}
${_api_level}
${ARGN}
COMMENT "Generating ${output}"
DEPENDS
@ -17,3 +18,11 @@ function(register_plugin_files _outputfile _registertype _blurb)
VERBATIM
)
endfunction()
macro(register_plugin_files _outputfile _registertype _blurb)
make_plugin_register(${_outputfile} ${_registertype} 0 ${_blurb} ${ARGN})
endmacro()
macro(register_codec_files _outputfile _api_level _blurb)
make_plugin_register(${_outputfile} plugin_codec ${_api_level} ${_blurb} ${ARGN})
endmacro()

View File

@ -169,7 +169,9 @@ by going to the plugins/foo directory and running
make install
5. Plugin registration API common to all plugin types
5. Plugin registration
5.1 Registration API common to all plugin types
You must include the plugin API header:
@ -218,7 +220,24 @@ WIRESHARK_PLUGIN_REGISTER_CODEC(&module, 0)
As the name implies each plugin binary type has a specific registration
macro (but they all have the same arguments). The macro takes a pointer to
the struct ws_module as the first argument. The second argument is
currently unused and should be zero.
unused only by codec plugins and explained in the next section. For all
other plugin types it is ignored and should be zero.
5.2 The plugin minimum API level argument
The ABI version test for equality is the main check for compatibility for
all plugin types but additionally the codec plugin API has an extra
check called the API level. The codec API is very small and changes very
infrequently. Codecs plugins that only use the API in wsutil/codecs.h should
declare a minimum API level required. If the API needs to be changed and that
can be done in a backward-compatible manner for the ABI/API then only the
maximum API level will be increased and the minimum API level can stay the same.
This means the same plugin can still be loaded successfully without
recompilation using the older API (possibly with reduced functionality).
This is currently experimental and may change in the future or be removed
entirely.
6 How to plugin related interface options

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"A-law G.711"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"G.722"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"G.726"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"G.729"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"OpenCORE Adaptive Multi Rate (AMR)"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"Internet Low Bitrate Codec (iLBC)"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"16-bit audio, mono"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"Opus audio"
${CODEC_SRC}
)

View File

@ -27,8 +27,7 @@ set_source_files_properties(
COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
)
register_plugin_files(plugin.c
plugin_codec
register_codec_files(plugin.c 1
"Bluetooth SBC"
${CODEC_SRC}
)

View File

@ -54,7 +54,7 @@
#include <epan/uat-int.h>
#include <epan/secrets.h>
#include <wsutil/codecs.h>
#include <wsutil/codecs_priv.h>
#include <wsutil/str_util.h>
#include <wsutil/utf8_entities.h>

View File

@ -23,9 +23,13 @@ registertype = sys.argv[2]
#
plugin_blurb = sys.argv[3]
#
# The fourth argument is the plugin minimum api level
#
min_api_level = sys.argv[4]
#
# All subsequent arguments are the files to scan.
#
files = sys.argv[4:]
files = sys.argv[5:]
final_filename = "plugin.c"
preamble = """\
@ -201,8 +205,8 @@ static struct ws_module module = {
.register_cb = &plugin_register,
};
%s(&module, 0)
""" % (DESCRIPTION_FLAG[registertype], plugin_blurb, PLUGIN_REGISTER[registertype])
%s(&module, %s)
""" % (DESCRIPTION_FLAG[registertype], plugin_blurb, PLUGIN_REGISTER[registertype], min_api_level)
try:
fh = open(final_filename, 'w')

View File

@ -125,7 +125,7 @@
#include "extcap.h"
#ifdef HAVE_PLUGINS
#include <wsutil/codecs.h>
#include <wsutil/codecs_priv.h>
#include <wsutil/plugins.h>
#endif

View File

@ -53,7 +53,7 @@
#include <epan/dissectors/packet-kerberos.h>
#endif
#include <wsutil/codecs.h>
#include <wsutil/codecs_priv.h>
#include <extcap.h>

View File

@ -51,7 +51,7 @@
#include <epan/dissectors/packet-kerberos.h>
#endif
#include <wsutil/codecs.h>
#include <wsutil/codecs_priv.h>
#include <extcap.h>

View File

@ -14,7 +14,7 @@
#include "config.h"
#include <wsutil/codecs.h>
#include <wsutil/codecs_priv.h>
#include <epan/rtp_pt.h>
#include <epan/dissectors/packet-rtp.h>

View File

@ -12,4 +12,13 @@
#define WIRESHARK_ABI_VERSION_WIRETAP @PROJECT_ABI_VERSION_WIRETAP@
#define WIRESHARK_ABI_VERSION_CODEC @PROJECT_ABI_VERSION_CODEC@
/*
* API level for codec plugins
*/
/* The minimum level is the minimum API requirement for a codec
* to be used with this version of Wireshark (possibly with reduced functionality). */
#define WIRESHARK_API_MIN_LEVEL_CODEC 1
/* The maximum level supported for this version of Wireshark. */
#define WIRESHARK_API_MAX_LEVEL_CODEC 1
#endif /* __WS_VERSION_H__ */

View File

@ -10,9 +10,8 @@
#include "config.h"
#include "codecs.h"
#include "codecs_priv.h"
#include <wsutil/wslog.h>
#ifdef HAVE_PLUGINS
#include <wsutil/plugins.h>
#endif

View File

@ -11,38 +11,31 @@
#ifndef _CODECS_H_
#define _CODECS_H_
#include "ws_symbol_export.h"
#include "ws_attributes.h"
#include <stdbool.h>
#include "wsutil/wmem/wmem_map.h"
#include <wireshark.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
* IMPORTANT: This header is the public plugin API for codecs.
*
* When this API changes the API level must be bumped in ws_version.h.in.
* If the change is backward-compatible only the maximum (codec) API level is increased by one.
* If the change is backward-incompatible, meaning a plugin that does not use
* new functionaly may not compile anymore, both the maximum (codec) API level is increased by one
* and the minimum (codec) API level is bumped to the new maximum (codec) API level.
*
* API functionality above level one should be annotated with a comment indicating
* the API level required (when it was first introduced).
*/
typedef struct {
void (*register_codec_module)(void); /* routine to call to register a codec */
} codecs_plugin;
WS_DLL_PUBLIC void codecs_register_plugin(const codecs_plugin *plug);
/**
* For all built-in codecs and codec plugins, call their register routines.
*/
WS_DLL_PUBLIC void codecs_init(void);
WS_DLL_PUBLIC void codecs_cleanup(void);
/**
* Get compile-time information for libraries used by libwscodecs.
*/
WS_DLL_PUBLIC void codec_get_compiled_version_info(GString *str);
struct codec_handle;
typedef struct codec_handle *codec_handle_t;
typedef struct _codec_context_t {
unsigned sample_rate;
unsigned channels;
@ -119,15 +112,6 @@ typedef size_t (*codec_decode_fn)(codec_context_t *context,
WS_DLL_PUBLIC bool register_codec(const char *name, codec_init_fn init_fn,
codec_release_fn release_fn, codec_get_channels_fn channels_fn,
codec_get_frequency_fn frequency_fn, codec_decode_fn decode_fn);
WS_DLL_PUBLIC bool deregister_codec(const char *name);
WS_DLL_PUBLIC codec_handle_t find_codec(const char *name);
WS_DLL_PUBLIC void *codec_init(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC void codec_release(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC unsigned codec_get_channels(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC unsigned codec_get_frequency(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC size_t codec_decode(codec_handle_t codec, codec_context_t *context,
const void *inputBytes, size_t inputBytesSize,
void *outputSamples, size_t *outputSamplesSize);
#ifdef __cplusplus
}

63
wsutil/codecs_priv.h Normal file
View File

@ -0,0 +1,63 @@
/** @file
* codecs interface 2007 Tomas Kukosa
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef _CODECS_INT_H_
#define _CODECS_INT_H_
#include "codecs.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct codec_handle;
typedef struct codec_handle *codec_handle_t;
WS_DLL_PUBLIC bool deregister_codec(const char *name);
WS_DLL_PUBLIC codec_handle_t find_codec(const char *name);
WS_DLL_PUBLIC void *codec_init(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC void codec_release(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC unsigned codec_get_channels(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC unsigned codec_get_frequency(codec_handle_t codec, codec_context_t *context);
WS_DLL_PUBLIC size_t codec_decode(codec_handle_t codec, codec_context_t *context,
const void *inputBytes, size_t inputBytesSize,
void *outputSamples, size_t *outputSamplesSize);
/**
* For all built-in codecs and codec plugins, call their register routines.
*/
WS_DLL_PUBLIC void codecs_init(void);
WS_DLL_PUBLIC void codecs_cleanup(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _CODECS_INT_H_ */
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -111,7 +111,7 @@ compare_plugins(gconstpointer a, gconstpointer b)
static bool
pass_plugin_compatibility(const char *name, plugin_type_e type,
int abi_version)
int abi_version, int min_api_level)
{
if (abi_version != plugins_abi_version(type)) {
report_failure("The plugin '%s' has incompatible ABI, have version %d, expected %d",
@ -119,6 +119,14 @@ pass_plugin_compatibility(const char *name, plugin_type_e type,
return false;
}
/* Check if the minimum requested API level is supported by this version
* of Wireshark (only used with codec plugins). */
if (min_api_level > 0 && min_api_level > plugins_api_max_level(type)) {
report_failure("The plugin '%s' requires API level %d, have %d",
name, min_api_level, plugins_api_max_level(type));
return false;
}
return true;
}
@ -136,6 +144,7 @@ scan_plugins_dir(GHashTable *plugins_module, const char *dirpath,
plugin *new_plug;
plugin_type_e have_type;
int abi_version;
int min_api_level;
struct ws_module *module;
char *s;
@ -196,7 +205,7 @@ scan_plugins_dir(GHashTable *plugins_module, const char *dirpath,
DIAG_OFF_PEDANTIC
/* Found it, load module. */
have_type = ((ws_load_module_func)symbol)(&abi_version, NULL, &module);
have_type = ((ws_load_module_func)symbol)(&abi_version, &min_api_level, &module);
DIAG_ON_PEDANTIC
if (have_type != type) {
@ -208,7 +217,7 @@ DIAG_ON_PEDANTIC
continue;
}
if (!pass_plugin_compatibility(name, type, abi_version)) {
if (!pass_plugin_compatibility(name, type, abi_version, min_api_level)) {
g_module_close(handle);
g_free(plugin_file);
continue;
@ -367,6 +376,7 @@ plugins_check_file(const char *from_filename)
void *symbol;
plugin_type_e have_type;
int abi_version;
int min_api_level;
handle = g_module_open(from_filename, G_MODULE_BIND_LAZY);
if (handle == NULL) {
@ -383,12 +393,12 @@ plugins_check_file(const char *from_filename)
DIAG_OFF_PEDANTIC
/* Load module. */
have_type = ((ws_load_module_func)symbol)(&abi_version, NULL, NULL);
have_type = ((ws_load_module_func)symbol)(&abi_version, &min_api_level, NULL);
DIAG_ON_PEDANTIC
name = g_path_get_basename(from_filename);
if (!pass_plugin_compatibility(name, have_type, abi_version)) {
if (!pass_plugin_compatibility(name, have_type, abi_version, min_api_level)) {
g_module_close(handle);
g_free(name);
return WS_PLUGIN_NONE;
@ -412,6 +422,23 @@ plugins_file_suffix(plugin_type_e type)
return ws_strdup_printf("%s.%d", WS_PLUGIN_MODULE_SUFFIX, plugins_abi_version(type));
}
int
plugins_api_max_level(plugin_type_e type)
{
/*
* The API level is only defined for codecs because it is a small
* and easy to define API.
* Maybe we could do the same for wiretap (file type) plugins?
* For the various epan plugin types it seems pointless and futile.
*/
switch (type) {
case WS_PLUGIN_CODEC: return WIRESHARK_API_MAX_LEVEL_CODEC;
default: return 0;
}
ws_assert_not_reached();
}
int
plugins_abi_version(plugin_type_e type)
{

View File

@ -94,6 +94,9 @@ WS_DLL_PUBLIC char *plugins_pers_type_folder(plugin_type_e type);
WS_DLL_PUBLIC char *plugins_file_suffix(plugin_type_e type);
WS_DLL_PUBLIC
int plugins_api_max_level(plugin_type_e type);
WS_DLL_PUBLIC
int plugins_abi_version(plugin_type_e type);