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