Package Kit, pkgman, SoftwareUpdater: Offer to delete old state directories.
This adds a new job to the Package Kit, and an invocation in pkgman after changes are applied. The job takes a time_t before, and an int32 minToKeep, and (after confirmation) deletes all state directories which were last modified before that time, but also keeps at least the minimum specified. pkgman defaults to calling it with (now - 30 days, minimum 10). SoftwareUpdater does the same, but without bothering the user and just defaults to doing the cleanup. Change-Id: I15f5232b11daba5955e7fa07f696ad7785507931 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10404 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
79ab5081b2
commit
5f0f0b02b4
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026, Haiku, Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _PACKAGE__CLEAN_UP_ADMIN_DIR_REQUEST_H_
|
||||||
|
#define _PACKAGE__CLEAN_UP_ADMIN_DIR_REQUEST_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
#include <package/Context.h>
|
||||||
|
#include <package/Request.h>
|
||||||
|
#include <package/InstallationLocationInfo.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace BPackageKit {
|
||||||
|
|
||||||
|
|
||||||
|
class CleanUpAdminDirectoryRequest : public BRequest {
|
||||||
|
typedef BRequest inherited;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CleanUpAdminDirectoryRequest(const BContext& context,
|
||||||
|
const BInstallationLocationInfo& location,
|
||||||
|
time_t cleanupBefore, int32 minStatesToKeep);
|
||||||
|
virtual ~CleanUpAdminDirectoryRequest();
|
||||||
|
|
||||||
|
virtual status_t CreateInitialJobs();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BInstallationLocationInfo fLocationInfo;
|
||||||
|
time_t fCleanupBefore;
|
||||||
|
int32 fMinimumStatesToKeep;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace BPackageKit
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _PACKAGE__CLEAN_UP_ADMIN_DIR_REQUEST_H_
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <package/CleanUpAdminDirectoryRequest.h>
|
||||||
#include <package/CommitTransactionResult.h>
|
#include <package/CommitTransactionResult.h>
|
||||||
#include <package/DownloadFileRequest.h>
|
#include <package/DownloadFileRequest.h>
|
||||||
#include <package/RefreshRepositoryRequest.h>
|
#include <package/RefreshRepositoryRequest.h>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include <package/solver/SolverProblemSolution.h>
|
#include <package/solver/SolverProblemSolution.h>
|
||||||
|
|
||||||
#include "pkgman.h"
|
#include "pkgman.h"
|
||||||
|
#include "JobStateListener.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace BPackageKit::BPrivate;
|
using namespace BPackageKit::BPrivate;
|
||||||
@@ -332,6 +334,19 @@ PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository)
|
|||||||
{
|
{
|
||||||
printf("[%s] Done.\n", repository.Name().String());
|
printf("[%s] Done.\n", repository.Name().String());
|
||||||
|
|
||||||
|
BInstallationLocationInfo info;
|
||||||
|
if (BPackageRoster().GetInstallationLocationInfo(repository.Location(), info) == B_OK) {
|
||||||
|
// Offer to delete older state and transaction directories.
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
if (BPackageRoster().IsRebootNeeded())
|
if (BPackageRoster().IsRebootNeeded())
|
||||||
printf("A reboot is necessary to complete the installation process.\n");
|
printf("A reboot is necessary to complete the installation process.\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026, Haiku, Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <package/CleanUpAdminDirectoryRequest.h>
|
||||||
|
|
||||||
|
#include <Directory.h>
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <JobQueue.h>
|
||||||
|
#include <Path.h>
|
||||||
|
#include <RemoveEngine.h>
|
||||||
|
#include <StringList.h>
|
||||||
|
#include <StringFormat.h>
|
||||||
|
|
||||||
|
#include <StringForSize.h>
|
||||||
|
|
||||||
|
#include <package/PackagesDirectoryDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace BPackageKit {
|
||||||
|
|
||||||
|
|
||||||
|
using namespace BPrivate;
|
||||||
|
|
||||||
|
|
||||||
|
class CleanUpAdminDirectoryJob : public BJob {
|
||||||
|
typedef BJob inherited;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CleanUpAdminDirectoryJob(
|
||||||
|
const BContext& context, const BString& title,
|
||||||
|
const BInstallationLocationInfo& location,
|
||||||
|
time_t cleanupBefore, int32 minStatesToKeep);
|
||||||
|
virtual ~CleanUpAdminDirectoryJob();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual status_t Execute();
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _GetOldStateDirectories(BStringList& directories);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BInstallationLocationInfo& fLocationInfo;
|
||||||
|
time_t fCleanupBefore;
|
||||||
|
int32 fMinimumStatesToKeep;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CleanUpAdminDirectoryJob::CleanUpAdminDirectoryJob(const BContext& context,
|
||||||
|
const BString& title, const BInstallationLocationInfo& location,
|
||||||
|
time_t cleanupBefore, int32 minStatesToKeep)
|
||||||
|
:
|
||||||
|
inherited(context, title),
|
||||||
|
fLocationInfo(location),
|
||||||
|
fCleanupBefore(cleanupBefore),
|
||||||
|
fMinimumStatesToKeep(minStatesToKeep)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CleanUpAdminDirectoryJob::~CleanUpAdminDirectoryJob()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CleanUpAdminDirectoryJob::Execute()
|
||||||
|
{
|
||||||
|
BStringList oldStatesAndTransactions;
|
||||||
|
status_t status = _GetOldStateDirectories(oldStatesAndTransactions);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
if (oldStatesAndTransactions.IsEmpty())
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
size_t totalSize = 0;
|
||||||
|
for (int32 i = 0; i < oldStatesAndTransactions.CountStrings(); i++) {
|
||||||
|
BDirectory dir(oldStatesAndTransactions.StringAt(i));
|
||||||
|
BEntry entry;
|
||||||
|
while (dir.GetNextEntry(&entry, true) == B_OK) {
|
||||||
|
off_t entrySize;
|
||||||
|
if (entry.GetSize(&entrySize) == B_OK)
|
||||||
|
totalSize += entrySize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BStringFormat format("{0, plural,"
|
||||||
|
"one{Clean up # old state (%size)?} other{Clean up # old states (%size)?}}");
|
||||||
|
|
||||||
|
BString question;
|
||||||
|
format.Format(question, oldStatesAndTransactions.CountStrings());
|
||||||
|
|
||||||
|
char buffer[128];
|
||||||
|
question.ReplaceAll("%size", string_for_size(totalSize, buffer, sizeof(buffer)));
|
||||||
|
|
||||||
|
bool yes = fContext.DecisionProvider().YesNoDecisionNeeded("", question,
|
||||||
|
"yes", "no", "yes");
|
||||||
|
if (!yes)
|
||||||
|
return B_CANCELED;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < oldStatesAndTransactions.CountStrings(); i++) {
|
||||||
|
BString directory = oldStatesAndTransactions.StringAt(i);
|
||||||
|
status_t status = BRemoveEngine().RemoveEntry(BRemoveEngine::Entry(
|
||||||
|
directory));
|
||||||
|
|
||||||
|
if (status != B_OK) {
|
||||||
|
SetErrorString(BString().SetToFormat("Failed to remove %s: %s\n",
|
||||||
|
directory.String(), strerror(status)));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CleanUpAdminDirectoryJob::_GetOldStateDirectories(BStringList& directories)
|
||||||
|
{
|
||||||
|
BDirectory packages(&fLocationInfo.PackagesDirectoryRef());
|
||||||
|
BDirectory administrative(&packages, PACKAGES_DIRECTORY_ADMIN_DIRECTORY);
|
||||||
|
if (administrative.InitCheck() != B_OK)
|
||||||
|
return administrative.InitCheck();
|
||||||
|
|
||||||
|
BStringList states, transactions;
|
||||||
|
BEntry entry;
|
||||||
|
int32 skippedStates = 0;
|
||||||
|
while (administrative.GetNextEntry(&entry) == B_OK) {
|
||||||
|
if (!entry.IsDirectory())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
time_t mtime;
|
||||||
|
if (entry.GetModificationTime(&mtime) != B_OK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BStringList* list = NULL;
|
||||||
|
BString name = entry.Name();
|
||||||
|
if (name.StartsWith("transaction-")) {
|
||||||
|
if (mtime >= fCleanupBefore)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
list = &transactions;
|
||||||
|
} else if (name.StartsWith("state_")) {
|
||||||
|
if (mtime >= fCleanupBefore) {
|
||||||
|
skippedStates++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!fLocationInfo.ActiveStateName().IsEmpty()
|
||||||
|
&& name >= fLocationInfo.ActiveStateName()) {
|
||||||
|
skippedStates++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list = &states;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->Add(BPath(&administrative, name).Path());
|
||||||
|
}
|
||||||
|
|
||||||
|
states.Sort();
|
||||||
|
while (!states.IsEmpty() && skippedStates < fMinimumStatesToKeep) {
|
||||||
|
states.Remove(states.CountStrings() - 1);
|
||||||
|
skippedStates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
directories.MakeEmpty();
|
||||||
|
directories.Add(states);
|
||||||
|
directories.Add(transactions);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CleanUpAdminDirectoryRequest::CleanUpAdminDirectoryRequest(const BContext& context,
|
||||||
|
const BInstallationLocationInfo& location,
|
||||||
|
time_t cleanupBefore, int32 minStatesToKeep)
|
||||||
|
:
|
||||||
|
inherited(context),
|
||||||
|
fLocationInfo(location),
|
||||||
|
fCleanupBefore(cleanupBefore),
|
||||||
|
fMinimumStatesToKeep(minStatesToKeep)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CleanUpAdminDirectoryRequest::~CleanUpAdminDirectoryRequest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CleanUpAdminDirectoryRequest::CreateInitialJobs()
|
||||||
|
{
|
||||||
|
status_t result = InitCheck();
|
||||||
|
if (result != B_OK)
|
||||||
|
return B_NO_INIT;
|
||||||
|
|
||||||
|
CleanUpAdminDirectoryJob* cleanUpJob
|
||||||
|
= new (std::nothrow) CleanUpAdminDirectoryJob(fContext,
|
||||||
|
"", fLocationInfo, fCleanupBefore, fMinimumStatesToKeep);
|
||||||
|
if (cleanUpJob == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
if ((result = QueueJob(cleanUpJob)) != B_OK) {
|
||||||
|
delete cleanUpJob;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace BPackageKit
|
||||||
@@ -77,6 +77,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
|||||||
AddRepositoryRequest.cpp
|
AddRepositoryRequest.cpp
|
||||||
Attributes.cpp
|
Attributes.cpp
|
||||||
ChecksumAccessors.cpp
|
ChecksumAccessors.cpp
|
||||||
|
CleanUpAdminDirectoryRequest.cpp
|
||||||
Context.cpp
|
Context.cpp
|
||||||
DaemonClient.cpp
|
DaemonClient.cpp
|
||||||
DownloadFileRequest.cpp
|
DownloadFileRequest.cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user