xref: /haiku/src/tests/add-ons/kernel/network/userland_modules.cpp (revision 5c9e6a3953923a7c60c3d9c69c4f935492f26997)
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 **) &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