1 /*
2 * Copyright 2007-2009, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #include <DiskSystemAddOnManager.h>
7
8 #include <exception>
9 #include <new>
10 #include <set>
11 #include <string>
12
13 #include <stdio.h>
14 #include <pthread.h>
15
16 #include <Directory.h>
17 #include <Entry.h>
18 #include <image.h>
19 #include <Path.h>
20
21 #include <AutoDeleter.h>
22 #include <AutoLocker.h>
23
24 #include <DiskSystemAddOn.h>
25
26
27 #undef TRACE
28 #define TRACE(format...)
29 //#define TRACE(format...) printf(format)
30
31
32 using std::nothrow;
33
34
35 static pthread_once_t sManagerInitOnce = PTHREAD_ONCE_INIT;
36 DiskSystemAddOnManager* DiskSystemAddOnManager::sManager = NULL;
37
38
39 // AddOnImage
40 struct DiskSystemAddOnManager::AddOnImage {
AddOnImageDiskSystemAddOnManager::AddOnImage41 AddOnImage(image_id image)
42 : image(image),
43 refCount(0)
44 {
45 }
46
~AddOnImageDiskSystemAddOnManager::AddOnImage47 ~AddOnImage()
48 {
49 unload_add_on(image);
50 }
51
52 image_id image;
53 int32 refCount;
54 };
55
56
57 // AddOn
58 struct DiskSystemAddOnManager::AddOn {
AddOnDiskSystemAddOnManager::AddOn59 AddOn(AddOnImage* image, BDiskSystemAddOn* addOn)
60 : image(image),
61 addOn(addOn),
62 refCount(1)
63 {
64 }
65
66 AddOnImage* image;
67 BDiskSystemAddOn* addOn;
68 int32 refCount;
69 };
70
71
72 // StringSet
73 struct DiskSystemAddOnManager::StringSet : std::set<std::string> {
74 };
75
76
77 // Default
78 DiskSystemAddOnManager*
Default()79 DiskSystemAddOnManager::Default()
80 {
81 if (sManager == NULL)
82 pthread_once(&sManagerInitOnce, &_InitSingleton);
83
84 return sManager;
85 }
86
87
88 // Lock
89 bool
Lock()90 DiskSystemAddOnManager::Lock()
91 {
92 return fLock.Lock();
93 }
94
95
96 // Unlock
97 void
Unlock()98 DiskSystemAddOnManager::Unlock()
99 {
100 fLock.Unlock();
101 }
102
103
104 // LoadDiskSystems
105 status_t
LoadDiskSystems()106 DiskSystemAddOnManager::LoadDiskSystems()
107 {
108 AutoLocker<BLocker> _(fLock);
109
110 if (++fLoadCount > 1)
111 return B_OK;
112
113 StringSet alreadyLoaded;
114 status_t error
115 = _LoadAddOns(alreadyLoaded, B_USER_NONPACKAGED_ADDONS_DIRECTORY);
116
117 if (error == B_OK)
118 error = _LoadAddOns(alreadyLoaded, B_USER_ADDONS_DIRECTORY);
119
120 if (error == B_OK) {
121 error
122 = _LoadAddOns(alreadyLoaded, B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY);
123 }
124
125 if (error == B_OK)
126 error = _LoadAddOns(alreadyLoaded, B_SYSTEM_ADDONS_DIRECTORY);
127
128 if (error != B_OK)
129 UnloadDiskSystems();
130
131 return error;
132 }
133
134
135 // UnloadDiskSystems
136 void
UnloadDiskSystems()137 DiskSystemAddOnManager::UnloadDiskSystems()
138 {
139 AutoLocker<BLocker> _(fLock);
140
141 if (fLoadCount == 0 || --fLoadCount > 0)
142 return;
143
144 fAddOnsToBeUnloaded.AddList(&fAddOns);
145 fAddOns.MakeEmpty();
146
147 // put all add-ons -- that will cause them to be deleted as soon as they're
148 // unused
149 for (int32 i = fAddOnsToBeUnloaded.CountItems() - 1; i >= 0; i--)
150 _PutAddOn(i);
151 }
152
153
154 // CountAddOns
155 int32
CountAddOns() const156 DiskSystemAddOnManager::CountAddOns() const
157 {
158 return fAddOns.CountItems();
159 }
160
161
162 // AddOnAt
163 BDiskSystemAddOn*
AddOnAt(int32 index) const164 DiskSystemAddOnManager::AddOnAt(int32 index) const
165 {
166 AddOn* addOn = _AddOnAt(index);
167 return addOn ? addOn->addOn : NULL;
168 }
169
170
171 // GetAddOn
172 BDiskSystemAddOn*
GetAddOn(const char * name)173 DiskSystemAddOnManager::GetAddOn(const char* name)
174 {
175 if (!name)
176 return NULL;
177
178 AutoLocker<BLocker> _(fLock);
179
180 for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) {
181 if (strcmp(addOn->addOn->Name(), name) == 0) {
182 addOn->refCount++;
183 return addOn->addOn;
184 }
185 }
186
187 return NULL;
188 }
189
190
191 // PutAddOn
192 void
PutAddOn(BDiskSystemAddOn * _addOn)193 DiskSystemAddOnManager::PutAddOn(BDiskSystemAddOn* _addOn)
194 {
195 if (!_addOn)
196 return;
197
198 AutoLocker<BLocker> _(fLock);
199
200 for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) {
201 if (_addOn == addOn->addOn) {
202 if (addOn->refCount > 1) {
203 addOn->refCount--;
204 } else {
205 debugger("Unbalanced call to "
206 "DiskSystemAddOnManager::PutAddOn()");
207 }
208 return;
209 }
210 }
211
212 for (int32 i = 0;
213 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(i); i++) {
214 if (_addOn == addOn->addOn) {
215 _PutAddOn(i);
216 return;
217 }
218 }
219
220 debugger("DiskSystemAddOnManager::PutAddOn(): disk system not found");
221 }
222
223
224 // constructor
DiskSystemAddOnManager()225 DiskSystemAddOnManager::DiskSystemAddOnManager()
226 : fLock("disk system add-ons manager"),
227 fAddOns(),
228 fAddOnsToBeUnloaded(),
229 fLoadCount(0)
230 {
231 }
232
233
234 /*static*/ void
_InitSingleton()235 DiskSystemAddOnManager::_InitSingleton()
236 {
237 sManager = new DiskSystemAddOnManager();
238 }
239
240
241 // _AddOnAt
242 DiskSystemAddOnManager::AddOn*
_AddOnAt(int32 index) const243 DiskSystemAddOnManager::_AddOnAt(int32 index) const
244 {
245 return (AddOn*)fAddOns.ItemAt(index);
246 }
247
248
249 // _PutAddOn
250 void
_PutAddOn(int32 index)251 DiskSystemAddOnManager::_PutAddOn(int32 index)
252 {
253 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(index);
254 if (!addOn)
255 return;
256
257 if (--addOn->refCount == 0) {
258 if (--addOn->image->refCount == 0)
259 delete addOn->image;
260
261 fAddOnsToBeUnloaded.RemoveItem(index);
262 delete addOn;
263 }
264 }
265
266
267 // _LoadAddOns
268 status_t
_LoadAddOns(StringSet & alreadyLoaded,directory_which addOnDir)269 DiskSystemAddOnManager::_LoadAddOns(StringSet& alreadyLoaded,
270 directory_which addOnDir)
271 {
272 // get the add-on directory path
273 BPath path;
274 status_t error = find_directory(addOnDir, &path, false);
275 if (error != B_OK)
276 return error;
277
278 TRACE("DiskSystemAddOnManager::_LoadAddOns(): %s\n", path.Path());
279
280 error = path.Append("disk_systems");
281 if (error != B_OK)
282 return error;
283
284 if (!BEntry(path.Path()).Exists())
285 return B_OK;
286
287 // open the directory and iterate through its entries
288 BDirectory directory;
289 error = directory.SetTo(path.Path());
290 if (error != B_OK)
291 return error;
292
293 entry_ref ref;
294 while (directory.GetNextRef(&ref) == B_OK) {
295 // skip, if already loaded
296 if (alreadyLoaded.find(ref.name) != alreadyLoaded.end()) {
297 TRACE(" skipping \"%s\" -- already loaded\n", ref.name);
298 continue;
299 }
300
301 // get the entry path
302 BPath entryPath;
303 error = entryPath.SetTo(&ref);
304 if (error != B_OK) {
305 if (error == B_NO_MEMORY)
306 return error;
307 TRACE(" skipping \"%s\" -- failed to get path\n", ref.name);
308 continue;
309 }
310
311 // load the add-on
312 image_id image = load_add_on(entryPath.Path());
313 if (image < 0) {
314 TRACE(" skipping \"%s\" -- failed to load add-on\n", ref.name);
315 continue;
316 }
317
318 AddOnImage* addOnImage = new(nothrow) AddOnImage(image);
319 if (!addOnImage) {
320 unload_add_on(image);
321 return B_NO_MEMORY;
322 }
323 ObjectDeleter<AddOnImage> addOnImageDeleter(addOnImage);
324
325 // get the add-on objects
326 status_t (*getAddOns)(BList*);
327 error = get_image_symbol(image, "get_disk_system_add_ons",
328 B_SYMBOL_TYPE_TEXT, (void**)&getAddOns);
329 if (error != B_OK) {
330 TRACE(" skipping \"%s\" -- function symbol not found\n", ref.name);
331 continue;
332 }
333
334 BList addOns;
335 error = getAddOns(&addOns);
336 if (error != B_OK || addOns.IsEmpty()) {
337 TRACE(" skipping \"%s\" -- getting add-ons failed\n", ref.name);
338 continue;
339 }
340
341 // create and add AddOn objects
342 int32 count = addOns.CountItems();
343 for (int32 i = 0; i < count; i++) {
344 BDiskSystemAddOn* diskSystemAddOn
345 = (BDiskSystemAddOn*)addOns.ItemAt(i);
346 AddOn* addOn = new(nothrow) AddOn(addOnImage, diskSystemAddOn);
347 if (!addOn)
348 return B_NO_MEMORY;
349
350 if (fAddOns.AddItem(addOn)) {
351 addOnImage->refCount++;
352 addOnImageDeleter.Detach();
353 } else {
354 delete addOn;
355 return B_NO_MEMORY;
356 }
357 }
358
359 TRACE(" got %ld BDiskSystemAddOn(s) from add-on \"%s\"\n", count,
360 ref.name);
361
362 // add the add-on name to the set of already loaded add-ons
363 try {
364 alreadyLoaded.insert(ref.name);
365 } catch (std::bad_alloc& exception) {
366 return B_NO_MEMORY;
367 }
368 }
369
370 return B_OK;
371 }
372