#include #include #include #include #include #include "probes.h" /* * probes.yaml parser. Structure: * * defaults: * VAR: value * ... * profiles: * name: * VAR: value * other: {} * * Parsed once and cached. See probes.h for semantics. */ #define DEFAULT_PROBES_FILE "probes.yaml" typedef struct { char *key; char *val; } kv; typedef struct { char *name; kv *vars; int nvars; } profile; static kv *g_def = NULL; static int g_ndef = 0; static profile *g_prof = NULL; static int g_nprof = 0; static int g_loaded = 0; static char g_source[1024] = ""; static char *xstrdup(const char *s) { char *p; size_t n; if (!s) return NULL; n = strlen(s) + 1; p = (char *)malloc(n); if (p) memcpy(p, s, n); return p; } static void add_kv(kv **arr, int *n, const char *k, const char *v) { kv *t = (kv *)realloc(*arr, (*n + 1) * sizeof(kv)); if (!t) { fprintf(stderr, "probes: out of memory\n"); return; } *arr = t; t[*n].key = xstrdup(k); t[*n].val = xstrdup(v); (*n)++; } static profile *add_profile(const char *name) { profile *t = (profile *)realloc(g_prof, (g_nprof + 1) * sizeof(profile)); if (!t) { fprintf(stderr, "probes: out of memory\n"); return NULL; } g_prof = t; g_prof[g_nprof].name = xstrdup(name); g_prof[g_nprof].vars = NULL; g_prof[g_nprof].nvars = 0; return &g_prof[g_nprof++]; } /* Mapping context at each nesting depth. */ enum { C_NONE = 0, C_ROOT, C_DEFAULTS, C_PROFILES, C_PROFBODY }; #define MAX_DEPTH 16 static int load(const char *path) { FILE *f; yaml_parser_t parser; yaml_event_t ev; int depth = 0, done = 0, rc = 0; int ctx[MAX_DEPTH]; char *key[MAX_DEPTH]; profile *cur = NULL; int i; f = fopen(path, "rb"); if (!f) return -1; if (!yaml_parser_initialize(&parser)) { fclose(f); return -1; } yaml_parser_set_input_file(&parser, f); memset(ctx, 0, sizeof(ctx)); memset(key, 0, sizeof(key)); while (!done) { if (!yaml_parser_parse(&parser, &ev)) { fprintf(stderr, "probes: YAML parse error in %s: %s (line %lu)\n", path, parser.problem ? parser.problem : "?", (unsigned long)parser.problem_mark.line + 1); rc = -1; break; } switch (ev.type) { case YAML_MAPPING_START_EVENT: { int newc = C_NONE; if (depth == 0) { newc = C_ROOT; } else { int c = ctx[depth]; const char *k = key[depth]; if (c == C_ROOT) { if (k && !strcmp(k, "defaults")) newc = C_DEFAULTS; else if (k && !strcmp(k, "profiles")) newc = C_PROFILES; } else if (c == C_PROFILES) { cur = k ? add_profile(k) : NULL; newc = C_PROFBODY; } } if (depth < MAX_DEPTH - 1) depth++; ctx[depth] = newc; free(key[depth]); key[depth] = NULL; break; } case YAML_MAPPING_END_EVENT: free(key[depth]); key[depth] = NULL; if (depth > 0) depth--; break; case YAML_SCALAR_EVENT: { const char *v = (const char *)ev.data.scalar.value; int c = ctx[depth]; if (c == C_DEFAULTS) { if (!key[depth]) { key[depth] = xstrdup(v); } else { add_kv(&g_def, &g_ndef, key[depth], v); free(key[depth]); key[depth] = NULL; } } else if (c == C_PROFBODY) { if (!key[depth]) { key[depth] = xstrdup(v); } else { if (cur) add_kv(&cur->vars, &cur->nvars, key[depth], v); free(key[depth]); key[depth] = NULL; } } else { /* C_ROOT / C_PROFILES: the scalar is a key/name whose * value is the next mapping. */ free(key[depth]); key[depth] = xstrdup(v); } break; } case YAML_STREAM_END_EVENT: done = 1; break; default: break; } yaml_event_delete(&ev); } for (i = 0; i < MAX_DEPTH; i++) free(key[i]); yaml_parser_delete(&parser); fclose(f); return rc; } static void ensure_loaded(void) { const char *path; if (g_loaded) return; g_loaded = 1; path = getenv("BS_PROBES"); if (!path || !*path) path = DEFAULT_PROBES_FILE; if (load(path) == 0 && (g_ndef > 0 || g_nprof > 0)) { strncpy(g_source, path, sizeof(g_source) - 1); g_source[sizeof(g_source) - 1] = '\0'; } /* Silent when absent: probe profiles are optional. */ } int probe_apply_defaults(probe_set_fn set, void *user) { int i; ensure_loaded(); for (i = 0; i < g_ndef; i++) set(user, g_def[i].key, g_def[i].val); return g_ndef; } int probe_apply_profile(const char *name, probe_set_fn set, void *user) { int i, j; ensure_loaded(); for (i = 0; i < g_nprof; i++) { if (!strcmp(g_prof[i].name, name)) { for (j = 0; j < g_prof[i].nvars; j++) set(user, g_prof[i].vars[j].key, g_prof[i].vars[j].val); return g_prof[i].nvars; } } return -1; } int probe_profile_count(void) { ensure_loaded(); return g_nprof; } const char *probe_profile_name(int index) { ensure_loaded(); return (index >= 0 && index < g_nprof) ? g_prof[index].name : NULL; } const char *probe_config_source(void) { ensure_loaded(); return g_source[0] ? g_source : NULL; }