2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * util/support/plugins.c
2N/A *
2N/A * Copyright 2006, 2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A *
2N/A * Plugin module support, and shims around dlopen/whatever.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "k5-plugin.h"
2N/A#if USE_DLOPEN
2N/A#include <dlfcn.h>
2N/A#endif
2N/A#include <stdio.h>
2N/A#include <sys/types.h>
2N/A#ifdef HAVE_SYS_STAT_H
2N/A#include <sys/stat.h>
2N/A#endif
2N/A#ifdef HAVE_SYS_PARAM_H
2N/A#include <sys/param.h>
2N/A#endif
2N/A#include <errno.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#ifdef HAVE_UNISTD_H
2N/A#include <unistd.h>
2N/A#endif
2N/A
2N/A#include "k5-platform.h"
2N/A
2N/A#if USE_DLOPEN && USE_CFBUNDLE
2N/A#include <CoreFoundation/CoreFoundation.h>
2N/A
2N/A/* Currently CoreFoundation only exists on the Mac so we just use
2N/A * pthreads directly to avoid creating empty function calls on other
2N/A * platforms. If a thread initializer ever gets created in the common
2N/A * plugin code, move this there */
2N/Astatic pthread_mutex_t krb5int_bundle_mutex = PTHREAD_MUTEX_INITIALIZER;
2N/A#endif
2N/A
2N/A#include <stdarg.h>
2N/Astatic void Tprintf (const char *fmt, ...)
2N/A{
2N/A#ifdef DEBUG
2N/A va_list va;
2N/A va_start (va, fmt);
2N/A vfprintf (stderr, fmt, va);
2N/A va_end (va);
2N/A#endif
2N/A}
2N/A
2N/Astruct plugin_file_handle {
2N/A#if USE_DLOPEN
2N/A void *dlhandle;
2N/A#endif
2N/A#ifdef _WIN32
2N/A HMODULE hinstPlugin;
2N/A#endif
2N/A#if !defined (USE_DLOPEN) && !defined (_WIN32)
2N/A char dummy;
2N/A#endif
2N/A};
2N/A
2N/A#ifdef _WIN32
2N/Astruct dirent {
2N/A long d_ino; /* inode (always 1 in WIN32) */
2N/A off_t d_off; /* offset to this dirent */
2N/A unsigned short d_reclen; /* length of d_name */
2N/A char d_name[_MAX_FNAME+1]; /* filename (null terminated) */
2N/A};
2N/A
2N/Atypedef struct {
2N/A long handle; /* _findfirst/_findnext handle */
2N/A short offset; /* offset into directory */
2N/A short finished; /* 1 if there are not more files */
2N/A struct _finddata_t fileinfo;/* from _findfirst/_findnext */
2N/A char *dir; /* the dir we are reading */
2N/A struct dirent dent; /* the dirent to return */
2N/A} DIR;
2N/A
2N/ADIR * opendir(const char *dir)
2N/A{
2N/A DIR *dp;
2N/A char *filespec;
2N/A long handle;
2N/A int index;
2N/A
2N/A filespec = malloc(strlen(dir) + 2 + 1);
2N/A strcpy(filespec, dir);
2N/A index = strlen(filespec) - 1;
2N/A if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
2N/A filespec[index] = '\0';
2N/A strcat(filespec, "/*");
2N/A
2N/A dp = (DIR *)malloc(sizeof(DIR));
2N/A dp->offset = 0;
2N/A dp->finished = 0;
2N/A dp->dir = strdup(dir);
2N/A
2N/A if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) {
2N/A if (errno == ENOENT)
2N/A dp->finished = 1;
2N/A else {
2N/A free(filespec);
2N/A free(dp->dir);
2N/A free(dp);
2N/A return NULL;
2N/A }
2N/A }
2N/A
2N/A dp->handle = handle;
2N/A free(filespec);
2N/A
2N/A return dp;
2N/A}
2N/A
2N/Astruct dirent * readdir(DIR *dp)
2N/A{
2N/A if (!dp || dp->finished) return NULL;
2N/A
2N/A if (dp->offset != 0) {
2N/A if (_findnext(dp->handle, &(dp->fileinfo)) < 0) {
2N/A dp->finished = 1;
2N/A return NULL;
2N/A }
2N/A }
2N/A dp->offset++;
2N/A
2N/A strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
2N/A dp->dent.d_ino = 1;
2N/A dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
2N/A dp->dent.d_off = dp->offset;
2N/A
2N/A return &(dp->dent);
2N/A}
2N/A
2N/Aint closedir(DIR *dp)
2N/A{
2N/A if (!dp) return 0;
2N/A _findclose(dp->handle);
2N/A if (dp->dir) free(dp->dir);
2N/A if (dp) free(dp);
2N/A
2N/A return 0;
2N/A}
2N/A#endif
2N/A
2N/Along KRB5_CALLCONV
2N/Akrb5int_open_plugin (const char *filepath, struct plugin_file_handle **h, struct errinfo *ep)
2N/A{
2N/A long err = 0;
2N/A struct stat statbuf;
2N/A struct plugin_file_handle *htmp = NULL;
2N/A int got_plugin = 0;
2N/A
2N/A if (!err) {
2N/A if (stat (filepath, &statbuf) < 0) {
2N/A Tprintf ("stat(%s): %s\n", filepath, strerror (errno));
2N/A err = errno;
2N/A }
2N/A }
2N/A
2N/A if (!err) {
2N/A htmp = calloc (1, sizeof (*htmp)); /* calloc initializes ptrs to NULL */
2N/A if (htmp == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A#if USE_DLOPEN
2N/A if (!err && ((statbuf.st_mode & S_IFMT) == S_IFREG
2N/A#if USE_CFBUNDLE
2N/A || (statbuf.st_mode & S_IFMT) == S_IFDIR
2N/A#endif /* USE_CFBUNDLE */
2N/A )) {
2N/A void *handle = NULL;
2N/A
2N/A#if USE_CFBUNDLE
2N/A char executablepath[MAXPATHLEN];
2N/A
2N/A if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A int lock_err = 0;
2N/A CFStringRef pluginString = NULL;
2N/A CFURLRef pluginURL = NULL;
2N/A CFBundleRef pluginBundle = NULL;
2N/A CFURLRef executableURL = NULL;
2N/A
2N/A /* Lock around CoreFoundation calls since objects are refcounted
2N/A * and the refcounts are not thread-safe. Using pthreads directly
2N/A * because this code is Mac-specific */
2N/A lock_err = pthread_mutex_lock(&krb5int_bundle_mutex);
2N/A if (lock_err) { err = lock_err; }
2N/A
2N/A if (!err) {
2N/A pluginString = CFStringCreateWithCString (kCFAllocatorDefault,
2N/A filepath,
2N/A kCFStringEncodingASCII);
2N/A if (pluginString == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err) {
2N/A pluginURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault,
2N/A pluginString,
2N/A kCFURLPOSIXPathStyle,
2N/A true);
2N/A if (pluginURL == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err) {
2N/A pluginBundle = CFBundleCreate (kCFAllocatorDefault, pluginURL);
2N/A if (pluginBundle == NULL) { err = ENOENT; } /* XXX need better error */
2N/A }
2N/A
2N/A if (!err) {
2N/A executableURL = CFBundleCopyExecutableURL (pluginBundle);
2N/A if (executableURL == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err) {
2N/A if (!CFURLGetFileSystemRepresentation (executableURL,
2N/A true, /* absolute */
2N/A (UInt8 *)executablepath,
2N/A sizeof (executablepath))) {
2N/A err = ENOMEM;
2N/A }
2N/A }
2N/A
2N/A if (!err) {
2N/A /* override the path the caller passed in */
2N/A filepath = executablepath;
2N/A }
2N/A
2N/A if (executableURL != NULL) { CFRelease (executableURL); }
2N/A if (pluginBundle != NULL) { CFRelease (pluginBundle); }
2N/A if (pluginURL != NULL) { CFRelease (pluginURL); }
2N/A if (pluginString != NULL) { CFRelease (pluginString); }
2N/A
2N/A /* unlock after CFRelease calls since they modify refcounts */
2N/A if (!lock_err) { pthread_mutex_unlock (&krb5int_bundle_mutex); }
2N/A }
2N/A#endif /* USE_CFBUNDLE */
2N/A
2N/A#ifdef RTLD_GROUP
2N/A#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | RTLD_GROUP)
2N/A#else
2N/A#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL)
2N/A#endif
2N/A if (!err) {
2N/A handle = dlopen(filepath, PLUGIN_DLOPEN_FLAGS);
2N/A if (handle == NULL) {
2N/A const char *e = dlerror();
2N/A if (e == NULL)
2N/A e = "unknown failure";
2N/A Tprintf ("dlopen(%s): %s\n", filepath, e);
2N/A err = ENOENT; /* XXX */
2N/A krb5int_set_error (ep, err, "%s", e);
2N/A }
2N/A }
2N/A
2N/A if (!err) {
2N/A got_plugin = 1;
2N/A htmp->dlhandle = handle;
2N/A handle = NULL;
2N/A }
2N/A
2N/A if (handle != NULL) { dlclose (handle); }
2N/A }
2N/A#endif /* USE_DLOPEN */
2N/A
2N/A#ifdef _WIN32
2N/A if (!err && (statbuf.st_mode & S_IFMT) == S_IFREG) {
2N/A HMODULE handle = NULL;
2N/A
2N/A handle = LoadLibrary(filepath);
2N/A if (handle == NULL) {
2N/A Tprintf ("Unable to load dll: %s\n", filepath);
2N/A err = ENOENT; /* XXX */
2N/A krb5int_set_error (ep, err, "%s", "unable to load dll");
2N/A }
2N/A
2N/A if (!err) {
2N/A got_plugin = 1;
2N/A htmp->hinstPlugin = handle;
2N/A handle = NULL;
2N/A }
2N/A
2N/A if (handle != NULL)
2N/A FreeLibrary(handle);
2N/A }
2N/A#endif
2N/A
2N/A if (!err && !got_plugin) {
2N/A err = ENOENT; /* no plugin or no way to load plugins */
2N/A }
2N/A
2N/A if (!err) {
2N/A *h = htmp;
2N/A htmp = NULL; /* h takes ownership */
2N/A }
2N/A
2N/A if (htmp != NULL) { free (htmp); }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Astatic long
2N/Akrb5int_get_plugin_sym (struct plugin_file_handle *h,
2N/A const char *csymname, int isfunc, void **ptr,
2N/A struct errinfo *ep)
2N/A{
2N/A long err = 0;
2N/A void *sym = NULL;
2N/A
2N/A#if USE_DLOPEN
2N/A if (!err && !sym && (h->dlhandle != NULL)) {
2N/A /* XXX Do we need to add a leading "_" to the symbol name on any
2N/A modern platforms? */
2N/A sym = dlsym (h->dlhandle, csymname);
2N/A if (sym == NULL) {
2N/A const char *e = dlerror (); /* XXX copy and save away */
2N/A if (e == NULL)
2N/A e = "unknown failure";
2N/A Tprintf ("dlsym(%s): %s\n", csymname, e);
2N/A err = ENOENT; /* XXX */
2N/A krb5int_set_error(ep, err, "%s", e);
2N/A }
2N/A }
2N/A#endif
2N/A
2N/A#ifdef _WIN32
2N/A LPVOID lpMsgBuf;
2N/A DWORD dw;
2N/A
2N/A if (!err && !sym && (h->hinstPlugin != NULL)) {
2N/A sym = GetProcAddress(h->hinstPlugin, csymname);
2N/A if (sym == NULL) {
2N/A const char *e = "unable to get dll symbol"; /* XXX copy and save away */
2N/A Tprintf ("GetProcAddress(%s): %s\n", csymname, GetLastError());
2N/A err = ENOENT; /* XXX */
2N/A krb5int_set_error(ep, err, "%s", e);
2N/A
2N/A dw = GetLastError();
2N/A if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
2N/A FORMAT_MESSAGE_FROM_SYSTEM,
2N/A NULL,
2N/A dw,
2N/A MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2N/A (LPTSTR) &lpMsgBuf,
2N/A 0, NULL )) {
2N/A
2N/A fprintf (stderr, "unable to get dll symbol, %s\n", (LPCTSTR)lpMsgBuf);
2N/A LocalFree(lpMsgBuf);
2N/A }
2N/A }
2N/A }
2N/A#endif
2N/A
2N/A if (!err && (sym == NULL)) {
2N/A err = ENOENT; /* unimplemented */
2N/A }
2N/A
2N/A if (!err) {
2N/A *ptr = sym;
2N/A }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Along KRB5_CALLCONV
2N/Akrb5int_get_plugin_data (struct plugin_file_handle *h, const char *csymname,
2N/A void **ptr, struct errinfo *ep)
2N/A{
2N/A return krb5int_get_plugin_sym (h, csymname, 0, ptr, ep);
2N/A}
2N/A
2N/Along KRB5_CALLCONV
2N/Akrb5int_get_plugin_func (struct plugin_file_handle *h, const char *csymname,
2N/A void (**ptr)(), struct errinfo *ep)
2N/A{
2N/A void *dptr = NULL;
2N/A long err = krb5int_get_plugin_sym (h, csymname, 1, &dptr, ep);
2N/A if (!err) {
2N/A /* Cast function pointers to avoid code duplication */
2N/A *ptr = (void (*)()) dptr;
2N/A }
2N/A return err;
2N/A}
2N/A
2N/Avoid KRB5_CALLCONV
2N/Akrb5int_close_plugin (struct plugin_file_handle *h)
2N/A{
2N/A#if USE_DLOPEN
2N/A if (h->dlhandle != NULL) { dlclose(h->dlhandle); }
2N/A#endif
2N/A#ifdef _WIN32
2N/A if (h->hinstPlugin != NULL) { FreeLibrary(h->hinstPlugin); }
2N/A#endif
2N/A free (h);
2N/A}
2N/A
2N/A/* autoconf docs suggest using this preference order */
2N/A#if HAVE_DIRENT_H || USE_DIRENT_H
2N/A#include <dirent.h>
2N/A#define NAMELEN(D) strlen((D)->d_name)
2N/A#else
2N/A#ifndef _WIN32
2N/A#define dirent direct
2N/A#define NAMELEN(D) ((D)->d->namlen)
2N/A#else
2N/A#define NAMELEN(D) strlen((D)->d_name)
2N/A#endif
2N/A#if HAVE_SYS_NDIR_H
2N/A# include <sys/ndir.h>
2N/A#elif HAVE_SYS_DIR_H
2N/A# include <sys/dir.h>
2N/A#elif HAVE_NDIR_H
2N/A# include <ndir.h>
2N/A#endif
2N/A#endif
2N/A
2N/A
2N/A#ifdef HAVE_STRERROR_R
2N/A#define ERRSTR(ERR, BUF) \
2N/A (strerror_r (ERR, BUF, sizeof(BUF)) == 0 ? BUF : strerror (ERR))
2N/A#else
2N/A#define ERRSTR(ERR, BUF) \
2N/A (strerror (ERR))
2N/A#endif
2N/A
2N/Astatic long
2N/Akrb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
2N/A{
2N/A long err = 0;
2N/A
2N/A *harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
2N/A if (*harray == NULL) { err = ENOMEM; }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Astatic long
2N/Akrb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,
2N/A struct plugin_file_handle *p)
2N/A{
2N/A long err = 0;
2N/A struct plugin_file_handle **newharray = NULL;
2N/A size_t newcount = *count + 1;
2N/A
2N/A newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
2N/A if (newharray == NULL) {
2N/A err = ENOMEM;
2N/A } else {
2N/A newharray[newcount - 1] = p;
2N/A newharray[newcount] = NULL;
2N/A *count = newcount;
2N/A *harray = newharray;
2N/A }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Astatic void
2N/Akrb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
2N/A{
2N/A if (harray != NULL) {
2N/A int i;
2N/A for (i = 0; harray[i] != NULL; i++) {
2N/A krb5int_close_plugin (harray[i]);
2N/A }
2N/A free (harray);
2N/A }
2N/A}
2N/A
2N/A#if TARGET_OS_MAC
2N/A#define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }
2N/A#elif defined(_WIN32)
2N/A#define FILEEXTS { "", ".dll", NULL }
2N/A#else
2N/A#define FILEEXTS { "", ".so", NULL }
2N/A#endif
2N/A
2N/A
2N/Astatic void
2N/Akrb5int_free_plugin_filenames (char **filenames)
2N/A{
2N/A if (filenames != NULL) {
2N/A int i;
2N/A for (i = 0; filenames[i] != NULL; i++) {
2N/A free (filenames[i]);
2N/A }
2N/A free (filenames);
2N/A }
2N/A}
2N/A
2N/A
2N/Astatic long
2N/Akrb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
2N/A{
2N/A long err = 0;
2N/A static const char *const fileexts[] = FILEEXTS;
2N/A char **tempnames = NULL;
2N/A size_t bases_count = 0;
2N/A size_t exts_count = 0;
2N/A size_t i;
2N/A
2N/A if (!filebases) { err = EINVAL; }
2N/A if (!filenames) { err = EINVAL; }
2N/A
2N/A if (!err) {
2N/A for (i = 0; filebases[i]; i++) { bases_count++; }
2N/A for (i = 0; fileexts[i]; i++) { exts_count++; }
2N/A tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));
2N/A if (!tempnames) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err) {
2N/A size_t j;
2N/A for (i = 0; !err && filebases[i]; i++) {
2N/A for (j = 0; !err && fileexts[j]; j++) {
2N/A if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",
2N/A filebases[i], fileexts[j]) < 0) {
2N/A tempnames[(i*exts_count)+j] = NULL;
2N/A err = ENOMEM;
2N/A }
2N/A }
2N/A }
2N/A tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */
2N/A }
2N/A
2N/A if (!err) {
2N/A *filenames = tempnames;
2N/A tempnames = NULL;
2N/A }
2N/A
2N/A if (tempnames) { krb5int_free_plugin_filenames (tempnames); }
2N/A
2N/A return err;
2N/A}
2N/A
2N/A
2N/A/* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored
2N/A * all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,
2N/A * only plugins in the directories with those name (plus any platform extension) are loaded. */
2N/A
2N/Along KRB5_CALLCONV
2N/Akrb5int_open_plugin_dirs (const char * const *dirnames,
2N/A const char * const *filebases,
2N/A struct plugin_dir_handle *dirhandle,
2N/A struct errinfo *ep)
2N/A{
2N/A long err = 0;
2N/A struct plugin_file_handle **h = NULL;
2N/A size_t count = 0;
2N/A char **filenames = NULL;
2N/A int i;
2N/A
2N/A if (!err) {
2N/A err = krb5int_plugin_file_handle_array_init (&h);
2N/A }
2N/A
2N/A if (!err && (filebases != NULL)) {
2N/A err = krb5int_get_plugin_filenames (filebases, &filenames);
2N/A }
2N/A
2N/A for (i = 0; !err && dirnames[i] != NULL; i++) {
2N/A if (filenames != NULL) {
2N/A /* load plugins with names from filenames from each directory */
2N/A int j;
2N/A
2N/A for (j = 0; !err && filenames[j] != NULL; j++) {
2N/A struct plugin_file_handle *handle = NULL;
2N/A char *filepath = NULL;
2N/A
2N/A if (!err) {
2N/A if (asprintf(&filepath, "%s/%s", dirnames[i], filenames[j]) < 0) {
2N/A filepath = NULL;
2N/A err = ENOMEM;
2N/A }
2N/A }
2N/A
2N/A if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
2N/A err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
2N/A if (!err) { handle = NULL; } /* h takes ownership */
2N/A }
2N/A
2N/A if (filepath != NULL) { free (filepath); }
2N/A if (handle != NULL) { krb5int_close_plugin (handle); }
2N/A }
2N/A } else {
2N/A /* load all plugins in each directory */
2N/A DIR *dir = opendir (dirnames[i]);
2N/A
2N/A while (dir != NULL && !err) {
2N/A struct dirent *d = NULL;
2N/A char *filepath = NULL;
2N/A struct plugin_file_handle *handle = NULL;
2N/A int len;
2N/A
2N/A d = readdir (dir);
2N/A if (d == NULL) { break; }
2N/A
2N/A if ((strcmp (d->d_name, ".") == 0) ||
2N/A (strcmp (d->d_name, "..") == 0)) {
2N/A continue;
2N/A }
2N/A
2N/A /* Solaris Kerberos: Only open files with a .so extension */
2N/A len = NAMELEN (d);
2N/A if (len < 3 || strcmp(".so", d->d_name + len - 3 ) != 0)
2N/A continue;
2N/A
2N/A if (!err) {
2N/A int len = NAMELEN (d);
2N/A if (asprintf(&filepath, "%s/%*s", dirnames[i], len, d->d_name) < 0) {
2N/A filepath = NULL;
2N/A err = ENOMEM;
2N/A }
2N/A }
2N/A
2N/A if (!err) {
2N/A if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
2N/A err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
2N/A if (!err) { handle = NULL; } /* h takes ownership */
2N/A }
2N/A }
2N/A
2N/A if (filepath != NULL) { free (filepath); }
2N/A if (handle != NULL) { krb5int_close_plugin (handle); }
2N/A }
2N/A
2N/A if (dir != NULL) { closedir (dir); }
2N/A }
2N/A }
2N/A
2N/A if (err == ENOENT) {
2N/A err = 0; /* ran out of plugins -- do nothing */
2N/A }
2N/A
2N/A if (!err) {
2N/A dirhandle->files = h;
2N/A h = NULL; /* dirhandle->files takes ownership */
2N/A }
2N/A
2N/A if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
2N/A if (h != NULL) { krb5int_plugin_file_handle_array_free (h); }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Avoid KRB5_CALLCONV
2N/Akrb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
2N/A{
2N/A if (dirhandle->files != NULL) {
2N/A int i;
2N/A for (i = 0; dirhandle->files[i] != NULL; i++) {
2N/A krb5int_close_plugin (dirhandle->files[i]);
2N/A }
2N/A free (dirhandle->files);
2N/A dirhandle->files = NULL;
2N/A }
2N/A}
2N/A
2N/Avoid KRB5_CALLCONV
2N/Akrb5int_free_plugin_dir_data (void **ptrs)
2N/A{
2N/A /* Nothing special to be done per pointer. */
2N/A free(ptrs);
2N/A}
2N/A
2N/Along KRB5_CALLCONV
2N/Akrb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
2N/A const char *symname,
2N/A void ***ptrs,
2N/A struct errinfo *ep)
2N/A{
2N/A long err = 0;
2N/A void **p = NULL;
2N/A size_t count = 0;
2N/A
2N/A /* XXX Do we need to add a leading "_" to the symbol name on any
2N/A modern platforms? */
2N/A
2N/A Tprintf("get_plugin_data_sym(%s)\n", symname);
2N/A
2N/A if (!err) {
2N/A p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
2N/A if (p == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
2N/A int i = 0;
2N/A
2N/A for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
2N/A void *sym = NULL;
2N/A
2N/A if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
2N/A void **newp = NULL;
2N/A
2N/A count++;
2N/A newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
2N/A if (newp == NULL) {
2N/A err = ENOMEM;
2N/A } else {
2N/A p = newp;
2N/A p[count - 1] = sym;
2N/A p[count] = NULL;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (!err) {
2N/A *ptrs = p;
2N/A p = NULL; /* ptrs takes ownership */
2N/A }
2N/A
2N/A if (p != NULL) { free (p); }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Avoid KRB5_CALLCONV
2N/Akrb5int_free_plugin_dir_func (void (**ptrs)(void))
2N/A{
2N/A /* Nothing special to be done per pointer. */
2N/A free(ptrs);
2N/A}
2N/A
2N/Along KRB5_CALLCONV
2N/Akrb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
2N/A const char *symname,
2N/A void (***ptrs)(void),
2N/A struct errinfo *ep)
2N/A{
2N/A long err = 0;
2N/A void (**p)() = NULL;
2N/A size_t count = 0;
2N/A
2N/A /* XXX Do we need to add a leading "_" to the symbol name on any
2N/A modern platforms? */
2N/A
2N/A Tprintf("get_plugin_data_sym(%s)\n", symname);
2N/A
2N/A if (!err) {
2N/A p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
2N/A if (p == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
2N/A int i = 0;
2N/A
2N/A for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
2N/A void (*sym)() = NULL;
2N/A
2N/A if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
2N/A void (**newp)() = NULL;
2N/A
2N/A count++;
2N/A newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
2N/A if (newp == NULL) {
2N/A err = ENOMEM;
2N/A } else {
2N/A p = newp;
2N/A p[count - 1] = sym;
2N/A p[count] = NULL;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (!err) {
2N/A *ptrs = p;
2N/A p = NULL; /* ptrs takes ownership */
2N/A }
2N/A
2N/A if (p != NULL) { free (p); }
2N/A
2N/A return err;
2N/A}