1 /* 2 * Copyright 2004-2008, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marcus Overhagen 7 * Axel Dörfler, axeld@pinc-software.de 8 * Jérôme Duval 9 */ 10 11 //! Manager for input_server add-ons (devices, filters, methods) 12 13 #include "AddOnManager.h" 14 15 #include <stdio.h> 16 #include <string.h> 17 18 #include <Autolock.h> 19 #include <Deskbar.h> 20 #include <Directory.h> 21 #include <Entry.h> 22 #include <FindDirectory.h> 23 #include <image.h> 24 #include <Path.h> 25 #include <Roster.h> 26 #include <String.h> 27 28 #include <PathMonitor.h> 29 30 #include "InputServer.h" 31 #include "InputServerTypes.h" 32 #include "MethodReplicant.h" 33 34 35 #undef TRACE 36 //#define TRACE_ADD_ON_MONITOR 37 #ifdef TRACE_ADD_ON_MONITOR 38 # define TRACE(x...) debug_printf(x) 39 # define ERROR(x...) debug_printf(x) 40 #else 41 # define TRACE(x...) ; 42 // TODO: probably better to the syslog 43 # define ERROR(x...) debug_printf(x) 44 #endif 45 46 47 48 class AddOnManager::MonitorHandler : public AddOnMonitorHandler { 49 public: 50 MonitorHandler(AddOnManager* manager) 51 { 52 fManager = manager; 53 } 54 55 virtual void AddOnEnabled(const add_on_entry_info* entryInfo) 56 { 57 CALLED(); 58 entry_ref ref; 59 make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node, 60 entryInfo->name, &ref); 61 BEntry entry(&ref, false); 62 63 fManager->_RegisterAddOn(entry); 64 } 65 66 virtual void AddOnDisabled(const add_on_entry_info* entryInfo) 67 { 68 CALLED(); 69 entry_ref ref; 70 make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node, 71 entryInfo->name, &ref); 72 BEntry entry(&ref, false); 73 74 fManager->_UnregisterAddOn(entry); 75 } 76 77 private: 78 AddOnManager* fManager; 79 }; 80 81 82 // #pragma mark - 83 84 85 template<class T> T* 86 instantiate_add_on(image_id image, const char* path, const char* type) 87 { 88 T* (*instantiateFunction)(); 89 90 BString functionName = "instantiate_input_"; 91 functionName += type; 92 93 if (get_image_symbol(image, functionName.String(), B_SYMBOL_TYPE_TEXT, 94 (void**)&instantiateFunction) < B_OK) { 95 ERROR("AddOnManager::_RegisterAddOn(): can't find %s() in \"%s\"\n", 96 functionName.String(), path); 97 return NULL; 98 } 99 100 T* addOn = (*instantiateFunction)(); 101 if (addOn == NULL) { 102 ERROR("AddOnManager::_RegisterAddOn(): %s() in \"%s\" returned " 103 "NULL\n", functionName.String(), path); 104 return NULL; 105 } 106 107 status_t status = addOn->InitCheck(); 108 if (status != B_OK) { 109 ERROR("AddOnManager::_RegisterAddOn(): InitCheck() in \"%s\" " 110 "returned %s\n", path, strerror(status)); 111 delete addOn; 112 return NULL; 113 } 114 115 return addOn; 116 } 117 118 119 // #pragma mark - 120 121 122 AddOnManager::AddOnManager(bool safeMode) 123 : BLooper("add-on manager"), 124 fSafeMode(safeMode) 125 { 126 } 127 128 129 AddOnManager::~AddOnManager() 130 { 131 } 132 133 134 void 135 AddOnManager::LoadState() 136 { 137 _RegisterAddOns(); 138 } 139 140 141 void 142 AddOnManager::SaveState() 143 { 144 CALLED(); 145 _UnregisterAddOns(); 146 } 147 148 149 // #pragma mark - 150 151 152 void 153 AddOnManager::_RegisterAddOns() 154 { 155 CALLED(); 156 BAutolock locker(this); 157 status_t err; 158 159 fHandler = new MonitorHandler(this); 160 fAddOnMonitor = new AddOnMonitor(fHandler); 161 162 err = fAddOnMonitor->InitCheck(); 163 if (err != B_OK) { 164 ERROR("AddOnManager::RegisterAddOns(): fAddOnMonitor->InitCheck() " 165 "returned %s\n", strerror(err)); 166 return; 167 } 168 169 const directory_which directories[] = { 170 B_USER_ADDONS_DIRECTORY, 171 B_COMMON_ADDONS_DIRECTORY, 172 B_BEOS_ADDONS_DIRECTORY 173 }; 174 const char* subDirectories[] = { 175 "input_server/devices", 176 "input_server/filters", 177 "input_server/methods" 178 }; 179 int32 subDirectoryCount = sizeof(subDirectories) / sizeof(const char*); 180 181 node_ref nref; 182 BDirectory directory; 183 BPath path; 184 // when safemode, only B_BEOS_ADDONS_DIRECTORY is used 185 for (uint32 i = fSafeMode ? 2 : 0; 186 i < sizeof(directories) / sizeof(directory_which); i++) { 187 for (uint32 j = 0; j < subDirectoryCount; j++) { 188 if (find_directory(directories[i], &path) == B_OK 189 && path.Append(subDirectories[j]) == B_OK 190 && directory.SetTo(path.Path()) == B_OK 191 && directory.GetNodeRef(&nref) == B_OK) { 192 fHandler->AddDirectory(&nref); 193 } 194 } 195 } 196 } 197 198 199 void 200 AddOnManager::_UnregisterAddOns() 201 { 202 BAutolock locker(this); 203 204 BMessenger messenger(fAddOnMonitor); 205 messenger.SendMessage(B_QUIT_REQUESTED); 206 int32 exitValue; 207 wait_for_thread(fAddOnMonitor->Thread(), &exitValue); 208 delete fHandler; 209 fHandler = NULL; 210 211 // We have to stop manually the add-ons because the monitor doesn't 212 // disable them on exit 213 214 while (device_info* info = fDeviceList.RemoveItemAt(0)) { 215 gInputServer->StartStopDevices(*info->add_on, false); 216 delete info; 217 } 218 219 // TODO: what about the filters/methods lists in the input_server? 220 221 while (filter_info* info = fFilterList.RemoveItemAt(0)) { 222 delete info; 223 } 224 225 while (method_info* info = fMethodList.RemoveItemAt(0)) { 226 delete info; 227 } 228 } 229 230 231 bool 232 AddOnManager::_IsDevice(const char* path) const 233 { 234 return strstr(path, "input_server/devices") != 0; 235 } 236 237 238 bool 239 AddOnManager::_IsFilter(const char* path) const 240 { 241 return strstr(path, "input_server/filters") != 0; 242 } 243 244 245 bool 246 AddOnManager::_IsMethod(const char* path) const 247 { 248 return strstr(path, "input_server/methods") != 0; 249 } 250 251 252 status_t 253 AddOnManager::_RegisterAddOn(BEntry& entry) 254 { 255 BPath path(&entry); 256 257 entry_ref ref; 258 status_t status = entry.GetRef(&ref); 259 if (status < B_OK) 260 return status; 261 262 TRACE("AddOnManager::RegisterAddOn(): trying to load \"%s\"\n", 263 path.Path()); 264 265 image_id image = load_add_on(path.Path()); 266 if (image < B_OK) { 267 ERROR("load addon %s failed\n", path.Path()); 268 return image; 269 } 270 271 status = B_ERROR; 272 273 if (_IsDevice(path.Path())) { 274 BInputServerDevice* device = instantiate_add_on<BInputServerDevice>( 275 image, path.Path(), "device"); 276 if (device != NULL) 277 status = _RegisterDevice(device, ref, image); 278 } else if (_IsFilter(path.Path())) { 279 BInputServerFilter* filter = instantiate_add_on<BInputServerFilter>( 280 image, path.Path(), "filter"); 281 if (filter != NULL) 282 status = _RegisterFilter(filter, ref, image); 283 } else if (_IsMethod(path.Path())) { 284 BInputServerMethod* method = instantiate_add_on<BInputServerMethod>( 285 image, path.Path(), "method"); 286 if (method != NULL) 287 status = _RegisterMethod(method, ref, image); 288 } else { 289 ERROR("AddOnManager::_RegisterAddOn(): addon type not found for " 290 "\"%s\" \n", path.Path()); 291 } 292 293 if (status != B_OK) 294 unload_add_on(image); 295 296 return status; 297 } 298 299 300 status_t 301 AddOnManager::_UnregisterAddOn(BEntry& entry) 302 { 303 BPath path(&entry); 304 305 entry_ref ref; 306 status_t status = entry.GetRef(&ref); 307 if (status < B_OK) 308 return status; 309 310 TRACE("AddOnManager::UnregisterAddOn(): trying to unload \"%s\"\n", 311 path.Path()); 312 313 BAutolock _(this); 314 315 if (_IsDevice(path.Path())) { 316 for (int32 i = fDeviceList.CountItems(); i-- > 0;) { 317 device_info* info = fDeviceList.ItemAt(i); 318 if (!strcmp(info->ref.name, ref.name)) { 319 gInputServer->StartStopDevices(*info->add_on, false); 320 delete fDeviceList.RemoveItemAt(i); 321 break; 322 } 323 } 324 } else if (_IsFilter(path.Path())) { 325 for (int32 i = fFilterList.CountItems(); i-- > 0;) { 326 filter_info* info = fFilterList.ItemAt(i); 327 if (!strcmp(info->ref.name, ref.name)) { 328 BAutolock locker(InputServer::gInputFilterListLocker); 329 InputServer::gInputFilterList.RemoveItem(info->add_on); 330 delete fFilterList.RemoveItemAt(i); 331 break; 332 } 333 } 334 } else if (_IsMethod(path.Path())) { 335 BInputServerMethod* method = NULL; 336 337 for (int32 i = fMethodList.CountItems(); i-- > 0;) { 338 method_info* info = fMethodList.ItemAt(i); 339 if (!strcmp(info->ref.name, ref.name)) { 340 BAutolock locker(InputServer::gInputMethodListLocker); 341 InputServer::gInputMethodList.RemoveItem(info->add_on); 342 method = info->add_on; 343 // this will only be used as a cookie, and not referenced 344 // anymore 345 delete fMethodList.RemoveItemAt(i); 346 break; 347 } 348 } 349 350 if (fMethodList.CountItems() <= 0) { 351 // we remove the method replicant 352 BDeskbar().RemoveItem(REPLICANT_CTL_NAME); 353 gInputServer->SetMethodReplicant(NULL); 354 } else if (method != NULL) { 355 BMessage msg(IS_REMOVE_METHOD); 356 msg.AddInt32("cookie", (uint32)method); 357 if (gInputServer->MethodReplicant()) 358 gInputServer->MethodReplicant()->SendMessage(&msg); 359 } 360 } 361 362 return B_OK; 363 } 364 365 366 //! Takes over ownership of the \a device, regardless of success. 367 status_t 368 AddOnManager::_RegisterDevice(BInputServerDevice* device, const entry_ref& ref, 369 image_id addOnImage) 370 { 371 BAutolock locker(this); 372 373 for (int32 i = fDeviceList.CountItems(); i-- > 0;) { 374 device_info* info = fDeviceList.ItemAt(i); 375 if (!strcmp(info->ref.name, ref.name)) { 376 // we already know this device 377 delete device; 378 return B_NAME_IN_USE; 379 } 380 } 381 382 TRACE("AddOnManager::RegisterDevice, name %s\n", ref.name); 383 384 device_info* info = new(std::nothrow) device_info; 385 if (info == NULL) { 386 delete device; 387 return B_NO_MEMORY; 388 } 389 390 info->ref = ref; 391 info->add_on = device; 392 393 if (!fDeviceList.AddItem(info)) { 394 delete info; 395 return B_NO_MEMORY; 396 } 397 398 info->image = addOnImage; 399 400 return B_OK; 401 } 402 403 404 //! Takes over ownership of the \a filter, regardless of success. 405 status_t 406 AddOnManager::_RegisterFilter(BInputServerFilter* filter, const entry_ref& ref, 407 image_id addOnImage) 408 { 409 BAutolock _(this); 410 411 for (int32 i = fFilterList.CountItems(); i-- > 0;) { 412 filter_info* info = fFilterList.ItemAt(i); 413 if (!strcmp(info->ref.name, ref.name)) { 414 // we already know this ref 415 delete filter; 416 return B_NAME_IN_USE; 417 } 418 } 419 420 TRACE("%s, name %s\n", __PRETTY_FUNCTION__, ref.name); 421 422 filter_info* info = new(std::nothrow) filter_info; 423 if (info == NULL) { 424 delete filter; 425 return B_NO_MEMORY; 426 } 427 428 info->ref = ref; 429 info->add_on = filter; 430 431 if (!fFilterList.AddItem(info)) { 432 delete info; 433 return B_NO_MEMORY; 434 } 435 436 BAutolock locker(InputServer::gInputFilterListLocker); 437 if (!InputServer::gInputFilterList.AddItem(filter)) { 438 fFilterList.RemoveItem(info); 439 delete info; 440 return B_NO_MEMORY; 441 } 442 443 info->image = addOnImage; 444 445 return B_OK; 446 } 447 448 449 //! Takes over ownership of the \a method, regardless of success. 450 status_t 451 AddOnManager::_RegisterMethod(BInputServerMethod* method, const entry_ref& ref, 452 image_id addOnImage) 453 { 454 BAutolock _(this); 455 456 for (int32 i = fMethodList.CountItems(); i-- > 0;) { 457 method_info* info = fMethodList.ItemAt(i); 458 if (!strcmp(info->ref.name, ref.name)) { 459 // we already know this ref 460 delete method; 461 return B_NAME_IN_USE; 462 } 463 } 464 465 TRACE("%s, name %s\n", __PRETTY_FUNCTION__, ref.name); 466 467 method_info* info = new(std::nothrow) method_info; 468 if (info == NULL) { 469 delete method; 470 return B_NO_MEMORY; 471 } 472 473 info->ref = ref; 474 info->add_on = method; 475 476 if (!fMethodList.AddItem(info)) { 477 delete info; 478 return B_NO_MEMORY; 479 } 480 481 BAutolock locker(InputServer::gInputMethodListLocker); 482 if (!InputServer::gInputMethodList.AddItem(method)) { 483 fMethodList.RemoveItem(info); 484 delete info; 485 return B_NO_MEMORY; 486 } 487 488 info->image = addOnImage; 489 490 if (gInputServer->MethodReplicant() == NULL) { 491 _LoadReplicant(); 492 493 if (gInputServer->MethodReplicant()) { 494 _BMethodAddOn_ *addon = InputServer::gKeymapMethod.fOwner; 495 addon->AddMethod(); 496 } 497 } 498 499 if (gInputServer->MethodReplicant() != NULL) { 500 _BMethodAddOn_ *addon = method->fOwner; 501 addon->AddMethod(); 502 } 503 504 return B_OK; 505 } 506 507 508 // #pragma mark - 509 510 511 void 512 AddOnManager::_UnloadReplicant() 513 { 514 BDeskbar().RemoveItem(REPLICANT_CTL_NAME); 515 } 516 517 518 void 519 AddOnManager::_LoadReplicant() 520 { 521 CALLED(); 522 app_info info; 523 be_app->GetAppInfo(&info); 524 525 status_t err = BDeskbar().AddItem(&info.ref); 526 if (err != B_OK) { 527 ERROR("Deskbar refuses to add method replicant: %s\n", strerror(err)); 528 } 529 BMessage request(B_GET_PROPERTY); 530 BMessenger to; 531 BMessenger status; 532 533 request.AddSpecifier("Messenger"); 534 request.AddSpecifier("Shelf"); 535 536 // In the Deskbar the Shelf is in the View "Status" in Window "Deskbar" 537 request.AddSpecifier("View", "Status"); 538 request.AddSpecifier("Window", "Deskbar"); 539 to = BMessenger("application/x-vnd.Be-TSKB", -1); 540 541 BMessage reply; 542 543 if (to.SendMessage(&request, &reply) == B_OK 544 && reply.FindMessenger("result", &status) == B_OK) { 545 // enum replicant in Status view 546 int32 index = 0; 547 int32 uid; 548 while ((uid = _GetReplicantAt(status, index++)) >= B_OK) { 549 BMessage replicantInfo; 550 if (_GetReplicantName(status, uid, &replicantInfo) != B_OK) 551 continue; 552 553 const char *name; 554 if (replicantInfo.FindString("result", &name) == B_OK 555 && !strcmp(name, REPLICANT_CTL_NAME)) { 556 BMessage replicant; 557 if (_GetReplicantView(status, uid, &replicant) == B_OK) { 558 BMessenger result; 559 if (replicant.FindMessenger("result", &result) == B_OK) { 560 gInputServer->SetMethodReplicant(new BMessenger(result)); 561 } 562 } 563 } 564 } 565 } 566 567 if (!gInputServer->MethodReplicant()) { 568 ERROR("LoadReplicant(): Method replicant not found!\n"); 569 } 570 } 571 572 573 int32 574 AddOnManager::_GetReplicantAt(BMessenger target, int32 index) const 575 { 576 // So here we want to get the Unique ID of the replicant at the given index 577 // in the target Shelf. 578 579 BMessage request(B_GET_PROPERTY);// We're getting the ID property 580 BMessage reply; 581 status_t err; 582 583 request.AddSpecifier("ID");// want the ID 584 request.AddSpecifier("Replicant", index);// of the index'th replicant 585 586 if ((err = target.SendMessage(&request, &reply)) != B_OK) 587 return err; 588 589 int32 uid; 590 if ((err = reply.FindInt32("result", &uid)) != B_OK) 591 return err; 592 593 return uid; 594 } 595 596 597 status_t 598 AddOnManager::_GetReplicantName(BMessenger target, int32 uid, 599 BMessage* reply) const 600 { 601 // We send a message to the target shelf, asking it for the Name of the 602 // replicant with the given unique id. 603 604 BMessage request(B_GET_PROPERTY); 605 BMessage uid_specifier(B_ID_SPECIFIER);// specifying via ID 606 status_t err; 607 status_t e; 608 609 request.AddSpecifier("Name");// ask for the Name of the replicant 610 611 // IDs are specified using code like the following 3 lines: 612 uid_specifier.AddInt32("id", uid); 613 uid_specifier.AddString("property", "Replicant"); 614 request.AddSpecifier(&uid_specifier); 615 616 if ((err = target.SendMessage(&request, reply)) != B_OK) 617 return err; 618 619 if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK)) 620 return err ? err : e; 621 622 return B_OK; 623 } 624 625 626 status_t 627 AddOnManager::_GetReplicantView(BMessenger target, int32 uid, 628 BMessage* reply) const 629 { 630 // We send a message to the target shelf, asking it for the Name of the 631 // replicant with the given unique id. 632 633 BMessage request(B_GET_PROPERTY); 634 BMessage uid_specifier(B_ID_SPECIFIER); 635 // specifying via ID 636 status_t err; 637 status_t e; 638 639 request.AddSpecifier("View"); 640 // ask for the Name of the replicant 641 642 // IDs are specified using code like the following 3 lines: 643 uid_specifier.AddInt32("id", uid); 644 uid_specifier.AddString("property", "Replicant"); 645 request.AddSpecifier(&uid_specifier); 646 647 if ((err = target.SendMessage(&request, reply)) != B_OK) 648 return err; 649 650 if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK)) 651 return err ? err : e; 652 653 return B_OK; 654 } 655 656 657 void 658 AddOnManager::MessageReceived(BMessage* message) 659 { 660 CALLED(); 661 662 BMessage reply; 663 status_t status; 664 665 ERROR("%s what: %.4s\n", __PRETTY_FUNCTION__, (char*)&message->what); 666 667 switch (message->what) { 668 case IS_FIND_DEVICES: 669 status = _HandleFindDevices(message, &reply); 670 break; 671 case IS_WATCH_DEVICES: 672 status = _HandleWatchDevices(message, &reply); 673 break; 674 case IS_IS_DEVICE_RUNNING: 675 status = _HandleIsDeviceRunning(message, &reply); 676 break; 677 case IS_START_DEVICE: 678 status = _HandleStartStopDevices(message, &reply); 679 break; 680 case IS_STOP_DEVICE: 681 status = _HandleStartStopDevices(message, &reply); 682 break; 683 case IS_CONTROL_DEVICES: 684 status = _HandleControlDevices(message, &reply); 685 break; 686 case SYSTEM_SHUTTING_DOWN: 687 status = _HandleSystemShuttingDown(message, &reply); 688 break; 689 case IS_METHOD_REGISTER: 690 status = _HandleMethodReplicant(message, &reply); 691 break; 692 693 case B_PATH_MONITOR: 694 _HandleDeviceMonitor(message); 695 return; 696 697 default: 698 return; 699 } 700 701 reply.AddInt32("status", status); 702 message->SendReply(&reply); 703 } 704 705 706 status_t 707 AddOnManager::_HandleStartStopDevices(BMessage* message, BMessage* reply) 708 { 709 const char *name = NULL; 710 int32 type = 0; 711 if (!((message->FindInt32("type", &type) != B_OK) 712 ^ (message->FindString("device", &name) != B_OK))) 713 return B_ERROR; 714 715 return gInputServer->StartStopDevices(name, (input_device_type)type, 716 message->what == IS_START_DEVICE); 717 } 718 719 720 status_t 721 AddOnManager::_HandleFindDevices(BMessage* message, BMessage* reply) 722 { 723 CALLED(); 724 const char *name = NULL; 725 input_device_type type; 726 if (message->FindString("device", &name) == B_OK) { 727 if (gInputServer->GetDeviceInfo(name, &type) != B_OK) 728 return B_NAME_NOT_FOUND; 729 reply->AddString("device", name); 730 reply->AddInt32("type", type); 731 } else { 732 gInputServer->GetDeviceInfos(reply); 733 } 734 return B_OK; 735 } 736 737 738 status_t 739 AddOnManager::_HandleWatchDevices(BMessage* message, BMessage* reply) 740 { 741 // TODO 742 return B_OK; 743 } 744 745 746 status_t 747 AddOnManager::_HandleIsDeviceRunning(BMessage* message, BMessage* reply) 748 { 749 const char* name; 750 bool running; 751 if (message->FindString("device", &name) != B_OK 752 || gInputServer->GetDeviceInfo(name, NULL, &running) != B_OK) 753 return B_NAME_NOT_FOUND; 754 755 return running ? B_OK : B_ERROR; 756 } 757 758 759 status_t 760 AddOnManager::_HandleControlDevices(BMessage* message, BMessage* reply) 761 { 762 CALLED(); 763 const char *name = NULL; 764 int32 type = 0; 765 if (!((message->FindInt32("type", &type) != B_OK) 766 ^ (message->FindString("device", &name) != B_OK))) 767 return B_BAD_VALUE; 768 769 uint32 code = 0; 770 BMessage controlMessage; 771 bool hasMessage = true; 772 if (message->FindInt32("code", (int32*)&code) != B_OK) 773 return B_BAD_VALUE; 774 if (message->FindMessage("message", &controlMessage) != B_OK) 775 hasMessage = false; 776 777 return gInputServer->ControlDevices(name, (input_device_type)type, 778 code, hasMessage ? &controlMessage : NULL); 779 } 780 781 782 status_t 783 AddOnManager::_HandleSystemShuttingDown(BMessage* message, BMessage* reply) 784 { 785 CALLED(); 786 787 for (int32 i = 0; i < fDeviceList.CountItems(); i++) { 788 device_info* info = fDeviceList.ItemAt(i); 789 info->add_on->SystemShuttingDown(); 790 } 791 792 return B_OK; 793 } 794 795 796 status_t 797 AddOnManager::_HandleMethodReplicant(BMessage* message, BMessage* reply) 798 { 799 CALLED(); 800 801 if (InputServer::gInputMethodList.CountItems() == 0) { 802 _UnloadReplicant(); 803 return B_OK; 804 } 805 806 _LoadReplicant(); 807 808 BAutolock lock(InputServer::gInputMethodListLocker); 809 810 if (gInputServer->MethodReplicant()) { 811 _BMethodAddOn_* addon = InputServer::gKeymapMethod.fOwner; 812 addon->AddMethod(); 813 814 for (int32 i = 0; i < InputServer::gInputMethodList.CountItems(); i++) { 815 BInputServerMethod* method 816 = (BInputServerMethod*)InputServer::gInputMethodList.ItemAt(i); 817 818 _BMethodAddOn_* addon = method->fOwner; 819 addon->AddMethod(); 820 } 821 } 822 823 return B_OK; 824 } 825 826 827 void 828 AddOnManager::_HandleDeviceMonitor(BMessage* message) 829 { 830 int32 opcode; 831 if (message->FindInt32("opcode", &opcode) != B_OK) 832 return; 833 834 switch (opcode) { 835 case B_ENTRY_CREATED: 836 case B_ENTRY_REMOVED: 837 { 838 const char* path; 839 const char* watchedPath; 840 if (message->FindString("watched_path", &watchedPath) != B_OK 841 || message->FindString("path", &path) != B_OK) { 842 #if DEBUG 843 char string[1024]; 844 sprintf(string, "message does not contain all fields - " 845 "watched_path: %d, path: %d\n", 846 message->HasString("watched_path"), 847 message->HasString("path")); 848 debugger(string); 849 #endif 850 return; 851 } 852 853 // Notify all watching devices 854 855 for (int32 i = 0; i < fDeviceAddOns.CountItems(); i++) { 856 DeviceAddOn* addOn = fDeviceAddOns.ItemAt(i); 857 if (!addOn->HasPath(watchedPath)) 858 continue; 859 860 addOn->Device()->Control(NULL, NULL, B_NODE_MONITOR, message); 861 } 862 break; 863 } 864 } 865 } 866 867 868 status_t 869 AddOnManager::_AddDevicePath(DeviceAddOn* addOn, const char* path, 870 bool& newPath) 871 { 872 newPath = !fDevicePaths.HasPath(path); 873 874 status_t status = fDevicePaths.AddPath(path); 875 if (status == B_OK) { 876 status = addOn->AddPath(path); 877 if (status == B_OK) { 878 if (!fDeviceAddOns.HasItem(addOn) 879 && !fDeviceAddOns.AddItem(addOn)) { 880 addOn->RemovePath(path); 881 status = B_NO_MEMORY; 882 } 883 } else 884 fDevicePaths.RemovePath(path); 885 } 886 887 return status; 888 } 889 890 891 status_t 892 AddOnManager::_RemoveDevicePath(DeviceAddOn* addOn, const char* path, 893 bool& lastPath) 894 { 895 if (!fDevicePaths.HasPath(path) || !addOn->HasPath(path)) 896 return B_ENTRY_NOT_FOUND; 897 898 fDevicePaths.RemovePath(path); 899 900 lastPath = !fDevicePaths.HasPath(path); 901 902 addOn->RemovePath(path); 903 if (addOn->CountPaths() == 0) 904 fDeviceAddOns.RemoveItem(addOn); 905 906 return B_OK; 907 } 908 909 910 status_t 911 AddOnManager::StartMonitoringDevice(DeviceAddOn* addOn, const char* device) 912 { 913 CALLED(); 914 915 BString path; 916 if (device[0] != '/') 917 path = "/dev/"; 918 path += device; 919 920 TRACE("AddOnMonitor::StartMonitoringDevice(%s)\n", path.String()); 921 922 bool newPath; 923 status_t status = _AddDevicePath(addOn, path.String(), newPath); 924 if (status == B_OK && newPath) { 925 status = BPathMonitor::StartWatching(path.String(), B_ENTRY_CREATED 926 | B_ENTRY_REMOVED | B_ENTRY_MOVED | B_WATCH_FILES_ONLY 927 | B_WATCH_RECURSIVELY, this); 928 if (status != B_OK) { 929 bool lastPath; 930 _RemoveDevicePath(addOn, path.String(), lastPath); 931 } 932 } 933 934 return status; 935 } 936 937 938 status_t 939 AddOnManager::StopMonitoringDevice(DeviceAddOn* addOn, const char *device) 940 { 941 CALLED(); 942 943 BString path; 944 if (device[0] != '/') 945 path = "/dev/"; 946 path += device; 947 948 TRACE("AddOnMonitor::StopMonitoringDevice(%s)\n", path.String()); 949 950 bool lastPath; 951 status_t status = _RemoveDevicePath(addOn, path.String(), lastPath); 952 if (status == B_OK && lastPath) 953 BPathMonitor::StopWatching(path.String(), this); 954 955 return status; 956 } 957