2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
18 #include <Directory.h>
22 #include <MessageRunner.h>
23 #include <NodeMonitor.h>
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>
37 #include <AutoDeleter.h>
38 #include <AutoLocker.h>
39 #include <NotOwningEntryRef.h>
40 #include <package/DaemonDefs.h>
41 #include <RosterPrivate.h>
43 #include "CommitTransactionHandler.h"
44 #include "Constants.h"
45 #include "DebugSupport.h"
46 #include "Exception.h"
47 #include "PackageFileManager.h"
49 #include "VolumeState.h"
52 using namespace BPackageKit::BPrivate
;
55 // #pragma mark - Listener
58 Volume::Listener::~Listener()
63 // #pragma mark - NodeMonitorEvent
66 struct Volume::NodeMonitorEvent
67 : public DoublyLinkedListLinkImpl
<NodeMonitorEvent
> {
69 NodeMonitorEvent(const BString
& entryName
, bool created
)
71 fEntryName(entryName
),
76 const BString
& EntryName() const
81 bool WasCreated() const
92 // #pragma mark - PackagesDirectory
95 struct Volume::PackagesDirectory
{
104 void Init(const node_ref
& nodeRef
, bool isPackagesDir
)
111 BDirectory directory
;
113 if (directory
.SetTo(&fNodeRef
) == B_OK
114 && directory
.GetEntry(&entry
) == B_OK
) {
115 fName
= entry
.Name();
119 fName
= "unknown state";
122 const node_ref
& NodeRef() const
127 const BString
& Name() const
138 // #pragma mark - Volume
141 Volume::Volume(BLooper
* looper
)
145 fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM
),
147 fPackagesDirectories(NULL
),
148 fPackagesDirectoryCount(0),
151 fPackageFileManager(NULL
),
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)
164 looper
->AddHandler(this);
171 // needed for error case in InitPackages()
173 _SetLatestState(NULL
, true);
175 delete[] fPackagesDirectories
;
176 delete fPackageFileManager
;
181 Volume::Init(const node_ref
& rootDirectoryRef
, node_ref
& _packageRootRef
)
183 status_t error
= fLock
.InitCheck();
187 error
= fPendingNodeMonitorEventsLock
.InitCheck();
191 fLatestState
= new(std::nothrow
) VolumeState
;
192 if (fLatestState
== NULL
|| !fLatestState
->Init())
193 RETURN_ERROR(B_NO_MEMORY
);
195 fPackageFileManager
= new(std::nothrow
) PackageFileManager(fLock
);
196 if (fPackageFileManager
== NULL
)
197 RETURN_ERROR(B_NO_MEMORY
);
199 error
= fPackageFileManager
->Init();
203 fRootDirectoryRef
= rootDirectoryRef
;
205 // open the root directory
206 BDirectory directory
;
207 error
= directory
.SetTo(&fRootDirectoryRef
);
209 ERROR("Volume::Init(): failed to open root directory: %s\n",
214 // get the directory path
216 error
= directory
.GetEntry(&entry
);
220 error
= entry
.GetPath(&path
);
223 ERROR("Volume::Init(): failed to get root directory path: %s\n",
230 RETURN_ERROR(B_NO_MEMORY
);
232 // get a volume info from the FS
233 int fd
= directory
.Dup();
235 ERROR("Volume::Init(): failed to get root directory FD: %s\n",
239 FileDescriptorCloser
fdCloser(fd
);
241 // get the volume info from packagefs
242 uint32 maxPackagesDirCount
= 16;
243 PackageFSVolumeInfo
* info
= NULL
;
244 MemoryDeleter infoDeleter
;
247 bufferSize
= sizeof(PackageFSVolumeInfo
)
248 + (maxPackagesDirCount
- 1) * sizeof(PackageFSDirectoryInfo
);
249 info
= (PackageFSVolumeInfo
*)malloc(bufferSize
);
251 RETURN_ERROR(B_NO_MEMORY
);
252 infoDeleter
.SetTo(info
);
254 if (ioctl(fd
, PACKAGE_FS_OPERATION_GET_VOLUME_INFO
, info
,
256 ERROR("Volume::Init(): failed to get volume info: %s\n",
261 if (info
->packagesDirectoryCount
<= maxPackagesDirCount
)
264 maxPackagesDirCount
= info
->packagesDirectoryCount
;
268 if (info
->packagesDirectoryCount
< 1) {
269 ERROR("Volume::Init(): got invalid volume info from packagefs\n");
270 RETURN_ERROR(B_BAD_VALUE
);
273 fMountType
= info
->mountType
;
275 fPackagesDirectories
= new(std::nothrow
) PackagesDirectory
[
276 info
->packagesDirectoryCount
];
277 if (fPackagesDirectories
== NULL
)
278 RETURN_ERROR(B_NO_MEMORY
);
280 fPackagesDirectoryCount
= info
->packagesDirectoryCount
;
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
),
289 _packageRootRef
.device
= info
->rootDeviceID
;
290 _packageRootRef
.node
= info
->rootDirectoryID
;
297 Volume::InitPackages(Listener
* listener
)
299 // node-monitor the volume's packages directory
300 status_t error
= watch_node(&PackagesDirectoryRef(), B_WATCH_DIRECTORY
,
303 fListener
= listener
;
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.
312 // read the packages directory and get the active packages
313 int fd
= OpenRootDirectory();
315 ERROR("Volume::InitPackages(): failed to open root directory: %s\n",
319 FileDescriptorCloser
fdCloser(fd
);
321 error
= _ReadPackagesDirectory();
325 error
= _InitLatestState();
329 error
= _GetActivePackages(fd
);
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
);
345 Volume::AddPackagesToRepository(BSolverRepository
& repository
, bool activeOnly
)
347 for (PackageFileNameHashTable::Iterator it
348 = fLatestState
->ByFileNameIterator(); it
.HasNext();) {
349 Package
* package
= it
.Next();
350 if (activeOnly
&& !package
->IsActive())
353 status_t error
= repository
.AddPackage(package
->Info());
355 ERROR("Volume::AddPackagesToRepository(): failed to add package %s "
356 "to repository: %s\n", package
->FileName().String(),
367 Volume::InitialVerify(Volume
* nextVolume
, Volume
* nextNextVolume
)
369 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume
, nextNextVolume
);
372 status_t error
= BSolver::Create(solver
);
374 ERROR("Volume::InitialVerify(): failed to create solver: %s\n",
378 ObjectDeleter
<BSolver
> solverDeleter(solver
);
380 // add a repository with all active packages
381 BSolverRepository repository
;
382 error
= _AddRepository(solver
, repository
, true, true);
384 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
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);
395 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
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,
408 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
415 error
= solver
->VerifyInstallation();
417 ERROR("Volume::InitialVerify(): failed to verify: %s\n",
422 if (!solver
->HasProblems()) {
423 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n",
428 // print the problems
429 // TODO: Notify the user ...
430 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n",
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());
453 Volume::HandleGetLocationInfoRequest(BMessage
* message
)
455 AutoLocker
<BLocker
> locker(fLock
);
457 // If the cached reply message is up-to-date, just send it.
459 if (fLocationInfoReply
.FindInt64("change count", &changeCount
) == B_OK
460 && changeCount
== fChangeCount
) {
462 message
->SendReply(&fLocationInfoReply
, (BHandler
*)NULL
,
463 kCommunicationTimeout
);
467 // rebuild the reply message
468 fLocationInfoReply
.MakeEmpty();
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
) {
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
)
494 if (fActiveState
!= fLatestState
) {
495 if (fPackagesDirectoryCount
> 1) {
496 fLocationInfoReply
.AddString("old state",
497 fPackagesDirectories
[fPackagesDirectoryCount
- 1].Name());
500 for (PackageFileNameHashTable::Iterator it
501 = fActiveState
->ByFileNameIterator(); it
.HasNext();) {
502 Package
* package
= it
.Next();
503 if (!package
->IsActive())
506 BMessage packageArchive
;
507 if (package
->Info().Archive(&packageArchive
) != B_OK
508 || fLocationInfoReply
.AddMessage("currently active packages",
509 &packageArchive
) != B_OK
) {
515 if (fLocationInfoReply
.AddInt64("change count", fChangeCount
) != B_OK
)
520 message
->SendReply(&fLocationInfoReply
, (BHandler
*)NULL
,
521 kCommunicationTimeout
);
526 Volume::HandleCommitTransactionRequest(BMessage
* message
)
528 BCommitTransactionResult result
;
530 _CommitTransaction(message
, NULL
, dummy
, dummy
, result
);
532 BMessage
reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY
);
533 status_t error
= result
.AddToMessage(reply
);
535 ERROR("Volume::HandleCommitTransactionRequest(): Failed to add "
536 "transaction result to reply: %s\n", strerror(error
));
540 message
->SendReply(&reply
, (BHandler
*)NULL
, kCommunicationTimeout
);
545 Volume::PackageJobPending()
547 atomic_add(&fPendingPackageJobCount
, 1);
552 Volume::PackageJobFinished()
554 atomic_add(&fPendingPackageJobCount
, -1);
559 Volume::IsPackageJobPending() const
561 return fPendingPackageJobCount
!= 0;
568 if (fListener
!= NULL
) {
569 stop_watching(BMessenger(this));
573 if (BLooper
* looper
= Looper())
574 looper
->RemoveHandler(this);
579 Volume::MessageReceived(BMessage
* message
)
581 switch (message
->what
) {
585 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
589 case B_ENTRY_CREATED
:
590 _HandleEntryCreatedOrRemoved(message
, true);
592 case B_ENTRY_REMOVED
:
593 _HandleEntryCreatedOrRemoved(message
, false);
596 _HandleEntryMoved(message
);
604 case kHandleNodeMonitorEvents
:
605 if (fListener
!= NULL
) {
606 if (system_time() >= fNodeMonitorEventHandleTime
)
607 fListener
->VolumeNodeMonitorEventOccurred(this);
612 BHandler::MessageReceived(message
);
618 BPackageInstallationLocation
619 Volume::Location() const
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
:
628 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT
;
634 Volume::PackagesDirectoryRef() const
636 return fPackagesDirectories
[0].NodeRef();
640 PackageFileNameHashTable::Iterator
641 Volume::PackagesByFileNameIterator() const
643 return fLatestState
->ByFileNameIterator();
648 Volume::OpenRootDirectory() const
650 BDirectory directory
;
651 status_t error
= directory
.SetTo(&fRootDirectoryRef
);
653 ERROR("Volume::OpenRootDirectory(): failed to open root directory: "
654 "%s\n", strerror(error
));
658 return directory
.Dup();
663 Volume::ProcessPendingNodeMonitorEvents()
666 NodeMonitorEventList events
;
668 AutoLocker
<BLocker
> eventsLock(fPendingNodeMonitorEventsLock
);
669 events
.MoveFrom(&fPendingNodeMonitorEvents
);
673 while (NodeMonitorEvent
* event
= events
.RemoveHead()) {
674 ObjectDeleter
<NodeMonitorEvent
> eventDeleter(event
);
675 if (event
->WasCreated())
676 _PackagesEntryCreated(event
->EntryName());
678 _PackagesEntryRemoved(event
->EntryName());
684 Volume::HasPendingPackageActivationChanges() const
686 return !fPackagesToBeActivated
.empty() || !fPackagesToBeDeactivated
.empty();
691 Volume::ProcessPendingPackageActivationChanges()
693 if (!HasPendingPackageActivationChanges())
696 // perform the request
697 BCommitTransactionResult result
;
698 _CommitTransaction(NULL
, NULL
, fPackagesToBeActivated
,
699 fPackagesToBeDeactivated
, result
);
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!
707 // clear the activation/deactivation sets in any event
708 fPackagesToBeActivated
.clear();
709 fPackagesToBeDeactivated
.clear();
714 Volume::ClearPackageActivationChanges()
716 fPackagesToBeActivated
.clear();
717 fPackagesToBeDeactivated
.clear();
722 Volume::CreateTransaction(BPackageInstallationLocation location
,
723 BActivationTransaction
& _transaction
, BDirectory
& _transactionDirectory
)
725 // open admin directory
726 BDirectory adminDirectory
;
727 status_t error
= _OpenPackagesSubDirectory(
728 RelativePath(kAdminDirectoryName
), true, adminDirectory
);
732 // create a transaction directory
734 BString directoryName
;
735 for (;; uniqueId
++) {
736 directoryName
.SetToFormat("transaction-%d", uniqueId
);
737 if (directoryName
.IsEmpty())
740 error
= adminDirectory
.CreateDirectory(directoryName
,
741 &_transactionDirectory
);
744 if (error
!= B_FILE_EXISTS
)
748 // init the transaction
749 error
= _transaction
.SetTo(location
, fChangeCount
, directoryName
);
752 _transactionDirectory
.GetEntry(&entry
);
753 _transactionDirectory
.Unset();
754 if (entry
.InitCheck() == B_OK
)
764 Volume::CommitTransaction(const BActivationTransaction
& transaction
,
765 const PackageSet
& packagesAlreadyAdded
,
766 const PackageSet
& packagesAlreadyRemoved
, BCommitTransactionResult
& _result
)
768 _CommitTransaction(NULL
, &transaction
, packagesAlreadyAdded
,
769 packagesAlreadyRemoved
, _result
);
774 Volume::_HandleEntryCreatedOrRemoved(const BMessage
* message
, bool created
)
776 // only moves to or from our packages directory are interesting
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()) {
787 _QueueNodeMonitorEvent(name
, created
);
792 Volume::_HandleEntryMoved(const BMessage
* message
)
795 int64 fromDirectoryID
;
797 const char* fromName
;
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())) {
810 AutoLocker
<BLocker
> eventsLock(fPendingNodeMonitorEventsLock
);
811 // make sure for a move the two events cannot get split
813 if (fromDirectoryID
== PackagesDirectoryID())
814 _QueueNodeMonitorEvent(fromName
, false);
815 if (toDirectoryID
== PackagesDirectoryID())
816 _QueueNodeMonitorEvent(toName
, true);
821 Volume::_QueueNodeMonitorEvent(const BString
& name
, bool wasCreated
)
823 if (name
.IsEmpty()) {
824 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n");
828 // ignore entries that don't have the ".hpkg" extension
829 if (!name
.EndsWith(kPackageFileNameExtension
))
832 NodeMonitorEvent
* event
833 = new(std::nothrow
) NodeMonitorEvent(name
, wasCreated
);
835 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n");
839 AutoLocker
<BLocker
> eventsLock(fPendingNodeMonitorEventsLock
);
840 fPendingNodeMonitorEvents
.Add(event
);
843 fNodeMonitorEventHandleTime
844 = system_time() + kNodeMonitorEventHandlingDelay
;
845 BMessage
message(kHandleNodeMonitorEvents
);
846 BMessageRunner::StartSending(this, &message
, kNodeMonitorEventHandlingDelay
,
852 Volume::_PackagesEntryCreated(const char* name
)
854 INFORM("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();
861 WARN("node monitoring created event for already known entry "
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
);
874 status_t error
= fPackageFileManager
->CreatePackage(
875 NotOwningEntryRef(PackagesDirectoryRef(), name
),
878 ERROR("failed to init package for file \"%s\"\n", name
);
883 fLatestState
->AddPackage(package
);
888 fPackagesToBeActivated
.insert(package
);
889 } catch (std::bad_alloc
& exception
) {
890 ERROR("out of memory\n");
897 Volume::_PackagesEntryRemoved(const char* name
)
899 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name
);
900 Package
* package
= fLatestState
->FindPackage(name
);
904 // Ignore the event, if we generated it ourselves.
905 if (package
->File()->EntryRemovedIgnoreLevel() > 0) {
906 package
->File()->DecrementEntryRemovedIgnoreLevel();
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
);
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
);
925 // The package must be deactivated.
927 fPackagesToBeDeactivated
.insert(package
);
928 } catch (std::bad_alloc
& exception
) {
929 ERROR("out of memory\n");
936 Volume::_ReadPackagesDirectory()
938 BDirectory directory
;
939 status_t error
= directory
.SetTo(&PackagesDirectoryRef());
941 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages "
942 "directory: %s\n", strerror(error
));
947 while (directory
.GetNextRef(&entry
) == B_OK
) {
948 if (!BString(entry
.name
).EndsWith(kPackageFileNameExtension
))
952 status_t error
= fPackageFileManager
->CreatePackage(entry
, package
);
954 AutoLocker
<BLocker
> locker(fLock
);
955 fLatestState
->AddPackage(package
);
965 Volume::_InitLatestState()
967 if (_InitLatestStateFromActivatedPackages() == B_OK
)
970 INFORM("Failed to get activated packages info from activated packages file."
971 " Assuming all package files in package directory are activated.\n");
973 AutoLocker
<BLocker
> locker(fLock
);
975 for (PackageFileNameHashTable::Iterator it
976 = fLatestState
->ByFileNameIterator();
977 Package
* package
= it
.Next();) {
978 fLatestState
->SetPackageActive(package
, true);
987 Volume::_InitLatestStateFromActivatedPackages()
989 // try reading the activation file
990 NotOwningEntryRef
entryRef(PackagesDirectoryRef(), kActivationFileName
);
992 status_t error
= file
.SetTo(&entryRef
, B_READ_ONLY
);
994 INFORM("Failed to open packages activation file: %s\n",
999 // read the whole file into memory to simplify things
1001 error
= file
.GetSize(&size
);
1002 if (error
!= B_OK
) {
1003 ERROR("Failed to packages activation file size: %s\n",
1005 RETURN_ERROR(error
);
1008 if (size
> (off_t
)kMaxActivationFileSize
) {
1009 ERROR("The packages activation file is too big.\n");
1010 RETURN_ERROR(B_BAD_DATA
);
1013 char* fileContent
= (char*)malloc(size
+ 1);
1014 if (fileContent
== NULL
)
1015 RETURN_ERROR(B_NO_MEMORY
);
1016 MemoryDeleter
fileContentDeleter(fileContent
);
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
);
1025 if (bytesRead
!= size
) {
1026 ERROR("Failed to read whole packages activation file.\n");
1027 RETURN_ERROR(B_ERROR
);
1030 // null-terminate to simplify parsing
1031 fileContent
[size
] = '\0';
1033 AutoLocker
<BLocker
> locker(fLock
);
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
;
1044 if (packageName
== packageNameEnd
) {
1048 *packageNameEnd
= '\0';
1050 if (packageNameEnd
- packageName
>= B_FILE_NAME_LENGTH
) {
1051 ERROR("Invalid packages activation file content.\n");
1052 RETURN_ERROR(B_BAD_DATA
);
1055 Package
* package
= fLatestState
->FindPackage(packageName
);
1056 if (package
!= NULL
) {
1057 fLatestState
->SetPackageActive(package
, true);
1060 WARN("Package \"%s\" from activation file not in packages "
1061 "directory.\n", packageName
);
1064 packageName
= packageNameEnd
+ 1;
1072 Volume::_GetActivePackages(int fd
)
1074 // get the info from packagefs
1075 PackageFSGetPackageInfosRequest
* request
= NULL
;
1076 MemoryDeleter requestDeleter
;
1077 size_t bufferSize
= 64 * 1024;
1079 request
= (PackageFSGetPackageInfosRequest
*)malloc(bufferSize
);
1080 if (request
== NULL
)
1081 RETURN_ERROR(B_NO_MEMORY
);
1082 requestDeleter
.SetTo(request
);
1084 if (ioctl(fd
, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS
, request
,
1086 ERROR("Volume::_GetActivePackages(): failed to get active package "
1087 "info from package FS: %s\n", strerror(errno
));
1088 RETURN_ERROR(errno
);
1091 if (request
->bufferSize
<= bufferSize
)
1094 bufferSize
= request
->bufferSize
;
1095 requestDeleter
.Unset();
1098 INFORM("latest volume state:\n");
1099 _DumpState(fLatestState
);
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
;
1108 // There's a mismatch. We need a new state that reflects the actual
1109 // activation situation.
1110 VolumeState
* state
= new(std::nothrow
) VolumeState
;
1112 RETURN_ERROR(B_NO_MEMORY
);
1113 ObjectDeleter
<VolumeState
> stateDeleter(state
);
1115 for (uint32 i
= 0; i
< request
->packageCount
; i
++) {
1116 const PackageFSPackageInfo
& info
= request
->infos
[i
];
1117 NotOwningEntryRef
entryRef(info
.directoryDeviceID
, info
.directoryNodeID
,
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
));
1128 state
->AddPackage(package
);
1129 state
->SetPackageActive(package
, true);
1132 INFORM("currently active volume state:\n");
1135 fActiveState
= stateDeleter
.Detach();
1141 Volume::_RunQueuedScripts()
1143 BDirectory adminDirectory
;
1144 status_t error
= _OpenPackagesSubDirectory(
1145 RelativePath(kAdminDirectoryName
), false, adminDirectory
);
1149 BDirectory scriptsDirectory
;
1150 error
= scriptsDirectory
.SetTo(&adminDirectory
, kQueuedScriptsDirectoryName
);
1154 // enumerate all the symlinks in the queued scripts directory
1156 while (scriptsDirectory
.GetNextEntry(&scriptEntry
, false) == B_OK
) {
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",
1167 int result
= system(scriptPath
.Path());
1169 INFORM("running post-installation script \"%s\" "
1170 "failed: %d (errno: %s)\n", scriptPath
.Leaf(), errno
, strerror(errno
));
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",
1184 Volume::_CheckActivePackagesMatchLatestState(
1185 PackageFSGetPackageInfosRequest
* request
)
1187 if (fPackagesDirectoryCount
!= 1) {
1188 INFORM("An old packages state (\"%s\") seems to be active.\n",
1189 fPackagesDirectories
[fPackagesDirectoryCount
- 1].Name().String());
1193 const node_ref
packagesDirRef(PackagesDirectoryRef());
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
);
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
,
1213 ? "found in packages directory" : "supposed to be active");
1218 // Check whether there are packages that aren't active but should be.
1220 for (PackageNodeRefHashTable::Iterator it
1221 = fLatestState
->ByNodeRefIterator(); it
.HasNext();) {
1222 Package
* package
= it
.Next();
1223 if (package
->IsActive())
1227 if (count
!= request
->packageCount
) {
1228 INFORM("There seem to be packages in the packages directory that "
1229 "should be active.\n");
1238 Volume::_SetLatestState(VolumeState
* state
, bool isActive
)
1240 AutoLocker
<BLocker
> locker(fLock
);
1242 if (fLatestState
!= fActiveState
)
1243 delete fActiveState
;
1244 fActiveState
= state
;
1247 if (fLatestState
!= fActiveState
)
1248 delete fLatestState
;
1249 fLatestState
= state
;
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);
1268 Volume::_DumpState(VolumeState
* state
)
1270 uint32 inactiveCount
= 0;
1271 for (PackageNodeRefHashTable::Iterator it
= state
->ByNodeRefIterator();
1273 Package
* package
= it
.Next();
1274 if (package
->IsActive()) {
1275 INFORM("active package: \"%s\"\n", package
->FileName().String());
1280 if (inactiveCount
== 0)
1283 for (PackageNodeRefHashTable::Iterator it
= state
->ByNodeRefIterator();
1285 Package
* package
= it
.Next();
1286 if (!package
->IsActive())
1287 INFORM("inactive package: \"%s\"\n", package
->FileName().String());
1293 Volume::_AddRepository(BSolver
* solver
, BSolverRepository
& repository
,
1294 bool activeOnly
, bool installed
)
1296 status_t error
= repository
.SetTo(Path());
1297 if (error
!= B_OK
) {
1298 ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1303 repository
.SetInstalled(installed
);
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
));
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
));
1324 Volume::_OpenPackagesSubDirectory(const RelativePath
& path
, bool create
,
1325 BDirectory
& _directory
)
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
);
1336 return FSUtils::OpenSubDirectory(directory
, path
, create
, _directory
);
1341 Volume::_CommitTransaction(BMessage
* message
,
1342 const BActivationTransaction
* transaction
,
1343 const PackageSet
& packagesAlreadyAdded
,
1344 const PackageSet
& packagesAlreadyRemoved
, BCommitTransactionResult
& _result
)
1348 // perform the request
1349 CommitTransactionHandler
handler(this, fPackageFileManager
, _result
);
1350 BTransactionError error
= B_TRANSACTION_INTERNAL_ERROR
;
1352 handler
.Init(fLatestState
, fLatestState
== fActiveState
,
1353 packagesAlreadyAdded
, packagesAlreadyRemoved
);
1355 if (message
!= NULL
)
1356 handler
.HandleRequest(message
);
1357 else if (transaction
!= NULL
)
1358 handler
.HandleRequest(*transaction
);
1360 handler
.HandleRequest();
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());
1372 } catch (std::bad_alloc
& exception
) {
1373 error
= B_TRANSACTION_NO_MEMORY
;
1376 _result
.SetError(B_TRANSACTION_OK
);
1379 if (error
!= B_TRANSACTION_OK
)