1/*
2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
8
9
10#include "Volume.h"
11
12#include <errno.h>
13#include <stdlib.h>
14#include <sys/stat.h>
15#include <time.h>
16#include <unistd.h>
17
18#include <Directory.h>
19#include <Entry.h>
20#include <File.h>
21#include <Looper.h>
22#include <MessageRunner.h>
23#include <NodeMonitor.h>
24#include <Path.h>
25#include <Roster.h>
26#include <SymLink.h>
27
28#include <package/CommitTransactionResult.h>
29#include <package/PackageRoster.h>
30#include <package/solver/Solver.h>
31#include <package/solver/SolverPackage.h>
32#include <package/solver/SolverProblem.h>
33#include <package/solver/SolverProblemSolution.h>
34#include <package/solver/SolverRepository.h>
35#include <package/solver/SolverResult.h>
36
37#include <AutoDeleter.h>
38#include <AutoLocker.h>
39#include <NotOwningEntryRef.h>
40#include <package/DaemonDefs.h>
41#include <RosterPrivate.h>
42
43#include "CommitTransactionHandler.h"
44#include "Constants.h"
45#include "DebugSupport.h"
46#include "Exception.h"
47#include "PackageFileManager.h"
48#include "Root.h"
49#include "VolumeState.h"
50
51
52using namespace BPackageKit::BPrivate;
53
54
55// #pragma mark - Listener
56
57
58Volume::Listener::~Listener()
59{
60}
61
62
63// #pragma mark - NodeMonitorEvent
64
65
66struct Volume::NodeMonitorEvent
67	: public DoublyLinkedListLinkImpl<NodeMonitorEvent> {
68public:
69	NodeMonitorEvent(const BString& entryName, bool created)
70		:
71		fEntryName(entryName),
72		fCreated(created)
73	{
74	}
75
76	const BString& EntryName() const
77	{
78		return fEntryName;
79	}
80
81	bool WasCreated() const
82	{
83		return fCreated;
84	}
85
86private:
87	BString	fEntryName;
88	bool	fCreated;
89};
90
91
92// #pragma mark - PackagesDirectory
93
94
95struct Volume::PackagesDirectory {
96public:
97	PackagesDirectory()
98		:
99		fNodeRef(),
100		fName()
101	{
102	}
103
104	void Init(const node_ref& nodeRef, bool isPackagesDir)
105	{
106		fNodeRef = nodeRef;
107
108		if (isPackagesDir)
109			return;
110
111		BDirectory directory;
112		BEntry entry;
113		if (directory.SetTo(&fNodeRef) == B_OK
114			&& directory.GetEntry(&entry) == B_OK) {
115			fName = entry.Name();
116		}
117
118		if (fName.IsEmpty())
119			fName = "unknown state";
120	}
121
122	const node_ref& NodeRef() const
123	{
124		return fNodeRef;
125	}
126
127	const BString& Name() const
128	{
129		return fName;
130	}
131
132private:
133	node_ref	fNodeRef;
134	BString		fName;
135};
136
137
138// #pragma mark - Volume
139
140
141Volume::Volume(BLooper* looper)
142	:
143	BHandler(),
144	fPath(),
145	fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM),
146	fRootDirectoryRef(),
147	fPackagesDirectories(NULL),
148	fPackagesDirectoryCount(0),
149	fRoot(NULL),
150	fListener(NULL),
151	fPackageFileManager(NULL),
152	fLatestState(NULL),
153	fActiveState(NULL),
154	fChangeCount(0),
155	fLock("volume"),
156	fPendingNodeMonitorEventsLock("pending node monitor events"),
157	fPendingNodeMonitorEvents(),
158	fNodeMonitorEventHandleTime(0),
159	fPackagesToBeActivated(),
160	fPackagesToBeDeactivated(),
161	fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY),
162	fPendingPackageJobCount(0)
163{
164	looper->AddHandler(this);
165}
166
167
168Volume::~Volume()
169{
170	Unmounted();
171		// needed for error case in InitPackages()
172
173	_SetLatestState(NULL, true);
174
175	delete[] fPackagesDirectories;
176	delete fPackageFileManager;
177}
178
179
180status_t
181Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef)
182{
183	status_t error = fLock.InitCheck();
184	if (error != B_OK)
185		return error;
186
187	error = fPendingNodeMonitorEventsLock.InitCheck();
188	if (error != B_OK)
189		return error;
190
191	fLatestState = new(std::nothrow) VolumeState;
192	if (fLatestState == NULL || !fLatestState->Init())
193		RETURN_ERROR(B_NO_MEMORY);
194
195	fPackageFileManager = new(std::nothrow) PackageFileManager(fLock);
196	if (fPackageFileManager == NULL)
197		RETURN_ERROR(B_NO_MEMORY);
198
199	error = fPackageFileManager->Init();
200	if (error != B_OK)
201		RETURN_ERROR(error);
202
203	fRootDirectoryRef = rootDirectoryRef;
204
205	// open the root directory
206	BDirectory directory;
207	error = directory.SetTo(&fRootDirectoryRef);
208	if (error != B_OK) {
209		ERROR("Volume::Init(): failed to open root directory: %s\n",
210			strerror(error));
211		RETURN_ERROR(error);
212	}
213
214	// get the directory path
215	BEntry entry;
216	error = directory.GetEntry(&entry);
217
218	BPath path;
219	if (error == B_OK)
220		error = entry.GetPath(&path);
221
222	if (error != B_OK) {
223		ERROR("Volume::Init(): failed to get root directory path: %s\n",
224			strerror(error));
225		RETURN_ERROR(error);
226	}
227
228	fPath = path.Path();
229	if (fPath.IsEmpty())
230		RETURN_ERROR(B_NO_MEMORY);
231
232	// get a volume info from the FS
233	int fd = directory.Dup();
234	if (fd < 0) {
235		ERROR("Volume::Init(): failed to get root directory FD: %s\n",
236			strerror(fd));
237		RETURN_ERROR(fd);
238	}
239	FileDescriptorCloser fdCloser(fd);
240
241	// get the volume info from packagefs
242	uint32 maxPackagesDirCount = 16;
243	PackageFSVolumeInfo* info = NULL;
244	MemoryDeleter infoDeleter;
245	size_t bufferSize;
246	for (;;) {
247		bufferSize = sizeof(PackageFSVolumeInfo)
248			+ (maxPackagesDirCount - 1) * sizeof(PackageFSDirectoryInfo);
249		info = (PackageFSVolumeInfo*)malloc(bufferSize);
250		if (info == NULL)
251			RETURN_ERROR(B_NO_MEMORY);
252		infoDeleter.SetTo(info);
253
254		if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, info,
255				bufferSize) != 0) {
256			ERROR("Volume::Init(): failed to get volume info: %s\n",
257				strerror(errno));
258			RETURN_ERROR(errno);
259		}
260
261		if (info->packagesDirectoryCount <= maxPackagesDirCount)
262			break;
263
264		maxPackagesDirCount = info->packagesDirectoryCount;
265		infoDeleter.Unset();
266	}
267
268	if (info->packagesDirectoryCount < 1) {
269		ERROR("Volume::Init(): got invalid volume info from packagefs\n");
270		RETURN_ERROR(B_BAD_VALUE);
271	}
272
273	fMountType = info->mountType;
274
275	fPackagesDirectories = new(std::nothrow) PackagesDirectory[
276		info->packagesDirectoryCount];
277	if (fPackagesDirectories == NULL)
278		RETURN_ERROR(B_NO_MEMORY);
279
280	fPackagesDirectoryCount = info->packagesDirectoryCount;
281
282	for (uint32 i = 0; i < info->packagesDirectoryCount; i++) {
283		fPackagesDirectories[i].Init(
284			node_ref(info->packagesDirectoryInfos[i].deviceID,
285				info->packagesDirectoryInfos[i].nodeID),
286			i == 0);
287	}
288
289	_packageRootRef.device = info->rootDeviceID;
290	_packageRootRef.node = info->rootDirectoryID;
291
292	return B_OK;
293}
294
295
296status_t
297Volume::InitPackages(Listener* listener)
298{
299	// node-monitor the volume's packages directory
300	status_t error = watch_node(&PackagesDirectoryRef(), B_WATCH_DIRECTORY,
301		BMessenger(this));
302	if (error == B_OK) {
303		fListener = listener;
304	} else {
305		ERROR("Volume::InitPackages(): failed to start watching the packages "
306			"directory of the volume at \"%s\": %s\n",
307			fPath.String(), strerror(error));
308		// Not good, but not fatal. Only the manual package operations in the
309		// packages directory won't work correctly.
310	}
311
312	// read the packages directory and get the active packages
313	int fd = OpenRootDirectory();
314	if (fd < 0) {
315		ERROR("Volume::InitPackages(): failed to open root directory: %s\n",
316			strerror(fd));
317		RETURN_ERROR(fd);
318	}
319	FileDescriptorCloser fdCloser(fd);
320
321	error = _ReadPackagesDirectory();
322	if (error != B_OK)
323		RETURN_ERROR(error);
324
325	error = _InitLatestState();
326	if (error != B_OK)
327		RETURN_ERROR(error);
328
329	error = _GetActivePackages(fd);
330	if (error != B_OK)
331		RETURN_ERROR(error);
332
333	// create the admin directory, if it doesn't exist yet
334	BDirectory packagesDirectory;
335	if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) {
336		if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists())
337			packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL);
338	}
339
340	return B_OK;
341}
342
343
344status_t
345Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly)
346{
347	for (PackageFileNameHashTable::Iterator it
348			= fLatestState->ByFileNameIterator(); it.HasNext();) {
349		Package* package = it.Next();
350		if (activeOnly && !package->IsActive())
351			continue;
352
353		status_t error = repository.AddPackage(package->Info());
354		if (error != B_OK) {
355			ERROR("Volume::AddPackagesToRepository(): failed to add package %s "
356				"to repository: %s\n", package->FileName().String(),
357				strerror(error));
358			return error;
359		}
360	}
361
362	return B_OK;
363}
364
365
366void
367Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume)
368{
369INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume);
370	// create the solver
371	BSolver* solver;
372	status_t error = BSolver::Create(solver);
373	if (error != B_OK) {
374		ERROR("Volume::InitialVerify(): failed to create solver: %s\n",
375			strerror(error));
376		return;
377	}
378	ObjectDeleter<BSolver> solverDeleter(solver);
379
380	// add a repository with all active packages
381	BSolverRepository repository;
382	error = _AddRepository(solver, repository, true, true);
383	if (error != B_OK) {
384		ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
385			strerror(error));
386		return;
387	}
388
389	// add a repository for the next volume
390	BSolverRepository nextRepository;
391	if (nextVolume != NULL) {
392		nextRepository.SetPriority(1);
393		error = nextVolume->_AddRepository(solver, nextRepository, true, false);
394		if (error != B_OK) {
395			ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
396				strerror(error));
397			return;
398		}
399	}
400
401	// add a repository for the next next volume
402	BSolverRepository nextNextRepository;
403	if (nextNextVolume != NULL) {
404		nextNextRepository.SetPriority(2);
405		error = nextNextVolume->_AddRepository(solver, nextNextRepository, true,
406			false);
407		if (error != B_OK) {
408			ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
409				strerror(error));
410			return;
411		}
412	}
413
414	// verify
415	error = solver->VerifyInstallation();
416	if (error != B_OK) {
417		ERROR("Volume::InitialVerify(): failed to verify: %s\n",
418			strerror(error));
419		return;
420	}
421
422	if (!solver->HasProblems()) {
423		INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n",
424			Path().String());
425		return;
426	}
427
428	// print the problems
429// TODO: Notify the user ...
430	INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n",
431		Path().String());
432
433	int32 problemCount = solver->CountProblems();
434	for (int32 i = 0; i < problemCount; i++) {
435		BSolverProblem* problem = solver->ProblemAt(i);
436		INFORM("  %" B_PRId32 ": %s\n", i + 1, problem->ToString().String());
437		int32 solutionCount = problem->CountSolutions();
438		for (int32 k = 0; k < solutionCount; k++) {
439			const BSolverProblemSolution* solution = problem->SolutionAt(k);
440			INFORM("    solution %" B_PRId32 ":\n", k + 1);
441			int32 elementCount = solution->CountElements();
442			for (int32 l = 0; l < elementCount; l++) {
443				const BSolverProblemSolutionElement* element
444					= solution->ElementAt(l);
445				INFORM("      - %s\n", element->ToString().String());
446			}
447		}
448	}
449}
450
451
452void
453Volume::HandleGetLocationInfoRequest(BMessage* message)
454{
455	AutoLocker<BLocker> locker(fLock);
456
457	// If the cached reply message is up-to-date, just send it.
458	int64 changeCount;
459	if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK
460		&& changeCount == fChangeCount) {
461		locker.Unlock();
462		message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
463			kCommunicationTimeout);
464		return;
465	}
466
467	// rebuild the reply message
468	fLocationInfoReply.MakeEmpty();
469
470	if (fLocationInfoReply.AddInt32("base directory device",
471			fRootDirectoryRef.device) != B_OK
472		|| fLocationInfoReply.AddInt64("base directory node",
473			fRootDirectoryRef.node) != B_OK
474		|| fLocationInfoReply.AddInt32("packages directory device",
475			PackagesDeviceID()) != B_OK
476		|| fLocationInfoReply.AddInt64("packages directory node",
477			PackagesDirectoryID()) != B_OK) {
478		return;
479	}
480
481	for (PackageFileNameHashTable::Iterator it
482			= fLatestState->ByFileNameIterator(); it.HasNext();) {
483		Package* package = it.Next();
484		const char* fieldName = package->IsActive()
485			? "latest active packages" : "latest inactive packages";
486		BMessage packageArchive;
487		if (package->Info().Archive(&packageArchive) != B_OK
488			|| fLocationInfoReply.AddMessage(fieldName, &packageArchive)
489				!= B_OK) {
490			return;
491		}
492	}
493
494	if (fActiveState != fLatestState) {
495		if (fPackagesDirectoryCount > 1) {
496			fLocationInfoReply.AddString("old state",
497				fPackagesDirectories[fPackagesDirectoryCount - 1].Name());
498		}
499
500		for (PackageFileNameHashTable::Iterator it
501				= fActiveState->ByFileNameIterator(); it.HasNext();) {
502			Package* package = it.Next();
503			if (!package->IsActive())
504				continue;
505
506			BMessage packageArchive;
507			if (package->Info().Archive(&packageArchive) != B_OK
508				|| fLocationInfoReply.AddMessage("currently active packages",
509					&packageArchive) != B_OK) {
510				return;
511			}
512		}
513	}
514
515	if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK)
516		return;
517
518	locker.Unlock();
519
520	message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
521		kCommunicationTimeout);
522}
523
524
525void
526Volume::HandleCommitTransactionRequest(BMessage* message)
527{
528	BCommitTransactionResult result;
529	PackageSet dummy;
530	_CommitTransaction(message, NULL, dummy, dummy, result);
531
532	BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY);
533	status_t error = result.AddToMessage(reply);
534	if (error != B_OK) {
535		ERROR("Volume::HandleCommitTransactionRequest(): Failed to add "
536			"transaction result to reply: %s\n", strerror(error));
537		return;
538	}
539
540	message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout);
541}
542
543
544void
545Volume::PackageJobPending()
546{
547	atomic_add(&fPendingPackageJobCount, 1);
548}
549
550
551void
552Volume::PackageJobFinished()
553{
554	atomic_add(&fPendingPackageJobCount, -1);
555}
556
557
558bool
559Volume::IsPackageJobPending() const
560{
561	return fPendingPackageJobCount != 0;
562}
563
564
565void
566Volume::Unmounted()
567{
568	if (fListener != NULL) {
569		stop_watching(BMessenger(this));
570		fListener = NULL;
571	}
572
573	if (BLooper* looper = Looper())
574		looper->RemoveHandler(this);
575}
576
577
578void
579Volume::MessageReceived(BMessage* message)
580{
581	switch (message->what) {
582		case B_NODE_MONITOR:
583		{
584			int32 opcode;
585			if (message->FindInt32("opcode", &opcode) != B_OK)
586				break;
587
588			switch (opcode) {
589				case B_ENTRY_CREATED:
590					_HandleEntryCreatedOrRemoved(message, true);
591					break;
592				case B_ENTRY_REMOVED:
593					_HandleEntryCreatedOrRemoved(message, false);
594					break;
595				case B_ENTRY_MOVED:
596					_HandleEntryMoved(message);
597					break;
598				default:
599					break;
600			}
601			break;
602		}
603
604		case kHandleNodeMonitorEvents:
605			if (fListener != NULL) {
606				if (system_time() >= fNodeMonitorEventHandleTime)
607					fListener->VolumeNodeMonitorEventOccurred(this);
608			}
609			break;
610
611		default:
612			BHandler::MessageReceived(message);
613			break;
614	}
615}
616
617
618BPackageInstallationLocation
619Volume::Location() const
620{
621	switch (fMountType) {
622		case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
623			return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
624		case PACKAGE_FS_MOUNT_TYPE_HOME:
625			return B_PACKAGE_INSTALLATION_LOCATION_HOME;
626		case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
627		default:
628			return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT;
629	}
630}
631
632
633const node_ref&
634Volume::PackagesDirectoryRef() const
635{
636	return fPackagesDirectories[0].NodeRef();
637}
638
639
640PackageFileNameHashTable::Iterator
641Volume::PackagesByFileNameIterator() const
642{
643	return fLatestState->ByFileNameIterator();
644}
645
646
647int
648Volume::OpenRootDirectory() const
649{
650	BDirectory directory;
651	status_t error = directory.SetTo(&fRootDirectoryRef);
652	if (error != B_OK) {
653		ERROR("Volume::OpenRootDirectory(): failed to open root directory: "
654			"%s\n", strerror(error));
655		RETURN_ERROR(error);
656	}
657
658	return directory.Dup();
659}
660
661
662void
663Volume::ProcessPendingNodeMonitorEvents()
664{
665	// get the events
666	NodeMonitorEventList events;
667	{
668		AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
669		events.MoveFrom(&fPendingNodeMonitorEvents);
670	}
671
672	// process them
673	while (NodeMonitorEvent* event = events.RemoveHead()) {
674		ObjectDeleter<NodeMonitorEvent> eventDeleter(event);
675		if (event->WasCreated())
676			_PackagesEntryCreated(event->EntryName());
677		else
678			_PackagesEntryRemoved(event->EntryName());
679	}
680}
681
682
683bool
684Volume::HasPendingPackageActivationChanges() const
685{
686	return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
687}
688
689
690void
691Volume::ProcessPendingPackageActivationChanges()
692{
693	if (!HasPendingPackageActivationChanges())
694		return;
695
696	// perform the request
697	BCommitTransactionResult result;
698	_CommitTransaction(NULL, NULL, fPackagesToBeActivated,
699		fPackagesToBeDeactivated, result);
700
701	if (result.Error() != B_TRANSACTION_OK) {
702		ERROR("Volume::ProcessPendingPackageActivationChanges(): package "
703			"activation failed: %s\n", result.FullErrorMessage().String());
704// TODO: Notify the user!
705	}
706
707	// clear the activation/deactivation sets in any event
708	fPackagesToBeActivated.clear();
709	fPackagesToBeDeactivated.clear();
710}
711
712
713void
714Volume::ClearPackageActivationChanges()
715{
716	fPackagesToBeActivated.clear();
717	fPackagesToBeDeactivated.clear();
718}
719
720
721status_t
722Volume::CreateTransaction(BPackageInstallationLocation location,
723	BActivationTransaction& _transaction, BDirectory& _transactionDirectory)
724{
725	// open admin directory
726	BDirectory adminDirectory;
727	status_t error = _OpenPackagesSubDirectory(
728		RelativePath(kAdminDirectoryName), true, adminDirectory);
729	if (error != B_OK)
730		return error;
731
732	// create a transaction directory
733	int uniqueId = 1;
734	BString directoryName;
735	for (;; uniqueId++) {
736		directoryName.SetToFormat("transaction-%d", uniqueId);
737		if (directoryName.IsEmpty())
738			return B_NO_MEMORY;
739
740		error = adminDirectory.CreateDirectory(directoryName,
741			&_transactionDirectory);
742		if (error == B_OK)
743			break;
744		if (error != B_FILE_EXISTS)
745			return error;
746	}
747
748	// init the transaction
749	error = _transaction.SetTo(location, fChangeCount, directoryName);
750	if (error != B_OK) {
751		BEntry entry;
752		_transactionDirectory.GetEntry(&entry);
753		_transactionDirectory.Unset();
754		if (entry.InitCheck() == B_OK)
755			entry.Remove();
756		return error;
757	}
758
759	return B_OK;
760}
761
762
763void
764Volume::CommitTransaction(const BActivationTransaction& transaction,
765	const PackageSet& packagesAlreadyAdded,
766	const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
767{
768	_CommitTransaction(NULL, &transaction, packagesAlreadyAdded,
769		packagesAlreadyRemoved, _result);
770}
771
772
773void
774Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created)
775{
776	// only moves to or from our packages directory are interesting
777	int32 deviceID;
778	int64 directoryID;
779	const char* name;
780	if (message->FindInt32("device", &deviceID) != B_OK
781		|| message->FindInt64("directory", &directoryID) != B_OK
782		|| message->FindString("name", &name) != B_OK
783		|| node_ref(deviceID, directoryID) != PackagesDirectoryRef()) {
784		return;
785	}
786
787	_QueueNodeMonitorEvent(name, created);
788}
789
790
791void
792Volume::_HandleEntryMoved(const BMessage* message)
793{
794	int32 deviceID;
795	int64 fromDirectoryID;
796	int64 toDirectoryID;
797	const char* fromName;
798	const char* toName;
799	if (message->FindInt32("device", &deviceID) != B_OK
800		|| message->FindInt64("from directory", &fromDirectoryID) != B_OK
801		|| message->FindInt64("to directory", &toDirectoryID) != B_OK
802		|| message->FindString("from name", &fromName) != B_OK
803		|| message->FindString("name", &toName) != B_OK
804		|| deviceID != PackagesDeviceID()
805		|| (fromDirectoryID != PackagesDirectoryID()
806			&& toDirectoryID != PackagesDirectoryID())) {
807		return;
808	}
809
810	AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
811		// make sure for a move the two events cannot get split
812
813	if (fromDirectoryID == PackagesDirectoryID())
814		_QueueNodeMonitorEvent(fromName, false);
815	if (toDirectoryID == PackagesDirectoryID())
816		_QueueNodeMonitorEvent(toName, true);
817}
818
819
820void
821Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated)
822{
823	if (name.IsEmpty()) {
824		ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n");
825		return;
826	}
827
828	// ignore entries that don't have the ".hpkg" extension
829	if (!name.EndsWith(kPackageFileNameExtension))
830		return;
831
832	NodeMonitorEvent* event
833		= new(std::nothrow) NodeMonitorEvent(name, wasCreated);
834	if (event == NULL) {
835		ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n");
836		return;
837	}
838
839	AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
840	fPendingNodeMonitorEvents.Add(event);
841	eventsLock.Unlock();
842
843	fNodeMonitorEventHandleTime
844		= system_time() + kNodeMonitorEventHandlingDelay;
845	BMessage message(kHandleNodeMonitorEvents);
846	BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay,
847		1);
848}
849
850
851void
852Volume::_PackagesEntryCreated(const char* name)
853{
854INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
855	// Ignore the event, if the package is already known.
856	Package* package = fLatestState->FindPackage(name);
857	if (package != NULL) {
858		if (package->File()->EntryCreatedIgnoreLevel() > 0) {
859			package->File()->DecrementEntryCreatedIgnoreLevel();
860		} else {
861			WARN("node monitoring created event for already known entry "
862				"\"%s\"\n", name);
863		}
864
865		// Remove the package from the packages-to-be-deactivated set, if it is in
866		// there (unlikely, unless we see a remove-create sequence).
867		PackageSet::iterator it = fPackagesToBeDeactivated.find(package);
868		if (it != fPackagesToBeDeactivated.end())
869			fPackagesToBeDeactivated.erase(it);
870
871		return;
872	}
873
874	status_t error = fPackageFileManager->CreatePackage(
875		NotOwningEntryRef(PackagesDirectoryRef(), name),
876		package);
877	if (error != B_OK) {
878		ERROR("failed to init package for file \"%s\"\n", name);
879		return;
880	}
881
882	fLock.Lock();
883	fLatestState->AddPackage(package);
884	fChangeCount++;
885	fLock.Unlock();
886
887	try {
888		fPackagesToBeActivated.insert(package);
889	} catch (std::bad_alloc& exception) {
890		ERROR("out of memory\n");
891		return;
892	}
893}
894
895
896void
897Volume::_PackagesEntryRemoved(const char* name)
898{
899INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
900	Package* package = fLatestState->FindPackage(name);
901	if (package == NULL)
902		return;
903
904	// Ignore the event, if we generated it ourselves.
905	if (package->File()->EntryRemovedIgnoreLevel() > 0) {
906		package->File()->DecrementEntryRemovedIgnoreLevel();
907		return;
908	}
909
910	// Remove the package from the packages-to-be-activated set, if it is in
911	// there (unlikely, unless we see a create-remove-create sequence).
912	PackageSet::iterator it = fPackagesToBeActivated.find(package);
913	if (it != fPackagesToBeActivated.end())
914		fPackagesToBeActivated.erase(it);
915
916	// If the package isn't active, just remove it for good.
917	if (!package->IsActive()) {
918		AutoLocker<BLocker> locker(fLock);
919		fLatestState->RemovePackage(package);
920		fChangeCount++;
921		delete package;
922		return;
923	}
924
925	// The package must be deactivated.
926	try {
927		fPackagesToBeDeactivated.insert(package);
928	} catch (std::bad_alloc& exception) {
929		ERROR("out of memory\n");
930		return;
931	}
932}
933
934
935status_t
936Volume::_ReadPackagesDirectory()
937{
938	BDirectory directory;
939	status_t error = directory.SetTo(&PackagesDirectoryRef());
940	if (error != B_OK) {
941		ERROR("Volume::_ReadPackagesDirectory(): failed to open packages "
942			"directory: %s\n", strerror(error));
943		RETURN_ERROR(error);
944	}
945
946	entry_ref entry;
947	while (directory.GetNextRef(&entry) == B_OK) {
948		if (!BString(entry.name).EndsWith(kPackageFileNameExtension))
949			continue;
950
951		Package* package;
952		status_t error = fPackageFileManager->CreatePackage(entry, package);
953		if (error == B_OK) {
954			AutoLocker<BLocker> locker(fLock);
955			fLatestState->AddPackage(package);
956			fChangeCount++;
957		}
958	}
959
960	return B_OK;
961}
962
963
964status_t
965Volume::_InitLatestState()
966{
967	if (_InitLatestStateFromActivatedPackages() == B_OK)
968		return B_OK;
969
970	INFORM("Failed to get activated packages info from activated packages file."
971		" Assuming all package files in package directory are activated.\n");
972
973	AutoLocker<BLocker> locker(fLock);
974
975	for (PackageFileNameHashTable::Iterator it
976				= fLatestState->ByFileNameIterator();
977			Package* package = it.Next();) {
978		fLatestState->SetPackageActive(package, true);
979		fChangeCount++;
980	}
981
982	return B_OK;
983}
984
985
986status_t
987Volume::_InitLatestStateFromActivatedPackages()
988{
989	// try reading the activation file
990	NotOwningEntryRef entryRef(PackagesDirectoryRef(), kActivationFileName);
991	BFile file;
992	status_t error = file.SetTo(&entryRef, B_READ_ONLY);
993	if (error != B_OK) {
994		INFORM("Failed to open packages activation file: %s\n",
995			strerror(error));
996		RETURN_ERROR(error);
997	}
998
999	// read the whole file into memory to simplify things
1000	off_t size;
1001	error = file.GetSize(&size);
1002	if (error != B_OK) {
1003		ERROR("Failed to packages activation file size: %s\n",
1004			strerror(error));
1005		RETURN_ERROR(error);
1006	}
1007
1008	if (size > (off_t)kMaxActivationFileSize) {
1009		ERROR("The packages activation file is too big.\n");
1010		RETURN_ERROR(B_BAD_DATA);
1011	}
1012
1013	char* fileContent = (char*)malloc(size + 1);
1014	if (fileContent == NULL)
1015		RETURN_ERROR(B_NO_MEMORY);
1016	MemoryDeleter fileContentDeleter(fileContent);
1017
1018	ssize_t bytesRead = file.Read(fileContent, size);
1019	if (bytesRead < 0) {
1020		ERROR("Failed to read packages activation file: %s\n",
1021			strerror(bytesRead));
1022		RETURN_ERROR(errno);
1023	}
1024
1025	if (bytesRead != size) {
1026		ERROR("Failed to read whole packages activation file.\n");
1027		RETURN_ERROR(B_ERROR);
1028	}
1029
1030	// null-terminate to simplify parsing
1031	fileContent[size] = '\0';
1032
1033	AutoLocker<BLocker> locker(fLock);
1034
1035	// parse the file and mark the respective packages active
1036	const char* packageName = fileContent;
1037	char* const fileContentEnd = fileContent + size;
1038	while (packageName < fileContentEnd) {
1039		char* packageNameEnd = strchr(packageName, '\n');
1040		if (packageNameEnd == NULL)
1041			packageNameEnd = fileContentEnd;
1042
1043		// skip empty lines
1044		if (packageName == packageNameEnd) {
1045			packageName++;
1046			continue;
1047		}
1048		*packageNameEnd = '\0';
1049
1050		if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
1051			ERROR("Invalid packages activation file content.\n");
1052			RETURN_ERROR(B_BAD_DATA);
1053		}
1054
1055		Package* package = fLatestState->FindPackage(packageName);
1056		if (package != NULL) {
1057			fLatestState->SetPackageActive(package, true);
1058			fChangeCount++;
1059		} else {
1060			WARN("Package \"%s\" from activation file not in packages "
1061				"directory.\n", packageName);
1062		}
1063
1064		packageName = packageNameEnd + 1;
1065	}
1066
1067	return B_OK;
1068}
1069
1070
1071status_t
1072Volume::_GetActivePackages(int fd)
1073{
1074	// get the info from packagefs
1075	PackageFSGetPackageInfosRequest* request = NULL;
1076	MemoryDeleter requestDeleter;
1077	size_t bufferSize = 64 * 1024;
1078	for (;;) {
1079		request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize);
1080		if (request == NULL)
1081			RETURN_ERROR(B_NO_MEMORY);
1082		requestDeleter.SetTo(request);
1083
1084		if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request,
1085				bufferSize) != 0) {
1086			ERROR("Volume::_GetActivePackages(): failed to get active package "
1087				"info from package FS: %s\n", strerror(errno));
1088			RETURN_ERROR(errno);
1089		}
1090
1091		if (request->bufferSize <= bufferSize)
1092			break;
1093
1094		bufferSize = request->bufferSize;
1095		requestDeleter.Unset();
1096	}
1097
1098	INFORM("latest volume state:\n");
1099	_DumpState(fLatestState);
1100
1101	// check whether that matches the expected state
1102	if (_CheckActivePackagesMatchLatestState(request)) {
1103		INFORM("The latest volume state is also the currently active one\n");
1104		fActiveState = fLatestState;
1105		return B_OK;
1106	}
1107
1108	// There's a mismatch. We need a new state that reflects the actual
1109	// activation situation.
1110	VolumeState* state = new(std::nothrow) VolumeState;
1111	if (state == NULL)
1112		RETURN_ERROR(B_NO_MEMORY);
1113	ObjectDeleter<VolumeState> stateDeleter(state);
1114
1115	for (uint32 i = 0; i < request->packageCount; i++) {
1116		const PackageFSPackageInfo& info = request->infos[i];
1117		NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID,
1118			info.name);
1119		Package* package;
1120		status_t error = fPackageFileManager->CreatePackage(entryRef, package);
1121		if (error != B_OK) {
1122			WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %"
1123				B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID,
1124				info.directoryNodeID, info.name, strerror(error));
1125			continue;
1126		}
1127
1128		state->AddPackage(package);
1129		state->SetPackageActive(package, true);
1130	}
1131
1132	INFORM("currently active volume state:\n");
1133	_DumpState(state);
1134
1135	fActiveState = stateDeleter.Detach();
1136	return B_OK;
1137}
1138
1139
1140void
1141Volume::_RunQueuedScripts()
1142{
1143	BDirectory adminDirectory;
1144	status_t error = _OpenPackagesSubDirectory(
1145		RelativePath(kAdminDirectoryName), false, adminDirectory);
1146	if (error != B_OK)
1147		return;
1148
1149	BDirectory scriptsDirectory;
1150	error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName);
1151	if (error != B_OK)
1152		return;
1153
1154	// enumerate all the symlinks in the queued scripts directory
1155	BEntry scriptEntry;
1156	while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) {
1157		BPath scriptPath;
1158		scriptEntry.GetPath(&scriptPath);
1159		error = scriptPath.InitCheck();
1160		if (error != B_OK) {
1161			INFORM("failed to get path of post-installation script \"%s\"\n",
1162				strerror(error));
1163			continue;
1164		}
1165
1166		errno = 0;
1167		int result = system(scriptPath.Path());
1168		if (result != 0) {
1169			INFORM("running post-installation script \"%s\" "
1170				"failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno));
1171		}
1172
1173		// remove the symlink, now that we've run the post-installation script
1174		error = scriptEntry.Remove();
1175		if (error != B_OK) {
1176			INFORM("removing queued post-install script failed \"%s\"\n",
1177				strerror(error));
1178		}
1179	}
1180}
1181
1182
1183bool
1184Volume::_CheckActivePackagesMatchLatestState(
1185	PackageFSGetPackageInfosRequest* request)
1186{
1187	if (fPackagesDirectoryCount != 1) {
1188		INFORM("An old packages state (\"%s\") seems to be active.\n",
1189			fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String());
1190		return false;
1191	}
1192
1193	const node_ref packagesDirRef(PackagesDirectoryRef());
1194
1195	// mark the returned packages active
1196	for (uint32 i = 0; i < request->packageCount; i++) {
1197		const PackageFSPackageInfo& info = request->infos[i];
1198		if (node_ref(info.directoryDeviceID, info.directoryNodeID)
1199				!= packagesDirRef) {
1200			WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1201				") not in packages directory\n", info.name,
1202				info.packageDeviceID, info.packageNodeID);
1203			return false;
1204		}
1205
1206		Package* package = fLatestState->FindPackage(
1207			node_ref(info.packageDeviceID, info.packageNodeID));
1208		if (package == NULL || !package->IsActive()) {
1209			WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1210				") not %s\n", info.name,
1211				info.packageDeviceID, info.packageNodeID,
1212				package == NULL
1213					? "found in packages directory" : "supposed to be active");
1214			return false;
1215		}
1216	}
1217
1218	// Check whether there are packages that aren't active but should be.
1219	uint32 count = 0;
1220	for (PackageNodeRefHashTable::Iterator it
1221			= fLatestState->ByNodeRefIterator(); it.HasNext();) {
1222		Package* package = it.Next();
1223		if (package->IsActive())
1224			count++;
1225	}
1226
1227	if (count != request->packageCount) {
1228		INFORM("There seem to be packages in the packages directory that "
1229			"should be active.\n");
1230		return false;
1231	}
1232
1233	return true;
1234}
1235
1236
1237void
1238Volume::_SetLatestState(VolumeState* state, bool isActive)
1239{
1240	AutoLocker<BLocker> locker(fLock);
1241	if (isActive) {
1242		if (fLatestState != fActiveState)
1243			delete fActiveState;
1244		fActiveState = state;
1245	}
1246
1247	if (fLatestState != fActiveState)
1248		delete fLatestState;
1249	fLatestState = state;
1250	fChangeCount++;
1251
1252	locker.Unlock();
1253
1254	// Send a notification, if this is a system root volume.
1255	if (fRoot->IsSystemRoot()) {
1256		BMessage message(B_PACKAGE_UPDATE);
1257		if (message.AddInt32("event",
1258				(int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK
1259			&& message.AddInt32("location", (int32)Location()) == B_OK
1260			&& message.AddInt64("change count", fChangeCount) == B_OK) {
1261			BRoster::Private().SendTo(&message, NULL, false);
1262		}
1263	}
1264}
1265
1266
1267void
1268Volume::_DumpState(VolumeState* state)
1269{
1270	uint32 inactiveCount = 0;
1271	for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1272			it.HasNext();) {
1273		Package* package = it.Next();
1274		if (package->IsActive()) {
1275			INFORM("active package: \"%s\"\n", package->FileName().String());
1276		} else
1277			inactiveCount++;
1278	}
1279
1280	if (inactiveCount == 0)
1281		return;
1282
1283	for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1284			it.HasNext();) {
1285		Package* package = it.Next();
1286		if (!package->IsActive())
1287			INFORM("inactive package: \"%s\"\n", package->FileName().String());
1288	}
1289}
1290
1291
1292status_t
1293Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
1294	bool activeOnly, bool installed)
1295{
1296	status_t error = repository.SetTo(Path());
1297	if (error != B_OK) {
1298		ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1299			strerror(error));
1300		return error;
1301	}
1302
1303	repository.SetInstalled(installed);
1304
1305	error = AddPackagesToRepository(repository, true);
1306	if (error != B_OK) {
1307		ERROR("Volume::_AddRepository(): failed to add packages to "
1308			"repository: %s\n", strerror(error));
1309		return error;
1310	}
1311
1312	error = solver->AddRepository(&repository);
1313	if (error != B_OK) {
1314		ERROR("Volume::_AddRepository(): failed to add repository to solver: "
1315			"%s\n", strerror(error));
1316		return error;
1317	}
1318
1319	return B_OK;
1320}
1321
1322
1323status_t
1324Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
1325	BDirectory& _directory)
1326{
1327	// open the packages directory
1328	BDirectory directory;
1329	status_t error = directory.SetTo(&PackagesDirectoryRef());
1330	if (error != B_OK) {
1331		ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
1332			"directory: %s\n", strerror(error));
1333		RETURN_ERROR(error);
1334	}
1335
1336	return FSUtils::OpenSubDirectory(directory, path, create, _directory);
1337}
1338
1339
1340void
1341Volume::_CommitTransaction(BMessage* message,
1342	const BActivationTransaction* transaction,
1343	const PackageSet& packagesAlreadyAdded,
1344	const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
1345{
1346	_result.Unset();
1347
1348	// perform the request
1349	CommitTransactionHandler handler(this, fPackageFileManager, _result);
1350	BTransactionError error = B_TRANSACTION_INTERNAL_ERROR;
1351	try {
1352		handler.Init(fLatestState, fLatestState == fActiveState,
1353			packagesAlreadyAdded, packagesAlreadyRemoved);
1354
1355		if (message != NULL)
1356			handler.HandleRequest(message);
1357		else if (transaction != NULL)
1358			handler.HandleRequest(*transaction);
1359		else
1360			handler.HandleRequest();
1361
1362		_SetLatestState(handler.DetachVolumeState(),
1363			handler.IsActiveVolumeState());
1364		error = B_TRANSACTION_OK;
1365	} catch (Exception& exception) {
1366		error = exception.Error();
1367		exception.SetOnResult(_result);
1368		if (_result.ErrorPackage().IsEmpty()
1369			&& handler.CurrentPackage() != NULL) {
1370			_result.SetErrorPackage(handler.CurrentPackage()->FileName());
1371		}
1372	} catch (std::bad_alloc& exception) {
1373		error = B_TRANSACTION_NO_MEMORY;
1374	}
1375
1376	_result.SetError(error);
1377
1378	// revert on error
1379	if (error != B_TRANSACTION_OK)
1380		handler.Revert();
1381}
1382