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