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