Initial work on an index server. No add-on yet so quite useless at the moment, but a clucent full-text search add-on is coming soon.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39160 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Clemens Zeidler
2010-10-27 13:55:45 +00:00
parent 7b5743bab4
commit 1294543de9
14 changed files with 1939 additions and 0 deletions
@@ -0,0 +1,136 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef INDEX_SERVER_ADD_ON_H
#define INDEX_SERVER_ADD_ON_H
#include <Autolock.h>
#include <Entry.h>
#include <image.h>
#include <ObjectList.h>
#include <String.h>
#include <Volume.h>
#include "Referenceable.h"
class analyser_settings {
public:
analyser_settings();
bool catchUpEnabled;
//! the volume is scanned form 0 to syncPosition, from
//! syncPosition to watchingStart the volume is not scanned
bigtime_t syncPosition;
bigtime_t watchingStart;
bigtime_t watchingPosition;
};
class FileAnalyser;
/*! Thread safe class to sync settings between different FileAnalyser. For
example the watcher analyser and the catch up analyser. Because the lock
overhead use it only when necessary or use a cached analyser_settings if
possible. */
class AnalyserSettings : public BReferenceable {
public:
AnalyserSettings(const BString& name,
const BVolume& volume);
const BString& Name() { return fName; }
const BVolume& Volume() { return fVolume; }
bool ReadSettings();
bool WriteSettings();
analyser_settings RawSettings();
// settings
void SetCatchUpEnabled(bool enabled);
void SetSyncPosition(bigtime_t time);
void SetWatchingStart(bigtime_t time);
void SetWatchingPosition(bigtime_t time);
bool CatchUpEnabled();
bigtime_t SyncPosition();
bigtime_t WatchingStart();
bigtime_t WatchingPosition();
private:
BString fName;
BVolume fVolume;
BLocker fSettingsLock;
analyser_settings fAnalyserSettings;
};
class FileAnalyser {
public:
FileAnalyser(const BString& name,
const BVolume& volume);
virtual ~FileAnalyser() {}
void SetSettings(AnalyserSettings* settings);
AnalyserSettings* Settings() const;
const analyser_settings& CachedSettings() const;
void UpdateSettingsCache();
const BString& Name() const { return fName; }
const BVolume& Volume() const { return fVolume; }
virtual status_t InitCheck() = 0;
virtual void AnalyseEntry(const entry_ref& ref) = 0;
virtual void DeleteEntry(const entry_ref& ref) { }
virtual void MoveEntry(const entry_ref& oldRef,
const entry_ref& newRef) { }
//! If the indexer send a bunch of entry this indicates that the last one
//! has been arrived.
virtual void LastEntry() { }
protected:
BVolume fVolume;
BReference<AnalyserSettings> fAnalyserSettings;
analyser_settings fCachedSettings;
private:
BString fName;
};
typedef BObjectList<FileAnalyser> FileAnalyserList;
class IndexServerAddOn {
public:
IndexServerAddOn(image_id id, const char* name)
:
fImageId(id),
fName(name)
{
}
virtual ~IndexServerAddOn() {}
image_id ImageId() { return fImageId; }
BString Name() { return fName; }
virtual FileAnalyser* CreateFileAnalyser(const BVolume& volume) = 0;
private:
image_id fImageId;
BString fName;
};
typedef IndexServerAddOn* create_index_server_addon(image_id id,
const char* name);
#endif
@@ -0,0 +1,34 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef INDEX_SERVER_PRIVATE_H
#define INDEX_SERVER_PRIVATE_H
#include <String.h>
const BString kIndexServerDirectory = ".IndexServer";
const BString kVolumeStatusFileName = "VolumeStatus";
// messages between preferences app
const uint32 kStopWatching = 'StoW';
const uint32 kStartWatching = 'StaW';
const uint32 kRegisterWatcher = 'RegW';
const uint32 kVolumenAdded = 'VAdd';
const uint32 kVolumenRemoved = 'VRem';
const uint32 kAddOnAdded = 'AAdd';
const uint32 kAddOnRemoved = 'ARem';
const uint32 kGetVolumenInfos = 'GVIn';
const uint32 kGetAddOnInfos = 'GAIn';
const uint32 kEnableAddOn = 'EnaA';
const uint32 kDisableAddOn = 'DisA';
#endif // INDEX_SERVER_PRIVATE_H
@@ -0,0 +1,55 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef ANALYSER_DISPATCHER
#define ANALYSER_DISPATCHER
#include <Looper.h>
#include <String.h>
#include "IndexServerAddOn.h"
class FileAnalyser;
class AnalyserDispatcher : public BLooper {
public:
AnalyserDispatcher();
~AnalyserDispatcher();
void Stop();
bool Stopped();
bool Busy();
void AnalyseEntry(const entry_ref& ref);
void DeleteEntry(const entry_ref& ref);
void MoveEntry(const entry_ref& oldRef,
const entry_ref& newRef);
void LastEntry();
//! thread safe
bool AddAnalyser(FileAnalyser* analyser);
bool RemoveAnalyser(const BString& name);
void WriteAnalyserSettings();
void SetSyncPosition(bigtime_t time);
void SetWatchingStart(bigtime_t time);
void SetWatchingPosition(bigtime_t time);
protected:
FileAnalyserList fFileAnalyserList;
private:
FileAnalyser* _FindAnalyser(const BString& name);
vint32 fStopped;
};
#endif // ANALYSER_DISPATCHER
+248
View File
@@ -0,0 +1,248 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#include "CatchUpManager.h"
#include <vector>
#include <Debug.h>
#include <Query.h>
#include "IndexServer.h"
const uint32 kCatchUp = '&CaU';
const uint32 kCatchUpDone = '&CUD';
const bigtime_t kSecond = 1000000;
CatchUpAnalyser::CatchUpAnalyser(const BVolume& volume, time_t start,
time_t end, BHandler* manager)
:
fVolume(volume),
fStart(start),
fEnd(end),
fCatchUpManager(manager)
{
}
void
CatchUpAnalyser::MessageReceived(BMessage *message)
{
switch (message->what) {
case kCatchUp:
_CatchUp();
break;
default:
BLooper::MessageReceived(message);
}
}
void
CatchUpAnalyser::StartAnalysing()
{
PostMessage(kCatchUp);
Run();
}
void
CatchUpAnalyser::AnalyseEntry(const entry_ref& ref)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++) {
FileAnalyser* analyser = fFileAnalyserList.ItemAt(i);
const analyser_settings& settings = analyser->CachedSettings();
if (settings.syncPosition / kSecond >= fStart
&& settings.watchingStart / kSecond <= fEnd)
analyser->AnalyseEntry(ref);
}
}
void
CatchUpAnalyser::_CatchUp()
{
STRACE("_CatchUp start %i, end %i\n", (int)fStart, (int)fEnd);
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
STRACE("- Analyser %s\n", fFileAnalyserList.ItemAt(i)->Name().String());
BQuery query;
query.SetVolume(&fVolume);
query.PushAttr("last_modified");
query.PushInt32(fStart);
query.PushOp(B_GE);
query.PushAttr("last_modified");
query.PushInt32(fEnd);
query.PushOp(B_LE);
query.PushOp(B_AND);
query.Fetch();
std::vector<entry_ref> entryList;
entry_ref ref;
while (query.GetNextRef(&ref) == B_OK)
entryList.push_back(ref);
printf("CatchUpAnalyser:: entryList.size() %i\n", (int)entryList.size());
if (entryList.size() == 0)
return;
for (uint32 i = 0; i < entryList.size(); i++) {
if (Stopped())
return;
AnalyseEntry(entryList[i]);
}
LastEntry();
_WriteSyncSatus(fEnd);
BMessenger managerMessenger(fCatchUpManager);
BMessage msg(kCatchUpDone);
msg.AddPointer("Analyser", this);
managerMessenger.SendMessage(&msg);
}
void
CatchUpAnalyser::_WriteSyncSatus(bigtime_t syncTime)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++) {
AnalyserSettings* settings = fFileAnalyserList.ItemAt(i)->Settings();
ASSERT(settings);
settings->SetSyncPosition(syncTime);
settings->WriteSettings();
}
}
CatchUpManager::CatchUpManager(const BVolume& volume)
:
fVolume(volume)
{
}
CatchUpManager::~CatchUpManager()
{
Stop();
for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++)
delete fFileAnalyserQueue.ItemAt(i);
}
void
CatchUpManager::MessageReceived(BMessage *message)
{
CatchUpAnalyser* analyser;
switch (message->what) {
case kCatchUpDone:
message->AddPointer("Analyser", &analyser);
fCatchUpAnalyserList.RemoveItem(analyser);
analyser->PostMessage(B_QUIT_REQUESTED);
break;
default:
BHandler::MessageReceived(message);
}
}
bool
CatchUpManager::AddAnalyser(const FileAnalyser* analyserOrg)
{
IndexServer* server = (IndexServer*)be_app;
FileAnalyser* analyser = server->CreateFileAnalyser(analyserOrg->Name(),
fVolume);
if (!analyser)
return false;
ASSERT(analyserOrg->Settings());
analyser->SetSettings(analyserOrg->Settings());
bool status = fFileAnalyserQueue.AddItem(analyser);
if (!status)
delete analyser;
return status;
}
void
CatchUpManager::RemoveAnalyser(const BString& name)
{
for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++) {
FileAnalyser* analyser = fFileAnalyserQueue.ItemAt(i);
if (analyser->Name() == name) {
fFileAnalyserQueue.RemoveItem(analyser);
delete analyser;
}
}
for (int i = 0; i < fCatchUpAnalyserList.CountItems(); i++)
fCatchUpAnalyserList.ItemAt(i)->RemoveAnalyser(name);
}
bool
CatchUpManager::CatchUp()
{
STRACE("CatchUpManager::CatchUp()\n");
bigtime_t startBig = real_time_clock_usecs();
bigtime_t endBig = 0;
for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++) {
FileAnalyser* analyser = fFileAnalyserQueue.ItemAt(i);
analyser->UpdateSettingsCache();
const analyser_settings& settings = analyser->CachedSettings();
STRACE("%s, %i, %i\n", analyser->Name().String(),
(int)settings.syncPosition, (int)settings.watchingStart);
if (settings.syncPosition < startBig)
startBig = settings.syncPosition;
if (settings.watchingStart > endBig)
endBig = settings.watchingStart;
}
CatchUpAnalyser* catchUpAnalyser = new CatchUpAnalyser(fVolume,
startBig / kSecond, endBig / kSecond, this);
if (!catchUpAnalyser)
return false;
if (!fCatchUpAnalyserList.AddItem(catchUpAnalyser)) {
delete catchUpAnalyser;
return false;
}
for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++) {
FileAnalyser* analyser = fFileAnalyserQueue.ItemAt(i);
// if AddAnalyser fails at least don't leak
if (!catchUpAnalyser->AddAnalyser(analyser))
delete analyser;
}
fFileAnalyserQueue.MakeEmpty();
catchUpAnalyser->StartAnalysing();
return true;
}
void
CatchUpManager::Stop()
{
for (int i = 0; i < fCatchUpAnalyserList.CountItems(); i++) {
CatchUpAnalyser* catchUpAnalyser = fCatchUpAnalyserList.ItemAt(i);
catchUpAnalyser->Stop();
catchUpAnalyser->PostMessage(B_QUIT_REQUESTED);
}
fCatchUpAnalyserList.MakeEmpty();
}
+77
View File
@@ -0,0 +1,77 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef CATCH_UP_MANAGER_H
#define CATCH_UP_MANAGER_H
#include "AnalyserDispatcher.h"
#define DEBUG_CATCH_UP
#ifdef DEBUG_CATCH_UP
#include <stdio.h>
# define STRACE(x...) printf(x)
#else
# define STRACE(x...) ;
#endif
class CatchUpAnalyser : public AnalyserDispatcher {
public:
CatchUpAnalyser(const BVolume& volume,
time_t start, time_t end,
BHandler* manager);
void MessageReceived(BMessage *message);
void StartAnalysing();
void AnalyseEntry(const entry_ref& ref);
const BVolume& Volume() { return fVolume; }
private:
void _CatchUp();
void _WriteSyncSatus(bigtime_t syncTime);
BVolume fVolume;
time_t fStart;
time_t fEnd;
BHandler* fCatchUpManager;
};
typedef BObjectList<CatchUpAnalyser> CatchUpAnalyserList;
class CatchUpManager : public BHandler {
public:
CatchUpManager(const BVolume& volume);
~CatchUpManager();
void MessageReceived(BMessage *message);
//! Add analyser to the queue.
bool AddAnalyser(const FileAnalyser* analyser);
void RemoveAnalyser(const BString& name);
//! Spawn a CatchUpAnalyser and fill it with the analyser in the
//! queue
bool CatchUp();
//! Stop all catch up threads and put the analyser back into the
//! queue.
void Stop();
private:
BVolume fVolume;
FileAnalyserList fFileAnalyserQueue;
CatchUpAnalyserList fCatchUpAnalyserList;
};
#endif
+364
View File
@@ -0,0 +1,364 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#include "IndexServer.h"
#include <Directory.h>
#include <driver_settings.h>
#include <FindDirectory.h>
#include <Path.h>
#include <String.h>
#include <syscalls.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
char parameter[32];
size_t parameterLength = sizeof(parameter);
bool safeMode = false;
if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter,
&parameterLength) == B_OK) {
if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
safeMode = true;
}
// load dormant media nodes
const directory_which directories[] = {
B_USER_ADDONS_DIRECTORY,
B_COMMON_ADDONS_DIRECTORY,
B_BEOS_ADDONS_DIRECTORY
};
// when safemode, only B_BEOS_ADDONS_DIRECTORY is used
for (uint32 i = safeMode ? 2 : 0;
i < sizeof(directories) / sizeof(directory_which); i++) {
BDirectory directory;
node_ref nodeRef;
BPath path;
if (find_directory(directories[i], &path) == B_OK
&& path.Append("index_server") == B_OK
&& directory.SetTo(path.Path()) == B_OK
&& directory.GetNodeRef(&nodeRef) == B_OK)
fAddOnMonitorHandler.AddDirectory(&nodeRef, true);
}
}
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;
}
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef INDEX_SERVER_H
#define INDEX_SERVER_H
#include <Application.h>
#include <MessageRunner.h>
#include <VolumeRoster.h>
#include <AddOnMonitorHandler.h>
#include <ObjectList.h>
#include "IndexServerAddOn.h"
#include "VolumeWatcher.h"
#define DEBUG_INDEX_SERVER
#ifdef DEBUG_INDEX_SERVER
#include <stdio.h>
# define STRACE(x...) printf(x)
#else
# define STRACE(x...) ;
#endif
class IndexServer;
class VolumeObserverHandler : public BHandler {
public:
VolumeObserverHandler(IndexServer* indexServer);
void MessageReceived(BMessage *message);
private:
IndexServer* fIndexServer;
};
class AnalyserMonitorHandler : public AddOnMonitorHandler {
public:
AnalyserMonitorHandler(
IndexServer* indexServer);
private:
void AddOnEnabled(
const add_on_entry_info* entryInfo);
void AddOnDisabled(
const add_on_entry_info* entryInfo);
IndexServer* fIndexServer;
};
class IndexServer : public BApplication {
public:
IndexServer();
virtual ~IndexServer();
virtual void ReadyToRun();
virtual void MessageReceived(BMessage *message);
virtual bool QuitRequested();
void AddVolume(const BVolume& volume);
void RemoveVolume(const BVolume& volume);
void RegisterAddOn(entry_ref ref);
void UnregisterAddOn(entry_ref ref);
//! thread safe
FileAnalyser* CreateFileAnalyser(const BString& name,
const BVolume& volume);
private:
void _StartWatchingVolumes();
void _StopWatchingVolumes();
void _SetupVolumeWatcher(VolumeWatcher* watcher);
FileAnalyser* _SetupFileAnalyser(IndexServerAddOn* addon,
const BVolume& volume);
void _StartWatchingAddOns();
inline IndexServerAddOn* _FindAddon(const BString& name);
BVolumeRoster fVolumeRoster;
BObjectList<VolumeWatcher> fVolumeWatcherList;
BObjectList<IndexServerAddOn> fAddOnList;
VolumeObserverHandler fVolumeObserverHandler;
AnalyserMonitorHandler fAddOnMonitorHandler;
BMessageRunner* fPulseRunner;
};
#endif
+28
View File
@@ -0,0 +1,28 @@
SubDir HAIKU_TOP src servers index_server ;
AddResources index_server : index_server.rdef ;
UsePrivateHeaders index_server shared storage kernel app ;
UsePrivateSystemHeaders ;
Server index_server :
CatchUpManager.cpp
main.cpp
IndexServer.cpp
IndexServerAddOn.cpp
ModifiedNotifications.cpp
VolumeWatcher.cpp
# storage
AddOnMonitorHandler.cpp
NodeMonitorHandler.cpp
:
be
$(TARGET_LIBSTDC++)
;
SEARCH on [ FGristFiles AddOnMonitorHandler.cpp NodeMonitorHandler.cpp ]
+= [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) kits storage ] ;
SEARCH on [ FGristFiles IndexServerAddOn.cpp ]
+= [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) add-ons index_server ] ;
@@ -0,0 +1,86 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#include "ModifiedNotifications.h"
#include "fs_query.h"
#include <MessengerPrivate.h>
#include <syscalls.h>
#include "query_private.h"
NotifyAllQuery::NotifyAllQuery()
:
fQueryFd(-1)
{
}
NotifyAllQuery::~NotifyAllQuery()
{
StopWatching();
}
status_t
NotifyAllQuery::StartWatching(const BVolume& volume, const char* query,
const BMessenger& target)
{
if (fQueryFd >= 0)
return B_NOT_ALLOWED;
BMessenger::Private messengerPrivate(const_cast<BMessenger&>(target));
port_id port = messengerPrivate.Port();
long token = (messengerPrivate.IsPreferredTarget() ? -1
: messengerPrivate.Token());
fQueryFd = _kern_open_query(volume.Device(), query, strlen(query),
B_LIVE_QUERY | B_ATTR_CHANGE_NOTIFICATION, port, token);
if (fQueryFd < 0)
return fQueryFd;
return B_OK;
}
status_t
NotifyAllQuery::StopWatching()
{
status_t error = B_OK;
if (fQueryFd >= 0) {
error = _kern_close(fQueryFd);
fQueryFd = -1;
}
return error;
}
ModfiedNotifications::~ModfiedNotifications()
{
StopWatching();
}
status_t
ModfiedNotifications::StartWatching(const BVolume& volume, time_t startTime,
const BMessenger& target)
{
BString string = "(last_modified>=";
string << startTime;
string << ")";
return fQuery.StartWatching(volume, string.String(), target);
}
status_t
ModfiedNotifications::StopWatching()
{
return fQuery.StopWatching();
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef MODIFIED_NOTIFICATIONS_H
#define MODIFIED_NOTIFICATIONS_H
#include <String.h>
#include <Volume.h>
#include <Messenger.h>
class NotifyAllQuery {
public:
NotifyAllQuery();
~NotifyAllQuery();
status_t StartWatching(const BVolume& volume,
const char* query,
const BMessenger& target);
status_t StopWatching();
private:
int fQueryFd;
};
class ModfiedNotifications {
public:
~ModfiedNotifications();
status_t StartWatching(const BVolume& volume,
time_t startTime, const BMessenger& target);
status_t StopWatching();
private:
NotifyAllQuery fQuery;
};
#endif // MODIFIED_NOTIFICATIONS_H
+541
View File
@@ -0,0 +1,541 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#include "VolumeWatcher.h"
#include <sys/stat.h>
#include <Directory.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <VolumeRoster.h>
#include <Query.h>
#include "IndexServerPrivate.h"
const bigtime_t kSecond = 1000000;
WatchNameHandler::WatchNameHandler(VolumeWatcher* volumeWatcher)
:
fVolumeWatcher(volumeWatcher)
{
}
void
WatchNameHandler::EntryCreated(const char *name, ino_t directory, dev_t device,
ino_t node)
{
entry_ref ref(device, directory, name);
fVolumeWatcher->fCreatedList.CurrentList()->push_back(ref);
fVolumeWatcher->_NewEntriesArrived();
}
void
WatchNameHandler::EntryRemoved(const char *name, ino_t directory, dev_t device,
ino_t node)
{
entry_ref ref(device, directory, name);
fVolumeWatcher->fDeleteList.CurrentList()->push_back(ref);
fVolumeWatcher->_NewEntriesArrived();
}
void
WatchNameHandler::EntryMoved(const char *name, const char *fromName,
ino_t from_directory, ino_t to_directory, dev_t device, ino_t node,
dev_t nodeDevice)
{
entry_ref ref(device, to_directory, name);
entry_ref refFrom(device, from_directory, fromName);
fVolumeWatcher->fMovedList.CurrentList()->push_back(ref);
fVolumeWatcher->fMovedFromList.CurrentList()->push_back(refFrom);
fVolumeWatcher->_NewEntriesArrived();
}
void
WatchNameHandler::StatChanged(ino_t node, dev_t device, int32 statFields)
{
if ((statFields & B_STAT_MODIFICATION_TIME) == 0)
return;
}
AnalyserDispatcher::AnalyserDispatcher()
:
BLooper(NULL, B_LOW_PRIORITY),
fStopped(0)
{
}
AnalyserDispatcher::~AnalyserDispatcher()
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
delete fFileAnalyserList.ItemAt(i);
}
void
AnalyserDispatcher::Stop()
{
atomic_set(&fStopped, 1);
}
bool
AnalyserDispatcher::Stopped()
{
return (atomic_get(&fStopped) != 0);
}
void
AnalyserDispatcher::AnalyseEntry(const entry_ref& ref)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->AnalyseEntry(ref);
}
void
AnalyserDispatcher::DeleteEntry(const entry_ref& ref)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->DeleteEntry(ref);
}
void
AnalyserDispatcher::MoveEntry(const entry_ref& oldRef, const entry_ref& newRef)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->MoveEntry(oldRef, newRef);
}
void
AnalyserDispatcher::LastEntry()
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->LastEntry();
}
bool
AnalyserDispatcher::AddAnalyser(FileAnalyser* analyser)
{
if (analyser == NULL)
return false;
bool result;
Lock();
if (_FindAnalyser(analyser->Name())) {
Unlock();
return false;
}
result = fFileAnalyserList.AddItem(analyser);
Unlock();
return result;
}
bool
AnalyserDispatcher::RemoveAnalyser(const BString& name)
{
Lock();
FileAnalyser* analyser = _FindAnalyser(name);
if (analyser) {
fFileAnalyserList.RemoveItem(analyser);
delete analyser;
Unlock();
return true;
}
Unlock();
return false;
}
FileAnalyser*
AnalyserDispatcher::_FindAnalyser(const BString& name)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++) {
FileAnalyser* analyser = fFileAnalyserList.ItemAt(i);
if (analyser->Name() == name)
return analyser;
}
return NULL;
}
void
AnalyserDispatcher::WriteAnalyserSettings()
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->Settings()->WriteSettings();
}
void
AnalyserDispatcher::SetSyncPosition(bigtime_t time)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->Settings()->SetSyncPosition(time);
}
void
AnalyserDispatcher::SetWatchingStart(bigtime_t time)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingStart(time);
}
void
AnalyserDispatcher::SetWatchingPosition(bigtime_t time)
{
for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingPosition(time);
}
VolumeWorker::VolumeWorker(VolumeWatcher* watcher)
:
fVolumeWatcher(watcher),
fBusy(0)
{
}
void
VolumeWorker::MessageReceived(BMessage *message)
{
switch (message->what) {
case kTriggerWork:
_Work();
break;
default:
BLooper::MessageReceived(message);
}
}
bool
VolumeWorker::IsBusy()
{
return (atomic_get(&fBusy) != 0);
}
void
VolumeWorker::_Work()
{
list_collection collection;
fVolumeWatcher->GetSecureEntries(collection);
if (collection.createdList->size() == 0
&& collection.deletedList->size() == 0
&& collection.modifiedList->size() == 0
&& collection.movedList->size() == 0)
return;
_SetBusy(true);
for (unsigned int i = 0; i < collection.createdList->size() || Stopped();
i++)
AnalyseEntry(collection.createdList->at(i));
collection.createdList->clear();
for (unsigned int i = 0; i < collection.modifiedList->size() || Stopped();
i++)
AnalyseEntry(collection.modifiedList->at(i));
collection.modifiedList->clear();
for (unsigned int i = 0; i < collection.createdList->size() || Stopped();
i++)
AnalyseEntry(collection.createdList->at(i));
collection.createdList->clear();
for (unsigned int i = 0; i < collection.movedList->size() || Stopped();
i++)
MoveEntry(collection.movedFromList->at(i), collection.movedList->at(i));
collection.movedList->clear();
collection.movedFromList->clear();
LastEntry();
PostMessage(kTriggerWork);
_SetBusy(false);
}
void
VolumeWorker::_SetBusy(bool busy)
{
if (busy)
atomic_set(&fBusy, 1);
else
atomic_set(&fBusy, 0);
}
VolumeWatcherBase::VolumeWatcherBase(const BVolume& volume)
:
fVolume(volume),
fEnabled(true),
fLastUpdated(0)
{
ReadSettings();
}
const char* kEnabledAttr = "Enabled";
bool
VolumeWatcherBase::ReadSettings()
{
// TODO remove this
BVolume bootVolume;
BVolumeRoster roster;
roster.GetBootVolume(&bootVolume);
if (bootVolume == fVolume) {
fEnabled = true;
WriteSettings();
}
BDirectory rootDir;
fVolume.GetRootDirectory(&rootDir);
BPath path(&rootDir);
path.Append(kIndexServerDirectory);
path.Append(kVolumeStatusFileName);
BFile file(path.Path(), B_READ_ONLY);
if (file.InitCheck() != B_OK)
return false;
uint32 enabled;
file.WriteAttr(kEnabledAttr, B_UINT32_TYPE, 0, &enabled, sizeof(uint32));
fEnabled = enabled == 0 ? false : true;
return true;
}
bool
VolumeWatcherBase::WriteSettings()
{
BDirectory rootDir;
fVolume.GetRootDirectory(&rootDir);
BPath path(&rootDir);
path.Append(kIndexServerDirectory);
if (create_directory(path.Path(), 777) != B_OK)
return false;
path.Append(kVolumeStatusFileName);
BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
if (file.InitCheck() != B_OK)
return false;
uint32 enabled = fEnabled ? 1 : 0;
file.WriteAttr(kEnabledAttr, B_UINT32_TYPE, 0, &enabled, sizeof(uint32));
return true;
}
SwapEntryRefVector::SwapEntryRefVector()
{
fCurrentList = &fFirstList;
fNextList = &fSecondList;
}
EntryRefVector*
SwapEntryRefVector::SwapList()
{
EntryRefVector* temp = fCurrentList;
fCurrentList = fNextList;
fNextList = temp;
return fCurrentList;
}
EntryRefVector*
SwapEntryRefVector::CurrentList()
{
return fCurrentList;
}
VolumeWatcher::VolumeWatcher(const BVolume& volume)
:
VolumeWatcherBase(volume),
fWatching(false),
fWatchNameHandler(this),
fCatchUpManager(volume)
{
AddHandler(&fWatchNameHandler);
fVolumeWorker = new VolumeWorker(this);
fVolumeWorker->Run();
}
VolumeWatcher::~VolumeWatcher()
{
printf("~VolumeWatcher()\n");
Stop();
thread_id threadId = fVolumeWorker->Thread();
fVolumeWorker->PostMessage(B_QUIT_REQUESTED);
status_t error;
wait_for_thread(threadId, &error);
}
void
VolumeWatcher::MessageReceived(BMessage *message)
{
int32 opcode;
switch (message->what) {
case B_QUERY_UPDATE:
message->FindInt32("opcode", &opcode);
if (opcode == B_ATTR_CHANGED || opcode == B_ENTRY_CREATED) {
const char *name;
ino_t directory;
dev_t device;
if ((message->FindString("name", &name) != B_OK) ||
(message->FindInt64("directory", &directory) != B_OK) ||
(message->FindInt32("device", &device) != B_OK))
break;
entry_ref ref(device, directory, name);
fModifiedList.CurrentList()->push_back(ref);
_NewEntriesArrived();
}
break;
default:
BLooper::MessageReceived(message);
}
}
bool
VolumeWatcher::StartWatching()
{
Run();
watch_volume(fVolume.Device(), B_WATCH_NAME | B_WATCH_STAT,
&fWatchNameHandler);
if (fModfiedNotifications.StartWatching(fVolume.Device(), real_time_clock(),
this) != B_OK)
return false;
// set the time after start watching to not miss anything
fVolumeWorker->SetWatchingStart(real_time_clock_usecs());
char name[255];
fVolume.GetName(name);
fCatchUpManager.CatchUp();
fWatching = true;
return true;
}
void
VolumeWatcher::Stop()
{
char name[255];
fVolume.GetName(name);
// set the time before stop watching to not miss anything
fVolumeWorker->SetWatchingPosition(real_time_clock_usecs());
stop_watching(&fWatchNameHandler);
fVolumeWorker->WriteAnalyserSettings();
// don't stop the work because we have to handle all entries after writing
// the watching position
//fVolumeWorker->Stop();
fCatchUpManager.Stop();
}
bool
VolumeWatcher::AddAnalyser(FileAnalyser* analyser)
{
if (!fVolumeWorker->AddAnalyser(analyser))
return false;
Lock();
if (!fCatchUpManager.AddAnalyser(analyser)) {
Unlock();
return false;
}
if (fWatching)
fCatchUpManager.CatchUp();
Unlock();
return true;
}
bool
VolumeWatcher::RemoveAnalyser(const BString& name)
{
if (!fVolumeWorker->RemoveAnalyser(name))
return false;
Lock();
fCatchUpManager.RemoveAnalyser(name);
Unlock();
return true;
}
void
VolumeWatcher::GetSecureEntries(list_collection& collection)
{
collection.createdList = fCreatedList.SwapList();
collection.deletedList = fDeleteList.SwapList();
collection.modifiedList = fModifiedList.SwapList();
collection.movedList = fMovedList.SwapList();
collection.movedFromList = fMovedFromList.SwapList();
}
bool
VolumeWatcher::FindEntryRef(ino_t node, dev_t device, entry_ref& entry)
{
return false;
}
void
VolumeWatcher::_NewEntriesArrived()
{
// The fVolumeWorker has to exist as long as we live so directly post to
// the queue.
if (fVolumeWorker->IsBusy())
return;
fVolumeWorker->PostMessage(kTriggerWork);
}
+164
View File
@@ -0,0 +1,164 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#ifndef VOLUME_WATCHER_H
#define VOLUME_WATCHER_H
#include <vector>
#include <Debug.h>
#include <Handler.h>
#include <NodeMonitorHandler.h>
#include <Volume.h>
#include <ObjectList.h>
#include "AnalyserDispatcher.h"
#include "CatchUpManager.h"
#include "IndexServerAddOn.h"
#include "ModifiedNotifications.h"
class VolumeWatcher;
class WatchNameHandler : public NodeMonitorHandler {
public:
WatchNameHandler(VolumeWatcher* volumeWatcher);
void EntryCreated(const char *name, ino_t directory,
dev_t device, ino_t node);
void EntryRemoved(const char *name, ino_t directory,
dev_t device, ino_t node);
void EntryMoved(const char *name,
const char *fromName, ino_t from_directory,
ino_t to_directory, dev_t device,
ino_t node, dev_t nodeDevice);
void StatChanged(ino_t node, dev_t device,
int32 statFields);
private:
VolumeWatcher* fVolumeWatcher;
};
typedef std::vector<entry_ref> EntryRefVector;
class VolumeWatcher;
const uint32 kTriggerWork = '&twk'; // what a bad message
class VolumeWorker : public AnalyserDispatcher
{
public:
VolumeWorker(VolumeWatcher* watcher);
void MessageReceived(BMessage *message);
bool IsBusy();
private:
void _Work();
void _SetBusy(bool busy = true);
VolumeWatcher* fVolumeWatcher;
vint32 fBusy;
};
class VolumeWatcherBase {
public:
VolumeWatcherBase(const BVolume& volume);
const BVolume& Volume() { return fVolume; }
bool Enabled() { return fEnabled; }
bigtime_t GetLastUpdated() { return fLastUpdated; }
protected:
bool ReadSettings();
bool WriteSettings();
BVolume fVolume;
bool fEnabled;
bigtime_t fLastUpdated;
};
/*! Used to thread safe exchange refs. While the watcher thread file the current
list the worker thread can handle the second list. The worker thread gets his entries by calling SwapList while holding the watcher thread lock. */
class SwapEntryRefVector {
public:
SwapEntryRefVector();
EntryRefVector* SwapList();
EntryRefVector* CurrentList();
private:
EntryRefVector fFirstList;
EntryRefVector fSecondList;
EntryRefVector* fCurrentList;
EntryRefVector* fNextList;
};
struct list_collection
{
EntryRefVector* createdList;
EntryRefVector* deletedList;
EntryRefVector* modifiedList;
EntryRefVector* movedList;
EntryRefVector* movedFromList;
};
/*! Watch a volume and delegate changed entries to a VolumeWorker. */
class VolumeWatcher : public VolumeWatcherBase, public BLooper {
public:
VolumeWatcher(const BVolume& volume);
~VolumeWatcher();
void MessageReceived(BMessage *message);
bool StartWatching();
void Stop();
//! thread safe
bool AddAnalyser(FileAnalyser* analyser);
bool RemoveAnalyser(const BString& name);
void GetSecureEntries(list_collection& collection);
bool FindEntryRef(ino_t node, dev_t device,
entry_ref& entry);
private:
friend class WatchNameHandler;
void _NewEntriesArrived();
bool fWatching;
WatchNameHandler fWatchNameHandler;
SwapEntryRefVector fCreatedList;
SwapEntryRefVector fDeleteList;
SwapEntryRefVector fModifiedList;
SwapEntryRefVector fMovedList;
SwapEntryRefVector fMovedFromList;
VolumeWorker* fVolumeWorker;
CatchUpManager fCatchUpManager;
ModfiedNotifications fModfiedNotifications;
};
#endif
@@ -0,0 +1,44 @@
resource app_signature "application/x-vnd.Haiku-index_server";
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_FINAL,
internal = 0,
short_info = "Index Server",
long_info = "Index Server ©2010 Haiku, Inc."
};
resource app_flags B_SINGLE_LAUNCH;
resource vector_icon {
$"6E6369660B05000400540510057802001604378852378852378852B788524B07"
$"8548705100883FFFBF88FF88020016033B1FD73CCE7EBF7EBC3DE4084BE51A48"
$"106D58E700FDFFFF020106023C80000000000000003A00004940004A800000CE"
$"CEFFFF9898FF03666698020006023E5400000000000000400000495800000000"
$"006464BAFFC7C7E005FF020006033D0000000000000000400000498000000000"
$"00D1D1EA964C4C76FF75759B0E0604FEC7E3BF763F603F60C62C605E4D5EC91C"
$"5EC2C7C7E3BF76CA3DC0D6C7E3BF760604FA503C533E5E305E30CC4DB8A05F2A"
$"602C5F2A0605EB035626582456264436513D5D2E5D2ECBFCB7AD5E256128CA9B"
$"B3E50604BF503B503B5239C66C34C73836C5A0324A334B324A3345370802C62C"
$"30C9E92702073B343B34B7D134204520BE002045204F204F20C977BDC360B7D1"
$"60C3B460564F56C977564F5645564556BE003B34C3B2343B3402053B363B36B8"
$"4136224522BE7022C50BBDC354B84254C34354C7B94554C50B54BE703B36C342"
$"363B3602053B3A3B3AC2603AC6214550BF5250C429BDC350C26150B923502645"
$"26C42926BF523B3AB9223A3B3A02053B4E3B4EC15C4EC52447C45F4BC45FC0F0"
$"3B40C15C40BA2840B66147B724C0F0B7244B3B4EBA274E3B4E0003B6A1C2E3B6"
$"A1C2E3B7DDC1B43BC0CBBAC9C0CBC0BAC0CBC4D7C2D6C399C1A8C4D7C2D60207"
$"3B3C3B3CB9933C284528BFC328C204B66147B641C248B724C0F03B40BA2740C1"
$"5C40C52447C45FC0F0C542C2484E454EC2044EBFC33B3CC1EF3C3B3C02023144"
$"2E43344535403841323F0203C230C23DC230C23DC171C316BE81C3B6C065C37C"
$"BE81C3B6C14BC395C04CC3EFC28FC32402073B543B54B8415422C1EC22C51F22"
$"C1EC22C58B22C58B22C8F23B5EB8425EC3435E54C58B54C8F254C58B54C1EC54"
$"C1EC54C51F3B54C342543B540B0A01020001000A020102000A040103000A0001"
$"05000A05020607000A060108000A0701091815FF01178200040A08010A000A09"
$"020B0C000A0301041001178320040A0A010D00"
};
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler <[email protected]>
*/
#include "IndexServer.h"
int
main()
{
IndexServer indexServer;
indexServer.Run();
return 0;
}