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 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 status_t 1250 _user_stop_notifying(port_id port, uint32 token) 1251 { 1252 io_context *context = get_current_io_context(false); 1253 1254 return sNodeMonitorService.RemoveUserListeners(context, port, token); 1255 } 1256 1257 1258 status_t 1259 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, 1260 uint32 token) 1261 { 1262 io_context *context = get_current_io_context(false); 1263 1264 UserNodeListener listener(port, token); 1265 return sNodeMonitorService.UpdateUserListener(context, device, node, flags, 1266 listener); 1267 } 1268 1269 1270 status_t 1271 _user_stop_watching(dev_t device, ino_t node, port_id port, uint32 token) 1272 { 1273 io_context *context = get_current_io_context(false); 1274 1275 UserNodeListener listener(port, token); 1276 return sNodeMonitorService.RemoveListener(context, device, node, 1277 listener); 1278 } 1279 1280