1 /* 2 * Copyright 2010, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler <haiku@clemens-zeidler.de> 7 */ 8 9 #include "VolumeWatcher.h" 10 11 #include <sys/stat.h> 12 13 #include <Autolock.h> 14 #include <Directory.h> 15 #include <NodeMonitor.h> 16 #include <Path.h> 17 #include <VolumeRoster.h> 18 #include <Query.h> 19 20 21 #include "IndexServerPrivate.h" 22 23 24 const bigtime_t kSecond = 1000000; 25 26 27 WatchNameHandler::WatchNameHandler(VolumeWatcher* volumeWatcher) 28 : 29 fVolumeWatcher(volumeWatcher) 30 { 31 32 } 33 34 35 void 36 WatchNameHandler::EntryCreated(const char *name, ino_t directory, dev_t device, 37 ino_t node) 38 { 39 entry_ref ref(device, directory, name); 40 fVolumeWatcher->fCreatedList.CurrentList()->push_back(ref); 41 fVolumeWatcher->_NewEntriesArrived(); 42 } 43 44 45 void 46 WatchNameHandler::EntryRemoved(const char *name, ino_t directory, dev_t device, 47 ino_t node) 48 { 49 entry_ref ref(device, directory, name); 50 fVolumeWatcher->fDeleteList.CurrentList()->push_back(ref); 51 fVolumeWatcher->_NewEntriesArrived(); 52 } 53 54 55 void 56 WatchNameHandler::EntryMoved(const char *name, const char *fromName, 57 ino_t from_directory, ino_t to_directory, dev_t device, ino_t node, 58 dev_t nodeDevice) 59 { 60 entry_ref ref(device, to_directory, name); 61 entry_ref refFrom(device, from_directory, fromName); 62 63 fVolumeWatcher->fMovedList.CurrentList()->push_back(ref); 64 fVolumeWatcher->fMovedFromList.CurrentList()->push_back(refFrom); 65 fVolumeWatcher->_NewEntriesArrived(); 66 } 67 68 69 void 70 WatchNameHandler::StatChanged(ino_t node, dev_t device, int32 statFields) 71 { 72 if ((statFields & B_STAT_MODIFICATION_TIME) == 0) 73 return; 74 } 75 76 77 void 78 WatchNameHandler::MessageReceived(BMessage* msg) 79 { 80 if (msg->what == B_NODE_MONITOR) { 81 int32 opcode; 82 if (msg->FindInt32("opcode", &opcode) == B_OK) { 83 switch (opcode) { 84 case B_STAT_CHANGED: { 85 BString name; 86 entry_ref ref; 87 ino_t node; 88 int32 statFields; 89 msg->FindInt32("fields", &statFields); 90 if ((statFields & B_STAT_MODIFICATION_TIME) == 0) 91 break; 92 msg->FindInt32("device", &ref.device); 93 msg->FindInt64("node", &node); 94 msg->FindInt64("directory", &ref.directory); 95 msg->FindString("name", &name); 96 97 ref.set_name(name); 98 99 BPath path(&ref); 100 printf("stat changed node %i name %s %s\n", (int)node, 101 name.String(), path.Path()); 102 103 fVolumeWatcher->fModifiedList.CurrentList()->push_back(ref); 104 fVolumeWatcher->_NewEntriesArrived(); 105 106 break; 107 } 108 } 109 } 110 } 111 NodeMonitorHandler::MessageReceived(msg); 112 } 113 114 115 AnalyserDispatcher::AnalyserDispatcher(const char* name) 116 : 117 BLooper(name, B_LOW_PRIORITY), 118 119 fStopped(0) 120 { 121 122 } 123 124 125 AnalyserDispatcher::~AnalyserDispatcher() 126 { 127 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 128 delete fFileAnalyserList.ItemAt(i); 129 } 130 131 132 void 133 AnalyserDispatcher::Stop() 134 { 135 atomic_set(&fStopped, 1); 136 } 137 138 139 bool 140 AnalyserDispatcher::Stopped() 141 { 142 return (atomic_get(&fStopped) != 0); 143 } 144 145 146 void 147 AnalyserDispatcher::AnalyseEntry(const entry_ref& ref) 148 { 149 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 150 fFileAnalyserList.ItemAt(i)->AnalyseEntry(ref); 151 } 152 153 154 void 155 AnalyserDispatcher::DeleteEntry(const entry_ref& ref) 156 { 157 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 158 fFileAnalyserList.ItemAt(i)->DeleteEntry(ref); 159 } 160 161 162 void 163 AnalyserDispatcher::MoveEntry(const entry_ref& oldRef, const entry_ref& newRef) 164 { 165 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 166 fFileAnalyserList.ItemAt(i)->MoveEntry(oldRef, newRef); 167 } 168 169 170 void 171 AnalyserDispatcher::LastEntry() 172 { 173 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 174 fFileAnalyserList.ItemAt(i)->LastEntry(); 175 } 176 177 178 bool 179 AnalyserDispatcher::AddAnalyser(FileAnalyser* analyser) 180 { 181 if (analyser == NULL) 182 return false; 183 184 bool result; 185 BAutolock _(this); 186 if (_FindAnalyser(analyser->Name())) 187 return false; 188 189 result = fFileAnalyserList.AddItem(analyser); 190 return result; 191 } 192 193 194 bool 195 AnalyserDispatcher::RemoveAnalyser(const BString& name) 196 { 197 BAutolock _(this); 198 FileAnalyser* analyser = _FindAnalyser(name); 199 if (analyser) { 200 fFileAnalyserList.RemoveItem(analyser); 201 delete analyser; 202 return true; 203 } 204 return false; 205 } 206 207 208 FileAnalyser* 209 AnalyserDispatcher::_FindAnalyser(const BString& name) 210 { 211 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) { 212 FileAnalyser* analyser = fFileAnalyserList.ItemAt(i); 213 if (analyser->Name() == name) 214 return analyser; 215 } 216 return NULL; 217 } 218 219 220 void 221 AnalyserDispatcher::WriteAnalyserSettings() 222 { 223 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 224 fFileAnalyserList.ItemAt(i)->Settings()->WriteSettings(); 225 } 226 227 228 void 229 AnalyserDispatcher::SetSyncPosition(bigtime_t time) 230 { 231 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 232 fFileAnalyserList.ItemAt(i)->Settings()->SetSyncPosition(time); 233 } 234 235 236 void 237 AnalyserDispatcher::SetWatchingStart(bigtime_t time) 238 { 239 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 240 fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingStart(time); 241 } 242 243 244 void 245 AnalyserDispatcher::SetWatchingPosition(bigtime_t time) 246 { 247 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 248 fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingPosition(time); 249 } 250 251 252 VolumeWorker::VolumeWorker(VolumeWatcher* watcher) 253 : 254 AnalyserDispatcher("VolumeWorker"), 255 256 fVolumeWatcher(watcher), 257 fBusy(0) 258 { 259 260 } 261 262 263 void 264 VolumeWorker::MessageReceived(BMessage *message) 265 { 266 switch (message->what) { 267 case kTriggerWork: 268 _Work(); 269 break; 270 271 default: 272 BLooper::MessageReceived(message); 273 } 274 } 275 276 277 bool 278 VolumeWorker::IsBusy() 279 { 280 return (atomic_get(&fBusy) != 0); 281 } 282 283 284 void 285 VolumeWorker::_Work() 286 { 287 list_collection collection; 288 fVolumeWatcher->GetSecureEntries(collection); 289 290 if (collection.createdList->size() == 0 291 && collection.deletedList->size() == 0 292 && collection.modifiedList->size() == 0 293 && collection.movedList->size() == 0) 294 return; 295 296 _SetBusy(true); 297 for (unsigned int i = 0; i < collection.createdList->size() || Stopped(); 298 i++) 299 AnalyseEntry((*collection.createdList)[i]); 300 collection.createdList->clear(); 301 302 for (unsigned int i = 0; i < collection.deletedList->size() || Stopped(); 303 i++) 304 DeleteEntry((*collection.deletedList)[i]); 305 collection.deletedList->clear(); 306 307 for (unsigned int i = 0; i < collection.modifiedList->size() || Stopped(); 308 i++) 309 AnalyseEntry((*collection.modifiedList)[i]); 310 collection.modifiedList->clear(); 311 312 for (unsigned int i = 0; i < collection.movedList->size() || Stopped(); 313 i++) 314 MoveEntry((*collection.movedFromList)[i], (*collection.movedList)[i]); 315 collection.movedList->clear(); 316 collection.movedFromList->clear(); 317 318 LastEntry(); 319 PostMessage(kTriggerWork); 320 321 _SetBusy(false); 322 } 323 324 325 void 326 VolumeWorker::_SetBusy(bool busy) 327 { 328 if (busy) 329 atomic_set(&fBusy, 1); 330 else 331 atomic_set(&fBusy, 0); 332 } 333 334 335 VolumeWatcherBase::VolumeWatcherBase(const BVolume& volume) 336 : 337 fVolume(volume), 338 fEnabled(true), 339 fLastUpdated(0) 340 { 341 ReadSettings(); 342 } 343 344 345 const char* kEnabledAttr = "Enabled"; 346 347 348 bool 349 VolumeWatcherBase::ReadSettings() 350 { 351 // TODO remove this 352 BVolume bootVolume; 353 BVolumeRoster roster; 354 roster.GetBootVolume(&bootVolume); 355 if (bootVolume == fVolume) { 356 fEnabled = true; 357 WriteSettings(); 358 } 359 360 BDirectory rootDir; 361 fVolume.GetRootDirectory(&rootDir); 362 BPath path(&rootDir); 363 path.Append(kIndexServerDirectory); 364 path.Append(kVolumeStatusFileName); 365 BFile file(path.Path(), B_READ_ONLY); 366 if (file.InitCheck() != B_OK) 367 return false; 368 369 uint32 enabled; 370 file.WriteAttr(kEnabledAttr, B_UINT32_TYPE, 0, &enabled, sizeof(uint32)); 371 fEnabled = enabled == 0 ? false : true; 372 373 return true; 374 } 375 376 377 bool 378 VolumeWatcherBase::WriteSettings() 379 { 380 BDirectory rootDir; 381 fVolume.GetRootDirectory(&rootDir); 382 BPath path(&rootDir); 383 path.Append(kIndexServerDirectory); 384 if (create_directory(path.Path(), 777) != B_OK) 385 return false; 386 387 path.Append(kVolumeStatusFileName); 388 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 389 if (file.InitCheck() != B_OK) 390 return false; 391 392 uint32 enabled = fEnabled ? 1 : 0; 393 file.WriteAttr(kEnabledAttr, B_UINT32_TYPE, 0, &enabled, sizeof(uint32)); 394 395 return true; 396 } 397 398 399 SwapEntryRefVector::SwapEntryRefVector() 400 { 401 fCurrentList = &fFirstList; 402 fNextList = &fSecondList; 403 } 404 405 406 EntryRefVector* 407 SwapEntryRefVector::SwapList() 408 { 409 EntryRefVector* temp = fCurrentList; 410 fCurrentList = fNextList; 411 fNextList = temp; 412 return temp; 413 } 414 415 416 EntryRefVector* 417 SwapEntryRefVector::CurrentList() 418 { 419 return fCurrentList; 420 } 421 422 423 VolumeWatcher::VolumeWatcher(const BVolume& volume) 424 : 425 VolumeWatcherBase(volume), 426 BLooper("VolumeWatcher"), 427 428 fWatching(false), 429 fWatchNameHandler(this), 430 fCatchUpManager(volume) 431 { 432 AddHandler(&fWatchNameHandler); 433 434 fVolumeWorker = new VolumeWorker(this); 435 fVolumeWorker->Run(); 436 } 437 438 439 VolumeWatcher::~VolumeWatcher() 440 { 441 Stop(); 442 thread_id threadId = fVolumeWorker->Thread(); 443 fVolumeWorker->PostMessage(B_QUIT_REQUESTED); 444 status_t error; 445 wait_for_thread(threadId, &error); 446 } 447 448 449 bool 450 VolumeWatcher::StartWatching() 451 { 452 Run(); 453 454 watch_volume(fVolume.Device(), B_WATCH_NAME | B_WATCH_STAT, 455 &fWatchNameHandler); 456 457 // set the time after start watching to not miss anything 458 fVolumeWorker->SetWatchingStart(real_time_clock_usecs()); 459 460 char name[255]; 461 fVolume.GetName(name); 462 463 fCatchUpManager.CatchUp(); 464 465 fWatching = true; 466 return true; 467 } 468 469 470 void 471 VolumeWatcher::Stop() 472 { 473 474 char name[255]; 475 fVolume.GetName(name); 476 477 // set the time before stop watching to not miss anything 478 fVolumeWorker->SetWatchingPosition(real_time_clock_usecs()); 479 480 stop_watching(&fWatchNameHandler); 481 482 fVolumeWorker->WriteAnalyserSettings(); 483 484 // don't stop the work because we have to handle all entries after writing 485 // the watching position 486 //fVolumeWorker->Stop(); 487 fCatchUpManager.Stop(); 488 } 489 490 491 bool 492 VolumeWatcher::AddAnalyser(FileAnalyser* analyser) 493 { 494 if (!fVolumeWorker->AddAnalyser(analyser)) 495 return false; 496 497 BAutolock _(this); 498 if (!fCatchUpManager.AddAnalyser(analyser)) 499 return false; 500 501 if (fWatching) 502 fCatchUpManager.CatchUp(); 503 504 return true; 505 } 506 507 508 bool 509 VolumeWatcher::RemoveAnalyser(const BString& name) 510 { 511 if (!fVolumeWorker->RemoveAnalyser(name)) 512 return false; 513 514 BAutolock _(this); 515 fCatchUpManager.RemoveAnalyser(name); 516 return true; 517 } 518 519 520 void 521 VolumeWatcher::GetSecureEntries(list_collection& collection) 522 { 523 BAutolock _(this); 524 collection.createdList = fCreatedList.SwapList(); 525 collection.deletedList = fDeleteList.SwapList(); 526 collection.modifiedList = fModifiedList.SwapList(); 527 collection.movedList = fMovedList.SwapList(); 528 collection.movedFromList = fMovedFromList.SwapList(); 529 } 530 531 532 bool 533 VolumeWatcher::FindEntryRef(ino_t node, dev_t device, entry_ref& entry) 534 { 535 return false; 536 } 537 538 539 void 540 VolumeWatcher::_NewEntriesArrived() 541 { 542 // The fVolumeWorker has to exist as long as we live so directly post to 543 // the queue. 544 if (fVolumeWorker->IsBusy()) 545 return; 546 fVolumeWorker->PostMessage(kTriggerWork); 547 } 548