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