libdbi implements a database-independent abstraction layer in C, similar to the DBI/DBD layer in Perl. This module brings support for all database types supported by libdbi. Signed-off-by: Pierre Chifflier <chifflier@xxxxxx> --- configure.in | 1 + output/Makefile.am | 2 +- output/dbi/Makefile.am | 12 ++ output/dbi/ulogd_output_DBI.c | 313 +++++++++++++++++++++++++++++++++++++++++ ulogd.conf.in | 10 ++ 5 files changed, 337 insertions(+), 1 deletions(-) create mode 100644 output/dbi/Makefile.am create mode 100644 output/dbi/ulogd_output_DBI.c diff --git a/configure.in b/configure.in index 0e173a3..5e9a02b 100644 --- a/configure.in +++ b/configure.in @@ -75,4 +75,5 @@ AC_OUTPUT(include/Makefile include/ulogd/Makefile include/libipulog/Makefile \ input/Makefile input/packet/Makefile input/flow/Makefile \ filter/Makefile filter/raw2packet/Makefile filter/packet2flow/Makefile \ output/Makefile output/pcap/Makefile output/mysql/Makefile output/pgsql/Makefile output/sqlite3/Makefile \ + output/dbi/Makefile \ src/Makefile Makefile Rules.make) diff --git a/output/Makefile.am b/output/Makefile.am index 78df01d..69578a0 100644 --- a/output/Makefile.am +++ b/output/Makefile.am @@ -1,7 +1,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include LIBS="" -SUBDIRS= pcap mysql pgsql sqlite3 +SUBDIRS= pcap mysql pgsql sqlite3 dbi pkglib_LTLIBRARIES = ulogd_output_LOGEMU.la ulogd_output_SYSLOG.la \ ulogd_output_OPRINT.la ulogd_output_IPFIX.la \ diff --git a/output/dbi/Makefile.am b/output/dbi/Makefile.am new file mode 100644 index 0000000..d127587 --- /dev/null +++ b/output/dbi/Makefile.am @@ -0,0 +1,12 @@ + +INCLUDES = $(all_includes) -I$(top_srcdir)/include $(DBI_INC) +LIBS=$(DBI_LIB) + +if HAVE_DBI + +pkglib_LTLIBRARIES = ulogd_output_DBI.la + +ulogd_output_DBI_la_SOURCES = ulogd_output_DBI.c ../../util/db.c +ulogd_output_DBI_la_LDFLAGS = -module + +endif diff --git a/output/dbi/ulogd_output_DBI.c b/output/dbi/ulogd_output_DBI.c new file mode 100644 index 0000000..a758b7b --- /dev/null +++ b/output/dbi/ulogd_output_DBI.c @@ -0,0 +1,313 @@ +/* ulogd_DBI.c, Version $Revision$ + * + * ulogd output plugin for logging to a database using the DBI abstraction + * layer + * + * (C) 2000-2008 by Pierre Chifflier <chifflier@xxxxxx> + * This software is distributed under the terms of GNU GPL + * + * This plugin is based on the PostgreSQL plugin made by Harald Welte. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> + +#include <ulogd/ulogd.h> +#include <ulogd/conffile.h> +#include <ulogd/db.h> + +#include <dbi.h> + +#ifdef DEBUG_DBI +#define DEBUGP(x, args...) fprintf(stderr, x, ## args) +#else +#define DEBUGP(x, args...) +#endif + +struct dbi_instance { + struct db_instance db_inst; + + dbi_conn dbh; + dbi_result result; +}; +#define TIME_ERR ((time_t)-1) + +/* our configuration directives */ +static struct config_keyset dbi_kset = { + .num_ces = DB_CE_NUM + 7, + .ces = { + DB_CES, + { + .key = "db", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_MANDATORY, + }, + { + .key = "host", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + }, + { + .key = "user", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_MANDATORY, + }, + { + .key = "pass", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + }, + { + .key = "port", + .type = CONFIG_TYPE_INT, + .options = CONFIG_OPT_NONE, + }, + { + .key = "schema", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + .u.string = "public", + }, + { + .key = "dbtype", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_MANDATORY, + }, + }, +}; +#define db_ce(x) (x->ces[DB_CE_NUM+0]) +#define host_ce(x) (x->ces[DB_CE_NUM+1]) +#define user_ce(x) (x->ces[DB_CE_NUM+2]) +#define pass_ce(x) (x->ces[DB_CE_NUM+3]) +#define port_ce(x) (x->ces[DB_CE_NUM+4]) +#define schema_ce(x) (x->ces[DB_CE_NUM+5]) +#define dbtype_ce(x) (x->ces[DB_CE_NUM+6]) + +/* find out which columns the table has */ +static int get_columns_dbi(struct ulogd_pluginstance *upi) +{ + struct dbi_instance *pi = (struct dbi_instance *) upi->private; + char query[256] = "SELECT * FROM ulog\0"; + unsigned int ui; + + if (!pi->dbh) { + ulogd_log(ULOGD_ERROR, "no database handle\n"); + return 1; + } + + ulogd_log(ULOGD_DEBUG, "%s\n", query); + pi->result = dbi_conn_query(pi->dbh,query); + if (!pi->result) { + const char *errptr; + dbi_conn_error(pi->dbh, &errptr); + ulogd_log(ULOGD_DEBUG, "Could not fetch columns (%s)", + errptr); + return -1; + } + + if (upi->input.keys) + free(upi->input.keys); + + upi->input.num_keys = dbi_result_get_numfields(pi->result); + ulogd_log(ULOGD_DEBUG, "%u fields in table\n", upi->input.num_keys); + + upi->input.keys = malloc(sizeof(struct ulogd_key) * + upi->input.num_keys); + if (!upi->input.keys) { + upi->input.num_keys = 0; + ulogd_log(ULOGD_ERROR, "ENOMEM\n"); + dbi_result_free(pi->result); + return -ENOMEM; + } + + memset(upi->input.keys, 0, sizeof(struct ulogd_key) * + upi->input.num_keys); + + for (ui=1; ui<=upi->input.num_keys; ui++) { + char buf[ULOGD_MAX_KEYLEN+1]; + char *underscore; + const char* field_name = dbi_result_get_field_name(pi->result, ui); + + if (!field_name) + break; + + /* replace all underscores with dots */ + strncpy(buf, field_name, ULOGD_MAX_KEYLEN); + while ((underscore = strchr(buf, '_'))) + *underscore = '.'; + + DEBUGP("field '%s' found: ", buf); + + /* add it to list of input keys */ + strncpy(upi->input.keys[ui-1].name, buf, ULOGD_MAX_KEYLEN); + } + + /* ID is a sequence */ + upi->input.keys[0].flags |= ULOGD_KEYF_INACTIVE; + + dbi_result_free(pi->result); + + return 0; +} + +static int close_db_dbi(struct ulogd_pluginstance *upi) +{ + struct dbi_instance *pi = (struct dbi_instance *) upi->private; + + ulogd_log(ULOGD_DEBUG, "dbi: closing connection\n"); + dbi_conn_close(pi->dbh); + pi->dbh = NULL; + //dbi_shutdown(); + + return 0; +} + +/* make connection and select database */ +static int open_db_dbi(struct ulogd_pluginstance *upi) +{ + struct dbi_instance *pi = (struct dbi_instance *) upi->private; + char *server = host_ce(upi->config_kset).u.string; + char *user = user_ce(upi->config_kset).u.string; + char *pass = pass_ce(upi->config_kset).u.string; + char *db = db_ce(upi->config_kset).u.string; + char *dbtype = dbtype_ce(upi->config_kset).u.string; + dbi_driver driver; + int ret; + + if (pi->dbh != NULL) + return 0; + + ulogd_log(ULOGD_ERROR, "Opening connection for db type %s\n", + dbtype); + driver = dbi_driver_open(dbtype); + if (driver == NULL) { + ulogd_log(ULOGD_ERROR, "unable to load driver for db type %s\n", + dbtype); + close_db_dbi(upi); + return -1; + } + pi->dbh = dbi_conn_new(dbtype); + if (pi->dbh == NULL) { + ulogd_log(ULOGD_ERROR, "unable to initialize db type %s\n", + dbtype); + close_db_dbi(upi); + return -1; + } + + if (server) + dbi_conn_set_option(pi->dbh, "host", server); + if (user) + dbi_conn_set_option(pi->dbh, "username", user); + if (pass) + dbi_conn_set_option(pi->dbh, "password", pass); + if (db) + dbi_conn_set_option(pi->dbh, "dbname", db); + + ret = dbi_conn_connect(pi->dbh); + if (ret < 0) { + ulogd_log(ULOGD_ERROR, "unable to connect to db %s\n", + db); + close_db_dbi(upi); + return -1; + } + + return 0; +} + +static int escape_string_dbi(struct ulogd_pluginstance *upi, + char *dst, const char *src, unsigned int len) +{ + struct dbi_instance *pi = (struct dbi_instance *) upi->private; + char *newstr; + int ret; + + if (len == 0) { + *dst = '\0'; + return 0; + } + + ret = dbi_conn_quote_string_copy(pi->dbh, src, &newstr); + if (ret <= 2) + return 0; + + /* dbi_conn_quote_string_copy returns a quoted string, + * but __interp_db already quotes the string + * So we return a string without the quotes + */ + strncpy(dst,newstr+1,ret-2); + dst[ret-2] = '\0'; + free(newstr); + + return (ret-2); +} + +static int execute_dbi(struct ulogd_pluginstance *upi, + const char *stmt, unsigned int len) +{ + struct dbi_instance *pi = (struct dbi_instance *) upi->private; + + pi->result = dbi_conn_query(pi->dbh,stmt); + if (!pi->result) { + const char *errptr; + dbi_conn_error(pi->dbh, &errptr); + ulogd_log(ULOGD_DEBUG, "execute failed (%s)\n", + errptr); + ulogd_log(ULOGD_DEBUG, "failed query: [%s]\n", + stmt); + return -1; + } + + dbi_result_free(pi->result); + + return 0; +} + +static struct db_driver db_driver_dbi = { + .get_columns = &get_columns_dbi, + .open_db = &open_db_dbi, + .close_db = &close_db_dbi, + .escape_string = &escape_string_dbi, + .execute = &execute_dbi, +}; + +static int configure_dbi(struct ulogd_pluginstance *upi, + struct ulogd_pluginstance_stack *stack) +{ + struct dbi_instance *pi = (struct dbi_instance *) upi->private; + + pi->db_inst.driver = &db_driver_dbi; + + return ulogd_db_configure(upi, stack); +} + +static struct ulogd_plugin dbi_plugin = { + .name = "DBI", + .input = { + .keys = NULL, + .num_keys = 0, + .type = ULOGD_DTYPE_PACKET | ULOGD_DTYPE_FLOW, + }, + .output = { + .type = ULOGD_DTYPE_SINK, + }, + .config_kset = &dbi_kset, + .priv_size = sizeof(struct dbi_instance), + .configure = &configure_dbi, + .start = &ulogd_db_start, + .stop = &ulogd_db_stop, + .signal = &ulogd_db_signal, + .interp = &ulogd_db_interp, + .version = ULOGD_VERSION, +}; + +void __attribute__ ((constructor)) init(void); + +void init(void) +{ + dbi_initialize(NULL); + + ulogd_register_plugin(&dbi_plugin); +} diff --git a/ulogd.conf.in b/ulogd.conf.in index 93662a8..e24e6b6 100644 --- a/ulogd.conf.in +++ b/ulogd.conf.in @@ -42,6 +42,7 @@ plugin="@libdir@/ulogd/ulogd_output_SYSLOG.so" #plugin="@libdir@/ulogd/ulogd_output_PCAP.so" #plugin="@libdir@/ulogd/ulogd_output_PGSQL.so" #plugin="@libdir@/ulogd/ulogd_output_MYSQL.so" +#plugin="@libdir@/ulogd/ulogd_output_DBI.so" plugin="@libdir@/ulogd/ulogd_raw2packet_BASE.so" # this is a stack for IPv4 packet-based logging via LOGEMU @@ -173,6 +174,15 @@ table="ulog2_ct" pass="changeme" procedure="INSERT_OR_REPLACE_CT" +[dbi1] +db="ulog2" +dbtype="pgsql" +host="localhost" +user="ulog2" +table="ulog" +pass="ulog2" +procedure="INSERT_PACKET_FULL" + [sys2] facility=LOG_LOCAL2 -- 1.5.6.5 -- 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