Add libprofile support for vtable-backed profiles
authorGreg Hudson <ghudson@mit.edu>
Wed, 20 Jul 2011 19:14:20 +0000 (19:14 +0000)
committerGreg Hudson <ghudson@mit.edu>
Wed, 20 Jul 2011 19:14:20 +0000 (19:14 +0000)
ticket: 6929

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25024 dc483132-0cff-0310-8789-dd5450dbe970

src/util/profile/Makefile.in
src/util/profile/prof_err.et
src/util/profile/prof_get.c
src/util/profile/prof_init.c
src/util/profile/prof_int.h
src/util/profile/prof_set.c
src/util/profile/prof_tree.c
src/util/profile/profile.hin
src/util/profile/test_profile.c
src/util/profile/test_vtable.c [new file with mode: 0644]

index 326fd19676ce59883fb4769cf86f72d979ed1ad6..e5c973fd3354205bfc0495f68e572e766fdf5ab1 100644 (file)
@@ -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)
index 2384127af95dfe46362218f8b1463d42bc26259f..41aec89c6876e5e430a4905bfee64ca6c516e94a 100644 (file)
@@ -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
index 460d2e5f21735aa1426d1f1cfe690c14f4a6ad69..f3bc3146372fdb7755b8d2c06bd346f82ce1a911 100644 (file)
@@ -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)
 {
index 408549dca07240a8f8e67e5489c255661ef39b4b..4c6b0d2dd6df249c65795e77e9b1f3b1871af7d7 100644 (file)
 #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);
index 541a31845335f6a6b3852a70aaccbc7c5a352fc6..1795f70ae520e111909a74dbfcd11613f14810d9 100644 (file)
@@ -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 */
index 893048ffb92cbba4e748d94053648fd58bdbe259..369bc938d8092dfba5c78b2d1f9cead6c08f78d4 100644 (file)
@@ -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;
index 711fc951b837a19cf25f03321ae5c9ea4c215507..0064ad32a63bbc81df3829a28956dba19a6bd2b2 100644 (file)
@@ -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)
index 128676c6f425881f15fb825cbc4e47358f6f9ea0..0cfc7d1b4165546fdba44b44869766ac1b9832ff 100644 (file)
@@ -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 */
index 8155156eee73a6f5517777d22261593416027004..ea7113daebc77b138378012ccde871dd8e60fcd8 100644 (file)
@@ -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 (file)
index 0000000..115eed1
--- /dev/null
@@ -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 <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;
+}