use private data structure for managing static table content
authoreric <eric@openbsd.org>
Fri, 28 Dec 2018 10:42:18 +0000 (10:42 +0000)
committereric <eric@openbsd.org>
Fri, 28 Dec 2018 10:42:18 +0000 (10:42 +0000)
ok gilles@

usr.sbin/smtpd/table_static.c

index 361be8f..d7e9698 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: table_static.c,v 1.29 2018/12/27 15:04:59 eric Exp $  */
+/*     $OpenBSD: table_static.c,v 1.30 2018/12/28 10:42:18 eric Exp $  */
 
 /*
  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -26,7 +26,8 @@
 #include <arpa/inet.h>
 
 #include <ctype.h>
-#include <err.h>
+#include <errno.h>
+
 #include <event.h>
 #include <fcntl.h>
 #include <imsg.h>
 #include "smtpd.h"
 #include "log.h"
 
+struct table_static_priv {
+       int              type;
+       struct dict      dict;
+       void            *iter;
+};
+
 /* static backend */
 static int table_static_config(struct table *);
 static int table_static_add(struct table *, const char *, const char *);
@@ -75,8 +82,47 @@ static struct keycmp {
 };
 
 
+static void
+table_static_priv_free(struct table_static_priv *priv)
+{
+       void *p;
+
+       while (dict_poproot(&priv->dict, (void **)&p))
+               if (p != priv)
+                       free(p);
+       free(priv);
+}
+
 static int
-table_static_config(struct table *t)
+table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
+{
+       char lkey[1024];
+       void *old, *new = NULL;
+
+       if (!lowercase(lkey, key, sizeof lkey)) {
+               errno = ENAMETOOLONG;
+               return (-1);
+       }
+
+       if (val) {
+               new = strdup(val);
+               if (new == NULL)
+                       return (-1);
+       }
+
+       /* use priv if value is null, so we can detect duplicate entries */
+       old = dict_set(&priv->dict, lkey, new ? new : priv);
+       if (old) {
+               if (old != priv)
+                       free(old);
+               return (1);
+       }
+
+       return (0);
+}
+
+static int
+table_static_priv_load(struct table_static_priv *priv, const char *path)
 {
        FILE    *fp;
        char    *buf = NULL, *p;
@@ -85,14 +131,10 @@ table_static_config(struct table *t)
        ssize_t  flen;
        char    *keyp;
        char    *valp;
-       size_t   ret = 0;
-
-       /* no config ? ok */
-       if (*t->t_config == '\0')
-               return 1;
+       int      ret = 0;
 
-       if ((fp = fopen(t->t_config, "r")) == NULL) {
-               log_warn("warn: Table \"%s\"", t->t_config);
+       if ((fp = fopen(path, "r")) == NULL) {
+               log_warn("%s: fopen", path);
                return 0;
        }
 
@@ -111,29 +153,29 @@ table_static_config(struct table *t)
                while (isspace((unsigned char)keyp[flen - 1]))
                        keyp[--flen] = '\0';
                if (*keyp == '#') {
-                       if (t->t_type == T_NONE) {
+                       if (priv->type == T_NONE) {
                                keyp++;
                                while (isspace((unsigned char)*keyp))
                                        ++keyp;
                                if (!strcmp(keyp, "@list"))
-                                       t->t_type = T_LIST;
+                                       priv->type = T_LIST;
                        }
                        continue;
                }
 
-               if (t->t_type == T_NONE) {
+               if (priv->type == T_NONE) {
                        for (p = keyp; *p; p++) {
                                if (*p == ' ' || *p == '\t' || *p == ':') {
-                                       t->t_type = T_HASH;
+                                       priv->type = T_HASH;
                                        break;
                                }
                        }
-                       if (t->t_type == T_NONE)
-                               t->t_type = T_LIST;
+                       if (priv->type == T_NONE)
+                               priv->type = T_LIST;
                }
 
-               if (t->t_type == T_LIST) {
-                       table_add(t, keyp, NULL);
+               if (priv->type == T_LIST) {
+                       table_static_priv_add(priv, keyp, NULL);
                        continue;
                }
 
@@ -152,22 +194,22 @@ table_static_config(struct table *t)
                                valp = NULL;
                }
                if (valp == NULL) {
-                       log_warnx("%s: invalid map entry line %d", t->t_config,
-                           lineno);
+                       log_warnx("%s: invalid map entry line %d",
+                           path, lineno);
                        goto end;
                }
 
-               table_add(t, keyp, valp);
+               table_static_priv_add(priv, keyp, valp);
        }
 
        if (ferror(fp)) {
-               log_warn("%s: getline", t->t_config);
+               log_warn("%s: getline", path);
                goto end;
        }
 
        /* Accept empty alias files; treat them as hashes */
-       if (t->t_type == T_NONE && t->t_backend->services & K_ALIAS)
-           t->t_type = T_HASH;
+       if (priv->type == T_NONE)
+               priv->type = T_HASH;
 
        ret = 1;
 end:
@@ -177,34 +219,69 @@ end:
 }
 
 static int
-table_static_add(struct table *table, const char *key, const char *val)
+table_static_config(struct table *t)
 {
-       char    lkey[1024], *old;
+       struct table_static_priv *priv, *old;
 
-       if (!lowercase(lkey, key, sizeof lkey)) {
-               log_warnx("warn: lookup key too long: %s", key);
+       /* already up, and no config file? ok */
+       if (t->t_handle && *t->t_config == '\0')
+               return 1;
+
+       /* new config */
+       priv = calloc(1, sizeof(*priv));
+       if (priv == NULL)
                return 0;
+       priv->type = t->t_type;
+       dict_init(&priv->dict);
+       
+       if (*t->t_config) {
+               /* load the config file */
+               if (table_static_priv_load(priv, t->t_config) == 0) {
+                       table_static_priv_free(priv);
+                       return 0;
+               }
        }
 
-       old = dict_set(&table->t_dict, lkey, val ? xstrdup(val) : NULL);
-       if (old) {
-               log_warnx("warn: duplicate key \"%s\" in static table \"%s\"",
-                   lkey, table->t_name);
-               free(old);
+       if ((old = t->t_handle))
+               table_static_priv_free(old);
+       t->t_handle = priv;
+       t->t_type = priv->type;
+
+       return 1;
+}
+
+static int
+table_static_add(struct table *table, const char *key, const char *val)
+{
+       struct table_static_priv *priv = table->t_handle;
+       int r;
+
+       /* cannot add to a table read from a file */
+       if (*table->t_config)
+               return 0;
+
+       if (priv == NULL) {
+               if (table_static_config(table) == 0)
+                       return 0;
+               priv = table->t_handle;
        }
 
+       r = table_static_priv_add(priv, key, val);
+       if (r == -1)
+               return 0;
        return 1;
 }
 
 static void
 table_static_dump(struct table *table)
 {
+       struct table_static_priv *priv = table->t_handle;
        const char *key;
        char *value;
        void *iter;
 
        iter = NULL;
-       while (dict_iter(&table->t_dict, &iter, &key, (void**)&value)) {
+       while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
                if (value)
                        log_debug("     \"%s\" -> \"%s\"", key, value);
                else
@@ -215,29 +292,11 @@ table_static_dump(struct table *table)
 static int
 table_static_update(struct table *table)
 {
-       struct table    *t;
-       void            *p = NULL;
-
-       /* no config ? ok */
-       if (table->t_config[0] == '\0')
-               goto ok;
-
-       t = table_create(env, "static", table->t_name, "update", table->t_config);
-       if (!table_config(t))
-               goto err;
-
-       /* replace former table, frees t */
-       while (dict_poproot(&table->t_dict, (void **)&p))
-               free(p);
-       dict_merge(&table->t_dict, &t->t_dict);
-       table_destroy(env, t);
-
-ok:
-       log_info("info: Table \"%s\" successfully updated", table->t_name);
-       return 1;
+       if (table_static_config(table) == 1) {
+               log_info("info: Table \"%s\" successfully updated", table->t_name);
+               return 1;
+       }
 
-err:
-       table_destroy(env, t);
        log_info("info: Failed to update table \"%s\"", table->t_name);
        return 0;
 }
@@ -245,20 +304,26 @@ err:
 static int
 table_static_open(struct table *table)
 {
-       table->t_handle = table;
+       if (table->t_handle == NULL)
+               return table_static_config(table);
        return 1;
 }
 
 static void
 table_static_close(struct table *table)
 {
+       struct table_static_priv *priv = table->t_handle;
+
+       if (priv)
+               table_static_priv_free(priv);
        table->t_handle = NULL;
 }
 
 static int
-table_static_lookup(struct table *m, enum table_service service, const char *key,
+table_static_lookup(struct table *table, enum table_service service, const char *key,
     char **dst)
 {
+       struct table_static_priv *priv = table->t_handle;
        char           *line;
        int             ret;
        int            (*match)(const char *, const char *) = NULL;
@@ -274,7 +339,7 @@ table_static_lookup(struct table *m, enum table_service service, const char *key
        line = NULL;
        iter = NULL;
        ret = 0;
-       while (dict_iter(&m->t_dict, &iter, &k, (void **)&v)) {
+       while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
                if (match) {
                        if (match(key, k)) {
                                line = v;
@@ -307,11 +372,12 @@ table_static_lookup(struct table *m, enum table_service service, const char *key
 static int
 table_static_fetch(struct table *t, enum table_service service, char **dst)
 {
+       struct table_static_priv *priv = t->t_handle;
        const char *k;
 
-       if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) {
-               t->t_iter = NULL;
-               if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL))
+       if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
+               priv->iter = NULL;
+               if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
                        return 0;
        }