From 6ea59e4695628ef53bf18ce2e837c2edc4879d0f Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Wed, 20 Jul 2011 19:14:28 +0000 Subject: [PATCH] Add support for loadable profile modules ticket: 6929 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25025 dc483132-0cff-0310-8789-dd5450dbe970 --- src/configure.in | 2 +- src/lib/krb5/os/init_os_ctx.c | 7 +- src/util/profile/Makefile.in | 22 ++- src/util/profile/deps | 48 +++-- src/util/profile/libprofile.exports | 2 + src/util/profile/prof_err.et | 4 + src/util/profile/prof_file.c | 14 +- src/util/profile/prof_init.c | 219 +++++++++++++++++++--- src/util/profile/prof_int.h | 23 ++- src/util/profile/prof_parse.c | 37 +++- src/util/profile/prof_set.c | 2 +- src/util/profile/prof_tree.c | 2 +- src/util/profile/profile.hin | 22 ++- src/util/profile/test_load.c | 51 +++++ src/util/profile/test_parse.c | 2 +- src/util/profile/testmod/Makefile.in | 22 +++ src/util/profile/testmod/deps | 7 + src/util/profile/testmod/proftest.exports | 1 + src/util/profile/testmod/testmod_main.c | 104 ++++++++++ 19 files changed, 511 insertions(+), 80 deletions(-) create mode 100644 src/util/profile/test_load.c create mode 100644 src/util/profile/testmod/Makefile.in create mode 100644 src/util/profile/testmod/deps create mode 100644 src/util/profile/testmod/proftest.exports create mode 100644 src/util/profile/testmod/testmod_main.c diff --git a/src/configure.in b/src/configure.in index 8789de75b..4ed9e31e3 100644 --- a/src/configure.in +++ b/src/configure.in @@ -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 diff --git a/src/lib/krb5/os/init_os_ctx.c b/src/lib/krb5/os/init_os_ctx.c index 9387cf872..98b8ae230 100644 --- a/src/lib/krb5/os/init_os_ctx.c +++ b/src/lib/krb5/os/init_os_ctx.c @@ -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; diff --git a/src/util/profile/Makefile.in b/src/util/profile/Makefile.in index e5c973fd3..e7d84aa23 100644 --- a/src/util/profile/Makefile.in +++ b/src/util/profile/Makefile.in @@ -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) diff --git a/src/util/profile/deps b/src/util/profile/deps index 837bea7b8..f5fcdbfdd 100644 --- a/src/util/profile/deps +++ b/src/util/profile/deps @@ -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 diff --git a/src/util/profile/libprofile.exports b/src/util/profile/libprofile.exports index ba34354ec..3f02b4f4f 100644 --- a/src/util/profile/libprofile.exports +++ b/src/util/profile/libprofile.exports @@ -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 diff --git a/src/util/profile/prof_err.et b/src/util/profile/prof_err.et index 41aec89c6..d5850d00e 100644 --- a/src/util/profile/prof_err.et +++ b/src/util/profile/prof_err.et @@ -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 diff --git a/src/util/profile/prof_file.c b/src/util/profile/prof_file.c index bf25b2e4d..988afacec 100644 --- a/src/util/profile/prof_file.c +++ b/src/util/profile/prof_file.c @@ -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; } diff --git a/src/util/profile/prof_init.c b/src/util/profile/prof_init.c index 4c6b0d2dd..959598e42 100644 --- a/src/util/profile/prof_init.c +++ b/src/util/profile/prof_init.c @@ -21,9 +21,11 @@ #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; diff --git a/src/util/profile/prof_int.h b/src/util/profile/prof_int.h index 1795f70ae..da1b937f1 100644 --- a/src/util/profile/prof_int.h +++ b/src/util/profile/prof_int.h @@ -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 diff --git a/src/util/profile/prof_parse.c b/src/util/profile/prof_parse.c index 8fab90b29..77c0adbd6 100644 --- a/src/util/profile/prof_parse.c +++ b/src/util/profile/prof_parse.c @@ -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; diff --git a/src/util/profile/prof_set.c b/src/util/profile/prof_set.c index 369bc938d..52e5b0519 100644 --- a/src/util/profile/prof_set.c +++ b/src/util/profile/prof_set.c @@ -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; } diff --git a/src/util/profile/prof_tree.c b/src/util/profile/prof_tree.c index 0064ad32a..454344188 100644 --- a/src/util/profile/prof_tree.c +++ b/src/util/profile/prof_tree.c @@ -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? */ diff --git a/src/util/profile/profile.hin b/src/util/profile/profile.hin index 0cfc7d1b4..45ad55430 100644 --- a/src/util/profile/profile.hin +++ b/src/util/profile/profile.hin @@ -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 index 000000000..cb870eff9 --- /dev/null +++ b/src/util/profile/test_load.c @@ -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; +} diff --git a/src/util/profile/test_parse.c b/src/util/profile/test_parse.c index 2fcd046e8..9f2631e94 100644 --- a/src/util/profile/test_parse.c +++ b/src/util/profile/test_parse.c @@ -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 index 000000000..fdd366328 --- /dev/null +++ b/src/util/profile/testmod/Makefile.in @@ -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 index 000000000..c5583074f --- /dev/null +++ b/src/util/profile/testmod/deps @@ -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 index 000000000..b1b975ffa --- /dev/null +++ b/src/util/profile/testmod/proftest.exports @@ -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 index 000000000..c58ac0487 --- /dev/null +++ b/src/util/profile/testmod/testmod_main.c @@ -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; +} -- 2.26.2