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