1/*
2 * Copyright 2003-2008, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Copyright 2005-2008, Ingo Weinhold, bonefish@users.sf.net.
4 * Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de.
5 *
6 * Distributed under the terms of the MIT License.
7 */
8
9
10#include <fs/node_monitor.h>
11
12#include <stddef.h>
13#include <stdlib.h>
14
15#include <AppDefs.h>
16#include <NodeMonitor.h>
17
18#include <fd.h>
19#include <khash.h>
20#include <lock.h>
21#include <messaging.h>
22#include <Notifications.h>
23#include <vfs.h>
24#include <util/AutoLock.h>
25#include <util/DoublyLinkedList.h>
26#include <util/KMessage.h>
27#include <util/list.h>
28
29#include "node_monitor_private.h"
30
31
32//#define TRACE_MONITOR
33#ifdef TRACE_MONITOR
34#	define TRACE(x) dprintf x
35#else
36#	define TRACE(x) ;
37#endif
38
39
40// ToDo: add more fine grained locking - maybe using a ref_count in the
41//		node_monitor structure?
42// ToDo: return another error code than B_NO_MEMORY if the team's maximum is hit
43
44
45typedef struct monitor_listener monitor_listener;
46typedef struct node_monitor node_monitor;
47
48struct monitor_listener {
49	list_link			context_link;
50	DoublyLinkedListLink<monitor_listener> monitor_link;
51	NotificationListener *listener;
52	uint32				flags;
53	node_monitor		*monitor;
54};
55
56typedef DoublyLinkedList<monitor_listener, DoublyLinkedListMemberGetLink<
57	monitor_listener, &monitor_listener::monitor_link> > MonitorListenerList;
58
59struct node_monitor {
60	node_monitor*		hash_link;
61	dev_t				device;
62	ino_t				node;
63	MonitorListenerList	listeners;
64};
65
66struct interested_monitor_listener_list {
67	MonitorListenerList::Iterator iterator;
68	uint32				flags;
69};
70
71static UserMessagingMessageSender sNodeMonitorSender;
72
73class UserNodeListener : public UserMessagingListener {
74	public:
75		UserNodeListener(port_id port, int32 token)
76			: UserMessagingListener(sNodeMonitorSender, port, token)
77		{
78		}
79
80		bool operator==(const NotificationListener& _other) const
81		{
82			const UserNodeListener* other
83				= dynamic_cast<const UserNodeListener*>(&_other);
84			return other != NULL && other->Port() == Port()
85				&& other->Token() == Token();
86		}
87};
88
89class NodeMonitorService : public NotificationService {
90	public:
91		NodeMonitorService();
92		virtual ~NodeMonitorService();
93
94		status_t InitCheck();
95
96		status_t NotifyEntryCreatedOrRemoved(int32 opcode, dev_t device,
97			ino_t directory, const char *name, ino_t node);
98		status_t NotifyEntryMoved(dev_t device, ino_t fromDirectory,
99			const char *fromName, ino_t toDirectory, const char *toName,
100			ino_t node);
101		status_t NotifyStatChanged(dev_t device, ino_t node, uint32 statFields);
102		status_t NotifyAttributeChanged(dev_t device, ino_t node,
103			const char *attribute, int32 cause);
104		status_t NotifyUnmount(dev_t device);
105		status_t NotifyMount(dev_t device, dev_t parentDevice,
106			ino_t parentDirectory);
107
108		status_t RemoveListeners(io_context *context);
109
110		status_t AddListener(const KMessage *eventSpecifier,
111			NotificationListener &listener);
112		status_t UpdateListener(const KMessage *eventSpecifier,
113			NotificationListener &listener);
114		status_t RemoveListener(const KMessage *eventSpecifier,
115			NotificationListener &listener);
116
117		status_t AddListener(io_context *context, dev_t device, ino_t node,
118			uint32 flags, NotificationListener &notificationListener);
119		status_t RemoveListener(io_context *context, dev_t device, ino_t node,
120			NotificationListener &notificationListener);
121
122		status_t RemoveUserListeners(struct io_context *context,
123			port_id port, uint32 token);
124		status_t UpdateUserListener(io_context *context, dev_t device,
125			ino_t node, uint32 flags, UserNodeListener &userListener);
126
127		virtual const char* Name() { return "node monitor"; }
128
129	private:
130		void _RemoveMonitor(node_monitor *monitor, uint32 flags);
131		status_t _RemoveListener(io_context *context, dev_t device, ino_t node,
132			NotificationListener& notificationListener, bool isVolumeListener);
133		void _RemoveListener(monitor_listener *listener);
134		node_monitor *_MonitorFor(dev_t device, ino_t node,
135			bool isVolumeListener);
136		status_t _GetMonitor(io_context *context, dev_t device, ino_t node,
137			bool addIfNecessary, node_monitor **_monitor,
138			bool isVolumeListener);
139		monitor_listener *_MonitorListenerFor(node_monitor* monitor,
140			NotificationListener& notificationListener);
141		status_t _AddMonitorListener(io_context *context,
142			node_monitor* monitor, uint32 flags,
143			NotificationListener& notificationListener);
144		status_t _UpdateListener(io_context *context, dev_t device, ino_t node,
145			uint32 flags, bool addFlags,
146			NotificationListener &notificationListener);
147		void _GetInterestedMonitorListeners(dev_t device, ino_t node,
148			uint32 flags, interested_monitor_listener_list *interestedListeners,
149			int32 &interestedListenerCount);
150		void _GetInterestedVolumeListeners(dev_t device, uint32 flags,
151			interested_monitor_listener_list *interestedListeners,
152			int32 &interestedListenerCount);
153		status_t _SendNotificationMessage(KMessage &message,
154			interested_monitor_listener_list *interestedListeners,
155			int32 interestedListenerCount);
156
157		struct monitor_hash_key {
158			dev_t	device;
159			ino_t	node;
160		};
161
162		struct HashDefinition {
163			typedef monitor_hash_key* KeyType;
164			typedef	node_monitor ValueType;
165
166			size_t HashKey(monitor_hash_key* key) const
167				{ return _Hash(key->device, key->node); }
168			size_t Hash(node_monitor *monitor) const
169				{ return _Hash(monitor->device, monitor->node); }
170
171			bool Compare(monitor_hash_key* key, node_monitor *monitor) const
172			{
173				return key->device == monitor->device
174					&& key->node == monitor->node;
175			}
176
177			node_monitor*& GetLink(node_monitor* monitor) const
178				{ return monitor->hash_link; }
179
180			uint32 _Hash(dev_t device, ino_t node) const
181			{
182				return ((uint32)(node >> 32) + (uint32)node) ^ (uint32)device;
183			}
184		};
185
186		typedef BOpenHashTable<HashDefinition> MonitorHash;
187
188		struct VolumeHashDefinition {
189			typedef dev_t KeyType;
190			typedef	node_monitor ValueType;
191
192			size_t HashKey(dev_t key) const
193				{ return _Hash(key); }
194			size_t Hash(node_monitor *monitor) const
195				{ return _Hash(monitor->device); }
196
197			bool Compare(dev_t key, node_monitor *monitor) const
198			{
199				return key == monitor->device;
200			}
201
202			node_monitor*& GetLink(node_monitor* monitor) const
203				{ return monitor->hash_link; }
204
205			uint32 _Hash(dev_t device) const
206			{
207				return (uint32)(device >> 16) + (uint16)device;
208			}
209		};
210
211		typedef BOpenHashTable<VolumeHashDefinition> VolumeMonitorHash;
212
213		MonitorHash	fMonitors;
214		VolumeMonitorHash fVolumeMonitors;
215		recursive_lock fRecursiveLock;
216};
217
218static NodeMonitorService sNodeMonitorService;
219
220
221/*!	\brief Notifies the listener of a live query that an entry has been added
222  		   to or removed from or updated and still in the query (for whatever
223  		   reason).
224  	\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED or \c B_ATTR_CHANGED.
225  	\param port The target port of the listener.
226  	\param token The BHandler token of the listener.
227  	\param device The ID of the mounted FS, the entry lives in.
228  	\param directory The entry's parent directory ID.
229  	\param name The entry's name.
230  	\param node The ID of the node the entry refers to.
231  	\return
232  	- \c B_OK, if everything went fine,
233  	- another error code otherwise.
234*/
235static status_t
236notify_query_entry_event(int32 opcode, port_id port, int32 token,
237	dev_t device, ino_t directory, const char *name, ino_t node)
238{
239	if (!name)
240		return B_BAD_VALUE;
241
242	// construct the message
243	char messageBuffer[1024];
244	KMessage message;
245	message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE);
246	message.AddInt32("opcode", opcode);
247	message.AddInt32("device", device);
248	message.AddInt64("directory", directory);
249	message.AddInt64("node", node);
250	message.AddString("name", name);
251
252	// send the message
253	messaging_target target;
254	target.port = port;
255	target.token = token;
256
257	return send_message(&message, &target, 1);
258}
259
260
261//	#pragma mark - NodeMonitorService
262
263
264NodeMonitorService::NodeMonitorService()
265{
266	recursive_lock_init(&fRecursiveLock, "node monitor");
267}
268
269
270NodeMonitorService::~NodeMonitorService()
271{
272	recursive_lock_destroy(&fRecursiveLock);
273}
274
275
276status_t
277NodeMonitorService::InitCheck()
278{
279	return B_OK;
280}
281
282
283/*! Removes the specified node_monitor from the hashtable
284	and free it.
285	Must be called with monitors lock hold.
286*/
287void
288NodeMonitorService::_RemoveMonitor(node_monitor *monitor, uint32 flags)
289{
290	if ((flags & B_WATCH_VOLUME) != 0)
291		fVolumeMonitors.Remove(monitor);
292	else
293		fMonitors.Remove(monitor);
294	delete monitor;
295}
296
297
298//! Helper function for the RemoveListener function.
299status_t
300NodeMonitorService::_RemoveListener(io_context *context, dev_t device,
301	ino_t node, NotificationListener& notificationListener,
302	bool isVolumeListener)
303{
304	TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n",
305		__PRETTY_FUNCTION__, device, node, &notificationListener));
306
307	RecursiveLocker _(fRecursiveLock);
308
309	// get the monitor for this device/node pair
310	node_monitor *monitor = _MonitorFor(device, node, isVolumeListener);
311	if (monitor == NULL)
312		return B_BAD_VALUE;
313
314	// see if it has the listener we are looking for
315	monitor_listener* listener = _MonitorListenerFor(monitor,
316		notificationListener);
317	if (listener == NULL)
318		return B_BAD_VALUE;
319
320	_RemoveListener(listener);
321	context->num_monitors--;
322
323	return B_OK;
324}
325
326
327/*! Removes the specified monitor_listener from all lists
328	and free it.
329	Must be called with monitors lock hold.
330*/
331void
332NodeMonitorService::_RemoveListener(monitor_listener *listener)
333{
334	uint32 flags = listener->flags;
335	node_monitor *monitor = listener->monitor;
336
337	// remove it from the listener and I/O context lists
338	monitor->listeners.Remove(listener);
339	list_remove_link(listener);
340
341	if (dynamic_cast<UserNodeListener*>(listener->listener) != NULL) {
342		// This is a listener we copied ourselves in UpdateUserListener(),
343		// so we have to delete it here.
344		delete listener->listener;
345	}
346
347	delete listener;
348
349	if (monitor->listeners.IsEmpty())
350		_RemoveMonitor(monitor, flags);
351}
352
353
354/*! Returns the monitor that matches the specified device/node pair.
355	Must be called with monitors lock hold.
356*/
357node_monitor *
358NodeMonitorService::_MonitorFor(dev_t device, ino_t node, bool isVolumeListener)
359{
360	if (isVolumeListener)
361		return fVolumeMonitors.Lookup(device);
362
363	struct monitor_hash_key key;
364	key.device = device;
365	key.node = node;
366
367	return fMonitors.Lookup(&key);
368}
369
370
371/*! Returns the monitor that matches the specified device/node pair.
372	If the monitor does not exist yet, it will be created.
373	Must be called with monitors lock hold.
374*/
375status_t
376NodeMonitorService::_GetMonitor(io_context *context, dev_t device, ino_t node,
377	bool addIfNecessary, node_monitor** _monitor, bool isVolumeListener)
378{
379	node_monitor* monitor = _MonitorFor(device, node, isVolumeListener);
380	if (monitor != NULL) {
381		*_monitor = monitor;
382		return B_OK;
383	}
384	if (!addIfNecessary)
385		return B_BAD_VALUE;
386
387	// check if this team is allowed to have more listeners
388	if (context->num_monitors >= context->max_monitors) {
389		// the BeBook says to return B_NO_MEMORY in this case, but
390		// we should have another one.
391		return B_NO_MEMORY;
392	}
393
394	// create new monitor
395	monitor = new(std::nothrow) node_monitor;
396	if (monitor == NULL)
397		return B_NO_MEMORY;
398
399	// initialize monitor
400	monitor->device = device;
401	monitor->node = node;
402
403	status_t status;
404	if (isVolumeListener)
405		status = fVolumeMonitors.Insert(monitor);
406	else
407		status = fMonitors.Insert(monitor);
408	if (status < B_OK) {
409		delete monitor;
410		return B_NO_MEMORY;
411	}
412
413	*_monitor = monitor;
414	return B_OK;
415}
416
417
418/*! Returns the listener that matches the specified port/token pair.
419	Must be called with monitors lock hold.
420*/
421monitor_listener*
422NodeMonitorService::_MonitorListenerFor(node_monitor* monitor,
423	NotificationListener& notificationListener)
424{
425	MonitorListenerList::Iterator iterator
426		= monitor->listeners.GetIterator();
427
428	while (monitor_listener* listener = iterator.Next()) {
429		// does this listener match?
430		if (*listener->listener == notificationListener)
431			return listener;
432	}
433
434	return NULL;
435}
436
437
438status_t
439NodeMonitorService::_AddMonitorListener(io_context *context,
440	node_monitor* monitor, uint32 flags,
441	NotificationListener& notificationListener)
442{
443	monitor_listener *listener = new(std::nothrow) monitor_listener;
444	if (listener == NULL) {
445		// no memory for the listener, so remove the monitor as well if needed
446		if (monitor->listeners.IsEmpty())
447			_RemoveMonitor(monitor, flags);
448
449		return B_NO_MEMORY;
450	}
451
452	// initialize listener, and add it to the lists
453	listener->listener = &notificationListener;
454	listener->flags = flags;
455	listener->monitor = monitor;
456
457	monitor->listeners.Add(listener);
458	list_add_link_to_head(&context->node_monitors, listener);
459
460	context->num_monitors++;
461	return B_OK;
462}
463
464
465status_t
466NodeMonitorService::AddListener(io_context *context, dev_t device, ino_t node,
467	uint32 flags, NotificationListener& notificationListener)
468{
469	TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
470		__PRETTY_FUNCTION__, device, node, flags, &notificationListener));
471
472	RecursiveLocker _(fRecursiveLock);
473
474	node_monitor *monitor;
475	status_t status = _GetMonitor(context, device, node, true, &monitor,
476		(flags & B_WATCH_VOLUME) != 0);
477	if (status < B_OK)
478		return status;
479
480	// add listener
481
482	return _AddMonitorListener(context, monitor, flags, notificationListener);
483}
484
485
486status_t
487NodeMonitorService::_UpdateListener(io_context *context, dev_t device,
488	ino_t node, uint32 flags, bool addFlags,
489	NotificationListener& notificationListener)
490{
491	TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
492		__PRETTY_FUNCTION__, device, node, flags, &notificationListener));
493
494	RecursiveLocker _(fRecursiveLock);
495
496	node_monitor *monitor;
497	status_t status = _GetMonitor(context, device, node, false, &monitor,
498		(flags & B_WATCH_VOLUME) != 0);
499	if (status < B_OK)
500		return status;
501
502	MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
503	while (monitor_listener* listener = iterator.Next()) {
504		if (*listener->listener == notificationListener) {
505			if (addFlags)
506				listener->flags |= flags;
507			else
508				listener->flags = flags;
509			return B_OK;
510		}
511	}
512
513	return B_BAD_VALUE;
514}
515
516
517/*!	\brief Given device and node ID and a node monitoring event mask, the
518		   function checks whether there are listeners interested in any of
519		   the events for that node and, if so, adds the respective listener
520		   list to a supplied array of listener lists.
521
522	Note, that in general not all of the listeners in an appended list will be
523	interested in the events, but it is guaranteed that
524	interested_monitor_listener_list::first_listener is indeed
525	the first listener in the list, that is interested.
526
527	\param device The ID of the mounted FS, the node lives in.
528	\param node The ID of the node.
529	\param flags The mask specifying the events occurred for the given node
530		   (a combination of \c B_WATCH_* constants).
531	\param interestedListeners An array of listener lists. If there are
532		   interested listeners for the node, the list will be appended to
533		   this array.
534	\param interestedListenerCount The number of elements in the
535		   \a interestedListeners array. Will be incremented, if a list is
536		   appended.
537*/
538void
539NodeMonitorService::_GetInterestedMonitorListeners(dev_t device, ino_t node,
540	uint32 flags, interested_monitor_listener_list *interestedListeners,
541	int32 &interestedListenerCount)
542{
543	// get the monitor for the node
544	node_monitor *monitor = _MonitorFor(device, node, false);
545	if (monitor == NULL)
546		return;
547
548	// iterate through the listeners until we find one with matching flags
549	MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
550	while (monitor_listener *listener = iterator.Next()) {
551		if (listener->flags & flags) {
552			interested_monitor_listener_list &list
553				= interestedListeners[interestedListenerCount++];
554			list.iterator = iterator;
555			list.flags = flags;
556			return;
557		}
558	}
559}
560
561
562void
563NodeMonitorService::_GetInterestedVolumeListeners(dev_t device, uint32 flags,
564	interested_monitor_listener_list *interestedListeners,
565	int32 &interestedListenerCount)
566{
567	// get the monitor for the node
568	node_monitor *monitor = _MonitorFor(device, -1, true);
569	if (monitor == NULL)
570		return;
571
572	// iterate through the listeners until we find one with matching flags
573	MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
574	while (monitor_listener *listener = iterator.Next()) {
575		if (listener->flags & flags) {
576			interested_monitor_listener_list &list
577				= interestedListeners[interestedListenerCount++];
578			list.iterator = iterator;
579			list.flags = flags;
580			return;
581		}
582	}
583}
584
585
586/*!	\brief Sends a notifcation message to the given listeners.
587	\param message The message to be sent.
588	\param interestedListeners An array of listener lists.
589	\param interestedListenerCount The number of elements in the
590		   \a interestedListeners array.
591	\return
592	- \c B_OK, if everything went fine,
593	- another error code otherwise.
594*/
595status_t
596NodeMonitorService::_SendNotificationMessage(KMessage &message,
597	interested_monitor_listener_list *interestedListeners,
598	int32 interestedListenerCount)
599{
600	// iterate through the lists
601	interested_monitor_listener_list *list = interestedListeners;
602	for (int32 i = 0; i < interestedListenerCount; i++, list++) {
603		// iterate through the listeners
604		MonitorListenerList::Iterator iterator = list->iterator;
605		do {
606			monitor_listener *listener = iterator.Current();
607			if (listener->flags & list->flags)
608				listener->listener->EventOccurred(*this, &message);
609		} while (iterator.Next() != NULL);
610	}
611
612	list = interestedListeners;
613	for (int32 i = 0; i < interestedListenerCount; i++, list++) {
614		// iterate through the listeners
615		do {
616			monitor_listener *listener = list->iterator.Current();
617			if (listener->flags & list->flags)
618				listener->listener->AllListenersNotified(*this);
619		} while (list->iterator.Next() != NULL);
620	}
621
622	return B_OK;
623}
624
625
626/*!	\brief Notifies all interested listeners that an entry has been created
627		   or removed.
628	\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
629	\param device The ID of the mounted FS, the entry lives/lived in.
630	\param directory The entry's parent directory ID.
631	\param name The entry's name.
632	\param node The ID of the node the entry refers/referred to.
633	\return
634	- \c B_OK, if everything went fine,
635	- another error code otherwise.
636*/
637status_t
638NodeMonitorService::NotifyEntryCreatedOrRemoved(int32 opcode, dev_t device,
639	ino_t directory, const char *name, ino_t node)
640{
641	if (!name)
642		return B_BAD_VALUE;
643
644	RecursiveLocker locker(fRecursiveLock);
645
646	// get the lists of all interested listeners
647	interested_monitor_listener_list interestedListeners[3];
648	int32 interestedListenerCount = 0;
649	// ... for the volume
650	_GetInterestedVolumeListeners(device, B_WATCH_NAME,
651		interestedListeners, interestedListenerCount);
652	// ... for the node
653	if (opcode != B_ENTRY_CREATED) {
654		_GetInterestedMonitorListeners(device, node, B_WATCH_NAME,
655			interestedListeners, interestedListenerCount);
656	}
657	// ... for the directory
658	_GetInterestedMonitorListeners(device, directory, B_WATCH_DIRECTORY,
659		interestedListeners, interestedListenerCount);
660
661	if (interestedListenerCount == 0)
662		return B_OK;
663
664	// there are interested listeners: construct the message and send it
665	char messageBuffer[1024];
666	KMessage message;
667	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
668	message.AddInt32("opcode", opcode);
669	message.AddInt32("device", device);
670	message.AddInt64("directory", directory);
671	message.AddInt64("node", node);
672	message.AddString("name", name);			// for "removed" Haiku only
673
674	return _SendNotificationMessage(message, interestedListeners,
675		interestedListenerCount);
676}
677
678
679inline status_t
680NodeMonitorService::NotifyEntryMoved(dev_t device, ino_t fromDirectory,
681	const char *fromName, ino_t toDirectory, const char *toName,
682	ino_t node)
683{
684	if (!fromName || !toName)
685		return B_BAD_VALUE;
686
687	// If node is a mount point, we need to resolve it to the mounted
688	// volume's root node.
689	dev_t nodeDevice = device;
690	vfs_resolve_vnode_to_covering_vnode(device, node, &nodeDevice, &node);
691
692	RecursiveLocker locker(fRecursiveLock);
693
694	// get the lists of all interested listeners
695	interested_monitor_listener_list interestedListeners[4];
696	int32 interestedListenerCount = 0;
697	// ... for the volume
698	_GetInterestedVolumeListeners(device, B_WATCH_NAME,
699		interestedListeners, interestedListenerCount);
700	// ... for the node
701	_GetInterestedMonitorListeners(nodeDevice, node, B_WATCH_NAME,
702		interestedListeners, interestedListenerCount);
703	// ... for the source directory
704	_GetInterestedMonitorListeners(device, fromDirectory, B_WATCH_DIRECTORY,
705		interestedListeners, interestedListenerCount);
706	// ... for the target directory
707	if (toDirectory != fromDirectory) {
708		_GetInterestedMonitorListeners(device, toDirectory, B_WATCH_DIRECTORY,
709			interestedListeners, interestedListenerCount);
710	}
711
712	if (interestedListenerCount == 0)
713		return B_OK;
714
715	// there are interested listeners: construct the message and send it
716	char messageBuffer[1024];
717	KMessage message;
718	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
719	message.AddInt32("opcode", B_ENTRY_MOVED);
720	message.AddInt32("device", device);
721	message.AddInt64("from directory", fromDirectory);
722	message.AddInt64("to directory", toDirectory);
723	message.AddInt32("node device", nodeDevice);	// Haiku only
724	message.AddInt64("node", node);
725	message.AddString("from name", fromName);		// Haiku only
726	message.AddString("name", toName);
727
728	return _SendNotificationMessage(message, interestedListeners,
729		interestedListenerCount);
730}
731
732
733inline status_t
734NodeMonitorService::NotifyStatChanged(dev_t device, ino_t node,
735	uint32 statFields)
736{
737	RecursiveLocker locker(fRecursiveLock);
738
739	// get the lists of all interested listeners
740	interested_monitor_listener_list interestedListeners[3];
741	int32 interestedListenerCount = 0;
742	// ... for the volume
743	_GetInterestedVolumeListeners(device, B_WATCH_STAT,
744		interestedListeners, interestedListenerCount);
745	// ... for the node, depending on whether its an interim update or not
746	_GetInterestedMonitorListeners(device, node,
747		(statFields & B_STAT_INTERIM_UPDATE) != 0
748			? B_WATCH_INTERIM_STAT : B_WATCH_STAT,
749		interestedListeners, interestedListenerCount);
750
751	if (interestedListenerCount == 0)
752		return B_OK;
753
754	// there are interested listeners: construct the message and send it
755	char messageBuffer[1024];
756	KMessage message;
757	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
758	message.AddInt32("opcode", B_STAT_CHANGED);
759	message.AddInt32("device", device);
760	message.AddInt64("node", node);
761	message.AddInt32("fields", statFields);		// Haiku only
762
763	return _SendNotificationMessage(message, interestedListeners,
764		interestedListenerCount);
765}
766
767
768/*!	\brief Notifies all interested listeners that a node attribute has changed.
769	\param device The ID of the mounted FS, the node lives in.
770	\param node The ID of the node.
771	\param attribute The attribute's name.
772	\param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or
773		   \c B_ATTR_CHANGED, indicating what exactly happened to the attribute.
774	\return
775	- \c B_OK, if everything went fine,
776	- another error code otherwise.
777*/
778status_t
779NodeMonitorService::NotifyAttributeChanged(dev_t device, ino_t node,
780	const char *attribute, int32 cause)
781{
782	if (!attribute)
783		return B_BAD_VALUE;
784
785	RecursiveLocker locker(fRecursiveLock);
786
787	// get the lists of all interested listeners
788	interested_monitor_listener_list interestedListeners[3];
789	int32 interestedListenerCount = 0;
790	// ... for the volume
791	_GetInterestedVolumeListeners(device, B_WATCH_ATTR,
792		interestedListeners, interestedListenerCount);
793	// ... for the node
794	_GetInterestedMonitorListeners(device, node, B_WATCH_ATTR,
795		interestedListeners, interestedListenerCount);
796
797	if (interestedListenerCount == 0)
798		return B_OK;
799
800	// there are interested listeners: construct the message and send it
801	char messageBuffer[1024];
802	KMessage message;
803	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
804	message.AddInt32("opcode", B_ATTR_CHANGED);
805	message.AddInt32("device", device);
806	message.AddInt64("node", node);
807	message.AddString("attr", attribute);
808	message.AddInt32("cause", cause);		// Haiku only
809
810	return _SendNotificationMessage(message, interestedListeners,
811		interestedListenerCount);
812}
813
814
815inline status_t
816NodeMonitorService::NotifyUnmount(dev_t device)
817{
818	TRACE(("unmounted device: %ld\n", device));
819
820	RecursiveLocker locker(fRecursiveLock);
821
822	// get the lists of all interested listeners
823	interested_monitor_listener_list interestedListeners[3];
824	int32 interestedListenerCount = 0;
825	_GetInterestedMonitorListeners(-1, -1, B_WATCH_MOUNT,
826		interestedListeners, interestedListenerCount);
827
828	if (interestedListenerCount == 0)
829		return B_OK;
830
831	// there are interested listeners: construct the message and send it
832	char messageBuffer[96];
833	KMessage message;
834	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
835	message.AddInt32("opcode", B_DEVICE_UNMOUNTED);
836	message.AddInt32("device", device);
837
838	return _SendNotificationMessage(message, interestedListeners,
839		interestedListenerCount);
840}
841
842
843inline status_t
844NodeMonitorService::NotifyMount(dev_t device, dev_t parentDevice,
845	ino_t parentDirectory)
846{
847	TRACE(("mounted device: %ld, parent %ld:%Ld\n", device, parentDevice,
848		parentDirectory));
849
850	RecursiveLocker locker(fRecursiveLock);
851
852	// get the lists of all interested listeners
853	interested_monitor_listener_list interestedListeners[3];
854	int32 interestedListenerCount = 0;
855	_GetInterestedMonitorListeners(-1, -1, B_WATCH_MOUNT,
856		interestedListeners, interestedListenerCount);
857
858	if (interestedListenerCount == 0)
859		return B_OK;
860
861	// there are interested listeners: construct the message and send it
862	char messageBuffer[128];
863	KMessage message;
864	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
865	message.AddInt32("opcode", B_DEVICE_MOUNTED);
866	message.AddInt32("new device", device);
867	message.AddInt32("device", parentDevice);
868	message.AddInt64("directory", parentDirectory);
869
870	return _SendNotificationMessage(message, interestedListeners,
871		interestedListenerCount);
872}
873
874
875inline status_t
876NodeMonitorService::RemoveListeners(io_context *context)
877{
878	RecursiveLocker locker(fRecursiveLock);
879
880	while (!list_is_empty(&context->node_monitors)) {
881		// the _RemoveListener() method will also free the node_monitor
882		// if it doesn't have any listeners attached anymore
883		_RemoveListener(
884			(monitor_listener*)list_get_first_item(&context->node_monitors));
885	}
886
887	return B_OK;
888}
889
890
891status_t
892NodeMonitorService::AddListener(const KMessage* eventSpecifier,
893	NotificationListener& listener)
894{
895	if (eventSpecifier == NULL)
896		return B_BAD_VALUE;
897
898	io_context *context = get_current_io_context(
899		eventSpecifier->GetBool("kernel", true));
900
901	dev_t device = eventSpecifier->GetInt32("device", -1);
902	ino_t node = eventSpecifier->GetInt64("node", -1);
903	uint32 flags = eventSpecifier->GetInt32("flags", 0);
904
905	return AddListener(context, device, node, flags, listener);
906}
907
908
909status_t
910NodeMonitorService::UpdateListener(const KMessage* eventSpecifier,
911	NotificationListener& listener)
912{
913	if (eventSpecifier == NULL)
914		return B_BAD_VALUE;
915
916	io_context *context = get_current_io_context(
917		eventSpecifier->GetBool("kernel", true));
918
919	dev_t device = eventSpecifier->GetInt32("device", -1);
920	ino_t node = eventSpecifier->GetInt64("node", -1);
921	uint32 flags = eventSpecifier->GetInt32("flags", 0);
922	bool addFlags = eventSpecifier->GetBool("add flags", false);
923
924	return _UpdateListener(context, device, node, flags, addFlags, listener);
925}
926
927
928status_t
929NodeMonitorService::RemoveListener(const KMessage* eventSpecifier,
930	NotificationListener& listener)
931{
932	if (eventSpecifier == NULL)
933		return B_BAD_VALUE;
934
935	io_context *context = get_current_io_context(
936		eventSpecifier->GetBool("kernel", true));
937
938	dev_t device = eventSpecifier->GetInt32("device", -1);
939	ino_t node = eventSpecifier->GetInt64("node", -1);
940
941	return RemoveListener(context, device, node, listener);
942}
943
944
945status_t
946NodeMonitorService::RemoveListener(io_context *context, dev_t device,
947	ino_t node, NotificationListener& notificationListener)
948{
949	TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n",
950		__PRETTY_FUNCTION__, device, node, &notificationListener));
951
952	RecursiveLocker _(fRecursiveLock);
953
954	if (_RemoveListener(context, device, node, notificationListener, false)
955		== B_OK)
956		return B_OK;
957
958	return _RemoveListener(context, device, node, notificationListener, true);
959}
960
961
962inline status_t
963NodeMonitorService::RemoveUserListeners(struct io_context *context,
964	port_id port, uint32 token)
965{
966	UserNodeListener userListener(port, token);
967	monitor_listener *listener = NULL;
968	int32 count = 0;
969
970	RecursiveLocker _(fRecursiveLock);
971
972	while ((listener = (monitor_listener*)list_get_next_item(
973			&context->node_monitors, listener)) != NULL) {
974		monitor_listener *removeListener;
975
976		if (*listener->listener != userListener)
977			continue;
978
979		listener = (monitor_listener*)list_get_prev_item(
980			&context->node_monitors, removeListener = listener);
981			// this line sets the listener one item back, allowing us
982			// to remove its successor (which is saved in "removeListener")
983
984		_RemoveListener(removeListener);
985		count++;
986	}
987
988	return count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
989}
990
991
992status_t
993NodeMonitorService::UpdateUserListener(io_context *context, dev_t device,
994	ino_t node, uint32 flags, UserNodeListener& userListener)
995{
996	TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
997		__PRETTY_FUNCTION__, device, node, flags, &userListener));
998
999	RecursiveLocker _(fRecursiveLock);
1000
1001	node_monitor *monitor;
1002	status_t status = _GetMonitor(context, device, node, true, &monitor,
1003		(flags & B_WATCH_VOLUME) != 0);
1004	if (status < B_OK)
1005		return status;
1006
1007	MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
1008	while (monitor_listener* listener = iterator.Next()) {
1009		if (*listener->listener == userListener) {
1010			listener->flags |= flags;
1011			return B_OK;
1012		}
1013	}
1014
1015	UserNodeListener* copiedListener = new(std::nothrow) UserNodeListener(
1016		userListener);
1017	if (copiedListener == NULL) {
1018		if (monitor->listeners.IsEmpty())
1019			_RemoveMonitor(monitor, flags);
1020		return B_NO_MEMORY;
1021	}
1022
1023	status = _AddMonitorListener(context, monitor, flags, *copiedListener);
1024	if (status != B_OK)
1025		delete copiedListener;
1026
1027	return status;
1028}
1029
1030
1031//	#pragma mark - private kernel API
1032
1033
1034status_t
1035remove_node_monitors(struct io_context *context)
1036{
1037	return sNodeMonitorService.RemoveListeners(context);
1038}
1039
1040
1041status_t
1042node_monitor_init(void)
1043{
1044	new(&sNodeMonitorSender) UserMessagingMessageSender();
1045	new(&sNodeMonitorService) NodeMonitorService();
1046
1047	if (sNodeMonitorService.InitCheck() < B_OK)
1048		panic("initializing node monitor failed\n");
1049
1050	return B_OK;
1051}
1052
1053
1054status_t
1055notify_unmount(dev_t device)
1056{
1057	return sNodeMonitorService.NotifyUnmount(device);
1058}
1059
1060
1061status_t
1062notify_mount(dev_t device, dev_t parentDevice, ino_t parentDirectory)
1063{
1064	return sNodeMonitorService.NotifyMount(device, parentDevice,
1065		parentDirectory);
1066}
1067
1068
1069status_t
1070remove_node_listener(dev_t device, ino_t node, NotificationListener& listener)
1071{
1072	return sNodeMonitorService.RemoveListener(get_current_io_context(true),
1073		device, node, listener);
1074}
1075
1076
1077status_t
1078add_node_listener(dev_t device, ino_t node, uint32 flags,
1079	NotificationListener& listener)
1080{
1081	return sNodeMonitorService.AddListener(get_current_io_context(true),
1082		device, node, flags, listener);
1083}
1084
1085
1086//	#pragma mark - public kernel API
1087
1088
1089/*!	\brief Notifies all interested listeners that an entry has been created.
1090  	\param device The ID of the mounted FS, the entry lives in.
1091  	\param directory The entry's parent directory ID.
1092  	\param name The entry's name.
1093  	\param node The ID of the node the entry refers to.
1094  	\return
1095  	- \c B_OK, if everything went fine,
1096  	- another error code otherwise.
1097*/
1098status_t
1099notify_entry_created(dev_t device, ino_t directory, const char *name,
1100	ino_t node)
1101{
1102	return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_CREATED,
1103		device, directory, name, node);
1104}
1105
1106
1107/*!	\brief Notifies all interested listeners that an entry has been removed.
1108  	\param device The ID of the mounted FS, the entry lived in.
1109  	\param directory The entry's former parent directory ID.
1110  	\param name The entry's name.
1111  	\param node The ID of the node the entry referred to.
1112  	\return
1113  	- \c B_OK, if everything went fine,
1114  	- another error code otherwise.
1115*/
1116status_t
1117notify_entry_removed(dev_t device, ino_t directory, const char *name,
1118	ino_t node)
1119{
1120	return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_REMOVED,
1121		device, directory, name, node);
1122}
1123
1124
1125/*!	\brief Notifies all interested listeners that an entry has been moved.
1126  	\param device The ID of the mounted FS, the entry lives in.
1127  	\param fromDirectory The entry's previous parent directory ID.
1128  	\param fromName The entry's previous name.
1129  	\param toDirectory The entry's new parent directory ID.
1130  	\param toName The entry's new name.
1131  	\param node The ID of the node the entry refers to.
1132  	\return
1133  	- \c B_OK, if everything went fine,
1134  	- another error code otherwise.
1135*/
1136status_t
1137notify_entry_moved(dev_t device, ino_t fromDirectory,
1138	const char *fromName, ino_t toDirectory, const char *toName,
1139	ino_t node)
1140{
1141	return sNodeMonitorService.NotifyEntryMoved(device, fromDirectory,
1142		fromName, toDirectory, toName, node);
1143}
1144
1145
1146/*!	\brief Notifies all interested listeners that a node's stat data have
1147  		   changed.
1148  	\param device The ID of the mounted FS, the node lives in.
1149  	\param node The ID of the node.
1150  	\param statFields A bitwise combination of one or more of the \c B_STAT_*
1151  		   constants defined in <NodeMonitor.h>, indicating what fields of the
1152  		   stat data have changed.
1153  	\return
1154  	- \c B_OK, if everything went fine,
1155  	- another error code otherwise.
1156*/
1157status_t
1158notify_stat_changed(dev_t device, ino_t node, uint32 statFields)
1159{
1160	return sNodeMonitorService.NotifyStatChanged(device, node, statFields);
1161}
1162
1163
1164/*!	\brief Notifies all interested listeners that a node attribute has changed.
1165  	\param device The ID of the mounted FS, the node lives in.
1166  	\param node The ID of the node.
1167  	\param attribute The attribute's name.
1168  	\param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or
1169  		   \c B_ATTR_CHANGED, indicating what exactly happened to the attribute.
1170  	\return
1171  	- \c B_OK, if everything went fine,
1172  	- another error code otherwise.
1173*/
1174status_t
1175notify_attribute_changed(dev_t device, ino_t node, const char *attribute,
1176	int32 cause)
1177{
1178	return sNodeMonitorService.NotifyAttributeChanged(device, node, attribute,
1179		cause);
1180}
1181
1182
1183/*!	\brief Notifies the listener of a live query that an entry has been added
1184  		   to the query (for whatever reason).
1185  	\param port The target port of the listener.
1186  	\param token The BHandler token of the listener.
1187  	\param device The ID of the mounted FS, the entry lives in.
1188  	\param directory The entry's parent directory ID.
1189  	\param name The entry's name.
1190  	\param node The ID of the node the entry refers to.
1191  	\return
1192  	- \c B_OK, if everything went fine,
1193  	- another error code otherwise.
1194*/
1195status_t
1196notify_query_entry_created(port_id port, int32 token, dev_t device,
1197	ino_t directory, const char *name, ino_t node)
1198{
1199	return notify_query_entry_event(B_ENTRY_CREATED, port, token,
1200		device, directory, name, node);
1201}
1202
1203
1204/*!	\brief Notifies the listener of a live query that an entry has been removed
1205  		   from the query (for whatever reason).
1206  	\param port The target port of the listener.
1207  	\param token The BHandler token of the listener.
1208  	\param device The ID of the mounted FS, the entry lives in.
1209  	\param directory The entry's parent directory ID.
1210  	\param name The entry's name.
1211  	\param node The ID of the node the entry refers to.
1212  	\return
1213  	- \c B_OK, if everything went fine,
1214  	- another error code otherwise.
1215*/
1216status_t
1217notify_query_entry_removed(port_id port, int32 token, dev_t device,
1218	ino_t directory, const char *name, ino_t node)
1219{
1220	return notify_query_entry_event(B_ENTRY_REMOVED, port, token,
1221		device, directory, name, node);
1222}
1223
1224
1225/*!	\brief Notifies the listener of a live query that an entry has been changed
1226  		   and is still in the query (for whatever reason).
1227  	\param port The target port of the listener.
1228  	\param token The BHandler token of the listener.
1229  	\param device The ID of the mounted FS, the entry lives in.
1230  	\param directory The entry's parent directory ID.
1231  	\param name The entry's name.
1232  	\param node The ID of the node the entry refers to.
1233  	\return
1234  	- \c B_OK, if everything went fine,
1235  	- another error code otherwise.
1236*/
1237status_t
1238notify_query_attr_changed(port_id port, int32 token, dev_t device,
1239	ino_t directory, const char* name, ino_t node)
1240{
1241	return notify_query_entry_event(B_ATTR_CHANGED, port, token,
1242		device, directory, name, node);
1243}
1244
1245
1246//	#pragma mark - User syscalls
1247
1248
1249status_t
1250_user_stop_notifying(port_id port, uint32 token)
1251{
1252	io_context *context = get_current_io_context(false);
1253
1254	return sNodeMonitorService.RemoveUserListeners(context, port, token);
1255}
1256
1257
1258status_t
1259_user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port,
1260	uint32 token)
1261{
1262	io_context *context = get_current_io_context(false);
1263
1264	UserNodeListener listener(port, token);
1265	return sNodeMonitorService.UpdateUserListener(context, device, node, flags,
1266		listener);
1267}
1268
1269
1270status_t
1271_user_stop_watching(dev_t device, ino_t node, port_id port, uint32 token)
1272{
1273	io_context *context = get_current_io_context(false);
1274
1275	UserNodeListener listener(port, token);
1276	return sNodeMonitorService.RemoveListener(context, device, node,
1277		listener);
1278}
1279
1280