From 0de3219e339506a0c94b584a0df0287414437033 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 14 Jun 2014 17:40:57 +0200 Subject: [PATCH] package daemon: Rework error and issue propagation to client * BDaemonClient: Move inner class BCommitTransactionResult to top level and make it public. * BCommitTransactionResult: - Add a whole bunch of specific error code enum values. Such an error code is now the primary error, as opposed to before where we would mix status_t and enum value errors. There's a systemError property of type status_t which may provide additional information, though (depending on the primary error type). - Remove the errorMessage property. Due to mapping all errors to the specific error codes this is no longer necessary. Mixing such a message with another error description is also not very helpful when it comes to localization (still not supported, though). - Add several properties (paths, strings, error codes) that serve as arguments to the primary error and are used by FullErrorMessage(). - Add issues property, a list of instances of new class BTransactionIssue. Those describe non-critical issues (e.g. failed update of a settings file) that occurred in the process of committing the transaction. Those issues should be presented to the user by the package management program. * Exception: Adjust to transport the BCommitTransactionResult properties. * CommitTransactionHandler, FsTransactions, Root, Volume: Adjust to BCommitTransactionResult/Exception changes. * CommitTransactionHandler: Now requires a BCommitTransactionResult to which it adds the issues it encounters. The reply BMessage is no longer needed, though. * Volume: Refactor common code from the three methods that use CommitTransactionHandler into new method _CommitTransaction. --- .../os/package/CommitTransactionResult.h | 1 + headers/os/package/CommitTransactionResult.h | 168 ++++ headers/private/package/DaemonClient.h | 39 +- headers/private/package/DaemonDefs.h | 47 +- .../private/package/manager/PackageManager.h | 6 +- src/build/libpackage/Jamfile | 1 + src/kits/package/CommitTransactionResult.cpp | 662 ++++++++++++++++ src/kits/package/DaemonClient.cpp | 149 +--- src/kits/package/Jamfile | 1 + src/kits/package/manager/PackageManager.cpp | 9 +- .../package/CommitTransactionHandler.cpp | 726 ++++++++++++------ .../package/CommitTransactionHandler.h | 36 +- src/servers/package/Exception.cpp | 112 +-- src/servers/package/Exception.h | 46 +- src/servers/package/FSTransaction.cpp | 17 +- src/servers/package/PackageManager.cpp | 4 +- src/servers/package/PackageManager.h | 6 +- src/servers/package/Root.cpp | 8 +- src/servers/package/Volume.cpp | 138 ++-- src/servers/package/Volume.h | 9 +- 20 files changed, 1573 insertions(+), 612 deletions(-) create mode 100644 headers/build/os/package/CommitTransactionResult.h create mode 100644 headers/os/package/CommitTransactionResult.h create mode 100644 src/kits/package/CommitTransactionResult.cpp diff --git a/headers/build/os/package/CommitTransactionResult.h b/headers/build/os/package/CommitTransactionResult.h new file mode 100644 index 0000000000..309b7fef6a --- /dev/null +++ b/headers/build/os/package/CommitTransactionResult.h @@ -0,0 +1 @@ +#include <../os/package/CommitTransactionResult.h> diff --git a/headers/os/package/CommitTransactionResult.h b/headers/os/package/CommitTransactionResult.h new file mode 100644 index 0000000000..3f9f288b2e --- /dev/null +++ b/headers/os/package/CommitTransactionResult.h @@ -0,0 +1,168 @@ +/* + * Copyright 2014, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _PACKAGE__COMMIT_TRANSACTION_RESULT_H_ +#define _PACKAGE__COMMIT_TRANSACTION_RESULT_H_ + + +#include +#include + + +class BMessage; + + +namespace BPackageKit { + + +enum BTransactionError { + B_TRANSACTION_OK = 0, + B_TRANSACTION_NO_MEMORY, + B_TRANSACTION_INTERNAL_ERROR, + B_TRANSACTION_INSTALLATION_LOCATION_BUSY, + B_TRANSACTION_CHANGE_COUNT_MISMATCH, + B_TRANSACTION_BAD_REQUEST, + B_TRANSACTION_NO_SUCH_PACKAGE, + B_TRANSACTION_PACKAGE_ALREADY_EXISTS, + B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH, + B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY, + B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY, + B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY, + B_TRANSACTION_FAILED_TO_MOVE_DIRECTORY, + B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE, + B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE, + B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE, + B_TRANSACTION_FAILED_TO_OPEN_FILE, + B_TRANSACTION_FAILED_TO_MOVE_FILE, + B_TRANSACTION_FAILED_TO_COPY_FILE, + B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE, + B_TRANSACTION_FAILED_TO_ACCESS_ENTRY, + B_TRANSACTION_FAILED_TO_ADD_GROUP, + B_TRANSACTION_FAILED_TO_ADD_USER, + B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP, + B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION, +}; + + +class BTransactionIssue { +public: + enum BType { + B_WRITABLE_FILE_TYPE_MISMATCH, + B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE, + B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING, + B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH, + B_WRITABLE_FILE_COMPARISON_FAILED, + B_WRITABLE_FILE_NOT_EQUAL, + B_WRITABLE_SYMLINK_COMPARISON_FAILED, + B_WRITABLE_SYMLINK_NOT_EQUAL, + B_POST_INSTALL_SCRIPT_NOT_FOUND, + B_STARTING_POST_INSTALL_SCRIPT_FAILED, + B_POST_INSTALL_SCRIPT_FAILED, + }; + +public: + BTransactionIssue(); + BTransactionIssue(BType type, + const BString& packageName, + const BString& path1, const BString& path2, + status_t systemError, int exitCode); + BTransactionIssue( + const BTransactionIssue& other); + ~BTransactionIssue(); + + BType Type() const; + const BString& PackageName() const; + const BString& Path1() const; + const BString& Path2() const; + status_t SystemError() const; + int ExitCode() const; + + BString ToString() const; + + status_t AddToMessage(BMessage& message) const; + status_t ExtractFromMessage(const BMessage& message); + + BTransactionIssue& operator=(const BTransactionIssue& other); + +private: + BType fType; + BString fPackageName; + BString fPath1; + BString fPath2; + status_t fSystemError; + int fExitCode; +}; + + +class BCommitTransactionResult { +public: + BCommitTransactionResult(); + BCommitTransactionResult( + BTransactionError error); + BCommitTransactionResult( + const BCommitTransactionResult& other); + ~BCommitTransactionResult(); + + void Unset(); + + int32 CountIssues() const; + const BTransactionIssue* IssueAt(int32 index) const; + bool AddIssue(const BTransactionIssue& issue); + + BTransactionError Error() const; + void SetError(BTransactionError error); + + status_t SystemError() const; + void SetSystemError(status_t error); + + const BString& ErrorPackage() const; + // may be empty, even on error + void SetErrorPackage(const BString& packageName); + + BString FullErrorMessage() const; + + const BString& Path1() const; + void SetPath1(const BString& path); + + const BString& Path2() const; + void SetPath2(const BString& path); + + const BString& Path3() const; + void SetPath3(const BString& path); + + const BString& String1() const; + void SetString1(const BString& string); + + const BString& String2() const; + void SetString2(const BString& string); + + const BString& OldStateDirectory() const; + void SetOldStateDirectory(const BString& directory); + + status_t AddToMessage(BMessage& message) const; + status_t ExtractFromMessage(const BMessage& message); + + BCommitTransactionResult& operator=( + const BCommitTransactionResult& other); + +private: + typedef BObjectList IssueList; + +private: + BTransactionError fError; + status_t fSystemError; + BString fErrorPackage; + BString fPath1; + BString fPath2; + BString fString1; + BString fString2; + BString fOldStateDirectory; + IssueList fIssues; +}; + + +} // namespace BPackageKit + + +#endif // _PACKAGE__COMMIT_TRANSACTION_RESULT_H_ diff --git a/headers/private/package/DaemonClient.h b/headers/private/package/DaemonClient.h index d050e3e31e..cdc0e28dd6 100644 --- a/headers/private/package/DaemonClient.h +++ b/headers/private/package/DaemonClient.h @@ -22,6 +22,7 @@ class BDirectory; namespace BPackageKit { +class BCommitTransactionResult; class BInstallationLocationInfo; class BPackageInfoSet; @@ -33,9 +34,6 @@ class BActivationTransaction; class BDaemonClient { -public: - class BCommitTransactionResult; - public: BDaemonClient(); ~BDaemonClient(); @@ -65,41 +63,6 @@ private: }; -class BDaemonClient::BCommitTransactionResult { -public: - BCommitTransactionResult(); - BCommitTransactionResult(int32 error, - const BString& errorMessage, - const BString& errorPackage, - const BString& oldStateDirectory); - ~BCommitTransactionResult(); - - void SetTo(int32 error, const BString& errorMessage, - const BString& errorPackage, - const BString& oldStateDirectory); - - status_t Error() const; - BDaemonError DaemonError() const; - // may be B_DAEMON_OK, even if Error() is - // != B_OK, then Error() is as specific as - // is known - const BString& ErrorMessage() const; - // may be empty, even on error - const BString& ErrorPackage() const; - // may be empty, even on error - - BString FullErrorMessage() const; - - const BString& OldStateDirectory() const; - -private: - int32 fError; - BString fErrorMessage; - BString fErrorPackage; - BString fOldStateDirectory; -}; - - } // namespace BPrivate } // namespace BPackageKit diff --git a/headers/private/package/DaemonDefs.h b/headers/private/package/DaemonDefs.h index 0f9f6e62bd..0fd7d5767e 100644 --- a/headers/private/package/DaemonDefs.h +++ b/headers/private/package/DaemonDefs.h @@ -16,16 +16,6 @@ namespace BPrivate { #define B_PACKAGE_DAEMON_APP_SIGNATURE "application/x-vnd.haiku-package_daemon" -enum BDaemonError { - B_DAEMON_OK = 0, - B_DAEMON_INSTALLATION_LOCATION_BUSY, - B_DAEMON_CHANGE_COUNT_MISMATCH, - B_DAEMON_BAD_REQUEST, - B_DAEMON_NO_SUCH_PACKAGE, - B_DAEMON_PACKAGE_ALREADY_EXISTS -}; - - // message codes for requests to and replies from the daemon enum { B_MESSAGE_GET_INSTALLATION_LOCATION_INFO = 'PKLI', @@ -59,17 +49,40 @@ enum { // transaction directory B_MESSAGE_COMMIT_TRANSACTION_REPLY = 'PKTR' // "error": int32 - // regular error code or BDaemonError describing how committing - // the transaction went - // "error message": string - // [error case only] gives some additional information what went - // wrong; optional + // a BTransactionError describing how committing the transaction + // went + // "system error": int32 + // a status_t for the operation that failed; B_ERROR, if n/a // "error package": string // [error case only] file name of the package causing the error, // if any in particarly; optional + // "path1": string + // [error case only] first path specific to the error + // "path2": string + // [error case only] second path specific to the error + // "string1": string + // [error case only] first string specific to the error + // "string2": string + // [error case only] second string specific to the error // "old state": string - // name of the directory (subdirectory of the administrative - // directory) containing the deactivated packages + // [success case only] name of the directory (subdirectory of the + // administrative directory) containing the deactivated packages + // "issues": message[] + // A list of non-critical issues that occurred while performing the + // package activation. On success the user should be notified about + // these. Each contains: + // "type": int32 + // a BTransactionIssue::BType specifying the kind of issue + // "package": string + // file name of the package which the issue is related to + // "path1": string + // first path specific to the issue + // "path2": string + // second path specific to the issue + // "system error": int32 + // a status_t for the operation that failed; B_OK, if n/a + // "exit code": int32 + // a exit code of the program that failed; 0, if n/a }; diff --git a/headers/private/package/manager/PackageManager.h b/headers/private/package/manager/PackageManager.h index 7b7644ee40..549c604f56 100644 --- a/headers/private/package/manager/PackageManager.h +++ b/headers/private/package/manager/PackageManager.h @@ -234,8 +234,7 @@ public: virtual status_t PrepareTransaction(Transaction& transaction) = 0; virtual status_t CommitTransaction(Transaction& transaction, - BDaemonClient::BCommitTransactionResult& - _result) = 0; + BCommitTransactionResult& _result) = 0; }; @@ -250,8 +249,7 @@ public: virtual status_t PrepareTransaction(Transaction& transaction); virtual status_t CommitTransaction(Transaction& transaction, - BDaemonClient::BCommitTransactionResult& - _result); + BCommitTransactionResult& _result); private: BDaemonClient fDaemonClient; diff --git a/src/build/libpackage/Jamfile b/src/build/libpackage/Jamfile index be248fd836..6814f3c981 100644 --- a/src/build/libpackage/Jamfile +++ b/src/build/libpackage/Jamfile @@ -97,6 +97,7 @@ BuildPlatformSharedLibrary libpackage_build.so AddRepositoryRequest.cpp Attributes.cpp ChecksumAccessors.cpp + CommitTransactionResult.cpp Context.cpp DownloadFileRequest.cpp DropRepositoryRequest.cpp diff --git a/src/kits/package/CommitTransactionResult.cpp b/src/kits/package/CommitTransactionResult.cpp new file mode 100644 index 0000000000..239c83acfe --- /dev/null +++ b/src/kits/package/CommitTransactionResult.cpp @@ -0,0 +1,662 @@ +/* + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + + +#include + +#include + +//#include + + +namespace BPackageKit { + + +// #pragma mark - BTransactionIssue + + +BTransactionIssue::BTransactionIssue() + : + fType(B_WRITABLE_FILE_TYPE_MISMATCH), + fPackageName(), + fPath1(), + fPath2(), + fSystemError(B_OK), + fExitCode(0) +{ +} + + +BTransactionIssue::BTransactionIssue(BType type, const BString& packageName, + const BString& path1, const BString& path2, status_t systemError, + int exitCode) + : + fType(type), + fPackageName(packageName), + fPath1(path1), + fPath2(path2), + fSystemError(systemError), + fExitCode(exitCode) +{ +} + + +BTransactionIssue::BTransactionIssue(const BTransactionIssue& other) +{ + *this = other; +} + + +BTransactionIssue::~BTransactionIssue() +{ +} + + +BTransactionIssue::BType +BTransactionIssue::Type() const +{ + return fType; +} + + +const BString& +BTransactionIssue::PackageName() const +{ + return fPackageName; +} + + +const BString& +BTransactionIssue::Path1() const +{ + return fPath1; +} + + +const BString& +BTransactionIssue::Path2() const +{ + return fPath2; +} + + +status_t +BTransactionIssue::SystemError() const +{ + return fSystemError; +} + + +int +BTransactionIssue::ExitCode() const +{ + return fExitCode; +} + + +BString +BTransactionIssue::ToString() const +{ + const char* messageTemplate = ""; + switch (fType) { + case B_WRITABLE_FILE_TYPE_MISMATCH: + messageTemplate = "\"%path1%\" cannot be updated automatically," + " since its type doesn't match the type of \"%path2%\" which it" + " is supposed to be updated with." + " Please perform the update manually if needed."; + break; + case B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE: + messageTemplate = "\"%path1%\" cannot be updated automatically," + " since it doesn't have a SYS:PACKAGE attribute." + " Please perform the update manually if needed."; + break; + case B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING: + messageTemplate = "\"%path1%\" cannot be updated automatically," + " since \"%path2%\" which we need to compare it with is" + " missing." + " Please perform the update manually if needed."; + break; + case B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH: + messageTemplate = "\"%path1%\" cannot be updated automatically," + " since its type doesn't match the type of \"%path2%\" which we" + " need to compare it with." + " Please perform the update manually if needed."; + break; + case B_WRITABLE_FILE_COMPARISON_FAILED: + messageTemplate = "\"%path1%\" cannot be updated automatically," + " since the comparison with \"%path2%\" failed: %error%." + " Please perform the update manually if needed."; + break; + case B_WRITABLE_FILE_NOT_EQUAL: // !keep old + messageTemplate = "\"%path1%\" cannot be updated automatically," + " since it was changed manually from previous version" + " \"%path2\"" + " Please perform the update manually if needed."; + break; + case B_WRITABLE_SYMLINK_COMPARISON_FAILED: // !keep old + messageTemplate = "Symbolic link \"%path1%\" cannot be updated" + " automatically, since the comparison with \"%path2%\" failed:" + " %error%." + " Please perform the update manually if needed."; + break; + case B_WRITABLE_SYMLINK_NOT_EQUAL: // !keep old + messageTemplate = "Symbolic link \"%path1%\" cannot be updated" + " automatically, since it was changed manually from previous" + " version \"%path2\"" + " Please perform the update manually if needed."; + break; + case B_POST_INSTALL_SCRIPT_NOT_FOUND: + messageTemplate = "Failed to find post-installation script " + " \"%path1%\": %error%."; + break; + case B_STARTING_POST_INSTALL_SCRIPT_FAILED: + messageTemplate = "Failed to run post-installation script " + " \"%path1%\": %error%."; + break; + case B_POST_INSTALL_SCRIPT_FAILED: + messageTemplate = "The post-installation script " + " \"%path1%\" failed with exit code %exitCode%."; + break; + } + + BString message(messageTemplate); + message.ReplaceAll("%path1%", fPath1) + .ReplaceAll("%path2%", fPath2) + .ReplaceAll("%error%", strerror(fSystemError)) + .ReplaceAll("%exitCode%", BString() << fExitCode); + return message; +} + + +status_t +BTransactionIssue::AddToMessage(BMessage& message) const +{ + status_t error; + if ((error = message.AddInt32("type", (int32)fType)) != B_OK + || (error = message.AddString("package", fPackageName)) != B_OK + || (error = message.AddString("path1", fPath1)) != B_OK + || (error = message.AddString("path2", fPath2)) != B_OK + || (error = message.AddInt32("system error", (int32)fSystemError)) + != B_OK + || (error = message.AddInt32("exit code", (int32)fExitCode)) != B_OK) { + return error; + } + + return B_OK; +} + + +status_t +BTransactionIssue::ExtractFromMessage(const BMessage& message) +{ + status_t error; + int32 type; + int32 systemError; + int32 exitCode; + if ((error = message.FindInt32("type", &type)) != B_OK + || (error = message.FindString("package", &fPackageName)) != B_OK + || (error = message.FindString("path1", &fPath1)) != B_OK + || (error = message.FindString("path2", &fPath2)) != B_OK + || (error = message.FindInt32("system error", &systemError)) != B_OK + || (error = message.FindInt32("exit code", &exitCode)) != B_OK) { + return error; + } + + fType = (BType)type; + fSystemError = (status_t)systemError; + fExitCode = (int)exitCode; + + return B_OK; +} + + +BTransactionIssue& +BTransactionIssue::operator=(const BTransactionIssue& other) +{ + fType = other.fType; + fPackageName = other.fPackageName; + fPath1 = other.fPath1; + fPath2 = other.fPath2; + fSystemError = other.fSystemError; + fExitCode = other.fExitCode; + + return *this; +} + + +// #pragma mark - BCommitTransactionResult + + +BCommitTransactionResult::BCommitTransactionResult() + : + fError(B_TRANSACTION_INTERNAL_ERROR), + fSystemError(B_ERROR), + fErrorPackage(), + fPath1(), + fPath2(), + fString1(), + fString2(), + fOldStateDirectory(), + fIssues(10, true) +{ +} + + +BCommitTransactionResult::BCommitTransactionResult(BTransactionError error) + : + fError(error), + fSystemError(B_ERROR), + fErrorPackage(), + fPath1(), + fPath2(), + fString1(), + fString2(), + fOldStateDirectory(), + fIssues(10, true) +{ +} + + +BCommitTransactionResult::BCommitTransactionResult( + const BCommitTransactionResult& other) + : + fError(B_TRANSACTION_INTERNAL_ERROR), + fSystemError(B_ERROR), + fErrorPackage(), + fPath1(), + fPath2(), + fString1(), + fString2(), + fOldStateDirectory(), + fIssues(10, true) +{ + *this = other; +} + + +BCommitTransactionResult::~BCommitTransactionResult() +{ +} + + +void +BCommitTransactionResult::Unset() +{ + fError = B_TRANSACTION_INTERNAL_ERROR; + fSystemError = B_ERROR; + fErrorPackage.Truncate(0); + fPath1.Truncate(0); + fPath2.Truncate(0); + fString1.Truncate(0); + fString2.Truncate(0); + fOldStateDirectory.Truncate(0); + fIssues.MakeEmpty(); +} + + +int32 +BCommitTransactionResult::CountIssues() const +{ + return fIssues.CountItems(); +} + + +const BTransactionIssue* +BCommitTransactionResult::IssueAt(int32 index) const +{ + if (index < 0 || index >= CountIssues()) + return NULL; + return fIssues.ItemAt(index); +} + + +bool +BCommitTransactionResult::AddIssue(const BTransactionIssue& issue) +{ + BTransactionIssue* newIssue = new(std::nothrow) BTransactionIssue(issue); + if (newIssue == NULL || !fIssues.AddItem(newIssue)) { + delete newIssue; + return false; + } + return true; +} + + +BTransactionError +BCommitTransactionResult::Error() const +{ + return fError > 0 ? (BTransactionError)fError : B_TRANSACTION_OK; +} + + +void +BCommitTransactionResult::SetError(BTransactionError error) +{ + fError = error; +} + + +status_t +BCommitTransactionResult::SystemError() const +{ + return fSystemError; +} + + +void +BCommitTransactionResult::SetSystemError(status_t error) +{ + fSystemError = error; +} + + +const BString& +BCommitTransactionResult::ErrorPackage() const +{ + return fErrorPackage; +} + + +void +BCommitTransactionResult::SetErrorPackage(const BString& packageName) +{ + fErrorPackage = packageName; +} + + +BString +BCommitTransactionResult::FullErrorMessage() const +{ + if (fError == 0) + return "no error"; + + const char* messageTemplate = ""; + switch ((BTransactionError)fError) { + case B_TRANSACTION_OK: + messageTemplate = "Everything went fine."; + break; + case B_TRANSACTION_NO_MEMORY: + messageTemplate = "Out of memory."; + break; + case B_TRANSACTION_INTERNAL_ERROR: + messageTemplate = "An internal error occurred. Specifics can be" + " found in the syslog."; + break; + case B_TRANSACTION_INSTALLATION_LOCATION_BUSY: + messageTemplate = "Another package operation is already in" + " progress."; + break; + case B_TRANSACTION_CHANGE_COUNT_MISMATCH: + messageTemplate = "The transaction is out of date."; + break; + case B_TRANSACTION_BAD_REQUEST: + messageTemplate = "The requested transaction is invalid."; + break; + case B_TRANSACTION_NO_SUCH_PACKAGE: + messageTemplate = "No such package \"%package%\"."; + break; + case B_TRANSACTION_PACKAGE_ALREADY_EXISTS: + messageTemplate = "The to be activated package \"%package%\" does" + " already exist."; + break; + case B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH: + if (fPath1.IsEmpty()) { + if (fErrorPackage.IsEmpty()) { + messageTemplate = "A file path could not be determined:" + "%error%"; + } else { + messageTemplate = "While processing package \"%package%\"" + " a file path could not be determined: %error%"; + } + } else { + if (fErrorPackage.IsEmpty()) { + messageTemplate = "The file path for \"%path1%\" could not" + " be determined: %error%"; + } else { + messageTemplate = "While processing package \"%package%\"" + " the file path for \"%path1%\" could not be" + " determined: %error%"; + } + } + break; + case B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY: + messageTemplate = "Failed to open directory \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY: + messageTemplate = "Failed to create directory \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY: + messageTemplate = "Failed to remove directory \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_MOVE_DIRECTORY: + messageTemplate = "Failed to move directory \"%path1%\" to" + " \"%path2%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE: + messageTemplate = "Failed to write new package activation file" + " \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE: + messageTemplate = "Failed to read package file \"%path1%\":" + " %error%"; + break; + case B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE: + messageTemplate = "Failed to extract \"%path1%\" from package" + " \"%package%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_OPEN_FILE: + messageTemplate = "Failed to open file \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_MOVE_FILE: + messageTemplate = "Failed to move file \"%path1%\" to \"%path2%\":" + " %error%"; + break; + case B_TRANSACTION_FAILED_TO_COPY_FILE: + messageTemplate = "Failed to copy file \"%path1%\" to \"%path2%\":" + " %error%"; + break; + case B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE: + messageTemplate = "Failed to write attribute \"%string1%\" of file" + " \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_ACCESS_ENTRY: + messageTemplate = "Failed to access entry \"%path1%\": %error%"; + break; + case B_TRANSACTION_FAILED_TO_ADD_GROUP: + messageTemplate = "Failed to add user group \"%string1%\" required" + " by package \"%package%\"."; + break; + case B_TRANSACTION_FAILED_TO_ADD_USER: + messageTemplate = "Failed to add user \"%string1%\" required" + " by package \"%package%\"."; + break; + case B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP: + messageTemplate = "Failed to add user \"%string1%\" to group" + " \"%string2%\" as required by package \"%package%\"."; + break; + case B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION: + messageTemplate = "Failed to change the package activation in" + " packagefs: %error%"; + break; + } + + BString message(messageTemplate); + message.ReplaceAll("%package%", fErrorPackage) + .ReplaceAll("%path1%", fPath1) + .ReplaceAll("%path2%", fPath2) + .ReplaceAll("%string1%", fString1) + .ReplaceAll("%string2%", fString2) + .ReplaceAll("%error%", strerror(fSystemError)); + return message; +} + + +const BString& +BCommitTransactionResult::Path1() const +{ + return fPath1; +} + + +void +BCommitTransactionResult::SetPath1(const BString& path) +{ + fPath1 = path; +} + + +const BString& +BCommitTransactionResult::Path2() const +{ + return fPath2; +} + + +void +BCommitTransactionResult::SetPath2(const BString& path) +{ + fPath2 = path; +} + + +const BString& +BCommitTransactionResult::String1() const +{ + return fString1; +} + + +void +BCommitTransactionResult::SetString1(const BString& string) +{ + fString1 = string; +} + + +const BString& +BCommitTransactionResult::String2() const +{ + return fString2; +} + + +void +BCommitTransactionResult::SetString2(const BString& string) +{ + fString2 = string; +} + + +const BString& +BCommitTransactionResult::OldStateDirectory() const +{ + return fOldStateDirectory; +} + + +void +BCommitTransactionResult::SetOldStateDirectory(const BString& directory) +{ + fOldStateDirectory = directory; +} + + +status_t +BCommitTransactionResult::AddToMessage(BMessage& message) const +{ + status_t error; + if ((error = message.AddInt32("error", (int32)fError)) != B_OK + || (error = message.AddInt32("system error", (int32)fSystemError)) + != B_OK + || (error = message.AddString("error package", fErrorPackage)) != B_OK + || (error = message.AddString("path1", fPath1)) != B_OK + || (error = message.AddString("path2", fPath2)) != B_OK + || (error = message.AddString("string1", fString1)) != B_OK + || (error = message.AddString("string2", fString2)) != B_OK + || (error = message.AddString("old state", fOldStateDirectory)) + != B_OK) { + return error; + } + + int32 count = fIssues.CountItems(); + for (int32 i = 0; i < count; i++) { + const BTransactionIssue* issue = fIssues.ItemAt(i); + BMessage issueMessage; + if ((error = issue->AddToMessage(issueMessage)) != B_OK + || (error = message.AddMessage("issues", &issueMessage)) != B_OK) { + return error; + } + } + + return B_OK; +} + + +status_t +BCommitTransactionResult::ExtractFromMessage(const BMessage& message) +{ + Unset(); + + int32 resultError; + int32 systemError; + status_t error; + if ((error = message.FindInt32("error", &resultError)) != B_OK + || (error = message.FindInt32("system error", &systemError)) != B_OK + || (error = message.FindString("error package", &fErrorPackage)) != B_OK + || (error = message.FindString("path1", &fPath1)) != B_OK + || (error = message.FindString("path2", &fPath2)) != B_OK + || (error = message.FindString("string1", &fString1)) != B_OK + || (error = message.FindString("string2", &fString2)) != B_OK + || (error = message.FindString("old state", &fOldStateDirectory)) + != B_OK) { + return error; + } + + fError = (BTransactionError)resultError; + fSystemError = (status_t)systemError; + + BMessage issueMessage; + for (int32 i = 0; message.FindMessage("issues", i, &issueMessage) == B_OK; + i++) { + BTransactionIssue issue; + error = issue.ExtractFromMessage(issueMessage); + if (error != B_OK) + return error; + + if (!AddIssue(issue)) + return B_NO_MEMORY; + } + + return B_OK; +} + + +BCommitTransactionResult& +BCommitTransactionResult::operator=(const BCommitTransactionResult& other) +{ + Unset(); + + fError = other.fError; + fSystemError = other.fSystemError; + fErrorPackage = other.fErrorPackage; + fPath1 = other.fPath1; + fPath2 = other.fPath2; + fString1 = other.fString1; + fString2 = other.fString2; + fOldStateDirectory = other.fOldStateDirectory; + + for (int32 i = 0; const BTransactionIssue* issue = other.fIssues.ItemAt(i); + i++) { + AddIssue(*issue); + } + + return *this; +} + + +} // namespace BPackageKit diff --git a/src/kits/package/DaemonClient.cpp b/src/kits/package/DaemonClient.cpp index 6aacffc629..ee0e7c2cd8 100644 --- a/src/kits/package/DaemonClient.cpp +++ b/src/kits/package/DaemonClient.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -24,9 +25,6 @@ namespace BPackageKit { namespace BPrivate { -// #pragma mark - BCommitTransactionResult - - BDaemonClient::BDaemonClient() : fDaemonMessenger() @@ -131,26 +129,7 @@ BDaemonClient::CommitTransaction(const BActivationTransaction& transaction, return B_ERROR; // extract the result - int32 requestError; - error = reply.FindInt32("error", &requestError); - if (error != B_OK) - return error; - - BString errorMessage; - BString errorPackage; - BString oldStateDirectory; - if (requestError == 0) { - error = reply.FindString("old state", &oldStateDirectory); - if (error != B_OK) - return error; - } else { - reply.FindString("error message", &errorMessage); - reply.FindString("error package", &errorPackage); - } - - _result.SetTo(requestError, errorMessage, errorPackage, oldStateDirectory); - return B_OK; - // Even on error. B_OK just indicates that we have initialized _result. + return _result.ExtractFromMessage(reply); } @@ -254,129 +233,5 @@ BDaemonClient::_ExtractPackageInfoSet(const BMessage& message, } -// #pragma mark - BCommitTransactionResult - - -BDaemonClient::BCommitTransactionResult::BCommitTransactionResult() - : - fError(B_NO_INIT), - fErrorMessage(), - fErrorPackage(), - fOldStateDirectory() -{ -} - - -BDaemonClient::BCommitTransactionResult::BCommitTransactionResult(int32 error, - const BString& errorMessage, const BString& errorPackage, - const BString& oldStateDirectory) - : - fError(error), - fErrorMessage(errorMessage), - fErrorPackage(errorPackage), - fOldStateDirectory(oldStateDirectory) -{ -} - - -BDaemonClient::BCommitTransactionResult::~BCommitTransactionResult() -{ -} - - -void -BDaemonClient::BCommitTransactionResult::SetTo(int32 error, - const BString& errorMessage, const BString& errorPackage, - const BString& oldStateDirectory) -{ - fError = error; - fErrorMessage = errorMessage; - fErrorPackage = errorPackage; - fOldStateDirectory = oldStateDirectory; -} - - -status_t -BDaemonClient::BCommitTransactionResult::Error() const -{ - return fError <= 0 ? fError : B_ERROR; -} - - -BDaemonError -BDaemonClient::BCommitTransactionResult::DaemonError() const -{ - return fError > 0 ? (BDaemonError)fError : B_DAEMON_OK; -} - - -const BString& -BDaemonClient::BCommitTransactionResult::ErrorMessage() const -{ - return fErrorMessage; -} - - -const BString& -BDaemonClient::BCommitTransactionResult::ErrorPackage() const -{ - return fErrorPackage; -} - - -BString -BDaemonClient::BCommitTransactionResult::FullErrorMessage() const -{ - if (fError == 0) - return "no error"; - - const char* errorString; - if (fError > 0) { - switch ((BDaemonError)fError) { - case B_DAEMON_INSTALLATION_LOCATION_BUSY: - errorString = "another package operation already in progress"; - break; - case B_DAEMON_CHANGE_COUNT_MISMATCH: - errorString = "transaction out of date"; - break; - case B_DAEMON_BAD_REQUEST: - errorString = "invalid transaction"; - break; - case B_DAEMON_NO_SUCH_PACKAGE: - errorString = "no such package"; - break; - case B_DAEMON_PACKAGE_ALREADY_EXISTS: - errorString = "package already exists"; - break; - case B_DAEMON_OK: - default: - errorString = "unknown error"; - break; - } - } else - errorString = strerror(fError); - - BString result; - if (!fErrorMessage.IsEmpty()) { - result = fErrorMessage; - result << ": "; - } - - result << errorString; - - if (!fErrorPackage.IsEmpty()) - result << ", package: \"" << fErrorPackage << '"'; - - return result; -} - - -const BString& -BDaemonClient::BCommitTransactionResult::OldStateDirectory() const -{ - return fOldStateDirectory; -} - - } // namespace BPrivate } // namespace BPackageKit diff --git a/src/kits/package/Jamfile b/src/kits/package/Jamfile index d17dac71e0..3863cd1ea3 100644 --- a/src/kits/package/Jamfile +++ b/src/kits/package/Jamfile @@ -14,6 +14,7 @@ HPKG_SOURCES = BlockBufferPoolNoLock.cpp BufferDataOutput.cpp BufferPool.cpp + CommitTransactionResult.cpp DataReader.cpp DataWriters.cpp ErrorOutput.cpp diff --git a/src/kits/package/manager/PackageManager.cpp b/src/kits/package/manager/PackageManager.cpp index 95761bb777..5620f962f8 100644 --- a/src/kits/package/manager/PackageManager.cpp +++ b/src/kits/package/manager/PackageManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -576,12 +577,12 @@ BPackageManager::_CommitPackageChanges(Transaction& transaction) installationRepository); // commit the transaction - BDaemonClient::BCommitTransactionResult transactionResult; + BCommitTransactionResult transactionResult; status_t error = fInstallationInterface->CommitTransaction(transaction, transactionResult); if (error != B_OK) DIE(error, "failed to commit transaction"); - if (transactionResult.Error() != B_OK) { + if (transactionResult.Error() != B_TRANSACTION_OK) { DIE("failed to commit transaction: %s", transactionResult.FullErrorMessage().String()); } @@ -933,7 +934,7 @@ BPackageManager::ClientInstallationInterface::PrepareTransaction( status_t BPackageManager::ClientInstallationInterface::CommitTransaction( - Transaction& transaction, BDaemonClient::BCommitTransactionResult& _result) + Transaction& transaction, BCommitTransactionResult& _result) { return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(), _result); diff --git a/src/servers/package/CommitTransactionHandler.cpp b/src/servers/package/CommitTransactionHandler.cpp index a7394b5761..7336c05144 100644 --- a/src/servers/package/CommitTransactionHandler.cpp +++ b/src/servers/package/CommitTransactionHandler.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,9 +32,84 @@ using namespace BPackageKit::BPrivate; +using BPackageKit::BTransactionIssue; + + +// #pragma mark - TransactionIssueBuilder + + +struct CommitTransactionHandler::TransactionIssueBuilder { + TransactionIssueBuilder(BTransactionIssue::BType type, + Package* package = NULL) + : + fType(type), + fPackageName(package != NULL ? package->FileName() : BString()), + fPath1(), + fPath2(), + fSystemError(B_OK), + fExitCode(0) + { + } + + TransactionIssueBuilder& SetPath1(const BString& path) + { + fPath1 = path; + return *this; + } + + TransactionIssueBuilder& SetPath1(const FSUtils::Entry& entry) + { + return SetPath1(entry.Path()); + } + + TransactionIssueBuilder& SetPath2(const BString& path) + { + fPath2 = path; + return *this; + } + + TransactionIssueBuilder& SetPath2(const FSUtils::Entry& entry) + { + return SetPath2(entry.Path()); + } + + TransactionIssueBuilder& SetSystemError(status_t error) + { + fSystemError = error; + return *this; + } + + TransactionIssueBuilder& SetExitCode(int exitCode) + { + fExitCode = exitCode; + return *this; + } + + BTransactionIssue BuildIssue(Package* package) const + { + BString packageName(fPackageName); + if (packageName.IsEmpty() && package != NULL) + packageName = package->FileName(); + + return BTransactionIssue(fType, packageName, fPath1, fPath2, + fSystemError, fExitCode); + } + +private: + BTransactionIssue::BType fType; + BString fPackageName; + BString fPath1; + BString fPath2; + status_t fSystemError; + int fExitCode; +}; + + +// #pragma mark - CommitTransactionHandler + CommitTransactionHandler::CommitTransactionHandler(Volume* volume, - PackageFileManager* packageFileManager) + PackageFileManager* packageFileManager, BCommitTransactionResult& result) : fVolume(volume), fPackageFileManager(packageFileManager), @@ -52,7 +128,9 @@ CommitTransactionHandler::CommitTransactionHandler(Volume* volume, fWritableFilesDirectory(), fAddedGroups(), fAddedUsers(), - fFSTransaction() + fFSTransaction(), + fResult(result), + fCurrentPackage(NULL) { } @@ -100,7 +178,7 @@ CommitTransactionHandler::Init(VolumeState* volumeState, void -CommitTransactionHandler::HandleRequest(BMessage* request, BMessage* reply) +CommitTransactionHandler::HandleRequest(BMessage* request) { status_t error; BActivationTransaction transaction(request, &error); @@ -108,21 +186,21 @@ CommitTransactionHandler::HandleRequest(BMessage* request, BMessage* reply) error = transaction.InitCheck(); if (error != B_OK) { if (error == B_NO_MEMORY) - throw Exception(B_NO_MEMORY); - throw Exception(B_DAEMON_BAD_REQUEST); + throw Exception(B_TRANSACTION_NO_MEMORY); + throw Exception(B_TRANSACTION_BAD_REQUEST); } - HandleRequest(transaction, reply); + HandleRequest(transaction); } void CommitTransactionHandler::HandleRequest( - const BActivationTransaction& transaction, BMessage* reply) + const BActivationTransaction& transaction) { // check the change count if (transaction.ChangeCount() != fVolume->ChangeCount()) - throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH); + throw Exception(B_TRANSACTION_CHANGE_COUNT_MISMATCH); // collect the packages to deactivate _GetPackagesToDeactivate(transaction); @@ -132,11 +210,12 @@ CommitTransactionHandler::HandleRequest( // anything to do at all? if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) { - throw Exception(B_DAEMON_BAD_REQUEST, - "no packages to activate or deactivate"); + WARN("Bad package activation request: no packages to activate or" + " deactivate\n"); + throw Exception(B_TRANSACTION_BAD_REQUEST); } - _ApplyChanges(reply); + _ApplyChanges(); } @@ -151,7 +230,7 @@ CommitTransactionHandler::HandleRequest() fPackagesToDeactivate = fPackagesAlreadyRemoved; - _ApplyChanges(NULL); + _ApplyChanges(); } @@ -197,8 +276,8 @@ CommitTransactionHandler::_GetPackagesToDeactivate( BString packageName = packagesToDeactivate.StringAt(i); Package* package = fVolumeState->FindPackage(packageName); if (package == NULL) { - throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package", - packageName); + throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE) + .SetPackageName(packageName); } fPackagesToDeactivate.insert(package); @@ -225,7 +304,9 @@ CommitTransactionHandler::_ReadPackagesToActivate( || transactionDirectoryName.FindFirst('/') >= 0 || transactionDirectoryName == "." || transactionDirectoryName == "..") { - throw Exception(B_DAEMON_BAD_REQUEST); + WARN("Bad package activation request: malformed transaction" + " directory name: \"%s\"\n", transactionDirectoryName.String()); + throw Exception(B_TRANSACTION_BAD_REQUEST); } // open the directory @@ -233,13 +314,22 @@ CommitTransactionHandler::_ReadPackagesToActivate( transactionDirectoryName); BDirectory directory; status_t error = _OpenPackagesSubDirectory(directoryPath, false, directory); - if (error != B_OK) - throw Exception(error, "failed to open transaction directory"); + if (error == B_OK) { + error = directory.GetNodeRef(&fTransactionDirectoryRef); + if (error != B_OK) { + ERROR("Failed to get transaction directory node ref: %s\n", + strerror(error)); + } + } else + ERROR("Failed to open transaction directory: %s\n", strerror(error)); - error = directory.GetNodeRef(&fTransactionDirectoryRef); if (error != B_OK) { - throw Exception(error, - "failed to get transaction directory node ref"); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(fVolume->PackagesDirectoryRef(), + directoryPath.ToString()), + directoryPath.ToString())) + .SetSystemError(error); } // read the packages @@ -252,14 +342,14 @@ CommitTransactionHandler::_ReadPackagesToActivate( if (fPackagesAlreadyAdded.find(package) != fPackagesAlreadyAdded.end()) { if (!fPackagesToActivate.AddItem(package)) - throw Exception(B_NO_MEMORY); + throw Exception(B_TRANSACTION_NO_MEMORY); continue; } if (fPackagesToDeactivate.find(package) == fPackagesToDeactivate.end()) { - throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL, - packageName); + throw Exception(B_TRANSACTION_PACKAGE_ALREADY_EXISTS) + .SetPackageName(packageName); } } @@ -267,22 +357,32 @@ CommitTransactionHandler::_ReadPackagesToActivate( error = fPackageFileManager->CreatePackage( NotOwningEntryRef(fTransactionDirectoryRef, packageName), package); - if (error != B_OK) - throw Exception(error, "failed to read package", packageName); + if (error != B_OK) { + if (error == B_NO_MEMORY) + throw Exception(B_TRANSACTION_NO_MEMORY); + throw Exception(B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE) + .SetPackageName(packageName) + .SetPath1(_GetPath( + FSUtils::Entry( + NotOwningEntryRef(fTransactionDirectoryRef, + packageName)), + packageName)) + .SetSystemError(error); + } if (!fPackagesToActivate.AddItem(package)) { delete package; - throw Exception(B_NO_MEMORY); + throw Exception(B_TRANSACTION_NO_MEMORY); } } } void -CommitTransactionHandler::_ApplyChanges(BMessage* reply) +CommitTransactionHandler::_ApplyChanges() { // create an old state directory - _CreateOldStateDirectory(reply); + _CreateOldStateDirectory(); // move packages to deactivate to old state directory _RemovePackagesToDeactivate(); @@ -309,7 +409,7 @@ CommitTransactionHandler::_ApplyChanges(BMessage* reply) void -CommitTransactionHandler::_CreateOldStateDirectory(BMessage* reply) +CommitTransactionHandler::_CreateOldStateDirectory() { // construct a nice name from the current date and time time_t nowSeconds = time(NULL); @@ -323,21 +423,28 @@ CommitTransactionHandler::_CreateOldStateDirectory(BMessage* reply) baseName = "state"; if (baseName.IsEmpty()) - throw Exception(B_NO_MEMORY); + throw Exception(B_TRANSACTION_NO_MEMORY); // make sure the directory doesn't exist yet BDirectory adminDirectory; status_t error = _OpenPackagesSubDirectory( RelativePath(kAdminDirectoryName), true, adminDirectory); - if (error != B_OK) - throw Exception(error, "failed to open administrative directory"); + if (error != B_OK) { + ERROR("Failed to open administrative directory: %s\n", strerror(error)); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(fVolume->PackagesDirectoryRef(), + kAdminDirectoryName), + kAdminDirectoryName)) + .SetSystemError(error); + } int uniqueId = 1; BString directoryName = baseName; while (BEntry(&adminDirectory, directoryName).Exists()) { directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++); if (directoryName.IsEmpty()) - throw Exception(B_NO_MEMORY); + throw Exception(B_TRANSACTION_NO_MEMORY); } // create the directory @@ -346,30 +453,31 @@ CommitTransactionHandler::_CreateOldStateDirectory(BMessage* reply) error = adminDirectory.CreateDirectory(directoryName, &fOldStateDirectory); - if (error != B_OK) - throw Exception(error, "failed to create old state directory"); + if (error == B_OK) { + createOldStateDirectoryOperation.Finished(); - createOldStateDirectoryOperation.Finished(); + fOldStateDirectoryName = directoryName; - fOldStateDirectoryName = directoryName; + error = fOldStateDirectory.GetNodeRef(&fOldStateDirectoryRef); + if (error != B_OK) + ERROR("Failed get old state directory ref: %s\n", strerror(error)); + } else + ERROR("Failed to create old state directory: %s\n", strerror(error)); - error = fOldStateDirectory.GetNodeRef(&fOldStateDirectoryRef); - if (error != B_OK) - throw Exception(error, "failed get old state directory ref"); + if (error != B_OK) { + throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(adminDirectory, directoryName), + directoryName)) + .SetSystemError(error); + } // write the old activation file BEntry activationFile; - error = _WriteActivationFile( - RelativePath(kAdminDirectoryName, directoryName), + _WriteActivationFile(RelativePath(kAdminDirectoryName, directoryName), kActivationFileName, PackageSet(), PackageSet(), activationFile); - if (error != B_OK) - throw Exception(error, "failed to write old activation file"); - // add the old state directory to the reply - if (reply != NULL) { - if (reply->AddString("old state", fOldStateDirectoryName) != B_OK) - throw Exception(B_NO_MEMORY); - } + fResult.SetOldStateDirectory(fOldStateDirectoryName); } @@ -399,8 +507,12 @@ CommitTransactionHandler::_RemovePackagesToDeactivate() BEntry entry; status_t error = entry.SetTo(&entryRef); if (error != B_OK) { - throw Exception(error, "failed to get package entry", - package->FileName()); + ERROR("Failed to get package entry for %s: %s\n", + package->FileName().String(), strerror(error)); + throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH) + .SetPath1(package->FileName()) + .SetPackageName(package->FileName()) + .SetSystemError(error); } // move entry @@ -409,9 +521,15 @@ CommitTransactionHandler::_RemovePackagesToDeactivate() error = entry.MoveTo(&fOldStateDirectory); if (error != B_OK) { fRemovedPackages.erase(package); - throw Exception(error, - "failed to move old package from packages directory", - package->FileName()); + ERROR("Failed to move old package %s from packages directory: %s\n", + package->FileName().String(), strerror(error)); + throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) + .SetPath1( + _GetPath(FSUtils::Entry(entryRef), package->FileName())) + .SetPath2(_GetPath( + FSUtils::Entry(fOldStateDirectory), + fOldStateDirectoryName)) + .SetSystemError(error); } fPackageFileManager->PackageFileMoved(package->File(), @@ -431,8 +549,12 @@ CommitTransactionHandler::_AddPackagesToActivate() BDirectory packagesDirectory; status_t error = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef()); - if (error != B_OK) - throw Exception(error, "failed to open packages directory"); + if (error != B_OK) { + ERROR("Failed to open packages directory: %s\n", strerror(error)); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1("") + .SetSystemError(error); + } int32 count = fPackagesToActivate.CountItems(); for (int32 i = 0; i < count; i++) { @@ -450,8 +572,12 @@ CommitTransactionHandler::_AddPackagesToActivate() BEntry entry; error = entry.SetTo(&entryRef); if (error != B_OK) { - throw Exception(error, "failed to get package entry", - package->FileName()); + ERROR("Failed to get package entry for %s: %s\n", + package->FileName().String(), strerror(error)); + throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH) + .SetPath1(package->FileName()) + .SetPackageName(package->FileName()) + .SetSystemError(error); } // move entry @@ -460,9 +586,15 @@ CommitTransactionHandler::_AddPackagesToActivate() error = entry.MoveTo(&packagesDirectory); if (error != B_OK) { fAddedPackages.erase(package); - throw Exception(error, - "failed to move new package to packages directory", - package->FileName()); + ERROR("Failed to move new package %s to packages directory: %s\n", + package->FileName().String(), strerror(error)); + throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) + .SetPath1( + _GetPath(FSUtils::Entry(entryRef), package->FileName())) + .SetPath2(_GetPath( + FSUtils::Entry(packagesDirectory), + "packages")) + .SetSystemError(error); } fPackageFileManager->PackageFileMoved(package->File(), @@ -480,6 +612,8 @@ CommitTransactionHandler::_AddPackagesToActivate() void CommitTransactionHandler::_PreparePackageToActivate(Package* package) { + fCurrentPackage = package; + // add groups const BStringList& groups = package->Info().Groups(); int32 count = groups.CountStrings(); @@ -493,6 +627,8 @@ CommitTransactionHandler::_PreparePackageToActivate(Package* package) // handle global writable files _AddGlobalWritableFiles(package); + + fCurrentPackage = NULL; } @@ -516,10 +652,10 @@ CommitTransactionHandler::_AddGroup(Package* package, const BString& groupName) if (system(commandLine.c_str()) != 0) { fAddedGroups.erase(groupName.String()); - throw Exception(error, - BString().SetToFormat("failed to add group \%s\"", - groupName.String()), - package->FileName()); + ERROR("Failed to add group \"%s\".\n", groupName.String()); + throw Exception(B_TRANSACTION_FAILED_TO_ADD_GROUP) + .SetPackageName(package->FileName()) + .SetString1(groupName); } } @@ -566,10 +702,11 @@ CommitTransactionHandler::_AddUser(Package* package, const BUser& user) if (system(commandLine.c_str()) != 0) { fAddedUsers.erase(user.Name().String()); - throw Exception(error, - BString().SetToFormat("failed to add user \%s\"", - user.Name().String()), - package->FileName()); + ERROR("Failed to add user \"%s\".\n", user.Name().String()); + throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER) + .SetPackageName(package->FileName()) + .SetString1(user.Name()); + } // add the supplementary groups @@ -582,11 +719,12 @@ CommitTransactionHandler::_AddUser(Package* package, const BUser& user) .String(); if (system(commandLine.c_str()) != 0) { fAddedUsers.erase(user.Name().String()); - throw Exception(error, - BString().SetToFormat("failed to add user \%s\" to group " - "\"%s\"", user.Name().String(), - user.Groups().StringAt(i).String()), - package->FileName()); + ERROR("Failed to add user \"%s\" to group \"%s\".\n", + user.Name().String(), user.Groups().StringAt(i).String()); + throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP) + .SetPackageName(package->FileName()) + .SetString1(user.Name()) + .SetString2(user.Groups().StringAt(i)); } } } @@ -613,23 +751,28 @@ CommitTransactionHandler::_AddGlobalWritableFiles(Package* package) BDirectory rootDirectory; status_t error = rootDirectory.SetTo(&fVolume->RootDirectoryRef()); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to get the root directory " - "for writable files"), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(fVolume->RootDirectoryRef()), + "")) + .SetSystemError(error); } // Open writable-files directory in the administrative directory. if (fWritableFilesDirectory.InitCheck() != B_OK) { - error = _OpenPackagesSubDirectory( - RelativePath(kAdminDirectoryName, kWritableFilesDirectoryName), - true, fWritableFilesDirectory); + RelativePath directoryPath(kAdminDirectoryName, + kWritableFilesDirectoryName); + error = _OpenPackagesSubDirectory(directoryPath, true, + fWritableFilesDirectory); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to get the backup directory " - "for writable files"), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(fVolume->PackagesDirectoryRef(), + directoryPath.ToString()), + directoryPath.ToString())) + .SetPackageName(package->FileName()) + .SetSystemError(error); } } @@ -679,13 +822,12 @@ CommitTransactionHandler::_AddGlobalWritableFile(Package* package, status_t error = stackSourceDirectory.SetTo( &extractedFilesDirectory, sourceParentPath); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to open directory \"%s\"", - _GetPath( - FSUtils::Entry(extractedFilesDirectory, - sourceParentPath), - sourceParentPath).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(extractedFilesDirectory, sourceParentPath), + sourceParentPath)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } } else { sourceDirectory = &extractedFilesDirectory; @@ -704,13 +846,12 @@ CommitTransactionHandler::_AddGlobalWritableFile(Package* package, status_t error = FSUtils::OpenSubDirectory(rootDirectory, RelativePath(targetParentPath), true, targetDirectory); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to open/create directory " - "\"%s\"", - _GetPath( - FSUtils::Entry(rootDirectory,targetParentPath), - targetParentPath).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(rootDirectory, targetParentPath), + targetParentPath)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } _AddGlobalWritableFileRecurse(package, *sourceDirectory, relativeSourcePath, targetDirectory, lastSlash + 1, @@ -752,13 +893,15 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK) copyOperation.Finished(); - throw Exception(error, - BString().SetToFormat("failed to copy entry \"%s\"", - _GetPath( - FSUtils::Entry(sourceDirectory, - relativeSourcePath.Leaf()), - relativeSourcePath).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(sourceDirectory, + relativeSourcePath.Leaf()), + relativeSourcePath)) + .SetPath2(_GetPath( + FSUtils::Entry(targetDirectory, targetName), + targetName)) + .SetSystemError(error); } copyOperation.Finished(); return; @@ -768,13 +911,12 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(), &sourceStat); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to get stat data for entry " - "\"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, targetName), - targetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) + .SetPath1(_GetPath( + FSUtils::Entry(sourceDirectory, + relativeSourcePath.Leaf()), + relativeSourcePath)) + .SetSystemError(error); } if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT) @@ -784,7 +926,11 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, // we cannot handle. The user must handle this manually. PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " "writable file exists, but type doesn't match previous type\n"); -// TODO: Notify user! + _AddIssue(TransactionIssueBuilder( + BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(sourceDirectory, + relativeSourcePath.Leaf()))); return; } @@ -794,24 +940,24 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, error = sourceSubDirectory.SetTo(&sourceDirectory, relativeSourcePath.Leaf()); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to open directory \"%s\"", - _GetPath( - FSUtils::Entry(sourceDirectory, - relativeSourcePath.Leaf()), - relativeSourcePath).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(sourceDirectory, + relativeSourcePath.Leaf()), + relativeSourcePath)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } BDirectory targetSubDirectory; error = targetSubDirectory.SetTo(&targetDirectory, targetName); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to open directory \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, targetName), - targetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, targetName), + targetName)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } entry_ref entry; @@ -834,9 +980,13 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, kPackageFileAttribute, &originalPackage) != B_OK) { // Can't determine the original package. The user must handle this // manually. -// TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD! PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " "failed to get SYS:PACKAGE attribute\n"); + if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE) + .SetPath1(FSUtils::Entry(targetDirectory, targetName))); + } return; } @@ -855,8 +1005,9 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, throw std::bad_alloc(); struct stat originalPackageStat; - if (fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath, - &originalPackageStat) != B_OK + error = fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath, + &originalPackageStat); + if (error != B_OK || (sourceStat.st_mode & S_IFMT) != (originalPackageStat.st_mode & S_IFMT)) { // Original entry doesn't exist (either we don't have the data from @@ -868,8 +1019,22 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, _GetPath(FSUtils::Entry(fWritableFilesDirectory, originalRelativeSourcePath), originalRelativeSourcePath).String()); + if (error != B_OK) { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue + ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(fWritableFilesDirectory, + originalRelativeSourcePath))); + } else { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue + ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(fWritableFilesDirectory, + originalRelativeSourcePath))); + } return; -// TODO: Notify user! } if (S_ISREG(sourceStat.st_mode)) { @@ -888,8 +1053,25 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, "_AddGlobalWritableFile(): " "file comparison failed (%s) or files aren't equal\n", strerror(error)); + if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { + if (error != B_OK) { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue + ::B_WRITABLE_FILE_COMPARISON_FAILED) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(fWritableFilesDirectory, + originalRelativeSourcePath)) + .SetSystemError(error)); + } else { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue + ::B_WRITABLE_FILE_NOT_EQUAL) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(fWritableFilesDirectory, + originalRelativeSourcePath))); + } + } return; -// TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD! } } else { // compare symlinks @@ -906,8 +1088,25 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, "_AddGlobalWritableFile(): " "symlink comparison failed (%s) or symlinks aren't equal\n", strerror(error)); + if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { + if (error != B_OK) { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue + ::B_WRITABLE_SYMLINK_COMPARISON_FAILED) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(fWritableFilesDirectory, + originalRelativeSourcePath)) + .SetSystemError(error)); + } else { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue + ::B_WRITABLE_SYMLINK_NOT_EQUAL) + .SetPath1(FSUtils::Entry(targetDirectory, targetName)) + .SetPath2(FSUtils::Entry(fWritableFilesDirectory, + originalRelativeSourcePath))); + } + } return; -// TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD! } } @@ -928,13 +1127,15 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), FSUtils::Entry(targetDirectory, tempTargetName)); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to copy entry \"%s\"", - _GetPath( - FSUtils::Entry(sourceDirectory, - relativeSourcePath.Leaf()), - relativeSourcePath).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(sourceDirectory, + relativeSourcePath.Leaf()), + relativeSourcePath)) + .SetPath2(_GetPath( + FSUtils::Entry(targetDirectory, tempTargetName), + tempTargetName)) + .SetSystemError(error); } copyOperation.Finished(); @@ -950,13 +1151,12 @@ CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, if (error == B_OK) error = targetEntry.Rename(targetName, true); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, tempTargetName), - tempTargetName).String(), - targetName), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, tempTargetName), + tempTargetName)) + .SetPath2(targetName) + .SetSystemError(error); } renameOperation.Finished(); @@ -1029,8 +1229,11 @@ CommitTransactionHandler::_RevertRemovePackagesToDeactivate() BDirectory packagesDirectory; status_t error = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef()); - if (error != B_OK) - throw Exception(error, "failed to open packages directory"); + if (error != B_OK) { + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1("") + .SetSystemError(error); + } for (PackageSet::iterator it = fRemovedPackages.begin(); it != fRemovedPackages.end(); ++it) { @@ -1095,11 +1298,14 @@ CommitTransactionHandler::_RunPostInstallScripts() for (PackageSet::iterator it = fAddedPackages.begin(); it != fAddedPackages.end(); ++it) { Package* package = *it; + fCurrentPackage = package; const BStringList& scripts = package->Info().PostInstallScripts(); int32 count = scripts.CountStrings(); for (int32 i = 0; i < count; i++) _RunPostInstallScript(package, scripts.StringAt(i)); } + + fCurrentPackage = NULL; } @@ -1115,16 +1321,32 @@ CommitTransactionHandler::_RunPostInstallScript(Package* package, "failed get path of post-installation script \"%s\" of package " "%s: %s\n", script.String(), package->FileName().String(), strerror(error)); -// TODO: Notify the user! + _AddIssue(TransactionIssueBuilder( + BTransactionIssue::B_POST_INSTALL_SCRIPT_NOT_FOUND) + .SetPath1(script) + .SetSystemError(error)); return; } - if (system(scriptPath.Path()) != 0) { + errno = 0; + int result = system(scriptPath.Path()); + if (result != 0) { ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): " "running post-installation script \"%s\" of package %s " - "failed: %s\n", script.String(), package->FileName().String(), - strerror(error)); -// TODO: Notify the user! + "failed: %d (errno: %s)\n", script.String(), + package->FileName().String(), result, + strerror(errno)); + if (result < 0 && errno != 0) { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue::B_POST_INSTALL_SCRIPT_FAILED) + .SetPath1(BString(scriptPath.Path())) + .SetSystemError(errno)); + } else { + _AddIssue(TransactionIssueBuilder( + BTransactionIssue::B_STARTING_POST_INSTALL_SCRIPT_FAILED) + .SetPath1(BString(scriptPath.Path())) + .SetExitCode(result)); + } } } @@ -1140,12 +1362,12 @@ CommitTransactionHandler::_ExtractPackageContent(Package* package, BEntry targetEntry; status_t error = targetEntry.SetTo(&targetDirectory, targetName); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to init entry \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, targetName), - targetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, targetName), + targetName)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } if (targetEntry.Exists()) { // nothing to do -- the very same version of the package has already @@ -1153,12 +1375,12 @@ CommitTransactionHandler::_ExtractPackageContent(Package* package, error = _extractedFilesDirectory.SetTo(&targetDirectory, targetName); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to open directory \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, targetName), - targetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, targetName), + targetName)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } return; } @@ -1172,25 +1394,24 @@ CommitTransactionHandler::_ExtractPackageContent(Package* package, error = targetEntry.SetTo(&targetDirectory, temporaryTargetName); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to init entry \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, temporaryTargetName), - temporaryTargetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, temporaryTargetName), + temporaryTargetName)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } if (targetEntry.Exists()) { // remove pre-existing error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry)); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to remove directory \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, - temporaryTargetName), - temporaryTargetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, temporaryTargetName), + temporaryTargetName)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } } @@ -1201,12 +1422,12 @@ CommitTransactionHandler::_ExtractPackageContent(Package* package, error = targetDirectory.CreateDirectory(temporaryTargetName, &subDirectory); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to create directory \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, temporaryTargetName), - temporaryTargetName).String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, temporaryTargetName), + temporaryTargetName)) + .SetPackageName(package->FileName()) + .SetSystemError(error); } createSubDirectoryOperation.Finished(); @@ -1221,35 +1442,26 @@ CommitTransactionHandler::_ExtractPackageContent(Package* package, error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef), contentPath, FSUtils::Entry(subDirectory)); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat( - "failed to extract \"%s\" from package", contentPath), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE) + .SetPath1(contentPath) + .SetPackageName(package->FileName()) + .SetSystemError(error); } } // tag all entries with the package attribute - error = _TagPackageEntriesRecursively(subDirectory, targetName, true); - if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to tag extract files in \"%s\" " - "with package attribute", - _GetPath( - FSUtils::Entry(targetDirectory, temporaryTargetName), - temporaryTargetName).String()), - package->FileName()); - } + _TagPackageEntriesRecursively(subDirectory, targetName, true); // rename the subdirectory error = targetEntry.Rename(targetName); if (error != B_OK) { - throw Exception(error, - BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"", - _GetPath( - FSUtils::Entry(targetDirectory, temporaryTargetName), - temporaryTargetName).String(), - targetName.String()), - package->FileName()); + throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(targetDirectory, temporaryTargetName), + temporaryTargetName)) + .SetPath2(targetName) + .SetPackageName(package->FileName()) + .SetSystemError(error); } // keep the directory, regardless of whether the transaction is rolled @@ -1312,7 +1524,7 @@ CommitTransactionHandler::_OpenPackagesFile( } -status_t +void CommitTransactionHandler::_WriteActivationFile( const RelativePath& directoryPath, const char* fileName, const PackageSet& toActivate, const PackageSet& toDeactivate, @@ -1320,26 +1532,24 @@ CommitTransactionHandler::_WriteActivationFile( { // create the content BString activationFileContent; - status_t error = _CreateActivationFileContent(toActivate, toDeactivate, + _CreateActivationFileContent(toActivate, toDeactivate, activationFileContent); - if (error != B_OK) - return error; // write the file - error = _WriteTextFile(directoryPath, fileName, activationFileContent, - _entry); + status_t error = _WriteTextFile(directoryPath, fileName, + activationFileContent, _entry); if (error != B_OK) { - ERROR("CommitTransactionHandler::_WriteActivationFile(): failed to " - "write activation file \"%s/%s\": %s\n", - directoryPath.ToString().String(), fileName, strerror(error)); - return error; + BString filePath = directoryPath.ToString() << '/' << fileName; + throw Exception(B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(fVolume->PackagesDirectoryRef(), filePath), + filePath)) + .SetSystemError(error); } - - return B_OK; } -status_t +void CommitTransactionHandler::_CreateActivationFileContent( const PackageSet& toActivate, const PackageSet& toDeactivate, BString& _content) @@ -1354,7 +1564,7 @@ CommitTransactionHandler::_CreateActivationFileContent( activationFileContent << package->FileName() << '\n'; if (activationFileContent.Length() < length + package->FileName().Length() + 1) { - return B_NO_MEMORY; + throw Exception(B_TRANSACTION_NO_MEMORY); } } } @@ -1366,12 +1576,11 @@ CommitTransactionHandler::_CreateActivationFileContent( activationFileContent << package->FileName() << '\n'; if (activationFileContent.Length() < length + package->FileName().Length() + 1) { - return B_NO_MEMORY; + throw Exception(B_TRANSACTION_NO_MEMORY); } } _content = activationFileContent; - return B_OK; } @@ -1413,11 +1622,9 @@ CommitTransactionHandler::_ChangePackageActivation( // write the temporary package activation file BEntry activationFileEntry; - status_t error = _WriteActivationFile(RelativePath(kAdminDirectoryName), + _WriteActivationFile(RelativePath(kAdminDirectoryName), kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate, activationFileEntry); - if (error != B_OK) - throw Exception(error, "failed to write activation file"); // notify packagefs if (fVolumeStateIsActive) { @@ -1428,10 +1635,15 @@ CommitTransactionHandler::_ChangePackageActivation( } // rename the temporary activation file to the final file - error = activationFileEntry.Rename(kActivationFileName, true); + status_t error = activationFileEntry.Rename(kActivationFileName, true); if (error != B_OK) { - throw Exception(error, - "failed to rename temporary activation file to final file"); + throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(activationFileEntry), + activationFileEntry.Name())) + .SetPath2(kActivationFileName) + .SetSystemError(error); + // TODO: We should probably try to revert the activation changes, though that // will fail, if this method has been called in response to node monitoring // events. Alternatively moving the package activation file could be made part @@ -1472,7 +1684,7 @@ CommitTransactionHandler::_ChangePackageActivationIOCtl( PackageFSActivationChangeRequest* request = (PackageFSActivationChangeRequest*)malloc(requestSize); if (request == NULL) - throw Exception(B_NO_MEMORY); + throw Exception(B_TRANSACTION_NO_MEMORY); MemoryDeleter requestDeleter(request); request->itemCount = itemCount; @@ -1494,14 +1706,20 @@ CommitTransactionHandler::_ChangePackageActivationIOCtl( // issue the request int fd = fVolume->OpenRootDirectory(); - if (fd < 0) - throw Exception(fd, "failed to open root directory"); + if (fd < 0) { + throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) + .SetPath1(_GetPath( + FSUtils::Entry(fVolume->RootDirectoryRef()), + "")) + .SetSystemError(fd); + } FileDescriptorCloser fdCloser(fd); if (ioctl(fd, PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, requestSize) != 0) { // TODO: We need more error information and error handling! - throw Exception(errno, "ioctl() to de-/activate packages failed"); + throw Exception(B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION) + .SetSystemError(errno); } } @@ -1541,6 +1759,13 @@ CommitTransactionHandler::_IsSystemPackage(Package* package) } +void +CommitTransactionHandler::_AddIssue(const TransactionIssueBuilder& builder) +{ + fResult.AddIssue(builder.BuildIssue(fCurrentPackage)); +} + + /*static*/ BString CommitTransactionHandler::_GetPath(const FSUtils::Entry& entry, const BString& fallback) @@ -1550,7 +1775,7 @@ CommitTransactionHandler::_GetPath(const FSUtils::Entry& entry, } -/*static*/ status_t +/*static*/ void CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory& directory, const BString& value, bool nonDirectoriesOnly) { @@ -1565,8 +1790,13 @@ CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory& directory, // determine type struct stat st; status_t error = directory.GetStatFor(entry->d_name, &st); - if (error != B_OK) - return error; + if (error != B_OK) { + throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) + .SetPath1(_GetPath( + FSUtils::Entry(directory, entry->d_name), + entry->d_name)) + .SetSystemError(error); + } bool isDirectory = S_ISDIR(st.st_mode); // open the node and set the attribute @@ -1581,23 +1811,31 @@ CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory& directory, error = stackNode.SetTo(&directory, entry->d_name); } - if (error != B_OK) - return error; + if (error != B_OK) { + throw Exception(isDirectory + ? B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY + : B_TRANSACTION_FAILED_TO_OPEN_FILE) + .SetPath1(_GetPath( + FSUtils::Entry(directory, entry->d_name), + entry->d_name)) + .SetSystemError(error); + } if (!isDirectory || !nonDirectoriesOnly) { error = node->WriteAttrString(kPackageFileAttribute, &value); - if (error != B_OK) - return error; + if (error != B_OK) { + throw Exception(B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE) + .SetPath1(_GetPath( + FSUtils::Entry(directory, entry->d_name), + entry->d_name)) + .SetSystemError(error); + } } // recurse if (isDirectory) { - error = _TagPackageEntriesRecursively(stackDirectory, value, + _TagPackageEntriesRecursively(stackDirectory, value, nonDirectoriesOnly); - if (error != B_OK) - return error; } } - - return B_OK; } diff --git a/src/servers/package/CommitTransactionHandler.h b/src/servers/package/CommitTransactionHandler.h index e16d18d11d..bf6a4b897d 100644 --- a/src/servers/package/CommitTransactionHandler.h +++ b/src/servers/package/CommitTransactionHandler.h @@ -22,10 +22,18 @@ typedef std::set StringSet; +namespace BPackageKit { + class BCommitTransactionResult; +} + +using BPackageKit::BCommitTransactionResult; + + class CommitTransactionHandler { public: CommitTransactionHandler(Volume* volume, - PackageFileManager* packageFileManager); + PackageFileManager* packageFileManager, + BCommitTransactionResult& result); ~CommitTransactionHandler(); void Init(VolumeState* volumeState, @@ -33,11 +41,9 @@ public: const PackageSet& packagesAlreadyAdded, const PackageSet& packagesAlreadyRemoved); - void HandleRequest(BMessage* request, - BMessage* reply); + void HandleRequest(BMessage* request); void HandleRequest( - const BActivationTransaction& transaction, - BMessage* reply); + const BActivationTransaction& transaction); void HandleRequest(); // uses packagesAlreadyAdded and // packagesAlreadyRemoved from Init() @@ -47,6 +53,9 @@ public: const BString& OldStateDirectoryName() const { return fOldStateDirectoryName; } + Package* CurrentPackage() const + { return fCurrentPackage; } + VolumeState* DetachVolumeState(); bool IsActiveVolumeState() const { return fVolumeStateIsActive; } @@ -55,13 +64,15 @@ private: typedef BObjectList PackageList; typedef FSUtils::RelativePath RelativePath; + struct TransactionIssueBuilder; + private: void _GetPackagesToDeactivate( const BActivationTransaction& transaction); void _ReadPackagesToActivate( const BActivationTransaction& transaction); - void _ApplyChanges(BMessage* reply); - void _CreateOldStateDirectory(BMessage* reply); + void _ApplyChanges(); + void _CreateOldStateDirectory(); void _RemovePackagesToDeactivate(); void _AddPackagesToActivate(); @@ -102,13 +113,13 @@ private: const char* fileName, uint32 openMode, BFile& _file, BEntry* _entry = NULL); - status_t _WriteActivationFile( + void _WriteActivationFile( const RelativePath& directoryPath, const char* fileName, const PackageSet& toActivate, const PackageSet& toDeactivate, BEntry& _entry); - status_t _CreateActivationFileContent( + void _CreateActivationFileContent( const PackageSet& toActivate, const PackageSet& toDeactivate, BString& _content); @@ -131,10 +142,13 @@ private: bool _IsSystemPackage(Package* package); + void _AddIssue( + const TransactionIssueBuilder& builder); + static BString _GetPath(const FSUtils::Entry& entry, const BString& fallback); - static status_t _TagPackageEntriesRecursively( + static void _TagPackageEntriesRecursively( BDirectory& directory, const BString& value, bool nonDirectoriesOnly); @@ -157,6 +171,8 @@ private: StringSet fAddedGroups; StringSet fAddedUsers; FSTransaction fFSTransaction; + BCommitTransactionResult& fResult; + Package* fCurrentPackage; }; diff --git a/src/servers/package/Exception.cpp b/src/servers/package/Exception.cpp index fd282d8025..d00b930a59 100644 --- a/src/servers/package/Exception.cpp +++ b/src/servers/package/Exception.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -7,57 +7,77 @@ #include "Exception.h" -using namespace BPackageKit::BPrivate; +using namespace BPackageKit; -Exception::Exception(int32 error, const char* errorMessage, - const char* packageName) +Exception::Exception(BTransactionError error) : fError(error), - fErrorMessage(errorMessage), - fPackageName(packageName) + fSystemError(B_ERROR), + fPackageName(), + fPath1(), + fPath2(), + fString1(), + fString2() { } - -BString -Exception::ToString() const +Exception& +Exception::SetSystemError(status_t error) { - const char* error; - if (fError >= 0) { - switch (fError) { - case B_DAEMON_OK: - error = "no error"; - break; - case B_DAEMON_CHANGE_COUNT_MISMATCH: - error = "transaction out of date"; - break; - case B_DAEMON_BAD_REQUEST: - error = "invalid transaction"; - break; - case B_DAEMON_NO_SUCH_PACKAGE: - error = "no such package"; - break; - case B_DAEMON_PACKAGE_ALREADY_EXISTS: - error = "package already exists"; - break; - default: - error = "unknown error"; - break; - } - } else - error = strerror(fError); - - BString string; - if (!fErrorMessage.IsEmpty()) { - string = fErrorMessage; - string << ": "; - } - - string << error; - - if (!fPackageName.IsEmpty()) - string << ", package: \"" << fPackageName << '"'; - - return string; + fSystemError = error; + return *this; +} + + +Exception& +Exception::SetPackageName(const BString& packageName) +{ + fPackageName = packageName; + return *this; +} + + +Exception& +Exception::SetPath1(const BString& path) +{ + fPath1 = path; + return *this; +} + + +Exception& +Exception::SetPath2(const BString& path) +{ + fPath2 = path; + return *this; +} + + +Exception& +Exception::SetString1(const BString& string) +{ + fString1 = string; + return *this; +} + + +Exception& +Exception::SetString2(const BString& string) +{ + fString2 = string; + return *this; +} + + +void +Exception::SetOnResult(BCommitTransactionResult& result) +{ + result.SetError(fError); + result.SetSystemError(fSystemError); + result.SetErrorPackage(fPackageName); + result.SetPath1(fPath1); + result.SetPath2(fPath2); + result.SetString1(fString1); + result.SetString2(fString2); } diff --git a/src/servers/package/Exception.h b/src/servers/package/Exception.h index dc5f5f84db..2e41c1d9c2 100644 --- a/src/servers/package/Exception.h +++ b/src/servers/package/Exception.h @@ -1,5 +1,5 @@ /* - * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef EXCEPTION_H @@ -8,30 +8,54 @@ #include -#include +#include + + +using BPackageKit::BCommitTransactionResult; +using BPackageKit::BTransactionError; class Exception { public: - Exception(int32 error, - const char* errorMessage = NULL, - const char* packageName = NULL); + Exception(BTransactionError error); - int32 Error() const + BTransactionError Error() const { return fError; } - const BString& ErrorMessage() const - { return fErrorMessage; } + status_t SystemError() const + { return fSystemError; } + Exception& SetSystemError(status_t error); const BString& PackageName() const { return fPackageName; } + Exception& SetPackageName(const BString& packageName); - BString ToString() const; + const BString& Path1() const + { return fPath1; } + Exception& SetPath1(const BString& path); + + const BString& Path2() const + { return fPath2; } + Exception& SetPath2(const BString& path); + + const BString& String1() const + { return fString1; } + Exception& SetString1(const BString& string); + + const BString& String2() const + { return fString2; } + Exception& SetString2(const BString& string); + + void SetOnResult(BCommitTransactionResult& result); private: - int32 fError; - BString fErrorMessage; + BTransactionError fError; + status_t fSystemError; BString fPackageName; + BString fPath1; + BString fPath2; + BString fString1; + BString fString2; }; diff --git a/src/servers/package/FSTransaction.cpp b/src/servers/package/FSTransaction.cpp index dfba68dcda..4d2acbab36 100644 --- a/src/servers/package/FSTransaction.cpp +++ b/src/servers/package/FSTransaction.cpp @@ -7,6 +7,7 @@ #include "FSTransaction.h" #include +#include #include #include @@ -197,9 +198,9 @@ FSTransaction::RemoveOperationAt(int32 index) { int32 count = fOperations.size(); if (index < 0 || index >= count) { - throw Exception(B_ERROR, - BString().SetToFormat("FSTransaction::RemoveOperationAt(): invalid " - "operation index %" B_PRId32 "/%" B_PRId32, index, count)); + ERROR("FSTransaction::RemoveOperationAt(): invalid " + "operation index %" B_PRId32 "/%" B_PRId32, index, count); + throw Exception(BPackageKit::B_TRANSACTION_INTERNAL_ERROR); } fOperations.erase(fOperations.begin() + index); @@ -225,8 +226,14 @@ FSTransaction::_GetPath(const Entry& entry) error = pathBuffer.SetTo(path); } - if (error != B_OK) - throw Exception(error); + if (error != B_OK) { + if (error == B_NO_MEMORY) + throw Exception(BPackageKit::B_TRANSACTION_NO_MEMORY); + + throw Exception(BPackageKit::B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH) + .SetPath1(entry.PathOrName()) + .SetSystemError(error); + } return path; } diff --git a/src/servers/package/PackageManager.cpp b/src/servers/package/PackageManager.cpp index 97deb2cf7e..bf5a4b2373 100644 --- a/src/servers/package/PackageManager.cpp +++ b/src/servers/package/PackageManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -209,7 +209,7 @@ PackageManager::PrepareTransaction(Transaction& transaction) status_t PackageManager::CommitTransaction(Transaction& transaction, - BDaemonClient::BCommitTransactionResult& _result) + BCommitTransactionResult& _result) { Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); if (volume == NULL) diff --git a/src/servers/package/PackageManager.h b/src/servers/package/PackageManager.h index 440cefb653..76d2340652 100644 --- a/src/servers/package/PackageManager.h +++ b/src/servers/package/PackageManager.h @@ -1,5 +1,5 @@ /* - * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef PACKAGE_MANAGER_H @@ -16,6 +16,7 @@ #include +using BPackageKit::BCommitTransactionResult; using BPackageKit::BContext; using BPackageKit::BJob; using BPackageKit::BJobStateListener; @@ -51,8 +52,7 @@ private: virtual status_t PrepareTransaction(Transaction& transaction); virtual status_t CommitTransaction(Transaction& transaction, - BDaemonClient::BCommitTransactionResult& - _result); + BCommitTransactionResult& _result); private: // UserInteractionHandler diff --git a/src/servers/package/Root.cpp b/src/servers/package/Root.cpp index 426b4a0be0..ddab4d5b95 100644 --- a/src/servers/package/Root.cpp +++ b/src/servers/package/Root.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -330,8 +331,9 @@ Root::HandleRequest(BMessage* message) // pending already. if (volume->IsPackageJobPending()) { BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); - if (reply.AddInt32("error", B_DAEMON_INSTALLATION_LOCATION_BUSY) - == B_OK) { + BCommitTransactionResult result( + B_TRANSACTION_INSTALLATION_LOCATION_BUSY); + if (result.AddToMessage(reply) == B_OK) { message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); } diff --git a/src/servers/package/Volume.cpp b/src/servers/package/Volume.cpp index 9f3486381b..acec55b35b 100644 --- a/src/servers/package/Volume.cpp +++ b/src/servers/package/Volume.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -519,39 +520,18 @@ Volume::HandleGetLocationInfoRequest(BMessage* message) void Volume::HandleCommitTransactionRequest(BMessage* message) { - // Prepare the reply in so far that we can at least set the error code - // without risk of failure. + BCommitTransactionResult result; + PackageSet dummy; + _CommitTransaction(message, NULL, dummy, dummy, result); + BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); - if (reply.AddInt32("error", B_ERROR) != B_OK) + status_t error = result.AddToMessage(reply); + if (error != B_OK) { + ERROR("Volume::HandleCommitTransactionRequest(): Failed to add " + "transaction result to reply: %s\n", strerror(error)); return; - - // perform the request - CommitTransactionHandler handler(this, fPackageFileManager); - int32 error; - try { - PackageSet dummy; - handler.Init(fLatestState, fLatestState == fActiveState, dummy, dummy); - handler.HandleRequest(message, &reply); - _SetLatestState(handler.DetachVolumeState(), - handler.IsActiveVolumeState()); - error = B_DAEMON_OK; - } catch (Exception& exception) { - error = exception.Error(); - - if (!exception.ErrorMessage().IsEmpty()) - reply.AddString("error message", exception.ErrorMessage()); - if (!exception.PackageName().IsEmpty()) - reply.AddString("error package", exception.PackageName()); - } catch (std::bad_alloc& exception) { - error = B_NO_MEMORY; } - // revert on error - if (error != B_DAEMON_OK) - handler.Revert(); - - // send the reply - reply.ReplaceInt32("error", error); message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); } @@ -709,31 +689,16 @@ Volume::ProcessPendingPackageActivationChanges() return; // perform the request - CommitTransactionHandler handler(this, fPackageFileManager); - int32 error; - try { - handler.Init(fLatestState, fLatestState == fActiveState, - fPackagesToBeActivated, fPackagesToBeDeactivated); - handler.HandleRequest(); - _SetLatestState(handler.DetachVolumeState(), - handler.IsActiveVolumeState()); - error = B_DAEMON_OK; - } catch (Exception& exception) { - error = exception.Error(); + BCommitTransactionResult result; + _CommitTransaction(NULL, NULL, fPackagesToBeActivated, + fPackagesToBeDeactivated, result); + + if (result.Error() != B_TRANSACTION_OK) { ERROR("Volume::ProcessPendingPackageActivationChanges(): package " - "activation failed: %s\n", exception.ToString().String()); -// TODO: Notify the user! - } catch (std::bad_alloc& exception) { - error = B_NO_MEMORY; - ERROR("Volume::ProcessPendingPackageActivationChanges(): package " - "activation failed: out of memory\n"); + "activation failed: %s\n", result.FullErrorMessage().String()); // TODO: Notify the user! } - // revert on error - if (error != B_DAEMON_OK) - handler.Revert(); - // clear the activation/deactivation sets in any event fPackagesToBeActivated.clear(); fPackagesToBeDeactivated.clear(); @@ -793,33 +758,10 @@ Volume::CreateTransaction(BPackageInstallationLocation location, void Volume::CommitTransaction(const BActivationTransaction& transaction, const PackageSet& packagesAlreadyAdded, - const PackageSet& packagesAlreadyRemoved, - BDaemonClient::BCommitTransactionResult& _result) + const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) { - // perform the request - CommitTransactionHandler handler(this, fPackageFileManager); - int32 error; - try { - handler.Init(fLatestState, fLatestState == fActiveState, - packagesAlreadyAdded, packagesAlreadyRemoved); - handler.HandleRequest(transaction, NULL); - _SetLatestState(handler.DetachVolumeState(), - handler.IsActiveVolumeState()); - error = B_DAEMON_OK; - _result.SetTo(error, BString(), BString(), - handler.OldStateDirectoryName()); - } catch (Exception& exception) { - error = exception.Error(); - _result.SetTo(error, exception.ErrorMessage(), exception.PackageName(), - BString()); - } catch (std::bad_alloc& exception) { - error = B_NO_MEMORY; - _result.SetTo(error, BString(), BString(), BString()); - } - - // revert on error - if (error != B_DAEMON_OK) - handler.Revert(); + _CommitTransaction(NULL, &transaction, packagesAlreadyAdded, + packagesAlreadyRemoved, _result); } @@ -1325,3 +1267,47 @@ Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, return FSUtils::OpenSubDirectory(directory, path, create, _directory); } + + +void +Volume::_CommitTransaction(BMessage* message, + const BActivationTransaction* transaction, + const PackageSet& packagesAlreadyAdded, + const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) +{ + _result.Unset(); + + // perform the request + CommitTransactionHandler handler(this, fPackageFileManager, _result); + BTransactionError error = B_TRANSACTION_INTERNAL_ERROR; + try { + handler.Init(fLatestState, fLatestState == fActiveState, + packagesAlreadyAdded, packagesAlreadyRemoved); + + if (message != NULL) + handler.HandleRequest(message); + else if (transaction != NULL) + handler.HandleRequest(*transaction); + else + handler.HandleRequest(); + + _SetLatestState(handler.DetachVolumeState(), + handler.IsActiveVolumeState()); + error = B_TRANSACTION_OK; + } catch (Exception& exception) { + error = exception.Error(); + exception.SetOnResult(_result); + if (_result.ErrorPackage().IsEmpty() + && handler.CurrentPackage() != NULL) { + _result.SetErrorPackage(handler.CurrentPackage()->FileName()); + } + } catch (std::bad_alloc& exception) { + error = B_TRANSACTION_NO_MEMORY; + } + + _result.SetError(B_TRANSACTION_OK); + + // revert on error + if (error != B_TRANSACTION_OK) + handler.Revert(); +} diff --git a/src/servers/package/Volume.h b/src/servers/package/Volume.h index a76cd8b50c..f083860f9b 100644 --- a/src/servers/package/Volume.h +++ b/src/servers/package/Volume.h @@ -137,8 +137,7 @@ public: const BActivationTransaction& transaction, const PackageSet& packagesAlreadyAdded, const PackageSet& packagesAlreadyRemoved, - BDaemonClient::BCommitTransactionResult& - _result); + BCommitTransactionResult& _result); private: struct NodeMonitorEvent; @@ -175,6 +174,12 @@ private: const RelativePath& path, bool create, BDirectory& _directory); + void _CommitTransaction(BMessage* message, + const BActivationTransaction* transaction, + const PackageSet& packagesAlreadyAdded, + const PackageSet& packagesAlreadyRemoved, + BCommitTransactionResult& _result); + private: BString fPath; PackageFSMountType fMountType;