* Copyright 2004-2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus, superstippi@gmx.de
* Andrew Bachmann
* John Scipione, jscipione@gmail.com
*/
#include "AddOnMonitorHandler.h"
#include <string.h>
#include <strings.h>
#include <Autolock.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <Path.h>
#include <driver_settings.h>
#include <safemode_defs.h>
#include <syscalls.h>
#ifndef ADD_ON_STABLE_SECONDS
# define ADD_ON_STABLE_SECONDS 1
#endif
AddOnMonitorHandler::AddOnMonitorHandler(const char* name)
:
NodeMonitorHandler(name != NULL ? name : "AddOnMonitorHandler")
{
}
AddOnMonitorHandler::~AddOnMonitorHandler()
{
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);
}
}
void
AddOnMonitorHandler::MessageReceived(BMessage* msg)
{
if (msg->what == B_PULSE)
_HandlePendingEntries();
inherited::MessageReceived(msg);
}
status_t
AddOnMonitorHandler::AddDirectory(const node_ref* nref, bool sync)
{
BAutolock _(Looper());
DirectoryList::iterator it = fDirectories.begin();
for (; it != fDirectories.end(); it++) {
if (it->nref == *nref)
return B_OK;
}
BDirectory directory(nref);
status_t status = directory.InitCheck();
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);
add_on_entry_info entryInfo;
entryInfo.dir_nref = *nref;
BEntry entry;
while (directory.GetNextEntry(&entry) == B_OK) {
if (entry.GetName(entryInfo.name) != B_OK
|| entry.GetNodeRef(&entryInfo.nref) != B_OK) {
continue;
}
fPendingEntries.push_back(entryInfo);
}
if (sync)
_HandlePendingEntries();
return B_OK;
}
status_t
AddOnMonitorHandler::AddAddOnDirectories(const char* leafPath)
{
char parameter[32];
size_t parameterLength = sizeof(parameter);
uint32 start = 0;
const directory_which addOnDirectories[] = {
B_USER_NONPACKAGED_ADDONS_DIRECTORY,
B_USER_ADDONS_DIRECTORY,
B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
B_SYSTEM_ADDONS_DIRECTORY
};
if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS, parameter,
¶meterLength) == B_OK) {
if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1")) {
start = 2;
}
}
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")) {
start = 3;
}
}
for (uint32 i = start;
i < sizeof(addOnDirectories) / sizeof(directory_which); i++) {
BDirectory directory;
node_ref nodeRef;
BPath path;
if (find_directory(addOnDirectories[i], &path) == B_OK
&& path.Append(leafPath) == B_OK
&& directory.SetTo(path.Path()) == B_OK
&& directory.GetNodeRef(&nodeRef) == B_OK) {
status_t result = this->AddDirectory(&nodeRef);
if (result != B_OK)
return result;
}
}
return B_OK;
}
void
AddOnMonitorHandler::AddOnCreated(const add_on_entry_info* entryInfo)
{
}
void
AddOnMonitorHandler::AddOnEnabled(const add_on_entry_info* entryInfo)
{
}
void
AddOnMonitorHandler::AddOnDisabled(const add_on_entry_info* entryInfo)
{
}
void
AddOnMonitorHandler::AddOnRemoved(const add_on_entry_info* entryInfo)
{
}
void
AddOnMonitorHandler::EntryCreated(const char* name, ino_t directory,
dev_t device, ino_t node)
{
add_on_entry_info entryInfo;
strlcpy(entryInfo.name, name, sizeof(entryInfo.name));
make_node_ref(device, node, &entryInfo.nref);
make_node_ref(device, directory, &entryInfo.dir_nref);
fPendingEntries.push_back(entryInfo);
}
void
AddOnMonitorHandler::EntryRemoved(const char* name, ino_t directory,
dev_t device, ino_t node)
{
node_ref entryNodeRef;
make_node_ref(device, node, &entryNodeRef);
EntryList::iterator eiter = fPendingEntries.begin();
while (eiter != fPendingEntries.end()) {
if (eiter->nref == entryNodeRef)
eiter = fPendingEntries.erase(eiter);
else
eiter++;
}
DirectoryList::iterator diter = fDirectories.begin();
if (!_FindDirectory(directory, device, diter)) {
return;
}
eiter = diter->entries.begin();
if (!_FindEntry(entryNodeRef, diter->entries, eiter)) {
return;
}
add_on_entry_info info = *eiter;
watch_node(&entryNodeRef, B_STOP_WATCHING, this);
diter->entries.erase(eiter);
DirectoryList::iterator diter2 = fDirectories.begin();
for (; diter2 != diter; diter2++) {
if (_HasEntry(info.name, diter2->entries)) {
AddOnRemoved(&info);
return;
}
}
AddOnDisabled(&info);
AddOnRemoved(&info);
for (diter++; diter != fDirectories.end(); diter++) {
eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter)) {
AddOnEnabled(&*eiter);
break;
}
}
}
void
AddOnMonitorHandler::EntryMoved(const char* name, const char* fromName,
ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node,
dev_t nodeDevice)
{
node_ref toNodeRef;
make_node_ref(device, toDirectory, &toNodeRef);
DirectoryList::iterator fromIter = fDirectories.begin();
bool watchingFromDirectory = _FindDirectory(fromDirectory, device,
fromIter);
DirectoryList::iterator toIter = fDirectories.begin();
bool watchingToDirectory = _FindDirectory(toNodeRef, toIter);
if (!watchingFromDirectory && !watchingToDirectory) {
return;
}
add_on_entry_info info;
node_ref entryNodeRef;
make_node_ref(device, node, &entryNodeRef);
if (!watchingToDirectory) {
EntryList::iterator eiter = fromIter->entries.begin();
if (!_FindEntry(entryNodeRef, fromIter->entries, eiter)) {
return;
}
info = *eiter;
watch_node(&entryNodeRef, B_STOP_WATCHING, this);
fromIter->entries.erase(eiter);
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != fromIter; diter++) {
eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter))
return;
}
AddOnDisabled(&info);
for (fromIter++; fromIter != fDirectories.end(); fromIter++) {
eiter = fromIter->entries.begin();
if (_FindEntry(info.name, fromIter->entries, eiter)) {
AddOnEnabled(&*eiter);
return;
}
}
AddOnRemoved(&info);
return;
}
if (!watchingFromDirectory) {
strlcpy(info.name, name, sizeof(info.name));
info.nref = entryNodeRef;
info.dir_nref = toNodeRef;
AddOnCreated(&info);
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != toIter; diter++) {
if (_HasEntry(info.name, diter->entries)) {
return;
}
}
for (diter++ ; diter != fDirectories.end(); diter++) {
EntryList::iterator eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter)) {
AddOnDisabled(&*eiter);
break;
}
}
AddOnEnabled(&info);
_AddNewEntry(toIter->entries, info);
return;
}
EntryList::iterator eiter = fromIter->entries.begin();
if (_FindEntry(entryNodeRef, fromIter->entries, eiter)) {
info = *eiter;
} else {
return;
}
if (strcmp(info.name, name) == 0) {
EntryRemoved(name, fromDirectory, device, node);
info.dir_nref = toNodeRef;
_EntryCreated(info);
} else {
fromIter->entries.erase(eiter);
bool wasEnabled = true;
DirectoryList::iterator oldIter = fDirectories.begin();
for (; oldIter != fromIter; oldIter++) {
if (_HasEntry(info.name, oldIter->entries)) {
wasEnabled = false;
break;
}
}
if (wasEnabled) {
AddOnDisabled(&info);
for (; oldIter != fDirectories.end(); oldIter++) {
eiter = oldIter->entries.begin();
if (_FindEntry(info.name, oldIter->entries, eiter)) {
AddOnEnabled(&*eiter);
break;
}
}
}
AddOnRemoved(&info);
strlcpy(info.name, name, sizeof(info.name));
info.dir_nref = toNodeRef;
AddOnCreated(&info);
bool isEnabled = true;
DirectoryList::iterator newIter = fDirectories.begin();
for (; newIter != toIter; newIter++) {
if (_HasEntry(info.name, newIter->entries)) {
isEnabled = false;
break;
}
}
if (isEnabled) {
for (; newIter != fDirectories.end(); newIter++) {
eiter = newIter->entries.begin();
if (_FindEntry(info.name, newIter->entries, eiter)) {
AddOnDisabled(&*eiter);
break;
}
}
AddOnEnabled(&info);
}
toIter->entries.push_back(info);
}
}
void
AddOnMonitorHandler::StatChanged(ino_t node, dev_t device, int32 statFields)
{
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) {
const add_on_entry_info* info = &*eiter;
AddOnDisabled(info);
AddOnRemoved(info);
AddOnCreated(info);
AddOnEnabled(info);
return;
}
}
}
}
void
AddOnMonitorHandler::_HandlePendingEntries()
{
BDirectory directory;
EntryList::iterator iter = fPendingEntries.begin();
while (iter != fPendingEntries.end()) {
add_on_entry_info info = *iter;
node_ref dirNodeRef;
if (directory.GetNodeRef(&dirNodeRef) != B_OK
|| dirNodeRef != info.dir_nref) {
if (directory.SetTo(&info.dir_nref) != B_OK) {
iter = fPendingEntries.erase(iter);
continue;
}
dirNodeRef = info.dir_nref;
}
struct stat st;
if (directory.GetStatFor(info.name, &st) != B_OK) {
iter = fPendingEntries.erase(iter);
continue;
}
if (real_time_clock() - st.st_mtime < ADD_ON_STABLE_SECONDS) {
iter++;
continue;
}
iter = fPendingEntries.erase(iter);
_EntryCreated(info);
}
}
void
AddOnMonitorHandler::_EntryCreated(add_on_entry_info& info)
{
DirectoryList::iterator diter = fDirectories.begin();
for (; diter != fDirectories.end(); diter++) {
if (diter->nref == info.dir_nref) {
_AddNewEntry(diter->entries, info);
break;
}
}
AddOnCreated(&info);
bool enabled = true;
DirectoryList::iterator diter2 = fDirectories.begin();
for (; diter2 != diter; diter2++) {
if (_HasEntry(info.name, diter2->entries)) {
enabled = false;
break;
}
}
if (!enabled)
return;
for (diter++ ; diter != fDirectories.end(); diter++) {
EntryList::iterator eiter = diter->entries.begin();
if (_FindEntry(info.name, diter->entries, eiter)) {
AddOnDisabled(&*eiter);
break;
}
}
AddOnEnabled(&info);
}
bool
AddOnMonitorHandler::_FindEntry(const node_ref& entry, const EntryList& list,
EntryList::iterator& it) const
{
for (; EntryList::const_iterator(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 (; EntryList::const_iterator(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 (; DirectoryList::const_iterator(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);
}