$(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)
$(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!!
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)
"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
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)
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)))
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)
retval = PROF_NO_RELATION;
cleanup:
- profile_node_iterator_free(&state);
+ profile_iterator_free(&state);
return retval;
}
const char *subsubname, const char *def_val,
char **ret_string)
{
- const char *value;
+ char *value;
errcode_t retval;
const char *names[4];
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;
}
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)
} 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[] = {
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];
} else if (retval)
return retval;
- return profile_parse_boolean (value, ret_boolean);
+ retval = profile_parse_boolean(value, ret_boolean);
+ free(value);
+ return retval;
}
/*
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);
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);
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) {
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)
{
#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)
{
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. */
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);
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);
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);
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);
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);
}
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);
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);
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;
};
/*
/* 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 */
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;
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;
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;
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;
* 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;
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)
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;
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;
* (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)
(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 */
switch (print_status) {
case PRINT_VALUE:
printf("%s\n", value);
+ profile_release_string(value);
break;
case PRINT_VALUES:
for (cpp = values; *cpp; cpp++)
--- /dev/null
+/* -*- 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 <k5-platform.h>
+#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;
+}