1/*
2 * Copyright 2010, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Clemens Zeidler <haiku@clemens-zeidler.de>
7 */
8
9#include "IndexServer.h"
10
11#include <Directory.h>
12#include <driver_settings.h>
13#include <FindDirectory.h>
14#include <Path.h>
15#include <String.h>
16
17#include <syscalls.h>
18
19
20VolumeObserverHandler::VolumeObserverHandler(IndexServer* indexServer)
21	:
22	fIndexServer(indexServer)
23{
24
25}
26
27
28void
29VolumeObserverHandler::MessageReceived(BMessage* message)
30{
31	if (message->what != B_NODE_MONITOR)
32		return;
33
34	dev_t device;
35	int32 opcode;
36	message->FindInt32("opcode", &opcode) ;
37	switch (opcode) {
38		case B_DEVICE_MOUNTED :
39			message->FindInt32("new device", &device);
40			fIndexServer->AddVolume(BVolume(device));
41			break ;
42
43		case B_DEVICE_UNMOUNTED :
44			message->FindInt32("device", &device);
45			fIndexServer->RemoveVolume(BVolume(device));
46			break ;
47	}
48}
49
50
51AnalyserMonitorHandler::AnalyserMonitorHandler(IndexServer* indexServer)
52	:
53	fIndexServer(indexServer)
54{
55
56}
57
58
59void
60AnalyserMonitorHandler::AddOnEnabled(const add_on_entry_info* entryInfo)
61{
62	entry_ref ref;
63	make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
64		entryInfo->name, &ref);
65	fIndexServer->RegisterAddOn(ref);
66};
67
68
69void
70AnalyserMonitorHandler::AddOnDisabled(const add_on_entry_info* entryInfo)
71{
72	entry_ref ref;
73	make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
74		entryInfo->name, &ref);
75	fIndexServer->UnregisterAddOn(ref);
76};
77
78
79IndexServer::IndexServer()
80	:
81	BApplication("application/x-vnd.Haiku-index_server"),
82
83	fVolumeObserverHandler(this),
84	fAddOnMonitorHandler(this),
85	fPulseRunner(NULL)
86{
87	AddHandler(&fVolumeObserverHandler);
88	AddHandler(&fAddOnMonitorHandler);
89}
90
91
92IndexServer::~IndexServer()
93{
94	for (int i = 0; i < fAddOnList.CountItems(); i++) {
95		IndexServerAddOn* addon = fAddOnList.ItemAt(i);
96		for (int i = 0; i < fVolumeWatcherList.CountItems(); i++)
97			fVolumeWatcherList.ItemAt(i)->RemoveAnalyser(addon->Name());
98		image_id image = addon->ImageId();
99		delete addon;
100		unload_add_on(image);
101	}
102
103	_StopWatchingVolumes();
104
105	delete fPulseRunner;
106
107	RemoveHandler(&fVolumeObserverHandler);
108	RemoveHandler(&fAddOnMonitorHandler);
109}
110
111
112void
113IndexServer::ReadyToRun()
114{
115	_StartWatchingAddOns();
116	_StartWatchingVolumes();
117}
118
119
120void
121IndexServer::MessageReceived(BMessage *message)
122{
123	BApplication::MessageReceived(message);
124}
125
126
127bool
128IndexServer::QuitRequested()
129{
130	_StopWatchingVolumes();
131	return BApplication::QuitRequested();
132}
133
134
135void
136IndexServer::AddVolume(const BVolume& volume)
137{
138	// ignore volumes like / or /dev
139	if (volume.Capacity() == 0)
140		return;
141
142	// check if volume is already in our list
143	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
144		VolumeWatcher* current = fVolumeWatcherList.ItemAt(i);
145		if (current->Volume() == volume)
146			return;
147	}
148
149	char name[256];
150	volume.GetName(name);
151	STRACE("IndexServer::AddVolume %s\n", name);
152
153	VolumeWatcher* watcher = new VolumeWatcher(volume);
154/*	if (!watcher->Enabled()) {
155		delete watcher;
156		return;
157	}*/
158	fVolumeWatcherList.AddItem(watcher);
159	_SetupVolumeWatcher(watcher);
160	watcher->StartWatching();
161}
162
163
164void
165IndexServer::RemoveVolume(const BVolume& volume)
166{
167	VolumeWatcher* watcher = NULL;
168	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
169		VolumeWatcher* current = fVolumeWatcherList.ItemAt(i);
170		if (current->Volume() == volume) {
171			watcher = current;
172			break;
173		}
174	}
175
176	if (!watcher)
177		return;
178
179	watcher->Stop();
180	fVolumeWatcherList.RemoveItem(watcher);
181	watcher->PostMessage(B_QUIT_REQUESTED);
182}
183
184
185void
186IndexServer::RegisterAddOn(entry_ref ref)
187{
188	STRACE("RegisterAddOn %s\n", ref.name);
189
190	BPath path(&ref);
191	image_id image = load_add_on(path.Path());
192	if (image < 0)
193		return;
194
195	create_index_server_addon* createFunc;
196
197	// Get the instantiation function
198	status_t status = get_image_symbol(image, "instantiate_index_server_addon",
199		B_SYMBOL_TYPE_TEXT, (void**)&createFunc);
200	if (status != B_OK) {
201		unload_add_on(image);
202		return;
203	}
204
205	IndexServerAddOn* addon = createFunc(image, ref.name);
206	if (!addon) {
207		unload_add_on(image);
208		return;
209	}
210	if (!fAddOnList.AddItem(addon)) {
211		unload_add_on(image);
212		return;
213	}
214
215	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
216		VolumeWatcher* watcher = fVolumeWatcherList.ItemAt(i);
217		FileAnalyser* analyser = _SetupFileAnalyser(addon, watcher->Volume());
218		if (!analyser)
219			continue;
220		if (!watcher->AddAnalyser(analyser))
221			delete analyser;
222	}
223
224}
225
226
227void
228IndexServer::UnregisterAddOn(entry_ref ref)
229{
230	IndexServerAddOn* addon = _FindAddon(ref.name);
231	if (!addon)
232		return;
233
234	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++)
235		fVolumeWatcherList.ItemAt(i)->RemoveAnalyser(addon->Name());
236
237	fAddOnList.RemoveItem(addon);
238	unload_add_on(addon->ImageId());
239	delete addon;
240}
241
242
243FileAnalyser*
244IndexServer::CreateFileAnalyser(const BString& name, const BVolume& volume)
245{
246	Lock();
247	IndexServerAddOn* addon = _FindAddon(name);
248	if (!addon) {
249		Unlock();
250		return NULL;
251	}
252	FileAnalyser* analyser = addon->CreateFileAnalyser(volume);
253	Unlock();
254	return analyser;
255}
256
257
258void
259IndexServer::_StartWatchingVolumes()
260{
261	BVolume volume;
262	while (fVolumeRoster.GetNextVolume(&volume) != B_BAD_VALUE)
263		AddVolume(volume);
264	fVolumeRoster.StartWatching(this);
265}
266
267
268void
269IndexServer::_StopWatchingVolumes()
270{
271	STRACE("_StopWatchingVolumes\n");
272
273	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
274		VolumeWatcher* watcher = fVolumeWatcherList.ItemAt(i);
275		watcher->Stop();
276		watcher->PostMessage(B_QUIT_REQUESTED);
277	}
278	fVolumeWatcherList.MakeEmpty();
279}
280
281
282void
283IndexServer::_SetupVolumeWatcher(VolumeWatcher* watcher)
284{
285	for (int i = 0; i < fAddOnList.CountItems(); i++) {
286		IndexServerAddOn* addon = fAddOnList.ItemAt(i);
287		FileAnalyser* analyser = _SetupFileAnalyser(addon, watcher->Volume());
288		if (!analyser)
289			continue;
290		if (!watcher->AddAnalyser(analyser))
291			delete analyser;
292	}
293}
294
295
296FileAnalyser*
297IndexServer::_SetupFileAnalyser(IndexServerAddOn* addon, const BVolume& volume)
298{
299	FileAnalyser* analyser = addon->CreateFileAnalyser(volume);
300	if (!analyser)
301		return NULL;
302	AnalyserSettings* settings = new AnalyserSettings(analyser->Name(),
303		analyser->Volume());
304	BReference<AnalyserSettings> settingsRef(settings, true);
305	if (!settings) {
306		delete analyser;
307		return NULL;
308	}
309	analyser->SetSettings(settings);
310	return analyser;
311}
312
313
314void
315IndexServer::_StartWatchingAddOns()
316{
317	AddHandler(&fAddOnMonitorHandler);
318	BMessage pulse(B_PULSE);
319	fPulseRunner = new BMessageRunner(&fAddOnMonitorHandler, &pulse, 1000000LL);
320		// the monitor handler needs a pulse to check if add-ons are ready
321
322	char parameter[32];
323	size_t parameterLength = sizeof(parameter);
324	bool safeMode = false;
325	if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter,
326			&parameterLength) == B_OK) {
327		if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
328			|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
329			|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
330			safeMode = true;
331	}
332
333	// load dormant media nodes
334	const directory_which directories[] = {
335		B_USER_ADDONS_DIRECTORY,
336		B_COMMON_ADDONS_DIRECTORY,
337		B_SYSTEM_ADDONS_DIRECTORY
338	};
339
340	// when safemode, only B_SYSTEM_ADDONS_DIRECTORY is used
341	for (uint32 i = safeMode ? 4 : 0;
342			i < sizeof(directories) / sizeof(directory_which); i++) {
343		BDirectory directory;
344		node_ref nodeRef;
345		BPath path;
346		if (find_directory(directories[i], &path) == B_OK
347			&& path.Append("index_server") == B_OK
348			&& directory.SetTo(path.Path()) == B_OK
349			&& directory.GetNodeRef(&nodeRef) == B_OK)
350			fAddOnMonitorHandler.AddDirectory(&nodeRef, true);
351	}
352}
353
354
355IndexServerAddOn*
356IndexServer::_FindAddon(const BString& name)
357{
358	for (int i = 0; i < fAddOnList.CountItems(); i++) {
359		IndexServerAddOn* current = fAddOnList.ItemAt(i);
360		if (current->Name() == name)
361			return current;
362	}
363	return NULL;
364}
365