1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* util/support/plugins.c - Plugin module support functions */
4 * Copyright 2006, 2008 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
27 #include "k5-platform.h"
28 #include "k5-plugin.h"
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_STAT_H
36 #ifdef HAVE_SYS_PARAM_H
37 #include <sys/param.h>
45 #define GROUP RTLD_GROUP
50 #define NODELETE RTLD_NODELETE
54 #define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | GROUP | NODELETE)
57 #if USE_DLOPEN && USE_CFBUNDLE
58 #include <CoreFoundation/CoreFoundation.h>
60 /* Currently CoreFoundation only exists on the Mac so we just use
61 * pthreads directly to avoid creating empty function calls on other
62 * platforms. If a thread initializer ever gets created in the common
63 * plugin code, move this there */
64 static pthread_mutex_t krb5int_bundle_mutex = PTHREAD_MUTEX_INITIALIZER;
68 static void Tprintf (const char *fmt, ...)
73 vfprintf (stderr, fmt, va);
78 struct plugin_file_handle {
85 #if !defined (USE_DLOPEN) && !defined (_WIN32)
92 long d_ino; /* inode (always 1 in WIN32) */
93 off_t d_off; /* offset to this dirent */
94 unsigned short d_reclen; /* length of d_name */
95 char d_name[_MAX_FNAME+1]; /* filename (null terminated) */
99 intptr_t handle; /* _findfirst/_findnext handle */
100 short offset; /* offset into directory */
101 short finished; /* 1 if there are not more files */
102 struct _finddata_t fileinfo;/* from _findfirst/_findnext */
103 char *dir; /* the dir we are reading */
104 struct dirent dent; /* the dirent to return */
107 DIR * opendir(const char *dir)
114 filespec = malloc(strlen(dir) + 2 + 1);
115 strcpy(filespec, dir);
116 index = strlen(filespec) - 1;
117 if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
118 filespec[index] = '\0';
119 strcat(filespec, "/*");
121 dp = (DIR *)malloc(sizeof(DIR));
124 dp->dir = strdup(dir);
126 if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) {
143 struct dirent * readdir(DIR *dp)
145 if (!dp || dp->finished) return NULL;
147 if (dp->offset != 0) {
148 if (_findnext(dp->handle, &(dp->fileinfo)) < 0) {
155 strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
157 dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
158 dp->dent.d_off = dp->offset;
163 int closedir(DIR *dp)
166 _findclose(dp->handle);
175 krb5int_open_plugin (const char *filepath, struct plugin_file_handle **h, struct errinfo *ep)
179 struct plugin_file_handle *htmp = NULL;
183 if (stat (filepath, &statbuf) < 0) {
185 Tprintf ("stat(%s): %s\n", filepath, strerror (err));
186 k5_set_error(ep, err, _("unable to find plugin [%s]: %s"),
187 filepath, strerror(err));
192 htmp = calloc (1, sizeof (*htmp)); /* calloc initializes ptrs to NULL */
193 if (htmp == NULL) { err = ENOMEM; }
197 if (!err && ((statbuf.st_mode & S_IFMT) == S_IFREG
199 || (statbuf.st_mode & S_IFMT) == S_IFDIR
200 #endif /* USE_CFBUNDLE */
205 char executablepath[MAXPATHLEN];
207 if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
209 CFStringRef pluginString = NULL;
210 CFURLRef pluginURL = NULL;
211 CFBundleRef pluginBundle = NULL;
212 CFURLRef executableURL = NULL;
214 /* Lock around CoreFoundation calls since objects are refcounted
215 * and the refcounts are not thread-safe. Using pthreads directly
216 * because this code is Mac-specific */
217 lock_err = pthread_mutex_lock(&krb5int_bundle_mutex);
218 if (lock_err) { err = lock_err; }
221 pluginString = CFStringCreateWithCString (kCFAllocatorDefault,
223 kCFStringEncodingASCII);
224 if (pluginString == NULL) { err = ENOMEM; }
228 pluginURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault,
230 kCFURLPOSIXPathStyle,
232 if (pluginURL == NULL) { err = ENOMEM; }
236 pluginBundle = CFBundleCreate (kCFAllocatorDefault, pluginURL);
237 if (pluginBundle == NULL) { err = ENOENT; } /* XXX need better error */
241 executableURL = CFBundleCopyExecutableURL (pluginBundle);
242 if (executableURL == NULL) { err = ENOMEM; }
246 if (!CFURLGetFileSystemRepresentation (executableURL,
248 (UInt8 *)executablepath,
249 sizeof (executablepath))) {
255 /* override the path the caller passed in */
256 filepath = executablepath;
259 if (executableURL != NULL) { CFRelease (executableURL); }
260 if (pluginBundle != NULL) { CFRelease (pluginBundle); }
261 if (pluginURL != NULL) { CFRelease (pluginURL); }
262 if (pluginString != NULL) { CFRelease (pluginString); }
264 /* unlock after CFRelease calls since they modify refcounts */
265 if (!lock_err) { pthread_mutex_unlock (&krb5int_bundle_mutex); }
267 #endif /* USE_CFBUNDLE */
270 handle = dlopen(filepath, PLUGIN_DLOPEN_FLAGS);
271 if (handle == NULL) {
272 const char *e = dlerror();
274 e = _("unknown failure");
275 Tprintf ("dlopen(%s): %s\n", filepath, e);
276 err = ENOENT; /* XXX */
277 k5_set_error(ep, err, _("unable to load plugin [%s]: %s"),
284 htmp->dlhandle = handle;
288 if (handle != NULL) { dlclose (handle); }
290 #endif /* USE_DLOPEN */
293 if (!err && (statbuf.st_mode & S_IFMT) == S_IFREG) {
294 HMODULE handle = NULL;
296 handle = LoadLibrary(filepath);
297 if (handle == NULL) {
298 Tprintf ("Unable to load dll: %s\n", filepath);
299 err = ENOENT; /* XXX */
300 k5_set_error(ep, err, _("unable to load DLL [%s]"), filepath);
305 htmp->hinstPlugin = handle;
314 if (!err && !got_plugin) {
315 err = ENOENT; /* no plugin or no way to load plugins */
316 k5_set_error(ep, err, _("plugin unavailable: %s"), strerror(err));
321 htmp = NULL; /* h takes ownership */
330 krb5int_get_plugin_sym (struct plugin_file_handle *h,
331 const char *csymname, int isfunc, void **ptr,
338 if (!err && !sym && (h->dlhandle != NULL)) {
339 /* XXX Do we need to add a leading "_" to the symbol name on any
341 sym = dlsym (h->dlhandle, csymname);
343 const char *e = dlerror (); /* XXX copy and save away */
345 e = "unknown failure";
346 Tprintf ("dlsym(%s): %s\n", csymname, e);
347 err = ENOENT; /* XXX */
348 k5_set_error(ep, err, "%s", e);
357 if (!err && !sym && (h->hinstPlugin != NULL)) {
358 sym = GetProcAddress(h->hinstPlugin, csymname);
360 const char *e = "unable to get dll symbol"; /* XXX copy and save away */
361 Tprintf ("GetProcAddress(%s): %i\n", csymname, GetLastError());
362 err = ENOENT; /* XXX */
363 k5_set_error(ep, err, "%s", e);
366 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
367 FORMAT_MESSAGE_FROM_SYSTEM,
370 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
374 fprintf (stderr, "unable to get dll symbol, %s\n", (LPCTSTR)lpMsgBuf);
381 if (!err && (sym == NULL)) {
382 err = ENOENT; /* unimplemented */
393 krb5int_get_plugin_data (struct plugin_file_handle *h, const char *csymname,
394 void **ptr, struct errinfo *ep)
396 return krb5int_get_plugin_sym (h, csymname, 0, ptr, ep);
400 krb5int_get_plugin_func (struct plugin_file_handle *h, const char *csymname,
401 void (**ptr)(), struct errinfo *ep)
404 long err = krb5int_get_plugin_sym (h, csymname, 1, &dptr, ep);
406 /* Cast function pointers to avoid code duplication */
407 *ptr = (void (*)()) dptr;
413 krb5int_close_plugin (struct plugin_file_handle *h)
416 if (h->dlhandle != NULL) { dlclose(h->dlhandle); }
419 if (h->hinstPlugin != NULL) { FreeLibrary(h->hinstPlugin); }
424 /* autoconf docs suggest using this preference order */
425 #if HAVE_DIRENT_H || USE_DIRENT_H
427 #define NAMELEN(D) strlen((D)->d_name)
430 #define dirent direct
431 #define NAMELEN(D) ((D)->d->namlen)
433 #define NAMELEN(D) strlen((D)->d_name)
436 # include <sys/ndir.h>
438 # include <sys/dir.h>
445 krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
449 *harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
450 if (*harray == NULL) { err = ENOMEM; }
456 krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,
457 struct plugin_file_handle *p)
460 struct plugin_file_handle **newharray = NULL;
461 size_t newcount = *count + 1;
463 newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
464 if (newharray == NULL) {
467 newharray[newcount - 1] = p;
468 newharray[newcount] = NULL;
477 krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
479 if (harray != NULL) {
481 for (i = 0; harray[i] != NULL; i++) {
482 krb5int_close_plugin (harray[i]);
489 #define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }
490 #elif defined(_WIN32)
491 #define FILEEXTS { "", ".dll", NULL }
493 #define FILEEXTS { "", ".so", NULL }
498 krb5int_free_plugin_filenames (char **filenames)
500 if (filenames != NULL) {
502 for (i = 0; filenames[i] != NULL; i++) {
511 krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
514 static const char *const fileexts[] = FILEEXTS;
515 char **tempnames = NULL;
516 size_t bases_count = 0;
517 size_t exts_count = 0;
520 if (!filebases) { err = EINVAL; }
521 if (!filenames) { err = EINVAL; }
524 for (i = 0; filebases[i]; i++) { bases_count++; }
525 for (i = 0; fileexts[i]; i++) { exts_count++; }
526 tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));
527 if (!tempnames) { err = ENOMEM; }
532 for (i = 0; !err && filebases[i]; i++) {
533 for (j = 0; !err && fileexts[j]; j++) {
534 if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",
535 filebases[i], fileexts[j]) < 0) {
536 tempnames[(i*exts_count)+j] = NULL;
541 tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */
545 *filenames = tempnames;
549 krb5int_free_plugin_filenames(tempnames);
555 /* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored
556 * all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,
557 * only plugins in the directories with those name (plus any platform extension) are loaded. */
560 krb5int_open_plugin_dirs (const char * const *dirnames,
561 const char * const *filebases,
562 struct plugin_dir_handle *dirhandle,
566 struct plugin_file_handle **h = NULL;
568 char **filenames = NULL;
572 err = krb5int_plugin_file_handle_array_init (&h);
575 if (!err && (filebases != NULL)) {
576 err = krb5int_get_plugin_filenames (filebases, &filenames);
579 for (i = 0; !err && dirnames[i] != NULL; i++) {
580 if (filenames != NULL) {
581 /* load plugins with names from filenames from each directory */
584 for (j = 0; !err && filenames[j] != NULL; j++) {
585 struct plugin_file_handle *handle = NULL;
586 char *filepath = NULL;
589 if (asprintf(&filepath, "%s/%s", dirnames[i], filenames[j]) < 0) {
595 if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {
596 err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
598 handle = NULL; /* h takes ownership */
602 if (handle != NULL) { krb5int_close_plugin (handle); }
605 /* load all plugins in each directory */
606 DIR *dir = opendir (dirnames[i]);
608 while (dir != NULL && !err) {
609 struct dirent *d = NULL;
610 char *filepath = NULL;
611 struct plugin_file_handle *handle = NULL;
614 if (d == NULL) { break; }
616 if ((strcmp (d->d_name, ".") == 0) ||
617 (strcmp (d->d_name, "..") == 0)) {
622 int len = NAMELEN (d);
623 if (asprintf(&filepath, "%s/%*s", dirnames[i], len, d->d_name) < 0) {
630 if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
631 err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
632 if (!err) { handle = NULL; } /* h takes ownership */
637 if (handle != NULL) { krb5int_close_plugin (handle); }
640 if (dir != NULL) { closedir (dir); }
645 err = 0; /* ran out of plugins -- do nothing */
649 dirhandle->files = h;
650 h = NULL; /* dirhandle->files takes ownership */
653 if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
654 if (h != NULL) { krb5int_plugin_file_handle_array_free (h); }
660 krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
662 if (dirhandle->files != NULL) {
664 for (i = 0; dirhandle->files[i] != NULL; i++) {
665 krb5int_close_plugin (dirhandle->files[i]);
667 free (dirhandle->files);
668 dirhandle->files = NULL;
673 krb5int_free_plugin_dir_data (void **ptrs)
675 /* Nothing special to be done per pointer. */
680 krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
689 /* XXX Do we need to add a leading "_" to the symbol name on any
692 Tprintf("get_plugin_data_sym(%s)\n", symname);
695 p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
696 if (p == NULL) { err = ENOMEM; }
699 if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
702 for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
705 if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
709 newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
723 p = NULL; /* ptrs takes ownership */
732 krb5int_free_plugin_dir_func (void (**ptrs)(void))
734 /* Nothing special to be done per pointer. */
739 krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
741 void (***ptrs)(void),
748 /* XXX Do we need to add a leading "_" to the symbol name on any
751 Tprintf("get_plugin_data_sym(%s)\n", symname);
754 p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
755 if (p == NULL) { err = ENOMEM; }
758 if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
761 for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
762 void (*sym)() = NULL;
764 if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
765 void (**newp)() = NULL;
768 newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
782 p = NULL; /* ptrs takes ownership */