diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h index 636c744640..2157eae75f 100644 --- a/include/asterisk/logger.h +++ b/include/asterisk/logger.h @@ -45,6 +45,13 @@ extern "C" { #define AST_CALLID_BUFFER_LENGTH 13 +enum ast_logger_results { + AST_LOGGER_SUCCESS = 0, /*!< Log channel was created or deleted successfully*/ + AST_LOGGER_FAILURE = 1, /*!< Log channel already exists for create or doesn't exist for deletion of log channel */ + AST_LOGGER_DECLINE = -1, /*!< Log channel request was not accepted */ + AST_LOGGER_ALLOC_ERROR = -2 /*!< filename allocation error */ +}; + /*! \brief Used for sending a log message This is the standard logger function. Probably the only way you will invoke it would be something like this: ast_log(AST_LOG_WHATEVER, "Problem with the %s Captain. We should get some more. Will %d be enough?\n", "flux capacitor", 10); @@ -91,6 +98,13 @@ typedef unsigned int ast_callid; void ast_log_callid(int level, const char *file, int line, const char *function, ast_callid callid, const char *fmt, ...) __attribute__((format(printf, 6, 7))); +/*! + * \brief Delete the specified log channel + * + * \param log_channel The log channel to delete + */ +int ast_logger_remove_channel(const char *log_channel); + /*! * \brief Log a backtrace of the current thread's execution stack to the Asterisk log */ diff --git a/main/logger.c b/main/logger.c index 607755b584..cea9530e27 100644 --- a/main/logger.c +++ b/main/logger.c @@ -1095,6 +1095,41 @@ static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct return CLI_FAILURE; } +int ast_logger_remove_channel(const char *log_channel) +{ + struct logchannel *chan; + struct ast_str *filename = ast_str_create(64); + + if (!filename) { + return AST_LOGGER_ALLOC_ERROR; + } + + ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel); + + AST_RWLIST_WRLOCK(&logchannels); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&logchannels, chan, list) { + if (chan->dynamic && !strcmp(chan->filename, ast_str_buffer(filename))) { + AST_RWLIST_REMOVE_CURRENT(list); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&logchannels); + + if (!chan) { + return AST_LOGGER_FAILURE; + } + + if (chan->fileptr) { + fclose(chan->fileptr); + chan->fileptr = NULL; + } + ast_free(chan); + chan = NULL; + + return AST_LOGGER_SUCCESS; +} + static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct logchannel *chan; diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index 1be32de3d6..6b76a85166 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -366,18 +366,19 @@ int ast_ari_validate_log_channel(struct ast_json *json) { int res = 1; struct ast_json_iter *iter; - int has_logging_levels = 0; + int has_configuration = 0; int has_name = 0; + int has_status = 0; + int has_type = 0; for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { - if (strcmp("logging_levels", ast_json_object_iter_key(iter)) == 0) { + if (strcmp("configuration", ast_json_object_iter_key(iter)) == 0) { int prop_is_valid; - has_logging_levels = 1; - prop_is_valid = ast_ari_validate_list( - ast_json_object_iter_value(iter), - ast_ari_validate_string); + has_configuration = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); if (!prop_is_valid) { - ast_log(LOG_ERROR, "ARI LogChannel field logging_levels failed validation\n"); + ast_log(LOG_ERROR, "ARI LogChannel field configuration failed validation\n"); res = 0; } } else @@ -391,6 +392,26 @@ int ast_ari_validate_log_channel(struct ast_json *json) res = 0; } } else + if (strcmp("status", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_status = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LogChannel field status failed validation\n"); + res = 0; + } + } else + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LogChannel field type failed validation\n"); + res = 0; + } + } else { ast_log(LOG_ERROR, "ARI LogChannel has undocumented field %s\n", @@ -399,8 +420,8 @@ int ast_ari_validate_log_channel(struct ast_json *json) } } - if (!has_logging_levels) { - ast_log(LOG_ERROR, "ARI LogChannel missing required field logging_levels\n"); + if (!has_configuration) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field configuration\n"); res = 0; } @@ -409,6 +430,16 @@ int ast_ari_validate_log_channel(struct ast_json *json) res = 0; } + if (!has_status) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field status\n"); + res = 0; + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field type\n"); + res = 0; + } + return res; } diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index fa393448dc..b181506d24 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -1302,8 +1302,10 @@ ari_validator ast_ari_validate_application_fn(void); * - attribute: string (required) * - value: string (required) * LogChannel - * - logging_levels: List[string] (required) + * - configuration: string (required) * - name: string (required) + * - status: string (required) + * - type: string (required) * Module * - description: string (required) * - name: string (required) diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c index 115adb3274..75b4d9a98d 100644 --- a/res/ari/resource_asterisk.c +++ b/res/ari/resource_asterisk.c @@ -653,6 +653,29 @@ void ast_ari_asterisk_rotate_log(struct ast_variable *headers, ast_ari_response_no_content(response); } +void ast_ari_asterisk_delete_log(struct ast_variable *headers, + struct ast_ari_asterisk_delete_log_args *args, + struct ast_ari_response *response) +{ + int res; + + ast_assert(response != NULL); + + res = ast_logger_remove_channel(args->log_channel_name); + + if (res == AST_LOGGER_FAILURE) { + ast_ari_response_error(response, 404, "Not Found", + "Log channel does not exist"); + return; + } else if (res == AST_LOGGER_ALLOC_ERROR) { + ast_ari_response_error(response, 500, "Internal Server Error", + "Allocation failed"); + return; + } + + ast_ari_response_no_content(response); +} + 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 e271b0547f..00f463ceeb 100644 --- a/res/ari/resource_asterisk.h +++ b/res/ari/resource_asterisk.h @@ -194,6 +194,19 @@ struct ast_ari_asterisk_reload_module_args { * \param[out] response HTTP response */ void ast_ari_asterisk_reload_module(struct ast_variable *headers, struct ast_ari_asterisk_reload_module_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_delete_log() */ +struct ast_ari_asterisk_delete_log_args { + /*! Log channels name */ + const char *log_channel_name; +}; +/*! + * \brief Deletes a log channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_delete_log(struct ast_variable *headers, struct ast_ari_asterisk_delete_log_args *args, struct ast_ari_response *response); /*! Argument struct for ast_ari_asterisk_rotate_log() */ struct ast_ari_asterisk_rotate_log_args { /*! Log channel's name */ diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c index 25bffacf17..5733d747dd 100644 --- a/res/res_ari_asterisk.c +++ b/res/res_ari_asterisk.c @@ -718,6 +718,65 @@ static void ast_ari_asterisk_reload_module_cb( } #endif /* AST_DEVMODE */ +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}. + * \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_delete_log_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_delete_log_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, "logChannelName") == 0) { + args.log_channel_name = (i->value); + } else + {} + } + ast_ari_asterisk_delete_log(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: /* Log channel does not exist. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + fin: __attribute__((unused)) return; } @@ -1061,6 +1120,7 @@ static struct stasis_rest_handlers asterisk_logging_logChannelName = { .path_segment = "logChannelName", .is_wildcard = 1, .callbacks = { + [AST_HTTP_DELETE] = ast_ari_asterisk_delete_log_cb, }, .num_children = 1, .children = { &asterisk_logging_logChannelName_rotate, } diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json index 5109419d74..53f690d47d 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -296,6 +296,34 @@ } ] }, + { + "path": "/asterisk/logging/{logChannelName}", + "description": "Asterisk log channel", + "operations": [ + { + "httpMethod": "DELETE", + "summary": "Deletes a log channel.", + "nickname": "deleteLog", + "responseClass": "void", + "parameters": [ + { + "name": "logChannelName", + "description": "Log channels name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Log channel does not exist." + } + ] + } + ] + }, { "path": "/asterisk/logging/{logChannelName}/rotate", "description": "Asterisk log channel", @@ -565,7 +593,7 @@ "id": "LogChannel", "description": "Details of an Asterisk log channel", "properties": { - "name": { + "channel": { "type": "string", "description": "The log channel path", "required": true @@ -581,7 +609,7 @@ "required": true }, "configuration": { - "type": "string", + "type": "List[string]", "description": "The various log levels", "required": true }