[core] config_plugin_values_init() new interface

new data structures and interface for processing config directives
(towards more efficient approach to config merging)

continue work to isolate data_config
personal/stbuehler/ci-build
Glenn Strauss 3 years ago
parent aba290e0bd
commit b87e8783c4

@ -13,7 +13,7 @@ struct data_methods {
void (*print)(const struct data_unset *p, int depth);
};
typedef enum { TYPE_UNSET, TYPE_STRING, TYPE_OTHER, TYPE_ARRAY, TYPE_INTEGER, TYPE_DONOTUSE, TYPE_CONFIG } data_type_t;
typedef enum { TYPE_STRING, TYPE_ARRAY, TYPE_INTEGER, TYPE_CONFIG, TYPE_OTHER } data_type_t;
#define DATA_UNSET \
buffer key; \
const struct data_methods *fn; /* function table */ \

@ -8,6 +8,7 @@
#include "sock_addr.h"
#include "configfile.h"
#include "plugin.h"
#include <string.h>
#include <stdlib.h> /* strtol */
@ -24,6 +25,221 @@
*/
int config_plugin_values_init(server * const srv, void *p_d, const config_plugin_keys_t * const cpk, const char * const mname) {
plugin_data_base * const p = (plugin_data_base *)p_d;
array * const touched = srv->config_touched;
unsigned char matches[4096]; /*directives matches (4k is way too many!)*/
unsigned short contexts[4096]; /*conditions matches (4k is way too many!)*/
uint32_t n = 0;
int rc = 1; /* default is success */
force_assert(sizeof(matches) >= srv->config_context->used);
/* traverse config contexts twice: once to count, once to store matches */
for (uint32_t u = 0; u < srv->config_context->used; ++u) {
const array *ca =
((data_config const *)srv->config_context->data[u])->value;
matches[n] = 0;
for (int i = 0; cpk[i].ktype != T_CONFIG_UNSET; ++i) {
data_unset * const du =
array_get_element_klen(ca, cpk[i].k, cpk[i].klen);
if (NULL == du) continue; /* not found */
++matches[n];
array_set_key_value(touched,cpk[i].k,cpk[i].klen,CONST_STR_LEN(""));
if (cpk[i].scope == T_CONFIG_SCOPE_SERVER && 0 != u) {
/* server scope options should be set only in server scope */
log_error(srv->errh, __FILE__, __LINE__,
"DEPRECATED: do not set server options in conditionals, "
"variable: %s", cpk[i].k);
}
}
if (matches[n]) contexts[n++] = (unsigned short)u;
}
uint32_t elts = 0;
for (uint32_t u = 0; u < n; ++u) elts += matches[u];
p->nconfig = n;
/*(+1 to include global scope, whether or not any directives exist)*/
/*(+n for extra element to end each list)*/
p->cvlist = (config_plugin_value_t *)
calloc(1+n+n+elts, sizeof(config_plugin_value_t));
force_assert(p->cvlist);
elts = 1+n;
/* shift past first element if no directives in global scope */
const uint32_t shft = (0 != n && 0 != contexts[0]);
if (shft) ++p->nconfig;
for (uint32_t u = 0; u < n; ++u) {
config_plugin_value_t * const cpv = p->cvlist+shft+u;
cpv->k_id = (int)contexts[u];
cpv->v.u2[0] = elts;
cpv->v.u2[1] = matches[u];
elts += matches[u]+1; /* +1 to end list with cpv->k_id = -1 */
}
for (uint32_t u = 0; u < n; ++u) {
const array *ca =
((data_config const *)srv->config_context->data[contexts[u]])->value;
config_plugin_value_t *cpv = p->cvlist + p->cvlist[shft+u].v.u2[0];
for (int i = 0; cpk[i].ktype != T_CONFIG_UNSET; ++i) {
data_unset * const du =
array_get_element_klen(ca, cpk[i].k, cpk[i].klen);
if (NULL == du) continue; /* not found */
cpv->k_id = i;
cpv->vtype = cpk[i].ktype;
switch (cpk[i].ktype) {
case T_CONFIG_ARRAY:
if (du->type == TYPE_ARRAY) {
cpv->v.a = &((const data_array *)du)->value;
/* future: might provide modifiers to perform one of
* array_is_{vlist,kvany,kvarray,kvstring}() tests
* and provide generic error message if mismatch */
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"%s should have been an array of strings like "
"... = ( \"...\" )", cpk[i].k);
rc = 0;
continue;
}
break;
case T_CONFIG_STRING:
if (du->type == TYPE_STRING) {
cpv->v.b = &((const data_string *)du)->value;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"%s should have been a string like ... = \"...\"",
cpk[i].k);
rc = 0;
continue;
}
break;
case T_CONFIG_SHORT:
switch(du->type) {
case TYPE_INTEGER:
cpv->v.shrt =
(unsigned short)((const data_integer *)du)->value;
break;
case TYPE_STRING: {
/* If the value came from an environment variable, then it
* is a data_string, although it may contain a number in
* ASCII decimal format. We try to interpret the string as
* a decimal short before giving up, in order to support
* setting numeric values with environment variables
* (e.g. port number).
*/
const char * const v = ((const data_string *)du)->value.ptr;
if (v && *v) {
char *e;
long l = strtol(v, &e, 10);
if (e != v && !*e && l >= 0 && l <= 65535) {
cpv->v.shrt = (unsigned short)l;
break;
}
}
log_error(srv->errh, __FILE__, __LINE__,
"got a string but expected a short: %s %s", cpk[i].k, v);
rc = 0;
continue;
}
default:
log_error(srv->errh, __FILE__, __LINE__,
"unexpected type for key: %s %d expected a short integer,"
" range 0 ... 65535", cpk[i].k, du->type);
rc = 0;
continue;
}
break;
case T_CONFIG_INT:
switch(du->type) {
case TYPE_INTEGER:
cpv->v.u = ((const data_integer *)du)->value;
break;
case TYPE_STRING: {
const char * const v = ((const data_string *)du)->value.ptr;
if (v && *v) {
char *e;
long l = strtol(v, &e, 10);
if (e != v && !*e && l >= 0) {
cpv->v.shrt = (unsigned int)l;
break;
}
}
log_error(srv->errh, __FILE__, __LINE__,
"got a string but expected an integer: %s %s",cpk[i].k,v);
rc = 0;
continue;
}
default:
log_error(srv->errh, __FILE__, __LINE__,
"unexpected type for key: %s %d expected an integer, "
"range 0 ... 4294967295", cpk[i].k, du->type);
rc = 0;
continue;
}
break;
case T_CONFIG_BOOL:
if (du->type == TYPE_STRING) {
const buffer *b = &((const data_string *)du)->value;
if (buffer_eq_icase_slen(b, CONST_STR_LEN("enable"))
|| buffer_eq_icase_slen(b, CONST_STR_LEN("true"))) {
cpv->v.u = 1;
}
else if (buffer_eq_icase_slen(b, CONST_STR_LEN("disable"))
|| buffer_eq_icase_slen(b,CONST_STR_LEN("false"))){
cpv->v.u = 0;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: unexpected value for key: %s %s "
"(enable|disable)", cpk[i].k, b->ptr);
rc = 0;
continue;
}
}
else if (du->type == TYPE_INTEGER) {
cpv->v.u = (0 != ((const data_integer *)du)->value);
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: unexpected type for key: %s (string) "
"\"(enable|disable)\"", cpk[i].k);
rc = 0;
continue;
}
break;
case T_CONFIG_LOCAL:
case T_CONFIG_UNSET:
continue;
case T_CONFIG_UNSUPPORTED:
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: found unsupported key: %s (%s)", cpk[i].k, mname);
srv->config_unsupported = 1;
continue;
case T_CONFIG_DEPRECATED:
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: found deprecated key: %s (%s)", cpk[i].k, mname);
srv->config_deprecated = 1;
continue;
}
++cpv;
}
cpv->k_id = -1; /* indicate list end */
}
return rc;
}
/* handle global options */
/* parse config array */

@ -126,18 +126,46 @@ typedef enum { T_CONFIG_UNSET,
T_CONFIG_STRING,
T_CONFIG_SHORT,
T_CONFIG_INT,
T_CONFIG_BOOLEAN,
T_CONFIG_BOOL,
T_CONFIG_ARRAY,
T_CONFIG_LOCAL,
T_CONFIG_DEPRECATED,
T_CONFIG_UNSUPPORTED
} config_values_type_t;
#define T_CONFIG_BOOLEAN T_CONFIG_BOOL
typedef enum { T_CONFIG_SCOPE_UNSET,
T_CONFIG_SCOPE_SERVER,
T_CONFIG_SCOPE_CONNECTION
} config_scope_type_t;
typedef struct {
int k_id;
config_values_type_t vtype;
union v_u {
void *v;
const array *a;
const buffer *b;
const char *s;
unsigned int u;
unsigned short int shrt;
double d;
off_t o;
uint32_t u2[2];
} v;
} config_plugin_value_t;
typedef struct {
const char *k;
uint32_t klen;
/*uint32_t k_id;*//*(array index is used for k_id)*/
config_values_type_t ktype;
config_scope_type_t scope;
} config_plugin_keys_t;
__attribute_cold__
int config_plugin_values_init(server *srv, void *p_d, const config_plugin_keys_t *cpk, const char *mname);
typedef struct {
const char *key;
void *destination;

@ -27,7 +27,9 @@
#define REQUESTDONE_FUNC CONNECTION_FUNC
#define URIHANDLER_FUNC CONNECTION_FUNC
#define PLUGIN_DATA int id; int nconfig
#define PLUGIN_DATA int id; \
int nconfig; \
config_plugin_value_t *cvlist
typedef struct {
PLUGIN_DATA;

Loading…
Cancel
Save