From eeb43dd0f42f3f9cd93cf19dda0a303dbbca0909 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Wed, 20 Jul 2011 19:14:20 +0000 Subject: [PATCH] Add libprofile support for vtable-backed profiles ticket: 6929 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25024 dc483132-0cff-0310-8789-dd5450dbe970 --- src/util/profile/Makefile.in | 10 +- src/util/profile/prof_err.et | 2 + src/util/profile/prof_get.c | 272 ++++++++++++++++++++++------- src/util/profile/prof_init.c | 111 +++++++++++- src/util/profile/prof_int.h | 7 +- src/util/profile/prof_set.c | 26 +++ src/util/profile/prof_tree.c | 24 +-- src/util/profile/profile.hin | 150 ++++++++++++++++ src/util/profile/test_profile.c | 1 + src/util/profile/test_vtable.c | 301 ++++++++++++++++++++++++++++++++ 10 files changed, 819 insertions(+), 85 deletions(-) create mode 100644 src/util/profile/test_vtable.c diff --git a/src/util/profile/Makefile.in b/src/util/profile/Makefile.in index 326fd1967..e5c973fd3 100644 --- a/src/util/profile/Makefile.in +++ b/src/util/profile/Makefile.in @@ -38,7 +38,7 @@ SRCS = $(srcdir)/prof_tree.c \ $(srcdir)/prof_init.c EXTRADEPSRCS=$(srcdir)/test_parse.c $(srcdir)/test_profile.c \ - $(srcdir)/profile_tcl.c + $(srcdir)/test_vtable.c $(srcdir)/profile_tcl.c DEPLIBS = $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB) MLIBS = -lcom_err $(SUPPORT_LIB) $(LIBS) @@ -93,6 +93,9 @@ test_profile: test_profile.$(OBJEXT) argv_parse.$(OBJEXT) $(OBJS) $(DEPLIBS) $(CC_LINK) -o test_profile test_profile.$(OBJEXT) \ argv_parse.$(OBJEXT) $(OBJS) $(MLIBS) +test_vtable: test_vtable.$(OBJEXT) $(OBJS) $(DEPLIBS) + $(CC_LINK) -o test_vtable test_vtable.$(OBJEXT) $(OBJS) $(MLIBS) + .d: includes # NEED TO FIX!! @@ -129,12 +132,13 @@ profile_tcl: profile_tcl.o $(PROF_DEPLIB) $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB) clean-unix:: clean-libs clean-libobjs $(RM) $(PROGS) *.o *~ test_parse core prof_err.h \ - prof_err.c test_profile profile.h profile_tcl + prof_err.c test_profile test_vtable profile.h profile_tcl clean-windows:: $(RM) $(PROFILE_HDR) -check-unix:: test_parse test_profile +check-unix:: test_parse test_profile test_vtable + $(KRB5_RUN_ENV) $(VALGRIND) ./test_vtable DO_TCL=@DO_TCL@ check-unix:: check-unix-tcl-$(DO_TCL) diff --git a/src/util/profile/prof_err.et b/src/util/profile/prof_err.et index 2384127af..41aec89c6 100644 --- a/src/util/profile/prof_err.et +++ b/src/util/profile/prof_err.et @@ -68,5 +68,7 @@ error_code PROF_FAIL_INCLUDE_FILE, "Included profile file could not be read" error_code PROF_FAIL_INCLUDE_DIR, "Included profile directory could not be read" +error_code PROF_UNSUPPORTED, "Operation not supported on this profile" +error_code PROF_MAGIC_NODE_ITERATOR, "Bad magic value in profile iterator" end diff --git a/src/util/profile/prof_get.c b/src/util/profile/prof_get.c index 460d2e5f2..f3bc31463 100644 --- a/src/util/profile/prof_get.c +++ b/src/util/profile/prof_get.c @@ -128,6 +128,30 @@ void KRB5_CALLCONV profile_free_list(char **list) free(list); } +/* Look up a relation in a vtable profile. */ +static errcode_t +get_values_vt(profile_t profile, const char *const *names, char ***ret_values) +{ + errcode_t retval; + char **vtvalues, **val; + struct profile_string_list values; + + retval = profile->vt->get_values(profile->cbdata, names, &vtvalues); + if (retval) + return retval; + + /* Copy the result into memory we can free. */ + retval = init_list(&values); + if (retval == 0) { + for (val = vtvalues; *val; val++) + add_to_list(&values, *val); + end_list(&values, ret_values); + } + + profile->vt->free_values(profile->cbdata, vtvalues); + return retval; +} + errcode_t KRB5_CALLCONV profile_get_values(profile_t profile, const char *const *names, char ***ret_values) @@ -137,6 +161,9 @@ profile_get_values(profile_t profile, const char *const *names, char *value; struct profile_string_list values; + if (profile->vt) + return get_values_vt(profile, names, ret_values); + if ((retval = profile_node_iterator_create(profile, names, PROFILE_ITER_RELATIONS_ONLY, &state))) @@ -165,23 +192,46 @@ cleanup: return retval; } +/* Look up a relation in a vtable profile and return the first value in the + * result. */ +static errcode_t +get_value_vt(profile_t profile, const char *const *names, char **ret_value) +{ + errcode_t retval; + char **vtvalues; + + retval = profile->vt->get_values(profile->cbdata, names, &vtvalues); + if (retval) + return retval; + *ret_value = strdup(*vtvalues); + if (*ret_value == NULL) + retval = ENOMEM; + profile->vt->free_values(profile->cbdata, vtvalues); + return retval; +} + /* * This function only gets the first value from the file; it is a * helper function for profile_get_string, profile_get_integer, etc. */ errcode_t profile_get_value(profile_t profile, const char **names, - const char **ret_value) + char **ret_value) { errcode_t retval; void *state; char *value; - if ((retval = profile_node_iterator_create(profile, names, - PROFILE_ITER_RELATIONS_ONLY, - &state))) + *ret_value = NULL; + if (profile->vt) + return get_value_vt(profile, names, ret_value); + + retval = profile_iterator_create(profile, names, + PROFILE_ITER_RELATIONS_ONLY, &state); + if (retval) return retval; - if ((retval = profile_node_iterator(&state, 0, 0, &value))) + retval = profile_iterator(&state, NULL, &value); + if (retval) goto cleanup; if (value) @@ -190,7 +240,7 @@ errcode_t profile_get_value(profile_t profile, const char **names, retval = PROF_NO_RELATION; cleanup: - profile_node_iterator_free(&state); + profile_iterator_free(&state); return retval; } @@ -199,7 +249,7 @@ profile_get_string(profile_t profile, const char *name, const char *subname, const char *subsubname, const char *def_val, char **ret_string) { - const char *value; + char *value; errcode_t retval; const char *names[4]; @@ -209,19 +259,45 @@ profile_get_string(profile_t profile, const char *name, const char *subname, names[2] = subsubname; names[3] = 0; retval = profile_get_value(profile, names, &value); - if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) - value = def_val; - else if (retval) + if (retval == 0) { + *ret_string = value; + return 0; + } else if (retval != PROF_NO_SECTION && retval != PROF_NO_RELATION) return retval; - } else - value = def_val; + } - if (value) { - *ret_string = strdup(value); - if (*ret_string == 0) + if (def_val) { + *ret_string = strdup(def_val); + if (*ret_string == NULL) return ENOMEM; } else - *ret_string = 0; + *ret_string = NULL; + return 0; +} + +static errcode_t +parse_int(const char *value, int *ret_int) +{ + char *end_value; + long ret_long; + + if (value[0] == 0) + /* Empty string is no good. */ + return PROF_BAD_INTEGER; + errno = 0; + ret_long = strtol(value, &end_value, 10); + + /* Overflow or underflow. */ + if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) + return PROF_BAD_INTEGER; + /* Value outside "int" range. */ + if ((long) (int) ret_long != ret_long) + return PROF_BAD_INTEGER; + /* Garbage in string. */ + if (end_value != value + strlen (value)) + return PROF_BAD_INTEGER; + + *ret_int = ret_long; return 0; } @@ -229,11 +305,9 @@ errcode_t KRB5_CALLCONV profile_get_integer(profile_t profile, const char *name, const char *subname, const char *subsubname, int def_val, int *ret_int) { - const char *value; + char *value; errcode_t retval; const char *names[4]; - char *end_value; - long ret_long; *ret_int = def_val; if (profile == 0) @@ -250,25 +324,9 @@ profile_get_integer(profile_t profile, const char *name, const char *subname, } else if (retval) return retval; - if (value[0] == 0) - /* Empty string is no good. */ - return PROF_BAD_INTEGER; - errno = 0; - ret_long = strtol (value, &end_value, 10); - - /* Overflow or underflow. */ - if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) - return PROF_BAD_INTEGER; - /* Value outside "int" range. */ - if ((long) (int) ret_long != ret_long) - return PROF_BAD_INTEGER; - /* Garbage in string. */ - if (end_value != value + strlen (value)) - return PROF_BAD_INTEGER; - - - *ret_int = ret_long; - return 0; + retval = parse_int(value, ret_int); + free(value); + return retval; } static const char *const conf_yes[] = { @@ -310,7 +368,7 @@ errcode_t KRB5_CALLCONV profile_get_boolean(profile_t profile, const char *name, const char *subname, const char *subsubname, int def_val, int *ret_boolean) { - const char *value; + char *value; errcode_t retval; const char *names[4]; @@ -330,7 +388,9 @@ profile_get_boolean(profile_t profile, const char *name, const char *subname, } else if (retval) return retval; - return profile_parse_boolean (value, ret_boolean); + retval = profile_parse_boolean(value, ret_boolean); + free(value); + return retval; } /* @@ -346,19 +406,21 @@ profile_get_subsection_names(profile_t profile, const char **names, char *name; struct profile_string_list values; - if ((retval = profile_node_iterator_create(profile, names, - PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY, - &state))) + if ((retval = profile_iterator_create(profile, names, + PROFILE_ITER_LIST_SECTION | + PROFILE_ITER_SECTIONS_ONLY, + &state))) return retval; if ((retval = init_list(&values))) return retval; do { - if ((retval = profile_node_iterator(&state, 0, &name, 0))) + if ((retval = profile_iterator(&state, &name, NULL))) goto cleanup; if (name) add_to_list(&values, name); + free(name); } while (state); end_list(&values, ret_names); @@ -382,19 +444,21 @@ profile_get_relation_names(profile_t profile, const char **names, char *name; struct profile_string_list values; - if ((retval = profile_node_iterator_create(profile, names, - PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY, - &state))) + if ((retval = profile_iterator_create(profile, names, + PROFILE_ITER_LIST_SECTION | + PROFILE_ITER_RELATIONS_ONLY, + &state))) return retval; if ((retval = init_list(&values))) return retval; do { - if ((retval = profile_node_iterator(&state, 0, &name, 0))) + if ((retval = profile_iterator(&state, &name, NULL))) goto cleanup; if (name && !is_list_member(&values, name)) add_to_list(&values, name); + free(name); } while (state); end_list(&values, ret_names); @@ -405,36 +469,80 @@ cleanup: return retval; } +struct profile_iterator { + prf_magic_t magic; + profile_t profile; + void *idata; +}; + errcode_t KRB5_CALLCONV profile_iterator_create(profile_t profile, const char *const *names, int flags, void **ret_iter) { - return profile_node_iterator_create(profile, names, flags, ret_iter); + struct profile_iterator *iter; + errcode_t retval; + + *ret_iter = NULL; + iter = malloc(sizeof(*iter)); + if (iter == NULL) + return ENOMEM; + iter->magic = PROF_MAGIC_ITERATOR; + iter->profile = profile; + + /* Create the underlying iterator representation using the vtable or the + * built-in node iterator. */ + if (profile->vt) { + if (!profile->vt->iterator_create) + retval = PROF_UNSUPPORTED; + else + retval = profile->vt->iterator_create(profile->cbdata, names, + flags, &iter->idata); + } else { + retval = profile_node_iterator_create(profile, names, flags, + &iter->idata); + } + if (retval) { + free(iter); + return retval; + } + + *ret_iter = iter; + return 0; } void KRB5_CALLCONV profile_iterator_free(void **iter_p) { - profile_node_iterator_free(iter_p); + struct profile_iterator *iter; + profile_t profile; + + if (!iter_p) + return; + iter = *iter_p; + if (!iter || iter->magic != PROF_MAGIC_ITERATOR) + return; + profile = iter->profile; + if (profile->vt) + profile->vt->iterator_free(profile->cbdata, iter->idata); + else + profile_node_iterator_free(&iter->idata); + free(iter); + *iter_p = NULL; } -errcode_t KRB5_CALLCONV -profile_iterator(void **iter_p, char **ret_name, char **ret_value) +/* Make copies of name and value into *ret_name and *ret_value. Handle null + * values of any argument. */ +static errcode_t +set_results(const char *name, const char *value, char **ret_name, + char **ret_value) { - char *name, *value; - errcode_t retval; - - retval = profile_node_iterator(iter_p, 0, &name, &value); - if (retval) - return retval; - if (ret_name) { if (name) { *ret_name = strdup(name); if (!*ret_name) return ENOMEM; } else - *ret_name = 0; + *ret_name = NULL; } if (ret_value) { if (value) { @@ -442,16 +550,56 @@ profile_iterator(void **iter_p, char **ret_name, char **ret_value) if (!*ret_value) { if (ret_name) { free(*ret_name); - *ret_name = 0; + *ret_name = NULL; } return ENOMEM; } } else - *ret_value = 0; + *ret_value = NULL; } return 0; } +errcode_t KRB5_CALLCONV +profile_iterator(void **iter_p, char **ret_name, char **ret_value) +{ + char *name, *value; + errcode_t retval; + struct profile_iterator *iter = *iter_p; + profile_t profile; + + if (iter->magic != PROF_MAGIC_ITERATOR) + return PROF_MAGIC_ITERATOR; + profile = iter->profile; + + if (profile->vt) { + retval = profile->vt->iterator(profile->cbdata, iter->idata, &name, + &value); + if (retval) + return retval; + if (name == NULL) { + profile->vt->iterator_free(profile->cbdata, iter->idata); + free(iter); + *iter_p = NULL; + } + retval = set_results(name, value, ret_name, ret_value); + if (name) + profile->vt->free_string(profile->cbdata, name); + if (value) + profile->vt->free_string(profile->cbdata, value); + return retval; + } + + retval = profile_node_iterator(&iter->idata, 0, &name, &value); + if (iter->idata == NULL) { + free(iter); + *iter_p = NULL; + } + if (retval) + return retval; + return set_results(name, value, ret_name, ret_value); +} + void KRB5_CALLCONV profile_release_string(char *str) { diff --git a/src/util/profile/prof_init.c b/src/util/profile/prof_init.c index 408549dca..4c6b0d2dd 100644 --- a/src/util/profile/prof_init.c +++ b/src/util/profile/prof_init.c @@ -21,6 +21,44 @@ #endif typedef int32_t prof_int32; +errcode_t KRB5_CALLCONV +profile_init_vtable(struct profile_vtable *vtable, void *cbdata, + profile_t *ret_profile) +{ + profile_t profile; + struct profile_vtable *vt_copy; + + /* Check that the vtable's minor version is sane and that mandatory methods + * are implemented. */ + if (vtable->minor_ver < 1 || !vtable->get_values || !vtable->free_values) + return EINVAL; + if (vtable->cleanup && !vtable->copy) + return EINVAL; + if (vtable->iterator_create && + (!vtable->iterator || !vtable->iterator_free || !vtable->free_string)) + return EINVAL; + + profile = malloc(sizeof(*profile)); + if (!profile) + return ENOMEM; + memset(profile, 0, sizeof(*profile)); + + vt_copy = malloc(sizeof(*vt_copy)); + if (!vt_copy) { + free(profile); + return ENOMEM; + } + /* It's safe to just copy the caller's vtable for now. If the minor + * version is bumped, we'll need to copy individual fields. */ + *vt_copy = *vtable; + + profile->vt = vt_copy; + profile->cbdata = cbdata; + profile->magic = PROF_MAGIC_PROFILE; + *ret_profile = profile; + return 0; +} + errcode_t KRB5_CALLCONV profile_init(const_profile_filespec_t *files, profile_t *ret_profile) { @@ -93,6 +131,26 @@ profile_copy(profile_t old_profile, profile_t *new_profile) const_profile_filespec_t *files; prf_file_t file; errcode_t err; + void *cbdata; + + /* For copies of vtable profiles, use the same vtable and perhaps a new + * cbdata pointer. */ + if (old_profile->vt) { + if (old_profile->vt->copy) { + /* Make a copy of the cbdata for the new profile. */ + err = old_profile->vt->copy(old_profile->cbdata, &cbdata); + if (err) + return err; + err = profile_init_vtable(old_profile->vt, cbdata, new_profile); + if (err && old_profile->vt->cleanup) + old_profile->vt->cleanup(cbdata); + return err; + } else { + /* Use the same vtable and cbdata as the old profile. */ + return profile_init_vtable(old_profile->vt, old_profile->cbdata, + new_profile); + } + } /* The fields we care about are read-only after creation, so no locking is needed. */ @@ -168,6 +226,14 @@ profile_is_writable(profile_t profile, int *writable) if (!writable) return EINVAL; + *writable = 0; + + if (profile->vt) { + if (profile->vt->writable) + return profile->vt->writable(profile->cbdata, writable); + else + return 0; + } if (profile->first_file) *writable = profile_file_is_writable(profile->first_file); @@ -183,6 +249,14 @@ profile_is_modified(profile_t profile, int *modified) if (!modified) return EINVAL; + *modified = 0; + + if (profile->vt) { + if (profile->vt->modified) + return profile->vt->modified(profile->cbdata, modified); + else + return 0; + } if (profile->first_file) *modified = (profile->first_file->data->flags & PROFILE_FILE_DIRTY); @@ -196,6 +270,12 @@ profile_flush(profile_t profile) if (!profile || profile->magic != PROF_MAGIC_PROFILE) return PROF_MAGIC_PROFILE; + if (profile->vt) { + if (profile->vt->flush) + return profile->vt->flush(profile->cbdata); + return 0; + } + if (profile->first_file) return profile_flush_file(profile->first_file); @@ -208,6 +288,9 @@ profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile) if (!profile || profile->magic != PROF_MAGIC_PROFILE) return PROF_MAGIC_PROFILE; + if (profile->vt) + return PROF_UNSUPPORTED; + if (profile->first_file) return profile_flush_file_to_file(profile->first_file, outfile); @@ -218,6 +301,8 @@ profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile) errcode_t KRB5_CALLCONV profile_flush_to_buffer(profile_t profile, char **buf) { + if (profile->vt) + return PROF_UNSUPPORTED; return profile_flush_file_data_to_buffer(profile->first_file->data, buf); } @@ -235,9 +320,15 @@ profile_abandon(profile_t profile) if (!profile || profile->magic != PROF_MAGIC_PROFILE) return; - for (p = profile->first_file; p; p = next) { - next = p->next; - profile_free_file(p); + if (profile->vt) { + if (profile->vt->cleanup) + profile->vt->cleanup(profile->cbdata); + free(profile->vt); + } else { + for (p = profile->first_file; p; p = next) { + next = p->next; + profile_free_file(p); + } } profile->magic = 0; free(profile); @@ -251,9 +342,17 @@ profile_release(profile_t profile) if (!profile || profile->magic != PROF_MAGIC_PROFILE) return; - for (p = profile->first_file; p; p = next) { - next = p->next; - profile_close_file(p); + if (profile->vt) { + if (profile->vt->flush) + profile->vt->flush(profile->cbdata); + if (profile->vt->cleanup) + profile->vt->cleanup(profile->cbdata); + free(profile->vt); + } else { + for (p = profile->first_file; p; p = next) { + next = p->next; + profile_close_file(p); + } } profile->magic = 0; free(profile); diff --git a/src/util/profile/prof_int.h b/src/util/profile/prof_int.h index 541a31845..1795f70ae 100644 --- a/src/util/profile/prof_int.h +++ b/src/util/profile/prof_int.h @@ -87,6 +87,10 @@ typedef struct _prf_file_t *prf_file_t; struct _profile_t { prf_magic_t magic; prf_file_t first_file; + + /* If non-null, use vtable operations instead of native ones. */ + struct profile_vtable *vt; + void *cbdata; }; /* @@ -245,8 +249,7 @@ errcode_t profile_ser_internalize /* prof_get.c */ errcode_t profile_get_value - (profile_t profile, const char **names, - const char **ret_value); + (profile_t profile, const char **names, char **ret_value); /* Others included from profile.h */ /* prof_set.c -- included from profile.h */ diff --git a/src/util/profile/prof_set.c b/src/util/profile/prof_set.c index 893048ffb..369bc938d 100644 --- a/src/util/profile/prof_set.c +++ b/src/util/profile/prof_set.c @@ -89,6 +89,13 @@ profile_update_relation(profile_t profile, const char **names, void *state; const char **cpp; + if (profile->vt) { + if (!profile->vt->update_relation) + return PROF_UNSUPPORTED; + return profile->vt->update_relation(profile->cbdata, names, old_value, + new_value); + } + retval = rw_setup(profile); if (retval) return retval; @@ -141,6 +148,13 @@ profile_clear_relation(profile_t profile, const char **names) void *state; const char **cpp; + if (profile->vt) { + if (!profile->vt->update_relation) + return PROF_UNSUPPORTED; + return profile->vt->update_relation(profile->cbdata, names, NULL, + NULL); + } + retval = rw_setup(profile); if (retval) return retval; @@ -187,6 +201,12 @@ profile_rename_section(profile_t profile, const char **names, void *state; const char **cpp; + if (profile->vt) { + if (!profile->vt->rename_section) + return PROF_UNSUPPORTED; + return profile->vt->rename_section(profile->cbdata, names, new_name); + } + retval = rw_setup(profile); if (retval) return retval; @@ -240,6 +260,12 @@ profile_add_relation(profile_t profile, const char **names, const char **cpp; void *state; + if (profile->vt) { + if (!profile->vt->add_relation) + return PROF_UNSUPPORTED; + return profile->vt->add_relation(profile->cbdata, names, new_value); + } + retval = rw_setup(profile); if (retval) return retval; diff --git a/src/util/profile/prof_tree.c b/src/util/profile/prof_tree.c index 711fc951b..0064ad32a 100644 --- a/src/util/profile/prof_tree.c +++ b/src/util/profile/prof_tree.c @@ -382,9 +382,8 @@ errcode_t profile_get_node_parent(struct profile_node *section, * This is a general-purpose iterator for returning all nodes that * match the specified name array. */ -struct profile_iterator { +struct profile_node_iterator { prf_magic_t magic; - profile_t profile; int flags; const char *const *names; const char *name; @@ -399,7 +398,7 @@ errcode_t profile_node_iterator_create(profile_t profile, const char *const *names, int flags, void **ret_iter) { - struct profile_iterator *iter; + struct profile_node_iterator *iter; int done_idx = 0; if (profile == 0) @@ -414,11 +413,11 @@ errcode_t profile_node_iterator_create(profile_t profile, done_idx = 1; } - if ((iter = malloc(sizeof(struct profile_iterator))) == NULL) + iter = malloc(sizeof(*iter)); + if (iter == NULL) return ENOMEM; - iter->magic = PROF_MAGIC_ITERATOR; - iter->profile = profile; + iter->magic = PROF_MAGIC_NODE_ITERATOR; iter->names = names; iter->flags = flags; iter->file = profile->first_file; @@ -431,12 +430,12 @@ errcode_t profile_node_iterator_create(profile_t profile, void profile_node_iterator_free(void **iter_p) { - struct profile_iterator *iter; + struct profile_node_iterator *iter; if (!iter_p) return; iter = *iter_p; - if (!iter || iter->magic != PROF_MAGIC_ITERATOR) + if (!iter || iter->magic != PROF_MAGIC_NODE_ITERATOR) return; free(iter); *iter_p = 0; @@ -449,17 +448,18 @@ void profile_node_iterator_free(void **iter_p) * (profile_node_iterator is not an exported interface), it should be * strdup()'ed. */ -errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node, +errcode_t profile_node_iterator(void **iter_p, + struct profile_node **ret_node, char **ret_name, char **ret_value) { - struct profile_iterator *iter = *iter_p; + struct profile_node_iterator *iter = *iter_p; struct profile_node *section, *p; const char *const *cpp; errcode_t retval; int skip_num = 0; - if (!iter || iter->magic != PROF_MAGIC_ITERATOR) - return PROF_MAGIC_ITERATOR; + if (!iter || iter->magic != PROF_MAGIC_NODE_ITERATOR) + return PROF_MAGIC_NODE_ITERATOR; if (iter->file && iter->file->magic != PROF_MAGIC_FILE) return PROF_MAGIC_FILE; if (iter->file && iter->file->data->magic != PROF_MAGIC_FILE_DATA) diff --git a/src/util/profile/profile.hin b/src/util/profile/profile.hin index 128676c6f..0cfc7d1b4 100644 --- a/src/util/profile/profile.hin +++ b/src/util/profile/profile.hin @@ -118,6 +118,156 @@ long KRB5_CALLCONV profile_add_relation (profile_t profile, const char **names, const char *new_value); +/* + * profile_init_vtable allows a caller to create a profile-compatible object + * with a different back end. + */ + +/* + * Mandatory: Look up all of the relations for names, placing the resulting + * values in *ret_values. If no relations exist, return PROF_NO_RELATION, or + * PROF_NO_SECTION to indicate that one of the intermediate names does not + * exist as a section. The list will be freed with free_values. + */ +typedef long +(*profile_get_values_fn)(void *cbdata, const char *const *names, + char ***ret_values); + +/* Mandatory: Free a list of strings returned by get_values. */ +typedef void +(*profile_free_values_fn)(void *cbdata, char **values); + +/* Optional: Release any data associated with the profile. */ +typedef void +(*profile_cleanup_fn)(void *cbdata); + +/* + * Optional (mandatory if cleanup is defined): Generate a new cbdata pointer + * for a copy of the profile. If not implemented, the new profile will receive + * the same cbdata pointer as the old one. + */ +typedef long +(*profile_copy_fn)(void *cbdata, void **ret_cbdata); + +/* + * Optional: Create an iterator handle. + * + * If flags contains PROFILE_ITER_LIST_SECTION, iterate over all of the + * relations and sections within names. Otherwise, iterate over the relation + * values for names, or produce a single section result if names is a section. + * + * If flags contains PROFILE_ITER_SECTIONS_ONLY, produce only sections. + * + * If flags contains PROFILE_ITER_RELATIONS_ONLY, produce only relations. + */ +typedef long +(*profile_iterator_create_fn)(void *cbdata, const char *const *names, + int flags, void **ret_iter); + +/* + * Optional (mandatory if iterator_create is defined): Produce the next + * relation or section in an iteration. If producing a section result, set + * *ret_value to NULL. The returned strings will be freed with free_string. + */ +typedef long +(*profile_iterator_fn)(void *cbdata, void *iter, char **ret_name, + char **ret_value); + +/* + * Optional (mandatory if iterator_create is defined): Free the memory for an + * iterator. + */ +typedef void +(*profile_iterator_free_fn)(void *cbdata, void *iter); + +/* Optional (mandatory if iterator is defined): Free a string value. */ +typedef void +(*profile_free_string_fn)(void *cbdata, char *string); + +/* + * Optional: Determine if a profile is writable. If not implemented, the + * profile is never writable. + */ +typedef long +(*profile_writable_fn)(void *cbdata, int *writable); + +/* + * Optional: Determine if a profile is modified in memory relative to the + * persistent store. If not implemented, the profile is assumed to never be + * modified. + */ +typedef long +(*profile_modified_fn)(void *cbdata, int *modified); + +/* + * Optional: Change the value of a relation, or remove it if new_value is NULL. + * If old_value is set and the relation does not have that value, return + * PROF_NO_RELATION. + */ +typedef long +(*profile_update_relation_fn)(void *cbdata, const char **names, + const char *old_value, const char *new_value); + +/* + * Optional: Rename a section to new_name, or remove the section if new_name is + * NULL. + */ +typedef long +(*profile_rename_section_fn)(void *cbdata, const char **names, + const char *new_name); + +/* + * Optional: Add a new relation, or a new section if new_value is NULL. Add + * any intermediate sections as necessary. + */ +typedef long +(*profile_add_relation_fn)(void *cbdata, const char **names, + const char *new_value); + +/* + * Optional: Flush any pending memory updates to the persistent store. If + * implemented, this function will be called by profile_release as well as + * profile_flush, so make sure it's not inefficient to flush an unmodified + * profile. + */ +typedef long +(*profile_flush_fn)(void *cbdata); + +struct profile_vtable { + int minor_ver; /* Set this to 1. */ + + /* Methods needed for a basic read-only non-iterable profile (cleanup is + * optional). */ + profile_get_values_fn get_values; + profile_free_values_fn free_values; + profile_cleanup_fn cleanup; + profile_copy_fn copy; + + /* Methods for iterable profiles. */ + profile_iterator_create_fn iterator_create; + profile_iterator_fn iterator; + profile_iterator_free_fn iterator_free; + profile_free_string_fn free_string; + + /* Methods for writable profiles. */ + profile_writable_fn writable; + profile_modified_fn modified; + profile_update_relation_fn update_relation; + profile_rename_section_fn rename_section; + profile_add_relation_fn add_relation; + profile_flush_fn flush; + + /* End of minor version 1. */ +}; + +/* + * Create a profile object whose operations will be performed using the + * function pointers in vtable. cbdata will be supplied to each vtable + * function as the first argument. + */ +long KRB5_CALLCONV profile_init_vtable + (struct profile_vtable *vtable, void *cbdata, profile_t *ret_profile); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/util/profile/test_profile.c b/src/util/profile/test_profile.c index 8155156ee..ea7113dae 100644 --- a/src/util/profile/test_profile.c +++ b/src/util/profile/test_profile.c @@ -93,6 +93,7 @@ static void do_batchmode(profile) switch (print_status) { case PRINT_VALUE: printf("%s\n", value); + profile_release_string(value); break; case PRINT_VALUES: for (cpp = values; *cpp; cpp++) diff --git a/src/util/profile/test_vtable.c b/src/util/profile/test_vtable.c new file mode 100644 index 000000000..115eed1e2 --- /dev/null +++ b/src/util/profile/test_vtable.c @@ -0,0 +1,301 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/profile/test_vtable.c - Test program for vtable-backed profiles */ +/* + * Copyright (C) 2011 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * This test program exercises vtable profile functionality using two vtables, + * one which implements just the basic methods and one which implements all of + * the methods. The program doesn't attempt to create a working profile + * implementation; it just verifies the expected control flow into the vtable + * and back out to the caller. + */ + +#include +#include "profile.h" + +static int basic_cbdata; +static int full_cbdata; +static const char *empty_names[] = { NULL }; +static const char *name_string = "get_string"; +static const char *name_int = "get_int"; +static const char *name_bool = "get_bool"; + +static long +basic_get_values(void *cbdata, const char *const *names, char ***ret_values) +{ + assert(cbdata == &basic_cbdata); + assert(names == empty_names); + *ret_values = calloc(3, sizeof(*ret_values)); + (*ret_values)[0] = strdup("one"); + (*ret_values)[1] = strdup("two"); + (*ret_values)[2] = NULL; + return 0; +} + +static void +free_values(void *cbdata, char **values) +{ + char **v; + + for (v = values; *v; v++) + free(*v); + free(values); +} + +static long +full_get_values(void *cbdata, const char *const *names, char ***ret_values) +{ + assert(cbdata == &full_cbdata); + *ret_values = calloc(2, sizeof(*ret_values)); + if (names[0] == name_string) + (*ret_values)[0] = strdup("string result"); + else if (names[0] == name_int) + (*ret_values)[0] = strdup("23"); + else if (names[0] == name_bool) + (*ret_values)[0] = strdup("on"); + else { + free(*ret_values); + return PROF_NO_RELATION; + } + (*ret_values)[1] = NULL; + return 0; +} + +static void +full_cleanup(void *cbdata) +{ + assert(cbdata == &full_cbdata); +} + +static void +full_copy(void *cbdata, void **ret_cbdata) +{ + assert(cbdata == &full_cbdata); + *ret_cbdata = &full_cbdata; +} + +struct iterator { + int count; +}; + +static long +full_iterator_create(void *cbdata, const char *const *names, int flags, + void **ret_iter) +{ + struct iterator *iter; + + assert(cbdata == &full_cbdata); + assert(names == empty_names); + assert(flags == 126); + iter = malloc(sizeof(*iter)); + iter->count = 0; + *ret_iter = iter; + return 0; +} + +static long +full_iterator(void *cbdata, void *iter_arg, char **ret_name, char **ret_value) +{ + struct iterator *iter = iter_arg; + + assert(cbdata == &full_cbdata); + assert(iter->count >= 0 && iter->count <= 2); + if (iter->count == 0) { + *ret_name = strdup("name1"); + *ret_value = strdup("value1"); + } else if (iter->count == 1) { + *ret_name = strdup("name2"); + *ret_value = NULL; + } else { + *ret_name = NULL; + *ret_value = NULL; + } + iter->count++; + return 0; +} + +static void +full_iterator_free(void *cbdata, void *iter_arg) +{ + struct iterator *iter = iter_arg; + + assert(cbdata == &full_cbdata); + assert(iter->count == 3); + free(iter); +} + +static void +full_free_string(void *cbdata, char *string) +{ + assert(cbdata == &full_cbdata); + free(string); +} + +static long +full_writable(void *cbdata, int *writable) +{ + assert(cbdata == &full_cbdata); + *writable = 12; + return 0; +} + +static long +full_modified(void *cbdata, int *modified) +{ + assert(cbdata == &full_cbdata); + *modified = 6; + return 0; +} + +static long +full_update_relation(void *cbdata, const char **names, + const char *old_value, const char *new_value) +{ + assert(cbdata == &full_cbdata); + assert(names == empty_names); + assert(old_value == name_string || old_value == NULL); + assert(new_value == NULL); + return 0; +} + +static long +full_rename_section(void *cbdata, const char **names, const char *new_name) +{ + assert(cbdata == &full_cbdata); + assert(names == empty_names); + assert(new_name == name_int); + return 0; +} + +static long +full_add_relation(void *cbdata, const char **names, const char *new_value) +{ + assert(cbdata == &full_cbdata); + assert(names == empty_names); + assert(new_value == name_bool); + return 0; +} + +static long +full_flush(void *cbdata) +{ + assert(cbdata == &full_cbdata); +} + +struct profile_vtable basic_vtable = { + 1, + basic_get_values, + free_values, +}; + +struct profile_vtable full_vtable = { + 1, + full_get_values, + free_values, + full_cleanup, + full_copy, + + full_iterator_create, + full_iterator, + full_iterator_free, + full_free_string, + + full_writable, + full_modified, + full_update_relation, + full_rename_section, + full_add_relation, + full_flush +}; + +int main() +{ + profile_t profile; + char **values, *str, *name, *value; + void *iter; + int intval; + + assert(profile_init_vtable(&basic_vtable, &basic_cbdata, &profile) == 0); + assert(profile_get_values(profile, empty_names, &values) == 0); + assert(strcmp(values[0], "one") == 0); + assert(strcmp(values[1], "two") == 0); + assert(values[2] == NULL); + profile_free_list(values); + assert(profile_iterator_create(profile, NULL, 0, &iter) == + PROF_UNSUPPORTED); + assert(profile_is_writable(profile, &intval) == 0); + assert(intval == 0); + assert(profile_is_modified(profile, &intval) == 0); + assert(intval == 0); + assert(profile_update_relation(profile, NULL, NULL, NULL) == + PROF_UNSUPPORTED); + assert(profile_clear_relation(profile, NULL) == PROF_UNSUPPORTED); + assert(profile_rename_section(profile, NULL, NULL) == PROF_UNSUPPORTED); + assert(profile_add_relation(profile, NULL, NULL) == PROF_UNSUPPORTED); + profile_flush(profile); + profile_abandon(profile); + + assert(profile_init_vtable(&full_vtable, &full_cbdata, &profile) == 0); + assert(profile_get_string(profile, name_string, NULL, NULL, "wrong", + &str) == 0); + assert(strcmp(str, "string result") == 0); + profile_release_string(str); + assert(profile_get_integer(profile, name_int, NULL, NULL, 24, + &intval) == 0); + assert(intval == 23); + assert(profile_get_boolean(profile, name_bool, NULL, NULL, 0, + &intval) == 0); + assert(intval == 1); + assert(profile_get_integer(profile, "xxx", NULL, NULL, 62, &intval) == 0); + assert(intval == 62); + + assert(profile_iterator_create(profile, empty_names, 126, &iter) == 0); + assert(profile_iterator(&iter, &name, &value) == 0); + assert(strcmp(name, "name1") == 0); + assert(strcmp(value, "value1") == 0); + profile_release_string(name); + profile_release_string(value); + assert(profile_iterator(&iter, &name, &value) == 0); + assert(strcmp(name, "name2") == 0); + assert(value == NULL); + profile_release_string(name); + assert(profile_iterator(&iter, &name, &value) == 0); + assert(iter == NULL); + assert(name == NULL); + assert(value == NULL); + + assert(profile_is_writable(profile, &intval) == 0); + assert(intval == 12); + assert(profile_is_modified(profile, &intval) == 0); + assert(intval == 6); + assert(profile_update_relation(profile, empty_names, name_string, + NULL) == 0); + assert(profile_clear_relation(profile, empty_names) == 0); + assert(profile_rename_section(profile, empty_names, name_int) == 0); + assert(profile_add_relation(profile, empty_names, name_bool) == 0); + profile_release(profile); + + return 0; +} -- 2.26.2