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 32using std::nothrow; 33 34 35static pthread_once_t sManagerInitOnce = PTHREAD_ONCE_INIT; 36DiskSystemAddOnManager* DiskSystemAddOnManager::sManager = NULL; 37 38 39// AddOnImage 40struct 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 58struct 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 73struct DiskSystemAddOnManager::StringSet : std::set<std::string> { 74}; 75 76 77// Default 78DiskSystemAddOnManager* 79DiskSystemAddOnManager::Default() 80{ 81 if (sManager == NULL) 82 pthread_once(&sManagerInitOnce, &_InitSingleton); 83 84 return sManager; 85} 86 87 88// Lock 89bool 90DiskSystemAddOnManager::Lock() 91{ 92 return fLock.Lock(); 93} 94 95 96// Unlock 97void 98DiskSystemAddOnManager::Unlock() 99{ 100 fLock.Unlock(); 101} 102 103 104// LoadDiskSystems 105status_t 106DiskSystemAddOnManager::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 136void 137DiskSystemAddOnManager::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 155int32 156DiskSystemAddOnManager::CountAddOns() const 157{ 158 return fAddOns.CountItems(); 159} 160 161 162// AddOnAt 163BDiskSystemAddOn* 164DiskSystemAddOnManager::AddOnAt(int32 index) const 165{ 166 AddOn* addOn = _AddOnAt(index); 167 return addOn ? addOn->addOn : NULL; 168} 169 170 171// GetAddOn 172BDiskSystemAddOn* 173DiskSystemAddOnManager::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 192void 193DiskSystemAddOnManager::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 225DiskSystemAddOnManager::DiskSystemAddOnManager() 226 : fLock("disk system add-ons manager"), 227 fAddOns(), 228 fAddOnsToBeUnloaded(), 229 fLoadCount(0) 230{ 231} 232 233 234/*static*/ void 235DiskSystemAddOnManager::_InitSingleton() 236{ 237 sManager = new DiskSystemAddOnManager(); 238} 239 240 241// _AddOnAt 242DiskSystemAddOnManager::AddOn* 243DiskSystemAddOnManager::_AddOnAt(int32 index) const 244{ 245 return (AddOn*)fAddOns.ItemAt(index); 246} 247 248 249// _PutAddOn 250void 251DiskSystemAddOnManager::_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 268status_t 269DiskSystemAddOnManager::_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