* Refactored a lot of repetitive code and made

everything more readible.
 * Once I understood better how everthing is supposed
   to work, I've fixed some bugs.
 * The most important problem was that AddDirectory()
   placed the wrong entry (resolved symlinks) into the
   entry list it keeps for each directory. This resulted
   in the mechanisms not working at all when an add-on
   was a symlink.
 * There was a hidden TODO, which would mean that moving
   an add-on from one watched directory into another,
   like from home/config/add-ons/... into common/add-ons/...
   would drop the client application (media_server,
   input_server, ...) into the debugger.
 * The fFormerEntries list did not seem to serve any
   purpose. Basically it would not disable add-ons
   removed from a watched directory unless it changed
   it's name at the same time. I've removed it completely,
   since it didn't seem to be an optimization
   (entry cache) either.
 * Each actual add-on file is now node-monitored for
   stat changes. So if you have a link in the add-on
   folder, and the linked to add-on changes, it triggers
   a reload of the add-on now. This will make it much
   more pleasant to develop add-ons and have them affective
   immediately.

I tested with a fresh image, but there are no immediate
regressions I am aware of. I could imagine that messing
with certain add-ons can have a bad effect now, like
removing the keyboard input_server add-on may trigger the
keyboard to stop working immediately without an input_server
restart, but Tracker should warn before it happens.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38280 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2010-08-19 19:52:19 +00:00
parent 258b762aef
commit 2faa1532f8
2 changed files with 413 additions and 299 deletions
+83 -25
View File
@@ -1,10 +1,11 @@
/*
* Copyright 2004-2008, Haiku, Inc. All rights reserved.
* Copyright 2004-2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _ADD_ON_MONITOR_HANDLER_H
#define _ADD_ON_MONITOR_HANDLER_H
#include <list>
#include "NodeMonitorHandler.h"
@@ -13,55 +14,112 @@
namespace BPrivate {
namespace Storage {
struct add_on_entry_info {
char name[B_FILE_NAME_LENGTH];
node_ref nref;
node_ref dir_nref;
node_ref addon_nref;
};
struct add_on_directory_info {
node_ref nref;
std::list<add_on_entry_info> entries;
};
class AddOnMonitorHandler : public NodeMonitorHandler {
public:
AddOnMonitorHandler(const char* name = NULL);
virtual ~AddOnMonitorHandler();
AddOnMonitorHandler(const char* name = NULL);
virtual ~AddOnMonitorHandler();
virtual void MessageReceived(BMessage* msg);
virtual void MessageReceived(BMessage* message);
// supply the add-on directories here, in the order you want them checked
virtual status_t AddDirectory(const node_ref* nref);
// Supply the add-on directories here, in the order you want them checked.
// Add-ons in directories added earlier will shadow add-ons in directories
// added later, if they share the same file name. If an add-on is removed
// from or renamed in a directory and it has previously shadowed another
// add-on, the previously shadowed add-on shall become enabled
// (AddOnEnabled()). If an add-on appears in a directory, or is renamed,
// it can cause another add-on to become disabled, if it has the same name.
// Note that directories are not watched recursively, and all entries
// are reported as add-ons regardless of their node type (files,
// directories, symlinks).
virtual status_t AddDirectory(const node_ref* nref);
protected:
// hooks for subclass
virtual void AddOnCreated(const add_on_entry_info* entryInfo);
virtual void AddOnEnabled(const add_on_entry_info* entryInfo);
virtual void AddOnDisabled(const add_on_entry_info* entryInfo);
virtual void AddOnRemoved(const add_on_entry_info* entryInfo);
// hooks for sub-class
virtual void AddOnCreated(
const add_on_entry_info* entryInfo);
virtual void AddOnEnabled(
const add_on_entry_info* entryInfo);
virtual void AddOnDisabled(
const add_on_entry_info* entryInfo);
// name field will be invalid!
virtual void AddOnRemoved(
const add_on_entry_info* entryInfo);
// name field will be invalid!
protected:
virtual void EntryCreated(const char* name, ino_t directory,
dev_t device, ino_t node);
virtual void EntryRemoved(ino_t directory, dev_t device, ino_t node);
virtual void EntryMoved(const char* name, ino_t fromDirectory,
ino_t toDirectory, dev_t device, ino_t node);
virtual void EntryCreated(const char* name, ino_t directory,
dev_t device, ino_t node);
virtual void EntryRemoved(ino_t directory, dev_t device,
ino_t node);
virtual void EntryMoved(const char* name,
ino_t fromDirectory, ino_t toDirectory,
dev_t device, ino_t node);
virtual void StatChanged(ino_t node, dev_t device);
private:
typedef NodeMonitorHandler inherited;
void _HandlePulse();
void _EntryCreated(add_on_entry_info& info);
void _HandlePulse();
typedef NodeMonitorHandler inherited;
typedef std::list<add_on_entry_info> EntryList;
std::list<add_on_directory_info> fDirectories;
std::list<add_on_entry_info> fPendingEntries;
std::list<add_on_entry_info> fFormerEntries;
struct add_on_directory_info {
node_ref nref;
EntryList entries;
};
typedef std::list<add_on_directory_info> DirectoryList;
bool _FindEntry(const node_ref& entry,
const EntryList& list,
EntryList::iterator& it) const;
bool _FindEntry(const char* name,
const EntryList& list,
EntryList::iterator& it) const;
bool _HasEntry(const node_ref& entry,
EntryList& list) const;
bool _HasEntry(const char* name,
EntryList& list) const;
bool _FindDirectory(ino_t directory, dev_t device,
DirectoryList::iterator& it) const;
bool _FindDirectory(
const node_ref& directoryNodeRef,
DirectoryList::iterator& it) const;
bool _FindDirectory(ino_t directory, dev_t device,
DirectoryList::iterator& it,
const DirectoryList::const_iterator& end)
const;
bool _FindDirectory(
const node_ref& directoryNodeRef,
DirectoryList::iterator& it,
const DirectoryList::const_iterator& end)
const;
void _AddNewEntry(EntryList& list,
add_on_entry_info& info);
private:
DirectoryList fDirectories;
EntryList fPendingEntries;
};
}; // namespace Storage
}; // namespace BPrivate
using namespace BPrivate::Storage;
#endif // _ADD_ON_MONITOR_HANDLER_H
+330 -274
View File
@@ -4,6 +4,7 @@
*
* Authors:
* Andrew Bachmann
* Stephan Aßmus <[email protected]>
*/
@@ -29,6 +30,16 @@ AddOnMonitorHandler::AddOnMonitorHandler(const char* name)
AddOnMonitorHandler::~AddOnMonitorHandler()
{
// TODO: Actually calling watch_node() here should be too late, since we
// are likely not attached to a looper anymore, and thus consitute no valid
// BMessenger at this time.
DirectoryList::iterator it = fDirectories.begin();
for (; it != fDirectories.end(); it++) {
EntryList::iterator eiter = it->entries.begin();
for (; eiter != it->entries.end(); eiter++)
watch_node(&eiter->addon_nref, B_STOP_WATCHING, this);
watch_node(&it->nref, B_STOP_WATCHING, this);
}
}
@@ -47,16 +58,17 @@ AddOnMonitorHandler::AddDirectory(const node_ref* nref)
{
// Keep the looper thread locked, since this method is likely to be called
// in a thread other than the looper thread. Otherwise we may access the
// lists concurrently with the looper thread, when node monitor notifications
// arrive while we are still adding initial entries from the directory, or
// (much more likely) if the looper thread handles a pulse message and wants
// to process pending entries while we are still adding them.
// lists concurrently with the looper thread, when node monitor
// notifications arrive while we are still adding initial entries from the
// directory, or (much more likely) if the looper thread handles a pulse
// message and wants to process pending entries while we are still adding
// them.
BAutolock _(Looper());
// ignore directories added twice
std::list<add_on_directory_info>::iterator iterator = fDirectories.begin();
for (; iterator != fDirectories.end() ; iterator++) {
if (iterator->nref == *nref)
DirectoryList::iterator it = fDirectories.begin();
for (; it != fDirectories.end(); it++) {
if (it->nref == *nref)
return B_OK;
}
@@ -65,25 +77,24 @@ AddOnMonitorHandler::AddDirectory(const node_ref* nref)
if (status != B_OK)
return status;
status = watch_node(nref, B_WATCH_DIRECTORY, this);
if (status != B_OK)
return status;
add_on_directory_info dirInfo;
dirInfo.nref = *nref;
fDirectories.push_back(dirInfo);
status = watch_node(nref, B_WATCH_DIRECTORY, this);
if (status != B_OK) {
fDirectories.pop_back();
return status;
}
add_on_entry_info entryInfo;
entryInfo.dir_nref = *nref;
BEntry entry;
while (directory.GetNextEntry(&entry, true) == B_OK) {
add_on_entry_info entryInfo;
while (directory.GetNextEntry(&entry) == B_OK) {
if (entry.GetName(entryInfo.name) != B_OK
|| entry.GetNodeRef(&entryInfo.nref) != B_OK) {
continue;
}
entryInfo.dir_nref = *nref;
fPendingEntries.push_back(entryInfo);
}
@@ -143,70 +154,55 @@ AddOnMonitorHandler::EntryRemoved(ino_t directory, dev_t device, ino_t node)
// Search pending entries first, which can simply be discarded
// We might have this entry in the pending list multiple times,
// so we search entire list through, even after finding one.
std::list<add_on_entry_info>::iterator eiter = fPendingEntries.begin();
EntryList::iterator eiter = fPendingEntries.begin();
while (eiter != fPendingEntries.end()) {
if (eiter->nref == entryNodeRef) {
if (eiter->nref == entryNodeRef)
eiter = fPendingEntries.erase(eiter);
} else {
else
eiter++;
}
}
add_on_entry_info info;
node_ref dirNodeRef;
make_node_ref(device, directory, &dirNodeRef);
// find the entry's info, and the entry's directory info
std::list<add_on_directory_info>::iterator diter = fDirectories.begin();
for (; diter != fDirectories.end() ; diter++) {
if (diter->nref == dirNodeRef) {
std::list<add_on_entry_info>::iterator eiter
= diter->entries.begin();
for (; eiter != diter->entries.end() ; eiter++) {
info = *eiter;
if (eiter->nref == entryNodeRef) {
info = *eiter;
diter->entries.erase(eiter);
break;
}
}
break;
}
}
// if it was not found, we're done
if (diter == fDirectories.end())
// Find the directory of the entry.
DirectoryList::iterator diter = fDirectories.begin();
if (!_FindDirectory(directory, device, diter)) {
// If it was not found, we're done
return;
}
eiter = diter->entries.begin();
if (!_FindEntry(entryNodeRef, diter->entries, eiter)) {
// This must be the directory, but we didn't find the entry.
return;
}
add_on_entry_info info = *eiter;
watch_node(&entryNodeRef, B_STOP_WATCHING, this);
diter->entries.erase(eiter);
// Start at the top again, and search until the directory we found
// the old add-on in. If we find an add-on with the same name then
// the old add-on was not enabled. So we deallocate the old
// add-on and return.
std::list<add_on_directory_info>::iterator diter2 = fDirectories.begin();
for (; diter2 != diter ; diter2++) {
std::list<add_on_entry_info>::iterator eiter = diter2->entries.begin();
for (; eiter != diter2->entries.end() ; eiter++) {
if (strcmp(eiter->name, info.name) == 0) {
AddOnRemoved(&info);
return;
}
// the old add-on was not enabled. So we deallocate the old add-on and
// return.
DirectoryList::iterator diter2 = fDirectories.begin();
for (; diter2 != diter; diter2++) {
if (_HasEntry(info.name, diter2->entries)) {
AddOnRemoved(&info);
return;
}
}
// The active add-on was removed. We need to disable and
// then subsequently deallocate it.
// An active add-on was removed. We need to disable and then subsequently
// deallocate it.
AddOnDisabled(&info);
AddOnRemoved(&info);
// Continue searching for an add-on below us. If we find an add-on
// with the same name, we must enable it.
for (diter++ ; diter != fDirectories.end() ; diter++) {
std::list<add_on_entry_info>::iterator eiter = diter->entries.begin();
for (; eiter != diter->entries.end() ; eiter++) {
if (strcmp(eiter->name, info.name) == 0) {
AddOnEnabled(&*eiter);
return;
}
for (diter++; diter != fDirectories.end(); diter++) {
eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter)) {
AddOnEnabled(&*eiter);
break;
}
}
}
@@ -216,69 +212,49 @@ void
AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory,
ino_t toDirectory, dev_t device, ino_t node)
{
// Search the "from" directory in the known entries
node_ref fromNodeRef;
make_node_ref(device, fromDirectory, &fromNodeRef);
std::list<add_on_directory_info>::iterator from_iter = fDirectories.begin();
for (; from_iter != fDirectories.end(); from_iter++) {
if (from_iter->nref == fromNodeRef)
break;
}
// Search the "to" directory in the known entries
node_ref toNodeRef;
make_node_ref(device, toDirectory, &toNodeRef);
std::list<add_on_directory_info>::iterator to_iter = fDirectories.begin();
for ( ; to_iter != fDirectories.end() ; to_iter++) {
if (to_iter->nref == toNodeRef)
break;
}
if (from_iter == fDirectories.end() && to_iter == fDirectories.end()) {
// Search the "from" and "to" directory in the known directories
DirectoryList::iterator from_iter = fDirectories.begin();
bool watchingFromDirectory = _FindDirectory(fromDirectory, device,
from_iter);
DirectoryList::iterator to_iter = fDirectories.begin();
bool watchingToDirectory = _FindDirectory(toNodeRef, to_iter);
if (!watchingFromDirectory && !watchingToDirectory) {
// It seems the notification was for a directory we are not
// actually watching by intention.
// actually watching (at least not by intention).
return;
}
add_on_entry_info info;
node_ref entryNodeRef;
make_node_ref(device, node, &entryNodeRef);
if (to_iter == fDirectories.end()) {
if (!watchingToDirectory) {
// moved out of our view
std::list<add_on_entry_info>::iterator eiter
= from_iter->entries.begin();
for (; eiter != from_iter->entries.end() ; eiter++) {
if (entryNodeRef == eiter->nref) {
// save the info and remove the entry
info = *eiter;
from_iter->entries.erase(eiter);
break;
}
}
if (eiter == from_iter->entries.end()) {
EntryList::iterator eiter = from_iter->entries.begin();
if (!_FindEntry(entryNodeRef, from_iter->entries, eiter)) {
// we don't know anything about this entry yet.. ignore it
return;
}
// if the name is the same, save the information about this
// add-on into fFormerEntries for later use
if (strcmp(info.name, name) == 0)
fFormerEntries.push_back(info);
// save the info and remove the entry
info = *eiter;
watch_node(&entryNodeRef, B_STOP_WATCHING, this);
from_iter->entries.erase(eiter);
// Start at the top again, and search until the from directory.
// If we find a add-on with the same name then the moved add-on
// was not enabled. So we are done.
std::list<add_on_directory_info>::iterator diter = fDirectories.begin();
for (; diter != from_iter ; diter++) {
std::list<add_on_entry_info>::iterator eiter2
= diter->entries.begin();
for (; eiter2 != diter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
return;
}
}
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != from_iter; diter++) {
eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter))
return;
}
// finally disable the add-on
@@ -286,141 +262,110 @@ AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory,
// Continue searching for a add-on below us. If we find a add-on
// with the same name, we must enable it.
for (from_iter++ ; from_iter != fDirectories.end() ; from_iter++) {
std::list<add_on_entry_info>::iterator eiter2
= from_iter->entries.begin();
for (; eiter2 != from_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
AddOnEnabled(&*eiter2);
return;
}
for (from_iter++; from_iter != fDirectories.end(); from_iter++) {
eiter = from_iter->entries.begin();
if (_FindEntry(info.name, from_iter->entries, eiter)) {
AddOnEnabled(&*eiter);
return;
}
}
// finally, if the new name is different, destroy the addon
if (strcmp(info.name, name) != 0)
AddOnRemoved(&info);
// finally destroy the addon
AddOnRemoved(&info);
// done
return;
}
if (from_iter == fDirectories.end()) {
if (!watchingFromDirectory) {
// moved into our view
std::list<add_on_entry_info>::iterator eiter = fFormerEntries.begin();
for (; eiter != fFormerEntries.end() ; eiter++) {
if (entryNodeRef == eiter->nref) {
// save the info and remove the entry
info = *eiter;
fFormerEntries.erase(eiter);
break;
}
}
if (eiter != fFormerEntries.end()) {
if (strcmp(info.name, name) != 0) {
// name changed on the way in, remove the old one
AddOnRemoved(&info);
}
}
// update the info
strncpy(info.name, name, sizeof(info.name));
info.nref = entryNodeRef;
info.dir_nref = toNodeRef;
if (eiter == fFormerEntries.end()) {
// this add-on was not seen before
AddOnCreated(&info);
}
AddOnCreated(&info);
// Start at the top again, and search until the to directory.
// If we find an add-on with the same name then the moved add-on
// is not to be enabled. So we are done.
std::list<add_on_directory_info>::iterator diter = fDirectories.begin();
for (; diter != to_iter ; diter++) {
std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin();
for (; eiter2 != diter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0)
return;
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != to_iter; diter++) {
if (_HasEntry(info.name, diter->entries)) {
// The new add-on is being shadowed.
return;
}
}
// The new add-on should be enabled, but first we check to see
// if there is an add-on below us. If we find one, we disable it.
bool shadowing = false;
for (diter++ ; diter != fDirectories.end() ; diter++) {
std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin();
for (; eiter2 != diter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
AddOnDisabled(&*eiter2);
shadowing = true;
break;
}
}
if (shadowing)
for (diter++ ; diter != fDirectories.end(); diter++) {
EntryList::iterator eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter)) {
AddOnDisabled(&*eiter);
break;
}
}
// enable the new add-on
AddOnEnabled(&info);
// put the new entry into the target directory
to_iter->entries.push_back(info);
_AddNewEntry(to_iter->entries, info);
// done
return;
}
std::list<add_on_entry_info>::iterator eiter = from_iter->entries.begin();
for (; eiter != from_iter->entries.end() ; eiter++) {
if (entryNodeRef == eiter->nref) {
// save the old info and remove the entry
info = *eiter;
from_iter->entries.erase(eiter);
break;
}
// The add-on was renamed, or moved within our hierarchy.
EntryList::iterator eiter = from_iter->entries.begin();
if (_FindEntry(entryNodeRef, from_iter->entries, eiter)) {
// save the old info and remove the entry
info = *eiter;
} else {
// If an entry moved from one watched directory into another watched
// directory, there will be two notifications, and this may be the
// second. We have handled everything in the first. In that case the
// entry was already removed from the fromDirectory and added in the
// toDirectory list.
return;
}
if (strcmp(info.name, name) == 0) {
// same name moved in heirarchy
debugger("add-on moved inside the heirarchy");
// It should be impossible for the name to stay the same, unless the
// node moved in the watched hierarchy. Handle this case by removing
// the entry and readding it. TODO: This can temporarily enable add-ons
// which should in fact stay hidden (moving add-on from home to common
// folder or vice versa, the system add-on should remain hidden).
EntryRemoved(fromDirectory, device, node);
info.dir_nref = toNodeRef;
_EntryCreated(info);
} else {
// Erase the entry
from_iter->entries.erase(eiter);
// check to see if it was formerly enabled
bool was_enabled = true;
std::list<add_on_directory_info>::iterator old_iter
= fDirectories.begin();
for (; old_iter != from_iter ; old_iter++) {
std::list<add_on_entry_info>::iterator eiter2
= old_iter->entries.begin();
for (; eiter2 != old_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
was_enabled = false;
break;
}
}
if (!was_enabled) {
bool wasEnabled = true;
DirectoryList::iterator old_iter = fDirectories.begin();
for (; old_iter != from_iter; old_iter++) {
if (_HasEntry(info.name, old_iter->entries)) {
wasEnabled = false;
break;
}
}
// if it was enabled, disable it and enable the one under us, if it
// exists
if (was_enabled) {
// If it was enabled, disable it and enable the one under us, if it
// exists.
if (wasEnabled) {
AddOnDisabled(&info);
bool done = false;
for (; old_iter != fDirectories.end() ; old_iter++) {
std::list<add_on_entry_info>::iterator eiter2
= old_iter->entries.begin();
for (; eiter2 != old_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
AddOnEnabled(&*eiter2);
done = true;
break;
}
}
if (done)
for (; old_iter != fDirectories.end(); old_iter++) {
eiter = old_iter->entries.begin();
if (_FindEntry(info.name, old_iter->entries, eiter)) {
AddOnEnabled(&*eiter);
break;
}
}
}
@@ -436,56 +381,75 @@ AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory,
// check to see if we are newly enabled
bool isEnabled = true;
std::list<add_on_directory_info>::iterator new_iter
= fDirectories.begin();
for (; new_iter != to_iter ; new_iter++) {
std::list<add_on_entry_info>::iterator eiter2
= new_iter->entries.begin();
for (; eiter2 != new_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
isEnabled = false;
break;
}
}
if (!isEnabled)
DirectoryList::iterator new_iter = fDirectories.begin();
for (; new_iter != to_iter; new_iter++) {
if (_HasEntry(info.name, new_iter->entries)) {
isEnabled = false;
break;
}
}
// if it is newly enabled, check under us for an enabled one, and
// disable that first
if (isEnabled) {
bool done = false;
for (; new_iter != fDirectories.end() ; new_iter++) {
std::list<add_on_entry_info>::iterator eiter2
= new_iter->entries.begin();
for (; eiter2 != new_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
AddOnDisabled(&*eiter2);
done = true;
break;
}
}
if (done)
for (; new_iter != fDirectories.end(); new_iter++) {
eiter = new_iter->entries.begin();
if (_FindEntry(info.name, new_iter->entries, eiter)) {
AddOnDisabled(&*eiter);
break;
}
}
AddOnEnabled(&info);
}
// put the new entry into the target directory
to_iter->entries.push_back(info);
}
// put the new entry into the target directory
to_iter->entries.push_back(info);
}
void
AddOnMonitorHandler::StatChanged(ino_t node, dev_t device)
{
// This notification is received for the add-ons themselves.
// TODO: Add the entry to the pending list, disable/enable it
// when the modification time remains stable.
node_ref entryNodeRef;
make_node_ref(device, node, &entryNodeRef);
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != fDirectories.end(); diter++) {
EntryList::iterator eiter = diter->entries.begin();
for (; eiter != diter->entries.end(); eiter++) {
if (eiter->addon_nref == entryNodeRef) {
// Trigger reloading of the add-on
const add_on_entry_info* info = &*eiter;
AddOnDisabled(info);
AddOnRemoved(info);
AddOnCreated(info);
AddOnEnabled(info);
return;
}
}
}
}
// #pragma mark - private
//! Process pending entries.
void
AddOnMonitorHandler::_HandlePulse()
{
BDirectory directory;
std::list<add_on_entry_info>::iterator iter = fPendingEntries.begin();
EntryList::iterator iter = fPendingEntries.begin();
while (iter != fPendingEntries.end()) {
add_on_entry_info info = *iter;
// Initialize directory, or re-use from previous iteration, if
// directory node_ref remained the same from the last pending entry.
node_ref dirNodeRef;
if (directory.GetNodeRef(&dirNodeRef) != B_OK
|| dirNodeRef != info.dir_nref) {
@@ -514,58 +478,150 @@ AddOnMonitorHandler::_HandlePulse()
// we are going to deal with the stable entry, so remove it
iter = fPendingEntries.erase(iter);
// put the new entry into the directory info
std::list<add_on_directory_info>::iterator diter = fDirectories.begin();
for (; diter != fDirectories.end() ; diter++) {
if (diter->nref == dirNodeRef) {
diter->entries.push_back(info);
break;
}
}
// report it
AddOnCreated(&info);
// Start at the top again, and search until the directory we put
// the new add-on in. If we find an add-on with the same name then
// the new add-on should not be enabled.
bool enabled = true;
std::list<add_on_directory_info>::iterator diter2
= fDirectories.begin();
for (; diter2 != diter ; diter2++) {
std::list<add_on_entry_info>::iterator eiter
= diter2->entries.begin();
for (; eiter != diter2->entries.end() ; eiter++) {
if (strcmp(eiter->name, info.name) == 0) {
enabled = false;
break;
}
}
if (!enabled)
break;
}
if (!enabled) {
// if we are not enabled, go on to the next pending entry
continue;
}
// The new add-on should be enabled, but first we check to see
// if there is an add-on below us. If we find one, we disable it.
bool shadowing = false;
for (diter++ ; diter != fDirectories.end() ; diter++) {
std::list<add_on_entry_info>::iterator eiter = diter->entries.begin();
for (; eiter != diter->entries.end() ; eiter++) {
if (strcmp(eiter->name, info.name) == 0) {
AddOnDisabled(&*eiter);
shadowing = true;
break;
}
}
if (shadowing)
break;
}
// finally, enable the new entry
AddOnEnabled(&info);
_EntryCreated(info);
}
}
void
AddOnMonitorHandler::_EntryCreated(add_on_entry_info& info)
{
// put the new entry into the directory info
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != fDirectories.end(); diter++) {
if (diter->nref == info.dir_nref) {
_AddNewEntry(diter->entries, info);
break;
}
}
// report it
AddOnCreated(&info);
// Start at the top again, and search until the directory we put
// the new add-on in. If we find an add-on with the same name then
// the new add-on should not be enabled.
bool enabled = true;
DirectoryList::iterator diter2 = fDirectories.begin();
for (; diter2 != diter; diter2++) {
if (_HasEntry(info.name, diter2->entries)) {
enabled = false;
break;
}
}
if (!enabled)
return;
// The new add-on should be enabled, but first we check to see
// if there is an add-on shadowed by the new one. If we find one,
// we disable it.
for (diter++ ; diter != fDirectories.end(); diter++) {
EntryList::iterator eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter)) {
AddOnDisabled(&*eiter);
break;
}
}
// enable the new entry
AddOnEnabled(&info);
}
bool
AddOnMonitorHandler::_FindEntry(const node_ref& entry, const EntryList& list,
EntryList::iterator& it) const
{
for (; it != list.end(); it++) {
if (it->nref == entry)
return true;
}
return false;
}
bool
AddOnMonitorHandler::_FindEntry(const char* name, const EntryList& list,
EntryList::iterator& it) const
{
for (; it != list.end(); it++) {
if (strcmp(it->name, name) == 0)
return true;
}
return false;
}
bool
AddOnMonitorHandler::_HasEntry(const node_ref& entry, EntryList& list) const
{
EntryList::iterator it = list.begin();
return _FindEntry(entry, list, it);
}
bool
AddOnMonitorHandler::_HasEntry(const char* name, EntryList& list) const
{
EntryList::iterator it = list.begin();
return _FindEntry(name, list, it);
}
bool
AddOnMonitorHandler::_FindDirectory(ino_t directory, dev_t device,
DirectoryList::iterator& it) const
{
node_ref nodeRef;
make_node_ref(device, directory, &nodeRef);
return _FindDirectory(nodeRef, it, fDirectories.end());
}
bool
AddOnMonitorHandler::_FindDirectory(const node_ref& directoryNodeRef,
DirectoryList::iterator& it) const
{
return _FindDirectory(directoryNodeRef, it, fDirectories.end());
}
bool
AddOnMonitorHandler::_FindDirectory(ino_t directory, dev_t device,
DirectoryList::iterator& it,
const DirectoryList::const_iterator& end) const
{
node_ref nodeRef;
make_node_ref(device, directory, &nodeRef);
return _FindDirectory(nodeRef, it, end);
}
bool
AddOnMonitorHandler::_FindDirectory(const node_ref& directoryNodeRef,
DirectoryList::iterator& it,
const DirectoryList::const_iterator& end) const
{
for (; it != end; it++) {
if (it->nref == directoryNodeRef)
return true;
}
return false;
}
void
AddOnMonitorHandler::_AddNewEntry(EntryList& list, add_on_entry_info& info)
{
BDirectory directory(&info.dir_nref);
BEntry entry(&directory, info.name, true);
node_ref addOnRef;
if (entry.GetNodeRef(&addOnRef) == B_OK) {
watch_node(&addOnRef, B_WATCH_STAT, this);
info.addon_nref = addOnRef;
} else
info.addon_nref = info.nref;
list.push_back(info);
}