|
|
1.1.1.5 root 1: /* $Id: module.c,v 1.10 2010/06/05 19:04:42 fredette Exp $ */
1.1 root 2:
3: /* libtme/module.c - module management: */
4:
5: /*
6: * Copyright (c) 2003 Matt Fredette
7: * All rights reserved.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by Matt Fredette.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33: * POSSIBILITY OF SUCH DAMAGE.
34: */
35:
36: #include <tme/common.h>
1.1.1.5 root 37: _TME_RCSID("$Id: module.c,v 1.10 2010/06/05 19:04:42 fredette Exp $");
1.1 root 38:
39: /* includes: */
40: #include <tme/threads.h>
41: #include <tme/module.h>
42: #include <tme/log.h>
1.1.1.2 root 43: #include <tme/misc.h>
1.1 root 44: #include <stdio.h>
45: #include <stdlib.h>
46: #include <ctype.h>
47: #include <string.h>
48: #include <ltdl.h>
49: #include <shlibvar.h>
50:
1.1.1.2 root 51: /* the libtool 1.5 used in tme development is supposed to add code to
52: configure that decides whether to use an already-installed libltdl
53: instead of the libltdl that comes with tme. unfortunately, on some
54: systems it appears to decide to use an installed libltdl without
55: checking that the installed ltdl.h is recent enough to define
56: lt_ptr. we try to compensate for this here: */
57: #ifndef lt_ptr
58: #ifdef lt_ptr_t
59: #define lt_ptr lt_ptr_t
60: #else /* !lt_ptr_t */
61: #error "installed libtool is too old"
62: #endif /* !lt_ptr_t */
63: #endif /* !lt_ptr */
64:
1.1.1.5 root 65: /* similarly, the installed libltdl may be so recent that its ltdl.h
66: renames lt_preloaded_symbols with a macro, to a name different from
67: what our libtool script makes. it's possible that the renaming
68: macro is meant to be undefined to handle this problem: */
69: #undef lt_preloaded_symbols
70:
1.1 root 71: /* types: */
72: struct tme_module {
73:
74: /* the next module on a list: */
75: struct tme_module *tme_module_next;
76:
77: /* the libltdl handle for this module: */
78: lt_dlhandle tme_module_dlhandle;
79:
80: /* any "submodule" symbol prefix: */
81: char *tme_module_submodule;
82: };
83:
84: /* globals: */
85: static tme_mutex_t _tme_module_mutex;
86:
87: /* this initializes modules: */
88: int
89: tme_module_init(void)
90: {
91: int rc;
92: tme_mutex_init(&_tme_module_mutex);
93: LTDL_SET_PRELOADED_SYMBOLS();
94: rc = lt_dlinit();
95: if (rc != 0) {
96: return (EINVAL);
97: }
98: return (TME_OK);
99: }
100:
101: /* this finds a modules directory: */
102: static FILE *
103: _tme_modules_find(const char *top_name,
104: unsigned int top_name_length,
105: char **_modules_dir)
106: {
107: unsigned int modules_dir_length;
108: int pass;
109: const char *search_path;
110: const char *p1, *p2, *p3;
111: char c;
112: char *modules_index_pathname;
113: FILE *modules_index;
114:
115: /* pass over the search path environment variables: */
1.1.1.4 root 116: for (pass = 0; ++pass <= 2; ) {
1.1 root 117:
118: /* get the next search path environment variable value: */
119: search_path = NULL;
120: switch (pass) {
121: case 1: search_path = getenv("LTDL_LIBRARY_PATH"); break;
122: case 2: search_path = getenv(LTDL_SHLIBPATH_VAR); break;
123: default: assert(FALSE);
124: }
125: if (search_path == NULL) {
126: continue;
127: }
128:
129: /* take apart this module path: */
130: p1 = p2 = search_path;
131: for (p3 = search_path;; p3++) {
132:
133: /* get the next character: */
134: c = *p3;
135:
136: /* continue if this is not a delimiter, tracking the last
137: non-slash: */
138: if (c != ':'
139: && c != '\0') {
140: if (c != '/') {
141: p2 = p3;
142: }
143: continue;
144: }
145:
146: /* if this path is absolute: */
147: if (*p1 == '/') {
148:
149: /* form the modules index pathname to try, remembering what
150: part of it is the modules directory pathname: */
151: modules_dir_length =
152: /* the search path part, less any trailing slashes: */
153: (p2 - p1) + 1
154: /* a slash: */
155: + 1
156: + top_name_length
157: /* a slash: */
158: + 1;
159: modules_index_pathname =
160: tme_new(char,
161: modules_dir_length
162: + top_name_length
163: + strlen("-plugins.txt")
164: /* a NUL: */
165: + 1);
166: memcpy(modules_index_pathname, p1, (p2 - p1) + 1);
167: modules_index_pathname[(p2 - p1) + 1] = '/';
168: memcpy(modules_index_pathname
169: + (p2 - p1) + 1
170: + 1,
171: top_name,
172: top_name_length);
173: modules_index_pathname[((p2 - p1) + 1
174: + 1
175: + top_name_length)] = '/';
176: memcpy(modules_index_pathname
177: + modules_dir_length,
178: top_name,
179: top_name_length);
180: strcpy(modules_index_pathname
181: + modules_dir_length
182: + top_name_length,
183: "-plugins.txt");
184:
185: /* try to open the modules index: */
186: modules_index = fopen(modules_index_pathname, "r");
187:
188: /* if we opened it, we're done: */
189: if (modules_index != NULL) {
190: modules_index_pathname[modules_dir_length] = '\0';
191: *_modules_dir = modules_index_pathname;
192: return (modules_index);
193: }
194:
195: /* keep trying: */
196: tme_free(modules_index_pathname);
197: }
198:
199: /* stop if this was the last path: */
200: if (c == '\0') {
201: break;
202: }
203:
204: /* advance to the next path: */
205: p1 = p2 = p3 + 1;
206: }
207: }
208:
209: /* this search failed: */
210: return (NULL);
211: }
212:
213: /* this opens a module: */
214: int
215: tme_module_open(const char *module_fake_pathname, void **_module, char **_output)
216: {
217: char *module_raw_name;
1.1.1.2 root 218: char *p1, c, *first_slash;
1.1 root 219: FILE *modules_index;
220: char *modules_dir;
221: char line_buffer[1024];
1.1.1.2 root 222: char **tokens;
1.1.1.4 root 223: int tokens_count;
1.1 root 224: char *module_basename;
225: char *module_pathname;
226: lt_dlhandle handle;
227: struct tme_module *module;
228:
229: /* strip leading slashes from the fake module pathname: */
230: for (; *module_fake_pathname == '/'; module_fake_pathname++);
231:
232: /* turn all of the non-alphanumerics in the fake module pathname
233: into underscores, and remember where the first slash was: */
234: module_raw_name = tme_strdup(module_fake_pathname);
235: first_slash = NULL;
236: for (p1 = module_raw_name;
237: (c = *p1) != '\0';
238: p1++) {
1.1.1.4 root 239: if (!isalnum((unsigned char) c)) {
1.1 root 240: *p1 = '_';
241: if (c == '/'
242: && first_slash == NULL) {
243: first_slash = p1;
244: }
245: }
246: }
247:
248: /* if there were no slashes in the fake module pathname, there is no
249: top name, which is incorrect: */
250: if (first_slash == NULL) {
1.1.1.6 ! root 251: tme_output_append_error(_output, "%s", module_fake_pathname);
1.1 root 252: tme_free(module_raw_name);
253: return (EINVAL);
254: }
255:
256: /* open the modules index for this top name: */
257: modules_index = _tme_modules_find(module_raw_name,
258: (first_slash - module_raw_name),
259: &modules_dir);
260: if (modules_index == NULL) {
1.1.1.6 ! root 261: tme_output_append_error(_output, "%s", module_fake_pathname);
1.1 root 262: tme_free(module_raw_name);
263: return (ENOENT);
264: }
265:
266: /* find the requested module in the index: */
1.1.1.2 root 267: tokens = NULL;
1.1 root 268: for (;;) {
269:
270: /* get the next line from the index: */
271: tokens_count = 0;
272: if (fgets(line_buffer, sizeof(line_buffer) - 1, modules_index) == NULL) {
273: break;
274: }
275: line_buffer[sizeof(line_buffer) - 1] = '\0';
276: if ((p1 = strchr(line_buffer, '\n')) != NULL) {
277: *p1 = '\0';
278: }
279:
1.1.1.2 root 280: /* tokenize this line: */
281: tokens = tme_misc_tokenize(line_buffer, '#', &tokens_count);
1.1 root 282:
283: /* there must be either one or three tokens, and the first
284: token must match the raw module name: */
285: if ((tokens_count == 1
286: || tokens_count == 3)
287: && !strcmp(tokens[0], module_raw_name)) {
288: break;
289: }
1.1.1.2 root 290:
291: /* free the bad tokens: */
292: tme_free_string_array(tokens, -1);
1.1 root 293: }
294:
295: /* close the index: */
296: fclose(modules_index);
297:
298: /* we no longer need the module raw name: */
299: tme_free(module_raw_name);
300:
301: /* if we didn't find the module in the index: */
302: if (tokens_count == 0) {
1.1.1.6 ! root 303: tme_output_append_error(_output, "%s", module_fake_pathname);
1.1 root 304: tme_free(modules_dir);
305: return (ENOENT);
306: }
307:
308: /* if there are three tokens, the module basename is the second,
309: else it is the same as the raw module name: */
310: module_basename = (tokens_count == 3
311: ? tokens[1]
312: : tokens[0]);
313:
314: /* form the real module pathname: */
315: module_pathname = tme_renew(char,
316: modules_dir,
317: strlen(modules_dir)
318: + strlen(module_basename)
319: + 1);
320: strcat(module_pathname, module_basename);
321:
322: /* dlopen the module: */
323: tme_mutex_lock(&_tme_module_mutex);
324: handle = lt_dlopenext(module_pathname);
325: tme_mutex_unlock(&_tme_module_mutex);
1.1.1.2 root 326: tme_free(module_pathname);
1.1 root 327: if (handle == NULL) {
1.1.1.6 ! root 328: tme_output_append_error(_output, "%s", module_fake_pathname);
1.1.1.2 root 329: tme_free_string_array(tokens, -1);
1.1 root 330: return (ENOENT);
331: }
332:
333: /* return the new module: */
334: module = tme_new(struct tme_module, 1);
335: module->tme_module_dlhandle = handle;
336: module->tme_module_submodule = (tokens_count == 3
337: ? tme_strdup(tokens[2])
338: : NULL);
339: *_module = module;
1.1.1.2 root 340: tme_free_string_array(tokens, -1);
1.1 root 341: return (TME_OK);
342: }
343:
344: /* this looks up a symbol: */
345: void *
346: tme_module_symbol(void *_module, const char *symbol)
347: {
348: struct tme_module *module;
349: char *module_symbol;
350: lt_ptr address;
351:
352: /* recover the module: */
353: module = (struct tme_module *) _module;
354:
355: /* form the symbol to look up: */
356: if (module->tme_module_submodule == NULL) {
357: module_symbol = tme_strdup(symbol);
358: }
359: else {
360: module_symbol = tme_new(char,
361: strlen(module->tme_module_submodule)
362: /* an underscore: */
363: + 1
364: + strlen(symbol)
365: /* a NUL: */
366: + 1);
367: sprintf(module_symbol,
368: "%s_%s",
369: module->tme_module_submodule,
370: symbol);
371: }
372:
373: /* look up the symbol: */
374: tme_mutex_lock(&_tme_module_mutex);
375: address = lt_dlsym(module->tme_module_dlhandle, module_symbol);
376: tme_mutex_unlock(&_tme_module_mutex);
377: tme_free(module_symbol);
378: return (address);
379: }
380:
381: /* this immediately closes a module: */
382: int
383: tme_module_close(void *_module)
384: {
385: struct tme_module *module;
386: int rc;
387:
388: /* recover the module: */
389: module = (struct tme_module *) _module;
390:
391: /* close the module: */
392: tme_mutex_lock(&_tme_module_mutex);
393: rc = lt_dlclose(module->tme_module_dlhandle);
394: tme_mutex_unlock(&_tme_module_mutex);
395:
396: /* free our structure: */
397: if (module->tme_module_submodule != NULL) {
398: tme_free(module->tme_module_submodule);
399: }
400: tme_free(module);
401:
402: /* XXX assume success: */
403: return (TME_OK);
404: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.