xref: /haiku/src/kits/storage/disk_device/DiskSystemAddOnManager.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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
114 		= _LoadAddOns(alreadyLoaded, B_USER_NONPACKAGED_ADDONS_DIRECTORY);
115 
116 	if (error == B_OK)
117 		error = _LoadAddOns(alreadyLoaded, B_USER_ADDONS_DIRECTORY);
118 
119 	if (error == B_OK) {
120 		error
121 			= _LoadAddOns(alreadyLoaded, B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY);
122 	}
123 
124 	if (error == B_OK)
125 		error = _LoadAddOns(alreadyLoaded, B_SYSTEM_ADDONS_DIRECTORY);
126 
127 	if (error != B_OK)
128 		UnloadDiskSystems();
129 
130 	return error;
131 }
132 
133 
134 // UnloadDiskSystems
135 void
136 DiskSystemAddOnManager::UnloadDiskSystems()
137 {
138 	AutoLocker<BLocker> _(fLock);
139 
140 	if (fLoadCount == 0 || --fLoadCount > 0)
141 		return;
142 
143 	fAddOnsToBeUnloaded.AddList(&fAddOns);
144 	fAddOns.MakeEmpty();
145 
146 	// put all add-ons -- that will cause them to be deleted as soon as they're
147 	// unused
148 	for (int32 i = fAddOnsToBeUnloaded.CountItems() - 1; i >= 0; i--)
149 		_PutAddOn(i);
150 }
151 
152 
153 // CountAddOns
154 int32
155 DiskSystemAddOnManager::CountAddOns() const
156 {
157 	return fAddOns.CountItems();
158 }
159 
160 
161 // AddOnAt
162 BDiskSystemAddOn*
163 DiskSystemAddOnManager::AddOnAt(int32 index) const
164 {
165 	AddOn* addOn = _AddOnAt(index);
166 	return addOn ? addOn->addOn : NULL;
167 }
168 
169 
170 // GetAddOn
171 BDiskSystemAddOn*
172 DiskSystemAddOnManager::GetAddOn(const char* name)
173 {
174 	if (!name)
175 		return NULL;
176 
177 	AutoLocker<BLocker> _(fLock);
178 
179 	for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) {
180 		if (strcmp(addOn->addOn->Name(), name) == 0) {
181 			addOn->refCount++;
182 			return addOn->addOn;
183 		}
184 	}
185 
186 	return NULL;
187 }
188 
189 
190 // PutAddOn
191 void
192 DiskSystemAddOnManager::PutAddOn(BDiskSystemAddOn* _addOn)
193 {
194 	if (!_addOn)
195 		return;
196 
197 	AutoLocker<BLocker> _(fLock);
198 
199 	for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) {
200 		if (_addOn == addOn->addOn) {
201 			if (addOn->refCount > 1) {
202 				addOn->refCount--;
203 			} else {
204 				debugger("Unbalanced call to "
205 					"DiskSystemAddOnManager::PutAddOn()");
206 			}
207 			return;
208 		}
209 	}
210 
211 	for (int32 i = 0;
212 		 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(i); i++) {
213 		if (_addOn == addOn->addOn) {
214 			_PutAddOn(i);
215 			return;
216 		}
217 	}
218 
219 	debugger("DiskSystemAddOnManager::PutAddOn(): disk system not found");
220 }
221 
222 
223 // constructor
224 DiskSystemAddOnManager::DiskSystemAddOnManager()
225 	: fLock("disk system add-ons manager"),
226 	  fAddOns(),
227 	  fAddOnsToBeUnloaded(),
228 	  fLoadCount(0)
229 {
230 }
231 
232 
233 /*static*/ void
234 DiskSystemAddOnManager::_InitSingleton()
235 {
236 	sManager = new DiskSystemAddOnManager();
237 }
238 
239 
240 // _AddOnAt
241 DiskSystemAddOnManager::AddOn*
242 DiskSystemAddOnManager::_AddOnAt(int32 index) const
243 {
244 	return (AddOn*)fAddOns.ItemAt(index);
245 }
246 
247 
248 // _PutAddOn
249 void
250 DiskSystemAddOnManager::_PutAddOn(int32 index)
251 {
252 	AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(index);
253 	if (!addOn)
254 		return;
255 
256 	if (--addOn->refCount == 0) {
257 		if (--addOn->image->refCount == 0)
258 			delete addOn->image;
259 
260 		fAddOnsToBeUnloaded.RemoveItem(index);
261 		delete addOn;
262 	}
263 }
264 
265 
266 // _LoadAddOns
267 status_t
268 DiskSystemAddOnManager::_LoadAddOns(StringSet& alreadyLoaded,
269 	directory_which addOnDir)
270 {
271 	// get the add-on directory path
272 	BPath path;
273 	status_t error = find_directory(addOnDir, &path, false);
274 	if (error != B_OK)
275 		return error;
276 
277 	TRACE("DiskSystemAddOnManager::_LoadAddOns(): %s\n", path.Path());
278 
279 	error = path.Append("disk_systems");
280 	if (error != B_OK)
281 		return error;
282 
283 	if (!BEntry(path.Path()).Exists())
284 		return B_OK;
285 
286 	// open the directory and iterate through its entries
287 	BDirectory directory;
288 	error = directory.SetTo(path.Path());
289 	if (error != B_OK)
290 		return error;
291 
292 	entry_ref ref;
293 	while (directory.GetNextRef(&ref) == B_OK) {
294 		// skip, if already loaded
295 		if (alreadyLoaded.find(ref.name) != alreadyLoaded.end()) {
296 			TRACE("  skipping \"%s\" -- already loaded\n", ref.name);
297 			continue;
298 		}
299 
300 		// get the entry path
301 		BPath entryPath;
302 		error = entryPath.SetTo(&ref);
303 		if (error != B_OK) {
304 			if (error == B_NO_MEMORY)
305 				return error;
306 			TRACE("  skipping \"%s\" -- failed to get path\n", ref.name);
307 			continue;
308 		}
309 
310 		// load the add-on
311 		image_id image = load_add_on(entryPath.Path());
312 		if (image < 0) {
313 			TRACE("  skipping \"%s\" -- failed to load add-on\n", ref.name);
314 			continue;
315 		}
316 
317 		AddOnImage* addOnImage = new(nothrow) AddOnImage(image);
318 		if (!addOnImage) {
319 			unload_add_on(image);
320 			return B_NO_MEMORY;
321 		}
322 		ObjectDeleter<AddOnImage> addOnImageDeleter(addOnImage);
323 
324 		// get the add-on objects
325 		status_t (*getAddOns)(BList*);
326 		error = get_image_symbol(image, "get_disk_system_add_ons",
327 			B_SYMBOL_TYPE_TEXT, (void**)&getAddOns);
328 		if (error != B_OK) {
329 			TRACE("  skipping \"%s\" -- function symbol not found\n", ref.name);
330 			continue;
331 		}
332 
333 		BList addOns;
334 		error = getAddOns(&addOns);
335 		if (error != B_OK || addOns.IsEmpty()) {
336 			TRACE("  skipping \"%s\" -- getting add-ons failed\n", ref.name);
337 			continue;
338 		}
339 
340 		// create and add AddOn objects
341 		int32 count = addOns.CountItems();
342 		for (int32 i = 0; i < count; i++) {
343 			BDiskSystemAddOn* diskSystemAddOn
344 				= (BDiskSystemAddOn*)addOns.ItemAt(i);
345 			AddOn* addOn = new(nothrow) AddOn(addOnImage, diskSystemAddOn);
346 			if (!addOn)
347 				return B_NO_MEMORY;
348 
349 			if (fAddOns.AddItem(addOn)) {
350 				addOnImage->refCount++;
351 				addOnImageDeleter.Detach();
352 			} else {
353 				delete addOn;
354 				return B_NO_MEMORY;
355 			}
356 		}
357 
358 		TRACE("  got %ld BDiskSystemAddOn(s) from add-on \"%s\"\n", count,
359 			ref.name);
360 
361 		// add the add-on name to the set of already loaded add-ons
362 		try {
363 			alreadyLoaded.insert(ref.name);
364 		} catch (std::bad_alloc& exception) {
365 			return B_NO_MEMORY;
366 		}
367 	}
368 
369 	return B_OK;
370 }
371