Package Kit: Proper Installation for First Boot Packages
Do the final installation operations for all the packages in the /system/packages directory when the OS is booted for the first time. This will run their post-install scripts, create users, groups and generate settings files (marked with a package version attribute). Previously we just ran all the shell scripts found in the /system/boot/post-install directory (don't do that as much now). Fixes bug #14382 This patch has simpler code flow in CommitTransactionHandler::_ApplyChanges Tested on 32 and 64 bit systems. Once it's official, need to remove the open_ssh redundant post-install script that creates users etc. from HaikuPorts. Now we can notice bugs like package version attributes on settings files aren't fully working. :-) Didn't remove special case for add_catalog_entry_attributes.sh since it still does stuff that the build system doesn't do. Might be able to add that script as part of the Haiku.hpkg. See change 3751 for removing it, https://review.haiku-os.org/c/haiku/+/3751 Change-Id: I3807b78042fdb70e5a79eca2e2a45816ece0236f Reviewed-on: https://review.haiku-os.org/c/haiku/+/2342 Reviewed-by: Alexander G. M. Smith <[email protected]> Reviewed-by: Niels Sascha Reedijk <[email protected]> Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
e459857d1c
commit
3376ed1a72
Regular → Executable
+25
-7
@@ -1,6 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Check for fresh install and run post install scripts.
|
# Check for fresh install and run oddball system post install stuff that the
|
||||||
|
# package_daemon doesn't handle. All Home and other post install scripts
|
||||||
|
# get run the old way here (probably won't be any, unless you reinstalled?).
|
||||||
|
|
||||||
title=$1
|
title=$1
|
||||||
freshInstallIndicator=$2
|
freshInstallIndicator=$2
|
||||||
@@ -8,13 +10,29 @@ postInstallDir=$3
|
|||||||
|
|
||||||
if [ -e $freshInstallIndicator ]; then
|
if [ -e $freshInstallIndicator ]; then
|
||||||
# execute scripts
|
# execute scripts
|
||||||
for f in $postInstallDir/*.sh
|
|
||||||
do
|
if [ "$postInstallDir" == "/boot/system/boot/post-install" ]; then
|
||||||
if [ -f $f ]; then
|
# Special case for one script file that isn't in a package. Rest
|
||||||
echo "Running $title script $f ..." > /dev/dprintf
|
# of the files in there will be run by package_daemon when
|
||||||
$f
|
# doing first boot processing. Can be removed when Gerrit
|
||||||
|
# Change #3751 is done.
|
||||||
|
specialCaseFile="$postInstallDir/add_catalog_entry_attributes.sh"
|
||||||
|
if [ -e "$specialCaseFile" ]; then
|
||||||
|
echo "Running $title special case $specialCaseFile first boot processing." > /dev/dprintf
|
||||||
|
"$specialCaseFile"
|
||||||
|
else
|
||||||
|
echo "Skipping $title obsolete first boot processing, files:" > /dev/dprintf
|
||||||
|
ls "$postInstallDir" > /dev/dprintf
|
||||||
fi
|
fi
|
||||||
done
|
else
|
||||||
|
for f in $postInstallDir/*.sh
|
||||||
|
do
|
||||||
|
if [ -f $f ]; then
|
||||||
|
echo "Running $title script $f ..." > /dev/dprintf
|
||||||
|
$f
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
sync
|
sync
|
||||||
rm $freshInstallIndicator
|
rm $freshInstallIndicator
|
||||||
|
|||||||
@@ -179,7 +179,10 @@ The supported attributes are:
|
|||||||
- ``post-install-scripts``: A list of paths of files included in the package,
|
- ``post-install-scripts``: A list of paths of files included in the package,
|
||||||
which shall be executed on package activation. Each path must start with
|
which shall be executed on package activation. Each path must start with
|
||||||
"boot/post-install/". All the files in that directory are also run on first
|
"boot/post-install/". All the files in that directory are also run on first
|
||||||
boot after installing or copying the OS to a new disk.
|
boot after installing or copying the OS to a new disk. As an odd bonus,
|
||||||
|
they're also run when you boot the installer disc, and the installer copies
|
||||||
|
some of the resulting settings data to the new install too. So try to
|
||||||
|
handle being run twice.
|
||||||
- ``pre-uninstall-scripts``: A list of paths of files included in the package,
|
- ``pre-uninstall-scripts``: A list of paths of files included in the package,
|
||||||
which shall be executed on package deactivation. For consistency, each path
|
which shall be executed on package deactivation. For consistency, each path
|
||||||
should start with "boot/pre-uninstall/".
|
should start with "boot/pre-uninstall/".
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace BPrivate {
|
|||||||
class BActivationTransaction : public BArchivable {
|
class BActivationTransaction : public BArchivable {
|
||||||
public:
|
public:
|
||||||
BActivationTransaction();
|
BActivationTransaction();
|
||||||
BActivationTransaction(BMessage* archive,
|
BActivationTransaction(BMessage* archive,
|
||||||
status_t* _error = NULL);
|
status_t* _error = NULL);
|
||||||
virtual ~BActivationTransaction();
|
virtual ~BActivationTransaction();
|
||||||
|
|
||||||
status_t SetTo(BPackageInstallationLocation location,
|
status_t SetTo(BPackageInstallationLocation location,
|
||||||
@@ -52,6 +52,9 @@ public:
|
|||||||
const BStringList& packages);
|
const BStringList& packages);
|
||||||
bool AddPackageToDeactivate(const BString& package);
|
bool AddPackageToDeactivate(const BString& package);
|
||||||
|
|
||||||
|
bool FirstBootProcessing() const;
|
||||||
|
void SetFirstBootProcessing(bool processingIsOn);
|
||||||
|
|
||||||
virtual status_t Archive(BMessage* archive,
|
virtual status_t Archive(BMessage* archive,
|
||||||
bool deep = true) const;
|
bool deep = true) const;
|
||||||
static BArchivable* Instantiate(BMessage* archive);
|
static BArchivable* Instantiate(BMessage* archive);
|
||||||
@@ -66,6 +69,7 @@ private:
|
|||||||
BString fTransactionDirectoryName;
|
BString fTransactionDirectoryName;
|
||||||
BStringList fPackagesToActivate;
|
BStringList fPackagesToActivate;
|
||||||
BStringList fPackagesToDeactivate;
|
BStringList fPackagesToDeactivate;
|
||||||
|
bool fFirstBootProcessing;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ enum {
|
|||||||
// "deactivate": string[]
|
// "deactivate": string[]
|
||||||
// file names of the packages to activate; must be in the
|
// file names of the packages to activate; must be in the
|
||||||
// transaction directory
|
// transaction directory
|
||||||
|
// "first boot processing": bool
|
||||||
|
// if true then runs the package installation processing for
|
||||||
|
// specified (in the activate list) packages on the volume.
|
||||||
|
// Doesn't actually add the packages. Package_daemon preemptively
|
||||||
|
// deletes an empty transaction directory when done.
|
||||||
B_MESSAGE_COMMIT_TRANSACTION_REPLY = 'PKTR'
|
B_MESSAGE_COMMIT_TRANSACTION_REPLY = 'PKTR'
|
||||||
// "error": int32
|
// "error": int32
|
||||||
// a BTransactionError describing how committing the transaction
|
// a BTransactionError describing how committing the transaction
|
||||||
|
|||||||
@@ -312,6 +312,15 @@ WorkerThread::_LaunchFinishScript(BPath &path)
|
|||||||
if (system(command.String()) != 0)
|
if (system(command.String()) != 0)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
|
// Ask for first boot processing of all the packages copied into the new
|
||||||
|
// installation, since by just copying them the normal package processing
|
||||||
|
// isn't done. package_daemon will detect the magic file and do it.
|
||||||
|
command.SetToFormat("echo 'First Boot written by Installer.' > "
|
||||||
|
"\"%s/system/packages/administrative/FirstBootProcessingNeeded\"",
|
||||||
|
path.Path());
|
||||||
|
if (system(command.String()) != 0)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
command.SetToFormat("rm -f \"%s/home/Desktop/Installer\"", path.Path());
|
command.SetToFormat("rm -f \"%s/home/Desktop/Installer\"", path.Path());
|
||||||
return system(command.String());
|
return system(command.String());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ BActivationTransaction::BActivationTransaction()
|
|||||||
fChangeCount(0),
|
fChangeCount(0),
|
||||||
fTransactionDirectoryName(),
|
fTransactionDirectoryName(),
|
||||||
fPackagesToActivate(),
|
fPackagesToActivate(),
|
||||||
fPackagesToDeactivate()
|
fPackagesToDeactivate(),
|
||||||
|
fFirstBootProcessing(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,10 +37,16 @@ BActivationTransaction::BActivationTransaction(BMessage* archive,
|
|||||||
fChangeCount(0),
|
fChangeCount(0),
|
||||||
fTransactionDirectoryName(),
|
fTransactionDirectoryName(),
|
||||||
fPackagesToActivate(),
|
fPackagesToActivate(),
|
||||||
fPackagesToDeactivate()
|
fPackagesToDeactivate(),
|
||||||
|
fFirstBootProcessing(false)
|
||||||
{
|
{
|
||||||
status_t error;
|
status_t error;
|
||||||
int32 location;
|
int32 location;
|
||||||
|
|
||||||
|
if (archive->FindBool("first boot processing", &fFirstBootProcessing)
|
||||||
|
!= B_OK)
|
||||||
|
fFirstBootProcessing = false; // Field is optional for compatibility.
|
||||||
|
|
||||||
if ((error = archive->FindInt32("location", &location)) == B_OK
|
if ((error = archive->FindInt32("location", &location)) == B_OK
|
||||||
&& (error = archive->FindInt64("change count", &fChangeCount)) == B_OK
|
&& (error = archive->FindInt64("change count", &fChangeCount)) == B_OK
|
||||||
&& (error = archive->FindString("transaction",
|
&& (error = archive->FindString("transaction",
|
||||||
@@ -91,6 +98,7 @@ BActivationTransaction::SetTo(BPackageInstallationLocation location,
|
|||||||
fTransactionDirectoryName = directoryName;
|
fTransactionDirectoryName = directoryName;
|
||||||
fPackagesToActivate.MakeEmpty();
|
fPackagesToActivate.MakeEmpty();
|
||||||
fPackagesToDeactivate.MakeEmpty();
|
fPackagesToDeactivate.MakeEmpty();
|
||||||
|
fFirstBootProcessing = false;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -183,6 +191,20 @@ BActivationTransaction::AddPackageToDeactivate(const BString& package)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BActivationTransaction::FirstBootProcessing() const
|
||||||
|
{
|
||||||
|
return fFirstBootProcessing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BActivationTransaction::SetFirstBootProcessing(bool processingIsOn)
|
||||||
|
{
|
||||||
|
fFirstBootProcessing = processingIsOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BActivationTransaction::Archive(BMessage* archive, bool deep) const
|
BActivationTransaction::Archive(BMessage* archive, bool deep) const
|
||||||
{
|
{
|
||||||
@@ -197,7 +219,9 @@ BActivationTransaction::Archive(BMessage* archive, bool deep) const
|
|||||||
|| (error = archive->AddStrings("activate", fPackagesToActivate))
|
|| (error = archive->AddStrings("activate", fPackagesToActivate))
|
||||||
!= B_OK
|
!= B_OK
|
||||||
|| (error = archive->AddStrings("deactivate", fPackagesToDeactivate))
|
|| (error = archive->AddStrings("deactivate", fPackagesToDeactivate))
|
||||||
!= B_OK) {
|
!= B_OK
|
||||||
|
|| (error = archive->AddBool("first boot processing",
|
||||||
|
fFirstBootProcessing)) != B_OK) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ CommitTransactionHandler::CommitTransactionHandler(Volume* volume,
|
|||||||
fOldStateDirectoryRef(),
|
fOldStateDirectoryRef(),
|
||||||
fOldStateDirectoryName(),
|
fOldStateDirectoryName(),
|
||||||
fTransactionDirectoryRef(),
|
fTransactionDirectoryRef(),
|
||||||
|
fFirstBootProcessing(false),
|
||||||
fWritableFilesDirectory(),
|
fWritableFilesDirectory(),
|
||||||
fAddedGroups(),
|
fAddedGroups(),
|
||||||
fAddedUsers(),
|
fAddedUsers(),
|
||||||
@@ -182,6 +183,7 @@ void
|
|||||||
CommitTransactionHandler::HandleRequest(BMessage* request)
|
CommitTransactionHandler::HandleRequest(BMessage* request)
|
||||||
{
|
{
|
||||||
status_t error;
|
status_t error;
|
||||||
|
|
||||||
BActivationTransaction transaction(request, &error);
|
BActivationTransaction transaction(request, &error);
|
||||||
if (error == B_OK)
|
if (error == B_OK)
|
||||||
error = transaction.InitCheck();
|
error = transaction.InitCheck();
|
||||||
@@ -203,6 +205,8 @@ CommitTransactionHandler::HandleRequest(
|
|||||||
if (transaction.ChangeCount() != fVolume->ChangeCount())
|
if (transaction.ChangeCount() != fVolume->ChangeCount())
|
||||||
throw Exception(B_TRANSACTION_CHANGE_COUNT_MISMATCH);
|
throw Exception(B_TRANSACTION_CHANGE_COUNT_MISMATCH);
|
||||||
|
|
||||||
|
fFirstBootProcessing = transaction.FirstBootProcessing();
|
||||||
|
|
||||||
// collect the packages to deactivate
|
// collect the packages to deactivate
|
||||||
_GetPackagesToDeactivate(transaction);
|
_GetPackagesToDeactivate(transaction);
|
||||||
|
|
||||||
@@ -210,13 +214,30 @@ CommitTransactionHandler::HandleRequest(
|
|||||||
_ReadPackagesToActivate(transaction);
|
_ReadPackagesToActivate(transaction);
|
||||||
|
|
||||||
// anything to do at all?
|
// anything to do at all?
|
||||||
if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) {
|
if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) {
|
||||||
WARN("Bad package activation request: no packages to activate or"
|
WARN("Bad package activation request: no packages to activate or"
|
||||||
" deactivate\n");
|
" deactivate\n");
|
||||||
throw Exception(B_TRANSACTION_BAD_REQUEST);
|
throw Exception(B_TRANSACTION_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ApplyChanges();
|
_ApplyChanges();
|
||||||
|
|
||||||
|
// Clean up the unused empty transaction directory for first boot
|
||||||
|
// processing, since it's usually an internal to package_daemon
|
||||||
|
// operation and there is no external client to clean it up.
|
||||||
|
if (fFirstBootProcessing) {
|
||||||
|
RelativePath directoryPath(kAdminDirectoryName,
|
||||||
|
transaction.TransactionDirectoryName().String());
|
||||||
|
BDirectory transactionDir;
|
||||||
|
status_t error = _OpenPackagesSubDirectory(directoryPath, false,
|
||||||
|
transactionDir);
|
||||||
|
if (error == B_OK) {
|
||||||
|
BEntry transactionDirEntry;
|
||||||
|
error = transactionDir.GetEntry(&transactionDirEntry);
|
||||||
|
if (error == B_OK)
|
||||||
|
transactionDirEntry.Remove(); // Okay to fail when non-empty.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -336,22 +357,32 @@ CommitTransactionHandler::_ReadPackagesToActivate(
|
|||||||
// read the packages
|
// read the packages
|
||||||
for (int32 i = 0; i < packagesToActivateCount; i++) {
|
for (int32 i = 0; i < packagesToActivateCount; i++) {
|
||||||
BString packageName = packagesToActivate.StringAt(i);
|
BString packageName = packagesToActivate.StringAt(i);
|
||||||
|
// make sure it doesn't clash with an already existing package,
|
||||||
// make sure it doesn't clash with an already existing package
|
// except in first boot mode where it should always clash.
|
||||||
Package* package = fVolumeState->FindPackage(packageName);
|
Package* package = fVolumeState->FindPackage(packageName);
|
||||||
if (package != NULL) {
|
if (fFirstBootProcessing) {
|
||||||
if (fPackagesAlreadyAdded.find(package)
|
if (package == NULL) {
|
||||||
!= fPackagesAlreadyAdded.end()) {
|
throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE)
|
||||||
if (!fPackagesToActivate.AddItem(package))
|
|
||||||
throw Exception(B_TRANSACTION_NO_MEMORY);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fPackagesToDeactivate.find(package)
|
|
||||||
== fPackagesToDeactivate.end()) {
|
|
||||||
throw Exception(B_TRANSACTION_PACKAGE_ALREADY_EXISTS)
|
|
||||||
.SetPackageName(packageName);
|
.SetPackageName(packageName);
|
||||||
}
|
}
|
||||||
|
if (!fPackagesToActivate.AddItem(package))
|
||||||
|
throw Exception(B_TRANSACTION_NO_MEMORY);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (package != NULL) {
|
||||||
|
if (fPackagesAlreadyAdded.find(package)
|
||||||
|
!= fPackagesAlreadyAdded.end()) {
|
||||||
|
if (!fPackagesToActivate.AddItem(package))
|
||||||
|
throw Exception(B_TRANSACTION_NO_MEMORY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fPackagesToDeactivate.find(package)
|
||||||
|
== fPackagesToDeactivate.end()) {
|
||||||
|
throw Exception(B_TRANSACTION_PACKAGE_ALREADY_EXISTS)
|
||||||
|
.SetPackageName(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the package
|
// read the package
|
||||||
@@ -382,27 +413,31 @@ CommitTransactionHandler::_ReadPackagesToActivate(
|
|||||||
void
|
void
|
||||||
CommitTransactionHandler::_ApplyChanges()
|
CommitTransactionHandler::_ApplyChanges()
|
||||||
{
|
{
|
||||||
// create an old state directory
|
if (!fFirstBootProcessing)
|
||||||
_CreateOldStateDirectory();
|
{
|
||||||
|
// create an old state directory
|
||||||
|
_CreateOldStateDirectory();
|
||||||
|
|
||||||
// move packages to deactivate to old state directory
|
// move packages to deactivate to old state directory
|
||||||
_RemovePackagesToDeactivate();
|
_RemovePackagesToDeactivate();
|
||||||
|
|
||||||
// move packages to activate to packages directory
|
// move packages to activate to packages directory
|
||||||
_AddPackagesToActivate();
|
_AddPackagesToActivate();
|
||||||
|
|
||||||
// run pre-uninstall scripts, before their packages vanish.
|
// run pre-uninstall scripts, before their packages vanish.
|
||||||
_RunPreUninstallScripts();
|
_RunPreUninstallScripts();
|
||||||
|
|
||||||
// activate/deactivate packages
|
// activate/deactivate packages and create users, groups, settings files.
|
||||||
_ChangePackageActivation(fAddedPackages, fRemovedPackages);
|
_ChangePackageActivation(fAddedPackages, fRemovedPackages);
|
||||||
|
} else // FirstBootProcessing, skip several steps and just do package setup.
|
||||||
|
_PrepareFirstBootPackages();
|
||||||
|
|
||||||
// run post-installation scripts
|
// run post-install scripts now that the new packages are visible in the
|
||||||
if (fVolumeStateIsActive) {
|
// package file system.
|
||||||
|
if (fVolumeStateIsActive || fFirstBootProcessing) {
|
||||||
_RunPostInstallScripts();
|
_RunPostInstallScripts();
|
||||||
} else {
|
} else {
|
||||||
// need to reboot to finish installation so queue up scripts as
|
// Do post-install scripts later after a reboot, for Haiku OS packages.
|
||||||
// symbolic links in a work directory, which will run later.
|
|
||||||
_QueuePostInstallScripts();
|
_QueuePostInstallScripts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,6 +666,31 @@ CommitTransactionHandler::_AddPackagesToActivate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
CommitTransactionHandler::_PrepareFirstBootPackages()
|
||||||
|
{
|
||||||
|
int32 count = fPackagesToActivate.CountItems();
|
||||||
|
|
||||||
|
BDirectory transactionDir(&fTransactionDirectoryRef);
|
||||||
|
BEntry transactionEntry;
|
||||||
|
BPath transactionPath;
|
||||||
|
if (transactionDir.InitCheck() == B_OK &&
|
||||||
|
transactionDir.GetEntry(&transactionEntry) == B_OK &&
|
||||||
|
transactionEntry.GetPath(&transactionPath) == B_OK) {
|
||||||
|
INFORM("Starting First Boot Processing for %d packages in %s.\n",
|
||||||
|
(int) count, transactionPath.Path());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 i = 0; i < count; i++) {
|
||||||
|
Package* package = fPackagesToActivate.ItemAt(i);
|
||||||
|
fAddedPackages.insert(package);
|
||||||
|
INFORM("Doing first boot processing #%d for package %s.\n",
|
||||||
|
(int) i, package->FileName().String());
|
||||||
|
_PreparePackageToActivate(package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CommitTransactionHandler::_PreparePackageToActivate(Package* package)
|
CommitTransactionHandler::_PreparePackageToActivate(Package* package)
|
||||||
{
|
{
|
||||||
@@ -904,7 +964,8 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) {
|
if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) {
|
||||||
// target doesn't exist -- just copy
|
// target doesn't exist -- just copy
|
||||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||||
"couldn't get stat for writable file, copying...\n");
|
"couldn't get stat for writable file \"%s\", copying...\n",
|
||||||
|
targetName);
|
||||||
FSTransaction::CreateOperation copyOperation(&fFSTransaction,
|
FSTransaction::CreateOperation copyOperation(&fFSTransaction,
|
||||||
FSUtils::Entry(targetDirectory, targetName));
|
FSUtils::Entry(targetDirectory, targetName));
|
||||||
status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY)
|
status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY)
|
||||||
@@ -947,7 +1008,8 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
// Source and target entry types don't match or this is an entry
|
// Source and target entry types don't match or this is an entry
|
||||||
// we cannot handle. The user must handle this manually.
|
// we cannot handle. The user must handle this manually.
|
||||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||||
"writable file exists, but type doesn't match previous type\n");
|
"writable file \"%s\" exists, but type doesn't match previous "
|
||||||
|
"type\n", targetName);
|
||||||
_AddIssue(TransactionIssueBuilder(
|
_AddIssue(TransactionIssueBuilder(
|
||||||
BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH)
|
BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH)
|
||||||
.SetPath1(FSUtils::Entry(targetDirectory, targetName))
|
.SetPath1(FSUtils::Entry(targetDirectory, targetName))
|
||||||
@@ -1003,7 +1065,9 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
// Can't determine the original package. The user must handle this
|
// Can't determine the original package. The user must handle this
|
||||||
// manually.
|
// manually.
|
||||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||||
"failed to get SYS:PACKAGE attribute\n");
|
"failed to get SYS:PACKAGE attribute for \"%s\", can't tell if "
|
||||||
|
"file needs to be updated\n",
|
||||||
|
targetName);
|
||||||
if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
|
if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
|
||||||
_AddIssue(TransactionIssueBuilder(
|
_AddIssue(TransactionIssueBuilder(
|
||||||
BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE)
|
BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE)
|
||||||
@@ -1015,7 +1079,8 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
// If that's our package, we're happy.
|
// If that's our package, we're happy.
|
||||||
if (originalPackage == package->RevisionedNameThrows()) {
|
if (originalPackage == package->RevisionedNameThrows()) {
|
||||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||||
"file tagged with same package version we're activating\n");
|
"file \"%s\" tagged with same package version we're activating\n",
|
||||||
|
targetName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1073,8 +1138,8 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
// handle this manually.
|
// handle this manually.
|
||||||
PRINT("Volume::CommitTransactionHandler::"
|
PRINT("Volume::CommitTransactionHandler::"
|
||||||
"_AddGlobalWritableFile(): "
|
"_AddGlobalWritableFile(): "
|
||||||
"file comparison failed (%s) or files aren't equal\n",
|
"file comparison \"%s\" failed (%s) or files aren't equal\n",
|
||||||
strerror(error));
|
targetName, strerror(error));
|
||||||
if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
|
if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
_AddIssue(TransactionIssueBuilder(
|
_AddIssue(TransactionIssueBuilder(
|
||||||
@@ -1108,8 +1173,8 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
// handle this manually.
|
// handle this manually.
|
||||||
PRINT("Volume::CommitTransactionHandler::"
|
PRINT("Volume::CommitTransactionHandler::"
|
||||||
"_AddGlobalWritableFile(): "
|
"_AddGlobalWritableFile(): "
|
||||||
"symlink comparison failed (%s) or symlinks aren't equal\n",
|
"symlink comparison \"%s\" failed (%s) or symlinks aren't "
|
||||||
strerror(error));
|
"equal\n", targetName, strerror(error));
|
||||||
if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
|
if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
_AddIssue(TransactionIssueBuilder(
|
_AddIssue(TransactionIssueBuilder(
|
||||||
@@ -1189,7 +1254,7 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
|
|||||||
void
|
void
|
||||||
CommitTransactionHandler::_RevertAddPackagesToActivate()
|
CommitTransactionHandler::_RevertAddPackagesToActivate()
|
||||||
{
|
{
|
||||||
if (fAddedPackages.empty())
|
if (fAddedPackages.empty() || fFirstBootProcessing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// open transaction directory
|
// open transaction directory
|
||||||
@@ -1244,7 +1309,7 @@ CommitTransactionHandler::_RevertAddPackagesToActivate()
|
|||||||
void
|
void
|
||||||
CommitTransactionHandler::_RevertRemovePackagesToDeactivate()
|
CommitTransactionHandler::_RevertRemovePackagesToDeactivate()
|
||||||
{
|
{
|
||||||
if (fRemovedPackages.empty())
|
if (fRemovedPackages.empty() || fFirstBootProcessing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// open packages directory
|
// open packages directory
|
||||||
@@ -1979,4 +2044,4 @@ CommitTransactionHandler::_AssertEntriesAreEqual(const BEntry& entry,
|
|||||||
"Package file '%s' already exists in target folder "
|
"Package file '%s' already exists in target folder "
|
||||||
"with equal contents\n", entry.Name());
|
"with equal contents\n", entry.Name());
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ private:
|
|||||||
const PackageSet& packagesToActivate,
|
const PackageSet& packagesToActivate,
|
||||||
const PackageSet& packagesToDeactivate);
|
const PackageSet& packagesToDeactivate);
|
||||||
// throws Exception
|
// throws Exception
|
||||||
|
void _PrepareFirstBootPackages();
|
||||||
void _FillInActivationChangeItem(
|
void _FillInActivationChangeItem(
|
||||||
PackageFSActivationChangeItem* item,
|
PackageFSActivationChangeItem* item,
|
||||||
PackageFSActivationChangeType type,
|
PackageFSActivationChangeType type,
|
||||||
@@ -174,6 +175,7 @@ private:
|
|||||||
node_ref fOldStateDirectoryRef;
|
node_ref fOldStateDirectoryRef;
|
||||||
BString fOldStateDirectoryName;
|
BString fOldStateDirectoryName;
|
||||||
node_ref fTransactionDirectoryRef;
|
node_ref fTransactionDirectoryRef;
|
||||||
|
bool fFirstBootProcessing;
|
||||||
BDirectory fWritableFilesDirectory;
|
BDirectory fWritableFilesDirectory;
|
||||||
StringSet fAddedGroups;
|
StringSet fAddedGroups;
|
||||||
StringSet fAddedUsers;
|
StringSet fAddedUsers;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ const char* const kAdminDirectoryName = PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
|
|||||||
const char* const kActivationFileName = PACKAGES_DIRECTORY_ACTIVATION_FILE;
|
const char* const kActivationFileName = PACKAGES_DIRECTORY_ACTIVATION_FILE;
|
||||||
const char* const kTemporaryActivationFileName
|
const char* const kTemporaryActivationFileName
|
||||||
= PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp";
|
= PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp";
|
||||||
|
const char* const kFirstBootProcessingNeededFileName
|
||||||
|
= "FirstBootProcessingNeeded";
|
||||||
const char* const kWritableFilesDirectoryName = "writable-files";
|
const char* const kWritableFilesDirectoryName = "writable-files";
|
||||||
const char* const kPackageFileAttribute = "SYS:PACKAGE";
|
const char* const kPackageFileAttribute = "SYS:PACKAGE";
|
||||||
const char* const kQueuedScriptsDirectoryName = "queued-scripts";
|
const char* const kQueuedScriptsDirectoryName = "queued-scripts";
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ extern const char* const kPackageFileNameExtension;
|
|||||||
extern const char* const kAdminDirectoryName;
|
extern const char* const kAdminDirectoryName;
|
||||||
extern const char* const kActivationFileName;
|
extern const char* const kActivationFileName;
|
||||||
extern const char* const kTemporaryActivationFileName;
|
extern const char* const kTemporaryActivationFileName;
|
||||||
|
extern const char* const kFirstBootProcessingNeededFileName;
|
||||||
extern const char* const kWritableFilesDirectoryName;
|
extern const char* const kWritableFilesDirectoryName;
|
||||||
extern const char* const kPackageFileAttribute;
|
extern const char* const kPackageFileAttribute;
|
||||||
extern const char* const kQueuedScriptsDirectoryName;
|
extern const char* const kQueuedScriptsDirectoryName;
|
||||||
|
|||||||
@@ -332,9 +332,75 @@ Volume::InitPackages(Listener* listener)
|
|||||||
|
|
||||||
// create the admin directory, if it doesn't exist yet
|
// create the admin directory, if it doesn't exist yet
|
||||||
BDirectory packagesDirectory;
|
BDirectory packagesDirectory;
|
||||||
|
bool createdAdminDirectory = false;
|
||||||
if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) {
|
if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) {
|
||||||
if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists())
|
if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists()) {
|
||||||
packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL);
|
packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL);
|
||||||
|
createdAdminDirectory = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BDirectory adminDirectory(&packagesDirectory, kAdminDirectoryName);
|
||||||
|
error = adminDirectory.InitCheck();
|
||||||
|
if (error != B_OK)
|
||||||
|
RETURN_ERROR(error);
|
||||||
|
|
||||||
|
// First boot processing requested by a magic file left by the OS installer?
|
||||||
|
BEntry flagFileEntry(&adminDirectory, kFirstBootProcessingNeededFileName);
|
||||||
|
if (createdAdminDirectory || flagFileEntry.Exists()) {
|
||||||
|
INFORM("Volume::InitPackages Requesting delayed first boot processing "
|
||||||
|
"for packages dir %s.\n", BPath(&packagesDirectory).Path());
|
||||||
|
if (flagFileEntry.Exists())
|
||||||
|
flagFileEntry.Remove(); // Remove early on to avoid an error loop.
|
||||||
|
|
||||||
|
// Are there any packages needing processing? Don't want to create an
|
||||||
|
// empty transaction directory and then never have it cleaned up when
|
||||||
|
// the empty transaction gets rejected.
|
||||||
|
bool anyPackages = false;
|
||||||
|
for (PackageNodeRefHashTable::Iterator it =
|
||||||
|
fActiveState->ByNodeRefIterator(); it.HasNext();) {
|
||||||
|
Package* package = it.Next();
|
||||||
|
if (package->IsActive()) {
|
||||||
|
anyPackages = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anyPackages) {
|
||||||
|
// Create first boot processing special transaction for current
|
||||||
|
// volume, which also creates an empty transaction directory.
|
||||||
|
BPackageInstallationLocation location = Location();
|
||||||
|
BDirectory transactionDirectory;
|
||||||
|
BActivationTransaction transaction;
|
||||||
|
error = CreateTransaction(location, transaction,
|
||||||
|
transactionDirectory);
|
||||||
|
if (error != B_OK)
|
||||||
|
RETURN_ERROR(error);
|
||||||
|
|
||||||
|
// Add all package files in currently active state to transaction.
|
||||||
|
for (PackageNodeRefHashTable::Iterator it =
|
||||||
|
fActiveState->ByNodeRefIterator(); it.HasNext();) {
|
||||||
|
Package* package = it.Next();
|
||||||
|
if (package->IsActive()) {
|
||||||
|
if (!transaction.AddPackageToActivate(
|
||||||
|
package->FileName().String()))
|
||||||
|
RETURN_ERROR(B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transaction.SetFirstBootProcessing(true);
|
||||||
|
|
||||||
|
// Queue up the transaction as a BMessage for processing a bit
|
||||||
|
// later, once the package daemon has finished initialising.
|
||||||
|
BMessage commitMessage(B_MESSAGE_COMMIT_TRANSACTION);
|
||||||
|
error = transaction.Archive(&commitMessage);
|
||||||
|
if (error != B_OK)
|
||||||
|
RETURN_ERROR(error);
|
||||||
|
BLooper *myLooper = Looper() ;
|
||||||
|
if (myLooper == NULL)
|
||||||
|
RETURN_ERROR(B_NOT_INITIALIZED);
|
||||||
|
error = myLooper->PostMessage(&commitMessage);
|
||||||
|
if (error != B_OK)
|
||||||
|
RETURN_ERROR(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -1003,8 +1069,14 @@ Volume::_InitLatestStateFromActivatedPackages()
|
|||||||
BFile file;
|
BFile file;
|
||||||
error = file.SetTo(&entryRef, B_READ_ONLY);
|
error = file.SetTo(&entryRef, B_READ_ONLY);
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
INFORM("Failed to open packages activation file: %s\n",
|
BEntry activationEntry(&entryRef);
|
||||||
strerror(error));
|
BPath activationPath;
|
||||||
|
const char *activationFilePathName = "Unknown due to errors";
|
||||||
|
if (activationEntry.InitCheck() == B_OK &&
|
||||||
|
activationEntry.GetPath(&activationPath) == B_OK)
|
||||||
|
activationFilePathName = activationPath.Path();
|
||||||
|
INFORM("Failed to open packages activation file %s: %s\n",
|
||||||
|
activationFilePathName, strerror(error));
|
||||||
RETURN_ERROR(error);
|
RETURN_ERROR(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
|
|||||||
resource app_version {
|
resource app_version {
|
||||||
major = 1,
|
major = 1,
|
||||||
middle = 0,
|
middle = 0,
|
||||||
minor = 0,
|
minor = 1,
|
||||||
|
|
||||||
variety = B_APPV_ALPHA,
|
variety = B_APPV_ALPHA,
|
||||||
internal = 0,
|
internal = 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user