1 /* 2 * Copyright 2002-2009, Haiku Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Ingo Weinhold, bonefish@cs.tu-berlin.de. 7 * Axel Dörfler, axeld@pinc-software.de. 8 */ 9 10 #include <set> 11 #include <stdio.h> 12 #include <string> 13 14 #include <Autolock.h> 15 #include <Directory.h> 16 #include <Entry.h> 17 #include <KernelExport.h> 18 #include <module.h> 19 #include <kmodule.h> 20 #include <List.h> 21 #include <Locker.h> 22 #include <Notifications.h> 23 #include <ObjectList.h> 24 #include <Path.h> 25 #include <String.h> 26 27 #ifdef TRACE 28 # undef TRACE 29 #endif 30 #define TRACE(x) 31 //#define TRACE(x) printf x 32 33 34 using namespace std; 35 36 37 static const char *gModuleDirs[] = { 38 "generated/objects/haiku/x86/release/add-ons/userland", 39 "generated/objects/haiku/x86/release/tests/add-ons/kernel", 40 NULL 41 }; 42 43 44 struct module_name_list { 45 set<string> names; 46 set<string>::iterator it; 47 }; 48 49 50 // ModuleAddOn 51 52 class ModuleAddOn { 53 public: 54 ModuleAddOn(); 55 ~ModuleAddOn(); 56 57 status_t Load(const char *path, const char *dirPath); 58 void Unload(); 59 60 const char *Name() { return fName.String(); } 61 62 status_t Get(); 63 bool Put(); 64 65 module_info **ModuleInfos() const { return fInfos; } 66 module_info *FindModuleInfo(const char *name) const; 67 68 private: 69 image_id fAddOn; 70 module_info **fInfos; 71 int32 fReferenceCount; 72 BString fName; 73 }; 74 75 class Module { 76 public: 77 Module(ModuleAddOn *addon, module_info *info); 78 ~Module(); 79 80 status_t Init(); 81 status_t Uninit(); 82 83 status_t Get(); 84 bool Put(); 85 86 ModuleAddOn *AddOn() const { return fAddOn; } 87 module_info *Info() const { return fInfo; } 88 89 private: 90 ModuleAddOn *fAddOn; 91 module_info *fInfo; 92 int32 fReferenceCount; 93 bool fInitialized; 94 }; 95 96 class ModuleList : public BLocker { 97 public: 98 ModuleList(); 99 ~ModuleList(); 100 101 int32 CountModules() const; 102 Module *ModuleAt(int32 index) const; 103 104 bool AddModule(Module *module); 105 bool RemoveModule(Module *module); 106 Module *FindModule(const char *path); 107 108 private: 109 BList fModules; 110 }; 111 112 class ModuleManager { 113 public: 114 ModuleManager(); 115 ~ModuleManager(); 116 117 static ModuleManager *Default() { return &sDefaultManager; } 118 119 status_t GetModule(const char *path, module_info **infop); 120 status_t PutModule(const char *path); 121 122 status_t GetNextLoadedModuleName(uint32 *cookie, char *buffer, 123 size_t *bufferSize); 124 125 module_name_list *OpenModuleList(const char *prefix, 126 const char *suffix = NULL); 127 status_t ReadNextModuleName(module_name_list *list, char *buffer, 128 size_t *bufferSize); 129 status_t CloseModuleList(module_name_list *list); 130 131 status_t AddBuiltInModule(module_info *info); 132 133 status_t GetDependencies(image_id image); 134 void PutDependencies(image_id image); 135 136 private: 137 bool _MatchSuffix(const char *name, const char *suffix); 138 void _FindModules(BDirectory &dir, const char *moduleDir, 139 const char *suffix, module_name_list *list); 140 void _FindBuiltInModules(const char *prefix, const char *suffix, 141 module_name_list *list); 142 143 status_t _GetAddOn(const char *path, ModuleAddOn **addon); 144 void _PutAddOn(ModuleAddOn *addon); 145 146 private: 147 static ModuleManager sDefaultManager; 148 ModuleList fModules; 149 BObjectList<ModuleAddOn> fAddOns; 150 }; 151 152 153 // #pragma mark - ModuleAddOn 154 155 156 ModuleAddOn::ModuleAddOn() 157 : fAddOn(-1), 158 fInfos(NULL), 159 fReferenceCount(0) 160 { 161 } 162 163 164 ModuleAddOn::~ModuleAddOn() 165 { 166 Unload(); 167 } 168 169 // Load 170 status_t 171 ModuleAddOn::Load(const char *path, const char *dirPath) 172 { 173 TRACE(("ModuleAddOn::Load(): searching module `%s'...\n", path)); 174 Unload(); 175 status_t error = (path && dirPath ? B_OK : B_BAD_VALUE); 176 if (error == B_OK) { 177 // get the module dir relative path 178 BPath absPath; 179 BPath absDirPath; 180 if (absPath.SetTo(path, NULL, true) != B_OK 181 || absDirPath.SetTo(dirPath, NULL, true) != B_OK 182 || strlen(absPath.Path()) <= strlen(absDirPath.Path())) { 183 return B_ENTRY_NOT_FOUND; 184 } 185 int32 dirPathLen = strlen(absDirPath.Path()); 186 if (strncmp(absPath.Path(), absDirPath.Path(), dirPathLen) 187 || absPath.Path()[dirPathLen] != '/') { 188 return B_ENTRY_NOT_FOUND; 189 } 190 const char *name = absPath.Path() + dirPathLen + 1; 191 // load the file 192 error = B_ENTRY_NOT_FOUND; 193 BEntry entry; 194 if (entry.SetTo(path) == B_OK && entry.Exists()) { 195 image_id image = load_add_on(path); 196 module_info **infos = NULL; 197 if (image >= 0 198 && get_image_symbol(image, "modules", B_SYMBOL_TYPE_DATA, 199 (void**)&infos) == B_OK 200 && infos != NULL) { 201 fAddOn = image; 202 fInfos = infos; 203 fName = name; 204 fReferenceCount = 0; 205 error = B_OK; 206 } 207 } 208 } 209 return error; 210 } 211 212 // Unload 213 void 214 ModuleAddOn::Unload() 215 { 216 if (fAddOn >= 0) 217 unload_add_on(fAddOn); 218 fAddOn = -1; 219 fInfos = NULL; 220 fReferenceCount = 0; 221 } 222 223 // Get 224 status_t 225 ModuleAddOn::Get() 226 { 227 if (fAddOn >= 0) { 228 if (fReferenceCount == 0) { 229 status_t status = ModuleManager::Default()->GetDependencies(fAddOn); 230 if (status < B_OK) 231 return status; 232 } 233 fReferenceCount++; 234 } 235 236 return B_OK; 237 } 238 239 // Put 240 bool 241 ModuleAddOn::Put() 242 { 243 if (fAddOn >= 0) 244 fReferenceCount--; 245 246 if (fReferenceCount == 0) { 247 ModuleManager::Default()->PutDependencies(fAddOn); 248 return true; 249 } 250 return false; 251 } 252 253 // FindModuleInfo 254 module_info * 255 ModuleAddOn::FindModuleInfo(const char *name) const 256 { 257 if (fInfos && name) { 258 for (int32 i = 0; module_info *info = fInfos[i]; i++) { 259 if (!strcmp(info->name, name)) 260 return info; 261 } 262 } 263 return NULL; 264 } 265 266 267 // #pragma mark - Module 268 269 270 Module::Module(ModuleAddOn *addon, module_info *info) 271 : fAddOn(addon), 272 fInfo(info), 273 fReferenceCount(0), 274 fInitialized(false) 275 { 276 } 277 278 // destructor 279 Module::~Module() 280 { 281 } 282 283 // Init 284 status_t 285 Module::Init() 286 { 287 status_t error = (fInfo ? B_OK : B_NO_INIT); 288 if (error == B_OK && !fInitialized) { 289 if (fInfo->std_ops != NULL) 290 error = fInfo->std_ops(B_MODULE_INIT); 291 if (error == B_OK) 292 fInitialized = true; 293 } 294 return error; 295 } 296 297 // Uninit 298 status_t 299 Module::Uninit() 300 { 301 status_t error = (fInfo ? B_OK : B_NO_INIT); 302 if (error == B_OK && fInitialized) { 303 if (fInfo->std_ops != NULL) 304 error = fInfo->std_ops(B_MODULE_UNINIT); 305 fInitialized = false; 306 } 307 return error; 308 } 309 310 311 status_t 312 Module::Get() 313 { 314 if (fReferenceCount == 0) { 315 status_t status = Init(); 316 if (status < B_OK) 317 return status; 318 } 319 320 fReferenceCount++; 321 return B_OK; 322 } 323 324 325 bool 326 Module::Put() 327 { 328 if (--fReferenceCount > 0) 329 return false; 330 331 Uninit(); 332 return fAddOn && !(fInfo->flags & B_KEEP_LOADED); 333 } 334 335 336 // #pragma mark - ModuleList 337 338 339 ModuleList::ModuleList() 340 { 341 } 342 343 344 ModuleList::~ModuleList() 345 { 346 } 347 348 // CountModules 349 int32 350 ModuleList::CountModules() const 351 { 352 return fModules.CountItems(); 353 } 354 355 // ModuleAt 356 Module * 357 ModuleList::ModuleAt(int32 index) const 358 { 359 return (Module*)fModules.ItemAt(index); 360 } 361 362 // AddModule 363 bool 364 ModuleList::AddModule(Module *module) 365 { 366 bool result = false; 367 if (module && !FindModule(module->Info()->name)) 368 result = fModules.AddItem(module); 369 return result; 370 } 371 372 // RemoveModule 373 bool 374 ModuleList::RemoveModule(Module *module) 375 { 376 return (module && fModules.RemoveItem(module)); 377 } 378 379 // FindModule 380 Module * 381 ModuleList::FindModule(const char *path) 382 { 383 if (path) { 384 for (int32 i = 0; Module *module = ModuleAt(i); i++) { 385 if (!strcmp(path, module->Info()->name)) 386 return module; 387 } 388 } 389 return NULL; 390 } 391 392 393 // #pragma mark - ModuleManager 394 395 396 ModuleManager::ModuleManager() 397 : fModules() 398 { 399 } 400 401 // destructor 402 ModuleManager::~ModuleManager() 403 { 404 for (int32 i = 0; Module *module = fModules.ModuleAt(i); i++) 405 delete module; 406 } 407 408 409 status_t 410 ModuleManager::GetModule(const char *path, module_info **_info) 411 { 412 if (path == NULL || _info == NULL) 413 return B_BAD_VALUE; 414 415 BAutolock _lock(fModules); 416 status_t error = B_OK; 417 418 Module *module = fModules.FindModule(path); 419 if (module == NULL) { 420 // module not yet loaded, try to get it 421 // get the responsible add-on 422 ModuleAddOn *addon = NULL; 423 error = _GetAddOn(path, &addon); 424 if (error == B_OK) { 425 // add-on found, get the module 426 if (module_info *info = addon->FindModuleInfo(path)) { 427 module = new Module(addon, info); 428 fModules.AddModule(module); 429 } else { 430 _PutAddOn(addon); 431 error = B_ENTRY_NOT_FOUND; 432 } 433 } 434 } 435 436 // "get" the module 437 if (error == B_OK) 438 error = module->Get(); 439 if (error == B_OK) 440 *_info = module->Info(); 441 442 return error; 443 } 444 445 // PutModule 446 status_t 447 ModuleManager::PutModule(const char *path) 448 { 449 if (path == NULL) 450 return B_BAD_VALUE; 451 452 BAutolock _lock(fModules); 453 454 if (Module *module = fModules.FindModule(path)) { 455 if (module->Put()) { 456 ModuleAddOn *addon = module->AddOn(); 457 fModules.RemoveModule(module); 458 delete module; 459 _PutAddOn(addon); 460 } 461 } else 462 return B_BAD_VALUE; 463 464 return B_OK; 465 } 466 467 // GetNextLoadedModuleName 468 status_t 469 ModuleManager::GetNextLoadedModuleName(uint32 *cookie, char *buffer, 470 size_t *bufferSize) 471 { 472 status_t error = (cookie && buffer && bufferSize ? B_OK : B_BAD_VALUE); 473 if (error == B_OK) { 474 BAutolock _lock(fModules); 475 if (Module *module = fModules.ModuleAt(*cookie)) { 476 module_info *info = module->Info(); 477 size_t nameLen = strlen(info->name); 478 if (nameLen < *bufferSize) { 479 strcpy(buffer, info->name); 480 *bufferSize = nameLen; 481 (*cookie)++; 482 } else 483 error = B_BAD_VALUE; 484 } else 485 error = B_ENTRY_NOT_FOUND; 486 } 487 return error; 488 } 489 490 // OpenModuleList 491 module_name_list * 492 ModuleManager::OpenModuleList(const char *prefix, const char *suffix) 493 { 494 module_name_list *list = NULL; 495 if (prefix) { 496 list = new module_name_list; 497 _FindBuiltInModules(prefix, suffix, list); 498 499 for (int32 i = 0; gModuleDirs[i]; i++) { 500 BPath path; 501 BDirectory dir; 502 if (path.SetTo(gModuleDirs[i], prefix) == B_OK 503 && dir.SetTo(path.Path()) == B_OK) { 504 _FindModules(dir, gModuleDirs[i], suffix, list); 505 } 506 } 507 508 list->it = list->names.begin(); 509 } 510 return list; 511 } 512 513 // ReadNextModuleName 514 status_t 515 ModuleManager::ReadNextModuleName(module_name_list *list, char *buffer, 516 size_t *bufferSize) 517 { 518 status_t error = (list && buffer && bufferSize ? B_OK : B_BAD_VALUE); 519 if (error == B_OK) { 520 if (list->it != list->names.end()) { 521 const string &name = *list->it; 522 size_t nameLen = name.length(); 523 if (nameLen < *bufferSize) { 524 strcpy(buffer, name.c_str()); 525 *bufferSize = nameLen; 526 list->it++; 527 } else 528 error = B_BAD_VALUE; 529 } else 530 error = B_ENTRY_NOT_FOUND; 531 } 532 return error; 533 } 534 535 536 // CloseModuleList 537 status_t 538 ModuleManager::CloseModuleList(module_name_list *list) 539 { 540 status_t error = (list ? B_OK : B_BAD_VALUE); 541 if (error == B_OK) 542 delete list; 543 return error; 544 } 545 546 547 status_t 548 ModuleManager::AddBuiltInModule(module_info *info) 549 { 550 BAutolock _lock(fModules); 551 552 TRACE(("add module %p, \"%s\"\n", info, info->name)); 553 return fModules.AddModule(new Module(NULL, info)) ? B_OK : B_ERROR; 554 } 555 556 557 status_t 558 ModuleManager::GetDependencies(image_id image) 559 { 560 module_dependency *dependencies; 561 status_t status = get_image_symbol(image, "module_dependencies", 562 B_SYMBOL_TYPE_DATA, (void**)&dependencies); 563 if (status < B_OK) { 564 // no dependencies means we don't have to do anything 565 return B_OK; 566 } 567 568 for (uint32 i = 0; dependencies[i].name != NULL; i++) { 569 status = GetModule(dependencies[i].name, dependencies[i].info); 570 if (status < B_OK) 571 return status; 572 } 573 return B_OK; 574 } 575 576 577 void 578 ModuleManager::PutDependencies(image_id image) 579 { 580 module_dependency *dependencies; 581 status_t status = get_image_symbol(image, "module_dependencies", 582 B_SYMBOL_TYPE_DATA, (void**)&dependencies); 583 if (status < B_OK) { 584 // no dependencies means we don't have to do anything 585 return; 586 } 587 588 for (uint32 i = 0; dependencies[i].name != NULL; i++) { 589 PutModule(dependencies[i].name); 590 } 591 } 592 593 594 bool 595 ModuleManager::_MatchSuffix(const char *name, const char *suffix) 596 { 597 if (suffix == NULL || suffix[0] == '\0') 598 return true; 599 600 size_t suffixLength = strlen(suffix); 601 size_t length = strlen(name); 602 if (length <= suffixLength) 603 return false; 604 605 return name[length - suffixLength - 1] == '/' 606 && !strcmp(name + length - suffixLength, suffix); 607 } 608 609 610 void 611 ModuleManager::_FindModules(BDirectory &dir, const char *moduleDir, 612 const char *suffix, module_name_list *list) 613 { 614 BEntry entry; 615 while (dir.GetNextEntry(&entry) == B_OK) { 616 if (entry.IsFile()) { 617 ModuleAddOn addon; 618 BPath path; 619 if (entry.GetPath(&path) == B_OK 620 && addon.Load(path.Path(), moduleDir) == B_OK) { 621 module_info **infos = addon.ModuleInfos(); 622 for (int32 i = 0; infos[i]; i++) { 623 if (infos[i]->name 624 && _MatchSuffix(infos[i]->name, suffix)) 625 list->names.insert(infos[i]->name); 626 } 627 } 628 } else if (entry.IsDirectory()) { 629 BDirectory subdir; 630 if (subdir.SetTo(&entry) == B_OK) 631 _FindModules(subdir, moduleDir, suffix, list); 632 } 633 } 634 } 635 636 637 void 638 ModuleManager::_FindBuiltInModules(const char *prefix, const char *suffix, 639 module_name_list *list) 640 { 641 uint32 count = fModules.CountModules(); 642 uint32 prefixLength = strlen(prefix); 643 644 for (uint32 i = 0; i < count; i++) { 645 Module *module = fModules.ModuleAt(i); 646 if (!strncmp(module->Info()->name, prefix, prefixLength) 647 && _MatchSuffix(module->Info()->name, suffix)) 648 list->names.insert(module->Info()->name); 649 } 650 } 651 652 653 status_t 654 ModuleManager::_GetAddOn(const char *name, ModuleAddOn **_addon) 655 { 656 // search list first 657 for (int32 i = 0; ModuleAddOn *addon = fAddOns.ItemAt(i); i++) { 658 BString addonName(addon->Name()); 659 addonName << "/"; 660 if (!strcmp(name, addon->Name()) 661 || !strncmp(addonName.String(), name, addonName.Length())) { 662 addon->Get(); 663 *_addon = addon; 664 return B_OK; 665 } 666 } 667 // not in list yet, load from disk 668 // iterate through module dirs 669 for (int32 i = 0; gModuleDirs[i]; i++) { 670 BPath path; 671 if (path.SetTo(gModuleDirs[i]) == B_OK 672 && path.SetTo(path.Path(), name) == B_OK) { 673 BEntry entry; 674 for (;;) { 675 if (entry.SetTo(path.Path()) == B_OK && entry.Exists()) { 676 // found an entry: if it is a file, try to load it 677 if (entry.IsFile()) { 678 ModuleAddOn *addon = new ModuleAddOn; 679 if (addon->Load(path.Path(), gModuleDirs[i]) == B_OK) { 680 status_t status = addon->Get(); 681 if (status < B_OK) { 682 delete addon; 683 return status; 684 } 685 686 fAddOns.AddItem(addon); 687 *_addon = addon; 688 return B_OK; 689 } 690 delete addon; 691 } 692 break; 693 } 694 // chop off last path component 695 if (path.GetParent(&path) != B_OK) 696 break; 697 } 698 } 699 } 700 return B_ENTRY_NOT_FOUND; 701 } 702 703 // _PutAddOn 704 void 705 ModuleManager::_PutAddOn(ModuleAddOn *addon) 706 { 707 if (addon) { 708 if (addon->Put()) { 709 fAddOns.RemoveItem(addon); 710 delete addon; 711 } 712 } 713 } 714 715 716 // singleton instance 717 ModuleManager ModuleManager::sDefaultManager; 718 719 720 // #pragma mark - Private emulation functions 721 722 723 extern "C" status_t 724 _add_builtin_module(module_info *info) 725 { 726 return ModuleManager::Default()->AddBuiltInModule(info); 727 } 728 729 730 extern "C" status_t 731 _get_builtin_dependencies(void) 732 { 733 image_info info; 734 int32 cookie = 0; 735 while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) { 736 if (info.type != B_APP_IMAGE) 737 continue; 738 739 return ModuleManager::Default()->GetDependencies(info.id); 740 } 741 742 return B_OK; 743 } 744 745 746 // #pragma mark - Emulated kernel functions 747 748 749 status_t 750 get_module(const char *path, module_info **_info) 751 { 752 TRACE(("get_module(`%s')\n", path)); 753 return ModuleManager::Default()->GetModule(path, _info); 754 } 755 756 757 status_t 758 put_module(const char *path) 759 { 760 TRACE(("put_module(`%s')\n", path)); 761 return ModuleManager::Default()->PutModule(path); 762 } 763 764 765 status_t 766 get_next_loaded_module_name(uint32 *cookie, char *name, size_t *nameLength) 767 { 768 TRACE(("get_next_loaded_module_name(%lu)\n", *cookie)); 769 return ModuleManager::Default()->GetNextLoadedModuleName(cookie, name, 770 nameLength); 771 } 772 773 774 void * 775 open_module_list_etc(const char *prefix, const char *suffix) 776 { 777 TRACE(("open_module_list_etc('%s', '%s')\n", prefix, suffix)); 778 return (void*)ModuleManager::Default()->OpenModuleList(prefix, suffix); 779 } 780 781 782 void * 783 open_module_list(const char *prefix) 784 { 785 TRACE(("open_module_list('%s')\n", prefix)); 786 return (void*)ModuleManager::Default()->OpenModuleList(prefix); 787 } 788 789 790 status_t 791 read_next_module_name(void *cookie, char *buf, size_t *bufsize) 792 { 793 TRACE(("read_next_module_name(%p, %p, %lu)\n", cookie, buf, *bufsize)); 794 return ModuleManager::Default()->ReadNextModuleName( 795 (module_name_list*)cookie, buf, bufsize); 796 } 797 798 799 status_t 800 close_module_list(void *cookie) 801 { 802 TRACE(("close_module_list(%p)\n", cookie)); 803 return ModuleManager::Default()->CloseModuleList( 804 (module_name_list*)cookie); 805 } 806 807 808 status_t 809 start_watching_modules(const char* prefix, NotificationListener& listener) 810 { 811 return B_NOT_SUPPORTED; 812 } 813 814 815 status_t 816 stop_watching_modules(const char* prefix, NotificationListener& listener) 817 { 818 return B_NOT_SUPPORTED; 819 } 820