From: Ken-ichirou MATSUZAWA <chamaken@gmail.com>
To: Eric Leblond <eric@regit.org>
Cc: netfilter-devel@vger.kernel.org
Subject: [ulogd RFC PATCH 0/2 resend] introduce new string output plugin
Date: Tue, 22 Apr 2014 20:51:16 +0900 [thread overview]
Message-ID: <20140422115116.GA29347@gmail.com> (raw)
In-Reply-To: <1398071128.25953.16.camel@ice-age2.regit.org>
This patch introduces a new string output plugin. The output string can be
specified by "form" in config file. Format is consists of:
key: struct ulogd_key name enclosed by <>, e.g. <orig.ip.saddr.str>
group: enclosed by () and separated by |, pick first one if exists.
(<orig.l4.dport>|<icmp.type>|unknown) means
pick orig.l4.dport value if exist, or icmp.type value. if both
of them do not exist, select "unknown" string.
+: add two key value if it can be
anything else: as is
meta character <>()|+\ needs to be escaped by \. Sink can be specified by "dest"
like URI syntax, proto://host:port, proto can be either file, tcp and udp,
e.g. dest="tcp://192.168.1.1:8125" for statsite. host is filename in case of
proto is file or stdout if dest is "file://"
'.' and ':' in ip address format can be changed by specifying "addrsep" for use
of graphite and statsd.
Signed-off-by Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
---
output/Makefile.am | 5 +-
output/ulogd_output_SPRINT.c | 1063 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1067 insertions(+), 1 deletion(-)
create mode 100644 output/ulogd_output_SPRINT.c
diff --git a/output/Makefile.am b/output/Makefile.am
index ff851ad..5f2cb0e 100644
--- a/output/Makefile.am
+++ b/output/Makefile.am
@@ -7,7 +7,7 @@ SUBDIRS= pcap mysql pgsql sqlite3 dbi
pkglib_LTLIBRARIES = ulogd_output_LOGEMU.la ulogd_output_SYSLOG.la \
ulogd_output_OPRINT.la ulogd_output_GPRINT.la \
ulogd_output_NACCT.la ulogd_output_XML.la \
- ulogd_output_GRAPHITE.la
+ ulogd_output_GRAPHITE.la ulogd_output_SPRINT.la
if HAVE_JANSSON
pkglib_LTLIBRARIES += ulogd_output_JSON.la
@@ -42,3 +42,6 @@ ulogd_output_JSON_la_SOURCES = ulogd_output_JSON.c
ulogd_output_JSON_la_LIBADD = ${libjansson_LIBS}
ulogd_output_JSON_la_LDFLAGS = -avoid-version -module
endif
+
+ulogd_output_SPRINT_la_SOURCES = ulogd_output_SPRINT.c
+ulogd_output_SPRINT_la_LDFLAGS = -avoid-version -module
diff --git a/output/ulogd_output_SPRINT.c b/output/ulogd_output_SPRINT.c
new file mode 100644
index 0000000..7b7860e
--- /dev/null
+++ b/output/ulogd_output_SPRINT.c
@@ -0,0 +1,1063 @@
+/* ulogd_output_SPRINT.c
+ *
+ * ulogd output target for sending value specified `form' in config.
+ *
+ * (C) 2014 by Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+#include <ulogd/ulogd.h>
+#include <ulogd/conffile.h>
+
+#define IPADDR_LENGTH 128
+
+#ifndef ULOGD_SPRINT_DEFAULT
+#define ULOGD_SPRINT_DEFAULT "file:///var/log/ulogd.sprint"
+#endif
+
+struct sprint_priv {
+ int ofd;
+ struct llist_head form_head;
+};
+
+enum sprint_conf {
+ SPRINT_CONF_FORM = 0,
+ SPRINT_CONF_DEST,
+ SPRINT_CONF_ADDRSEP,
+ SPRINT_CONF_MAX
+};
+
+static struct config_keyset sprint_kset = {
+ .num_ces = SPRINT_CONF_MAX,
+ .ces = {
+ [SPRINT_CONF_FORM] = {
+ .key = "form",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ },
+ [SPRINT_CONF_DEST] = {
+ .key = "dest",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u = {.string = ULOGD_SPRINT_DEFAULT },
+ },
+ [SPRINT_CONF_ADDRSEP] = {
+ .key = "addrsep",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u = {.string = "" },
+ },
+ },
+};
+
+enum sprint_node_type {
+ NODE_HEAD,
+ NODE_STRING,
+ NODE_KEY,
+ NODE_CONCAT,
+ NODE_GROUP,
+ NODE_KEYCALC,
+};
+
+enum {
+ TOKEN_STRING = 256,
+ TOKEN_KEY,
+ TOKEN_ERROR,
+};
+
+struct keyop {
+ int opcode;
+ struct node *l;
+ struct node *r;
+};
+
+struct node {
+ enum sprint_node_type type;
+ struct llist_head list;
+ union {
+ char *string; /* NODE_STRING */
+ int kindex; /* NODE_KEY */
+ struct llist_head group; /* NODE_CONCAT, NODE_GROUP */
+ struct keyop keycalc; /* NODE_KEYCALC */
+ };
+};
+
+struct keysym {
+ struct llist_head list;
+ char *name;
+};
+
+struct outform {
+ char *formstr;
+ char *cur;
+ char *prev; /* for unput */
+ char *prev_lval;
+ struct llist_head form_head;
+ int num_keys;
+ struct llist_head keysyms;
+};
+
+static int unput(struct outform *scan)
+{
+ if (scan->cur == scan->prev)
+ return -1;
+ scan->cur = scan->prev;
+ if (scan->prev_lval)
+ free(scan->prev_lval);
+
+ return 0;
+}
+
+static int lval_term(struct outform *scan, int type,
+ char **dst, char *from, char *to, char *prev)
+{
+ char c;
+
+ if (to == NULL) {
+ *dst = scan->prev_lval = strdup(from);
+ } else {
+ c = *to;
+ *to = '\0';
+ *dst = scan->prev_lval = strdup(from);
+ *to = c;
+ }
+ if (*dst == NULL) {
+ ulogd_log(ULOGD_ERROR, "%s\n", strerror(errno));
+ return TOKEN_ERROR;
+ }
+ scan->prev = prev;
+ return type;
+}
+
+static int lval_char(struct outform *scan, int type, char *prev)
+{
+ scan->prev = prev;
+ scan->prev_lval = NULL;
+ return type;
+}
+
+static int lval_error(struct outform *scan, char **dst, char *msg)
+{
+ /* *dst = msg */
+ ulogd_log(ULOGD_ERROR, "%s around %d\n",
+ msg, scan->cur - scan->formstr);
+ scan->prev_lval = NULL;
+ return TOKEN_ERROR;
+}
+
+static int lex_key(struct outform *scan, char **lval)
+{
+ char c, *start;
+
+ for (start = scan->cur; (c = *scan->cur) != '\0'; scan->cur++) {
+ if (c == '>') {
+ scan->cur++;
+ return lval_term(scan, TOKEN_KEY, lval,
+ start - 1, scan->cur - 1, start - 2);
+ }
+ if (!isascii(c) && !isalnum(c)
+ && c != '.' && c != '_' && c != '-')
+ return lval_error(scan, lval,
+ "invalid key char");
+ }
+
+ return lval_error(scan, lval, "EOF in key");
+}
+
+static int lex_escape(struct outform *scan, char **lval)
+{
+ char sbuf[2];
+ char c = *scan->cur++;
+
+ switch (c) {
+ case 'n':
+ return lval_term(scan, TOKEN_STRING, lval,
+ "\n", NULL, scan->cur - 2);
+ case 't':
+ return lval_term(scan, TOKEN_STRING, lval,
+ "\t", NULL, scan->cur - 2);
+ case '\\':
+ case '<':
+ case '>':
+ case '(':
+ case ')':
+ case '|':
+ case '+':
+ snprintf(sbuf, 2, "%c", c);
+ return lval_term(scan, TOKEN_STRING, lval,
+ sbuf, NULL, scan->cur - 2);
+ case '\0':
+ return lval_error(scan, lval, "EOF in escape");
+ default:
+ return lval_error(scan, lval,
+ "invalid escape char");
+ }
+}
+
+static int lex(struct outform *scan, char **lval)
+{
+ char c, *start = scan->cur;
+
+ while ((c = *scan->cur) != '\0') {
+ switch(c) {
+ case '\\':
+ if (scan->cur != start)
+ return lval_term(scan, TOKEN_STRING, lval,
+ start, scan->cur, start);
+ scan->cur++;
+ return lex_escape(scan, lval);
+ break;
+ case '<':
+ if (scan->cur != start)
+ return lval_term(scan, TOKEN_STRING, lval,
+ start, scan->cur, start);
+ scan->cur++;
+ if (!isascii(*scan->cur) && !isalpha(*scan->cur))
+ return lval_error(scan, lval,
+ "invalid key start");
+ scan->cur++; /* consume key's first char */
+ return lex_key(scan, lval);
+ break;
+ case '>':
+ return lval_error(scan, lval,
+ "unexpected key end");
+ case ')':
+ case '(':
+ case '|':
+ if (scan->cur != start)
+ return lval_term(scan, TOKEN_STRING, lval,
+ start, scan->cur, start);
+ scan->cur++;
+ return lval_char(scan, c, start);
+ case '+':
+ do
+ scan->cur++;
+ while (*scan->cur == ' ' || *scan->cur == '\t');
+ return lval_char(scan, c, start);
+ case ' ':
+ case '\t':
+ default:
+ scan->cur++;
+ break;
+ }
+ }
+
+ if (scan->cur != start)
+ return lval_term(scan, TOKEN_STRING, lval,
+ start, scan->cur, start);
+
+ return 0;
+}
+
+static void *sprint_calloc(size_t len)
+{
+ void *p = calloc(len, 1);
+ if (p == NULL) {
+ ulogd_log(ULOGD_ERROR, "%s\n", strerror(errno));
+ return NULL;
+ }
+ return p;
+}
+
+static struct node *sprint_string_node(char *string)
+{
+ struct node *node = sprint_calloc(sizeof(struct node));
+
+ if (node == NULL)
+ return NULL;
+
+ node->type = NODE_STRING;
+ node->string = string;
+
+ return node;
+}
+
+static int sprint_key_index(struct outform *form, char *name)
+{
+ struct keysym *cur;
+ int i = 0;
+
+ llist_for_each_entry(cur, &form->keysyms, list) {
+ if (!strcmp(cur->name, name))
+ return i;
+ i++;
+ }
+
+ return -1;
+}
+
+static struct node *sprint_key_node(struct outform *form, char *name)
+{
+ struct node *node;
+ struct keysym *sym;
+
+ if (strlen(name) > ULOGD_MAX_KEYLEN) {
+ ulogd_log(ULOGD_ERROR, "too long key: %s\n", name);
+ return NULL;
+ }
+
+ node = sprint_calloc(sizeof(struct node));
+ if (node == NULL)
+ return NULL;
+
+ node->type = NODE_KEY;
+ node->kindex = sprint_key_index(form, name);
+ if (node->kindex < 0) {
+ sym = sprint_calloc(sizeof(struct keysym));
+ if (sym == NULL) {
+ free(node);
+ return NULL;
+ }
+ sym->name = name;
+ node->kindex = form->num_keys++;
+ llist_add_tail(&sym->list, &form->keysyms);
+ }
+
+ return node;
+}
+
+static struct node *sprint_list_node(enum sprint_node_type type,
+ struct node *term)
+{
+ struct node *node = sprint_calloc(sizeof(struct node));
+
+ if (node == NULL)
+ return NULL;
+
+ node->type = type;
+ INIT_LLIST_HEAD(&node->group);
+ llist_add_tail(&term->list, &node->group);
+ return node;
+}
+
+static struct node *sprint_keycalc_node(int opcode,
+ struct node *l, struct node *r)
+{
+ struct node *node = sprint_calloc(sizeof(struct node));
+
+ if (node == NULL)
+ return NULL;
+
+ node->type = NODE_KEYCALC;
+ node->keycalc.opcode = opcode;
+ node->keycalc.l = l;
+ node->keycalc.r = r;
+
+ return node;
+}
+
+static void sprint_free_nodes(struct llist_head *nodes);
+
+static void sprint_free_node(struct node *node)
+{
+ switch (node->type) {
+ case NODE_STRING:
+ free(node->string);
+ break;
+ case NODE_KEY:
+ break;
+ case NODE_GROUP:
+ case NODE_CONCAT:
+ sprint_free_nodes(&node->group);
+ break;
+ case NODE_KEYCALC:
+ sprint_free_node(node->keycalc.l);
+ sprint_free_node(node->keycalc.r);
+ break;
+ default:
+ ulogd_log(ULOGD_ERROR, "unknown node: %p"
+ " type: %d\n", node, node->type);
+ break;
+ }
+}
+
+static void sprint_free_nodes(struct llist_head *nodes)
+{
+ struct node *node, *nnode;
+
+ llist_for_each_entry_safe(node, nnode, nodes, list) {
+ sprint_free_node(node);
+ llist_del(&node->list);
+ free(node);
+ }
+}
+
+static void sprint_free_keysyms(struct llist_head *head)
+{
+ struct keysym *sym, *nsym;
+
+ llist_for_each_entry_safe(sym, nsym, head, list) {
+ llist_del(&sym->list);
+ free(sym->name);
+ free(sym);
+ }
+}
+
+/*
+ * form := part*
+ * part := concat | '(' selector ')'
+ * selector := concat ('|' concat)*
+ * concat := term term*
+ * term := STRING | KEY ('+' KEY)*
+ */
+static struct node *term(struct outform *form)
+{
+ char *lval;
+ struct node *nl, *nr;
+ int opcode, ret = lex(form, &lval);
+
+ if (ret == TOKEN_ERROR)
+ return NULL;
+ if (ret == TOKEN_STRING)
+ return sprint_string_node(lval);
+ if (ret != TOKEN_KEY) {
+ ulogd_log(ULOGD_ERROR,
+ "form char: %d, invalid meta char: %c\n",
+ form->cur - form->formstr, ret);
+ return NULL;
+ }
+
+ /* ret == TOKEN_KEY */
+ nl = sprint_key_node(form, lval);
+ if (nl == NULL)
+ return NULL;
+
+ opcode = lex(form, &lval);
+ if (opcode == TOKEN_ERROR)
+ return NULL;
+ if (opcode != '+') {
+ if (opcode != '\0')
+ unput(form);
+ return nl;
+ }
+
+ ret = lex(form, &lval);
+ if (ret == TOKEN_ERROR)
+ return NULL;
+ if (ret != TOKEN_KEY) {
+ ulogd_log(ULOGD_ERROR,
+ "form char: %d, right operand must be a KEY\n",
+ form->cur - form->formstr);
+ return NULL;
+ }
+ unput(form);
+
+ nr = term(form);
+ if (nr == NULL)
+ return NULL;
+
+ return sprint_keycalc_node(opcode, nl, nr);
+}
+
+static struct node *concat(struct outform *form)
+{
+ char *lval;
+ int ret;
+ struct node *terms, *last, *n = term(form);
+
+ if (n == NULL)
+ return NULL;
+
+ terms = sprint_list_node(NODE_CONCAT, n);
+ if (terms == NULL)
+ return NULL;
+
+ ret = lex(form, &lval);
+ while (ret == TOKEN_STRING || ret == TOKEN_KEY) {
+ unput(form);
+ n = term(form);
+ if (n == NULL)
+ return NULL;
+ last = llist_entry(terms->group.prev, struct node, list);
+ if (last->type == NODE_STRING && n->type == NODE_STRING) {
+ /* a little bit optimize */
+ int len1 = strlen(last->string),
+ len2 = strlen(n->string);
+
+ last->string = realloc(last->string, len1 + len2 + 1);
+ if (last->string == NULL) {
+ ulogd_log(ULOGD_ERROR, "%s\n", strerror(errno));
+ return NULL;
+ }
+ strncpy(last->string + len1, n->string, len2);
+ sprint_free_node(n);
+ free(n);
+ } else {
+ llist_add_tail(&n->list, &terms->group);
+ }
+ ret = lex(form, &lval);
+ }
+ if (ret != '\0')
+ unput(form);
+
+ return terms;
+}
+
+static struct node *selector(struct outform *form)
+{
+ int ret;
+ char *lval;
+ struct node *concats, *n = concat(form);
+
+ if (n == NULL)
+ return NULL;
+
+ concats = sprint_list_node(NODE_GROUP, n);
+ if (concats == NULL)
+ return NULL;
+
+ while ((ret = lex(form, &lval)) == '|') {
+ n = concat(form);
+ if (n == NULL)
+ return NULL;
+ llist_add_tail(&n->list, &concats->group);
+ }
+ if (ret != '\0')
+ unput(form);
+
+ return concats;
+}
+
+static struct node *part(struct outform *form)
+{
+ char *lval;
+ struct node *n;
+ int ret = lex(form, &lval);
+
+ if (ret == TOKEN_ERROR)
+ return NULL;
+
+ if (ret == '(') {
+ n = selector(form);
+ ret = lex(form, &lval);
+ if (ret != ')') {
+ ulogd_log(ULOGD_ERROR,
+ "form char: %d, no right parenthesis\n",
+ form->cur - form->formstr);
+ return NULL;
+ }
+ } else {
+ unput(form);
+ n = concat(form);
+ }
+
+ return n;
+}
+
+static int parse_form(struct outform *form)
+{
+ struct node *n;
+
+ while (*form->cur) {
+ n = part(form);
+ if (n == NULL)
+ return -1;
+ llist_add_tail(&n->list, &form->form_head);
+ }
+
+ return 0;
+}
+
+static int init_outform(struct outform *form, char *s)
+{
+ struct keysym *oob_family = calloc(sizeof(struct keysym), 1);
+
+ if (oob_family == NULL)
+ return -1;
+
+ form->formstr = form->cur = form->prev = s;
+ INIT_LLIST_HEAD(&form->form_head);
+
+ INIT_LLIST_HEAD(&form->keysyms);
+ /* for ULOGD_RET_IPADDR in sprint_key_puts() */
+ oob_family->name = strdup("oob.family");
+ if (oob_family->name == NULL)
+ return -1;
+ llist_add_tail(&oob_family->list, &form->keysyms);
+ form->num_keys = 1;
+
+ return 0;
+}
+
+static int open_connect_descriptor(struct ulogd_pluginstance *upi)
+{
+ char *proto, *host, *port;
+ struct addrinfo hint, *result, *rp;
+ int ret, fd;
+
+ proto = upi->config_kset->ces[SPRINT_CONF_DEST].u.string;
+ host = strchr(proto, ':');
+ if (host == NULL) {
+ ulogd_log(ULOGD_ERROR, "invalid dest\n");
+ return -1;
+ }
+ *host++ = '\0';
+ if (*host++ != '/') {
+ ulogd_log(ULOGD_ERROR, "invalid dest\n");
+ return -1;
+ }
+ if (*host++ != '/') {
+ ulogd_log(ULOGD_ERROR, "invalid dest\n");
+ return -1;
+ }
+
+ /* file */
+ if (!strcasecmp(proto, "file")) {
+ if (strlen(host) == 0)
+ return STDOUT_FILENO;
+ return open(host, O_CREAT|O_WRONLY|O_APPEND);
+ }
+
+ /* socket */
+ port = strrchr(host, ':');
+ if (port == NULL) {
+ ulogd_log(ULOGD_ERROR, "no port in dest\n");
+ return -1;
+ }
+ *port++ = '\0';
+
+ memset(&hint, 0, sizeof(struct addrinfo));
+ hint.ai_family = AF_UNSPEC;
+ if (!strcasecmp(proto, "udp")) {
+ hint.ai_socktype = SOCK_DGRAM;
+ hint.ai_protocol = IPPROTO_UDP;
+ } else if (!strcasecmp(proto, "tcp")) {
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+ } else {
+ ulogd_log(ULOGD_ERROR, "unknown protocol `%s'\n",
+ proto);
+ return -1;
+ }
+
+ ret = getaddrinfo(host, port, &hint, &result);
+ if (ret != 0) {
+ ulogd_log(ULOGD_ERROR, "can't resolve host/service: %s\n",
+ gai_strerror(ret));
+ if (ret != EAI_SYSTEM)
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ int on = 1;
+
+ fd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (fd == -1)
+ continue;
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&on, sizeof(on));
+ if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+ }
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ ulogd_log(ULOGD_ERROR, "could not connect\n");
+ return -1;
+ }
+
+ return fd;
+}
+
+static double sprint_key_calc(struct ulogd_key *keys, struct node *node,
+ bool *is_valid)
+{
+ *is_valid = false;
+ if (node->type == NODE_KEY) {
+ struct ulogd_key *key = keys[node->kindex].u.source;
+ if (!(key->flags & ULOGD_RETF_VALID))
+ return 0.0;
+
+ switch (key->type) {
+ case ULOGD_RET_BOOL:
+ case ULOGD_RET_INT8:
+ case ULOGD_RET_INT16:
+ case ULOGD_RET_INT32:
+ *is_valid = true;
+ return (double)key->u.value.i32;
+ break;
+ case ULOGD_RET_UINT8:
+ case ULOGD_RET_UINT16:
+ case ULOGD_RET_UINT32:
+ case ULOGD_RET_UINT64:
+ *is_valid = true;
+ return (double)key->u.value.ui64;
+ break;
+ default:
+ ulogd_log(ULOGD_INFO, "could not calc"
+ " key: %s type: %d\n", key->name, key->type);
+ }
+ } else if (node->type == NODE_KEYCALC) {
+ bool lvalid, rvalid;
+ double lval = sprint_key_calc(keys, node->keycalc.l, &lvalid),
+ rval = sprint_key_calc(keys, node->keycalc.r, &rvalid);
+
+ if (!lvalid || !rvalid)
+ return 0.0; /* without setting is_valid */
+
+ switch (node->keycalc.opcode) {
+ case '+':
+ *is_valid = true;
+ return lval + rval;
+ break;
+ default:
+ ulogd_log(ULOGD_NOTICE, "unknown opcode: %c\n",
+ node->keycalc.opcode);
+ break;
+ }
+ } else {
+ ulogd_log(ULOGD_NOTICE, "invalid node type in keycalc: %d\n",
+ node->type);
+ }
+
+ return 0.0; /* without setting is_valid */
+}
+
+static int sprint_keycalc_puts(char *buf, size_t size, bool in_group,
+ struct ulogd_key *keys, struct node *node)
+{
+ bool is_valid;
+ double ret = sprint_key_calc(keys, node, &is_valid);
+
+ if (!is_valid && in_group)
+ return 0;
+
+ return snprintf(buf, size, "%.0f", ret);
+}
+
+static int sprint_key_puts(char *buf, size_t size, bool in_group,
+ struct ulogd_key *keys, struct node *node,
+ int addrsep)
+{
+ struct ulogd_key *key = keys[node->kindex].u.source;
+ char family, *p;
+ int i;
+
+ if (!(key->flags & ULOGD_RETF_VALID)) {
+ if (!in_group) {
+ ulogd_log(ULOGD_INFO, "no key value: %s\n", key->name);
+ return printf("<>");
+ }
+ return 0;
+ }
+
+ switch (key->type) {
+ case ULOGD_RET_STRING:
+ return snprintf(buf, size, "%s", (char *)key->u.value.ptr);
+ break;
+ case ULOGD_RET_BOOL:
+ case ULOGD_RET_INT8:
+ case ULOGD_RET_INT16:
+ case ULOGD_RET_INT32:
+ return snprintf(buf, size, "%d", key->u.value.i32);
+ break;
+ case ULOGD_RET_UINT8:
+ case ULOGD_RET_UINT16:
+ case ULOGD_RET_UINT32:
+ case ULOGD_RET_UINT64:
+ return snprintf(buf, size, "%" PRIu64, key->u.value.ui64);
+ break;
+ case ULOGD_RET_IPADDR:
+ family = ikey_get_u8(keys);
+ if (family == AF_INET6) {
+ inet_ntop(AF_INET6, ikey_get_u128(&keys[node->kindex]),
+ buf, size);
+ i = ':';
+ } else if (family == AF_INET) {
+ u_int32_t ip = ikey_get_u32(&keys[node->kindex]);
+ inet_ntop(AF_INET, &ip, buf, size);
+ i = '.';
+ } else {
+ ulogd_log(ULOGD_ERROR,
+ "unknown address family: %d\n", family);
+ return 0;
+ }
+ if (addrsep)
+ for (p = strchr(buf, i); p; p = strchr(p + 1, i))
+ *p = addrsep;
+ for (i = 0, p = buf; *p != '\0'; p++, i++)
+ ;
+ return i;
+ default:
+ ulogd_log(ULOGD_INFO, "could not interpret"
+ " key: %s, type: %d\n", key->name, key->type);
+ break;
+ }
+ return 0; /* default */
+}
+
+static int sprint_term_puts(char *buf, size_t size, bool in_group,
+ struct ulogd_key *keys, struct node *node,
+ int addrsep)
+{
+ struct node *n;
+ int ret;
+ size_t len = 0;
+
+ switch (node->type) {
+ case NODE_KEY:
+ return sprint_key_puts(buf, size, in_group, keys, node,
+ addrsep);
+ break;
+ case NODE_STRING:
+ return snprintf(buf, size, "%s", node->string);
+ break;
+ case NODE_KEYCALC:
+ return sprint_keycalc_puts(buf, size, in_group, keys, node);
+ break;
+ case NODE_CONCAT:
+ llist_for_each_entry(n, &node->group, list) {
+ ret = sprint_term_puts(buf + len, size - len,
+ in_group, keys, n, addrsep);
+ if ((n->type == NODE_KEY || n->type == NODE_KEYCALC)
+ && ret <= 0) {
+ /* no key value found in a group */
+ return 0;
+ }
+ len += ret;
+ if (len >= size) {
+ ulogd_log(ULOGD_NOTICE, "exceeds bufsize\n");
+ return len;
+ }
+ }
+ return len;
+ break;
+ default:
+ ulogd_log(ULOGD_NOTICE, "unknown node type: %d\n",
+ node->type);
+ break;
+ }
+
+ return 0; /* unknown node type */
+}
+
+static int sprint_group_puts(char *buf, size_t size, struct ulogd_key *keys,
+ struct node *node, int addrsep)
+{
+ int ret;
+ struct node *n;
+
+ llist_for_each_entry(n, &node->group, list) {
+ ret = sprint_term_puts(buf, size, true, keys, n, addrsep);
+ if (ret > 0) /* put first valid value and return */
+ return ret;
+ }
+
+ ulogd_log(ULOGD_NOTICE, "no value found in group\n");
+ return snprintf(buf, size, "()");
+}
+
+static int sprint_interp(struct ulogd_pluginstance *upi)
+{
+ struct sprint_priv *sp = (struct sprint_priv *)&upi->private;
+ struct node *cur;
+ char buf[4096];
+ int rem = sizeof(buf) - 1, len = 0, ret;
+ int addrsep = *upi->config_kset->ces[SPRINT_CONF_ADDRSEP].u.string;
+
+ llist_for_each_entry(cur, &sp->form_head, list) {
+ switch (cur->type) {
+ case NODE_KEY:
+ case NODE_STRING:
+ case NODE_CONCAT:
+ case NODE_KEYCALC:
+ len += sprint_term_puts(buf + len, rem, false,
+ upi->input.keys, cur, addrsep);
+ break;
+ case NODE_GROUP:
+ len += sprint_group_puts(buf + len, rem,
+ upi->input.keys, cur, addrsep);
+ break;
+ default:
+ ulogd_log(ULOGD_NOTICE, "unknown node type: %d\n",
+ cur->type);
+ }
+ rem -= len;
+ if (rem <= 0) {
+ ulogd_log(ULOGD_NOTICE,
+ "sprint_term_puts exceeds bufsize\n");
+ len = sizeof(buf);
+ break;
+ }
+ }
+
+ ret = write(sp->ofd, buf, len);
+ if (ret != len) {
+ buf[len] = '\0';
+ ulogd_log(ULOGD_ERROR, "Failure sending message: %s\n", buf);
+ if (ret == -1) {
+ sp->ofd = open_connect_descriptor(upi);
+ if (sp->ofd == -1)
+ return ULOGD_IRET_ERR;
+ }
+ }
+ return ULOGD_IRET_OK;
+}
+
+static void sighup_handler_print(struct ulogd_pluginstance *upi, int signal)
+{
+ struct sprint_priv *sp = (struct sprint_priv *)&upi->private;
+ int old = sp->ofd;
+
+ switch (signal) {
+ case SIGHUP:
+ ulogd_log(ULOGD_NOTICE, "SPRINT: reopening logfile\n");
+ sp->ofd = open_connect_descriptor(upi);
+ if (sp->ofd == -1) {
+ ulogd_log(ULOGD_ERROR, "can't open SPRINT "
+ "log file: %s\n",
+ strerror(errno));
+ sp->ofd = old;
+ } else {
+ close(old);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int sprint_configure_form(struct ulogd_pluginstance *upi)
+{
+ struct sprint_priv *priv = (struct sprint_priv *)&upi->private;
+ struct keysym *sym, *nsym;
+ struct ulogd_key *ikey;
+ int ret;
+ struct outform form;
+
+ if (init_outform(&form,
+ upi->config_kset->ces[SPRINT_CONF_FORM].u.string)) {
+ ulogd_log(ULOGD_FATAL, "could not init form data\n");
+ return ULOGD_IRET_ERR;
+ }
+
+ ret = parse_form(&form);
+ if (ret == -1) {
+ /* parser error, already logged */
+ sprint_free_nodes(&form.form_head);
+ sprint_free_keysyms(&form.keysyms);
+ return ULOGD_IRET_ERR;
+ }
+
+ llist_add(&priv->form_head, &form.form_head);
+ llist_del(&form.form_head);
+
+ ulogd_log(ULOGD_DEBUG, "allocating %u input keys for SPRINT\n",
+ form.num_keys);
+ upi->input.keys = ikey = calloc(sizeof(struct ulogd_key),
+ form.num_keys);
+ if (!upi->input.keys)
+ return -ENOMEM;
+
+ /* create input keys from key symbol list created by form parsing */
+ llist_for_each_entry_safe(sym, nsym, &form.keysyms, list) {
+ ikey->flags = ULOGD_RETF_NONE;
+ strncpy(ikey->name, sym->name, strlen(sym->name));
+ free(sym->name);
+ free(sym);
+ ikey++;
+ }
+ upi->input.num_keys = form.num_keys;
+
+ return ret;
+}
+
+static int sprint_configure(struct ulogd_pluginstance *upi,
+ struct ulogd_pluginstance_stack *stack)
+{
+ int ret;
+
+ ret = config_parse_file(upi->id, upi->config_kset);
+ if (ret < 0)
+ return ret;
+
+ ret = sprint_configure_form(upi);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int sprint_init(struct ulogd_pluginstance *upi)
+{
+ struct sprint_priv *sp = (struct sprint_priv *) &upi->private;
+
+ sp->ofd = open_connect_descriptor(upi);
+ if (sp->ofd < 0) {
+ ulogd_log(ULOGD_FATAL, "can't open SPRINT destination: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sprint_fini(struct ulogd_pluginstance *pi)
+{
+ struct sprint_priv *sp = (struct sprint_priv *) &pi->private;
+
+ if (sp->ofd != STDOUT_FILENO)
+ close(sp->ofd);
+
+ /* sprint_priv->form_head is initialized at configure pharse.
+ * Should it be released here? by:
+ * sprint_free_nodes(&sp->form_head);
+ */
+
+ return 0;
+}
+
+static struct ulogd_plugin sprint_plugin = {
+ .name = "SPRINT",
+ .input = {
+ .type = ULOGD_DTYPE_PACKET | ULOGD_DTYPE_FLOW | ULOGD_DTYPE_SUM,
+ },
+ .output = {
+ .type = ULOGD_DTYPE_SINK,
+ },
+ .configure = &sprint_configure,
+ .interp = &sprint_interp,
+ .start = &sprint_init,
+ .stop = &sprint_fini,
+ .signal = &sighup_handler_print,
+ .config_kset = &sprint_kset,
+ .version = VERSION,
+};
+
+void __attribute__ ((constructor)) init(void);
+
+void init(void)
+{
+ ulogd_register_plugin(&sprint_plugin);
+}
--
1.9.1
prev parent reply other threads:[~2014-04-22 11:51 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-03-29 4:23 [ulogd RFC PATCH 0/2] introduce new string output plugin Ken-ichirou MATSUZAWA
2014-03-29 4:27 ` [ulogd RFC PATCH 1/2] sprint: introduce new " Ken-ichirou MATSUZAWA
2014-03-31 21:06 ` Eric Leblond
2014-03-29 4:29 ` [ulogd RFC PATCH 2/2] ip2str: introduce changable address separator Ken-ichirou MATSUZAWA
2014-03-31 20:17 ` Eric Leblond
2014-03-31 20:51 ` [ulogd RFC PATCH 0/2] introduce new string output plugin Eric Leblond
2014-04-02 10:14 ` Ken-ichirou MATSUZAWA
2014-04-21 9:05 ` Eric Leblond
2014-04-22 11:51 ` Ken-ichirou MATSUZAWA [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140422115116.GA29347@gmail.com \
--to=chamaken@gmail.com \
--cc=eric@regit.org \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).