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.
6 * Distributed under the terms of the MIT License.
10 #include <fs/node_monitor.h>
16 #include <NodeMonitor.h>
20 #include <messaging.h>
21 #include <Notifications.h>
23 #include <util/AutoLock.h>
24 #include <util/DoublyLinkedList.h>
25 #include <util/KMessage.h>
26 #include <util/list.h>
28 #include "node_monitor_private.h"
32 //#define TRACE_MONITOR
34 # define TRACE(x) dprintf x
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
45 typedef struct monitor_listener monitor_listener
;
46 typedef struct node_monitor node_monitor
;
48 struct monitor_listener
{
49 list_link context_link
;
50 DoublyLinkedListLink
<monitor_listener
> monitor_link
;
51 NotificationListener
*listener
;
53 node_monitor
*monitor
;
56 typedef DoublyLinkedList
<monitor_listener
, DoublyLinkedListMemberGetLink
<
57 monitor_listener
, &monitor_listener::monitor_link
> > MonitorListenerList
;
60 node_monitor
* hash_link
;
63 MonitorListenerList listeners
;
66 struct interested_monitor_listener_list
{
67 MonitorListenerList::Iterator iterator
;
71 static UserMessagingMessageSender sNodeMonitorSender
;
73 class UserNodeListener
: public UserMessagingListener
{
75 UserNodeListener(port_id port
, int32 token
)
76 : UserMessagingListener(sNodeMonitorSender
, port
, token
)
80 bool operator==(const NotificationListener
& _other
) const
82 const UserNodeListener
* other
83 = dynamic_cast<const UserNodeListener
*>(&_other
);
84 return other
!= NULL
&& other
->Port() == Port()
85 && other
->Token() == Token();
89 class NodeMonitorService
: public NotificationService
{
92 virtual ~NodeMonitorService();
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
,
101 status_t
NotifyStatChanged(dev_t device
, ino_t directory
, ino_t node
,
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
);
109 status_t
RemoveListeners(io_context
*context
);
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
);
118 status_t
AddListener(io_context
*context
, dev_t device
, ino_t node
,
119 uint32 flags
, NotificationListener
¬ificationListener
);
120 status_t
RemoveListener(io_context
*context
, dev_t device
, ino_t node
,
121 NotificationListener
¬ificationListener
);
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
);
128 virtual const char* Name() { return "node monitor"; }
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
¬ificationListener
);
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
);
160 struct monitor_hash_key
{
165 struct HashDefinition
{
166 typedef monitor_hash_key
* KeyType
;
167 typedef node_monitor ValueType
;
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
); }
174 bool Compare(monitor_hash_key
* key
, node_monitor
*monitor
) const
176 return key
->device
== monitor
->device
177 && key
->node
== monitor
->node
;
180 node_monitor
*& GetLink(node_monitor
* monitor
) const
181 { return monitor
->hash_link
; }
183 uint32
_Hash(dev_t device
, ino_t node
) const
185 return ((uint32
)(node
>> 32) + (uint32
)node
) ^ (uint32
)device
;
189 typedef BOpenHashTable
<HashDefinition
> MonitorHash
;
191 struct VolumeHashDefinition
{
192 typedef dev_t KeyType
;
193 typedef node_monitor ValueType
;
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
); }
200 bool Compare(dev_t key
, node_monitor
*monitor
) const
202 return key
== monitor
->device
;
205 node_monitor
*& GetLink(node_monitor
* monitor
) const
206 { return monitor
->hash_link
; }
208 uint32
_Hash(dev_t device
) const
210 return (uint32
)(device
>> 16) + (uint16
)device
;
214 typedef BOpenHashTable
<VolumeHashDefinition
> VolumeMonitorHash
;
216 MonitorHash fMonitors
;
217 VolumeMonitorHash fVolumeMonitors
;
218 recursive_lock fRecursiveLock
;
221 static NodeMonitorService sNodeMonitorService
;
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
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.
235 - \c B_OK, if everything went fine,
236 - another error code otherwise.
239 notify_query_entry_event(int32 opcode
, port_id port
, int32 token
,
240 dev_t device
, ino_t directory
, const char *name
, ino_t node
)
245 // construct the message
246 char messageBuffer
[1024];
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
);
256 messaging_target target
;
258 target
.token
= token
;
260 return send_message(&message
, &target
, 1);
264 // #pragma mark - NodeMonitorService
267 NodeMonitorService::NodeMonitorService()
269 recursive_lock_init(&fRecursiveLock
, "node monitor");
273 NodeMonitorService::~NodeMonitorService()
275 recursive_lock_destroy(&fRecursiveLock
);
280 NodeMonitorService::InitCheck()
286 /*! Removes the specified node_monitor from the hashtable
288 Must be called with monitors lock hold.
291 NodeMonitorService::_RemoveMonitor(node_monitor
*monitor
, uint32 flags
)
293 if ((flags
& B_WATCH_VOLUME
) != 0)
294 fVolumeMonitors
.Remove(monitor
);
296 fMonitors
.Remove(monitor
);
301 //! Helper function for the RemoveListener function.
303 NodeMonitorService::_RemoveListener(io_context
*context
, dev_t device
,
304 ino_t node
, NotificationListener
& notificationListener
,
305 bool isVolumeListener
)
307 TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n",
308 __PRETTY_FUNCTION__
, device
, node
, ¬ificationListener
));
310 RecursiveLocker
_(fRecursiveLock
);
312 // get the monitor for this device/node pair
313 node_monitor
*monitor
= _MonitorFor(device
, node
, isVolumeListener
);
317 // see if it has the listener we are looking for
318 monitor_listener
* listener
= _MonitorListenerFor(monitor
,
319 notificationListener
);
320 if (listener
== NULL
)
323 _RemoveListener(listener
);
324 context
->num_monitors
--;
330 /*! Removes the specified monitor_listener from all lists
332 Must be called with monitors lock hold.
335 NodeMonitorService::_RemoveListener(monitor_listener
*listener
)
337 uint32 flags
= listener
->flags
;
338 node_monitor
*monitor
= listener
->monitor
;
340 // remove it from the listener and I/O context lists
341 monitor
->listeners
.Remove(listener
);
342 list_remove_link(listener
);
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
;
352 if (monitor
->listeners
.IsEmpty())
353 _RemoveMonitor(monitor
, flags
);
357 /*! Returns the monitor that matches the specified device/node pair.
358 Must be called with monitors lock hold.
361 NodeMonitorService::_MonitorFor(dev_t device
, ino_t node
, bool isVolumeListener
)
363 if (isVolumeListener
)
364 return fVolumeMonitors
.Lookup(device
);
366 struct monitor_hash_key key
;
370 return fMonitors
.Lookup(&key
);
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.
379 NodeMonitorService::_GetMonitor(io_context
*context
, dev_t device
, ino_t node
,
380 bool addIfNecessary
, node_monitor
** _monitor
, bool isVolumeListener
)
382 node_monitor
* monitor
= _MonitorFor(device
, node
, isVolumeListener
);
383 if (monitor
!= NULL
) {
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.
397 // create new monitor
398 monitor
= new(std::nothrow
) node_monitor
;
402 // initialize monitor
403 monitor
->device
= device
;
404 monitor
->node
= node
;
407 if (isVolumeListener
)
408 status
= fVolumeMonitors
.Insert(monitor
);
410 status
= fMonitors
.Insert(monitor
);
421 /*! Returns the listener that matches the specified port/token pair.
422 Must be called with monitors lock hold.
425 NodeMonitorService::_MonitorListenerFor(node_monitor
* monitor
,
426 NotificationListener
& notificationListener
)
428 MonitorListenerList::Iterator iterator
429 = monitor
->listeners
.GetIterator();
431 while (monitor_listener
* listener
= iterator
.Next()) {
432 // does this listener match?
433 if (*listener
->listener
== notificationListener
)
442 NodeMonitorService::_AddMonitorListener(io_context
*context
,
443 node_monitor
* monitor
, uint32 flags
,
444 NotificationListener
& notificationListener
)
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
);
455 // initialize listener, and add it to the lists
456 listener
->listener
= ¬ificationListener
;
457 listener
->flags
= flags
;
458 listener
->monitor
= monitor
;
460 monitor
->listeners
.Add(listener
);
461 list_add_link_to_head(&context
->node_monitors
, listener
);
463 context
->num_monitors
++;
469 NodeMonitorService::AddListener(io_context
*context
, dev_t device
, ino_t node
,
470 uint32 flags
, NotificationListener
& notificationListener
)
472 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
473 __PRETTY_FUNCTION__
, device
, node
, flags
, ¬ificationListener
));
475 RecursiveLocker
_(fRecursiveLock
);
477 node_monitor
*monitor
;
478 status_t status
= _GetMonitor(context
, device
, node
, true, &monitor
,
479 (flags
& B_WATCH_VOLUME
) != 0);
485 return _AddMonitorListener(context
, monitor
, flags
, notificationListener
);
490 NodeMonitorService::_UpdateListener(io_context
*context
, dev_t device
,
491 ino_t node
, uint32 flags
, bool addFlags
,
492 NotificationListener
& notificationListener
)
494 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
495 __PRETTY_FUNCTION__
, device
, node
, flags
, ¬ificationListener
));
497 RecursiveLocker
_(fRecursiveLock
);
499 node_monitor
*monitor
;
500 status_t status
= _GetMonitor(context
, device
, node
, false, &monitor
,
501 (flags
& B_WATCH_VOLUME
) != 0);
505 MonitorListenerList::Iterator iterator
= monitor
->listeners
.GetIterator();
506 while (monitor_listener
* listener
= iterator
.Next()) {
507 if (*listener
->listener
== notificationListener
) {
509 listener
->flags
|= flags
;
511 listener
->flags
= flags
;
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.
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.
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
537 \param interestedListenerCount The number of elements in the
538 \a interestedListeners array. Will be incremented, if a list is
542 NodeMonitorService::_GetInterestedMonitorListeners(dev_t device
, ino_t node
,
543 uint32 flags
, interested_monitor_listener_list
*interestedListeners
,
544 int32
&interestedListenerCount
)
546 // get the monitor for the node
547 node_monitor
*monitor
= _MonitorFor(device
, node
, false);
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
;
566 NodeMonitorService::_GetInterestedVolumeListeners(dev_t device
, uint32 flags
,
567 interested_monitor_listener_list
*interestedListeners
,
568 int32
&interestedListenerCount
)
570 // get the monitor for the node
571 node_monitor
*monitor
= _MonitorFor(device
, -1, true);
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
;
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.
595 - \c B_OK, if everything went fine,
596 - another error code otherwise.
599 NodeMonitorService::_SendNotificationMessage(KMessage
&message
,
600 interested_monitor_listener_list
*interestedListeners
,
601 int32 interestedListenerCount
)
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
;
609 monitor_listener
*listener
= iterator
.Current();
610 if (listener
->flags
& list
->flags
)
611 listener
->listener
->EventOccurred(*this, &message
);
612 } while (iterator
.Next() != NULL
);
615 list
= interestedListeners
;
616 for (int32 i
= 0; i
< interestedListenerCount
; i
++, list
++) {
617 // iterate through the listeners
619 monitor_listener
*listener
= list
->iterator
.Current();
620 if (listener
->flags
& list
->flags
)
621 listener
->listener
->AllListenersNotified(*this);
622 } while (list
->iterator
.Next() != NULL
);
629 /*! \brief Resolves the device/directory node pair to the node it's covered
633 NodeMonitorService::_ResolveMountPoint(dev_t device
, ino_t directory
,
634 dev_t
& parentDevice
, ino_t
& parentDirectory
)
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
);
643 if (status
!= B_OK
) {
644 dprintf("Resolving mount point %" B_PRIdDEV
":%" B_PRIdINO
" failed!"
645 "\n", device
, directory
);
650 /*! \brief Notifies all interested listeners that an entry has been created
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.
658 - \c B_OK, if everything went fine,
659 - another error code otherwise.
662 NodeMonitorService::NotifyEntryCreatedOrRemoved(int32 opcode
, dev_t device
,
663 ino_t directory
, const char *name
, ino_t node
)
668 RecursiveLocker
locker(fRecursiveLock
);
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
);
677 if (opcode
!= B_ENTRY_CREATED
) {
678 _GetInterestedMonitorListeners(device
, node
, B_WATCH_NAME
,
679 interestedListeners
, interestedListenerCount
);
681 // ... for the directory
682 _GetInterestedMonitorListeners(device
, directory
, B_WATCH_DIRECTORY
,
683 interestedListeners
, interestedListenerCount
);
685 if (interestedListenerCount
== 0)
688 // there are interested listeners: construct the message and send it
689 char messageBuffer
[1024];
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
698 return _SendNotificationMessage(message
, interestedListeners
,
699 interestedListenerCount
);
704 NodeMonitorService::NotifyEntryMoved(dev_t device
, ino_t fromDirectory
,
705 const char *fromName
, ino_t toDirectory
, const char *toName
,
708 if (!fromName
|| !toName
)
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
);
716 RecursiveLocker
locker(fRecursiveLock
);
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
);
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
);
736 if (interestedListenerCount
== 0)
739 // there are interested listeners: construct the message and send it
740 char messageBuffer
[1024];
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
);
752 return _SendNotificationMessage(message
, interestedListeners
,
753 interestedListenerCount
);
758 NodeMonitorService::NotifyStatChanged(dev_t device
, ino_t directory
, ino_t node
,
761 RecursiveLocker
locker(fRecursiveLock
);
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
;
770 // ... for the volume
771 _GetInterestedVolumeListeners(device
, watchFlag
, interestedListeners
,
772 interestedListenerCount
);
773 // ... for the directory
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
,
782 _GetInterestedMonitorListeners(parentDevice
, parentDirectory
,
783 B_WATCH_CHILDREN
| watchFlag
,
784 interestedListeners
, interestedListenerCount
);
786 // ... and for the node
787 _GetInterestedMonitorListeners(device
, node
, watchFlag
,
788 interestedListeners
, interestedListenerCount
);
790 if (interestedListenerCount
== 0)
793 // there are interested listeners: construct the message and send it
794 char messageBuffer
[1024];
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
802 return _SendNotificationMessage(message
, interestedListeners
,
803 interestedListenerCount
);
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.
814 - \c B_OK, if everything went fine,
815 - another error code otherwise.
818 NodeMonitorService::NotifyAttributeChanged(dev_t device
, ino_t directory
,
819 ino_t node
, const char *attribute
, int32 cause
)
824 RecursiveLocker
locker(fRecursiveLock
);
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
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
,
841 _GetInterestedMonitorListeners(parentDevice
, parentDirectory
,
842 B_WATCH_CHILDREN
| B_WATCH_ATTR
,
843 interestedListeners
, interestedListenerCount
);
846 _GetInterestedMonitorListeners(device
, node
, B_WATCH_ATTR
,
847 interestedListeners
, interestedListenerCount
);
849 if (interestedListenerCount
== 0)
852 // there are interested listeners: construct the message and send it
853 char messageBuffer
[1024];
855 message
.SetTo(messageBuffer
, sizeof(messageBuffer
), B_NODE_MONITOR
);
856 message
.AddInt32("opcode", B_ATTR_CHANGED
);
857 message
.AddInt32("device", device
);
859 message
.AddInt64("directory", directory
);
860 message
.AddInt64("node", node
);
861 message
.AddString("attr", attribute
);
862 message
.AddInt32("cause", cause
); // Haiku only
864 return _SendNotificationMessage(message
, interestedListeners
,
865 interestedListenerCount
);
870 NodeMonitorService::NotifyUnmount(dev_t device
)
872 TRACE(("unmounted device: %ld\n", device
));
874 RecursiveLocker
locker(fRecursiveLock
);
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
);
882 if (interestedListenerCount
== 0)
885 // there are interested listeners: construct the message and send it
886 char messageBuffer
[96];
888 message
.SetTo(messageBuffer
, sizeof(messageBuffer
), B_NODE_MONITOR
);
889 message
.AddInt32("opcode", B_DEVICE_UNMOUNTED
);
890 message
.AddInt32("device", device
);
892 return _SendNotificationMessage(message
, interestedListeners
,
893 interestedListenerCount
);
898 NodeMonitorService::NotifyMount(dev_t device
, dev_t parentDevice
,
899 ino_t parentDirectory
)
901 TRACE(("mounted device: %ld, parent %ld:%Ld\n", device
, parentDevice
,
904 RecursiveLocker
locker(fRecursiveLock
);
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
);
912 if (interestedListenerCount
== 0)
915 // there are interested listeners: construct the message and send it
916 char messageBuffer
[128];
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
);
924 return _SendNotificationMessage(message
, interestedListeners
,
925 interestedListenerCount
);
930 NodeMonitorService::RemoveListeners(io_context
*context
)
932 RecursiveLocker
locker(fRecursiveLock
);
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
938 (monitor_listener
*)list_get_first_item(&context
->node_monitors
));
946 NodeMonitorService::AddListener(const KMessage
* eventSpecifier
,
947 NotificationListener
& listener
)
949 if (eventSpecifier
== NULL
)
952 io_context
*context
= get_current_io_context(
953 eventSpecifier
->GetBool("kernel", true));
955 dev_t device
= eventSpecifier
->GetInt32("device", -1);
956 ino_t node
= eventSpecifier
->GetInt64("node", -1);
957 uint32 flags
= eventSpecifier
->GetInt32("flags", 0);
959 return AddListener(context
, device
, node
, flags
, listener
);
964 NodeMonitorService::UpdateListener(const KMessage
* eventSpecifier
,
965 NotificationListener
& listener
)
967 if (eventSpecifier
== NULL
)
970 io_context
*context
= get_current_io_context(
971 eventSpecifier
->GetBool("kernel", true));
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);
978 return _UpdateListener(context
, device
, node
, flags
, addFlags
, listener
);
983 NodeMonitorService::RemoveListener(const KMessage
* eventSpecifier
,
984 NotificationListener
& listener
)
986 if (eventSpecifier
== NULL
)
989 io_context
*context
= get_current_io_context(
990 eventSpecifier
->GetBool("kernel", true));
992 dev_t device
= eventSpecifier
->GetInt32("device", -1);
993 ino_t node
= eventSpecifier
->GetInt64("node", -1);
995 return RemoveListener(context
, device
, node
, listener
);
1000 NodeMonitorService::RemoveListener(io_context
*context
, dev_t device
,
1001 ino_t node
, NotificationListener
& notificationListener
)
1003 TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n",
1004 __PRETTY_FUNCTION__
, device
, node
, ¬ificationListener
));
1006 RecursiveLocker
_(fRecursiveLock
);
1008 if (_RemoveListener(context
, device
, node
, notificationListener
, false)
1012 return _RemoveListener(context
, device
, node
, notificationListener
, true);
1017 NodeMonitorService::RemoveUserListeners(struct io_context
*context
,
1018 port_id port
, uint32 token
)
1020 UserNodeListener
userListener(port
, token
);
1021 monitor_listener
*listener
= NULL
;
1024 RecursiveLocker
_(fRecursiveLock
);
1026 while ((listener
= (monitor_listener
*)list_get_next_item(
1027 &context
->node_monitors
, listener
)) != NULL
) {
1028 monitor_listener
*removeListener
;
1030 if (*listener
->listener
!= userListener
)
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")
1038 _RemoveListener(removeListener
);
1039 context
->num_monitors
--;
1043 return count
> 0 ? B_OK
: B_ENTRY_NOT_FOUND
;
1048 NodeMonitorService::UpdateUserListener(io_context
*context
, dev_t device
,
1049 ino_t node
, uint32 flags
, UserNodeListener
& userListener
)
1051 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
1052 __PRETTY_FUNCTION__
, device
, node
, flags
, &userListener
));
1054 RecursiveLocker
_(fRecursiveLock
);
1056 node_monitor
*monitor
;
1057 status_t status
= _GetMonitor(context
, device
, node
, true, &monitor
,
1058 (flags
& B_WATCH_VOLUME
) != 0);
1062 MonitorListenerList::Iterator iterator
= monitor
->listeners
.GetIterator();
1063 while (monitor_listener
* listener
= iterator
.Next()) {
1064 if (*listener
->listener
== userListener
) {
1065 listener
->flags
|= flags
;
1070 UserNodeListener
* copiedListener
= new(std::nothrow
) UserNodeListener(
1072 if (copiedListener
== NULL
) {
1073 if (monitor
->listeners
.IsEmpty())
1074 _RemoveMonitor(monitor
, flags
);
1078 status
= _AddMonitorListener(context
, monitor
, flags
, *copiedListener
);
1080 delete copiedListener
;
1086 // #pragma mark - private kernel API
1090 remove_node_monitors(struct io_context
*context
)
1092 return sNodeMonitorService
.RemoveListeners(context
);
1097 node_monitor_init(void)
1099 new(&sNodeMonitorSender
) UserMessagingMessageSender();
1100 new(&sNodeMonitorService
) NodeMonitorService();
1102 if (sNodeMonitorService
.InitCheck() < B_OK
)
1103 panic("initializing node monitor failed\n");
1110 notify_unmount(dev_t device
)
1112 return sNodeMonitorService
.NotifyUnmount(device
);
1117 notify_mount(dev_t device
, dev_t parentDevice
, ino_t parentDirectory
)
1119 return sNodeMonitorService
.NotifyMount(device
, parentDevice
,
1125 remove_node_listener(dev_t device
, ino_t node
, NotificationListener
& listener
)
1127 return sNodeMonitorService
.RemoveListener(get_current_io_context(true),
1128 device
, node
, listener
);
1133 add_node_listener(dev_t device
, ino_t node
, uint32 flags
,
1134 NotificationListener
& listener
)
1136 return sNodeMonitorService
.AddListener(get_current_io_context(true),
1137 device
, node
, flags
, listener
);
1141 // #pragma mark - public kernel API
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.
1150 - \c B_OK, if everything went fine,
1151 - another error code otherwise.
1154 notify_entry_created(dev_t device
, ino_t directory
, const char *name
,
1157 return sNodeMonitorService
.NotifyEntryCreatedOrRemoved(B_ENTRY_CREATED
,
1158 device
, directory
, name
, node
);
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.
1168 - \c B_OK, if everything went fine,
1169 - another error code otherwise.
1172 notify_entry_removed(dev_t device
, ino_t directory
, const char *name
,
1175 return sNodeMonitorService
.NotifyEntryCreatedOrRemoved(B_ENTRY_REMOVED
,
1176 device
, directory
, name
, node
);
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.
1188 - \c B_OK, if everything went fine,
1189 - another error code otherwise.
1192 notify_entry_moved(dev_t device
, ino_t fromDirectory
,
1193 const char *fromName
, ino_t toDirectory
, const char *toName
,
1196 return sNodeMonitorService
.NotifyEntryMoved(device
, fromDirectory
,
1197 fromName
, toDirectory
, toName
, node
);
1201 /*! \brief Notifies all interested listeners that a node's stat data have
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.
1209 - \c B_OK, if everything went fine,
1210 - another error code otherwise.
1213 notify_stat_changed(dev_t device
, ino_t directory
, ino_t node
,
1216 return sNodeMonitorService
.NotifyStatChanged(device
, directory
, node
,
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.
1228 - \c B_OK, if everything went fine,
1229 - another error code otherwise.
1232 notify_attribute_changed(dev_t device
, ino_t directory
, ino_t node
,
1233 const char *attribute
, int32 cause
)
1235 return sNodeMonitorService
.NotifyAttributeChanged(device
, directory
, node
,
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.
1249 - \c B_OK, if everything went fine,
1250 - another error code otherwise.
1253 notify_query_entry_created(port_id port
, int32 token
, dev_t device
,
1254 ino_t directory
, const char *name
, ino_t node
)
1256 return notify_query_entry_event(B_ENTRY_CREATED
, port
, token
,
1257 device
, directory
, name
, node
);
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.
1270 - \c B_OK, if everything went fine,
1271 - another error code otherwise.
1274 notify_query_entry_removed(port_id port
, int32 token
, dev_t device
,
1275 ino_t directory
, const char *name
, ino_t node
)
1277 return notify_query_entry_event(B_ENTRY_REMOVED
, port
, token
,
1278 device
, directory
, name
, node
);
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.
1291 - \c B_OK, if everything went fine,
1292 - another error code otherwise.
1295 notify_query_attr_changed(port_id port
, int32 token
, dev_t device
,
1296 ino_t directory
, const char* name
, ino_t node
)
1298 return notify_query_entry_event(B_ATTR_CHANGED
, port
, token
,
1299 device
, directory
, name
, node
);
1303 // #pragma mark - User syscalls
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.
1313 _user_stop_notifying(port_id port
, uint32 token
)
1315 io_context
*context
= get_current_io_context(false);
1317 return sNodeMonitorService
.RemoveUserListeners(context
, port
, token
);
1322 _user_start_watching(dev_t device
, ino_t node
, uint32 flags
, port_id port
,
1325 io_context
*context
= get_current_io_context(false);
1327 UserNodeListener
listener(port
, token
);
1328 return sNodeMonitorService
.UpdateUserListener(context
, device
, node
, flags
,
1334 _user_stop_watching(dev_t device
, ino_t node
, port_id port
, uint32 token
)
1336 io_context
*context
= get_current_io_context(false);
1338 UserNodeListener
listener(port
, token
);
1339 return sNodeMonitorService
.RemoveListener(context
, device
, node
,