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
32using namespace std;
33
34
35static 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
42struct module_name_list {
43	set<string>				names;
44	set<string>::iterator	it;
45};
46
47
48// ModuleAddOn
49
50class ModuleAddOn {
51public:
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
66private:
67	image_id	fAddOn;
68	module_info	**fInfos;
69	int32		fReferenceCount;
70	BString		fName;
71};
72
73class Module {
74public:
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
87private:
88	ModuleAddOn	*fAddOn;
89	module_info	*fInfo;
90	int32		fReferenceCount;
91	bool		fInitialized;
92};
93
94class ModuleList : public BLocker {
95public:
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
106private:
107	BList	fModules;
108};
109
110class ModuleManager {
111public:
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
134private:
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
144private:
145	static ModuleManager		sDefaultManager;
146	ModuleList					fModules;
147	BObjectList<ModuleAddOn>	fAddOns;
148};
149
150
151//	#pragma mark - ModuleAddOn
152
153
154ModuleAddOn::ModuleAddOn()
155	: fAddOn(-1),
156	  fInfos(NULL),
157	  fReferenceCount(0)
158{
159}
160
161
162ModuleAddOn::~ModuleAddOn()
163{
164	Unload();
165}
166
167// Load
168status_t
169ModuleAddOn::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
211void
212ModuleAddOn::Unload()
213{
214	if (fAddOn >= 0)
215		unload_add_on(fAddOn);
216	fAddOn = -1;
217	fInfos = NULL;
218	fReferenceCount = 0;
219}
220
221// Get
222status_t
223ModuleAddOn::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
238bool
239ModuleAddOn::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
252module_info *
253ModuleAddOn::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
268Module::Module(ModuleAddOn *addon, module_info *info)
269	: fAddOn(addon),
270	  fInfo(info),
271	  fReferenceCount(0),
272	  fInitialized(false)
273{
274}
275
276// destructor
277Module::~Module()
278{
279}
280
281// Init
282status_t
283Module::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
296status_t
297Module::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
309status_t
310Module::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
323bool
324Module::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
337ModuleList::ModuleList()
338{
339}
340
341
342ModuleList::~ModuleList()
343{
344}
345
346// CountModules
347int32
348ModuleList::CountModules() const
349{
350	return fModules.CountItems();
351}
352
353// ModuleAt
354Module *
355ModuleList::ModuleAt(int32 index) const
356{
357	return (Module*)fModules.ItemAt(index);
358}
359
360// AddModule
361bool
362ModuleList::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
371bool
372ModuleList::RemoveModule(Module *module)
373{
374	return (module && fModules.RemoveItem(module));
375}
376
377// FindModule
378Module *
379ModuleList::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
394ModuleManager::ModuleManager()
395	: fModules()
396{
397}
398
399// destructor
400ModuleManager::~ModuleManager()
401{
402	for (int32 i = 0; Module *module = fModules.ModuleAt(i); i++)
403		delete module;
404}
405
406
407status_t
408ModuleManager::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
444status_t
445ModuleManager::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
466status_t
467ModuleManager::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
489module_name_list *
490ModuleManager::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
512status_t
513ModuleManager::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
535status_t
536ModuleManager::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
545status_t
546ModuleManager::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
555status_t
556ModuleManager::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
575void
576ModuleManager::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
592bool
593ModuleManager::_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
608void
609ModuleManager::_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
635void
636ModuleManager::_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
651status_t
652ModuleManager::_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
702void
703ModuleManager::_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
715ModuleManager ModuleManager::sDefaultManager;
716
717
718//	#pragma mark - Private emulation functions
719
720
721extern "C" status_t
722_add_builtin_module(module_info *info)
723{
724	return ModuleManager::Default()->AddBuiltInModule(info);
725}
726
727
728extern "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
747status_t
748get_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
755status_t
756put_module(const char *path)
757{
758	TRACE(("put_module(`%s')\n", path));
759	return ModuleManager::Default()->PutModule(path);
760}
761
762
763status_t
764get_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
772void *
773open_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
780void *
781open_module_list(const char *prefix)
782{
783	TRACE(("open_module_list('%s')\n", prefix));
784	return (void*)ModuleManager::Default()->OpenModuleList(prefix);
785}
786
787
788status_t
789read_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
797status_t
798close_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