diff --git a/CHANGES b/CHANGES index 7521777c57..dcec202fbc 100644 --- a/CHANGES +++ b/CHANGES @@ -192,7 +192,8 @@ AMI ARI ------------------ * A new feature has been added that enables the retrieval of modules and - module information through an HTTP request. + module information through an HTTP request. Information on a single module + can be also be retrieved. res_pjsip ------------------ diff --git a/include/asterisk/module.h b/include/asterisk/module.h index 59e6c7ed55..9fbeb5ebca 100644 --- a/include/asterisk/module.h +++ b/include/asterisk/module.h @@ -178,6 +178,25 @@ int ast_update_module_list_data(int (*modentry)(const char *module, const char * void *data), const char *like, void *data); +/*! + * \brief Ask for a list of modules, descriptions, use counts and status. + * \param modentry A callback to an updater function + * \param like + * \param data Data passed into the callback for manipulation + * \param condition The condition to meet + * + * For each of the modules loaded, modentry will be executed with the resource, + * description, and usecount values of each particular module. + * + * \return the number of conditions met + * \since 13.5.0 + */ +int ast_update_module_list_condition(int (*modentry)(const char *module, const char *description, + int usecnt, const char *status, const char *like, + enum ast_module_support_level support_level, + void *data, const char *condition), + const char *like, void *data, const char *condition); + /*! * \brief Check if module with the name given is loaded * \param name Module name, like "chan_sip.so" diff --git a/main/loader.c b/main/loader.c index 99a47b3b4d..b2bdd4a3d2 100644 --- a/main/loader.c +++ b/main/loader.c @@ -1445,6 +1445,34 @@ int ast_update_module_list_data(int (*modentry)(const char *module, const char * return total_mod_loaded; } +int ast_update_module_list_condition(int (*modentry)(const char *module, const char *description, + int usecnt, const char *status, + const char *like, + enum ast_module_support_level support_level, + void *data, const char *condition), + const char *like, void *data, const char *condition) +{ + struct ast_module *cur; + int conditions_met = 0; + AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + + AST_DLLIST_LOCK(&module_list); + + AST_DLLIST_TRAVERSE(&module_list, cur, entry) { + AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource); + } + + while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) { + conditions_met += modentry(cur->resource, cur->info->description, cur->usecount, + cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data, + condition); + } + + AST_DLLIST_UNLOCK(&module_list); + + return conditions_met; +} + /*! \brief Check if module exists */ int ast_module_check(const char *name) { diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c index cc80d0ca3d..ea1a073b02 100644 --- a/res/ari/resource_asterisk.c +++ b/res/ari/resource_asterisk.c @@ -185,6 +185,79 @@ void ast_ari_asterisk_list_modules(struct ast_variable *headers, ast_ari_response_ok(response, json); } +/*! + * \brief Identify module by name and process resource information + * \param module Resource name + * \param description Resource description + * \param usecnt Resource use count + * \param status Resource running status + * \param like + * \param support_level Resource support level + * \param data JSON body for resource + * \param condition Name to match resource to + * + * \retval 0 if no resource exists + * \retval 1 if resource exists + */ +static int identify_module(const char *module, const char *description, int usecnt, + const char *status, const char *like, + enum ast_module_support_level support_level, void *data, + const char *condition) +{ + int json_obj_set = 0; + + if (strcmp(condition, module) != 0) { + return 0; + } + + json_obj_set += ast_json_object_set(data, "name", ast_json_string_create(module)); + json_obj_set += ast_json_object_set(data, "description", ast_json_string_create(description)); + json_obj_set += ast_json_object_set(data, "use_count", ast_json_integer_create(usecnt)); + json_obj_set += ast_json_object_set(data, "status", ast_json_string_create(status)); + json_obj_set += ast_json_object_set(data, "support_level", ast_json_string_create( + ast_module_support_level_to_string(support_level))); + + if (json_obj_set != 0) { + return 0; + } + + return 1; +} + +void ast_ari_asterisk_get_module(struct ast_variable *headers, + struct ast_ari_asterisk_get_module_args *args, + struct ast_ari_response *response) +{ + struct ast_json *json; + int module_retrieved = 0; + + ast_assert(response != NULL); + + if (!ast_module_check(args->module_name)) { + ast_ari_response_error( + response, 404, "Not Found", + "Module could not be found in running modules"); + return; + } + + json = ast_json_object_create(); + if (!json) { + ast_ari_response_alloc_failed(response); + return; + } + + module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json, + args->module_name); + if (!module_retrieved) { + ast_ari_response_error( + response, 409, "Conflict", + "Module information could not be retrieved"); + return; + } + + ast_ari_response_ok(response, json); +} + void ast_ari_asterisk_get_global_var(struct ast_variable *headers, struct ast_ari_asterisk_get_global_var_args *args, struct ast_ari_response *response) diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h index b09d2715af..8689f3ec10 100644 --- a/res/ari/resource_asterisk.h +++ b/res/ari/resource_asterisk.h @@ -78,6 +78,19 @@ struct ast_ari_asterisk_list_modules_args { * \param[out] response HTTP response */ void ast_ari_asterisk_list_modules(struct ast_variable *headers, struct ast_ari_asterisk_list_modules_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_get_module() */ +struct ast_ari_asterisk_get_module_args { + /*! Module's name */ + const char *module_name; +}; +/*! + * \brief Get Asterisk module information. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_get_module(struct ast_variable *headers, struct ast_ari_asterisk_get_module_args *args, struct ast_ari_response *response); /*! Argument struct for ast_ari_asterisk_get_global_var() */ struct ast_ari_asterisk_get_global_var_args { /*! The variable to get */ diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c index fb4207aabd..214bb2fea1 100644 --- a/res/res_ari_asterisk.c +++ b/res/res_ari_asterisk.c @@ -257,6 +257,66 @@ static void ast_ari_asterisk_list_modules_cb( } #endif /* AST_DEVMODE */ +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/modules/{moduleName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_get_module_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_get_module_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "moduleName") == 0) { + args.module_name = (i->value); + } else + {} + } + ast_ari_asterisk_get_module(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Module could not be found in running modules. */ + case 409: /* Module information could not be retrieved. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_module( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + fin: __attribute__((unused)) return; } @@ -460,13 +520,23 @@ static struct stasis_rest_handlers asterisk_info = { .children = { } }; /*! \brief REST handler for /api-docs/asterisk.{format} */ +static struct stasis_rest_handlers asterisk_modules_moduleName = { + .path_segment = "moduleName", + .is_wildcard = 1, + .callbacks = { + [AST_HTTP_GET] = ast_ari_asterisk_get_module_cb, + }, + .num_children = 0, + .children = { } +}; +/*! \brief REST handler for /api-docs/asterisk.{format} */ static struct stasis_rest_handlers asterisk_modules = { .path_segment = "modules", .callbacks = { [AST_HTTP_GET] = ast_ari_asterisk_list_modules_cb, }, - .num_children = 0, - .children = { } + .num_children = 1, + .children = { &asterisk_modules_moduleName, } }; /*! \brief REST handler for /api-docs/asterisk.{format} */ static struct stasis_rest_handlers asterisk_variable = { diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json index ade5938c99..76c834e45f 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -50,6 +50,38 @@ } ] }, + { + "path": "/asterisk/modules/{moduleName}", + "description": "Asterisk module", + "operations": [ + { + "httpMethod": "GET", + "summary": "Get Asterisk module information.", + "nickname": "getModule", + "responseClass": "Module", + "parameters": [ + { + "name": "moduleName", + "description": "Module's name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Module could not be found in running modules." + }, + { + "code": 409, + "reason": "Module information could not be retrieved." + } + ] + } + ] + }, { "path": "/asterisk/variable", "description": "Global variables",