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