|
|
1.1 root 1: /* $Id: module.c,v 1.2 2003/05/17 20:16:04 fredette Exp $ */
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>
37: _TME_RCSID("$Id: module.c,v 1.2 2003/05/17 20:16:04 fredette Exp $");
38:
39: /* includes: */
40: #include <tme/threads.h>
41: #include <tme/module.h>
42: #include <tme/log.h>
43: #include <stdio.h>
44: #include <stdlib.h>
45: #include <ctype.h>
46: #include <string.h>
47: #include <ltdl.h>
48: #include <shlibvar.h>
49:
50: /* types: */
51: struct tme_module {
52:
53: /* the next module on a list: */
54: struct tme_module *tme_module_next;
55:
56: /* the libltdl handle for this module: */
57: lt_dlhandle tme_module_dlhandle;
58:
59: /* any "submodule" symbol prefix: */
60: char *tme_module_submodule;
61: };
62:
63: /* globals: */
64: static tme_mutex_t _tme_module_mutex;
65:
66: /* this initializes modules: */
67: int
68: tme_module_init(void)
69: {
70: int rc;
71: tme_mutex_init(&_tme_module_mutex);
72: LTDL_SET_PRELOADED_SYMBOLS();
73: rc = lt_dlinit();
74: if (rc != 0) {
75: return (EINVAL);
76: }
77: return (TME_OK);
78: }
79:
80: /* this finds a modules directory: */
81: static FILE *
82: _tme_modules_find(const char *top_name,
83: unsigned int top_name_length,
84: char **_modules_dir)
85: {
86: unsigned int modules_dir_length;
87: int pass;
88: const char *search_path;
89: const char *p1, *p2, *p3;
90: char c;
91: char *modules_index_pathname;
92: FILE *modules_index;
93:
94: /* pass over the search path environment variables: */
95: for (pass = 0; ++pass < 2; ) {
96:
97: /* get the next search path environment variable value: */
98: search_path = NULL;
99: switch (pass) {
100: case 1: search_path = getenv("LTDL_LIBRARY_PATH"); break;
101: case 2: search_path = getenv(LTDL_SHLIBPATH_VAR); break;
102: default: assert(FALSE);
103: }
104: if (search_path == NULL) {
105: continue;
106: }
107:
108: /* take apart this module path: */
109: p1 = p2 = search_path;
110: for (p3 = search_path;; p3++) {
111:
112: /* get the next character: */
113: c = *p3;
114:
115: /* continue if this is not a delimiter, tracking the last
116: non-slash: */
117: if (c != ':'
118: && c != '\0') {
119: if (c != '/') {
120: p2 = p3;
121: }
122: continue;
123: }
124:
125: /* if this path is absolute: */
126: if (*p1 == '/') {
127:
128: /* form the modules index pathname to try, remembering what
129: part of it is the modules directory pathname: */
130: modules_dir_length =
131: /* the search path part, less any trailing slashes: */
132: (p2 - p1) + 1
133: /* a slash: */
134: + 1
135: + top_name_length
136: /* a slash: */
137: + 1;
138: modules_index_pathname =
139: tme_new(char,
140: modules_dir_length
141: + top_name_length
142: + strlen("-plugins.txt")
143: /* a NUL: */
144: + 1);
145: memcpy(modules_index_pathname, p1, (p2 - p1) + 1);
146: modules_index_pathname[(p2 - p1) + 1] = '/';
147: memcpy(modules_index_pathname
148: + (p2 - p1) + 1
149: + 1,
150: top_name,
151: top_name_length);
152: modules_index_pathname[((p2 - p1) + 1
153: + 1
154: + top_name_length)] = '/';
155: memcpy(modules_index_pathname
156: + modules_dir_length,
157: top_name,
158: top_name_length);
159: strcpy(modules_index_pathname
160: + modules_dir_length
161: + top_name_length,
162: "-plugins.txt");
163:
164: /* try to open the modules index: */
165: modules_index = fopen(modules_index_pathname, "r");
166:
167: /* if we opened it, we're done: */
168: if (modules_index != NULL) {
169: modules_index_pathname[modules_dir_length] = '\0';
170: *_modules_dir = modules_index_pathname;
171: return (modules_index);
172: }
173:
174: /* keep trying: */
175: tme_free(modules_index_pathname);
176: }
177:
178: /* stop if this was the last path: */
179: if (c == '\0') {
180: break;
181: }
182:
183: /* advance to the next path: */
184: p1 = p2 = p3 + 1;
185: }
186: }
187:
188: /* this search failed: */
189: return (NULL);
190: }
191:
192: /* this opens a module: */
193: int
194: tme_module_open(const char *module_fake_pathname, void **_module, char **_output)
195: {
196: char *module_raw_name;
197: char *p1, *p2, c, *first_slash;
198: FILE *modules_index;
199: char *modules_dir;
200: char line_buffer[1024];
201: char *tokens[5];
202: unsigned int tokens_count;
203: char *module_basename;
204: char *module_pathname;
205: lt_dlhandle handle;
206: struct tme_module *module;
207:
208: /* strip leading slashes from the fake module pathname: */
209: for (; *module_fake_pathname == '/'; module_fake_pathname++);
210:
211: /* turn all of the non-alphanumerics in the fake module pathname
212: into underscores, and remember where the first slash was: */
213: module_raw_name = tme_strdup(module_fake_pathname);
214: first_slash = NULL;
215: for (p1 = module_raw_name;
216: (c = *p1) != '\0';
217: p1++) {
218: if (!isalnum(c)) {
219: *p1 = '_';
220: if (c == '/'
221: && first_slash == NULL) {
222: first_slash = p1;
223: }
224: }
225: }
226:
227: /* if there were no slashes in the fake module pathname, there is no
228: top name, which is incorrect: */
229: if (first_slash == NULL) {
230: tme_output_append_error(_output, module_fake_pathname);
231: tme_free(module_raw_name);
232: return (EINVAL);
233: }
234:
235: /* open the modules index for this top name: */
236: modules_index = _tme_modules_find(module_raw_name,
237: (first_slash - module_raw_name),
238: &modules_dir);
239: if (modules_index == NULL) {
240: tme_output_append_error(_output, module_fake_pathname);
241: tme_free(module_raw_name);
242: return (ENOENT);
243: }
244:
245: /* find the requested module in the index: */
246: for (;;) {
247:
248: /* get the next line from the index: */
249: tokens_count = 0;
250: if (fgets(line_buffer, sizeof(line_buffer) - 1, modules_index) == NULL) {
251: break;
252: }
253: line_buffer[sizeof(line_buffer) - 1] = '\0';
254: if ((p1 = strchr(line_buffer, '\n')) != NULL) {
255: *p1 = '\0';
256: }
257:
258: /* tokenize this line by whitespace and watch for comments: */
259: p1 = NULL;
260: for (p2 = line_buffer;; p2++) {
261: c = *p2;
262:
263: /* if this is a token delimiter: */
264: if (c == '\0'
265: || isspace(c)
266: || c == '#') {
267:
268: /* if we had been collecting a token, it's finished: */
269: if (p1 != NULL) {
270:
271:
272: /* skip this line if it has too many tokens. XXX we should
273: report an error: */
274: if (tokens_count == TME_ARRAY_ELS(tokens)) {
275: tokens_count = 0;
276: break;
277: }
278:
279: /* save this token: */
280: *p2 = '\0';
281: tokens[tokens_count++] = p1;
282: p1 = NULL;
283: }
284:
285: /* stop if this is the end of the line or the beginning of a
286: comment: */
287: if (c == '\0'
288: || c == '#') {
289: break;
290: }
291: }
292:
293: /* otherwise this is part of a token: */
294: else {
295: if (p1 == NULL) {
296: p1 = p2;
297: }
298: }
299: }
300:
301: /* there must be either one or three tokens, and the first
302: token must match the raw module name: */
303: if ((tokens_count == 1
304: || tokens_count == 3)
305: && !strcmp(tokens[0], module_raw_name)) {
306: break;
307: }
308: }
309:
310: /* close the index: */
311: fclose(modules_index);
312:
313: /* we no longer need the module raw name: */
314: tme_free(module_raw_name);
315:
316: /* if we didn't find the module in the index: */
317: if (tokens_count == 0) {
318: tme_output_append_error(_output, module_fake_pathname);
319: tme_free(modules_dir);
320: return (ENOENT);
321: }
322:
323: /* if there are three tokens, the module basename is the second,
324: else it is the same as the raw module name: */
325: module_basename = (tokens_count == 3
326: ? tokens[1]
327: : tokens[0]);
328:
329: /* form the real module pathname: */
330: module_pathname = tme_renew(char,
331: modules_dir,
332: strlen(modules_dir)
333: + strlen(module_basename)
334: + 1);
335: strcat(module_pathname, module_basename);
336:
337: /* dlopen the module: */
338: tme_mutex_lock(&_tme_module_mutex);
339: handle = lt_dlopenext(module_pathname);
340: tme_mutex_unlock(&_tme_module_mutex);
341: if (handle == NULL) {
342: tme_output_append_error(_output, module_fake_pathname);
343: tme_free(module_pathname);
344: return (ENOENT);
345: }
346:
347: /* return the new module: */
348: module = tme_new(struct tme_module, 1);
349: module->tme_module_dlhandle = handle;
350: module->tme_module_submodule = (tokens_count == 3
351: ? tme_strdup(tokens[2])
352: : NULL);
353: *_module = module;
354: return (TME_OK);
355: }
356:
357: /* this looks up a symbol: */
358: void *
359: tme_module_symbol(void *_module, const char *symbol)
360: {
361: struct tme_module *module;
362: char *module_symbol;
363: lt_ptr address;
364:
365: /* recover the module: */
366: module = (struct tme_module *) _module;
367:
368: /* form the symbol to look up: */
369: if (module->tme_module_submodule == NULL) {
370: module_symbol = tme_strdup(symbol);
371: }
372: else {
373: module_symbol = tme_new(char,
374: strlen(module->tme_module_submodule)
375: /* an underscore: */
376: + 1
377: + strlen(symbol)
378: /* a NUL: */
379: + 1);
380: sprintf(module_symbol,
381: "%s_%s",
382: module->tme_module_submodule,
383: symbol);
384: }
385:
386: /* look up the symbol: */
387: tme_mutex_lock(&_tme_module_mutex);
388: address = lt_dlsym(module->tme_module_dlhandle, module_symbol);
389: tme_mutex_unlock(&_tme_module_mutex);
390: tme_free(module_symbol);
391: return (address);
392: }
393:
394: /* this immediately closes a module: */
395: int
396: tme_module_close(void *_module)
397: {
398: struct tme_module *module;
399: int rc;
400:
401: /* recover the module: */
402: module = (struct tme_module *) _module;
403:
404: /* close the module: */
405: tme_mutex_lock(&_tme_module_mutex);
406: rc = lt_dlclose(module->tme_module_dlhandle);
407: tme_mutex_unlock(&_tme_module_mutex);
408:
409: /* free our structure: */
410: if (module->tme_module_submodule != NULL) {
411: tme_free(module->tme_module_submodule);
412: }
413: tme_free(module);
414:
415: /* XXX assume success: */
416: return (TME_OK);
417: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.