haproxy/addons/ot/src/parser.c
Miroslav Zagorac 9425ed488f BUG/MEDIUM: opentracing: initialization before establishing daemon and/or chroot mode
This patch solves the problem reported in github issue #1204, where the
OpenTracing filter cannot communicate with the selected tracer if HAProxy
is run in daemon mode.

This commit also solves github issue #1274, where the problem manifests
itself when using the 'chroot' keyword in the HAProxy configuration.

This is solved so that the initialization of the OpenTracing plugin is
split into two operations, first the plugin (dynamic library) is loaded
before switching the HAProxy to daemon mode (or chroot) and then the
tracer thread is started.

This means that nothing is retrieved from the file system in runtime.

After applying this commit, opentracing C wrapper version 1.1.0 should be
used because the earlier version does not have separated initialization
functions.

This resolves GitHub issues #1204 and #1274.
2021-06-10 06:45:39 +02:00

1222 lines
35 KiB
C

/***
* 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
struct flt_ot_debug flt_ot_debug;
THREAD_LOCAL int dbg_indent_level = 0;
#endif
#ifdef OTC_DBG_MEM
static struct otc_dbg_mem_data dbg_mem_data[1000000];
static struct otc_dbg_mem dbg_mem;
#endif
static struct flt_ot_conf *flt_ot_current_config = NULL;
static struct flt_ot_conf_tracer *flt_ot_current_tracer = NULL;
static struct flt_ot_conf_group *flt_ot_current_group = NULL;
static struct flt_ot_conf_scope *flt_ot_current_scope = NULL;
static struct flt_ot_conf_span *flt_ot_current_span = NULL;
/***
* NAME
* flt_ot_parse_strdup -
*
* ARGUMENTS
* ptr -
* str -
* err -
* err_msg -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_strdup(char **ptr, const char *str, char **err, const char *err_msg)
{
int retval = ERR_NONE;
FLT_OT_FUNC("%p:%p, %p, %p:%p, \"%s\"", FLT_OT_DPTR_ARGS(ptr), str, FLT_OT_DPTR_ARGS(err), err_msg);
*ptr = FLT_OT_STRDUP(str);
if (*ptr == NULL) {
FLT_OT_PARSE_ERR(err, "'%s' : out of memory", err_msg);
retval |= ERR_ABORT | ERR_ALERT;
}
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_keyword -
*
* ARGUMENTS
* ptr -
* args -
* cur_arg -
* pos -
* err -
* err_msg -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_keyword(char **ptr, char **args, int cur_arg, int pos, char **err, const char *err_msg)
{
int retval = ERR_NONE;
FLT_OT_FUNC("%p:%p, %p, %d, %d, %p:%p, \"%s\"", FLT_OT_DPTR_ARGS(ptr), args, cur_arg, pos, FLT_OT_DPTR_ARGS(err), err_msg);
if (*ptr != NULL) {
if (cur_arg == pos)
FLT_OT_PARSE_ERR(err, FLT_OT_FMT_TYPE "%s already set", err_msg);
else
FLT_OT_PARSE_ERR(err, "'%s' : %s already set", args[cur_arg], err_msg);
}
else if (!FLT_OT_ARG_ISVALID(pos + 1)) {
if (cur_arg == pos)
FLT_OT_PARSE_ERR(err, FLT_OT_FMT_TYPE "no %s set", err_msg);
else
FLT_OT_PARSE_ERR(err, "'%s' : no %s set", args[cur_arg], err_msg);
}
else {
retval = flt_ot_parse_strdup(ptr, args[pos + 1], err, args[cur_arg]);
}
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_invalid_char -
*
* ARGUMENTS
* name -
* type -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static const char *flt_ot_parse_invalid_char(const char *name, int type)
{
const char *retptr = NULL;
FLT_OT_FUNC("\"%s\", %d", name, type);
if (!FLT_OT_STR_ISVALID(name))
FLT_OT_RETURN(retptr);
if (type == 1) {
retptr = invalid_char(name);
}
else if (type == 2) {
retptr = invalid_domainchar(name);
}
else if (type == 3) {
retptr = invalid_prefix_char(name);
}
else if (type == 4) {
retptr = name;
/*
* Allowed characters are letters, numbers and '_', the first
* character in the string must not be a number.
*/
if (!isdigit(*retptr))
for (++retptr; (*retptr == '_') || isalnum(*retptr); retptr++);
if (*retptr == '\0')
retptr = NULL;
}
FLT_OT_RETURN(retptr);
}
/***
* NAME
* flt_ot_parse_cfg_check -
*
* ARGUMENTS
* file -
* linenum -
* args -
* id -
* parse_data -
* parse_data_size -
* pdata -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_check(const char *file, int linenum, char **args, const void *id, const struct flt_ot_parse_data *parse_data, size_t parse_data_size, const struct flt_ot_parse_data **pdata, char **err)
{
int i, argc, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p, %zu, %p:%p, %p:%p", file, linenum, args, id, parse_data, parse_data_size, FLT_OT_DPTR_ARGS(pdata), FLT_OT_DPTR_ARGS(err));
FLT_OT_ARGS_DUMP();
*pdata = NULL;
/* First check here if args[0] is the correct keyword. */
for (i = 0; (*pdata == NULL) && (i < parse_data_size); i++)
if (strcmp(parse_data[i].name, args[0]) == 0)
*pdata = parse_data + i;
if (*pdata == NULL)
FLT_OT_PARSE_ERR(err, "'%s' : unknown keyword", args[0]);
else
argc = flt_ot_args_count(args);
if ((retval & ERR_CODE) || (id == NULL))
/* Do nothing. */;
else if ((id != flt_ot_current_tracer) && (flt_ot_current_config->tracer == NULL))
FLT_OT_PARSE_ERR(err, "tracer not defined");
/*
* Checking that fewer arguments are specified in the configuration
* line than is required.
*/
if (!(retval & ERR_CODE))
if (argc < (*pdata)->args_min)
FLT_OT_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
/*
* Checking that more arguments are specified in the configuration
* line than the maximum allowed.
*/
if (!(retval & ERR_CODE) && ((*pdata)->args_max > 0))
if (argc > (*pdata)->args_max)
FLT_OT_PARSE_ERR(err, "'%s' : too many arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
/* Checking that the first argument has only allowed characters. */
if (!(retval & ERR_CODE) && ((*pdata)->check_name > 0)) {
const char *ic;
ic = flt_ot_parse_invalid_char(args[1], (*pdata)->check_name);
if (ic != NULL)
FLT_OT_PARSE_ERR(err, "%s '%s' : invalid character '%c'", args[0], args[1], *ic);
}
/* Checking that the data group name is defined. */
if (!(retval & ERR_CODE) && (*pdata)->flag_check_id && (id == NULL))
FLT_OT_PARSE_ERR(err, "'%s' : %s ID not set (use '%s%s')", args[0], parse_data[1].name, parse_data[1].name, parse_data[1].usage);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg_sample_expr -
*
* ARGUMENTS
* file -
* linenum -
* args -
* idx -
* head -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_sample_expr(const char *file, int linenum, char **args, int *idx, struct list *head, char **err)
{
struct flt_ot_conf_sample_expr *expr;
int retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p, %p:%p", file, linenum, args, idx, head, FLT_OT_DPTR_ARGS(err));
expr = flt_ot_conf_sample_expr_init(args[*idx], linenum, head, err);
if (expr != NULL) {
expr->expr = sample_parse_expr(args, idx, file, linenum, err, &(flt_ot_current_config->proxy->conf.args), NULL);
if (expr->expr != NULL)
FLT_OT_DBG(3, "sample expression '%s' added", expr->value);
else
retval |= ERR_ABORT | ERR_ALERT;
} else {
retval |= ERR_ABORT | ERR_ALERT;
}
if (retval & ERR_CODE)
flt_ot_conf_sample_expr_free(&expr);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg_sample -
*
* ARGUMENTS
* file -
* linenum -
* args -
* head -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_sample(const char *file, int linenum, char **args, struct list *head, char **err)
{
struct flt_ot_conf_sample *sample;
int idx = 2, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
sample = flt_ot_conf_sample_init(args, linenum, head, err);
if (sample == NULL)
FLT_OT_PARSE_ERR(err, "'%s' : out of memory", args[0]);
if (!(retval & ERR_CODE)) {
flt_ot_current_config->proxy->conf.args.ctx = ARGC_OT;
flt_ot_current_config->proxy->conf.args.file = file;
flt_ot_current_config->proxy->conf.args.line = linenum;
while (!(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(idx))
retval = flt_ot_parse_cfg_sample_expr(file, linenum, args, &idx, &(sample->exprs), err);
flt_ot_current_config->proxy->conf.args.file = NULL;
flt_ot_current_config->proxy->conf.args.line = 0;
}
if (retval & ERR_CODE)
flt_ot_conf_sample_free(&sample);
else
FLT_OT_DBG(3, "sample '%s' -> '%s' added", sample->key, sample->value);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg_str -
*
* ARGUMENTS
* file -
* linenum -
* args -
* head -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, struct list *head, char **err)
{
struct flt_ot_conf_str *str = NULL;
int i, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
if (flt_ot_conf_str_init(args[i], linenum, head, err) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
if (retval & ERR_CODE)
flt_ot_conf_str_free(&str);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg_file -
*
* ARGUMENTS
* ptr -
* file -
* linenum -
* args -
* err -
* err_msg -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_file(char **ptr, const char *file, int linenum, char **args, char **err, const char *err_msg)
{
int retval = ERR_NONE;
FLT_OT_FUNC("%p:%p, \"%s\", %d, %p, %p:%p, \"%s\"", FLT_OT_DPTR_ARGS(ptr), file, linenum, args, FLT_OT_DPTR_ARGS(err), err_msg);
if (!FLT_OT_ARG_ISVALID(1))
FLT_OT_PARSE_ERR(err, "'%s' : no %s specified", flt_ot_current_tracer->id, err_msg);
else if (alertif_too_many_args(1, file, linenum, args, &retval))
retval |= ERR_ABORT | ERR_ALERT;
else if (access(args[1], R_OK) == -1)
FLT_OT_PARSE_ERR(err, "'%s' : %s", args[1], strerror(errno));
else
retval = flt_ot_parse_keyword(ptr, args, 0, 0, err, err_msg);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_check_scope -
*
* ARGUMENTS
* This function takes no arguments.
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns TRUE in case the configuration is not in the currently defined
* scope, FALSE otherwise.
*/
static bool flt_ot_parse_check_scope(void)
{
bool retval = 0;
if ((cfg_scope != NULL) && (flt_ot_current_config->id != NULL) && (strcmp(flt_ot_current_config->id, cfg_scope) != 0)) {
FLT_OT_DBG(1, "cfg_scope: '%s', id: '%s'", cfg_scope, flt_ot_current_config->id);
retval = 1;
}
return retval;
}
/***
* NAME
* flt_ot_parse_cfg_tracer -
*
* ARGUMENTS
* file -
* linenum -
* args -
* kw_mod -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_tracer(const char *file, int linenum, char **args, int kw_mod)
{
#define FLT_OT_PARSE_TRACER_DEF(a,b,c,d,e,f,g) { FLT_OT_PARSE_TRACER_##a, b, c, d, e, f, g },
static const struct flt_ot_parse_data parse_data[] = { FLT_OT_PARSE_TRACER_DEFINES };
#undef FLT_OT_PARSE_TRACER_DEF
const struct flt_ot_parse_data *pdata = NULL;
char *err = NULL, *err_log = NULL;
int i, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
if (flt_ot_parse_check_scope())
FLT_OT_RETURN(retval);
retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_tracer, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
if (retval & ERR_CODE) {
FLT_OT_PARSE_IFERR_ALERT();
FLT_OT_RETURN(retval);
}
if (pdata->keyword == FLT_OT_PARSE_TRACER_ID) {
if (flt_ot_current_config->tracer != NULL) {
FLT_OT_PARSE_ERR(&err, "'%s' : tracer can be defined only once", args[1]);
} else {
flt_ot_current_tracer = flt_ot_conf_tracer_init(args[1], linenum, &err);
if (flt_ot_current_tracer == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_LOG) {
if (parse_logsrv(args, &(flt_ot_current_tracer->proxy_log.logsrvs), kw_mod == KWM_NO, file, linenum, &err_log) == 0) {
FLT_OT_PARSE_ERR(&err, "'%s %s ...' : %s", args[0], args[1], err_log);
FLT_OT_FREE_CLEAR(err_log);
retval |= ERR_ABORT | ERR_ALERT;
} else {
flt_ot_current_tracer->logging |= FLT_OT_LOGGING_ON;
}
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_CONFIG) {
retval = flt_ot_parse_cfg_file(&(flt_ot_current_tracer->config), file, linenum, args, &err, "configuration file");
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_PLUGIN) {
retval = flt_ot_parse_cfg_file(&(flt_ot_current_tracer->plugin), file, linenum, args, &err, "plugin library");
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_GROUPS) {
for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
if (flt_ot_conf_ph_init(args[i], linenum, &(flt_ot_current_tracer->ph_groups), &err) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_SCOPES) {
for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
if (flt_ot_conf_ph_init(args[i], linenum, &(flt_ot_current_tracer->ph_scopes), &err) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_ACL) {
if (strcasecmp(args[1], "or") == 0)
FLT_OT_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
else if (parse_acl((const char **)args + 1, &(flt_ot_current_tracer->acls), &err, &(flt_ot_current_config->proxy->conf.args), file, linenum) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_RATE_LIMIT) {
flt_ot_current_tracer->rate_limit = FLT_OT_FLOAT_U32(flt_ot_strtod(args[1], 0.0, FLT_OT_RATE_LIMIT_MAX, &err), FLT_OT_RATE_LIMIT_MAX);
}
else if (pdata->keyword == FLT_OT_PARSE_TRACER_OPTION) {
if (strcmp(args[1], FLT_OT_PARSE_OPTION_DISABLED) == 0) {
flt_ot_current_tracer->flag_disabled = (kw_mod == KWM_NO) ? 0 : 1;
}
else if (strcmp(args[1], FLT_OT_PARSE_OPTION_HARDERR) == 0) {
flt_ot_current_tracer->flag_harderr = (kw_mod == KWM_NO) ? 0 : 1;
}
else if (strcmp(args[1], FLT_OT_PARSE_OPTION_NOLOGNORM) == 0) {
if (kw_mod == KWM_NO)
flt_ot_current_tracer->logging &= ~FLT_OT_LOGGING_NOLOGNORM;
else
flt_ot_current_tracer->logging |= FLT_OT_LOGGING_NOLOGNORM;
}
else
FLT_OT_PARSE_ERR(&err, "'%s' : unknown option '%s'", args[0], args[1]);
}
#ifdef DEBUG_OT
else if (pdata->keyword == FLT_OT_PARSE_TRACER_DEBUG_LEVEL) {
flt_ot_debug.level = flt_ot_strtoll(args[1], 0, 255, &err);
}
#else
else {
FLT_OT_PARSE_WARNING("'%s' : keyword ignored", file, linenum, args[0]);
}
#endif
FLT_OT_PARSE_IFERR_ALERT();
if ((retval & ERR_CODE) && (flt_ot_current_tracer != NULL))
flt_ot_conf_tracer_free(&flt_ot_current_tracer);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_post_parse_cfg_tracer -
*
* ARGUMENTS
* This function takes no arguments.
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_post_parse_cfg_tracer(void)
{
char errbuf[BUFSIZ] = "";
int retval = ERR_NONE;
FLT_OT_FUNC("");
if (flt_ot_current_tracer == NULL)
FLT_OT_RETURN(retval);
flt_ot_current_config->tracer = flt_ot_current_tracer;
if (flt_ot_current_tracer->id == NULL)
FLT_OT_RETURN(retval);
if (flt_ot_current_tracer->config == NULL) {
FLT_OT_POST_PARSE_ALERT("tracer '%s' has no configuration file specified", flt_ot_current_tracer->cfg_line, flt_ot_current_tracer->id);
} else {
flt_ot_current_tracer->cfgbuf = otc_file_read(flt_ot_current_tracer->config, "#", errbuf, sizeof(errbuf));
if (flt_ot_current_tracer->cfgbuf == NULL)
FLT_OT_POST_PARSE_ALERT("tracer '%s' %s", flt_ot_current_tracer->cfg_line, flt_ot_current_tracer->id, (*errbuf == '\0') ? "cannot load configuration file" : errbuf);
}
if (flt_ot_current_tracer->plugin == NULL)
FLT_OT_POST_PARSE_ALERT("tracer '%s' has no plugin library specified", flt_ot_current_tracer->cfg_line, flt_ot_current_tracer->id);
flt_ot_current_tracer = NULL;
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg_group -
*
* ARGUMENTS
* file -
* linenum -
* args -
* kw_mod -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_group(const char *file, int linenum, char **args, int kw_mod)
{
#define FLT_OT_PARSE_GROUP_DEF(a,b,c,d,e,f,g) { FLT_OT_PARSE_GROUP_##a, b, c, d, e, f, g },
static const struct flt_ot_parse_data parse_data[] = { FLT_OT_PARSE_GROUP_DEFINES };
#undef FLT_OT_PARSE_GROUP_DEF
const struct flt_ot_parse_data *pdata = NULL;
char *err = NULL;
int i, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
if (flt_ot_parse_check_scope())
FLT_OT_RETURN(retval);
retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_group, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
if (retval & ERR_CODE) {
FLT_OT_PARSE_IFERR_ALERT();
FLT_OT_RETURN(retval);
}
if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
flt_ot_current_group = flt_ot_conf_group_init(args[1], linenum, &(flt_ot_current_config->groups), &err);
if (flt_ot_current_config == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_GROUP_SCOPES) {
for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
if (flt_ot_conf_ph_init(args[i], linenum, &(flt_ot_current_group->ph_scopes), &err) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
FLT_OT_PARSE_IFERR_ALERT();
if ((retval & ERR_CODE) && (flt_ot_current_group != NULL))
flt_ot_conf_group_free(&flt_ot_current_group);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_post_parse_cfg_group -
*
* ARGUMENTS
* This function takes no arguments.
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_post_parse_cfg_group(void)
{
int retval = ERR_NONE;
FLT_OT_FUNC("");
if (flt_ot_current_group == NULL)
FLT_OT_RETURN(retval);
/* Check that the group has at least one scope defined. */
if (LIST_ISEMPTY(&(flt_ot_current_group->ph_scopes)))
FLT_OT_POST_PARSE_ALERT("group '%s' has no defined scope(s)", flt_ot_current_group->cfg_line, flt_ot_current_group->id);
flt_ot_current_group = NULL;
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg_scope_ctx -
*
* ARGUMENTS
* args -
* cur_arg -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_scope_ctx(char **args, int cur_arg, char **err)
{
uint8_t flags = 0;
int retval = ERR_NONE;
FLT_OT_FUNC("%p, %d, %p:%p", args, cur_arg, FLT_OT_DPTR_ARGS(err));
if (strcmp(args[cur_arg], FLT_OT_PARSE_CTX_USE_HEADERS) == 0)
flags = FLT_OT_CTX_USE_HEADERS;
else if (strcmp(args[cur_arg], FLT_OT_PARSE_CTX_USE_VARS) == 0)
flags = FLT_OT_CTX_USE_VARS;
else
FLT_OT_PARSE_ERR(err, "'%s' : invalid context storage type", args[0]);
if (flags == 0)
/* Do nothing. */;
else if (flt_ot_current_span->ctx_flags & flags)
FLT_OT_PARSE_ERR(err, "'%s' : %s already used", args[0], args[cur_arg]);
else
flt_ot_current_span->ctx_flags |= flags;
FLT_OT_DBG(2, "ctx_flags: 0x%02hhx (0x%02hhx)", flt_ot_current_span->ctx_flags, flags);
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_acl -
*
* ARGUMENTS
* file -
* linenum -
* px -
* args -
* err -
* head -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static struct acl_cond *flt_ot_parse_acl(const char *file, int linenum, struct proxy *px, const char **args, char **err, struct list *head, ...)
{
va_list ap;
int n = 0;
struct acl_cond *retptr = NULL;
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p, %p, ...", file, linenum, px, args, FLT_OT_DPTR_ARGS(err), head);
for (va_start(ap, head); (retptr == NULL) && (head != NULL); head = va_arg(ap, typeof(head)), n++) {
retptr = build_acl_cond(file, linenum, head, px, args, (n == 0) ? err : NULL);
if (retptr != NULL)
FLT_OT_DBG(2, "ACL build done, using list %p %d", head, n);
}
va_end(ap);
if ((retptr != NULL) && (err != NULL))
FLT_OT_FREE_CLEAR(*err);
FLT_OT_RETURN(retptr);
}
/***
* NAME
* flt_ot_parse_cfg_scope -
*
* ARGUMENTS
* file -
* linenum -
* args -
* kw_mod -
*
* DESCRIPTION
* Function used to load the scope block configuration.
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg_scope(const char *file, int linenum, char **args, int kw_mod)
{
#define FLT_OT_PARSE_SCOPE_DEF(a,b,c,d,e,f,g) { FLT_OT_PARSE_SCOPE_##a, b, c, d, e, f, g },
static const struct flt_ot_parse_data parse_data[] = { FLT_OT_PARSE_SCOPE_DEFINES };
#undef FLT_OT_PARSE_SCOPE_DEF
const struct flt_ot_parse_data *pdata = NULL;
char *err = NULL;
int i, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
if (flt_ot_parse_check_scope())
FLT_OT_RETURN(retval);
retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_span, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
if (retval & ERR_CODE) {
FLT_OT_PARSE_IFERR_ALERT();
FLT_OT_RETURN(retval);
}
if (pdata->keyword == FLT_OT_PARSE_SCOPE_ID) {
/* Initialization of a new scope. */
flt_ot_current_scope = flt_ot_conf_scope_init(args[1], linenum, &(flt_ot_current_config->scopes), &err);
if (flt_ot_current_scope == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_SPAN) {
/*
* Checking if this is the beginning of the definition of
* a new span.
*/
if (flt_ot_current_span != NULL) {
FLT_OT_DBG(3, "span '%s' (done)", flt_ot_current_span->id);
flt_ot_current_span = NULL;
}
/* Initialization of a new span. */
flt_ot_current_span = flt_ot_conf_span_init(args[1], linenum, &(flt_ot_current_scope->spans), &err);
/*
* In case the span has a defined reference,
* the correctness of the arguments is checked here.
*/
if (flt_ot_current_span == NULL) {
retval |= ERR_ABORT | ERR_ALERT;
}
else if (FLT_OT_ARG_ISVALID(2)) {
for (i = 2; (i < pdata->args_max) && FLT_OT_ARG_ISVALID(i); i++)
if (strcmp(args[i], FLT_OT_PARSE_SPAN_ROOT) == 0) {
if (flt_ot_current_span->flag_root)
FLT_OT_PARSE_ERR(&err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
else
flt_ot_current_span->flag_root = 1;
}
else if ((strcmp(args[i], FLT_OT_PARSE_SPAN_REF_CHILD) == 0) || (strcmp(args[i], FLT_OT_PARSE_SPAN_REF_FOLLOWS) == 0)) {
if (!FLT_OT_ARG_ISVALID(i + 1)) {
FLT_OT_PARSE_ERR(&err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
}
else if (strcmp(args[i++], FLT_OT_PARSE_SPAN_REF_CHILD) == 0) {
flt_ot_current_span->ref_type = otc_span_reference_child_of;
flt_ot_current_span->ref_id_len = strlen(args[i]);
retval = flt_ot_parse_strdup(&(flt_ot_current_span->ref_id), args[i], &err, args[1]);
}
else {
flt_ot_current_span->ref_type = otc_span_reference_follows_from;
flt_ot_current_span->ref_id_len = strlen(args[i]);
retval = flt_ot_parse_strdup(&(flt_ot_current_span->ref_id), args[i], &err, args[1]);
}
}
else {
FLT_OT_PARSE_ERR(&err, "'%s' : invalid argument (use '%s%s')", args[i], pdata->name, pdata->usage);
}
}
else {
/*
* This is not a faulty configuration, only such a case
* will be logged.
*/
FLT_OT_DBG(3, "new span '%s' without reference", flt_ot_current_span->id);
}
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_TAG) {
retval = flt_ot_parse_cfg_sample(file, linenum, args, &(flt_ot_current_span->tags), &err);
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_LOG) {
retval = flt_ot_parse_cfg_sample(file, linenum, args, &(flt_ot_current_span->logs), &err);
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_BAGGAGE) {
retval = flt_ot_parse_cfg_sample(file, linenum, args, &(flt_ot_current_span->baggages), &err);
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_INJECT) {
/*
* Automatic context name generation can be specified here
* if the contents of the FLT_OT_PARSE_CTX_AUTONAME macro
* are used as the name. In that case, if the context is
* after a particular event, it gets its name; otherwise
* it gets the name of the current span.
*/
if (flt_ot_current_span->ctx_id != NULL)
FLT_OT_PARSE_ERR(&err, "'%s' : only one context per span is allowed", args[1]);
else if (strcmp(args[1], FLT_OT_PARSE_CTX_AUTONAME) != 0)
retval = flt_ot_parse_strdup(&(flt_ot_current_span->ctx_id), args[1], &err, args[0]);
else if (flt_ot_current_scope->event != FLT_OT_EVENT_REQ_NONE)
retval = flt_ot_parse_strdup(&(flt_ot_current_span->ctx_id), flt_ot_event_data[flt_ot_current_scope->event].name, &err, args[0]);
else
retval = flt_ot_parse_strdup(&(flt_ot_current_span->ctx_id), flt_ot_current_span->id, &err, args[0]);
if (flt_ot_current_span->ctx_id != NULL) {
flt_ot_current_span->ctx_id_len = strlen(flt_ot_current_span->ctx_id);
/*
* Here is checked the context storage type; which, if
* not explicitly specified, is set to HTTP headers.
*
* It is possible to use both types of context storage
* at the same time.
*/
if (FLT_OT_ARG_ISVALID(2)) {
retval = flt_ot_parse_cfg_scope_ctx(args, 2, &err);
if (!(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(3))
retval = flt_ot_parse_cfg_scope_ctx(args, 3, &err);
} else {
flt_ot_current_span->ctx_flags = FLT_OT_CTX_USE_HEADERS;
}
}
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_EXTRACT) {
struct flt_ot_conf_context *conf_ctx;
/*
* Here is checked the context storage type; which, if
* not explicitly specified, is set to HTTP headers.
*/
conf_ctx = flt_ot_conf_context_init(args[1], linenum, &(flt_ot_current_scope->contexts), &err);
if (conf_ctx == NULL)
retval |= ERR_ABORT | ERR_ALERT;
else if (!FLT_OT_ARG_ISVALID(2))
conf_ctx->flags = FLT_OT_CTX_USE_HEADERS;
else if (strcmp(args[2], FLT_OT_PARSE_CTX_USE_HEADERS) == 0)
conf_ctx->flags = FLT_OT_CTX_USE_HEADERS;
else if (strcmp(args[2], FLT_OT_PARSE_CTX_USE_VARS) == 0)
conf_ctx->flags = FLT_OT_CTX_USE_VARS;
else
FLT_OT_PARSE_ERR(&err, "'%s' : invalid context storage type", args[2]);
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_FINISH) {
retval = flt_ot_parse_cfg_str(file, linenum, args, &(flt_ot_current_scope->finish), &err);
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_ACL) {
if (strcasecmp(args[1], "or") == 0)
FLT_OT_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
else if (parse_acl((const char **)args + 1, &(flt_ot_current_scope->acls), &err, &(flt_ot_current_config->proxy->conf.args), file, linenum) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_SCOPE_EVENT) {
/* Scope can only have one event defined. */
if (flt_ot_current_scope->event != FLT_OT_EVENT_REQ_NONE) {
FLT_OT_PARSE_ERR(&err, "'%s' : event already set", flt_ot_current_scope->id);
} else {
/* Check the event name. */
for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_event_data); i++)
if (strcmp(flt_ot_event_data[i].name, args[1]) == 0) {
flt_ot_current_scope->event = i;
break;
}
/*
* The event can have some condition defined and this
* is checked here.
*/
if (flt_ot_current_scope->event == FLT_OT_EVENT_REQ_NONE) {
FLT_OT_PARSE_ERR(&err, "'%s' : unknown event", args[1]);
}
else if (!FLT_OT_ARG_ISVALID(2)) {
/* Do nothing. */
}
else if ((strcmp(args[2], FLT_OT_CONDITION_IF) == 0) || (strcmp(args[2], FLT_OT_CONDITION_UNLESS) == 0)) {
/*
* We will first try to build ACL condition using
* local settings and then if that fails, using
* global settings (from tracer block). If it
* also fails, then try to use ACL defined in
* the HAProxy configuration.
*/
flt_ot_current_scope->cond = flt_ot_parse_acl(file, linenum, flt_ot_current_config->proxy, (const char **)args + 2, &err, &(flt_ot_current_scope->acls), &(flt_ot_current_config->tracer->acls), &(flt_ot_current_config->proxy->acl), NULL);
if (flt_ot_current_scope->cond == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else {
FLT_OT_PARSE_ERR(&err, "'%s' : expects either 'if' or 'unless' followed by a condition but found '%s'", args[1], args[2]);
}
if (!(retval & ERR_CODE))
FLT_OT_DBG(3, "event '%s'", args[1]);
}
}
FLT_OT_PARSE_IFERR_ALERT();
if ((retval & ERR_CODE) && (flt_ot_current_scope != NULL)) {
flt_ot_conf_scope_free(&flt_ot_current_scope);
flt_ot_current_span = NULL;
}
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_post_parse_cfg_scope -
*
* ARGUMENTS
* This function takes no arguments.
*
* DESCRIPTION
* In this function the correctness of the complete scope block is examined.
* This does not mean that all elements are checked here, but only those for
* which it has not been possible to establish their complete correctness in
* the function flt_ot_parse_cfg_scope().
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_post_parse_cfg_scope(void)
{
struct flt_ot_conf_span *conf_span;
int retval = ERR_NONE;
FLT_OT_FUNC("");
if (flt_ot_current_scope == NULL)
FLT_OT_RETURN(retval);
/* If span context inject is used, check that this is possible. */
list_for_each_entry(conf_span, &(flt_ot_current_scope->spans), list)
if ((conf_span->ctx_id != NULL) && (conf_span->ctx_flags & FLT_OT_CTX_USE_HEADERS))
if (!flt_ot_event_data[flt_ot_current_scope->event].flag_http_inject)
FLT_OT_POST_PARSE_ALERT("inject '%s' : cannot use on this event", conf_span->cfg_line, conf_span->ctx_id);
if (retval & ERR_CODE)
flt_ot_conf_scope_free(&flt_ot_current_scope);
flt_ot_current_scope = NULL;
flt_ot_current_span = NULL;
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse_cfg -
*
* ARGUMENTS
* conf -
* flt_name -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse_cfg(struct flt_ot_conf *conf, const char *flt_name, char **err)
{
struct list backup_sections;
int retval = ERR_ABORT | ERR_ALERT;
FLT_OT_FUNC("%p, \"%s\", %p:%p", conf, flt_name, FLT_OT_DPTR_ARGS(err));
flt_ot_current_config = conf;
/* Backup sections. */
LIST_INIT(&backup_sections);
cfg_backup_sections(&backup_sections);
/* Register new OT sections and parse the OT filter configuration file. */
if (!cfg_register_section(FLT_OT_PARSE_SECTION_TRACER_ID, flt_ot_parse_cfg_tracer, flt_ot_post_parse_cfg_tracer))
/* Do nothing. */;
else if (!cfg_register_section(FLT_OT_PARSE_SECTION_GROUP_ID, flt_ot_parse_cfg_group, flt_ot_post_parse_cfg_group))
/* Do nothing. */;
else if (!cfg_register_section(FLT_OT_PARSE_SECTION_SCOPE_ID, flt_ot_parse_cfg_scope, flt_ot_post_parse_cfg_scope))
/* Do nothing. */;
else if (access(conf->cfg_file, R_OK) == -1)
FLT_OT_PARSE_ERR(err, "'%s' : %s", conf->cfg_file, strerror(errno));
else
retval = readcfgfile(conf->cfg_file);
/* Unregister OT sections and restore previous sections. */
cfg_unregister_sections();
cfg_restore_sections(&backup_sections);
flt_ot_current_config = NULL;
FLT_OT_RETURN(retval);
}
/***
* NAME
* flt_ot_parse -
*
* ARGUMENTS
* args -
* cur_arg -
* px -
* fconf -
* err -
* private -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_ot_parse(char **args, int *cur_arg, struct proxy *px, struct flt_conf *fconf, char **err, void *private)
{
struct flt_ot_conf *conf = NULL;
int pos, retval = ERR_NONE;
#ifdef DEBUG_OT
FLT_OT_RUN_ONCE(
# ifndef DEBUG_OT_SYSTIME
(void)memcpy(&(flt_ot_debug.start), &now, sizeof(flt_ot_debug.start));
# endif
flt_ot_debug.level = FLT_OT_DEBUG_LEVEL;
);
#endif
FLT_OT_FUNC("%p, %p, %p, %p, %p:%p, %p", args, cur_arg, px, fconf, FLT_OT_DPTR_ARGS(err), private);
#ifdef OTC_DBG_MEM
FLT_OT_RUN_ONCE(
if (otc_dbg_mem_init(&dbg_mem, dbg_mem_data, FLT_OT_TABLESIZE(dbg_mem_data), 0xff) == -1) {
FLT_OT_PARSE_ERR(err, "cannot initialize memory debugger");
FLT_OT_RETURN(retval);
}
);
#endif
FLT_OT_ARGS_DUMP();
conf = flt_ot_conf_init(px);
if (conf == NULL) {
FLT_OT_PARSE_ERR(err, "'%s' : out of memory", args[*cur_arg]);
FLT_OT_RETURN(retval);
}
for (pos = *cur_arg + 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(pos); pos++) {
FLT_OT_DBG(3, "args[%d:2] : { '%s' '%s' }", pos, args[pos], args[pos + 1]);
if (strcmp(args[pos], FLT_OT_OPT_FILTER_ID) == 0) {
retval = flt_ot_parse_keyword(&(conf->id), args, *cur_arg, pos, err, "name");
pos++;
}
else if (strcmp(args[pos], FLT_OT_OPT_CONFIG) == 0) {
retval = flt_ot_parse_keyword(&(conf->cfg_file), args, *cur_arg, pos, err, "configuration file");
if (!(retval & ERR_CODE))
retval = flt_ot_parse_cfg(conf, args[*cur_arg], err);
pos++;
}
else {
FLT_OT_PARSE_ERR(err, "'%s' : unknown keyword '%s'", args[*cur_arg], args[pos]);
}
}
/* If the OpenTracing filter ID is not set, use default name. */
if (!(retval & ERR_CODE) && (conf->id == NULL)) {
ha_warning("parsing : " FLT_OT_FMT_TYPE FLT_OT_FMT_NAME "'no filter id set, using default id '%s'\n", FLT_OT_OPT_FILTER_ID_DEFAULT);
retval = flt_ot_parse_strdup(&(conf->id), FLT_OT_OPT_FILTER_ID_DEFAULT, err, args[*cur_arg]);
}
if (!(retval & ERR_CODE) && (conf->cfg_file == NULL))
FLT_OT_PARSE_ERR(err, "'%s' : no configuration file specified", args[*cur_arg]);
if (retval & ERR_CODE) {
flt_ot_conf_free(&conf);
} else {
fconf->id = ot_flt_id;
fconf->ops = &flt_ot_ops;
fconf->conf = conf;
*cur_arg = pos;
FLT_OT_DBG(3, "filter set: id '%s', config '%s'", conf->id, conf->cfg_file);
}
FLT_OT_RETURN(retval);
}
/* Declare the filter parser for FLT_OT_OPT_NAME keyword. */
static struct flt_kw_list flt_kws = { FLT_OT_SCOPE, { }, {
{ FLT_OT_OPT_NAME, flt_ot_parse, NULL },
{ NULL, NULL, NULL },
}
};
INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/