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