Hi Tim, I've finally implemented the replacement of the global variables table with a hash instead. However it now obviously breaks the "ifexist" argument that you added to Lua's set_var() that was designed to work around the growing table problem.
Given that the limitation used to be made on the existence of a similarly named variable anywhere in the process (and not of the variable in the current list), I conclude that it was only used to preserve precious resources and never to conditionally set a variable in one's context. As such we could simply remove this test and silently ignore the ifexist argument now. Do you agree with this ? I'd really like it if we could definitely get rid of this old mess! For reference I'm appending the current patch series. Thanks! Willy
>From 6abb2e9fd745311c091029933a86fe363d09a7fb Mon Sep 17 00:00:00 2001 From: Willy Tarreau <[email protected]> Date: Tue, 31 Aug 2021 08:13:25 +0200 Subject: MINOR: vars: rename vars_init() to vars_init_head() The vars_init() name is particularly confusing as it does not initialize the variables code but the head of a list of variables passed in arguments. And we'll soon need to have proper initialization code, so let's rename it now. --- include/haproxy/vars.h | 2 +- src/haproxy.c | 2 +- src/http_ana.c | 6 +++--- src/session.c | 2 +- src/stream.c | 6 +++--- src/tcpcheck.c | 2 +- src/vars.c | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/haproxy/vars.h b/include/haproxy/vars.h index f809c62d5..fedc8ca15 100644 --- a/include/haproxy/vars.h +++ b/include/haproxy/vars.h @@ -29,7 +29,7 @@ extern struct vars proc_vars; -void vars_init(struct vars *vars, enum vars_scope scope); +void vars_init_head(struct vars *vars, enum vars_scope scope); void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size); unsigned int var_clear(struct var *var); void vars_prune(struct vars *vars, struct session *sess, struct stream *strm); diff --git a/src/haproxy.c b/src/haproxy.c index db957256e..32a08441d 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -1529,7 +1529,7 @@ static void init(int argc, char **argv) hlua_init(); /* Initialize process vars */ - vars_init(&proc_vars, SCOPE_PROC); + vars_init_head(&proc_vars, SCOPE_PROC); global.tune.options |= GTUNE_USE_SELECT; /* select() is always available */ #if defined(USE_POLL) diff --git a/src/http_ana.c b/src/http_ana.c index b36054088..3443f7ec4 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2955,7 +2955,7 @@ int http_eval_after_res_rules(struct stream *s) if (s->vars_reqres.scope != SCOPE_RES) { if (!LIST_ISEMPTY(&s->vars_reqres.head)) vars_prune(&s->vars_reqres, s->sess, s); - vars_init(&s->vars_reqres, SCOPE_RES); + vars_init_head(&s->vars_reqres, SCOPE_RES); } ret = http_res_get_intercept_rule(s->be, &s->be->http_after_res_rules, s); @@ -5083,8 +5083,8 @@ struct http_txn *http_create_txn(struct stream *s) txn->auth.method = HTTP_AUTH_UNKNOWN; - vars_init(&s->vars_txn, SCOPE_TXN); - vars_init(&s->vars_reqres, SCOPE_REQ); + vars_init_head(&s->vars_txn, SCOPE_TXN); + vars_init_head(&s->vars_reqres, SCOPE_REQ); return txn; } diff --git a/src/session.c b/src/session.c index 92d03eaed..7ad5cbb3e 100644 --- a/src/session.c +++ b/src/session.c @@ -47,7 +47,7 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type sess->accept_date = date; /* user-visible date for logging */ sess->tv_accept = now; /* corrected date for internal use */ memset(sess->stkctr, 0, sizeof(sess->stkctr)); - vars_init(&sess->vars, SCOPE_SESS); + vars_init_head(&sess->vars, SCOPE_SESS); sess->task = NULL; sess->t_handshake = -1; /* handshake not done yet */ sess->t_idle = -1; diff --git a/src/stream.c b/src/stream.c index 132ee3abd..27062ea4b 100644 --- a/src/stream.c +++ b/src/stream.c @@ -451,8 +451,8 @@ struct stream *stream_new(struct session *sess, enum obj_type *origin, struct bu /* Initialise all the variables contexts even if not used. * This permits to prune these contexts without errors. */ - vars_init(&s->vars_txn, SCOPE_TXN); - vars_init(&s->vars_reqres, SCOPE_REQ); + vars_init_head(&s->vars_txn, SCOPE_TXN); + vars_init_head(&s->vars_reqres, SCOPE_REQ); /* this part should be common with other protocols */ if (si_reset(&s->si[0]) < 0) @@ -2201,7 +2201,7 @@ struct task *process_stream(struct task *t, void *context, unsigned int state) if (s->vars_reqres.scope != SCOPE_RES) { if (!LIST_ISEMPTY(&s->vars_reqres.head)) vars_prune(&s->vars_reqres, s->sess, s); - vars_init(&s->vars_reqres, SCOPE_RES); + vars_init_head(&s->vars_reqres, SCOPE_RES); } do { diff --git a/src/tcpcheck.c b/src/tcpcheck.c index e57b05fc6..a3b946c8c 100644 --- a/src/tcpcheck.c +++ b/src/tcpcheck.c @@ -2118,7 +2118,7 @@ int tcpcheck_main(struct check *check) set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area); goto out_end_tcpcheck; } - vars_init(&check->vars, SCOPE_CHECK); + vars_init_head(&check->vars, SCOPE_CHECK); rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list); /* Preset tcp-check variables */ diff --git a/src/vars.c b/src/vars.c index 5b35aa391..ebfb74ea3 100644 --- a/src/vars.c +++ b/src/vars.c @@ -195,8 +195,8 @@ void vars_prune_per_sess(struct vars *vars) _HA_ATOMIC_SUB(&var_global_size, size); } -/* This function init a list of variables. */ -void vars_init(struct vars *vars, enum vars_scope scope) +/* This function initializes a variables list head */ +void vars_init_head(struct vars *vars, enum vars_scope scope) { LIST_INIT(&vars->head); vars->scope = scope; -- 2.28.0
>From fa7f2f5b5c49c913ff1ffa208c37127054ce6f0f Mon Sep 17 00:00:00 2001 From: Willy Tarreau <[email protected]> Date: Tue, 31 Aug 2021 08:48:55 +0200 Subject: MINOR: vars: preset a random seed to hash variables names Variables names will be hashed, but for this we need a random seed. The XXH3() algorithms is bijective over the whole 64-bit space, which is great as it guarantees no collision for 1..8 byte names. But above that even if the risk is extremely faint, it theoretically exists and since variables may be set from Lua we'd rather do our best to limit the risk of controlled collision, hence the random seed. --- src/vars.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vars.c b/src/vars.c index ebfb74ea3..db651ab5b 100644 --- a/src/vars.c +++ b/src/vars.c @@ -41,6 +41,7 @@ static unsigned int var_sess_limit = 0; static unsigned int var_txn_limit = 0; static unsigned int var_reqres_limit = 0; static unsigned int var_check_limit = 0; +static uint64_t var_name_hash_seed = 0; __decl_rwlock(var_names_rwlock); @@ -1034,6 +1035,14 @@ static int vars_max_size_check(char **args, int section_type, struct proxy *curp return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit); } +/* early boot initialization */ +static void vars_init() +{ + var_name_hash_seed = ha_random64(); +} + +INITCALL0(STG_PREPARE, vars_init); + static void vars_deinit() { while (var_names_nb-- > 0) -- 2.28.0
>From 12e73282d0be537975bce61c0146bc63a823de91 Mon Sep 17 00:00:00 2001 From: Willy Tarreau <[email protected]> Date: Tue, 31 Aug 2021 08:51:02 +0200 Subject: BROKEN/WIP: vars: replace the global name index with a hash The global table of known variables names can only grow and was designed for static names that are registered at boot. Nowadays it's possible to set dynamic variable names from Lua or from the CLI, which causes a real problem that was partially addressed in 2.2 with commit 4e172c93f ("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). This patch simplifies all this by removing the need for a central registry of known names, and storing 64-bit hashes instead. This is highly sufficient given the low number of variables in each context. The hash is calculated using XXH64() which is bijective over the 64-bit space thus is guaranteed collision-free for 1..8 chars. Above that the risk remains around 1/2^64 per extra 8 chars so in practice this is highly sufficient for our usage. A random seed is used at boot to seed the hash so that it's not attackable from Lua for example. BROKEN: the "ifexist" variant of Lua's set_var() does not work anymore, which is by design, but that feature was a workaround for the previous design limitation. Should we simply get rid of it ? --- include/haproxy/action-t.h | 2 +- include/haproxy/vars-t.h | 8 +- src/vars.c | 180 +++++++++++++++---------------------- 3 files changed, 75 insertions(+), 115 deletions(-) diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index cceed8039..f70aaa121 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -163,7 +163,7 @@ struct act_rule { struct hlua_rule *hlua_rule; struct { struct sample_expr *expr; - const char *name; + uint64_t name_hash; enum vars_scope scope; } vars; struct { diff --git a/include/haproxy/vars-t.h b/include/haproxy/vars-t.h index e1e04524c..71400ab8b 100644 --- a/include/haproxy/vars-t.h +++ b/include/haproxy/vars-t.h @@ -41,15 +41,15 @@ struct vars { __decl_thread(HA_RWLOCK_T rwlock); }; -/* This struct describes a variable. */ +/* This struct describes a variable as stored as an argument */ struct var_desc { - const char *name; /* Contains the normalized variable name. */ + uint64_t name_hash; enum vars_scope scope; }; struct var { - struct list l; /* Used for chaining vars. */ - const char *name; /* Contains the variable name. */ + struct list l; /* Used for chaining vars. */ + uint64_t name_hash; /* XXH3() of the variable's name */ struct sample_data data; /* data storage. */ }; diff --git a/src/vars.c b/src/vars.c index db651ab5b..09ef195ff 100644 --- a/src/vars.c +++ b/src/vars.c @@ -205,29 +205,19 @@ void vars_init_head(struct vars *vars, enum vars_scope scope) HA_RWLOCK_INIT(&vars->rwlock); } -/* This function declares a new variable name. It returns a pointer - * on the string identifying the name. This function assures that - * the same name exists only once. - * - * This function check if the variable name is acceptable. - * - * The function returns NULL if an error occurs, and <err> is filled. - * In this case, the HAProxy must be stopped because the structs are - * left inconsistent. Otherwise, it returns the pointer on the global - * name. +/* This function returns a hash value and a scope for a variable name of a + * specified length. It makes sure that the scope is valid. It returns non-zero + * on success, 0 on failure. Neither hash nor scope may be NULL. */ -static char *register_name(const char *name, int len, enum vars_scope *scope, - int alloc, char **err) +static int vars_hash_name(const char *name, int len, enum vars_scope *scope, + uint64_t *hash, char **err) { - int i; - char **var_names2; const char *tmp; - char *res = NULL; /* Check length. */ if (len == 0) { memprintf(err, "Empty variable name cannot be accepted"); - return res; + return 0; } /* Check scope. */ @@ -264,73 +254,32 @@ static char *register_name(const char *name, int len, enum vars_scope *scope, else { memprintf(err, "invalid variable name '%s'. A variable name must be start by its scope. " "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", name); - return res; + return 0; } - if (alloc) - HA_RWLOCK_WRLOCK(VARS_LOCK, &var_names_rwlock); - else - HA_RWLOCK_RDLOCK(VARS_LOCK, &var_names_rwlock); - - - /* Look for existing variable name. */ - for (i = 0; i < var_names_nb; i++) - if (strncmp(var_names[i], name, len) == 0 && var_names[i][len] == '\0') { - res = var_names[i]; - goto end; - } - - if (!alloc) { - res = NULL; - goto end; - } - - /* Store variable name. If realloc fails, var_names remains valid */ - var_names2 = realloc(var_names, (var_names_nb + 1) * sizeof(*var_names)); - if (!var_names2) { - memprintf(err, "out of memory error"); - res = NULL; - goto end; - } - var_names_nb++; - var_names = var_names2; - var_names[var_names_nb - 1] = malloc(len + 1); - if (!var_names[var_names_nb - 1]) { - memprintf(err, "out of memory error"); - res = NULL; - goto end; - } - memcpy(var_names[var_names_nb - 1], name, len); - var_names[var_names_nb - 1][len] = '\0'; - /* Check variable name syntax. */ - tmp = var_names[var_names_nb - 1]; - while (*tmp) { + for (tmp = name; tmp < name + len; tmp++) { if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') { memprintf(err, "invalid syntax at char '%s'", tmp); - res = NULL; - goto end; + return 0; } - tmp++; } - res = var_names[var_names_nb - 1]; - end: - if (alloc) - HA_RWLOCK_WRUNLOCK(VARS_LOCK, &var_names_rwlock); - else - HA_RWLOCK_RDUNLOCK(VARS_LOCK, &var_names_rwlock); - - return res; + *hash = XXH3(name, len, var_name_hash_seed); + return 1; } -/* This function returns an existing variable or returns NULL. */ -static inline struct var *var_get(struct vars *vars, const char *name) +/* This function returns the variable from the given list that matches + * <name_hash> or returns NULL if not found. It's only a linked list since it + * is not expected to have many variables per scope (a few tens at best). + * The caller is responsible for ensuring that <vars> is properly locked. + */ +static struct var *var_get(struct vars *vars, uint64_t name_hash) { struct var *var; list_for_each_entry(var, &vars->head, l) - if (var->name == name) + if (var->name_hash == name_hash) return var; return NULL; } @@ -343,17 +292,18 @@ static int smp_fetch_var(const struct arg *args, struct sample *smp, const char return vars_get_by_desc(var_desc, smp); } -/* This function search in the <head> a variable with the same - * pointer value that the <name>. If the variable doesn't exists, - * create it. The function stores a copy of smp> if the variable. - * It returns 0 if fails, else returns 1. +/* This function searches in the variables list a variable whose name hash + * matches <name_hash>. If the variable doesn't exist, it's created. The + * function stores a copy of <smp> if the variable. It returns 0 if fails, + * otherwise returns 1. The caller is responsible for ensuring that <vars> + * is properly locked. */ -static int sample_store(struct vars *vars, const char *name, struct sample *smp) +static int sample_store(struct vars *vars, uint64_t name_hash, struct sample *smp) { struct var *var; /* Look for existing variable name. */ - var = var_get(vars, name); + var = var_get(vars, name_hash); if (var) { /* free its used memory. */ @@ -379,7 +329,7 @@ static int sample_store(struct vars *vars, const char *name, struct sample *smp) if (!var) return 0; LIST_APPEND(&vars->head, &var->l); - var->name = name; + var->name_hash = name_hash; } /* Set type. */ @@ -439,8 +389,11 @@ static int sample_store(struct vars *vars, const char *name, struct sample *smp) return 1; } -/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */ -static inline int sample_store_stream(const char *name, enum vars_scope scope, struct sample *smp) +/* Stores a sample into a variable matching name hash <name_hash> and scope + * <scope>. Returns 0 if fails, else returns 1. Note that stream may be null + * for SCOPE_SESS. (The stream and session are retrieved from the sample). + */ +static inline int sample_store_stream(uint64_t name_hash, enum vars_scope scope, struct sample *smp) { struct vars *vars; int ret; @@ -450,13 +403,16 @@ static inline int sample_store_stream(const char *name, enum vars_scope scope, s return 0; HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock); - ret = sample_store(vars, name, smp); + ret = sample_store(vars, name_hash, smp); HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock); return ret; } -/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */ -static inline int sample_clear_stream(const char *name, enum vars_scope scope, struct sample *smp) +/* Deletes a variable matching name hash <name_hash> and scope <scope> for the + * session and stream found in <smp>. Returns 0 if fails, else returns 1. Note + * that stream may be null for SCOPE_SESS. + */ +static inline int sample_clear_stream(uint64_t name_hash, enum vars_scope scope, struct sample *smp) { struct vars *vars; struct var *var; @@ -468,7 +424,7 @@ static inline int sample_clear_stream(const char *name, enum vars_scope scope, s /* Look for existing variable name. */ HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock); - var = var_get(vars, name); + var = var_get(vars, name_hash); if (var) { size = var_clear(var); var_accounting_diff(vars, smp->sess, smp->strm, -size); @@ -480,13 +436,19 @@ static inline int sample_clear_stream(const char *name, enum vars_scope scope, s /* Returns 0 if fails, else returns 1. */ static int smp_conv_store(const struct arg *args, struct sample *smp, void *private) { - return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp); + uint64_t seed = var_name_hash_seed; + uint64_t name_hash = XXH3(smp->data.u.str.area, smp->data.u.str.data, seed); + + return sample_store_stream(name_hash, args[0].data.var.scope, smp); } /* Returns 0 if fails, else returns 1. */ static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private) { - return sample_clear_stream(args[0].data.var.name, args[0].data.var.scope, smp); + uint64_t seed = var_name_hash_seed; + uint64_t name_hash = XXH3(smp->data.u.str.area, smp->data.u.str.data, seed); + + return sample_clear_stream(name_hash, args[0].data.var.scope, smp); } /* This functions check an argument entry and fill it with a variable @@ -495,8 +457,8 @@ static int smp_conv_clear(const struct arg *args, struct sample *smp, void *priv */ int vars_check_arg(struct arg *arg, char **err) { - char *name; enum vars_scope scope; + uint64_t hash; /* Check arg type. */ if (arg->type != ARGT_STR) { @@ -505,10 +467,7 @@ int vars_check_arg(struct arg *arg, char **err) } /* Register new variable name. */ - name = register_name(arg->data.str.area, arg->data.str.data, &scope, - 1, - err); - if (!name) + if (!vars_hash_name(arg->data.str.area, arg->data.str.data, &scope, &hash, err)) return 0; /* properly destroy the chunk */ @@ -516,56 +475,58 @@ int vars_check_arg(struct arg *arg, char **err) /* Use the global variable name pointer. */ arg->type = ARGT_VAR; - arg->data.var.name = name; + arg->data.var.name_hash = hash; arg->data.var.scope = scope; return 1; } -/* This function store a sample in a variable if it was already defined. +/* This function stores a sample in a variable if it was already defined. * Returns zero on failure and non-zero otherwise. The variable not being * defined is treated as a failure. */ int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp) { enum vars_scope scope; + uint64_t hash; /* Resolve name and scope. */ - name = register_name(name, len, &scope, 0, NULL); - if (!name) + if (!vars_hash_name(name, len, &scope, &hash, NULL)) return 0; - return sample_store_stream(name, scope, smp); + /* FIXME: we should now check if the variable *really* exists */ + + return sample_store_stream(hash, scope, smp); } -/* This function store a sample in a variable. +/* This function stores a sample in a variable. * Returns zero on failure and non-zero otherwise. */ int vars_set_by_name(const char *name, size_t len, struct sample *smp) { enum vars_scope scope; + uint64_t hash; /* Resolve name and scope. */ - name = register_name(name, len, &scope, 1, NULL); - if (!name) + if (!vars_hash_name(name, len, &scope, &hash, NULL)) return 0; - return sample_store_stream(name, scope, smp); + return sample_store_stream(hash, scope, smp); } -/* This function unset a variable if it was already defined. +/* This function unsets a variable if it was already defined. * Returns zero on failure and non-zero otherwise. */ int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp) { enum vars_scope scope; + uint64_t hash; /* Resolve name and scope. */ - name = register_name(name, len, &scope, 0, NULL); - if (!name) + if (!vars_hash_name(name, len, &scope, &hash, NULL)) return 0; - return sample_clear_stream(name, scope, smp); + return sample_clear_stream(hash, scope, smp); } @@ -582,10 +543,10 @@ int vars_get_by_name(const char *name, size_t len, struct sample *smp) struct vars *vars; struct var *var; enum vars_scope scope; + uint64_t hash; /* Resolve name and scope. */ - name = register_name(name, len, &scope, 0, NULL); - if (!name) + if (!vars_hash_name(name, len, &scope, &hash, NULL)) return 0; /* Select "vars" pool according with the scope. */ @@ -595,7 +556,7 @@ int vars_get_by_name(const char *name, size_t len, struct sample *smp) /* Get the variable entry. */ HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock); - var = var_get(vars, name); + var = var_get(vars, hash); if (!var) { HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock); return 0; @@ -632,7 +593,7 @@ int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp) /* Get the variable entry. */ HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock); - var = var_get(vars, var_desc->name); + var = var_get(vars, var_desc->name_hash); if (!var) { HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock); return 0; @@ -676,7 +637,7 @@ static enum act_return action_store(struct act_rule *rule, struct proxy *px, return ACT_RET_CONT; /* Store the sample, and ignore errors. */ - sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp); + sample_store_stream(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp); return ACT_RET_CONT; } @@ -690,7 +651,7 @@ static enum act_return action_clear(struct act_rule *rule, struct proxy *px, smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL); /* Clear the variable using the sample context, and ignore errors. */ - sample_clear_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp); + sample_clear_stream(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp); return ACT_RET_CONT; } @@ -760,8 +721,7 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy return ACT_RET_PRS_ERR; } - rule->arg.vars.name = register_name(var_name, var_len, &rule->arg.vars.scope, 1, err); - if (!rule->arg.vars.name) + if (!vars_hash_name(var_name, var_len, &rule->arg.vars.scope, &rule->arg.vars.name_hash, err)) return ACT_RET_PRS_ERR; /* There is no fetch method when variable is unset. Just set the right -- 2.28.0
>From f25d47aa0746285ae6ec9f01d0621d46775cbabd Mon Sep 17 00:00:00 2001 From: Willy Tarreau <[email protected]> Date: Tue, 31 Aug 2021 09:00:16 +0200 Subject: CLEANUP: vars: remove the now unused var_names array This was the table of all variable names known to the haproxy process. It's not used anymore. --- src/vars.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/vars.c b/src/vars.c index 09ef195ff..b28c8da98 100644 --- a/src/vars.c +++ b/src/vars.c @@ -25,14 +25,6 @@ DECLARE_STATIC_POOL(var_pool, "vars", sizeof(struct var)); /* list of variables for the process scope. */ struct vars proc_vars THREAD_ALIGNED(64); -/* This array contain all the names of all the HAProxy vars. - * This permits to identify two variables name with - * only one pointer. It permits to not using strdup() for - * each variable name used during the runtime. - */ -static char **var_names = NULL; -static int var_names_nb = 0; - /* This array of int contains the system limits per context. */ static unsigned int var_global_limit = 0; static unsigned int var_global_size = 0; @@ -43,8 +35,6 @@ static unsigned int var_reqres_limit = 0; static unsigned int var_check_limit = 0; static uint64_t var_name_hash_seed = 0; -__decl_rwlock(var_names_rwlock); - /* returns the struct vars pointer for a session, stream and scope, or NULL if * it does not exist. */ @@ -1003,15 +993,6 @@ static void vars_init() INITCALL0(STG_PREPARE, vars_init); -static void vars_deinit() -{ - while (var_names_nb-- > 0) - free(var_names[var_names_nb]); - free(var_names); -} - -REGISTER_POST_DEINIT(vars_deinit); - static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "var", smp_fetch_var, ARG1(1,STR), smp_check_var, SMP_T_STR, SMP_USE_CONST }, -- 2.28.0

