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 45 typedef struct monitor_listener monitor_listener; 46 typedef struct node_monitor node_monitor; 47 48 struct 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 56 typedef DoublyLinkedList<monitor_listener, DoublyLinkedListMemberGetLink< 57 monitor_listener, &monitor_listener::monitor_link> > MonitorListenerList; 58 59 struct node_monitor { 60 node_monitor* hash_link; 61 dev_t device; 62 ino_t node; 63 MonitorListenerList listeners; 64 }; 65 66 struct interested_monitor_listener_list { 67 MonitorListenerList::Iterator iterator; 68 uint32 flags; 69 }; 70 71 static UserMessagingMessageSender sNodeMonitorSender; 72 73 class 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 89 class 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 ¬ificationListener); 119 status_t RemoveListener(io_context *context, dev_t device, ino_t node, 120 NotificationListener ¬ificationListener); 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 ¬ificationListener); 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 218 static 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 */ 235 static status_t 236 notify_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 264 NodeMonitorService::NodeMonitorService() 265 { 266 recursive_lock_init(&fRecursiveLock, "node monitor"); 267 } 268 269 270 NodeMonitorService::~NodeMonitorService() 271 { 272 recursive_lock_destroy(&fRecursiveLock); 273 } 274 275 276 status_t 277 NodeMonitorService::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 */ 287 void 288 NodeMonitorService::_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. 299 status_t 300 NodeMonitorService::_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, ¬ificationListener)); 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 */ 331 void 332 NodeMonitorService::_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 */ 357 node_monitor * 358 NodeMonitorService::_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 */ 375 status_t 376 NodeMonitorService::_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 */ 421 monitor_listener* 422 NodeMonitorService::_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 438 status_t 439 NodeMonitorService::_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 = ¬ificationListener; 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 465 status_t 466 NodeMonitorService::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, ¬ificationListener)); 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 486 status_t 487 NodeMonitorService::_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, ¬ificationListener)); 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 */ 538 void 539 NodeMonitorService::_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 562 void 563 NodeMonitorService::_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 */ 595 status_t 596 NodeMonitorService::_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 */ 637 status_t 638 NodeMonitorService::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 679 inline status_t 680 NodeMonitorService::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 733 inline status_t 734 NodeMonitorService::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 */ 778 status_t 779 NodeMonitorService::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 815 inline status_t 816 NodeMonitorService::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 843 inline status_t 844 NodeMonitorService::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 875 inline status_t 876 NodeMonitorService::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 891 status_t 892 NodeMonitorService::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 909 status_t 910 NodeMonitorService::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 928 status_t 929 NodeMonitorService::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 945 status_t 946 NodeMonitorService::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, ¬ificationListener)); 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 962 inline status_t 963 NodeMonitorService::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 context->num_monitors--; 986 count++; 987 } 988 989 return count > 0 ? B_OK : B_ENTRY_NOT_FOUND; 990 } 991 992 993 status_t 994 NodeMonitorService::UpdateUserListener(io_context *context, dev_t device, 995 ino_t node, uint32 flags, UserNodeListener& userListener) 996 { 997 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n", 998 __PRETTY_FUNCTION__, device, node, flags, &userListener)); 999 1000 RecursiveLocker _(fRecursiveLock); 1001 1002 node_monitor *monitor; 1003 status_t status = _GetMonitor(context, device, node, true, &monitor, 1004 (flags & B_WATCH_VOLUME) != 0); 1005 if (status < B_OK) 1006 return status; 1007 1008 MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator(); 1009 while (monitor_listener* listener = iterator.Next()) { 1010 if (*listener->listener == userListener) { 1011 listener->flags |= flags; 1012 return B_OK; 1013 } 1014 } 1015 1016 UserNodeListener* copiedListener = new(std::nothrow) UserNodeListener( 1017 userListener); 1018 if (copiedListener == NULL) { 1019 if (monitor->listeners.IsEmpty()) 1020 _RemoveMonitor(monitor, flags); 1021 return B_NO_MEMORY; 1022 } 1023 1024 status = _AddMonitorListener(context, monitor, flags, *copiedListener); 1025 if (status != B_OK) 1026 delete copiedListener; 1027 1028 return status; 1029 } 1030 1031 1032 // #pragma mark - private kernel API 1033 1034 1035 status_t 1036 remove_node_monitors(struct io_context *context) 1037 { 1038 return sNodeMonitorService.RemoveListeners(context); 1039 } 1040 1041 1042 status_t 1043 node_monitor_init(void) 1044 { 1045 new(&sNodeMonitorSender) UserMessagingMessageSender(); 1046 new(&sNodeMonitorService) NodeMonitorService(); 1047 1048 if (sNodeMonitorService.InitCheck() < B_OK) 1049 panic("initializing node monitor failed\n"); 1050 1051 return B_OK; 1052 } 1053 1054 1055 status_t 1056 notify_unmount(dev_t device) 1057 { 1058 return sNodeMonitorService.NotifyUnmount(device); 1059 } 1060 1061 1062 status_t 1063 notify_mount(dev_t device, dev_t parentDevice, ino_t parentDirectory) 1064 { 1065 return sNodeMonitorService.NotifyMount(device, parentDevice, 1066 parentDirectory); 1067 } 1068 1069 1070 status_t 1071 remove_node_listener(dev_t device, ino_t node, NotificationListener& listener) 1072 { 1073 return sNodeMonitorService.RemoveListener(get_current_io_context(true), 1074 device, node, listener); 1075 } 1076 1077 1078 status_t 1079 add_node_listener(dev_t device, ino_t node, uint32 flags, 1080 NotificationListener& listener) 1081 { 1082 return sNodeMonitorService.AddListener(get_current_io_context(true), 1083 device, node, flags, listener); 1084 } 1085 1086 1087 // #pragma mark - public kernel API 1088 1089 1090 /*! \brief Notifies all interested listeners that an entry has been created. 1091 \param device The ID of the mounted FS, the entry lives in. 1092 \param directory The entry's parent directory ID. 1093 \param name The entry's name. 1094 \param node The ID of the node the entry refers to. 1095 \return 1096 - \c B_OK, if everything went fine, 1097 - another error code otherwise. 1098 */ 1099 status_t 1100 notify_entry_created(dev_t device, ino_t directory, const char *name, 1101 ino_t node) 1102 { 1103 return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_CREATED, 1104 device, directory, name, node); 1105 } 1106 1107 1108 /*! \brief Notifies all interested listeners that an entry has been removed. 1109 \param device The ID of the mounted FS, the entry lived in. 1110 \param directory The entry's former parent directory ID. 1111 \param name The entry's name. 1112 \param node The ID of the node the entry referred to. 1113 \return 1114 - \c B_OK, if everything went fine, 1115 - another error code otherwise. 1116 */ 1117 status_t 1118 notify_entry_removed(dev_t device, ino_t directory, const char *name, 1119 ino_t node) 1120 { 1121 return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_REMOVED, 1122 device, directory, name, node); 1123 } 1124 1125 1126 /*! \brief Notifies all interested listeners that an entry has been moved. 1127 \param device The ID of the mounted FS, the entry lives in. 1128 \param fromDirectory The entry's previous parent directory ID. 1129 \param fromName The entry's previous name. 1130 \param toDirectory The entry's new parent directory ID. 1131 \param toName The entry's new name. 1132 \param node The ID of the node the entry refers to. 1133 \return 1134 - \c B_OK, if everything went fine, 1135 - another error code otherwise. 1136 */ 1137 status_t 1138 notify_entry_moved(dev_t device, ino_t fromDirectory, 1139 const char *fromName, ino_t toDirectory, const char *toName, 1140 ino_t node) 1141 { 1142 return sNodeMonitorService.NotifyEntryMoved(device, fromDirectory, 1143 fromName, toDirectory, toName, node); 1144 } 1145 1146 1147 /*! \brief Notifies all interested listeners that a node's stat data have 1148 changed. 1149 \param device The ID of the mounted FS, the node lives in. 1150 \param node The ID of the node. 1151 \param statFields A bitwise combination of one or more of the \c B_STAT_* 1152 constants defined in <NodeMonitor.h>, indicating what fields of the 1153 stat data have changed. 1154 \return 1155 - \c B_OK, if everything went fine, 1156 - another error code otherwise. 1157 */ 1158 status_t 1159 notify_stat_changed(dev_t device, ino_t node, uint32 statFields) 1160 { 1161 return sNodeMonitorService.NotifyStatChanged(device, node, statFields); 1162 } 1163 1164 1165 /*! \brief Notifies all interested listeners that a node attribute has changed. 1166 \param device The ID of the mounted FS, the node lives in. 1167 \param node The ID of the node. 1168 \param attribute The attribute's name. 1169 \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or 1170 \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. 1171 \return 1172 - \c B_OK, if everything went fine, 1173 - another error code otherwise. 1174 */ 1175 status_t 1176 notify_attribute_changed(dev_t device, ino_t node, const char *attribute, 1177 int32 cause) 1178 { 1179 return sNodeMonitorService.NotifyAttributeChanged(device, node, attribute, 1180 cause); 1181 } 1182 1183 1184 /*! \brief Notifies the listener of a live query that an entry has been added 1185 to the query (for whatever reason). 1186 \param port The target port of the listener. 1187 \param token The BHandler token of the listener. 1188 \param device The ID of the mounted FS, the entry lives in. 1189 \param directory The entry's parent directory ID. 1190 \param name The entry's name. 1191 \param node The ID of the node the entry refers to. 1192 \return 1193 - \c B_OK, if everything went fine, 1194 - another error code otherwise. 1195 */ 1196 status_t 1197 notify_query_entry_created(port_id port, int32 token, dev_t device, 1198 ino_t directory, const char *name, ino_t node) 1199 { 1200 return notify_query_entry_event(B_ENTRY_CREATED, port, token, 1201 device, directory, name, node); 1202 } 1203 1204 1205 /*! \brief Notifies the listener of a live query that an entry has been removed 1206 from the query (for whatever reason). 1207 \param port The target port of the listener. 1208 \param token The BHandler token of the listener. 1209 \param device The ID of the mounted FS, the entry lives in. 1210 \param directory The entry's parent directory ID. 1211 \param name The entry's name. 1212 \param node The ID of the node the entry refers to. 1213 \return 1214 - \c B_OK, if everything went fine, 1215 - another error code otherwise. 1216 */ 1217 status_t 1218 notify_query_entry_removed(port_id port, int32 token, dev_t device, 1219 ino_t directory, const char *name, ino_t node) 1220 { 1221 return notify_query_entry_event(B_ENTRY_REMOVED, port, token, 1222 device, directory, name, node); 1223 } 1224 1225 1226 /*! \brief Notifies the listener of a live query that an entry has been changed 1227 and is still in the query (for whatever reason). 1228 \param port The target port of the listener. 1229 \param token The BHandler token of the listener. 1230 \param device The ID of the mounted FS, the entry lives in. 1231 \param directory The entry's parent directory ID. 1232 \param name The entry's name. 1233 \param node The ID of the node the entry refers to. 1234 \return 1235 - \c B_OK, if everything went fine, 1236 - another error code otherwise. 1237 */ 1238 status_t 1239 notify_query_attr_changed(port_id port, int32 token, dev_t device, 1240 ino_t directory, const char* name, ino_t node) 1241 { 1242 return notify_query_entry_event(B_ATTR_CHANGED, port, token, 1243 device, directory, name, node); 1244 } 1245 1246 1247 // #pragma mark - User syscalls 1248 1249 1250 // TODO: We should verify that the port specified in the syscalls does actually 1251 // belong to the calling team. The situation is complicated by the fact that a 1252 // port can be transferred to another team. Consequently we should remove all 1253 // associated monitor listeners when a port is transferred/deleted. 1254 1255 1256 status_t 1257 _user_stop_notifying(port_id port, uint32 token) 1258 { 1259 io_context *context = get_current_io_context(false); 1260 1261 return sNodeMonitorService.RemoveUserListeners(context, port, token); 1262 } 1263 1264 1265 status_t 1266 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, 1267 uint32 token) 1268 { 1269 io_context *context = get_current_io_context(false); 1270 1271 UserNodeListener listener(port, token); 1272 return sNodeMonitorService.UpdateUserListener(context, device, node, flags, 1273 listener); 1274 } 1275 1276 1277 status_t 1278 _user_stop_watching(dev_t device, ino_t node, port_id port, uint32 token) 1279 { 1280 io_context *context = get_current_io_context(false); 1281 1282 UserNodeListener listener(port, token); 1283 return sNodeMonitorService.RemoveListener(context, device, node, 1284 listener); 1285 } 1286 1287