From 4dd45837e9a6c8bb8f8d75058dab0a3c1d77630e Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Mon, 27 Apr 2026 16:29:33 -0400 Subject: [PATCH] Package Kit & pkgman: Split cleanup operations into a "cleanup" command. And only inform that some can be deleted at the end of install/uninstall. As discussed in https://review.haiku-os.org/c/haiku/+/10711. --- .../os/package/CleanUpAdminDirectoryRequest.h | 1 + src/bin/pkgman/Jamfile | 1 + src/bin/pkgman/PackageManager.cpp | 18 +-- src/bin/pkgman/command_cleanup.cpp | 110 ++++++++++++++++++ src/bin/pkgman/pkgman.h | 4 + .../package/CleanUpAdminDirectoryRequest.cpp | 22 ++++ 6 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 src/bin/pkgman/command_cleanup.cpp diff --git a/headers/os/package/CleanUpAdminDirectoryRequest.h b/headers/os/package/CleanUpAdminDirectoryRequest.h index 925e8611b6..2792988344 100644 --- a/headers/os/package/CleanUpAdminDirectoryRequest.h +++ b/headers/os/package/CleanUpAdminDirectoryRequest.h @@ -25,6 +25,7 @@ public: time_t cleanupBefore, int32 minStatesToKeep); virtual ~CleanUpAdminDirectoryRequest(); + status_t GetOldStatesCount(size_t& count); virtual status_t CreateInitialJobs(); private: diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile index cc66f9bf1c..be1d6ecfe8 100644 --- a/src/bin/pkgman/Jamfile +++ b/src/bin/pkgman/Jamfile @@ -5,6 +5,7 @@ UsePrivateHeaders shared storage support ; Application pkgman : Command.cpp command_add_repo.cpp + command_cleanup.cpp command_drop_repo.cpp command_full_sync.cpp command_info.cpp diff --git a/src/bin/pkgman/PackageManager.cpp b/src/bin/pkgman/PackageManager.cpp index 300963054f..114a86f8d0 100644 --- a/src/bin/pkgman/PackageManager.cpp +++ b/src/bin/pkgman/PackageManager.cpp @@ -29,7 +29,6 @@ #include #include "pkgman.h" -#include "JobStateListener.h" using namespace BPackageKit::BPrivate; @@ -336,15 +335,16 @@ PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) BInstallationLocationInfo info; if (BPackageRoster().GetInstallationLocationInfo(repository.Location(), info) == B_OK) { - // Offer to delete older state and transaction directories. + time_t before = time(NULL) - kCleanUpKeepDays * 24 * 60 * 60; BJobStateListener listener; - BContext context(fDecisionProvider, listener); - - const int days = 30; - time_t before = time(NULL) - days * 24 * 60 * 60; - - CleanUpAdminDirectoryRequest request(context, info, before, 10); - request.Process(); + CleanUpAdminDirectoryRequest request(BContext(fDecisionProvider, listener), + info, before, kCleanUpKeepStates); + size_t count; + if (request.GetOldStatesCount(count) == B_OK && count > 0) { + printf("[%s] %" B_PRIuSIZE " old state(s) can be cleaned up. " + "Use \"pkgman cleanup\" to remove them.\n", + repository.Name().String(), count); + } } if (BPackageRoster().IsRebootNeeded()) diff --git a/src/bin/pkgman/command_cleanup.cpp b/src/bin/pkgman/command_cleanup.cpp new file mode 100644 index 0000000000..9cade3f8ad --- /dev/null +++ b/src/bin/pkgman/command_cleanup.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2013-2025, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + * Humdinger + * Adrien Destugues + */ + + +#include +#include +#include + +#include +#include +#include + +#include "Command.h" +#include "DecisionProvider.h" +#include "pkgman.h" + + +// TODO: internationalization! + + +using namespace BPackageKit; +using namespace BPackageKit::BPrivate; + + +static const char* const kShortUsage = + " %command% [-H]\n" + " Cleans up old states and transactions in the administrative directory.\n"; + +static const char* const kLongUsage = + "Usage: %program% %command% [-H]\n" + "\n" + "Cleans up old states and transactions in the administrative directory.\n" + "\n" + "Options:\n" + " -H, --home\n" + " Clean up the administrative directoryin the user's home directory.\n" + " Default is to clean up the system directory.\n" + " -y\n" + " Non-interactive mode. Automatically confirm changes, but fail when\n" + " encountering problems.\n" + ; + + +DEFINE_COMMAND(CleanupCommand, "cleanup", kShortUsage, kLongUsage, + COMMAND_CATEGORY_OTHER) + + +int +CleanupCommand::Execute(int argc, const char* const* argv) +{ + BPackageInstallationLocation location + = B_PACKAGE_INSTALLATION_LOCATION_SYSTEM; + bool interactive = true; + + while (true) { + static struct option sLongOptions[] = { + { "debug", required_argument, 0, OPTION_DEBUG }, + { "help", no_argument, 0, 'h' }, + { "home", no_argument, 0, 'H' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "hHy", sLongOptions, NULL); + if (c == -1) + break; + + if (fCommonOptions.HandleOption(c)) + continue; + + switch (c) { + case 'h': + PrintUsageAndExit(false); + break; + + case 'H': + location = B_PACKAGE_INSTALLATION_LOCATION_HOME; + break; + + case 'y': + interactive = false; + break; + + default: + PrintUsageAndExit(true); + break; + } + } + + BInstallationLocationInfo info; + status_t status = BPackageRoster().GetInstallationLocationInfo(location, info); + if (status == B_OK) { + DecisionProvider decisionProvider(interactive); + BSupportKit::BJobStateListener listener; + BContext context(decisionProvider, listener); + + time_t before = time(NULL) - kCleanUpKeepDays * 24 * 60 * 60; + CleanUpAdminDirectoryRequest request(context, info, before, kCleanUpKeepStates); + status = request.Process(); + } + + return status; +} diff --git a/src/bin/pkgman/pkgman.h b/src/bin/pkgman/pkgman.h index 1019a187f8..b3af505822 100644 --- a/src/bin/pkgman/pkgman.h +++ b/src/bin/pkgman/pkgman.h @@ -43,4 +43,8 @@ void print_usage_and_exit(bool error); #define COMMAND_CATEGORY_OTHER "other" +static const size_t kCleanUpKeepDays = 30; +static const size_t kCleanUpKeepStates = 10; + + #endif // PKGMAN_H diff --git a/src/kits/package/CleanUpAdminDirectoryRequest.cpp b/src/kits/package/CleanUpAdminDirectoryRequest.cpp index 995bcdd2c0..fb93997caf 100644 --- a/src/kits/package/CleanUpAdminDirectoryRequest.cpp +++ b/src/kits/package/CleanUpAdminDirectoryRequest.cpp @@ -39,6 +39,8 @@ protected: virtual status_t Execute(); private: + friend class CleanUpAdminDirectoryRequest; + status_t _GetOldStateDirectories(BStringList& directories); private: @@ -191,6 +193,26 @@ CleanUpAdminDirectoryRequest::~CleanUpAdminDirectoryRequest() } +status_t +CleanUpAdminDirectoryRequest::GetOldStatesCount(size_t& count) +{ + status_t status = InitCheck(); + if (status != B_OK) + return B_NO_INIT; + + CleanUpAdminDirectoryJob temp(fContext, "", + fLocationInfo, fCleanupBefore, fMinimumStatesToKeep); + + BStringList dirs; + status = temp._GetOldStateDirectories(dirs); + if (status != B_OK) + return status; + + count = dirs.CountStrings(); + return B_OK; +} + + status_t CleanUpAdminDirectoryRequest::CreateInitialJobs() {