xref: /haiku/src/kits/storage/disk_device/DiskSystemAddOnManager.cpp (revision f1fafe317f3b018f7d4d5e209aef1dbb35f77ad7)
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