xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/module.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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