* 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. * Distributed under the terms of the MIT License.
*/ */
#ifndef _ADD_ON_MONITOR_HANDLER_H #ifndef _ADD_ON_MONITOR_HANDLER_H
#define _ADD_ON_MONITOR_HANDLER_H #define _ADD_ON_MONITOR_HANDLER_H
#include <list> #include <list>
#include "NodeMonitorHandler.h" #include "NodeMonitorHandler.h"
@@ -13,55 +14,112 @@
namespace BPrivate { namespace BPrivate {
namespace Storage { namespace Storage {
struct add_on_entry_info { struct add_on_entry_info {
char name[B_FILE_NAME_LENGTH]; char name[B_FILE_NAME_LENGTH];
node_ref nref; node_ref nref;
node_ref dir_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 { class AddOnMonitorHandler : public NodeMonitorHandler {
public: public:
AddOnMonitorHandler(const char* name = NULL); AddOnMonitorHandler(const char* name = NULL);
virtual ~AddOnMonitorHandler(); 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 // Supply the add-on directories here, in the order you want them checked.
virtual status_t AddDirectory(const node_ref* nref); // 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: protected:
// hooks for subclass // hooks for sub-class
virtual void AddOnCreated(const add_on_entry_info* entryInfo); virtual void AddOnCreated(
virtual void AddOnEnabled(const add_on_entry_info* entryInfo); const add_on_entry_info* entryInfo);
virtual void AddOnDisabled(const add_on_entry_info* entryInfo); virtual void AddOnEnabled(
virtual void AddOnRemoved(const add_on_entry_info* entryInfo); 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: protected:
virtual void EntryCreated(const char* name, ino_t directory, virtual void EntryCreated(const char* name, ino_t directory,
dev_t device, ino_t node); dev_t device, ino_t node);
virtual void EntryRemoved(ino_t directory, dev_t device, ino_t node); virtual void EntryRemoved(ino_t directory, dev_t device,
virtual void EntryMoved(const char* name, ino_t fromDirectory, ino_t node);
ino_t toDirectory, 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: 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; struct add_on_directory_info {
std::list<add_on_entry_info> fPendingEntries; node_ref nref;
std::list<add_on_entry_info> fFormerEntries; 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 Storage
}; // namespace BPrivate }; // namespace BPrivate
using namespace BPrivate::Storage; using namespace BPrivate::Storage;
#endif // _ADD_ON_MONITOR_HANDLER_H #endif // _ADD_ON_MONITOR_HANDLER_H
+330 -274
View File
@@ -4,6 +4,7 @@
* *
* Authors: * Authors:
* Andrew Bachmann * Andrew Bachmann
* Stephan Aßmus <[email protected]>
*/ */
@@ -29,6 +30,16 @@ AddOnMonitorHandler::AddOnMonitorHandler(const char* name)
AddOnMonitorHandler::~AddOnMonitorHandler() 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 // 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 // in a thread other than the looper thread. Otherwise we may access the
// lists concurrently with the looper thread, when node monitor notifications // lists concurrently with the looper thread, when node monitor
// arrive while we are still adding initial entries from the directory, or // notifications arrive while we are still adding initial entries from the
// (much more likely) if the looper thread handles a pulse message and wants // directory, or (much more likely) if the looper thread handles a pulse
// to process pending entries while we are still adding them. // message and wants to process pending entries while we are still adding
// them.
BAutolock _(Looper()); BAutolock _(Looper());
// ignore directories added twice // ignore directories added twice
std::list<add_on_directory_info>::iterator iterator = fDirectories.begin(); DirectoryList::iterator it = fDirectories.begin();
for (; iterator != fDirectories.end() ; iterator++) { for (; it != fDirectories.end(); it++) {
if (iterator->nref == *nref) if (it->nref == *nref)
return B_OK; return B_OK;
} }
@@ -65,25 +77,24 @@ AddOnMonitorHandler::AddDirectory(const node_ref* nref)
if (status != B_OK) if (status != B_OK)
return status; return status;
status = watch_node(nref, B_WATCH_DIRECTORY, this);
if (status != B_OK)
return status;
add_on_directory_info dirInfo; add_on_directory_info dirInfo;
dirInfo.nref = *nref; dirInfo.nref = *nref;
fDirectories.push_back(dirInfo); fDirectories.push_back(dirInfo);
status = watch_node(nref, B_WATCH_DIRECTORY, this); add_on_entry_info entryInfo;
if (status != B_OK) { entryInfo.dir_nref = *nref;
fDirectories.pop_back();
return status;
}
BEntry entry; BEntry entry;
while (directory.GetNextEntry(&entry, true) == B_OK) { while (directory.GetNextEntry(&entry) == B_OK) {
add_on_entry_info entryInfo;
if (entry.GetName(entryInfo.name) != B_OK if (entry.GetName(entryInfo.name) != B_OK
|| entry.GetNodeRef(&entryInfo.nref) != B_OK) { || entry.GetNodeRef(&entryInfo.nref) != B_OK) {
continue; continue;
} }
entryInfo.dir_nref = *nref;
fPendingEntries.push_back(entryInfo); 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 // Search pending entries first, which can simply be discarded
// We might have this entry in the pending list multiple times, // We might have this entry in the pending list multiple times,
// so we search entire list through, even after finding one. // 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()) { while (eiter != fPendingEntries.end()) {
if (eiter->nref == entryNodeRef) { if (eiter->nref == entryNodeRef)
eiter = fPendingEntries.erase(eiter); eiter = fPendingEntries.erase(eiter);
} else { else
eiter++; eiter++;
}
} }
add_on_entry_info info; // Find the directory of the entry.
node_ref dirNodeRef; DirectoryList::iterator diter = fDirectories.begin();
make_node_ref(device, directory, &dirNodeRef); if (!_FindDirectory(directory, device, diter)) {
// If it was not found, we're done
// 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())
return; 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 // 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 in. If we find an add-on with the same name then
// the old add-on was not enabled. So we deallocate the old // the old add-on was not enabled. So we deallocate the old add-on and
// add-on and return. // return.
std::list<add_on_directory_info>::iterator diter2 = fDirectories.begin(); DirectoryList::iterator diter2 = fDirectories.begin();
for (; diter2 != diter ; diter2++) { for (; diter2 != diter; diter2++) {
std::list<add_on_entry_info>::iterator eiter = diter2->entries.begin(); if (_HasEntry(info.name, diter2->entries)) {
for (; eiter != diter2->entries.end() ; eiter++) { AddOnRemoved(&info);
if (strcmp(eiter->name, info.name) == 0) { return;
AddOnRemoved(&info);
return;
}
} }
} }
// The active add-on was removed. We need to disable and // An active add-on was removed. We need to disable and then subsequently
// then subsequently deallocate it. // deallocate it.
AddOnDisabled(&info); AddOnDisabled(&info);
AddOnRemoved(&info); AddOnRemoved(&info);
// Continue searching for an add-on below us. If we find an add-on // Continue searching for an add-on below us. If we find an add-on
// with the same name, we must enable it. // with the same name, we must enable it.
for (diter++ ; diter != fDirectories.end() ; diter++) { for (diter++; diter != fDirectories.end(); diter++) {
std::list<add_on_entry_info>::iterator eiter = diter->entries.begin(); eiter = diter->entries.begin();
for (; eiter != diter->entries.end() ; eiter++) { if (_FindEntry(info.name, diter->entries, eiter)) {
if (strcmp(eiter->name, info.name) == 0) { AddOnEnabled(&*eiter);
AddOnEnabled(&*eiter); break;
return;
}
} }
} }
} }
@@ -216,69 +212,49 @@ void
AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory, AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory,
ino_t toDirectory, dev_t device, ino_t node) 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; node_ref toNodeRef;
make_node_ref(device, toDirectory, &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 // It seems the notification was for a directory we are not
// actually watching by intention. // actually watching (at least not by intention).
return; return;
} }
add_on_entry_info info; add_on_entry_info info;
node_ref entryNodeRef; node_ref entryNodeRef;
make_node_ref(device, node, &entryNodeRef); make_node_ref(device, node, &entryNodeRef);
if (to_iter == fDirectories.end()) { if (!watchingToDirectory) {
// moved out of our view // moved out of our view
std::list<add_on_entry_info>::iterator eiter EntryList::iterator eiter = from_iter->entries.begin();
= from_iter->entries.begin(); if (!_FindEntry(entryNodeRef, from_iter->entries, eiter)) {
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()) {
// we don't know anything about this entry yet.. ignore it // we don't know anything about this entry yet.. ignore it
return; return;
} }
// if the name is the same, save the information about this // save the info and remove the entry
// add-on into fFormerEntries for later use info = *eiter;
if (strcmp(info.name, name) == 0) watch_node(&entryNodeRef, B_STOP_WATCHING, this);
fFormerEntries.push_back(info); from_iter->entries.erase(eiter);
// Start at the top again, and search until the from directory. // 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 // If we find a add-on with the same name then the moved add-on
// was not enabled. So we are done. // was not enabled. So we are done.
std::list<add_on_directory_info>::iterator diter = fDirectories.begin(); DirectoryList::iterator diter = fDirectories.begin();
for (; diter != from_iter ; diter++) { for (; diter != from_iter; diter++) {
std::list<add_on_entry_info>::iterator eiter2 eiter = diter->entries.begin();
= diter->entries.begin(); if (_FindEntry(info.name, diter->entries, eiter))
for (; eiter2 != diter->entries.end() ; eiter2++) { return;
if (strcmp(eiter2->name, info.name) == 0) {
return;
}
}
} }
// finally disable the add-on // 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 // Continue searching for a add-on below us. If we find a add-on
// with the same name, we must enable it. // with the same name, we must enable it.
for (from_iter++ ; from_iter != fDirectories.end() ; from_iter++) { for (from_iter++; from_iter != fDirectories.end(); from_iter++) {
std::list<add_on_entry_info>::iterator eiter2 eiter = from_iter->entries.begin();
= from_iter->entries.begin(); if (_FindEntry(info.name, from_iter->entries, eiter)) {
for (; eiter2 != from_iter->entries.end() ; eiter2++) { AddOnEnabled(&*eiter);
if (strcmp(eiter2->name, info.name) == 0) { return;
AddOnEnabled(&*eiter2);
return;
}
} }
} }
// finally, if the new name is different, destroy the addon // finally destroy the addon
if (strcmp(info.name, name) != 0) AddOnRemoved(&info);
AddOnRemoved(&info);
// done // done
return; return;
} }
if (from_iter == fDirectories.end()) { if (!watchingFromDirectory) {
// moved into our view // 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 // update the info
strncpy(info.name, name, sizeof(info.name)); strncpy(info.name, name, sizeof(info.name));
info.nref = entryNodeRef; info.nref = entryNodeRef;
info.dir_nref = toNodeRef; info.dir_nref = toNodeRef;
if (eiter == fFormerEntries.end()) { AddOnCreated(&info);
// this add-on was not seen before
AddOnCreated(&info);
}
// Start at the top again, and search until the to directory. // 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 // If we find an add-on with the same name then the moved add-on
// is not to be enabled. So we are done. // is not to be enabled. So we are done.
std::list<add_on_directory_info>::iterator diter = fDirectories.begin(); DirectoryList::iterator diter = fDirectories.begin();
for (; diter != to_iter ; diter++) { for (; diter != to_iter; diter++) {
std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); if (_HasEntry(info.name, diter->entries)) {
for (; eiter2 != diter->entries.end() ; eiter2++) { // The new add-on is being shadowed.
if (strcmp(eiter2->name, info.name) == 0) return;
return;
} }
} }
// The new add-on should be enabled, but first we check to see // 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. // if there is an add-on below us. If we find one, we disable it.
bool shadowing = false; for (diter++ ; diter != fDirectories.end(); diter++) {
for (diter++ ; diter != fDirectories.end() ; diter++) { EntryList::iterator eiter = diter->entries.begin();
std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); if (_FindEntry(info.name, diter->entries, eiter)) {
for (; eiter2 != diter->entries.end() ; eiter2++) { AddOnDisabled(&*eiter);
if (strcmp(eiter2->name, info.name) == 0) {
AddOnDisabled(&*eiter2);
shadowing = true;
break;
}
}
if (shadowing)
break; break;
}
} }
// enable the new add-on // enable the new add-on
AddOnEnabled(&info); AddOnEnabled(&info);
// put the new entry into the target directory // put the new entry into the target directory
to_iter->entries.push_back(info); _AddNewEntry(to_iter->entries, info);
// done // done
return; return;
} }
std::list<add_on_entry_info>::iterator eiter = from_iter->entries.begin(); // The add-on was renamed, or moved within our hierarchy.
for (; eiter != from_iter->entries.end() ; eiter++) {
if (entryNodeRef == eiter->nref) { EntryList::iterator eiter = from_iter->entries.begin();
// save the old info and remove the entry if (_FindEntry(entryNodeRef, from_iter->entries, eiter)) {
info = *eiter; // save the old info and remove the entry
from_iter->entries.erase(eiter); info = *eiter;
break; } 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) { if (strcmp(info.name, name) == 0) {
// same name moved in heirarchy // It should be impossible for the name to stay the same, unless the
debugger("add-on moved inside the heirarchy"); // 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 { } else {
// Erase the entry
from_iter->entries.erase(eiter);
// check to see if it was formerly enabled // check to see if it was formerly enabled
bool was_enabled = true; bool wasEnabled = true;
std::list<add_on_directory_info>::iterator old_iter DirectoryList::iterator old_iter = fDirectories.begin();
= fDirectories.begin(); for (; old_iter != from_iter; old_iter++) {
for (; old_iter != from_iter ; old_iter++) { if (_HasEntry(info.name, old_iter->entries)) {
std::list<add_on_entry_info>::iterator eiter2 wasEnabled = false;
= 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) {
break; break;
} }
} }
// if it was enabled, disable it and enable the one under us, if it // If it was enabled, disable it and enable the one under us, if it
// exists // exists.
if (was_enabled) { if (wasEnabled) {
AddOnDisabled(&info); AddOnDisabled(&info);
bool done = false; for (; old_iter != fDirectories.end(); old_iter++) {
for (; old_iter != fDirectories.end() ; old_iter++) { eiter = old_iter->entries.begin();
std::list<add_on_entry_info>::iterator eiter2 if (_FindEntry(info.name, old_iter->entries, eiter)) {
= old_iter->entries.begin(); AddOnEnabled(&*eiter);
for (; eiter2 != old_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
AddOnEnabled(&*eiter2);
done = true;
break;
}
}
if (done)
break; break;
}
} }
} }
@@ -436,56 +381,75 @@ AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory,
// check to see if we are newly enabled // check to see if we are newly enabled
bool isEnabled = true; bool isEnabled = true;
std::list<add_on_directory_info>::iterator new_iter DirectoryList::iterator new_iter = fDirectories.begin();
= fDirectories.begin(); for (; new_iter != to_iter; new_iter++) {
for (; new_iter != to_iter ; new_iter++) { if (_HasEntry(info.name, new_iter->entries)) {
std::list<add_on_entry_info>::iterator eiter2 isEnabled = false;
= new_iter->entries.begin();
for (; eiter2 != new_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
isEnabled = false;
break;
}
}
if (!isEnabled)
break; break;
}
} }
// if it is newly enabled, check under us for an enabled one, and // if it is newly enabled, check under us for an enabled one, and
// disable that first // disable that first
if (isEnabled) { if (isEnabled) {
bool done = false; for (; new_iter != fDirectories.end(); new_iter++) {
for (; new_iter != fDirectories.end() ; new_iter++) { eiter = new_iter->entries.begin();
std::list<add_on_entry_info>::iterator eiter2 if (_FindEntry(info.name, new_iter->entries, eiter)) {
= new_iter->entries.begin(); AddOnDisabled(&*eiter);
for (; eiter2 != new_iter->entries.end() ; eiter2++) {
if (strcmp(eiter2->name, info.name) == 0) {
AddOnDisabled(&*eiter2);
done = true;
break;
}
}
if (done)
break; break;
}
} }
AddOnEnabled(&info); 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. //! Process pending entries.
void void
AddOnMonitorHandler::_HandlePulse() AddOnMonitorHandler::_HandlePulse()
{ {
BDirectory directory; BDirectory directory;
std::list<add_on_entry_info>::iterator iter = fPendingEntries.begin(); EntryList::iterator iter = fPendingEntries.begin();
while (iter != fPendingEntries.end()) { while (iter != fPendingEntries.end()) {
add_on_entry_info info = *iter; 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; node_ref dirNodeRef;
if (directory.GetNodeRef(&dirNodeRef) != B_OK if (directory.GetNodeRef(&dirNodeRef) != B_OK
|| dirNodeRef != info.dir_nref) { || dirNodeRef != info.dir_nref) {
@@ -514,58 +478,150 @@ AddOnMonitorHandler::_HandlePulse()
// we are going to deal with the stable entry, so remove it // we are going to deal with the stable entry, so remove it
iter = fPendingEntries.erase(iter); iter = fPendingEntries.erase(iter);
// put the new entry into the directory info _EntryCreated(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);
} }
} }
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);
}