summaryrefslogtreecommitdiffstatsabout
path: root/utility/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'utility/config.c')
-rw-r--r--utility/config.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/utility/config.c b/utility/config.c
new file mode 100644
index 0000000..6867476
--- /dev/null
+++ b/utility/config.c
@@ -0,0 +1,534 @@
1#include "apr.h"
2#include "apr_file_info.h"
3#include "apr_file_io.h"
4#include "apr_strings.h"
5#include "apr_hash.h"
6#include "apr_uri.h"
7
8#include "shell.h"
9#include "config.h"
10#include "util.h"
11#include "logparse.h"
12#include "autoconfig.h"
13
14apr_hash_t *g_config_opts;
15
16static apr_status_t config_set_string(config_t *cfg, config_opt_t *opt,
17 int argc, const char **argv)
18{
19 int offset = (int)(long)opt->data;
20 char **data = (char **)((void *)cfg + offset);
21 if (argc != 2)
22 return APR_EINVAL;
23 *data = apr_pstrdup(cfg->pool, argv[1]);
24 return APR_SUCCESS;
25}
26
27static apr_status_t config_set_file(config_t *cfg, config_opt_t *opt,
28 int argc, const char **argv)
29{
30 int offset = (int)(long)opt->data;
31 char **data = (char **)((void *)cfg + offset);
32 if (argc != 2)
33 return APR_EINVAL;
34 apr_filepath_merge(data, NULL, argv[1],
35 APR_FILEPATH_TRUENAME, cfg->pool);
36 return APR_SUCCESS;
37}
38
39static apr_status_t config_set_int(config_t *cfg, config_opt_t *opt, int argc,
40 const char **argv)
41{
42 int offset = (int)(long)opt->data;
43 int *data = (int *)((void *)cfg + offset);
44 if (argc != 2)
45 return APR_EINVAL;
46 *data = apr_atoi64(argv[1]);
47 return APR_SUCCESS;
48}
49
50static apr_status_t config_set_flag(config_t *cfg, config_opt_t *opt, int argc,
51 const char **argv)
52{
53 int offset = (int)(long)opt->data;
54 int *data = (int *)((void *)cfg + offset);
55 if (argc != 2)
56 return APR_EINVAL;
57 *data = CHECK_YESNO(argv[1]);
58 return APR_SUCCESS;
59}
60
61static apr_status_t config_set_loglevel(config_t *cfg, config_opt_t *opt,
62 int argc, const char **argv)
63{
64 if (argc != 2)
65 return APR_EINVAL;
66 if (!strcasecmp(argv[1], "error")) {
67 cfg->loglevel = LOGLEVEL_ERROR;
68 } else if (!strcasecmp(argv[1], "notice")) {
69 cfg->loglevel = LOGLEVEL_NOTICE;
70 } else if (!strcasecmp(argv[1], "debug")) {
71 cfg->loglevel = LOGLEVEL_DEBUG;
72 } else {
73 cfg->loglevel = LOGLEVEL_ERROR;
74 }
75 return APR_SUCCESS;
76}
77
78static apr_status_t config_set_inputfile(config_t *cfg, config_opt_t *opt,
79 int argc, const char **argv)
80{
81 config_filestat_t *newp;
82 if (argc != 2)
83 return APR_EINVAL;
84 newp = (config_filestat_t *)apr_array_push(cfg->input_files);
85 char *temp;
86 apr_filepath_merge(&temp, NULL, argv[1],
87 APR_FILEPATH_TRUENAME, cfg->pool);
88 newp->fname = temp;
89 newp->result = "Not Parsed";
90 return APR_SUCCESS;
91}
92
93static apr_status_t config_set_dummy(config_t *cfg, config_opt_t *opt,
94 int argc, const char **argv)
95{
96 return APR_SUCCESS;
97}
98
99static apr_status_t config_set_logformat(config_t *cfg, config_opt_t *opt,
100 int argc, const char **argv)
101{
102 config_logformat_t *format;
103 config_logformat_field_t *field;
104
105 if (argc != 4)
106 return APR_EINVAL;
107
108 format = apr_hash_get(cfg->log_formats, argv[1], APR_HASH_KEY_STRING);
109 if (!format) {
110 format = apr_palloc(cfg->pool, sizeof(config_logformat_t));
111 format->name = apr_pstrdup(cfg->pool, argv[1]);
112 format->fields = apr_array_make(cfg->pool, 5,
113 sizeof(config_logformat_field_t));
114 apr_hash_set(cfg->log_formats, apr_pstrdup(cfg->pool,argv[1]), APR_HASH_KEY_STRING, format);
115 }
116 field = (config_logformat_field_t *)apr_array_push(format->fields);
117 field->name = apr_pstrdup(cfg->pool, argv[2]);
118 field->datatype = apr_pstrdup(cfg->pool, argv[3]);
119 return APR_SUCCESS;
120}
121
122static apr_status_t config_set_output_field(config_t *cfg, config_opt_t *opt,
123 int argc, const char **argv)
124{
125 config_output_field_t *field;
126 char *type, *size, *temp;
127
128 if (argc < 5)
129 return APR_EINVAL;
130 field = (config_output_field_t *)apr_array_push(cfg->output_fields);
131 field->field = apr_pstrdup(cfg->pool, argv[1]);
132 field->source = apr_pstrdup(cfg->pool, argv[4]);
133 field->def = apr_pstrdup(cfg->pool, argv[3]);
134 type = size = apr_pstrdup(cfg->pool, argv[2]);
135 while (*size!='\0' && *size!='(')
136 size++;
137 if (*size == '(') {
138 *size = '\0';
139 size++;
140 temp = size;
141 while (*temp != '\0' && *temp != ')')
142 temp++;
143 *temp = '\0';
144 field->size = apr_atoi64(size);
145 }
146 if (strcasecmp("VARCHAR", type)==0) {
147 field->datatype = LOGSQL_DATATYPE_VARCHAR;
148 } else if (strcasecmp("INT", type)==0) {
149 field->datatype = LOGSQL_DATATYPE_INT;
150 } else if (strcasecmp("CHAR", type)==0) {
151 field->datatype = LOGSQL_DATATYPE_CHAR;
152 } else if (strcasecmp("SMALLINT", type)==0) {
153 field->datatype = LOGSQL_DATATYPE_SMALLINT;
154 } else if (strcasecmp("BIGINT", type)==0) {
155 field->datatype = LOGSQL_DATATYPE_BIGINT;
156 } else {
157 return APR_EINVAL;
158 }
159
160 // Has a function
161 if (argc > 5) {
162 int i;
163 field->fname = apr_pstrdup(cfg->pool, argv[5]);
164 field->func = parser_get_func(field->fname);
165 field->args = apr_pcalloc(cfg->pool, sizeof(char *) * (argc-5+1));
166 for (i=6; i<=argc; i++) {
167 field->args[i-6] = apr_pstrdup(cfg->pool, argv[i]);
168 }
169 }
170
171 return APR_SUCCESS;
172}
173
174static apr_status_t config_set_filter(config_t *cfg, config_opt_t *opt,
175 int argc, const char **argv)
176{
177 int argn = 1;
178 config_filter_t *filter;
179 switch (opt->name[1]) {
180 case 'i': //line
181 filter = apr_array_push(cfg->linefilters);
182 break;
183 case 'r': //pre
184 filter = apr_array_push(cfg->prefilters);
185 break;
186 case 'o': //post
187 filter = apr_array_push(cfg->postfilters);
188 break;
189 }
190
191 if (opt->name[0]=='P') { // Pre or post 2-3 args
192 if (argc == 1)
193 return APR_EINVAL;
194 filter->field = apr_pstrdup(cfg->pool, argv[1]);
195 argn++;
196 } // Otherwise Line based only 1-2 args (no field)
197 if (argc <= argn)
198 return APR_EINVAL;
199 if (*argv[argn] == '+')
200 argn++;
201 if (*argv[argn] == '-') {
202 filter->negative = 1;
203 argn++;
204 }
205 if (filter->negative && argc == argn) {
206 // if no filter for negative.. that's ok.. Assume ALL
207 return APR_SUCCESS;
208 }
209 if (argc <= argn)
210 return APR_EINVAL;
211 filter->filter = apr_pstrdup(cfg->pool, argv[argn]);
212 filter->regex = ap_pregcomp(cfg->pool, filter->filter, AP_REG_EXTENDED|AP_REG_ICASE);
213 return APR_SUCCESS;
214}
215
216void config_dump(config_t *cfg)
217{
218 apr_hash_index_t *hi;
219 int i;
220 config_output_field_t *fields;
221 config_filter_t *filters;
222
223 printf("ErrorLog: %s\n", cfg->errorlog);
224 printf("LogLevel: %d\n", cfg->loglevel);
225
226 printf("BadLineFile: %s\n", cfg->badlinefile);
227 printf("BadLineMax: %d\n", cfg->badlinemax);
228
229 printf("InputDir: %s\n", cfg->input_dir);
230
231 printf("Split input files: %d\n", cfg->split_enabled);
232 printf("Split output directory: %s\n", cfg->split_dir);
233 printf("Split file count: %d\n", cfg->split_count);
234 printf("Split min lines: %'d\n", cfg->split_minimum);
235 printf("Split max lines: %'d\n", cfg->split_maximum);
236
237 printf("DB Driver: %s\n", cfg->dbdriver);
238 printf("DB Params: %s\n", cfg->dbparams);
239
240 printf("Table: %s\n", cfg->table);
241 printf("Transactions: %d\n", cfg->transactions);
242 printf("MachineID: %s\n", cfg->machineid);
243
244 printf("Log formats:\n");
245 for (hi = apr_hash_first(cfg->pool, cfg->log_formats); hi; hi
246 = apr_hash_next(hi)) {
247 config_logformat_t *format;
248 config_logformat_field_t *fields;
249 int i;
250
251 apr_hash_this(hi, NULL, NULL, (void **)&format);
252 printf(">> '%s'\n", format->name);
253 fields = (config_logformat_field_t *)format->fields->elts;
254 for (i=0; i<format->fields->nelts; i++) {
255 printf(">>>> %s:%s\n", fields[i].name, fields[i].datatype);
256 }
257 }
258 printf("Log Format: '%s'\n", cfg->logformat);
259
260 printf("Output Fields:\n");
261 fields = (config_output_field_t *)cfg->output_fields->elts;
262 for (i=0; i<cfg->output_fields->nelts; i++) {
263 printf(">> %s %s(%d) DEFAULT '%s': %s", fields[i].field,
264 logsql_field_datatyeName(fields[i].datatype),
265 fields[i].size, fields[i].def, fields[i].source);
266 if (fields[i].func) {
267 printf(" :: %s(", fields[i].fname);
268 if (fields[i].args) {
269 int a = 0;
270 while (fields[i].args[a]) {
271 printf("%s,", fields[i].args[a]);
272 a++;
273 }
274 }
275 printf(")");
276 }
277 printf("\n");
278 }
279 printf("Filters:\n>> Line:\n");
280 filters = (config_filter_t *)cfg->linefilters->elts;
281 for (i=0; i<cfg->linefilters->nelts; i++) {
282 printf(">>>> %c \"%s\" (%pp)\n",filters[i].negative ? '-':'+',
283 filters[i].filter, filters[i].regex);
284 }
285 printf(">> Pre:\n");
286 filters = (config_filter_t *)cfg->prefilters->elts;
287 for (i=0; i<cfg->prefilters->nelts; i++) {
288 printf(">>>> %s %c \"%s\" (%pp)\n",
289 filters[i].field, filters[i].negative ? '-':'+',
290 filters[i].filter, filters[i].regex);
291 }
292 printf(">> Post:\n");
293 filters = (config_filter_t *)cfg->postfilters->elts;
294 for (i=0; i<cfg->postfilters->nelts; i++) {
295 printf(">>>> %s %c \"%s\" (%pp)\n",
296 filters[i].field, filters[i].negative ? '-':'+',
297 filters[i].filter, filters[i].regex);
298 }
299
300 printf("DryRun: %d\n", cfg->dryrun);
301 printf("Summary: %d\n", cfg->summary);
302}
303
304#define config_get_option(name) apr_hash_get(g_config_opts, name, APR_HASH_KEY_STRING)
305
306static void config_add_option(apr_pool_t *p, const char *const name,
307 const char *const help, config_func_t func, void *data)
308{
309 config_opt_t *opt;
310 if (!g_config_opts) {
311 g_config_opts = apr_hash_make(p);
312 }
313 opt = apr_palloc(p, sizeof(config_opt_t));
314 opt->name = name;
315 opt->help = help;
316 opt->func = func;
317 opt->data = data;
318 apr_hash_set(g_config_opts, lowerstr(p, name), APR_HASH_KEY_STRING, opt);
319}
320
321void config_init(apr_pool_t *p)
322{
323 config_add_option(p, "ErrorLog", "File to log errors", config_set_file,
324 (void *)APR_OFFSETOF(config_t, errorlog));
325 config_add_option(p, "LogLevel", "Set Log Level (error, warn, debug, quiet)",
326 config_set_loglevel, NULL);
327
328 config_add_option(p, "BadLineFile", "File to log bad log lines", config_set_file,
329 (void *)APR_OFFSETOF(config_t, badlinefile));
330 config_add_option(p, "BadLineMax", "Max number of bad lines before aborting",
331 config_set_int, (void *)APR_OFFSETOF(config_t, badlinemax));
332
333
334 config_add_option(p, "InputDirectory", "Directory to scan for log files",
335 config_set_file, (void *)APR_OFFSETOF(config_t, input_dir));
336 config_add_option(p, "InputFile", "Parse only this file",
337 config_set_inputfile, NULL);
338
339 config_add_option(p, "SplitInput",
340 "Split the file into pieces, then process",
341 config_set_flag, (void *)APR_OFFSETOF(config_t, split_enabled));
342 config_add_option(p, "SplitCount",
343 "Split the file into N number of pieces",
344 config_set_int, (void *)APR_OFFSETOF(config_t, split_count));
345 config_add_option(p, "SplitMinLines",
346 "Each split piece will have a minumum of N lines",
347 config_set_int, (void *)APR_OFFSETOF(config_t, split_minimum));
348 config_add_option(p, "SplitMaxLines",
349 "Each split piece will have a maximum of N lines",
350 config_set_int, (void *)APR_OFFSETOF(config_t, split_maximum));
351 config_add_option(p, "SplitDirectory",
352 "Output directory to put intermediate split files",
353 config_set_file, (void *)APR_OFFSETOF(config_t, split_dir));
354
355 config_add_option(p, "ThreadCount",
356 "Numer of threads to use for processing the input files",
357 config_set_int, (void *)APR_OFFSETOF(config_t, thread_count));
358
359 config_add_option(p, "DBDDriver", "DBD Driver to use",
360 config_set_string, (void *)APR_OFFSETOF(config_t, dbdriver));
361 config_add_option(p, "DBDParams", "DBD Connection Parameters",
362 config_set_string, (void *)APR_OFFSETOF(config_t, dbparams));
363 config_add_option(p, "Table", "Table to import the log to",
364 config_set_string, (void *)APR_OFFSETOF(config_t, table));
365 config_add_option(p, "UseTransactions", "Enable Transactions?",
366 config_set_flag, (void *)APR_OFFSETOF(config_t, transactions));
367 config_add_option(p, "MachineID", "Machine ID to set",
368 config_set_string, (void *)APR_OFFSETOF(config_t, machineid));
369
370 config_add_option(p, "LogFormatConfig", "Define input log formats",
371 config_set_logformat, NULL);
372 config_add_option(p, "LogFormat", "Use this logformat when parsing files",
373 config_set_string, (void *)APR_OFFSETOF(config_t, logformat));
374
375 config_add_option(p, "LineFilter",
376 "A regular expression to apply to the input line",
377 config_set_filter, (void *)APR_OFFSETOF(config_t, linefilters));
378 config_add_option(p, "PreFilter",
379 "A regular expression to apply to a specific input field",
380 config_set_filter, (void *)APR_OFFSETOF(config_t, prefilters));
381 config_add_option(p, "PostFilter",
382 "A regular expression to apply to a specific SQL output field",
383 config_set_filter, (void *)APR_OFFSETOF(config_t, postfilters));
384
385 config_add_option(p, "OutputField",
386 "Define output fields: field datatype source optfunc optarg...",
387 config_set_output_field, NULL);
388
389 config_add_option(p, "DryRun", "Don't perform any actual database changes",
390 config_set_flag, (void *)APR_OFFSETOF(config_t, dryrun));
391 config_add_option(p, "Dump", "Dump Configuration and quit",
392 config_set_flag, (void *)APR_OFFSETOF(config_t, dump));
393 config_add_option(p, "Config", "Dummy to handle config directive",
394 config_set_dummy, NULL);
395 config_add_option(p, "Summary", "Show the summary before exit?",
396 config_set_flag, (void *)APR_OFFSETOF(config_t, summary));
397}
398
399config_t *config_create(apr_pool_t *p)
400{
401 config_t *cfg;
402 apr_pool_t *sp;
403 apr_pool_create(&sp, p);
404 cfg = apr_pcalloc(sp, sizeof(config_t));
405 cfg->pool = sp;
406 cfg->loglevel = LOGLEVEL_ERROR;
407 cfg->summary = 1;
408 cfg->transactions = 1;
409 cfg->thread_count = 0; // default zero thread (aka non-threaded)
410 cfg->split_count = 4;
411 cfg->split_minimum = 10000;
412 cfg->split_maximum = 50000;
413 cfg->input_files = apr_array_make(cfg->pool, 2, sizeof(config_filestat_t));
414 cfg->log_formats = apr_hash_make(cfg->pool);
415 cfg->output_fields = apr_array_make(cfg->pool, 10,
416 sizeof(config_output_field_t));
417 cfg->linefilters = apr_array_make(cfg->pool, 2, sizeof(config_filter_t));
418 cfg->prefilters = apr_array_make(cfg->pool, 2, sizeof(config_filter_t));
419 cfg->postfilters = apr_array_make(cfg->pool, 2, sizeof(config_filter_t));
420 return cfg;
421}
422
423apr_status_t config_check(config_t *cfg)
424{
425 apr_status_t ret = APR_SUCCESS;
426 if (!cfg->dbdriver || !cfg->dbparams) {
427 logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: Database configuration is missing");
428 ret = APR_EINVAL;
429 }
430 if (!cfg->table) {
431 logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: No Log Table defined");
432 ret = APR_EINVAL;
433 }
434 if (apr_is_empty_array(cfg->output_fields)) {
435 logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: No Output Fields Defined");
436 ret = APR_EINVAL;
437 }
438 if (apr_hash_count(cfg->log_formats)==0) {
439 logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: No Input Log Formats Defined");
440 ret = APR_EINVAL;
441 }
442#if !defined(HAVE_APR_DBD_TRANSACTION_MODE_GET)
443 if (cfg->transactions) {
444 logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: Disabling Transaction Support. Requires apr-util 1.3.0 or higher");
445 cfg->transactions = 0;
446 }
447#endif
448 return ret;
449}
450
451static int config_merge(void *rec, const char *key, const char *value)
452{
453 config_t *cfg = (config_t *)rec;
454
455 config_opt_t *opt= config_get_option(key);
456 if (opt) {
457 const char *args[] = {
458 key,
459 value };
460 opt->func(cfg, opt, 2, args);
461 } else {
462 logging_log(cfg, LOGLEVEL_NOISE, "Unhandled: %s\n", key);
463 }
464 return 1;
465}
466
467apr_status_t config_read(config_t *cfg, const char *filename,
468 apr_table_t *merge)
469{
470 apr_finfo_t finfo;
471 apr_file_t *file;
472 apr_status_t rv, ret= APR_SUCCESS;
473 apr_pool_t *tp, *targp;
474 config_opt_t *opt;
475 char buff[1024];
476 char *ptr;
477 char **targv;
478 int targc;
479 int line;
480
481 apr_pool_create(&tp, cfg->pool);
482 apr_pool_create(&targp, tp);
483
484 if (apr_stat(&finfo, filename, APR_FINFO_MIN, tp) != APR_SUCCESS) {
485 return APR_ENOENT;
486 }
487 rv = apr_file_open(&file, filename, APR_FOPEN_READ | APR_BUFFERED,
488 APR_OS_DEFAULT, tp);
489 if (rv != APR_SUCCESS)
490 return rv;
491
492 line = 0;
493 do {
494 rv = apr_file_gets(buff, 1024, file);
495 if (rv == APR_SUCCESS) { // we read data
496 line++;
497
498 // skip leading white space
499 for (ptr = buff; *ptr == ' ' || *ptr == '\t'; ptr++)
500 ;
501 line_chomp(ptr);
502
503 // skip comments
504 if (*ptr == '#')
505 continue;
506 if (*ptr == '\0')
507 continue;
508 apr_pool_clear(targp);
509 parser_tokenize_line(ptr, &targv, targp);
510 targc = 0;
511 while (targv[targc])
512 targc++;
513 opt = config_get_option(lowerstr(targp,targv[0]));
514 if (opt) {
515 rv = opt->func(cfg, opt, targc, (const char **)targv);
516 if (APR_STATUS_IS_EINVAL(rv)) {
517 logging_log(cfg, LOGLEVEL_NOISE,
518 "Config Error: Invalid Arguments for %s\n\t%s\n",
519 opt->name, opt->help);
520 ret = rv;
521 }
522 } else {
523 logging_log(cfg, LOGLEVEL_NOISE, "Unhandled: %s\n", targv[0]);
524 }
525 }
526 } while (rv == APR_SUCCESS);
527
528 // Apply merges
529 apr_table_do(config_merge, (void *)cfg, merge, NULL);
530
531 apr_file_close(file);
532 apr_pool_destroy(tp);
533 return ret;
534}