⛏️ index : haiku.git

/*
 * Copyright 2010-2013 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		John Scipione, jscipione@gmail.com
 *		Clemens Zeidler, haiku@clemens-zeidler.de
 */


#include "IndexServer.h"

#include <Path.h>
#include <String.h>


VolumeObserverHandler::VolumeObserverHandler(IndexServer* indexServer)
	:
	fIndexServer(indexServer)
{
	
}


void
VolumeObserverHandler::MessageReceived(BMessage* message)
{
	if (message->what != B_NODE_MONITOR)
		return;

	dev_t device;
	int32 opcode;
	message->FindInt32("opcode", &opcode) ;
	switch (opcode) {
		case B_DEVICE_MOUNTED :
			message->FindInt32("new device", &device);
			fIndexServer->AddVolume(BVolume(device));
			break ;
		
		case B_DEVICE_UNMOUNTED :
			message->FindInt32("device", &device);
			fIndexServer->RemoveVolume(BVolume(device));
			break ;
	}
}


AnalyserMonitorHandler::AnalyserMonitorHandler(IndexServer* indexServer)
	:
	fIndexServer(indexServer)
{
	
}


void
AnalyserMonitorHandler::AddOnEnabled(const add_on_entry_info* entryInfo)
{
	entry_ref ref;
	make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
		entryInfo->name, &ref);
	fIndexServer->RegisterAddOn(ref);
};


void
AnalyserMonitorHandler::AddOnDisabled(const add_on_entry_info* entryInfo)
{
	entry_ref ref;
	make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
		entryInfo->name, &ref);
	fIndexServer->UnregisterAddOn(ref);
};


IndexServer::IndexServer()
	:
	BApplication("application/x-vnd.Haiku-index_server"),
	fVolumeObserverHandler(this),
	fAddOnMonitorHandler(this),
	fPulseRunner(NULL)
{
	AddHandler(&fVolumeObserverHandler);
	AddHandler(&fAddOnMonitorHandler);
}


IndexServer::~IndexServer()
{
	for (int i = 0; i < fAddOnList.CountItems(); i++) {
		IndexServerAddOn* addon = fAddOnList.ItemAt(i);
		for (int i = 0; i < fVolumeWatcherList.CountItems(); i++)
			fVolumeWatcherList.ItemAt(i)->RemoveAnalyser(addon->Name());
		image_id image = addon->ImageId();
		delete addon;
		unload_add_on(image);
	}

	_StopWatchingVolumes();

	delete fPulseRunner;

	RemoveHandler(&fVolumeObserverHandler);
	RemoveHandler(&fAddOnMonitorHandler);
}


void
IndexServer::ReadyToRun()
{
	_StartWatchingAddOns();
	_StartWatchingVolumes();
}


void
IndexServer::MessageReceived(BMessage *message)
{
	BApplication::MessageReceived(message);
}


bool
IndexServer::QuitRequested()
{
	_StopWatchingVolumes();
	return BApplication::QuitRequested();
}


void
IndexServer::AddVolume(const BVolume& volume)
{
	// ignore volumes like / or /dev
	if (volume.Capacity() == 0)
		return;
	
	// check if volume is already in our list
	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
		VolumeWatcher* current = fVolumeWatcherList.ItemAt(i);
		if (current->Volume() == volume)
			return;
	}

	char name[256];
	volume.GetName(name);
	STRACE("IndexServer::AddVolume %s\n", name);

	VolumeWatcher* watcher = new VolumeWatcher(volume);
/*	if (!watcher->Enabled()) {
		delete watcher;
		return;
	}*/
	fVolumeWatcherList.AddItem(watcher);
	_SetupVolumeWatcher(watcher);
	watcher->StartWatching();
}


void
IndexServer::RemoveVolume(const BVolume& volume)
{
	VolumeWatcher* watcher = NULL;
	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
		VolumeWatcher* current = fVolumeWatcherList.ItemAt(i);
		if (current->Volume() == volume) {
			watcher = current;
			break;
		}
	}

	if (!watcher)
		return;

	watcher->Stop();
	fVolumeWatcherList.RemoveItem(watcher);
	watcher->PostMessage(B_QUIT_REQUESTED);
}


void
IndexServer::RegisterAddOn(entry_ref ref)
{
	STRACE("RegisterAddOn %s\n", ref.name);

	BPath path(&ref);
	image_id image = load_add_on(path.Path());
	if (image < 0)
		return;

	create_index_server_addon* createFunc;

	// Get the instantiation function
	status_t status = get_image_symbol(image, "instantiate_index_server_addon",
		B_SYMBOL_TYPE_TEXT, (void**)&createFunc);
	if (status != B_OK) {
		unload_add_on(image);
		return;
	}

	IndexServerAddOn* addon = createFunc(image, ref.name);
	if (!addon) {
		unload_add_on(image);
		return;
	}
	if (!fAddOnList.AddItem(addon)) {
		unload_add_on(image);
		return;
	}

	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
		VolumeWatcher* watcher = fVolumeWatcherList.ItemAt(i);
		FileAnalyser* analyser = _SetupFileAnalyser(addon, watcher->Volume());
		if (!analyser)
			continue;
		if (!watcher->AddAnalyser(analyser))
			delete analyser;
	}

}


void
IndexServer::UnregisterAddOn(entry_ref ref)
{
	IndexServerAddOn* addon = _FindAddon(ref.name);
	if (!addon)
		return;

	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++)
		fVolumeWatcherList.ItemAt(i)->RemoveAnalyser(addon->Name());

	fAddOnList.RemoveItem(addon);
	unload_add_on(addon->ImageId());
	delete addon;
}


FileAnalyser*
IndexServer::CreateFileAnalyser(const BString& name, const BVolume& volume)
{
	Lock();
	IndexServerAddOn* addon = _FindAddon(name);
	if (!addon) {
		Unlock();
		return NULL;
	}
	FileAnalyser* analyser = addon->CreateFileAnalyser(volume);
	Unlock();
	return analyser;
}


void
IndexServer::_StartWatchingVolumes()
{
	BVolume volume;
	while (fVolumeRoster.GetNextVolume(&volume) != B_BAD_VALUE)
		AddVolume(volume);
	fVolumeRoster.StartWatching(this);
}


void
IndexServer::_StopWatchingVolumes()
{
	STRACE("_StopWatchingVolumes\n");

	for (int i = 0; i < fVolumeWatcherList.CountItems(); i++) {
		VolumeWatcher* watcher = fVolumeWatcherList.ItemAt(i);
		watcher->Stop();
		watcher->PostMessage(B_QUIT_REQUESTED);
	}
	fVolumeWatcherList.MakeEmpty();
}


void
IndexServer::_SetupVolumeWatcher(VolumeWatcher* watcher)
{
	for (int i = 0; i < fAddOnList.CountItems(); i++) {
		IndexServerAddOn* addon = fAddOnList.ItemAt(i);
		FileAnalyser* analyser = _SetupFileAnalyser(addon, watcher->Volume());
		if (!analyser)
			continue;
		if (!watcher->AddAnalyser(analyser))
			delete analyser;
	}
}


FileAnalyser*
IndexServer::_SetupFileAnalyser(IndexServerAddOn* addon, const BVolume& volume)
{
	FileAnalyser* analyser = addon->CreateFileAnalyser(volume);
	if (!analyser)
		return NULL;
	AnalyserSettings* settings = new AnalyserSettings(analyser->Name(),
		analyser->Volume());
	BReference<AnalyserSettings> settingsRef(settings, true);
	if (!settings) {
		delete analyser;
		return NULL;
	}
	analyser->SetSettings(settings);
	return analyser;
}


void
IndexServer::_StartWatchingAddOns()
{
	AddHandler(&fAddOnMonitorHandler);

	BMessage pulse(B_PULSE);
	fPulseRunner = new BMessageRunner(&fAddOnMonitorHandler, &pulse, 1000000LL);
		// the monitor handler needs a pulse to check if add-ons are ready

	fAddOnMonitorHandler.AddAddOnDirectories("index_server");
}


IndexServerAddOn*
IndexServer::_FindAddon(const BString& name)
{
	for (int i = 0; i < fAddOnList.CountItems(); i++) {
		IndexServerAddOn* current = fAddOnList.ItemAt(i);
		if (current->Name() == name)
			return current;
	}
	return NULL;
}