1/* 2 * "$Id: module.c,v 1.26 2006/09/28 15:40:05 m0m Exp $" 3 * 4 * Gutenprint module loader - load modules with libltdl/libdl. 5 * 6 * Copyright 2002 Roger Leigh (rleigh@debian.org) 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the Free 10 * Software Foundation; either version 2 of the License, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 */ 22 23#ifdef HAVE_CONFIG_H 24#include <config.h> 25#endif 26#include <gutenprint/gutenprint.h> 27#include "gutenprint-internal.h" 28#include <gutenprint/gutenprint-intl-internal.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <libgen.h> 33#include <errno.h> 34#include <unistd.h> 35 36 37typedef struct stpi_internal_module_class 38{ 39 stp_module_class_t class; 40 const char *description; 41} stpi_internal_module_class_t; 42 43 44static void module_list_freefunc(void *item); 45static int stp_module_register(stp_module_t *module); 46#ifdef USE_DLOPEN 47static void *stp_dlsym(void *handle, const char *symbol, const char *modulename); 48#endif 49 50static const stpi_internal_module_class_t module_classes[] = 51 { 52 {STP_MODULE_CLASS_MISC, N_("Miscellaneous (unclassified)")}, 53 {STP_MODULE_CLASS_FAMILY, N_("Family driver")}, 54 {STP_MODULE_CLASS_COLOR, N_("Color conversion module")}, 55 {STP_MODULE_CLASS_DITHER, N_("Dither algorithm")}, 56 {STP_MODULE_CLASS_INVALID, NULL} /* Must be last */ 57 }; 58 59#if !defined(USE_LTDL) && !defined(USE_DLOPEN) 60extern stp_module_t print_canon_LTX_stp_module_data; 61extern stp_module_t print_escp2_LTX_stp_module_data; 62extern stp_module_t print_lexmark_LTX_stp_module_data; 63extern stp_module_t print_pcl_LTX_stp_module_data; 64extern stp_module_t print_ps_LTX_stp_module_data; 65extern stp_module_t print_dyesub_LTX_stp_module_data; 66extern stp_module_t print_raw_LTX_stp_module_data; 67extern stp_module_t color_traditional_LTX_stp_module_data; 68 69/* 70 * A list of modules, for use when the modules are linked statically. 71 */ 72static stp_module_t *static_modules[] = 73 { 74 &print_ps_LTX_stp_module_data, 75 &print_canon_LTX_stp_module_data, 76 &print_escp2_LTX_stp_module_data, 77 &print_pcl_LTX_stp_module_data, 78 &print_lexmark_LTX_stp_module_data, 79 &print_dyesub_LTX_stp_module_data, 80 &print_raw_LTX_stp_module_data, 81 &color_traditional_LTX_stp_module_data, 82 NULL 83 }; 84#endif 85 86static stp_list_t *module_list = NULL; 87 88 89/* 90 * Callback for removing a module from stp_module_list. 91 */ 92static void 93module_list_freefunc(void *item /* module to remove */) 94{ 95 stp_module_t *module = (stp_module_t *) item; 96 if (module && module->fini) /* Call the module exit function */ 97 module->fini(); 98#if defined(USE_LTDL) || defined(USE_DLOPEN) 99 DLCLOSE(module->handle); /* Close the module if it's not static */ 100#endif 101} 102 103 104/* 105 * Load all available modules. Return nonzero on failure. 106 */ 107int stp_module_load(void) 108{ 109 /* initialise libltdl */ 110#ifdef USE_LTDL 111 static int ltdl_is_initialised = 0; /* Is libltdl initialised? */ 112#endif 113 static int module_list_is_initialised = 0; /* Is the module list initialised? */ 114#if defined(USE_LTDL) || defined(USE_DLOPEN) 115 stp_list_t *dir_list; /* List of directories to scan */ 116 stp_list_t *file_list; /* List of modules to open */ 117 stp_list_item_t *file; /* Pointer to current module */ 118#endif 119 120#ifdef USE_LTDL 121 if (!ltdl_is_initialised) 122 { 123 if (lt_dlinit()) 124 { 125 stp_erprintf("Error initialising libltdl: %s\n", DLERROR()); 126 return 1; 127 } 128 ltdl_is_initialised = 1; 129 } 130 /* set default search paths */ 131 lt_dladdsearchdir(PKGMODULEDIR); 132#endif 133 134 /* initialise module_list */ 135 if (!module_list_is_initialised) 136 { 137 if (!(module_list = stp_list_create())) 138 return 1; 139 stp_list_set_freefunc(module_list, module_list_freefunc); 140 module_list_is_initialised = 1; 141 } 142 143 /* search for available modules */ 144#if defined (USE_LTDL) || defined (USE_DLOPEN) 145 if (!(dir_list = stp_list_create())) 146 return 1; 147 stp_list_set_freefunc(dir_list, stp_list_node_free_data); 148 if (getenv("STP_MODULE_PATH")) 149 { 150 stp_path_split(dir_list, getenv("STP_MODULE_PATH")); 151 } 152 else 153 { 154#ifdef USE_LTDL 155 stp_path_split(dir_list, getenv("LTDL_LIBRARY_PATH")); 156 stp_path_split(dir_list, lt_dlgetsearchpath()); 157#else 158 stp_path_split(dir_list, PKGMODULEDIR); 159#endif 160 } 161#ifdef USE_LTDL 162 file_list = stp_path_search(dir_list, ".la"); 163#else 164 file_list = stp_path_search(dir_list, ".so"); 165#endif 166 stp_list_destroy(dir_list); 167 168 /* load modules */ 169 file = stp_list_get_start(file_list); 170 while (file) 171 { 172 stp_module_open((const char *) stp_list_item_get_data(file)); 173 file = stp_list_item_next(file); 174 } 175 176 stp_list_destroy(file_list); 177#else /* use a static module list */ 178 { 179 int i=0; 180 while (static_modules[i]) 181 { 182 stp_module_register(static_modules[i]); 183 i++; 184 } 185 } 186#endif 187 return 0; 188 } 189 190 191/* 192 * Unload all modules and clean up. 193 */ 194int 195stp_module_exit(void) 196{ 197 /* destroy the module list (modules unloaded by callback) */ 198 if (module_list) 199 stp_list_destroy(module_list); 200 /* shut down libltdl (forces close of any unclosed modules) */ 201#ifdef USE_LTDL 202 return lt_dlexit(); 203#else 204 return 0; 205#endif 206} 207 208 209/* 210 * Find all modules in a given class. 211 */ 212stp_list_t * 213stp_module_get_class(stp_module_class_t class /* Module class */) 214{ 215 stp_list_t *list; /* List to return */ 216 stp_list_item_t *ln; /* Module to check*/ 217 218 list = stp_list_create(); /* No freefunc, so it can be destroyed 219 without unloading any modules! */ 220 if (!list) 221 return NULL; 222 223 ln = stp_list_get_start(module_list); 224 while (ln) 225 { 226 /* Add modules of the same class to our list */ 227 if (((stp_module_t *) stp_list_item_get_data(ln))->class == class) 228 stp_list_item_create(list, NULL, stp_list_item_get_data(ln)); 229 ln = stp_list_item_next(ln); 230 } 231 return list; 232} 233 234 235/* 236 * Open a module. 237 */ 238int 239stp_module_open(const char *modulename /* Module filename */) 240{ 241#if defined(USE_LTDL) || defined(USE_DLOPEN) 242#ifdef USE_LTDL 243 lt_dlhandle module; /* Handle for module */ 244#else 245 void *module; /* Handle for module */ 246#endif 247 stp_module_version_t *version; /* Module version */ 248 stp_module_t *data; /* Module data */ 249 stp_list_item_t *reg_module; /* Pointer to module list nodes */ 250 int error = 0; /* Error status */ 251 252 stp_deprintf(STP_DBG_MODULE, "stp-module: open: %s\n", modulename); 253 while(1) 254 { 255 module = DLOPEN(modulename); 256 if (!module) 257 break; 258 259 /* check version is valid */ 260 version = (stp_module_version_t *) DLSYM(module, "stp_module_version"); 261 if (!version) 262 break; 263 if (version->major != 1 && version->minor < 0) 264 break; 265 266 data = (void *) DLSYM(module, "stp_module_data"); 267 if (!data) 268 break; 269 data->handle = module; /* store module handle */ 270 271 /* check same module isn't already loaded */ 272 reg_module = stp_list_get_start(module_list); 273 while (reg_module) 274 { 275 if (!strcmp(data->name, ((stp_module_t *) 276 stp_list_item_get_data(reg_module))->name) && 277 data->class == ((stp_module_t *) 278 stp_list_item_get_data(reg_module))->class) 279 { 280 stp_deprintf(STP_DBG_MODULE, 281 "stp-module: reject duplicate: %s\n", 282 data->name); 283 error = 1; 284 break; 285 } 286 reg_module = stp_list_item_next(reg_module); 287 } 288 if (error) 289 break; 290 291 /* Register the module */ 292 if (stp_module_register(data)) 293 break; 294 295 return 0; 296 } 297 298 if (module) 299 DLCLOSE(module); 300#endif 301 return 1; 302} 303 304 305/* 306 * Register a loaded module. 307 */ 308static int stp_module_register(stp_module_t *module /* Module to register */) 309{ 310 /* Add to the module list */ 311 if (stp_list_item_create(module_list, NULL, module)) 312 return 1; 313 314 stp_deprintf(STP_DBG_MODULE, "stp-module: register: %s\n", module->name); 315 return 0; 316} 317 318 319/* 320 * Initialise all loaded modules 321 */ 322int stp_module_init(void) 323{ 324 stp_list_item_t *module_item; /* Module list pointer */ 325 stp_module_t *module; /* Module to initialise */ 326 327 module_item = stp_list_get_start(module_list); 328 while (module_item) 329 { 330 module = (stp_module_t *) stp_list_item_get_data(module_item); 331 if (module) 332 { 333 stp_deprintf(STP_DBG_MODULE, "stp-module-init: %s\n", module->name); 334 /* Initialise module */ 335 if (module->init && module->init()) 336 { 337 stp_deprintf(STP_DBG_MODULE, 338 "stp-module-init: %s: Module init failed\n", 339 module->name); 340 } 341 } 342 module_item = stp_list_item_next(module_item); 343 } 344 return 0; 345} 346 347 348 349/* 350 * Close a module. 351 */ 352int 353stp_module_close(stp_list_item_t *module /* Module to close */) 354{ 355 return stp_list_item_destroy(module_list, module); 356} 357 358 359/* 360 * If using dlopen, add modulename_LTX_ to symbol name 361 */ 362#ifdef USE_DLOPEN 363static void *stp_dlsym(void *handle, /* Module */ 364 const char *symbol, /* Symbol name */ 365 const char *modulename) /* Module name */ 366{ 367 int len; /* Length of string */ 368 static char *full_symbol = NULL; /* Symbol to return */ 369 char *module; /* Real module name */ 370 char *tmp = stp_strdup(modulename); /* Temporary string */ 371 372 module = basename(tmp); 373 374 if (full_symbol) 375 { 376 stp_free (full_symbol); 377 full_symbol = NULL; 378 } 379 380 full_symbol = (char *) stp_malloc(sizeof(char) * (strlen(module) - 2)); 381 382 /* "_LTX_" + '\0' - ".so" */ 383 len = strlen(symbol) + strlen(module) + 3; 384 full_symbol = (char *) stp_malloc(sizeof(char) * len); 385 386 len = 0; 387 strncpy (full_symbol, module, strlen(module) - 3); 388 len = strlen(module) - 3; 389 strcpy (full_symbol+len, "_LTX_"); 390 len += 5; 391 strcpy (full_symbol+len, symbol); 392 len += strlen(symbol); 393 full_symbol[len] = '\0'; 394 395#if defined(__OpenBSD__) 396/* OpenBSD needs a prepended underscore to match symbols */ 397 { 398 char *prefix_symbol = stp_malloc(sizeof(char) * (strlen(full_symbol) + 2)); 399 prefix_symbol[0] = '_'; 400 strcpy(prefix_symbol+1, full_symbol); 401 stp_free(full_symbol); 402 full_symbol = prefix_symbol; 403 } 404#endif 405 406 /* Change any hyphens to underscores */ 407 for (len = 0; full_symbol[len] != '\0'; len++) 408 if (full_symbol[len] == '-') 409 full_symbol[len] = '_'; 410 411 stp_deprintf(STP_DBG_MODULE, "SYMBOL: %s\n", full_symbol); 412 413 return dlsym(handle, full_symbol); 414} 415#endif 416