diff --git a/addons/otel/Makefile b/addons/otel/Makefile index bfcfd744e..feddc6596 100644 --- a/addons/otel/Makefile +++ b/addons/otel/Makefile @@ -51,7 +51,9 @@ $(error OpenTelemetry C wrapper : can't find library) endif OPTIONS_OBJS += \ + addons/otel/src/conf.o \ addons/otel/src/filter.o \ - addons/otel/src/parser.o + addons/otel/src/parser.o \ + addons/otel/src/util.o OTEL_CFLAGS := $(OTEL_CFLAGS) -Iaddons/otel/include $(OTEL_DEFINE) diff --git a/addons/otel/include/conf.h b/addons/otel/include/conf.h new file mode 100644 index 000000000..e0ce4c081 --- /dev/null +++ b/addons/otel/include/conf.h @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_CONF_H_ +#define _OTEL_CONF_H_ + +/* Extract the OTel filter configuration from a filter instance. */ +#define FLT_OTEL_CONF(f) ((struct flt_otel_conf *)FLT_CONF(f)) + +/* Expand to a string pointer and its length for a named member. */ +#define FLT_OTEL_STR_HDR_ARGS(p,m) (p)->m, (p)->m##_len +/*** + * It should be noted that the macro FLT_OTEL_CONF_HDR_ARGS() does not have + * all the parameters defined that would correspond to the format found in + * the FLT_OTEL_CONF_HDR_FMT macro (first pointer is missing). + * + * This is because during the expansion of the OTELC_DBG_STRUCT() macro, an + * incorrect conversion is performed and instead of the first correct code, + * a second incorrect code is generated: + * + * do { + * if ((p) == NULL) + * .. + * } while (0) + * + * do { + * if ((p), (int) (p)->id_len, (p)->id, (p)->id_len, (p)->cfg_line == NULL) + * .. + * } while (0) + * + */ +#define FLT_OTEL_CONF_HDR_FMT "%p:{ { '%.*s' %zu %d } " +#define FLT_OTEL_CONF_HDR_ARGS(p,m) (int)(p)->m##_len, (p)->m, (p)->m##_len, (p)->cfg_line + +#define FLT_OTEL_CONF_STR_CMP(s,S) ((s##_len == S##_len) && (memcmp(s, S, S##_len) == 0)) + +#define FLT_OTEL_DBG_CONF_SAMPLE_EXPR(h,p) \ + OTELC_DBG(DEBUG, h "%p:{ '%s' %p }", (p), (p)->fmt_expr, (p)->expr) + +#define FLT_OTEL_DBG_CONF_SAMPLE(h,p) \ + OTELC_DBG(DEBUG, h "%p:{ '%s' '%s' %s %s %d }", (p), \ + (p)->key, (p)->fmt_string, otelc_value_dump(&((p)->extra), ""), \ + flt_otel_list_dump(&((p)->exprs)), (p)->num_exprs) + +#define FLT_OTEL_DBG_CONF_HDR(h,p,i) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "}", (p), FLT_OTEL_CONF_HDR_ARGS(p, i)) + +#define FLT_OTEL_DBG_CONF_CONTEXT(h,p) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "0x%02hhx }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flags) + +#define FLT_OTEL_DBG_CONF_SPAN(h,p) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %zu %s' %zu %hhu 0x%02hhx %s %s %s %s }", \ + (p), FLT_OTEL_CONF_HDR_ARGS(p, id), FLT_OTEL_STR_HDR_ARGS(p, ref_id), \ + FLT_OTEL_STR_HDR_ARGS(p, ctx_id), (p)->flag_root, (p)->ctx_flags, \ + flt_otel_list_dump(&((p)->attributes)), flt_otel_list_dump(&((p)->events)), \ + flt_otel_list_dump(&((p)->baggages)), flt_otel_list_dump(&((p)->statuses))) + +#define FLT_OTEL_DBG_CONF_SCOPE(h,p) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %d %u %s %p %s %s %s }", (p), \ + FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flag_used, (p)->event, (p)->idle_timeout, \ + flt_otel_list_dump(&((p)->acls)), (p)->cond, flt_otel_list_dump(&((p)->contexts)), \ + flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->spans_to_finish))) + +#define FLT_OTEL_DBG_CONF_GROUP(h,p) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %s }", (p), \ + FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flag_used, flt_otel_list_dump(&((p)->ph_scopes))) + +#define FLT_OTEL_DBG_CONF_PH(h,p) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%p }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->ptr) + +#define FLT_OTEL_DBG_CONF_INSTR(h,p) \ + OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %u %s %s %s }", (p), \ + FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->config, (p)->tracer, (p)->rate_limit, (p)->flag_harderr, \ + (p)->flag_disabled, (p)->logging, &((p)->proxy_log), flt_otel_list_dump(&((p)->proxy_log.loggers)), \ + (p)->analyzers, (p)->idle_timeout, flt_otel_list_dump(&((p)->acls)), flt_otel_list_dump(&((p)->ph_groups)), \ + flt_otel_list_dump(&((p)->ph_scopes))) + +#define FLT_OTEL_DBG_CONF(h,p) \ + OTELC_DBG(DEBUG, h "%p:{ %p '%s' '%s' %p %s %s }", (p), \ + (p)->proxy, (p)->id, (p)->cfg_file, (p)->instr, \ + flt_otel_list_dump(&((p)->groups)), flt_otel_list_dump(&((p)->scopes))) + +/* Anonymous struct containing a string pointer and its length. */ +#define FLT_OTEL_CONF_STR(p) \ + struct { \ + char *p; \ + size_t p##_len; \ + } + +/* Common header embedded in all configuration structures. */ +#define FLT_OTEL_CONF_HDR(p) \ + struct { \ + FLT_OTEL_CONF_STR(p); \ + int cfg_line; \ + struct list list; \ + } + + +/* Generic configuration header used for simple named list entries. */ +struct flt_otel_conf_hdr { + FLT_OTEL_CONF_HDR(id); /* A list containing header names. */ +}; + +/* flt_otel_conf_sample->exprs */ +struct flt_otel_conf_sample_expr { + FLT_OTEL_CONF_HDR(fmt_expr); /* The original sample expression format string. */ + struct sample_expr *expr; /* The sample expression. */ +}; + +/* + * flt_otel_conf_span->attributes + * flt_otel_conf_span->events (event_name -> OTELC_VALUE_STR(&extra)) + * flt_otel_conf_span->baggages + * flt_otel_conf_span->statuses (status_code -> extra.u.value_int32) + */ +struct flt_otel_conf_sample { + FLT_OTEL_CONF_HDR(key); /* The list containing sample names. */ + char *fmt_string; /* All sample-expression arguments are combined into a single string. */ + struct otelc_value extra; /* Optional supplementary data. */ + struct list exprs; /* Used to chain sample expressions. */ + int num_exprs; /* Number of defined expressions. */ +}; + +/* + * flt_otel_conf_scope->spans_to_finish + * + * It can be seen that this structure is actually identical to the structure + * flt_otel_conf_hdr. + */ +struct flt_otel_conf_str { + FLT_OTEL_CONF_HDR(str); /* A list containing character strings. */ +}; + +/* flt_otel_conf_scope->contexts */ +struct flt_otel_conf_context { + FLT_OTEL_CONF_HDR(id); /* The name of the context. */ + uint8_t flags; /* The type of storage from which the span context is extracted. */ +}; + +/* + * Span configuration within a scope. + * flt_otel_conf_scope->spans + */ +struct flt_otel_conf_span { + FLT_OTEL_CONF_HDR(id); /* The name of the span. */ + FLT_OTEL_CONF_STR(ref_id); /* The reference name, if used. */ + FLT_OTEL_CONF_STR(ctx_id); /* The span context name, if used. */ + uint8_t ctx_flags; /* The type of storage used for the span context. */ + bool flag_root; /* Whether this is a root span. */ + struct list attributes; /* The set of key:value attributes. */ + struct list events; /* The set of events with key-value attributes. */ + struct list baggages; /* The set of key:value baggage items. */ + struct list statuses; /* Span status code and description (only one per list). */ +}; + +/* Configuration for a single event scope. */ +struct flt_otel_conf_scope { + FLT_OTEL_CONF_HDR(id); /* The scope name. */ + bool flag_used; /* The indication that the scope is being used. */ + int event; /* FLT_OTEL_EVENT_* */ + uint idle_timeout; /* Idle timeout interval in milliseconds (0 = off). */ + struct list acls; /* ACLs declared on this scope. */ + struct acl_cond *cond; /* ACL condition to meet. */ + struct list contexts; /* Declared contexts. */ + struct list spans; /* Declared spans. */ + struct list spans_to_finish; /* The list of spans scheduled for finishing. */ +}; + +/* Configuration for a named group of scopes. */ +struct flt_otel_conf_group { + FLT_OTEL_CONF_HDR(id); /* The group name. */ + bool flag_used; /* The indication that the group is being used. */ + struct list ph_scopes; /* List of all used scopes. */ +}; + +/* Placeholder referencing a scope or group by name. */ +struct flt_otel_conf_ph { + FLT_OTEL_CONF_HDR(id); /* The scope/group name. */ + void *ptr; /* Pointer to real placeholder structure. */ +}; +#define flt_otel_conf_ph_group flt_otel_conf_ph +#define flt_otel_conf_ph_scope flt_otel_conf_ph + +/* Top-level OTel instrumentation settings (tracer, options). */ +struct flt_otel_conf_instr { + FLT_OTEL_CONF_HDR(id); /* The OpenTelemetry instrumentation name. */ + char *config; /* The OpenTelemetry configuration file name. */ + struct otelc_tracer *tracer; /* The OpenTelemetry tracer handle. */ + uint32_t rate_limit; /* [0 2^32-1] <-> [0.0 100.0] */ + bool flag_harderr; /* [0 1] */ + bool flag_disabled; /* [0 1] */ + uint8_t logging; /* [0 1 3] */ + struct proxy proxy_log; /* The log server list. */ + uint analyzers; /* Defined channel analyzers. */ + uint idle_timeout; /* Minimum idle timeout across scopes (ms, 0 = off). */ + struct list acls; /* ACLs declared on this tracer. */ + struct list ph_groups; /* List of all used groups. */ + struct list ph_scopes; /* List of all used scopes. */ +}; + +/* The OpenTelemetry filter configuration. */ +struct flt_otel_conf { + struct proxy *proxy; /* Proxy owning the filter. */ + char *id; /* The OpenTelemetry filter id. */ + char *cfg_file; /* The OpenTelemetry filter configuration file name. */ + struct flt_otel_conf_instr *instr; /* The OpenTelemetry instrumentation settings. */ + struct list groups; /* List of all available groups. */ + struct list scopes; /* List of all available scopes. */ + struct list smp_args; /* Deferred OTEL sample fetch args to resolve. */ +}; + + +/* Allocate and initialize a sample from parsed arguments. */ +struct flt_otel_conf_sample *flt_otel_conf_sample_init_ex(const char **args, int idx, int n, const struct otelc_value *extra, int line, struct list *head, char **err); + +/* Allocate and initialize the top-level OTel filter configuration. */ +struct flt_otel_conf *flt_otel_conf_init(struct proxy *px); + +/* Free the top-level OTel filter configuration. */ +void flt_otel_conf_free(struct flt_otel_conf **ptr); + +#endif /* _OTEL_CONF_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/include/conf_funcs.h b/addons/otel/include/conf_funcs.h new file mode 100644 index 000000000..af19aa9e1 --- /dev/null +++ b/addons/otel/include/conf_funcs.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_CONF_FUNCS_H_ +#define _OTEL_CONF_FUNCS_H_ + +/* + * Macro that generates a flt_otel_conf__init() function. The generated + * function allocates and initializes a configuration structure of the given + * type, checks for duplicate names in the list, and optionally runs a custom + * initializer body. + */ +#define FLT_OTEL_CONF_FUNC_INIT(_type_, _id_, _func_) \ + struct flt_otel_conf_##_type_ *flt_otel_conf_##_type_##_init(const char *id, int line, struct list *head, char **err) \ + { \ + struct flt_otel_conf_##_type_ *retptr = NULL; \ + struct flt_otel_conf_##_type_ *ptr; \ + size_t _id_##_len; \ + \ + OTELC_FUNC("\"%s\", %d, %p, %p:%p", OTELC_STR_ARG(id), line, head, OTELC_DPTR_ARGS(err)); \ + \ + if ((id == NULL) || (*id == '\0')) { \ + FLT_OTEL_ERR("name not set"); \ + \ + OTELC_RETURN_PTR(retptr); \ + } \ + \ + _id_##_len = strlen(id); \ + if (_id_##_len >= FLT_OTEL_ID_MAXLEN) { \ + FLT_OTEL_ERR("'%s' : name too long", id); \ + \ + OTELC_RETURN_PTR(retptr); \ + } \ + \ + if (head != NULL) \ + list_for_each_entry(ptr, head, list) \ + if (strcmp(ptr->_id_, id) == 0) { \ + FLT_OTEL_ERR("'%s' : already defined", id); \ + \ + OTELC_RETURN_PTR(retptr); \ + } \ + \ + retptr = OTELC_CALLOC(1, sizeof(*retptr)); \ + if (retptr != NULL) { \ + retptr->cfg_line = line; \ + retptr->_id_##_len = _id_##_len; \ + retptr->_id_ = OTELC_STRDUP(id); \ + if (retptr->_id_ != NULL) { \ + if (head != NULL) \ + LIST_APPEND(head, &(retptr->list)); \ + \ + FLT_OTEL_DBG_CONF_HDR("- conf_" #_type_ " init ", retptr, _id_); \ + } \ + else \ + OTELC_SFREE_CLEAR(retptr); \ + } \ + \ + if (retptr != NULL) { \ + _func_ \ + } \ + \ + if (retptr == NULL) \ + FLT_OTEL_ERR("out of memory"); \ + \ + OTELC_RETURN_PTR(retptr); \ + } + +/* + * Macro that generates a flt_otel_conf__free() function. The generated + * function runs a custom cleanup body, then frees the name string, removes the + * structure from its list, and frees the structure. + */ +#define FLT_OTEL_CONF_FUNC_FREE(_type_, _id_, _func_) \ + void flt_otel_conf_##_type_##_free(struct flt_otel_conf_##_type_ **ptr) \ + { \ + OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr)); \ + \ + if ((ptr == NULL) || (*ptr == NULL)) \ + OTELC_RETURN(); \ + \ + { _func_ } \ + \ + OTELC_SFREE((*ptr)->_id_); \ + FLT_OTEL_LIST_DEL(&((*ptr)->list)); \ + OTELC_SFREE_CLEAR(*ptr); \ + \ + OTELC_RETURN(); \ + } + + +/* The FLT_OTEL_LIST_DESTROY() macro uses the following two definitions. */ +#define flt_otel_conf_ph_group_free flt_otel_conf_ph_free +#define flt_otel_conf_ph_scope_free flt_otel_conf_ph_free + +/* Declare init/free function prototypes for a configuration type. */ +#define FLT_OTEL_CONF_FUNC_DECL(_type_) \ + struct flt_otel_conf_##_type_ *flt_otel_conf_##_type_##_init(const char *id, int line, struct list *head, char **err); \ + void flt_otel_conf_##_type_##_free(struct flt_otel_conf_##_type_ **ptr); + +FLT_OTEL_CONF_FUNC_DECL(hdr) +FLT_OTEL_CONF_FUNC_DECL(str) +FLT_OTEL_CONF_FUNC_DECL(ph) +FLT_OTEL_CONF_FUNC_DECL(sample_expr) +FLT_OTEL_CONF_FUNC_DECL(sample) +FLT_OTEL_CONF_FUNC_DECL(context) +FLT_OTEL_CONF_FUNC_DECL(span) +FLT_OTEL_CONF_FUNC_DECL(scope) +FLT_OTEL_CONF_FUNC_DECL(group) +FLT_OTEL_CONF_FUNC_DECL(instr) + +#endif /* _OTEL_CONF_FUNCS_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/include/config.h b/addons/otel/include/config.h index 75ac5d175..2f28c2a57 100644 --- a/addons/otel/include/config.h +++ b/addons/otel/include/config.h @@ -3,6 +3,7 @@ #ifndef _OTEL_CONFIG_H_ #define _OTEL_CONFIG_H_ +#define FLT_OTEL_ID_MAXLEN 64 /* Maximum identifier length. */ #define FLT_OTEL_DEBUG_LEVEL 0b11101111111 /* Default debug bitmask. */ #endif /* _OTEL_CONFIG_H_ */ diff --git a/addons/otel/include/define.h b/addons/otel/include/define.h index 92b9eb732..9e71df77e 100644 --- a/addons/otel/include/define.h +++ b/addons/otel/include/define.h @@ -3,9 +3,70 @@ #ifndef _OTEL_DEFINE_H_ #define _OTEL_DEFINE_H_ +/* Check whether argument at index n is in range, non-NULL and non-empty. */ +#define FLT_OTEL_ARG_ISVALID(n) ({ typeof(n) _n = (n); OTELC_IN_RANGE(_n, 0, MAX_LINE_ARGS - 1) && (args[_n] != NULL) && (*args[_n] != '\0'); }) + +/* Convert a floating-point percentage to a uint32_t rate value. */ +#define FLT_OTEL_FLOAT_U32(a) ((uint32_t)((a) / 100.0 * UINT32_MAX + 0.5)) + +/* Compile-time string length excluding the null terminator. */ +#define FLT_OTEL_STR_SIZE(a) (sizeof(a) - 1) + /* Execute a statement exactly once across all invocations. */ #define FLT_OTEL_RUN_ONCE(f) do { static bool _f = 1; if (_f) { _f = 0; { f; } } } while (0) +/* Check whether a list head has been initialized. */ +#define FLT_OTEL_LIST_ISVALID(a) ({ typeof(a) _a = (a); (_a != NULL) && (_a->n != NULL) && (_a->p != NULL); }) + +/* Safely delete a list element if its list head is valid. */ +#define FLT_OTEL_LIST_DEL(a) do { if (FLT_OTEL_LIST_ISVALID(a)) LIST_DELETE(a); } while (0) + +/* Destroy all elements in a typed configuration list. */ +#define FLT_OTEL_LIST_DESTROY(t,h) \ + do { \ + struct flt_otel_conf_##t *_ptr, *_back; \ + \ + if (!FLT_OTEL_LIST_ISVALID(h) || LIST_ISEMPTY(h)) \ + break; \ + \ + OTELC_DBG(NOTICE, "- deleting " #t " list %s", flt_otel_list_dump(h)); \ + \ + list_for_each_entry_safe(_ptr, _back, (h), list) \ + flt_otel_conf_##t##_free(&_ptr); \ + } while (0) + +/* Declare a rotating thread-local string buffer pool. */ +#define FLT_OTEL_BUFFER_THR(b,m,n,p) \ + static THREAD_LOCAL char b[m][n]; \ + static THREAD_LOCAL size_t __idx = 0; \ + char *p = b[__idx]; \ + __idx = (__idx + 1) % (m) + +/* Format an error message if none has been set yet. */ +#define FLT_OTEL_ERR(f, ...) \ + do { \ + if ((err != NULL) && (*err == NULL)) { \ + (void)memprintf(err, f, ##__VA_ARGS__); \ + \ + OTELC_DBG(DEBUG, "err: '%s'", *err); \ + } \ + } while (0) +/* Append to an existing error message unconditionally. */ +#define FLT_OTEL_ERR_APPEND(f, ...) \ + do { \ + if (err != NULL) \ + (void)memprintf(err, f, ##__VA_ARGS__); \ + } while (0) +/* Log an error message and free its memory. */ +#define FLT_OTEL_ERR_FREE(p) \ + do { \ + if ((p) == NULL) \ + break; \ + \ + OTELC_DBG(LOG, "%s:%d: ERROR: %s", __func__, __LINE__, (p)); \ + OTELC_SFREE_CLEAR(p); \ + } while (0) + #endif /* _OTEL_DEFINE_H_ */ /* diff --git a/addons/otel/include/include.h b/addons/otel/include/include.h index 64339a55f..a3e6c500f 100644 --- a/addons/otel/include/include.h +++ b/addons/otel/include/include.h @@ -27,8 +27,11 @@ #include "config.h" #include "define.h" +#include "conf.h" +#include "conf_funcs.h" #include "filter.h" #include "parser.h" +#include "util.h" #endif /* _OTEL_INCLUDE_H_ */ diff --git a/addons/otel/include/util.h b/addons/otel/include/util.h new file mode 100644 index 000000000..ae230da21 --- /dev/null +++ b/addons/otel/include/util.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_UTIL_H_ +#define _OTEL_UTIL_H_ + +#define FLT_OTEL_HTTP_METH_DEFINES \ + FLT_OTEL_HTTP_METH_DEF(OPTIONS) \ + FLT_OTEL_HTTP_METH_DEF(GET) \ + FLT_OTEL_HTTP_METH_DEF(HEAD) \ + FLT_OTEL_HTTP_METH_DEF(POST) \ + FLT_OTEL_HTTP_METH_DEF(PUT) \ + FLT_OTEL_HTTP_METH_DEF(DELETE) \ + FLT_OTEL_HTTP_METH_DEF(TRACE) \ + FLT_OTEL_HTTP_METH_DEF(CONNECT) + +#ifdef DEBUG_OTEL +# define FLT_OTEL_ARGS_DUMP() do { if (otelc_dbg_level & (1 << OTELC_DBG_LEVEL_LOG)) flt_otel_args_dump((const char **)args); } while (0) +#else +# define FLT_OTEL_ARGS_DUMP() while (0) +#endif + + +#ifdef DEBUG_OTEL +/* Dump configuration arguments for debugging. */ +void flt_otel_args_dump(const char **args); + +/* Dump a linked list of configuration items as a string. */ +const char *flt_otel_list_dump(const struct list *head); +#endif + +/* Count the number of non-NULL arguments in an argument array. */ +int flt_otel_args_count(const char **args); + +/* Concatenate argument array elements into a single string. */ +int flt_otel_args_concat(const char **args, int idx, int n, char **str); + +/* Convert sample data to a string representation. */ +int flt_otel_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err); + +#endif /* _OTEL_UTIL_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/src/conf.c b/addons/otel/src/conf.c new file mode 100644 index 000000000..e5a5279eb --- /dev/null +++ b/addons/otel/src/conf.c @@ -0,0 +1,715 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../include/include.h" + + +/*** + * NAME + * flt_otel_conf_hdr_init - conf_hdr structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_hdr *flt_otel_conf_hdr_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_hdr structure. The string is + * duplicated and stored as the header identifier. If is non-NULL, + * the structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(hdr, id, ) + + +/*** + * NAME + * flt_otel_conf_hdr_free - conf_hdr structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_hdr_free(struct flt_otel_conf_hdr **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_hdr structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(hdr, id, + FLT_OTEL_DBG_CONF_HDR("- conf_hdr free ", *ptr, id); +) + + +/*** + * NAME + * flt_otel_conf_str_init - conf_str structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_str *flt_otel_conf_str_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_str structure. The string is + * duplicated and stored as the string value. If is non-NULL, the + * structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(str, str, ) + + +/*** + * NAME + * flt_otel_conf_str_free - conf_str structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_str_free(struct flt_otel_conf_str **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_str structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(str, str, + FLT_OTEL_DBG_CONF_HDR("- conf_str free ", *ptr, str); +) + + +/*** + * NAME + * flt_otel_conf_ph_init - conf_ph placeholder structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_ph *flt_otel_conf_ph_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_ph (placeholder) structure. The + * string is duplicated and stored as the placeholder identifier. If + * is non-NULL, the structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(ph, id, ) + + +/*** + * NAME + * flt_otel_conf_ph_free - conf_ph structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_ph_free(struct flt_otel_conf_ph **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_ph structure and its contents, + * then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(ph, id, + FLT_OTEL_DBG_CONF_HDR("- conf_ph free ", *ptr, id); +) + + +/*** + * NAME + * flt_otel_conf_sample_expr_init - conf_sample_expr structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_sample_expr *flt_otel_conf_sample_expr_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_sample_expr structure. The string is + * duplicated and stored as the expression value. If is non-NULL, the + * structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(sample_expr, fmt_expr, ) + + +/*** + * NAME + * flt_otel_conf_sample_expr_free - conf_sample_expr structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_sample_expr_free(struct flt_otel_conf_sample_expr **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_sample_expr structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(sample_expr, fmt_expr, + FLT_OTEL_DBG_CONF_SAMPLE_EXPR("- conf_sample_expr free ", *ptr); + + release_sample_expr((*ptr)->expr); +) + + +/*** + * NAME + * flt_otel_conf_sample_init - conf_sample structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_sample *flt_otel_conf_sample_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_sample structure. The string is + * duplicated and stored as the sample key. If is non-NULL, the + * structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(sample, key, + LIST_INIT(&(retptr->exprs)); +) + + +/*** + * NAME + * flt_otel_conf_sample_init_ex - extended sample initialization + * + * SYNOPSIS + * struct flt_otel_conf_sample *flt_otel_conf_sample_init_ex(const char **args, int idx, int n, const struct otelc_value *extra, int line, struct list *head, char **err) + * + * ARGUMENTS + * args - configuration line arguments array + * idx - position where sample value starts + * n - maximum number of arguments to concatenate (0 means all) + * extra - optional extra data (event name or status code) + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Creates and initializes a conf_sample structure with extended data. Calls + * flt_otel_conf_sample_init() with as the sample key to + * create the base structure, copies data (event name string or status + * code integer), concatenates the remaining arguments into the sample value + * string, and counts the number of sample expressions. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +struct flt_otel_conf_sample *flt_otel_conf_sample_init_ex(const char **args, int idx, int n, const struct otelc_value *extra, int line, struct list *head, char **err) +{ + struct flt_otel_conf_sample *retptr = NULL; + + OTELC_FUNC("%p, %d, %d, %p, %d, %p, %p:%p", args, idx, n, extra, line, head, OTELC_DPTR_ARGS(err)); + + OTELC_DBG_VALUE(DEBUG, "extra ", extra); + + /* Ensure the sample value is present in the args[] array. */ + if (flt_otel_args_count(args) <= idx) { + FLT_OTEL_ERR("'%s' : too few arguments", args[0]); + + OTELC_RETURN_PTR(retptr); + } + + /* The sample key is located at the idx location of the args[] field. */ + retptr = flt_otel_conf_sample_init(args[idx - 1], line, head, err); + if (retptr == NULL) + OTELC_RETURN_PTR(retptr); + + if ((extra == NULL) || (extra->u_type == OTELC_VALUE_NULL)) { + /* + * Do nothing - sample extra data is not set or initialized, + * which means it is not used. + */ + } + else if (extra->u_type == OTELC_VALUE_STRING) { + retptr->extra.u_type = OTELC_VALUE_DATA; + retptr->extra.u.value_data = OTELC_STRDUP(extra->u.value_string); + if (retptr->extra.u.value_data == NULL) { + FLT_OTEL_ERR("out of memory"); + flt_otel_conf_sample_free(&retptr); + + OTELC_RETURN_PTR(retptr); + } + } + else if (extra->u_type == OTELC_VALUE_INT32) { + retptr->extra.u_type = extra->u_type; + retptr->extra.u.value_int32 = extra->u.value_int32; + } + else { + FLT_OTEL_ERR("invalid sample extra data type: %d", extra->u_type); + flt_otel_conf_sample_free(&retptr); + + OTELC_RETURN_PTR(retptr); + } + + /* The sample value starts in the args[] array after the key. */ + retptr->num_exprs = flt_otel_args_concat(args, idx, n, &(retptr->fmt_string)); + if (retptr->num_exprs == FLT_OTEL_RET_ERROR) { + FLT_OTEL_ERR("out of memory"); + flt_otel_conf_sample_free(&retptr); + + OTELC_RETURN_PTR(retptr); + } + + FLT_OTEL_DBG_CONF_SAMPLE("- conf_sample init ", retptr); + + OTELC_RETURN_PTR(retptr); +} + + +/*** + * NAME + * flt_otel_conf_sample_free - conf_sample structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_sample_free(struct flt_otel_conf_sample **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_sample structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(sample, key, + FLT_OTEL_DBG_CONF_SAMPLE("- conf_sample free ", *ptr); + + OTELC_SFREE((*ptr)->fmt_string); + if ((*ptr)->extra.u_type == OTELC_VALUE_DATA) + OTELC_SFREE((*ptr)->extra.u.value_data); + FLT_OTEL_LIST_DESTROY(sample_expr, &((*ptr)->exprs)); +) + + +/*** + * NAME + * flt_otel_conf_context_init - conf_context structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_context *flt_otel_conf_context_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_context structure. The string is + * duplicated and stored as the context identifier. If is non-NULL, + * the structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(context, id, ) + + +/*** + * NAME + * flt_otel_conf_context_free - conf_context structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_context_free(struct flt_otel_conf_context **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_context structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(context, id, + FLT_OTEL_DBG_CONF_HDR("- conf_context free ", *ptr, id); +) + + +/*** + * NAME + * flt_otel_conf_span_init - conf_span structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_span *flt_otel_conf_span_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_span structure with empty lists for links, + * attributes, events, baggages, and statuses. The string is duplicated + * and stored as the span name. If is non-NULL, the structure is + * appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(span, id, + LIST_INIT(&(retptr->attributes)); + LIST_INIT(&(retptr->events)); + LIST_INIT(&(retptr->baggages)); + LIST_INIT(&(retptr->statuses)); +) + + +/*** + * NAME + * flt_otel_conf_span_free - conf_span structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_span_free(struct flt_otel_conf_span **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_span structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(span, id, + FLT_OTEL_DBG_CONF_HDR("- conf_span free ", *ptr, id); + + OTELC_SFREE((*ptr)->ref_id); + OTELC_SFREE((*ptr)->ctx_id); + FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes)); + FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->events)); + FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->baggages)); + FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->statuses)); +) + + +/*** + * NAME + * flt_otel_conf_scope_init - conf_scope structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_scope *flt_otel_conf_scope_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_scope structure with empty lists for ACLs, + * contexts, spans, spans_to_finish, and instruments. The string is + * duplicated and stored as the scope name. If is non-NULL, the + * structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(scope, id, + LIST_INIT(&(retptr->acls)); + LIST_INIT(&(retptr->contexts)); + LIST_INIT(&(retptr->spans)); + LIST_INIT(&(retptr->spans_to_finish)); +) + + +/*** + * NAME + * flt_otel_conf_scope_free - conf_scope structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_scope_free(struct flt_otel_conf_scope **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_scope structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(scope, id, + struct acl *acl; + struct acl *aclback; + + FLT_OTEL_DBG_CONF_SCOPE("- conf_scope free ", *ptr); + + list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) { + prune_acl(acl); + FLT_OTEL_LIST_DEL(&(acl->list)); + OTELC_SFREE(acl); + } + free_acl_cond((*ptr)->cond); + FLT_OTEL_LIST_DESTROY(context, &((*ptr)->contexts)); + FLT_OTEL_LIST_DESTROY(span, &((*ptr)->spans)); + FLT_OTEL_LIST_DESTROY(str, &((*ptr)->spans_to_finish)); +) + + +/*** + * NAME + * flt_otel_conf_group_init - conf_group structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_group *flt_otel_conf_group_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_group structure with an empty placeholder + * scope list. The string is duplicated and stored as the group name. + * If is non-NULL, the structure is appended to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(group, id, + LIST_INIT(&(retptr->ph_scopes)); +) + + +/*** + * NAME + * flt_otel_conf_group_free - conf_group structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_group_free(struct flt_otel_conf_group **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_group structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(group, id, + FLT_OTEL_DBG_CONF_GROUP("- conf_group free ", *ptr); + + FLT_OTEL_LIST_DESTROY(ph_scope, &((*ptr)->ph_scopes)); +) + + +/*** + * NAME + * flt_otel_conf_instr_init - conf_instr structure allocation + * + * SYNOPSIS + * struct flt_otel_conf_instr *flt_otel_conf_instr_init(const char *id, int line, struct list *head, char **err) + * + * ARGUMENTS + * id - identifier string to duplicate + * line - configuration file line number + * head - list to append to (or NULL) + * err - indirect pointer to error message string + * + * DESCRIPTION + * Allocates and initializes a conf_instr (instrumentation) structure. Sets + * the default rate limit to 100%, initializes the proxy_log for logger + * support, and creates empty lists for ACLs, placeholder groups, and + * placeholder scopes. The string is duplicated and stored as the + * instrumentation name. If is non-NULL, the structure is appended + * to the list. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +FLT_OTEL_CONF_FUNC_INIT(instr, id, + retptr->rate_limit = FLT_OTEL_FLOAT_U32(100.0); + init_new_proxy(&(retptr->proxy_log)); + LIST_INIT(&(retptr->acls)); + LIST_INIT(&(retptr->ph_groups)); + LIST_INIT(&(retptr->ph_scopes)); +) + + +/*** + * NAME + * flt_otel_conf_instr_free - conf_instr structure deallocation + * + * SYNOPSIS + * void flt_otel_conf_instr_free(struct flt_otel_conf_instr **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf_instr structure and its + * contents, then removes it from the list of structures of that type. + * + * RETURN VALUE + * This function does not return a value. + */ +FLT_OTEL_CONF_FUNC_FREE(instr, id, + struct acl *acl; + struct acl *aclback; + struct logger *logger; + struct logger *loggerback; + + FLT_OTEL_DBG_CONF_INSTR("- conf_instr free ", *ptr); + + OTELC_SFREE((*ptr)->config); + OTELC_DBG(NOTICE, "- deleting acls list %s", flt_otel_list_dump(&((*ptr)->acls))); + list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) { + prune_acl(acl); + FLT_OTEL_LIST_DEL(&(acl->list)); + OTELC_SFREE(acl); + } + OTELC_DBG(NOTICE, "- deleting proxy_log.loggers list %s", flt_otel_list_dump(&((*ptr)->proxy_log.loggers))); + list_for_each_entry_safe(logger, loggerback, &((*ptr)->proxy_log.loggers), list) { + LIST_DELETE(&(logger->list)); + ha_free(&logger); + } + FLT_OTEL_LIST_DESTROY(ph_group, &((*ptr)->ph_groups)); + FLT_OTEL_LIST_DESTROY(ph_scope, &((*ptr)->ph_scopes)); +) + + +/*** + * NAME + * flt_otel_conf_init - top-level filter configuration allocation + * + * SYNOPSIS + * struct flt_otel_conf *flt_otel_conf_init(struct proxy *px) + * + * ARGUMENTS + * px - proxy instance to associate with + * + * DESCRIPTION + * Allocates and initializes the top-level flt_otel_conf structure. Stores + * the proxy reference and creates empty group and scope lists. + * + * RETURN VALUE + * Returns a pointer to the initialized structure, or NULL on failure. + */ +struct flt_otel_conf *flt_otel_conf_init(struct proxy *px) +{ + struct flt_otel_conf *retptr; + + OTELC_FUNC("%p", px); + + retptr = OTELC_CALLOC(1, sizeof(*retptr)); + if (retptr == NULL) + OTELC_RETURN_PTR(retptr); + + retptr->proxy = px; + LIST_INIT(&(retptr->groups)); + LIST_INIT(&(retptr->scopes)); + LIST_INIT(&(retptr->smp_args)); + + FLT_OTEL_DBG_CONF("- conf init ", retptr); + + OTELC_RETURN_PTR(retptr); +} + + +/*** + * NAME + * flt_otel_conf_free - top-level filter configuration deallocation + * + * SYNOPSIS + * void flt_otel_conf_free(struct flt_otel_conf **ptr) + * + * ARGUMENTS + * ptr - a pointer to the address of a structure + * + * DESCRIPTION + * Deallocates memory used by the flt_otel_conf structure and its contents. + * + * RETURN VALUE + * This function does not return a value. + */ +void flt_otel_conf_free(struct flt_otel_conf **ptr) +{ + struct arg_list *cur, *back; + + OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr)); + + if ((ptr == NULL) || (*ptr == NULL)) + OTELC_RETURN(); + + FLT_OTEL_DBG_CONF("- conf free ", *ptr); + + OTELC_SFREE((*ptr)->id); + OTELC_SFREE((*ptr)->cfg_file); + flt_otel_conf_instr_free(&((*ptr)->instr)); + FLT_OTEL_LIST_DESTROY(group, &((*ptr)->groups)); + FLT_OTEL_LIST_DESTROY(scope, &((*ptr)->scopes)); + /* Free any unresolved OTEL sample fetch args (error path). */ + list_for_each_entry_safe(cur, back, &((*ptr)->smp_args), list) { + LIST_DELETE(&(cur->list)); + ha_free(&cur); + } + OTELC_SFREE_CLEAR(*ptr); + + OTELC_RETURN(); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/src/parser.c b/addons/otel/src/parser.c index 4d9172598..4f78c90f1 100644 --- a/addons/otel/src/parser.c +++ b/addons/otel/src/parser.c @@ -50,6 +50,8 @@ static int flt_otel_parse(char **args, int *cur_arg, struct proxy *px, struct fl ); #endif + FLT_OTEL_ARGS_DUMP(); + OTELC_RETURN_INT(retval); } diff --git a/addons/otel/src/util.c b/addons/otel/src/util.c new file mode 100644 index 000000000..6be6cd368 --- /dev/null +++ b/addons/otel/src/util.c @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../include/include.h" + + +#ifdef DEBUG_OTEL + +/*** + * NAME + * flt_otel_args_dump - debug configuration arguments dump + * + * SYNOPSIS + * void flt_otel_args_dump(const char **args) + * + * ARGUMENTS + * args - configuration line arguments array + * + * DESCRIPTION + * Dumps all configuration arguments to stderr. Counts the number of valid + * arguments via flt_otel_args_count() and prints each one surrounded by + * single quotes. + * + * RETURN VALUE + * This function does not return a value. + */ +void flt_otel_args_dump(const char **args) +{ + int i, argc; + + argc = flt_otel_args_count(args); + + (void)fprintf(stderr, OTELC_DBG_FMT("args[%d]: { '%s' "), argc, args[0]); + + for (i = 1; i < argc; i++) + (void)fprintf(stderr, "'%s' ", args[i]); + + (void)fprintf(stderr, "}\n"); +} + + +/*** + * NAME + * flt_otel_list_dump - debug list summary + * + * SYNOPSIS + * const char *flt_otel_list_dump(const struct list *head) + * + * ARGUMENTS + * head - list head to summarize + * + * DESCRIPTION + * Returns a concise summary string describing the state of a linked list. + * For NULL or empty lists, returns a descriptive label. For single-element + * lists, returns the element pointer. For multi-element lists, returns the + * first and last pointers along with the element count. Uses a rotating + * thread-local buffer for the return value. + * + * RETURN VALUE + * Returns a pointer to a thread-local string describing the list. + */ +const char *flt_otel_list_dump(const struct list *head) +{ + FLT_OTEL_BUFFER_THR(retbuf, 4, 64, retptr); + + if ((head == NULL) || LIST_ISEMPTY(head)) { + (void)strncpy(retptr, (head == NULL) ? "{ null list }" : "{ empty list }", sizeof(retbuf[0])); + } + else if (head->p == head->n) { + (void)snprintf(retptr, sizeof(retbuf[0]), "{ %p * 1 }", head->p); + } + else { + const struct list *ptr; + size_t count = 0; + + for (ptr = head->n; ptr != head; ptr = ptr->n, count++); + + (void)snprintf(retptr, sizeof(retbuf[0]), "{ %p %p %zu }", head->p, head->n, count); + } + + return (retptr); +} + +#endif /* DEBUG_OTEL */ + + +/*** + * NAME + * flt_otel_args_count - argument count + * + * SYNOPSIS + * int flt_otel_args_count(const char **args) + * + * ARGUMENTS + * args - configuration line arguments array + * + * DESCRIPTION + * Counts the number of valid (non-NULL) arguments in . Scans up to + * MAX_LINE_ARGS entries, handling gaps from blank arguments by returning the + * index of the last valid argument incremented by one. + * + * RETURN VALUE + * Returns the number of valid arguments. + */ +int flt_otel_args_count(const char **args) +{ + int i, retval = 0; + + if (args == NULL) + return retval; + + /* + * It is possible that some arguments within the configuration line + * are not specified; that is, they are set to a blank string. + * + * For example: + * keyword '' arg_2 + * + * In that case the content of the args field will be like this: + * args[0]: 'keyword' + * args[1]: NULL pointer + * args[2]: 'arg_2' + * args[3 .. MAX_LINE_ARGS): NULL pointers + * + * The total number of arguments is the index of the last argument + * (increased by 1) that is not a NULL pointer. + */ + for (i = 0; i < MAX_LINE_ARGS; i++) + if (FLT_OTEL_ARG_ISVALID(i)) + retval = i + 1; + + return retval; +} + + +/*** + * NAME + * flt_otel_args_concat - argument concatenation + * + * SYNOPSIS + * int flt_otel_args_concat(const char **args, int idx, int n, char **str) + * + * ARGUMENTS + * args - configuration line arguments array + * idx - starting index for concatenation + * n - maximum number of arguments to concatenate (0 means all) + * str - indirect pointer to the result string + * + * DESCRIPTION + * Concatenates arguments starting from index into a single + * space-separated string. The result is built via memprintf() into <*str>. + * NULL arguments within the range are treated as empty strings. + * + * RETURN VALUE + * Returns the number of concatenated arguments, or FLT_OTEL_RET_ERROR on + * failure. + */ +int flt_otel_args_concat(const char **args, int idx, int n, char **str) +{ + int i, argc; + + if ((args == NULL) || (str == NULL)) + return FLT_OTEL_RET_ERROR; + else if ((idx < 0) || (n < 0)) + return FLT_OTEL_RET_ERROR; + + argc = (n == 0) ? flt_otel_args_count(args) : OTELC_MIN(flt_otel_args_count(args), idx + n); + + for (i = idx; i < argc; i++) + (void)memprintf(str, "%s%s%s", (*str == NULL) ? "" : *str, (i == idx) ? "" : " ", (args[i] == NULL) ? "" : args[i]); + + OTELC_DBG(DEBUG, "args[%d, %d]: '%s'", idx, argc, (*str == NULL) ? "" : *str); + + return (*str == NULL) ? FLT_OTEL_RET_ERROR : (i - idx); +} + + +/*** + * NAME + * flt_otel_sample_to_str - sample data to string conversion + * + * SYNOPSIS + * int flt_otel_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err) + * + * ARGUMENTS + * data - sample data to convert + * value - output buffer for the string representation + * size - output buffer size + * err - indirect pointer to error message string + * + * DESCRIPTION + * Converts sample data to its string representation. Handles bool, sint, + * IPv4, IPv6, str, and HTTP method types. Boolean values are written as + * "0" or "1". Integer values use snprintf(). IP addresses are converted + * via inet_ntop(). String values are copied directly. HTTP methods are + * resolved to their standard string names; the HTTP_METH_OTHER type uses + * the method's raw string data. Binary and unknown types produce an error. + * + * RETURN VALUE + * Returns the number of characters written to , + * or FLT_OTEL_RET_ERROR on failure. + */ +int flt_otel_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err) +{ + int retval = FLT_OTEL_RET_ERROR; + + OTELC_FUNC("%p, %p, %zu, %p:%p", data, value, size, OTELC_DPTR_ARGS(err)); + + if ((data == NULL) || (value == NULL) || (size == 0)) + OTELC_RETURN_INT(retval); + + *value = '\0'; + + /* Convert the sample value to a string based on its type. */ + if (data->type == SMP_T_ANY) { + FLT_OTEL_ERR("invalid sample data type %d", data->type); + } + else if (data->type == SMP_T_BOOL) { + value[0] = data->u.sint ? '1' : '0'; + value[1] = '\0'; + + retval = 1; + } + else if (data->type == SMP_T_SINT) { + retval = snprintf(value, size, "%lld", data->u.sint); + } + else if (data->type == SMP_T_ADDR) { + /* This type is never used to qualify a sample. */ + } + else if (data->type == SMP_T_IPV4) { + if (INET_ADDRSTRLEN > size) + FLT_OTEL_ERR("sample data size too large"); + else if (inet_ntop(AF_INET, &(data->u.ipv4), value, INET_ADDRSTRLEN) == NULL) + FLT_OTEL_ERR("invalid IPv4 address"); + else + retval = strlen(value); + } + else if (data->type == SMP_T_IPV6) { + if (INET6_ADDRSTRLEN > size) + FLT_OTEL_ERR("sample data size too large"); + else if (inet_ntop(AF_INET6, &(data->u.ipv6), value, INET6_ADDRSTRLEN) == NULL) + FLT_OTEL_ERR("invalid IPv6 address"); + else + retval = strlen(value); + } + else if (data->type == SMP_T_STR) { + if (data->u.str.data >= size) { + FLT_OTEL_ERR("sample data size too large"); + } + else if (data->u.str.data > 0) { + retval = data->u.str.data; + (void)memcpy(value, data->u.str.area, retval); + value[retval] = '\0'; + } + else { + /* + * There is no content to add but we will still return + * the correct status. + */ + retval = 0; + } + } + else if (data->type == SMP_T_BIN) { + FLT_OTEL_ERR("invalid sample data type %d", data->type); + } + else if (data->type != SMP_T_METH) { + FLT_OTEL_ERR("invalid sample data type %d", data->type); + } + else if (OTELC_IN_RANGE(data->u.meth.meth, HTTP_METH_OPTIONS, HTTP_METH_CONNECT)) { +#define FLT_OTEL_HTTP_METH_DEF(a) { #a, FLT_OTEL_STR_SIZE(#a) }, + static const struct { + const char *str; + size_t len; + } http_meth_str[] = { FLT_OTEL_HTTP_METH_DEFINES }; +#undef FLT_OTEL_HTTP_METH_DEF + + retval = http_meth_str[data->u.meth.meth].len; + (void)memcpy(value, http_meth_str[data->u.meth.meth].str, retval + 1); + } + else if (data->u.meth.meth == HTTP_METH_OTHER) { + if (data->u.meth.str.data >= size) { + FLT_OTEL_ERR("sample data size too large"); + } else { + retval = data->u.meth.str.data; + (void)memcpy(value, data->u.meth.str.area, retval); + value[retval] = '\0'; + } + } + else { + FLT_OTEL_ERR("invalid HTTP method"); + } + + if (retval != FLT_OTEL_RET_ERROR) + OTELC_DBG(DEBUG, "sample value (%d): '%.*s' %d", data->type, retval, value, retval); + + OTELC_RETURN_INT(retval); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */