1 /* Userland modules emulation support 2 */ 3 4 #include <stdio.h> 5 #include <string.h> 6 #include <stdlib.h> 7 #include <stdarg.h> 8 #include <signal.h> 9 10 #include <drivers/KernelExport.h> 11 #include <drivers/module.h> 12 13 #include <app/Application.h> 14 #include <app/Roster.h> 15 #include <kernel/OS.h> 16 #include <kernel/image.h> 17 #include <storage/StorageDefs.h> 18 #include <storage/FindDirectory.h> 19 #include <storage/Path.h> 20 #include <storage/Directory.h> 21 22 #define ASSERT(condition) if (!(condition)) { debugger("Assertion failed!"); } 23 24 typedef enum { 25 MODULE_LOADED = 0, 26 MODULE_INITING, 27 MODULE_READY, 28 MODULE_UNINITING, 29 MODULE_ERROR 30 } module_state; 31 32 typedef struct module { 33 struct module * next; 34 uint32 id; 35 char * name; 36 module_info * info; 37 struct module_addon * addon; // the module addon this module live in 38 // if NULL, builtin module addon 39 int32 ref_count; // reference count of get_module() made on this module 40 bool keep_loaded; 41 module_state state; 42 } module; 43 44 typedef struct module_addon { 45 struct module_addon * next; 46 int32 ref_count; // reference count of get_module() made using this addon 47 bool keep_loaded; 48 char * path; 49 image_id addon_image; // if -1, not loaded in memory currently 50 module_info ** infos; // valid only when addon_image != -1 51 } module_addon; 52 53 typedef struct module_list_cookie { 54 char * prefix; 55 char * search_paths; 56 char * search_path; 57 char * next_path_token; 58 BList * dir_stack; 59 module_addon * ma; // current module addon looked up 60 module_info ** mi; // current module addon module info 61 } module_list_cookie; 62 63 #define LOCK_MODULES acquire_sem(g_modules_lock) 64 #define UNLOCK_MODULES release_sem(g_modules_lock) 65 66 // local prototypes 67 // ------------------ 68 69 static module * search_module(const char * name); 70 static status_t init_module(module * m); 71 static status_t uninit_module(module * m); 72 static module * find_loaded_module_by_name(const char * name); 73 static module * find_loaded_module_by_id(uint32 id); 74 75 static module_addon * load_module_addon(const char * path); 76 static status_t unload_module_addon(module_addon * ma); 77 78 // globals 79 // ------------------ 80 81 static sem_id g_modules_lock = -1; // One lock for rule them all, etc... 82 static module * g_modules = NULL; 83 static module_addon * g_module_addons = NULL; 84 static int32 g_next_module_id = 1; 85 86 87 // Public routines 88 // --------------- 89 90 extern "C" { 91 92 _EXPORT status_t get_module(const char * name, module_info ** mi) 93 { 94 status_t status; 95 module * m; 96 97 // printf("get_module(%s)\n", name); 98 99 m = find_loaded_module_by_name(name); 100 if (!m) 101 m = search_module(name); 102 103 if (!m) 104 return B_NAME_NOT_FOUND; 105 106 *mi = m->info; 107 108 status = B_OK; 109 110 if (m->addon) // built-in modules don't comes from addon... 111 atomic_add(&m->addon->ref_count, 1); 112 113 if (atomic_add(&m->ref_count, 1) == 0) { 114 // first time we reference this module, so let's init it: 115 status = init_module(m); 116 if (status != B_OK) { 117 printf("Failed to init module %s: %s.\n", m->name, strerror(status)); 118 unload_module_addon(m->addon); // unload the module addon... 119 }; 120 }; 121 122 return status; 123 } 124 125 _EXPORT status_t put_module(const char * name) 126 { 127 module * m; 128 129 // printf("put_module(%s)\n", name); 130 131 m = find_loaded_module_by_name(name); 132 if (!m) 133 // Hum??? Sorry, this module name was never get_module()'d 134 return B_NAME_NOT_FOUND; 135 136 if (atomic_add(&m->ref_count, -1) <= 1) 137 // this module is no more used... 138 uninit_module(m); 139 140 if (!m->addon) 141 // built-in modules are module addon less... 142 return B_OK; 143 144 if (atomic_add(&m->addon->ref_count, -1) > 1) 145 // Still other module(s) using this module addon 146 return B_OK; 147 148 // okay, this module addon is no more used 149 // let's free up some memory 150 return unload_module_addon(m->addon); 151 } 152 153 154 _EXPORT status_t get_next_loaded_module_name(uint32 *cookie, char *buf, size_t *bufsize) 155 { 156 module * m; 157 status_t status; 158 159 if (buf == NULL && bufsize == NULL) 160 return B_BAD_VALUE; 161 162 LOCK_MODULES; 163 164 if (*cookie == 0) 165 // first call expected value 166 m = g_modules; 167 else { 168 // find last loaded module returned, and seek to next one 169 m = (module *) find_loaded_module_by_id((int) *cookie); 170 if (m) 171 m = m->next; 172 }; 173 174 // find next loaded module 175 while (m) { 176 if (m->ref_count) 177 break; 178 m = m->next; 179 }; 180 181 status = B_OK; 182 if (m) { 183 ASSERT(m->info); 184 if (buf != NULL) 185 strncpy(buf, m->info->name, *bufsize); 186 else 187 *bufsize = strlen(m->info->name + 1); 188 *cookie = m->id; 189 } else 190 status = B_BAD_INDEX; 191 192 UNLOCK_MODULES; 193 194 return status; 195 } 196 197 198 _EXPORT void * open_module_list(const char *prefix) 199 { 200 module_list_cookie * mlc; 201 char * addon_path; 202 203 if (prefix == NULL) 204 return NULL; 205 206 mlc = (module_list_cookie *) malloc(sizeof(*mlc)); 207 mlc->prefix = strdup(prefix); 208 209 addon_path = getenv("ADDON_PATH"); 210 mlc->search_paths = (addon_path ? strdup(addon_path) : NULL); 211 mlc->search_path = strtok_r(mlc->search_paths, ":", &mlc->next_path_token); 212 mlc->dir_stack = new BList(); 213 214 mlc->ma = NULL; 215 mlc->mi = NULL; 216 217 return mlc; 218 } 219 220 221 _EXPORT status_t read_next_module_name(void *cookie, char *buf, size_t *bufsize) 222 { 223 module_list_cookie * mlc = (module_list_cookie *) cookie; 224 225 if (!bufsize) 226 return B_BAD_VALUE; 227 228 if (!mlc) 229 return B_BAD_VALUE; 230 231 /* Okay, take some time to understand how this function works! 232 Basicly, we iterate thru: 233 - each searchable add-ons path root 234 - each (sub-)directory under the current add-ons path root 235 - each module add-on file in the current (sub-)directory 236 - each module name published by current module add-on 237 238 As the iteration involve sub-directory walks, we use recursive calls. 239 Sorry if this code sounds too complex... 240 */ 241 242 if (mlc->ma && mlc->mi) { 243 // we have a module addon still loaded from a last call 244 // so keep looking at his exported module names list 245 while (*mlc->mi) { 246 module_info * mi = *mlc->mi; 247 mlc->mi++; 248 if(strstr(mi->name, mlc->prefix)) { 249 // We find a matching module name. At least. Yeah!!! 250 if (buf) strncpy(buf, mi->name, *bufsize); 251 *bufsize = strlen(mi->name); 252 return B_OK; 253 }; 254 }; 255 256 // We've iterate all module names of this module addon. Find another one... 257 atomic_add(&mlc->ma->ref_count, -1); 258 unload_module_addon(mlc->ma); 259 mlc->ma = NULL; 260 mlc->mi = NULL; 261 }; 262 263 // Iterate all searchable add-ons paths 264 while (mlc->search_path) { 265 BDirectory * dir; 266 BEntry entry; 267 BPath path; 268 status_t status; 269 270 // Get current directory 271 dir = (BDirectory *) mlc->dir_stack->LastItem(); 272 if (!dir) { 273 // find add-ons root directory in this search path 274 if (strncmp(mlc->search_path, "%A/", 3) == 0) { 275 // resolve "%A/..." path 276 app_info ai; 277 278 be_app->GetAppInfo(&ai); 279 entry.SetTo(&ai.ref); 280 entry.GetPath(&path); 281 path.GetParent(&path); 282 path.Append(mlc->search_path + 3); 283 } else { 284 path.SetTo(mlc->search_path); 285 }; 286 287 // We look *only* under prefix-matching sub-path 288 path.Append(mlc->prefix); 289 290 // printf("Looking module(s) in %s/%s...\n", mlc->search_path, mlc->prefix); 291 292 dir = new BDirectory(path.Path()); 293 if (dir) 294 mlc->dir_stack->AddItem(dir); 295 }; 296 297 // Iterate current directory content 298 if (dir) { 299 while (dir->GetNextEntry(&entry) == B_OK) { 300 entry.GetPath(&path); 301 // printf(" %s ?\n", path.Path()); 302 303 if (entry.IsDirectory()) { 304 BDirectory * subdir; 305 // push this directory on dir_stack 306 subdir = new BDirectory(path.Path()); 307 if (!subdir) 308 continue; 309 310 mlc->dir_stack->AddItem(subdir); 311 // recursivly search this sub-directory 312 return read_next_module_name(cookie, buf, bufsize); 313 }; 314 315 if (entry.IsFile() || entry.IsSymLink()) { 316 mlc->ma = load_module_addon(path.Path()); 317 if (!mlc->ma) 318 // Oh-oh, not a loadable module addon!? 319 // WTF it's doing there?!? 320 continue; 321 322 atomic_add(&mlc->ma->ref_count, 1); 323 // call ourself to enter the module names list iteration at 324 // function begining code... 325 mlc->mi = mlc->ma->infos; 326 return read_next_module_name(cookie, buf, bufsize); 327 }; 328 }; 329 330 // We walk thru all this directory content, go back to parent 331 status = mlc->dir_stack->RemoveItem(dir); 332 delete dir; 333 }; 334 335 if (!mlc->dir_stack->IsEmpty()) 336 continue; 337 338 // We walk thru all this search path content, next now 339 mlc->search_path = strtok_r(NULL, ":", &mlc->next_path_token); 340 }; 341 342 // Module(s) list search done, ending... 343 return B_ERROR; 344 } 345 346 347 _EXPORT status_t close_module_list(void *cookie) 348 { 349 module_list_cookie * mlc = (module_list_cookie *) cookie; 350 BDirectory * dir; 351 352 ASSERT(mlc); 353 ASSERT(mlc->prefix); 354 355 if (mlc->ma) { 356 atomic_add(&mlc->ma->ref_count, -1); 357 unload_module_addon(mlc->ma); 358 }; 359 360 while((dir = (BDirectory *) mlc->dir_stack->FirstItem())) { 361 mlc->dir_stack->RemoveItem(dir); 362 delete dir; 363 }; 364 365 delete mlc->dir_stack; 366 367 free(mlc->search_paths); 368 free(mlc->prefix); 369 free(mlc); 370 371 return B_ERROR; 372 } 373 374 // #pragma mark - 375 // Some KernelExport.h support from userland 376 377 _EXPORT void dprintf(const char *fmt, ...) 378 { 379 va_list args; 380 381 va_start(args, fmt); 382 vprintf(fmt, args); 383 va_end(args); 384 } 385 386 387 _EXPORT void kprintf(const char *fmt, ...) 388 { 389 va_list args; 390 391 va_start(args, fmt); 392 vprintf(fmt, args); 393 va_end(args); 394 } 395 396 397 _EXPORT status_t load_driver_symbols(const char *driver_name) 398 { 399 // Userland debugger will extract symbols itself... 400 return B_OK; 401 } 402 403 404 _EXPORT thread_id spawn_kernel_thread(thread_entry func, const char *name, long priority, void *arg) 405 { 406 return spawn_thread(func, name, priority, arg); 407 } 408 409 410 411 _EXPORT int send_signal_etc(pid_t thid, uint sig, uint32 flags) 412 { 413 return send_signal(thid, sig); 414 } 415 416 417 } // extern "C" 418 419 420 // #pragma mark - 421 // Private routines 422 423 static module_addon * load_module_addon(const char * path) 424 { 425 module_addon * ma; 426 image_id addon_id; 427 module_info ** mi; 428 status_t status; 429 430 ASSERT(path); 431 432 addon_id = load_add_on(path); 433 if (addon_id < 0) { 434 printf("Failed to load %s addon: %s.\n", path, strerror(addon_id)); 435 return NULL; 436 }; 437 438 // printf("Addon %s loaded.\n", path); 439 440 ma = NULL; 441 442 status = get_image_symbol(addon_id, "modules", B_SYMBOL_TYPE_DATA, (void **) &mi); 443 if (status != B_OK) { 444 // No "modules" symbol found in this addon 445 printf("Symbol \"modules\" not found in %s addon: not a module addon!\n", path); 446 goto error; 447 }; 448 449 ma = (module_addon *) malloc(sizeof(*ma)); 450 if (!ma) 451 // Gasp: not enough memory! 452 goto error; 453 454 LOCK_MODULES; 455 456 ma->ref_count = 0; 457 ma->keep_loaded = false; 458 ma->path = strdup(path); 459 ma->addon_image = addon_id; 460 ma->infos = mi; 461 462 while(*mi) { 463 module * m; 464 465 m = (module *) malloc(sizeof(*m)); 466 if (!m) 467 // Gasp, again: not enough memory! 468 goto error; 469 470 m->ref_count = 0; 471 m->id = atomic_add(&g_next_module_id, 1); 472 m->info = (*mi); 473 m->name = strdup(m->info->name); 474 m->addon = ma; 475 m->keep_loaded = (m->info->flags & B_KEEP_LOADED) ? true : false; 476 477 m->state = MODULE_LOADED; 478 479 m->next = g_modules; 480 g_modules = m; 481 482 mi++; 483 }; 484 485 // add this module addon to the list 486 ma->next = g_module_addons; 487 g_module_addons = ma; 488 489 UNLOCK_MODULES; 490 491 return ma; 492 493 error: 494 printf("Error while load_module_addon(%s)\n", path); 495 496 if (ma) { 497 // remove any appended modules by this module addon until we got error... 498 module * prev; 499 module * m; 500 501 prev = NULL; 502 m = g_modules; 503 while (m) { 504 if (m->addon == ma) { 505 module * tmp = m; 506 507 m = tmp->next; 508 509 if (prev) 510 prev->next = tmp->next; 511 else 512 g_modules = tmp->next; 513 514 if (tmp->name) 515 free(tmp->name); 516 free(tmp); 517 continue; 518 }; 519 520 prev = m; 521 m = m->next; 522 }; 523 524 525 UNLOCK_MODULES; 526 527 if (ma->path) 528 free(ma->path); 529 free(ma); 530 }; 531 532 unload_add_on(addon_id); 533 // printf("Addon %s unloaded.\n", path); 534 return NULL; 535 } 536 537 static status_t unload_module_addon(module_addon * ma) 538 { 539 module * m; 540 module * prev; 541 status_t status; 542 543 if (!ma) 544 // built-in modules are addon-less, so nothing to do... 545 return B_OK; 546 547 if (ma->keep_loaded) { 548 printf("B_KEEP_LOADED flag set for %s module addon. Will be *never* unloaded!\n", 549 ma->path); 550 return B_OK; 551 }; 552 553 if (ma->ref_count) 554 // still someone needing this module addon, it seems? 555 return B_OK; 556 557 if (ma->addon_image < 0) 558 // built-in addon, it seems... 559 return B_OK; 560 561 status = unload_add_on(ma->addon_image); 562 if (status != B_OK) { 563 printf("Failed to unload %s addon: %s.\n", ma->path, strerror(status)); 564 return status; 565 }; 566 // printf("Addon %s unloaded.\n", ma->path); 567 568 LOCK_MODULES; 569 570 // remove the modules coming from this module addon from g_modules list 571 prev = NULL; 572 m = g_modules; 573 while (m) { 574 if (m->addon == ma) { 575 module * tmp = m; 576 577 m = tmp->next; 578 579 if (prev) 580 prev->next = tmp->next; 581 else 582 g_modules = tmp->next; 583 584 if (tmp->name) 585 free(tmp->name); 586 free(tmp); 587 continue; 588 }; 589 590 prev = m; 591 m = m->next; 592 }; 593 594 // remove the module addon from g_module_addons list: 595 if (g_module_addons == ma) 596 g_module_addons = ma->next; 597 else { 598 module_addon * tmp; 599 tmp = g_module_addons; 600 while (tmp && tmp->next != ma) 601 tmp = tmp->next; 602 603 ASSERT(tmp); 604 tmp->next = ma->next; 605 }; 606 607 if (ma->path) 608 free(ma->path); 609 free(ma); 610 611 UNLOCK_MODULES; 612 613 return B_OK; 614 } 615 616 617 static module * search_module(const char * name) 618 { 619 BPath path; 620 BPath addons_path; 621 BEntry entry; 622 module * found_module; 623 char * search_paths; 624 char * search_path; 625 char * next_path_token; 626 627 // printf("search_module(%s):\n", name); 628 629 search_paths = getenv("ADDON_PATH"); 630 if (!search_paths) 631 // Nowhere to search addons!!! 632 return NULL; 633 634 search_paths = strdup(search_paths); 635 search_path = strtok_r(search_paths, ":", &next_path_token); 636 637 found_module = NULL; 638 while (search_path && found_module == NULL) { 639 if (strncmp(search_path, "%A/", 3) == 0) { 640 // compute "%A/..." path 641 app_info ai; 642 643 be_app->GetAppInfo(&ai); 644 entry.SetTo(&ai.ref); 645 entry.GetPath(&addons_path); 646 addons_path.GetParent(&addons_path); 647 addons_path.Append(search_path + 3); 648 } else { 649 addons_path.SetTo(search_path); 650 }; 651 652 // printf("Looking into %s\n", search_path); 653 654 path.SetTo(addons_path.Path()); 655 path.Append(name); 656 657 while(path != addons_path) { 658 // printf(" %s ?\n", path.Path()); 659 entry.SetTo(path.Path()); 660 if (entry.IsFile() || entry.IsSymLink()) { 661 module_addon * ma; 662 663 // try to load the module addon 664 ma = load_module_addon(path.Path()); 665 if (ma) { 666 found_module = find_loaded_module_by_name(name); 667 if (found_module) 668 break; 669 670 unload_module_addon(ma); 671 }; // if (ma) 672 }; // if (entry.IsFile() || entry.IsSymLink()) 673 674 // okay, remove the current path leaf and try again... 675 path.GetParent(&path); 676 }; 677 678 search_path = strtok_r(NULL, ":", &next_path_token); 679 }; 680 681 free(search_paths); 682 683 /* 684 if (found_module) 685 printf(" Found it in %s addon module!\n", 686 found_module->addon ? found_module->addon->path : "BUILTIN"); 687 */ 688 689 return found_module; 690 } 691 692 693 static status_t init_module(module * m) 694 { 695 status_t status; 696 697 ASSERT(m); 698 699 switch (m->state) { 700 case MODULE_LOADED: 701 m->state = MODULE_INITING; 702 ASSERT(m->info); 703 // printf("Initing module %s... ", m->name); 704 status = m->info->std_ops(B_MODULE_INIT); 705 // printf("done (%s).\n", strerror(status)); 706 m->state = (status == B_OK) ? MODULE_READY : MODULE_LOADED; 707 708 if (m->state == MODULE_READY && m->keep_loaded && m->addon) { 709 // one module (at least) was inited and request to never being 710 // unload from memory, so keep the corresponding addon loaded 711 // printf("module %s set B_KEEP_LOADED flag:\nmodule addon %s will never be unloaded!\n", 712 // m->name, m->addon->path); 713 m->addon->keep_loaded = true; 714 }; 715 break; 716 717 case MODULE_READY: 718 status = B_OK; 719 break; 720 721 case MODULE_INITING: // circular reference!!! 722 case MODULE_UNINITING: // initing a module currently unloading... 723 case MODULE_ERROR: // module failed to unload previously... 724 default: // Unknown module state!!! 725 status = B_ERROR; 726 break; 727 }; 728 729 return status; 730 } 731 732 733 static status_t uninit_module(module * m) 734 { 735 status_t status; 736 737 ASSERT(m); 738 739 switch (m->state) { 740 case MODULE_READY: 741 m->state = MODULE_UNINITING; 742 ASSERT(m->info); 743 // printf("Uniniting module %s... ", m->name); 744 status = m->info->std_ops(B_MODULE_UNINIT); 745 // printf("done (%s).\n", strerror(status)); 746 m->state = (status == B_OK) ? MODULE_LOADED : MODULE_ERROR; 747 break; 748 749 case MODULE_LOADED: 750 // No need to uninit it, all is fine so. 751 status = B_OK; 752 break; 753 754 case MODULE_INITING: // uniniting while initializing 755 case MODULE_UNINITING: // uniniting already pending 756 case MODULE_ERROR: // module failed previously... 757 default: // Unknown module state!!! 758 status = B_ERROR; 759 break; 760 }; 761 762 return status; 763 } 764 765 766 static module * find_loaded_module_by_name(const char * name) 767 { 768 module * m; 769 770 LOCK_MODULES; 771 772 m = g_modules; 773 while (m) { 774 if (strcmp(name, m->name) == 0) 775 break; 776 m = m->next; 777 }; 778 779 UNLOCK_MODULES; 780 return m; 781 } 782 783 784 static module * find_loaded_module_by_id(uint32 id) 785 { 786 module * m; 787 788 LOCK_MODULES; 789 790 m = g_modules; 791 while (m) { 792 if (m->id == id) 793 break; 794 m = m->next; 795 }; 796 797 UNLOCK_MODULES; 798 return m; 799 } 800 801 #if 0 802 // #pragma mark - 803 804 #define NET_CORE_MODULE_NAME "network/core/v1" 805 #define NET_ETHERNET_MODULE_NAME "network/interfaces/ethernet" 806 #define NET_IPV4_MODULE_NAME "network/protocols/ipv4/v1" 807 808 #define MODULE_LIST_PREFIX "network" 809 810 int main(int argc, char **argv) 811 { 812 module_info * core; 813 module_info * ethernet; 814 module_info * ipv4; 815 char module_name[256]; 816 uint32 cookie; 817 size_t sz; 818 void * ml_cookie; 819 820 new BApplication("application/x-vnd-OBOS-net_server"); 821 822 printf("open_module_list(%s):\n", MODULE_LIST_PREFIX); 823 ml_cookie = open_module_list(MODULE_LIST_PREFIX); 824 sz = sizeof(module_name); 825 while(read_next_module_name(ml_cookie, module_name, &sz) == B_OK) { 826 if (strlen(module_name)) 827 printf(" %s\n", module_name); 828 sz = sizeof(module_name); 829 }; 830 close_module_list(ml_cookie); 831 printf("close_module_list()\n"); 832 // return 0; 833 834 core = NULL; 835 get_module(NET_CORE_MODULE_NAME, (module_info **) &core); 836 837 ethernet = NULL; 838 get_module(NET_ETHERNET_MODULE_NAME, (module_info **) ðernet); 839 840 ipv4 = NULL; 841 get_module(NET_IPV4_MODULE_NAME, (module_info **) &ipv4); 842 843 printf("get_next_loaded_module_name() test:\n"); 844 cookie = 0; 845 sz = sizeof(module_name); 846 while (get_next_loaded_module_name(&cookie, module_name, &sz) == B_OK) 847 printf("%ld: %s\n", cookie, module_name); 848 849 if (ipv4) 850 put_module(NET_IPV4_MODULE_NAME); 851 852 if (ethernet) 853 put_module(NET_ETHERNET_MODULE_NAME); 854 855 if (core) 856 put_module(NET_CORE_MODULE_NAME); 857 858 printf("get_next_loaded_module_name() test:\n"); 859 cookie = 0; 860 sz = sizeof(module_name); 861 while (get_next_loaded_module_name(&cookie, module_name, &sz) == B_OK) 862 printf("%ld: %s\n", cookie, module_name); 863 864 delete be_app; 865 return 0; 866 } 867 #endif 868 869 870