xref: /haiku/src/kits/storage/disk_device/DiskSystemAddOnManager.cpp (revision 3be9edf8da228afd9fec0390f408c964766122aa)
1 /*
2  * Copyright 2007, 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 
15 #include <Directory.h>
16 #include <Entry.h>
17 #include <image.h>
18 #include <Path.h>
19 
20 #include <AppMisc.h>
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 // sManager
36 DiskSystemAddOnManager* DiskSystemAddOnManager::sManager = NULL;
37 
38 
39 // AddOnImage
40 struct DiskSystemAddOnManager::AddOnImage {
41 	AddOnImage(image_id image)
42 		: image(image),
43 		  refCount(0)
44 	{
45 	}
46 
47 	~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 {
59 	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*
79 DiskSystemAddOnManager::Default()
80 {
81 	if (!sManager) {
82 		DiskSystemAddOnManager* manager = new DiskSystemAddOnManager();
83 
84 		BPrivate::gInitializationLock.Lock();
85 
86 		// set manager, if no one beat us to it
87 		if (!sManager) {
88 			sManager = manager;
89 			manager = NULL;
90 		}
91 
92 		BPrivate::gInitializationLock.Unlock();
93 
94 		// delete the object we created, if someone else was quicker
95 		delete manager;
96 	}
97 
98 	return sManager;
99 }
100 
101 
102 // Lock
103 bool
104 DiskSystemAddOnManager::Lock()
105 {
106 	return fLock.Lock();
107 }
108 
109 
110 // Unlock
111 void
112 DiskSystemAddOnManager::Unlock()
113 {
114 	fLock.Unlock();
115 }
116 
117 
118 // LoadDiskSystems
119 status_t
120 DiskSystemAddOnManager::LoadDiskSystems()
121 {
122 	AutoLocker<BLocker> _(fLock);
123 
124 	if (++fLoadCount > 1)
125 		return B_OK;
126 
127 	StringSet alreadyLoaded;
128 	status_t error = _LoadAddOns(alreadyLoaded, B_USER_ADDONS_DIRECTORY);
129 
130 	if (error == B_OK)
131 		error = _LoadAddOns(alreadyLoaded, B_COMMON_ADDONS_DIRECTORY);
132 
133 	if (error == B_OK)
134 		error = _LoadAddOns(alreadyLoaded, B_BEOS_ADDONS_DIRECTORY);
135 
136 	if (error != B_OK)
137 		UnloadDiskSystems();
138 
139 	return error;
140 }
141 
142 
143 // UnloadDiskSystems
144 void
145 DiskSystemAddOnManager::UnloadDiskSystems()
146 {
147 	AutoLocker<BLocker> _(fLock);
148 
149 	if (fLoadCount == 0 || --fLoadCount > 0)
150 		return;
151 
152 	fAddOnsToBeUnloaded.AddList(&fAddOns);
153 	fAddOns.MakeEmpty();
154 
155 	// put all add-ons -- that will cause them to be deleted as soon as they're
156 	// unused
157 	for (int32 i = fAddOnsToBeUnloaded.CountItems() - 1; i >= 0; i--)
158 		_PutAddOn(i);
159 }
160 
161 
162 // CountAddOns
163 int32
164 DiskSystemAddOnManager::CountAddOns() const
165 {
166 	return fAddOns.CountItems();
167 }
168 
169 
170 // AddOnAt
171 BDiskSystemAddOn*
172 DiskSystemAddOnManager::AddOnAt(int32 index) const
173 {
174 	AddOn* addOn = _AddOnAt(index);
175 	return addOn ? addOn->addOn : NULL;
176 }
177 
178 
179 // GetAddOn
180 BDiskSystemAddOn*
181 DiskSystemAddOnManager::GetAddOn(const char* name)
182 {
183 	if (!name)
184 		return NULL;
185 
186 	AutoLocker<BLocker> _(fLock);
187 
188 	for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) {
189 		if (strcmp(addOn->addOn->Name(), name) == 0) {
190 			addOn->refCount++;
191 			return addOn->addOn;
192 		}
193 	}
194 
195 	return NULL;
196 }
197 
198 
199 // PutAddOn
200 void
201 DiskSystemAddOnManager::PutAddOn(BDiskSystemAddOn* _addOn)
202 {
203 	if (!_addOn)
204 		return;
205 
206 	AutoLocker<BLocker> _(fLock);
207 
208 	for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) {
209 		if (_addOn == addOn->addOn) {
210 			if (addOn->refCount > 1) {
211 				addOn->refCount--;
212 			} else {
213 				debugger("Unbalanced call to "
214 					"DiskSystemAddOnManager::PutAddOn()");
215 			}
216 			return;
217 		}
218 
219 	for (int32 i = 0;
220 		 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(i); i++) {
221 		if (_addOn == addOn->addOn)
222 			_PutAddOn(i);
223 			return;
224 		}
225 	}
226 
227 	debugger("DiskSystemAddOnManager::PutAddOn(): disk system not found");
228 }
229 
230 
231 // constructor
232 DiskSystemAddOnManager::DiskSystemAddOnManager()
233 	: fLock("disk system add-ons manager"),
234 	  fAddOns(),
235 	  fAddOnsToBeUnloaded(),
236 	  fLoadCount(0)
237 {
238 }
239 
240 
241 // _AddOnAt
242 DiskSystemAddOnManager::AddOn*
243 DiskSystemAddOnManager::_AddOnAt(int32 index) const
244 {
245 	return (AddOn*)fAddOns.ItemAt(index);
246 }
247 
248 
249 // _PutAddOn
250 void
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
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 
373