1 /**
2 * @file libcomprex/module.c Module API
3 *
4 * $Id: module.c,v 1.28 2003/01/01 06:22:36 chipx86 Exp $
5 *
6 * @Copyright (C) 2001-2003 The GNUpdate Project.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23 #include <libcomprex/module.h>
24 #include <libcomprex/internal.h>
25
26 #ifdef DYNAMIC_MODS
27 # include <ltdl.h>
28
29 # define USER_MODULES_COUNT (sizeof(user_modules) / sizeof(*user_modules))
30 # define SYS_MODULES_COUNT (sizeof(system_modules) / sizeof(*system_modules))
31
32 static const char *user_modules[] =
33 {
34 ".gnupdate/comprex"
35 };
36
37 static const char *system_modules[] =
38 {
39 COMPREX_LIBDIR,
40 "/usr/lib/comprex", "/usr/local/lib/comprex"
41 };
42
43
44 static int ltdl_refCount = 0;
45
46 # define LOADERS_UNINITIALIZED -4444
47
48 static int errors = LOADERS_UNINITIALIZED;
49 #endif /* DYNAMIC_MODS */
50
51
52 static CxModule *firstArchiveModule = NULL;
53 static CxModule *lastArchiveModule = NULL;
54
55 static CxModule *firstSchemeModule = NULL;
56 static CxModule *lastSchemeModule = NULL;
57
58 static int __initialized = 0;
59
60
61 #ifdef HAVE_ATEXIT
62 # define EXIT_FUNC(proc) static void (proc)(void)
63 #else
64 # ifdef HAVE_ON_EXIT
65 # define EXIT_FUNC(proc) static void (proc)(int i, void *v)
66 # else
67 # define EXIT_FUNC(proc) static void (proc)(void)
68 # endif
69 #endif
70
71 STATIC_ARCHIVE_INIT
72 STATIC_SCHEME_INIT
73
74
75 #ifdef DYNAMIC_MODS
76 static void __ltdlInit(void);
77 #endif
78
79 #if 0
80 EXIT_FUNC(__uninitialize)
81 {
82 cxCleanup();
83 }
84 #endif
85
86 static void
__initialize()87 __initialize()
88 {
89 if (__initialized == 0)
90 {
91 #if 0
92 /*
93 * NOTE: This can cause segfaults when __uninitialize is called on
94 * exit.
95 */
96
97 /*
98 * This is a good place to initialize the atexit for the entire
99 * library.
100 */
101 #ifdef HAVE_ATEXIT
102 atexit(__uninitialize);
103 #else
104 # ifdef HAVE_ON_EXIT
105 on_exit(__uninitialize, NULL);
106 # endif /* HAVE_ON_EXIT */
107 #endif /* HAVE_ATEXIT */
108 #endif /* 0 */
109
110 #ifdef DYNAMIC_MODS
111 __ltdlInit();
112 #endif
113
114 static_archive_init();
115 static_scheme_init();
116
117 __initialized = 1;
118 }
119 }
120
121 #ifdef DYNAMIC_MODS
122 static void
__ltdlInit(void)123 __ltdlInit(void)
124 {
125 /* Do this only once! */
126
127 if (errors == LOADERS_UNINITIALIZED)
128 {
129 errors = lt_dlinit();
130
131 /* Initialize libltdl's memory management. */
132 lt_dlmalloc = malloc;
133 lt_dlfree = free;
134 }
135
136 if (errors != 0)
137 {
138 const char *dlerror = lt_dlerror();
139
140 fprintf(stderr,
141 _("libcomprex: error: failed to initialize ltdl: %s\n"),
142 dlerror);
143 exit(1);
144 }
145 }
146
147 static void
__ltdlExit(void)148 __ltdlExit(void)
149 {
150 if (errors != LOADERS_UNINITIALIZED)
151 {
152 ltdl_refCount = 0;
153 errors = LOADERS_UNINITIALIZED;
154 lt_dlexit();
155 }
156 }
157
158 static CxModule *
__cxLoadModule(const char * file,CxModuleType type)159 __cxLoadModule(const char *file, CxModuleType type)
160 {
161 CxModule *(*initModule)(CxModuleType type);
162 CxModule *module;
163 lt_dlhandle handle;
164
165 handle = lt_dlopenext(file);
166
167 if (handle == NULL)
168 {
169 const char *dlerror = lt_dlerror();
170
171 fprintf(stderr, _("libcomprex: error: failed to open %s: %s\n"),
172 file, dlerror);
173
174 return NULL;
175 }
176
177 initModule = lt_dlsym(handle, "initComprexModule");
178
179 if (initModule == NULL)
180 {
181 lt_dlclose(handle);
182
183 return NULL;
184 }
185
186 module = initModule(type);
187
188 if (module == NULL)
189 {
190 lt_dlclose(handle);
191 free(module);
192
193 return NULL;
194 }
195
196 module->handle = handle;
197
198 ltdl_refCount++;
199
200 return module;
201 }
202
203 static void
__cxUnloadModule(CxModule * module)204 __cxUnloadModule(CxModule *module)
205 {
206 if (module->handle != NULL)
207 {
208 int result;
209
210 ltdl_refCount--;
211
212 result = lt_dlclose((lt_dlhandle)module->handle);
213
214 module->handle = NULL;
215 }
216
217 if (ltdl_refCount == 0)
218 __ltdlExit();
219 }
220
221 static char **
__trimModuleList(char ** list,int * num)222 __trimModuleList(char **list, int *num)
223 {
224 int n, size = 0, found = 0;
225 char **ret = NULL;
226 char **c = NULL;
227 char **d = NULL;
228
229 if (list == NULL)
230 return NULL;
231
232 if (*num == 0)
233 return list;
234
235 n = *num;
236
237 for (c = list; c - list < n; c++)
238 {
239 char *ext;
240
241 if (*c == NULL)
242 continue;
243
244 ext = strrchr(*c, '.');
245
246 if (ext != NULL)
247 {
248 *ext = '\0';
249
250 /* Don't add the same loader multiple times... */
251 found = 0;
252
253 for (d = ret; d - ret < size; d++)
254 {
255 if (!strcmp(*d, *c))
256 {
257 found = 1;
258 break;
259 }
260 }
261
262 if (!found)
263 {
264 ret = realloc(ret, (size + 1) * sizeof(char *));
265
266 ret[size] = strdup(*c);
267
268 size++;
269 }
270 }
271
272 if (*c != NULL)
273 free(*c);
274 }
275
276 if (list != NULL)
277 free(list);
278
279 *num = size;
280
281 return ret;
282 }
283
284 static void
__scanModulesInDir(char *** list,int * num_ret,const char * dir)285 __scanModulesInDir(char ***list, int *num_ret, const char *dir)
286 {
287 char **files;
288 char buffer[4096];
289 int offset = *num_ret;
290 int num;
291 int i;
292
293 /* Get the files in the directory */
294 files = cxListDir(dir, &num, "lib");
295
296 if (num > 0)
297 {
298 /* The directory is not empty. */
299
300 *num_ret += num;
301
302 if (*list == NULL)
303 {
304 MEM_CHECK(*list = (char **)malloc(*num_ret * sizeof(char *)));
305
306 }
307 else
308 {
309 MEM_CHECK(*list = (char **)realloc(*list, *num_ret *
310 sizeof(char *)));
311 }
312
313 for (i = 0; i < num; i++)
314 {
315 sprintf(buffer, "%s/%s", dir, files[i]);
316 (*list)[offset + i] = strdup(buffer);
317 }
318
319 cxFreeDirList(files, num);
320 }
321 }
322
323 static char **
__scanModules(int * num_ret,CxModuleType type)324 __scanModules(int *num_ret, CxModuleType type)
325 {
326 const char *homeDir;
327 char **list = NULL;
328 char buffer[4096];
329 int i;
330
331 *num_ret = 0;
332
333 /* Get the user's home directory */
334 homeDir = cxGetHomeDir();
335
336 /* Loop through the user module directories. */
337 for (i = 0; i < USER_MODULES_COUNT; i++)
338 {
339 sprintf(buffer, "%s/%s/%s", homeDir, user_modules[i],
340 (type == CX_MODULE_ARCHIVE ? "archive" : "scheme"));
341
342 __scanModulesInDir(&list, num_ret, buffer);
343 }
344
345 /* Loop through the system module directories. */
346 for (i = 0; i < SYS_MODULES_COUNT; i++)
347 {
348 sprintf(buffer, "%s/%s", system_modules[i],
349 (type == CX_MODULE_ARCHIVE ? "archive" : "scheme"));
350
351 __scanModulesInDir(&list, num_ret, buffer);
352 }
353
354 /* Get rid of duplicates and invalid entries. */
355 list = __trimModuleList(list, num_ret);
356
357 return list;
358 }
359 #endif /* DYNAMIC_MODS */
360
361 static void
__loadAllModules(CxModuleType type)362 __loadAllModules(CxModuleType type)
363 {
364 #ifdef DYNAMIC_MODS
365 int i, num;
366 char **list;
367 #endif
368
369 __initialize();
370
371 #ifdef DYNAMIC_MODS
372 /* Scan for the file modules. */
373 list = __scanModules(&num, type);
374
375 if (list == NULL)
376 return;
377
378 for (i = 0; i < num; i++)
379 {
380 cxLoadModule(list[i], type);
381
382 free(list[i]);
383 }
384
385 free(list);
386 #endif
387 }
388
389 CxModule *
cxRegisterModule(const char * name,void * _ops,CxModuleType type)390 cxRegisterModule(const char *name, void *_ops, CxModuleType type)
391 {
392 CxModule *module;
393
394 if (name == NULL || _ops == NULL)
395 return NULL;
396
397 /* Build the module structure */
398 MEM_CHECK(module = (CxModule *)malloc(sizeof(CxModule)));
399 memset(module, 0, sizeof(CxModule));
400
401 if (type == CX_MODULE_ARCHIVE)
402 {
403 CxArchiveOps *ops = (CxArchiveOps *)_ops;
404
405 if (ops->supportsExtension == NULL ||
406 ops->readArchive == NULL ||
407 ops->openFile == NULL ||
408 ops->destroyFile == NULL)
409 {
410 free(module);
411
412 return NULL;
413 }
414
415 module->ops.archive = ops;
416 }
417 else if (type == CX_MODULE_SCHEME)
418 {
419 CxSchemeOps *ops = (CxSchemeOps *)_ops;
420
421 if (ops->get == NULL || ops->supports == NULL)
422 {
423 free(module);
424
425 return NULL;
426 }
427 }
428 else
429 {
430 free(module);
431
432 return NULL;
433 }
434
435 module->name = strdup(name);
436 module->type = type;
437
438 if (type == CX_MODULE_ARCHIVE)
439 {
440 if (firstArchiveModule == NULL)
441 firstArchiveModule = module;
442
443 module->prev = lastArchiveModule;
444
445 if (lastArchiveModule != NULL)
446 lastArchiveModule->next = module;
447
448 lastArchiveModule = module;
449 }
450 else
451 {
452 if (firstSchemeModule == NULL)
453 firstSchemeModule = module;
454
455 module->prev = lastSchemeModule;
456
457 if (lastSchemeModule != NULL)
458 lastSchemeModule->next = module;
459
460 lastSchemeModule = module;
461 }
462
463 module->next = NULL;
464
465 return module;
466 }
467
468 CxModule *
cxLoadModule(const char * name,CxModuleType type)469 cxLoadModule(const char *name, CxModuleType type)
470 {
471 #ifndef DYNAMIC_MODS
472 return NULL;
473 #else
474 CxModule *module;
475
476 if (name == NULL || *name == '\0')
477 return NULL;
478
479 module = __cxLoadModule(name, type);
480
481 if (module == NULL)
482 return NULL;
483
484 module->filename = strdup(name);
485
486 return module;
487 #endif /* DYNAMIC_MODS */
488 }
489
490 void
cxUnloadModule(CxModule * module)491 cxUnloadModule(CxModule *module)
492 {
493 if (module == NULL)
494 return;
495
496 #ifdef DYNAMIC_MODS
497 __cxUnloadModule(module);
498 #endif
499
500 if (module->prev != NULL)
501 module->prev->next = module->next;
502 else
503 {
504 if (module->type == CX_MODULE_ARCHIVE)
505 firstArchiveModule = module->next;
506 else
507 firstSchemeModule = module->next;
508 }
509
510 if (module->next != NULL)
511 module->next->prev = module->prev;
512 else
513 {
514 if (module->type == CX_MODULE_ARCHIVE)
515 lastArchiveModule = module->prev;
516 else
517 lastSchemeModule = module->prev;
518 }
519
520 if (module->filename != NULL) free(module->filename);
521 if (module->name != NULL) free(module->name);
522
523 free(module);
524 }
525
526 CxModule *
cxGetModule(const char * name,CxModuleType type)527 cxGetModule(const char *name, CxModuleType type)
528 {
529 CxModule *module;
530
531 for (module = cxGetFirstModule(type);
532 module != NULL;
533 module = module->next)
534 {
535 if (!strcmp(module->name, name))
536 return module;
537 }
538
539 /* Not found. Load it. */
540 module = cxLoadModule(name, type);
541
542 return module;
543 }
544
545 void
cxLinkModule(CxModule ** ptr)546 cxLinkModule(CxModule **ptr)
547 {
548 if (ptr == NULL || *ptr == NULL)
549 return;
550
551 CX_LINK(*ptr);
552 }
553
554 void
cxUnlinkModule(CxModule ** ptr)555 cxUnlinkModule(CxModule **ptr)
556 {
557 CxModule *module;
558
559 if (ptr == NULL || *ptr == NULL)
560 return;
561
562 module = *ptr;
563
564 CX_UNLINK(module);
565
566 if (module->refCount == 0)
567 {
568 cxUnloadModule(module);
569 }
570
571 *ptr = NULL;
572 }
573
574 CxModule *
cxGetFirstModule(CxModuleType type)575 cxGetFirstModule(CxModuleType type)
576 {
577 if (type == CX_MODULE_ARCHIVE)
578 {
579 if (firstArchiveModule == NULL)
580 __loadAllModules(CX_MODULE_ARCHIVE);
581
582 return firstArchiveModule;
583 }
584 else
585 {
586 if (firstSchemeModule == NULL)
587 __loadAllModules(CX_MODULE_SCHEME);
588
589 return firstSchemeModule;
590 }
591 }
592
593 void
cxCleanupModules()594 cxCleanupModules()
595 {
596 CxModule *module, *nextModule;
597
598 for (module = firstArchiveModule; module != NULL; module = nextModule)
599 {
600 nextModule = module->next;
601
602 cxUnloadModule(module);
603 }
604
605 for (module = firstSchemeModule; module != NULL; module = nextModule)
606 {
607 nextModule = module->next;
608
609 cxUnloadModule(module);
610 }
611
612 firstArchiveModule = NULL;
613 lastArchiveModule = NULL;
614 firstSchemeModule = NULL;
615 lastSchemeModule = NULL;
616 }
617
618 void
cxCleanup()619 cxCleanup()
620 {
621 cxCleanupModules();
622 cxCleanupEnvInfo();
623
624 #ifdef DYNAMIC_MODS
625 __ltdlExit();
626 #endif
627
628 __initialized = 0;
629 }
630