summaryrefslogtreecommitdiff
path: root/accel-pppd/logs/log_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/logs/log_tcp.c')
-rw-r--r--accel-pppd/logs/log_tcp.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/accel-pppd/logs/log_tcp.c b/accel-pppd/logs/log_tcp.c
new file mode 100644
index 00000000..306c450f
--- /dev/null
+++ b/accel-pppd/logs/log_tcp.c
@@ -0,0 +1,323 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "log.h"
+#include "triton.h"
+#include "events.h"
+#include "ppp.h"
+#include "spinlock.h"
+#include "mempool.h"
+
+#include "memdebug.h"
+
+struct tcp_target_t
+{
+ struct log_target_t target;
+ struct list_head entry;
+ struct triton_md_handler_t hnd;
+ struct triton_timer_t conn_timer;
+ struct sockaddr_in addr;
+ char *buf;
+ int buf_size;
+ int buf_pos;
+ spinlock_t lock;
+ struct list_head queue;
+ int queue_len;
+ int connected:1;
+ int wait:1;
+};
+
+static int conf_connect_interval = 5;
+static int conf_queue_len = 1000;
+
+static struct triton_context_t tcp_ctx;
+
+static const char* level_name[]={" msg", "error", " warn", " info", " info", "debug"};
+
+static void start_connect(struct tcp_target_t *t);
+
+static LIST_HEAD(targets);
+
+static void disconnect(struct tcp_target_t *t)
+{
+ triton_md_unregister_handler(&t->hnd);
+ close(t->hnd.fd);
+
+ start_connect(t);
+}
+
+static void unpack_msg(struct tcp_target_t *t, struct log_msg_t *msg)
+{
+ struct log_chunk_t *chunk;
+ int pos = strlen(msg->hdr->msg);
+
+ strcpy(t->buf, msg->hdr->msg);
+
+ list_for_each_entry(chunk, msg->chunks, entry) {
+ memcpy(t->buf + pos, chunk->msg, chunk->len);
+ pos += chunk->len;
+ }
+
+ t->buf_size = pos;
+ t->buf_pos = 0;
+}
+
+static int send_log(struct tcp_target_t *t)
+{
+ struct log_msg_t *msg;
+ int n;
+
+ while (1) {
+ spin_lock(&t->lock);
+ if (!t->queue_len) {
+ t->wait = 0;
+ spin_unlock(&t->lock);
+ return 0;
+ }
+ msg = list_entry(t->queue.next, typeof(*msg), entry);
+ list_del(&msg->entry);
+ t->queue_len--;
+ spin_unlock(&t->lock);
+
+ unpack_msg(t, msg);
+
+ log_free_msg(msg);
+
+ while (t->buf_pos != t->buf_size) {
+ n = write(t->hnd.fd, t->buf + t->buf_pos, t->buf_size - t->buf_pos);
+ if (n < 0) {
+ if (errno == EAGAIN)
+ return 1;
+ if (errno != EPIPE)
+ log_emerg("log-tcp: write: %s\n", strerror(errno));
+ disconnect(t);
+ return 0;
+ }
+ t->buf_pos += n;
+ }
+ }
+}
+
+static void queue_log(struct tcp_target_t *t, struct log_msg_t *msg)
+{
+ int r;
+
+ spin_lock(&t->lock);
+ if (t->queue_len == conf_queue_len) {
+ spin_unlock(&t->lock);
+ log_free_msg(msg);
+ return;
+ }
+ list_add_tail(&msg->entry, &t->queue);
+ t->queue_len++;
+ if (t->connected) {
+ r = t->wait;
+ t->wait = 1;
+ } else
+ r = 1;
+ spin_unlock(&t->lock);
+
+ if (!r) {
+ if (send_log(t))
+ triton_md_enable_handler(&t->hnd, MD_MODE_WRITE);
+ }
+}
+
+static void set_hdr(struct log_msg_t *msg, struct ppp_t *ppp)
+{
+ struct tm tm;
+ char timestamp[32];
+
+ localtime_r(&msg->timestamp.tv_sec, &tm);
+
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &tm);
+ sprintf(msg->hdr->msg, "[%s]: %s: %s: ", timestamp, level_name[msg->level], ppp ? ppp->ifname : "");
+ msg->hdr->len = strlen(msg->hdr->msg);
+}
+
+static void general_log(struct log_target_t *lt, struct log_msg_t *msg, struct ppp_t *ppp)
+{
+ struct tcp_target_t *t = container_of(lt, typeof(*t), target);
+
+ set_hdr(msg, ppp);
+ queue_log(t, msg);
+}
+
+static int log_tcp_write(struct triton_md_handler_t *h)
+{
+ struct tcp_target_t *t = container_of(h, typeof(*t), hnd);
+
+ if (!send_log(t))
+ triton_md_disable_handler(h, MD_MODE_WRITE);
+
+ return 0;
+}
+
+static int log_tcp_connect(struct triton_md_handler_t *h)
+{
+ struct tcp_target_t *t = container_of(h, typeof(*t), hnd);
+
+ if (connect(t->hnd.fd, &t->addr, sizeof(t->addr))) {
+ if (errno == EAGAIN)
+ return 0;
+ if (errno == EINPROGRESS)
+ return 0;
+ log_emerg("log-tcp: connect: %s\n", strerror(errno));
+ triton_md_unregister_handler(&t->hnd);
+ close(t->hnd.fd);
+ triton_timer_add(&tcp_ctx, &t->conn_timer, 0);
+ return 0;
+ }
+
+ t->hnd.write = log_tcp_write;
+
+ triton_md_disable_handler(&t->hnd, MD_MODE_WRITE);
+
+ spin_lock(&t->lock);
+ t->connected = 1;
+ t->wait = 1;
+ spin_unlock(&t->lock);
+
+ if (send_log(t))
+ triton_md_enable_handler(&t->hnd, MD_MODE_WRITE);
+
+ return 0;
+}
+
+static void connect_timer(struct triton_timer_t *timer)
+{
+ struct tcp_target_t *t = container_of(timer, typeof(*t), conn_timer);
+
+ triton_timer_del(timer);
+
+ start_connect(t);
+}
+
+static void start_connect(struct tcp_target_t *t)
+{
+ t->hnd.write = log_tcp_connect;
+ t->hnd.fd = socket(PF_INET, SOCK_STREAM, 0);
+
+ if (!t->hnd.fd) {
+ log_emerg("log-tcp: socket: %s\n", strerror(errno));
+ return;
+ }
+
+ if (fcntl(t->hnd.fd, F_SETFL, O_NONBLOCK)) {
+ log_emerg("log-tcp: failed to set nonblocking mode: %s\n", strerror(errno));
+ close(t->hnd.fd);
+ return;
+ }
+
+ if (connect(t->hnd.fd, &t->addr, sizeof(t->addr))) {
+ if (errno != EINPROGRESS) {
+ log_emerg("log-tcp: connect: %s\n", strerror(errno));
+ close(t->hnd.fd);
+ return;
+ }
+ }
+
+ triton_md_register_handler(&tcp_ctx, &t->hnd);
+ triton_md_enable_handler(&t->hnd, MD_MODE_WRITE);
+}
+
+static void log_tcp_close(struct triton_context_t *ctx)
+{
+ struct tcp_target_t *t;
+
+ while (!list_empty(&targets)) {
+ t = list_entry(targets.next, typeof(*t), entry);
+ list_del(&t->entry);
+ if (t->conn_timer.tpd)
+ triton_timer_del(&t->conn_timer);
+ else {
+ t->connected = 0;
+ triton_md_unregister_handler(&t->hnd);
+ close(t->hnd.fd);
+ }
+ }
+
+ triton_context_unregister(&tcp_ctx);
+}
+
+static int start_log(const char *_opt)
+{
+ struct tcp_target_t *t;
+ char *opt = strdup(_opt);
+ int port;
+ char *d;
+
+ d = strchr(opt, ':');
+ if (!d)
+ goto err;
+
+ *d = 0;
+
+ port = atoi(d + 1);
+ if (port <= 0)
+ goto err;
+
+ t = _malloc(sizeof(*t));
+ memset(t, 0, sizeof(*t));
+
+ t->buf = _malloc(LOG_MAX_SIZE + 64);
+
+ t->conn_timer.expire_tv.tv_sec = conf_connect_interval;
+ t->conn_timer.expire = connect_timer;
+
+ t->target.log = general_log;
+
+ memset(&t->addr, 0, sizeof(t->addr));
+ t->addr.sin_family = AF_INET;
+ t->addr.sin_port = htons(port);
+ t->addr.sin_addr.s_addr = inet_addr(opt);
+
+ INIT_LIST_HEAD(&t->queue);
+
+ spinlock_init(&t->lock);
+
+ start_connect(t);
+
+ log_register_target(&t->target);
+
+ list_add_tail(&t->entry, &targets);
+
+ return 0;
+
+err:
+ free(opt);
+ return -1;
+}
+
+static struct triton_context_t tcp_ctx ={
+ .close = log_tcp_close,
+ .before_switch = log_switch,
+};
+
+static void __init init(void)
+{
+ struct conf_sect_t *s = conf_get_section("log");
+ struct conf_option_t *opt;
+
+ if (!s)
+ return;
+
+ triton_context_register(&tcp_ctx, NULL);
+
+ list_for_each_entry(opt, &s->items, entry) {
+ if (strcmp(opt->name, "log-tcp"))
+ continue;
+ if (!opt->val || start_log(opt->val))
+ log_emerg("log: log-tcp: invalid format: '%s'\n", opt->val);
+ }
+
+ triton_context_wakeup(&tcp_ctx);
+}
+