]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
print out more stats, and start defining statistics structs and APIs
authorAlan T. DeKok <aland@freeradius.org>
Mon, 12 Jan 2026 16:39:33 +0000 (11:39 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 12 Jan 2026 20:31:25 +0000 (15:31 -0500)
the plan is to:

* define the stats in the dictionaries
* have radict load up a statistics structure, and
  * print out variable definitions for dict autoload
  * print out dict autoload entries
  * print out a C structure which holds the statistics
  * print out a "linking" structure which ties the C structure
    to the autoloaded DAs
  * glue all of this into the build with GNU Make magic.

Right now, we can / have:
* define an "instance" of a statistics thing, which can be linked
  to other instances of the same struct type
* allow instance data to be merged, so that we can collate all of
  the similar statistics.
* functions to convert the C struct to pairs, and back
* functions to iterate over statistics

src/bin/radict.c
src/lib/util/stats.c
src/lib/util/stats.h

index bd9f914eabcbea6739017a8bc7dcc5b7cf91e60e..9029918f0795d286e6c5bdf473db0529cce6ea8d 100644 (file)
@@ -339,31 +339,65 @@ static void da_normalize_name(fr_dict_attr_t const *da, char buffer[static FR_DI
        *q = '\0';
 }
 
-static void da_print_name(fr_dict_attr_t const *da)
+static void da_print_name(FILE *fp, fr_dict_attr_t const *da)
 {
        char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
 
        da_normalize_name(da, buffer);
-       printf("%s", buffer);
+       fprintf(fp, "%s", buffer);
 }
 
+static const bool type_allowed[FR_TYPE_MAX] = {
+       [FR_TYPE_STRING] = true,
+       [FR_TYPE_OCTETS] = true,
+
+       [FR_TYPE_UINT16] = true,
+       [FR_TYPE_UINT32] = true,
+       [FR_TYPE_UINT64] = true,
+
+       [FR_TYPE_IPV4_ADDR] = true,
+       [FR_TYPE_IPV6_ADDR] = true,
+
+       [FR_TYPE_DATE] = true,
+       [FR_TYPE_TIME_DELTA] = true,
+
+};
+
+static bool children_ok(fr_dict_attr_t const *parent)
+{
+       int i;
+       fr_dict_attr_t const *da;
+
+       for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
+               if (!type_allowed[da->type]) return false;
+       }
+
+       return true;
+}
+
+#define CHECK_TYPE(_parent) \
+do { \
+       if ((parent->type != FR_TYPE_STRUCT) && (parent->type != FR_TYPE_TLV)) { \
+               fprintf(stderr, "%s is not a struct or tlv\n", parent->name); \
+               return; \
+       } \
+       if (!children_ok(parent)) fr_exit(EXIT_FAILURE); \
+} while (0)
+
 /** Print structures and mappings, mainly for statistics.
  */
-static void da_print_struct(fr_dict_attr_t const *parent)
+static void da_print_struct(FILE *fp, fr_dict_attr_t const *parent)
 {
        int i;
        fr_dict_attr_t const *da;
 
-       if (parent->type != FR_TYPE_STRUCT) {
-               fprintf(stderr, "%s is not a struct\n", parent->name);
-               return;
-       }
+       CHECK_TYPE(parent);
 
        /*
         *      @todo - print full OID path and filename?
         */
-       printf("/*\n *\t%s\n */\n", parent->name);
-       printf("typedef struct {\n");
+       fprintf(fp, "/*\n *\t%s\n */\n", parent->name);
+       fprintf(fp, "typedef struct {\n");
 
        for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
                unsigned int length = 0;
@@ -380,96 +414,71 @@ static void da_print_struct(fr_dict_attr_t const *parent)
                         *      this is via a separate function that we call.  But this shouldn't be necessary
                         *      for statistics structures, as they shouldn't contain bitfields.
                         */
-                       printf("\tunsigned int : %u\t", da->flags.length);
+                       fprintf(fp, "\tunsigned int : %u\t", da->flags.length);
 
                } else switch (da->type) {
                        case FR_TYPE_STRING:
+                               if ((parent->type == FR_TYPE_TLV) && !da->flags.length) {
+                                       fprintf(fp, "\t%s\t*", type_to_c_type[da->type]);
+                                       break;
+                               }
+                               FALL_THROUGH;
+
                        case FR_TYPE_OCTETS:
                                fr_assert(da->flags.length > 0);
                                length = da->flags.length;
-                               printf("\t%s\t", type_to_c_type[da->type]);
+                               fprintf(fp, "\t%s\t", type_to_c_type[da->type]);
                                break;
 
                        case FR_TYPE_DATE:
                                fr_assert(da->flags.length <= 8);
                                fr_assert(length_to_c_type[da->flags.length] != NULL);
-                               printf("\t%s\t", length_to_c_type[da->flags.length]);
+                               fprintf(fp, "\t%s\t", length_to_c_type[da->flags.length]);
                                break;
 
                        default:
                                fr_assert(type_to_c_type[da->type] != NULL);
-                               printf("\t%s\t", type_to_c_type[da->type]);
+                               fprintf(fp, "\t%s\t", type_to_c_type[da->type]);
                                break;
                }
 
-               da_print_name(da);
+               da_print_name(fp, da);
 
                if (length) {
-                       printf("[%u]", length);
+                       fprintf(fp, "[%u]", length);
                }
 
-               printf(";\n");
-       }
-
-       printf("} ");
-
-       printf("fr_");
-       da_print_name(fr_dict_root(parent->dict));
-       printf("_");
-       da_print_name(parent);
-       printf("_t;\n");
-}
-
-static void da_print_stats_link(fr_dict_attr_t const *parent)
-{
-       int i;
-       fr_dict_attr_t const *da;
-       char dict_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
-       char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
-
-       if (parent->type != FR_TYPE_STRUCT) {
-               fprintf(stderr, "%s is not a struct\n", parent->name);
-               return;
+               fprintf(fp, ";\n");
        }
 
-       da_normalize_name(fr_dict_root(parent->dict), dict_name);
-       da_normalize_name(parent, parent_name);
-
-       printf("fr_stats_link_t const fr_%s_%s_stats_link_t[] = {\n", dict_name, parent_name);
-
-       for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
-               printf("\t{ &attr_%s_", parent_name);
-               da_print_name(da);
-               printf(", offsetof(fr_%s_%s_t, ", dict_name, parent_name);
-               da_print_name(da);
-               printf(") },\n");
-       }
+       fprintf(fp, "} ");
 
-       printf("};\n\n");
+       fprintf(fp, "fr_");
+       da_print_name(fp, fr_dict_root(parent->dict));
+       fprintf(fp, "_");
+       da_print_name(fp, parent);
+       fprintf(fp, "_t;\n");
 }
 
-static void da_print_attr_def(fr_dict_attr_t const *parent)
+static void da_print_attr_def(FILE *fp, fr_dict_attr_t const *parent)
 {
        int i;
        fr_dict_attr_t const *da;
        char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
 
-       if (parent->type != FR_TYPE_STRUCT) {
-               fprintf(stderr, "%s is not a struct\n", parent->name);
-               return;
-       }
+       CHECK_TYPE(parent);
 
        da_normalize_name(parent, parent_name);
 
-       printf("static fr_dict_attr_t const *attr_%s;\n", parent_name);
+       fprintf(fp, "static fr_dict_attr_t const *attr_%s;\n", parent_name);
 
        for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
-               printf("static fr_dict_attr_t const *attr_%s_", parent_name);
-               da_print_name(da);
-               printf(";\n");
+               fprintf(fp, "static fr_dict_attr_t const *attr_%s_", parent_name);
+               da_print_name(fp, da);
+               fprintf(fp, ";\n");
        }
 
-       printf("\n\n");
+       fprintf(fp, "\n\n");
 }
 
 /** Map data types to enum names representing those types
@@ -521,18 +530,65 @@ static char const *fr_type_to_enum_name[] = {
        ENUM_NAME(FR_TYPE_ATTR),
 };
 
-static void da_print_attr_autoload(fr_dict_attr_t const *parent)
+static void da_print_stats_link(FILE *fp, fr_dict_attr_t const *parent)
 {
-       int i;
+       int i, num_elements = 0;
        fr_dict_attr_t const *da;
        char dict_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
        char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
 
-       if (parent->type != FR_TYPE_STRUCT) {
-               fprintf(stderr, "%s is not a struct\n", parent->name);
-               return;
+       CHECK_TYPE(parent);
+
+       da_normalize_name(fr_dict_root(parent->dict), dict_name);
+       da_normalize_name(parent, parent_name);
+
+       fprintf(fp, "fr_stats_link_t const fr_%s_%s_stats_link_t[] = {\n", dict_name, parent_name);
+
+       fprintf(fp, "\t.name = \"fr_%s_%s_t\",\n", dict_name, parent_name);
+       fprintf(fp, "\t.da_p = &attr_%s,\n", parent_name);
+       fprintf(fp, "\t.size = sizeof(fr_%s_%s_t),\n", dict_name, parent_name);
+
+       for (i = 1; fr_dict_attr_child_by_num(parent, i) != NULL; i++) {
+               num_elements = i;
+       }
+       fprintf(fp, "\t.num_elements = %d,\n", num_elements);
+
+       fprintf(fp, "\t.entry = {\n");
+
+       /*
+        *      For locality, also print out data type and size.  That way we _can_ dereference the da, but we
+        *      don't _need_ to.
+        */
+       for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
+               fprintf(fp, "\t\t{\n");
+               fprintf(fp, "\t\t\t.da_p = &attr_%s_", parent_name);
+               da_print_name(fp, da);
+               fprintf(fp, ",\n");
+
+               fprintf(fp, "\t\t\t.type = %s,\n", fr_type_to_enum_name[da->type]);
+
+               fprintf(fp, "\t\t\t.offset = offsetof(fr_%s_%s_t, ", dict_name, parent_name);
+               da_print_name(fp, da);
+               fprintf(fp, "),\n");
+
+               fprintf(fp, "\t\t\t.size = %u,\n", da->flags.length);
+
+               fprintf(fp, "\t\t},\n");
        }
 
+       fprintf(fp, "\t};\n");
+       fprintf(fp, "};\n\n");
+}
+
+static void da_print_attr_autoload(FILE *fp, fr_dict_attr_t const *parent)
+{
+       int i;
+       fr_dict_attr_t const *da;
+       char dict_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+       char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+
+       CHECK_TYPE(parent);
+
        da_normalize_name(fr_dict_root(parent->dict), dict_name);
        da_normalize_name(parent, parent_name);
 
@@ -541,17 +597,17 @@ static void da_print_attr_autoload(fr_dict_attr_t const *parent)
         *
         *      @todo - print the enum for the data type
         */
-       printf("{ .out = &attr_%s, .name = \"%s\", .type = %s, .dict = &dict_%s },\n",
+       fprintf(fp, "{ .out = &attr_%s, .name = \"%s\", .type = %s, .dict = &dict_%s },\n",
               parent_name, parent->name, fr_type_to_enum_name[parent->type], dict_name);
 
        for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
-               printf("{ .out = &attr_%s_", parent_name);
-               da_print_name(da);
-               printf(", .name = \".%s\", .type = %s, .dict = &dict_%s },\n",
+               fprintf(fp, "{ .out = &attr_%s_", parent_name);
+               da_print_name(fp, da);
+               fprintf(fp, ", .name = \".%s\", .type = %s, .dict = &dict_%s },\n",
                       da->name, fr_type_to_enum_name[da->type], dict_name);
        }
 
-       printf("\n\n");
+       fprintf(fp, "\n\n");
 }
 
 static void _raddict_export(fr_dict_t const *dict, uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_attr_t const *da, unsigned int lvl)
@@ -834,19 +890,19 @@ int main(int argc, char *argv[])
                                break;
 
                        case RADICT_OUT_STRUCT:
-                               da_print_struct(da);
+                               da_print_struct(stdout, da);
                                break;
 
                        case RADICT_OUT_STATS_LINK:
-                               da_print_stats_link(da);
+                               da_print_stats_link(stdout, da);
                                break;
 
                        case RADICT_OUT_ATTR_DEF:
-                               da_print_attr_def(da);
+                               da_print_attr_def(stdout, da);
                                break;
 
                        case RADICT_OUT_ATTR_AUTOLOAD:
-                               da_print_attr_autoload(da);
+                               da_print_attr_autoload(stdout, da);
                                break;
                        }
                        found = true;
index ac83ec7d7fb561ab3fde64fefc12698385c2801f..aac8850cbaa507e6e9c2b26854c5215b0756755d 100644 (file)
@@ -24,68 +24,309 @@ RCSID("$Id$")
 
 #include <freeradius-devel/util/stats.h>
 
-/** Define dictionary attributes for a given statistics structure.
+// convert a set of pairs to a struct
+
+/** Convert a statistics structure to #fr_pair_t
  *
- *  @param     parent the parent attribute under which statistics are defined
- *  @param     stats the statistics mapping structure.
- *  @return
- *     - <0 on error
- *     - 0 on success
  */
-int fr_stats_attr_init(fr_dict_attr_t *parent, fr_stats_struct_t const *stats)
+int fr_stats_to_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_stats_instance_t const *inst)
 {
-       fr_stats_entry_t const *entry;
-       fr_dict_t *dict;
+       size_t i;
+       fr_stats_link_t const *def = inst->def;
+       uint8_t const *in = inst->stats;
+       fr_pair_t *parent, *vp;
+
+       parent = fr_pair_afrom_da(ctx, *(def->root_p));
+       if (!parent) return -1;
+
+       for (i = 0; i < def->num_elements; i++) {
+               uint8_t const *field;
+
+               vp = fr_pair_afrom_da(parent, *(def->entry[i].da_p));
+               if (!vp) goto fail;
+
+               field = ((uint8_t const *) in) + def->entry[i].offset;
+
+               /*
+                *      Strings of length 0 are "const char *".
+                *
+                *      Everything else is an in-line field.
+                */
+               switch (def->entry[i].type) {
+               case FR_TYPE_STRING:
+                       if (!def->entry[i].size) {
+                               char const *str;
+
+                               memcpy(&str, field, sizeof(str));
+
+                               if (fr_pair_value_strdup(vp, str, false) < 0) goto fail;
+                               break;
+                       }
+                       FALL_THROUGH;
+
+               case FR_TYPE_OCTETS:
+               case FR_TYPE_UINT16:
+               case FR_TYPE_UINT32:
+               case FR_TYPE_UINT64:
+               case FR_TYPE_IPV4_ADDR:
+               case FR_TYPE_IPV6_ADDR:
+                       fr_assert(def->entry[i].size > 0);
+
+                       if (fr_value_box_from_network(vp, &vp->data, def->entry[i].type, vp->da,
+                                                     &FR_DBUFF_TMP(field, def->entry[i].size), def->entry[i].size, false) < 0) {
+                               goto fail;
+                       }
+                       break;
 
-       dict = fr_dict_unconst(fr_dict_by_da(parent));
+               case FR_TYPE_DATE:
+                       memcpy(&vp->vp_date, field, sizeof(vp->vp_date));
+                       break;
 
-       for (entry = &stats->table[0]; entry->name != NULL; entry++) {
-               fr_dict_attr_flags_t flags = {
-                       .internal = true,
-                       .counter = entry->flags.counter,
-               };
+               case FR_TYPE_TIME_DELTA:
+                       memcpy(&vp->vp_time_delta, field, sizeof(vp->vp_time_delta));
+                       break;
 
-               fr_assert(entry->number > 0);
-               fr_assert((entry->type == FR_TYPE_TIME_DELTA) || (fr_type_is_integer(entry->type)));
+               default:
+                       fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(def->entry[i].type));
+                       return -1;
+               }
 
-               if (fr_dict_attr_add(dict, parent, entry->name, entry->number, entry->type, &flags) < 0) return -1;
+               if (fr_pair_append(&parent->vp_group, vp) < 0) goto fail;
+       }
+
+       if (fr_pair_append(out, parent) < 0) {
+       fail:
+               talloc_free(parent);
+               return -1;
        }
 
        return 0;
 }
 
-/** Add statistics VPs for a particular struct / context
+int fr_stats_from_pairs(TALLOC_CTX *ctx, fr_stats_instance_t *inst, fr_pair_list_t *in)
+{
+       size_t i;
+       fr_stats_link_t const *def = inst->def;
+       uint8_t *out = inst->stats;
+       fr_pair_t const *parent, *vp;
+
+       parent = fr_pair_find_by_da(in, NULL, *(def->root_p));
+       if (!parent) return -1;
+
+       memset(out, 0, inst->def->size);
+
+       for (i = 0; i < def->num_elements; i++) {
+               uint8_t *field;
+
+               vp = fr_pair_find_by_da(&parent->vp_group, NULL, *(def->entry[i].da_p));
+               if (!vp) continue;
+
+               field = ((uint8_t *) out) + def->entry[i].offset;
+
+               /*
+                *      Strings of length 0 are "const char *".
+                *
+                *      Everything else is an in-line field.
+                */
+               switch (def->entry[i].type) {
+               case FR_TYPE_STRING:
+                       if (!def->entry[i].size) {
+                               char const *str;
+
+                               str = talloc_bstrndup(ctx, vp->vp_strvalue, vp->vp_length);
+                               if (!str) return -1;
+
+                               memcpy(&field, &str, sizeof(str));
+                               break;
+                       }
+                       FALL_THROUGH;
+
+               case FR_TYPE_OCTETS:
+                       if (vp->vp_length <= def->entry[i].size) {
+                               memcpy(field, vp->vp_ptr, vp->vp_length);
+                       } else {
+                               memcpy(field, vp->vp_ptr, def->entry[i].size);
+                       }
+                       break;
+
+#undef COPY
+#define COPY(_type, _field) \
+       case _type: \
+               memcpy(field, &vp->vp_ ## _field, sizeof(vp->vp_ ##_field)); \
+               break
+
+                       COPY(FR_TYPE_UINT16, uint16);
+                       COPY(FR_TYPE_UINT32, uint32);
+                       COPY(FR_TYPE_UINT64, uint64);
+
+                       COPY(FR_TYPE_IPV4_ADDR, ipv4addr); /* struct in_addr, and not vp_ip */
+                       COPY(FR_TYPE_IPV6_ADDR, ipv6addr); /* struct in6_addr, and not vp_ip */
+
+                       COPY(FR_TYPE_DATE, date);
+                       COPY(FR_TYPE_TIME_DELTA, time_delta);
+
+                       default:
+                               fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(def->entry[i].type));
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+#undef add
+#define add(_type, _out, _in) \
+do { \
+       _type _a, _b, _sum; \
+       memcpy(&_a, &_out, sizeof(_a)); \
+       memcpy(&_b, &_in, sizeof(_b)); \
+       _sum = _a + _b; \
+       memcpy(&_out, &_sum, sizeof(_sum)); \
+} while (0)
+
+/** Merge to statistics structures
+ *
+ *  @todo - ensure that the struct magic is the same as the def magic
  *
- *  @param parent      structural vp where the children will be added
- *  @param stats       structure which maps between #fr_dict_attr_t and internal stats structures
- *  @param ctx         the internal structure holding the stastics
- *  @return
- *     - <0 on error
- *     - 0 on success
  */
-int fr_stats_pair_add(fr_pair_t *parent, fr_stats_struct_t const *stats, void const *ctx)
+static int stats_merge_internal(fr_stats_link_t const *def, void *out, void const *in)
 {
-       fr_stats_entry_t const *entry;
+       size_t i;
 
-       fr_assert(fr_type_is_structural(parent->vp_type));
+       for (i = 0; i < def->num_elements; i++) {
+               uint8_t const *field_in;
+               uint8_t const *field_out;
 
-       for (entry = &stats->table[0]; entry->name != NULL; entry++) {
-               fr_pair_t *vp;
-               fr_dict_attr_t const *da;
+               field_in = ((uint8_t const *) in) + def->entry[i].offset;
+               field_out = ((uint8_t const *) out) + def->entry[i].offset;
 
-               da = fr_dict_attr_child_by_num(parent->da, entry->number);
-               if (!da) {
-                       fr_strerror_printf("Unknown child %d for parent %s", entry->number, parent->da->name);
-                       return -1;
+               switch (def->entry[i].type) {
+               case FR_TYPE_UINT16:
+                       add(uint16_t, field_out, field_in);
+                       break;
+
+               case FR_TYPE_UINT32:
+                       add(uint32_t, field_out, field_in);
+                       break;
+
+               case FR_TYPE_UINT64:
+                       add(uint64_t, field_out, field_in);
+                       break;
+
+               default:
+                       break;
                }
+       }
+
+       return 0;
+}
+
+/** Public API for merging two statistics structures
+ *
+ */
+int fr_stats_merge(fr_stats_instance_t *out, fr_stats_instance_t const *in)
+{
+       if (out->def != in->def) {
+               fr_strerror_printf("Cannot merge stats into structure %s from different structure %s",
+                                  out->def->name, in->def->name);
+               return -1;
+       }
 
-               vp = fr_pair_afrom_da(parent, da);
-               if (!vp) return -1;
+       return stats_merge_internal(out->def, out->stats, in->stats);
+}
 
-               if (fr_value_box_memcpy_in(&vp->data, ((uint8_t const *) ctx) + entry->offset) < 0) return -1;
 
-               fr_pair_append(&parent->vp_group, vp);
+void fr_stats_iter_init(fr_stats_instance_t const *in, fr_stats_iter_t *iter)
+{
+       iter->inst = in;
+       iter->current = 0;
+}
+
+bool fr_stats_iter_next(fr_stats_iter_t *iter)
+{
+       if (iter->current < iter->inst->def->num_elements) {
+               iter->current++;
+               return true;
        }
 
+       return false;
+}
+
+int fr_stats_iter_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_iter_t *iter)
+{
+       fr_value_box_t *box;
+       uint8_t const *field;
+       fr_dict_attr_t const *da;
+
+       fr_assert(iter->inst);
+       fr_assert(iter->current < iter->inst->def->num_elements);
+
+       da = *(iter->inst->def->entry[iter->current].da_p);
+       fr_assert(da != NULL);
+
+       box = fr_value_box_alloc(ctx, iter->inst->def->entry[iter->current].type, da);
+       if (!box) return -1;
+
+       field = ((uint8_t const *) iter->inst->stats) + iter->inst->def->entry[iter->current].offset;
+
+#undef COPY
+#define COPY(_type, _field) \
+       case _type: \
+               memcpy(&box->vb_ ## _field, field, sizeof(box->vb_ ## _field)); \
+               break
+
+       switch (box->type) {
+               case FR_TYPE_STRING:
+                       if (!iter->inst->def->entry[iter->current].size) {
+                               char const *str;
+
+                               memcpy(&str, field, sizeof(str));
+
+                               if (fr_value_box_strdup(box, box, da, str, false) < 0) {
+                               fail:
+                                       talloc_free(box);
+                                       return -1;
+                               }
+                       } else {
+                               uint8_t const *end;
+
+                               /*
+                                *      Find the trailing NUL within the fixed-size field.
+                                */
+                               end = memchr(field, '\0', iter->inst->def->entry[iter->current].size);
+
+                               if (fr_value_box_bstrndup(box, box, da, (char const *) field,
+                                                         (size_t) (end - field), false) < 0) {
+                                       goto fail;
+                               }
+                       }
+                       break;
+
+               case FR_TYPE_OCTETS:
+                       if (fr_value_box_memdup(box, box, da, field,
+                                               iter->inst->def->entry[iter->current].size, false) < 0) {
+                               goto fail;
+                       }
+                       break;
+
+               COPY(FR_TYPE_UINT16, uint16);
+               COPY(FR_TYPE_UINT32, uint32);
+               COPY(FR_TYPE_UINT64, uint64);
+
+               COPY(FR_TYPE_IPV4_ADDR, ipv4addr); /* struct in_addr, and not vp_ip */
+               COPY(FR_TYPE_IPV6_ADDR, ipv6addr); /* struct in6_addr, and not vp_ip */
+
+               COPY(FR_TYPE_DATE, date);
+               COPY(FR_TYPE_TIME_DELTA, time_delta);
+
+       default:
+               fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(box->type));
+               return -1;
+       }
+
+       *out = box;
        return 0;
 }
+
+
index fb888897784cc29ff32e519feb43d6a75377df5b..993081628223f2559981b5e9fb9e96663d7860e4 100644 (file)
@@ -31,35 +31,78 @@ RCSIDH(stats_h, "$Id$")
 extern "C" {
 #endif
 
-/** Define a statistics mapping between dictionary attribute and a field in an internal structure.
+/** Link a struct entry to an autoloaded #fr_dict_attr_t
  *
- *  Note that the data types used in the internal structure have to match the #fr_type_t.
  */
 typedef struct {
-       char const      *name;          //!< Attribute name
-       fr_type_t       type;           //!< data type for this statistics
-       int             number;         //!< attribute number, so that numbers are consistent
-       struct {
-               bool    counter;        //!< data type is a counter (can add them)
-               bool    gauge;          //!< data type is a gauge (take the maximum)
-       } flags;
+       fr_dict_attr_t const    **da_p;         //!< point to the autoload definition of the DA for this field
+       fr_type_t               type;           //!< cached for locality and simplicity
+       size_t                  offset;         //!< from the start of the struct
+       size_t                  size;           //!< size of this field
+} fr_stats_link_entry_t;
 
-       size_t          offset;         //!< from start of the structure
-} fr_stats_entry_t;
+typedef struct {
+       char const              *name;          //!< name of the stats structure
+       fr_dict_attr_t const    **root_p;       //!< point to the autoload definition of the DA for this structure
+       size_t                  size;           //!< overall size of the structure
+       size_t                  num_elements;   //!< number of elements in the table.  Note no "NULL" terminator
+       fr_stats_link_entry_t   entry[];        //!< the field entries, in offset order
+} fr_stats_link_t;
+
+/**  Common header for all statistics instances
+ *
+ *     def     pointer to the definition of the structure for this instance
+ *     path    the full path to this instance
+ *     uctx    for any necessary callbacks
+ *     entry   in the doubly linked list of statics which all use the same "def".
+ */
+#define STATS_HEADER_COMMON \
+       fr_stats_link_t const   *def; \
+       char const              *path; \
+       void                    *uctx; \
+       fr_dlist_t              entry
+       
+/** Generic statistics structure
+ *
+ */
+typedef struct {
+       STATS_HEADER_COMMON;                    //!< common header
 
-#define STATS_ENTRY_TERMINATOR { .attr = NULL }
+       uint8_t                 stats[];        //!< generic statistics data
+} fr_stats_instance_t;
 
-/** Define a statistics mapping between a public name and an entire internal structure
+/** Macro to define a typedef for a particular kind of statistics
  *
  */
+#define FR_STATS_TYPEDEF(_name, _type) \
+       typedef struct {                \
+               STATS_HEADER_COMMON;    \
+               _type   stats;          \
+       } fr_stats_## _name ## _instance_t
+
+/** Macro used when declaring a variable which contains the statistics
+ *
+ */
+#define FR_STATS_ENTRY(_name) fr_stats_## _name ## _instance_t
+
+/** Macro used to increment one field in the statistics structure
+ *
+ */
+#define FR_STATS_INC(_var, _field) _var->stats._field++
+
 typedef struct {
-       char const      *name;          //!< of this structure for public consumption
-       fr_stats_entry_t table[];       //!< of mappings
-} fr_stats_struct_t;
+       fr_stats_instance_t const *inst;
+       unsigned int              current;
+} fr_stats_iter_t;
+
+int    fr_stats_to_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_stats_instance_t const *in) CC_HINT(nonnull);
+int    fr_stats_from_pairs(TALLOC_CTX *ctx, fr_stats_instance_t *out, fr_pair_list_t *in) CC_HINT(nonnull);
+int    fr_stats_merge(fr_stats_instance_t *out, fr_stats_instance_t const *in) CC_HINT(nonnull);
 
-int    fr_stats_attr_init(fr_dict_attr_t *parent, fr_stats_struct_t const *stats) CC_HINT(nonnull);
+void   fr_stats_iter_init(fr_stats_instance_t const *in, fr_stats_iter_t *iter);
+bool   fr_stats_iter_next(fr_stats_iter_t *iter);
+int    fr_stats_iter_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_iter_t *iter);
 
-int    fr_stats_pair_add(fr_pair_t *parent, fr_stats_struct_t const *stats, void const *ctx) CC_HINT(nonnull);
 
 #ifdef __cplusplus
 }