/*** * 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 * - * * 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 * - * * 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_get_vars - * * ARGUMENTS * s - * scope - * * DESCRIPTION * - * * RETURN VALUE * - */ 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(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(retval); } /*** * NAME * flt_ot_var_name - * * ARGUMENTS * scope - * prefix - * name - * var_name - * size - * err - * * DESCRIPTION * - * * 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(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; FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p:%p", scope, prefix, name, FLT_OT_DPTR_ARGS(err)); retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err); if (retval == -1) FLT_OT_RETURN(retval); /* Set 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 = retval; if (vars_check_arg(&arg, err) == 0) { FLT_OT_ERR_APPEND("failed to register variable '%s': %s", var_name, *err); retval = -1; } else { FLT_OT_DBG(2, "variable '%s' registered", arg.data.var.name); } FLT_OT_RETURN(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; FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, value, opt, FLT_OT_DPTR_ARGS(err)); retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err); if (retval == -1) FLT_OT_RETURN(retval); (void)memset(&smp, 0, sizeof(smp)); (void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL); smp.data.type = SMP_T_STR; smp.data.u.str.area = (char *)value; smp.data.u.str.data = strlen(value); vars_set_by_name_ifexist(var_name, retval, &smp); FLT_OT_RETURN(retval); } /*** * NAME * flt_ot_var_unset - * * ARGUMENTS * s - * scope - * prefix - * name - * opt - * err - * * DESCRIPTION * - * * RETURN VALUE * - */ int flt_ot_var_unset(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err) { struct sample smp; char var_name[BUFSIZ]; int retval; FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, opt, FLT_OT_DPTR_ARGS(err)); retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err); if (retval == -1) FLT_OT_RETURN(retval); (void)memset(&smp, 0, sizeof(smp)); (void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL); vars_unset_by_name_ifexist(var_name, retval, &smp); FLT_OT_RETURN(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(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(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); (void)memset(&smp, 0, sizeof(smp)); (void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL); size = var_clear(var); var_accounting_diff(vars, smp.sess, smp.strm, -size); retval++; } } HA_RWLOCK_WRUNLOCK(VARS_LOCK, &(vars->rwlock)); FLT_OT_RETURN(retval); } /*** * NAME * flt_ot_var_get - * * ARGUMENTS * s - * scope - * prefix - * name - * value - * opt - * err - * * DESCRIPTION * - * * RETURN VALUE * - */ int flt_ot_var_get(struct stream *s, const char *scope, const char *prefix, const char *name, char **value, uint opt, char **err) { struct sample smp; char var_name[BUFSIZ], var_value[BUFSIZ]; int retval; FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %p:%p, %u, %p:%p", s, scope, prefix, name, FLT_OT_DPTR_ARGS(value), opt, FLT_OT_DPTR_ARGS(err)); retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err); if (retval == -1) FLT_OT_RETURN(retval); (void)memset(&smp, 0, sizeof(smp)); (void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL); if (vars_get_by_name(var_name, retval, &smp)) { retval = flt_ot_sample_to_str(&(smp.data), var_value, sizeof(var_value), err); if (retval != -1) FLT_OT_DBG(3, "data type %d: '%s' = '%s'", smp.data.type, var_name, var_value); } else { FLT_OT_ERR("failed to get variable '%s'", var_name); retval = -1; } FLT_OT_RETURN(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(retptr); rc = flt_ot_var_name(NULL, prefix, NULL, var_name, sizeof(var_name), err); if (rc == -1) FLT_OT_RETURN(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(retptr); } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * End: * * vi: noexpandtab shiftwidth=8 tabstop=8 */