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
get_module(const char * name,module_info ** mi)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
put_module(const char * name)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
get_next_loaded_module_name(uint32 * cookie,char * buf,size_t * bufsize)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
open_module_list(const char * prefix)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
read_next_module_name(void * cookie,char * buf,size_t * bufsize)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
close_module_list(void * cookie)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
dprintf(const char * fmt,...)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
kprintf(const char * fmt,...)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
load_driver_symbols(const char * driver_name)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
spawn_kernel_thread(thread_entry func,const char * name,long priority,void * arg)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
send_signal_etc(pid_t thid,uint sig,uint32 flags)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
load_module_addon(const char * path)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
unload_module_addon(module_addon * ma)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
search_module(const char * name)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
init_module(module * m)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
uninit_module(module * m)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
find_loaded_module_by_name(const char * name)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
find_loaded_module_by_id(uint32 id)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