From f708f887cf30e94288670b2bfe6d7dc561fa4eea Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 8 Mar 2013 23:46:56 +0100 Subject: add syslog and klog support --- syslog.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 syslog.c (limited to 'syslog.c') diff --git a/syslog.c b/syslog.c new file mode 100644 index 0000000..a558e50 --- /dev/null +++ b/syslog.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "procd.h" +#include "syslog.h" + +#define LOG_DEFAULT_SIZE (16 * 1024) +#define LOG_DEFAULT_SOCKET "/dev/log" +#define LOG_LINE_LEN 256 +#define SYSLOG_PADDING 16 + +#define KLOG_DEFAULT_PROC "/proc/kmsg" + +#define PAD(x) (x % 4) ? (((x) - (x % 4)) + 4) : (x) + +static char *log_dev = LOG_DEFAULT_SOCKET; +static int log_size = LOG_DEFAULT_SIZE; +static struct log_head *log, *log_end, *oldest, *newest; +static int current_id = 0; +regex_t pat_prio; +regex_t pat_tstamp; + +static struct log_head *log_next(struct log_head *h, int size) +{ + struct log_head *n = (struct log_head *) &h->data[PAD(sizeof(struct log_head) + size)]; + + return (n >= log_end) ? (log) : (n); +} + +void log_add(char *buf, int size, int source) +{ + regmatch_t matches[4]; + struct log_head *next; + int priority = 0; + int ret; + + /* strip trailing newline */ + if (buf[size - 2] == '\n') { + buf[size - 2] = '\0'; + size -= 1; + } + + /* strip the priority */ + ret = regexec(&pat_prio, buf, 3, matches, 0); + if (!ret) { + priority = atoi(&buf[matches[1].rm_so]); + size -= matches[2].rm_so; + buf += matches[2].rm_so; + } + +#if 0 + /* strip kernel timestamp */ + ret = regexec(&pat_tstamp,buf, 4, matches, 0); + if ((source == SOURCE_KLOG) && !ret) { + size -= matches[3].rm_so; + buf += matches[3].rm_so; + } +#endif + + /* strip syslog timestamp */ + if ((source == SOURCE_SYSLOG) && (size > SYSLOG_PADDING) && (buf[SYSLOG_PADDING - 1] == ' ')) { + size -= SYSLOG_PADDING; + buf += SYSLOG_PADDING; + } + + DEBUG(2, "-> %d - %s\n", priority, buf); + + /* find new oldest entry */ + next = log_next(newest, size); + if (next > newest) { + while ((oldest > newest) && (oldest <= next) && (oldest != log)) + oldest = log_next(oldest, oldest->size); + } else { + DEBUG(2, "Log wrap\n"); + newest->size = 0; + next = log_next(log, size); + for (oldest = log; oldest <= next; oldest = log_next(oldest, oldest->size)) + ; + newest = log; + } + + /* add the log message */ + newest->size = size; + newest->id = current_id++; + newest->priority = priority; + newest->source = source; + clock_gettime(CLOCK_REALTIME, &newest->ts); + strcpy(newest->data, buf); + + ubus_notify_log(newest); + + newest = next; +} + +static void slog_cb(struct ustream *s, int bytes) +{ + struct ustream_buf *buf = s->r.head; + char *str; + int len; + + do { + str = ustream_get_read_buf(s, NULL); + if (!str) + break; + len = strlen(buf->data); + if (!len) { + bytes -= 1; + ustream_consume(s, 1); + continue; + } + log_add(buf->data, len + 1, SOURCE_SYSLOG); + ustream_consume(s, len); + bytes -= len; + } while (bytes > 0); +} + +static void klog_cb(struct ustream *s, int bytes) +{ + struct ustream_buf *buf = s->r.head; + char *newline, *str; + int len; + + do { + str = ustream_get_read_buf(s, NULL); + if (!str) + break; + newline = strchr(buf->data, '\n'); + if (!newline) + break; + *newline = 0; + len = newline + 1 - str; + log_add(buf->data, len, SOURCE_KLOG); + ustream_consume(s, len); + } while (1); +} + +struct ustream_fd slog = { + .stream.string_data = true, + .stream.notify_read = slog_cb, +}; + +struct ustream_fd klog = { + .stream.string_data = true, + .stream.notify_read = klog_cb, +}; + +static int klog_open(void) +{ + int fd; + + DEBUG(1, "Opening %s\n", KLOG_DEFAULT_PROC); + fd = open(KLOG_DEFAULT_PROC, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + ERROR("Failed to open %s\n", KLOG_DEFAULT_PROC); + return -1; + } + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + ustream_fd_init(&klog, fd); + return 0; +} + +static int syslog_open(void) +{ + int fd; + + DEBUG(1, "Opening %s\n", log_dev); + unlink(log_dev); + fd = usock(USOCK_UNIX | USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, log_dev, NULL); + if (fd < 0) { + ERROR("Failed to open %s\n", log_dev); + return -1; + } + chmod(log_dev, 0666); + ustream_fd_init(&slog, fd); + return 0; +} + +struct log_head* log_list(int count, struct log_head *h) +{ + unsigned int min = count; + + if (count) + min = (count < current_id) ? (current_id - count) : (0); + if (!h && oldest->id >= min) + return oldest; + if (!h) + h = oldest; + + while (h != newest) { + h = log_next(h, h->size); + if (!h->size && (h > newest)) + h = log; + if (h->id >= min && (h != newest)) + return h; + } + + return NULL; +} + +int log_buffer_init(int size) +{ + struct log_head *_log = malloc(size); + + if (!_log) { + ERROR("Failed to initialize log buffer with size %d\n", log_size); + return -1; + } + + memset(_log, 0, size); + + if (log && ((log_size + sizeof(struct log_head)) < size)) { + struct log_head *start = _log; + struct log_head *end = ((void*) _log) + size; + struct log_head *l; + + l = log_list(0, NULL); + while ((start < end) && l && l->size) { + memcpy(start, l, PAD(sizeof(struct log_head) + l->size)); + start = (struct log_head *) &l->data[PAD(l->size)]; + l = log_list(0, l); + } + free(log); + newest = start; + newest->size = 0; + oldest = log = _log; + log_end = ((void*) log) + size; + } else { + oldest = newest = log = _log; + log_end = ((void*) log) + size; + } + log_size = size; + + return 0; +} + +int log_buffer_size(void) +{ + return log_size; +} + +void log_init(void) +{ + regcomp(&pat_prio, "^<([0-9]*)>(.*)", REG_EXTENDED); + regcomp(&pat_tstamp, "^\[[ 0]*([0-9]*).([0-9]*)] (.*)", REG_EXTENDED); + + if (log_buffer_init(log_size)) { + ERROR("Failed to allocate log memory\n"); + exit(-1); + } + + syslog_open(); + klog_open(); + openlog("procd", LOG_PID, LOG_DAEMON); +} -- cgit v1.2.3