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