From f8d43c5f222b99d5a7a0e2bd1fdc0d442e030e89 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 14 Nov 2013 13:47:18 +0100 Subject: [PATCH] add log daemon Signed-off-by: John Crispin --- CMakeLists.txt | 12 ++ log/logd.c | 185 +++++++++++++++++++++++++ log/logread.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++ log/syslog.c | 299 +++++++++++++++++++++++++++++++++++++++ log/syslog.h | 42 ++++++ 5 files changed, 907 insertions(+) create mode 100644 log/logd.c create mode 100644 log/logread.c create mode 100644 log/syslog.c create mode 100644 log/syslog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cfbbc8..1aa9a86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,3 +70,15 @@ TARGET_LINK_LIBRARIES(validate_data ${LIBS} validate) INSTALL(TARGETS validate_data RUNTIME DESTINATION sbin ) + +ADD_EXECUTABLE(logd log/logd.c log/syslog.c) +TARGET_LINK_LIBRARIES(logd ${LIBS}) +INSTALL(TARGETS logd + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(logread log/logread.c) +TARGET_LINK_LIBRARIES(logread ${LIBS}) +INSTALL(TARGETS logread + RUNTIME DESTINATION sbin +) diff --git a/log/logd.c b/log/logd.c new file mode 100644 index 0000000..978d7d1 --- /dev/null +++ b/log/logd.c @@ -0,0 +1,185 @@ +/* + * 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 "syslog.h" + +int debug = 0; +static int notify; +static struct blob_buf b; +static struct ubus_context *_ctx; +static struct uloop_timeout ubus_timer; + +static const struct blobmsg_policy read_policy = + { .name = "lines", .type = BLOBMSG_TYPE_INT32 }; + +static const struct blobmsg_policy write_policy = + { .name = "event", .type = BLOBMSG_TYPE_STRING }; + +static int +read_log(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb; + struct log_head *l; + void *lines, *entry; + int count = 0; + + if (msg) { + blobmsg_parse(&read_policy, 1, &tb, blob_data(msg), blob_len(msg)); + if (tb) + count = blobmsg_get_u32(tb); + } + + blob_buf_init(&b, 0); + lines = blobmsg_open_array(&b, "lines"); + + l = log_list(count, NULL); + + while (l) { + entry = blobmsg_open_table(&b, NULL); + blobmsg_add_string(&b, "msg", l->data); + blobmsg_add_u32(&b, "id", l->id); + blobmsg_add_u32(&b, "priority", l->priority); + blobmsg_add_u32(&b, "source", l->source); + blobmsg_add_u64(&b, "time", l->ts.tv_sec); + blobmsg_close_table(&b, entry); + l = log_list(count, l); + } + blobmsg_close_table(&b, lines); + ubus_send_reply(ctx, req, b.head); + + return 0; +} + +static int +write_log(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb; + char *event; + + if (msg) { + blobmsg_parse(&write_policy, 1, &tb, blob_data(msg), blob_len(msg)); + if (tb) { + event = blobmsg_get_string(tb); + log_add(event, strlen(event) + 1, SOURCE_SYSLOG); + } + } + + return 0; +} + +static void +log_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) +{ + notify = obj->has_subscribers; +} + +static const struct ubus_method log_methods[] = { + { .name = "read", .handler = read_log, .policy = &read_policy, .n_policy = 1 }, + { .name = "write", .handler = write_log, .policy = &write_policy, .n_policy = 1 }, +}; + +static struct ubus_object_type log_object_type = + UBUS_OBJECT_TYPE("log", log_methods); + +static struct ubus_object log_object = { + .name = "log", + .type = &log_object_type, + .methods = log_methods, + .n_methods = ARRAY_SIZE(log_methods), + .subscribe_cb = log_subscribe_cb, +}; + +void +ubus_notify_log(struct log_head *l) +{ + int ret; + + if (!notify) + return; + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "id", l->id); + blobmsg_add_u32(&b, "priority", l->priority); + blobmsg_add_u32(&b, "source", l->source); + blobmsg_add_u64(&b, "time", (((__u64) l->ts.tv_sec) * 1000) + (l->ts.tv_nsec / 1000000)); + + ret = ubus_notify(_ctx, &log_object, l->data, b.head, -1); + if (ret) + fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret)); +} + +static void +ubus_reconnect_cb(struct uloop_timeout *timeout) +{ + if (!ubus_reconnect(_ctx, NULL)) + ubus_add_uloop(_ctx); + else + uloop_timeout_set(timeout, 1000); +} + +static void +ubus_disconnect_cb(struct ubus_context *ctx) +{ + ubus_timer.cb = ubus_reconnect_cb; + uloop_timeout_set(&ubus_timer, 1000); +} + +static void +ubus_connect_cb(struct uloop_timeout *timeout) +{ + int ret; + + _ctx = ubus_connect(NULL); + if (!_ctx) { + uloop_timeout_set(timeout, 1000); + fprintf(stderr, "failed to connect to ubus\n"); + return; + } + _ctx->connection_lost = ubus_disconnect_cb; + ret = ubus_add_object(_ctx, &log_object); + if (ret) + fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); + fprintf(stderr, "log: connected to ubus\n"); + ubus_add_uloop(_ctx); +} + +int +main(int argc, char **argv) +{ + signal(SIGPIPE, SIG_IGN); + + uloop_init(); + ubus_timer.cb = ubus_connect_cb; + uloop_timeout_set(&ubus_timer, 1000); + log_init(); + uloop_run(); + if (_ctx) + ubus_free(_ctx); + log_shutdown(); + uloop_done(); + + return 0; +} diff --git a/log/logread.c b/log/logread.c new file mode 100644 index 0000000..e8749f8 --- /dev/null +++ b/log/logread.c @@ -0,0 +1,369 @@ +/* + * 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 + +#define SYSLOG_NAMES +#include + +#include +#include +#include +#include "libubus.h" +#include "syslog.h" + +enum { + LOG_STDOUT, + LOG_FILE, + LOG_NET, +}; + +enum { + LOG_MSG, + LOG_ID, + LOG_PRIO, + LOG_SOURCE, + LOG_TIME, + __LOG_MAX +}; + +static const struct blobmsg_policy log_policy[] = { + [LOG_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING }, + [LOG_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [LOG_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 }, + [LOG_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_INT32 }, + [LOG_TIME] = { .name = "time", .type = BLOBMSG_TYPE_INT64 }, +}; + +static struct ubus_subscriber log_event; +static struct uloop_timeout retry; +static struct uloop_fd sender; +static const char *log_file, *log_ip, *log_port, *log_prefix, *pid_file, *hostname; +static int log_type = LOG_STDOUT; +static int log_size, log_udp; + +static const char* getcodetext(int value, CODE *codetable) { + CODE *i; + + if (value >= 0) + for (i = codetable; i->c_val != -1; i++) + if (i->c_val == value) + return (i->c_name); + return ""; +}; + +static void log_handle_reconnect(struct uloop_timeout *timeout) +{ + sender.fd = usock((log_udp) ? (USOCK_UDP) : (USOCK_TCP), log_ip, log_port); + if (sender.fd < 0) { + fprintf(stderr, "failed to connect: %s\n", strerror(errno)); + uloop_timeout_set(&retry, 1000); + } else { + uloop_fd_add(&sender, ULOOP_READ); + syslog(0, "Logread connected to %s:%s\n", log_ip, log_port); + } +} + +static void log_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s, + uint32_t id) +{ + fprintf(stderr, "Object %08x went away\n", id); +} + +static void log_handle_fd(struct uloop_fd *u, unsigned int events) +{ + if (u->eof) { + uloop_fd_delete(u); + close(sender.fd); + sender.fd = -1; + uloop_timeout_set(&retry, 1000); + } +} + +static int log_notify(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__LOG_MAX]; + struct stat s; + char buf[512]; + uint32_t p; + char *str; + time_t t; + char *c; + + if (sender.fd < 0) + return 0; + + blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blob_data(msg), blob_len(msg)); + if (!tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME]) + return 1; + + if ((log_type == LOG_FILE) && log_size && (!stat(log_file, &s)) && (s.st_size > log_size)) { + char *old = malloc(strlen(log_file) + 5); + + close(sender.fd); + if (old) { + sprintf(old, "%s.old", log_file); + rename(log_file, old); + free(old); + } + sender.fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, 0600); + if (sender.fd < 0) { +// fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno)); + exit(-1); + } + } + + t = blobmsg_get_u64(tb[LOG_TIME]) / 1000; + c = ctime(&t); + p = blobmsg_get_u32(tb[LOG_PRIO]); + c[strlen(c) - 1] = '\0'; + str = blobmsg_format_json(msg, true); + if (log_type == LOG_NET) { + int err; + + *buf = '\0'; + if (hostname) + snprintf(buf, sizeof(buf), "%s ", hostname); + if (log_prefix) { + strncat(buf, log_prefix, sizeof(buf)); + strncat(buf, ": ", sizeof(buf)); + } + if (blobmsg_get_u32(tb[LOG_SOURCE]) == SOURCE_KLOG) + strncat(buf, "kernel: ", sizeof(buf)); + strncat(buf, method, sizeof(buf)); + if (log_udp) + err = write(sender.fd, buf, strlen(buf)); + else + err = send(sender.fd, buf, strlen(buf), 0); + + if (err < 0) { + syslog(0, "failed to send log data to %s:%s via %s\n", + log_ip, log_port, (log_udp) ? ("udp") : ("tcp")); + uloop_fd_delete(&sender); + close(sender.fd); + sender.fd = -1; + uloop_timeout_set(&retry, 1000); + } + } else { + snprintf(buf, sizeof(buf), "%s %s.%s%s %s\n", + c, getcodetext(LOG_FAC(p) << 3, facilitynames), getcodetext(LOG_PRI(p), prioritynames), + (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), + method); + write(sender.fd, buf, strlen(buf)); + } + + free(str); + if (log_type == LOG_FILE) + fsync(sender.fd); + + return 0; +} + +static void follow_log(struct ubus_context *ctx, int id) +{ + FILE *fp; + int ret; + + signal(SIGPIPE, SIG_IGN); + + if (pid_file) { + fp = fopen(pid_file, "w+"); + if (fp) { + fprintf(fp, "%d", getpid()); + fclose(fp); + } + } + + uloop_init(); + ubus_add_uloop(ctx); + + log_event.remove_cb = log_handle_remove; + log_event.cb = log_notify; + ret = ubus_register_subscriber(ctx, &log_event); + if (ret) + fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); + + ret = ubus_subscribe(ctx, &log_event, id); + if (ret) + fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); + + if (log_ip && log_port) { + openlog("logread", LOG_PID, LOG_DAEMON); + log_type = LOG_NET; + sender.cb = log_handle_fd; + retry.cb = log_handle_reconnect; + uloop_timeout_set(&retry, 1000); + } else if (log_file) { + log_type = LOG_FILE; + sender.fd = open(log_file, O_CREAT | O_WRONLY| O_APPEND, 0600); + if (sender.fd < 0) { + fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno)); + exit(-1); + } + } else { + sender.fd = STDOUT_FILENO; + } + + uloop_run(); + ubus_free(ctx); + uloop_done(); +} + +enum { + READ_LINE, + __READ_MAX +}; + + + +static const struct blobmsg_policy read_policy[] = { + [READ_LINE] = { .name = "lines", .type = BLOBMSG_TYPE_ARRAY }, +}; + +static void read_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr *cur; + struct blob_attr *_tb[__READ_MAX]; + time_t t; + int rem; + + if (!msg) + return; + + blobmsg_parse(read_policy, ARRAY_SIZE(read_policy), _tb, blob_data(msg), blob_len(msg)); + if (!_tb[READ_LINE]) + return; + blobmsg_for_each_attr(cur, _tb[READ_LINE], rem) { + struct blob_attr *tb[__LOG_MAX]; + uint32_t p; + char *c; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) + continue; + + blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur)); + if (!tb[LOG_MSG] || !tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME]) + continue; + + t = blobmsg_get_u64(tb[LOG_TIME]); + p = blobmsg_get_u32(tb[LOG_PRIO]); + c = ctime(&t); + c[strlen(c) - 1] = '\0'; + + printf("%s %s.%s%s %s\n", + c, getcodetext(LOG_FAC(p) << 3, facilitynames), getcodetext(LOG_PRI(p), prioritynames), + (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), + blobmsg_get_string(tb[LOG_MSG])); + } +} + +static int usage(const char *prog) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options:\n" + " -s Path to ubus socket\n" + " -l Got only the last 'count' messages\n" + " -r Stream message to a server\n" + " -F Log file\n" + " -S Log size\n" + " -p PID file\n" + " -h Add hostname to the message\n" + " -P Prefix custom text to streamed messages\n" + " -f Follow log messages\n" + " -u Use UDP as the protocol\n" + "\n", prog); + return 1; +} + +int main(int argc, char **argv) +{ + struct ubus_context *ctx; + uint32_t id; + const char *ubus_socket = NULL; + int ch, ret, subscribe = 0, lines = 0; + static struct blob_buf b; + + while ((ch = getopt(argc, argv, "ufcs:l:r:F:p:S:P:h:")) != -1) { + switch (ch) { + case 'u': + log_udp = 1; + break; + case 's': + ubus_socket = optarg; + break; + case 'r': + log_ip = optarg++; + log_port = argv[optind++]; + break; + case 'F': + log_file = optarg; + break; + case 'p': + pid_file = optarg; + break; + case 'P': + log_prefix = optarg; + break; + case 'f': + subscribe = 1; + break; + case 'l': + lines = atoi(optarg); + break; + case 'S': + log_size = atoi(optarg); + if (log_size < 1) + log_size = 1; + log_size *= 1024; + break; + case 'h': + hostname = optarg; + break; + default: + return usage(*argv); + } + } + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ret = ubus_lookup_id(ctx, "log", &id); + if (ret) + fprintf(stderr, "Failed to find log object: %s\n", ubus_strerror(ret)); + + if (!subscribe || lines) { + blob_buf_init(&b, 0); + if (lines) + blobmsg_add_u32(&b, "lines", lines); + ubus_invoke(ctx, id, "read", b.head, read_cb, 0, 3000); + } + + if (subscribe) + follow_log(ctx, id); + + return 0; +} diff --git a/log/syslog.c b/log/syslog.c new file mode 100644 index 0000000..fcc4a74 --- /dev/null +++ b/log/syslog.c @@ -0,0 +1,299 @@ +/* + * 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 + +#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; +static regex_t pat_prio; +static 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; + + /* bounce out if we don't have init'ed yet (regmatch etc will blow) */ + if (!log) { + fprintf(stderr, buf); + return; + } + + /* 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; + } + + //fprintf(stderr, "-> %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 { + //fprintf(stderr, "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; + + fd = open(KLOG_DEFAULT_PROC, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "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; + + unlink(log_dev); + fd = usock(USOCK_UNIX | USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, log_dev, NULL); + if (fd < 0) { + fprintf(stderr,"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) { + fprintf(stderr, "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; +} + +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)) { + fprintf(stderr, "Failed to allocate log memory\n"); + exit(-1); + } + + syslog_open(); + klog_open(); + openlog("sysinit", LOG_CONS, LOG_DAEMON); +} + +void +log_shutdown(void) +{ + ustream_free(&slog.stream); + ustream_free(&klog.stream); + close(slog.fd.fd); + close(klog.fd.fd); +} diff --git a/log/syslog.h b/log/syslog.h new file mode 100644 index 0000000..dc712ff --- /dev/null +++ b/log/syslog.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef __SYSLOG_H +#define __SYSLOG_H + +enum { + SOURCE_KLOG = 0, + SOURCE_SYSLOG = 1, + SOURCE_INTERNAL = 2, + SOURCE_ANY = 0xff, +}; + +struct log_head { + unsigned int size; + unsigned int id; + int priority; + int source; + struct timespec ts; + char data[]; +}; + +void log_init(void); +void log_shutdown(void); + +typedef void (*log_list_cb)(struct log_head *h); +struct log_head* log_list(int count, struct log_head *h); +int log_buffer_init(int size); +void log_add(char *buf, int size, int source); +void ubus_notify_log(struct log_head *l); + +#endif -- 2.30.2