Cleanup configuration parser, add 'check-conf' option.
authorFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 9 Nov 2008 21:32:30 +0000 (22:32 +0100)
committerFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 9 Nov 2008 21:32:30 +0000 (22:32 +0100)
Signed-off-by: Florent Bruneau <florent.bruneau@polytechnique.org>
postlicyd/config.c
postlicyd/config.h
postlicyd/main-postlicyd.c

index 672faca..0111b7a 100644 (file)
@@ -113,70 +113,8 @@ static void config_exit()
 }
 module_exit(config_exit);
 
-static bool config_second_pass(config_t *config)
-{
-    bool ok = true;
-    if (config->filters.len > 0) {
-#       define QSORT_TYPE filter_t
-#       define QSORT_BASE config->filters.data
-#       define QSORT_NELT config->filters.len
-#       define QSORT_LT(a,b) strcmp(a->name, b->name) < 0
-#       include "qsort.c"
-    }
-
-    foreach (filter_t *filter, config->filters) {
-        if (!filter_update_references(filter, &config->filters)) {
-            ok = false;
-            break;
-        }
-    }}
-    if (!ok) {
-        return false;
-    }
-    if (!filter_check_safety(&config->filters)) {
-        return false;
-    }
-
-    ok = false;
-#define PARSE_CHECK(Expr, Fmt, ...)                                            \
-    if (!(Expr)) {                                                             \
-        err(Fmt, ##__VA_ARGS__);                                               \
-        return false;                                                          \
-    }
-    foreach (filter_param_t *param, config->params) {
-        switch (param->type) {
-#define   CASE(Param, State)                                                   \
-            case ATK_ ## Param ## _FILTER:                                     \
-              ok = true;                                                       \
-              config->entry_points[SMTP_ ## State]                             \
-                  = filter_find_with_name(&config->filters, param->value);     \
-              PARSE_CHECK(config->entry_points[SMTP_ ## State] >= 0,           \
-                          "invalid filter name %s", param->value);             \
-              break;
-          CASE(CLIENT,      CONNECT)
-          CASE(EHLO,        EHLO)
-          CASE(HELO,        HELO)
-          CASE(SENDER,      MAIL)
-          CASE(RECIPIENT,   RCPT)
-          CASE(DATA,        DATA)
-          CASE(END_OF_DATA, END_OF_MESSAGE)
-          CASE(VERIFY,      VRFY)
-          CASE(ETRN,        ETRN)
-#undef    CASE
-          FILTER_PARAM_PARSE_INT(PORT, config->port);
-          default: break;
-        }
-    }}
-    array_deep_wipe(config->params, filter_params_wipe);
 
-    if (!ok) {
-        err("no entry point defined");
-    }
-
-    return ok;
-}
-
-static bool config_load(config_t *config)
+static bool config_parse(config_t *config)
 {
     filter_t filter;
     file_map_t map;
@@ -388,17 +326,11 @@ read_filter:
     }
     end_of_section = true;
     READ_NEXT;
-    if (!filter_build(&filter)) {
-        READ_ERROR("invalid filter %s", filter.name);
-    }
     array_add(config->filters, filter);
     filter_init(&filter);
     goto read_section;
 
 ok:
-    if (!config_second_pass(config)) {
-        goto error;
-    }
     file_map_close(&map);
     return true;
 
@@ -413,6 +345,95 @@ error:
     return false;
 }
 
+static bool config_build_structure(config_t *config)
+{
+    bool ok = true;
+    if (config->filters.len > 0) {
+#       define QSORT_TYPE filter_t
+#       define QSORT_BASE config->filters.data
+#       define QSORT_NELT config->filters.len
+#       define QSORT_LT(a,b) strcmp(a->name, b->name) < 0
+#       include "qsort.c"
+    }
+
+    foreach (filter_t *filter, config->filters) {
+        if (!filter_update_references(filter, &config->filters)) {
+            ok = false;
+            break;
+        }
+    }}
+    if (!ok) {
+        return false;
+    }
+    if (!filter_check_safety(&config->filters)) {
+        return false;
+    }
+
+    ok = false;
+#define PARSE_CHECK(Expr, Fmt, ...)                                            \
+    if (!(Expr)) {                                                             \
+        err(Fmt, ##__VA_ARGS__);                                               \
+        return false;                                                          \
+    }
+    foreach (filter_param_t *param, config->params) {
+        switch (param->type) {
+#define   CASE(Param, State)                                                   \
+            case ATK_ ## Param ## _FILTER:                                     \
+              ok = true;                                                       \
+              config->entry_points[SMTP_ ## State]                             \
+                  = filter_find_with_name(&config->filters, param->value);     \
+              PARSE_CHECK(config->entry_points[SMTP_ ## State] >= 0,           \
+                          "invalid filter name %s", param->value);             \
+              break;
+          CASE(CLIENT,      CONNECT)
+          CASE(EHLO,        EHLO)
+          CASE(HELO,        HELO)
+          CASE(SENDER,      MAIL)
+          CASE(RECIPIENT,   RCPT)
+          CASE(DATA,        DATA)
+          CASE(END_OF_DATA, END_OF_MESSAGE)
+          CASE(VERIFY,      VRFY)
+          CASE(ETRN,        ETRN)
+#undef    CASE
+          FILTER_PARAM_PARSE_INT(PORT, config->port);
+          default: break;
+        }
+    }}
+    array_deep_wipe(config->params, filter_params_wipe);
+
+    if (!ok) {
+        err("no entry point defined");
+    }
+    return ok;
+}
+
+static bool config_build_filters(config_t *config)
+{
+    foreach (filter_t *filter, config->filters) {
+        if (!filter_build(filter)) {
+            return false;
+        }
+    }}
+
+    return true;
+}
+
+static bool config_load(config_t *config) {
+    if (!config_parse(config)) {
+        err("Invalid configuration: cannot parse configuration file \"%s\"", config->filename);
+        return false;
+    }
+    if (!config_build_structure(config)) {
+        err("Invalid configuration: inconsistent filter structure");
+        return false;
+    }
+    if (!config_build_filters(config)) {
+        err("Invalid configuration: invalid filter");
+        return false;
+    }
+    return true;
+}
+
 bool config_reload(config_t *config)
 {
     return config_load(config);
@@ -428,3 +449,14 @@ config_t *config_read(const char *file)
     }
     return config;
 }
+
+bool config_check(const char *file)
+{
+    config_t *config = config_new();
+    config->filename = file;
+
+    bool ret = config_parse(config) && config_build_structure(config);
+
+    config_delete(&config);
+    return ret;
+}
index 6584919..26fb619 100644 (file)
@@ -70,6 +70,9 @@ struct config_t {
 __attribute__((nonnull(1)))
 config_t *config_read(const char *file);
 
+__attribute__((nonnull(1)))
+bool config_check(const char *file);
+
 __attribute__((nonnull(1)))
 bool config_reload(config_t *config);
 
index 08c6a2b..87920be 100644 (file)
@@ -283,6 +283,7 @@ void usage(void)
           "    -f           stay in foreground\n"
           "    -d           grow logging level\n"
           "    -u           unsafe mode (don't drop privileges)\n"
+          "    -c           check-conf\n"
          , stderr);
 }
 
@@ -295,8 +296,9 @@ int main(int argc, char *argv[])
     bool daemonize = true;
     int port = DEFAULT_PORT;
     bool port_from_cli = false;
+    bool check_conf = false;
 
-    for (int c = 0; (c = getopt(argc, argv, "ufd" "l:p:")) >= 0; ) {
+    for (int c = 0; (c = getopt(argc, argv, "ufdc" "l:p:")) >= 0; ) {
         switch (c) {
           case 'p':
             pidfile = optarg;
@@ -314,6 +316,11 @@ int main(int argc, char *argv[])
           case 'd':
             ++log_level;
             break;
+          case 'c':
+            check_conf = true;
+            daemonize  = false;
+            unsafe     = true;
+            break;
           default:
             usage();
             return EXIT_FAILURE;
@@ -330,6 +337,9 @@ int main(int argc, char *argv[])
     }
 
     info("%s v%s...", DAEMON_NAME, DAEMON_VERSION);
+    if (check_conf) {
+        return config_check(argv[optind]) ? EXIT_SUCCESS : EXIT_FAILURE;
+    }
 
     if (pidfile_open(pidfile) < 0) {
         crit("unable to write pidfile %s", pidfile);