]>
Commit | Line | Data |
---|---|---|
1 | /* lt_dlloader.c -- dynamic library loader interface | |
2 | ||
3 | Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc. | |
4 | Written by Gary V. Vaughan, 2004 | |
5 | ||
6 | NOTE: The canonical source of this file is maintained with the | |
7 | GNU Libtool package. Report bugs to bug-libtool@gnu.org. | |
8 | ||
9 | GNU Libltdl is free software; you can redistribute it and/or | |
10 | modify it under the terms of the GNU Lesser General Public | |
11 | License as published by the Free Software Foundation; either | |
12 | version 2 of the License, or (at your option) any later version. | |
13 | ||
14 | As a special exception to the GNU Lesser General Public License, | |
15 | if you distribute this file as part of a program or library that | |
16 | is built using GNU Libtool, you may include this file under the | |
17 | same distribution terms that you use for the rest of that program. | |
18 | ||
19 | GNU Libltdl is distributed in the hope that it will be useful, | |
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | GNU Lesser General Public License for more details. | |
23 | ||
24 | You should have received a copy of the GNU Lesser General Public | |
25 | License along with GNU Libltdl; see the file COPYING.LIB. If not, a | |
26 | copy can be downloaded from http://www.gnu.org/licenses/lgpl.html, | |
27 | or obtained by writing to the Free Software Foundation, Inc., | |
28 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
29 | */ | |
30 | ||
31 | #include "lt__private.h" | |
32 | #include "lt_dlloader.h" | |
33 | ||
34 | #define RETURN_SUCCESS 0 | |
35 | #define RETURN_FAILURE 1 | |
36 | ||
37 | static void * loader_callback (SList *item, void *userdata); | |
38 | ||
39 | /* A list of all the dlloaders we know about, each stored as a boxed | |
40 | SList item: */ | |
41 | static SList *loaders = 0; | |
42 | ||
43 | ||
44 | /* Return NULL, unless the loader in this ITEM has a matching name, | |
45 | in which case we return the matching item so that its address is | |
46 | passed back out (for possible freeing) by slist_remove. */ | |
47 | static void * | |
48 | loader_callback (SList *item, void *userdata) | |
49 | { | |
50 | const lt_dlvtable *vtable = (const lt_dlvtable *) item->userdata; | |
51 | const char * name = (const char *) userdata; | |
52 | ||
53 | assert (vtable); | |
54 | ||
55 | return streq (vtable->name, name) ? (void *) item : NULL; | |
56 | } | |
57 | ||
58 | ||
59 | /* Hook VTABLE into our global LOADERS list according to its own | |
60 | PRIORITY field value. */ | |
61 | int | |
62 | lt_dlloader_add (const lt_dlvtable *vtable) | |
63 | { | |
64 | SList *item; | |
65 | ||
66 | if ((vtable == 0) /* diagnose invalid vtable fields */ | |
67 | || (vtable->module_open == 0) | |
68 | || (vtable->module_close == 0) | |
69 | || (vtable->find_sym == 0) | |
70 | || ((vtable->priority != LT_DLLOADER_PREPEND) && | |
71 | (vtable->priority != LT_DLLOADER_APPEND))) | |
72 | { | |
73 | LT__SETERROR (INVALID_LOADER); | |
74 | return RETURN_FAILURE; | |
75 | } | |
76 | ||
77 | item = slist_box (vtable); | |
78 | if (!item) | |
79 | { | |
80 | (*lt__alloc_die) (); | |
81 | ||
82 | /* Let the caller know something went wrong if lt__alloc_die | |
83 | doesn't abort. */ | |
84 | return RETURN_FAILURE; | |
85 | } | |
86 | ||
87 | if (vtable->priority == LT_DLLOADER_PREPEND) | |
88 | { | |
89 | loaders = slist_cons (item, loaders); | |
90 | } | |
91 | else | |
92 | { | |
93 | assert (vtable->priority == LT_DLLOADER_APPEND); | |
94 | loaders = slist_concat (loaders, item); | |
95 | } | |
96 | ||
97 | return RETURN_SUCCESS; | |
98 | } | |
99 | ||
100 | #ifdef LT_DEBUG_LOADERS | |
101 | static void * | |
102 | loader_dump_callback (SList *item, void *userdata) | |
103 | { | |
104 | const lt_dlvtable *vtable = (const lt_dlvtable *) item->userdata; | |
105 | fprintf (stderr, ", %s", (vtable && vtable->name) ? vtable->name : "(null)"); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | void | |
110 | lt_dlloader_dump (void) | |
111 | { | |
112 | fprintf (stderr, "loaders: "); | |
113 | if (!loaders) | |
114 | { | |
115 | fprintf (stderr, "(empty)"); | |
116 | } | |
117 | else | |
118 | { | |
119 | const lt_dlvtable *head = (const lt_dlvtable *) loaders->userdata; | |
120 | fprintf (stderr, "%s", (head && head->name) ? head->name : "(null)"); | |
121 | if (slist_tail (loaders)) | |
122 | slist_foreach (slist_tail (loaders), loader_dump_callback, NULL); | |
123 | } | |
124 | fprintf (stderr, "\n"); | |
125 | } | |
126 | #endif | |
127 | ||
128 | /* An iterator for the global loader list: if LOADER is NULL, then | |
129 | return the first element, otherwise the following element. */ | |
130 | lt_dlloader | |
131 | lt_dlloader_next (lt_dlloader loader) | |
132 | { | |
133 | SList *item = (SList *) loader; | |
134 | return (lt_dlloader) (item ? item->next : loaders); | |
135 | } | |
136 | ||
137 | ||
138 | /* Non-destructive unboxing of a loader. */ | |
139 | const lt_dlvtable * | |
140 | lt_dlloader_get (lt_dlloader loader) | |
141 | { | |
142 | return (const lt_dlvtable *) (loader ? ((SList *) loader)->userdata : NULL); | |
143 | } | |
144 | ||
145 | ||
146 | /* Return the contents of the first item in the global loader list | |
147 | with a matching NAME after removing it from that list. If there | |
148 | was no match, return NULL; if there is an error, return NULL and | |
149 | set an error for lt_dlerror; do not set an error if only resident | |
150 | modules need this loader; in either case, the loader list is not | |
151 | changed if NULL is returned. */ | |
152 | lt_dlvtable * | |
153 | lt_dlloader_remove (const char *name) | |
154 | { | |
155 | const lt_dlvtable * vtable = lt_dlloader_find (name); | |
156 | static const char id_string[] = "lt_dlloader_remove"; | |
157 | lt_dlinterface_id iface; | |
158 | lt_dlhandle handle = 0; | |
159 | int in_use = 0; | |
160 | int in_use_by_resident = 0; | |
161 | ||
162 | if (!vtable) | |
163 | { | |
164 | LT__SETERROR (INVALID_LOADER); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | /* Fail if there are any open modules which use this loader. */ | |
169 | iface = lt_dlinterface_register (id_string, NULL); | |
170 | while ((handle = lt_dlhandle_iterate (iface, handle))) | |
171 | { | |
172 | lt_dlhandle cur = handle; | |
173 | if (cur->vtable == vtable) | |
174 | { | |
175 | in_use = 1; | |
176 | if (lt_dlisresident (handle)) | |
177 | in_use_by_resident = 1; | |
178 | } | |
179 | } | |
180 | lt_dlinterface_free (iface); | |
181 | if (in_use) | |
182 | { | |
183 | if (!in_use_by_resident) | |
184 | LT__SETERROR (REMOVE_LOADER); | |
185 | return 0; | |
186 | } | |
187 | ||
188 | /* Call the loader finalisation function. */ | |
189 | if (vtable && vtable->dlloader_exit) | |
190 | { | |
191 | if ((*vtable->dlloader_exit) (vtable->dlloader_data) != 0) | |
192 | { | |
193 | /* If there is an exit function, and it returns non-zero | |
194 | then it must set an error, and we will not remove it | |
195 | from the list. */ | |
196 | return 0; | |
197 | } | |
198 | } | |
199 | ||
200 | /* If we got this far, remove the loader from our global list. */ | |
201 | return (lt_dlvtable *) | |
202 | slist_unbox ((SList *) slist_remove (&loaders, loader_callback, (void *) name)); | |
203 | } | |
204 | ||
205 | ||
206 | const lt_dlvtable * | |
207 | lt_dlloader_find (const char *name) | |
208 | { | |
209 | return lt_dlloader_get (slist_find (loaders, loader_callback, (void *) name)); | |
210 | } |