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