sys/log/full/src/log.c (841 lines of code) (raw):
/*
* 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 <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "os/mynewt.h"
#include "cbmem/cbmem.h"
#include "log/log.h"
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
#include "config/config.h"
#endif
#if MYNEWT_VAL(LOG_FLAGS_IMAGE_HASH)
#include "imgmgr/imgmgr.h"
#endif
#if MYNEWT_VAL(LOG_CLI)
#include "shell/shell.h"
#endif
struct log_module_entry {
int16_t id;
const char *name;
};
struct log_info g_log_info;
static STAILQ_HEAD(, log) g_log_list = STAILQ_HEAD_INITIALIZER(g_log_list);
static struct log_module_entry g_log_module_list[
MYNEWT_VAL(LOG_MAX_USER_MODULES)];
static int g_log_module_count;
static uint8_t log_written;
#if MYNEWT_VAL(LOG_CLI)
int shell_log_dump_cmd(int, char **);
struct shell_cmd g_shell_log_cmd = {
.sc_cmd = "log",
.sc_cmd_func = shell_log_dump_cmd
};
#if MYNEWT_VAL(LOG_FCB_SLOT1)
int shell_log_slot1_cmd(int, char **);
struct shell_cmd g_shell_slot1_cmd = {
.sc_cmd = "slot1",
.sc_cmd_func = shell_log_slot1_cmd,
};
#endif
#if MYNEWT_VAL(LOG_STORAGE_INFO)
int shell_log_storage_cmd(int, char **);
struct shell_cmd g_shell_storage_cmd = {
.sc_cmd = "log-storage",
.sc_cmd_func = shell_log_storage_cmd,
};
#endif
#endif
#if MYNEWT_VAL(LOG_STATS)
STATS_NAME_START(logs)
STATS_NAME(logs, writes)
STATS_NAME(logs, drops)
STATS_NAME(logs, errs)
STATS_NAME(logs, lost)
STATS_NAME(logs, too_long)
STATS_NAME_END(logs)
#endif
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
#if MYNEWT_VAL(LOG_PERSIST_WATERMARK)
static int log_conf_set(int argc, char **argv, char *val);
static struct conf_handler log_conf = {
.ch_name = "log",
.ch_get = NULL,
.ch_set = log_conf_set,
.ch_commit = NULL,
.ch_export = NULL,
};
static int
log_conf_set(int argc, char **argv, char *val)
{
struct log *cur;
if (argc < 2) {
return -1;
}
/* Only support log/<name>/mark entries for now */
if (strcmp("mark", argv[1])) {
return -1;
}
/* Find proper log */
STAILQ_FOREACH(cur, &g_log_list, l_next) {
if (!strcmp(argv[0], cur->l_name)) {
break;
}
}
if (!cur) {
return -1;
}
/* Set watermark if supported */
if (cur->l_log->log_set_watermark) {
cur->l_log->log_set_watermark(cur, atoi(val));
}
return 0;
}
#endif
#endif
void
log_init(void)
{
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
(void)rc;
log_written = 0;
STAILQ_INIT(&g_log_list);
g_log_info.li_version = MYNEWT_VAL(LOG_VERSION);
#if MYNEWT_VAL(LOG_GLOBAL_IDX)
g_log_info.li_next_index = 0;
#endif
#if MYNEWT_VAL(LOG_CLI)
shell_cmd_register(&g_shell_log_cmd);
#if MYNEWT_VAL(LOG_FCB_SLOT1)
shell_cmd_register(&g_shell_slot1_cmd);
#endif
#if MYNEWT_VAL(LOG_STORAGE_INFO)
shell_cmd_register(&g_shell_storage_cmd);
#endif
#endif
#if MYNEWT_VAL(LOG_CONSOLE)
log_console_init();
#endif
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
#if MYNEWT_VAL(LOG_PERSIST_WATERMARK)
rc = conf_register(&log_conf);
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
#endif
}
struct log *
log_list_get_next(struct log *log)
{
struct log *next;
if (log == NULL) {
next = STAILQ_FIRST(&g_log_list);
} else {
next = STAILQ_NEXT(log, l_next);
}
return (next);
}
static int
log_module_find_idx(uint8_t id)
{
const struct log_module_entry *entry;
int i;
for (i = 0; i < g_log_module_count; i++) {
entry = &g_log_module_list[i];
if (entry->id == id) {
return i;
}
}
return -1;
}
uint8_t
log_module_register(uint8_t id, const char *name)
{
int idx;
if (g_log_module_count >= MYNEWT_VAL(LOG_MAX_USER_MODULES)) {
/* No free entries. */
return 0;
}
idx = log_module_find_idx(id);
if (idx != -1) {
/* Already registered. */
return 0;
}
/* Write to first unused entry. */
g_log_module_list[g_log_module_count] = (struct log_module_entry) {
.id = id,
.name = name,
};
g_log_module_count++;
return id;
}
const char *
log_module_get_name(uint8_t module)
{
int idx;
switch (module) {
#ifdef MYNEWT_VAL_DFLT_LOG_MOD
case MYNEWT_VAL(DFLT_LOG_MOD):
return "DEFAULT";
#endif
#ifdef MYNEWT_VAL_OS_LOG_MOD
case MYNEWT_VAL(OS_LOG_MOD):
return "OS";
#endif
#ifdef MYNEWT_VAL_BLE_LL_LOG_MOD
case MYNEWT_VAL(BLE_LL_LOG_MOD):
return "NIMBLE_CTLR";
#endif
#ifdef MYNEWT_VAL_BLE_HS_LOG_MOD
case MYNEWT_VAL(BLE_HS_LOG_MOD):
return "NIMBLE_HOST";
#endif
#ifdef MYNEWT_VAL_NFFS_LOG_MOD
case MYNEWT_VAL(NFFS_LOG_MOD):
return "NFFS";
#endif
#ifdef MYNEWT_VAL_REBOOT_LOG_MOD
case MYNEWT_VAL(REBOOT_LOG_MOD):
return "REBOOT";
#endif
#ifdef MYNEWT_VAL_OC_LOG_MOD
case MYNEWT_VAL(OC_LOG_MOD):
return "IOTIVITY";
#endif
#ifdef MYNEWT_VAL_TEST_LOG_MOD
case MYNEWT_VAL(TEST_LOG_MOD):
return "TEST";
#endif
default:
idx = log_module_find_idx(module);
if (idx != -1) {
return g_log_module_list[idx].name;
}
}
return NULL;
}
/**
* Indicates whether the specified log has been regiestered.
*/
static int
log_registered(struct log *log)
{
struct log *cur;
STAILQ_FOREACH(cur, &g_log_list, l_next) {
if (cur == log) {
return 1;
}
}
return 0;
}
struct log *
log_find(const char *name)
{
struct log *log;
log = NULL;
while ((log = log_list_get_next(log)) != NULL) {
if (strcmp(log->l_name, name) == 0) {
break;
}
}
return log;
}
struct log_read_hdr_arg {
struct log_entry_hdr *hdr;
int read_success;
};
static int
log_read_hdr_walk(struct log *log, struct log_offset *log_offset, const void *dptr,
uint16_t len)
{
struct log_read_hdr_arg *arg;
int rc;
arg = log_offset->lo_arg;
rc = log_read(log, dptr, arg->hdr, 0, LOG_BASE_ENTRY_HDR_SIZE);
if (rc >= LOG_BASE_ENTRY_HDR_SIZE) {
arg->read_success = 1;
}
if (arg->hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
rc = log_fill_current_img_hash(arg->hdr);
if (!rc || rc == SYS_ENOTSUP) {
arg->read_success = 1;
}
}
/* Abort the walk; only one header needed. */
return 1;
}
/**
* Reads the final log entry's header from the specified log.
*
* @param log The log to read from.
* @param out_hdr On success, the last entry header gets written
* here.
*
* @return 0 on success; nonzero on failure.
*/
static int
log_read_last_hdr(struct log *log, struct log_entry_hdr *out_hdr)
{
struct log_read_hdr_arg arg;
struct log_offset log_offset;
arg.hdr = out_hdr;
arg.read_success = 0;
log_offset.lo_arg = &arg;
log_offset.lo_ts = -1;
log_offset.lo_index = 0;
log_offset.lo_data_len = 0;
log_walk(log, log_read_hdr_walk, &log_offset);
if (!arg.read_success) {
return -1;
}
return 0;
}
/*
* Associate an instantiation of a log with the logging infrastructure
*/
int
log_register(const char *name, struct log *log, const struct log_handler *lh,
void *arg, uint8_t level)
{
struct log_entry_hdr hdr;
int sr;
int rc;
assert(!log_written);
if (level > LOG_LEVEL_MAX) {
level = LOG_LEVEL_MAX;
}
log->l_name = name;
log->l_log = lh;
log->l_arg = arg;
log->l_level = level;
log->l_append_cb = NULL;
log->l_max_entry_len = 0;
#if !MYNEWT_VAL(LOG_GLOBAL_IDX)
log->l_idx = 0;
#endif
if (!log_registered(log)) {
STAILQ_INSERT_TAIL(&g_log_list, log, l_next);
#if MYNEWT_VAL(LOG_STATS)
stats_init(STATS_HDR(log->l_stats),
STATS_SIZE_INIT_PARMS(log->l_stats, STATS_SIZE_32),
STATS_NAME_INIT_PARMS(logs));
stats_register(log->l_name, STATS_HDR(log->l_stats));
#endif
}
/* Call registered handler now - log structure is set and put on list */
if (log->l_log->log_registered) {
rc = log->l_log->log_registered(log);
if (rc) {
STAILQ_REMOVE(&g_log_list, log, log, l_next);
return rc;
}
}
/* If this is a persisted log, read the index from its most recent entry.
* We need to ensure the index of all subseqently written entries is
* monotonically increasing.
*/
if (log->l_log->log_type == LOG_TYPE_STORAGE) {
rc = log_read_last_hdr(log, &hdr);
if (rc == 0) {
OS_ENTER_CRITICAL(sr);
#if MYNEWT_VAL(LOG_GLOBAL_IDX)
if (hdr.ue_index >= g_log_info.li_next_index) {
g_log_info.li_next_index = hdr.ue_index + 1;
}
#else
if (hdr.ue_index >= log->l_idx) {
log->l_idx = hdr.ue_index + 1;
}
#endif
OS_EXIT_CRITICAL(sr);
}
}
return (0);
}
void
log_set_append_cb(struct log *log, log_append_cb *cb)
{
log->l_append_cb = cb;
}
uint16_t
log_hdr_len(const struct log_entry_hdr *hdr)
{
if (hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
return LOG_BASE_ENTRY_HDR_SIZE + LOG_IMG_HASHLEN;
}
return LOG_BASE_ENTRY_HDR_SIZE;
}
void
log_set_rotate_notify_cb(struct log *log, log_notify_rotate_cb *cb)
{
log->l_rotate_notify_cb = cb;
}
static int
log_chk_type(uint8_t etype)
{
int rc;
rc = OS_OK;
switch(etype) {
case LOG_ETYPE_STRING:
case LOG_ETYPE_BINARY:
case LOG_ETYPE_CBOR:
break;
default:
rc = OS_ERROR;
break;
}
return rc;
}
static int
log_chk_max_entry_len(struct log *log, uint16_t len)
{
int rc;
rc = OS_OK;
if (log->l_max_entry_len != 0) {
if (len > log->l_max_entry_len) {
LOG_STATS_INC(log, too_long);
rc = OS_ENOMEM;
}
}
return rc;
}
static int
log_append_prepare(struct log *log, uint8_t module, uint8_t level,
uint8_t etype, struct log_entry_hdr *ue)
{
int rc;
int sr;
struct os_timeval tv;
uint32_t idx;
rc = 0;
rc = log_chk_type(etype);
assert(rc == OS_OK);
if (log->l_name == NULL || log->l_log == NULL) {
rc = -1;
goto err;
}
if (level > LOG_LEVEL_MAX) {
level = LOG_LEVEL_MAX;
}
if (log->l_log->log_type == LOG_TYPE_STORAGE) {
/* Remember that a log entry has been persisted since boot. */
log_written = 1;
}
/*
* If the log message is below what this log instance is
* configured to accept, then just drop it.
*/
if (level < log->l_level) {
rc = -1;
goto err;
}
/* Check if this module has a minimum level. */
if (level < log_level_get(module)) {
rc = -1;
goto err;
}
OS_ENTER_CRITICAL(sr);
#if MYNEWT_VAL(LOG_GLOBAL_IDX)
idx = g_log_info.li_next_index++;
#else
idx = log->l_idx++;
#endif
OS_EXIT_CRITICAL(sr);
/* Try to get UTC Time */
rc = os_gettimeofday(&tv, NULL);
if (rc || tv.tv_sec < UTC01_01_2016) {
ue->ue_ts = os_get_uptime_usec();
} else {
ue->ue_ts = tv.tv_sec * 1000000 + tv.tv_usec;
}
ue->ue_level = level;
ue->ue_module = module;
ue->ue_index = idx;
ue->ue_etype = etype;
/* Clear flags before assigning */
ue->ue_flags = 0;
#if MYNEWT_VAL(LOG_FLAGS_IMAGE_HASH)
rc = log_fill_current_img_hash(ue);
if (rc == SYS_ENOTSUP) {
rc = 0;
}
#endif
err:
return (rc);
}
/**
* Calls the given log's append callback, if it has one.
*/
static void
log_call_append_cb(struct log *log, uint32_t idx)
{
/* Qualify this as `volatile` to prevent a race condition. This prevents
* the compiler from optimizing this temp variable away. We copy the
* original pointer value into this variable, then inspect and use the temp
* variable. This allows us to read the original pointer only once,
* preventing a TOCTTOU race.
* (This all assumes that function pointer reads and writes are atomic.)
*/
log_append_cb * volatile cb;
cb = log->l_append_cb;
if (cb != NULL) {
cb(log, idx);
}
}
int
log_append_typed(struct log *log, uint8_t module, uint8_t level, uint8_t etype,
void *data, uint16_t len)
{
struct log_entry_hdr *hdr;
int rc;
LOG_STATS_INC(log, writes);
rc = log_chk_max_entry_len(log, len);
if (rc != OS_OK) {
goto err;
}
hdr = (struct log_entry_hdr *)data;
rc = log_append_prepare(log, module, level, etype, hdr);
if (rc != 0) {
LOG_STATS_INC(log, drops);
goto err;
}
rc = log->l_log->log_append(log, data, len + log_hdr_len(hdr));
if (rc != 0) {
LOG_STATS_INC(log, errs);
goto err;
}
log_call_append_cb(log, hdr->ue_index);
return (0);
err:
return (rc);
}
int
log_append_body(struct log *log, uint8_t module, uint8_t level, uint8_t etype,
const void *body, uint16_t body_len)
{
struct log_entry_hdr hdr;
int rc;
LOG_STATS_INC(log, writes);
rc = log_chk_max_entry_len(log, body_len);
if (rc != OS_OK) {
return rc;
}
rc = log_append_prepare(log, module, level, etype, &hdr);
if (rc != 0) {
LOG_STATS_INC(log, drops);
return rc;
}
rc = log->l_log->log_append_body(log, &hdr, body, body_len);
if (rc != 0) {
LOG_STATS_INC(log, errs);
return rc;
}
log_call_append_cb(log, hdr.ue_index);
return 0;
}
int
log_append_mbuf_typed_no_free(struct log *log, uint8_t module, uint8_t level,
uint8_t etype, struct os_mbuf **om_ptr)
{
struct log_entry_hdr *hdr;
struct os_mbuf *om;
uint16_t len;
uint16_t hdr_len;
int rc;
/* Remove a loyer of indirection for convenience. */
om = *om_ptr;
LOG_STATS_INC(log, writes);
if (!log->l_log->log_append_mbuf) {
rc = SYS_ENOTSUP;
goto err;
}
hdr = (struct log_entry_hdr *)om->om_data;
/*
* We do a pull up twice, once so that the base header is
* contiguous, so that we read the flags correctly, second
* time is so that we account for the image hash as well.
*/
om = os_mbuf_pullup(om, LOG_BASE_ENTRY_HDR_SIZE);
if (!om) {
rc = -1;
goto err;
}
hdr_len = log_hdr_len(hdr);
om = os_mbuf_pullup(om, hdr_len);
if (!om) {
rc = -1;
goto err;
}
/*
* Check that the log body length is less than the maximum entry. This code
* may appear a bit odd in that it checks that the length is greater than
* a log entry header length. The reason for this check is to ensure any
* error handling of this case to be the same as it was before the
* maximum entry length was checked.
*/
len = os_mbuf_len(om);
if (len > hdr_len) {
rc = log_chk_max_entry_len(log, len - hdr_len);
if (rc != OS_OK) {
goto drop;
}
}
rc = log_append_prepare(log, module, level, etype, hdr);
if (rc != 0) {
LOG_STATS_INC(log, drops);
goto drop;
}
rc = log->l_log->log_append_mbuf(log, om);
if (rc != 0) {
goto err;
}
log_call_append_cb(log, hdr->ue_index);
*om_ptr = om;
return 0;
err:
LOG_STATS_INC(log, errs);
drop:
if (om) {
os_mbuf_free_chain(om);
*om_ptr = NULL;
}
return rc;
}
int
log_append_mbuf_typed(struct log *log, uint8_t module, uint8_t level,
uint8_t etype, struct os_mbuf *om)
{
int rc;
rc = log_append_mbuf_typed_no_free(log, module, level, etype, &om);
if (rc != 0) {
return rc;
}
os_mbuf_free_chain(om);
return 0;
}
int
log_append_mbuf_body_no_free(struct log *log, uint8_t module, uint8_t level,
uint8_t etype, struct os_mbuf *om)
{
struct log_entry_hdr hdr;
uint16_t len;
int rc;
LOG_STATS_INC(log, writes);
if (!log->l_log->log_append_mbuf_body) {
rc = SYS_ENOTSUP;
goto err;
}
len = os_mbuf_len(om);
rc = log_chk_max_entry_len(log, len);
if (rc != OS_OK) {
goto drop;
}
rc = log_append_prepare(log, module, level, etype, &hdr);
if (rc != 0) {
LOG_STATS_INC(log, drops);
goto drop;
}
rc = log->l_log->log_append_mbuf_body(log, &hdr, om);
if (rc != 0) {
goto err;
}
log_call_append_cb(log, hdr.ue_index);
return 0;
err:
LOG_STATS_INC(log, errs);
drop:
return rc;
}
int
log_append_mbuf_body(struct log *log, uint8_t module, uint8_t level,
uint8_t etype, struct os_mbuf *om)
{
int rc;
rc = log_append_mbuf_body_no_free(log, module, level, etype, om);
os_mbuf_free_chain(om);
return rc;
}
void
log_printf(struct log *log, uint8_t module, uint8_t level,
const char *msg, ...)
{
va_list args;
char buf[LOG_PRINTF_MAX_ENTRY_LEN];
int len;
va_start(args, msg);
len = vsnprintf(buf, LOG_PRINTF_MAX_ENTRY_LEN, msg, args);
va_end(args);
if (len >= LOG_PRINTF_MAX_ENTRY_LEN) {
len = LOG_PRINTF_MAX_ENTRY_LEN-1;
}
log_append_body(log, module, level, LOG_ETYPE_STRING, buf, len);
}
int
log_walk(struct log *log, log_walk_func_t walk_func,
struct log_offset *log_offset)
{
int rc;
rc = log->l_log->log_walk(log, walk_func, log_offset);
if (rc != 0) {
goto err;
}
return (0);
err:
return (rc);
}
/**
* Argument passed to `log_walk` to perform a log body walk. Wraps the
* original walk argument and the body walk callback in a single object.
*/
struct log_walk_body_arg {
/** The body walk function to call on each entry. */
log_walk_body_func_t fn;
/** The original argument passed to `log_walk`. */
void *arg;
};
/**
* Performs a body walk on a single log entry. This function reads the entry
* header, subtracts the header length from the total entry length, and
* forwards the data to the body walk callback.
*/
static int
log_walk_body_fn(struct log *log, struct log_offset *log_offset, const void *dptr,
uint16_t len)
{
struct log_walk_body_arg *lwba;
struct log_entry_hdr ueh;
int rc;
lwba = log_offset->lo_arg;
/* Read the log entry header. This gets passed to the body walk
* callback.
*/
rc = log_read_hdr(log, dptr, &ueh);
if (rc != 0) {
return rc;
}
if (log_offset->lo_index <= ueh.ue_index) {
len -= log_hdr_len(&ueh);
/* Pass the wrapped callback argument to the body walk function. */
log_offset->lo_arg = lwba->arg;
rc = lwba->fn(log, log_offset, &ueh, dptr, len);
/* Restore the original body walk argument. */
log_offset->lo_arg = lwba;
if (rc != 0) {
return rc;
}
}
return 0;
}
int
log_walk_body(struct log *log, log_walk_body_func_t walk_body_func,
struct log_offset *log_offset)
{
struct log_walk_body_arg lwba = {
.fn = walk_body_func,
.arg = log_offset->lo_arg,
};
int rc;
log_offset->lo_arg = &lwba;
rc = log->l_log->log_walk(log, log_walk_body_fn, log_offset);
log_offset->lo_arg = lwba.arg;
return rc;
}
int
log_walk_body_section(struct log *log, log_walk_body_func_t walk_body_func,
struct log_offset *log_offset)
{
struct log_walk_body_arg lwba = {
.fn = walk_body_func,
.arg = log_offset->lo_arg,
};
int rc;
if (!log->l_log->log_walk_sector) {
rc = SYS_ENOTSUP;
goto err;
}
log_offset->lo_arg = &lwba;
rc = log->l_log->log_walk_sector(log, log_walk_body_fn, log_offset);
log_offset->lo_arg = lwba.arg;
err:
return rc;
}
/**
* Reads from the specified log.
*
* @return The number of bytes read; 0 on failure.
*/
int
log_read(struct log *log, const void *dptr, void *buf, uint16_t off,
uint16_t len)
{
int rc;
rc = log->l_log->log_read(log, dptr, buf, off, len);
return (rc);
}
int
log_read_hdr(struct log *log, const void *dptr, struct log_entry_hdr *hdr)
{
int bytes_read;
bytes_read = log_read(log, dptr, hdr, 0, LOG_BASE_ENTRY_HDR_SIZE);
if (bytes_read != LOG_BASE_ENTRY_HDR_SIZE) {
return SYS_EIO;
}
if (hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
bytes_read = log_read(log, dptr, hdr->ue_imghash,
LOG_BASE_ENTRY_HDR_SIZE, LOG_IMG_HASHLEN);
if (bytes_read != LOG_IMG_HASHLEN) {
return SYS_EIO;
}
}
return 0;
}
int
log_read_body(struct log *log, const void *dptr, void *buf, uint16_t off,
uint16_t len)
{
int rc;
struct log_entry_hdr hdr;
rc = log_read_hdr(log, dptr, &hdr);
if (rc) {
return rc;
}
return log_read(log, dptr, buf, log_hdr_len(&hdr) + off, len);
}
int
log_read_mbuf(struct log *log, const void *dptr, struct os_mbuf *om, uint16_t off,
uint16_t len)
{
int rc;
if (!om || !log->l_log->log_read_mbuf) {
return 0;
}
rc = log->l_log->log_read_mbuf(log, dptr, om, off, len);
return (rc);
}
int
log_read_mbuf_body(struct log *log, const void *dptr, struct os_mbuf *om,
uint16_t off, uint16_t len)
{
int rc;
struct log_entry_hdr hdr;
rc = log_read_hdr(log, dptr, &hdr);
if (rc) {
return rc;
}
return log_read_mbuf(log, dptr, om, log_hdr_len(&hdr) + off, len);
}
int
log_flush(struct log *log)
{
int rc;
rc = log->l_log->log_flush(log);
if (rc != 0) {
goto err;
}
return (0);
err:
return (rc);
}
#if MYNEWT_VAL(LOG_STORAGE_INFO)
int
log_storage_info(struct log *log, struct log_storage_info *info)
{
int rc;
if (!log->l_log->log_storage_info) {
rc = OS_ENOENT;
goto err;
}
rc = log->l_log->log_storage_info(log, info);
if (rc != 0) {
goto err;
}
return (0);
err:
return (rc);
}
#endif
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
int
log_set_watermark(struct log *log, uint32_t index)
{
#if MYNEWT_VAL(LOG_PERSIST_WATERMARK)
char log_path[CONF_MAX_NAME_LEN];
char mark_val[10]; /* fits uint32_t + \0 */
#endif
int rc;
if (!log->l_log->log_set_watermark) {
rc = OS_ENOENT;
goto err;
}
rc = log->l_log->log_set_watermark(log, index);
if (rc != 0) {
goto err;
}
#if MYNEWT_VAL(LOG_PERSIST_WATERMARK)
snprintf(log_path, CONF_MAX_NAME_LEN, "log/%s/mark", log->l_name);
log_path[CONF_MAX_NAME_LEN - 1] = '\0';
sprintf(mark_val, "%u", (unsigned)index);
conf_save_one(log_path, mark_val);
#endif
return (0);
err:
return (rc);
}
#endif
void
log_set_level(struct log *log, uint8_t level)
{
assert(log);
log->l_level = level;
}
uint8_t
log_get_level(const struct log *log)
{
assert(log);
return log->l_level;
}
void
log_set_max_entry_len(struct log *log, uint16_t max_entry_len)
{
assert(log);
log->l_max_entry_len = max_entry_len;
}
int
log_fill_current_img_hash(struct log_entry_hdr *hdr)
{
#if MYNEWT_VAL(LOG_FLAGS_IMAGE_HASH)
hdr->ue_flags |= LOG_FLAGS_IMG_HASH;
/* We have to account for LOG_IMG_HASHLEN bytes of hash */
return imgr_get_current_hash(hdr->ue_imghash, LOG_IMG_HASHLEN);
#endif
memset(hdr->ue_imghash, 0, LOG_IMG_HASHLEN);
return SYS_ENOTSUP;
}
uint32_t
log_get_last_index(struct log *log)
{
#if MYNEWT_VAL(LOG_GLOBAL_IDX)
return g_log_info.li_next_index;
#else
return log->l_idx;
#endif
}