zephyr/subsys/logging/log_backend_net.c
Christian Taedcke d026b5dd34 logging: net: Make autostart of backend configurable
In certain application configuration the networking log backend must not
be started, when the application starts. Starting it must be delayed
until routes to the logging server are established.

This commit introduces a new Kconfig setting to be able to control that
behaviour.

Fixes #27464

Signed-off-by: Christian Taedcke <christian.taedcke@lemonbeat.com>
2020-09-01 13:34:13 +02:00

262 lines
6.3 KiB
C

/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(log_backend_net, CONFIG_LOG_DEFAULT_LEVEL);
#include <logging/log_backend.h>
#include <logging/log_core.h>
#include <logging/log_output.h>
#include <logging/log_msg.h>
#include <net/net_pkt.h>
#include <net/net_context.h>
/* Set this to 1 if you want to see what is being sent to server */
#define DEBUG_PRINTING 0
#if DEBUG_PRINTING
#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...)
#endif
#if defined(CONFIG_NET_IPV6) || CONFIG_NET_HOSTNAME_ENABLE
#define MAX_HOSTNAME_LEN NET_IPV6_ADDR_LEN
#else
#define MAX_HOSTNAME_LEN NET_IPV4_ADDR_LEN
#endif
static char dev_hostname[MAX_HOSTNAME_LEN + 1];
static uint8_t output_buf[CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE];
static bool net_init_done;
struct sockaddr server_addr;
static bool panic_mode;
const struct log_backend *log_backend_net_get(void);
NET_PKT_SLAB_DEFINE(syslog_tx_pkts, CONFIG_LOG_BACKEND_NET_MAX_BUF);
NET_PKT_DATA_POOL_DEFINE(syslog_tx_bufs,
ROUND_UP(CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE /
CONFIG_NET_BUF_DATA_SIZE, 1) *
CONFIG_LOG_BACKEND_NET_MAX_BUF);
static struct k_mem_slab *get_tx_slab(void)
{
return &syslog_tx_pkts;
}
struct net_buf_pool *get_data_pool(void)
{
return &syslog_tx_bufs;
}
static int line_out(uint8_t *data, size_t length, void *output_ctx)
{
struct net_context *ctx = (struct net_context *)output_ctx;
int ret = -ENOMEM;
if (ctx == NULL) {
return length;
}
ret = net_context_send(ctx, data, length, NULL, K_NO_WAIT, NULL);
if (ret < 0) {
goto fail;
}
DBG(data);
fail:
return length;
}
LOG_OUTPUT_DEFINE(log_output_net, line_out, output_buf, sizeof(output_buf));
static int do_net_init(void)
{
struct sockaddr *local_addr = NULL;
struct sockaddr_in6 local_addr6;
struct sockaddr_in local_addr4;
socklen_t server_addr_len;
struct net_context *ctx;
int ret;
if (IS_ENABLED(CONFIG_NET_IPV4) && server_addr.sa_family == AF_INET) {
local_addr = (struct sockaddr *)&local_addr4;
server_addr_len = sizeof(struct sockaddr_in);
local_addr4.sin_port = 0U;
}
if (IS_ENABLED(CONFIG_NET_IPV6) && server_addr.sa_family == AF_INET6) {
local_addr = (struct sockaddr *)&local_addr6;
server_addr_len = sizeof(struct sockaddr_in6);
local_addr6.sin6_port = 0U;
}
if (local_addr == NULL) {
DBG("Server address unknown\n");
return -EINVAL;
}
local_addr->sa_family = server_addr.sa_family;
ret = net_context_get(server_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
&ctx);
if (ret < 0) {
DBG("Cannot get context (%d)\n", ret);
return ret;
}
if (IS_ENABLED(CONFIG_NET_HOSTNAME_ENABLE)) {
(void)strncpy(dev_hostname, net_hostname_get(), MAX_HOSTNAME_LEN);
} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
server_addr.sa_family == AF_INET6) {
const struct in6_addr *src;
src = net_if_ipv6_select_src_addr(
NULL, &net_sin6(&server_addr)->sin6_addr);
if (src) {
net_addr_ntop(AF_INET6, src, dev_hostname,
MAX_HOSTNAME_LEN);
net_ipaddr_copy(&local_addr6.sin6_addr, src);
} else {
goto unknown;
}
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
server_addr.sa_family == AF_INET) {
const struct in_addr *src;
src = net_if_ipv4_select_src_addr(
NULL, &net_sin(&server_addr)->sin_addr);
if (src) {
net_addr_ntop(AF_INET, src, dev_hostname,
MAX_HOSTNAME_LEN);
net_ipaddr_copy(&local_addr4.sin_addr, src);
} else {
goto unknown;
}
} else {
unknown:
DBG("Cannot setup local context\n");
return -EINVAL;
}
ret = net_context_bind(ctx, local_addr, server_addr_len);
if (ret < 0) {
DBG("Cannot bind context (%d)\n", ret);
return ret;
}
(void)net_context_connect(ctx, &server_addr, server_addr_len,
NULL, K_NO_WAIT, NULL);
/* We do not care about return value for this UDP connect call that
* basically does nothing. Calling the connect is only useful so that
* we can see the syslog connection in net-shell.
*/
net_context_setup_pools(ctx, get_tx_slab, get_data_pool);
log_output_ctx_set(&log_output_net, ctx);
log_output_hostname_set(&log_output_net, dev_hostname);
return 0;
}
static void send_output(const struct log_backend *const backend,
struct log_msg *msg)
{
if (panic_mode) {
return;
}
if (!net_init_done && do_net_init() == 0) {
net_init_done = true;
}
log_msg_get(msg);
log_output_msg_process(&log_output_net, msg,
LOG_OUTPUT_FLAG_FORMAT_SYSLOG |
LOG_OUTPUT_FLAG_TIMESTAMP |
(IS_ENABLED(CONFIG_LOG_BACKEND_NET_SYST_ENABLE) ?
LOG_OUTPUT_FLAG_FORMAT_SYST : 0));
log_msg_put(msg);
}
static void init_net(void)
{
int ret;
net_sin(&server_addr)->sin_port = htons(514);
ret = net_ipaddr_parse(CONFIG_LOG_BACKEND_NET_SERVER,
sizeof(CONFIG_LOG_BACKEND_NET_SERVER) - 1,
&server_addr);
if (ret == 0) {
LOG_ERR("Cannot configure syslog server address");
return;
}
log_backend_deactivate(log_backend_net_get());
}
static void panic(struct log_backend const *const backend)
{
panic_mode = true;
}
static void sync_string(const struct log_backend *const backend,
struct log_msg_ids src_level, uint32_t timestamp,
const char *fmt, va_list ap)
{
uint32_t flags = LOG_OUTPUT_FLAG_LEVEL | LOG_OUTPUT_FLAG_FORMAT_SYSLOG |
LOG_OUTPUT_FLAG_TIMESTAMP |
(IS_ENABLED(CONFIG_LOG_BACKEND_NET_SYST_ENABLE) ?
LOG_OUTPUT_FLAG_FORMAT_SYST : 0);
uint32_t key;
if (!net_init_done && do_net_init() == 0) {
net_init_done = true;
}
key = irq_lock();
log_output_string(&log_output_net, src_level,
timestamp, fmt, ap, flags);
irq_unlock(key);
}
const struct log_backend_api log_backend_net_api = {
.panic = panic,
.init = init_net,
.put = IS_ENABLED(CONFIG_LOG_IMMEDIATE) ? NULL : send_output,
.put_sync_string = IS_ENABLED(CONFIG_LOG_IMMEDIATE) ?
sync_string : NULL,
/* Currently we do not send hexdumps over network to remote server
* in CONFIG_LOG_IMMEDIATE mode. This is just to save resources,
* this can be revisited if needed.
*/
.put_sync_hexdump = NULL,
};
/* Note that the backend can be activated only after we have networking
* subsystem ready so we must not start it immediately.
*/
LOG_BACKEND_DEFINE(log_backend_net, log_backend_net_api,
IS_ENABLED(CONFIG_LOG_BACKEND_NET_AUTOSTART));
const struct log_backend *log_backend_net_get(void)
{
return &log_backend_net;
}