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 32 using std::nothrow; 33 34 35 static pthread_once_t sManagerInitOnce = PTHREAD_ONCE_INIT; 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 == NULL) 82 pthread_once(&sManagerInitOnce, &_InitSingleton); 83 84 return sManager; 85 } 86 87 88 // Lock 89 bool 90 DiskSystemAddOnManager::Lock() 91 { 92 return fLock.Lock(); 93 } 94 95 96 // Unlock 97 void 98 DiskSystemAddOnManager::Unlock() 99 { 100 fLock.Unlock(); 101 } 102 103 104 // LoadDiskSystems 105 status_t 106 DiskSystemAddOnManager::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 136 void 137 DiskSystemAddOnManager::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 155 int32 156 DiskSystemAddOnManager::CountAddOns() const 157 { 158 return fAddOns.CountItems(); 159 } 160 161 162 // AddOnAt 163 BDiskSystemAddOn* 164 DiskSystemAddOnManager::AddOnAt(int32 index) const 165 { 166 AddOn* addOn = _AddOnAt(index); 167 return addOn ? addOn->addOn : NULL; 168 } 169 170 171 // GetAddOn 172 BDiskSystemAddOn* 173 DiskSystemAddOnManager::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 192 void 193 DiskSystemAddOnManager::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 225 DiskSystemAddOnManager::DiskSystemAddOnManager() 226 : fLock("disk system add-ons manager"), 227 fAddOns(), 228 fAddOnsToBeUnloaded(), 229 fLoadCount(0) 230 { 231 } 232 233 234 /*static*/ void 235 DiskSystemAddOnManager::_InitSingleton() 236 { 237 sManager = new DiskSystemAddOnManager(); 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