1 /* 2 ** Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the OpenBeOS License. 4 */ 5 6 7 #include <drivers/node_monitor.h> 8 #include <app/AppDefs.h> 9 10 #include <fs/node_monitor.h> 11 #include <vfs.h> 12 #include <fd.h> 13 #include <lock.h> 14 #include <khash.h> 15 #include <messaging.h> 16 #include <util/AutoLock.h> 17 #include <util/KMessage.h> 18 #include <util/list.h> 19 20 #include <malloc.h> 21 #include <stddef.h> 22 23 24 #define TRACE_MONITOR 0 25 #if TRACE_MONITOR 26 # define TRACE(x) dprintf x 27 #else 28 # define TRACE(x) ; 29 #endif 30 31 32 // ToDo: add more fine grained locking - maybe using a ref_count in the 33 // node_monitor structure? 34 // ToDo: implement watching mounts/unmounts 35 // ToDo: return another error code than B_NO_MEMORY if the team's maximum is hit 36 37 38 typedef struct monitor_listener monitor_listener; 39 typedef struct node_monitor node_monitor; 40 41 struct monitor_listener { 42 monitor_listener *next; 43 monitor_listener *prev; 44 list_link monitor_link; 45 port_id port; 46 uint32 token; 47 uint32 flags; 48 node_monitor *monitor; 49 }; 50 51 struct node_monitor { 52 node_monitor *next; 53 mount_id device; 54 vnode_id node; 55 struct list listeners; 56 }; 57 58 struct monitor_hash_key { 59 mount_id device; 60 vnode_id node; 61 }; 62 63 struct interested_monitor_listener_list { 64 struct list *listeners; 65 monitor_listener *first_listener; 66 uint32 flags; 67 }; 68 69 #define MONITORS_HASH_TABLE_SIZE 16 70 71 static hash_table *gMonitorHash; 72 static mutex gMonitorMutex; 73 74 75 static int 76 monitor_compare(void *_monitor, const void *_key) 77 { 78 node_monitor *monitor = (node_monitor*)_monitor; 79 const struct monitor_hash_key *key = (const struct monitor_hash_key*)_key; 80 81 if (monitor->device == key->device && monitor->node == key->node) 82 return 0; 83 84 return -1; 85 } 86 87 88 static uint32 89 monitor_hash(void *_monitor, const void *_key, uint32 range) 90 { 91 node_monitor *monitor = (node_monitor*)_monitor; 92 const struct monitor_hash_key *key = (const struct monitor_hash_key*)_key; 93 94 #define MHASH(device, node) (((uint32)((node) >> 32) + (uint32)(node)) ^ (uint32)(device)) 95 96 if (monitor != NULL) 97 return MHASH(monitor->device, monitor->node) % range; 98 99 return MHASH(key->device, key->node) % range; 100 #undef MHASH 101 } 102 103 104 /** Returns the monitor that matches the specified device/node pair. 105 * Must be called with monitors lock hold. 106 */ 107 108 static node_monitor * 109 get_monitor_for(mount_id device, vnode_id node) 110 { 111 struct monitor_hash_key key; 112 key.device = device; 113 key.node = node; 114 115 return (node_monitor *)hash_lookup(gMonitorHash, &key); 116 } 117 118 119 /** Returns the listener that matches the specified port/token pair. 120 * Must be called with monitors lock hold. 121 */ 122 123 static monitor_listener * 124 get_listener_for(node_monitor *monitor, port_id port, uint32 token) 125 { 126 monitor_listener *listener = NULL; 127 128 while ((listener = (monitor_listener*)list_get_next_item( 129 &monitor->listeners, listener)) != NULL) { 130 // does this listener match? 131 if (listener->port == port && listener->token == token) 132 return listener; 133 } 134 135 return NULL; 136 } 137 138 139 /** Removes the specified node_monitor from the hashtable 140 * and free it. 141 * Must be called with monitors lock hold. 142 */ 143 144 static void 145 remove_monitor(node_monitor *monitor) 146 { 147 hash_remove(gMonitorHash, monitor); 148 free(monitor); 149 } 150 151 152 /** Removes the specified monitor_listener from all lists 153 * and free it. 154 * Must be called with monitors lock hold. 155 */ 156 157 static void 158 remove_listener(monitor_listener *listener) 159 { 160 node_monitor *monitor = listener->monitor; 161 162 // remove it from the listener and I/O context lists 163 list_remove_link(&listener->monitor_link); 164 list_remove_link(listener); 165 166 free(listener); 167 168 if (list_is_empty(&monitor->listeners)) 169 remove_monitor(monitor); 170 } 171 172 173 static status_t 174 add_node_monitor(io_context *context, mount_id device, vnode_id node, 175 uint32 flags, port_id port, uint32 token) 176 { 177 monitor_listener *listener; 178 node_monitor *monitor; 179 status_t status = B_OK; 180 181 TRACE(("add_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n", 182 device, node, flags, port, token)); 183 184 mutex_lock(&gMonitorMutex); 185 186 monitor = get_monitor_for(device, node); 187 if (monitor == NULL) { 188 // check if this team is allowed to have more listeners 189 if (context->num_monitors >= context->max_monitors) { 190 // the BeBook says to return B_NO_MEMORY in this case, but 191 // we should have another one. 192 status = B_NO_MEMORY; 193 goto out; 194 } 195 196 // create new monitor 197 monitor = (node_monitor *)malloc(sizeof(node_monitor)); 198 if (monitor == NULL) { 199 status = B_NO_MEMORY; 200 goto out; 201 } 202 203 // initialize monitor 204 monitor->device = device; 205 monitor->node = node; 206 list_init_etc(&monitor->listeners, offsetof(monitor_listener, monitor_link)); 207 208 hash_insert(gMonitorHash, monitor); 209 } else { 210 // check if the listener is already listening, and 211 // if so, just add the new flags 212 213 listener = get_listener_for(monitor, port, token); 214 if (listener != NULL) { 215 listener->flags |= flags; 216 goto out; 217 } 218 219 // check if this team is allowed to have more listeners 220 if (context->num_monitors >= context->max_monitors) { 221 // the BeBook says to return B_NO_MEMORY in this case, but 222 // we should have another one. 223 status = B_NO_MEMORY; 224 goto out; 225 } 226 } 227 228 // add listener 229 230 listener = (monitor_listener *)malloc(sizeof(monitor_listener)); 231 if (listener == NULL) { 232 // no memory for the listener, so remove the monitor as well if needed 233 if (list_is_empty(&monitor->listeners)) 234 remove_monitor(monitor); 235 236 status = B_NO_MEMORY; 237 goto out; 238 } 239 240 // initialize listener, and add it to the lists 241 listener->port = port; 242 listener->token = token; 243 listener->flags = flags; 244 listener->monitor = monitor; 245 246 list_add_link_to_head(&monitor->listeners, &listener->monitor_link); 247 list_add_link_to_head(&context->node_monitors, listener); 248 249 context->num_monitors++; 250 251 out: 252 mutex_unlock(&gMonitorMutex); 253 return status; 254 } 255 256 257 static status_t 258 remove_node_monitor(struct io_context *context, mount_id device, vnode_id node, 259 uint32 flags, port_id port, uint32 token) 260 { 261 monitor_listener *listener; 262 node_monitor *monitor; 263 status_t status = B_OK; 264 265 TRACE(("remove_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n", 266 device, node, flags, port, token)); 267 268 mutex_lock(&gMonitorMutex); 269 270 // get the monitor for this device/node pair 271 monitor = get_monitor_for(device, node); 272 if (monitor == NULL) { 273 status = B_ENTRY_NOT_FOUND; 274 goto out; 275 } 276 277 // see if it has the listener we are looking for 278 listener = get_listener_for(monitor, port, token); 279 if (listener == NULL) { 280 status = B_ENTRY_NOT_FOUND; 281 goto out; 282 } 283 284 // no flags means remove all flags 285 if (flags == B_STOP_WATCHING) 286 flags = ~0; 287 288 listener->flags &= ~flags; 289 290 // if there aren't anymore flags, remove this listener 291 if (listener->flags == B_STOP_WATCHING) { 292 remove_listener(listener); 293 context->num_monitors--; 294 } 295 296 out: 297 mutex_unlock(&gMonitorMutex); 298 return status; 299 } 300 301 302 static status_t 303 remove_node_monitors_by_target(struct io_context *context, port_id port, uint32 token) 304 { 305 monitor_listener *listener = NULL; 306 int32 count = 0; 307 308 while ((listener = (monitor_listener*)list_get_next_item( 309 &context->node_monitors, listener)) != NULL) { 310 monitor_listener *removeListener; 311 312 if (listener->port != port || listener->token != token) 313 continue; 314 315 listener = (monitor_listener*)list_get_prev_item( 316 &context->node_monitors, removeListener = listener); 317 // this line sets the listener one item back, allowing us 318 // to remove its successor (which is saved in "removeListener") 319 320 remove_listener(removeListener); 321 count++; 322 } 323 324 return count > 0 ? B_OK : B_ENTRY_NOT_FOUND; 325 } 326 327 328 // #pragma mark - 329 // Exported private kernel API 330 331 332 status_t 333 remove_node_monitors(struct io_context *context) 334 { 335 mutex_lock(&gMonitorMutex); 336 337 while (!list_is_empty(&context->node_monitors)) { 338 // the remove_listener() function will also free the node_monitor 339 // if it doesn't have any listeners attached anymore 340 remove_listener( 341 (monitor_listener*)list_get_first_item(&context->node_monitors)); 342 } 343 344 mutex_unlock(&gMonitorMutex); 345 return B_OK; 346 } 347 348 349 status_t 350 node_monitor_init(void) 351 { 352 gMonitorHash = hash_init(MONITORS_HASH_TABLE_SIZE, offsetof(node_monitor, next), 353 &monitor_compare, &monitor_hash); 354 if (gMonitorHash == NULL) 355 panic("node_monitor_init: error creating mounts hash table\n"); 356 357 return mutex_init(&gMonitorMutex, "node monitor"); 358 } 359 360 361 // #pragma mark - 362 // Exported public kernel API 363 364 365 /** \brief Subscribes a target to node and/or mount watching. 366 * 367 * Depending on \a flags, different actions are performed. If flags is \c 0, 368 * mount watching is requested. \a device and \a node must be \c -1 in this 369 * case. Otherwise node watching is requested. \a device and \a node must 370 * refer to a valid node, and \a flags must note contain the flag 371 * \c B_WATCH_MOUNT, but at least one of the other valid flags. 372 * 373 * \param device The device the node resides on (node_ref::device). \c -1, if 374 * only mount watching is requested. 375 * \param node The node ID of the node (node_ref::device). \c -1, if 376 * only mount watching is requested. 377 * \param flags A bit mask composed of the values specified in 378 * <NodeMonitor.h>. 379 * \param port The port of the target (a looper port). 380 * \param handlerToken The token of the target handler. \c -2, if the 381 * preferred handler of the looper is the target. 382 * \return \c B_OK, if everything went fine, another error code otherwise. 383 */ 384 385 status_t 386 start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token) 387 { 388 io_context *context = get_current_io_context(true); 389 390 return add_node_monitor(context, device, node, flags, port, token); 391 } 392 393 394 /** \brief Unsubscribes a target from watching a node. 395 * \param device The device the node resides on (node_ref::device). 396 * \param node The node ID of the node (node_ref::device). 397 * \param flags Which monitors should be removed (B_STOP_WATCHING for all) 398 * \param port The port of the target (a looper port). 399 * \param handlerToken The token of the target handler. \c -2, if the 400 * preferred handler of the looper is the target. 401 * \return \c B_OK, if everything went fine, another error code otherwise. 402 */ 403 404 status_t 405 stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token) 406 { 407 io_context *context = get_current_io_context(true); 408 409 return remove_node_monitor(context, device, node, flags, port, token); 410 } 411 412 413 /** \brief Unsubscribes a target from node and mount monitoring. 414 * \param port The port of the target (a looper port). 415 * \param handlerToken The token of the target handler. \c -2, if the 416 * preferred handler of the looper is the target. 417 * \return \c B_OK, if everything went fine, another error code otherwise. 418 */ 419 420 status_t 421 stop_notifying(port_id port, uint32 token) 422 { 423 io_context *context = get_current_io_context(true); 424 425 return remove_node_monitors_by_target(context, port, token); 426 } 427 428 429 status_t 430 notify_listener(int op, mount_id device, vnode_id parentNode, vnode_id toParentNode, 431 vnode_id node, const char *name) 432 { 433 monitor_listener *listener; 434 node_monitor *monitor; 435 436 TRACE(("notify_listener(op = %d, device = %ld, node = %Ld, parent = %Ld, toParent = %Ld" 437 ", name = \"%s\"\n", op, device, node, parentNode, toParentNode, name)); 438 439 mutex_lock(&gMonitorMutex); 440 441 // check the main "node" 442 443 if ((op == B_ENTRY_MOVED 444 || op == B_ENTRY_REMOVED 445 || op == B_STAT_CHANGED 446 || op == B_ATTR_CHANGED) 447 && (monitor = get_monitor_for(device, node)) != NULL) { 448 // iterate over all listeners for this monitor, and see 449 // if we have to send anything 450 listener = NULL; 451 while ((listener = (monitor_listener*)list_get_next_item( 452 &monitor->listeners, listener)) != NULL) { 453 // do we have a reason to notify this listener? 454 if (((listener->flags & B_WATCH_NAME) != 0 455 && (op == B_ENTRY_MOVED || op == B_ENTRY_REMOVED)) 456 || ((listener->flags & B_WATCH_STAT) != 0 457 && op == B_STAT_CHANGED) 458 || ((listener->flags & B_WATCH_ATTR) != 0 459 && op == B_ATTR_CHANGED)) { 460 // then do it! 461 send_notification(listener->port, listener->token, B_NODE_MONITOR, 462 op, device, 0, parentNode, toParentNode, node, name); 463 } 464 } 465 } 466 467 // check its parent directory 468 469 if ((op == B_ENTRY_MOVED 470 || op == B_ENTRY_REMOVED 471 || op == B_ENTRY_CREATED) 472 && (monitor = get_monitor_for(device, parentNode)) != NULL) { 473 // iterate over all listeners for this monitor, and see 474 // if we have to send anything 475 listener = NULL; 476 while ((listener = (monitor_listener*)list_get_next_item( 477 &monitor->listeners, listener)) != NULL) { 478 // do we have a reason to notify this listener? 479 if ((listener->flags & B_WATCH_DIRECTORY) != 0) { 480 send_notification(listener->port, listener->token, B_NODE_MONITOR, 481 op, device, 0, parentNode, toParentNode, node, name); 482 } 483 } 484 } 485 486 // check its new target parent directory 487 488 if (op == B_ENTRY_MOVED 489 && (monitor = get_monitor_for(device, toParentNode)) != NULL) { 490 // iterate over all listeners for this monitor, and see 491 // if we have to send anything 492 listener = NULL; 493 while ((listener = (monitor_listener*)list_get_next_item( 494 &monitor->listeners, listener)) != NULL) { 495 // do we have a reason to notify this listener? 496 if ((listener->flags & B_WATCH_DIRECTORY) != 0) { 497 send_notification(listener->port, listener->token, B_NODE_MONITOR, 498 B_ENTRY_MOVED, device, 0, parentNode, toParentNode, node, name); 499 } 500 } 501 } 502 503 mutex_unlock(&gMonitorMutex); 504 return B_OK; 505 } 506 507 508 /** \brief Given device and node ID and a node monitoring event mask, the 509 function checks whether there are listeners interested in any of 510 the events for that node and, if so, adds the respective listener 511 list to a supplied array of listener lists. 512 513 Note, that in general not all of the listeners in an appended list will be 514 interested in the events, but it is guaranteed that 515 interested_monitor_listener_list::first_listener is indeed 516 the first listener in the list, that is interested. 517 518 \param device The ID of the mounted FS, the node lives in. 519 \param node The ID of the node. 520 \param flags The mask specifying the events occurred for the given node 521 (a combination of \c B_WATCH_* constants). 522 \param interestedListeners An array of listener lists. If there are 523 interested listeners for the node, the list will be appended to 524 this array. 525 \param interestedListenerCount The number of elements in the 526 \a interestedListeners array. Will be incremented, if a list is 527 appended. 528 */ 529 static 530 void 531 get_interested_monitor_listeners(mount_id device, vnode_id node, 532 uint32 flags, interested_monitor_listener_list *interestedListeners, 533 int32 &interestedListenerCount) 534 { 535 // get the monitor for the node 536 node_monitor *monitor = get_monitor_for(device, node); 537 if (!monitor) 538 return; 539 540 // iterate through the listeners until we find one with matching flags 541 monitor_listener *listener = NULL; 542 while ((listener = (monitor_listener*)list_get_next_item( 543 &monitor->listeners, listener)) != NULL) { 544 if (listener->flags & flags) { 545 interested_monitor_listener_list &list 546 = interestedListeners[interestedListenerCount++]; 547 list.listeners = &monitor->listeners; 548 list.first_listener = listener; 549 list.flags = flags; 550 return; 551 } 552 } 553 } 554 555 556 /** \brief Sends a notifcation message to the given listeners. 557 \param message The message to be sent. 558 \param interestedListeners An array of listener lists. 559 \param interestedListenerCount The number of elements in the 560 \a interestedListeners array. 561 \return 562 - \c B_OK, if everything went fine, 563 - another error code otherwise. 564 */ 565 static 566 status_t 567 send_notification_message(KMessage &message, 568 interested_monitor_listener_list *interestedListeners, 569 int32 interestedListenerCount) 570 { 571 // Since the messaging service supports broadcasting and that is more 572 // efficient than sending the messages individually, we collect the 573 // listener targets in an array and send the message to them at once. 574 const int32 maxTargetCount = 16; 575 messaging_target targets[maxTargetCount]; 576 int32 targetCount = 0; 577 578 // iterate through the lists 579 interested_monitor_listener_list *list = interestedListeners; 580 for (int32 i = 0; i < interestedListenerCount; i++, list++) { 581 // iterate through the listeners 582 monitor_listener *listener = list->first_listener; 583 do { 584 if (listener->flags & list->flags) { 585 // the listener's flags match: add it to the targets 586 messaging_target &target = targets[targetCount++]; 587 target.port = listener->port; 588 target.token = listener->token; 589 590 // if the target array is full, send the message 591 if (targetCount == maxTargetCount) { 592 status_t error = send_message(&message, targets, 593 targetCount); 594 if (error != B_OK) 595 return error; 596 597 targetCount = 0; 598 } 599 } 600 } while ((listener = (monitor_listener*)list_get_next_item( 601 list->listeners, listener)) != NULL); 602 } 603 604 // if any targets are left (the usual case, unless the target array got 605 // full early), send the message 606 if (targetCount > 0) 607 return send_message(&message, targets, targetCount); 608 609 return B_OK; 610 } 611 612 613 /** \brief Notifies all interested listeners that an entry has been created 614 or removed. 615 \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. 616 \param device The ID of the mounted FS, the entry lives/lived in. 617 \param directory The entry's parent directory ID. 618 \param name The entry's name. 619 \param node The ID of the node the entry refers/referred to. 620 \return 621 - \c B_OK, if everything went fine, 622 - another error code otherwise. 623 */ 624 static status_t 625 notify_entry_created_or_removed(int32 opcode, mount_id device, 626 vnode_id directory, const char *name, vnode_id node) 627 { 628 if (!name) 629 return B_BAD_VALUE; 630 631 MutexLocker locker(gMonitorMutex); 632 633 // get the lists of all interested listeners 634 interested_monitor_listener_list interestedListeners[3]; 635 int32 interestedListenerCount = 0; 636 // ... for the node 637 if (opcode != B_ENTRY_CREATED) { 638 get_interested_monitor_listeners(device, node, B_WATCH_NAME, 639 interestedListeners, interestedListenerCount); 640 } 641 // ... for the directory 642 get_interested_monitor_listeners(device, directory, B_WATCH_DIRECTORY, 643 interestedListeners, interestedListenerCount); 644 645 if (interestedListenerCount == 0) 646 return B_OK; 647 648 // there are interested listeners: construct the message and send it 649 char messageBuffer[1024]; 650 KMessage message; 651 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 652 message.AddInt32("opcode", opcode); 653 message.AddInt32("device", device); 654 message.AddInt64("directory", directory); 655 message.AddInt64("node", node); 656 message.AddString("name", name); // for "removed" Haiku only 657 658 return send_notification_message(message, interestedListeners, 659 interestedListenerCount); 660 } 661 662 663 /** \brief Notifies all interested listeners that an entry has been created. 664 \param device The ID of the mounted FS, the entry lives in. 665 \param directory The entry's parent directory ID. 666 \param name The entry's name. 667 \param node The ID of the node the entry refers to. 668 \return 669 - \c B_OK, if everything went fine, 670 - another error code otherwise. 671 */ 672 status_t 673 notify_entry_created(mount_id device, vnode_id directory, const char *name, 674 vnode_id node) 675 { 676 return notify_entry_created_or_removed(B_ENTRY_CREATED, device, 677 directory, name, node); 678 } 679 680 681 /** \brief Notifies all interested listeners that an entry has been removed. 682 \param device The ID of the mounted FS, the entry lived in. 683 \param directory The entry's former parent directory ID. 684 \param name The entry's name. 685 \param node The ID of the node the entry referred to. 686 \return 687 - \c B_OK, if everything went fine, 688 - another error code otherwise. 689 */ 690 status_t 691 notify_entry_removed(mount_id device, vnode_id directory, const char *name, 692 vnode_id node) 693 { 694 return notify_entry_created_or_removed(B_ENTRY_REMOVED, device, 695 directory, name, node); 696 } 697 698 699 /** \brief Notifies all interested listeners that an entry has been moved. 700 \param device The ID of the mounted FS, the entry lives in. 701 \param fromDirectory The entry's previous parent directory ID. 702 \param fromName The entry's previous name. 703 \param toDirectory The entry's new parent directory ID. 704 \param toName The entry's new name. 705 \param node The ID of the node the entry refers to. 706 \return 707 - \c B_OK, if everything went fine, 708 - another error code otherwise. 709 */ 710 status_t 711 notify_entry_moved(mount_id device, vnode_id fromDirectory, 712 const char *fromName, vnode_id toDirectory, const char *toName, 713 vnode_id node) 714 { 715 if (!fromName || !toName) 716 return B_BAD_VALUE; 717 718 // If node is a mount point, we need to resolve it to the mounted 719 // volume's root node. 720 mount_id nodeDevice = device; 721 resolve_mount_point_to_volume_root(device, node, &nodeDevice, &node); 722 723 MutexLocker locker(gMonitorMutex); 724 725 // get the lists of all interested listeners 726 interested_monitor_listener_list interestedListeners[3]; 727 int32 interestedListenerCount = 0; 728 // ... for the node 729 get_interested_monitor_listeners(nodeDevice, node, B_WATCH_NAME, 730 interestedListeners, interestedListenerCount); 731 // ... for the source directory 732 get_interested_monitor_listeners(device, fromDirectory, B_WATCH_DIRECTORY, 733 interestedListeners, interestedListenerCount); 734 // ... for the target directory 735 get_interested_monitor_listeners(device, toDirectory, B_WATCH_DIRECTORY, 736 interestedListeners, interestedListenerCount); 737 738 if (interestedListenerCount == 0) 739 return B_OK; 740 741 // there are interested listeners: construct the message and send it 742 char messageBuffer[1024]; 743 KMessage message; 744 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 745 message.AddInt32("opcode", B_ENTRY_MOVED); 746 message.AddInt32("device", device); 747 message.AddInt64("from directory", fromDirectory); 748 message.AddInt64("to directory", toDirectory); 749 message.AddInt32("node device", nodeDevice); // Haiku only 750 message.AddInt64("node", node); 751 message.AddString("from name", fromName); // Haiku only 752 message.AddString("name", toName); 753 754 return send_notification_message(message, interestedListeners, 755 interestedListenerCount); 756 } 757 758 759 /** \brief Notifies all interested listeners that a node's stat data have 760 changed. 761 \param device The ID of the mounted FS, the node lives in. 762 \param node The ID of the node. 763 \param statFields A bitwise combination of one or more of the \c B_STAT_* 764 constants defined in <NodeMonitor.h>, indicating what fields of the 765 stat data have changed. 766 \return 767 - \c B_OK, if everything went fine, 768 - another error code otherwise. 769 */ 770 status_t 771 notify_stat_changed(mount_id device, vnode_id node, uint32 statFields) 772 { 773 MutexLocker locker(gMonitorMutex); 774 775 // get the lists of all interested listeners 776 interested_monitor_listener_list interestedListeners[3]; 777 int32 interestedListenerCount = 0; 778 // ... for the node 779 get_interested_monitor_listeners(device, node, B_WATCH_STAT, 780 interestedListeners, interestedListenerCount); 781 782 if (interestedListenerCount == 0) 783 return B_OK; 784 785 // there are interested listeners: construct the message and send it 786 char messageBuffer[1024]; 787 KMessage message; 788 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 789 message.AddInt32("opcode", B_STAT_CHANGED); 790 message.AddInt32("device", device); 791 message.AddInt64("node", node); 792 message.AddInt32("fields", statFields); // Haiku only 793 794 return send_notification_message(message, interestedListeners, 795 interestedListenerCount); 796 } 797 798 799 /** \brief Notifies all interested listeners that a node attribute has changed. 800 \param device The ID of the mounted FS, the node lives in. 801 \param node The ID of the node. 802 \param attribute The attribute's name. 803 \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or 804 \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. 805 \return 806 - \c B_OK, if everything went fine, 807 - another error code otherwise. 808 */ 809 status_t 810 notify_attribute_changed(mount_id device, vnode_id node, const char *attribute, 811 int32 cause) 812 { 813 if (!attribute) 814 return B_BAD_VALUE; 815 816 MutexLocker locker(gMonitorMutex); 817 818 // get the lists of all interested listeners 819 interested_monitor_listener_list interestedListeners[3]; 820 int32 interestedListenerCount = 0; 821 // ... for the node 822 get_interested_monitor_listeners(device, node, B_WATCH_ATTR, 823 interestedListeners, interestedListenerCount); 824 825 if (interestedListenerCount == 0) 826 return B_OK; 827 828 // there are interested listeners: construct the message and send it 829 char messageBuffer[1024]; 830 KMessage message; 831 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 832 message.AddInt32("opcode", B_ATTR_CHANGED); 833 message.AddInt32("device", device); 834 message.AddInt64("node", node); 835 message.AddString("attr", attribute); 836 message.AddInt32("cause", cause); // Haiku only 837 838 return send_notification_message(message, interestedListeners, 839 interestedListenerCount); 840 } 841 842 843 /** \brief Notifies the listener of a live query that an entry has been added 844 to or removed from the query (for whatever reason). 845 \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. 846 \param port The target port of the listener. 847 \param token The BHandler token of the listener. 848 \param device The ID of the mounted FS, the entry lives in. 849 \param directory The entry's parent directory ID. 850 \param name The entry's name. 851 \param node The ID of the node the entry refers to. 852 \return 853 - \c B_OK, if everything went fine, 854 - another error code otherwise. 855 */ 856 static status_t 857 notify_query_entry_created_or_removed(int32 opcode, port_id port, int32 token, 858 mount_id device, vnode_id directory, const char *name, vnode_id node) 859 { 860 if (!name) 861 return B_BAD_VALUE; 862 863 // construct the message 864 char messageBuffer[1024]; 865 KMessage message; 866 message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE); 867 message.AddInt32("opcode", opcode); 868 message.AddInt32("device", device); 869 message.AddInt64("directory", directory); 870 message.AddInt64("node", node); 871 message.AddString("name", name); 872 873 // send the message 874 messaging_target target; 875 target.port = port; 876 target.token = token; 877 878 return send_message(&message, &target, 1); 879 } 880 881 882 /** \brief Notifies the listener of a live query that an entry has been added 883 to the query (for whatever reason). 884 \param port The target port of the listener. 885 \param token The BHandler token of the listener. 886 \param device The ID of the mounted FS, the entry lives in. 887 \param directory The entry's parent directory ID. 888 \param name The entry's name. 889 \param node The ID of the node the entry refers to. 890 \return 891 - \c B_OK, if everything went fine, 892 - another error code otherwise. 893 */ 894 status_t 895 notify_query_entry_created(port_id port, int32 token, mount_id device, 896 vnode_id directory, const char *name, vnode_id node) 897 { 898 return notify_query_entry_created_or_removed(B_ENTRY_CREATED, port, token, 899 device, directory, name, node); 900 } 901 902 903 /** \brief Notifies the listener of a live query that an entry has been removed 904 from the query (for whatever reason). 905 \param port The target port of the listener. 906 \param token The BHandler token of the listener. 907 \param device The ID of the mounted FS, the entry lives in. 908 \param directory The entry's parent directory ID. 909 \param name The entry's name. 910 \param node The ID of the node the entry refers to. 911 \return 912 - \c B_OK, if everything went fine, 913 - another error code otherwise. 914 */ 915 status_t 916 notify_query_entry_removed(port_id port, int32 token, 917 mount_id device, vnode_id directory, const char *name, vnode_id node) 918 { 919 return notify_query_entry_created_or_removed(B_ENTRY_REMOVED, port, token, 920 device, directory, name, node); 921 } 922 923 924 925 // #pragma mark - 926 // Userland syscalls 927 928 929 status_t 930 _user_stop_notifying(port_id port, uint32 token) 931 { 932 io_context *context = get_current_io_context(false); 933 934 return remove_node_monitors_by_target(context, port, token); 935 } 936 937 938 status_t 939 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token) 940 { 941 io_context *context = get_current_io_context(false); 942 943 return add_node_monitor(context, device, node, flags, port, token); 944 } 945 946 947 status_t 948 _user_stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token) 949 { 950 io_context *context = get_current_io_context(false); 951 952 return remove_node_monitor(context, device, node, flags, port, token); 953 } 954 955