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