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.
31 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
32 * Use is subject to license terms.
36 #include "k5-plugin.h"
41 #include <CoreFoundation/CoreFoundation.h>
44 #include <sys/types.h>
45 #ifdef HAVE_SYS_STAT_H
48 #ifdef HAVE_SYS_PARAM_H
49 #include <sys/param.h>
60 static void Tprintf (const char *fmt
, ...)
65 vfprintf (stderr
, fmt
, va
);
70 struct plugin_file_handle
{
77 #if !defined (USE_DLOPEN) && !defined (USE_CFBUNDLE)
84 krb5int_open_plugin (const char *filepath
, struct plugin_file_handle
**h
, struct errinfo
*ep
)
88 struct plugin_file_handle
*htmp
= NULL
;
92 if (stat (filepath
, &statbuf
) < 0) {
93 Tprintf ("stat(%s): %s\n", filepath
, strerror (errno
));
99 htmp
= calloc (1, sizeof (*htmp
)); /* calloc initializes ptrs to NULL */
100 if (htmp
== NULL
) { err
= errno
; }
104 if (!err
&& (statbuf
.st_mode
& S_IFMT
) == S_IFREG
) {
107 #define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | RTLD_GROUP)
109 #define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL)
113 handle
= dlopen(filepath
, PLUGIN_DLOPEN_FLAGS
);
114 if (handle
== NULL
) {
115 const char *e
= dlerror();
116 Tprintf ("dlopen(%s): %s\n", filepath
, e
);
117 err
= ENOENT
; /* XXX */
118 krb5int_set_error (ep
, err
, "%s", e
);
124 htmp
->dlhandle
= handle
;
128 if (handle
!= NULL
) { dlclose (handle
); }
133 if (!err
&& (statbuf
.st_mode
& S_IFMT
) == S_IFDIR
) {
134 CFStringRef pluginPath
= NULL
;
135 CFURLRef pluginURL
= NULL
;
136 CFBundleRef pluginBundle
= NULL
;
139 pluginPath
= CFStringCreateWithCString (kCFAllocatorDefault
, filepath
,
140 kCFStringEncodingASCII
);
141 if (pluginPath
== NULL
) { err
= ENOMEM
; }
145 pluginURL
= CFURLCreateWithFileSystemPath (kCFAllocatorDefault
, pluginPath
,
146 kCFURLPOSIXPathStyle
, true);
147 if (pluginURL
== NULL
) { err
= ENOMEM
; }
151 pluginBundle
= CFBundleCreate (kCFAllocatorDefault
, pluginURL
);
152 if (pluginBundle
== NULL
) { err
= ENOENT
; } /* XXX need better error */
156 if (!CFBundleIsExecutableLoaded (pluginBundle
)) {
157 int loaded
= CFBundleLoadExecutable (pluginBundle
);
158 if (!loaded
) { err
= ENOENT
; } /* XXX need better error */
164 htmp
->bundle
= pluginBundle
;
165 pluginBundle
= NULL
; /* htmp->bundle takes ownership */
168 if (pluginBundle
!= NULL
) { CFRelease (pluginBundle
); }
169 if (pluginURL
!= NULL
) { CFRelease (pluginURL
); }
170 if (pluginPath
!= NULL
) { CFRelease (pluginPath
); }
174 if (!err
&& !got_plugin
) {
175 err
= ENOENT
; /* no plugin or no way to load plugins */
180 htmp
= NULL
; /* h takes ownership */
183 if (htmp
!= NULL
) { free (htmp
); }
190 krb5int_get_plugin_sym (struct plugin_file_handle
*h
,
191 const char *csymname
, int isfunc
, void **ptr
,
198 if (!err
&& !sym
&& (h
->dlhandle
!= NULL
)) {
199 /* XXX Do we need to add a leading "_" to the symbol name on any
201 sym
= dlsym (h
->dlhandle
, csymname
);
203 const char *e
= dlerror (); /* XXX copy and save away */
204 Tprintf ("dlsym(%s): %s\n", csymname
, e
);
205 err
= ENOENT
; /* XXX */
206 krb5int_set_error(ep
, err
, "%s", e
);
212 if (!err
&& !sym
&& (h
->bundle
!= NULL
)) {
213 CFStringRef cfsymname
= NULL
;
216 cfsymname
= CFStringCreateWithCString (kCFAllocatorDefault
, csymname
,
217 kCFStringEncodingASCII
);
218 if (cfsymname
== NULL
) { err
= ENOMEM
; }
223 sym
= CFBundleGetFunctionPointerForName (h
->bundle
, cfsymname
);
225 sym
= CFBundleGetDataPointerForName (h
->bundle
, cfsymname
);
227 if (sym
== NULL
) { err
= ENOENT
; } /* XXX */
230 if (cfsymname
!= NULL
) { CFRelease (cfsymname
); }
234 if (!err
&& (sym
== NULL
)) {
235 err
= ENOENT
; /* unimplemented */
246 krb5int_get_plugin_data (struct plugin_file_handle
*h
, const char *csymname
,
247 void **ptr
, struct errinfo
*ep
)
249 return krb5int_get_plugin_sym (h
, csymname
, 0, ptr
, ep
);
253 krb5int_get_plugin_func (struct plugin_file_handle
*h
, const char *csymname
,
254 void (**ptr
)(), struct errinfo
*ep
)
257 long err
= krb5int_get_plugin_sym (h
, csymname
, 1, &dptr
, ep
);
259 /* Cast function pointers to avoid code duplication */
260 *ptr
= (void (*)()) dptr
;
266 krb5int_close_plugin (struct plugin_file_handle
*h
)
269 if (h
->dlhandle
!= NULL
) { dlclose(h
->dlhandle
); }
272 /* Do not call CFBundleUnloadExecutable because it's not ref counted.
273 * CFRelease will unload the bundle if the internal refcount goes to zero. */
274 if (h
->bundle
!= NULL
) { CFRelease (h
->bundle
); }
279 /* autoconf docs suggest using this preference order */
280 #if HAVE_DIRENT_H || USE_DIRENT_H
282 #define NAMELEN(D) strlen((D)->d_name)
284 #define dirent direct
285 #define NAMELEN(D) ((D)->d->namlen)
287 # include <sys/ndir.h>
289 # include <sys/dir.h>
296 #ifdef HAVE_STRERROR_R
297 #define ERRSTR(ERR, BUF) \
298 (strerror_r (ERR, BUF, sizeof(BUF)) == 0 ? BUF : strerror (ERR))
300 #define ERRSTR(ERR, BUF) \
305 krb5int_plugin_file_handle_array_init (struct plugin_file_handle
***harray
)
309 *harray
= calloc (1, sizeof (**harray
)); /* calloc initializes to NULL */
310 if (*harray
== NULL
) { err
= errno
; }
316 krb5int_plugin_file_handle_array_add (struct plugin_file_handle
***harray
, int *count
,
317 struct plugin_file_handle
*p
)
320 struct plugin_file_handle
**newharray
= NULL
;
321 int newcount
= *count
+ 1;
323 newharray
= realloc (*harray
, ((newcount
+ 1) * sizeof (**harray
))); /* +1 for NULL */
324 if (newharray
== NULL
) {
327 newharray
[newcount
- 1] = p
;
328 newharray
[newcount
] = NULL
;
337 krb5int_plugin_file_handle_array_free (struct plugin_file_handle
**harray
)
339 if (harray
!= NULL
) {
341 for (i
= 0; harray
[i
] != NULL
; i
++) {
342 krb5int_close_plugin (harray
[i
]);
349 #define FILEEXTS { "", ".bundle", ".so", NULL }
350 #elif defined(_WIN32)
351 #define FILEEXTS { "", ".dll", NULL }
353 #define FILEEXTS { "", ".so", NULL }
358 krb5int_free_plugin_filenames (char **filenames
)
360 if (filenames
!= NULL
) {
362 for (i
= 0; filenames
[i
] != NULL
; i
++) {
371 krb5int_get_plugin_filenames (const char * const *filebases
, char ***filenames
)
374 static const char *const fileexts
[] = FILEEXTS
;
375 char **tempnames
= NULL
;
380 for (i
= 0; filebases
[i
] != NULL
; i
++, count
++);
381 for (i
= 0; fileexts
[i
] != NULL
; i
++, count
++);
382 tempnames
= calloc (count
, sizeof (char *));
383 if (tempnames
== NULL
) { err
= errno
; }
388 for (i
= 0; !err
&& (filebases
[i
] != NULL
); i
++) {
389 size_t baselen
= strlen (filebases
[i
]);
390 for (j
= 0; !err
&& (fileexts
[j
] != NULL
); j
++) {
391 size_t len
= baselen
+ strlen (fileexts
[j
]) + 2; /* '.' + NULL */
392 tempnames
[i
+j
] = malloc (len
* sizeof (char));
393 if (tempnames
[i
+j
] == NULL
) {
397 sprintf (tempnames
[i
+j
], "%s%s", filebases
[i
], fileexts
[j
]);
404 *filenames
= tempnames
;
408 if (tempnames
!= NULL
) { krb5int_free_plugin_filenames (tempnames
); }
414 /* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored
415 * all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,
416 * only plugins in the directories with those name (plus any platform extension) are loaded. */
419 krb5int_open_plugin_dirs (const char * const *dirnames
,
420 const char * const *filebases
,
421 struct plugin_dir_handle
*dirhandle
,
425 struct plugin_file_handle
**h
= NULL
;
427 char **filenames
= NULL
;
431 err
= krb5int_plugin_file_handle_array_init (&h
);
434 if (!err
&& (filebases
!= NULL
)) {
435 err
= krb5int_get_plugin_filenames (filebases
, &filenames
);
438 for (i
= 0; !err
&& dirnames
[i
] != NULL
; i
++) {
439 size_t dirnamelen
= strlen (dirnames
[i
]) + 1; /* '/' */
440 if (filenames
!= NULL
) {
441 /* load plugins with names from filenames from each directory */
444 for (j
= 0; !err
&& filenames
[j
] != NULL
; j
++) {
445 struct plugin_file_handle
*handle
= NULL
;
446 char *filepath
= NULL
;
449 filepath
= malloc (dirnamelen
+ strlen (filenames
[j
]) + 1); /* NULL */
450 if (filepath
== NULL
) {
454 sprintf (filepath
, "%s/%s", dirnames
[i
], filenames
[j
]);
458 if (krb5int_open_plugin (filepath
, &handle
, ep
) == 0) {
459 err
= krb5int_plugin_file_handle_array_add (&h
, &count
, handle
);
460 if (!err
) { handle
= NULL
; } /* h takes ownership */
463 if (filepath
!= NULL
) { free (filepath
); }
464 if (handle
!= NULL
) { krb5int_close_plugin (handle
); }
467 /* load all plugins in each directory */
469 DIR *dir
= opendir (dirnames
[i
]);
471 while (dir
!= NULL
&& !err
) {
472 struct dirent
*d
= NULL
;
473 char *filepath
= NULL
;
474 struct plugin_file_handle
*handle
= NULL
;
478 if (d
== NULL
) { break; }
480 if ((strcmp (d
->d_name
, ".") == 0) ||
481 (strcmp (d
->d_name
, "..") == 0)) {
485 /* Solaris Kerberos: Only open files with a .so extension */
487 if (len
< 3 || strcmp(".so", d
->d_name
+ len
- 3 ) != 0)
491 filepath
= malloc (dirnamelen
+ len
+ 1); /* NULL */
492 if (filepath
== NULL
) {
496 sprintf (filepath
, "%s/%*s", dirnames
[i
], len
, d
->d_name
);
501 if (krb5int_open_plugin (filepath
, &handle
, ep
) == 0) {
502 err
= krb5int_plugin_file_handle_array_add (&h
, &count
, handle
);
503 if (!err
) { handle
= NULL
; } /* h takes ownership */
507 if (filepath
!= NULL
) { free (filepath
); }
508 if (handle
!= NULL
) { krb5int_close_plugin (handle
); }
511 if (dir
!= NULL
) { closedir (dir
); }
513 /* Until a Windows implementation of this code is implemented */
520 err
= 0; /* ran out of plugins -- do nothing */
524 dirhandle
->files
= h
;
525 h
= NULL
; /* dirhandle->files takes ownership */
528 if (filenames
!= NULL
) { krb5int_free_plugin_filenames (filenames
); }
529 if (h
!= NULL
) { krb5int_plugin_file_handle_array_free (h
); }
535 krb5int_close_plugin_dirs (struct plugin_dir_handle
*dirhandle
)
537 if (dirhandle
->files
!= NULL
) {
539 for (i
= 0; dirhandle
->files
[i
] != NULL
; i
++) {
540 krb5int_close_plugin (dirhandle
->files
[i
]);
542 free (dirhandle
->files
);
543 dirhandle
->files
= NULL
;
548 krb5int_free_plugin_dir_data (void **ptrs
)
550 /* Nothing special to be done per pointer. */
555 krb5int_get_plugin_dir_data (struct plugin_dir_handle
*dirhandle
,
564 /* XXX Do we need to add a leading "_" to the symbol name on any
567 Tprintf("get_plugin_data_sym(%s)\n", symname
);
570 p
= calloc (1, sizeof (*p
)); /* calloc initializes to NULL */
571 if (p
== NULL
) { err
= errno
; }
574 if (!err
&& (dirhandle
!= NULL
) && (dirhandle
->files
!= NULL
)) {
577 for (i
= 0; !err
&& (dirhandle
->files
[i
] != NULL
); i
++) {
580 if (krb5int_get_plugin_data (dirhandle
->files
[i
], symname
, &sym
, ep
) == 0) {
584 newp
= realloc (p
, ((count
+ 1) * sizeof (*p
))); /* +1 for NULL */
598 p
= NULL
; /* ptrs takes ownership */
601 if (p
!= NULL
) { free (p
); }
607 krb5int_free_plugin_dir_func (void (**ptrs
)(void))
609 /* Nothing special to be done per pointer. */
614 krb5int_get_plugin_dir_func (struct plugin_dir_handle
*dirhandle
,
616 void (***ptrs
)(void),
623 /* XXX Do we need to add a leading "_" to the symbol name on any
626 Tprintf("get_plugin_data_sym(%s)\n", symname
);
629 p
= calloc (1, sizeof (*p
)); /* calloc initializes to NULL */
630 if (p
== NULL
) { err
= errno
; }
633 if (!err
&& (dirhandle
!= NULL
) && (dirhandle
->files
!= NULL
)) {
636 for (i
= 0; !err
&& (dirhandle
->files
[i
] != NULL
); i
++) {
637 void (*sym
)() = NULL
;
639 if (krb5int_get_plugin_func (dirhandle
->files
[i
], symname
, &sym
, ep
) == 0) {
640 void (**newp
)() = NULL
;
643 newp
= realloc (p
, ((count
+ 1) * sizeof (*p
))); /* +1 for NULL */
657 p
= NULL
; /* ptrs takes ownership */
660 if (p
!= NULL
) { free (p
); }