1
/* $Id: er_pluginlist.c,v 1.38 2010/07/09 08:33:06 kbanse Exp $
3
* This file is part of the ESO Common Pipeline Library
4
* Copyright (C) 2001-2006 European Southern Observatory
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
* $Date: 2010/07/09 08:33:06 $
25
* $Name: esorex-3_9_0 $
39
#include <sys/types.h>
49
#include "er_fileutils.h"
50
#include "er_stringarray.h"
51
#include "er_plugin.h"
52
#include "er_pluginlist.h"
53
#include "er_paramutils.h"
55
extern int MyOS; /* 1 = Linux, 0 = Mac OSX */
58
* Symbolic constants used in the search_directory() function
61
#define INDENT_INCR 2 /* Amount to increment per indentation */
62
#define INDENT_STR "| " /* String to use for each indentation */
63
#define ENTRY_STR "+-" /* String to use at the start of each entry */
64
#define SEP_STR "/" /* Directory separator ("/" on UNIX) */
67
* symbols S_IFMT as well as S_IFREG seem not to be defined even though the
68
* relevant include files are there ...
72
#define S_IFMT 0170000 /* bitmask for the file type bitfields */
73
#define S_IFREG 0100000 /* regular file */
78
* @defgroup esorex_pluginlist EsoRex Plugin Listing Functions
80
* EsoRex Plugin Listing Functions.
89
/**********************************************************************/
92
* Test whether a file is a valid plugin file
94
* @param full_filename
95
* This is the full filename. That is, it includes the full path
96
* specification and any extension too.
99
* TRUE if the specified module is valid, or FALSE if not.
101
* This function tests a fully specified filename to see whether or not
102
* it may be loaded as a dynamic library.
104
/**********************************************************************/
106
static int plugin_valid_file (const char *full_filename)
109
char *ext; /* File extension */
110
lt_dlhandle module = NULL; /* Handle to the dynamic library */
112
int (*get_plugin_list) (cpl_pluginlist *) = NULL;
114
/* plugins use extension ".so" also on the Mac ...
115
char Linux_ext[] = ".so", Mac_ext[] = ".dylib";
117
char Linux_ext[] = ".so", Mac_ext[] = ".so";
123
/* If there was no filename, then return */
124
if (full_filename == NULL) return 0;
126
/* Get the file extension */
127
ext = strrchr (full_filename, '.');
129
/* Reject any file which doesn't have the correct file extension */
130
if (ext == NULL) return 0;
132
/* check against ext of dynamic object libraries:
133
before (Derek) checked against LTDL_SHLIB_EXT, a variable of libtool containing
135
if (strcmp (ext, LTDL_SHLIB_EXT) != 0) return 0;
136
this variable has disappeard in the new 2.x.y version of libtool (e.g. openSuse 11.1)
137
so now (as of Feb. 5, 2009) we test against fixed char string ".la" which is
138
the extension for dynamic object libraries on Linux systems */
140
if (MyOS == 1) /* dynamic libs ext for Linux */
141
fixed_ext = Linux_ext;
142
else /* dynamic libs ext for MAc */
144
if (strcmp (ext, fixed_ext) != 0) return 0;
147
/* Attempt to open the file as a dynamic library module
148
unless we linked statically */
151
get_plugin_list = &cpl_plugin_get_info;
154
/* printf("opening %s\n",full_filename); */
156
module = lt_dlopen (full_filename);
158
/* If this didn't load properly, then return */
161
const char *dl_errstring;
163
dl_errstring = lt_dlerror();
164
cpl_msg_warning (er_func, "lt_dlopen (%s) returned NULL,\nlt_dlerror() = %s\n",
165
full_filename,dl_errstring);
170
/* Check that it has the required plugin function */
171
get_plugin_list = (int (*)()) lt_dlsym (module, "cpl_plugin_get_info");
172
lt_dlclose (module); /* Close the module */
175
if (get_plugin_list == NULL) return 0;
177
/* If we get this far, then it was obviously a valid plugin file */
185
/**********************************************************************/
187
* @brief Recursively searches a directory tree for plugin files
189
* @param dirname The name of the directory to search.
190
* @param list_of_pllibs A list of filenames of validated plugin
191
* libraries. If this function finds any more,
192
* then they are added to this list.
194
* Scans all the files in the specified directory. If any of these are
195
* regular files, then another function is called to check whether or
196
* not they are a valid plugin file. If so, then this filename is added
197
* to the provided @a list_of_pllibs . In the case that one of the
198
* scanned files is actually a directory, then this function is
199
* (recursively) called on that function. That way, evenutally, all
200
* files in or below the original directory are searched.
204
* @todo Implement some check to prevent endless looping, for example,
205
* in the event of an unforeseen error during the recursive calling
209
/**********************************************************************/
211
static void search_directory (const char * dirname,
212
er_stringarray_t * list_of_pllibs )
215
struct stat finfo; /* File information */
216
struct dirent *entry; /* Entry in a given directory listing */
218
DIR *dp; /* Pointer to a directory */
219
char *fqfn; /* Fully qualified filename (path+file+ext) */
221
static int indent = 0; /* Level of indentation */
222
int valid; /* Is the specified file a valid plugin file */
224
int filename_len = 4096; /* the "usual" FILENAME_MAX */
232
if (list_of_pllibs == NULL) return;
235
if (ER_TRACEFLAG) (void) printf("Directory: %s", dirname);
237
dp = opendir (dirname); /* Open the directory */
240
cpl_msg_warning (er_func, "Unable to open directory %s", dirname);
245
ldir = (int) strlen(dirname);
246
fqfn = cpl_malloc ((size_t) filename_len);
247
if (fqfn == NULL) return;
249
while ((entry = readdir (dp)) != NULL)
251
/* Ignore hidden files, current directory and parent directory */
252
if (entry->d_name[0] == '.') continue;
254
/* We have a directory, so recursively search it with this function */
255
lentry = (int) strlen(entry->d_name);
256
if ((lentry+ldir+4) > filename_len) /* ensure enough memory is allocated */
258
filename_len = (lentry+ldir+4) * 2;
259
er_enlarge("search_directory",&fqfn,filename_len);
261
(void) sprintf (fqfn, "%s%s%s", dirname, SEP_STR, entry->d_name);
263
/* Get information about the current entry (file) */
264
status = stat (fqfn, &finfo);
267
if ((finfo.st_mode & S_IFMT) == S_IFREG)
269
if (ER_TRACEFLAG) printf("S_IFMT, S_IFREG = %o, %o for %s\n",
270
S_IFMT,S_IFREG,fqfn);
272
valid = plugin_valid_file (fqfn);
275
/* If we get this far, then we've found a plugin */
276
/* Add it to the list of plugins that we have so far */
278
er_stringarray_append (list_of_pllibs, fqfn);
279
if (ER_TRACEFLAG) printf("%s is a RECIPE...\n",fqfn);
283
if (ER_TRACEFLAG) printf("%s not a recipe...\n",fqfn);
286
else if (S_ISDIR (finfo.st_mode))
288
/* Recursively search any subdirectory that we find */
289
search_directory (fqfn, list_of_pllibs);
292
} /* End if valid status */
295
/* Release the memory allocated to the filename */
299
indent -= INDENT_INCR; /* Decrement the indentation again */
302
} /* End of search_directory() */
307
/**********************************************************************/
309
* @brief Creates a string array of plugin libraries in a given directory
311
* @param directories_to_search An array of directory names.
313
* @returns Pointer to stringarray
315
* This function establishes a new stringarray, @c list_of_pllibs .
316
* It then calls the @c search_directory function to recursively search
317
* the specified @ directory_to_search . @c search_directory will add
318
* the filenames of any found plugin files to @c list_of_pllibs . When
319
* this operation is complete, the function will return the stringarray.
322
/**********************************************************************/
324
er_stringarray_t *er_pluginlist_create_list (char ** directories_to_search)
327
er_stringarray_t *list_of_pllibs; /* List of plugin library (filenames) */
328
er_stringarray_t *reduced_list; /* List of plugin library (filenames) */
330
char **dir_str; /* Pointer to the list of directories */
332
int i1, i2; /* Indices into the plugin list */
340
/* Obviously, we need a directory to search, so bail out if there isn't one */
342
if (directories_to_search == NULL) return NULL;
345
/* Create a new array of strings to contain any plugin filenames */
347
if ((list_of_pllibs = er_stringarray_new ()) == NULL) return NULL;
349
for (dir_str = directories_to_search; *dir_str != NULL; dir_str++)
352
* Recursively search the specified directory, adding any found
353
* plugins to the list
355
search_directory (er_fileutils_tilde_replace (*dir_str),
359
/* Create a new array of strings for the consolidated list */
361
reduced_list = er_stringarray_new ();
362
if (reduced_list != NULL)
364
/* Consolidate the list - there may be duplicate entries */
365
for (i1=0; i1<er_stringarray_size (list_of_pllibs); i1++)
368
for (i2=0; i2<er_stringarray_size (reduced_list); i2++)
370
if (strcmp (er_stringarray_get (list_of_pllibs, i1),
371
er_stringarray_get (reduced_list, i2)) == 0)
379
er_stringarray_append (reduced_list,
380
er_stringarray_get (list_of_pllibs, i1));
385
er_stringarray_delete (list_of_pllibs);
387
/* Return the (possibly empty) list of plugin filenames */
391
} /* End of er_pluginlist_create_list() */
396
/**********************************************************************/
398
* @brief Gets the full pathname of the library containing the named plugin
400
* @param list_of_pllibs String array of plugin libraries
401
* @param plugin_name Name to look for in pllibs given in string array
403
* @returns the length of the library path found,
404
* or 0, in the case that no suitable library was found
406
* The function takes a list of plugin library path names, and opens
407
* all of them searching for the named plugin: @c plugin_name . While
408
* doing this, the funciton also checks for the existence of multiple
409
* plugins with the same name. Should multiples be found, then the
410
* function will print a warning message to this effect, indicating
411
* which one it chose to use.
413
* The choice is made based on the version number. If the same version
414
* number exists multiple times, then the first one it finds in the
415
* library path is used.
418
/**********************************************************************/
420
int er_pluginlist_get_libpath (er_stringarray_t * list_of_pllibs,
421
const char * plugin_name, char *libpath)
424
char *cptr, fully_qualified_library_name[MAXSTRLENCONF];
426
lt_dlhandle module = NULL;
428
cpl_pluginlist *pl_list = NULL;
429
cpl_plugin *pl = NULL;
431
int i = 0, e_code = 0;
432
int f_report_error = 1;
437
int (*get_plugin_list) (cpl_pluginlist *) = NULL;
440
unsigned long this_version = 0;
441
unsigned long best_version = 0;
447
fully_qualified_library_name[0] = '\0';
449
if (list_of_pllibs == NULL)
451
cpl_msg_warning (er_func, "Empty plugin list");
456
for (i=0; i< er_stringarray_size (list_of_pllibs); i++)
458
cptr = er_stringarray_get (list_of_pllibs, i);
459
m = (int) strlen(cptr);
460
if (m > (MAXSTRLENCONF-1))
462
cpl_msg_error (er_func, "size of plugin lib > %d ...", MAXSTRLENCONF);
466
(void) strcpy(fully_qualified_library_name, cptr);
469
get_plugin_list = &cpl_plugin_get_info;
472
module = lt_dlopen (fully_qualified_library_name); /* try to open library */
475
cpl_msg_error (er_func, "Could not open %s: %s",
476
fully_qualified_library_name, lt_dlerror ());
480
/* get plugin list */
482
get_plugin_list = (int (*)()) lt_dlsym (module, "cpl_plugin_get_info");
485
/* Cannot use lt_dlerror due to bug in 1.4.2 version of libtool */
487
if (get_plugin_list == NULL)
489
cpl_msg_error (er_func, "Could not find cpl_plugin_get_info() "
490
"index function for plugin library (%s): %s",
491
fully_qualified_library_name, lt_dlerror ());
499
/* Make a pluginlist containing all the plugins in the library */
501
pl_list = cpl_pluginlist_new ();
502
e_code = get_plugin_list (pl_list);
505
cpl_msg_warning (er_func,
506
"Unexpected error (%d) in recovering a pluginlist from "
507
"library '%s'", e_code,
508
fully_qualified_library_name);
511
/* Check the "one-plugin-per-library" assumption */
513
m = cpl_pluginlist_get_size (pl_list);
516
cpl_msg_warning (er_func, "No plugins contained within "
517
"library '%s'", fully_qualified_library_name);
521
cpl_msg_warning (er_func, "Multiple plugins contained within "
522
"library '%s'", fully_qualified_library_name);
525
/* Once we have a valid library, fetch the plugin out of it */
527
pl = cpl_pluginlist_find (pl_list, plugin_name);
529
/* Double check again, to make sure we got it */
532
{ /* Increment a counter of all instances that we've found */
535
/* Get the version number of this plugin */
537
this_version = cpl_plugin_get_version (pl);
539
/* Make sure the version number is value (i.e. non-zero) */
541
if (this_version == 0)
543
cpl_msg_warning (er_func,
544
"Recipe '%s' (in library '%s') has an invalid version number",
545
plugin_name, fully_qualified_library_name);
548
/* Now see if this version is later than the best we have so far */
550
if (this_version > best_version)
551
{ /* size check done above ... */
552
(void) strcpy(libpath, fully_qualified_library_name);
553
best_version = this_version;
557
else if (this_version == best_version)
563
cpl_pluginlist_delete (pl_list);
573
cpl_msg_error (er_func,
574
"Multiple copies (%d) of the latest version of recipe '%s' were found. "
575
"Using path '%s' ('%s' version %lu)", num_best, plugin_name, libpath,
576
plugin_name, best_version);
578
else if (num_copies > 1)
580
cpl_msg_warning (er_func,
581
"Older copies (%d) of recipe '%s' were also found. "
582
"Using latest version, path '%s' ('%s' version %lu)",
583
(num_copies - 1), plugin_name, libpath, plugin_name, best_version);
586
return ((int)strlen(libpath));
588
} /* End of er_pluginlist_get_libpath() */
593
/**********************************************************************/
595
* @brief Gets a plugin, of a given name, from a given library.
597
* @param list_of_pllibs String array of pllibs
598
* @param plugin_name Name of plugin to recover.
599
* @param module (output) A handle to the plugin library. This
600
* handle should be close (with lt_dlclose) when
601
* the returned plugin is no longer used. If the
602
* requested plugin could not be found, NULL is
605
* @returns Pointer to Plugin, or NULL if the requested plugin could not
608
* @warning At this stage, this function is only experimental. It should
609
* not be used in the normal EsoRex build/execution.
612
/**********************************************************************/
614
cpl_plugin *er_pluginlist_get_plugin (const char * library_name,
615
const char * plugin_name,
619
const char *fn = "er_pluginlist_get_plugin";
620
cpl_pluginlist *pl_list = NULL;
621
cpl_plugin *pl = NULL, *pl_copy = NULL;
624
int (*get_plugin_list) (cpl_pluginlist *) = NULL;
630
if (library_name == NULL)
632
cpl_msg_error (fn, "NULL pointer received instead of library_name");
635
if (plugin_name == NULL)
637
cpl_msg_error (fn, "NULL pointer received instead of plugin_name");
641
pl_list = cpl_pluginlist_new ();
643
/* Try to open library, unless we link statically */
646
get_plugin_list = &cpl_plugin_get_info;
649
*module = lt_dlopen (library_name);
652
cpl_msg_error (fn, "Could not open %s: %s", library_name, lt_dlerror ());
657
/* get plugin list */
659
get_plugin_list = (int (*)()) lt_dlsym (*module, "cpl_plugin_get_info");
663
/* lt_dlclose (module) will be done in caller routine plugin_process_plugin() */
665
if (get_plugin_list == NULL)
667
cpl_msg_error (fn, "Could not find cpl_plugin_get_info() "
668
"index function for plugin library (%s): %s",
669
library_name, lt_dlerror ());
674
e_code = get_plugin_list (pl_list);
679
"Unexpected error (%d) in recovering a pluginlist from "
680
"library '%s'", e_code, library_name);
684
/* Check the "one-plugin-per-library" assumption */
686
mm = cpl_pluginlist_get_size (pl_list);
689
cpl_msg_warning (fn, "Multiple plugins contained within "
690
"library '%s'", library_name);
694
cpl_msg_warning (fn, "No plugins contained within "
695
"library '%s'", library_name);
700
/* Now search for the plugin in the specified library */
702
pl = cpl_pluginlist_find (pl_list, plugin_name);
705
/* If found, make a copy of it (remains NULL otherwise) */
709
pl_copy = cpl_plugin_new ();
710
e_code = cpl_plugin_copy (pl_copy, pl);
711
if (e_code != CPL_ERROR_NONE)
713
cpl_msg_error (fn, "Failed to make internal copy of plugin");
717
/* Delete the plugin list, and return the pointer to the copy */
720
cpl_pluginlist_delete (pl_list);
723
} /* End of er_pluginlist_get_plugin() */
728
/**********************************************************************/
730
* @brief Creates a string array of the names of available plugins.
732
* @param list_of_pllibs String array of pllibs
733
* @retval list_of_plugins String array of plugin names
735
* This function creates a string array of all plugins contained in list
736
* of pllibs specified in a string array. The function does not display
739
/**********************************************************************/
741
void er_pluginlist_create_cache (er_stringarray_t * list_of_pllibs,
742
er_stringarray_t * list_of_plugin_names)
745
const char *fn = "er_pluginlist_create_cache";
747
char *cptr, fully_qualified_library_name[MAXSTRLENCONF];
749
cpl_plugin *tplugin = NULL;
751
lt_dlhandle module = NULL;
753
int m, e_code = 0, i = 0;
754
int (*get_plugin_list) (cpl_pluginlist *) = NULL;
760
if (list_of_pllibs == NULL) return;
762
fully_qualified_library_name[0] = '\0';
764
for (i = 0; i < er_stringarray_size (list_of_pllibs); i++)
766
cpl_pluginlist *pl_list = cpl_pluginlist_new ();
767
cptr = er_stringarray_get (list_of_pllibs, i);
769
m = (int) strlen(cptr);
770
if (m > (MAXSTRLENCONF-1))
772
cpl_msg_error (er_func, "size of plugin lib > %d ...", MAXSTRLENCONF);
775
(void) strcpy(fully_qualified_library_name,cptr);
778
/* try to open library, unless we link statically */
781
get_plugin_list = &cpl_plugin_get_info;
784
module = lt_dlopen (fully_qualified_library_name);
787
cpl_msg_error (fn, "Could not open %s: %s",
788
fully_qualified_library_name, lt_dlerror ());
789
cpl_pluginlist_delete (pl_list);
793
/* get plugin list */
795
get_plugin_list = (int (*)()) lt_dlsym (module, "cpl_plugin_get_info");
798
if (get_plugin_list == NULL)
800
cpl_msg_error (fn, "Could not find cpl_plugin_get_info()"
801
" of plugin library %s",
802
fully_qualified_library_name);
803
cpl_pluginlist_delete (pl_list);
811
e_code = get_plugin_list (pl_list);
813
tplugin = cpl_pluginlist_get_first (pl_list);
814
while (tplugin != NULL)
816
if (cpl_plugin_get_api (tplugin) == CPL_PLUGIN_API)
818
if (cpl_plugin_get_type (tplugin) & CPL_PLUGIN_TYPE_RECIPE)
820
er_stringarray_append (list_of_plugin_names,
821
(char *) cpl_plugin_get_name (tplugin));
825
tplugin = cpl_pluginlist_get_next (pl_list);
827
} /* End of while() */
829
cpl_pluginlist_delete (pl_list);
836
} /* End of for(each name in list_of_pllibs) */
840
} /* End of er_pluginlist_create_cache() */
845
/**********************************************************************/
847
* @brief Neatly print a list of all the plugins.
849
* @param list_of_pllibs String array of plugin library names.
851
* This function will take a stringarray, @a list_of_pllibs , which
852
* contains the names of all the plugin files (i.e. the dynamic libraries)
853
* which are visible within the current path. It then extracts from these
854
* files the plugins themselves, and makes use of CPL calls to access
855
* the name and synopsis of each individual file, before printing them.
857
/**********************************************************************/
859
void er_pluginlist_print_list (er_stringarray_t * list_of_pllibs)
862
char *ccptr, fully_qualified_library_name[MAXSTRLENCONF];
864
cpl_plugin *tplugin = NULL;
866
lt_dlhandle module = NULL;
868
int (*get_plugin_list) (cpl_pluginlist *) = NULL;
870
int m, e_code = 0, i = 0;
877
fully_qualified_library_name[0] = '\0';
879
if (list_of_pllibs == NULL) return;
882
(void) printf("List of Available Recipes :\n\n");
884
/* Loop through each dynamic library file that we have */
886
for (i=0; i < er_stringarray_size(list_of_pllibs); i++)
888
cpl_pluginlist *pl_list = cpl_pluginlist_new ();
890
ccptr = er_stringarray_get (list_of_pllibs, i);
891
m = (int) strlen(ccptr);
892
if (m > (MAXSTRLENCONF-1))
894
cpl_msg_error (er_func, "size of plugin lib > %d ...", MAXSTRLENCONF);
898
(void) strcpy(fully_qualified_library_name,ccptr);
901
get_plugin_list = &cpl_plugin_get_info;
904
module = lt_dlopen (fully_qualified_library_name); /* try to open library */
907
cpl_msg_error (er_func, "Could not open %s: %s",
908
fully_qualified_library_name, lt_dlerror ());
909
cpl_pluginlist_delete (pl_list);
913
get_plugin_list = (int (*)()) lt_dlsym (module, "cpl_plugin_get_info");
916
if (get_plugin_list == NULL)
918
cpl_msg_error (er_func, "Could not find the cpl_plugin_get_info() function"
919
" of plugin library %s", fully_qualified_library_name);
920
cpl_pluginlist_delete (pl_list);
929
e_code = get_plugin_list (pl_list);
930
if (e_code != CPL_ERROR_NONE)
932
cpl_msg_error (er_func, "Unable to read plugin list");
933
cpl_pluginlist_delete (pl_list);
942
/* Get the first plugin in the list */
944
tplugin = cpl_pluginlist_get_first (pl_list);
946
/* Loop through all the plugins that we can find */
948
while (tplugin != NULL)
949
{ /* Check that we are printing appropriate plugins only */
950
if (cpl_plugin_get_api (tplugin) != CPL_PLUGIN_API) continue;
951
if (!(cpl_plugin_get_type (tplugin) & CPL_PLUGIN_TYPE_RECIPE)) continue;
953
er_paramutils_print_key_desc ("", cpl_plugin_get_name (tplugin),
954
cpl_plugin_get_synopsis (tplugin));
956
num_plugins++; /* Increment the plugin counter */
958
/* Get the next plugin in the list */
959
tplugin = cpl_pluginlist_get_next (pl_list);
962
cpl_pluginlist_delete (pl_list);
968
} /* End of for(each name in list_of_pllibs) */
970
/* Print a warning message if no plugins were listed */
972
if (num_plugins == 0)
973
(void) printf(" No recipes were found in the specified recipe directory.\n");
979
} /* End of er_pluginlist_print_list() */