Add support for loadable profile modules
authorGreg Hudson <ghudson@mit.edu>
Wed, 20 Jul 2011 19:14:28 +0000 (19:14 +0000)
committerGreg Hudson <ghudson@mit.edu>
Wed, 20 Jul 2011 19:14:28 +0000 (19:14 +0000)
ticket: 6929

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

19 files changed:
src/configure.in
src/lib/krb5/os/init_os_ctx.c
src/util/profile/Makefile.in
src/util/profile/deps
src/util/profile/libprofile.exports
src/util/profile/prof_err.et
src/util/profile/prof_file.c
src/util/profile/prof_init.c
src/util/profile/prof_int.h
src/util/profile/prof_parse.c
src/util/profile/prof_set.c
src/util/profile/prof_tree.c
src/util/profile/profile.hin
src/util/profile/test_load.c [new file with mode: 0644]
src/util/profile/test_parse.c
src/util/profile/testmod/Makefile.in [new file with mode: 0644]
src/util/profile/testmod/deps [new file with mode: 0644]
src/util/profile/testmod/proftest.exports [new file with mode: 0644]
src/util/profile/testmod/testmod_main.c [new file with mode: 0644]

index 8789de75b70bfc8dbbc5f1ca2b45f3907eab04b6..4ed9e31e376ac8c8ebb43dac9805c611ef69264b 100644 (file)
@@ -1103,7 +1103,7 @@ fi
 AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config])
 V5_AC_OUTPUT_MAKEFILE(.
 
-       util util/support util/profile util/send-pr
+       util util/support util/profile util/profile/testmod util/send-pr
 
        lib lib/kdb
 
index 9387cf872009a00d7c256647271b79305771c5c8..98b8ae2307dd4e4c197181354150f1ed98c776b0 100644 (file)
@@ -325,8 +325,8 @@ os_init_paths(krb5_context ctx, krb5_boolean kdc)
         retval = add_kdc_config_file(&files);
 
     if (!retval) {
-        retval = profile_init((const_profile_filespec_t *) files,
-                              &ctx->profile);
+        retval = profile_init_flags((const_profile_filespec_t *) files,
+                                    PROFILE_INIT_ALLOW_MODULE, &ctx->profile);
 
 #ifdef KRB5_DNS_LOOKUP
         /* if none of the filenames can be opened use an empty profile */
@@ -405,7 +405,8 @@ krb5_set_config_files(krb5_context ctx, const char **filenames)
     krb5_error_code retval = 0;
     profile_t    profile;
 
-    retval = profile_init(filenames, &profile);
+    retval = profile_init_flags(filenames, PROFILE_INIT_ALLOW_MODULE,
+                                &profile);
     if (retval)
         return retval;
 
index e5c973fd3354205bfc0495f68e572e766fdf5ab1..e7d84aa23db4cea7629f7e3447a9d01e9fc06150 100644 (file)
@@ -1,5 +1,6 @@
 mydir=util$(S)profile
 BUILDTOP=$(REL)..$(S)..
+SUBDIRS=testmod
 PROG_LIBPATH=-L$(TOPLIBD) $(TCL_LIBPATH) -L.
 PROG_RPATH=$(KRB5_LIBDIR)$(TCL_RPATH)
 KRB5_RUN_ENV=@KRB5_RUN_ENV@
@@ -10,7 +11,7 @@ DEFS=
 
 LOCALINCLUDES=-I. $(TCL_INCLUDES)
 # for tcl.h
-DEFINES=-DHAS_STDARG
+DEFINES=-DHAS_STDARG -DLIBDIR=\"$(KRB5_LIBDIR)\"
 
 STLIBOBJS = \
        prof_tree.o \
@@ -37,8 +38,9 @@ SRCS = $(srcdir)/prof_tree.c \
        prof_err.c \
        $(srcdir)/prof_init.c
 
-EXTRADEPSRCS=$(srcdir)/test_parse.c $(srcdir)/test_profile.c \
-       $(srcdir)/test_vtable.c $(srcdir)/profile_tcl.c
+EXTRADEPSRCS=$(srcdir)/test_load.c $(srcdir)/test_parse.c \
+       $(srcdir)/test_profile.c $(srcdir)/test_vtable.c \
+       $(srcdir)/profile_tcl.c
 
 DEPLIBS = $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
 MLIBS = -lcom_err $(SUPPORT_LIB) $(LIBS)
@@ -96,6 +98,12 @@ test_profile: test_profile.$(OBJEXT) argv_parse.$(OBJEXT) $(OBJS) $(DEPLIBS)
 test_vtable: test_vtable.$(OBJEXT) $(OBJS) $(DEPLIBS)
        $(CC_LINK) -o test_vtable test_vtable.$(OBJEXT) $(OBJS) $(MLIBS)
 
+test_load: test_load.$(OBJEXT) $(SUPPORT_DEPLIB)
+       $(CC_LINK) -o test_load test_load.$(OBJEXT) $(OBJS) $(MLIBS)
+
+modtest.conf:
+       echo "module `pwd`/testmod/proftest$(DYNOBJEXT):teststring" > $@
+
 .d: includes
 
 # NEED TO FIX!!
@@ -131,14 +139,16 @@ profile_tcl: profile_tcl.o $(PROF_DEPLIB) $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
                -L../et -L../.. -lprofile $(TCL_LIBS) $(MLIBS)
 
 clean-unix:: clean-libs clean-libobjs
-       $(RM) $(PROGS) *.o *~ test_parse core prof_err.h \
-               prof_err.c test_profile test_vtable profile.h profile_tcl
+       $(RM) $(PROGS) *.o *~ core prof_err.h profile.h prof_err.c
+       $(RM) test_load test_parse test_profile test_vtable profile_tcl
+       $(RM) modtest.conf
 
 clean-windows::
        $(RM) $(PROFILE_HDR)
 
-check-unix:: test_parse test_profile test_vtable
+check-unix:: test_parse test_profile test_vtable test_load modtest.conf
        $(KRB5_RUN_ENV) $(VALGRIND) ./test_vtable
+       $(KRB5_RUN_ENV) $(VALGRIND) ./test_load
 
 DO_TCL=@DO_TCL@
 check-unix:: check-unix-tcl-$(DO_TCL)
index 837bea7b801a707fbd01ece786068f94beefd846..f5fcdbfdd288d79d5589c39d878d4cb7215ed819 100644 (file)
@@ -3,38 +3,54 @@
 #
 prof_tree.so prof_tree.po $(OUTPRE)prof_tree.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_int.h prof_tree.c
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_int.h prof_tree.c
 prof_file.so prof_file.po $(OUTPRE)prof_file.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_file.c prof_int.h
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_file.c prof_int.h
 prof_parse.so prof_parse.po $(OUTPRE)prof_parse.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_int.h prof_parse.c
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_int.h prof_parse.c
 prof_get.so prof_get.po $(OUTPRE)prof_get.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_get.c prof_int.h
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_get.c prof_int.h
 prof_set.so prof_set.po $(OUTPRE)prof_set.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_int.h prof_set.c
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_int.h prof_set.c
 prof_err.so prof_err.po $(OUTPRE)prof_err.$(OBJEXT): \
   $(COM_ERR_DEPS) prof_err.c
 prof_init.so prof_init.po $(OUTPRE)prof_init.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_init.c prof_int.h
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_init.c prof_int.h
+test_load.so test_load.po $(OUTPRE)test_load.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_int.h test_load.c
 test_parse.so test_parse.po $(OUTPRE)test_parse.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h prof_int.h test_parse.c
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  prof_int.h test_parse.c
 test_profile.so test_profile.po $(OUTPRE)test_profile.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  argv_parse.h prof_int.h test_profile.c
+test_vtable.so test_vtable.po $(OUTPRE)test_vtable.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
   $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-thread.h argv_parse.h prof_int.h \
-  test_profile.c
+  $(top_srcdir)/include/k5-thread.h test_vtable.c
 profile_tcl.so profile_tcl.po $(OUTPRE)profile_tcl.$(OBJEXT): \
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) profile_tcl.c
index ba34354ec57e24af2bbd152dec9d6f9fbf8e0792..3f02b4f4f4a8c5ee59d3f5b10d58b2c54ff1d61d 100644 (file)
@@ -12,7 +12,9 @@ profile_get_string
 profile_get_subsection_names
 profile_get_values
 profile_init
+profile_init_flags
 profile_init_path
+profile_init_vtable
 profile_iterator
 profile_iterator_create
 profile_iterator_free
index 41aec89c6876e5e430a4905bfee64ca6c516e94a..d5850d00ed5a97447540e34954ec757716444351 100644 (file)
@@ -70,5 +70,9 @@ 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"
+error_code     PROF_MODULE, "Unexpected module declaration in profile"
+error_code     PROF_MODULE_SYNTAX,
+       "Invalid syntax of module declaration in profile"
+error_code     PROF_MODULE_INVALID, "Invalid profile module"
 
 end
index bf25b2e4d0fb49da59873286cc634968f3e3ae0d..988afacecf88d92a9c483b5d356d1078cb3ec0e4 100644 (file)
@@ -193,7 +193,7 @@ profile_make_prf_data(const char *filename)
 }
 
 errcode_t profile_open_file(const_profile_filespec_t filespec,
-                            prf_file_t *ret_prof)
+                            prf_file_t *ret_prof, char **ret_modspec)
 {
     prf_file_t      prf;
     errcode_t       retval;
@@ -256,7 +256,7 @@ errcode_t profile_open_file(const_profile_filespec_t filespec,
     if (data) {
         data->refcount++;
         (void) k5_mutex_unlock(&g_shared_trees_mutex);
-        retval = profile_update_file_data(data);
+        retval = profile_update_file_data(data, NULL);
         free(expanded_filename);
         prf->data = data;
         *ret_prof = prf;
@@ -280,7 +280,7 @@ errcode_t profile_open_file(const_profile_filespec_t filespec,
         return retval;
     }
 
-    retval = profile_update_file(prf);
+    retval = profile_update_file(prf, ret_modspec);
     if (retval) {
         profile_close_file(prf);
         return retval;
@@ -303,7 +303,7 @@ errcode_t profile_open_file(const_profile_filespec_t filespec,
     return 0;
 }
 
-errcode_t profile_update_file_data_locked(prf_data_t data)
+errcode_t profile_update_file_data_locked(prf_data_t data, char **ret_modspec)
 {
     errcode_t retval;
 #ifdef HAVE_STAT
@@ -361,7 +361,7 @@ errcode_t profile_update_file_data_locked(prf_data_t data)
     set_cloexec_file(f);
     data->upd_serial++;
     data->flags &= PROFILE_FILE_SHARED;  /* FIXME same as '=' operator */
-    retval = profile_parse_file(f, &data->root);
+    retval = profile_parse_file(f, &data->root, ret_modspec);
     fclose(f);
     if (retval) {
         return retval;
@@ -374,14 +374,14 @@ errcode_t profile_update_file_data_locked(prf_data_t data)
     return 0;
 }
 
-errcode_t profile_update_file_data(prf_data_t data)
+errcode_t profile_update_file_data(prf_data_t data, char **ret_modspec)
 {
     errcode_t retval, retval2;
 
     retval = k5_mutex_lock(&data->lock);
     if (retval)
         return retval;
-    retval = profile_update_file_data_locked(data);
+    retval = profile_update_file_data_locked(data, ret_modspec);
     retval2 = k5_mutex_unlock(&data->lock);
     return retval ? retval : retval2;
 }
index 4c6b0d2dd6df249c65795e77e9b1f3b1871af7d7..959598e42a7adf8033d6464a7ba845ad68c980a8 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)
+/* Create a vtable profile, possibly with a library handle.  The new profile
+ * takes ownership of the handle refcount on success. */
+static errcode_t
+init_module(struct profile_vtable *vtable, void *cbdata,
+            prf_lib_handle_t handle, profile_t *ret_profile)
 {
     profile_t profile;
     struct profile_vtable *vt_copy;
@@ -54,18 +56,117 @@ profile_init_vtable(struct profile_vtable *vtable, void *cbdata,
 
     profile->vt = vt_copy;
     profile->cbdata = cbdata;
+    profile->lib_handle = handle;
     profile->magic = PROF_MAGIC_PROFILE;
     *ret_profile = profile;
     return 0;
 }
 
+/* Parse modspec into the module path and residual string. */
+static errcode_t
+parse_modspec(const char *modspec, char **ret_path, char **ret_residual)
+{
+    const char *p, *prefix;
+    char *path, *residual;
+
+    *ret_path = *ret_residual = NULL;
+
+    p = strchr(modspec, ':');
+    if (p == NULL)
+        return PROF_MODULE_SYNTAX;
+
+    /* XXX Unix path handling for now. */
+    prefix = (*modspec == '/') ? "" : LIBDIR "/";
+    if (asprintf(&path, "%s%.*s", prefix, (int)(p - modspec), modspec) < 0)
+        return ENOMEM;
+
+    residual = strdup(p + 1);
+    if (residual == NULL) {
+        free(path);
+        return ENOMEM;
+    }
+
+    *ret_path = path;
+    *ret_residual = residual;
+    return 0;
+}
+
+/* Load a dynamic profile module as specified by modspec and create a vtable
+ * profile for it in *ret_profile. */
+static errcode_t
+init_load_module(const char *modspec, profile_t *ret_profile)
+{
+    char *modpath = NULL, *residual = NULL;
+    struct errinfo einfo = { 0 };
+    prf_lib_handle_t lib_handle = NULL;
+    struct plugin_file_handle *plhandle = NULL;
+    void *cbdata = NULL, (*fptr)();
+    int have_lock = 0, have_cbdata = 0;
+    struct profile_vtable vtable = { 1 };  /* Set minor_ver to 1, rest null. */
+    errcode_t err;
+    profile_module_init_fn initfn;
+
+    err = parse_modspec(modspec, &modpath, &residual);
+    if (err)
+        goto cleanup;
+
+    /* Allocate a reference-counted library handle container. */
+    lib_handle = malloc(sizeof(*lib_handle));
+    if (lib_handle == NULL)
+        goto cleanup;
+    err = k5_mutex_init(&lib_handle->lock);
+    if (err)
+        goto cleanup;
+    have_lock = 1;
+
+    /* Open the module and get its initializer. */
+    err = krb5int_open_plugin(modpath, &plhandle, &einfo);
+    if (err)
+        goto cleanup;
+    err = krb5int_get_plugin_func(plhandle, "profile_module_init", &fptr,
+                                  &einfo);
+    if (err == ENOENT)
+        err = PROF_MODULE_INVALID;
+    if (err)
+        goto cleanup;
+
+    /* Get the profile vtable and callback data pointer. */
+    initfn = (profile_module_init_fn)fptr;
+    err = (*initfn)(residual, &vtable, &cbdata);
+    if (err)
+        goto cleanup;
+    have_cbdata = 1;
+
+    /* Create a vtable profile with the information obtained. */
+    lib_handle->plugin_handle = plhandle;
+    lib_handle->refcount = 1;
+    err = init_module(&vtable, cbdata, lib_handle, ret_profile);
+
+cleanup:
+    free(modpath);
+    free(residual);
+    krb5int_clear_error(&einfo);
+    if (err) {
+        if (have_cbdata && vtable.cleanup)
+            vtable.cleanup(cbdata);
+        if (have_lock)
+            k5_mutex_destroy(&lib_handle->lock);
+        free(lib_handle);
+        if (plhandle)
+            krb5int_close_plugin(plhandle);
+    }
+    return err;
+}
+
 errcode_t KRB5_CALLCONV
-profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
+profile_init_flags(const_profile_filespec_t *files, int flags,
+                   profile_t *ret_profile)
 {
     const_profile_filespec_t *fs;
     profile_t profile;
     prf_file_t  new_file, last = 0;
     errcode_t retval = 0, access_retval = 0;
+    char *modspec = NULL, **modspec_arg;
 
     profile = malloc(sizeof(struct _profile_t));
     if (!profile)
@@ -79,7 +180,18 @@ profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
      */
     if ( files && !PROFILE_LAST_FILESPEC(*files) ) {
         for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
-            retval = profile_open_file(*fs, &new_file);
+            /* Allow a module declaration if it is permitted by flags and this
+             * is the first file parsed. */
+            modspec_arg = ((flags & PROFILE_INIT_ALLOW_MODULE) && !last) ?
+                &modspec : NULL;
+            retval = profile_open_file(*fs, &new_file, modspec_arg);
+            if (retval == PROF_MODULE && modspec) {
+                /* Stop parsing files and load a dynamic module instead. */
+                free(profile);
+                retval = init_load_module(modspec, ret_profile);
+                free(modspec);
+                return retval;
+            }
             /* if this file is missing, skip to the next */
             if (retval == ENOENT) {
                 continue;
@@ -113,6 +225,63 @@ profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
     return 0;
 }
 
+errcode_t KRB5_CALLCONV
+profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
+{
+    return profile_init_flags(files, 0, ret_profile);
+}
+
+errcode_t KRB5_CALLCONV
+profile_init_vtable(struct profile_vtable *vtable, void *cbdata,
+                    profile_t *ret_profile)
+{
+    return init_module(vtable, cbdata, NULL, ret_profile);
+}
+
+/* Copy a vtable profile. */
+static errcode_t
+copy_vtable_profile(profile_t profile, profile_t *ret_new_profile)
+{
+    errcode_t err;
+    void *cbdata;
+    profile_t new_profile;
+
+    *ret_new_profile = NULL;
+
+    if (profile->vt->copy) {
+        /* Make a copy of profile's cbdata for the new profile. */
+        err = profile->vt->copy(profile->cbdata, &cbdata);
+        if (err)
+            return err;
+        err = init_module(profile->vt, cbdata, profile->lib_handle,
+                          &new_profile);
+        if (err && profile->vt->cleanup)
+            profile->vt->cleanup(cbdata);
+    } else {
+        /* Use the same cbdata as the old profile. */
+        err = init_module(profile->vt, profile->cbdata, profile->lib_handle,
+                          &new_profile);
+    }
+    if (err)
+        return err;
+
+    /* Increment the refcount on the library handle if there is one. */
+    if (profile->lib_handle) {
+        err = k5_mutex_lock(&profile->lib_handle->lock);
+        if (err) {
+            /* Don't decrement the refcount we failed to increment. */
+            new_profile->lib_handle = NULL;
+            profile_abandon(new_profile);
+            return err;
+        }
+        profile->lib_handle->refcount++;
+        k5_mutex_unlock(&profile->lib_handle->lock);
+    }
+
+    *ret_new_profile = new_profile;
+    return 0;
+}
+
 #define COUNT_LINKED_LIST(COUNT, PTYPE, START, FIELD)   \
     {                                                   \
         size_t cll_counter = 0;                         \
@@ -131,26 +300,9 @@ 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);
-        }
-    }
+    if (old_profile->vt)
+        return copy_vtable_profile(old_profile, new_profile);
 
     /* The fields we care about are read-only after creation, so
        no locking is needed.  */
@@ -208,8 +360,8 @@ profile_init_path(const_profile_filespec_list_t filepath,
     /* cap the array */
     filenames[i] = 0;
 
-    retval = profile_init((const_profile_filespec_t *) filenames,
-                          ret_profile);
+    retval = profile_init_flags((const_profile_filespec_t *) filenames, 0,
+                                ret_profile);
 
     /* count back down and free the entries */
     while(--i >= 0) free(filenames[i]);
@@ -316,6 +468,7 @@ void KRB5_CALLCONV
 profile_abandon(profile_t profile)
 {
     prf_file_t      p, next;
+    errcode_t       err;
 
     if (!profile || profile->magic != PROF_MAGIC_PROFILE)
         return;
@@ -323,6 +476,14 @@ profile_abandon(profile_t profile)
     if (profile->vt) {
         if (profile->vt->cleanup)
             profile->vt->cleanup(profile->cbdata);
+        if (profile->lib_handle) {
+            /* Decrement the refcount on the handle and maybe free it. */
+            err = k5_mutex_lock(&profile->lib_handle->lock);
+            if (!err && --profile->lib_handle->refcount == 0) {
+                krb5int_close_plugin(profile->lib_handle->plugin_handle);
+                free(profile->lib_handle);
+            }
+        }
         free(profile->vt);
     } else {
         for (p = profile->first_file; p; p = next) {
@@ -343,11 +504,11 @@ profile_release(profile_t profile)
         return;
 
     if (profile->vt) {
+        /* Flush the profile and then delegate to profile_abandon. */
         if (profile->vt->flush)
             profile->vt->flush(profile->cbdata);
-        if (profile->vt->cleanup)
-            profile->vt->cleanup(profile->cbdata);
-        free(profile->vt);
+        profile_abandon(profile);
+        return;
     } else {
         for (p = profile->first_file; p; p = next) {
             next = p->next;
index 1795f70ae520e111909a74dbfcd11613f14810d9..da1b937f104e2f2b031c6ca041d973c6b8d3fb1a 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "k5-thread.h"
 #include "k5-platform.h"
+#include "k5-plugin.h"
 #include "com_err.h"
 #include "profile.h"
 
@@ -79,6 +80,14 @@ typedef struct _prf_file_t *prf_file_t;
 #define PROFILE_FILE_DIRTY             0x0002
 #define PROFILE_FILE_SHARED            0x0004
 
+struct _prf_lib_handle_t {
+       k5_mutex_t lock;
+       int refcount;
+       struct plugin_file_handle *plugin_handle;
+};
+
+typedef struct _prf_lib_handle_t *prf_lib_handle_t;
+
 /*
  * This structure defines the high-level, user visible profile_t
  * object, which is used as a handle by users who need to query some
@@ -91,6 +100,7 @@ struct _profile_t {
        /* If non-null, use vtable operations instead of native ones. */
        struct profile_vtable *vt;
        void            *cbdata;
+       prf_lib_handle_t lib_handle;
 };
 
 /*
@@ -111,7 +121,7 @@ struct _profile_t {
 /* profile_parse.c */
 
 errcode_t profile_parse_file
-       (FILE *f, struct profile_node **root);
+       (FILE *f, struct profile_node **root, char **ret_modspec);
 
 errcode_t profile_write_tree_file
        (struct profile_node *root, FILE *dstfile);
@@ -201,14 +211,15 @@ errcode_t profile_rename_node
 errcode_t KRB5_CALLCONV profile_copy (profile_t, profile_t *);
 
 errcode_t profile_open_file
-       (const_profile_filespec_t file, prf_file_t *ret_prof);
+       (const_profile_filespec_t file, prf_file_t *ret_prof,
+        char **ret_modspec);
 
-#define profile_update_file(P) profile_update_file_data((P)->data)
+#define profile_update_file(P, M) profile_update_file_data((P)->data, M)
 errcode_t profile_update_file_data
-       (prf_data_t profile);
-#define profile_update_file_locked(P) profile_update_file_data_locked((P)->data)
+       (prf_data_t profile, char **ret_modspec);
+#define profile_update_file_locked(P, M) profile_update_file_data_locked((P)->data, M)
 errcode_t profile_update_file_data_locked
-       (prf_data_t data);
+       (prf_data_t data, char **ret_modspec);
 
 #define profile_flush_file(P) (((P) && (P)->magic == PROF_MAGIC_FILE) ? profile_flush_file_data((P)->data) : PROF_MAGIC_FILE)
 errcode_t profile_flush_file_data
index 8fab90b2905107f0cbd2c6518a57c712dcea4b8a..77c0adbd6122ef1e518b81e1678818a878fe1339 100644 (file)
@@ -26,7 +26,8 @@ struct parse_state {
     struct profile_node *current_section;
 };
 
-static errcode_t parse_file(FILE *f, struct parse_state *state);
+static errcode_t parse_file(FILE *f, struct parse_state *state,
+                            char **ret_modspec);
 
 static char *skip_over_blanks(char *cp)
 {
@@ -216,7 +217,7 @@ static errcode_t parse_include_file(char *filename, struct parse_state *state)
     fp = fopen(filename, "r");
     if (fp == NULL)
         return PROF_FAIL_INCLUDE_FILE;
-    retval = parse_file(fp, &incstate);
+    retval = parse_file(fp, &incstate, NULL);
     fclose(fp);
     return retval;
 }
@@ -302,7 +303,8 @@ cleanup:
 #endif /* not _WIN32 */
 }
 
-static errcode_t parse_line(char *line, struct parse_state *state)
+static errcode_t parse_line(char *line, struct parse_state *state,
+                            char **ret_modspec)
 {
     char    *cp;
 
@@ -318,6 +320,23 @@ static errcode_t parse_line(char *line, struct parse_state *state)
     }
     switch (state->state) {
     case STATE_INIT_COMMENT:
+        if (strncmp(line, "module", 6) == 0 && isspace(line[6])) {
+            /*
+             * If we are expecting a module declaration, fill in *ret_modspec
+             * and return PROF_MODULE, which will cause parsing to abort and
+             * the module to be loaded instead.  If we aren't expecting a
+             * module declaration, return PROF_MODULE without filling in
+             * *ret_modspec, which will be treated as an ordinary error.
+             */
+            if (ret_modspec) {
+                cp = skip_over_blanks(line + 6);
+                strip_line(cp);
+                *ret_modspec = strdup(cp);
+                if (!*ret_modspec)
+                    return ENOMEM;
+            }
+            return PROF_MODULE;
+        }
         if (line[0] != '[')
             return 0;
         state->state = STATE_STD_LINE;
@@ -332,7 +351,8 @@ static errcode_t parse_line(char *line, struct parse_state *state)
     return 0;
 }
 
-static errcode_t parse_file(FILE *f, struct parse_state *state)
+static errcode_t parse_file(FILE *f, struct parse_state *state,
+                            char **ret_modspec)
 {
 #define BUF_SIZE        2048
     char *bptr;
@@ -346,7 +366,7 @@ static errcode_t parse_file(FILE *f, struct parse_state *state)
         if (fgets(bptr, BUF_SIZE, f) == NULL)
             break;
 #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
-        retval = parse_line(bptr, state);
+        retval = parse_line(bptr, state, ret_modspec);
         if (retval) {
             free (bptr);
             return retval;
@@ -390,7 +410,7 @@ static errcode_t parse_file(FILE *f, struct parse_state *state)
 
                 /* parse_line modifies contents of p */
                 newp = p + strlen (p) + 1;
-                retval = parse_line (p, state);
+                retval = parse_line (p, state, ret_modspec);
                 if (retval) {
                     free (bptr);
                     return retval;
@@ -406,7 +426,8 @@ static errcode_t parse_file(FILE *f, struct parse_state *state)
     return 0;
 }
 
-errcode_t profile_parse_file(FILE *f, struct profile_node **root)
+errcode_t profile_parse_file(FILE *f, struct profile_node **root,
+                             char **ret_modspec)
 {
     struct parse_state state;
     errcode_t retval;
@@ -421,7 +442,7 @@ errcode_t profile_parse_file(FILE *f, struct profile_node **root)
     if (retval)
         return retval;
 
-    retval = parse_file(f, &state);
+    retval = parse_file(f, &state, ret_modspec);
     if (retval) {
         profile_free_node(state.root_section);
         return retval;
index 369bc938d8092dfba5c78b2d1f9cead6c08f78d4..52e5b0519ca7d44602b4738f90ee88b03ce3e90a 100644 (file)
@@ -69,7 +69,7 @@ static errcode_t rw_setup(profile_t profile)
     }
 
     profile_unlock_global();
-    retval = profile_update_file(file);
+    retval = profile_update_file(file, NULL);
 
     return retval;
 }
index 0064ad32a63bbc81df3829a28956dba19a6bd2b2..4543441883e17cb9a4082e89b92efa71197c05b9 100644 (file)
@@ -498,7 +498,7 @@ get_new_file:
                 *ret_value =0;
             return 0;
         }
-        if ((retval = profile_update_file_locked(iter->file))) {
+        if ((retval = profile_update_file_locked(iter->file, NULL))) {
             k5_mutex_unlock(&iter->file->data->lock);
             if (retval == ENOENT || retval == EACCES) {
                 /* XXX memory leak? */
index 0cfc7d1b4165546fdba44b44869766ac1b9832ff..45ad55430b827d5f6cc0dd0270eb11c877abd25c 100644 (file)
@@ -23,6 +23,9 @@
 
 typedef struct _profile_t *profile_t;
 
+/* Used by profile_init_flags(). */
+#define PROFILE_INIT_ALLOW_MODULE       0x0001  /* Allow module declaration */
+
 /*
  * Used by the profile iterator in prof_get.c
  */
@@ -42,6 +45,9 @@ typedef const char * const_profile_filespec_list_t;   /* list of : separated paths
 long KRB5_CALLCONV profile_init
        (const_profile_filespec_t *files, profile_t *ret_profile);
 
+long KRB5_CALLCONV profile_init_flags
+       (const_profile_filespec_t *files, int flags, profile_t *ret_profile);
+
 long KRB5_CALLCONV profile_init_path
        (const_profile_filespec_list_t filelist, profile_t *ret_profile);
 
@@ -234,7 +240,8 @@ typedef long
 (*profile_flush_fn)(void *cbdata);
 
 struct profile_vtable {
-    int minor_ver;              /* Set this to 1. */
+    int minor_ver;              /* Set to structure minor version (currently 1)
+                                * if calling profile_init_vtable. */
 
     /* Methods needed for a basic read-only non-iterable profile (cleanup is
      * optional). */
@@ -268,6 +275,19 @@ struct profile_vtable {
 long KRB5_CALLCONV profile_init_vtable
        (struct profile_vtable *vtable, void *cbdata, profile_t *ret_profile);
 
+/*
+ * Dynamically loadable profile modules should define a function named
+ * "profile_module_init" matching the following signature.  The function should
+ * initialize the methods of the provided vtable structure, stopping at the
+ * field corresponding to vtable->minor_ver.  Do not change the value of
+ * vtable->minor_ver.  Unimplemented methods can be left uninitialized.  The
+ * function should supply a callback data pointer in *cb_ret; this pointer can
+ * be cleaned up via the vtable cleanup method.
+ */
+typedef long
+(*profile_module_init_fn)(const char *residual, struct profile_vtable *vtable,
+                         void **cb_ret);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/src/util/profile/test_load.c b/src/util/profile/test_load.c
new file mode 100644 (file)
index 0000000..cb870ef
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/profile/test_load.c - Test harness for loadable profile modules */
+/*
+ * 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.
+ */
+
+#include "k5-platform.h"
+#include "profile.h"
+#include "prof_int.h"
+
+int
+main()
+{
+    profile_t pr, pr2;
+    const char *files[] = { "./modtest.conf", NULL };
+    char **values;
+
+    assert(profile_init_flags(files, PROFILE_INIT_ALLOW_MODULE, &pr) == 0);
+    assert(profile_copy(pr, &pr2) == 0);
+    assert(profile_get_values(pr, NULL, &values) == 0);
+    assert(strcmp(values[0], "teststring") == 0);
+    assert(strcmp(values[1], "0") == 0);
+    profile_free_list(values);
+    assert(profile_get_values(pr2, NULL, &values) == 0);
+    assert(strcmp(values[0], "teststring") == 0);
+    assert(strcmp(values[1], "1") == 0);
+    profile_release(pr);
+    profile_abandon(pr2);
+    profile_free_list(values);
+    return 0;
+}
index 2fcd046e8561014cb9b31549097a6935823c374b..9f2631e949b19942ac33ce564f4cac482b3693b5 100644 (file)
@@ -31,7 +31,7 @@ int main(argc, argv)
         exit(1);
     }
 
-    retval = profile_parse_file(f, &root);
+    retval = profile_parse_file(f, &root, NULL);
     if (retval) {
         printf("profile_parse_file error %s\n",
                error_message((errcode_t) retval));
diff --git a/src/util/profile/testmod/Makefile.in b/src/util/profile/testmod/Makefile.in
new file mode 100644 (file)
index 0000000..fdd3663
--- /dev/null
@@ -0,0 +1,22 @@
+mydir=util$(S)profile$(S)testmod
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+DEFS=@DEFS@
+
+LOCALINCLUDES = -I.. -I$(srcdir)/..
+
+LIBBASE=proftest
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+
+STOBJLISTS=OBJS.ST
+STLIBOBJS=testmod_main.o
+
+SRCS=$(srcdir)/testmod_main.c
+
+check-unix:: proftest$(DYNOBJEXT)
+clean-unix:: clean-libs clean-libobjs
+
+@libnover_frag@
+@libobj_frag@
diff --git a/src/util/profile/testmod/deps b/src/util/profile/testmod/deps
new file mode 100644 (file)
index 0000000..c558307
--- /dev/null
@@ -0,0 +1,7 @@
+# 
+# Generated makefile dependencies follow.
+#
+testmod_main.so testmod_main.po $(OUTPRE)testmod_main.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-thread.h testmod_main.c
diff --git a/src/util/profile/testmod/proftest.exports b/src/util/profile/testmod/proftest.exports
new file mode 100644 (file)
index 0000000..b1b975f
--- /dev/null
@@ -0,0 +1 @@
+profile_module_init
diff --git a/src/util/profile/testmod/testmod_main.c b/src/util/profile/testmod/testmod_main.c
new file mode 100644 (file)
index 0000000..c58ac04
--- /dev/null
@@ -0,0 +1,104 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/profile/proftest/test.c - Test dynamic profile module */
+/*
+ * 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 file implements a very simple profile module which just returns the
+ * residual string and the number of copies in response to any query.  The full
+ * range of vtable profile operations is tested elsewhere.
+ */
+
+#include "k5-platform.h"
+#include "profile.h"
+
+struct data {
+    char *residual;
+    int gen;
+};
+
+static long
+get_values(void *cbdata, const char *const *names, char ***ret_values)
+{
+    struct data *d = cbdata;
+
+    *ret_values = calloc(3, sizeof(*ret_values));
+    (*ret_values)[0] = strdup(d->residual);
+    asprintf(&(*ret_values)[1], "%d", d->gen);
+    (*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 void
+cleanup(void *cbdata)
+{
+    struct data *d = cbdata;
+
+    free(d->residual);
+    free(d);
+}
+
+static long
+copy(void *cbdata, void **ret_cbdata)
+{
+    struct data *old_data = cbdata, *new_data;
+
+    new_data = malloc(sizeof(*new_data));
+    new_data->residual = strdup(old_data->residual);
+    new_data->gen = old_data->gen + 1;
+    *ret_cbdata = new_data;
+    return 0;
+}
+
+long
+profile_module_init(const char *residual, struct profile_vtable *vtable,
+                   void **cb_ret);
+
+long
+profile_module_init(const char *residual, struct profile_vtable *vtable,
+                   void **cb_ret)
+{
+    struct data *d;
+
+    d = malloc(sizeof(*d));
+    d->residual = strdup(residual);
+    d->gen = 0;
+    *cb_ret = d;
+
+    vtable->get_values = get_values;
+    vtable->free_values = free_values;
+    vtable->cleanup = cleanup;
+    vtable->copy = copy;
+    return 0;
+}