1 /* 2 * Copyright 2007, 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 <AppMisc.h> 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 // sManager 36 DiskSystemAddOnManager* DiskSystemAddOnManager::sManager = NULL; 37 38 39 // AddOnImage 40 struct 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 58 struct 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 73 struct DiskSystemAddOnManager::StringSet : std::set<std::string> { 74 }; 75 76 77 // Default 78 DiskSystemAddOnManager* 79 DiskSystemAddOnManager::Default() 80 { 81 if (!sManager) { 82 DiskSystemAddOnManager* manager = new DiskSystemAddOnManager(); 83 84 BPrivate::gInitializationLock.Lock(); 85 86 // set manager, if no one beat us to it 87 if (!sManager) { 88 sManager = manager; 89 manager = NULL; 90 } 91 92 BPrivate::gInitializationLock.Unlock(); 93 94 // delete the object we created, if someone else was quicker 95 delete manager; 96 } 97 98 return sManager; 99 } 100 101 102 // Lock 103 bool 104 DiskSystemAddOnManager::Lock() 105 { 106 return fLock.Lock(); 107 } 108 109 110 // Unlock 111 void 112 DiskSystemAddOnManager::Unlock() 113 { 114 fLock.Unlock(); 115 } 116 117 118 // LoadDiskSystems 119 status_t 120 DiskSystemAddOnManager::LoadDiskSystems() 121 { 122 AutoLocker<BLocker> _(fLock); 123 124 if (++fLoadCount > 1) 125 return B_OK; 126 127 StringSet alreadyLoaded; 128 status_t error = _LoadAddOns(alreadyLoaded, B_USER_ADDONS_DIRECTORY); 129 130 if (error == B_OK) 131 error = _LoadAddOns(alreadyLoaded, B_COMMON_ADDONS_DIRECTORY); 132 133 if (error == B_OK) 134 error = _LoadAddOns(alreadyLoaded, B_BEOS_ADDONS_DIRECTORY); 135 136 if (error != B_OK) 137 UnloadDiskSystems(); 138 139 return error; 140 } 141 142 143 // UnloadDiskSystems 144 void 145 DiskSystemAddOnManager::UnloadDiskSystems() 146 { 147 AutoLocker<BLocker> _(fLock); 148 149 if (fLoadCount == 0 || --fLoadCount > 0) 150 return; 151 152 fAddOnsToBeUnloaded.AddList(&fAddOns); 153 fAddOns.MakeEmpty(); 154 155 // put all add-ons -- that will cause them to be deleted as soon as they're 156 // unused 157 for (int32 i = fAddOnsToBeUnloaded.CountItems() - 1; i >= 0; i--) 158 _PutAddOn(i); 159 } 160 161 162 // CountAddOns 163 int32 164 DiskSystemAddOnManager::CountAddOns() const 165 { 166 return fAddOns.CountItems(); 167 } 168 169 170 // AddOnAt 171 BDiskSystemAddOn* 172 DiskSystemAddOnManager::AddOnAt(int32 index) const 173 { 174 AddOn* addOn = _AddOnAt(index); 175 return addOn ? addOn->addOn : NULL; 176 } 177 178 179 // GetAddOn 180 BDiskSystemAddOn* 181 DiskSystemAddOnManager::GetAddOn(const char* name) 182 { 183 if (!name) 184 return NULL; 185 186 AutoLocker<BLocker> _(fLock); 187 188 for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) { 189 if (strcmp(addOn->addOn->Name(), name) == 0) { 190 addOn->refCount++; 191 return addOn->addOn; 192 } 193 } 194 195 return NULL; 196 } 197 198 199 // PutAddOn 200 void 201 DiskSystemAddOnManager::PutAddOn(BDiskSystemAddOn* _addOn) 202 { 203 if (!_addOn) 204 return; 205 206 AutoLocker<BLocker> _(fLock); 207 208 for (int32 i = 0; AddOn* addOn = _AddOnAt(i); i++) { 209 if (_addOn == addOn->addOn) { 210 if (addOn->refCount > 1) { 211 addOn->refCount--; 212 } else { 213 debugger("Unbalanced call to " 214 "DiskSystemAddOnManager::PutAddOn()"); 215 } 216 return; 217 } 218 219 for (int32 i = 0; 220 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(i); i++) { 221 if (_addOn == addOn->addOn) 222 _PutAddOn(i); 223 return; 224 } 225 } 226 227 debugger("DiskSystemAddOnManager::PutAddOn(): disk system not found"); 228 } 229 230 231 // constructor 232 DiskSystemAddOnManager::DiskSystemAddOnManager() 233 : fLock("disk system add-ons manager"), 234 fAddOns(), 235 fAddOnsToBeUnloaded(), 236 fLoadCount(0) 237 { 238 } 239 240 241 // _AddOnAt 242 DiskSystemAddOnManager::AddOn* 243 DiskSystemAddOnManager::_AddOnAt(int32 index) const 244 { 245 return (AddOn*)fAddOns.ItemAt(index); 246 } 247 248 249 // _PutAddOn 250 void 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 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 373