aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2010-04-01 19:45:35 +0400
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2010-04-01 19:45:35 +0400
commit6be82def714f297d54302953286223b4afb07208 (patch)
treeb156eea2e245efeb95702cc6bae6beaa02341440
parenta43a37453f497a919fb4a5358615d89f18706935 (diff)
downloadrspamd-6be82def714f297d54302953286223b4afb07208.tar.gz
rspamd-6be82def714f297d54302953286223b4afb07208.zip
* Add dumper to XML file (not all sections yet)
* Add checksum to config files * Some config parsing reorganizations: - post_load_config should be called after xml initialization - xml state machine is corrected to read main section variables - some linting is added to xml (checking of elements parity)
-rw-r--r--src/cfg_file.h22
-rw-r--r--src/cfg_file.l1
-rw-r--r--src/cfg_utils.c36
-rw-r--r--src/cfg_xml.c279
-rw-r--r--src/cfg_xml.h11
-rw-r--r--src/main.c6
6 files changed, 310 insertions, 45 deletions
diff --git a/src/cfg_file.h b/src/cfg_file.h
index b4f5a0e1f..3ec7391e4 100644
--- a/src/cfg_file.h
+++ b/src/cfg_file.h
@@ -266,16 +266,18 @@ struct config_file {
GHashTable* factors; /**< hash of factors indexed by symbol name */
GHashTable* c_modules; /**< hash of c modules indexed by module name */
GHashTable* composite_symbols; /**< hash of composite symbols indexed by its name */
- GList *classifiers; /**< list of all classifiers defined */
- GList *statfiles; /**< list of all statfiles in config file order */
- GHashTable *classifiers_symbols; /**< hashtable indexed by symbol name of classifiers */
- GHashTable* cfg_params; /**< all cfg params indexed by its name in this structure */
+ GList *classifiers; /**< list of all classifiers defined */
+ GList *statfiles; /**< list of all statfiles in config file order */
+ GHashTable *classifiers_symbols; /**< hashtable indexed by symbol name of classifiers */
+ GHashTable* cfg_params; /**< all cfg params indexed by its name in this structure */
int clock_res; /**< resolution of clock used */
double grow_factor; /**< grow factor for consolidation callback */
GList *views; /**< views */
- GHashTable* domain_settings; /**< settings per-domains */
- GHashTable* user_settings; /**< settings per-user */
-
+ GHashTable* domain_settings; /**< settings per-domains */
+ GHashTable* user_settings; /**< settings per-user */
+
+ gchar* checksum; /**< real checksum of config file */
+ gchar* dump_checksum; /**< dump checksum of config file */
};
/**
@@ -361,6 +363,12 @@ gchar* substitute_variable (struct config_file *cfg, gchar *name, gchar *str, gu
*/
void post_load_config (struct config_file *cfg);
+/**
+ * Calculate checksum for config file
+ * @param cfg config file
+ */
+gboolean get_config_checksum (struct config_file *cfg);
+
/**
* Replace all \" with a single " in given string
diff --git a/src/cfg_file.l b/src/cfg_file.l
index fd83be66c..83c35290c 100644
--- a/src/cfg_file.l
+++ b/src/cfg_file.l
@@ -163,7 +163,6 @@ yes|YES|no|NO|[yY]|[nN] yylval.flag=parse_flag(yytext); return FLAG;
if ( --include_stack_ptr < 0 ) {
include_stack_ptr = 0;
yylineno = 1;
- post_load_config (cfg);
yyterminate ();
}
else {
diff --git a/src/cfg_utils.c b/src/cfg_utils.c
index d2f75e11c..18677a1ca 100644
--- a/src/cfg_utils.c
+++ b/src/cfg_utils.c
@@ -230,6 +230,9 @@ free_config (struct config_file *cfg)
g_hash_table_remove_all (cfg->cfg_params);
g_hash_table_unref (cfg->cfg_params);
g_hash_table_destroy (cfg->classifiers_symbols);
+ if (cfg->checksum) {
+ g_free (cfg->checksum);
+ }
g_list_free (cfg->classifiers);
g_list_free (cfg->metrics_list);
memory_pool_delete (cfg->cfg_pool);
@@ -499,6 +502,37 @@ fill_cfg_params (struct config_file *cfg)
}
+gboolean
+get_config_checksum (struct config_file *cfg)
+{
+ int fd;
+ void *map;
+ struct stat st;
+
+ /* Compute checksum for config file that should be used by xml dumper */
+ if ((fd = open (cfg->cfg_name, O_RDONLY)) == -1) {
+ msg_err ("config file %s is no longer available, cannot calculate checksum");
+ return FALSE;
+ }
+ if (stat (cfg->cfg_name, &st) == -1) {
+ msg_err ("cannot stat %s: %s", cfg->cfg_name, strerror (errno));
+ return FALSE;
+ }
+
+ /* Now mmap this file to simplify reading process */
+ if ((map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ msg_err ("cannot mmap %s: %s", cfg->cfg_name, strerror (errno));
+ close (fd);
+ return FALSE;
+ }
+ close (fd);
+
+ /* Get checksum for a file */
+ cfg->checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, map, st.st_size);
+ munmap (map, st.st_size);
+
+ return TRUE;
+}
/*
* Perform post load actions
*/
@@ -539,7 +573,7 @@ post_load_config (struct config_file *cfg)
cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
}
-
+
}
diff --git a/src/cfg_xml.c b/src/cfg_xml.c
index 93c8a5785..aa9ccf2fe 100644
--- a/src/cfg_xml.c
+++ b/src/cfg_xml.c
@@ -81,6 +81,24 @@ static struct xml_parser_rule grammar[] = {
NULL
},
{
+ "raw_mode",
+ xml_handle_boolean,
+ G_STRUCT_OFFSET (struct config_file, raw_mode),
+ NULL
+ },
+ {
+ "tempdir",
+ xml_handle_string,
+ G_STRUCT_OFFSET (struct config_file, temp_dir),
+ NULL
+ },
+ {
+ "checksum",
+ xml_handle_string,
+ G_STRUCT_OFFSET (struct config_file, dump_checksum),
+ NULL
+ },
+ {
"statfile_pool_size",
xml_handle_size,
G_STRUCT_OFFSET (struct config_file, max_statfile_size),
@@ -718,7 +736,7 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam
if (g_ascii_strcasecmp (element_name, "module") == 0) {
/* Read module data */
if (extract_attr ("name", attribute_names, attribute_values, &res)) {
- g_strlcpy (ud->section_name, res, sizeof (res));
+ g_strlcpy (ud->section_name, res, sizeof (ud->section_name));
ud->state = XML_READ_MODULE;
}
else {
@@ -734,7 +752,7 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam
}
else if (g_ascii_strcasecmp (element_name, "metric") == 0) {
if (extract_attr ("name", attribute_names, attribute_values, &res)) {
- g_strlcpy (ud->section_name, res, sizeof (res));
+ g_strlcpy (ud->section_name, res, sizeof (ud->section_name));
ud->state = XML_READ_METRIC;
/* Create object */
ud->other_data = memory_pool_alloc0 (ud->cfg->cfg_pool, sizeof (struct metric));
@@ -746,7 +764,7 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam
}
else if (g_ascii_strcasecmp (element_name, "classifier") == 0) {
if (extract_attr ("type", attribute_names, attribute_values, &res)) {
- g_strlcpy (ud->section_name, res, sizeof (res));
+ g_strlcpy (ud->section_name, res, sizeof (ud->section_name));
ud->state = XML_READ_CLASSIFIER;
/* Create object */
ud->other_data = check_classifier_cfg (ud->cfg, NULL);
@@ -761,25 +779,21 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam
/* Create object */
ud->other_data = check_worker_conf (ud->cfg, NULL);
}
- else {
- /* Other params */
- if (g_ascii_strcasecmp (element_name, "variable") == 0) {
- if (extract_attr ("name", attribute_names, attribute_values, &res)) {
- g_strlcpy (ud->section_name, res, sizeof (res));
- ud->state = XML_READ_VARIABLE;
- }
- else {
- *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'name' is required for tag 'variable'");
- ud->state = XML_ERROR;
- }
-
- }
- else if (g_ascii_strcasecmp (element_name, "pidfile") == 0) {
- ud->state = XML_READ_PIDFILE;
+ else if (g_ascii_strcasecmp (element_name, "variable") == 0) {
+ if (extract_attr ("name", attribute_names, attribute_values, &res)) {
+ g_strlcpy (ud->section_name, res, sizeof (ud->section_name));
+ ud->state = XML_READ_VARIABLE;
}
- else if (g_ascii_strcasecmp (element_name, "filters") == 0) {
- ud->state = XML_READ_FILTERS;
+ else {
+ *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'name' is required for tag 'variable'");
+ ud->state = XML_ERROR;
}
+
+ }
+ else {
+ /* Extract other tags */
+ g_strlcpy (ud->section_name, element_name, sizeof (ud->section_name));
+ ud->state = XML_READ_VALUE;
}
break;
case XML_READ_MODULE:
@@ -857,14 +871,9 @@ rspamd_xml_end_element (GMarkupParseContext *context, const gchar *element_name,
case XML_READ_VARIABLE:
CHECK_TAG ("variable", TRUE);
break;
- case XML_READ_PIDFILE:
- CHECK_TAG ("pidfile", TRUE);
- break;
- case XML_READ_STATFILE_POOL:
- CHECK_TAG ("statfile_pool_size", TRUE);
- break;
- case XML_READ_FILTERS:
- CHECK_TAG ("filters", TRUE);
+ case XML_READ_VALUE:
+ /* Check tags parity */
+ CHECK_TAG (ud->section_name, TRUE);
break;
case XML_READ_LOGGING:
CHECK_TAG ("logging", FALSE);
@@ -930,9 +939,7 @@ rspamd_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len
}
break;
case XML_READ_VARIABLE:
- case XML_READ_PIDFILE:
- case XML_READ_STATFILE_POOL:
- case XML_READ_FILTERS:
+ case XML_READ_VALUE:
if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_MAIN)) {
*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val);
ud->state = XML_ERROR;
@@ -944,8 +951,6 @@ rspamd_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len
ud->state = XML_ERROR;
}
break;
- case XML_READ_PARAM:
- break;
default:
ud->state = XML_ERROR;
break;
@@ -961,3 +966,211 @@ rspamd_xml_error (GMarkupParseContext *context, GError *error, gpointer user_dat
msg_err ("xml parser error: %s, at state %d", error->message, ud->state);
}
+
+/* Dumper part */
+
+/* Dump specific sections */
+
+/* Dump main section variables */
+static gboolean
+xml_dump_main (struct config_file *cfg, FILE *f)
+{
+ char *escaped_str;
+
+ /* Print header comment */
+ fprintf (f, "<!-- Main section -->" CRLF);
+
+ escaped_str = g_markup_escape_text (cfg->temp_dir, -1);
+ fprintf (f, " <tempdir>%s</tempdir>" CRLF, escaped_str);
+ g_free (escaped_str);
+
+ escaped_str = g_markup_escape_text (cfg->pid_file, -1);
+ fprintf (f, " <pidfile>%s</pidfile>" CRLF, escaped_str);
+ g_free (escaped_str);
+
+ escaped_str = g_markup_escape_text (cfg->filters_str, -1);
+ fprintf (f, " <filters>%s</filters>" CRLF, escaped_str);
+ g_free (escaped_str);
+
+ if (cfg->checksum) {
+ escaped_str = g_markup_escape_text (cfg->checksum, -1);
+ fprintf (f, " <checksum>%s</checksum>" CRLF, escaped_str);
+ g_free (escaped_str);
+ }
+
+ fprintf (f, " <raw_mode>%s</raw_mode>" CRLF, cfg->raw_mode ? "yes" : "no");
+
+ /* Print footer comment */
+ fprintf (f, "<!-- End of main section -->" CRLF);
+
+ return TRUE;
+}
+
+/* Dump variables section */
+static void
+xml_variable_callback (gpointer key, gpointer value, gpointer user_data)
+{
+ FILE *f = user_data;
+ char *escaped_key, *escaped_value;
+
+ escaped_key = g_markup_escape_text (key, -1);
+ escaped_value = g_markup_escape_text (value, -1);
+ fprintf (f, " <variable name=\"%s\">%s</variable>" CRLF, escaped_key, escaped_value);
+ g_free (escaped_key);
+ g_free (escaped_value);
+}
+
+static gboolean
+xml_dump_variables (struct config_file *cfg, FILE *f)
+{
+ /* Print header comment */
+ fprintf (f, "<!-- Variables section -->" CRLF);
+
+ /* Iterate through variables */
+ g_hash_table_foreach (cfg->variables, xml_variable_callback, (gpointer)f);
+
+ /* Print footer comment */
+ fprintf (f, "<!-- End of variables section -->" CRLF);
+
+ return TRUE;
+}
+
+/* Workers */
+static void
+xml_worker_param_callback (gpointer key, gpointer value, gpointer user_data)
+{
+ FILE *f = user_data;
+ char *escaped_key, *escaped_value;
+
+ escaped_key = g_markup_escape_text (key, -1);
+ escaped_value = g_markup_escape_text (value, -1);
+ fprintf (f, " <param name=\"%s\">%s</param>" CRLF, escaped_key, escaped_value);
+ g_free (escaped_key);
+ g_free (escaped_value);
+}
+
+static gboolean
+xml_dump_workers (struct config_file *cfg, FILE *f)
+{
+ GList *cur;
+ struct worker_conf *wrk;
+ char *escaped_str;
+
+ /* Print header comment */
+ fprintf (f, "<!-- Workers section -->" CRLF);
+
+ /* Iterate through list */
+ cur = g_list_first (cfg->workers);
+ while (cur) {
+ wrk = cur->data;
+
+ fprintf (f, "<worker>" CRLF);
+ switch (wrk->type) {
+ case TYPE_WORKER:
+ fprintf (f, " <type>normal</type>" CRLF);
+ break;
+ case TYPE_CONTROLLER:
+ fprintf (f, " <type>controller</type>" CRLF);
+ break;
+ case TYPE_FUZZY:
+ fprintf (f, " <type>fuzzy</type>" CRLF);
+ break;
+ case TYPE_LMTP:
+ fprintf (f, " <type>lmtp</type>" CRLF);
+ break;
+ }
+ escaped_str = g_markup_escape_text (wrk->bind_host, -1);
+ fprintf (f, " <bind_socket>%s</bind_socket>" CRLF, escaped_str);
+ g_free (escaped_str);
+
+ fprintf (f, " <count>%u</count>" CRLF, wrk->count);
+ fprintf (f, " <maxfiles>%u</maxfiles>" CRLF, wrk->rlimit_nofile);
+ fprintf (f, " <maxcore>%u</maxcore>" CRLF, wrk->rlimit_maxcore);
+
+ /* Now dump other attrs */
+ fprintf (f, "<!-- Other params -->" CRLF);
+ g_hash_table_foreach (wrk->params, xml_worker_param_callback, f);
+
+ fprintf (f, "</worker>" CRLF);
+
+ cur = g_list_next (cur);
+ }
+
+ /* Print footer comment */
+ fprintf (f, "<!-- End of workers section -->" CRLF);
+
+ return TRUE;
+}
+
+/* Modules dump */
+static void
+xml_module_callback (gpointer key, gpointer value, gpointer user_data)
+{
+ FILE *f = user_data;
+ char *escaped_key, *escaped_value;
+ GList *cur;
+ struct module_opt *opt;
+
+ escaped_key = g_markup_escape_text (key, -1);
+ fprintf (f, "<!-- %s -->" CRLF, escaped_key);
+ fprintf (f, "<module name=\"%s\">" CRLF, escaped_key);
+ g_free (escaped_key);
+
+ cur = g_list_first (value);
+ while (cur) {
+ opt = cur->data;
+ escaped_key = g_markup_escape_text (opt->param, -1);
+ escaped_value = g_markup_escape_text (opt->value, -1);
+ fprintf (f, " <option name=\"%s\">%s</option>" CRLF, escaped_key, escaped_value);
+ g_free (escaped_key);
+ g_free (escaped_value);
+ cur = g_list_next (cur);
+ }
+ fprintf (f, "</module>" CRLF);
+}
+
+static gboolean
+xml_dump_modules (struct config_file *cfg, FILE *f)
+{
+ /* Print header comment */
+ fprintf (f, "<!-- Modules section -->" CRLF);
+
+ /* Iterate through variables */
+ g_hash_table_foreach (cfg->modules_opts, xml_module_callback, (gpointer)f);
+
+ /* Print footer comment */
+ fprintf (f, "<!-- End of modules section -->" CRLF);
+
+ return TRUE;
+}
+
+#define CHECK_RES do { if (!res) { fclose (f); return FALSE; } } while (0)
+gboolean
+xml_dump_config (struct config_file *cfg, const char *filename)
+{
+ FILE *f;
+ gboolean res = FALSE;
+
+ f = fopen (filename, "w");
+ if (f == NULL) {
+ msg_err ("cannot open file '%s': %s", filename, strerror (errno));
+ return FALSE;
+ }
+
+ /* Header */
+ fprintf (f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" CRLF "<rspamd>" CRLF);
+ /* Now dump all parts of config */
+ res = xml_dump_main (cfg, f);
+ CHECK_RES;
+ res = xml_dump_variables (cfg, f);
+ CHECK_RES;
+ res = xml_dump_workers (cfg, f);
+ CHECK_RES;
+ res = xml_dump_modules (cfg, f);
+ CHECK_RES;
+ /* Footer */
+ fprintf (f, "</rspamd>" CRLF);
+ fclose (f);
+
+ return TRUE;
+}
diff --git a/src/cfg_xml.h b/src/cfg_xml.h
index 513cf0f03..38c86c3d6 100644
--- a/src/cfg_xml.h
+++ b/src/cfg_xml.h
@@ -21,11 +21,9 @@ enum xml_read_state {
XML_READ_METRIC,
XML_READ_WORKER,
XML_READ_VARIABLE,
- XML_READ_PIDFILE,
- XML_READ_STATFILE_POOL,
- XML_READ_FILTERS,
XML_READ_LOGGING,
XML_ERROR,
+ XML_READ_VALUE,
XML_END
};
@@ -40,6 +38,8 @@ struct rspamd_xml_userdata {
/* Text is NULL terminated here */
typedef gboolean (*element_handler_func) (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset);
+/* Callbacks */
+
/* Called for open tags <foo bar="baz"> */
void rspamd_xml_start_element (GMarkupParseContext *context,
const gchar *element_name,
@@ -92,4 +92,9 @@ gboolean handle_factor (struct config_file *cfg, struct rspamd_xml_userdata *ctx
gboolean handle_module_opt (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset);
gboolean handle_log_type (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset);
gboolean handle_log_level (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset);
+
+
+/* Dumper functions */
+gboolean xml_dump_config (struct config_file *cfg, const char *filename);
+
#endif
diff --git a/src/main.c b/src/main.c
index fd250781d..350199632 100644
--- a/src/main.c
+++ b/src/main.c
@@ -638,6 +638,12 @@ main (int argc, char **argv, char **env)
}
fclose (f);
+ /* Dump it to xml */
+ if (get_config_checksum (rspamd->cfg)) {
+ xml_dump_config (rspamd->cfg, "/tmp/rspamd.xml");
+ }
+ /* Do post-load actions */
+ post_load_config (rspamd->cfg);
/* Init counters */
counters = rspamd_hash_new_shared (rspamd->server_pool, g_str_hash, g_str_equal, 64);