xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/module.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 <List.h>
20 #include <Locker.h>
21 #include <ObjectList.h>
22 #include <Path.h>
23 #include <String.h>
24 
25 #ifdef TRACE
26 #	undef TRACE
27 #endif
28 #define TRACE(x)
29 //#define TRACE(x) printf x
30 
31 
32 using namespace std;
33 
34 
35 static const char *gModuleDirs[] = {
36 	"generated/objects/haiku/x86/release/add-ons/userland",
37 	"generated/objects/haiku/x86/release/tests/add-ons/kernel",
38 	NULL
39 };
40 
41 
42 struct module_name_list {
43 	set<string>				names;
44 	set<string>::iterator	it;
45 };
46 
47 
48 // ModuleAddOn
49 
50 class ModuleAddOn {
51 public:
52 	ModuleAddOn();
53 	~ModuleAddOn();
54 
55 	status_t Load(const char *path, const char *dirPath);
56 	void Unload();
57 
58 	const char *Name()	{ return fName.String(); }
59 
60 	status_t Get();
61 	bool Put();
62 
63 	module_info **ModuleInfos() const { return fInfos; }
64 	module_info *FindModuleInfo(const char *name) const;
65 
66 private:
67 	image_id	fAddOn;
68 	module_info	**fInfos;
69 	int32		fReferenceCount;
70 	BString		fName;
71 };
72 
73 class Module {
74 public:
75 	Module(ModuleAddOn *addon, module_info *info);
76 	~Module();
77 
78 	status_t Init();
79 	status_t Uninit();
80 
81 	status_t Get();
82 	bool Put();
83 
84 	ModuleAddOn *AddOn() const	{ return fAddOn; }
85 	module_info *Info() const	{ return fInfo; }
86 
87 private:
88 	ModuleAddOn	*fAddOn;
89 	module_info	*fInfo;
90 	int32		fReferenceCount;
91 	bool		fInitialized;
92 };
93 
94 class ModuleList : public BLocker {
95 public:
96 	ModuleList();
97 	~ModuleList();
98 
99 	int32 CountModules() const;
100 	Module *ModuleAt(int32 index) const;
101 
102 	bool AddModule(Module *module);
103 	bool RemoveModule(Module *module);
104 	Module *FindModule(const char *path);
105 
106 private:
107 	BList	fModules;
108 };
109 
110 class ModuleManager {
111 public:
112 	ModuleManager();
113 	~ModuleManager();
114 
115 	static ModuleManager *Default() { return &sDefaultManager; }
116 
117 	status_t GetModule(const char *path, module_info **infop);
118 	status_t PutModule(const char *path);
119 
120 	status_t GetNextLoadedModuleName(uint32 *cookie, char *buffer,
121 		size_t *bufferSize);
122 
123 	module_name_list *OpenModuleList(const char *prefix,
124 		const char *suffix = NULL);
125 	status_t ReadNextModuleName(module_name_list *list, char *buffer,
126 		size_t *bufferSize);
127 	status_t CloseModuleList(module_name_list *list);
128 
129 	status_t AddBuiltInModule(module_info *info);
130 
131 	status_t GetDependencies(image_id image);
132 	void PutDependencies(image_id image);
133 
134 private:
135 	bool _MatchSuffix(const char *name, const char *suffix);
136 	void _FindModules(BDirectory &dir, const char *moduleDir,
137 		const char *suffix, module_name_list *list);
138 	void _FindBuiltInModules(const char *prefix, const char *suffix,
139 		module_name_list *list);
140 
141 	status_t _GetAddOn(const char *path, ModuleAddOn **addon);
142 	void _PutAddOn(ModuleAddOn *addon);
143 
144 private:
145 	static ModuleManager		sDefaultManager;
146 	ModuleList					fModules;
147 	BObjectList<ModuleAddOn>	fAddOns;
148 };
149 
150 
151 //	#pragma mark - ModuleAddOn
152 
153 
154 ModuleAddOn::ModuleAddOn()
155 	: fAddOn(-1),
156 	  fInfos(NULL),
157 	  fReferenceCount(0)
158 {
159 }
160 
161 
162 ModuleAddOn::~ModuleAddOn()
163 {
164 	Unload();
165 }
166 
167 // Load
168 status_t
169 ModuleAddOn::Load(const char *path, const char *dirPath)
170 {
171 	TRACE(("ModuleAddOn::Load(): searching module `%s'...\n", path));
172 	Unload();
173 	status_t error = (path && dirPath ? B_OK : B_BAD_VALUE);
174 	if (error == B_OK) {
175 		// get the module dir relative path
176 		BPath absPath;
177 		BPath absDirPath;
178 		if (absPath.SetTo(path, NULL, true) != B_OK
179 			|| absDirPath.SetTo(dirPath, NULL, true) != B_OK
180 			|| strlen(absPath.Path()) <= strlen(absDirPath.Path())) {
181 			return B_ENTRY_NOT_FOUND;
182 		}
183 		int32 dirPathLen = strlen(absDirPath.Path());
184 		if (strncmp(absPath.Path(), absDirPath.Path(), dirPathLen)
185 			|| absPath.Path()[dirPathLen] != '/') {
186 			return B_ENTRY_NOT_FOUND;
187 		}
188 		const char *name = absPath.Path() + dirPathLen + 1;
189 		// load the file
190 		error = B_ENTRY_NOT_FOUND;
191 		BEntry entry;
192 		if (entry.SetTo(path) == B_OK && entry.Exists()) {
193 			image_id image = load_add_on(path);
194 			module_info **infos = NULL;
195 			if (image >= 0
196 				&& get_image_symbol(image, "modules", B_SYMBOL_TYPE_DATA,
197 									(void**)&infos) == B_OK
198 				&& infos != NULL) {
199 				fAddOn = image;
200 				fInfos = infos;
201 				fName = name;
202 				fReferenceCount = 0;
203 				error = B_OK;
204 			}
205 		}
206 	}
207 	return error;
208 }
209 
210 // Unload
211 void
212 ModuleAddOn::Unload()
213 {
214 	if (fAddOn >= 0)
215 		unload_add_on(fAddOn);
216 	fAddOn = -1;
217 	fInfos = NULL;
218 	fReferenceCount = 0;
219 }
220 
221 // Get
222 status_t
223 ModuleAddOn::Get()
224 {
225 	if (fAddOn >= 0) {
226 		if (fReferenceCount == 0) {
227 			status_t status = ModuleManager::Default()->GetDependencies(fAddOn);
228 			if (status < B_OK)
229 				return status;
230 		}
231 		fReferenceCount++;
232 	}
233 
234 	return B_OK;
235 }
236 
237 // Put
238 bool
239 ModuleAddOn::Put()
240 {
241 	if (fAddOn >= 0)
242 		fReferenceCount--;
243 
244 	if (fReferenceCount == 0) {
245 		ModuleManager::Default()->PutDependencies(fAddOn);
246 		return true;
247 	}
248 	return false;
249 }
250 
251 // FindModuleInfo
252 module_info *
253 ModuleAddOn::FindModuleInfo(const char *name) const
254 {
255 	if (fInfos && name) {
256 		for (int32 i = 0; module_info *info = fInfos[i]; i++) {
257 			if (!strcmp(info->name, name))
258 				return info;
259 		}
260 	}
261 	return NULL;
262 }
263 
264 
265 //	#pragma mark - Module
266 
267 
268 Module::Module(ModuleAddOn *addon, module_info *info)
269 	: fAddOn(addon),
270 	  fInfo(info),
271 	  fReferenceCount(0),
272 	  fInitialized(false)
273 {
274 }
275 
276 // destructor
277 Module::~Module()
278 {
279 }
280 
281 // Init
282 status_t
283 Module::Init()
284 {
285 	status_t error = (fInfo ? B_OK : B_NO_INIT);
286 	if (error == B_OK && !fInitialized) {
287 		if (fInfo->std_ops != NULL)
288 			error = fInfo->std_ops(B_MODULE_INIT);
289 		if (error == B_OK)
290 			fInitialized = true;
291 	}
292 	return error;
293 }
294 
295 // Uninit
296 status_t
297 Module::Uninit()
298 {
299 	status_t error = (fInfo ? B_OK : B_NO_INIT);
300 	if (error == B_OK && fInitialized) {
301 		if (fInfo->std_ops != NULL)
302 			error = fInfo->std_ops(B_MODULE_UNINIT);
303 		fInitialized = false;
304 	}
305 	return error;
306 }
307 
308 
309 status_t
310 Module::Get()
311 {
312 	if (fReferenceCount == 0) {
313 		status_t status = Init();
314 		if (status < B_OK)
315 			return status;
316 	}
317 
318 	fReferenceCount++;
319 	return B_OK;
320 }
321 
322 
323 bool
324 Module::Put()
325 {
326 	if (--fReferenceCount > 0)
327 		return false;
328 
329 	Uninit();
330 	return fAddOn && !(fInfo->flags & B_KEEP_LOADED);
331 }
332 
333 
334 //	#pragma mark - ModuleList
335 
336 
337 ModuleList::ModuleList()
338 {
339 }
340 
341 
342 ModuleList::~ModuleList()
343 {
344 }
345 
346 // CountModules
347 int32
348 ModuleList::CountModules() const
349 {
350 	return fModules.CountItems();
351 }
352 
353 // ModuleAt
354 Module *
355 ModuleList::ModuleAt(int32 index) const
356 {
357 	return (Module*)fModules.ItemAt(index);
358 }
359 
360 // AddModule
361 bool
362 ModuleList::AddModule(Module *module)
363 {
364 	bool result = false;
365 	if (module && !FindModule(module->Info()->name))
366 		result = fModules.AddItem(module);
367 	return result;
368 }
369 
370 // RemoveModule
371 bool
372 ModuleList::RemoveModule(Module *module)
373 {
374 	return (module && fModules.RemoveItem(module));
375 }
376 
377 // FindModule
378 Module *
379 ModuleList::FindModule(const char *path)
380 {
381 	if (path) {
382 		for (int32 i = 0; Module *module = ModuleAt(i); i++) {
383 			if (!strcmp(path, module->Info()->name))
384 				return module;
385 		}
386 	}
387 	return NULL;
388 }
389 
390 
391 //	#pragma mark - ModuleManager
392 
393 
394 ModuleManager::ModuleManager()
395 	: fModules()
396 {
397 }
398 
399 // destructor
400 ModuleManager::~ModuleManager()
401 {
402 	for (int32 i = 0; Module *module = fModules.ModuleAt(i); i++)
403 		delete module;
404 }
405 
406 
407 status_t
408 ModuleManager::GetModule(const char *path, module_info **_info)
409 {
410 	if (path == NULL || _info == NULL)
411 		return B_BAD_VALUE;
412 
413 	BAutolock _lock(fModules);
414 	status_t error = B_OK;
415 
416 	Module *module = fModules.FindModule(path);
417 	if (module == NULL) {
418 		// module not yet loaded, try to get it
419 		// get the responsible add-on
420 		ModuleAddOn *addon = NULL;
421 		error = _GetAddOn(path, &addon);
422 		if (error == B_OK) {
423 			// add-on found, get the module
424 			if (module_info *info = addon->FindModuleInfo(path)) {
425 				module = new Module(addon, info);
426 				fModules.AddModule(module);
427 			} else {
428 				_PutAddOn(addon);
429 				error = B_ENTRY_NOT_FOUND;
430 			}
431 		}
432 	}
433 
434 	// "get" the module
435 	if (error == B_OK)
436 		error = module->Get();
437 	if (error == B_OK)
438 		*_info = module->Info();
439 
440 	return error;
441 }
442 
443 // PutModule
444 status_t
445 ModuleManager::PutModule(const char *path)
446 {
447 	if (path == NULL)
448 		return B_BAD_VALUE;
449 
450 	BAutolock _lock(fModules);
451 
452 	if (Module *module = fModules.FindModule(path)) {
453 		if (module->Put()) {
454 			ModuleAddOn *addon = module->AddOn();
455 			fModules.RemoveModule(module);
456 			delete module;
457 			_PutAddOn(addon);
458 		}
459 	} else
460 		return B_BAD_VALUE;
461 
462 	return B_OK;
463 }
464 
465 // GetNextLoadedModuleName
466 status_t
467 ModuleManager::GetNextLoadedModuleName(uint32 *cookie, char *buffer,
468 	size_t *bufferSize)
469 {
470 	status_t error = (cookie && buffer && bufferSize ? B_OK : B_BAD_VALUE);
471 	if (error == B_OK) {
472 		BAutolock _lock(fModules);
473 		if (Module *module = fModules.ModuleAt(*cookie)) {
474 			module_info *info = module->Info();
475 			size_t nameLen = strlen(info->name);
476 			if (nameLen < *bufferSize) {
477 				strcpy(buffer, info->name);
478 				*bufferSize = nameLen;
479 				(*cookie)++;
480 			} else
481 				error = B_BAD_VALUE;
482 		} else
483 			error = B_ENTRY_NOT_FOUND;
484 	}
485 	return error;
486 }
487 
488 // OpenModuleList
489 module_name_list *
490 ModuleManager::OpenModuleList(const char *prefix, const char *suffix)
491 {
492 	module_name_list *list = NULL;
493 	if (prefix) {
494 		list = new module_name_list;
495 		_FindBuiltInModules(prefix, suffix, list);
496 
497 		for (int32 i = 0; gModuleDirs[i]; i++) {
498 			BPath path;
499 			BDirectory dir;
500 			if (path.SetTo(gModuleDirs[i], prefix) == B_OK
501 				&& dir.SetTo(path.Path()) == B_OK) {
502 				_FindModules(dir, gModuleDirs[i], suffix, list);
503 			}
504 		}
505 
506 		list->it = list->names.begin();
507 	}
508 	return list;
509 }
510 
511 // ReadNextModuleName
512 status_t
513 ModuleManager::ReadNextModuleName(module_name_list *list, char *buffer,
514 	size_t *bufferSize)
515 {
516 	status_t error = (list && buffer && bufferSize ? B_OK : B_BAD_VALUE);
517 	if (error == B_OK) {
518 		if (list->it != list->names.end()) {
519 			const string &name = *list->it;
520 			size_t nameLen = name.length();
521 			if (nameLen < *bufferSize) {
522 				strcpy(buffer, name.c_str());
523 				*bufferSize = nameLen;
524 				list->it++;
525 			} else
526 				error = B_BAD_VALUE;
527 		} else
528 			error = B_ENTRY_NOT_FOUND;
529 	}
530 	return error;
531 }
532 
533 
534 // CloseModuleList
535 status_t
536 ModuleManager::CloseModuleList(module_name_list *list)
537 {
538 	status_t error = (list ? B_OK : B_BAD_VALUE);
539 	if (error == B_OK)
540 		delete list;
541 	return error;
542 }
543 
544 
545 status_t
546 ModuleManager::AddBuiltInModule(module_info *info)
547 {
548 	BAutolock _lock(fModules);
549 
550 	TRACE(("add module %p, \"%s\"\n", info, info->name));
551 	return fModules.AddModule(new Module(NULL, info)) ? B_OK : B_ERROR;
552 }
553 
554 
555 status_t
556 ModuleManager::GetDependencies(image_id image)
557 {
558 	module_dependency *dependencies;
559 	status_t status = get_image_symbol(image, "module_dependencies",
560 		B_SYMBOL_TYPE_DATA, (void**)&dependencies);
561 	if (status < B_OK) {
562 		// no dependencies means we don't have to do anything
563 		return B_OK;
564 	}
565 
566 	for (uint32 i = 0; dependencies[i].name != NULL; i++) {
567 		status = GetModule(dependencies[i].name, dependencies[i].info);
568 		if (status < B_OK)
569 			return status;
570 	}
571 	return B_OK;
572 }
573 
574 
575 void
576 ModuleManager::PutDependencies(image_id image)
577 {
578 	module_dependency *dependencies;
579 	status_t status = get_image_symbol(image, "module_dependencies",
580 		B_SYMBOL_TYPE_DATA, (void**)&dependencies);
581 	if (status < B_OK) {
582 		// no dependencies means we don't have to do anything
583 		return;
584 	}
585 
586 	for (uint32 i = 0; dependencies[i].name != NULL; i++) {
587 		PutModule(dependencies[i].name);
588 	}
589 }
590 
591 
592 bool
593 ModuleManager::_MatchSuffix(const char *name, const char *suffix)
594 {
595 	if (suffix == NULL || suffix[0] == '\0')
596 		return true;
597 
598 	size_t suffixLength = strlen(suffix);
599 	size_t length = strlen(name);
600 	if (length <= suffixLength)
601 		return false;
602 
603 	return name[length - suffixLength - 1] == '/'
604 		&& !strcmp(name + length - suffixLength, suffix);
605 }
606 
607 
608 void
609 ModuleManager::_FindModules(BDirectory &dir, const char *moduleDir,
610 	const char *suffix, module_name_list *list)
611 {
612 	BEntry entry;
613 	while (dir.GetNextEntry(&entry) == B_OK) {
614 		if (entry.IsFile()) {
615 			ModuleAddOn addon;
616 			BPath path;
617 			if (entry.GetPath(&path) == B_OK
618 				&& addon.Load(path.Path(), moduleDir) == B_OK) {
619 				module_info **infos = addon.ModuleInfos();
620 				for (int32 i = 0; infos[i]; i++) {
621 					if (infos[i]->name
622 						&& _MatchSuffix(infos[i]->name, suffix))
623 						list->names.insert(infos[i]->name);
624 				}
625 			}
626 		} else if (entry.IsDirectory()) {
627 			BDirectory subdir;
628 			if (subdir.SetTo(&entry) == B_OK)
629 				_FindModules(subdir, moduleDir, suffix, list);
630 		}
631 	}
632 }
633 
634 
635 void
636 ModuleManager::_FindBuiltInModules(const char *prefix, const char *suffix,
637 	module_name_list *list)
638 {
639 	uint32 count = fModules.CountModules();
640 	uint32 prefixLength = strlen(prefix);
641 
642 	for (uint32 i = 0; i < count; i++) {
643 		Module *module = fModules.ModuleAt(i);
644 		if (!strncmp(module->Info()->name, prefix, prefixLength)
645 			&& _MatchSuffix(module->Info()->name, suffix))
646 			list->names.insert(module->Info()->name);
647 	}
648 }
649 
650 
651 status_t
652 ModuleManager::_GetAddOn(const char *name, ModuleAddOn **_addon)
653 {
654 	// search list first
655 	for (int32 i = 0; ModuleAddOn *addon = fAddOns.ItemAt(i); i++) {
656 		BString addonName(addon->Name());
657 		addonName << "/";
658 		if (!strcmp(name, addon->Name())
659 			|| !strncmp(addonName.String(), name, addonName.Length())) {
660 			addon->Get();
661 			*_addon = addon;
662 			return B_OK;
663 		}
664 	}
665 	// not in list yet, load from disk
666 	// iterate through module dirs
667 	for (int32 i = 0; gModuleDirs[i]; i++) {
668 		BPath path;
669 		if (path.SetTo(gModuleDirs[i]) == B_OK
670 			&& path.SetTo(path.Path(), name) == B_OK) {
671 			BEntry entry;
672 			for (;;) {
673 				if (entry.SetTo(path.Path()) == B_OK && entry.Exists()) {
674 					// found an entry: if it is a file, try to load it
675 					if (entry.IsFile()) {
676 						ModuleAddOn *addon = new ModuleAddOn;
677 						if (addon->Load(path.Path(), gModuleDirs[i]) == B_OK) {
678 							status_t status = addon->Get();
679 							if (status < B_OK) {
680 								delete addon;
681 								return status;
682 							}
683 
684 							fAddOns.AddItem(addon);
685 							*_addon = addon;
686 							return B_OK;
687 						}
688 						delete addon;
689 					}
690 					break;
691 				}
692 				// chop off last path component
693 				if (path.GetParent(&path) != B_OK)
694 					break;
695 			}
696 		}
697 	}
698 	return B_ENTRY_NOT_FOUND;
699 }
700 
701 // _PutAddOn
702 void
703 ModuleManager::_PutAddOn(ModuleAddOn *addon)
704 {
705 	if (addon) {
706 		if (addon->Put()) {
707 			fAddOns.RemoveItem(addon);
708 			delete addon;
709 		}
710 	}
711 }
712 
713 
714 // singleton instance
715 ModuleManager ModuleManager::sDefaultManager;
716 
717 
718 //	#pragma mark - Private emulation functions
719 
720 
721 extern "C" status_t
722 _add_builtin_module(module_info *info)
723 {
724 	return ModuleManager::Default()->AddBuiltInModule(info);
725 }
726 
727 
728 extern "C" status_t
729 _get_builtin_dependencies(void)
730 {
731 	image_info info;
732 	int32 cookie = 0;
733 	while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
734 		if (info.type != B_APP_IMAGE)
735 			continue;
736 
737 		return ModuleManager::Default()->GetDependencies(info.id);
738 	}
739 
740 	return B_OK;
741 }
742 
743 
744 //	#pragma mark - Emulated kernel functions
745 
746 
747 status_t
748 get_module(const char *path, module_info **_info)
749 {
750 	TRACE(("get_module(`%s')\n", path));
751 	return ModuleManager::Default()->GetModule(path, _info);
752 }
753 
754 
755 status_t
756 put_module(const char *path)
757 {
758 	TRACE(("put_module(`%s')\n", path));
759 	return ModuleManager::Default()->PutModule(path);
760 }
761 
762 
763 status_t
764 get_next_loaded_module_name(uint32 *cookie, char *name, size_t *nameLength)
765 {
766 	TRACE(("get_next_loaded_module_name(%lu)\n", *cookie));
767 	return ModuleManager::Default()->GetNextLoadedModuleName(cookie, name,
768 		nameLength);
769 }
770 
771 
772 void *
773 open_module_list_etc(const char *prefix, const char *suffix)
774 {
775 	TRACE(("open_module_list_etc('%s', '%s')\n", prefix, suffix));
776 	return (void*)ModuleManager::Default()->OpenModuleList(prefix, suffix);
777 }
778 
779 
780 void *
781 open_module_list(const char *prefix)
782 {
783 	TRACE(("open_module_list('%s')\n", prefix));
784 	return (void*)ModuleManager::Default()->OpenModuleList(prefix);
785 }
786 
787 
788 status_t
789 read_next_module_name(void *cookie, char *buf, size_t *bufsize)
790 {
791 	TRACE(("read_next_module_name(%p, %p, %lu)\n", cookie, buf, *bufsize));
792 	return ModuleManager::Default()->ReadNextModuleName(
793 		(module_name_list*)cookie, buf, bufsize);
794 }
795 
796 
797 status_t
798 close_module_list(void *cookie)
799 {
800 	TRACE(("close_module_list(%p)\n", cookie));
801 	return ModuleManager::Default()->CloseModuleList(
802 		(module_name_list*)cookie);
803 }
804