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
24typedef enum {
25  MODULE_LOADED = 0,
26  MODULE_INITING,
27  MODULE_READY,
28  MODULE_UNINITING,
29  MODULE_ERROR
30} module_state;
31
32typedef 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
44typedef 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
53typedef 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
69static module * search_module(const char * name);
70static status_t init_module(module * m);
71static status_t uninit_module(module * m);
72static module * find_loaded_module_by_name(const char * name);
73static module * find_loaded_module_by_id(uint32 id);
74
75static module_addon * load_module_addon(const char * path);
76static status_t unload_module_addon(module_addon * ma);
77
78// globals
79// ------------------
80
81static sem_id g_modules_lock = -1;	// One lock for rule them all, etc...
82static module * g_modules = NULL;
83static module_addon * g_module_addons = NULL;
84static int32 g_next_module_id = 1;
85
86
87// Public routines
88// ---------------
89
90extern "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
423static 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
493error:
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
537static 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
617static 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
693static 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
733static 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
766static 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
784static 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
810int 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 **) &ethernet);
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