From: Jennifer Richards Date: Wed, 18 Apr 2018 03:38:27 +0000 (-0400) Subject: Replace static monitor handler tables with dynamic handler registry X-Git-Tag: 3.4.0~1^2~48^2 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=trust_router.git;a=commitdiff_plain;h=733b18697117cbc63ab3d9e44510c9850916ec90 Replace static monitor handler tables with dynamic handler registry * Keep a list of handlers as part of MONS_INSTANCE - each handles a command/opt_type pair - registered via mons_register_handler() * Scan the list of handlers when servicing a monitoring request * Add handlers for version and uptime, registered through tr_main.c (probably need to move these, but this works as a demo) --- diff --git a/CMakeLists.txt b/CMakeLists.txt index c970f02..f7bff87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ set(SOURCE_FILES trp/trp_upd.c trp/trpc.c trp/trps.c include/tr_name_internal.h mon/mon_req.c mon/mon_req_encode.c mon/mon_req_decode.c - mon/mon_resp.c mon/mon_common.c mon/mon_resp_encode.c mon/mon_resp_decode.c tr/tr_mon.c mon/mons.c include/tr_socket.h common/tr_gss.c include/tr_gss.h common/tr_config_internal.c mon/mons_handlers.c mon/mons_handlers.h mon/mons_handlers_show.c) + mon/mon_resp.c mon/mon_common.c mon/mon_resp_encode.c mon/mon_resp_decode.c tr/tr_mon.c mon/mons.c include/tr_socket.h common/tr_gss.c include/tr_gss.h common/tr_config_internal.c mon/mons_handlers.c include/mons_handlers.h) # Does not actually build! add_executable(trust_router ${SOURCE_FILES}) diff --git a/Makefile.am b/Makefile.am index 2df06b6..c73c623 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,8 +53,7 @@ mon_srcs = \ # monitoring server sources mons_srcs = \ mon/mons.c \ - mon/mons_handlers.c \ - mon/mons_handlers_show.c + mon/mons_handlers.c check_PROGRAMS = common/t_constraint TESTS = common/t_constraint diff --git a/include/mon.h b/include/mon.h index 037e717..33b8f4b 100644 --- a/include/mon.h +++ b/include/mon.h @@ -54,6 +54,8 @@ typedef enum mon_rc MON_RC; typedef struct mons_instance MONS_INSTANCE; typedef struct monc_instance MONC_INSTANCE; +typedef struct mons_dispatch_table_entry MONS_DISPATCH_TABLE_ENTRY; + typedef int (MONS_REQ_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *); typedef int (MONS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie); typedef int (MONC_RESP_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *); diff --git a/include/mon_internal.h b/include/mon_internal.h index d2d1572..8e439d2 100644 --- a/include/mon_internal.h +++ b/include/mon_internal.h @@ -91,6 +91,7 @@ enum mon_resp_code { enum mon_opt_type { OPT_TYPE_UNKNOWN=0, + OPT_TYPE_ANY, // System information OPT_TYPE_SHOW_VERSION, @@ -131,6 +132,7 @@ struct mons_instance { MONS_REQ_FUNC *req_handler; MONS_AUTH_FUNC *auth_handler; void *cookie; + GPtrArray *handlers; }; /* Client instance */ diff --git a/mon/mons_handlers.h b/include/mons_handlers.h similarity index 85% rename from mon/mons_handlers.h rename to include/mons_handlers.h index 03e2847..50bdd43 100644 --- a/mon/mons_handlers.h +++ b/include/mons_handlers.h @@ -36,10 +36,17 @@ #ifndef TRUST_ROUTER_MONS_HANDLERS_H #define TRUST_ROUTER_MONS_HANDLERS_H +typedef json_t *(MONS_HANDLER_FUNC)(void *); + +struct mons_dispatch_table_entry { + MON_CMD command; + MON_OPT_TYPE opt_type; + MONS_HANDLER_FUNC *handler; + void *cookie; +}; + /* mons_handlers.c */ MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req); - -/* mons_handlers_show.c */ -MON_RESP *mons_handle_show(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req); +MON_RC mons_register_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type, MONS_HANDLER_FUNC *f, void *cookie); #endif //TRUST_ROUTER_MONS_HANDLERS_H diff --git a/include/tr_mon.h b/include/tr_mon.h index e956db2..15ab640 100644 --- a/include/tr_mon.h +++ b/include/tr_mon.h @@ -38,6 +38,7 @@ #include #include #include +#include int tr_mons_event_init(struct event_base *base, MONS_INSTANCE *mons, diff --git a/mon/mon_common.c b/mon/mon_common.c index d0317f7..440a277 100644 --- a/mon/mon_common.c +++ b/mon/mon_common.c @@ -81,6 +81,7 @@ const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type) { switch(opt_type) { case OPT_TYPE_UNKNOWN: + case OPT_TYPE_ANY: return NULL; case OPT_TYPE_SHOW_VERSION: diff --git a/mon/mon_req.c b/mon/mon_req.c index dd9de4d..bffa92e 100644 --- a/mon/mon_req.c +++ b/mon/mon_req.c @@ -112,3 +112,4 @@ MON_OPT *mon_req_opt_index(MON_REQ *req, size_t index) MON_OPT *result = &g_array_index(req->options, MON_OPT, index); return result; } + diff --git a/mon/mons.c b/mon/mons.c index 2216f5f..4d638d6 100644 --- a/mon/mons.c +++ b/mon/mons.c @@ -46,6 +46,15 @@ #include "mons_handlers.h" +static int mons_destructor(void *object) +{ + MONS_INSTANCE *mons = talloc_get_type_abort(object, MONS_INSTANCE); + if (mons->handlers) { + g_ptr_array_unref(mons->handlers); + } + return 0; +} + /** * Allocate a new MONS_INSTANCE * @@ -64,10 +73,20 @@ MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx) mons->req_handler = NULL; mons->auth_handler = NULL; mons->cookie = NULL; + + /* Before any steps that may fail, install the destructor */ + talloc_set_destructor((void *)mons, mons_destructor); + mons->authorized_gss_names = tr_gss_names_new(mons); if (mons->authorized_gss_names == NULL) { talloc_free(mons); - mons = NULL; + return NULL; + } + + mons->handlers = g_ptr_array_new(); + if (mons->handlers == NULL) { + talloc_free(mons); + return NULL; } } return mons; diff --git a/mon/mons_handlers.c b/mon/mons_handlers.c index 68abd48..b546b89 100644 --- a/mon/mons_handlers.c +++ b/mon/mons_handlers.c @@ -34,55 +34,190 @@ /* Handlers for monitoring requests */ +#include + #include #include -#include "mons_handlers.h" - -typedef MON_RESP *(MONS_HANDLER_FUNC)(TALLOC_CTX *, MONS_INSTANCE *, MON_REQ *); +#include -/* Prototypes for the dispatch table */ -static MON_RESP *mons_handle_reconfigure(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req); +/* Static Prototypes */ +static int dispatch_entry_matches(MONS_DISPATCH_TABLE_ENTRY *e, MON_CMD command, MON_OPT_TYPE opt_type); +static MONS_HANDLER_FUNC *mons_find_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type); +static void request_helper(void *element, void *data); -struct dispatch_table_entry { +struct request_helper_data { MON_CMD command; - MONS_HANDLER_FUNC *handler; -}; - -static struct dispatch_table_entry dispatch_table[] = { - {MON_CMD_SHOW, mons_handle_show}, - {MON_CMD_RECONFIGURE, mons_handle_reconfigure}, - {MON_CMD_UNKNOWN} /* Must be the last entry in the table */ + MON_OPT_TYPE opt_type; + json_t *payload; }; /** * Call the appropriate handler for a request * + * TODO: report errors from handlers + * * @return a MON_RESP structure or null if there was a processing error */ MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req) { - struct dispatch_table_entry *entry = dispatch_table; + MON_RESP *resp = NULL; + json_t *payload = NULL; + struct request_helper_data cookie; + size_t ii = 0; tr_debug("mons_handle_request: Handling a request"); - /* Find the handler */ - while ((entry->command != req->command) && (entry->command != MON_CMD_UNKNOWN)) { - entry++; + /* Start off by allocating our response with a generic error message */ + resp = mon_resp_new(mem_ctx, + MON_RESP_ERROR, + "Error processing show request", + NULL); + if (resp == NULL) { + /* we can't respond, just return */ + tr_crit("mons_handle_request: Error allocating response structure."); + goto cleanup; + } + + /* Now get a JSON object for our return payload */ + payload = json_object(); + if (payload == NULL) { + tr_crit("mons_handle_request: Error allocating response payload."); + goto cleanup; /* This will return the generic error message set earlier */ + } + + /* Now call handlers */ + cookie.command = req->command; + cookie.payload = payload; /* borrowed reference */ + + if (mon_req_opt_count(req) == 0) { + /* call every handler that matches the command */ + cookie.opt_type = OPT_TYPE_ANY; + g_ptr_array_foreach(mons->handlers, request_helper, &cookie); + } else { + /* call only those handlers that match an option */ + for (ii=0; ii < mon_req_opt_count(req); ii++) { + cookie.opt_type = mon_req_opt_index(req, ii)->type; + /* Loop over all handlers - we know we can only have one match for each opt type */ + g_ptr_array_foreach(mons->handlers, request_helper, &cookie); + } + } + + /* If we get here, then we successfully processed the request. Return a successful reply. */ + if (mon_resp_set_message(resp, "success") == 0) { + /* Failed to set the response message to success - fail ironically */ + tr_crit("mons_handle_request: Error setting response message to 'success'."); + goto cleanup; + } + + /* Attach the accumulated payload to the response */ + if (json_object_size(payload) > 0) + mon_resp_set_payload(resp, payload); + + resp->code = MON_RESP_SUCCESS; /* at last... */ + tr_debug("mons_handle_request: Successfully processed request."); + +cleanup: + if (payload) + json_decref(payload); + return resp; +} + +/** + * Register a handler for a command/option combination + * + * @param mons + * @param cmd + * @param opt_type + * @param f + * @param cookie + * @return + */ +MON_RC mons_register_handler(MONS_INSTANCE *mons, + MON_CMD cmd, + MON_OPT_TYPE opt_type, + MONS_HANDLER_FUNC *f, + void *cookie) +{ + MONS_DISPATCH_TABLE_ENTRY *entry = NULL; + + if (mons_find_handler(mons, cmd, opt_type) != NULL) { + return MON_ERROR; } - /* See if we found a handler */ - if (entry->command == MON_CMD_UNKNOWN) { - tr_info("mons_handle_request: Unknown or unsupported monitoring request received"); - return NULL; + /* Put these in the mons talloc context so we don't have to muck about with + * a free function for the GPtrArray */ + entry = talloc(mons, MONS_DISPATCH_TABLE_ENTRY); + if (entry == NULL) { + return MON_NOMEM; } + entry->command = cmd; + entry->opt_type = opt_type; + entry->handler = f; + entry->cookie = cookie; - /* Call the handler */ - tr_debug("mons_handle_request: Calling handler for %s command", mon_cmd_to_string(entry->command)); - return entry->handler(mem_ctx, mons, req); + g_ptr_array_add(mons->handlers, entry); + return MON_SUCCESS; } -static MON_RESP *mons_handle_reconfigure(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req) +/** + * Two table entries match if none of the commands or opt_types are unknown, + * if the commands match, and if the opt types either match or at least one is + * OPT_TYPE_ANY. + * + * No comparison of the handler pointer is included. + * + * @return 1 if the two match, 0 if not + */ +static int dispatch_entry_matches(MONS_DISPATCH_TABLE_ENTRY *e, + MON_CMD command, + MON_OPT_TYPE opt_type) +{ + if ((command == MON_CMD_UNKNOWN) || (opt_type == OPT_TYPE_UNKNOWN)) + return 0; /* request is invalid */ + + if ((e->command == MON_CMD_UNKNOWN) || (e->opt_type == OPT_TYPE_UNKNOWN)) + return 0; /* e1 is invalid */ + + if (e->command != command) + return 0; /* commands do not match */ + + if (e->opt_type == opt_type) + return 1; /* exact match */ + + if ( (e->opt_type == OPT_TYPE_ANY) || (opt_type == OPT_TYPE_ANY) ) + return 1; /* one is a wildcard */ + + return 0; /* commands matched but opt_types did not */ +} + +static MONS_HANDLER_FUNC *mons_find_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type) { + guint index; + + for (index=0; index < mons->handlers->len; index++) { + if (dispatch_entry_matches(g_ptr_array_index(mons->handlers, index), cmd, opt_type)) + return g_ptr_array_index(mons->handlers, index); + } return NULL; } + +/** + * This calls every request handler that matches a command/opt_type, + * gathering their results. + * + * @param element + * @param data + */ +static void request_helper(void *element, void *data) +{ + MONS_DISPATCH_TABLE_ENTRY *entry = talloc_get_type_abort(element, MONS_DISPATCH_TABLE_ENTRY); + struct request_helper_data *helper_data = data; + + if (dispatch_entry_matches(entry, helper_data->command, helper_data->opt_type)) { + json_object_set(helper_data->payload, + mon_opt_type_to_string(entry->opt_type), + entry->handler(entry->cookie)); + } +} + diff --git a/mon/mons_handlers_show.c b/mon/mons_handlers_show.c deleted file mode 100644 index e4d4d22..0000000 --- a/mon/mons_handlers_show.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018, JANET(UK) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of JANET(UK) nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* Handlers for monitoring "show" requests */ - -#include -#include - -#include -#include -#include "mons_handlers.h" - -typedef json_t *(MONS_SHOW_FUNC)(MONS_INSTANCE *mons); - -/* prototypes for the dispatch table */ -static json_t *handle_show_version(MONS_INSTANCE *mons); - -struct dispatch_table_entry { - MON_OPT_TYPE opt_type; - MONS_SHOW_FUNC *handler; -}; - -struct dispatch_table_entry dispatch_table[] = { - {OPT_TYPE_SHOW_VERSION, handle_show_version}, - {OPT_TYPE_SHOW_SERIAL, NULL}, - {OPT_TYPE_SHOW_UPTIME, NULL}, - {OPT_TYPE_SHOW_TID_REQ_COUNT, NULL}, - {OPT_TYPE_SHOW_TID_REQ_PENDING, NULL}, - {OPT_TYPE_SHOW_ROUTES, NULL}, - {OPT_TYPE_SHOW_COMMUNITIES, NULL}, - {OPT_TYPE_UNKNOWN} /* must be the last entry */ -}; - -/** - * Should we include this opt_type in our response to this request? - * - * Returns 1 if the opt_type is in the options list, or if the options list - * is empty. - * - * @param opt_type - * @param req - * @return 1 if the opt_type should be included, else 0 - */ -static int opt_requested(MON_OPT_TYPE opt_type, MON_REQ *req) -{ - size_t ii; - - /* empty options list is a wildcard - return everything */ - if (mon_req_opt_count(req) == 0) - return 1; - - /* check whether this opt_type is in the list */ - for (ii=0; iitype) - return 1; - } - return 0; -} - -MON_RESP *mons_handle_show(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req) -{ - struct dispatch_table_entry *entry = NULL; - MON_RESP *resp = NULL; - json_t *payload = NULL; /* entire payload */ - json_t *payload_item = NULL; /* payload for a single option */ - - tr_debug("mons_handle_show: Handling a request"); - - /* Start off by allocating our response with a generic error message */ - resp = mon_resp_new(mem_ctx, - MON_RESP_ERROR, - "Error processing show request", - NULL); - if (resp == NULL) { - /* we can't respond, just return */ - tr_crit("mons_handle_show: Error allocating response structure."); - goto cleanup; - } - - /* Now get a JSON object for our return payload */ - payload = json_object(); - if (payload == NULL) { - tr_crit("mons_handle_show: Error allocating response payload."); - goto cleanup; /* This will return the generic error message set earlier */ - } - - tr_debug("mons_handle_show: Processing options"); - - /* Now step through the dispatch table. Call each requested option type. */ - for (entry = dispatch_table; entry->opt_type != OPT_TYPE_UNKNOWN; entry++) { - if (! opt_requested(entry->opt_type, req)) { - tr_debug("mons_handle_show: Not including %s in response", - mon_opt_type_to_string(entry->opt_type)); - } else { - /* This option is needed. Add its response to our payload. */ - if (entry->handler == NULL) { - tr_debug("mons_handle_show: Would include %s in response, but its handler is null", - mon_opt_type_to_string(entry->opt_type)); - continue; - } - - tr_debug("mons_handle_show: Including %s in response", - mon_opt_type_to_string(entry->opt_type)); - - payload_item = entry->handler(mons); - if (payload_item == NULL) { - tr_err("mons_handle_show: Error processing option %s", mon_opt_type_to_string(entry->opt_type)); - goto cleanup; - } - /* this steals the reference to payload_item */ - json_object_set_new(payload, - mon_opt_type_to_string(entry->opt_type), - payload_item); - } - } - - /* If we get here, then we successfully processed the request. Return a successful reply. */ - if (mon_resp_set_message(resp, "success") == 0) { - /* Failed to set the response message to success - fail ironically */ - tr_crit("mons_handle_show: Error setting response message to 'success'"); - goto cleanup; - } - - /* Attach the accumulated payload to the response */ - if (json_object_size(payload) > 0) - mon_resp_set_payload(resp, payload); - - resp->code = MON_RESP_SUCCESS; /* at last... */ - -cleanup: - if (payload) - json_decref(payload); - return resp; -} - - -static json_t *handle_show_version(MONS_INSTANCE *mons) -{ - return json_string(PACKAGE_VERSION); -} \ No newline at end of file diff --git a/tr/tr_main.c b/tr/tr_main.c index 7fad36f..3c74730 100644 --- a/tr/tr_main.c +++ b/tr/tr_main.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -144,6 +145,18 @@ static void configure_signals(void) pthread_sigmask(SIG_BLOCK, &signals, NULL); } +/* TODO move this function */ +static json_t *tr_mon_handle_version(void *cookie) +{ + return json_string(PACKAGE_VERSION); +} + +static json_t *tr_mon_handle_uptime(void *cookie) +{ + time_t *start_time = cookie; + return json_integer(time(NULL) - (*start_time)); +} + int main(int argc, char *argv[]) { TALLOC_CTX *main_ctx=NULL; @@ -155,6 +168,8 @@ int main(int argc, char *argv[]) struct tr_socket_event mon_ev = {0}; struct event *cfgwatch_ev; + time_t start_time = time(NULL); /* TODO move this? */ + configure_signals(); /* we're going to be multithreaded, so disable null context tracking */ @@ -212,6 +227,10 @@ int main(int argc, char *argv[]) tr->mons->tids = tr->tids; tr->mons->trps = tr->trps; + /* TODO do this more systematically */ + mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_VERSION, tr_mon_handle_version, NULL); + mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_UPTIME, tr_mon_handle_uptime, &start_time); + /***** process configuration *****/ tr->cfgwatch=tr_cfgwatch_create(tr); if (tr->cfgwatch == NULL) {