c6a9a21d57c71e01ba0936711007c4fa0b3f0652
[platform/upstream/krb5.git] / src / util / support / plugins.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* util/support/plugins.c - Plugin module support functions */
3 /*
4  * Copyright 2006, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
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.
11  *
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.
25  */
26
27 #include "k5-platform.h"
28 #include "k5-plugin.h"
29 #if USE_DLOPEN
30 #include <dlfcn.h>
31 #endif
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36 #ifdef HAVE_SYS_PARAM_H
37 #include <sys/param.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #if USE_DLOPEN
44 #ifdef RTLD_GROUP
45 #define GROUP RTLD_GROUP
46 #else
47 #define GROUP 0
48 #endif
49 #ifdef RTLD_NODELETE
50 #define NODELETE RTLD_NODELETE
51 #else
52 #define NODELETE 0
53 #endif
54 #define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | GROUP | NODELETE)
55 #endif
56
57 /*
58  * glibc bug 11941, fixed in release 2.25, can cause an assertion failure in
59  * dlclose() on process exit.  Our workaround is to leak dlopen() handles
60  * (which doesn't typically manifest in leak detection tools because the
61  * handles are still reachable via a global table in libdl).  Because we
62  * dlopen() with RTLD_NODELETE, we weren't going to unload the plugin objects
63  * anyway.
64  */
65 #ifdef __GLIBC_PREREQ
66 #if ! __GLIBC_PREREQ(2, 25)
67 #define dlclose(x)
68 #endif
69 #endif
70
71 #if USE_DLOPEN && USE_CFBUNDLE
72 #include <CoreFoundation/CoreFoundation.h>
73
74 /* Currently CoreFoundation only exists on the Mac so we just use
75  * pthreads directly to avoid creating empty function calls on other
76  * platforms.  If a thread initializer ever gets created in the common
77  * plugin code, move this there */
78 static pthread_mutex_t krb5int_bundle_mutex = PTHREAD_MUTEX_INITIALIZER;
79 #endif
80
81 #include <stdarg.h>
82 static void Tprintf (const char *fmt, ...)
83 {
84 #ifdef DEBUG
85     va_list va;
86     va_start (va, fmt);
87     vfprintf (stderr, fmt, va);
88     va_end (va);
89 #endif
90 }
91
92 struct plugin_file_handle {
93 #if USE_DLOPEN
94     void *dlhandle;
95 #endif
96 #ifdef _WIN32
97     HMODULE hinstPlugin;
98 #endif
99 #if !defined (USE_DLOPEN) && !defined (_WIN32)
100     char dummy;
101 #endif
102 };
103
104 #ifdef _WIN32
105 struct dirent {
106     long d_ino;                 /* inode (always 1 in WIN32) */
107     off_t d_off;                /* offset to this dirent */
108     unsigned short d_reclen;    /* length of d_name */
109     char d_name[_MAX_FNAME+1];  /* filename (null terminated) */
110 };
111
112 typedef struct {
113     intptr_t handle;            /* _findfirst/_findnext handle */
114     short offset;               /* offset into directory */
115     short finished;             /* 1 if there are not more files */
116     struct _finddata_t fileinfo;/* from _findfirst/_findnext */
117     char *dir;                  /* the dir we are reading */
118     struct dirent dent;         /* the dirent to return */
119 } DIR;
120
121 DIR * opendir(const char *dir)
122 {
123     DIR *dp;
124     char *filespec;
125     intptr_t handle;
126     int index;
127
128     filespec = malloc(strlen(dir) + 2 + 1);
129     strcpy(filespec, dir);
130     index = strlen(filespec) - 1;
131     if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
132         filespec[index] = '\0';
133     strcat(filespec, "/*");
134
135     dp = (DIR *)malloc(sizeof(DIR));
136     dp->offset = 0;
137     dp->finished = 0;
138     dp->dir = strdup(dir);
139
140     if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) {
141         if (errno == ENOENT)
142             dp->finished = 1;
143         else {
144             free(filespec);
145             free(dp->dir);
146             free(dp);
147             return NULL;
148         }
149     }
150
151     dp->handle = handle;
152     free(filespec);
153
154     return dp;
155 }
156
157 struct dirent * readdir(DIR *dp)
158 {
159     if (!dp || dp->finished) return NULL;
160
161     if (dp->offset != 0) {
162         if (_findnext(dp->handle, &(dp->fileinfo)) < 0) {
163             dp->finished = 1;
164             return NULL;
165         }
166     }
167     dp->offset++;
168
169     strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
170     dp->dent.d_ino = 1;
171     dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
172     dp->dent.d_off = dp->offset;
173
174     return &(dp->dent);
175 }
176
177 int closedir(DIR *dp)
178 {
179     if (!dp) return 0;
180     _findclose(dp->handle);
181     free(dp->dir);
182     free(dp);
183
184     return 0;
185 }
186 #endif
187
188 long KRB5_CALLCONV
189 krb5int_open_plugin (const char *filepath, struct plugin_file_handle **h, struct errinfo *ep)
190 {
191     long err = 0;
192     struct plugin_file_handle *htmp = NULL;
193     int got_plugin = 0;
194 #if defined(USE_CFBUNDLE) || defined(_WIN32)
195     struct stat statbuf;
196
197     if (!err) {
198         if (stat (filepath, &statbuf) < 0) {
199             err = errno;
200             Tprintf ("stat(%s): %s\n", filepath, strerror (err));
201             k5_set_error(ep, err, _("unable to find plugin [%s]: %s"),
202                          filepath, strerror(err));
203         }
204     }
205 #endif
206
207     if (!err) {
208         htmp = calloc (1, sizeof (*htmp)); /* calloc initializes ptrs to NULL */
209         if (htmp == NULL) { err = ENOMEM; }
210     }
211
212 #if USE_DLOPEN
213     if (!err
214 #if USE_CFBUNDLE
215                  && ((statbuf.st_mode & S_IFMT) == S_IFREG
216                  || (statbuf.st_mode & S_IFMT) == S_IFDIR)
217 #endif /* USE_CFBUNDLE */
218         ) {
219         void *handle = NULL;
220
221 #if USE_CFBUNDLE
222         char executablepath[MAXPATHLEN];
223
224         if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
225             int lock_err = 0;
226             CFStringRef pluginString = NULL;
227             CFURLRef pluginURL = NULL;
228             CFBundleRef pluginBundle = NULL;
229             CFURLRef executableURL = NULL;
230
231             /* Lock around CoreFoundation calls since objects are refcounted
232              * and the refcounts are not thread-safe.  Using pthreads directly
233              * because this code is Mac-specific */
234             lock_err = pthread_mutex_lock(&krb5int_bundle_mutex);
235             if (lock_err) { err = lock_err; }
236
237             if (!err) {
238                 pluginString = CFStringCreateWithCString (kCFAllocatorDefault,
239                                                           filepath,
240                                                           kCFStringEncodingASCII);
241                 if (pluginString == NULL) { err = ENOMEM; }
242             }
243
244             if (!err) {
245                 pluginURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault,
246                                                            pluginString,
247                                                            kCFURLPOSIXPathStyle,
248                                                            true);
249                 if (pluginURL == NULL) { err = ENOMEM; }
250             }
251
252             if (!err) {
253                 pluginBundle = CFBundleCreate (kCFAllocatorDefault, pluginURL);
254                 if (pluginBundle == NULL) { err = ENOENT; } /* XXX need better error */
255             }
256
257             if (!err) {
258                 executableURL = CFBundleCopyExecutableURL (pluginBundle);
259                 if (executableURL == NULL) { err = ENOMEM; }
260             }
261
262             if (!err) {
263                 if (!CFURLGetFileSystemRepresentation (executableURL,
264                                                        true, /* absolute */
265                                                        (UInt8 *)executablepath,
266                                                        sizeof (executablepath))) {
267                     err = ENOMEM;
268                 }
269             }
270
271             if (!err) {
272                 /* override the path the caller passed in */
273                 filepath = executablepath;
274             }
275
276             if (executableURL    != NULL) { CFRelease (executableURL); }
277             if (pluginBundle     != NULL) { CFRelease (pluginBundle); }
278             if (pluginURL        != NULL) { CFRelease (pluginURL); }
279             if (pluginString     != NULL) { CFRelease (pluginString); }
280
281             /* unlock after CFRelease calls since they modify refcounts */
282             if (!lock_err) { pthread_mutex_unlock (&krb5int_bundle_mutex); }
283         }
284 #endif /* USE_CFBUNDLE */
285
286         if (!err) {
287             handle = dlopen(filepath, PLUGIN_DLOPEN_FLAGS);
288             if (handle == NULL) {
289                 const char *e = dlerror();
290                 if (e == NULL)
291                     e = _("unknown failure");
292                 Tprintf ("dlopen(%s): %s\n", filepath, e);
293                 err = ENOENT; /* XXX */
294                 k5_set_error(ep, err, _("unable to load plugin [%s]: %s"),
295                              filepath, e);
296             }
297         }
298
299         if (!err) {
300             got_plugin = 1;
301             htmp->dlhandle = handle;
302             handle = NULL;
303         }
304
305         if (handle != NULL) { dlclose (handle); }
306     }
307 #endif /* USE_DLOPEN */
308
309 #ifdef _WIN32
310     if (!err && (statbuf.st_mode & S_IFMT) == S_IFREG) {
311         HMODULE handle = NULL;
312
313         handle = LoadLibrary(filepath);
314         if (handle == NULL) {
315             Tprintf ("Unable to load dll: %s\n", filepath);
316             err = ENOENT; /* XXX */
317             k5_set_error(ep, err, _("unable to load DLL [%s]"), filepath);
318         }
319
320         if (!err) {
321             got_plugin = 1;
322             htmp->hinstPlugin = handle;
323             handle = NULL;
324         }
325
326         if (handle != NULL)
327             FreeLibrary(handle);
328     }
329 #endif
330
331     if (!err && !got_plugin) {
332         err = ENOENT;  /* no plugin or no way to load plugins */
333         k5_set_error(ep, err, _("plugin unavailable: %s"), strerror(err));
334     }
335
336     if (!err) {
337         *h = htmp;
338         htmp = NULL;  /* h takes ownership */
339     }
340
341     free(htmp);
342
343     return err;
344 }
345
346 static long
347 krb5int_get_plugin_sym (struct plugin_file_handle *h,
348                         const char *csymname, int isfunc, void **ptr,
349                         struct errinfo *ep)
350 {
351     long err = 0;
352     void *sym = NULL;
353
354 #if USE_DLOPEN
355     if (!err && !sym && (h->dlhandle != NULL)) {
356         /* XXX Do we need to add a leading "_" to the symbol name on any
357            modern platforms?  */
358         sym = dlsym (h->dlhandle, csymname);
359         if (sym == NULL) {
360             const char *e = dlerror (); /* XXX copy and save away */
361             if (e == NULL)
362                 e = "unknown failure";
363             Tprintf ("dlsym(%s): %s\n", csymname, e);
364             err = ENOENT; /* XXX */
365             k5_set_error(ep, err, "%s", e);
366         }
367     }
368 #endif
369
370 #ifdef _WIN32
371     LPVOID lpMsgBuf;
372     DWORD dw;
373
374     if (!err && !sym && (h->hinstPlugin != NULL)) {
375         sym = GetProcAddress(h->hinstPlugin, csymname);
376         if (sym == NULL) {
377             const char *e = "unable to get dll symbol"; /* XXX copy and save away */
378             Tprintf ("GetProcAddress(%s): %i\n", csymname, GetLastError());
379             err = ENOENT; /* XXX */
380             k5_set_error(ep, err, "%s", e);
381
382             dw = GetLastError();
383             if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
384                               FORMAT_MESSAGE_FROM_SYSTEM,
385                               NULL,
386                               dw,
387                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
388                               (LPTSTR) &lpMsgBuf,
389                               0, NULL )) {
390
391                 fprintf (stderr, "unable to get dll symbol, %s\n", (LPCTSTR)lpMsgBuf);
392                 LocalFree(lpMsgBuf);
393             }
394         }
395     }
396 #endif
397
398     if (!err && (sym == NULL)) {
399         err = ENOENT;  /* unimplemented */
400     }
401
402     if (!err) {
403         *ptr = sym;
404     }
405
406     return err;
407 }
408
409 long KRB5_CALLCONV
410 krb5int_get_plugin_data (struct plugin_file_handle *h, const char *csymname,
411                          void **ptr, struct errinfo *ep)
412 {
413     return krb5int_get_plugin_sym (h, csymname, 0, ptr, ep);
414 }
415
416 long KRB5_CALLCONV
417 krb5int_get_plugin_func (struct plugin_file_handle *h, const char *csymname,
418                          void (**ptr)(), struct errinfo *ep)
419 {
420     void *dptr = NULL;
421     long err = krb5int_get_plugin_sym (h, csymname, 1, &dptr, ep);
422     if (!err) {
423         /* Cast function pointers to avoid code duplication */
424         *ptr = (void (*)()) dptr;
425     }
426     return err;
427 }
428
429 void KRB5_CALLCONV
430 krb5int_close_plugin (struct plugin_file_handle *h)
431 {
432 #if USE_DLOPEN
433     if (h->dlhandle != NULL) { dlclose(h->dlhandle); }
434 #endif
435 #ifdef _WIN32
436     if (h->hinstPlugin != NULL) { FreeLibrary(h->hinstPlugin); }
437 #endif
438     free (h);
439 }
440
441 /* autoconf docs suggest using this preference order */
442 #if HAVE_DIRENT_H || USE_DIRENT_H
443 #include <dirent.h>
444 #define NAMELEN(D) strlen((D)->d_name)
445 #else
446 #ifndef _WIN32
447 #define dirent direct
448 #define NAMELEN(D) ((D)->d->namlen)
449 #else
450 #define NAMELEN(D) strlen((D)->d_name)
451 #endif
452 #if HAVE_SYS_NDIR_H
453 # include <sys/ndir.h>
454 #elif HAVE_SYS_DIR_H
455 # include <sys/dir.h>
456 #elif HAVE_NDIR_H
457 # include <ndir.h>
458 #endif
459 #endif
460
461 static long
462 krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
463 {
464     long err = 0;
465
466     *harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
467     if (*harray == NULL) { err = ENOMEM; }
468
469     return err;
470 }
471
472 static long
473 krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,
474                                       struct plugin_file_handle *p)
475 {
476     long err = 0;
477     struct plugin_file_handle **newharray = NULL;
478     size_t newcount = *count + 1;
479
480     newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
481     if (newharray == NULL) {
482         err = ENOMEM;
483     } else {
484         newharray[newcount - 1] = p;
485         newharray[newcount] = NULL;
486         *count = newcount;
487         *harray = newharray;
488     }
489
490     return err;
491 }
492
493 static void
494 krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
495 {
496     if (harray != NULL) {
497         int i;
498         for (i = 0; harray[i] != NULL; i++) {
499             krb5int_close_plugin (harray[i]);
500         }
501         free (harray);
502     }
503 }
504
505 #if TARGET_OS_MAC
506 #define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }
507 #elif defined(_WIN32)
508 #define FILEEXTS  { "", ".dll", NULL }
509 #else
510 #define FILEEXTS  { "", ".so", NULL }
511 #endif
512
513
514 static void
515 krb5int_free_plugin_filenames (char **filenames)
516 {
517     if (filenames != NULL) {
518         int i;
519         for (i = 0; filenames[i] != NULL; i++) {
520             free (filenames[i]);
521         }
522         free (filenames);
523     }
524 }
525
526
527 static long
528 krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
529 {
530     long err = 0;
531     static const char *const fileexts[] = FILEEXTS;
532     char **tempnames = NULL;
533     size_t bases_count = 0;
534     size_t exts_count = 0;
535     size_t i;
536
537     if (!filebases) { err = EINVAL; }
538     if (!filenames) { err = EINVAL; }
539
540     if (!err) {
541         for (i = 0; filebases[i]; i++) { bases_count++; }
542         for (i = 0; fileexts[i]; i++) { exts_count++; }
543         tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));
544         if (!tempnames) { err = ENOMEM; }
545     }
546
547     if (!err) {
548         size_t j;
549         for (i = 0; !err && filebases[i]; i++) {
550             for (j = 0; !err && fileexts[j]; j++) {
551                 if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",
552                              filebases[i], fileexts[j]) < 0) {
553                     tempnames[(i*exts_count)+j] = NULL;
554                     err = ENOMEM;
555                 }
556             }
557         }
558         tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */
559     }
560
561     if (!err) {
562         *filenames = tempnames;
563         tempnames = NULL;
564     }
565
566     krb5int_free_plugin_filenames(tempnames);
567
568     return err;
569 }
570
571
572 /* Takes a NULL-terminated list of directories.  If filebases is NULL, filebases is ignored
573  * all plugins in the directories are loaded.  If filebases is a NULL-terminated array of names,
574  * only plugins in the directories with those name (plus any platform extension) are loaded. */
575
576 long KRB5_CALLCONV
577 krb5int_open_plugin_dirs (const char * const *dirnames,
578                           const char * const *filebases,
579                           struct plugin_dir_handle *dirhandle,
580                           struct errinfo *ep)
581 {
582     long err = 0;
583     struct plugin_file_handle **h = NULL;
584     size_t count = 0;
585     char **filenames = NULL;
586     int i;
587
588     if (!err) {
589         err = krb5int_plugin_file_handle_array_init (&h);
590     }
591
592     if (!err && (filebases != NULL)) {
593         err = krb5int_get_plugin_filenames (filebases, &filenames);
594     }
595
596     for (i = 0; !err && dirnames[i] != NULL; i++) {
597         if (filenames != NULL) {
598             /* load plugins with names from filenames from each directory */
599             int j;
600
601             for (j = 0; !err && filenames[j] != NULL; j++) {
602                 struct plugin_file_handle *handle = NULL;
603                 char *filepath = NULL;
604
605                 if (!err) {
606                     if (asprintf(&filepath, "%s/%s", dirnames[i], filenames[j]) < 0) {
607                         filepath = NULL;
608                         err = ENOMEM;
609                     }
610                 }
611
612                 if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {
613                     err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
614                     if (!err)
615                         handle = NULL; /* h takes ownership */
616                 }
617
618                 free(filepath);
619                 if (handle   != NULL) { krb5int_close_plugin (handle); }
620             }
621         } else {
622             /* load all plugins in each directory */
623             DIR *dir = opendir (dirnames[i]);
624
625             while (dir != NULL && !err) {
626                 struct dirent *d = NULL;
627                 char *filepath = NULL;
628                 struct plugin_file_handle *handle = NULL;
629
630                 d = readdir (dir);
631                 if (d == NULL) { break; }
632
633                 if ((strcmp (d->d_name, ".") == 0) ||
634                     (strcmp (d->d_name, "..") == 0)) {
635                     continue;
636                 }
637
638                 if (!err) {
639                     int len = NAMELEN (d);
640                     if (asprintf(&filepath, "%s/%*s", dirnames[i], len, d->d_name) < 0) {
641                         filepath = NULL;
642                         err = ENOMEM;
643                     }
644                 }
645
646                 if (!err) {
647                     if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
648                         err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
649                         if (!err) { handle = NULL; }  /* h takes ownership */
650                     }
651                 }
652
653                 free(filepath);
654                 if (handle    != NULL) { krb5int_close_plugin (handle); }
655             }
656
657             if (dir != NULL) { closedir (dir); }
658         }
659     }
660
661     if (err == ENOENT) {
662         err = 0;  /* ran out of plugins -- do nothing */
663     }
664
665     if (!err) {
666         dirhandle->files = h;
667         h = NULL;  /* dirhandle->files takes ownership */
668     }
669
670     if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
671     if (h         != NULL) { krb5int_plugin_file_handle_array_free (h); }
672
673     return err;
674 }
675
676 void KRB5_CALLCONV
677 krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
678 {
679     if (dirhandle->files != NULL) {
680         int i;
681         for (i = 0; dirhandle->files[i] != NULL; i++) {
682             krb5int_close_plugin (dirhandle->files[i]);
683         }
684         free (dirhandle->files);
685         dirhandle->files = NULL;
686     }
687 }
688
689 void KRB5_CALLCONV
690 krb5int_free_plugin_dir_data (void **ptrs)
691 {
692     /* Nothing special to be done per pointer.  */
693     free(ptrs);
694 }
695
696 long KRB5_CALLCONV
697 krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
698                              const char *symname,
699                              void ***ptrs,
700                              struct errinfo *ep)
701 {
702     long err = 0;
703     void **p = NULL;
704     size_t count = 0;
705
706     /* XXX Do we need to add a leading "_" to the symbol name on any
707        modern platforms?  */
708
709     Tprintf("get_plugin_data_sym(%s)\n", symname);
710
711     if (!err) {
712         p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
713         if (p == NULL) { err = ENOMEM; }
714     }
715
716     if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
717         int i = 0;
718
719         for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
720             void *sym = NULL;
721
722             if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
723                 void **newp = NULL;
724
725                 count++;
726                 newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
727                 if (newp == NULL) {
728                     err = ENOMEM;
729                 } else {
730                     p = newp;
731                     p[count - 1] = sym;
732                     p[count] = NULL;
733                 }
734             }
735         }
736     }
737
738     if (!err) {
739         *ptrs = p;
740         p = NULL; /* ptrs takes ownership */
741     }
742
743     free(p);
744
745     return err;
746 }
747
748 void KRB5_CALLCONV
749 krb5int_free_plugin_dir_func (void (**ptrs)(void))
750 {
751     /* Nothing special to be done per pointer.  */
752     free(ptrs);
753 }
754
755 long KRB5_CALLCONV
756 krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
757                              const char *symname,
758                              void (***ptrs)(void),
759                              struct errinfo *ep)
760 {
761     long err = 0;
762     void (**p)() = NULL;
763     size_t count = 0;
764
765     /* XXX Do we need to add a leading "_" to the symbol name on any
766        modern platforms?  */
767
768     Tprintf("get_plugin_data_sym(%s)\n", symname);
769
770     if (!err) {
771         p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
772         if (p == NULL) { err = ENOMEM; }
773     }
774
775     if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
776         int i = 0;
777
778         for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
779             void (*sym)() = NULL;
780
781             if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
782                 void (**newp)() = NULL;
783
784                 count++;
785                 newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
786                 if (newp == NULL) {
787                     err = ENOMEM;
788                 } else {
789                     p = newp;
790                     p[count - 1] = sym;
791                     p[count] = NULL;
792                 }
793             }
794         }
795     }
796
797     if (!err) {
798         *ptrs = p;
799         p = NULL; /* ptrs takes ownership */
800     }
801
802     free(p);
803
804     return err;
805 }