From eee120f9871cf6f4a1251be97a6a0dc3a4d91a87 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 20 Apr 2013 21:31:32 +0200 Subject: [PATCH] pkgman install: Complete basic functionality Download package files to install from the respective repositories and use BDaemonClient to perform the package de-/activation. Still missing is the interactive problem solution support. --- src/bin/pkgman/command_install.cpp | 169 ++++++++++++++++++++++++++--- 1 file changed, 154 insertions(+), 15 deletions(-) diff --git a/src/bin/pkgman/command_install.cpp b/src/bin/pkgman/command_install.cpp index c62a6ffd1e..743f19e978 100644 --- a/src/bin/pkgman/command_install.cpp +++ b/src/bin/pkgman/command_install.cpp @@ -11,8 +11,9 @@ #include #include #include -//#include +#include +#include #include #include #include @@ -22,8 +23,12 @@ #include #include +#include +#include #include "Command.h" +#include "DecisionProvider.h" +#include "JobStateListener.h" #include "pkgman.h" #include "RepositoryBuilder.h" @@ -32,6 +37,7 @@ using namespace BPackageKit; +using namespace BPackageKit::BPrivate; static const char* const kShortUsage = @@ -52,6 +58,28 @@ static const char* const kLongUsage = DEFINE_COMMAND(InstallCommand, "install", kShortUsage, kLongUsage) +struct Repository : public BSolverRepository { + Repository() + : + BSolverRepository() + { + } + + status_t Init(BPackageRoster& roster, const char* name) + { + return roster.GetRepositoryConfig(name, &fConfig); + } + + const BRepositoryConfig& Config() const + { + return fConfig; + } + +private: + BRepositoryConfig fConfig; +}; + + int InstallCommand::Execute(int argc, const char* const* argv) { @@ -121,16 +149,25 @@ InstallCommand::Execute(int argc, const char* const* argv) .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_COMMON, "common") .AddToSolver(solver, !installInHome); + BObjectList installedRepositories(10); + if (!installedRepositories.AddItem(&systemRepository) + || !installedRepositories.AddItem(&commonRepository)) { + DIE(B_NO_MEMORY, "failed to add installed repositories to list"); + } + BSolverRepository homeRepository; if (installInHome) { commonRepository.SetPriority(-2); RepositoryBuilder(homeRepository, "home") .AddPackages(B_PACKAGE_INSTALLATION_LOCATION_HOME, "home") .AddToSolver(solver, true); + + if (!installedRepositories.AddItem(&homeRepository)) + DIE(B_NO_MEMORY, "failed to add home repository to list"); } // other repositories - BObjectList otherRepositories(10, true); + BObjectList otherRepositories(10, true); BPackageRoster roster; BStringList repositoryNames; error = roster.GetRepositoryNames(repositoryNames); @@ -139,20 +176,20 @@ InstallCommand::Execute(int argc, const char* const* argv) int32 repositoryNameCount = repositoryNames.CountStrings(); for (int32 i = 0; i < repositoryNameCount; i++) { - const BString& name = repositoryNames.StringAt(i); - BRepositoryConfig config; - error = roster.GetRepositoryConfig(name, &config); - if (error != B_OK) { - WARN(error, "failed to get config for repository \"%s\". Skipping.", - name.String()); - continue; - } - - BSolverRepository* repository = new(std::nothrow) BSolverRepository; + Repository* repository = new(std::nothrow) Repository; if (repository == NULL || !otherRepositories.AddItem(repository)) DIE(B_NO_MEMORY, "out of memory"); - RepositoryBuilder(*repository, config) + const BString& name = repositoryNames.StringAt(i); + error = repository->Init(roster, name); + if (error != B_OK) { + WARN(error, "failed to get config for repository \"%s\". Skipping.", + name.String()); + otherRepositories.RemoveItem(repository, true); + continue; + } + + RepositoryBuilder(*repository, repository->Config()) .AddToSolver(solver, false); } @@ -205,22 +242,124 @@ exit(1); if (error != B_OK) DIE(error, "failed to compute packages to install"); - printf("transaction:\n"); + printf("The following changes will be made:\n"); for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); i++) { BSolverPackage* package = element->Package(); + switch (element->Type()) { case BSolverResultElement::B_TYPE_INSTALL: + if (installedRepositories.HasItem(package->Repository())) + continue; + printf(" install package %s from repository %s\n", - package->VersionedName().String(), + package->Info().CanonicalFileName().String(), package->Repository()->Name().String()); break; + case BSolverResultElement::B_TYPE_UNINSTALL: printf(" uninstall package %s\n", package->VersionedName().String()); break; } } +// TODO: Print file/download sizes. Unfortunately our package infos don't +// contain the file size. Which is probably correct. The file size (and possibly +// other information) should, however, be provided by the repository cache in +// some way. Extend BPackageInfo? Create a BPackageFileInfo? + + DecisionProvider decisionProvider; + if (!decisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n", + "y")) { + return 1; + } + + // create an activation transaction + BDaemonClient daemonClient; + BPackageInstallationLocation location = installInHome + ? B_PACKAGE_INSTALLATION_LOCATION_HOME + : B_PACKAGE_INSTALLATION_LOCATION_COMMON; + BActivationTransaction transaction; + BDirectory transactionDirectory; + error = daemonClient.CreateTransaction(location, transaction, + transactionDirectory); + if (error != B_OK) + DIE(error, "failed to create transaction"); + + // download the new packages and prepare the transaction + JobStateListener listener; + BContext context(decisionProvider, listener); + + for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); + i++) { + BSolverPackage* package = element->Package(); + + switch (element->Type()) { + case BSolverResultElement::B_TYPE_INSTALL: + { + if (installedRepositories.HasItem(package->Repository())) + continue; + + // get package URL and target entry + Repository* repository + = static_cast(package->Repository()); + BString url = repository->Config().BaseURL(); + BString fileName(package->Info().CanonicalFileName()); + if (fileName.IsEmpty()) + DIE(B_NO_MEMORY, "out of memory"); + url << '/' << fileName; + + BEntry entry; + error = entry.SetTo(&transactionDirectory, fileName); + if (error != B_OK) + DIE(error, "failed to create package entry"); + + // download the package + DownloadFileRequest downloadRequest(context, url, entry, + package->Info().Checksum()); + error = downloadRequest.Process(); + if (error != B_OK) + DIE(error, "failed to download package"); + + // add package to transaction + if (!transaction.AddPackageToActivate( + package->Info().CanonicalFileName())) { + DIE(B_NO_MEMORY, + "failed to add package to activate to transaction"); + } + break; + } + + case BSolverResultElement::B_TYPE_UNINSTALL: + // add package to transaction + if (!transaction.AddPackageToDeactivate( + package->Info().CanonicalFileName())) { + DIE(B_NO_MEMORY, + "failed to add package to deactivate to transaction"); + } + break; + } + } + + // commit the transaction + BDaemonClient::BCommitTransactionResult transactionResult; + error = daemonClient.CommitTransaction(transaction, transactionResult); + if (error != B_OK) { + fprintf(stderr, "*** failed to commit transaction: %s\n", + transactionResult.FullErrorMessage().String()); + exit(1); + } + + printf("Installation done. Old activation state backed up in \"%s\"\n", + transactionResult.OldStateDirectory().String()); + + printf("Cleaning up ...\n"); + BEntry transactionDirectoryEntry; + if ((error = transactionDirectory.GetEntry(&transactionDirectoryEntry)) + != B_OK + || (error = transactionDirectoryEntry.Remove()) != B_OK) { + WARN(error, "failed to remove transaction directory"); + } return 0; }