/*** * 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" static struct pool_head *pool_head_ot_scope_span __read_mostly = NULL; static struct pool_head *pool_head_ot_scope_context __read_mostly = NULL; static struct pool_head *pool_head_ot_runtime_context __read_mostly = NULL; #ifdef USE_POOL_OT_SCOPE_SPAN REGISTER_POOL(&pool_head_ot_scope_span, "ot_scope_span", sizeof(struct flt_ot_scope_span)); #endif #ifdef USE_POOL_OT_SCOPE_CONTEXT REGISTER_POOL(&pool_head_ot_scope_context, "ot_scope_context", sizeof(struct flt_ot_scope_context)); #endif #ifdef USE_POOL_OT_RUNTIME_CONTEXT REGISTER_POOL(&pool_head_ot_runtime_context, "ot_runtime_context", sizeof(struct flt_ot_runtime_context)); #endif #ifdef DEBUG_OT /*** * NAME * flt_ot_pools_info - * * ARGUMENTS * This function takes no arguments. * * DESCRIPTION * - * * RETURN VALUE * This function does not return a value. */ void flt_ot_pools_info(void) { /* * In case we have some error in the configuration file, * it is possible that this pool was not initialized. */ #ifdef USE_POOL_BUFFER FLT_OT_DBG(2, "sizeof_pool(buffer) = %u", FLT_OT_DEREF(pool_head_buffer, size, 0)); #endif #ifdef USE_TRASH_CHUNK FLT_OT_DBG(2, "sizeof_pool(trash) = %u", FLT_OT_DEREF(pool_head_trash, size, 0)); #endif #ifdef USE_POOL_OT_SCOPE_SPAN FLT_OT_DBG(2, "sizeof_pool(ot_scope_span) = %u", pool_head_ot_scope_span->size); #endif #ifdef USE_POOL_OT_SCOPE_CONTEXT FLT_OT_DBG(2, "sizeof_pool(ot_scope_context) = %u", pool_head_ot_scope_context->size); #endif #ifdef USE_POOL_OT_RUNTIME_CONTEXT FLT_OT_DBG(2, "sizeof_pool(ot_runtime_context) = %u", pool_head_ot_runtime_context->size); #endif } #endif /* DEBUG_OT */ /*** * NAME * flt_ot_runtime_context_init - * * ARGUMENTS * s - * f - * err - * * DESCRIPTION * - * * RETURN VALUE * - */ struct flt_ot_runtime_context *flt_ot_runtime_context_init(struct stream *s, struct filter *f, char **err) { const struct flt_ot_conf *conf = FLT_OT_CONF(f); struct buffer uuid; struct flt_ot_runtime_context *retptr = NULL; FLT_OT_FUNC("%p, %p, %p:%p", s, f, FLT_OT_DPTR_ARGS(err)); retptr = flt_ot_pool_alloc(pool_head_ot_runtime_context, sizeof(*retptr), 1, err); if (retptr == NULL) FLT_OT_RETURN_PTR(retptr); retptr->stream = s; retptr->filter = f; retptr->flag_harderr = conf->tracer->flag_harderr; retptr->flag_disabled = conf->tracer->flag_disabled; retptr->logging = conf->tracer->logging; LIST_INIT(&(retptr->spans)); LIST_INIT(&(retptr->contexts)); uuid = b_make(retptr->uuid, sizeof(retptr->uuid), 0, 0); ha_generate_uuid(&uuid); #ifdef USE_OT_VARS /* * The HAProxy variable 'sess.ot.uuid' is registered here, * after which its value is set to runtime context UUID. */ if (flt_ot_var_register(FLT_OT_VAR_UUID, err) != -1) (void)flt_ot_var_set(s, FLT_OT_VAR_UUID, retptr->uuid, SMP_OPT_DIR_REQ, err); #endif FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", retptr); FLT_OT_RETURN_PTR(retptr); } /*** * NAME * flt_ot_runtime_context_free - * * ARGUMENTS * f - * * DESCRIPTION * - * * RETURN VALUE * This function does not return a value. */ void flt_ot_runtime_context_free(struct filter *f) { struct flt_ot_runtime_context *rt_ctx = f->ctx; FLT_OT_FUNC("%p", f); if (rt_ctx == NULL) FLT_OT_RETURN(); FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx); if (!LIST_ISEMPTY(&(rt_ctx->spans))) { struct timespec ts; struct flt_ot_scope_span *span, *span_back; /* All spans should be completed at the same time. */ (void)clock_gettime(CLOCK_MONOTONIC, &ts); list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list) { ot_span_finish(&(span->span), &ts, NULL, NULL, NULL); flt_ot_scope_span_free(&span); } } if (!LIST_ISEMPTY(&(rt_ctx->contexts))) { struct flt_ot_scope_context *ctx, *ctx_back; list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list) flt_ot_scope_context_free(&ctx); } flt_ot_pool_free(pool_head_ot_runtime_context, &(f->ctx)); FLT_OT_RETURN(); } /*** * NAME * flt_ot_scope_span_init - * * ARGUMENTS * rt_ctx - * id - * id_len - * ref_type - * ref_id - * ref_id_len - * dir - * err - * * DESCRIPTION * - * * RETURN VALUE * - */ struct flt_ot_scope_span *flt_ot_scope_span_init(struct flt_ot_runtime_context *rt_ctx, const char *id, size_t id_len, otc_span_reference_type_t ref_type, const char *ref_id, size_t ref_id_len, uint dir, char **err) { struct otc_span *ref_span = NULL; struct otc_span_context *ref_ctx = NULL; struct flt_ot_scope_span *span, *retptr = NULL; struct flt_ot_scope_context *ctx; FLT_OT_FUNC("%p, \"%s\", %zu, %d, \"%s\", %zu, %u, %p:%p", rt_ctx, id, id_len, ref_type, ref_id, ref_id_len, dir, FLT_OT_DPTR_ARGS(err)); if ((rt_ctx == NULL) || (id == NULL)) FLT_OT_RETURN_PTR(retptr); list_for_each_entry(span, &(rt_ctx->spans), list) if ((span->id_len == id_len) && (memcmp(span->id, id, id_len) == 0)) { FLT_OT_DBG(2, "found span %p", span); FLT_OT_RETURN_PTR(span); } if (ref_id != NULL) { list_for_each_entry(span, &(rt_ctx->spans), list) if ((span->id_len == ref_id_len) && (memcmp(span->id, ref_id, ref_id_len) == 0)) { ref_span = span->span; break; } if (ref_span != NULL) { FLT_OT_DBG(2, "found referenced span %p", span); } else { list_for_each_entry(ctx, &(rt_ctx->contexts), list) if ((ctx->id_len == ref_id_len) && (memcmp(ctx->id, ref_id, ref_id_len) == 0)) { ref_ctx = ctx->context; break; } if (ref_ctx != NULL) { FLT_OT_DBG(2, "found referenced context %p", ctx); } else { FLT_OT_ERR("cannot find referenced span/context '%s'", ref_id); FLT_OT_RETURN_PTR(retptr); } } } retptr = flt_ot_pool_alloc(pool_head_ot_scope_span, sizeof(*retptr), 1, err); if (retptr == NULL) FLT_OT_RETURN_PTR(retptr); retptr->id = id; retptr->id_len = id_len; retptr->smp_opt_dir = dir; retptr->ref_type = ref_type; retptr->ref_span = ref_span; retptr->ref_ctx = ref_ctx; LIST_INSERT(&(rt_ctx->spans), &(retptr->list)); FLT_OT_DBG_SCOPE_SPAN("new span ", retptr); FLT_OT_RETURN_PTR(retptr); } /*** * NAME * flt_ot_scope_span_free - * * ARGUMENTS * ptr - * * DESCRIPTION * - * * RETURN VALUE * This function does not return a value. */ void flt_ot_scope_span_free(struct flt_ot_scope_span **ptr) { FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr)); if ((ptr == NULL) || (*ptr == NULL)) FLT_OT_RETURN(); FLT_OT_DBG_SCOPE_SPAN("", *ptr); /* If the span is still active, do nothing. */ if ((*ptr)->span != NULL) { FLT_OT_DBG(2, "cannot finish active span"); FLT_OT_RETURN(); } FLT_OT_LIST_DEL(&((*ptr)->list)); flt_ot_pool_free(pool_head_ot_scope_span, (void **)ptr); FLT_OT_RETURN(); } /*** * NAME * flt_ot_scope_context_init - * * ARGUMENTS * rt_ctx - * tracer - * id - * id_len - * text_map - * dir - * err - * * DESCRIPTION * - * * RETURN VALUE * - */ struct flt_ot_scope_context *flt_ot_scope_context_init(struct flt_ot_runtime_context *rt_ctx, struct otc_tracer *tracer, const char *id, size_t id_len, const struct otc_text_map *text_map, uint dir, char **err) { struct otc_http_headers_reader reader; struct otc_span_context *span_ctx; struct flt_ot_scope_context *retptr = NULL; FLT_OT_FUNC("%p, %p, \"%s\", %zu, %p, %u, %p:%p", rt_ctx, tracer, id, id_len, text_map, dir, FLT_OT_DPTR_ARGS(err)); if ((rt_ctx == NULL) || (tracer == NULL) || (id == NULL) || (text_map == NULL)) FLT_OT_RETURN_PTR(retptr); list_for_each_entry(retptr, &(rt_ctx->contexts), list) if ((retptr->id_len == id_len) && (memcmp(retptr->id, id, id_len) == 0)) { FLT_OT_DBG(2, "found context %p", retptr); FLT_OT_RETURN_PTR(retptr); } retptr = flt_ot_pool_alloc(pool_head_ot_scope_context, sizeof(*retptr), 1, err); if (retptr == NULL) FLT_OT_RETURN_PTR(retptr); span_ctx = ot_extract_http_headers(tracer, &reader, text_map, err); if (span_ctx == NULL) { flt_ot_scope_context_free(&retptr); FLT_OT_RETURN_PTR(retptr); } retptr->id = id; retptr->id_len = id_len; retptr->smp_opt_dir = dir; retptr->context = span_ctx; LIST_INSERT(&(rt_ctx->contexts), &(retptr->list)); FLT_OT_DBG_SCOPE_CONTEXT("new context ", retptr); FLT_OT_RETURN_PTR(retptr); } /*** * NAME * flt_ot_scope_context_free - * * ARGUMENTS * ptr - * * DESCRIPTION * - * * RETURN VALUE * This function does not return a value. */ void flt_ot_scope_context_free(struct flt_ot_scope_context **ptr) { FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr)); if ((ptr == NULL) || (*ptr == NULL)) FLT_OT_RETURN(); FLT_OT_DBG_SCOPE_CONTEXT("", *ptr); if ((*ptr)->context != NULL) (*ptr)->context->destroy(&((*ptr)->context)); FLT_OT_LIST_DEL(&((*ptr)->list)); flt_ot_pool_free(pool_head_ot_scope_context, (void **)ptr); FLT_OT_RETURN(); } /*** * NAME * flt_ot_scope_data_free - * * ARGUMENTS * ptr - * * DESCRIPTION * - * * RETURN VALUE * This function does not return a value. */ void flt_ot_scope_data_free(struct flt_ot_scope_data *ptr) { int i; FLT_OT_FUNC("%p", ptr); if (ptr == NULL) FLT_OT_RETURN(); FLT_OT_DBG_SCOPE_DATA("", ptr); for (i = 0; i < ptr->num_tags; i++) if (ptr->tags[i].value.type == otc_value_string) FLT_OT_FREE_VOID(ptr->tags[i].value.value.string_value); otc_text_map_destroy(&(ptr->baggage), OTC_TEXT_MAP_FREE_VALUE); for (i = 0; i < ptr->num_log_fields; i++) if (ptr->log_fields[i].value.type == otc_value_string) FLT_OT_FREE_VOID(ptr->log_fields[i].value.value.string_value); (void)memset(ptr, 0, sizeof(*ptr)); FLT_OT_RETURN(); } /*** * NAME * flt_ot_scope_finish_mark - * * ARGUMENTS * rt_ctx - * id - * id_len - * * DESCRIPTION * - * * RETURN VALUE * - */ int flt_ot_scope_finish_mark(const struct flt_ot_runtime_context *rt_ctx, const char *id, size_t id_len) { struct flt_ot_scope_span *span; struct flt_ot_scope_context *ctx; int span_cnt = 0, ctx_cnt = 0, retval; FLT_OT_FUNC("%p, \"%s\", %zu", rt_ctx, id, id_len); if (FLT_OT_STR_CMP(FLT_OT_SCOPE_SPAN_FINISH_ALL, id, id_len)) { list_for_each_entry(span, &(rt_ctx->spans), list) { span->flag_finish = 1; span_cnt++; } list_for_each_entry(ctx, &(rt_ctx->contexts), list) { ctx->flag_finish = 1; ctx_cnt++; } FLT_OT_DBG(2, "marked %d span(s), %d context(s)", span_cnt, ctx_cnt); } else if (FLT_OT_STR_CMP(FLT_OT_SCOPE_SPAN_FINISH_REQ, id, id_len)) { list_for_each_entry(span, &(rt_ctx->spans), list) if (span->smp_opt_dir == SMP_OPT_DIR_REQ) { span->flag_finish = 1; span_cnt++; } list_for_each_entry(ctx, &(rt_ctx->contexts), list) if (ctx->smp_opt_dir == SMP_OPT_DIR_REQ) { ctx->flag_finish = 1; span_cnt++; } FLT_OT_DBG(2, "marked REQuest channel %d span(s), %d context(s)", span_cnt, ctx_cnt); } else if (FLT_OT_STR_CMP(FLT_OT_SCOPE_SPAN_FINISH_RES, id, id_len)) { list_for_each_entry(span, &(rt_ctx->spans), list) if (span->smp_opt_dir == SMP_OPT_DIR_RES) { span->flag_finish = 1; span_cnt++; } list_for_each_entry(ctx, &(rt_ctx->contexts), list) if (ctx->smp_opt_dir == SMP_OPT_DIR_RES) { ctx->flag_finish = 1; ctx_cnt++; } FLT_OT_DBG(2, "marked RESponse channel %d span(s), %d context(s)", span_cnt, ctx_cnt); } else { list_for_each_entry(span, &(rt_ctx->spans), list) if ((span->id_len == id_len) && (memcmp(span->id, id, id_len) == 0)) { span->flag_finish = 1; span_cnt++; break; } list_for_each_entry(ctx, &(rt_ctx->contexts), list) if ((ctx->id_len == id_len) && (memcmp(ctx->id, id, id_len) == 0)) { ctx->flag_finish = 1; ctx_cnt++; break; } if (span_cnt > 0) FLT_OT_DBG(2, "marked span '%s'", id); if (ctx_cnt > 0) FLT_OT_DBG(2, "marked context '%s'", id); if ((span_cnt + ctx_cnt) == 0) FLT_OT_DBG(2, "cannot find span/context '%s'", id); } retval = span_cnt + ctx_cnt; FLT_OT_RETURN_INT(retval); } /*** * NAME * flt_ot_scope_finish_marked - * * ARGUMENTS * rt_ctx - * ts_finish - * * DESCRIPTION * Finish marked spans. * * RETURN VALUE * This function does not return a value. */ void flt_ot_scope_finish_marked(const struct flt_ot_runtime_context *rt_ctx, const struct timespec *ts_finish) { struct flt_ot_scope_span *span; struct flt_ot_scope_context *ctx; FLT_OT_FUNC("%p, %p", rt_ctx, ts_finish); list_for_each_entry(span, &(rt_ctx->spans), list) if (span->flag_finish) { FLT_OT_DBG_SCOPE_SPAN("finishing span ", span); ot_span_finish(&(span->span), ts_finish, NULL, NULL, NULL); span->flag_finish = 0; } list_for_each_entry(ctx, &(rt_ctx->contexts), list) if (ctx->flag_finish) { FLT_OT_DBG_SCOPE_CONTEXT("finishing context ", ctx); if (ctx->context != NULL) ctx->context->destroy(&(ctx->context)); ctx->flag_finish = 0; } FLT_OT_RETURN(); } /*** * NAME * flt_ot_scope_free_unused - * * ARGUMENTS * rt_ctx - * chn - * * DESCRIPTION * - * * RETURN VALUE * This function does not return a value. */ void flt_ot_scope_free_unused(struct flt_ot_runtime_context *rt_ctx, struct channel *chn) { FLT_OT_FUNC("%p", rt_ctx); if (rt_ctx == NULL) FLT_OT_RETURN(); if (!LIST_ISEMPTY(&(rt_ctx->spans))) { struct flt_ot_scope_span *span, *span_back; list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list) if (span->span == NULL) flt_ot_scope_span_free(&span); } if (!LIST_ISEMPTY(&(rt_ctx->contexts))) { struct flt_ot_scope_context *ctx, *ctx_back; list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list) if (ctx->context == NULL) { /* * All headers and variables associated with * the context in question should be deleted. */ (void)flt_ot_http_headers_remove(chn, ctx->id, NULL); #ifdef USE_OT_VARS (void)flt_ot_vars_unset(rt_ctx->stream, FLT_OT_VARS_SCOPE, ctx->id, ctx->smp_opt_dir, NULL); #endif flt_ot_scope_context_free(&ctx); } } FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx); FLT_OT_RETURN(); } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * End: * * vi: noexpandtab shiftwidth=8 tabstop=8 */