blob: a17189c09937137f2bf450568fc2d3b81f443483 [file] [log] [blame]
/***
* Copyright 2020 HAProxy Technologies
*
* This file is part of the HAProxy OpenTracing filter.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "include.h"
#ifdef DEBUG_OT
/***
* NAME
* flt_ot_vars_scope_dump -
*
* ARGUMENTS
* vars -
* scope -
*
* DESCRIPTION
* Function prints the contents of all variables defined for a particular
* scope.
*
* RETURN VALUE
* This function does not return a value.
*/
static void flt_ot_vars_scope_dump(struct vars *vars, const char *scope)
{
const struct var *var;
if (vars == NULL)
return;
HA_RWLOCK_RDLOCK(VARS_LOCK, &(vars->rwlock));
list_for_each_entry(var, &(vars->head), l)
FLT_OT_DBG(2, "'%s.%s' -> '%.*s'", scope, var->name, (int)var->data.u.str.data, var->data.u.str.area);
HA_RWLOCK_RDUNLOCK(VARS_LOCK, &(vars->rwlock));
}
/***
* NAME
* flt_ot_vars_dump -
*
* ARGUMENTS
* s -
*
* DESCRIPTION
* Function prints the contents of all variables grouped by individual
* scope.
*
* RETURN VALUE
* This function does not return a value.
*/
void flt_ot_vars_dump(struct stream *s)
{
FLT_OT_FUNC("%p", s);
/*
* It would be nice if we could use the get_vars() function from HAProxy
* source here to get the value of the 'vars' pointer, but it is defined
* as 'static inline', so unfortunately none of this is possible.
*/
flt_ot_vars_scope_dump(&(proc_vars), "PROC");
flt_ot_vars_scope_dump(&(s->sess->vars), "SESS");
flt_ot_vars_scope_dump(&(s->vars_txn), "TXN");
flt_ot_vars_scope_dump(&(s->vars_reqres), "REQ/RES");
FLT_OT_RETURN();
}
#endif /* DEBUG_OT */
/***
* NAME
* flt_ot_smp_init -
*
* ARGUMENTS
* s -
* smp -
* opt -
* type -
* data -
*
* DESCRIPTION
* The function initializes the value of the 'smp' structure. If the 'data'
* argument is set, then the 'sample_data' member of the 'smp' structure is
* also initialized.
*
* RETURN VALUE
* This function does not return a value.
*/
static inline void flt_ot_smp_init(struct stream *s, struct sample *smp, uint opt, int type, const char *data)
{
(void)memset(smp, 0, sizeof(*smp));
(void)smp_set_owner(smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
if (data != NULL) {
smp->data.type = type;
chunk_initstr(&(smp->data.u.str), data);
}
}
/***
* NAME
* flt_ot_get_vars -
*
* ARGUMENTS
* s -
* scope -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns the struct vars pointer for a stream and scope, or NULL if it does
* not exist.
*/
static inline struct vars *flt_ot_get_vars(struct stream *s, const char *scope)
{
struct vars *retptr = NULL;
if (strcasecmp(scope, "proc") == 0)
retptr = &(proc_vars);
else if (strcasecmp(scope, "sess") == 0)
retptr = (&(s->sess->vars));
else if (strcasecmp(scope, "txn") == 0)
retptr = (&(s->vars_txn));
else if ((strcasecmp(scope, "req") == 0) || (strcasecmp(scope, "res") == 0))
retptr = (&(s->vars_reqres));
return retptr;
}
/***
* NAME
* flt_ot_normalize_name -
*
* ARGUMENTS
* var_name -
* size -
* len -
* name -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_normalize_name(char *var_name, size_t size, int *len, const char *name, char **err)
{
int retval = 0;
FLT_OT_FUNC("%p, %zu, %p, \"%s\", %p:%p", var_name, size, len, name, FLT_OT_DPTR_ARGS(err));
if (!FLT_OT_STR_ISVALID(name))
FLT_OT_RETURN_INT(retval);
/*
* In case the name of the variable consists of several elements,
* the character '.' is added between them.
*/
if ((*len == 0) || (var_name[*len - 1] == '.'))
/* Do nothing. */;
else if (*len < (size - 1))
var_name[(*len)++] = '.';
else
retval = -1;
/*
* HAProxy does not allow the use of variable names containing '-'
* or ' '. This of course applies to HTTP header names as well.
* Also, here the capital letters are converted to lowercase.
*/
while (retval != -1)
if (*len >= (size - 1)) {
FLT_OT_ERR("failed to normalize variable name, buffer too small");
retval = -1;
} else {
uint8_t ch = name[retval];
if (ch == '\0')
break;
else if (ch == '-')
ch = FLT_OT_VAR_CHAR_DASH;
else if (ch == ' ')
ch = FLT_OT_VAR_CHAR_SPACE;
else if (isupper(ch))
ch = ist_lc[ch];
var_name[(*len)++] = ch;
retval++;
}
var_name[*len] = '\0';
FLT_OT_DBG(3, "var_name: \"%s\" %d/%d", var_name, retval, *len);
if (retval == -1)
*len = retval;
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_var_name -
*
* ARGUMENTS
* scope -
* prefix -
* name -
* var_name -
* size -
* err -
*
* DESCRIPTION
* The function initializes the value of the 'smp' structure. If the 'data'
* argument is set, then the 'sample_data' member of the 'smp' structure is
* also initialized.
*
* RETURN VALUE
* -
*/
static int flt_ot_var_name(const char *scope, const char *prefix, const char *name, char *var_name, size_t size, char **err)
{
int retval = 0;
FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p, %zu, %p:%p", scope, prefix, name, var_name, size, FLT_OT_DPTR_ARGS(err));
if (flt_ot_normalize_name(var_name, size, &retval, scope, err) >= 0)
if (flt_ot_normalize_name(var_name, size, &retval, prefix, err) >= 0)
(void)flt_ot_normalize_name(var_name, size, &retval, name, err);
if (retval == -1)
FLT_OT_ERR("failed to construct variable name '%s.%s.%s'", scope, prefix, name);
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_var_register -
*
* ARGUMENTS
* scope -
* prefix -
* name -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
int flt_ot_var_register(const char *scope, const char *prefix, const char *name, char **err)
{
struct arg arg;
char var_name[BUFSIZ];
int retval = -1, var_name_len;
FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p:%p", scope, prefix, name, FLT_OT_DPTR_ARGS(err));
var_name_len = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(retval);
/* Set <size> to 0 to not release var_name memory in vars_check_arg(). */
(void)memset(&arg, 0, sizeof(arg));
arg.type = ARGT_STR;
arg.data.str.area = var_name;
arg.data.str.data = var_name_len;
if (vars_check_arg(&arg, err) == 0) {
FLT_OT_ERR_APPEND("failed to register variable '%s': %s", var_name, *err);
} else {
FLT_OT_DBG(2, "variable '%s' registered", arg.data.var.name);
retval = var_name_len;
}
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_var_set -
*
* ARGUMENTS
* s -
* scope -
* prefix -
* name -
* value -
* opt -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
int flt_ot_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err)
{
struct sample smp;
char var_name[BUFSIZ];
int retval = -1, var_name_len;
FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, value, opt, FLT_OT_DPTR_ARGS(err));
var_name_len = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(retval);
flt_ot_smp_init(s, &smp, opt, SMP_T_STR, value);
if (vars_set_by_name_ifexist(var_name, var_name_len, &smp) == 0) {
FLT_OT_ERR("failed to set variable '%s'", var_name);
} else {
FLT_OT_DBG(2, "variable '%s' set", var_name);
retval = var_name_len;
}
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_vars_unset -
*
* ARGUMENTS
* s -
* scope -
* prefix -
* opt -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
int flt_ot_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
{
struct sample smp;
struct vars *vars;
struct var *var, *var_back;
char var_prefix[BUFSIZ], var_name[BUFSIZ];
uint size;
int var_prefix_len, var_name_len, retval = -1;
FLT_OT_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, opt, FLT_OT_DPTR_ARGS(err));
vars = flt_ot_get_vars(s, scope);
if (vars == NULL)
FLT_OT_RETURN_INT(retval);
var_prefix_len = flt_ot_var_name(NULL, prefix, NULL, var_prefix, sizeof(var_prefix), err);
if (var_prefix_len == -1)
FLT_OT_RETURN_INT(retval);
retval = 0;
HA_RWLOCK_WRLOCK(VARS_LOCK, &(vars->rwlock));
list_for_each_entry_safe(var, var_back, &(vars->head), l) {
FLT_OT_DBG(3, "variable cmp '%s' '%s' %d", var_prefix, var->name, var_prefix_len);
if (strncmp(var_prefix, var->name, var_prefix_len) == 0) {
var_name_len = snprintf(var_name, sizeof(var_name), "%s.%s", scope, var->name);
if ((var_name_len == -1) || (var_name_len >= sizeof(var_name))) {
FLT_OT_DBG(2, "'%s.%s' variable name too long", scope, var->name);
break;
}
FLT_OT_DBG(2, "- '%s' -> '%.*s'", var_name, (int)var->data.u.str.data, var->data.u.str.area);
size = var_clear(var);
flt_ot_smp_init(s, &smp, opt, 0, NULL);
var_accounting_diff(vars, smp.sess, smp.strm, -size);
retval++;
}
}
HA_RWLOCK_WRUNLOCK(VARS_LOCK, &(vars->rwlock));
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_vars_get -
*
* ARGUMENTS
* s -
* scope -
* prefix -
* opt -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
struct otc_text_map *flt_ot_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
{
struct vars *vars;
const struct var *var;
char var_name[BUFSIZ], ot_var_name[BUFSIZ];
int rc, i;
struct otc_text_map *retptr = NULL;
FLT_OT_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, opt, FLT_OT_DPTR_ARGS(err));
vars = flt_ot_get_vars(s, scope);
if (vars == NULL)
FLT_OT_RETURN_PTR(retptr);
rc = flt_ot_var_name(NULL, prefix, NULL, var_name, sizeof(var_name), err);
if (rc == -1)
FLT_OT_RETURN_PTR(retptr);
HA_RWLOCK_RDLOCK(VARS_LOCK, &(vars->rwlock));
list_for_each_entry(var, &(vars->head), l) {
FLT_OT_DBG(3, "variable cmp '%s' '%s' %d", var_name, var->name, rc);
if (strncmp(var_name, var->name, rc) == 0) {
FLT_OT_DBG(2, "'%s.%s' -> '%.*s'", scope, var->name, (int)var->data.u.str.data, var->data.u.str.area);
if (retptr == NULL) {
retptr = otc_text_map_new(NULL, 8);
if (retptr == NULL) {
FLT_OT_ERR("failed to create data");
break;
}
}
/*
* Eh, because the use of some characters is not allowed
* in the variable name, the conversion of the replaced
* characters to the original is performed here.
*/
for (i = 0; ; )
if (i >= (FLT_OT_TABLESIZE(ot_var_name) - 1)) {
FLT_OT_ERR("failed to reverse variable name, buffer too small");
otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
break;
} else {
char ch = var->name[rc + i + 1];
if (ch == '\0')
break;
else if (ch == FLT_OT_VAR_CHAR_DASH)
ch = '-';
else if (ch == FLT_OT_VAR_CHAR_SPACE)
ch = ' ';
ot_var_name[i++] = ch;
}
ot_var_name[i] = '\0';
if (retptr == NULL) {
break;
}
else if (otc_text_map_add(retptr, ot_var_name, i, var->data.u.str.area, var->data.u.str.data, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1) {
FLT_OT_ERR("failed to add map data");
otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
break;
}
}
}
HA_RWLOCK_RDUNLOCK(VARS_LOCK, &(vars->rwlock));
ot_text_map_show(retptr);
if ((retptr != NULL) && (retptr->count == 0)) {
FLT_OT_DBG(2, "WARNING: no variables found");
otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
}
FLT_OT_RETURN_PTR(retptr);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/