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
32static 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
39struct module_name_list {
40	set<string>				names;
41	set<string>::iterator	it;
42};
43
44
45// ModuleAddOn
46
47class ModuleAddOn {
48public:
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
63private:
64	image_id	fAddOn;
65	module_info	**fInfos;
66	int32		fReferenceCount;
67	BString		fName;
68};
69
70class Module {
71public:
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
84private:
85	ModuleAddOn	*fAddOn;
86	module_info	*fInfo;
87	int32		fReferenceCount;
88	bool		fInitialized;
89};
90
91class ModuleList : public BLocker {
92public:
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
103private:
104	BList	fModules;
105};
106
107class ModuleManager {
108public:
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
131private:
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
141private:
142	static ModuleManager		sDefaultManager;
143	ModuleList					fModules;
144	BObjectList<ModuleAddOn>	fAddOns;
145};
146
147
148//	#pragma mark - ModuleAddOn
149
150
151ModuleAddOn::ModuleAddOn()
152	: fAddOn(-1),
153	  fInfos(NULL),
154	  fReferenceCount(0)
155{
156}
157
158
159ModuleAddOn::~ModuleAddOn()
160{
161	Unload();
162}
163
164// Load
165status_t
166ModuleAddOn::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
208void
209ModuleAddOn::Unload()
210{
211	if (fAddOn >= 0)
212		unload_add_on(fAddOn);
213	fAddOn = -1;
214	fInfos = NULL;
215	fReferenceCount = 0;
216}
217
218// Get
219status_t
220ModuleAddOn::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
235bool
236ModuleAddOn::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
249module_info *
250ModuleAddOn::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
265Module::Module(ModuleAddOn *addon, module_info *info)
266	: fAddOn(addon),
267	  fInfo(info),
268	  fReferenceCount(0),
269	  fInitialized(false)
270{
271}
272
273// destructor
274Module::~Module()
275{
276}
277
278// Init
279status_t
280Module::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
293status_t
294Module::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
306status_t
307Module::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
320bool
321Module::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
334ModuleList::ModuleList()
335{
336}
337
338
339ModuleList::~ModuleList()
340{
341}
342
343// CountModules
344int32
345ModuleList::CountModules() const
346{
347	return fModules.CountItems();
348}
349
350// ModuleAt
351Module *
352ModuleList::ModuleAt(int32 index) const
353{
354	return (Module*)fModules.ItemAt(index);
355}
356
357// AddModule
358bool
359ModuleList::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
368bool
369ModuleList::RemoveModule(Module *module)
370{
371	return (module && fModules.RemoveItem(module));
372}
373
374// FindModule
375Module *
376ModuleList::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
391ModuleManager::ModuleManager()
392	: fModules()
393{
394}
395
396// destructor
397ModuleManager::~ModuleManager()
398{
399	for (int32 i = 0; Module *module = fModules.ModuleAt(i); i++)
400		delete module;
401}
402
403
404status_t
405ModuleManager::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
441status_t
442ModuleManager::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
463status_t
464ModuleManager::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
486module_name_list *
487ModuleManager::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
509status_t
510ModuleManager::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
532status_t
533ModuleManager::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
542status_t
543ModuleManager::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
552status_t
553ModuleManager::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
572void
573ModuleManager::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
589bool
590ModuleManager::_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
605void
606ModuleManager::_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
632void
633ModuleManager::_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
648status_t
649ModuleManager::_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
699void
700ModuleManager::_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
712ModuleManager ModuleManager::sDefaultManager;
713
714
715//	#pragma mark - Private emulation functions
716
717
718extern "C" status_t
719_add_builtin_module(module_info *info)
720{
721	return ModuleManager::Default()->AddBuiltInModule(info);
722}
723
724
725extern "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
744status_t
745get_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
752status_t
753put_module(const char *path)
754{
755	TRACE(("put_module(`%s')\n", path));
756	return ModuleManager::Default()->PutModule(path);
757}
758
759
760status_t
761get_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
769void *
770open_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
777void *
778open_module_list(const char *prefix)
779{
780	TRACE(("open_module_list('%s')\n", prefix));
781	return (void*)ModuleManager::Default()->OpenModuleList(prefix);
782}
783
784
785status_t
786read_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
794status_t
795close_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