From 8464a08c1a773d084adc1ff522726ba2ab653b8c Mon Sep 17 00:00:00 2001 From: laforge Date: Wed, 9 Feb 2005 15:01:25 +0000 Subject: add SQLITE3 support (ported from ulogd-1.02 patch by Ben La Monica) --- ulogd/Makefile.in | 5 + ulogd/Rules.make.in | 6 + ulogd/configure.in | 73 +++++++- ulogd/doc/sqlite3.table | 22 +++ ulogd/sqlite3/Makefile.in | 29 +++ ulogd/sqlite3/ulogd_SQLITE3.c | 406 ++++++++++++++++++++++++++++++++++++++++++ ulogd/ulogd.c | 3 +- ulogd/ulogd.conf.in | 8 +- 8 files changed, 549 insertions(+), 3 deletions(-) create mode 100644 ulogd/doc/sqlite3.table create mode 100644 ulogd/sqlite3/Makefile.in create mode 100644 ulogd/sqlite3/ulogd_SQLITE3.c (limited to 'ulogd') diff --git a/ulogd/Makefile.in b/ulogd/Makefile.in index 62c8015..87f8922 100644 --- a/ulogd/Makefile.in +++ b/ulogd/Makefile.in @@ -24,6 +24,11 @@ endif ULOGD_VERSION=1.10 OLD_ULOGD_VERSION=1.09 +ifeq (x@SQLITE3INCLUDES@,x) +else +SUBDIRS+=sqlite3 +endif + # Normally You should not need to change anything below all: recurse ulogd diff --git a/ulogd/Rules.make.in b/ulogd/Rules.make.in index 21b05d9..cab6b7d 100644 --- a/ulogd/Rules.make.in +++ b/ulogd/Rules.make.in @@ -41,3 +41,9 @@ MYSQL_LDFLAGS=@DATABASE_LIB_DIR@ @MYSQL_LIB@ #ULOGD_SL+=PGSQL PGSQL_CFLAGS=-I@PGSQLINCLUDES@ @EXTRA_PGSQL_DEF@ PGSQL_LDFLAGS=@DATABASE_LIB_DIR@ @PGSQL_LIB@ + +# mysql output support +#ULOGD_SL+=SQLITE3 +SQLITE3_CFLAGS=-I@SQLITE3INCLUDES@ @EXTRA_SQLITE3_DEF@ +SQLITE3_LDFLAGS=@DATABASE_LIB_DIR@ @SQLITE3_LIB@ + diff --git a/ulogd/configure.in b/ulogd/configure.in index 1b94de7..acf7628 100644 --- a/ulogd/configure.in +++ b/ulogd/configure.in @@ -195,6 +195,75 @@ AC_ARG_WITH(pgsql-log-ip-as-string, ]) +dnl +dnl test for sqlite3 +dnl +AC_ARG_WITH(sqlite3, + --with-sqlite3= sqlite3 installed in ,[ +if test $withval != yes +then + dir=$withval +else + dir="/usr/local" +fi +mysqldir="" +AC_MSG_CHECKING(for sqlite3 files) +for d in $dir /usr /usr/local /usr/local/sqlite3 +do + if test -f $d/lib/sqlite3/libsqlite3.so + then + AC_MSG_RESULT(found sqlite3 in $d) + sqlite3dir=$d + sqlite3dir_suffix=/sqlite3 + break + elif test -f $d/lib/libsqlite3.so + then + AC_MSG_RESULT(found sqlite in $d) + sqlite3dir=$d + sqlite3dir_suffix= + break + fi +done + +if test x$sqlite3dir = x +then + AC_MSG_WARN(sqlite3 backend not used) +else + AC_DEFINE(HAVE_SQLITE3) + SQLITE3INCLUDES=${sqlite3dir}/include${sqlite3dir_suffix} + SQLITE3LIBS=${sqlite3dir}/lib${sqlite3dir_suffix} + + DATABASE_DIR="${DATABASE_DIR} sqlite3" + + SQLITE3_LIB="${DATABASE_LIB} -lsqlite3 " + DATABASE_LIB_DIR="${DATABASE_LIB_DIR} -L${SQLITE3LIBS} " + +dnl DATABASE_DRIVERS="${DATABASE_DRIVERS} ../sqlite3/mysql_driver.o " + + DB_DEF="${DB_DEF} -DHAVE_SQLITE3 " + + + AC_SUBST(SQLITE3INCLUDES) + AC_SUBST(SQLITE3_LIB) + +fi +]) + + +dnl +dnl Check whether the user wants log IP-addresses as strings rather +dnl than as unsigned long-integers to his sqlite3-database. Since this +dnl feature is only used in ulogd_SQLITE3.c, there are no checks in any +dnl way. +dnl + +AC_ARG_WITH(sqlite3-log-ip-as-string, + --with-sqlite3-log-ip-as-string log IPs as string rather than as + unsigned long-integer. +,[ + EXTRA_SQLITE3_DEF="-D${EXTRA_SQLITE3_DEF} -DIP_AS_STRING=1" + AC_MSG_WARN(the use of --with-sqlite3-log-ip-as-string is discouraged) +]) AC_SUBST(DATABASE_DIR) @@ -203,11 +272,13 @@ AC_SUBST(DATABASE_LIB_DIR) AC_SUBST(DB_DEF) AC_SUBST(EXTRA_MYSQL_DEF) AC_SUBST(EXTRA_PGSQL_DEF) +AC_SUBST(EXTRA_SQLITE3_DEF) AC_SUBST(DATABASE_DRIVERS) AC_SUBST(HAVE_PCAP_H) AM_CONDITIONAL(HAVE_MYSQL, test x$mysqldir != x) AM_CONDITIONAL(HAVE_PGSQL, test x$pgsqldir != x) +AM_CONDITIONAL(HAVE_SQLITE3, test x$sqlite3dir != x) -AC_OUTPUT(extensions/Makefile doc/Makefile conffile/Makefile libipulog/Makefile mysql/Makefile pgsql/Makefile pcap/Makefile Makefile Rules.make) +AC_OUTPUT(extensions/Makefile doc/Makefile conffile/Makefile libipulog/Makefile mysql/Makefile pgsql/Makefile sqlite3/Makefile pcap/Makefile Makefile Rules.make) diff --git a/ulogd/doc/sqlite3.table b/ulogd/doc/sqlite3.table new file mode 100644 index 0000000..7b5e99a --- /dev/null +++ b/ulogd/doc/sqlite3.table @@ -0,0 +1,22 @@ +CREATE TABLE ulog ( + raw_mac VARCHAR(80), + oob_time_sec INT UNSIGNED, + oob_time_usec INT UNSIGNED, + ip_saddr INT UNSIGNED, + ip_daddr INT UNSIGNED, + ip_protocol TINYINT UNSIGNED, + ip_totlen SMALLINT UNSIGNED, + tcp_sport SMALLINT UNSIGNED, + tcp_dport SMALLINT UNSIGNED, + udp_sport SMALLINT UNSIGNED, + udp_dport SMALLINT UNSIGNED, + udp_len SMALLINT UNSIGNED, + icmp_type TINYINT UNSIGNED, + icmp_code TINYINT UNSIGNED, + icmp_echoid SMALLINT UNSIGNED, + icmp_echoseq SMALLINT UNSIGNED, + icmp_gateway INT UNSIGNED, + icmp_fragmtu SMALLINT UNSIGNED + ); + + diff --git a/ulogd/sqlite3/Makefile.in b/ulogd/sqlite3/Makefile.in new file mode 100644 index 0000000..5c0e1f1 --- /dev/null +++ b/ulogd/sqlite3/Makefile.in @@ -0,0 +1,29 @@ +# +# Normally You should not need to change anything below +# +include @top_srcdir@/Rules.make + +CFLAGS+=-I@top_srcdir@ -I@top_srcdir@/libipulog/include -I@top_srcdir@/include +SH_CFLAGS:=$(CFLAGS) -fPIC + +SHARED_LIBS=ulogd_SQLITE3.so + +all: $(SHARED_LIBS) + +distrib: + +$(SHARED_LIBS): %.so: %_sh.o + $(LD) -shared $(SQLITE3_LDFLAGS) -o $@ $< -lc + +%_sh.o: %.c + $(CC) $(SQLITE3_CFLAGS) $(SH_CFLAGS) -o $@ -c $< + +clean: + $(RM) $(SHARED_LIBS) *.o + +distclean: + $(RM) Makefile + +install: all + $(INSTALL) -m 755 -d $(DESTDIR)$(ULOGD_LIB_PATH) + $(INSTALL) -m 755 *.so $(DESTDIR)$(ULOGD_LIB_PATH) diff --git a/ulogd/sqlite3/ulogd_SQLITE3.c b/ulogd/sqlite3/ulogd_SQLITE3.c new file mode 100644 index 0000000..8d78f56 --- /dev/null +++ b/ulogd/sqlite3/ulogd_SQLITE3.c @@ -0,0 +1,406 @@ +/* + * ulogd output plugin for logging to a SQLITE database + * + * (C) 2005 by Ben La Monica + * + * 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 + * + * This module has been adapted from the ulogd_MYSQL.c written by + * Harald Welte + * Alex Janssen + * + * You can see benchmarks and an explanation of the testing + * at http://www.pojo.us/ulogd/ + * + * 2005-02-09 Harald Welte : + * - port to ulogd-1.20 + */ + +#include +#include +#include +#include +#include + +#ifdef DEBUG_SQLITE3 +#define DEBUGP(x, args...) fprintf(stderr, x, ## args) +#else +#define DEBUGP(x, args...) +#endif + +struct _field { + char name[ULOGD_MAX_KEYLEN]; + unsigned int id; + struct _field *next; +}; + +/* the database handle we are using */ +static sqlite3 *dbh; + +/* a linked list of the fields the table has */ +static struct _field *fields; + +/* buffer for our insert statement */ +static char *stmt; + +/* pointer to the final prepared statement */ +static sqlite3_stmt *p_stmt; + +/* number of statements to buffer before we commit */ +static int buffer_size; + +/* number of statements currently in the buffer */ +static int buffer_ctr; + +/* our configuration directives */ +static config_entry_t db_ce = { + .key = "db", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_MANDATORY, +}; + +static config_entry_t table_ce = { + .next = &db_ce, + .key = "table", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_MANDATORY, +}; + +static config_entry_t buffer_ce = { + .next = &table_ce, + .key = "buffer", + .type = CONFIG_TYPE_INT, + .options = CONFIG_OPT_MANDATORY, +}; + +/* our main output function, called by ulogd */ +static int _sqlite3_output(ulog_iret_t *result) +{ + struct _field *f; + ulog_iret_t *res; + int col_counter; + char *ipaddr; + + col_counter = 0; + for (f = fields; f; f = f->next) { + res = keyh_getres(f->id); + + if (!res) { + ulogd_log(ULOGD_NOTICE, + "no result for %s ?!?\n", f->name); + } + + if (!res || !IS_VALID((*res))) { + /* no result, pass a null */ + col_counter++; + continue; + } + + switch (res->type) { + case ULOGD_RET_INT8: + sqlite3_bind_int(p_stmt,col_counter,res->value.i8); + break; + case ULOGD_RET_INT16: + sqlite3_bind_int(p_stmt,col_counter,res->value.i16); + break; + case ULOGD_RET_INT32: + sqlite3_bind_int(p_stmt,col_counter,res->value.i32); + break; + case ULOGD_RET_INT64: + sqlite3_bind_int64(p_stmt,col_counter,res->value.i64); + break; + case ULOGD_RET_UINT8: + sqlite3_bind_int(p_stmt,col_counter,res->value.ui8); + break; + case ULOGD_RET_UINT16: + sqlite3_bind_int(p_stmt,col_counter,res->value.ui8); + break; + case ULOGD_RET_IPADDR: +#ifdef IP_AS_STRING + ipaddr = inet_ntoa(ntohl(res->value.ui32)); + sqlite3_bind_text(p_stmt,col_counter,ipaddr,strlen(ipaddr),SQLITE_STATIC); + break; +#endif /* IP_AS_STRING */ + /* EVIL: fallthrough when logging IP as u_int32_t */ + case ULOGD_RET_UINT32: + sqlite3_bind_int(p_stmt,col_counter,res->value.ui32); + break; + case ULOGD_RET_UINT64: + sqlite3_bind_int64(p_stmt,col_counter,res->value.ui64); + break; + case ULOGD_RET_BOOL: + sqlite3_bind_int(p_stmt,col_counter,res->value.b); + break; + case ULOGD_RET_STRING: + sqlite3_bind_text(p_stmt,col_counter,res->value.ptr,strlen(res->value.ptr),SQLITE_STATIC); + break; + default: + ulogd_log(ULOGD_NOTICE, + "unknown type %d for %s\n", + res->type, res->key); + break; + } + + col_counter++; + } + + /* now we have created our statement, insert it */ + + if (sqlite3_step(p_stmt) == SQLITE_DONE) { + sqlite3_reset(p_stmt); + buffer_ctr++; + } else { + ulogd_log(ULOGD_ERROR, "sql error during insert: %s\n", + sqlite3_errmsg(dbh)); + return 1; + } + + /* commit all of the inserts to the database, ie flush buffer */ + if (buffer_ctr >= buffer_size) { + if (sqlite3_exec(dbh,"commit",NULL,NULL,NULL) != SQLITE_OK) + ulogd_log(ULOGD_ERROR,"unable to commit records to db."); + + if (sqlite3_exec(dbh,"begin deferred",NULL,NULL,NULL) != SQLITE_OK) + ulogd_log(ULOGD_ERROR,"unable to begin a new transaction."); + + buffer_ctr = 0; + DEBUGP("committing.\n"); + } + + return 0; +} + +#define _SQLITE3_INSERTTEMPL "insert into X (Y) values (Z)" + +/* create the static part of our insert statement */ +static int _sqlite3_createstmt(void) +{ + struct _field *f; + unsigned int size; + char buf[ULOGD_MAX_KEYLEN]; + char *underscore; + char *stmt_pos; + int col_count; + int i; + + if (stmt) { + ulogd_log(ULOGD_NOTICE, "createstmt called, but stmt" + " already existing\n"); + return 1; + } + + /* caclulate the size for the insert statement */ + size = strlen(_SQLITE3_INSERTTEMPL) + strlen(table_ce.u.string); + + DEBUGP("initial size: %u\n", size); + + col_count = 0; + for (f = fields; f; f = f->next) { + /* we need space for the key and a comma, and a ? */ + size += strlen(f->name) + 3; + DEBUGP("size is now %u since adding %s\n",size,f->name); + col_count++; + } + + DEBUGP("there were %d columns\n",col_count); + DEBUGP("after calc name length: %u\n",size); + + ulogd_log(ULOGD_DEBUG, "allocating %u bytes for statement\n", size); + + stmt = (char *) malloc(size); + + if (!stmt) { + ulogd_log(ULOGD_ERROR, "OOM!\n"); + return 1; + } + + sprintf(stmt, "insert into %s (", table_ce.u.string); + stmt_pos = stmt + strlen(stmt); + + for (f = fields; f; f = f->next) { + strncpy(buf, f->name, ULOGD_MAX_KEYLEN); + while ((underscore = strchr(buf, '.'))) + *underscore = '_'; + sprintf(stmt_pos, "%s,", buf); + stmt_pos = stmt + strlen(stmt); + } + + *(stmt_pos - 1) = ')'; + + sprintf(stmt_pos, " values ("); + stmt_pos = stmt + strlen(stmt); + + for (i = 0; i < col_count - 1; i++) { + sprintf(stmt_pos,"?,"); + stmt_pos += 2; + } + + sprintf(stmt_pos, "?)"); + ulogd_log(ULOGD_DEBUG, "stmt='%s'\n", stmt); + + DEBUGP("about to prepare statement.\n"); + + sqlite3_prepare(dbh,stmt,-1,&p_stmt,0); + + DEBUGP("statement prepared.\n"); + + if (!p_stmt) { + ulogd_log(ULOGD_ERROR,"unable to prepare statement"); + return 1; + } + + return 0; +} + + +/* length of "select * from \0" */ +#define SQLITE_SELECT_LEN 15 + +/* find out which columns the table has */ +static int _sqlite3_get_columns(const char *table) +{ + char buf[ULOGD_MAX_KEYLEN]; + char query[SQLITE_SELECT_LEN + CONFIG_VAL_STRING_LEN] = "select * from \0"; + char *underscore; + struct _field *f; + sqlite3_stmt *schema_stmt; + int column; + int result; + int id; + + if (!dbh) + return 1; + + strncat(query,table,LINE_LEN); + + result = sqlite3_prepare(dbh,query,-1,&schema_stmt,0); + + if (result != SQLITE_OK) + return 1; + + for (column = 0; column < sqlite3_column_count(schema_stmt); column++) { + /* replace all underscores with dots */ + strncpy(buf, sqlite3_column_name(schema_stmt,column), ULOGD_MAX_KEYLEN); + while ((underscore = strchr(buf, '_'))) + *underscore = '.'; + + DEBUGP("field '%s' found: ", buf); + + if (!(id = keyh_getid(buf))) { + DEBUGP(" no keyid!\n"); + continue; + } + + DEBUGP("keyid %u\n", id); + + /* prepend it to the linked list */ + f = (struct _field *) malloc(sizeof *f); + if (!f) { + ulogd_log(ULOGD_ERROR, "OOM!\n"); + return 1; + } + strncpy(f->name, buf, ULOGD_MAX_KEYLEN); + f->id = id; + f->next = fields; + fields = f; + } + + sqlite3_finalize(schema_stmt); + return 0; +} + +/** + * make connection and select database + * returns 0 if database failed to open. + */ +static int _sqlite3_open_db(char *db_file) +{ + DEBUGP("opening database.\n"); + return sqlite3_open(db_file,&dbh); +} + +/* give us an opportunity to close the database down properly */ +static void _sqlite3_fini(void) +{ + DEBUGP("cleaning up db connection\n"); + + /* free up our prepared statements so we can close the db */ + if (p_stmt) { + sqlite3_finalize(p_stmt); + DEBUGP("prepared statement finalized\n"); + } + + if (dbh) { + /* flush the remaining insert statements to the database. */ + result = sqlite3_exec(dbh,"commit",NULL,NULL,NULL); + + if (result != SQLITE_OK) + ulogd_log(ULOGD_ERROR,"unable to commit remaining records to db."); + + sqlite3_close(dbh); + DEBUGP("database file closed\n"); + } +} + +static int _sqlite3_init(void) +{ + int result = 0; + + /* have the opts parsed */ + config_parse_file("SQLITE3", &buffer_ce); + + if (_sqlite3_open_db(db_ce.u.string)) { + ulogd_log(ULOGD_ERROR, "can't open the database file\n"); + return; + } + + /* set the timeout so that we don't automatically fail + * if the table is busy. */ + sqlite3_busy_timeout(dbh, _SQLITE3_BUSY_TIMEOUT); + + /* read the fieldnames to know which values to insert */ + if (_sqlite3_get_columns(table_ce.u.string)) { + ulogd_log(ULOGD_ERROR, "unable to get sqlite columns\n"); + return; + } + + /* initialize our buffer size and counter */ + buffer_size = buffer_ce.u.value; + buffer_ctr = 0; + + DEBUGP("Have a buffer size of : %d\n", buffer_size); + + if (sqlite3_exec(dbh,"begin deferred",NULL,NULL,NULL) != SQLITE_OK) + ulogd_log(ULOGD_ERROR,"can't create a new transaction\n"); + + /* create and prepare the actual insert statement */ + _sqlite3_createstmt(); + + register_output(&_sqlite3_plugin); +} + +static ulog_output_t _sqlite3_plugin = { + .name = "sqlite3", + .output = &_sqlite3_output, + .init = &_sqlite3_init, + .fini = &sqlite3_fini, +}; + +#define _SQLITE3_BUSY_TIMEOUT 300 + +void _init(void) +{ + register_output(&_sqlite3_plugin); +} + diff --git a/ulogd/ulogd.c b/ulogd/ulogd.c index 9367640..28ec6cc 100644 --- a/ulogd/ulogd.c +++ b/ulogd/ulogd.c @@ -757,7 +757,8 @@ int main(int argc, char* argv[]) setsid(); } - signal(SIGTERM, &sigterm_handler); + /* send SIGINT to the term handler, since they hit CTRL-C */ + signal(SIGINT, &sigterm_handler); signal(SIGHUP, &sighup_handler); ulogd_log(ULOGD_NOTICE, diff --git a/ulogd/ulogd.conf.in b/ulogd/ulogd.conf.in index b1bec43..6171267 100644 --- a/ulogd/ulogd.conf.in +++ b/ulogd/ulogd.conf.in @@ -1,5 +1,5 @@ # Example configuration for ulogd -# $Id: ulogd.conf.in,v 1.2 2003/05/04 10:00:10 laforge Exp $ +# $Id$ # [global] @@ -45,6 +45,7 @@ plugin="@libdir@/ulogd_LOGEMU.so" #plugin="@libdir@/ulogd_OPRINT.so" #plugin="@libdir@/ulogd_MYSQL.so" #plugin="@libdir@/ulogd_PGSQL.so" +#plugin="@libdir@/ulogd_SQLITE3.so" #plugin="@libdir@/ulogd_PCAP.so" @@ -69,6 +70,11 @@ user="postgres" db="ulogd" host="localhost" +[SQLITE3] +table="ulog" +db="/path/to/sqlite/db" +buffer=200 + [PCAP] file="/var/log/ulogd.pcap" sync=1 -- cgit v1.2.3