/* Copyright(c) 2009-2018 TP-Link Systems Inc.
*
* file tplogd_input.c
* brief functions about how message input.
* details
*
* author Kuang MinLong
* version 1.0.0
* date 2Jul18
*
* history \arg 1.0.0, 2Jul18, Kuang Minlong, Create the file.
*/
#include <syslog.h>
#include <libubox/blobmsg.h>
#include <libtpsocket/libtpsocket.h>
#include "tplogd.h"
/**************************************************************************************************/
/* LOCAL_FUNCTIONS */
/**************************************************************************************************/
int dbg_client_dbg_log(struct tplogd_input_tpdbg_client *client, int log_level, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = tplogd_core_add_entry_internal(client->server->core, TPLOG_ENTRY_TYPE_DBG, log_level,
client->module_name, "tpdbg", format, args);
va_end(args);
return ret;
}
static void dbg_client_close_output(struct tplogd_input_tpdbg_client_output *output, int poll)
{
if (output->ufd.fd != -1) {
if (poll) {
output->poll_all = 1;
output->ufd.cb(&output->ufd, ULOOP_READ);
output->poll_all = 0;
}
uloop_fd_delete(&output->ufd);
close(output->ufd.fd);
output->ufd.fd = -1;
}
output->log_level = -1;
}
static void dbg_client_output_entry_strip(char *msg, int *plen)
{
int i, j;
int len = *plen;
/* strip console color control */
for (i = 0; i+1 < len; ) {
if (msg[i] == '\033' && msg[i+1] == '[') {
j = i + 2;
while (j < len && (isdigit(msg[j]) || msg[j] == ';')) {
++j;
}
if (msg[j] == 'm') {
++j;
memmove(msg+i, msg+j, len+1 - j);
len -= j - i;
} else {
++i;
}
} else {
++i;
}
}
/* strip tail space */
while (len && isspace(msg[len-1])) {
msg[--len] = '\0';
}
*plen = len;
}
static void dbg_client_add_output_entry(struct tplogd_input_tpdbg_client_output *output, struct timespec *ts, char *msg, int len)
{
struct tplogd_input_tpdbg_client *client = output->client;
struct tplog_entry_hdr hdr;
const char *submod = (output == &client->output[0]) ? "out" : "err";
const char *msgv[3] = {client->module_name, submod, msg};
int lenv[3] = {client->module_name_len, 3, -1};
dbg_client_output_entry_strip(msg, &len);
if (len <= 0) {
return;
}
lenv[2] = len;
hdr.type = TPLOG_ENTRY_TYPE_DBG;
hdr.source = TPLOG_ENTRY_SOURCE_TPDBG;
hdr.log_level = output->log_level;
hdr.time_format = TPLOG_TIME_REALTIME;
hdr.t_sec = ts->tv_sec;
hdr.t_nsec = ts->tv_nsec;
++output->counter;
tplogd_core_add_entry(client->server->core, &hdr, msgv, lenv);
}
static void dbg_client_output_handler(struct uloop_fd *u, unsigned int events)
{
struct tplogd_input_tpdbg_client_output *output = container_of(u, struct tplogd_input_tpdbg_client_output, ufd);
struct timespec ts;
static char static_buff[BUFSIZ + 1];
char *const buf = static_buff;
char *const tail = static_buff + sizeof(static_buff) - 1;
char *head, *end;
ssize_t len;
char *tmp;
struct timespec ts0, ts1;
unsigned int ms;
int read_more = 1;
if (events & ULOOP_READ) {
clock_gettime(CLOCK_REALTIME, &ts);
memcpy(&ts0, &ts, sizeof(ts));
head = buf;
for (; read_more; clock_gettime(CLOCK_REALTIME, &ts)) {
len = read(u->fd, head, tail - head);
if (len <= 0) {
len = 0;
}
if (len == 0 || (len < tail - head && head[len-1] == '\n')) {
read_more = 0;
} else {
read_more = 1;
}
end = head + len;
head = buf;
*end = '\0';
while ((tmp = strchr(head, '\n')) != NULL) {
*tmp = '\0';
dbg_client_add_output_entry(output, &ts, head, tmp - head);
head = tmp + 1;
}
/* move the rest to buf, reset head */
if (head != buf) {
memmove(buf, head, end + 1 - head);
}
head = buf + (end - head);
/* break if we use too much time */
/* TBD: do not truncate line */
if (read_more && !u->error && !output->poll_all) {
clock_gettime(CLOCK_REALTIME, &ts1);
ms = (ts1.tv_sec - ts0.tv_sec) * 1000 + ts1.tv_nsec/1000000 - ts0.tv_nsec/1000000;
if (ms > 1500) {
read_more = 0;
}
}
/* truncate for buff full or cann't read a line */
if (head != buf && (!read_more || head == tail)) {
dbg_client_add_output_entry(output, &ts, buf, head - buf);
head = buf;
*head = '\0';
}
}
}
if (u->error) {
dbg_client_close_output(output, 0);
dbg_client_dbg_log(output->client, TPLOG_LEVEL_DEBUG, "Read error. Disable log of std%s.",
(output == &output->client->output[0]) ? "out" : "err");
}
}
static struct tplogd_input_tpdbg_client *dbg_client_new(struct tplogd_input_tpdbg *dbg)
{
struct tplogd_input_tpdbg_client *client;
int i;
client = malloc(sizeof(*client));
if (client) {
memset(client, 0, sizeof(*client));
client->server = dbg;
client->log_level = -1;
for (i = 0; i < 2; ++i) {
client->output[i].ufd.fd = -1;
client->output[i].ufd.cb = dbg_client_output_handler;
client->output[i].log_level = -1;
client->output[i].poll_all = 0;
client->output[i].client = client;
}
list_add_tail(&client->list, &dbg->clients);
}
return client;
}
static void dbg_client_del(struct tplogd_input_tpdbg_client *client, int poll)
{
dbg_client_close_output(&client->output[0], poll);
dbg_client_close_output(&client->output[1], poll);
dbg_client_dbg_log(client, TPLOG_LEVEL_DEBUG, poll ? "Terminate." : "Force terminate.");
free(client->module_name);
list_del(&client->list);
free(client);
}
static void tplogd_input_tpdbg_client_process_msg(struct tplogd_input_tpdbg_client *client, void *buf, int len, int fd)
{
struct tplog_msg_log *log = buf;
struct tplog_msg_header *cfg = buf;
struct tplog_entry_hdr hdr;
struct tplogd_input_tpdbg_client_output *o;
const char *msgv[3];
int lenv[3];
if (len > (int)sizeof(*log) && (log->type == TPLOG_MSG_TYPE_LOG || log->type == TPLOG_MSG_TYPE_DBG)) {
if (log->msg_failed | log->msg_discarded) {
dbg_client_dbg_log(client, TPLOG_LEVEL_WARNING,
"%u dbg message failed, %u dbg message droped.", log->msg_failed, log->msg_discarded);
}
hdr.type = (log->type == TPLOG_MSG_TYPE_LOG) ? TPLOG_ENTRY_TYPE_LOG : TPLOG_ENTRY_TYPE_DBG;
hdr.source = TPLOG_ENTRY_SOURCE_TPDBG;
hdr.log_level = log->log_level <= TPLOG_LEVEL_DEBUG ? log->log_level : TPLOG_LEVEL_DEBUG;
hdr.time_format = log->time_format;
hdr.t_sec = log->t_sec;
hdr.t_nsec = log->t_nsec;
if (!tplog_entry_parse_data(log->module_len, log->submod_len, log->data, len - sizeof(*log), msgv, lenv)) {
++client->counter[hdr.log_level];
tplogd_core_add_entry(client->server->core, &hdr, msgv, lenv);
} else {
tplogd_core_add_entry_self_dbg(client->server->core, TPLOG_LEVEL_INFO,
"invalid tpdbg message from %s", client->module_name ? client->module_name : "");
}
} else if (len > (int)sizeof(*cfg) && cfg->type == TPLOG_MSG_TYPE_CONFIG && client) {
struct blobmsg (
blobmsg_string module_name,
blobmsg_int32 log_level,
blobmsg_int32 blocking,
blobmsg_int32 time_format,
blobmsg_int32 output_log_level,
blobmsg_int32 output_idx,
) (req, ((struct blob_attr *)cfg->data), false);
if (req.module_name) {
if (req.module_name) {
free(client->module_name);
if (blobmsg_get_string(req.module_name)[0] != '\0') {
client->module_name = strdup(blobmsg_get_string(req.module_name));
} else {
client->module_name = NULL;
}
client->module_name_len = client->module_name ? strlen(client->module_name) : 0;
}
if (req.log_level) {
client->log_level = blobmsg_get_u32(req.log_level);
}
if (req.blocking) {
client->blocking = blobmsg_get_u32(req.blocking);
}
if (req.time_format) {
client->time_format = blobmsg_get_u32(req.time_format);
}
dbg_client_dbg_log(client, TPLOG_LEVEL_DEBUG, "Config. log_level:%d blocking:%d time_format:%s",
client->log_level, client->blocking, client->time_format == TPLOG_TIME_MONOTONIC ? "monotonic" : "realtime");
}
if (req.output_log_level && req.output_idx) {
int level, idx;
int close_old = 0;
level = blobmsg_get_u32(req.output_log_level);
idx = blobmsg_get_u32(req.output_idx);
if (level >= -1 && level <= TPLOG_LEVEL_DEBUG && idx >= 0 && idx <= 1) {
o = &client->output[idx];
if (o->ufd.fd != -1) {
dbg_client_close_output(o, 1);
close_old = 1;
}
if (fd != -1 && level != -1) {
o->ufd.fd = fd;
fd = -1;
uloop_fd_add(&o->ufd, ULOOP_READ | ULOOP_ERROR_CB);
o->log_level = level;
dbg_client_dbg_log(client, TPLOG_LEVEL_DEBUG, "Config. Set std%s output to log_level:%d.%s",
idx == 0 ? "out" : "err", level, close_old ? " Disable preview log config." : "");
} else {
if (close_old) {
dbg_client_dbg_log(client, TPLOG_LEVEL_DEBUG, "Config. Disable log of std%s.",
idx == 0 ? "out" : "err");
}
}
}
}
}
if (fd != -1) {
close(fd);
}
}
static void tplogd_input_tplog_client_process_msg(struct tplogd_input_tplog *tplog, void *buf, int len, int fd)
{
struct tplog_msg_log *log = buf;
struct tplog_msg_header *cfg = buf;
struct tplog_entry_hdr hdr;
const char *msgv[3];
int lenv[3];
if (len > (int)sizeof(*log) && (log->type == TPLOG_MSG_TYPE_LOG || log->type == TPLOG_MSG_TYPE_DBG)) {
hdr.type = (log->type == TPLOG_MSG_TYPE_LOG) ? TPLOG_ENTRY_TYPE_LOG : TPLOG_ENTRY_TYPE_DBG;
hdr.source = TPLOG_ENTRY_SOURCE_TPLOG;
hdr.log_level = log->log_level <= TPLOG_LEVEL_DEBUG ? log->log_level : TPLOG_LEVEL_DEBUG;
hdr.time_format = log->time_format;
hdr.t_sec = log->t_sec;
hdr.t_nsec = log->t_nsec;
if (!tplog_entry_parse_data(log->module_len, log->submod_len, log->data, len - sizeof(*log), msgv, lenv)) {
++tplog->counter[hdr.log_level];
tplogd_core_add_entry(tplog->core, &hdr, msgv, lenv);
} else {
tplogd_core_add_entry_self_dbg(tplog->core, TPLOG_LEVEL_INFO, "Invalid tplog message");
}
} else if (len > (int)sizeof(*cfg) && cfg->type == TPLOG_MSG_TYPE_CONFIG) {
int highlight = -1, verbose = 0;
struct blobmsg (
blobmsg_int32 config_output,
blobmsg_int32 highlight,
blobmsg_int32 verbose,
) (req, ((struct blob_attr *)cfg->data), false);
if (req.config_output) {
if (req.highlight) {
highlight = blobmsg_get_u32(req.highlight);
}
if (req.verbose) {
verbose = blobmsg_get_u32(req.verbose);
}
tplogd_output_console_config(tplog->core, fd, &highlight, &verbose, NULL);
if (fd != -1) {
fd = -1;
}
}
}
if (fd != -1) {
close(fd);
}
}
static bool tplogd_input_tpdbg_server_handler(struct tpsocket_handler *handler, struct list_head *buf, int event)
{
struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler);
struct tpsocket_buf *pbuf;
int fd;
struct tplogd_input_tpdbg_client *client;
switch(event) {
case TPSOCKET_EVENT_LISTEN:
break;
case TPSOCKET_EVENT_ACCEPT:
handler->priv = client = dbg_client_new(handler->priv);
if (client) {
client->tpsock = sock;
}
break;
case TPSOCKET_EVENT_CONNECTED:
break;
case TPSOCKET_EVENT_MESSAGE:
pbuf = list_first_entry(buf, struct tpsocket_buf, list);
if (pbuf) {
if (!tpsocket_recv_fd(sock, &fd)) {
fd = -1;
}
tplogd_input_tpdbg_client_process_msg(handler->priv, tpbuf_data(pbuf), tpbuf_data_len(pbuf), fd);
}
break;
case TPSOCKET_EVENT_RESET:
break;
case TPSOCKET_EVENT_CLOSED:
if (handler->priv) {
dbg_client_del(handler->priv, 1);
}
break;
case TPSOCKET_EVENT_ERROR:
default:
break;
}
tpsocket_free_buf(buf, tpsocket_event_name(event), 0);
return true;
}
static bool tplogd_input_tplog_server_handler(struct tpsocket_handler *handler, struct list_head *buf, int event)
{
struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler);
struct tpsocket_buf *pbuf;
int fd;
switch(event) {
case TPSOCKET_EVENT_LISTEN:
break;
case TPSOCKET_EVENT_ACCEPT:
break;
case TPSOCKET_EVENT_CONNECTED:
break;
case TPSOCKET_EVENT_MESSAGE:
pbuf = list_first_entry(buf, struct tpsocket_buf, list);
if (pbuf) {
if (!tpsocket_recv_fd(sock, &fd)) {
fd = -1;
}
tplogd_input_tplog_client_process_msg(handler->priv, tpbuf_data(pbuf), tpbuf_data_len(pbuf), fd);
}
break;
case TPSOCKET_EVENT_RESET:
break;
case TPSOCKET_EVENT_CLOSED:
break;
case TPSOCKET_EVENT_ERROR:
default:
break;
}
tpsocket_free_buf(buf, tpsocket_event_name(event), 0);
return true;
}
static struct tpsocket_fd *tplogd_input_tpdbg_server_new(struct tplogd_input_tpdbg *dbg)
{
struct tpsocket_handler handler = {
.cb = tplogd_input_tpdbg_server_handler,
.priv = dbg,
};
return tpsocket_from_url("unixspd:/" TPLOG_MSG_DBG_SOCKET_PATH, &handler);
}
static struct tpsocket_fd *tplogd_input_tplog_server_new(struct tplogd_input_tplog *log)
{
struct tpsocket_handler handler = {
.cb = tplogd_input_tplog_server_handler,
.priv = log,
};
return tpsocket_from_url("unixdgramd:/" TPLOG_MSG_LOG_SOCKET_PATH, &handler);
}
static int tplogd_input_tpdbg_set_enable(struct tplogd_input_class *ci, int enable)
{
struct tplogd_input_tpdbg *dbg;
struct tplogd_input_tpdbg_client *client, *tmp;
if (!ci->enabled && enable) {
dbg = malloc(sizeof(*dbg));
if (!dbg) {
return -1;
}
dbg->core = ci->core;
dbg->tpsock = tplogd_input_tpdbg_server_new(dbg);
INIT_LIST_HEAD(&dbg->clients);
ci->enabled = 1;
ci->priv = dbg;
ci->core->tpdbg = dbg;
} else if (ci->enabled && !enable) {
dbg = ci->priv;
list_for_each_entry_safe(client, tmp, &dbg->clients, list) {
client->tpsock->handler.priv = NULL;
dbg_client_del(client, 0);
}
tpsocket_free(dbg->tpsock);
free(dbg);
unlink(TPLOG_MSG_DBG_SOCKET_PATH);
ci->enabled = 0;
ci->priv = NULL;
ci->core->tpdbg = NULL;
}
return 0;
}
static int tplogd_input_tplog_set_enable(struct tplogd_input_class *ci, int enable)
{
struct tplogd_input_tplog *log;
if (!ci->enabled && enable) {
log = malloc(sizeof(*log));
if (!log) {
return -1;
}
memset(log, 0, sizeof(*log));
log->core = ci->core;
log->tpsock = tplogd_input_tplog_server_new(log);
ci->enabled = 1;
ci->priv = log;
ci->core->tplog = log;
} else if (ci->enabled && !enable) {
log = ci->priv;
tpsocket_free(log->tpsock);
free(log);
unlink(TPLOG_MSG_LOG_SOCKET_PATH);
ci->enabled = 0;
ci->priv = NULL;
ci->core->tplog = NULL;
}
return 0;
}
static int tplogd_input_ubus_set_enable(struct tplogd_input_class *ci, int enable)
{
ci->enabled = enable;
ci->core->ubus_input_enabled = enable;
return 0;
}
static int tplogd_input_ubusevent_set_enable(struct tplogd_input_class *ci, int enable)
{
ci->enabled = enable;
ci->core->ubusevent_input_enabled = enable;
if (enable) {
memset(&ci->core->ubus_dbg_event_counter, 0, sizeof(ci->core->ubus_dbg_event_counter));
}
return 0;
}
static void tplogd_input_syslog_client_process_msg(struct tplogd_input_syslog *syslog, char *buf, int len)
{
static const char *const fac_str[13] = {
"kern", "user", "mail", "daemon",
"auth", "syslog", "lpr", "news",
"uucp", "cron", "authpriv", "ftp",
"local",
};
const char *msgv[3] = {"syslog", NULL, NULL};
int lenv[3] = {6, -1, -1};
struct tplog_entry_hdr hdr;
char *p;
int pri = (LOG_USER | LOG_NOTICE);
struct timespec ts;
while (len && (buf[len-1] == '\n' || buf[len-1] == '\0')) {
--len;
}
buf[len] = '\0';
/* parse priority */
p = buf;
if (*p == '<') {
pri = strtoul(p + 1, &p, 10);
if (*p == '>') {
p++;
}
if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
pri = (LOG_USER | LOG_NOTICE);
}
}
/* parse time */
if (len - (p - buf) >= 16 && p[3] == ' ' && p[6] == ' '
&& p[9] == ':' && p[12] == ':' && p[15] == ' ') {
p += 16;
}
// TBD: parse time & escape msg
++syslog->counter[LOG_PRI(pri)];
hdr.type = TPLOG_ENTRY_TYPE_DBG;
hdr.source = TPLOG_ENTRY_SOURCE_SYSLOG;
hdr.log_level = LOG_PRI(pri);
hdr.time_format = TPLOG_TIME_REALTIME;
clock_gettime(CLOCK_REALTIME, &ts);
hdr.t_sec = ts.tv_sec;
hdr.t_nsec = ts.tv_nsec;
if (LOG_FAC(pri) > 12) {
msgv[1] = fac_str[12];
} else {
msgv[1] = fac_str[LOG_FAC(pri)];
}
msgv[2] = p;
lenv[2] = len - (p - buf);
tplogd_core_add_entry(syslog->core, &hdr, msgv, lenv);
}
static bool tplogd_input_syslog_server_handler(struct tpsocket_handler *handler, struct list_head *buf, int event)
{
struct tpsocket_buf *pbuf;
switch(event) {
case TPSOCKET_EVENT_LISTEN:
break;
case TPSOCKET_EVENT_ACCEPT:
break;
case TPSOCKET_EVENT_CONNECTED:
break;
case TPSOCKET_EVENT_MESSAGE:
pbuf = list_first_entry(buf, struct tpsocket_buf, list);
if (pbuf) {
if (tpbuf_tailroom(pbuf) >= 1) {
tplogd_input_syslog_client_process_msg(handler->priv, tpbuf_data(pbuf), tpbuf_data_len(pbuf));
} else {
/* drop */
}
}
break;
case TPSOCKET_EVENT_RESET:
break;
case TPSOCKET_EVENT_CLOSED:
break;
case TPSOCKET_EVENT_ERROR:
default:
break;
}
tpsocket_free_buf(buf, tpsocket_event_name(event), 0);
return true;
}
static struct tpsocket_fd *tplogd_input_syslog_server_new(struct tplogd_input_syslog *syslog)
{
struct tpsocket_handler handler = {
.cb = tplogd_input_syslog_server_handler,
.priv = syslog,
};
return tpsocket_from_url("unixdgramd:/" TPLOGD_SYSLOG_SOCKET_PATH, &handler);
}
static int tplogd_input_syslog_set_enable(struct tplogd_input_class *ci, int enable)
{
struct tplogd_input_syslog *syslog;
if (!ci->enabled && enable) {
syslog = malloc(sizeof(*syslog));
if (!syslog) {
return -1;
}
memset(syslog, 0, sizeof(*syslog));
syslog->core = ci->core;
syslog->tpsock = tplogd_input_syslog_server_new(syslog);
ci->enabled = 1;
ci->priv = syslog;
ci->core->syslog = syslog;
} else if (ci->enabled && !enable) {
syslog = ci->priv;
tpsocket_free(syslog->tpsock);
free(syslog);
unlink(TPLOGD_SYSLOG_SOCKET_PATH);
ci->enabled = 0;
ci->priv = NULL;
ci->core->syslog = NULL;
}
return 0;
}
static struct tplogd_input_class *tplogd_input_class_find(struct tplogd_core *core, const char *name)
{
struct tplogd_input_class *ci;
list_for_each_entry(ci, &core->inputs, list) {
if (!strcmp(ci->name, name)) {
return ci;
}
}
return NULL;
}
static struct tplogd_input_class *tplogd_input_class_create(struct tplogd_core *core, const char *name)
{
struct tplogd_input_class *ci;
ci = malloc(sizeof(*ci));
if (ci) {
memset(ci, 0, sizeof(*ci));
INIT_LIST_HEAD(&ci->list);
ci->name = strdup(name);
if (!ci->name) {
free(ci);
return NULL;
}
ci->core = core;
list_add_tail(&ci->list, &core->inputs);
}
return ci;
}
/**************************************************************************************************/
/* PUBLIC_FUNCTIONS */
/**************************************************************************************************/
int tplogd_input_init(struct tplogd_core *core)
{
struct tplogd_input_class *ci;
ci = tplogd_input_class_create(core, "tplog");
if (ci) {
ci->set_enable = tplogd_input_tplog_set_enable;
}
ci = tplogd_input_class_create(core, "tpdbg");
if (ci) {
ci->set_enable = tplogd_input_tpdbg_set_enable;
}
ci = tplogd_input_class_create(core, "ubus");
if (ci) {
ci->set_enable = tplogd_input_ubus_set_enable;
}
ci = tplogd_input_class_create(core, "ubusevent");
if (ci) {
ci->set_enable = tplogd_input_ubusevent_set_enable;
}
ci = tplogd_input_class_create(core, "syslog");
if (ci) {
ci->set_enable = tplogd_input_syslog_set_enable;
}
// TBD : kmsg
/*
ci = tplogd_input_class_create(core, "kmsg");
*/
return 0;
}
void tplogd_input_exit(struct tplogd_core *core)
{
struct tplogd_input_class *ci, *ci_tmp;
list_for_each_entry_safe(ci, ci_tmp, &core->inputs, list) {
if (ci->enabled) {
ci->set_enable(ci, 0);
}
if (ci->name) {
free(ci->name);
}
free(ci);
}
}
int tplogd_input_set_enable(struct tplogd_core *core, const char *name, int enable)
{
struct tplogd_input_class *ci = tplogd_input_class_find(core, name);
int succ = 0;
if (ci && ci->set_enable) {
if (ci->enabled != enable) {
succ = !ci->set_enable(ci, enable);
tplogd_core_add_entry_self_dbg(core, TPLOG_LEVEL_DEBUG,
"%s input \"%s\" %s.", enable ? "Enable" : "Disable", ci->name, succ ? "done" : "failed");
} else {
succ = 1;
}
}
return succ ? 0 : -1;
}
最新发布