diff --git a/headers/private/index_server/IndexServerAddOn.h b/headers/private/index_server/IndexServerAddOn.h new file mode 100644 index 0000000000..7c8f5fc5a1 --- /dev/null +++ b/headers/private/index_server/IndexServerAddOn.h @@ -0,0 +1,136 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef INDEX_SERVER_ADD_ON_H +#define INDEX_SERVER_ADD_ON_H + + +#include +#include +#include +#include +#include +#include + +#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 fAnalyserSettings; + analyser_settings fCachedSettings; +private: + BString fName; +}; + + +typedef BObjectList 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 diff --git a/headers/private/index_server/IndexServerPrivate.h b/headers/private/index_server/IndexServerPrivate.h new file mode 100644 index 0000000000..44c74015e8 --- /dev/null +++ b/headers/private/index_server/IndexServerPrivate.h @@ -0,0 +1,34 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef INDEX_SERVER_PRIVATE_H +#define INDEX_SERVER_PRIVATE_H + + +#include + + +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 diff --git a/src/servers/index_server/AnalyserDispatcher.h b/src/servers/index_server/AnalyserDispatcher.h new file mode 100644 index 0000000000..6e90ceef4a --- /dev/null +++ b/src/servers/index_server/AnalyserDispatcher.h @@ -0,0 +1,55 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef ANALYSER_DISPATCHER +#define ANALYSER_DISPATCHER + + +#include +#include + +#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 diff --git a/src/servers/index_server/CatchUpManager.cpp b/src/servers/index_server/CatchUpManager.cpp new file mode 100644 index 0000000000..72985ce800 --- /dev/null +++ b/src/servers/index_server/CatchUpManager.cpp @@ -0,0 +1,248 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "CatchUpManager.h" + +#include + +#include +#include + +#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 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(); +} diff --git a/src/servers/index_server/CatchUpManager.h b/src/servers/index_server/CatchUpManager.h new file mode 100644 index 0000000000..b97851a9ae --- /dev/null +++ b/src/servers/index_server/CatchUpManager.h @@ -0,0 +1,77 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef CATCH_UP_MANAGER_H +#define CATCH_UP_MANAGER_H + + +#include "AnalyserDispatcher.h" + + +#define DEBUG_CATCH_UP +#ifdef DEBUG_CATCH_UP +#include +# 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 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 diff --git a/src/servers/index_server/IndexServer.cpp b/src/servers/index_server/IndexServer.cpp new file mode 100644 index 0000000000..a4c7a925bd --- /dev/null +++ b/src/servers/index_server/IndexServer.cpp @@ -0,0 +1,364 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "IndexServer.h" + +#include +#include +#include +#include +#include + +#include + + +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 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; +} diff --git a/src/servers/index_server/IndexServer.h b/src/servers/index_server/IndexServer.h new file mode 100644 index 0000000000..b3487ca20f --- /dev/null +++ b/src/servers/index_server/IndexServer.h @@ -0,0 +1,100 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef INDEX_SERVER_H +#define INDEX_SERVER_H + + +#include +#include +#include + +#include +#include + +#include "IndexServerAddOn.h" +#include "VolumeWatcher.h" + + +#define DEBUG_INDEX_SERVER +#ifdef DEBUG_INDEX_SERVER +#include +# 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 fVolumeWatcherList; + BObjectList fAddOnList; + + VolumeObserverHandler fVolumeObserverHandler; + + AnalyserMonitorHandler fAddOnMonitorHandler; + BMessageRunner* fPulseRunner; +}; + + +#endif diff --git a/src/servers/index_server/Jamfile b/src/servers/index_server/Jamfile new file mode 100644 index 0000000000..d2c4b4ecd1 --- /dev/null +++ b/src/servers/index_server/Jamfile @@ -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 ] ; diff --git a/src/servers/index_server/ModifiedNotifications.cpp b/src/servers/index_server/ModifiedNotifications.cpp new file mode 100644 index 0000000000..a2412092cc --- /dev/null +++ b/src/servers/index_server/ModifiedNotifications.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "ModifiedNotifications.h" + +#include "fs_query.h" + +#include +#include + +#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(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(); +} diff --git a/src/servers/index_server/ModifiedNotifications.h b/src/servers/index_server/ModifiedNotifications.h new file mode 100644 index 0000000000..50cd1b2f8e --- /dev/null +++ b/src/servers/index_server/ModifiedNotifications.h @@ -0,0 +1,44 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef MODIFIED_NOTIFICATIONS_H +#define MODIFIED_NOTIFICATIONS_H + + +#include +#include +#include + + +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 diff --git a/src/servers/index_server/VolumeWatcher.cpp b/src/servers/index_server/VolumeWatcher.cpp new file mode 100644 index 0000000000..ced593e6f9 --- /dev/null +++ b/src/servers/index_server/VolumeWatcher.cpp @@ -0,0 +1,541 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "VolumeWatcher.h" + +#include + +#include +#include +#include +#include +#include + + +#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); +} diff --git a/src/servers/index_server/VolumeWatcher.h b/src/servers/index_server/VolumeWatcher.h new file mode 100644 index 0000000000..dc5e7e1fc4 --- /dev/null +++ b/src/servers/index_server/VolumeWatcher.h @@ -0,0 +1,164 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef VOLUME_WATCHER_H +#define VOLUME_WATCHER_H + + +#include + +#include +#include +#include +#include + +#include + +#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 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 diff --git a/src/servers/index_server/index_server.rdef b/src/servers/index_server/index_server.rdef new file mode 100644 index 0000000000..8eace6b4ea --- /dev/null +++ b/src/servers/index_server/index_server.rdef @@ -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" +}; + diff --git a/src/servers/index_server/main.cpp b/src/servers/index_server/main.cpp new file mode 100644 index 0000000000..d3e38b0efc --- /dev/null +++ b/src/servers/index_server/main.cpp @@ -0,0 +1,18 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "IndexServer.h" + + +int +main() +{ + IndexServer indexServer; + indexServer.Run(); + return 0; +}