From 0ddd719a72469f732a881c93d4c804e9aca787fe Mon Sep 17 00:00:00 2001 From: Edward Rudd Date: Wed, 22 Oct 2008 12:40:58 +0000 Subject: added more config options included PCRE wrapper from httpd more complete log parser code. fixed NASTY bug with setting values in the hash tables (Need to DUP the strings before setting the keys) --- diff --git a/Makefile.in b/Makefile.in index 7d71505..4c60869 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ # @configure_input@ # Modify these top variables. -SUBDIRS = docs contrib src parser utility +SUBDIRS = docs contrib src utility EXTRA_DIST = AUTHORS INSTALL TODO LICENSE CHANGELOG \ build-apache13.bat build-apache2.bat \ diff --git a/configure.ac b/configure.ac index 43633e3..e706103 100644 --- a/configure.ac +++ b/configure.ac @@ -11,6 +11,8 @@ dnl Add a test for a compiler. AC_PROG_CC AC_PROG_LIBTOOL +PKG_CHECK_MODULES(PCRE,libpcre) + APACHE20_VERSION=2.0.40 APACHE13_VERSION=1.3.20 CHECK_APACHE($APACHE13_VERSION,$APACHE20_VERSION, diff --git a/utility/Makefile.in b/utility/Makefile.in index d397b96..69a746c 100644 --- a/utility/Makefile.in +++ b/utility/Makefile.in @@ -4,9 +4,11 @@ top_srcdir = @top_srcdir@ srcdir = @abs_srcdir@ builddir = @abs_builddir@ -CFLAGS = -g -Wall -fno-strict-aliasing @APR_CFLAGS@ @APR_INCLUDES@ @APU_INCLUDES@ +#@APR_CFLAGS@ +CFLAGS = -g3 -Wall -fno-strict-aliasing \ + @APR_INCLUDES@ @APU_INCLUDES@ @PCRE_CFLAGS@ CPPFLAGS = @APR_CPPFLAGS@ -LDFLAGS = @APR_LDFLAGS@ @APU_LDFLAGS@ +LDFLAGS = @APR_LDFLAGS@ @APU_LDFLAGS@ @PCRE_LIBS@ ifeq (@OOO_MAINTAIN@,1) CFLAGS += -Werror @@ -16,10 +18,10 @@ STD_DIST = Makefile.in DISTFILES = $(STD_DIST) $(EXTRA_DIST) $(SOURCES) $(HEADERS) -SOURCES = shell.c config.c logparse.c -HEADERS = shell.h config.h logparse.h -OBJECTS = $(patsubst %.c,%.o,$(SOURCES)) - +SOURCES = shell.c config.c logparse.c ap_pcre.c util.c +HEADERS = shell.h config.h logparse.h ap_pcre.h util.h +OBJECTS = $(SOURCES:.c=.o) +DEPS = $(SOURCES:.c=.d) TARGETS = mod_log_sql all: $(TARGETS) @@ -27,8 +29,15 @@ all: $(TARGETS) mod_log_sql: $(OBJECTS) $(HEADERS) $(CC) -o $@ $(OBJECTS) $(LDFLAGS) +%.o: %.c %.d + gcc -c $(CFLAGS) $(CPPFLAGS) $< -o $@ +%.d: %.c + gcc -MM $(CFLAGS) $(CPPFLAGS) $< -o $@ + +-include $(DEPS) + clean: - $(RM) $(OBJECTS) $(TARGETS) + $(RM) $(OBJECTS) $(TARGETS) $(DEPS) local-dist: $(DISTFILES) mkdir -p $(DESTDIR) diff --git a/utility/ap_pcre.c b/utility/ap_pcre.c new file mode 100644 index 0000000..b2b9767 --- /dev/null +++ b/utility/ap_pcre.c @@ -0,0 +1,344 @@ +/************************************************* + * Perl-Compatible Regular Expressions * + *************************************************/ + +/* + This is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. See + the file Tech.Notes for some information on the internals. + + This module is a wrapper that provides a POSIX API to the underlying PCRE + functions. + + Written by: Philip Hazel + + Copyright (c) 1997-2004 University of Cambridge + + ----------------------------------------------------------------------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ----------------------------------------------------------------------------- + */ + +#include "apr_lib.h" +#include "apr_strings.h" +#include "ap_pcre.h" +#include "pcre.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#ifndef POSIX_MALLOC_THRESHOLD +#define POSIX_MALLOC_THRESHOLD (10) +#endif + +/* Table of error strings corresponding to POSIX error codes; must be + * kept in synch with include/ap_regex.h's AP_REG_E* definitions. */ + +static const char *const pstring[] = { + "", /* Dummy for value 0 */ + "internal error", /* AP_REG_ASSERT */ + "failed to get memory", /* AP_REG_ESPACE */ + "bad argument", /* AP_REG_INVARG */ + "match failed" /* AP_REG_NOMATCH */ +}; + +apr_size_t ap_regerror(int errcode, const ap_regex_t *preg, char *errbuf, + apr_size_t errbuf_size) +{ + const char *message, *addmessage; + apr_size_t length, addlength; + + message + = (errcode >= (int)(sizeof(pstring)/sizeof(char *))) ? "unknown error code" + : pstring[errcode]; + length = strlen(message) + 1; + + addmessage = " at offset "; + addlength + = (preg != NULL && (int)preg->re_erroffset != -1) ? strlen(addmessage) + + 6 + : 0; + + if (errbuf_size > 0) { + if (addlength > 0 && errbuf_size >= length + addlength) + apr_snprintf(errbuf, sizeof errbuf, "%s%s%-6d", message, + addmessage, (int)preg->re_erroffset); + else { + strncpy(errbuf, message, errbuf_size - 1); + errbuf[errbuf_size-1] = 0; + } + } + + return length + addlength; +} + +/************************************************* + * Free store held by a regex * + *************************************************/ + +void ap_regfree(ap_regex_t *preg) +{ + (pcre_free)(preg->re_pcre); +} + +/************************************************* + * Compile a regular expression * + *************************************************/ + +/* + Arguments: + preg points to a structure for recording the compiled expression + pattern the pattern to compile + cflags compilation flags + + Returns: 0 on success + various non-zero codes on failure + */ + +int ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags) +{ + const char *errorptr; + int erroffset; + int options = 0; + + if ((cflags & AP_REG_ICASE) != 0) + options |= PCRE_CASELESS; + if ((cflags & AP_REG_NEWLINE) != 0) + options |= PCRE_MULTILINE; + + preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL); + preg->re_erroffset = erroffset; + + if (preg->re_pcre == NULL) + return AP_REG_INVARG; + + preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL); + return 0; +} + +/************************************************* + * Match a regular expression * + *************************************************/ + +/* Unfortunately, PCRE requires 3 ints of working space for each captured + substring, so we have to get and release working store instead of just using + the POSIX structures as was done in earlier releases when PCRE needed only 2 + ints. However, if the number of possible capturing brackets is small, use a + block of store on the stack, to reduce the use of malloc/free. The threshold is + in a macro that can be changed at configure time. */ + +int ap_regexec(const ap_regex_t *preg, const char *string, apr_size_t nmatch, + ap_regmatch_t pmatch[], int eflags) +{ + int rc; + int options = 0; + int *ovector= NULL; + int small_ovector[POSIX_MALLOC_THRESHOLD * 3]; + int allocated_ovector = 0; + + if ((eflags & AP_REG_NOTBOL) != 0) + options |= PCRE_NOTBOL; + if ((eflags & AP_REG_NOTEOL) != 0) + options |= PCRE_NOTEOL; + + ((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */ + + if (nmatch > 0) { + if (nmatch <= POSIX_MALLOC_THRESHOLD) { + ovector = &(small_ovector[0]); + } else { + ovector = (int *)malloc(sizeof(int) * nmatch * 3); + if (ovector == NULL) + return AP_REG_ESPACE; + allocated_ovector = 1; + } + } + + rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, + (int)strlen(string), 0, options, ovector, nmatch * 3); + + if (rc == 0) + rc = nmatch; /* All captured slots were filled in */ + + if (rc >= 0) { + apr_size_t i; + for (i = 0; i < (apr_size_t)rc; i++) { + pmatch[i].rm_so = ovector[i*2]; + pmatch[i].rm_eo = ovector[i*2+1]; + } + if (allocated_ovector) + free(ovector); + for (; i < nmatch; i++) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + return 0; + } + + else { + if (allocated_ovector) + free(ovector); + switch (rc) { + case PCRE_ERROR_NOMATCH: + return AP_REG_NOMATCH; + case PCRE_ERROR_NULL: + return AP_REG_INVARG; + case PCRE_ERROR_BADOPTION: + return AP_REG_INVARG; + case PCRE_ERROR_BADMAGIC: + return AP_REG_INVARG; + case PCRE_ERROR_UNKNOWN_NODE: + return AP_REG_ASSERT; + case PCRE_ERROR_NOMEMORY: + return AP_REG_ESPACE; +#ifdef PCRE_ERROR_MATCHLIMIT + case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE; +#endif +#ifdef PCRE_ERROR_BADUTF8 + case PCRE_ERROR_BADUTF8: return AP_REG_INVARG; +#endif +#ifdef PCRE_ERROR_BADUTF8_OFFSET + case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG; +#endif + default: + return AP_REG_ASSERT; + } + } +} + +/* + * Here's a pool-based interface to the POSIX-esque ap_regcomp(). + * Note that we return ap_regex_t instead of being passed one. + * The reason is that if you use an already-used ap_regex_t structure, + * the memory that you've already allocated gets forgotten, and + * regfree() doesn't clear it. So we don't allow it. + */ + +static apr_status_t regex_cleanup(void *preg) +{ + ap_regfree((ap_regex_t *) preg); + return APR_SUCCESS; +} + +ap_regex_t *ap_pregcomp(apr_pool_t *p, const char *pattern, int cflags) +{ + ap_regex_t *preg = apr_palloc(p, sizeof *preg); + + if (ap_regcomp(preg, pattern, cflags)) { + return NULL; + } + + apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, + apr_pool_cleanup_null); + + return preg; +} + +void ap_pregfree(apr_pool_t *p, ap_regex_t *reg) +{ + ap_regfree(reg); + apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup); +} + +/* This function substitutes for $0-$9, filling in regular expression + * submatches. Pass it the same nmatch and pmatch arguments that you + * passed ap_regexec(). pmatch should not be greater than the maximum number + * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t. + * + * input should be the string with the $-expressions, source should be the + * string that was matched against. + * + * It returns the substituted string, or NULL on error. + * + * Parts of this code are based on Henry Spencer's regsub(), from his + * AT&T V8 regexp package. + */ + +char * ap_pregsub(apr_pool_t *p, const char *input, const char *source, + size_t nmatch, ap_regmatch_t pmatch[]) +{ + const char *src = input; + char *dest, *dst; + char c; + size_t no; + int len; + + if (!source) + return NULL; + if (!nmatch) + return apr_pstrdup(p, src); + + /* First pass, find the size */ + + len = 0; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && apr_isdigit(*src)) + no = *src++ - '0'; + else + no = 10; + + if (no> 9) { /* Ordinary character. */ + if (c == '\\' && (*src == '$' || *src == '&')) + c = *src++; + len++; + } else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { + len += pmatch[no].rm_eo - pmatch[no].rm_so; + } + + } + + dest = dst = apr_pcalloc(p, len + 1); + + /* Now actually fill in the string */ + + src = input; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && apr_isdigit(*src)) + no = *src++ - '0'; + else + no = 10; + + if (no> 9) { /* Ordinary character. */ + if (c == '\\' && (*src == '$' || *src == '&')) + c = *src++; + *dst++ = c; + } else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { + len = pmatch[no].rm_eo - pmatch[no].rm_so; + memcpy(dst, source + pmatch[no].rm_so, len); + dst += len; + } + + } + *dst = '\0'; + + return dest; +} +/* End of pcreposix.c */ diff --git a/utility/ap_pcre.h b/utility/ap_pcre.h new file mode 100644 index 0000000..a851d29 --- /dev/null +++ b/utility/ap_pcre.h @@ -0,0 +1,176 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Derived from PCRE's pcreposix.h. + + Copyright (c) 1997-2004 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/** + * @file ap_regex.h + * @brief Apache Regex defines + */ + +#ifndef AP_REGEX_H +#define AP_REGEX_H + +#include "apr.h" + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options for ap_regexec: */ + +#define AP_REG_ICASE 0x01 /** use a case-insensitive match */ +#define AP_REG_NEWLINE 0x02 /** don't match newlines against '.' etc */ +#define AP_REG_NOTBOL 0x04 /** ^ will not match against start-of-string */ +#define AP_REG_NOTEOL 0x08 /** $ will not match against end-of-string */ + +#define AP_REG_EXTENDED (0) /** unused */ +#define AP_REG_NOSUB (0) /** unused */ + +/* Error values: */ +enum { + AP_REG_ASSERT = 1, /** internal error ? */ + AP_REG_ESPACE, /** failed to get memory */ + AP_REG_INVARG, /** invalid argument */ + AP_REG_NOMATCH /** match failed */ +}; + +/* The structure representing a compiled regular expression. */ +typedef struct { + void *re_pcre; + apr_size_t re_nsub; + apr_size_t re_erroffset; +} ap_regex_t; + +/* The structure in which a captured offset is returned. */ +typedef struct { + int rm_so; + int rm_eo; +} ap_regmatch_t; + +/* The functions */ + +/** + * Compile a regular expression. + * @param preg Returned compiled regex + * @param regex The regular expression string + * @param cflags Must be zero (currently). + * @return Zero on success or non-zero on error + */ +int ap_regcomp(ap_regex_t *preg, const char *regex, int cflags); + +/** + * Match a NUL-terminated string against a pre-compiled regex. + * @param preg The pre-compiled regex + * @param string The string to match + * @param nmatch Provide information regarding the location of any matches + * @param pmatch Provide information regarding the location of any matches + * @param eflags Bitwise OR of any of AP_REG_* flags + * @return 0 for successful match, #REG_NOMATCH otherwise + */ +int ap_regexec(const ap_regex_t *preg, const char *string, + apr_size_t nmatch, ap_regmatch_t *pmatch, int eflags); + +/** + * Return the error code returned by regcomp or regexec into error messages + * @param errcode the error code returned by regexec or regcomp + * @param preg The precompiled regex + * @param errbuf A buffer to store the error in + * @param errbuf_size The size of the buffer + */ +apr_size_t ap_regerror(int errcode, const ap_regex_t *preg, + char *errbuf, apr_size_t errbuf_size); + +/** Destroy a pre-compiled regex. + * @param preg The pre-compiled regex to free. + */ +void ap_regfree(ap_regex_t *preg); + +/** + * Compile a regular expression to be used later + * @param p The pool to allocate from + * @param pattern the regular expression to compile + * @param cflags The bitwise or of one or more of the following: + * @li REG_EXTENDED - Use POSIX extended Regular Expressions + * @li REG_ICASE - Ignore case + * @li REG_NOSUB - Support for substring addressing of matches + * not required + * @li REG_NEWLINE - Match-any-character operators don't match new-line + * @return The compiled regular expression + */ +ap_regex_t * ap_pregcomp(apr_pool_t *p, const char *pattern, + int cflags); + +/** + * Free the memory associated with a compiled regular expression + * @param p The pool the regex was allocated from + * @param reg The regular expression to free + */ +void ap_pregfree(apr_pool_t *p, ap_regex_t *reg); + +/** + * After performing a successful regex match, you may use this function to + * perform a series of string substitutions based on subexpressions that were + * matched during the call to ap_regexec + * @param p The pool to allocate from + * @param input An arbitrary string containing $1 through $9. These are + * replaced with the corresponding matched sub-expressions + * @param source The string that was originally matched to the regex + * @param nmatch the nmatch returned from ap_pregex + * @param pmatch the pmatch array returned from ap_pregex + */ +char * ap_pregsub(apr_pool_t *p, const char *input, const char *source, + size_t nmatch, ap_regmatch_t pmatch[]); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AP_REGEX_T */ + diff --git a/utility/config.c b/utility/config.c index 6b3dce1..847d474 100644 --- a/utility/config.c +++ b/utility/config.c @@ -3,53 +3,61 @@ #include "apr_file_io.h" #include "apr_strings.h" #include "apr_hash.h" -#include "apr_lib.h" + #include "shell.h" #include "config.h" +#include "util.h" +#include "logparse.h" apr_hash_t *g_config_opts; -apr_status_t config_set_string(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_string(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { int offset = (int)(long)opt->data; char **data = (char **)((void *)cfg + offset); - if (argc != 2) return APR_EINVAL; + if (argc != 2) + return APR_EINVAL; *data = apr_pstrdup(cfg->pool, argv[1]); return APR_SUCCESS; } -apr_status_t config_set_int(config_t *cfg, config_opt_t *opt, int argc, +static apr_status_t config_set_int(config_t *cfg, config_opt_t *opt, int argc, + const char **argv) __attribute__ ((__unused__)); +static apr_status_t config_set_int(config_t *cfg, config_opt_t *opt, int argc, const char **argv) { int offset = (int)(long)opt->data; int *data = (int *)((void *)cfg + offset); - if (argc != 2) return APR_EINVAL; + if (argc != 2) + return APR_EINVAL; *data = apr_atoi64(argv[1]); return APR_SUCCESS; } -apr_status_t config_set_flag(config_t *cfg, config_opt_t *opt, int argc, +static apr_status_t config_set_flag(config_t *cfg, config_opt_t *opt, int argc, const char **argv) { int offset = (int)(long)opt->data; int *data = (int *)((void *)cfg + offset); - if (argc != 2) return APR_EINVAL; + if (argc != 2) + return APR_EINVAL; *data = CHECK_YESNO(argv[1]); return APR_SUCCESS; } -apr_status_t config_set_loglevel(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_loglevel(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { - if (argc != 2) return APR_EINVAL; - if (!strcasecmp(argv[1],"error")) { + if (argc != 2) + return APR_EINVAL; + if (!strcasecmp(argv[1], "error")) { cfg->loglevel = LOGLEVEL_ERROR; - } else if (!strcasecmp(argv[1],"warn")) { + } else if (!strcasecmp(argv[1], "warn")) { cfg->loglevel = LOGLEVEL_WARN; - } else if (!strcasecmp(argv[1],"debug")) { + } else if (!strcasecmp(argv[1], "debug")) { cfg->loglevel = LOGLEVEL_DEBUG; - } else if (!strcasecmp(argv[1],"quiet")) { + } else if (!strcasecmp(argv[1], "quiet")) { cfg->loglevel = LOGLEVEL_QUIET; } else { cfg->loglevel = LOGLEVEL_ERROR; @@ -57,49 +65,51 @@ apr_status_t config_set_loglevel(config_t *cfg, config_opt_t *opt, int argc, return APR_SUCCESS; } -apr_status_t config_set_dbconnect(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_dbconnect(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { return APR_SUCCESS; } -apr_status_t config_set_dbparam(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_dbparam(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { return APR_SUCCESS; } -apr_status_t config_set_inputfile(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_inputfile(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { char **newp; - if (argc != 2) return APR_EINVAL; + if (argc != 2) + return APR_EINVAL; newp = (char **)apr_array_push(cfg->input_files); *newp = apr_pstrdup(cfg->pool, argv[1]); return APR_SUCCESS; } -apr_status_t config_set_dummy(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_dummy(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { return APR_SUCCESS; } -apr_status_t config_set_logformat(config_t *cfg, config_opt_t *opt, int argc, - const char **argv) +static apr_status_t config_set_logformat(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) { config_logformat_t *format; config_logformat_field_t *field; - if (argc != 4) return APR_EINVAL; + if (argc != 4) + return APR_EINVAL; - format = apr_hash_get(cfg->log_formats,argv[1],APR_HASH_KEY_STRING); + format = apr_hash_get(cfg->log_formats, argv[1], APR_HASH_KEY_STRING); if (!format) { format = apr_palloc(cfg->pool, sizeof(config_logformat_t)); format->name = apr_pstrdup(cfg->pool, argv[1]); format->fields = apr_array_make(cfg->pool, 5, sizeof(config_logformat_field_t)); - apr_hash_set(cfg->log_formats, argv[1], APR_HASH_KEY_STRING, format); + apr_hash_set(cfg->log_formats, apr_pstrdup(cfg->pool,argv[1]), APR_HASH_KEY_STRING, format); } field = (config_logformat_field_t *)apr_array_push(format->fields); field->name = apr_pstrdup(cfg->pool, argv[2]); @@ -107,18 +117,100 @@ apr_status_t config_set_logformat(config_t *cfg, config_opt_t *opt, int argc, return APR_SUCCESS; } +static apr_status_t config_set_output_field(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) +{ + config_output_field_t *field; + char *type, *size, *temp; + + if (argc < 4) + return APR_EINVAL; + field = (config_output_field_t *)apr_array_push(cfg->output_fields); + field->field = apr_pstrdup(cfg->pool, argv[1]); + field->source = apr_pstrdup(cfg->pool, argv[3]); + + type = size = apr_pstrdup(cfg->pool, argv[2]); + while (*size!='\0' && *size!='(') + size++; + if (*size == '(') { + *size = '\0'; + size++; + temp = size; + while (*temp != '\0' && *temp != ')') + temp++; + *temp = '\0'; + field->size = apr_atoi64(size); + } + if (strcasecmp("VARCHAR", type)==0) { + field->datatype = LOGSQL_DATATYPE_VARCHAR; + } else if (strcasecmp("INT", type)==0) { + field->datatype = LOGSQL_DATATYPE_INT; + } else if (strcasecmp("CHAR", type)==0) { + field->datatype = LOGSQL_DATATYPE_CHAR; + } else if (strcasecmp("SMALLINT", type)==0) { + field->datatype = LOGSQL_DATATYPE_SMALLINT; + } else if (strcasecmp("BIGINT", type)==0) { + field->datatype = LOGSQL_DATATYPE_BIGINT; + } else { + return APR_EINVAL; + } + + // Has a function + if (argc > 4) { + int i; + field->fname = apr_pstrdup(cfg->pool, argv[4]); + field->func = parser_get_func(field->fname); + field->args = apr_pcalloc(cfg->pool, sizeof(char *) * (argc-4+1)); + for (i=5; i<=argc; i++) { + field->args[i-5] = apr_pstrdup(cfg->pool, argv[i]); + } + } + + return APR_SUCCESS; +} + +static apr_status_t config_set_filter(config_t *cfg, config_opt_t *opt, + int argc, const char **argv) +{ + int argn = 1; + config_filter_t *filter; + filter = apr_pcalloc(cfg->pool, sizeof(config_filter_t)); + + if (opt->name[0]!='L') { // Pre or post 2-3 args + if (argc == 1) + return APR_EINVAL; + filter->field = apr_pstrdup(cfg->pool, argv[1]); + argn++; + } // Otherwise Line based only 1-2 args (no field) + if (argc <= argn) + return APR_EINVAL; + if (argv[argn][0] == '+') + argn++; + if (argv[argn][0] == '-') { + filter->negative = 1; + argn++; + } + if (argc <= argn) + return APR_EINVAL; + filter->filter = apr_pstrdup(cfg->pool, argv[argn]); + filter->regex = ap_pregcomp(cfg->pool, filter->filter, AP_REG_ICASE); + return APR_SUCCESS; +} + void config_dump(config_t *cfg) { apr_hash_index_t *hi; + int i; + config_output_field_t *fields; - printf("ErrorLog: %s\n",cfg->errorlog); - printf("LogLevel: %d\n",cfg->loglevel); + printf("ErrorLog: %s\n", cfg->errorlog); + printf("LogLevel: %d\n", cfg->loglevel); - printf("InputDir: %s\n",cfg->input_dir); + printf("InputDir: %s\n", cfg->input_dir); - printf("Table: %s\n",cfg->table); - printf("Transactions: %d\n",cfg->transactions); - printf("MachineID: %s\n",cfg->machineid); + printf("Table: %s\n", cfg->table); + printf("Transactions: %d\n", cfg->transactions); + printf("MachineID: %s\n", cfg->machineid); printf("Log formats:\n"); for (hi = apr_hash_first(cfg->pool, cfg->log_formats); hi; hi @@ -128,31 +220,39 @@ void config_dump(config_t *cfg) int i; apr_hash_this(hi, NULL, NULL, (void **)&format); - printf(">> %s\n",format->name); + printf(">> '%s'\n", format->name); fields = (config_logformat_field_t *)format->fields->elts; for (i=0; ifields->nelts; i++) { printf(">>>> %s:%s\n", fields[i].name, fields[i].datatype); } } - printf("Log Format: %s\n",cfg->logformat); - - printf("DryRun: %d\n",cfg->dryrun); - printf("Summary: %d\n",cfg->summary); -} - -static char *lowerstr(apr_pool_t *pool, const char *input) { - char *temp; - char *itr; - temp = apr_pstrdup(pool, input); - for (itr=temp; *itr!='\0'; itr++) { - *itr = apr_tolower(*itr); + printf("Log Format: '%s'\n", cfg->logformat); + + printf("Output Fields:\n"); + fields = (config_output_field_t *)cfg->output_fields->elts; + for (i=0; ioutput_fields->nelts; i++) { + printf(">> %s %s(%d): %s", fields[i].field, logsql_field_datatyeName(fields[i].datatype), fields[i].size, fields[i].source); + if (fields[i].func) { + printf(" :: %s(", fields[i].fname); + if (fields[i].args) { + int a = 0; + while (fields[i].args[a]) { + printf("%s,", fields[i].args[a]); + a++; + } + } + printf(")"); } - return temp; + printf("\n"); + } + + printf("DryRun: %d\n", cfg->dryrun); + printf("Summary: %d\n", cfg->summary); } #define config_get_option(name) apr_hash_get(g_config_opts, name, APR_HASH_KEY_STRING) -void config_add_option(apr_pool_t *p, const char *const name, +static void config_add_option(apr_pool_t *p, const char *const name, const char *const help, config_func_t func, void *data) { config_opt_t *opt; @@ -164,22 +264,24 @@ void config_add_option(apr_pool_t *p, const char *const name, opt->help = help; opt->func = func; opt->data = data; - apr_hash_set(g_config_opts, lowerstr(p,name), APR_HASH_KEY_STRING, opt); + apr_hash_set(g_config_opts, lowerstr(p, name), APR_HASH_KEY_STRING, opt); } void config_init(apr_pool_t *p) { - config_add_option(p, "ErrorLog", "File to log errors", - config_set_string, (void *)APR_OFFSETOF(config_t, errorlog)); - config_add_option(p, "LogLevel", "Set Log Level (error, warn, debug, quiet)", - config_set_loglevel, NULL); + config_add_option(p, "ErrorLog", "File to log errors", config_set_string, + (void *)APR_OFFSETOF(config_t, errorlog)); + config_add_option(p, "LogLevel", + "Set Log Level (error, warn, debug, quiet)", config_set_loglevel, + NULL); config_add_option(p, "InputDirectory", "Directory to scan for log files", config_set_string, (void *)APR_OFFSETOF(config_t, input_dir)); config_add_option(p, "InputFile", "Parse only this file", config_set_inputfile, NULL); - config_add_option(p, "DBConnect", "DB Connection information type://user:pass@hostname/database", + config_add_option(p, "DBConnect", + "DB Connection information type://user:pass@hostname/database", config_set_dbconnect, NULL); config_add_option(p, "DBParam", "DB Connection Parameter", config_set_dbparam, NULL); @@ -187,14 +289,28 @@ void config_init(apr_pool_t *p) config_set_string, (void *)APR_OFFSETOF(config_t, table)); config_add_option(p, "UseTransactions", "Enable Transactions?", config_set_flag, (void *)APR_OFFSETOF(config_t, transactions)); - config_add_option(p, "MachineID", "Machine ID to set", - config_set_string, (void *)APR_OFFSETOF(config_t, machineid)); + config_add_option(p, "MachineID", "Machine ID to set", config_set_string, + (void *)APR_OFFSETOF(config_t, machineid)); config_add_option(p, "LogFormatConfig", "Define input log formats", config_set_logformat, NULL); config_add_option(p, "LogFormat", "Use this logformat when parsing files", config_set_string, (void *)APR_OFFSETOF(config_t, logformat)); + config_add_option(p, "LineFilter", + "A regular expression to apply to the input line", + config_set_filter, (void *)APR_OFFSETOF(config_t, linefilters)); + config_add_option(p, "PreFilter", + "A regular expression to apply to a specific input field", + config_set_filter, (void *)APR_OFFSETOF(config_t, prefilters)); + config_add_option(p, "PostFilter", + "A regular expression to apply to a specific SQL output field", + config_set_filter, (void *)APR_OFFSETOF(config_t, postfilters)); + + config_add_option(p, "OutputField", + "Define output fields: field datatype source optfunc optarg...", + config_set_output_field, NULL); + config_add_option(p, "DryRun", "Don't perform any actual database changes", config_set_flag, (void *)APR_OFFSETOF(config_t, dryrun)); config_add_option(p, "Config", "Dummy to handle config directive", @@ -216,16 +332,20 @@ config_t *config_create(apr_pool_t *p) cfg->input_files = apr_array_make(cfg->pool, 10, sizeof(char *)); cfg->dbconfig = apr_table_make(cfg->pool, 5); cfg->log_formats = apr_hash_make(cfg->pool); - + cfg->output_fields = apr_array_make(cfg->pool, 10, + sizeof(config_output_field_t)); return cfg; } -int config_merge(void *rec, const char *key, const char *value) { +static int config_merge(void *rec, const char *key, const char *value) +{ config_t *cfg = (config_t *)rec; - config_opt_t *opt = config_get_option(key); + config_opt_t *opt= config_get_option(key); if (opt) { - const char *args[] = {key, value}; + const char *args[] = { + key, + value }; opt->func(cfg, opt, 2, args); } else { printf("Unhandled: %s\n", key); @@ -238,11 +358,11 @@ apr_status_t config_read(config_t *cfg, const char *filename, { apr_finfo_t finfo; apr_file_t *file; - apr_status_t rv, ret = APR_SUCCESS; + apr_status_t rv, ret= APR_SUCCESS; apr_pool_t *tp, *targp; config_opt_t *opt; char buff[1024]; - char *ptr, *ptr2; + char *ptr; char **targv; int targc; int line; @@ -254,7 +374,7 @@ apr_status_t config_read(config_t *cfg, const char *filename, return APR_ENOENT; } rv = apr_file_open(&file, filename, APR_FOPEN_READ | APR_BUFFERED, - APR_OS_DEFAULT, tp); + APR_OS_DEFAULT, tp); if (rv != APR_SUCCESS) return rv; @@ -267,10 +387,7 @@ apr_status_t config_read(config_t *cfg, const char *filename, // skip leading white space for (ptr = buff; *ptr == ' ' || *ptr == '\t'; ptr++) ; - // chomp off newline - for (ptr2 = ptr + strlen(ptr); *ptr2 != '\r' && *ptr2 != '\n'; ptr2--) - ; - *ptr2 = '\0'; + line_chomp(ptr); // skip comments if (*ptr == '#') @@ -278,9 +395,10 @@ apr_status_t config_read(config_t *cfg, const char *filename, if (*ptr == '\0') continue; apr_pool_clear(targp); - apr_tokenize_to_argv(buff, &targv, targp); + apr_tokenize_to_argv(ptr, &targv, targp); targc = 0; - while (targv[targc]) targc++; + while (targv[targc]) + targc++; opt = config_get_option(lowerstr(targp,targv[0])); if (opt) { rv = opt->func(cfg, opt, targc, (const char **)targv); @@ -296,10 +414,9 @@ apr_status_t config_read(config_t *cfg, const char *filename, } while (rv == APR_SUCCESS); // Apply merges - apr_table_do(config_merge,(void *)cfg,merge,NULL); + apr_table_do(config_merge, (void *)cfg, merge, NULL); apr_file_close(file); apr_pool_destroy(tp); return ret; } - diff --git a/utility/config.h b/utility/config.h index e1827fe..67f8ea5 100644 --- a/utility/config.h +++ b/utility/config.h @@ -5,6 +5,8 @@ #include "apr_hash.h" #include "apr_file_io.h" +#include "ap_pcre.h" + typedef enum { LOGLEVEL_QUIET = 0, LOGLEVEL_ERROR = 1, @@ -12,7 +14,8 @@ typedef enum { LOGLEVEL_DEBUG = 3, } loglevel_e; -typedef struct { +typedef struct config_t config_t; +struct config_t { /** the structures pool (to ease function arguments) */ apr_pool_t *pool; @@ -46,29 +49,33 @@ typedef struct { apr_array_header_t *output_fields; /** filter configuration */ - apr_array_header_t *filters; + apr_array_header_t *linefilters; + apr_array_header_t *prefilters; + apr_array_header_t *postfilters; /** Dry Run */ int dryrun; /* Show the summary */ int summary; -} config_t; +}; + -typedef struct { +typedef struct config_logformat_t config_logformat_t; +struct config_logformat_t { const char *name; apr_array_header_t *fields; -} config_logformat_t; +}; -typedef struct { +typedef struct config_logformat_field_t config_logformat_field_t; +struct config_logformat_field_t { const char *name; const char *datatype; -} config_logformat_field_t; +}; typedef struct config_opt_t config_opt_t; typedef apr_status_t (*config_func_t)(config_t *cfg, config_opt_t *opt, int argc, const char **argv); - struct config_opt_t { const char *name; const char *help; @@ -76,6 +83,40 @@ struct config_opt_t { void *data; }; +typedef struct config_filter_t config_filter_t; +struct config_filter_t { + const char *field; + const char *filter; + int negative; + ap_regex_t *regex; +}; + +typedef enum { + LOGSQL_DATATYPE_INT = 0, + LOGSQL_DATATYPE_SMALLINT, + LOGSQL_DATATYPE_VARCHAR, + LOGSQL_DATATYPE_CHAR, + LOGSQL_DATATYPE_BIGINT +} logsql_field_datatype; +#define logsql_field_datatyeName(x) \ + (x == LOGSQL_DATATYPE_INT ? "INT" \ + : (x == LOGSQL_DATATYPE_SMALLINT ? "SMALLINT" \ + : (x == LOGSQL_DATATYPE_VARCHAR ? "VARCHAR" \ + : (x == LOGSQL_DATATYPE_CHAR ? "CHAR" \ + : (x == LOGSQL_DATATYPE_BIGINT ? "BIGINT" : "ERR"))))) + +typedef struct config_output_field_t config_output_field_t; + +struct config_output_field_t { + const char *field; + logsql_field_datatype datatype; + apr_size_t size; + const char *source; + const char *fname; + void *func; + const char **args; +}; + #define CHECK_YESNO(c) ((!strcasecmp(c,"on") || !strcasecmp(c,"yes")) ? 1 : 0) /** @@ -99,6 +140,4 @@ config_t *config_create(apr_pool_t *p); apr_status_t config_read(config_t *cfg, const char *filename, apr_table_t *merge); -void config_generate(const char *filename); - #endif /*CONFIG_H_*/ diff --git a/utility/database.c b/utility/database.c new file mode 100644 index 0000000..5fece50 --- /dev/null +++ b/utility/database.c @@ -0,0 +1,2 @@ +#include "database.h" + diff --git a/utility/database.h b/utility/database.h new file mode 100644 index 0000000..9fc4844 --- /dev/null +++ b/utility/database.h @@ -0,0 +1,4 @@ +#ifndef DATABASE_H_ +#define DATABASE_H_ + +#endif /*DATABASE_H_*/ diff --git a/utility/logparse.c b/utility/logparse.c index 2940534..4d823ce 100644 --- a/utility/logparse.c +++ b/utility/logparse.c @@ -3,7 +3,35 @@ #include "apr_file_io.h" #include "apr_strings.h" -void find_log_files(config_t *cfg) +#include "util.h" + +apr_hash_t *g_parser_funcs; + +static apr_status_t parser_func_regexmatch(config_t *cfg, const char *data, + int argc, const char **argv) +{ + return APR_SUCCESS; +} +parser_func_t parser_get_func(const char *name) +{ + return apr_hash_get(g_parser_funcs, name, APR_HASH_KEY_STRING); +} + +static void parser_add_func(apr_pool_t *p, const char *const name, + parser_func_t func) +{ + if (!g_parser_funcs) { + g_parser_funcs = apr_hash_make(p); + } + apr_hash_set(g_parser_funcs, lowerstr(p, name), APR_HASH_KEY_STRING, func); +} + +void parser_init(apr_pool_t *p) +{ + parser_add_func(p, "regexmatch", parser_func_regexmatch); +} + +void parser_find_logs(config_t *cfg) { apr_pool_t *tp; apr_dir_t *dir; @@ -39,7 +67,7 @@ void find_log_files(config_t *cfg) * found during parsing of the arg_str. * keepquotes: Keep the quotes instead of stripping them */ -apr_status_t tokenize_logline(const char *arg_str, char ***argv_out, +static apr_status_t tokenize_logline(const char *arg_str, char ***argv_out, apr_pool_t *token_context, int keepquotes) { const char *cp; @@ -157,7 +185,7 @@ apr_status_t tokenize_logline(const char *arg_str, char ***argv_out, apr_status_t parse_logfile(config_t *cfg, const char *filename) { - apr_pool_t *tp, *argp; + apr_pool_t *tp, *targp; apr_file_t *file; apr_status_t rv; char buff[2048]; @@ -166,7 +194,7 @@ apr_status_t parse_logfile(config_t *cfg, const char *filename) int line; apr_pool_create(&tp, cfg->pool); - apr_pool_create(&argp, tp); + apr_pool_create(&targp, tp); rv = apr_file_open(&file, filename, APR_FOPEN_READ | APR_BUFFERED, APR_OS_DEFAULT, tp); @@ -180,16 +208,16 @@ apr_status_t parse_logfile(config_t *cfg, const char *filename) rv = apr_file_gets(buff, 1024, file); if (rv == APR_SUCCESS) { line++; - char *ptr; // chomp off newline - for (ptr = buff + strlen(buff); *ptr != '\r' && *ptr != '\n'; ptr--) - ; - *ptr = '\0'; - apr_pool_clear(argp); - tokenize_logline(buff, &targv, argp, 1); + line_chomp(buff); + + apr_pool_clear(targp); + tokenize_logline(buff, &targv, targp, 1); targc = 0; while (targv[targc]) targc++; - if (targc != 9) { + /** @todo Run Line Filters here */ + rv = parse_processline(targp, cfg, targv, targc); + if (rv != APR_SUCCESS) { int i; printf("Line %d(%d): %s\n",line, targc, buff); for (i = 0; targv[i]; i++) { @@ -203,3 +231,46 @@ apr_status_t parse_logfile(config_t *cfg, const char *filename) apr_pool_destroy(tp); return APR_SUCCESS; } + +apr_status_t parse_processline(apr_pool_t *ptemp, config_t *cfg, char **argv, int argc) +{ + config_logformat_t *fmt; + config_logformat_field_t *ifields; + config_output_field_t *ofields; + apr_table_t *datain; + apr_table_t *dataout; + int i; + + fmt = apr_hash_get(cfg->log_formats, cfg->logformat, APR_HASH_KEY_STRING); + if (!fmt) return APR_EINVAL; + if (fmt->fields->nelts != argc) return APR_EINVAL; + + datain = apr_table_make(ptemp, fmt->fields->nelts); + dataout = apr_table_make(ptemp, cfg->output_fields->nelts); + + ifields = (config_logformat_field_t *)fmt->fields->elts; + for (i=0; ifields->nelts; i++) { + apr_table_setn(datain,ifields[i].name,argv[i]); + } + /** @todo Run Pre Filters here */ + + // Convert input fields to output fields + ofields = (config_output_field_t *)cfg->output_fields->elts; + for (i=0; ioutput_fields->nelts; i++) { + const char *t; + if (!ofields[i].func) { + t = apr_table_get(datain, ofields[i].source); + if (!t) { + return APR_EINVAL; + } + apr_table_setn(dataout,ofields[i].field, t); + printf("S: %s = %s\n",ofields[i].source, t); + } else { + printf("S: %s, F: %p\n",ofields[i].source, ofields[i].func); + } + } + + /** @todo Run Post Filters here */ + + return APR_SUCCESS; +} diff --git a/utility/logparse.h b/utility/logparse.h index 540a9e0..ebabf56 100644 --- a/utility/logparse.h +++ b/utility/logparse.h @@ -3,8 +3,17 @@ #include "config.h" -void find_log_files(config_t *cfg); +typedef apr_status_t (*parser_func_t)(config_t *cfg, const char *data, + int argc, const char **argv); + +parser_func_t parser_get_func(const char *name); + +void parser_init(apr_pool_t *p); + +void parser_find_logs(config_t *cfg); apr_status_t parse_logfile(config_t *cfg, const char *filename); +apr_status_t parse_processline(apr_pool_t *ptemp, config_t *cfg, char **argv, int argc); + #endif /*LOGPARSE_H_*/ diff --git a/utility/mod_log_sql.conf b/utility/mod_log_sql.conf index 6cfae61..72f0205 100644 --- a/utility/mod_log_sql.conf +++ b/utility/mod_log_sql.conf @@ -9,7 +9,7 @@ LogLevel notice DryRun on Summary on -LogFormatConfig CLF host String +LogFormatConfig CLF remhost String LogFormatConfig CLF ident String LogFormatConfig CLF user String LogFormatConfig CLF date Date @@ -17,7 +17,7 @@ LogFormatConfig CLF request String LogFormatConfig CLF status Number LogFormatConfig CLF bytes_sent Number -LogFormatConfig Combined host String +LogFormatConfig Combined remhost String LogFormatConfig Combined ident String LogFormatConfig Combined user String LogFormatConfig Combined date Date @@ -28,3 +28,23 @@ LogFormatConfig Combined referer String LogFormatConfig Combined agent String LogFormat Combined + +# not yet implemented +Linefilter - "BAD" +PreFilter request - "GET \/images" +PostFilter request_method "GET" + +# Usage field datatype(size) source [function [param]...] +OutputField bytes_sent int bytes_sent +OutputField request_protocol varchar(10) request regexmatch "(HTTP\/[\d\.]+)$" +OutputField remote_host varchar(50) remhost +OutputField request_method varchar(25) request regexmatch "^(\w+)" +OutputField time_stamp int date totimestamp +OutputField status smallint status +OutputField request_uri varchar(255) request regexmatch "^\w+ (.+) \w+\.[\d\.]+$" +OutputField remote_user varchar(50) user +OutputField remote_logname varchar(50) ident +OutputField remote_time char(28) date +#Only used for Combined log input, if standard CLF input, they are ignored +OutputField agent varchar(255) agent +OutputField referer varchar(255) referer diff --git a/utility/shell.c b/utility/shell.c index 07d9da1..eaa7098 100644 --- a/utility/shell.c +++ b/utility/shell.c @@ -54,7 +54,7 @@ void show_help(const char *prog, const apr_getopt_option_t *opts, FILE *output) int main(int argc, const char *const argv[]) { - apr_pool_t *pool; + apr_pool_t *pool, *ptemp; apr_getopt_t *opts; int opt; const char *opt_arg; @@ -69,12 +69,13 @@ int main(int argc, const char *const argv[]) fprintf(stderr, "Failed to create memory pool!\n"); exit(1); } + apr_pool_create(&ptemp, NULL); /** Iterate over command line arguments * shoving args in a apr_table for processing later*/ - args = apr_table_make(pool, 5); + args = apr_table_make(ptemp, 5); apr_table_setn(args, "config", "mod_log_sql.conf"); - apr_getopt_init(&opts, pool, argc, argv); + apr_getopt_init(&opts, ptemp, argc, argv); while ((rv = apr_getopt_long(opts, _opt_config, &opt, &opt_arg)) == APR_SUCCESS) { switch (opt) { case 'c': @@ -122,17 +123,21 @@ int main(int argc, const char *const argv[]) } // Process configuration file + parser_init(pool); config_init(pool); base = config_create(pool); rv = config_read(base, apr_table_get(args,"Config"), args); + apr_pool_destroy(ptemp); + if (APR_STATUS_IS_ENOENT(rv)) { fprintf(stderr,"Could not load configuration file: %s\n",apr_table_get(args,"config")); } else if (rv) { exit(1); } config_dump(base); - // Apply overrides from command line - find_log_files(base); + + // Find files and parse + parser_find_logs(base); if (!apr_is_empty_array(base->input_files)) { char **filelist; int f, l; diff --git a/utility/util.c b/utility/util.c new file mode 100644 index 0000000..ef3ea68 --- /dev/null +++ b/utility/util.c @@ -0,0 +1,28 @@ +#include "util.h" +#include "apr_strings.h" +#include "apr_lib.h" + + +char *lowerstr(apr_pool_t *pool, const char *input) +{ + char *temp; + char *itr; + temp = apr_pstrdup(pool, input); + for (itr=temp; *itr!='\0'; itr++) { + *itr = apr_tolower(*itr); + } + return temp; +} + +void line_chomp(char *str) +{ + int len; + // chomp off newline + len = strlen(str); + if (len) { + while (str[len-1] == '\r' || str[len-1] == '\n') { + str[len-1] = '\0'; + len--; + } + } +} diff --git a/utility/util.h b/utility/util.h new file mode 100644 index 0000000..8c48474 --- /dev/null +++ b/utility/util.h @@ -0,0 +1,13 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include "apr_pools.h" + +char *lowerstr(apr_pool_t *pool, const char *input); + +/** + * Chomp new line characters off the end of the line + */ +void line_chomp(char *str); + +#endif /*UTIL_H_*/ -- cgit v0.9.2