xref: /haiku/src/kits/storage/disk_device/DiskSystemAddOnManager.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
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 
205 	for (int32 i = 0;
206 		 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(i); i++) {
207 		if (_addOn == addOn->addOn) {
208 			_PutAddOn(i);
209 			return;
210 		}
211 	}
212 
213 	debugger("DiskSystemAddOnManager::PutAddOn(): disk system not found");
214 }
215 
216 
217 // constructor
218 DiskSystemAddOnManager::DiskSystemAddOnManager()
219 	: fLock("disk system add-ons manager"),
220 	  fAddOns(),
221 	  fAddOnsToBeUnloaded(),
222 	  fLoadCount(0)
223 {
224 }
225 
226 
227 /*static*/ void
228 DiskSystemAddOnManager::_InitSingleton()
229 {
230 	sManager = new DiskSystemAddOnManager();
231 }
232 
233 
234 // _AddOnAt
235 DiskSystemAddOnManager::AddOn*
236 DiskSystemAddOnManager::_AddOnAt(int32 index) const
237 {
238 	return (AddOn*)fAddOns.ItemAt(index);
239 }
240 
241 
242 // _PutAddOn
243 void
244 DiskSystemAddOnManager::_PutAddOn(int32 index)
245 {
246 	AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(index);
247 	if (!addOn)
248 		return;
249 
250 	if (--addOn->refCount == 0) {
251 		if (--addOn->image->refCount == 0)
252 			delete addOn->image;
253 
254 		fAddOnsToBeUnloaded.RemoveItem(index);
255 		delete addOn;
256 	}
257 }
258 
259 
260 // _LoadAddOns
261 status_t
262 DiskSystemAddOnManager::_LoadAddOns(StringSet& alreadyLoaded,
263 	directory_which addOnDir)
264 {
265 	// get the add-on directory path
266 	BPath path;
267 	status_t error = find_directory(addOnDir, &path, false);
268 	if (error != B_OK)
269 		return error;
270 
271 	TRACE("DiskSystemAddOnManager::_LoadAddOns(): %s\n", path.Path());
272 
273 	error = path.Append("disk_systems");
274 	if (error != B_OK)
275 		return error;
276 
277 	if (!BEntry(path.Path()).Exists())
278 		return B_OK;
279 
280 	// open the directory and iterate through its entries
281 	BDirectory directory;
282 	error = directory.SetTo(path.Path());
283 	if (error != B_OK)
284 		return error;
285 
286 	entry_ref ref;
287 	while (directory.GetNextRef(&ref) == B_OK) {
288 		// skip, if already loaded
289 		if (alreadyLoaded.find(ref.name) != alreadyLoaded.end()) {
290 			TRACE("  skipping \"%s\" -- already loaded\n", ref.name);
291 			continue;
292 		}
293 
294 		// get the entry path
295 		BPath entryPath;
296 		error = entryPath.SetTo(&ref);
297 		if (error != B_OK) {
298 			if (error == B_NO_MEMORY)
299 				return error;
300 			TRACE("  skipping \"%s\" -- failed to get path\n", ref.name);
301 			continue;
302 		}
303 
304 		// load the add-on
305 		image_id image = load_add_on(entryPath.Path());
306 		if (image < 0) {
307 			TRACE("  skipping \"%s\" -- failed to load add-on\n", ref.name);
308 			continue;
309 		}
310 
311 		AddOnImage* addOnImage = new(nothrow) AddOnImage(image);
312 		if (!addOnImage) {
313 			unload_add_on(image);
314 			return B_NO_MEMORY;
315 		}
316 		ObjectDeleter<AddOnImage> addOnImageDeleter(addOnImage);
317 
318 		// get the add-on objects
319 		status_t (*getAddOns)(BList*);
320 		error = get_image_symbol(image, "get_disk_system_add_ons",
321 			B_SYMBOL_TYPE_TEXT, (void**)&getAddOns);
322 		if (error != B_OK) {
323 			TRACE("  skipping \"%s\" -- function symbol not found\n", ref.name);
324 			continue;
325 		}
326 
327 		BList addOns;
328 		error = getAddOns(&addOns);
329 		if (error != B_OK || addOns.IsEmpty()) {
330 			TRACE("  skipping \"%s\" -- getting add-ons failed\n", ref.name);
331 			continue;
332 		}
333 
334 		// create and add AddOn objects
335 		int32 count = addOns.CountItems();
336 		for (int32 i = 0; i < count; i++) {
337 			BDiskSystemAddOn* diskSystemAddOn
338 				= (BDiskSystemAddOn*)addOns.ItemAt(i);
339 			AddOn* addOn = new(nothrow) AddOn(addOnImage, diskSystemAddOn);
340 			if (!addOn)
341 				return B_NO_MEMORY;
342 
343 			if (fAddOns.AddItem(addOn)) {
344 				addOnImage->refCount++;
345 				addOnImageDeleter.Detach();
346 			} else {
347 				delete addOn;
348 				return B_NO_MEMORY;
349 			}
350 		}
351 
352 		TRACE("  got %ld BDiskSystemAddOn(s) from add-on \"%s\"\n", count,
353 			ref.name);
354 
355 		// add the add-on name to the set of already loaded add-ons
356 		try {
357 			alreadyLoaded.insert(ref.name);
358 		} catch (std::bad_alloc& exception) {
359 			return B_NO_MEMORY;
360 		}
361 	}
362 
363 	return B_OK;
364 }
365