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