| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "mgmt/mgmt.h" |
| #include "cborattr/cborattr.h" |
| #include "tinycbor/cbor_cnt_writer.h" |
| #include "log_mgmt/log_mgmt.h" |
| #include "log_mgmt/log_mgmt_impl.h" |
| #include "log_mgmt_config.h" |
| #include "log/log.h" |
| |
| /** Context used during walks. */ |
| struct log_walk_ctxt { |
| /* The number of bytes encoded to the response so far. */ |
| size_t rsp_len; |
| /* The encoder to use to write the current log entry. */ |
| struct CborEncoder *enc; |
| /* Counter per encoder to understand if we are encoding the first chunk */ |
| uint32_t counter; |
| }; |
| |
| static mgmt_handler_fn log_mgmt_show; |
| static mgmt_handler_fn log_mgmt_clear; |
| static mgmt_handler_fn log_mgmt_module_list; |
| static mgmt_handler_fn log_mgmt_level_list; |
| static mgmt_handler_fn log_mgmt_logs_list; |
| |
| static struct mgmt_handler log_mgmt_handlers[] = { |
| [LOG_MGMT_ID_SHOW] = { log_mgmt_show, NULL }, |
| [LOG_MGMT_ID_CLEAR] = { NULL, log_mgmt_clear }, |
| [LOG_MGMT_ID_MODULE_LIST] = { log_mgmt_module_list, NULL }, |
| [LOG_MGMT_ID_LEVEL_LIST] = { log_mgmt_level_list, NULL }, |
| [LOG_MGMT_ID_LOGS_LIST] = { log_mgmt_logs_list, NULL }, |
| }; |
| |
| #define LOG_MGMT_HANDLER_CNT \ |
| sizeof log_mgmt_handlers / sizeof log_mgmt_handlers[0] |
| |
| static struct mgmt_group log_mgmt_group = { |
| .mg_handlers = log_mgmt_handlers, |
| .mg_handlers_count = LOG_MGMT_HANDLER_CNT, |
| .mg_group_id = MGMT_GROUP_ID_LOG, |
| }; |
| |
| static int |
| log_mgmt_encode_entry(CborEncoder *enc, const struct log_mgmt_entry *entry, |
| size_t *out_len) |
| { |
| CborError err = CborNoError; |
| CborEncoder rsp; |
| CborEncoder str_encoder; |
| uint16_t len; |
| int off; |
| |
| len = cbor_encode_bytes_written(enc); |
| |
| err |= cbor_encoder_create_map(enc, &rsp, CborIndefiniteLength); |
| |
| switch (entry->type) { |
| case LOG_MGMT_ETYPE_CBOR: |
| err |= cbor_encode_text_stringz(&rsp, "type"); |
| err |= cbor_encode_text_stringz(&rsp, "cbor"); |
| break; |
| case LOG_MGMT_ETYPE_BINARY: |
| err |= cbor_encode_text_stringz(&rsp, "type"); |
| err |= cbor_encode_text_stringz(&rsp, "bin"); |
| break; |
| case LOG_MGMT_ETYPE_STRING: |
| err |= cbor_encode_text_stringz(&rsp, "type"); |
| err |= cbor_encode_text_stringz(&rsp, "str"); |
| break; |
| default: |
| return MGMT_ERR_ECORRUPT; |
| } |
| |
| err |= cbor_encode_text_stringz(&rsp, "msg"); |
| |
| /* |
| * Write entry data as byte string. Since this may not fit into single |
| * chunk of data we will write as indefinite-length byte string which is |
| * basically a indefinite-length container with definite-length strings |
| * inside. |
| */ |
| err |= cbor_encoder_create_indef_byte_string(&rsp, &str_encoder); |
| for (off = 0; off < entry->len && !err; ) { |
| err |= cbor_encode_byte_string(&str_encoder, entry->data, entry->len); |
| off += entry->len; |
| } |
| |
| err |= cbor_encoder_close_container(&rsp, &str_encoder); |
| |
| err |= cbor_encode_text_stringz(&rsp, "ts"); |
| err |= cbor_encode_int(&rsp, entry->ts); |
| err |= cbor_encode_text_stringz(&rsp, "level"); |
| err |= cbor_encode_uint(&rsp, entry->level); |
| err |= cbor_encode_text_stringz(&rsp, "index"); |
| err |= cbor_encode_uint(&rsp, entry->index); |
| err |= cbor_encode_text_stringz(&rsp, "module"); |
| err |= cbor_encode_uint(&rsp, entry->module); |
| if (entry->flags & LOG_MGMT_FLAGS_IMG_HASH) { |
| err |= cbor_encode_text_stringz(&rsp, "imghash"); |
| err |= cbor_encode_byte_string(&rsp, entry->imghash, |
| LOG_MGMT_IMG_HASHLEN); |
| } |
| err |= cbor_encoder_close_container(enc, &rsp); |
| |
| if (out_len != NULL) { |
| *out_len = cbor_encode_bytes_written(enc) - len; |
| } |
| |
| return MGMT_ERR_EOK; |
| } |
| |
| static int |
| log_mgmt_cb_encode(struct log_mgmt_entry *entry, void *arg) |
| { |
| struct CborCntWriter cnt_writer; |
| struct log_walk_ctxt *ctxt; |
| CborEncoder cnt_encoder; |
| size_t entry_len; |
| int rc; |
| |
| ctxt = arg; |
| |
| /*** First, determine if this entry would fit. */ |
| |
| cbor_cnt_writer_init(&cnt_writer); |
| #ifdef __ZEPHYR__ |
| cbor_encoder_cust_writer_init(&cnt_encoder, &cnt_writer.enc, 0); |
| #else |
| cbor_encoder_init(&cnt_encoder, &cnt_writer.enc, 0); |
| #endif |
| rc = log_mgmt_encode_entry(&cnt_encoder, entry, &entry_len); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| /* |
| * Check if the response is too long. If more than one entry is in the |
| * response we will not add the current one and will return ENOMEM. If this |
| * is just a single entry we add the generic too long message text. |
| */ |
| /* `+ 1` to account for the CBOR array terminator. */ |
| if (ctxt->rsp_len + entry_len + 1 > LOG_MGMT_MAX_RSP_LEN) { |
| /* |
| * Is this just a single entry? If so, encode the generic error |
| * message in the "msg" field of the response |
| */ |
| if (ctxt->counter == 0) { |
| entry->type = LOG_ETYPE_STRING; |
| snprintf((char *)entry->data, LOG_MGMT_MAX_RSP_LEN, |
| "error: entry too large (%d bytes)", entry_len); |
| } |
| |
| return MGMT_ERR_EMSGSIZE; |
| } |
| ctxt->rsp_len += entry_len; |
| |
| /*** The entry fits. Now encode it. */ |
| |
| rc = log_mgmt_encode_entry(ctxt->enc, entry, NULL); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| ctxt->counter++; |
| |
| return 0; |
| } |
| |
| static int |
| log_encode_entries(const struct log_mgmt_log *log, CborEncoder *enc, |
| int64_t timestamp, uint32_t index) |
| { |
| struct log_mgmt_filter filter; |
| struct log_walk_ctxt ctxt; |
| CborEncoder entries; |
| CborError err; |
| int rc; |
| |
| err = 0; |
| err |= cbor_encode_text_stringz(enc, "entries"); |
| err |= cbor_encoder_create_array(enc, &entries, CborIndefiniteLength); |
| |
| filter = (struct log_mgmt_filter) { |
| .min_timestamp = timestamp, |
| .min_index = index, |
| }; |
| ctxt = (struct log_walk_ctxt) { |
| .enc = &entries, |
| .rsp_len = cbor_encode_bytes_written(enc), |
| }; |
| |
| rc = log_mgmt_impl_foreach_entry(log->name, &filter, |
| log_mgmt_cb_encode, &ctxt); |
| if (rc != 0 && rc != MGMT_ERR_EMSGSIZE) { |
| return rc; |
| } |
| |
| err |= cbor_encoder_close_container(enc, &entries); |
| |
| if (err != 0) { |
| return MGMT_ERR_ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| log_encode(const struct log_mgmt_log *log, CborEncoder *ctxt, |
| int64_t timestamp, uint32_t index) |
| { |
| CborEncoder logs; |
| CborError err; |
| int rc; |
| |
| err = 0; |
| err |= cbor_encoder_create_map(ctxt, &logs, CborIndefiniteLength); |
| err |= cbor_encode_text_stringz(&logs, "name"); |
| err |= cbor_encode_text_stringz(&logs, log->name); |
| err |= cbor_encode_text_stringz(&logs, "type"); |
| err |= cbor_encode_uint(&logs, log->type); |
| |
| rc = log_encode_entries(log, &logs, timestamp, index); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| err |= cbor_encoder_close_container(ctxt, &logs); |
| |
| if (err != 0) { |
| return MGMT_ERR_ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Command handler: log show |
| */ |
| static int |
| log_mgmt_show(struct mgmt_ctxt *ctxt) |
| { |
| char name[LOG_MGMT_NAME_LEN]; |
| struct log_mgmt_log log; |
| CborEncoder logs; |
| CborError err; |
| uint64_t index; |
| int64_t timestamp; |
| uint32_t next_idx; |
| int name_len; |
| int log_idx; |
| int rc; |
| |
| const struct cbor_attr_t attr[] = { |
| { |
| .attribute = "log_name", |
| .type = CborAttrTextStringType, |
| .addr.string = name, |
| .len = sizeof(name), |
| }, |
| { |
| .attribute = "ts", |
| .type = CborAttrIntegerType, |
| .addr.integer = ×tamp, |
| }, |
| { |
| .attribute = "index", |
| .type = CborAttrUnsignedIntegerType, |
| .addr.uinteger = &index, |
| }, |
| { |
| .attribute = NULL, |
| }, |
| }; |
| |
| name[0] = '\0'; |
| rc = cbor_read_object(&ctxt->it, attr); |
| if (rc != 0) { |
| return MGMT_ERR_EINVAL; |
| } |
| name_len = strlen(name); |
| |
| /* Determine the index that the next log entry would use. */ |
| rc = log_mgmt_impl_get_next_idx(&next_idx); |
| if (rc != 0) { |
| return MGMT_ERR_EUNKNOWN; |
| } |
| |
| err = 0; |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "next_index"); |
| err |= cbor_encode_uint(&ctxt->encoder, next_idx); |
| |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "logs"); |
| err |= cbor_encoder_create_array(&ctxt->encoder, &logs, |
| CborIndefiniteLength); |
| |
| /* Iterate list of logs, encoding each that matches the client request. */ |
| for (log_idx = 0; ; log_idx++) { |
| rc = log_mgmt_impl_get_log(log_idx, &log); |
| if (rc == MGMT_ERR_ENOENT) { |
| /* Log list fully iterated. */ |
| if (name_len != 0) { |
| /* Client specified log name, but the log wasn't found. */ |
| cbor_encoder_close_container(&ctxt->encoder, &logs); |
| return MGMT_ERR_ENOENT; |
| } else { |
| break; |
| } |
| } else if (rc != 0) { |
| return rc; |
| } |
| |
| /* Stream logs cannot be read. */ |
| if (log.type != LOG_MGMT_TYPE_STREAM) { |
| if (name_len == 0 || strcmp(name, log.name) == 0) { |
| rc = log_encode(&log, &logs, timestamp, index); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| /* If the client specified this log, he isn't interested in the |
| * remaining ones. |
| */ |
| if (name_len != 0) { |
| break; |
| } |
| } |
| } |
| } |
| |
| err |= cbor_encoder_close_container(&ctxt->encoder, &logs); |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "rc"); |
| err |= cbor_encode_int(&ctxt->encoder, rc); |
| |
| if (err != 0) { |
| return MGMT_ERR_ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Command handler: log module_list |
| */ |
| static int |
| log_mgmt_module_list(struct mgmt_ctxt *ctxt) |
| { |
| const char *module_name; |
| CborEncoder modules; |
| CborError err; |
| int module; |
| int rc; |
| |
| err = 0; |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "rc"); |
| err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK); |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "module_map"); |
| err |= cbor_encoder_create_map(&ctxt->encoder, &modules, |
| CborIndefiniteLength); |
| |
| for (module = 0; ; module++) { |
| rc = log_mgmt_impl_get_module(module, &module_name); |
| if (rc == MGMT_ERR_ENOENT) { |
| break; |
| } |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (module_name != NULL) { |
| err |= cbor_encode_text_stringz(&modules, module_name); |
| err |= cbor_encode_uint(&modules, module); |
| } |
| } |
| |
| err |= cbor_encoder_close_container(&ctxt->encoder, &modules); |
| |
| if (err != 0) { |
| return MGMT_ERR_ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Command handler: log list |
| */ |
| static int |
| log_mgmt_logs_list(struct mgmt_ctxt *ctxt) |
| { |
| struct log_mgmt_log log; |
| CborEncoder log_list; |
| CborError err; |
| int log_idx; |
| int rc; |
| |
| err = 0; |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "rc"); |
| err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK); |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "log_list"); |
| err |= cbor_encoder_create_array(&ctxt->encoder, &log_list, |
| CborIndefiniteLength); |
| |
| for (log_idx = 0; ; log_idx++) { |
| rc = log_mgmt_impl_get_log(log_idx, &log); |
| if (rc == MGMT_ERR_ENOENT) { |
| break; |
| } |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (log.type != LOG_MGMT_TYPE_STREAM) { |
| err |= cbor_encode_text_stringz(&log_list, log.name); |
| } |
| } |
| |
| err |= cbor_encoder_close_container(&ctxt->encoder, &log_list); |
| |
| if (err != 0) { |
| return MGMT_ERR_ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Command handler: log level_list |
| */ |
| static int |
| log_mgmt_level_list(struct mgmt_ctxt *ctxt) |
| { |
| const char *level_name; |
| CborEncoder level_map; |
| CborError err; |
| int level; |
| int rc; |
| |
| err = 0; |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "rc"); |
| err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK); |
| err |= cbor_encode_text_stringz(&ctxt->encoder, "level_map"); |
| err |= cbor_encoder_create_map(&ctxt->encoder, &level_map, |
| CborIndefiniteLength); |
| |
| for (level = 0; ; level++) { |
| rc = log_mgmt_impl_get_level(level, &level_name); |
| if (rc == MGMT_ERR_ENOENT) { |
| break; |
| } |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (level_name != NULL) { |
| err |= cbor_encode_text_stringz(&level_map, level_name); |
| err |= cbor_encode_uint(&level_map, level); |
| } |
| } |
| |
| err |= cbor_encoder_close_container(&ctxt->encoder, &level_map); |
| |
| if (err != 0) { |
| return MGMT_ERR_ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Command handler: log clear |
| */ |
| static int |
| log_mgmt_clear(struct mgmt_ctxt *ctxt) |
| { |
| struct log_mgmt_log log; |
| char name[LOG_MGMT_NAME_LEN] = {0}; |
| int name_len; |
| int log_idx; |
| int rc; |
| |
| const struct cbor_attr_t attr[] = { |
| { |
| .attribute = "log_name", |
| .type = CborAttrTextStringType, |
| .addr.string = name, |
| .len = sizeof(name) |
| }, |
| { |
| .attribute = NULL |
| }, |
| }; |
| |
| name[0] = '\0'; |
| rc = cbor_read_object(&ctxt->it, attr); |
| if (rc != 0) { |
| return MGMT_ERR_EINVAL; |
| } |
| name_len = strlen(name); |
| |
| for (log_idx = 0; ; log_idx++) { |
| rc = log_mgmt_impl_get_log(log_idx, &log); |
| if (rc == MGMT_ERR_ENOENT) { |
| return 0; |
| } |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (log.type != LOG_MGMT_TYPE_STREAM) { |
| if (name_len == 0 || strcmp(log.name, name) == 0) { |
| rc = log_mgmt_impl_clear(log.name); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (name_len != 0) { |
| return 0; |
| } |
| } |
| } |
| } |
| |
| if (name_len != 0) { |
| return MGMT_ERR_ENOENT; |
| } |
| |
| return 0; |
| } |
| |
| void |
| log_mgmt_register_group(void) |
| { |
| mgmt_register_group(&log_mgmt_group); |
| } |