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
24const bigtime_t kSecond = 1000000;
25
26
27WatchNameHandler::WatchNameHandler(VolumeWatcher* volumeWatcher)
28	:
29	fVolumeWatcher(volumeWatcher)
30{
31
32}
33
34
35void
36WatchNameHandler::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
45void
46WatchNameHandler::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
55void
56WatchNameHandler::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
69void
70WatchNameHandler::StatChanged(ino_t node, dev_t device, int32 statFields)
71{
72	if ((statFields & B_STAT_MODIFICATION_TIME) == 0)
73		return;
74}
75
76
77void
78WatchNameHandler::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
115AnalyserDispatcher::AnalyserDispatcher(const char* name)
116	:
117	BLooper(name, B_LOW_PRIORITY),
118
119	fStopped(0)
120{
121
122}
123
124
125AnalyserDispatcher::~AnalyserDispatcher()
126{
127	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
128		delete fFileAnalyserList.ItemAt(i);
129}
130
131
132void
133AnalyserDispatcher::Stop()
134{
135	atomic_set(&fStopped, 1);
136}
137
138
139bool
140AnalyserDispatcher::Stopped()
141{
142	return (atomic_get(&fStopped) != 0);
143}
144
145
146void
147AnalyserDispatcher::AnalyseEntry(const entry_ref& ref)
148{
149	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
150		fFileAnalyserList.ItemAt(i)->AnalyseEntry(ref);
151}
152
153
154void
155AnalyserDispatcher::DeleteEntry(const entry_ref& ref)
156{
157	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
158		fFileAnalyserList.ItemAt(i)->DeleteEntry(ref);
159}
160
161
162void
163AnalyserDispatcher::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
170void
171AnalyserDispatcher::LastEntry()
172{
173	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
174		fFileAnalyserList.ItemAt(i)->LastEntry();
175}
176
177
178bool
179AnalyserDispatcher::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
194bool
195AnalyserDispatcher::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
208FileAnalyser*
209AnalyserDispatcher::_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
220void
221AnalyserDispatcher::WriteAnalyserSettings()
222{
223	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
224		fFileAnalyserList.ItemAt(i)->Settings()->WriteSettings();
225}
226
227
228void
229AnalyserDispatcher::SetSyncPosition(bigtime_t time)
230{
231	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
232		fFileAnalyserList.ItemAt(i)->Settings()->SetSyncPosition(time);
233}
234
235
236void
237AnalyserDispatcher::SetWatchingStart(bigtime_t time)
238{
239	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
240		fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingStart(time);
241}
242
243
244void
245AnalyserDispatcher::SetWatchingPosition(bigtime_t time)
246{
247	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
248		fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingPosition(time);
249}
250
251
252VolumeWorker::VolumeWorker(VolumeWatcher* watcher)
253	:
254	AnalyserDispatcher("VolumeWorker"),
255
256	fVolumeWatcher(watcher),
257	fBusy(0)
258{
259
260}
261
262
263void
264VolumeWorker::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
277bool
278VolumeWorker::IsBusy()
279{
280	return (atomic_get(&fBusy) != 0);
281}
282
283
284void
285VolumeWorker::_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
325void
326VolumeWorker::_SetBusy(bool busy)
327{
328	if (busy)
329		atomic_set(&fBusy, 1);
330	else
331		atomic_set(&fBusy, 0);
332}
333
334
335VolumeWatcherBase::VolumeWatcherBase(const BVolume& volume)
336	:
337	fVolume(volume),
338	fEnabled(true),
339	fLastUpdated(0)
340{
341	ReadSettings();
342}
343
344
345const char* kEnabledAttr = "Enabled";
346
347
348bool
349VolumeWatcherBase::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
377bool
378VolumeWatcherBase::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
399SwapEntryRefVector::SwapEntryRefVector()
400{
401	fCurrentList = &fFirstList;
402	fNextList = &fSecondList;
403}
404
405
406EntryRefVector*
407SwapEntryRefVector::SwapList()
408{
409	EntryRefVector* temp = fCurrentList;
410	fCurrentList = fNextList;
411	fNextList = temp;
412	return temp;
413}
414
415
416EntryRefVector*
417SwapEntryRefVector::CurrentList()
418{
419	return fCurrentList;
420}
421
422
423VolumeWatcher::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
439VolumeWatcher::~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
449bool
450VolumeWatcher::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
470void
471VolumeWatcher::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
491bool
492VolumeWatcher::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
508bool
509VolumeWatcher::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
520void
521VolumeWatcher::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
532bool
533VolumeWatcher::FindEntryRef(ino_t node, dev_t device, entry_ref& entry)
534{
535	return false;
536}
537
538
539void
540VolumeWatcher::_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