Added almost complete node monitor implementation - support for watching mounts/
unmounts is not yet implemented. Added an empty implementation for send_notification() - the function that is (or will be) able to send out BMessages from within the kernel; currently just prints out what should be done. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2481 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
6d16fb1d30
commit
a7d654ad0a
39
src/kernel/core/fs/message.c
Normal file
39
src/kernel/core/fs/message.c
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <OS.h>
|
||||
#include <vfs.h>
|
||||
|
||||
|
||||
#define MESSAGE_HEADER 'FOB1'
|
||||
|
||||
/*
|
||||
static void
|
||||
init_message(uint8 **_buffer, uint32 what, uint32 token)
|
||||
{
|
||||
uint32 *header = *_buffer;
|
||||
*header++ = MESSAGE_HEADER;
|
||||
|
||||
*_buffer = (uint8 *)header;
|
||||
}
|
||||
*/
|
||||
|
||||
status_t
|
||||
send_notification(port_id port, long token, ulong what, long op, mount_id device,
|
||||
mount_id toDevice, vnode_id parentNode, vnode_id toParentNode,
|
||||
vnode_id node, const char *name)
|
||||
{
|
||||
// this is currently the BeOS compatible send_notification() function
|
||||
// and will probably stay that way, not yet implemented, though
|
||||
|
||||
dprintf("send_notification(port = %ld, token = %ld, op = %ld, device = %ld, "
|
||||
"node = %Ld, parentNode = %Ld, toParentNode = %Ld, name = \"%s\"\n",
|
||||
port, token, op, device, node, parentNode, toParentNode, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
485
src/kernel/core/fs/node_monitor.c
Normal file
485
src/kernel/core/fs/node_monitor.c
Normal file
@ -0,0 +1,485 @@
|
||||
/*
|
||||
** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
|
||||
#include <drivers/node_monitor.h>
|
||||
#include <app/AppDefs.h>
|
||||
|
||||
#include <fs/node_monitor.h>
|
||||
#include <vfs.h>
|
||||
#include <fd.h>
|
||||
#include <lock.h>
|
||||
#include <khash.h>
|
||||
#include <list.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#define TRACE_MONITOR 0
|
||||
#if TRACE_MONITOR
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
// ToDo: add more fine grained locking - maybe using a ref_count in the
|
||||
// node_monitor structure?
|
||||
// ToDo: implement watching mounts/unmounts
|
||||
// ToDo: return another error code than B_NO_MEMORY if the team's maximum is hit
|
||||
|
||||
|
||||
typedef struct monitor_listener monitor_listener;
|
||||
typedef struct node_monitor node_monitor;
|
||||
|
||||
struct monitor_listener {
|
||||
monitor_listener *next;
|
||||
monitor_listener *prev;
|
||||
list_link monitor_link;
|
||||
port_id port;
|
||||
uint32 token;
|
||||
uint32 flags;
|
||||
node_monitor *monitor;
|
||||
};
|
||||
|
||||
struct node_monitor {
|
||||
node_monitor *next;
|
||||
mount_id device;
|
||||
vnode_id node;
|
||||
struct list listeners;
|
||||
};
|
||||
|
||||
struct monitor_hash_key {
|
||||
mount_id device;
|
||||
vnode_id node;
|
||||
};
|
||||
|
||||
#define MONITORS_HASH_TABLE_SIZE 16
|
||||
|
||||
static hash_table *gMonitorHash;
|
||||
static mutex gMonitorMutex;
|
||||
|
||||
|
||||
static int
|
||||
monitor_compare(void *_monitor, const void *_key)
|
||||
{
|
||||
node_monitor *monitor = _monitor;
|
||||
const struct monitor_hash_key *key = _key;
|
||||
|
||||
if (monitor->device == key->device && monitor->node == key->node)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static uint32
|
||||
monitor_hash(void *_monitor, const void *_key, uint32 range)
|
||||
{
|
||||
node_monitor *monitor = _monitor;
|
||||
const struct monitor_hash_key *key = _key;
|
||||
|
||||
#define MHASH(device, node) (((uint32)((node) >> 32) + (uint32)(node)) ^ (uint32)(device))
|
||||
|
||||
if (monitor != NULL)
|
||||
return (MHASH(monitor->device, monitor->node) % range);
|
||||
|
||||
return (MHASH(key->device, key->node) % range);
|
||||
#undef MHASH
|
||||
}
|
||||
|
||||
|
||||
/** Returns the monitor that matches the specified device/node pair.
|
||||
* Must be called with monitors lock hold.
|
||||
*/
|
||||
|
||||
static node_monitor *
|
||||
get_monitor_for(mount_id device, vnode_id node)
|
||||
{
|
||||
struct monitor_hash_key key;
|
||||
key.device = device;
|
||||
key.node = node;
|
||||
|
||||
return (node_monitor *)hash_lookup(gMonitorHash, &key);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the listener that matches the specified port/token pair.
|
||||
* Must be called with monitors lock hold.
|
||||
*/
|
||||
|
||||
static monitor_listener *
|
||||
get_listener_for(node_monitor *monitor, port_id port, uint32 token)
|
||||
{
|
||||
monitor_listener *listener = NULL;
|
||||
|
||||
while ((listener = list_get_next_item(&monitor->listeners, listener)) != NULL) {
|
||||
// does this listener match?
|
||||
if (listener->port == port && listener->token == token)
|
||||
return listener;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/** Removes the specified node_monitor from the hashtable
|
||||
* and free it.
|
||||
* Must be called with monitors lock hold.
|
||||
*/
|
||||
|
||||
static void
|
||||
remove_monitor(node_monitor *monitor)
|
||||
{
|
||||
hash_remove(gMonitorHash, monitor);
|
||||
free(monitor);
|
||||
}
|
||||
|
||||
|
||||
/** Removes the specified monitor_listener from all lists
|
||||
* and free it.
|
||||
* Must be called with monitors lock hold.
|
||||
*/
|
||||
|
||||
static void
|
||||
remove_listener(monitor_listener *listener)
|
||||
{
|
||||
node_monitor *monitor = listener->monitor;
|
||||
|
||||
// remove it from the listener and I/O context lists
|
||||
list_remove_link(&listener->monitor_link);
|
||||
list_remove_link(listener);
|
||||
|
||||
free(listener);
|
||||
|
||||
if (list_is_empty(&monitor->listeners))
|
||||
remove_monitor(monitor);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
add_node_monitor(io_context *context, mount_id device, vnode_id node,
|
||||
uint32 flags, port_id port, uint32 token)
|
||||
{
|
||||
monitor_listener *listener;
|
||||
node_monitor *monitor;
|
||||
status_t status = B_OK;
|
||||
|
||||
TRACE(("add_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n",
|
||||
device, node, flags, port, token));
|
||||
|
||||
mutex_lock(&gMonitorMutex);
|
||||
|
||||
monitor = get_monitor_for(device, node);
|
||||
if (monitor == NULL) {
|
||||
// check if this team is allowed to have more listeners
|
||||
if (context->num_monitors >= context->max_monitors) {
|
||||
// the BeBook says to return B_NO_MEMORY in this case, but
|
||||
// we should have another one.
|
||||
status = B_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// create new monitor
|
||||
monitor = (node_monitor *)malloc(sizeof(node_monitor));
|
||||
if (monitor == NULL) {
|
||||
status = B_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// initialize monitor
|
||||
monitor->device = device;
|
||||
monitor->node = node;
|
||||
list_init_etc(&monitor->listeners, offsetof(monitor_listener, monitor_link));
|
||||
|
||||
hash_insert(gMonitorHash, monitor);
|
||||
} else {
|
||||
// check if the listener is already listening, and
|
||||
// if so, just add the new flags
|
||||
|
||||
listener = get_listener_for(monitor, port, token);
|
||||
if (listener != NULL) {
|
||||
listener->flags |= flags;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// check if this team is allowed to have more listeners
|
||||
if (context->num_monitors >= context->max_monitors) {
|
||||
// the BeBook says to return B_NO_MEMORY in this case, but
|
||||
// we should have another one.
|
||||
status = B_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
// add listener
|
||||
|
||||
listener = (monitor_listener *)malloc(sizeof(monitor_listener));
|
||||
if (listener == NULL) {
|
||||
// no memory for the listener, so remove the monitor as well if needed
|
||||
if (list_is_empty(&monitor->listeners))
|
||||
remove_monitor(monitor);
|
||||
|
||||
status = B_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// initialize listener, and add it to the lists
|
||||
listener->port = port;
|
||||
listener->token = token;
|
||||
listener->flags = flags;
|
||||
listener->monitor = monitor;
|
||||
|
||||
list_add_link_to_head(&monitor->listeners, &listener->monitor_link);
|
||||
list_add_link_to_head(&context->node_monitors, listener);
|
||||
|
||||
context->num_monitors++;
|
||||
|
||||
out:
|
||||
mutex_unlock(&gMonitorMutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
remove_node_monitor(struct io_context *context, mount_id device, vnode_id node,
|
||||
uint32 flags, port_id port, uint32 token)
|
||||
{
|
||||
monitor_listener *listener;
|
||||
node_monitor *monitor;
|
||||
status_t status = B_OK;
|
||||
|
||||
TRACE(("remove_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n",
|
||||
device, node, flags, port, token));
|
||||
|
||||
mutex_lock(&gMonitorMutex);
|
||||
|
||||
// get the monitor for this device/node pair
|
||||
monitor = get_monitor_for(device, node);
|
||||
if (monitor == NULL) {
|
||||
status = B_ENTRY_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// see if it has the listener we are looking for
|
||||
listener = get_listener_for(monitor, port, token);
|
||||
if (listener == NULL) {
|
||||
status = B_ENTRY_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// no flags means remove all flags
|
||||
if (flags == B_STOP_WATCHING)
|
||||
flags = ~0;
|
||||
|
||||
listener->flags &= ~flags;
|
||||
|
||||
// if there aren't anymore flags, remove this listener
|
||||
if (listener->flags == B_STOP_WATCHING) {
|
||||
remove_listener(listener);
|
||||
context->num_monitors--;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&gMonitorMutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
remove_node_monitors_by_target(struct io_context *context, port_id port, uint32 token)
|
||||
{
|
||||
monitor_listener *listener = NULL;
|
||||
int32 count = 0;
|
||||
|
||||
while ((listener = list_get_next_item(&context->node_monitors, listener)) != NULL) {
|
||||
monitor_listener *removeListener;
|
||||
|
||||
if (listener->port != port || listener->token != token)
|
||||
continue;
|
||||
|
||||
listener = list_get_prev_item(&context->node_monitors, removeListener = listener);
|
||||
// this line sets the listener one item back, allowing us
|
||||
// to remove its successor (which is saved in "removeListener")
|
||||
|
||||
remove_listener(removeListener);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Exported private kernel API
|
||||
|
||||
|
||||
status_t
|
||||
remove_node_monitors(struct io_context *context)
|
||||
{
|
||||
mutex_lock(&gMonitorMutex);
|
||||
|
||||
while (!list_is_empty(&context->node_monitors)) {
|
||||
// the remove_listener() function will also free the node_monitor
|
||||
// if it doesn't have any listeners attached anymore
|
||||
remove_listener(list_get_first_item(&context->node_monitors));
|
||||
}
|
||||
|
||||
mutex_unlock(&gMonitorMutex);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
node_monitor_init(void)
|
||||
{
|
||||
gMonitorHash = hash_init(MONITORS_HASH_TABLE_SIZE, offsetof(node_monitor, next),
|
||||
&monitor_compare, &monitor_hash);
|
||||
if (gMonitorHash == NULL)
|
||||
panic("node_monitor_init: error creating mounts hash table\n");
|
||||
|
||||
return mutex_init(&gMonitorMutex, "node monitor");
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Exported public kernel API
|
||||
|
||||
|
||||
status_t
|
||||
start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
||||
{
|
||||
io_context *context = get_current_io_context(true);
|
||||
|
||||
return add_node_monitor(context, device, node, flags, port, token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
||||
{
|
||||
io_context *context = get_current_io_context(true);
|
||||
|
||||
return remove_node_monitor(context, device, node, flags, port, token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
stop_notifying(port_id port, uint32 token)
|
||||
{
|
||||
io_context *context = get_current_io_context(true);
|
||||
|
||||
return remove_node_monitors_by_target(context, port, token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
notify_listener(int op, mount_id device, vnode_id parentNode, vnode_id toParentNode,
|
||||
vnode_id node, const char *name)
|
||||
{
|
||||
monitor_listener *listener;
|
||||
node_monitor *monitor;
|
||||
status_t status = B_OK;
|
||||
|
||||
TRACE(("notify_listener(op = %d, device = %ld, node = %Ld, parent = %Ld, toParent = %Ld"
|
||||
", name = \"%s\"\n", op, device, node, parentNode, toParentNode, name));
|
||||
|
||||
mutex_lock(&gMonitorMutex);
|
||||
|
||||
// check the main "node"
|
||||
|
||||
if ((op == B_ENTRY_MOVED
|
||||
|| op == B_ENTRY_REMOVED
|
||||
|| op == B_STAT_CHANGED
|
||||
|| op == B_ATTR_CHANGED)
|
||||
&& (monitor = get_monitor_for(device, node)) != NULL) {
|
||||
// iterate over all listeners for this monitor, and see
|
||||
// if we have to send anything
|
||||
listener = NULL;
|
||||
while ((listener = list_get_next_item(&monitor->listeners, listener)) != NULL) {
|
||||
// do we have a reason to notify this listener?
|
||||
if (((listener->flags & B_WATCH_NAME) != 0
|
||||
&& (op == B_ENTRY_MOVED || op == B_ENTRY_REMOVED))
|
||||
|| ((listener->flags & B_WATCH_STAT) != 0
|
||||
&& op == B_STAT_CHANGED)
|
||||
|| ((listener->flags & B_WATCH_ATTR) != 0
|
||||
&& op == B_ATTR_CHANGED)) {
|
||||
// then do it!
|
||||
send_notification(listener->port, listener->token, B_NODE_MONITOR,
|
||||
op, device, 0, parentNode, toParentNode, node, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check its parent directory
|
||||
|
||||
if ((op == B_ENTRY_MOVED
|
||||
|| op == B_ENTRY_REMOVED
|
||||
|| op == B_ENTRY_CREATED)
|
||||
&& (monitor = get_monitor_for(device, parentNode)) != NULL) {
|
||||
// iterate over all listeners for this monitor, and see
|
||||
// if we have to send anything
|
||||
listener = NULL;
|
||||
while ((listener = list_get_next_item(&monitor->listeners, listener)) != NULL) {
|
||||
// do we have a reason to notify this listener?
|
||||
if ((listener->flags & B_WATCH_DIRECTORY) != 0) {
|
||||
send_notification(listener->port, listener->token, B_NODE_MONITOR,
|
||||
op, device, 0, parentNode, toParentNode, node, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check its new target parent directory
|
||||
|
||||
if (op == B_ENTRY_MOVED
|
||||
&& (monitor = get_monitor_for(device, toParentNode)) != NULL) {
|
||||
// iterate over all listeners for this monitor, and see
|
||||
// if we have to send anything
|
||||
listener = NULL;
|
||||
while ((listener = list_get_next_item(&monitor->listeners, listener)) != NULL) {
|
||||
// do we have a reason to notify this listener?
|
||||
if ((listener->flags & B_WATCH_DIRECTORY) != 0) {
|
||||
send_notification(listener->port, listener->token, B_NODE_MONITOR,
|
||||
B_ENTRY_MOVED, device, 0, parentNode, toParentNode, node, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&gMonitorMutex);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Userland syscalls
|
||||
|
||||
|
||||
status_t
|
||||
user_stop_notifying(port_id port, uint32 token)
|
||||
{
|
||||
io_context *context = get_current_io_context(false);
|
||||
|
||||
return remove_node_monitors_by_target(context, port, token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
||||
{
|
||||
io_context *context = get_current_io_context(false);
|
||||
|
||||
return add_node_monitor(context, device, node, flags, port, token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
user_stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
||||
{
|
||||
io_context *context = get_current_io_context(false);
|
||||
|
||||
return remove_node_monitor(context, device, node, flags, port, token);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user