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:
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
¶meterLength) == 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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user