From 423496879855ebf23ae0198899c8b5ad8724ad56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Mon, 3 Dec 2018 15:30:02 +0100 Subject: [PATCH] add a function for processing a list of configured plugins Add a new libisccfg function, cfg_pluginlist_foreach(), which allows an arbitrary callback to be invoked for every "plugin" stanza present in a configuration object. Use this function for both loading plugins and checking their configuration in order to reduce duplication of configuration processing code present in bin/named/server.c and lib/bind9/check.c. --- bin/named/server.c | 78 +++++++++++++------------------- lib/bind9/check.c | 79 ++++++++++++++++++++------------- lib/isccfg/include/isccfg/cfg.h | 41 +++++++++++++++++ lib/isccfg/parser.c | 48 ++++++++++++++++++++ 4 files changed, 169 insertions(+), 77 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 19f67aeec5..b44fd1e281 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1535,45 +1535,6 @@ configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx, name, isc_result_totext(result)); return (result); } - -static isc_result_t -configure_plugin(dns_view_t *view, const cfg_obj_t *plugin, - const cfg_obj_t *config) -{ - isc_result_t result = ISC_R_SUCCESS; - const cfg_obj_t *obj; - const char *type, *library; - const char *parameters = NULL; - - /* Get the path to the plugin module. */ - obj = cfg_tuple_get(plugin, "type"); - type = cfg_obj_asstring(obj); - - /* Only query plugins are supported currently. */ - if (strcasecmp(type, "query") != 0) { - cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, - "unsupported plugin type"); - return (ISC_R_FAILURE); - } - - library = cfg_obj_asstring(cfg_tuple_get(plugin, "library")); - - obj = cfg_tuple_get(plugin, "parameters"); - if (obj != NULL && cfg_obj_isstring(obj)) { - parameters = cfg_obj_asstring(obj); - } - result = ns_plugin_register(library, parameters, config, - cfg_obj_file(obj), cfg_obj_line(obj), - named_g_mctx, named_g_lctx, - named_g_aclconfctx, view); - if (result != ISC_R_SUCCESS) { - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "%s: module configuration failed: %s", - library, isc_result_totext(result)); - } - return (result); -} #endif @@ -3686,6 +3647,35 @@ create_mapped_acl(void) { return (result); } +#ifdef HAVE_DLOPEN +/*% + * A callback for the cfg_pluginlist_foreach() call in configure_view() below. + * If registering any plugin fails, registering subsequent ones is not + * attempted. + */ +static isc_result_t +register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj, + const char *plugin_path, const char *parameters, + void *callback_data) +{ + dns_view_t *view = callback_data; + isc_result_t result; + + result = ns_plugin_register(plugin_path, parameters, config, + cfg_obj_file(obj), cfg_obj_line(obj), + named_g_mctx, named_g_lctx, + named_g_aclconfctx, view); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "%s: plugin configuration failed: %s", + plugin_path, isc_result_totext(result)); + } + + return (result); +} +#endif + /* * Configure 'view' according to 'vconfig', taking defaults from 'config' * where values are missing in 'vconfig'. @@ -5314,15 +5304,9 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, ns_plugins_create(view->mctx, (ns_plugins_t **)&view->plugins); view->plugins_free = ns_plugins_free; - } - for (element = cfg_list_first(plugin_list); - element != NULL; - element = cfg_list_next(element)) - { - const cfg_obj_t *plugin = cfg_listelt_value(element); - - CHECK(configure_plugin(view, plugin, config)); + CHECK(cfg_pluginlist_foreach(config, plugin_list, named_g_lctx, + register_one_plugin, view)); } #endif diff --git a/lib/bind9/check.c b/lib/bind9/check.c index ada1e257db..e07c3d0d0c 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -3348,6 +3348,46 @@ check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj, return (result); } +#ifdef HAVE_DLOPEN +/*% + * Data structure used for the 'callback_data' argument to check_one_plugin(). + */ +struct check_one_plugin_data { + isc_mem_t *mctx; + isc_log_t *lctx; + cfg_aclconfctx_t *actx; + isc_result_t *check_result; +}; + +/*% + * A callback for the cfg_pluginlist_foreach() call in check_viewconf() below. + * Since the point is to check configuration of all plugins even when + * processing some of them fails, always return ISC_R_SUCCESS and indicate any + * check failures through the 'check_result' variable passed in via the + * 'callback_data' structure. + */ +static isc_result_t +check_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj, + const char *plugin_path, const char *parameters, + void *callback_data) +{ + struct check_one_plugin_data *data = callback_data; + isc_result_t result; + + result = ns_plugin_check(plugin_path, parameters, config, + cfg_obj_file(obj), cfg_obj_line(obj), + data->mctx, data->lctx, data->actx); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, data->lctx, ISC_LOG_ERROR, + "%s: plugin check failed: %s", + plugin_path, isc_result_totext(result)); + *data->check_result = result; + } + + return (ISC_R_SUCCESS); +} +#endif + static isc_result_t check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, const char *viewname, dns_rdataclass_t vclass, @@ -3677,39 +3717,18 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, } #ifdef HAVE_DLOPEN - for (element = cfg_list_first(plugin_list); - element != NULL; - element = cfg_list_next(element)) { - const cfg_obj_t *plugin = cfg_listelt_value(element); + struct check_one_plugin_data check_one_plugin_data = { + .mctx = mctx, + .lctx = logctx, + .actx = actx, + .check_result = &tresult, + }; - const char *type, *library; - const char *parameters = NULL; - - /* Get the path to the plugin module. */ - obj = cfg_tuple_get(plugin, "type"); - type = cfg_obj_asstring(obj); - - /* Only query plugins are supported currently. */ - if (strcasecmp(type, "query") != 0) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "unsupported plugin type"); - return (ISC_R_FAILURE); - } - - library = cfg_obj_asstring(cfg_tuple_get(plugin, "library")); - - obj = cfg_tuple_get(plugin, "parameters"); - if (obj != NULL && cfg_obj_isstring(obj)) { - parameters = cfg_obj_asstring(obj); - } - tresult = ns_plugin_check(library, parameters, config, - cfg_obj_file(obj), cfg_obj_line(obj), - mctx, logctx, actx); + (void)cfg_pluginlist_foreach(config, plugin_list, logctx, + check_one_plugin, + &check_one_plugin_data); if (tresult != ISC_R_SUCCESS) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "%s: module check failed: %s", - library, isc_result_totext(tresult)); result = tresult; } } diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h index 8f8ca55e43..03d340bd81 100644 --- a/lib/isccfg/include/isccfg/cfg.h +++ b/lib/isccfg/include/isccfg/cfg.h @@ -555,6 +555,47 @@ const char * cfg_map_nextclause(const cfg_type_t *map, const void **clauses, unsigned int *idx); +typedef isc_result_t +(pluginlist_cb_t)(const cfg_obj_t *config, const cfg_obj_t *obj, + const char *plugin_path, const char *parameters, + void *callback_data); +/*%< + * Function prototype for the callback used with cfg_pluginlist_foreach(). + * Called once for each element of the list passed to cfg_pluginlist_foreach(). + * If this callback returns anything else than #ISC_R_SUCCESS, no further list + * elements will be processed. + * + * \li 'config' - the 'config' object passed to cfg_pluginlist_foreach() + * \li 'obj' - object representing the specific "plugin" stanza to be processed + * \li 'plugin_path' - path to the shared object with plugin code + * \li 'parameters' - configuration text for the plugin + * \li 'callback_data' - the pointer passed to cfg_pluginlist_foreach() + */ + +isc_result_t +cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list, + isc_log_t *lctx, pluginlist_cb_t callback, + void *callback_data); +/*%< + * For every "plugin" stanza present in 'list' (which in turn is a part of + * 'config'), invoke the given 'callback', passing 'callback_data' to it along + * with a fixed set of arguments (see the definition of the #pluginlist_cb_t + * type). Use logging context 'lctx' for logging error messages. Interrupt + * processing if 'callback' returns something else than #ISC_R_SUCCESS for any + * element of 'list'. + * + * Requires: + * + * \li 'config' is not NULL + * \li 'callback' is not NULL + * + * Returns: + * + * \li #ISC_R_SUCCESS if 'callback' returned #ISC_R_SUCCESS for all elements of + * 'list' + * \li first 'callback' return value which was not #ISC_R_SUCCESS otherwise + */ + ISC_LANG_ENDDECLS #endif /* ISCCFG_CFG_H */ diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index eea80b2789..07341bf725 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -3457,3 +3457,51 @@ cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj, return (result); } + +isc_result_t +cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list, + isc_log_t *lctx, pluginlist_cb_t *callback, + void *callback_data) +{ + isc_result_t result = ISC_R_SUCCESS; + const cfg_listelt_t *element; + + REQUIRE(config != NULL); + REQUIRE(callback != NULL); + + for (element = cfg_list_first(list); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *plugin = cfg_listelt_value(element); + const cfg_obj_t *obj; + const char *type, *library; + const char *parameters = NULL; + + /* Get the path to the plugin module. */ + obj = cfg_tuple_get(plugin, "type"); + type = cfg_obj_asstring(obj); + + /* Only query plugins are supported currently. */ + if (strcasecmp(type, "query") != 0) { + cfg_obj_log(obj, lctx, ISC_LOG_ERROR, + "unsupported plugin type"); + return (ISC_R_FAILURE); + } + + library = cfg_obj_asstring(cfg_tuple_get(plugin, "library")); + + obj = cfg_tuple_get(plugin, "parameters"); + if (obj != NULL && cfg_obj_isstring(obj)) { + parameters = cfg_obj_asstring(obj); + } + + result = callback(config, obj, library, parameters, + callback_data); + if (result != ISC_R_SUCCESS) { + break; + } + } + + return (result); +}