2
* util/support/plugins.c
4
* Copyright 2006 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
* Plugin module support, and shims around dlopen/whatever.
30
#include "k5-plugin.h"
35
#include <CoreFoundation/CoreFoundation.h>
38
#include <sys/types.h>
39
#ifdef HAVE_SYS_STAT_H
42
#ifdef HAVE_SYS_PARAM_H
43
#include <sys/param.h>
53
static void Tprintf (const char *fmt, ...)
58
vfprintf (stderr, fmt, va);
63
struct plugin_file_handle {
70
#if !defined (USE_DLOPEN) && !defined (USE_CFBUNDLE)
76
krb5int_open_plugin (const char *filepath, struct plugin_file_handle **h, struct errinfo *ep)
80
struct plugin_file_handle *htmp = NULL;
84
if (stat (filepath, &statbuf) < 0) {
85
Tprintf ("stat(%s): %s\n", filepath, strerror (errno));
91
htmp = calloc (1, sizeof (*htmp)); /* calloc initializes ptrs to NULL */
92
if (htmp == NULL) { err = errno; }
96
if (!err && (statbuf.st_mode & S_IFMT) == S_IFREG) {
99
#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | RTLD_GROUP)
101
#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL)
105
handle = dlopen(filepath, PLUGIN_DLOPEN_FLAGS);
106
if (handle == NULL) {
107
const char *e = dlerror();
108
Tprintf ("dlopen(%s): %s\n", filepath, e);
109
err = ENOENT; /* XXX */
110
krb5int_set_error (ep, err, "%s", e);
116
htmp->dlhandle = handle;
120
if (handle != NULL) { dlclose (handle); }
125
if (!err && (statbuf.st_mode & S_IFMT) == S_IFDIR) {
126
CFStringRef pluginPath = NULL;
127
CFURLRef pluginURL = NULL;
128
CFBundleRef pluginBundle = NULL;
131
pluginPath = CFStringCreateWithCString (kCFAllocatorDefault, filepath,
132
kCFStringEncodingASCII);
133
if (pluginPath == NULL) { err = ENOMEM; }
137
pluginURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, pluginPath,
138
kCFURLPOSIXPathStyle, true);
139
if (pluginURL == NULL) { err = ENOMEM; }
143
pluginBundle = CFBundleCreate (kCFAllocatorDefault, pluginURL);
144
if (pluginBundle == NULL) { err = ENOENT; } /* XXX need better error */
148
if (!CFBundleIsExecutableLoaded (pluginBundle)) {
149
int loaded = CFBundleLoadExecutable (pluginBundle);
150
if (!loaded) { err = ENOENT; } /* XXX need better error */
156
htmp->bundle = pluginBundle;
157
pluginBundle = NULL; /* htmp->bundle takes ownership */
160
if (pluginBundle != NULL) { CFRelease (pluginBundle); }
161
if (pluginURL != NULL) { CFRelease (pluginURL); }
162
if (pluginPath != NULL) { CFRelease (pluginPath); }
166
if (!err && !got_plugin) {
167
err = ENOENT; /* no plugin or no way to load plugins */
172
htmp = NULL; /* h takes ownership */
175
if (htmp != NULL) { free (htmp); }
181
krb5int_get_plugin_sym (struct plugin_file_handle *h,
182
const char *csymname, int isfunc, void **ptr,
189
if (!err && !sym && (h->dlhandle != NULL)) {
190
/* XXX Do we need to add a leading "_" to the symbol name on any
192
sym = dlsym (h->dlhandle, csymname);
194
const char *e = dlerror (); /* XXX copy and save away */
195
Tprintf ("dlsym(%s): %s\n", csymname, e);
196
err = ENOENT; /* XXX */
197
krb5int_set_error(ep, err, "%s", e);
203
if (!err && !sym && (h->bundle != NULL)) {
204
CFStringRef cfsymname = NULL;
207
cfsymname = CFStringCreateWithCString (kCFAllocatorDefault, csymname,
208
kCFStringEncodingASCII);
209
if (cfsymname == NULL) { err = ENOMEM; }
214
sym = CFBundleGetFunctionPointerForName (h->bundle, cfsymname);
216
sym = CFBundleGetDataPointerForName (h->bundle, cfsymname);
218
if (sym == NULL) { err = ENOENT; } /* XXX */
221
if (cfsymname != NULL) { CFRelease (cfsymname); }
225
if (!err && (sym == NULL)) {
226
err = ENOENT; /* unimplemented */
237
krb5int_get_plugin_data (struct plugin_file_handle *h, const char *csymname,
238
void **ptr, struct errinfo *ep)
240
return krb5int_get_plugin_sym (h, csymname, 0, ptr, ep);
244
krb5int_get_plugin_func (struct plugin_file_handle *h, const char *csymname,
245
void (**ptr)(), struct errinfo *ep)
248
long err = krb5int_get_plugin_sym (h, csymname, 1, &dptr, ep);
250
/* Cast function pointers to avoid code duplication */
251
*ptr = (void (*)()) dptr;
257
krb5int_close_plugin (struct plugin_file_handle *h)
260
if (h->dlhandle != NULL) { dlclose(h->dlhandle); }
263
/* Do not call CFBundleUnloadExecutable because it's not ref counted.
264
* CFRelease will unload the bundle if the internal refcount goes to zero. */
265
if (h->bundle != NULL) { CFRelease (h->bundle); }
270
/* autoconf docs suggest using this preference order */
271
#if HAVE_DIRENT_H || USE_DIRENT_H
273
#define NAMELEN(D) strlen((D)->d_name)
275
#define dirent direct
276
#define NAMELEN(D) ((D)->d->namlen)
278
# include <sys/ndir.h>
280
# include <sys/dir.h>
287
#ifdef HAVE_STRERROR_R
288
#define ERRSTR(ERR, BUF) \
289
(strerror_r (ERR, BUF, sizeof(BUF)) == 0 ? BUF : strerror (ERR))
291
#define ERRSTR(ERR, BUF) \
296
krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
300
*harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
301
if (*harray == NULL) { err = errno; }
307
krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, int *count,
308
struct plugin_file_handle *p)
311
struct plugin_file_handle **newharray = NULL;
312
int newcount = *count + 1;
314
newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
315
if (newharray == NULL) {
318
newharray[newcount - 1] = p;
319
newharray[newcount] = NULL;
328
krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
330
if (harray != NULL) {
332
for (i = 0; harray[i] != NULL; i++) {
333
krb5int_close_plugin (harray[i]);
340
#define FILEEXTS { "", ".bundle", ".so", NULL }
341
#elif defined(_WIN32)
342
#define FILEEXTS { "", ".dll", NULL }
344
#define FILEEXTS { "", ".so", NULL }
349
krb5int_free_plugin_filenames (char **filenames)
351
if (filenames != NULL) {
353
for (i = 0; filenames[i] != NULL; i++) {
362
krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
365
static const char *const fileexts[] = FILEEXTS;
366
char **tempnames = NULL;
371
for (i = 0; filebases[i] != NULL; i++, count++);
372
for (i = 0; fileexts[i] != NULL; i++, count++);
373
tempnames = calloc (count, sizeof (char *));
374
if (tempnames == NULL) { err = errno; }
379
for (i = 0; !err && (filebases[i] != NULL); i++) {
380
size_t baselen = strlen (filebases[i]);
381
for (j = 0; !err && (fileexts[j] != NULL); j++) {
382
size_t len = baselen + strlen (fileexts[j]) + 2; /* '.' + NULL */
383
tempnames[i+j] = malloc (len * sizeof (char));
384
if (tempnames[i+j] == NULL) {
387
sprintf (tempnames[i+j], "%s%s", filebases[i], fileexts[j]);
394
*filenames = tempnames;
398
if (tempnames != NULL) { krb5int_free_plugin_filenames (tempnames); }
404
/* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored
405
* all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,
406
* only plugins in the directories with those name (plus any platform extension) are loaded. */
409
krb5int_open_plugin_dirs (const char * const *dirnames,
410
const char * const *filebases,
411
struct plugin_dir_handle *dirhandle,
415
struct plugin_file_handle **h = NULL;
417
char **filenames = NULL;
421
err = krb5int_plugin_file_handle_array_init (&h);
424
if (!err && (filebases != NULL)) {
425
err = krb5int_get_plugin_filenames (filebases, &filenames);
428
for (i = 0; !err && dirnames[i] != NULL; i++) {
429
size_t dirnamelen = strlen (dirnames[i]) + 1; /* '/' */
430
if (filenames != NULL) {
431
/* load plugins with names from filenames from each directory */
434
for (j = 0; !err && filenames[j] != NULL; j++) {
435
struct plugin_file_handle *handle = NULL;
436
char *filepath = NULL;
439
filepath = malloc (dirnamelen + strlen (filenames[j]) + 1); /* NULL */
440
if (filepath == NULL) {
443
sprintf (filepath, "%s/%s", dirnames[i], filenames[j]);
447
if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
448
err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
449
if (!err) { handle = NULL; } /* h takes ownership */
452
if (filepath != NULL) { free (filepath); }
453
if (handle != NULL) { krb5int_close_plugin (handle); }
456
/* load all plugins in each directory */
458
DIR *dir = opendir (dirnames[i]);
460
while (dir != NULL && !err) {
461
struct dirent *d = NULL;
462
char *filepath = NULL;
463
struct plugin_file_handle *handle = NULL;
466
if (d == NULL) { break; }
468
if ((strcmp (d->d_name, ".") == 0) ||
469
(strcmp (d->d_name, "..") == 0)) {
474
int len = NAMELEN (d);
475
filepath = malloc (dirnamelen + len + 1); /* NULL */
476
if (filepath == NULL) {
479
sprintf (filepath, "%s/%*s", dirnames[i], len, d->d_name);
484
if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
485
err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
486
if (!err) { handle = NULL; } /* h takes ownership */
490
if (filepath != NULL) { free (filepath); }
491
if (handle != NULL) { krb5int_close_plugin (handle); }
494
if (dir != NULL) { closedir (dir); }
496
/* Until a Windows implementation of this code is implemented */
503
err = 0; /* ran out of plugins -- do nothing */
507
dirhandle->files = h;
508
h = NULL; /* dirhandle->files takes ownership */
511
if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
512
if (h != NULL) { krb5int_plugin_file_handle_array_free (h); }
518
krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
520
if (dirhandle->files != NULL) {
522
for (i = 0; dirhandle->files[i] != NULL; i++) {
523
krb5int_close_plugin (dirhandle->files[i]);
525
free (dirhandle->files);
526
dirhandle->files = NULL;
531
krb5int_free_plugin_dir_data (void **ptrs)
533
/* Nothing special to be done per pointer. */
538
krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
547
/* XXX Do we need to add a leading "_" to the symbol name on any
550
Tprintf("get_plugin_data_sym(%s)\n", symname);
553
p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
554
if (p == NULL) { err = errno; }
557
if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
560
for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
563
if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
567
newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
581
p = NULL; /* ptrs takes ownership */
584
if (p != NULL) { free (p); }
590
krb5int_free_plugin_dir_func (void (**ptrs)(void))
592
/* Nothing special to be done per pointer. */
597
krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
599
void (***ptrs)(void),
606
/* XXX Do we need to add a leading "_" to the symbol name on any
609
Tprintf("get_plugin_data_sym(%s)\n", symname);
612
p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
613
if (p == NULL) { err = errno; }
616
if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
619
for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
620
void (*sym)() = NULL;
622
if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
623
void (**newp)() = NULL;
626
newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
640
p = NULL; /* ptrs takes ownership */
643
if (p != NULL) { free (p); }