Hello, On Wed, 2014-04-02 at 19:14 +0900, Ken-ichirou MATSUZAWA wrote: > Hello Eric. > > Thank you for taking your time. > > On Mon, Mar 31, 2014 at 10:51:19PM +0200, Eric Leblond wrote: > > I'm a bit reluctant to use flex and bison for that. How complicated > > would it be to use a hand-made parsing here ? I go for that one. The patch size is similar and I don't think we will have a lot of evolution on the format. Can you rebase and resubmit with a clean message ? BR, -- Eric > Thanks to you, I've got an interesting work and could find odd syntax. > Here is a hand-made, which one would you like? It includes configurations > about URI like destination and address separator changing. > > 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@xxxxxxxxxxxxx> > --- > output/Makefile.am | 5 +- > output/ulogd_output_SPRINT.c | 1049 ++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 1053 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..decea49 > --- /dev/null > +++ b/output/ulogd_output_SPRINT.c > @@ -0,0 +1,1049 @@ > +/* ulogd_output_SPRINT.c > + * > + * ulogd output target for sending value specified `form' in config. > + * > + * (C) 2014 by Ken-ichirou MATSUZAWA <chamas@xxxxxxxxxxxxx> > + * > + * 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); > + 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 void init_outform(struct outform *form, char *s) > +{ > + form->formstr = form->cur = form->prev = s; > + INIT_LLIST_HEAD(&form->form_head); > + form->num_keys = 1; /* 0 is reserved for oob.family */ > + INIT_LLIST_HEAD(&form->keysyms); > +} > + > +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; > + > + init_outform(&form, > + upi->config_kset->ces[SPRINT_CONF_FORM].u.string); > + > + 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; > + > + /* reserve for putting ULOGD_RET_IPADDR */ > + ikey->type = ULOGD_RET_UINT8; > + ikey->flags = ULOGD_RETF_NONE; > + strcpy(ikey->name, "oob.family"); > + ikey++; > + > + /* 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); > + > + 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); > +} -- Eric Leblond <eric@xxxxxxxxx> -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html