diff options
Diffstat (limited to 'src/util/profile/prof_init.c')
| -rw-r--r-- | src/util/profile/prof_init.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/src/util/profile/prof_init.c b/src/util/profile/prof_init.c new file mode 100644 index 000000000000..cc92248f42ac --- /dev/null +++ b/src/util/profile/prof_init.c @@ -0,0 +1,666 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * prof_init.c --- routines that manipulate the user-visible profile_t + * object. + */ + +#include "prof_int.h" + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <errno.h> + +/* 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; + + /* 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->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; + char *path, *fullpath, *residual; + errcode_t ret; + + *ret_path = *ret_residual = NULL; + + /* Find the separator, skipping a Windows drive letter if present. */ + p = (*modspec != '\0' && modspec[1] == ':') ? modspec + 2 : modspec; + p = strchr(p, ':'); + if (p == NULL) + return PROF_MODULE_SYNTAX; + + /* Copy the path. */ + path = malloc(p - modspec + 1); + if (path == NULL) + return ENOMEM; + memcpy(path, modspec, p - modspec); + path[p - modspec] = '\0'; + + /* Compose the path with LIBDIR if it's not absolute. */ + ret = k5_path_join(LIBDIR, path, &fullpath); + free(path); + if (ret) + return ret; + + residual = strdup(p + 1); + if (residual == NULL) { + free(fullpath); + return ENOMEM; + } + + *ret_path = fullpath; + *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); + k5_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_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) + return ENOMEM; + memset(profile, 0, sizeof(struct _profile_t)); + profile->magic = PROF_MAGIC_PROFILE; + + /* + * If the filenames list is not specified or empty, return an empty + * profile. + */ + if ( files && !PROFILE_LAST_FILESPEC(*files) ) { + for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) { + /* 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; + } + /* If we can't read this file, remember it but keep going. */ + if (retval == EACCES || retval == EPERM) { + access_retval = retval; + continue; + } + if (retval) { + profile_release(profile); + return retval; + } + if (last) + last->next = new_file; + else + profile->first_file = new_file; + last = new_file; + } + /* + * If last is still null after the loop, then all the files were + * missing or unreadable, so return the appropriate error. + */ + if (!last) { + profile_release(profile); + return access_retval ? access_retval : ENOENT; + } + } + + *ret_profile = 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) { + k5_mutex_lock(&profile->lib_handle->lock); + 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; \ + PTYPE cll_ptr = (START); \ + while (cll_ptr != NULL) { \ + cll_counter++; \ + cll_ptr = cll_ptr->FIELD; \ + } \ + (COUNT) = cll_counter; \ + } + +errcode_t KRB5_CALLCONV +profile_copy(profile_t old_profile, profile_t *new_profile) +{ + size_t size, i; + const_profile_filespec_t *files; + prf_file_t file; + errcode_t err; + + 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. */ + COUNT_LINKED_LIST (size, prf_file_t, old_profile->first_file, next); + files = malloc ((size+1) * sizeof(*files)); + if (files == NULL) + return ENOMEM; + for (i = 0, file = old_profile->first_file; i < size; i++, file = file->next) + files[i] = file->data->filespec; + files[size] = NULL; + err = profile_init (files, new_profile); + free (files); + return err; +} + +errcode_t KRB5_CALLCONV +profile_init_path(const_profile_filespec_list_t filepath, + profile_t *ret_profile) +{ + unsigned int n_entries; + int i; + unsigned int ent_len; + const char *s, *t; + profile_filespec_t *filenames; + errcode_t retval; + + /* count the distinct filename components */ + for(s = filepath, n_entries = 1; *s; s++) { + if (*s == ':') + n_entries++; + } + + /* the array is NULL terminated */ + filenames = (profile_filespec_t*) malloc((n_entries+1) * sizeof(char*)); + if (filenames == 0) + return ENOMEM; + + /* measure, copy, and skip each one */ + for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) { + ent_len = (unsigned int) (t-s); + filenames[i] = (char*) malloc(ent_len + 1); + if (filenames[i] == 0) { + /* if malloc fails, free the ones that worked */ + while(--i >= 0) free(filenames[i]); + free(filenames); + return ENOMEM; + } + strncpy(filenames[i], s, ent_len); + filenames[i][ent_len] = 0; + if (*t == 0) { + i++; + break; + } + } + /* cap the array */ + filenames[i] = 0; + + 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]); + free(filenames); + + return retval; +} + +errcode_t KRB5_CALLCONV +profile_is_writable(profile_t profile, int *writable) +{ + if (!profile || profile->magic != PROF_MAGIC_PROFILE) + return PROF_MAGIC_PROFILE; + + 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); + + return 0; +} + +errcode_t KRB5_CALLCONV +profile_is_modified(profile_t profile, int *modified) +{ + if (!profile || profile->magic != PROF_MAGIC_PROFILE) + return PROF_MAGIC_PROFILE; + + 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); + + return 0; +} + +errcode_t KRB5_CALLCONV +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); + + return 0; +} + +errcode_t KRB5_CALLCONV +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); + + return 0; +} + +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); +} + +void KRB5_CALLCONV +profile_free_buffer(profile_t profile, char *buf) +{ + free(buf); +} + +void KRB5_CALLCONV +profile_abandon(profile_t profile) +{ + prf_file_t p, next; + + if (!profile || profile->magic != PROF_MAGIC_PROFILE) + return; + + 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. */ + k5_mutex_lock(&profile->lib_handle->lock); + if (--profile->lib_handle->refcount == 0) { + krb5int_close_plugin(profile->lib_handle->plugin_handle); + k5_mutex_unlock(&profile->lib_handle->lock); + k5_mutex_destroy(&profile->lib_handle->lock); + free(profile->lib_handle); + } else + k5_mutex_unlock(&profile->lib_handle->lock); + } + free(profile->vt); + } else { + for (p = profile->first_file; p; p = next) { + next = p->next; + profile_free_file(p); + } + } + profile->magic = 0; + free(profile); +} + +void KRB5_CALLCONV +profile_release(profile_t profile) +{ + prf_file_t p, next; + + if (!profile || profile->magic != PROF_MAGIC_PROFILE) + return; + + if (profile->vt) { + /* Flush the profile and then delegate to profile_abandon. */ + if (profile->vt->flush) + profile->vt->flush(profile->cbdata); + profile_abandon(profile); + return; + } else { + for (p = profile->first_file; p; p = next) { + next = p->next; + profile_close_file(p); + } + } + profile->magic = 0; + free(profile); +} + +/* + * Here begins the profile serialization functions. + */ +errcode_t profile_ser_size(const char *unused, profile_t profile, + size_t *sizep) +{ + size_t required; + prf_file_t pfp; + + required = 3*sizeof(int32_t); + for (pfp = profile->first_file; pfp; pfp = pfp->next) { + required += sizeof(int32_t); + required += strlen(pfp->data->filespec); + } + *sizep += required; + return 0; +} + +static void pack_int32(int32_t oval, unsigned char **bufpp, size_t *remainp) +{ + store_32_be(oval, *bufpp); + *bufpp += sizeof(int32_t); + *remainp -= sizeof(int32_t); +} + +errcode_t profile_ser_externalize(const char *unused, profile_t profile, + unsigned char **bufpp, size_t *remainp) +{ + errcode_t retval; + size_t required; + unsigned char *bp; + size_t remain; + prf_file_t pfp; + int32_t fcount, slen; + + required = 0; + bp = *bufpp; + remain = *remainp; + retval = EINVAL; + if (profile) { + retval = ENOMEM; + (void) profile_ser_size(unused, profile, &required); + if (required <= remain) { + fcount = 0; + for (pfp = profile->first_file; pfp; pfp = pfp->next) + fcount++; + pack_int32(PROF_MAGIC_PROFILE, &bp, &remain); + pack_int32(fcount, &bp, &remain); + for (pfp = profile->first_file; pfp; pfp = pfp->next) { + slen = (int32_t) strlen(pfp->data->filespec); + pack_int32(slen, &bp, &remain); + if (slen) { + memcpy(bp, pfp->data->filespec, (size_t) slen); + bp += slen; + remain -= (size_t) slen; + } + } + pack_int32(PROF_MAGIC_PROFILE, &bp, &remain); + retval = 0; + *bufpp = bp; + *remainp = remain; + } + } + return(retval); +} + +static int unpack_int32(int32_t *intp, unsigned char **bufpp, + size_t *remainp) +{ + if (*remainp >= sizeof(int32_t)) { + *intp = load_32_be(*bufpp); + *bufpp += sizeof(int32_t); + *remainp -= sizeof(int32_t); + return 0; + } + else + return 1; +} + +errcode_t profile_ser_internalize(const char *unused, profile_t *profilep, + unsigned char **bufpp, size_t *remainp) +{ + errcode_t retval; + unsigned char *bp; + size_t remain; + int i; + int32_t fcount, tmp; + profile_filespec_t *flist = 0; + + bp = *bufpp; + remain = *remainp; + fcount = 0; + + if (remain >= 12) + (void) unpack_int32(&tmp, &bp, &remain); + else + tmp = 0; + + if (tmp != PROF_MAGIC_PROFILE) { + retval = EINVAL; + goto cleanup; + } + + (void) unpack_int32(&fcount, &bp, &remain); + retval = ENOMEM; + + flist = (profile_filespec_t *) malloc(sizeof(profile_filespec_t) * (size_t) (fcount + 1)); + if (!flist) + goto cleanup; + + memset(flist, 0, sizeof(char *) * (size_t) (fcount+1)); + for (i=0; i<fcount; i++) { + if (!unpack_int32(&tmp, &bp, &remain)) { + flist[i] = (char *) malloc((size_t) (tmp+1)); + if (!flist[i]) + goto cleanup; + memcpy(flist[i], bp, (size_t) tmp); + flist[i][tmp] = '\0'; + bp += tmp; + remain -= (size_t) tmp; + } + } + + if (unpack_int32(&tmp, &bp, &remain) || + (tmp != PROF_MAGIC_PROFILE)) { + retval = EINVAL; + goto cleanup; + } + + if ((retval = profile_init((const_profile_filespec_t *) flist, + profilep))) + goto cleanup; + + *bufpp = bp; + *remainp = remain; + +cleanup: + if (flist) { + for (i=0; i<fcount; i++) { + if (flist[i]) + free(flist[i]); + } + free(flist); + } + return(retval); +} |
