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_SYSTEM_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 205 for (int32 i = 0; 206 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(i); i++) { 207 if (_addOn == addOn->addOn) { 208 _PutAddOn(i); 209 return; 210 } 211 } 212 213 debugger("DiskSystemAddOnManager::PutAddOn(): disk system not found"); 214 } 215 216 217 // constructor 218 DiskSystemAddOnManager::DiskSystemAddOnManager() 219 : fLock("disk system add-ons manager"), 220 fAddOns(), 221 fAddOnsToBeUnloaded(), 222 fLoadCount(0) 223 { 224 } 225 226 227 /*static*/ void 228 DiskSystemAddOnManager::_InitSingleton() 229 { 230 sManager = new DiskSystemAddOnManager(); 231 } 232 233 234 // _AddOnAt 235 DiskSystemAddOnManager::AddOn* 236 DiskSystemAddOnManager::_AddOnAt(int32 index) const 237 { 238 return (AddOn*)fAddOns.ItemAt(index); 239 } 240 241 242 // _PutAddOn 243 void 244 DiskSystemAddOnManager::_PutAddOn(int32 index) 245 { 246 AddOn* addOn = (AddOn*)fAddOnsToBeUnloaded.ItemAt(index); 247 if (!addOn) 248 return; 249 250 if (--addOn->refCount == 0) { 251 if (--addOn->image->refCount == 0) 252 delete addOn->image; 253 254 fAddOnsToBeUnloaded.RemoveItem(index); 255 delete addOn; 256 } 257 } 258 259 260 // _LoadAddOns 261 status_t 262 DiskSystemAddOnManager::_LoadAddOns(StringSet& alreadyLoaded, 263 directory_which addOnDir) 264 { 265 // get the add-on directory path 266 BPath path; 267 status_t error = find_directory(addOnDir, &path, false); 268 if (error != B_OK) 269 return error; 270 271 TRACE("DiskSystemAddOnManager::_LoadAddOns(): %s\n", path.Path()); 272 273 error = path.Append("disk_systems"); 274 if (error != B_OK) 275 return error; 276 277 if (!BEntry(path.Path()).Exists()) 278 return B_OK; 279 280 // open the directory and iterate through its entries 281 BDirectory directory; 282 error = directory.SetTo(path.Path()); 283 if (error != B_OK) 284 return error; 285 286 entry_ref ref; 287 while (directory.GetNextRef(&ref) == B_OK) { 288 // skip, if already loaded 289 if (alreadyLoaded.find(ref.name) != alreadyLoaded.end()) { 290 TRACE(" skipping \"%s\" -- already loaded\n", ref.name); 291 continue; 292 } 293 294 // get the entry path 295 BPath entryPath; 296 error = entryPath.SetTo(&ref); 297 if (error != B_OK) { 298 if (error == B_NO_MEMORY) 299 return error; 300 TRACE(" skipping \"%s\" -- failed to get path\n", ref.name); 301 continue; 302 } 303 304 // load the add-on 305 image_id image = load_add_on(entryPath.Path()); 306 if (image < 0) { 307 TRACE(" skipping \"%s\" -- failed to load add-on\n", ref.name); 308 continue; 309 } 310 311 AddOnImage* addOnImage = new(nothrow) AddOnImage(image); 312 if (!addOnImage) { 313 unload_add_on(image); 314 return B_NO_MEMORY; 315 } 316 ObjectDeleter<AddOnImage> addOnImageDeleter(addOnImage); 317 318 // get the add-on objects 319 status_t (*getAddOns)(BList*); 320 error = get_image_symbol(image, "get_disk_system_add_ons", 321 B_SYMBOL_TYPE_TEXT, (void**)&getAddOns); 322 if (error != B_OK) { 323 TRACE(" skipping \"%s\" -- function symbol not found\n", ref.name); 324 continue; 325 } 326 327 BList addOns; 328 error = getAddOns(&addOns); 329 if (error != B_OK || addOns.IsEmpty()) { 330 TRACE(" skipping \"%s\" -- getting add-ons failed\n", ref.name); 331 continue; 332 } 333 334 // create and add AddOn objects 335 int32 count = addOns.CountItems(); 336 for (int32 i = 0; i < count; i++) { 337 BDiskSystemAddOn* diskSystemAddOn 338 = (BDiskSystemAddOn*)addOns.ItemAt(i); 339 AddOn* addOn = new(nothrow) AddOn(addOnImage, diskSystemAddOn); 340 if (!addOn) 341 return B_NO_MEMORY; 342 343 if (fAddOns.AddItem(addOn)) { 344 addOnImage->refCount++; 345 addOnImageDeleter.Detach(); 346 } else { 347 delete addOn; 348 return B_NO_MEMORY; 349 } 350 } 351 352 TRACE(" got %ld BDiskSystemAddOn(s) from add-on \"%s\"\n", count, 353 ref.name); 354 355 // add the add-on name to the set of already loaded add-ons 356 try { 357 alreadyLoaded.insert(ref.name); 358 } catch (std::bad_alloc& exception) { 359 return B_NO_MEMORY; 360 } 361 } 362 363 return B_OK; 364 } 365